The Agent Loop
“One Loop Is All You Need”
The smallest useful agent is a loop that calls the model, runs tools, and feeds results back.
核心 agent 循环遵循一个清晰的模式:一个 while 循环调用 LLM,检查响应中是否包含工具调用,执行这些调用,并将结果回传至对话。这一模式是所有 AI 编码 agent 的基础。循环本身与框架无关,无论使用 Anthropic 的 SDK、OpenAI 还是其他提供商,它都同样适用。
架构流程图
问题:AI Agent 如何执行操作?
LLM 本身无法执行代码、读取文件或与环境交互,它只能生成文本。要成为一个 agent,模型需要一种机制来请求操作并接收结果。最简单的方法是让模型输出结构化响应(即工具调用),在循环中拦截这些调用,执行所请求的操作,并将结果回传至对话。这样就形成了一个闭环反馈系统,使模型能够迭代地朝着目标推进。
为什么简单循环胜出
while-true 循环代表了 AI agent 的最小可行架构。其中没有状态机,没有规划器,也没有任务队列。系统提示词规定了模型的行为方式,而模型自身的推理则决定调用哪些工具及其调用顺序。这种简洁性带来了显著的优势:循环是可证明正确的,始终运行至模型发出完成信号;它易于调试,每次迭代都是一个完整的请求-响应周期;它无限可扩展,新能力通过工具而非修改循环逻辑来加入。
真实实现:基于 Effect 的并发架构
OpenCode V2 的 agent 循环 (packages/core/src/session/runner/llm.ts) 以 Effect TypeScript 库取代了基础的 while-true 模式。每次轮次通过 runTurnAttempt() 执行,将 LLM 调用流经提供商 SDK,检测工具调用,并使用 FiberSet(Effect 的结构化并发原语)并行处理它们。循环处理多个退出路径:tool_use 延续、步骤限制(agent.info.steps 限定迭代次数)、上下文溢出(流中自动压缩)以及中断信号。工具调用并非顺序执行。FiberSet 并发运行所有工具,等待它们全部完成后再进行下一次 LLM 调用。
设计决策
循环通过检查 `stop_reason != 'tool_use'` 来判断完成,而非依赖显式的 'done' 信号。这意味着模型在没有更多操作可执行时会自然发出完成信号,无需额外的协议。
模型选择是配置参数而非硬编码值。同一个循环适用于任何兼容 OpenAI 的 API,从一开始就实现了与提供商无关。
对比 Claude Code
Claude Code 使用相同的工具调用循环架构(发送、tool_use、执行、追加、重复),但 OpenCode 的特色在于使用异步 Effect/Stream 原语进行 LLM 调用,并采用正式的 ToolID schema 注册表。Claude Code 使用扁平的工具列表,而 OpenCode 按类别组织工具(read、write、shell、skill),并配备结构化的输入/输出模式。
深入设计决策
为什么仅靠 Bash 就够了
Bash 能读写文件、运行任意程序、在进程间传递数据、管理文件系统。任何额外的工具都只是 bash 已有能力的子集。增加工具并不会解锁新能力,只会增加模型需要理解的接口。
没有规划框架——由模型自行决策
没有规划器,没有任务队列,没有状态机。系统提示词告诉模型如何处理问题,模型根据对话历史决定下一步执行什么工具。