1. 从零到一为什么我们需要一个Kotlin原生的AI Agent框架如果你和我一样是个常年泡在JVM生态里的开发者过去一年里肯定没少折腾AI应用。从调用OpenAI的API写个简单的聊天机器人到尝试用LangChain构建复杂的RAG应用再到最近火热的AI Agent概念。折腾一圈下来一个最直观的感受是现有的主流AI框架比如LangChain、LlamaIndex虽然功能强大但总感觉和咱们JVM/Kotlin的开发习惯有点“水土不服”。这种“不服”体现在哪首先是类型安全。Python的动态类型在快速原型阶段是优势但一旦项目规模上去需要维护、重构、团队协作时缺少编译时类型检查就成了噩梦。你永远不知道一个dict里到底有什么一个函数返回的到底是str、list还是None。其次是与现有技术栈的整合成本。我们已有的Spring Boot微服务、Ktor后端、甚至Android/iOS移动应用想要嵌入AI能力往往需要额外引入一整套Python运行时或进行复杂的HTTP桥接部署和调试复杂度陡增。最后是“Kotlin风味”的缺失。Kotlin的协程、DSL、扩展函数等现代语言特性能让异步流处理、构建复杂工作流变得异常优雅但在“翻译”过来的框架里很难享受到。这就是Koog出现的背景。它不是又一个跟风的概念项目而是JetBrains基于其内部AI产品比如AI Assistant的实战经验沉淀出的一个为JVM和Kotlin多平台开发者量身定制的AI Agent框架。它的核心目标很明确让你能用最地道的Kotlin和Java语法构建出生产级、可预测、高容错的智能体并且能无缝部署到从后端服务到移动端、甚至浏览器Wasm环境的任何地方。简单说Koog想解决的就是“最后一公里”的问题把前沿的AI Agent能力用咱们JVM开发者最熟悉、最舒适的方式真正落地到现有的、复杂的企业级应用中去。接下来我就结合官方文档和我的初步探索带你深入看看Koog是怎么做的以及我们该如何上手。2. 核心架构与设计哲学Koog如何让AI Agent“可靠”一个框架好不好用首先得看它的设计思路是否解决了核心痛点。Koog将自己定位为“用于构建可预测、容错且企业就绪的AI Agent的框架”这三个关键词——“可预测”、“容错”、“企业就绪”——正是它设计的出发点。2.1 以Kotlin协程为核心的异步流处理Koog的底层构建在Kotlin协程之上。这对于处理AI Agent这种本质上是异步、并发的场景来说是天然的优势。Agent的思考过程、工具调用、流式响应都可以用Flow或suspend函数来优雅地表达避免了回调地狱或复杂的线程池管理。// 一个简单的流式响应处理示例 fun main() runBlocking { val agent AIAgent(...) agent.runStreaming(请介绍下Kotlin协程。) .collect { chunk - // 实时处理每一个流式返回的文本块 print(chunk) } }这种设计让处理LLM的流式输出、并行工具调用变得非常直观也便于与Ktor、Spring WebFlux等响应式Web框架集成。2.2 模块化与可组合的特性系统Koog没有采用一个庞大、臃肿的单一类来定义Agent的所有能力而是采用了模块化设计。核心的AIAgent是一个相对轻量的容器其具体能力如记忆、工具调用、历史压缩等通过“特性”来添加。val agent AIAgent( promptExecutor openAIExecutor(apiKey), systemPrompt 你是一个代码助手。, features listOf( ToolCallingFeature(tools listOf(calculatorTool, webSearchTool)), MemoryFeature(retriever vectorStoreRetriever), HistoryCompressionFeature() ) )这种设计的好处显而易见关注点分离每个特性只负责一件事代码更清晰也便于测试。运行时灵活你可以根据场景动态地为Agent添加或移除特性。生态友好社区可以很容易地开发并共享第三方特性比如集成特定的数据库、消息队列等。2.3 内置的容错与状态持久化Agent在长时间运行中难免会遇到问题LLM API调用失败、工具执行超时、甚至整个进程崩溃。Koog将可靠性作为一等公民来支持。自动重试与回退对于可重试的失败如网络超时Koog可以自动配置重试策略。Agent状态持久化这是Koog一个非常强大的功能。它允许你将Agent的完整状态包括对话历史、中间思考过程在执行的特定检查点序列化并保存到数据库或文件中。当发生故障时可以从最近的检查点恢复执行而不是从头开始。这对于运行耗时很长的复杂工作流如自动数据分析、多步骤问题解决至关重要。智能历史压缩与LLM交互Token就是金钱。长对话会消耗大量Token并可能触及上下文窗口限制。Koog内置了智能的历史压缩算法能够在不丢失关键信息的前提下自动总结或剔除早期对话优化Token使用。你还可以定制压缩策略。2.4 图工作流可视化复杂Agent行为对于简单的问答Agent线性对话就够了。但对于需要规划、决策、循环的复杂任务就需要更结构化的表达。Koog提供了图工作流的支持允许你以节点和边的形式定义Agent的行为逻辑。你可以定义一个工作流其中包含“理解用户意图”、“调用工具A”、“根据结果判断分支”、“循环直到条件满足”等节点。这种声明式的定义方式不仅让复杂逻辑更清晰也便于进行可视化设计和调试。Koog的运行时引擎会负责按照你定义的图来执行。2.5 全面的可观测性集成在企业环境里你不能把Agent当黑盒。你需要知道它内部发生了什么每一步花了多长时间调用了哪些工具消耗了多少Token决策依据是什么Koog原生集成了OpenTelemetry这意味着Agent执行过程中的所有关键操作LLM调用、工具执行、工作流节点转换都会生成详细的追踪和指标。这些数据可以导出到任何支持OpenTelemetry的后端如Jaeger、Zipkin、Prometheus或者专门的AI观测平台如Weights Biases Weave和Langfuse。这为性能监控、成本分析和调试提供了强大的支持。3. 实战入门构建你的第一个Koog Agent理论说了这么多手痒了吗让我们从最简单的开始一步步搭建一个可运行的Koog Agent。这里假设你使用IntelliJ IDEA或Android Studio并且已经配置好了Kotlin开发环境。3.1 项目初始化与依赖配置首先创建一个新的Kotlin项目JVM或Multiplatform均可。这里以纯JVM项目为例。在你的build.gradle.kts文件中添加Maven中央仓库和Koog依赖。注意Koog的核心模块是koog-agents但根据你的目标平台可能需要不同的构件Artifact。对于JVM项目使用如下配置plugins { kotlin(jvm) version 2.3.10 // Koog要求Kotlin 2.3.10 } repositories { mavenCentral() } dependencies { implementation(ai.koog:koog-agents:0.7.3) // 使用最新版本 // 如果你需要与特定LLM提供商交互可能需要额外的依赖例如OpenAI // implementation(ai.koog:koog-providers-openai:0.7.3) // 为了运行示例我们还需要kotlinx-coroutines implementation(org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2) }同步Gradle项目确保依赖下载成功。注意Koog目前处于Alpha阶段从版本号0.x和Alpha徽章可以看出API可能发生变动。在生产环境采用前请密切关注其版本更新和迁移指南。3.2 获取LLM API密钥Koog支持多种LLM提供商。我们从最熟悉的OpenAI开始。你需要一个OpenAI的API密钥。访问 OpenAI平台 。登录后在左侧菜单找到“API Keys”。点击“Create new secret key”为其命名并创建。务必立即复制并妥善保存关闭页面后将无法再次查看完整密钥。为了安全起见永远不要将API密钥硬编码在代码中。最佳实践是使用环境变量。Linux/macOS: 在终端执行export OPENAI_API_KEYyour-api-key-hereWindows (CMD):set OPENAI_API_KEYyour-api-key-hereWindows (PowerShell):$env:OPENAI_API_KEYyour-api-key-here在IntelliJ IDEA中你可以在运行配置Run/Debug Configurations里添加环境变量。3.3 编写并运行基础Agent现在创建一个Kotlin文件例如SimpleAgent.kt并写入以下代码import ai.koog.agents.* import ai.koog.agents.executors.openai.* import kotlinx.coroutines.runBlocking fun main() runBlocking { // 1. 从环境变量读取API密钥 val apiKey System.getenv(OPENAI_API_KEY) if (apiKey.isNullOrEmpty()) { println(错误请设置 OPENAI_API_KEY 环境变量。) returnrunBlocking } // 2. 创建Prompt执行器这里是OpenAI val promptExecutor simpleOpenAIExecutor(apiKey) // 3. 构建AI Agent val agent AIAgent( promptExecutor promptExecutor, systemPrompt 你是一个乐于助人且知识渊博的助手。用中文回答用户的问题并尽可能简洁清晰。, llmModel OpenAIModels.Chat.GPT4o // 指定使用的模型例如GPT-4o // features参数可以留空使用默认特性集 ) // 4. 运行Agent并与用户交互简单示例 println(Agent已启动。输入‘退出’来结束对话。) while (true) { print(你: ) val userInput readLine() if (userInput null || userInput.equals(退出, ignoreCase true)) { println(对话结束。) break } // 5. 调用Agent并获取结果 val result: AgentRunResult agent.run(userInput) println(助手: ${result.content}) println(---) // 分隔线使对话更清晰 } }这段代码做了以下几件事安全地获取API密钥。创建一个针对OpenAI的PromptExecutor它是与LLM通信的桥梁。实例化一个AIAgent并定义了它的“系统提示”角色设定和要使用的模型。进入一个简单的循环读取用户控制台输入。将用户输入交给Agent处理并打印出结果。运行这个程序。如果一切配置正确你应该能在控制台与一个基于GPT-4o的AI助手进行对话了。这虽然简单但已经是一个功能完整的AI Agent核心。3.4 为Agent添加“工具”能力一个只会聊天的Agent能力有限。真正的威力在于它能调用外部工具。让我们给Agent加一个简单的“计算器”工具。在Koog中工具是一个实现了Tool接口的类。我们需要定义工具的名称、描述、参数模式schema和执行函数。import ai.koog.agents.tools.* import kotlinx.serialization.Serializable // 1. 定义工具输入参数的序列化数据类 Serializable data class CalculatorInput(val a: Double, val b: Double, val operation: String) // 2. 实现Tool接口 class CalculatorTool : Tool { // 工具的唯一标识符 override val id: String calculator // 工具的描述LLM会根据这个决定是否调用它 override val description: String 一个简单的计算器支持加、减、乘、除。 // 定义输入参数的JSON Schema用于让LLM理解如何构造调用 override val inputSchema: JsonSchema JsonSchema.obj( a to JsonSchema.number(第一个操作数), b to JsonSchema.number(第二个操作数), operation to JsonSchema.enum(运算类型, listOf(, -, *, /)) ) // 工具的执行逻辑 override suspend fun invoke(input: JsonElement, context: ToolInvocationContext): ToolResult { // 将JSON输入反序列化成我们的数据类 val params Json.decodeFromJsonElementCalculatorInput(input) val result when (params.operation) { - params.a params.b - - params.a - params.b * - params.a * params.b / - { if (params.b 0.0) { return ToolResult.failure(除数不能为零) } params.a / params.b } else - return ToolResult.failure(不支持的运算类型: ${params.operation}) } // 返回成功结果内容可以是字符串或任何可序列化的对象 return ToolResult.success(计算结果为: $result) } }现在修改我们之前创建的Agent将ToolCallingFeature和这个工具添加进去fun main() runBlocking { val apiKey System.getenv(OPENAI_API_KEY) ?: error(请设置OPENAI_API_KEY) val agent AIAgent( promptExecutor simpleOpenAIExecutor(apiKey), systemPrompt 你是一个助手可以回答问题和进行数学计算。当用户需要计算时使用计算器工具。, llmModel OpenAIModels.Chat.GPT4o, features listOf( // 添加工具调用特性并传入我们的计算器工具 ToolCallingFeature(tools listOf(CalculatorTool())) ) ) // 测试一下 val result1 agent.run(123加上456等于多少) println(问题1: 123 456 ?) println(助手: ${result1.content}) println(---) val result2 agent.run(计算一下圆的面积如果半径是5。) println(问题2: 半径5的圆面积) println(助手: ${result2.content}) }运行这个程序。你会看到对于第一个明确的数学问题Agent应该会识别出需要计算并调用CalculatorTool返回“计算结果为: 579”。对于第二个问题由于我们的工具只支持基本四则运算Agent可能会回答它无法直接计算面积或者尝试分解步骤如果LLM足够聪明。这展示了Agent如何将自然语言请求转化为结构化的工具调用。4. 深入核心特性解析与高级配置掌握了基础用法后我们来深入看看Koog的一些核心特性如何配置和使用这些是构建生产级应用的关键。4.1 记忆与检索Memory RAG让Agent拥有记忆是实现连贯对话和个性化服务的基础。Koog的MemoryFeature提供了灵活的存储和检索机制。a. 对话历史记忆最简单的记忆是保存当前会话的对话历史。这通常是默认开启的。但Koog允许你配置历史记录的保存方式和长度。b. 知识库记忆RAG更强大的是基于向量数据库的检索增强生成RAG。你可以将文档、知识库内容转换成向量嵌入Embeddings并存储起来。当用户提问时Agent会先从向量库中检索最相关的片段然后将这些片段作为上下文提供给LLM从而生成更准确、基于特定知识的回答。// 伪代码示例展示RAG集成的思路 import ai.koog.agents.features.memory.* import ai.koog.agents.retrievers.vector.* // 1. 创建一个向量存储检索器这里需要具体的向量数据库客户端如Chroma, Pinecone, Weaviate或本地Qdrant val vectorStore: VectorStore // ... 初始化你的向量数据库客户端 val embedder: Embedder // ... 初始化文本嵌入模型如OpenAI的text-embedding-ada-002 val retriever VectorStoreRetriever( vectorStore vectorStore, embedder embedder, topK 5 // 每次检索返回最相关的5个片段 ) // 2. 创建记忆特性 val memoryFeature MemoryFeature( retriever retriever, // 可以配置记忆的命名空间、自动保存时机等 namespace my_knowledge_base ) // 3. 在创建Agent时加入此特性 val agent AIAgent( // ... 其他配置 features listOf( ToolCallingFeature(...), memoryFeature ) ) // 使用在系统提示中引导Agent使用记忆 val agentWithRAG agent.copy( systemPrompt 你是一个技术支持助手拥有一个产品知识库。 在回答用户关于产品的问题时请优先参考检索到的相关知识片段。 如果知识库中没有相关信息请如实告知。 ${agent.systemPrompt} .trimIndent() )4.2 流式响应与并行工具调用为了提升用户体验特别是对于耗时较长的响应流式输出是必不可少的。Koog的Streaming API使用起来非常直观。fun main() runBlocking { val agent AIAgent(...) // 配置好的Agent val query 用大约200字介绍一下Kotlin协程的主要优势。 println(开始流式响应) agent.runStreaming(query) .collect { chunk - // chunk 是一个 AgentRunResult 的增量部分 print(chunk.content) // 逐块打印实现打字机效果 } println(\n--- 响应结束 ---) }并行工具调用是另一个强大功能。当LLM判断需要同时调用多个不相关的工具时例如同时查询天气和新闻Koog可以并行执行这些工具调用而不是串行等待从而大幅降低整体延迟。这通常由LLM模型本身如GPT-4和Koog的底层执行器共同支持开发者无需额外配置。4.3 集成MCP与ACP协议MCPModel Context Protocol和ACPAgent Client Protocol是新兴的标准化协议旨在解决AI应用中的工具集成和客户端-代理通信问题。MCP集成允许你的Koog Agent直接使用任何实现了MCP Server的工具。这意味着你可以利用一个不断增长的、标准化的工具生态而无需为每个工具编写特定的适配器。在Koog中你可以通过McpTool来包装一个MCP Server连接并将其作为普通工具添加到Agent中。ACP集成允许你构建的Agent通过标准协议与外部客户端如聊天界面、工作流引擎进行通信。这使得Agent的部署和交互方式更加灵活和标准化。Koog提供了构建ACP兼容Agent的模块。这两个协议的集成体现了Koog对AI开发生态标准化的前瞻性支持。4.4 与Spring Boot和Ktor集成对于企业级后端应用无缝融入现有技术栈是关键。Koog提供了对Spring Boot和Ktor的一流支持。在Spring Boot中使用Koog你可以将AIAgent或AIAgent的工厂类定义为Spring Bean然后在Controller或Service中注入使用。import org.springframework.web.bind.annotation.* import org.springframework.stereotype.Service Service class CustomerSupportService( private val supportAgent: AIAgent // 注入配置好的Agent Bean ) { suspend fun handleCustomerQuery(query: String): String { val result supportAgent.run(query) // 这里可以添加业务逻辑如记录日志、更新工单状态等 return result.content } } RestController RequestMapping(/api/support) class SupportController(private val supportService: CustomerSupportService) { PostMapping(/chat) suspend fun chat(RequestBody request: ChatRequest): ChatResponse { val answer supportService.handleCustomerQuery(request.message) return ChatResponse(answer) } } // 简单的请求/响应数据类 data class ChatRequest(val message: String) data class ChatResponse(val reply: String)在Spring配置中你需要通过Configuration类来创建Agent Bean并管理其生命周期如API密钥的读取、模型的选择。在Ktor中使用KoogKtor作为轻量级、协程优先的框架与Koog的搭配更是天作之合。import ai.koog.agents.* import io.ktor.server.application.* import io.ktor.server.response.* import io.ktor.server.routing.* import io.ktor.server.engine.* import io.ktor.server.netty.* fun Application.module() { // 在应用启动时创建Agent注意资源管理如使用单例或依赖注入 val agent AIAgent(...) routing { post(/chat) { val query call.receiveText() val result agent.run(query) call.respondText(result.content) } // 流式端点 post(/chat/stream) { val query call.receiveText() call.respondBytesWriter { agent.runStreaming(query).collect { chunk - write(chunk.content.encodeToByteArray()) flush() } } } } } fun main() { embeddedServer(Netty, port 8080, module Application::module).start(wait true) }这种集成方式让你可以轻松地将AI能力作为HTTP API暴露出来供前端或其他服务调用。5. 避坑指南与性能调优在实际项目中使用Koog我总结了一些经验和需要注意的地方。5.1 常见问题与排查依赖冲突Koog依赖于特定版本的Kotlin协程、序列化库等。如果你的项目中也引入了这些库的不同版本可能会导致冲突。使用./gradlew dependencies命令检查依赖树并使用Gradle的resolutionStrategy强制统一版本。configurations.all { resolutionStrategy { force(org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2) force(org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0) } }API密钥与网络问题最常见的运行时错误是API密钥无效或网络超时。确保环境变量设置正确并且运行环境能够访问对应的LLM服务如api.openai.com。对于生产环境考虑配置HTTP代理或使用云服务商的内网端点。上下文长度超限即使有历史压缩在超长对话或嵌入大量知识库上下文时仍可能触及模型的上下文窗口限制。Koog的AgentRunResult或相关异常中可能会包含Token超限的错误信息。解决方案包括启用并调优HistoryCompressionFeature。在MemoryFeature中限制检索返回的片段数量和质量。对输入文本进行智能分段或摘要。工具调用失败LLM可能生成不符合工具参数模式的调用。Koog的ToolCallingFeature会尝试进行验证和修正但并非万能。确保你的工具描述清晰参数模式定义准确。可以在Tool.invoke方法中加入详细的日志以便调试。5.2 性能与成本优化建议模型选择不是所有任务都需要GPT-4。对于简单的分类、提取或聊天GPT-3.5-Turbo可能更便宜、更快。Koog允许你根据不同的任务或特性为同一个Agent内的不同环节配置不同的模型LLM Switching。例如可以用小模型处理历史压缩用大模型进行核心推理。缓存策略对于频繁出现的、结果确定的查询如“今天的日期是什么”可以考虑在Agent外层或工具层实现缓存避免重复调用LLM节省成本和延迟。Koog的架构允许你在PromptExecutor或自定义工具中集成缓存逻辑。超时与重试配置为LLM调用和工具执行配置合理的超时时间和重试策略。对于非幂等的操作如创建订单重试要格外小心。Koog的Executor配置通常支持这些参数。val executor simpleOpenAIExecutor( apiKey apiKey, config OpenAIExecutorConfig( timeout Duration.seconds(30), maxRetries 2 // 对可重试错误进行最多2次重试 ) )利用可观测性务必启用OpenTelemetry导出。通过分析追踪数据你可以清晰地看到一次Agent调用中时间都花在了哪里LLM思考、工具执行、网络延迟从而有针对性地进行优化。识别出那些耗时过长或频繁失败的工具调用。5.3 生产环境部署考量状态持久化对于需要长时间运行或处理关键任务的Agent务必启用Agent状态持久化。将检查点保存到如PostgreSQL、Redis等可靠的存储中。这样在应用重启或实例迁移时能够恢复会话状态保证用户体验和业务连续性。资源管理AIAgent本身不持有昂贵的资源如模型权重主要资源消耗在于与远程LLM API的通信。但在高并发场景下仍需要注意连接池确保HTTP客户端配置了合适的连接池以应对突发流量。限流根据LLM供应商的速率限制在应用层或网关层实施限流避免因超出配额导致全体失败。优雅降级当核心LLM服务不可用时是否有备选方案如切换到另一个供应商的模型或返回缓存的通用答案。安全性工具权限仔细审查每个工具的能力。一个能执行系统命令或访问数据库的工具必须施加严格的权限控制和输入验证防止LLM被诱导执行恶意操作。提示词注入防护在系统提示词和用户输入拼接时注意防范提示词注入攻击。避免将未经处理的、不可信的用户输入直接放入可能改变Agent行为的指令上下文中。输出过滤对Agent生成的内容进行必要的审核或过滤特别是在面向公众的场合防止生成不当内容。Koog作为一个Alpha阶段的框架已经展示出了强大的潜力和对JVM开发者需求的深刻理解。它用Kotlin的方式重新思考了AI Agent的开发体验将类型安全、并发优雅、多平台支持和生产级可靠性放在了首位。虽然生态还在成长文档和最佳实践需要不断完善但对于已经在JVM栈上并希望深入探索AI Agent应用的团队来说Koog无疑是一个值得密切关注和尝试的选项。我的建议是可以从一个内部工具或非核心业务场景开始试点逐步积累经验等待框架更加成熟后再向更关键的业务场景推进。