Skip to content

Prompt API

Prompt API 提供了一个全面的工具包,用于在生产应用程序中与大语言模型 (LLM) 进行交互。它提供:

  • 结合类型安全的 Kotlin DSL 用于创建结构化提示词。
  • 对 OpenAI、Anthropic、Google 及其他 LLM 提供方提供多提供方支持
  • 生产特性,例如重试逻辑、错误处理和超时配置。
  • 用于处理文本、图像、音频和文档的多模态能力

架构概览

Prompt API 由三个主要层组成:

  • LLM 客户端:连接特定提供方(OpenAI、Anthropic 等)的底层接口。
  • 装饰器:可选的包装器,用于添加重试逻辑等功能。
  • 提示词执行器:管理客户端生命周期并简化使用的高级抽象。

创建提示词

Prompt API 使用 Kotlin DSL 创建提示词。它支持以下消息类型:

  • system:设置 LLM 的上下文和指令。
  • user:表示用户输入。
  • assistant:表示 LLM 响应。

这是一个简单提示词的示例:

kotlin
val prompt = prompt("prompt_name", LLMParams()) {
    // 添加 system 消息以设置上下文
    system("You are a helpful assistant.")

    // 添加 user 消息
    user("Tell me about Kotlin")

    // 你还可以添加 assistant 消息用于少样本示例
    assistant("Kotlin is a modern programming language...")

    // 再添加一条 user 消息
    user("What are its key features?")
}

多模态输入

除了在提示词中提供文本消息外,Koog 还允许你将图像、音频、视频和文件与 user 消息一同发送给 LLM。与标准的纯文本提示词一样,你也可以使用 DSL 结构来构建提示词并向其添加媒体。

kotlin
val prompt = prompt("multimodal_input") {
    system("You are a helpful assistant.")

    user {
        +"描述这些图像"

        attachments {
            image("https://example.com/test.png")
            image(Path("/User/koog/image.png"))
        }
    }
}

文本提示词内容

为了支持各种附件类型并在提示词中区分文本输入和文件输入,你可以将文本消息放在 user 提示词中一个专用的 content 形参中。要添加文件输入,请将它们作为列表提供给 attachments 形参。

包含文本消息和附件列表的 user 消息的通用格式如下:

kotlin
user(
    content = "This is the user message",
    attachments = listOf(
        // 添加附件
    )
)

文件附件

要包含附件,请将文件提供给 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/plain MIME 类型)的内容。接受以下形参:

名称数据类型必需描述
textStringYes文件的内容。

另请参见 API 参考

混合附件内容

除了在单独的提示词或消息中提供不同类型的附件外,你还可以在单个 user 消息中提供多种混合类型的附件:

kotlin
val prompt = prompt("mixed_content") {
    system("You are a helpful assistant.")

    user {
        +"比较图像与文档内容。"

        attachments {
            image(Path("/User/koog/page.png"))
            binaryFile(Path("/User/koog/page.pdf"), "application/pdf")
        }
    }
}

选择 LLM 客户端还是提示词执行器

在使用 Prompt API 时,你可以使用 LLM 客户端或提示词执行器来运行提示词。要在这两者之间进行选择,请考虑以下因素:

  • 如果你使用单个 LLM 提供方并且不需要高级生命周期管理,请直接使用 LLM 客户端。要了解更多信息,请参见 使用 LLM 客户端运行提示词
  • 如果你需要更高级别的抽象来管理 LLM 及其生命周期,或者你希望通过跨多个提供方的统一 API 运行提示词并动态切换它们,请使用提示词执行器。要了解更多信息,请参见 使用提示词执行器运行提示词

!!!note LLM 客户端和提示词执行器都允许你流式传输响应、生成多个选择和运行内容审核。有关更多信息,请参考特定客户端或执行器的 API 参考

使用 LLM 客户端运行提示词

如果你使用单个 LLM 提供方且不需要高级生命周期管理,则可以使用 LLM 客户端来运行提示词。Koog 提供以下 LLM 客户端:

要使用 LLM 客户端运行提示词,请执行以下操作:

  1. 创建 LLM 客户端,用于处理你的应用程序与 LLM 提供方之间的连接。例如:
kotlin
// 创建一个 OpenAI 客户端
val client = OpenAILLMClient(apiKey)
  1. 调用 execute 方法,将提示词和 LLM 作为实参。
kotlin
// 执行提示词
val response = client.execute(
    prompt = prompt,
    model = OpenAIModels.Chat.GPT4o  // 你可以选择不同的模型
)

这是一个使用 OpenAI 客户端运行提示词的示例:

kotlin

fun main() {
    runBlocking {
        // 使用你的 API 密钥设置 OpenAI 客户端
        val token = System.getenv("OPENAI_API_KEY")
        val client = OpenAILLMClient(token)

        // 创建提示词
        val prompt = prompt("prompt_name", LLMParams()) {
            // 添加 system 消息以设置上下文
            system("You are a helpful assistant.")

            // 添加 user 消息
            user("Tell me about Kotlin")

            // 你还可以添加 assistant 消息用于少样本示例
            assistant("Kotlin is a modern programming language...")

            // 再添加一条 user 消息
            user("What are its key features?")
        }

        // 执行提示词并获取响应
        val response = client.execute(prompt = prompt, model = OpenAIModels.Chat.GPT4o)
        println(response)
    }
}

