Skip to content

Prompt API

プロンプト API は、本番アプリケーションで大規模言語モデル (LLM) と対話するための包括的なツールキットを提供します。これは以下の機能を提供します。

  • 型安全な構造化プロンプトを作成するための Kotlin DSL
  • OpenAI、Anthropic、Google、その他のLLMプロバイダー向けの マルチプロバイダー対応
  • リトライロジック、エラーハンドリング、タイムアウト設定などの 本番環境向け機能
  • テキスト、画像、音声、ドキュメントを扱うための マルチモーダル機能

アーキテクチャの概要

プロンプト API は、主に3つのレイヤーで構成されます。

  • LLMクライアント: 特定のプロバイダー (OpenAI、Anthropicなど) への低レベルインターフェース。
  • デコレーター: リトライロジックなどの機能を追加するオプションのラッパー。
  • プロンプトエグゼキューター: クライアントのライフサイクルを管理し、使用を簡素化する高レベルの抽象化。

プロンプトの作成

プロンプト API は、Kotlin DSL を使用してプロンプトを作成します。以下の種類のメッセージをサポートしています。

  • system: LLMのコンテキストと指示を設定します。
  • user: ユーザー入力を表します。
  • assistant: LLMのレスポンスを表します。

シンプルなプロンプトの例を以下に示します。

kotlin
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構造を使用してメディアをプロンプトに追加します。

kotlin
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パラメータ内のリストとして提供します。

テキストメッセージとアタッチメントのリストを含むユーザーメッセージの一般的な形式は以下のとおりです。

kotlin
user(
    content = "This is the user message",
    attachments = listOf(
        // Add attachments
    )
)

ファイルアタッチメント

アタッチメントを含めるには、以下の形式に従ってattachmentsパラメータにファイルを提供します。

kotlin
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: jpgpngファイルなどの画像アタッチメント。
  • Attachment.Audio: mp3wavファイルなどの音声アタッチメント。
  • Attachment.Video: mpgaviファイルなどの動画アタッチメント。
  • Attachment.File: pdftxtファイルなどのファイルアタッチメント。

上記の各クラスは以下のパラメータを受け取ります。

名前データ型必須説明
contentAttachmentContentYes提供されるファイルコンテンツのソースです。詳細については、AttachmentContentを参照してください。
formatStringYes提供されるファイルの形式です。例: png
mimeTypeStringOnly for Attachment.File提供されるファイルのMIMEタイプです。例: image/png
fileNameStringNo拡張子を含む提供されるファイルの名前です。例: screenshot.png

詳細については、APIリファレンスを参照してください。

AttachmentContent

AttachmentContentは、LLMへの入力として提供されるコンテンツのタイプとソースを定義します。以下のクラスがサポートされています。

  • AttachmentContent.URL(val url: String)

    指定されたURLからファイルコンテンツを提供します。以下のパラメータを取ります。

    名前データ型必須説明
    urlStringYes提供されるコンテンツのURLです。

    詳細については、APIリファレンスも参照してください。

  • AttachmentContent.Binary.Bytes(val data: ByteArray)

    バイト配列としてファイルコンテンツを提供します。以下のパラメータを取ります。

    名前データ型必須説明
    dataByteArrayYesバイト配列として提供されるファイルコンテンツです。

    詳細については、APIリファレンスも参照してください。

  • AttachmentContent.Binary.Base64(val base64: String)

    Base64文字列としてエンコードされたファイルコンテンツを提供します。以下のパラメータを取ります。

    名前データ型必須説明
    base64StringYesファイルデータを含むBase64文字列です。

    詳細については、APIリファレンスも参照してください。

  • AttachmentContent.PlainText(val text: String)

TIP

アタッチメントタイプがAttachment.Fileの場合にのみ適用されます。

プレーンテキストファイル(text/plainMIMEタイプなど)からコンテンツを提供します。以下のパラメータを取ります。

名前データ型必須説明
textStringYesファイルのコンテンツです。

詳細については、APIリファレンスも参照してください。

混在するアタッチメントコンテンツ

異なるタイプのアタッチメントを個別のプロンプトやメッセージで提供するだけでなく、以下に示すように、単一のuserメッセージで複数かつ混在するタイプのアタッチメントを提供することもできます。

kotlin
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クライアントを提供します。

LLMクライアントを使用してプロンプトを実行するには、次の手順を実行します。

  1. アプリケーションとLLMプロバイダー間の接続を処理する、対応するLLMクライアントを作成します。例:
kotlin
// Create an OpenAI client
val client = OpenAILLMClient(apiKey)
  1. プロンプトとLLMを引数としてexecute メソッドを呼び出します。
kotlin
// Execute the prompt
val response = client.execute(
    prompt = prompt,
    model = OpenAIModels.Chat.GPT4o  // You can choose different models
)

以下は、OpenAIクライアントを使用してプロンプトを実行する例です。

kotlin

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プロバイダー向けのプロンプトエグゼキューターを作成するには、次の手順を実行します。

  1. 対応するAPIキーを使用して、特定のプロバイダーのLLMクライアントを構成します。
kotlin
val openAIClient = OpenAILLMClient(System.getenv("OPENAI_KEY"))
  1. SingleLLMPromptExecutorを使用してプロンプトエグゼキューターを作成します。
kotlin
val promptExecutor = SingleLLMPromptExecutor(openAIClient)

マルチプロバイダーエグゼキューターの作成

