01 Claude Code的Agent Loop核心循环
参考文献:
第 4 章:查询引擎 - 深入理解 Claude Code 源码
Agentic Loop:AI 自主循环的核心机制 - Claude Code Architecture
由于我的目的更多是了解agent的实现思路,所以文章中会具体描述做了哪些事,为什么要这样做。同时辅以少量源码。
什么是Agent Loop(代理循环)?
首先说一下,什么是Agent Loop(代理循环)?
当用户输入一条消息时,Claude Code 执行以下流程:
用户输入 → 上下文组装 → 模型决策 → 工具执行 → 结果注入 → 继续/停止
这个循环不断重复,直到模型决定不再调用工具——返回纯文本响应为止。这就是 Agent Loop(代理循环)的本质。

Agent Loop核心循环实现的双层架构
为什么要分两层?因为会话管理和查询执行的关注点完全不同。
系统分为两层QueryEngine与query():
QueryEngine 关心的是"用户说了什么、花了多少钱、这轮结果是否成功";
query() 关心的是"消息是否需要压缩、API 返回了什么、工具执行是否成功、是否需要恢复"。双层分离使得每层的代码都更聚焦、更容易测试。
在query方法中再调用queryLoop方法,在queryLoop方法中通过while ture 循环调用工具,实现react范式
QueryEngine
这块QueryEngine是用于管理一整个会话,从你开始对话到关闭命令行窗口。queryLoop用于每次对话后多次循环调用工具,实际的agent loop是在queryLoop中的。
对于QueryEngine,位于src\QueryEngine.ts,源码中的描述是这样的:

简要解析
1. QueryEngine职责:管控单次会话的 查询全生命周期+会话状态,是会话的核心载体。
2. 代码改造目的:将一次对话调用逻辑,即`ask()`里的核心逻辑抽离到这个类中,同时支持SDK无界面调用、后续REPL交互两种使用场景,实现代码复用。
3. 实例与调用规则:
- 一个会话对应 一个QueryEngine实例 ;
- 每次`submitMessage()`代表同一会话内一轮新问答;
- 消息记录、文件缓存、计费用量等状态 跨轮次持久保存 ,不会单轮结束就清空。
再看具体的代码部分,QueryEngine类定义其中最核心的部分是submitMessage()方法,同一轮对话中的新问答,一共有1000行代码:

query()方法
再说submitMessage()方法中最核心的query()方法调用,占有500行的模型调用:

可以看到每轮对话(一问一答,翻译过来叫 turn)都要输入这些信息,打个断点看一下:
一共会传入6种上下文,包含:

params本质是 本轮 submitMessage 需要的“会话 harness/上下文包”,用来回答三件事:
- 这次轮到谁说话、历史长什么样 → messages
- 给模型的环境身份/规则/环境事实 → systemPrompt+ systemContext
- 工具能不能执行、怎么走权限/审批流 → canUseTool(以及隐含的 toolUseContext)
在 Claude Code 的架构叙述里,submitMessage()更像“启动一轮 agent run”,而不是单纯发一次聊天请求;因此它会把配置、历史、权限、环境信息打包在一起再进 query()循环。
具体的提示词如下:
https://blog.csdn.net/m0_73980980/article/details/161338145?spm=1001.2014.3001.5501
之后进入queryLoop方法:

queryLoop的实现:
再具体说一下queryLoop的实现:
queryLoop 签名是 async function*——异步生成器。选这个而不是回调/事件的原因:
- 背压控制:消费端不处理完,生产端不继续,天然防止事件堆积
- 线性控制流:所有循环分支用普通
continue/break表达,不需要状态机
这块其实就是yield这种,通过把一个函数变成「生成器(generator)」,多次产出值,每次 yield 都“暂停”,从而在控制台输出内容。
这么理解,你每次打开终端,都是一个QueryEngine实例,每次对话都会调用query方法,而在query方法中会while true循环调用工具来解决你的问题,即queryLoop方法,这个方法是一个生成器,从而在对话过程中实现那种问你 “是否继续/在本次会话内允许/...”
再深入说一下queryLoop方法中的循环:
有两种结束方式,一是超过循环次数,另一种是大模型说做完了。
此外,每次循环只调用一次大模型,然后每次循环都会根据上次的问题再进行解决,类似于如下这种:
agent loo[是一个基于 while(true) 的状态机,通过“上下文预处理 -> 流式 API 调用 -> 工具执行 -> 终止判定”的循环,使 AI 能够自主执行多步操作.
其中的continue与终止条件如下:

当然,在流式调用和上下文预处理上也有很多工作与细节,具体可以看
Agentic Loop:AI 自主循环的核心机制 - Claude Code Architecture

| 维度 | QueryEngine | query() |
|---|---|---|
| 作用域 | 对话全生命周期 | 单次查询循环 |
| 状态 | 持久化(mutableMessages, usage) | 循环内(State 对象每次迭代重新赋值) |
| 预算追踪 | USD/轮次检查,结构化输出重试 | Task Budget 跨压缩结转,Token 预算续写 |
| 恢复策略 | 权限拒绝、孤儿权限 | PTL 排水/压缩、max_output_tokens 升级/重试 |
预算追踪与恢复策略
再预算追踪与恢复策略说其中的:
来源:https://ccb.agent-aura.top/docs/context/token-budget
|
概念 |
核心问题 |
类比 |
|---|---|---|
|
预算追踪 |
“还能不能继续跑?” |
钱包还剩多少钱 |
|
恢复策略 |
“跑炸了怎么办?” |
车坏了怎么修 |
具体含义拆解
-
USD / 轮次检查
每一轮调用模型前,预估这次会花多少钱,超了就直接拒绝或降级。
-
Token 预算
限制输入 + 输出的总 token 数,避免无限生成。
-
跨压缩结转
对话太长 → 压缩历史 → 将压缩前消耗的 token 数量结转并传递给 API → 确保服务器能正确计算剩余预算的机制

-
结构化输出重试
JSON / 函数调用格式错了 → 在预算内允许重试几次。
👉 本质:这是“财务 + 资源控制器”。
-
权限拒绝 / 孤儿权限
-
用户没权限 → 返回“无权访问”
-
权限中途失效 → 用安全兜底,不泄露数据
-
-
PTL 排水 / 压缩
-
PTL(Prompt Token Length)太长
-
把旧对话总结、裁剪、丢弃不重要部分
-
-
max_output_tokens 升级 / 重试
-
输出被截断(没说完)
-
自动提高输出上限,再试一次
-
👉 本质:这是“容错 + 自愈机制”。
更多推荐

所有评论(0)