学习ClaudeCode源码之Agent核心循环

学习ClaudeCode源码之Agent核心循环最近在研读 ClaudeCode 源码 其中 Agent 核心循环的设计堪称精巧 读完收获很大 这篇文章就从上到下完整梳理一遍核心循环的代码逻辑 既做一次学习记录 也为后续自己做类似项目积累可直接借鉴的设计思路 details data line 4 class md editor code open details

大家好,我是讯享网,很高兴认识大家。这里提供最前沿的Ai技术和互联网信息。



最近在研读 ClaudeCode 源码,其中 Agent 核心循环的设计堪称精巧,读完收获很大。这篇文章就从上到下完整梳理一遍核心循环的代码逻辑,既做一次学习记录,也为后续自己做类似项目积累可直接借鉴的设计思路。

 
  
    
    
 开始 │ ▼ ┌──────────────────┐ │ 1. 初始化 │ │ 状态/预取 │ └────────┬─────────┘ │ ▼ ┌────────┐ ◀───│ while │─── 继续 ◀─────┐ │ (true) │ │ └────┬───┘ │ │ │ ▼ │ ┌──────────────────┐ │ │ 2. 上下文管理 │ │ │ (多层压缩链) │ │ └────────┬─────────┘ │ │ │ ▼ │ ┌──────────────────┐ │ │ 3. 调用 AI 模型 │ │ │ 流式处理 │ │ └────────┬─────────┘ │ │ │ ▼ │ ┌───────────┐ │ │ 有工具调用?│ │ └─────┬─────┘ │ 是 │ 否 │ ▼ │ ▼ │ ┌──────┐ │ ┌──────┐ │ │执行 │ │ │后处理│ │ │工具 │ │ │返回 │ │ └──┬───┘ │ └──────┘ │ │ │ │ └─────┴──────────────────────┘ │ ▼ ┌──────────────────┐ │ 4. 组装并递归 │ │ state = next │ └────────┬─────────┘ │ └── continue ──▶ while(true)

state:记录了循环过程中所有可变的上下文信息,确保多轮循环的连贯性。

详情如下:

 
  
    
    
 let state: State = { //完整对话历史 //工具执行上下文 //最大输出token数 //自动压缩跟踪状态 //是否已激活停止钩子 //最大输出token恢复次数 //应急压缩标记 //当前轮数 //待处理工具使用摘要 //上一次迭代继续的原因 

}

 

就是一个 while(true) 循环、进入循环可以依次看到:

压缩机制:

  1. applyToolResultBudget: 工具结果存储优化,过大结果截断并持久到磁盘
  1. snipCompact :裁剪过旧的工具结果内容,精简历史消息。
  2. microcompact:超时自动卸载缓存的工具结果到磁盘,释放内存。
  3. contextCollapse:如果上下文折叠特性开启,执行折叠(智能折叠代码块、测试文件等)
  4. autocompact:执行自动压缩,生成摘要替换大量历史消息。返回压缩结果和连续失败次数。

上下文压缩后续代码还有一层:reactive compact(应急压缩 ),相关逻辑在无工具调用章节错误处理部分。

构建系统提示词:

系统提示分为两部分拼接,结构非常清晰:

  • systemPrompt:AI 身份、能力、MCP 工具规则、输出风格、语气等;
  • systemContext:动态的环境信息,此上下文会添加到每段对话开头,并在对话期间进行缓存。包括 Git 状态、缓存破坏器(仅 Ant 环境,用于强制刷新缓存)等。
const fullSystemPrompt = asSystemPrompt( appendSystemContext(systemPrompt, systemContext), );

处理完上下文之后,就该调用LLM了。

