Claude Code架构解析
2026-08-17 22:22:54 0 举报AI智能生成
Claude Code(CloudCode)架构解析的技术笔记,通过配套复现项目的纯Python源码逐模块拆解一个tool-use Agent的运行机制:双层架构与QueryEngine构建引擎、核心工具循环与流式执行、上下文压缩、工具安全关卡(Hooks+Permission)、记忆系统、11段System Prompt拼装,以及Agent Teams多智能体协作。聚焦工程实现细节与面试高频考点,适合想深入理解Agent Runtime底层设计、而非仅停留在API调用层的开发者
ClaudeCode
Agent架构
工具调用
记忆系统
AgentTeams
模板推荐
作者其他创作
大纲/内容
拓展知识
Harness Engineering(驾驭工程)
Agent 最重要的能力是**与现实环境交互——调用现实环境里的各种工具,操作你的本机环境。
为 Agent 适配的一整套"脚手架"(脚手架 = 围绕模型搭建的执行框架)
通过给模型加上详细的指令、数据和安全机制,让 Agent 的整个执行流程保持稳定、可控。
让 AI 驱动的、控制电脑上所有操作的引擎,能够实现全流程的可控
和 Context Engineering 的区别
上下文工程关注的是"给模型喂什么上下文"
提示词
解析
RAG
输入侧的控制
驾驭工程关注的是"模型驱动的一整套执行系统如何可控地与现实世界交互"
工具调用
安全检查
权限隔离
错误恢复
执行侧的控制
上下文工程偏输入,驾驭工程偏执行
Agent 架构三句话
工作流是灵魂
端到端任务要拆成哪几个 step、每个 step 如何流转,工作流定义了 Agent 的行为路径
组件化是核心
人在做这件事时调用了哪些工具,就把这些工具作为组件集成进去,让 Agent 可调用
记忆层做驱动
在运行过程中如何控制上下文、如何存取记忆,由记忆层驱动整个系统的持续进化
Claude Code举例
prompts 和 section 是工作流
tools、hooks、permissions、MCP是工具层(组件化)
memory 就是记忆层
整体架构
双层架构
控制面
cc/main.py
**主循环也在这一层**
负责用户输入输出
做 CLI 命令解析
判断输入是不是以斜杠 /开头
`/model` 切换模型
`/compact` 手动压缩
文字开头则构造 message 上下文进入对话流程
核心 loop 层
cc/core/query_loop.py+ query_engine.py
控制整个对话循环
怎么调模型
怎么处理工具调用
怎么错误恢复
底下又分出不同接口
调用模型的 API 接口
组装 prompt 的 prompts 模块
操作 memory 的记忆模块
压缩上下文的 compact 模块
读取 session 的会话模块
构建引擎(QueryEngine)
用 SDK 调过大模型就会发现:向模型发一次请求,光有 prompt 是不够的,还需要
tools
告诉模型有哪些工具可调
模型选择
调用哪个模型
输出侧检查
对模型输出的内容做安全检查(比如 hooks)
执行侧检查
对模型要调用的命令、工具做安全与权限检查(permissions)
四样东西加上 system prompt,靠 loop 里简单拼装 message 是搞不定的
一个引擎把它们统一组装起来。这就是 **QueryEngine(构建引擎)**
把所有需要输入到模型上下文里的东西,全部拼装绑定在一起,再向模型发请求
system prompt(11 段)
工具 schema
memory
CLAUDE.md
权限上下文
hooks
构建步骤
main.py` 的 `_build_engine()`
加载 CLAUDE.md
拼 system prompt
加载 hooks
注入 coordinator prompt
创建权限上下
创建任务注册表/后台 agent 管理器/团队上下文
注册工具
组装 QueryEngine
QueryEngine 是 Harness Engineering 最核心的体
有了它,整体过程才能做到可控
QueryEngine 有三个入口
# submit():print 模式用,自动把字符串包装成 UserMessage
# run_turn():REPL 用,消息已由主循环追加,直接驱动一轮 loop
# submit_messages():子 agent 用,接收独立消息列表,不污染父会话记录
延迟绑定设计
很多工具的执行依赖 engine 本身,但 engine 又要在工具注册后才存在
解决方案
用一个 `engine_ref` 空列表做闭包引用,先把工厂函数传给工具
等 engine 组装好后再 append 进去
之后调用工厂就能拿到真正的 engin
三种启动模式
Print 模式(`-p` 参数)
单次问答,不进入多轮对话
比如 `echo "用Python写一个快排" | python -m cc -p`,模型写完就退出。没有 `-p` 参数时从 stdin 读输入
REPL 交互模式
进入"**读输入 → 评估 → 输出 → 再读**"的循环(Read-Eval-Print Loop)
可以在终端里持续多轮对话
Resume 模式(`-c` 参数)
恢复原来的聊天对话上下文窗口
实现上并不是独立分支,而是给 REPL 传入一个 `resume_id`
加载历史会话(加载时必须先用 `validate_transcript()` 修复消息记录,把没恢复完的任务标记为 KILLED)
项目目录结构
<br>
控制面(main.py)控制主循环,主循环内嵌 query_loop 状态机,状态机控制工具、记忆、prompt、compact、session、agent 这六大分支的集成
只有 system prompt 不够——每次请求还要带 tools、模型选择、输出检查、执行检查,这就是 QueryEngine 存在的意义。
核心对话循环
全貌
本质就是 Function Calling 的循环
整个过程
例如帮我看看README里面写了什么
1. 构造上下文:控制面把用户输入组装成 user message,拼在 system message 后面<br>
2.进入 loop,先做消息规范化
3.并行做 token 估算:估算当前上下文长度,超过阈值就触发自动压缩(compact);
4.调用 API,流式输出:模型返回的流里既有 text(文字,一个字一个字吐给用户)也可能有 tool_use(比如调用 Read 工具);
5.执行工具:Read 执行,拿到文件内容;
6.结果拼回上下文:工具执行结果(tool_result)作为一条 user message 拼接到 message 列表里;
7.再次调用模型:模型基于新上下文继续决策——如果还要调工具就继续循环(多轮链式工具调用);如果返回纯文本且结束原因是 end_turn,就把文本输出给用户,本轮 loop 结束;
8.后处理:保存会话、统计 token、在后台异步触发记忆提取(不影响主流程)。
CC 的复刻本质就是一个 tool-use agent loop
把 tool_result 包装成 user message 送回 message 上下文
再传给模型决策下一步
和function calling 循环是完全同一套逻辑,OpenClaw、CC 底层都是这个
关键代码
cc/core/query_loop.py 是一个 async generator 状态机,核心变量一开头就初始化好
<br>
循环的骨架
四阶段
Phase 1 准备上下文(规范化 + 估算 + 压缩判断)
Phase 2 流式调用(边收流边启动工具)
Phase 3 错误恢复
Phase 4 收集工具结果拼成 user message 进下一轮
三个 continue
错误恢复后重试
max_tokens 截断后续写
有 tool_use 则带结果再循环
三个退出
不可恢复错误
无 tool_use 正常结束(end_turn)
达到最大轮次
消息规范化(normalize)
API 对 message 格式有硬性要求——user 和 assistant 必须一条隔一条严格交替
真实使用中会被破坏
你让 Agent 删文件,执行到一半你按两下 ESC 强行中断
这时上一轮可能只追加了一条 user message 没有对应的 assistant 回复;
你再输入新指令,就变成连续两条 user message,不合法。
每个 tool_use 必须有一个对应的 tool_result(工具结果也是作为 user message 传入的)
中断后可能只剩 tool_use 没有结果
解决方案
cc/models/messages.py 的 normalize_messages_for_api() 做三步修复
交替修复
遍历时把连续的多条 user 消息合并
连续两条 assistant 中间插入一条占位的 {"role":"user","content":"Continue."}
开头修复
如果第一条是 assistant,在前面插入 {"role":"user","content":"Begin."}
配对修复
(_ensure_tool_result_pairing())
收集所有 tool_use 和 tool_result 的 ID,删掉指空了的孤儿 tool_result
为没有结果的孤儿 tool_use 注入一条合成的错误结果(内容是 "[Tool result missing due to internal error]")
为什么要这么做
大模型训练时就是按交替格式训练的,上下文不交替它也能输出,但输出内容很可能偏离、质量下降
所以每次传入大模型之前,都要强制让整个上下文合规
错误恢复机制(三条路径)
错误恢复不是一个边角料功能,它在整个 loop 循环里非常重要
上下文过长
自动触发**紧急压缩(compact)**,压缩后重试
输出过长(max_tokens 截断)
尝试**提升 max 输出上限**(代码里从 16384 提升到 65536)
并追加"请从上次中断处继续"的消息让模型续写
最多恢复 3 次,还不行就停下来向用户提示
限流(429/529 请求失败)
退避chongshi
代码里 sleep 时间是 2s、4s、6s、8s、封顶 10s,最多重试 5 次
重要的工程细节
错误恢复成功后 turn_count -= 1
恢复不消耗轮次
为了用户体验,避免把重试浪费在用户的功能额度上
流式工具执行(Streaming Tool Execution)
如果模型一次返回了多个工具调用(比如同时返回 Read 和 Grep)
传统的做法是等整个响应全部输出完,再一个个执行工具
如果其中有网络搜索这种耗时的工具,整个过程就要等很久
解决方案
模型是流式输出的,一旦某个 tool_use block 完整生成,就立刻开始执行这个工具
不等后面的内容。可能模型还在吐字,先到的工具已经执行完了
等 message_stop 时所有工具都执行完了
并发安全判断
并发安全的工具(只读类,如 Read/Glob/Grep
只读类,如 Read/Glob/Grep
block 一生成立即并行执行,不排队
并发不安全的工具(写类,如 Edit/Write,会影响后续执行结果)
写类,如 Edit/Write,会影响后续执行结
进入一个工具执行队列,等前面的工具执行完再执行,串行排队
关键代码说明
cc/tools/streaming_executor.py
子主题
如果 Edit 正在写文件,连 Read 都不能并行——因为 Read 可能读到写了一半的内容
并发安全的判断维护了整个执行的正确性。
每个工具的固定流水线
信号量限流
PreToolUse hooks
权限检查
执行
异常不抛出,转成 is_error=True 的结果返回给模型,让模型自己看到错误并修正
PostToolUse hooks
三层数据结构
第一层:Content Blocks(内容块)
模型一次返回的 assistant 消息里,消息内部是块(block)结构的
类型
核心的就两个:text 和 tool_use
TextBlock
纯文本内容,直接流式输出给用户
ToolUseBlock
工具调用,里面有工具名、唯一 ID、输入参数
ToolUseBlock 和 ToolResultBlock 都带唯一 ID
结果和调用靠 ID 一一对应
保证了多轮链式调用时上下文不会出错
如果 ID 对不上,消息规范化阶段会强制修复
ToolResultBlock
工具执行完之后产生
工具执行结果,最终会作为 user message 的内容块拼回上下文
ThinkingBlock
模型思考的内容,不会反馈给客户,只会告诉客户"我正在思考"
ImageBlock(图片)
关键代码
cc/models/content_blocks.py
定义了各类 block 和统一反序列化入口
对未知 block 类型,消息层选择直接报错而不是静默忽略
因为静默忽略会悄悄丢掉对话数据。
第二层:Messages(消息列表)
消息层就是整个对话上下文,由 user 和 assistant 一条间隔一条排列
完整的消息类型
SystemMessage
系统级消息,用户看不到,由 11 段 prompt 拼装而成
放在消息列表最前面
SystemMessage 不直接发给 API,而是作为请求里的 system 参数
UserMessage
AssistantMessage
CompactBoundaryMessage
压缩边界消息
当上下文太长触发压缩后,前面所有历史被压缩成一份摘要,整个作为一个大的 user message
压缩后的上下文就变成:system message + 一条包含历史摘要的 user message + 最近几轮原始对话
第三层:State(状态机)
LangGraph 里的 state 是储存上下文内容、可读写的状态对象,本质上是维护整个 Agent 运行过程的状态机
这里的 state 是同样的逻辑
把 blocks、messages 的内容都储存在 state 里
监控整个过程——跑了几轮(turn_count)、要不要压缩、用什么模型
是基于这个 state 构造状态机来驱动整个逻辑
cc/models/state.py 里的 QueryState 就承担这个角色
记录轮次
token 估算值
压缩追踪状态(AutoCompactTracking)
上下文压缩(Compact)
触发时机
每轮把消息放入大模型之前,都会判断一次要不要触发压缩
具体做法
并行估算当前上下文的 token 数,超过阈值就触发自动压缩
cc/compact/compact.py
200K 的上下文窗口,在约 93.5%(187K)处触发,留 13K 缓冲区给新输入和输出
token 估算不加载分词器(太重),直接用"UTF-8 字节数 ÷ 4"估算,宁可偏保守早压缩
压缩做了什么
消息少于 10 条不压
把消息拆成"旧消息"和"最近 4 轮(8 条)"两部分,最近 4 轮原样保留
旧消息转成纯文本(特意展开 tool_result 的内容,让摘要保留文件路径、命令等关键信息),用一个小模型(max_tokens=4096)按专门的压缩 prompt 生成摘要;
返回 [CompactBoundaryMessage(摘要), *最近4轮],主循环收到后清空原消息列表、替换为新列表
结果
所有的 user/assistant 历史---->一条大的 user message(摘要)
当前上下文 = system message + 摘要 user message + 最近几轮对话,继续请求模型。
这就是你在一个窗口聊太久时它会自动执行 compact 命令的原因。system prompt 里也会明确告诉模型"系统会在接近上下文限制时自动压缩,对话不受窗口限制",让模型放心,不会因为上下文快满就敷衍输出。
压缩的失败保护
压缩本身失败:降级返回原消息列表;
连续失败 3 次:放弃压缩,防止死循环;
由 API 报 413(请求过大)触发的"响应式压缩":只允许尝试一次。
工具系统
核心问题
工具怎么注册
function calling 的基础
所有工具的 schema(名称 + 描述 + 参数定义)都注册在系统提示词里,系统提示词里写了什么时候用什么工具
模型在 loop 循环里读到了,自然就会返回对应的 tool_use block
比如提示词里写了"读文件用 Read 工具而不是 cat"
用户说"帮我读 README",模型就选择 Read
怎么被选中
怎么安全执行
工具的三层注册架构
第一层:6 个文件操作基石工具,启动时直接全量加载
Read、Edit、Write、Glob、Grep、Bash
文件操作的基石,没有任何外部依赖,注册上下文的那一瞬间就能直接用
注册时把工具名、描述、schema 直接注入 system prompt
设计细节
这六个工具本质上都是 Bash 命令的封装
为啥拆成六个
因为裸 Bash 是不安全命令,必须经过人类检查
拆分成 Read/Glob/Grep 三个只读命令
这三个就可以直接执行且并发安全
Edit 和 Write 并发不安全但可用(进队列串行)
Bash 保留为高风险工具
本质上是同一套命令,赋予了不同的安全级别和权限
安全级别体现
一是是否并发安全
二是读写权限(由 hooks 和 permission 两件事控制)
第二层:任务管理、网络、开发类工具,延迟导入
这类工具(WebFetch、WebSearch、TodoWrite、LSP 等)有外部依赖
如果一开始全部 import,容易产生循环依赖,也拖慢启动
正确做法
一开始只把工具名和描述注入 system prompt
依赖在系统启动后缓慢异步加载
确保用到的时候能直接用
第三层:Agent Teams 工具,二次布线
TeamCreate、TeamDelete、SendMessage 这类工具是运行过程中临时创建的
因为创建子 Agent 这件事会改变当前的上下文结构
多出了新工具,需要重新组装上下文、调整注入的内容、注入新的依赖
"触发后才临时布线、重构上下文"的方式就叫二次布线
关键代码
main.py 的 _build_registry()
先完成第一、二层注册
registry._tools.pop() + register()
二次布线在代码里的体现
创建 team 相关工具时,覆盖注入运行时依赖(比如把 team 名字、sender 身份注入 SendMessage 工具)
工具调度全流程
1.注册
读取工具注册表(tool list 字典),知道有哪些工具;
2.生成描述清单
把所有工具的 description/schema 组装进上下文
本质就是在最终发给模型的 system prompt 里加了一个工具清单
3.模型决策
模型根据用户输入和提示词规则,输出 block
比如先一个 TextBlock"好,我来帮你读取",再一个 ToolUseBlock 调 Read
4.流式分发执行
tool_use block 一完整就开始执行
5.安全检测
执行前过 hooks 和 permission 两道关卡
6.回传结果
tool_result 带唯一 ID 拼成 user message 回上下文,继续 loop
两道安全关卡
Hooks(PreToolUse 钩子)
检测的不是工具本身,而是工具输入参数的内容
比如模型生成了一条 Bash 命令,hook 会检查这个命令字符串里有没有危险内容(rm -rf、delete 等),遇到危险就拦截
用户在 settings.json 配置文件里自己写 hook 脚本(shell 脚本)
设计细节
hook 系统的任何故障都不应该影响工具执行
hook 脚本本身报错只记警告、超时放行,宁放过不阻塞
Post hooks(工具执行后触发)的返回值被忽略,因为结果已经产生,无法阻止
Permission Check(权限检查)
针对工具本身判断要不要执行
权限有三种模式
bypass 模式
所有工具全部放行(指读、编辑、命令都自动放行),最省事但不可控
acceptEdits 模式
只读工具和编辑工具自动放行
default 模式(日常可控模式)
只读工具(Read/Glob/Grep 等)直接放行
编辑工具(Edit/Write)需要向用户询问
高风险工具(如 Bash)必须询问
询问就是那个提示:yes(允许一次)/ no(不允许)/ always(总是允许)
选 always 后该工具进入"总是允许"集合,后续免确认
非交互模式下 fail-fast
非交互模式下 fail-fast——如果是 print 模式或子 agent(没有人可以回答询问),凡是需要询问的工具直接拒绝执行,宁可不做也不擅自做
如果工具不在任何白名单里,交互模式下就交给人类判断,非交互模式下直接拒绝
用户可以自定义规则
cc/permissions/rules.py
比如 "Bash:git*" 表示放行所有 git 开头的 Bash 命令,规则支持 glob 匹配,deny 规则优先于 allow 规则
hooks 管"工具执行的内容"(参数里有没有危险命令),permission 管"工具本身"(这个工具要不要执行、要不要问人)
内置工具速览
22 个工具
文件操作
文件操作
Read、Edit、Write、Glob、Grep、Bash(6 个基石)
网络
WebFetch、WebSearch(调用现有搜索服务接口)
交互
AskUser(向用户提问的机制,比如让模型问"你倾向哪个方案")
任务管理
TodoWrite、Task 系列
Skill
Skill 就是作为整个 agent loop 里的一个工具来嵌入的
子主题
当你确定要集成某个 skill 时
skill 的 frontmatter(name + description)
作为这个工具的 description 注入 system prompt
模型据此决定什么时候调用
调用后,skill 那套渐进式披露的过程,本质就是一段上下文拼接/子流程执行
最后的结果作为 tool_result 交还给主 agent
Plan Mode
模态切换工具——进入 plan 模式时只规划不改代码,<br>退出 plan 模式才开始改代码
Agent Teams
Agent、TeamCreate、TeamDelete、SendMessage
其他
NotebookEdit、ToolSearch、LSP、Brief
如何提升工具调用的准确度
<br>
记忆系统(Memory)
记忆系统全生命周期
记忆系统核心就三个字:存、取、用。整个生命周期和 loop有关
取(loop 之前)
每一轮把所有 message 传入大模型之前
从 memory 存储中加载记忆内容,注入 system prompt
拼接到当前 message 上下文里
阈值检查
注入后判断上下文是否超阈值,超了先触发 compact 再调模型
存(loop 之后)
本轮 loop 结束(大模型文本全部返回、没有工具调用了)之后
把对话内容做持久化,同时在后台异步触发一次记忆提取,把值得长期保存的内容写入 memory 文件
这一步是异步的,完全不影响主链路给用户返回内容
用(下一轮)
用户开启新一轮对话时,再从 memory 里加载,注入 system,开始新一轮循环
写入时机有两种
显式写入
用户在指令里直接说"记住这个",或者模型自己判断某些信息值得储存
立刻触发写入(哪怕工具循环还没结束)
后台自动提取
每一轮 loop 循环结束之后,自动触发一次记忆提取,把重要东西存起来
四类记忆类型
记忆按类型存成不同的 md 文件(user / feedback / project / reference)
User(用户身份)
用户的角色、偏好、水平、知识背景
比如"我是数据科学家,主要做日志分析"
"我写十年代码了,第一次接触这个仓库的 React 部分"
记住这些,模型就能用后端的类比来解释前端概念
Feedback(行为反馈)
用户对 Agent 工作方式的纠正和确认
典型触发词是"一定要""一定不要"
比如"测试不要用 mock 数据库——上季度 mock 通过
只保存修正性的、能避免过去错误的反馈
Project(项目上下文)
当前项目正在进行的工作、目标、计划
比如"周四后冻结非关键合并"
无法从代码或 git 历史里提取出来的信息
保存时务必把相对日期转化为绝对日期
Reference(外部引用)
外部资源/系统的指针,只存位置不存内容
比如"整个前端都要参考外部那套已做好的 UI 设计",就存下这个引用的位置,需要时再去读
不该保存什么
安全红线:永远不要保存密码或凭证
代码模式、规范、架构、git 历史、临时调试方案
这些是你立刻就能通过工具调用获取的东西,存进记忆反而是噪音
该保存的是令人惊讶的、非显而易见的信息
MEMORY.md 索引 + 渐进式加载
每个 md 记忆文件开头有 frontmatter(name + description + type)
启动/构建上下文时,只把 MEMORY.md 索引(所有记忆的 name + description 清单)注入 system prompt
注入非常轻量(索引上限 200 行)
模型在运行过程中判断需要哪条具体记忆时,再用 Read 工具去读那个具体的记忆文件
好处
一是节省上下文
没必要用向量数据库——记忆内容本身不多,直接渐进式加载就够
二是简单准确
三是记忆保存在本地而不是云端
按 project 隔离(不同项目路径下的记忆互不干扰)
悲观信任机制
记忆里提到的文件名、函数、"必须采取的行动"
在写入时存在,不代表现在还存在
文件可能被重命名、删除、从未合并
悲观(谨慎)信任策略
凡是根据记忆做出的决策或判断,都要进一步核实
记忆说某个文件存在,推荐之前先检查它是否真的存在
记忆说某函数存在,先 grep 一下
当记忆和当前现状冲突时
相信现状,并调用 AskUser 工具询问用户以哪个为准,然后更新记忆
记忆加载进来之后有时反而会干扰模型效果。记忆是参考,不是事实
记忆 vs 压缩
Compact 压缩
性质
有损:摘要替换原始信息
时机
上下文超阈值时(loop 内、请求前)
位置
消息列表里的大 user message
范围
当前会话的上下文窗口
Memory 记忆
性质
无损:往上下文里新增内容
时机
loop 结束后异步提取,请求前注入
位置
system prompt 第 10 段 + md 文件
范围
跨会话、按项目隔离、持久化到磁盘
两者之间的桥梁是 summarize tool results(工具结果摘要)
压缩的时候工具调用的结果也会被压掉,怎么办
在处理工具结果时,提醒模型把工具返回结果里的关键信息写进回复/保存下
通过这种"记忆化"的方式,缓解上下文压缩导致的工具结果丢失
短时记忆就是 message 上下文(只在当前窗口有效)
写入 memory.md 的才是长时记忆(每个新窗口组装上下文时都会注入)
关键代码说明
cc/memory/extractor.py 的 ExtractionCoordinator 实现了"后台 coalescing(合并)提取
<br>
System Prompt:11 段拼装
System prompt 本质上是11 段定义的集合
体现了对模型最系统的要求:把记忆层注入、工具层注入、CLAUDE.md 文件注入全部组织起来。最终组装下来非常长
关键代码
cc/prompts/sections.py 和 builder.py
11 段
身份与安全(Intro)
声明"你是一个什么 Agent",可以使用的工具,以及安全边界
安全边界
允许授权安全测试
拒绝恶意用途
禁止编造 URL
系统行为与契约(System)
告诉模型"你在工具调用之外输出的所有文本都会直接展示给用户"
工具在权限模式下执行的规则;<system-reminder> 等系统标签的含义
hooks 的反馈视为用户反馈
以及"系统会在对话接近上下文限制时自动压缩"
它让模型放心,不会因为上下文快满就返回敷衍信息
执行任务要求(Doing tasks)
任务执行中的要求和要避免的事
比如不要对没读过的代码提出修改、不留半成品、安全编码等
行为谨慎守则(Actions with care)
评估操作的可逆性和爆炸半径
危险操作(删除、强推、发消息)先确认
工具使用规范(Using tools)
专用工具优先于 Bash(Read 替代 cat、Edit 替代 sed)
无依赖关系的工具要并行调用
语气与风格(Tone and style)
不用 emoji;回复精简精炼;工具调用后面不用冒号
都是基于 bad case 一条条调出来的提示词工程
输出效率(Output efficiency)
直接说要点、不兜圈子
模型天生倾向于多生成内容、多输出工具
这里明确约束。文字聚焦于需要用户决策的关键节点、高层级进度汇报、计划变更或障碍,一句话说清楚
环境信息(Environment)
当前工作目录、是否 git 仓库、平台、shell、模型名、日期
工具摘要(Tools + Summarize)
工具 schema 集成的结果,以及 summarize tool results 提醒
把工具结果关键信息写进回复,防止压缩丢失
记忆系统(Memory,条件注入)
记忆行为指令 + MEMORY.md 索引
含软触发(模型自己判断该访问记忆)和硬触发(用户明确要求访问)。
CLAUDE.md
用户对整个系统每次提问都要集成的自定义指令
工程细节
为什么 CLAUDE.md 的执行优先级最高
提示词越靠近末尾,模型的注意力(attention)分配给它的权重越高(近因效应)
所以把用户自己的 CLAUDE.md 放在 system prompt 的最后,它的指令优先级就能压过前面的通用约束
system prompt 返回的是分段列表而不是拼接好的字符串
前 7 段是静态内容,可以被prompt caching(提示词缓存)命中省钱
动态段落(环境、记忆、CLAUDE.md)放在后面,不破坏缓存前缀
完整请求时序:一次对话的全过程
以"帮我看看 README 里面写了什么"为例,完整时序
控制面(main.py)
输入不是斜杠开头 → 构造 user message 追加到 message 列表
进入 query_loop
消息规范化
确保 user/assistant 一一交替、tool_use 配对完整
token 估算
超阈值则先自动压缩
QueryEngine 组装请求
模型名、max_tokens、流式开关、system(11 段)、messages、tools,发送给 API
第一轮模型响应(流式)
返回里有一个 TextBlock("好,我来帮你读取",流式逐字输出给用户)
一个 ToolUseBlock(Read)。stop_reason = tool_use
工具执行
Read 的 block 一完整立即执行(只读、并发安全)
执行前先过 PreToolUse hook(无危险指令)→ permission check(只读工具放行);得到文件内容
结果回写
tool_result(带唯一 ID,与 tool_use 对应)作为 user message 拼入上下文
此时上下文 = user 提问 + assistant(text+tool_use)+ user(tool_result)
第二轮模型调用
再次规范化、再次请求。这次模型返回纯文本
"README 的核心内容是……"
stop_reason = end_turn → 不再触发新一轮,流式输出文本给用户
后处理
会话持久化
把上下文按 session ID 存盘
token 统计
本次共调用 2 次 API、执行 1 次工具、4 条 message
后台记忆提取
异步触发,不阻塞主流程
回到 REPL 主循环,等待下一轮输入
整个过程就是 main.py 主循环里嵌套 query_loop 状态机,状态机驱动"规范化 → 压缩判断 → 流式请求 → 事件渲染与工具执行 → 结果拼回"的循环,直到 end_turn
Agent Teams:多智能体协作
什么时候触发
当模型判断任务极其复杂时,通过工具调用触发多智能体协作
比如调用 TeamCreate 创建团队、用 Agent 工具派生子 Agent
团队不是一开始就定义好的,而是在运行过程中动态创建和解散的
协作模式:Leader + Teammates
原来只有一个 loop 循环(主 Agent)
创建团队后,主 Agent 变成 leader(team-lead)
它创建若干子 Agent(teammate)
每个子 Agent 领到一个子任务
子 Agent 的 system prompt 不是完整的 11 段
而是 leader 派发的任务描述
极简版 prompt + teammate 附加说明
注入身份和"必须用 SendMessage 通信"的规则
工具和 memory 规则依然集成
所有子 Agent 的结果通过 Mailbox(信息收集系统) 发给 leader
leader 不停地轮询(polling)自己的收件箱
发现有新消息就消费掉
包装成 <task-notification> 作为 user message 注入自己的上下文
触发 leader 的下一轮 loop,继续决策(比如再派任务、或汇总结果)
关键代码说明
Mailbox 用文件系统实现(~/.claude/teams/{team}/inboxes/{agent}.json
发送 = 读收件人 JSON → 追加 → 写回
接收只取未读、读后标记
好处是跨进程、可持久、无需中间件
当所有子任务完成、结果收齐后,leader 调用 TeamDelete 解散团队——进程中的 team 就清理掉了(删除前有安全检查:仍有活跃成员则拒绝删除)
上下文隔离与防递归
每个子 Agent 的上下文完全隔离
子 Agent 之间彼此不知道对方和 leader 的上下文
只知道自己那套由 leader 生成的 system prompt 和任务
工具调用也彼此隔离
但它们可以串行协作——比如 team A 做一件事,team B 做 check,check 完的结果再返回给 team A
子 Agent 与主 Agent 的四大差异
独立 loop + 独立消息列表:不污染父会话
system prompt 不同:极简任务版,不含完整 11 段
工具过滤(防递归、防越权)
子 Agent 的工具表里排除 Agent、TeamCreate、TeamDelete(不能创建子 Agent 或管理团队——防止无限递归派生)、AskUser(无法与用户交互)
关键代码
权限是非交互的:遇到需要询问的操作直接拒绝(fail-fast),本质是一个 print 模式
同进程多 Agent 如何互不干扰
个 teammate 以协程(asyncio)并发跑在同一个进程/线程里,靠 contextvars(上下文变量) 保证每个协程持有独立的身份信息(agent_id、team_name 等),互不串扰
等价于 TypeScript 里的 AsyncLocalStorage。每个 teammate 以 asyncio.create_task 启动,不阻塞主 Agent
总结
核心流程
<br>
关键优化点
<br>
常见问题
<br>
Collect
Get Started
Collect
Get Started
Collect
Get Started
Collect
Get Started
评论
0 条评论
下一页