构建 Agent:从一个循环到通用架构
构建 Agent:从一个循环到通用架构 一次模型调用接收输入并生成输出。完成“修复一个测试失败并验证结果”需要多次调用:模型检查代码、执行操作、读取结果,再决定下一步。把这些调用连接起来,就得到一个最小的 Agent Loop。 本文沿着这段程序的数据和调用路径逐项扩展。每节从一个具体限制出发,加入解决它所需的机制。章节按工程实现依赖推进。 一、一个最小的 Agent 最小 Agent 只做三件事:让模型决定下一步,执行模型选择的工具,再把执行结果交还模型。模型给出最终回答时,循环结束。 TYPESCRIPTasync function runAgent(userInput: string): Promise<string> { const messages: ModelMessage[] = [ { role: 'user', content: userInput }, ] while (true) { const response = await model.generate({ messages, tools: toolRegistry.schemas(), }) messages.push(response.message) if (response.toolCalls.length === 0) { return response.text } for (const call of response.toolCalls) { const result = await toolRegistry.execute(call.name, call.arguments) messages.push({ role: 'tool', toolCallId: call.id, content: result, }) } } } 这个循环已经具备 Agent 的核心反馈结构: TEXT用户目标 -> 模型决策 -> 工具行动 -> 环境观察 -> 模型决策 -> ... -> 最终回答 本文先把这段程序称为最小 Agent:模型选择下一步行动,运行时执行行动,并把环境观察送回模型。模型停止调用 Tool 表示本轮循环结束,测试结果与任务要求共同决定工作是否完成。 模型既可以直接回答,也可以通过搜索、文件、Shell、浏览器或业务 API 观察或改变外部环境。工具结果成为下一轮观察,模型据此修正行动。 第一处问题就在 toolRegistry.execute:模型生成的工具名和参数必须先经过运行时校验。 二、校验并执行 Tool Call 要修复测试失败,模型需要读取源码、搜索符号、修改文件并运行测试。我们先把这些操作注册成 Tool: ...