Prompt API
プロンプト API は、本番アプリケーションで大規模言語モデル (LLM) と対話するための包括的なツールキットを提供します。これは以下の機能を提供します。
- 型安全な構造化プロンプトを作成するための Kotlin DSL。
- OpenAI、Anthropic、Google、その他のLLMプロバイダー向けの マルチプロバイダー対応。
- リトライロジック、エラーハンドリング、タイムアウト設定などの 本番環境向け機能。
- テキスト、画像、音声、ドキュメントを扱うための マルチモーダル機能。
アーキテクチャの概要
プロンプト API は、主に3つのレイヤーで構成されます。
- LLMクライアント: 特定のプロバイダー (OpenAI、Anthropicなど) への低レベルインターフェース。
- デコレーター: リトライロジックなどの機能を追加するオプションのラッパー。
- プロンプトエグゼキューター: クライアントのライフサイクルを管理し、使用を簡素化する高レベルの抽象化。
プロンプトの作成
プロンプト API は、Kotlin DSL を使用してプロンプトを作成します。以下の種類のメッセージをサポートしています。
system
: LLMのコンテキストと指示を設定します。user
: ユーザー入力を表します。assistant
: LLMのレスポンスを表します。
シンプルなプロンプトの例を以下に示します。
val prompt = prompt("prompt_name", LLMParams()) {
// Add a system message to set the context
system("You are a helpful assistant.")
// Add a user message
user("Tell me about Kotlin")
// You can also add assistant messages for few-shot examples
assistant("Kotlin is a modern programming language...")
// Add another user message
user("What are its key features?")
}
マルチモーダル入力
プロンプト内でテキストメッセージを提供するだけでなく、Koog ではuser
メッセージとともに画像、音声、動画、ファイルをLLMに送信することもできます。 標準的なテキストのみのプロンプトと同様に、プロンプト構築のためのDSL構造を使用してメディアをプロンプトに追加します。
val prompt = prompt("multimodal_input") {
system("You are a helpful assistant.")
user {
+"Describe these images"
attachments {
image("https://example.com/test.png")
image(Path("/User/koog/image.png"))
}
}
}
テキストプロンプトコンテンツ
さまざまなアタッチメントタイプをサポートし、プロンプト内のテキスト入力とファイル入力を明確に区別するために、テキストメッセージはユーザープロンプト内の専用のcontent
パラメータに配置します。 ファイル入力を追加するには、attachments
パラメータ内のリストとして提供します。
テキストメッセージとアタッチメントのリストを含むユーザーメッセージの一般的な形式は以下のとおりです。
user(
content = "This is the user message",
attachments = listOf(
// Add attachments
)
)
ファイルアタッチメント
アタッチメントを含めるには、以下の形式に従ってattachments
パラメータにファイルを提供します。
user(
content = "Describe this image",
attachments = listOf(
Attachment.Image(
content = AttachmentContent.URL("https://example.com/capture.png"),
format = "png",
mimeType = "image/png",
fileName = "capture.png"
)
)
)
attachments
パラメータはファイル入力のリストを受け取り、各項目は以下のクラスのいずれかのインスタンスです。
Attachment.Image
:jpg
やpng
ファイルなどの画像アタッチメント。Attachment.Audio
:mp3
やwav
ファイルなどの音声アタッチメント。Attachment.Video
:mpg
やavi
ファイルなどの動画アタッチメント。Attachment.File
:pdf
やtxt
ファイルなどのファイルアタッチメント。
上記の各クラスは以下のパラメータを受け取ります。
名前 | データ型 | 必須 | 説明 |
---|---|---|---|
content | AttachmentContent | Yes | 提供されるファイルコンテンツのソースです。詳細については、AttachmentContentを参照してください。 |
format | String | Yes | 提供されるファイルの形式です。例: png 。 |
mimeType | String | Only for Attachment.File | 提供されるファイルのMIMEタイプです。例: image/png 。 |
fileName | String | No | 拡張子を含む提供されるファイルの名前です。例: screenshot.png 。 |
詳細については、APIリファレンスを参照してください。
AttachmentContent
AttachmentContent
は、LLMへの入力として提供されるコンテンツのタイプとソースを定義します。以下のクラスがサポートされています。
AttachmentContent.URL(val url: String)
指定されたURLからファイルコンテンツを提供します。以下のパラメータを取ります。
名前 データ型 必須 説明 url
String Yes 提供されるコンテンツのURLです。 詳細については、APIリファレンスも参照してください。
AttachmentContent.Binary.Bytes(val data: ByteArray)
バイト配列としてファイルコンテンツを提供します。以下のパラメータを取ります。
名前 データ型 必須 説明 data
ByteArray Yes バイト配列として提供されるファイルコンテンツです。 詳細については、APIリファレンスも参照してください。
AttachmentContent.Binary.Base64(val base64: String)
Base64文字列としてエンコードされたファイルコンテンツを提供します。以下のパラメータを取ります。
名前 データ型 必須 説明 base64
String Yes ファイルデータを含むBase64文字列です。 詳細については、APIリファレンスも参照してください。
AttachmentContent.PlainText(val text: String)
TIP
アタッチメントタイプがAttachment.File
の場合にのみ適用されます。
プレーンテキストファイル(text/plain
MIMEタイプなど)からコンテンツを提供します。以下のパラメータを取ります。
名前 | データ型 | 必須 | 説明 |
---|---|---|---|
text | String | Yes | ファイルのコンテンツです。 |
詳細については、APIリファレンスも参照してください。
混在するアタッチメントコンテンツ
異なるタイプのアタッチメントを個別のプロンプトやメッセージで提供するだけでなく、以下に示すように、単一のuser
メッセージで複数かつ混在するタイプのアタッチメントを提供することもできます。
val prompt = prompt("mixed_content") {
system("You are a helpful assistant.")
user {
+"Compare the image with the document content."
attachments {
image(Path("/User/koog/page.png"))
binaryFile(Path("/User/koog/page.pdf"), "application/pdf")
}
}
}
LLMクライアントとプロンプトエグゼキューターの選択
プロンプト API を使用する際、LLMクライアントまたはプロンプトエグゼキューターのいずれかを使用してプロンプトを実行できます。 クライアントとエグゼキューターのどちらを選択するかは、以下の要因を考慮してください。
- 単一のLLMプロバイダーを扱い、高度なライフサイクル管理を必要としない場合は、LLMクライアントを直接使用してください。詳細については、「LLMクライアントを使用したプロンプトの実行」を参照してください。
- LLMとそのライフサイクルを管理するためのより高レベルの抽象化が必要な場合、または複数のプロバイダー間で一貫したAPIでプロンプトを実行し、それらを動的に切り替えたい場合は、プロンプトエグゼキューターを使用してください。 詳細については、「プロンプトエグゼキューターを使用したプロンプトの実行」を参照してください。
!!!note LLMクライアントとプロンプトエグゼキューターはどちらも、ストリーミングレスポンス、複数選択肢の生成、コンテンツモデレーションを可能にします。 詳細については、特定のクライアントまたはエグゼキューターのAPIリファレンスを参照してください。
LLMクライアントを使用したプロンプトの実行
単一のLLMプロバイダーを扱い、高度なライフサイクル管理を必要としない場合は、LLMクライアントを使用してプロンプトを実行できます。 Koogは以下のLLMクライアントを提供します。
- OpenAILLMClient
- AnthropicLLMClient
- GoogleLLMClient
- OpenRouterLLMClient
- DeepSeekLLMClient
- OllamaClient
- BedrockLLMClient (JVMのみ)
LLMクライアントを使用してプロンプトを実行するには、次の手順を実行します。
- アプリケーションとLLMプロバイダー間の接続を処理する、対応するLLMクライアントを作成します。例:
// Create an OpenAI client
val client = OpenAILLMClient(apiKey)
- プロンプトとLLMを引数として
execute
メソッドを呼び出します。
// Execute the prompt
val response = client.execute(
prompt = prompt,
model = OpenAIModels.Chat.GPT4o // You can choose different models
)
以下は、OpenAIクライアントを使用してプロンプトを実行する例です。
fun main() {
runBlocking {
// Set up the OpenAI client with your API key
val token = System.getenv("OPENAI_API_KEY")
val client = OpenAILLMClient(token)
// Create a prompt
val prompt = prompt("prompt_name", LLMParams()) {
// Add a system message to set the context
system("You are a helpful assistant.")
// Add a user message
user("Tell me about Kotlin")
// You can also add assistant messages for few-shot examples
assistant("Kotlin is a modern programming language...")
// Add another user message
user("What are its key features?")
}
// Execute the prompt and get the response
val response = client.execute(prompt = prompt, model = OpenAIModels.Chat.GPT4o)
println(response)
}
}
!!!note LLMクライアントは、ストリーミングレスポンス、複数選択肢の生成、コンテンツモデレーションを可能にします。 詳細については、特定のクライアントのAPIリファレンスを参照してください。 コンテンツモデレーションの詳細については、「コンテンツモデレーション」を参照してください。
プロンプトエグゼキューターを使用したプロンプトの実行
LLMクライアントがプロバイダーへの直接アクセスを提供するのに対し、プロンプトエグゼキューターは、一般的なユースケースを簡素化し、クライアントのライフサイクル管理を処理する、より高レベルの抽象化を提供します。 これらは、次のような場合に理想的です。
- クライアント設定を管理せずに迅速なプロトタイプ作成を行いたい場合。
- 統一されたインターフェースを通じて複数のプロバイダーと連携したい場合。
- 大規模なアプリケーションでの依存性注入 (Dependency Injection) を簡素化したい場合。
- プロバイダー固有の詳細を抽象化したい場合。
エグゼキューターの種類
Koogは主に2つのプロンプトエグゼキューターを提供します。
名前 | 説明 |
---|---|
SingleLLMPromptExecutor | 単一のプロバイダーのLLMクライアントをラップします。エージェントが単一のLLMプロバイダー内でモデルを切り替える機能のみを必要とする場合、このエグゼキューターを使用してください。 |
MultiLLMPromptExecutor | プロバイダーごとに複数のLLMクライアントにルーティングし、リクエストされたプロバイダーが利用できない場合はオプションのフォールバックを使用します。エージェントが異なるプロバイダーのモデル間を切り替える必要がある場合、このエグゼキューターを使用してください。 |
これらは、LLMでプロンプトを実行するためのPromtExecutor
インターフェースの実装です。
単一プロバイダーエグゼキューターの作成
特定のLLMプロバイダー向けのプロンプトエグゼキューターを作成するには、次の手順を実行します。
- 対応するAPIキーを使用して、特定のプロバイダーのLLMクライアントを構成します。
val openAIClient = OpenAILLMClient(System.getenv("OPENAI_KEY"))
SingleLLMPromptExecutor
を使用してプロンプトエグゼキューターを作成します。
val promptExecutor = SingleLLMPromptExecutor(openAIClient)
マルチプロバイダーエグゼキューターの作成
複数のLLMプロバイダーと連携するプロンプトエグゼキューターを作成するには、次の手順を実行します。
- 必要なLLMプロバイダーのクライアントを、対応するAPIキーを使用して構成します。
val openAIClient = OpenAILLMClient(System.getenv("OPENAI_KEY"))
val ollamaClient = OllamaClient()
- 構成されたクライアントを
MultiLLMPromptExecutor
クラスのコンストラクタに渡し、複数のLLMプロバイダーを持つプロンプトエグゼキューターを作成します。
val multiExecutor = MultiLLMPromptExecutor(
LLMProvider.OpenAI to openAIClient,
LLMProvider.Ollama to ollamaClient
)
事前定義されたプロンプトエグゼキューター
より迅速なセットアップのために、Koogは一般的なプロバイダー向けに、以下のすぐに使用できるエグゼキューター実装を提供します。
特定のLLMクライアントで構成された
SingleLLMPromptExecutor
を返す単一プロバイダーエグゼキューター:simpleOpenAIExecutor
: OpenAIモデルでプロンプトを実行するため。simpleAzureOpenAIExecutor
: Azure OpenAI Serviceを使用してプロンプトを実行するため。simpleAnthropicExecutor
: Anthropicモデルでプロンプトを実行するため。simpleGoogleAIExecutor
: Googleモデルでプロンプトを実行するため。simpleOpenRouterExecutor
: OpenRouterでプロンプトを実行するため。simpleOllamaExecutor
: Ollamaでプロンプトを実行するため。
マルチプロバイダーエグゼキューター:
DefaultMultiLLMPromptExecutor
: OpenAI、Anthropic、GoogleプロバイダーをサポートするMultiLLMPromptExecutor
の実装。
事前定義された単一およびマルチプロバイダーエグゼキューターを作成する例を以下に示します。
// Create an OpenAI executor
val promptExecutor = simpleOpenAIExecutor("OPENAI_KEY")
// Create a DefaultMultiLLMPromptExecutor with OpenAI, Anthropic, and Google LLM clients
val openAIClient = OpenAILLMClient("OPENAI_KEY")
val anthropicClient = AnthropicLLMClient("ANTHROPIC_KEY")
val googleClient = GoogleLLMClient("GOOGLE_KEY")
val multiExecutor = DefaultMultiLLMPromptExecutor(openAIClient, anthropicClient, googleClient)
プロンプトの実行
プロンプトエグゼキューターは、ストリーミング、複数選択肢の生成、コンテンツモデレーションなど、さまざまな機能を使用してプロンプトを実行するメソッドを提供します。
execute
メソッドを使用して特定のLLMでプロンプトを実行する方法の例を以下に示します。
// Execute a prompt
val response = promptExecutor.execute(
prompt = prompt,
model = OpenAIModels.Chat.GPT4o
)
これにより、GPT4o
モデルでプロンプトが実行され、レスポンスが返されます。
!!!note プロンプトエグゼキューターは、ストリーミングレスポンス、複数選択肢の生成、コンテンツモデレーションを可能にします。 詳細については、特定のExecutorのAPIリファレンスを参照してください。 コンテンツモデレーションの詳細については、「コンテンツモデレーション」を参照してください。
キャッシュ付きプロンプトエグゼキューター
繰り返されるリクエストの場合、LLMレスポンスをキャッシュしてパフォーマンスを最適化し、コストを削減できます。 Koogは、キャッシュ機能を追加するPromptExecutor
のラッパーであるCachedPromptExecutor
を提供します。 これにより、以前に実行されたプロンプトからのレスポンスを保存し、同じプロンプトが再度実行されたときにそれらを取得できます。
キャッシュ付きプロンプトエグゼキューターを作成するには、次の手順を実行します。
- レスポンスをキャッシュしたいプロンプトエグゼキューターを作成します。
val client = OpenAILLMClient(System.getenv("OPENAI_KEY"))
val promptExecutor = SingleLLMPromptExecutor(client)
- 目的のキャッシュを使用して
CachedPromptExecutor
インスタンスを作成し、作成したプロンプトエグゼキューターを提供します。
val cachedExecutor = CachedPromptExecutor(
cache = FilePromptCache(Path("/cache_directory")),
nested = promptExecutor
)
- 目的のプロンプトとモデルでキャッシュ付きプロンプトエグゼキューターを実行します。
val response = cachedExecutor.execute(prompt, OpenAIModels.Chat.GPT4o)
これで、同じプロンプトを同じモデルで複数回実行しても、レスポンスはキャッシュから取得されます。
!!!note * キャッシュ付きプロンプトエグゼキューターでexecuteStreaming()
を呼び出すと、レスポンスは単一のチャンクとして生成されます。 * キャッシュ付きプロンプトエグゼキューターでmoderate()
を呼び出すと、リクエストはネストされたプロンプトエグゼキューターに転送され、キャッシュは使用されません。 * 複数選択肢のレスポンスのキャッシュはサポートされていません。
リトライ機能
LLMプロバイダーと連携する際、レート制限や一時的なサービス停止といった一時的なエラーに遭遇することがあります。RetryingLLMClient
デコレーターは、あらゆるLLMクライアントに自動リトライロジックを追加します。
基本的な使用方法
既存のクライアントをリトライ機能でラップします。
// Wrap any client with retry capability
val client = OpenAILLMClient(apiKey)
val resilientClient = RetryingLLMClient(client)
// Now all operations will automatically retry on transient errors
val response = resilientClient.execute(prompt, OpenAIModels.Chat.GPT4o)
リトライ動作の設定
Koog は、いくつかの事前定義されたリトライ設定を提供します。
設定 | 最大試行回数 | 初期遅延 | 最大遅延 | ユースケース |
---|---|---|---|---|
DISABLED | 1回 (リトライなし) | - | - | 開発とテスト |
CONSERVATIVE | 3回 | 2秒 | 30秒 | 通常の本番運用 |
AGGRESSIVE | 5回 | 500ミリ秒 | 20秒 | クリティカルな操作 |
PRODUCTION | 3回 | 1秒 | 20秒 | 推奨されるデフォルト |
これらを直接使用するか、カスタム設定を作成できます。
// Use the predefined configuration
val conservativeClient = RetryingLLMClient(
delegate = client,
config = RetryConfig.CONSERVATIVE
)
// Or create a custom configuration
val customClient = RetryingLLMClient(
delegate = client,
config = RetryConfig(
maxAttempts = 5,
initialDelay = 1.seconds,
maxDelay = 30.seconds,
backoffMultiplier = 2.0,
jitterFactor = 0.2
)
)
リトライ可能なエラーパターン
デフォルトでは、リトライメカニズムは一般的な一時的エラーを認識します。
HTTPステータスコード:
429
: レート制限500
: 内部サーバーエラー502
: バッドゲートウェイ503
: サービス利用不可504
: ゲートウェイタイムアウト529
: Anthropic過負荷
エラーキーワード:
- rate limit (レート制限)
- too many requests (リクエストが多すぎます)
- request timeout (リクエストタイムアウト)
- connection timeout (接続タイムアウト)
- read timeout (読み取りタイムアウト)
- write timeout (書き込みタイムアウト)
- connection reset by peer (ピアによって接続がリセットされました)
- connection refused (接続拒否)
- temporarily unavailable (一時的に利用不可)
- service unavailable (サービス利用不可)
特定のニーズに合わせてカスタムパターンを定義できます。
val config = RetryConfig(
retryablePatterns = listOf(
RetryablePattern.Status(429), // 特定のステータスコード
RetryablePattern.Keyword("quota"), // エラーメッセージ内のキーワード
RetryablePattern.Regex(Regex("ERR_\\d+")), // カスタム正規表現パターン
RetryablePattern.Custom { error -> // カスタムロジック
error.contains("temporary") && error.length > 20
}
)
)
プロンプトエグゼキューターでのリトライ
プロンプトエグゼキューターを使用する際は、エグゼキューターを作成する前に基盤となるLLMクライアントをリトライメカニズムでラップします。
// Single provider executor with retry
val resilientClient = RetryingLLMClient(
OpenAILLMClient(System.getenv("OPENAI_KEY")),
RetryConfig.PRODUCTION
)
val executor = SingleLLMPromptExecutor(resilientClient)
// Multi-provider executor with flexible client configuration
val multiExecutor = MultiLLMPromptExecutor(
LLMProvider.OpenAI to RetryingLLMClient(
OpenAILLMClient(System.getenv("OPENAI_KEY")),
RetryConfig.CONSERVATIVE
),
LLMProvider.Anthropic to RetryingLLMClient(
AnthropicLLMClient(System.getenv("ANTHROPIC_API_KEY")),
RetryConfig.AGGRESSIVE
),
// The Bedrock client already has a built-in AWS SDK retry
LLMProvider.Bedrock to BedrockLLMClient(
awsAccessKeyId = System.getenv("AWS_ACCESS_KEY_ID"),
awsSecretAccessKey = System.getenv("AWS_SECRET_ACCESS_KEY"),
awsSessionToken = System.getenv("AWS_SESSION_TOKEN"),
))
ストリーミングでのリトライ
ストリーミング操作はオプションでリトライできます。この機能はデフォルトで無効になっています。
val config = RetryConfig(
maxAttempts = 3
)
val client = RetryingLLMClient(baseClient, config)
val stream = client.executeStreaming(prompt, OpenAIModels.Chat.GPT4o)
!!!note ストリーミングのリトライは、最初のトークンを受信する前の接続障害にのみ適用されます。ストリーミングが開始されると、コンテンツの整合性を維持するためにエラーはそのまま渡されます。
タイムアウト設定
すべてのLLMクライアントは、リクエストのハングを防ぐためのタイムアウト設定をサポートしています。
val client = OpenAILLMClient(
apiKey = apiKey,
settings = OpenAIClientSettings(
timeoutConfig = ConnectionTimeoutConfig(
connectTimeoutMillis = 5000, // 接続確立まで5秒
requestTimeoutMillis = 60000 // リクエスト全体で60秒
)
)
)
エラーハンドリング
本番環境でLLMを操作する際は、エラーハンドリング戦略を実装する必要があります。
- 予期せぬエラーを処理するために、try-catchブロックを使用してください。
- デバッグのために、コンテキスト情報とともにエラーをログに出力してください。
- クリティカルな操作には、フォールバック戦略を実装してください。
- 再発する問題やシステムの問題を特定するために、リトライパターンを監視してください。
包括的なエラーハンドリングの例を以下に示します。
try {
val response = resilientClient.execute(prompt, model)
processResponse(response)
} catch (e: Exception) {
logger.error("LLM operation failed", e)
when {
e.message?.contains("rate limit") == true -> {
// レート制限を特別に処理
scheduleRetryLater()
}
e.message?.contains("invalid api key") == true -> {
// 認証エラーを処理
notifyAdministrator()
}
else -> {
// 代替ソリューションにフォールバック
useDefaultResponse()
}
}
}