主循环(runLoop)是什么
为什么 AI 不是'一问一答'
一、原理:Chatbot 的 request/response 模式 vs Agent 的循环
你跟 ChatGPT 聊天时是这样的——你打字、按回车、它回复。一来一回。一次"对话"在结构上是一次完整的 HTTP 请求 + 一次响应。如果你想让它继续做点别的,你得再问一句。
这种模式叫 request/response——请求/响应。它的核心特征是同步:你的输入到它的输出之间没有"中间步骤"。
但 AI Agent 不一样。你跟 Cursor 或 opencode 说"修一下这个 bug",它的行为是这样的:
- 先读几个文件(看代码结构)
- 再 grep 几次(找 bug 相关的位置)
- 再读细节(确认问题)
- 改代码(edit 工具)
- 跑测试(bash 工具)
- 看测试结果(如果失败,回到改代码)
- 都通过了,给你一个总结
中间发生了 5-10 次(甚至几十次)跟 LLM 的交互,每一次 LLM 都决定"下一步做什么"。这些中间交互你看不见——只看到最后一句"已修复,测试通过"。
这种模式叫 agentic loop——智能体循环。它的核心特征是多轮自主决策:LLM 不只是回答用户,它会自己决定下一步该做什么、调用什么工具、看到结果后下一步怎么走。
为什么需要循环?因为LLM 自己不能"做一连串事"。LLM 一次只能输出一段文字或一个 tool call 请求。它不能在一次响应里"既改代码又跑测试又看结果"。要做一连串事,必须有外部代理(Agent)反复调它,每一轮把上一轮的结果塞回去让它继续。
这个"反复调它的代理"就是主循环。它的核心逻辑是:
loop:
1. 把当前消息历史发给 LLM
2. 收到 LLM 响应
3. 看响应里有没有 tool call 请求
4. 如果没有 tool call → LLM 觉得任务完了 → 跳出循环
5. 如果有 tool call → 执行那个工具 → 把结果塞回消息历史 → 回到步骤 1
5 行伪代码,但它是所有 AI Agent 的核心。Cursor 有、opencode 有、Devin 有、Claude Code 有。它们的差别不在"有没有主循环",在"主循环怎么实现"——怎么管理状态、怎么处理错误、怎么控制成本、怎么应对中断。
理解主循环你才能理解 AI Agent 的所有其他设计。tool 设计是为了让循环中的"执行步骤"有效;context 管理是为了让循环不超出 token 限额;permission 是为了让循环不做危险事;event stream 是为了让 UI 能看到循环的每一步。所有 Agent 机制都是围绕主循环转的。
二、案例:opencode 的 runLoop 状态机
opencode 的主循环在 packages/opencode/src/session/prompt.ts 的 runLoop 函数。打开看,它的结构大致是这样:
while (true) {
// 1. 检查是否需要 compaction(context 太满)
if (compaction.isOverflow()) {
compaction.create()
continue
}
// 2. 拼装请求并发给 LLM
const stream = llm.stream(messages, tools, ...)
// 3. 消费流式响应
for await (event of stream) {
// 实时发布事件给 UI(每个 token 都触发)
// 检测 tool call
}
// 4. 处理 tool calls
if (toolCalls.length > 0) {
for (call of toolCalls) {
executeAndStreamBack(call)
}
continue // 继续下一轮
}
// 5. 没 tool call,结束
break
}
这是简化版。真实代码加了大量分支:错误处理、用户中断、max steps、permission 等待、background task 通知。但骨架就是这 5 步。
我们看几个 opencode 的具体设计选择。
选择 1:循环用 Effect FP 的 Stream 模式。opencode 用 Stream.tap().takeUntil().runDrain() 处理 LLM 流式响应。这跟传统的 for await 不一样——Stream 支持 cancellation、错误隔离、并行 tap。用户按 Ctrl+C 时,整个 Stream 被中断,所有副作用(tool 执行、UI 更新)自动 cleanup。
选择 2:tool 执行不阻塞主循环。当 LLM 输出 tool call 时,opencode 把 tool 执行 fork 到一个新的 fiber。LLM 可以继续输出(也许还要 call 别的 tool),fork 的 fiber 在后台跑实际工具。所有 tool 完成后再进入下一轮。
选择 3:每个 turn 完整重新拼 system prompt。第 0.4 章我们提过,每次进入"步骤 2"(发给 LLM)时,opencode 都会重新拼一份完整 system prompt。这意味着 mode 切换、环境变化、用户改了 AGENTS.md 这些都能在下一轮反映出来。代价是 prompt caching 命中率下降——opencode 做了 trade-off 接受这个代价换实时性。
选择 4:循环没有显式的"最大轮数"上限。理论上 opencode 可以无限循环——只要 LLM 一直返回 tool call。实际上有 max-steps 机制(1.9 章讲过)作为安全网,但这是个软上限,不是循环本身的硬限制。
选择 5:循环跟 Session 解耦。一个 session 里可以有多个 turn,每个 turn 调用一次 runLoop。runLoop 是 stateless 的——它的所有状态都在 session 和 messages 里,runLoop 本身不持久化任何东西。这让循环可以 abort、resume、fork 都不影响其他 session。
这 5 个选择叠加,让 opencode 的主循环既灵活又健壮。但它的复杂度也不小——runLoop 函数和它依赖的 processor.ts 加起来大概 2000 行 TypeScript,是 opencode 最复杂的模块之一。
为什么这么复杂?因为主循环是 AI Agent 的"核心反应器"——所有的产品行为都从这里发出。让它健壮值得花这个工程量。
三、设计启示:什么场景需要循环 / 什么场景不需要
这一章的核心论点:主循环是 AI Agent 区别于 Chatbot 的本质特征——但不是所有 AI 产品都需要主循环。
什么场景需要主循环?
1. 用户的需求需要多步骤完成。"修这个 bug"涉及读、改、测、复查;"加这个功能"涉及设计、写、测;"解释这段代码"涉及读多个文件理解依赖。这类需求不能一次性完成,需要循环。
2. 中间步骤的结果影响下一步。读了文件 A 才知道要读文件 B;跑了测试才知道改没改对。这种"动态决策"必须有循环承载——LLM 看到上一步结果才能决定下一步。
3. 任务的边界不清晰。用户说"优化下这个函数",没有清晰的"完成标准"。LLM 可能改一次试试、看效果不够再改一次。这种探索性任务需要循环。
什么场景不需要主循环?
1. 输入到输出是确定的。"翻译这段话"、"把这个 JSON 转成 YAML"、"写一段 README"——一次请求一次响应就够。加循环只是浪费 token。
2. 用户期望即时响应。聊天产品、客服产品——用户敲一句话期待 1-2 秒回复。循环可能跑几十秒,不适合即时场景。
3. 没有工具集。如果 LLM 不能调用工具(纯文本生成场景),循环没意义——它每一轮都只是输出更多文字。
设计 AI 产品时关于主循环你要做的几件事:
1. 明确"循环边界"。一个 turn 是一个"用户问 → AI 完成"的单元。turn 内部可以有很多轮 LLM 调用,但 turn 边界要清晰——用户能感知到"这件事完了"。
2. 给循环一个软 + 硬退出条件。软退出是 LLM 自己觉得完了(finish_reason);硬退出是 max-steps、用户中断、context overflow。两者都要有——光软退出可能死循环,光硬退出可能太早砍。
3. 循环要能被中断。用户按 Ctrl+C 必须立刻停。这要求所有 fork 的 fiber、tool 执行、stream 消费都支持 cancellation。不支持中断的循环是不可用的——用户会忍受 30 秒卡死后再也不用了。
4. 循环的每一步要可观测。事件总线、日志、UI 更新——每一轮 LLM 调用、每一次 tool 执行都要发事件。用户看不到内部步骤会失去信任——觉得 AI 在"黑箱乱搞"。
5. 循环要无副作用 fail-safe。如果第 5 轮 tool 调用失败了,前 4 轮的工作不能全废。每一轮的结果应该是独立可见的——LLM 已经改的文件不要因为后面错了就回滚到改之前(除非用户明确要求)。
最后一个观察。主循环这个抽象在 AI Agent 设计里是相对新的——5 年前没人讨论这种东西。它的标准化(每家 Agent 产品都长这样)大概是 2023-2024 年发生的——ReAct 论文 + LangChain 早期版本推动了这个模式的普及。现在主循环已经是 AI Agent 设计的"事实标准"——任何新出现的 Agent 产品几乎肯定有类似的循环结构。
理解这个事实标准,你就能快速看懂任何 Agent 产品的核心架构。下一章 2.2 我们看主循环内部的"一个 turn"长什么样——这是更细的颗粒度。