!!!note LLM 客户端允许你流式传输响应、生成多个选择和运行内容审核。有关更多信息,请参考特定客户端的 API 参考。要了解有关内容审核的更多信息,请参见 Content moderation

使用提示词执行器运行提示词

尽管 LLM 客户端提供了对提供方的直接访问,但提示词执行器提供了一种更高级的抽象,可以简化常见用例并处理客户端生命周期管理。它们在你需要以下情况时非常理想:

  • 无需管理客户端配置即可快速原型化。
  • 通过统一接口与多个提供方协同工作。
  • 在大型应用程序中简化依赖注入。
  • 抽象化提供方特有的细节。

执行器类型

Koog 提供两种主要的提示词执行器:

名称描述
SingleLLMPromptExecutor包装单个 LLM 客户端用于一个提供方。如果你的代理仅需要在一个 LLM 提供方内切换模型,请使用此执行器。
MultiLLMPromptExecutor按提供方路由到多个 LLM 客户端,并为每个提供方提供可选的回退机制,以便在请求的提供方不可用时使用。如果你的代理需要在不同提供方的模型之间切换,请使用此执行器。

它们是用于使用 LLM 执行提示词的 PromtExecutor 接口的实现。

创建单提供方执行器

要为特定的 LLM 提供方创建提示词执行器,请执行以下操作:

  1. 为特定提供方配置 LLM 客户端,并提供相应的 API 密钥:
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 服务执行提示词。
    • simpleAnthropicExecutor 用于使用 Anthropic 模型执行提示词。
    • simpleGoogleAIExecutor 用于使用 Google 模型执行提示词。
    • simpleOpenRouterExecutor 用于使用 OpenRouter 执行提示词。
    • simpleOllamaExecutor 用于使用 Ollama 执行提示词。
  • 多提供方执行器:

    • DefaultMultiLLMPromptExecutorMultiLLMPromptExecutor 的一个实现,支持 OpenAI、Anthropic 和 Google 提供方。

以下是创建预定义的单提供方和多提供方执行器的示例:

kotlin
// 创建一个 OpenAI 执行器
val promptExecutor = simpleOpenAIExecutor("OPENAI_KEY")

// 创建一个包含 OpenAI、Anthropic 和 Google LLM 客户端的 DefaultMultiLLMPromptExecutor
val openAIClient = OpenAILLMClient("OPENAI_KEY")
val anthropicClient = AnthropicLLMClient("ANTHROPIC_KEY")
val googleClient = GoogleLLMClient("GOOGLE_KEY")
val multiExecutor = DefaultMultiLLMPromptExecutor(openAIClient, anthropicClient, googleClient)

执行提示词

提示词执行器提供使用各种功能(例如流式传输、生成多个选择和内容审核)来运行提示词的方法。

以下是使用 execute 方法通过特定 LLM 运行提示词的示例:

kotlin
// 执行提示词
val response = promptExecutor.execute(
    prompt = prompt,
    model = OpenAIModels.Chat.GPT4o
)

这将使用 GPT4o 模型运行提示词并返回响应。

!!!note 提示词执行器允许你流式传输响应、生成多个选择和运行内容审核。有关更多信息,请参考特定执行器的 API 参考。要了解有关内容审核的更多信息,请参见 Content moderation

缓存提示词执行器

对于重复请求,你可以缓存 LLM 响应以优化性能并降低成本。Koog 提供了 CachedPromptExecutor,它是 PromptExecutor 的一个包装器,添加了缓存功能。它允许你存储先前执行的提示词的响应,并在再次运行相同的提示词时检索它们。

要创建缓存提示词执行器,请执行以下操作:

  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
// 将任何客户端包装为具有重试能力
val client = OpenAILLMClient(apiKey)
val resilientClient = RetryingLLMClient(client)

// 现在,所有操作都将在瞬时错误时自动重试
val response = resilientClient.execute(prompt, OpenAIModels.Chat.GPT4o)

配置重试行为

Koog 提供了几种预定义的重试配置:

配置最大尝试次数初始延迟最大延迟用例
DISABLED1(无重试)--开发和测试
CONSERVATIVE32秒30秒正常生产使用
AGGRESSIVE5500毫秒20秒关键操作
PRODUCTION31秒20秒推荐默认值

直接使用它们或创建自定义配置:

kotlin
// 使用预定义配置
val conservativeClient = RetryingLLMClient(
    delegate = client,
    config = RetryConfig.CONSERVATIVE
)

// 或者创建自定义配置
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
// 具有重试功能的单提供方执行器
val resilientClient = RetryingLLMClient(
    OpenAILLMClient(System.getenv("OPENAI_KEY")),
    RetryConfig.PRODUCTION
)
val executor = SingleLLMPromptExecutor(resilientClient)

// 具有灵活客户端配置的多提供方执行器
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  
    ),
    // Bedrock 客户端已内置 AWS SDK 重试功能
    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 流式传输重试仅适用于在收到第一个 token 之前的连接失败。一旦流式传输开始,错误将直接传递以保留内容完整性。

超时配置

所有 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()
        }
    }
}