提示词 API
Prompt API 提供了一个全面的工具包,用于在生产应用程序中与大语言模型 (LLM) 进行交互。它提供:
- 结合类型安全的 Kotlin DSL 用于创建结构化提示词
- 对 OpenAI、Anthropic、Google 及其他 LLM 提供方提供多提供方支持
- 生产特性,例如重试逻辑、错误处理和超时配置
- 用于处理文本、图像、音频和文档的多模态能力
架构概览
Prompt API 由三个主要层组成:
- LLM 客户端 - 连接特定提供方(OpenAI、Anthropic 等)的底层接口
- 装饰器 - 可选的包装器,用于添加重试逻辑等功能
- 提示词执行器 - 管理客户端生命周期并简化使用的高级抽象
创建提示词
Prompt API 使用 Kotlin DSL 创建提示词。它支持以下消息类型:
system
: 设置 LLM 的上下文和指令。user
: 表示用户输入。assistant
: 表示 LLM 响应。
这是一个简单提示词的示例:
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?")
}
执行提示词
要使用特定的 LLM 执行提示词,你需要完成以下步骤:
- 创建相应的 LLM 客户端,用于处理你的应用程序与 LLM 提供方之间的连接。例如:
// 创建一个 OpenAI 客户端
val client = OpenAILLMClient(apiKey)
- 调用
execute
方法,将提示词和 LLM 作为实参。
// 执行提示词
val response = client.execute(
prompt = prompt,
model = OpenAIModels.Chat.GPT4o // 你可以选择不同的模型
)
以下 LLM 客户端可用:
- OpenAILLMClient
- AnthropicLLMClient
- GoogleLLMClient
- OpenRouterLLMClient
- OllamaClient
- BedrockLLMClient (仅限 JVM)
这是一个使用 Prompt API 的简单示例:
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)
}
}
重试功能
与 LLM 提供方协同工作时,你可能会遇到瞬时错误,例如速率限制或临时服务不可用。RetryingLLMClient
装饰器为任何 LLM 客户端添加了自动重试逻辑。
基本用法
将任何现有客户端包装为具有重试能力:
// 将任何客户端包装为具有重试能力
val client = OpenAILLMClient(apiKey)
val resilientClient = RetryingLLMClient(client)
// 现在,所有操作都将在瞬时错误时自动重试
val response = resilientClient.execute(prompt, OpenAIModels.Chat.GPT4o)
配置重试行为
Koog 提供了几种预定义的重试配置:
配置 | 最大尝试次数 | 初始延迟 | 最大延迟 | 用例 |
---|---|---|---|---|
DISABLED | 1(无重试) | - | - | 开发/测试 |
CONSERVATIVE | 3 | 2秒 | 30秒 | 正常生产使用 |
AGGRESSIVE | 5 | 500毫秒 | 20秒 | 关键操作 |
PRODUCTION | 3 | 1秒 | 20秒 | 推荐默认值 |
直接使用它们或创建自定义配置:
// 使用预定义配置
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
- 错误关键词:"rate limit"(速率限制)、"timeout"(超时)、"connection reset"(连接重置)、"overloaded"(过载)
你可以根据特定需求定义自定义模式:
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
}
)
)
提示词执行器重试
使用提示词执行器时,在创建执行器之前包装底层客户端:
// 具有重试功能的单提供方执行器
val resilientClient = RetryingLLMClient(
OpenAILLMClient(System.getenv("OPENAI_API_KEY")),
RetryConfig.PRODUCTION
)
val executor = SingleLLMPromptExecutor(resilientClient)
// 具有灵活客户端配置的多提供方执行器
val multiExecutor = MultiLLMPromptExecutor(
LLMProvider.OpenAI to RetryingLLMClient(
OpenAILLMClient(System.getenv("OPENAI_API_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"),
))
流式传输重试
流式传输操作可以选择性地重试(默认禁用):
val config = RetryConfig(
maxAttempts = 3
)
val client = RetryingLLMClient(baseClient, config)
val stream = client.executeStreaming(prompt, OpenAIModels.Chat.GPT4o)
注意:流式传输重试仅适用于在收到第一个 token 之前的连接失败。一旦流式传输开始,错误将直接传递以保留内容完整性。
超时配置
所有 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()
}
}
}
多模态输入
除了在提示词中提供文本消息外,Koog 还允许你将图像、音频、视频和文件与 user
消息一同发送给 LLM。与标准的纯文本提示词一样,你也可以使用 DSL 结构来构建提示词并向其添加媒体。
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
消息的通用格式如下:
user(
content = "This is the user message",
attachments = listOf(
// 添加附件
)
)
文件附件
要包含附件,请将文件提供给 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 。 |
AttachmentContent
AttachmentContent
定义了作为 LLM 输入提供的内容的类型和源。支持以下类:
AttachmentContent.URL(val url: String)
从指定的 URL 提供文件内容。接受以下形参:
名称 | 数据类型 | 必需 | 描述 |
---|---|---|---|
url | String | Yes | 所提供内容的 URL。 |
AttachmentContent.Binary.Bytes(val data: ByteArray)
将文件内容作为字节数组提供。接受以下形参:
名称 | 数据类型 | 必需 | 描述 |
---|---|---|---|
data | ByteArray | Yes | 作为字节数组提供的文件内容。 |
AttachmentContent.Binary.Base64(val base64: String)
将文件内容编码为 Base64 字符串提供。接受以下形参:
名称 | 数据类型 | 必需 | 描述 |
---|---|---|---|
base64 | String | Yes | 包含文件数据的 Base64 字符串。 |
AttachmentContent.PlainText(val text: String)
仅当附件类型为 Attachment.File
时适用。提供纯文本文件(例如 text/plain
MIME 类型)的内容。接受以下形参:
名称 | 数据类型 | 必需 | 描述 |
---|---|---|---|
text | String | Yes | 文件的内容。 |
混合附件内容
除了在单独的提示词或消息中提供不同类型的附件外,你还可以在单个 user
消息中提供多种混合类型的附件,如下所示:
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 客户端提供了对提供方的直接访问,但 提示词执行器 提供了一种更高级的抽象,可以简化常见用例并处理客户端生命周期管理。它们在以下场景中非常适用:
- 无需管理客户端配置即可快速原型化
- 通过统一接口与多个提供方协同工作
- 在大型应用程序中简化依赖注入
- 抽象化提供方特有的细节
执行器类型
Koog 框架提供了几种提示词执行器:
单提供方执行器:
simpleOpenAIExecutor
: 用于执行带有 OpenAI 模型的提示词。simpleAnthropicExecutor
: 用于执行带有 Anthropic 模型的提示词。simpleGoogleExecutor
: 用于执行带有 Google 模型的提示词。simpleOpenRouterExecutor
: 用于执行带有 OpenRouter 的提示词。simpleOllamaExecutor
: 用于执行带有 Ollama 的提示词。
多提供方执行器:
DefaultMultiLLMPromptExecutor
: 用于与多个 LLM 提供方协同工作
创建单提供方执行器
要为特定的 LLM 提供方创建提示词执行器,请使用相应函数。例如,要创建 OpenAI 提示词执行器,你需要调用 simpleOpenAIExecutor
函数并提供与 OpenAI 服务进行认证所需的 API 密钥:
- 创建提示词执行器:
// 创建一个 OpenAI 执行器
val promptExecutor = simpleOpenAIExecutor(apiToken)
- 使用特定的 LLM 执行提示词:
// 执行提示词
val response = promptExecutor.execute(
prompt = prompt,
model = OpenAIModels.Chat.GPT4o
).single()
创建多提供方执行器
要创建与多个 LLM 提供方协同工作的提示词执行器,请完成以下步骤:
- 为所需的 LLM 提供方配置客户端,并提供相应的 API 密钥。例如:
val openAIClient = OpenAILLMClient(System.getenv("OPENAI_KEY"))
val anthropicClient = AnthropicLLMClient(System.getenv("ANTHROPIC_KEY"))
val googleClient = GoogleLLMClient(System.getenv("GOOGLE_KEY"))
- 将配置好的客户端传递给
DefaultMultiLLMPromptExecutor
类构造函数,以创建具有多个 LLM 提供方的提示词执行器:
val multiExecutor = DefaultMultiLLMPromptExecutor(openAIClient, anthropicClient, googleClient)
- 使用特定的 LLM 执行提示词:
val response = multiExecutor.execute(
prompt = prompt,
model = OpenAIModels.Chat.GPT4o
).single()