チャットと言語モデル
こ のページでは低レベルの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
はインターフェースであり、以下の実装があります:
TextContent
ImageContent
AudioContent
VideoContent
PdfFileContent
どの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
を使用します。このバージョンは、コルーチンを通じて簡潔さとノンブロッキング動作の両方を提供します。