チャットと言語モデル
こ のページでは低レベルのLLM APIについて説明します。 高レベルのLLM APIについてはAIサービスをご覧ください。
サポートされているすべてのLLMはこちらで確認できます。
LLMは現在、2種類のAPIタイプで利用可能です:
LanguageModel。そのAPIは非常にシンプルで、入力としてStringを受け取り、出力としてStringを返します。 このAPIは現在、チャットAPI(2番目のAPIタイプ)に取って代わられつつあります。ChatModel。これらは入力として複数のChatMessageを受け取り、出力として単一のAiMessageを返します。ChatMessageは通常テキストを含みますが、一部のLLMは他のモダリティ(画像、音声など)もサポートしています。 そのようなチャットモデルの例には、OpenAIのgpt-4o-miniやGoogleのgemini-1.5-proがあります。
LangChain4jではLanguageModelのサポ ートはこれ以上拡張されないため、
すべての新機能ではChatModel APIを使用します。
ChatModelはLangChain4jでLLMと対話するための低レベルAPIであり、最も強力で柔軟性を提供します。
また、高レベルAPI(AIサービス)もあり、基本を説明した後で後ほど説明します。
ChatModelとLanguageModelの他に、LangChain4jは以下のタイプのモデルをサポートしています:
EmbeddingModel- このモデルはテキストをEmbeddingに変換できます。ImageModel- このモデルはImageを生成および編集できます。ModerationModel- このモデルはテキストに有害なコンテンツが含まれているかどうかを確認できます。ScoringModel- このモデルはクエリに対して複数のテキスト片をスコアリング(またはランク付け)し、 本質的に各テキスト片がクエリにどれだけ関連しているかを判断します。これはRAGに役立ちます。 これらについては後ほど説明します。
では、ChatModel APIをより詳しく見てみましょう。
public interface ChatModel {
String chat(String userMessage);
...
}
ご覧のように、LanguageModelと同様に、入力としてStringを受け取り、出力としてStringを返す単純なchatメソッドがあります。
これは単なる便宜的なメソッドで、StringをUserMessageでラップする必要なく、素早く簡単に試すことができます。
他のチャットAPIメソッドは以下の通りです:
...
ChatResponse chat(ChatMessage... messages);
ChatResponse chat(List<ChatMessage> messages);
...
これらのバージョンのchatメソッドは、入力として1つまたは複数のChatMessageを受け取ります。
ChatMessageはチャットメッセージを表す基本インターフェースです。
次のセクションでは、チャットメッセージについて詳しく説明します。
リクエスト をカスタマイズしたい場合(モデル名、温度、ツール、JSONスキーマなどを指定する場合)、
chat(ChatRequest)メソッドを使用できます:
...
ChatResponse chat(ChatRequest chatRequest);
...
ChatRequest chatRequest = ChatRequest.builder()
.messages(...)
.modelName(...)
.temperature(...)
.topP(...)
.topK(...)
.frequencyPenalty(...)
.presencePenalty(...)
.maxOutputTokens(...)
.stopSequences(...)
.toolSpecifications(...)
.toolChoice(...)
.responseFormat(...)
.parameters(...) // 共通またはプロバイダー固有のパラメータをまとめて設定することもできます
.build();
ChatResponse chatResponse = chatModel.chat(chatRequest);
ChatMessageの種類
現在、メッセージの「ソース」ごとに4種類のチャットメッセージがあります:
UserMessage:これはユーザーからのメッセージです。 ユーザーはアプリケーションのエンドユーザー(人間)またはアプリケーション自体のいずれかです。 LLMがサポートするモダリティに応じて、UserMessageはテキスト(String)のみを含むか、 または他のモダリティを含むことができます。AiMessage:これはAIによって生成されたメッセージで、通常はUserMessageに応答して生成されます。 お気づきかもしれませんが、generateメソッドはResponseでラップされたAiMessageを返します。AiMessageはテキスト応答(String)またはツールを実行するリクエスト(ToolExecutionRequest)を含むことができます。 ツールについては別のセクションで詳しく説明します。ToolExecutionResultMessage:これはToolExecutionRequestの結果です。SystemMessage:これはシステムからのメッセージです。 通常、開発者であるあなたがこのメッセージの内容を定義する必要があります。 通常、ここにはLLMの役割、どのように振る舞うべきか、どのようなスタイルで回答するかなどの指示を書きます。 LLMは他のタイプのメッセージよりもSystemMessageにより注意を払うように訓練されているため、 注意が必要であり、エンドユーザーにSystemMessageを自由に定義したり入力を注入したりする権限を与えないほうが良いでしょう。 通常、会話の冒頭に配置されます。CustomMessage:これは任意の属性を含むことができるカスタムメッセージです。このメッセージタイプは それをサポートするChatModel実装でのみ使用できます(現在はOllamaのみ)。
これでChatMessageのすべての種類を知ったので、会話でそれらをどのように組み合わせるかを見てみましょう。
最も単純なシナリオでは、chatメソッドにUserMessageの単一インスタンスを提供できます。
これは入力としてStringを受け取る最初のバージョンのchatメソッドと似ています。
主な違いは、StringではなくChatResponseを返すことです。
AiMessageに加えて、ChatResponseはChatResponseMetadataも含みます。
ChatResponseMetadataはTokenUsageを含み、これには入力(generateメソッドに提供したすべてのChatMessage)に含まれるトークン数、
出力(AiMessage内)として生成されたトークン数、および合 計(入力+出力)に関する統計が含まれます。
この情報は、LLMへの特定の呼び出しがどれだけのコストがかかるかを計算するために必要です。
また、ChatResponseMetadataにはFinishReasonも含まれており、
これは生成が停止した様々な理由を示す列挙型です。
通常、LLMが自ら生成を停止することを決定した場合はFinishReason.STOPになります。
内容に応じてUserMessageを作成する方法はいくつかあります。
最も単純なのはnew UserMessage("Hi")またはUserMessage.from("Hi")です。
複数のChatMessage
では、なぜ1つだけでなく、複数のChatMessageを入力として提供する必要があるのでしょうか?
これは、LLMが本質的にステートレスであり、会話の状態を維持しないためです。
したがって、複数ターンの会話をサポートしたい場合は、会話の状態を管理する必要があります。
チャットボットを構築したいとします。ユーザーとチャットボット(AI)の間の単純な複数ターンの会話を想像してみてください:
- ユーザー:こんにちは、私の名前はKlausです
- AI:こんにちはKlausさん、どのようにお手伝いできますか?
- ユーザー:私の名前は何ですか?
- AI:Klausです
これがChatModelとの対話がどのようになるかです:
UserMessage firstUserMessage = UserMessage.from("Hello, my name is Klaus");
AiMessage firstAiMessage = model.chat(firstUserMessage).aiMessage(); // Hi Klaus, how can I help you?
UserMessage secondUserMessage = UserMessage.from("What is my name?");
AiMessage secondAiMessage = model.chat(firstUserMessage, firstAiMessage, secondUserMessage).aiMessage(); // Klaus
ご覧のように、chatメソッドの2回目の呼び出しでは、単一のsecondUserMessageだけでなく、
会話の前のメッセージも提供しています。
これらのメッセージを手動で維持・管理することは面倒です。
そのため、ChatMemoryの概念が存在し、これについては次のセクションで詳しく説明します。
マルチモダリティ
UserMessageはテキストだけでなく、他のタイプのコンテンツも含むことができます。
UserMessageはList<Content> contentsを含みます。
Contentはインターフェースであり、以下の実装があります:
TextContentImageContentAudioContentVideoContentPdfFileContent
どのLLMプロバイダーがどのモダリティをサポートしているかは、こちらの比較表で確認できます。
以下はテキストと画像の両方をLLMに送信する例です:
UserMessage userMessage = UserMessage.from(
TextContent.from("Describe the following image"),
ImageContent.from("https://example.com/cat.jpg")
);
ChatResponse response = model.chat(userMessage);
テキストコンテンツ
TextContentは単純なテキストを表す最も単純な形式のContentで、単一のStringをラップします。
UserMessage.from(TextContent.from("Hello!"))はUserMessage.from("Hello!")と同等です。
UserMessage内に1つまたは複数のTextContentを提供できます:
UserMessage userMessage = UserMessage.from(
TextContent.from("Hello!"),
TextContent.from("How are you?")
);
画像コンテンツ
LLMプロバイダーに応じて、ImageContentはリモート画像のURL(上記の例を参照)から作成するか、
Base64エンコードされたバイナリデータから作成できます:
byte[] imageBytes = readBytes("/home/me/cat.jpg");
String base64Data = Base64.getEncoder().encodeToString(imageBytes);
ImageContent imageContent = ImageContent.from(base64Data, "image/jpg");
UserMessage userMessage = UserMessage.from(imageContent);
また、モデルが画像を処理する方法を制御するためにDetailLevel列挙型(LOW/HIGH/AUTOオプション)を指定することもできます。
詳細はこちらをご覧ください。
音声コンテンツ
AudioContentはImageContentに似ていますが、音声コンテンツを表します。
ビデオコンテンツ
VideoContentはImageContentに似ていますが、ビデオコンテンツを表します。
PDFファイルコンテンツ
PdfFileContentはImageContentに似ていますが、PDFファイルのバイナリコンテンツを表します。
Kotlin拡張機能
ChatModel Kotlin拡張機能は、Kotlinのコルーチン機能を活用して、言語モデルとのチャット対話を処理する非同期メソッドを提供します。chatAsyncメソッドは、ChatRequestまたはChatRequest.Builder構成のノンブロッキング処理を可能にし、モデルの返信を含むChatResponseを返します。同様に、generateAsyncはチャットメッセージからの応答の非同期生成を処理します。これらの拡張機能により、Kotlinアプリケーションでのチャッ トリクエストの構築と会話の効率的な処理が簡素化されます。これらのメソッドは実験的なものとしてマークされており、時間とともに進化する可能性があることに注意してください。
ChatModel.chatAsync(request: ChatRequest):Kotlinコルーチン用に設計されたこの非同期拡張関数は、同期的なchatメソッドをDispatchers.IOを使用してコルーチンスコープ内でラップします。これにより、アプリケーションの応答性を維持するために重要なノンブロッキング操作が可能になります。既存の同期的なchatとの競合を避けるために、特にchatAsyncと名付けられています。その関数シグネチャは:suspend fun ChatModel.chatAsync(request: ChatRequest): ChatResponseです。キーワードsuspendはそれがコルーチン関数であることを示します。
ChatModel.chat(block: ChatRequestBuilder.() -> Unit):このchatのバリアントは、KotlinのタイプセーフビルダーDSLを使用することで、よりスリムなアプローチを提供します。これにより、ChatRequestオブジェクトの構築が簡素化され、内部的には非同期実行のためにchatAsyncを使用します。このバージョンは、コルーチンを通じて簡潔さとノンブロッキング動作の両方を提供します。