参考文献:

第 4 章:查询引擎 - 深入理解 Claude Code 源码

Agentic Loop:AI 自主循环的核心机制 - Claude Code Architecture

1. Agent Loop — 核心循环

由于我的目的更多是了解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/上下文包”,用来回答三件事:

  1. 这次轮到谁说话、历史长什么样​ → messages
  2. 给模型的环境身份/规则/环境事实​ → systemPrompt+ systemContext
  3. 工具能不能执行、怎么走权限/审批流​ → 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*——异步生成器。选这个而不是回调/事件的原因:

  1. 背压控制:消费端不处理完,生产端不继续,天然防止事件堆积
  2. 线性控制流:所有循环分支用普通 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 升级 / 重试

    • 输出被截断(没说完)

    • 自动提高输出上限,再试一次

👉 本质:这是“容错 + 自愈机制”

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