调用 LLM:callModel(一大堆参数)

 
  
    
    
 for await (const message of deps.callModel({ // ... 一大堆参数 messages: prependUserContext(messagesForQuery, userContext), //把用户上下文加到消息列表前面 systemPrompt: fullSystemPrompt, // 系统提示 thinkingConfig: toolUseContext.options.thinkingConfig, // 思考配置 tools: toolUseContext.options.tools, // 工具列表 signal: toolUseContext.abortController.signal, // 终止信号 options: { // 模型调用选项 // 获取工具权限上下文(异步) // 当前使用的模型 // 快速模式(如果启用) // 工具选择策略 // 是否非交互式会话 // 回退模型 :当主模型不可用 时(比如 Claude Opus 负载过高),自动切换到的 备用模型 (比如 Claude Sonnet) // 查询来源 // Agent 定义 // 追加系统提示 // 最大输出 token 数 // MCP 工具列表 // 查询追踪 // 努力值和建议模型 // 跳过缓存写入 是否跳过缓存写入 // 任务预算 }))

消息处理逻辑

  1. if (streamingFallbackOccured):如果发生流式回退,将之前生成的消息标记为"墓碑"(从界面移除),清空所有收集的消息,重置状态;
  2. 为工具调用块的输入字段;
  3. 检查消息是否是可恢复的错误(提示过长、最大输出 token 等)。
  1. 收集助手消息和工具调用块;
  2. 处理模型回退错误 切换到回退模型,处理已生成的消息;

回退模型 :当主模型不可用 时(比如 Claude Opus 负载过高),自动切换到的 备用模型 (比如 Claude Sonnet)。

  1. 处理模型调用错误: 记录错误,返回模型错误状态(退出循环)

后处理与恢复:

  1. 执行后采样钩子executePostSamplingHooks,比如会话记忆(sessionMemory)/技能改进(skillImprovement);
  2. 处理流式中断: 用户按 Escape 时,尽可能优雅地停止一切正在做的事情,给用户一个清晰的反馈,然后退出这一轮对话;
  3. 从上一轮对话中生成工具使用总结pendingToolUseSummary。实现方法:这里使用的是 Claude 轻量级模型(Haiku),快速生成摘要。Haiku(约1秒),而主模型的流式响应需要 5-30 秒。

如果没有工具调用,处理最后一条消息,阻断式编程,先进行多种错误拦截与恢复:

  1. 处理 isWithheld413:prompt-too-long 错误,
    • 首先尝试 context-collapse 恢复(上下文折叠)
    • 如果 context-collapse 无法恢复,尝试 reactive compact(应急压缩 )
      • 如果成功,更新状态并继续
      • 如果失败,产出错误消息并退出return { reason: 'prompt_too_long' }
  2. 处理isWithheldMaxOutputTokens:max_output_tokens 错误:首先尝试升级重试(从 8k 提升到 64k),如果不行,进行多轮恢复(最多 3 次),如果恢复耗尽,产出错误消息
  3. 执行 stop hooks:
  4. Token 预算控制机制:
    • 预算未用完:添加提示消息 → 重置恢复计数 → 继续下一轮
    • 预算已用完:记录完成事件 → 退出循环
    • 边际收益递减:提前停止 → 记录日志 → 退出循环

如果没有错误,就正常完成 return { reason: 'completed' }, 退出循环

两种执行模式 ,根据是否使用了流式工具执行,选择不同的工具执行路径:

  1. 流式执行 vs 批量执行 :
执行模式 触发条件 执行逻辑 优势 流式执行(Streaming) 存在 streamingToolExecutor 多工具并行执行,谁先完成谁先返回结果 速度快,响应流畅,适合多工具调用场景 批量执行(Batch) 无流式执行器 工具按顺序执行,所有工具完成后统一返回结果 逻辑简单,适合工具间有依赖的场景
  1. 批量工具执行完成后生成工具使用摘要,并将该摘要传递至下一次递归调用
    • 提取最后一条助手消息的文本作为上下文
    • 收集工具信息用于生成摘要
    • 提取结果内容
    • 生成工具使用摘要generateToolUseSummary(异步,不阻塞下一轮 API 调用):生成摘要后保存到 nextPendingToolUseSummary,在下一轮使用之前就可以生成.
  2. 处理工具执行中断:
    • 清理计算机使用状态
    • 产出中断消息
    • 如果达到最大轮数,产出提示
    • 返回工具中断状态 return { reason: 'aborted_tools' } // 退出循环

最后的阶段,确保了 Agent 的上下文能够无缝传递给下一轮:

  1. 获取命令队列快照,按优先级筛选 。主线程只取无 agentId 的命令,子 Agent 只取针对自己的任务;
  2. 产出附件消息(文件变更、任务通知、记忆、技能等)
  3. 记忆预取和消费pendingMemoryPrefetch,保证记忆不被重复使用;
  4. 注入 skill :注入技能发现(Skill Discovery)的结果;
  5. 从命令队列中移除已消费的命令;
  6. 刷新 mcp 工具:在每次轮次之间刷新工具,使新连接的MCP服务器可用;
  7. 轮次计数:每执行一轮工具调用,轮次计数 +1;
  8. 主 Agent 生成任务摘要(用于 claude ps);
  9. 检查是否达到最大轮数限制
  10. 构建下一轮循环状态 state:
    • 合并所有消息messages,toolUseContext等;
    • 重置恢复计数器;
    • 传递待处理的工具摘要,把本轮生成的摘要传给下一轮: pendingToolUseSummary: nextPendingToolUseSummary;
    • 设置迭代原因为"下一轮",回到循环开头。

到这里整个循环逻辑就从上到下走了一遍。

返回值 含义 { reason: 'completed' } 正常完成 { reason: 'aborted_streaming' } 流式中断 { reason: 'aborted_tools' } 工具执行中断 { reason: 'blocking_limit' } 超过阻塞限制 { reason: 'prompt_too_long' } 提示过长 { reason: 'max_turns' } 达到最大轮数 { reason: 'stop_hook_prevented' } stop hook 阻止 { reason: 'hook_stopped' } hook 停止

整个代码看下来,不愧是"宇宙最强",觉得可以借鉴一二:

  1. 多层上下文压缩:层层压缩,保证多轮对话质量与成本平衡;
  2. 流式工具执行:调用工具极快,在 LLM 生成的过程中,有些工具已经执行完毕。 体验极快,只是工程复杂度更高;
  3. 错误处理与恢复:多种错误处理与兜底、重试机制等;
  4. 异步处理: 工具摘要、记忆预取、技能刷新都异步做,不阻塞主交互流程。

最后也分享个彩蛋。在源代码开头有一段Claudecode开发者的注释,可以看到其精神状态,也是相当幽默,估计也踩过很多坑。取了其中一段翻译一下:

年轻的巫师,请谨记这些规则。它们是thinking的法则,而thinking的法则即是宇宙的法则。倘若你不遵守这些规则,将会遭受整日调试代码、抓耳挠腮的惩罚。

本人水平有限,文章难免有不严谨之处,后续还会继续深挖学习。

小讯
上一篇 2026-04-15 16:14
下一篇 2026-04-15 16:12

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/264188.html