SpringAIを利用してOpenAIのAPIと連携する方法

今回はSpringの一つのプロジェクトで、SpringAIの紹介とOpenAI社のAPIとの接続方法についてまとめました。
(2024/6/17 現在の内容となります)

## SpringAIとは
OpenAIなどの生成AIのAPIとの繋ぎこみを容易にするJavaのライブラリになります。
2024/6/16現在でOpenAI直接だけでなくAzure経由のOpenAIやAmazonのBedrockやGoogle Geminiなどの各種APIとの繋ぎこみが利用できます。

対応についてはこちらをご覧ください。

https://spring.io/projects/spring-ai

今回はOpenAIのAPIとの接続方法について説明します。

## OpenAIとの連携準備
今回はGradleで構築して、OpenAIの一番よく使われるチェットのAPIを使って応答を取得する方法です。
OpenAIのAPIキーは事前に順をしておいてください。

### 0.準備　
ライブラリの読み込み設定を定義します。gradleの設定は以下の通りです。
SpringBootも利用して実行するサンプルのため、読み込むライブラリにSpringBoot関連も含まれています。
最後にSpringBootでリクエストを受け付けて、レスポンスを返すコードを記載しています。

```
plugins {
	id 'java'
	id 'org.springframework.boot' version '3.3.0'
	id 'io.spring.dependency-management' version '1.1.5'
}

group = 'com.deaconess'
version = '0.0.1-SNAPSHOT'

java {
	toolchain {
		languageVersion = JavaLanguageVersion.of(22)
	}
}

repositories {
	mavenCentral()
	maven { url 'https://repo.spring.io/milestone' }
	maven { url 'https://repo.spring.io/snapshot' }
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
	implementation platform("org.springframework.ai:spring-ai-bom:1.0.0-SNAPSHOT")
	implementation 'org.springframework.ai:spring-ai-openai'
}

tasks.named('test') {
	useJUnitPlatform()
}
```

実際は他のライブラリも使用することになると思いますが、こちらが最低限の設定になるかと思います。

### 1.OpenAIのAPIの呼び出すためのクラスの宣言
OpenAIの呼び出しにはOpenAI用のクラスであるOpenAiApiクラスのインスタンスを生成します。


```java
OpenAiApi openAiApi = new OpenAiApi(APIキー);
```

### 2.APIを実際に実行するChatModelの宣言

```java 
ChatModel chatModel = new OpenAiChatModel(openAiApi);
```

これであとはプロンプトを与えてあげれば取得できます。
プロンプトの指定方法を記述します。

## プロンプトの指定
ここでは二つの指定方法を紹介します。どちらの方法もChatResponseというクラスが返されてきます。
今回はどちらもgpt-4oを使用するコードを紹介します。
### テキストでプロンプトを定義

```java 
ChatResponse response = chatModel.call(
                    new Prompt(
                                "プロンプトになる文字列",
                                OpenAiChatOptions.builder()
                                        .withModel("gpt-4o")
                                        .build()
                        ));
```
プロンプトになる文字列の部分に文字列(String型)を指定する方法です。
イメージとしては直接ChatGPTなどにテキストを入れるようなイメージです。

### メッセージクラスのリストで定義
こちらは専用のメッセージクラスで定義を行う方法で、文字列より細かく定義ができます。

```java
List<Message> messageList=new ArrayList<>();
                Message systemMessage=new SystemMessage("あなたは有能なアシスタントです。与えられた文章を要約することができます");
                Message userMessage=new UserMessage("私は~です");
                Message userMessageText=new UserMessage("要約したい文章");

                messageList.add(systemMessage);
                messageList.add(userMessage);
                messageList.add(userMessageText);

                ChatResponse response = chatModel.call(
                        new Prompt(
                                messageList,
                                OpenAiChatOptions.builder()
                                        .withModel("gpt-4o")
                                        .build()
                        ));
```

実行されるイメージとしては以下のjsonをAPIで呼んだパターンと同じ扱いになるはずです。

```json
messages=[
    {"role": "system", "content": "あなたは有能なアシスタントです。与えられた文章を要約することができます"},
    {"role": "user", "content": "私は~です"},
    {"role": "user", "content": "要約したい文章"}
  ]
```

SystemMessageに応答結果として定義したい共通の内容を入れて、実際に変化する部分をUserMessageに入れるという形なので、呼び出しの形が直感的にわかりやすいかと思います。

## 実際の全体のコード
SpringBootのリクエスト先としてopenAiResponseを定義してそのレスポンスを文字列で返すコードです。
SpringBootの起動などについては割愛します。

```java
@RequestMapping(value = "/openAiResponse",
            method = RequestMethod.GET)
    public String openAiResponse() {
        OpenAiApi openAiApi = new OpenAiApi("APIキー");
        ChatModel chatModel = new OpenAiChatModel(openAiApi);
        List<Message> messageList=new ArrayList<>();
        Message systemMessage=new SystemMessage("あなたは有能なアシスタントです。与えられた文章を要約することができます");
        Message userMessage=new UserMessage("私はエンジニアです");
        Message userMessageText=new UserMessage("このコードは私が記述したJavaで描かれているコードで、OpenAIのAPIを簡単に呼び出すことができます。呼び出しもシンプルで使いやすいライブラリであるSpringAIを使用しています。ぜひこのコードを参考にしてみてください。");

        messageList.add(systemMessage);
        messageList.add(userMessage);
        messageList.add(userMessageText);

        ChatResponse response = chatModel.call(
                new Prompt(
                        messageList,
                        OpenAiChatOptions.builder()
                                .withModel("gpt-4o")
                                .build()
                ));
        return response.getResult().getOutput().getContent();
    }
```
レスポンス
```
ユーザーが記述したJavaコードは、OpenAIのAPIを簡単に呼び出すためのものであり、SpringAIというシンプルで使いやすいライブラリを利用しています。
```


## まとめ
SpringAIのライブラリの簡単な利用方法についてまとめました。
様々な企業が出している代表的な大規模言語モデルのAPIを呼び出すことを一つのライブラリだけでできるように
してくれるのは、個別のライブラリを用意したり対応した言語のライブラリがないと自作する必要が出てくる部分を補ってくれるのでとっても良いと思いました。
注意点は様々なAPIに対応するライブラリなので、一つのライブラリで色々な連携ができる反面最新の新しいAPIの反映は特定のAPIを専門にしているライブラリに軍配が上がると思われます。
一般的なAPIはSpringAI、新しいAPIを試したい場合は専門のライブラリというふうな使い分けが必要になってくると感じました。

料金についても記載しておくと、あくまでSpringAIは各種APIとの橋渡しをしているだけなのでライブラリの利用に料金はかかりません。かかるのはAIのAPIの利用料金なのでシステムに組み込んだ際、必要以上にAPIを実行しないようになど実装上気を付ける必要はあるかと思います。

## PS
使い始めて数週間しか経っておらず詳しくみれていない部分もあるので、これから詳細も必要だと思っています。