複数のLLMプロバイダーと連携するプロンプトエグゼキューターを作成するには、次の手順を実行します。

  1. 必要なLLMプロバイダーのクライアントを、対応するAPIキーを使用して構成します。
kotlin
val openAIClient = OpenAILLMClient(System.getenv("OPENAI_KEY"))
val ollamaClient = OllamaClient()
  1. 構成されたクライアントをMultiLLMPromptExecutor クラスのコンストラクタに渡し、複数のLLMプロバイダーを持つプロンプトエグゼキューターを作成します。
kotlin
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の実装。

事前定義された単一およびマルチプロバイダーエグゼキューターを作成する例を以下に示します。

kotlin
// 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でプロンプトを実行する方法の例を以下に示します。

kotlin
// Execute a prompt
val response = promptExecutor.execute(
    prompt = prompt,
    model = OpenAIModels.Chat.GPT4o
)

これにより、GPT4oモデルでプロンプトが実行され、レスポンスが返されます。

!!!note プロンプトエグゼキューターは、ストリーミングレスポンス、複数選択肢の生成、コンテンツモデレーションを可能にします。 詳細については、特定のExecutorのAPIリファレンスを参照してください。 コンテンツモデレーションの詳細については、「コンテンツモデレーション」を参照してください。

キャッシュ付きプロンプトエグゼキューター

繰り返されるリクエストの場合、LLMレスポンスをキャッシュしてパフォーマンスを最適化し、コストを削減できます。 Koogは、キャッシュ機能を追加するPromptExecutorのラッパーであるCachedPromptExecutorを提供します。 これにより、以前に実行されたプロンプトからのレスポンスを保存し、同じプロンプトが再度実行されたときにそれらを取得できます。

キャッシュ付きプロンプトエグゼキューターを作成するには、次の手順を実行します。

  1. レスポンスをキャッシュしたいプロンプトエグゼキューターを作成します。
kotlin
val client = OpenAILLMClient(System.getenv("OPENAI_KEY"))
val promptExecutor = SingleLLMPromptExecutor(client)
  1. 目的のキャッシュを使用してCachedPromptExecutorインスタンスを作成し、作成したプロンプトエグゼキューターを提供します。
kotlin
val cachedExecutor = CachedPromptExecutor(
    cache = FilePromptCache(Path("/cache_directory")),
    nested = promptExecutor
)
  1. 目的のプロンプトとモデルでキャッシュ付きプロンプトエグゼキューターを実行します。
kotlin
val response = cachedExecutor.execute(prompt, OpenAIModels.Chat.GPT4o)

これで、同じプロンプトを同じモデルで複数回実行しても、レスポンスはキャッシュから取得されます。

!!!note * キャッシュ付きプロンプトエグゼキューターでexecuteStreaming()を呼び出すと、レスポンスは単一のチャンクとして生成されます。 * キャッシュ付きプロンプトエグゼキューターでmoderate()を呼び出すと、リクエストはネストされたプロンプトエグゼキューターに転送され、キャッシュは使用されません。 * 複数選択肢のレスポンスのキャッシュはサポートされていません。

リトライ機能

LLMプロバイダーと連携する際、レート制限や一時的なサービス停止といった一時的なエラーに遭遇することがあります。RetryingLLMClientデコレーターは、あらゆるLLMクライアントに自動リトライロジックを追加します。

基本的な使用方法

既存のクライアントをリトライ機能でラップします。

kotlin
// 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 は、いくつかの事前定義されたリトライ設定を提供します。

設定最大試行回数初期遅延最大遅延ユースケース
DISABLED1回 (リトライなし)--開発とテスト
CONSERVATIVE3回2秒30秒通常の本番運用
AGGRESSIVE5回500ミリ秒20秒クリティカルな操作
PRODUCTION3回1秒20秒推奨されるデフォルト

これらを直接使用するか、カスタム設定を作成できます。

kotlin
// 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 (サービス利用不可)

特定のニーズに合わせてカスタムパターンを定義できます。

kotlin
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クライアントをリトライメカニズムでラップします。

kotlin
// 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"),
    ))

ストリーミングでのリトライ

ストリーミング操作はオプションでリトライできます。この機能はデフォルトで無効になっています。

kotlin
val config = RetryConfig(
    maxAttempts = 3
)

val client = RetryingLLMClient(baseClient, config)
val stream = client.executeStreaming(prompt, OpenAIModels.Chat.GPT4o)

!!!note ストリーミングのリトライは、最初のトークンを受信する前の接続障害にのみ適用されます。ストリーミングが開始されると、コンテンツの整合性を維持するためにエラーはそのまま渡されます。

タイムアウト設定

すべてのLLMクライアントは、リクエストのハングを防ぐためのタイムアウト設定をサポートしています。

kotlin
val client = OpenAILLMClient(
    apiKey = apiKey,
    settings = OpenAIClientSettings(
        timeoutConfig = ConnectionTimeoutConfig(
            connectTimeoutMillis = 5000,    // 接続確立まで5秒
            requestTimeoutMillis = 60000    // リクエスト全体で60秒
        )
    )
)

エラーハンドリング

本番環境でLLMを操作する際は、エラーハンドリング戦略を実装する必要があります。

  • 予期せぬエラーを処理するために、try-catchブロックを使用してください
  • デバッグのために、コンテキスト情報とともにエラーをログに出力してください
  • クリティカルな操作には、フォールバック戦略を実装してください
  • 再発する問題やシステムの問題を特定するために、リトライパターンを監視してください

包括的なエラーハンドリングの例を以下に示します。

kotlin
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()
        }
    }
}