English | 中文
- 已完成 P1,准备理解 Claude Code 真正心脏的人
- 想把“入口分流”接到“一轮请求怎么跑”的读者
90-120 分钟
这一阶段只抓住单轮主线里的三件事:
- 一轮请求如何在
query/queryLoop中形成主循环节奏 - 模型产出、工具调用、工具执行、
tool_result回流如何串成闭环 - permissions / streaming 为什么会在这一层变得重要
主线位置: 进入 query / queryLoop → 模型产出 → 工具调用 → 工具执行 → 工具结果回流 → 下一轮或退出
sequenceDiagram
participant QE as QueryEngine
participant Q as query()
participant QL as queryLoop() — while(true)
participant API as services/api/claude.ts
participant Tool as BashTool + 权限三层
QE->>Q: yield* query(params)
Q->>QL: yield* queryLoop()
loop 每一轮
QL->>QL: yield stream_request_start
QL->>API: queryModelWithStreaming
API-->>QL: streaming 事件流
alt end_turn(任务完成)
QL-->>Q: yield done → return
else tool_use(发起工具调用)
QL->>Tool: 语义拒绝 → 规则匹配 → 询问用户 → 执行
Tool-->>QL: tool_result
QL->>QL: 追回消息历史,继续 while(true)
end
end
Q-->>QE: 事件流结束
和 P1 不同,这一页已经正式进入请求主线本体。
你现在要看清的不是”系统有哪些功能”,而是一次请求为什么会按这个顺序推进:
query/queryLoop驱动一轮- 模型返回文本或工具调用
- 工具执行结果必须重新进入历史
- streaming 和 permissions 在这里决定这一轮能否继续安全推进
先把 l2_agent_loop.py 作为主轴:
python examples/l2_agent_loop.py有 API Key 时,再补两条边界:
python examples/l7_permissions.py
python examples/l8_streaming.py没有 API Key 时,至少跑:
python examples/l7_permissions.py不要让 l7 / l8 抢走主角位置:它们是补边界,不是替代 l2。
按这条顺序读:
当 l2 主线站稳之后,再补:
这一阶段优先开这三个主线文件:
claudecode_src/src/query.ts—— 看query/queryLoop如何驱动单轮主循环claudecode_src/src/QueryEngine.ts—— 看单轮执行中的会话 orchestrationclaudecode_src/src/services/api/claude.ts—— 看模型 streaming 如何变成上层事件流
当主线已经连起来后,再补一条工具边界:
claudecode_src/src/tools/BashTool/bashPermissions.tsclaudecode_src/src/tools/BashTool/bashSecurity.ts
现在先主动忽略这些分支:
- prompt cache 和 memory
- MCP / hooks / plugins 扩展面
- multi-agent / structured output
- REPL 的深层 UI 状态组织
- 不要先把所有工具注册细节全部读完,只追当前单轮需要的边界
这一页的目标是把单轮主线钉死,不是提早展开整个系统地图。
建议分两遍:
l2_agent_loop.pyL2query.tsQueryEngine.ts
l7_permissions.pyl8_streaming.pyL7L8services/api/claude.ts
export async function* queryasync function* queryLooptool_resultBASH_SECURITY_CHECK_IDSstream_request_startqueryModelWithStreamingfirst_chunk
- 为什么 Claude Code 的核心抽象更像“状态机 + 事件流”,而不是普通
while? - 为什么工具结果必须追回消息历史,而不是临时打印完就丢掉?
- 为什么 permissions 和 streaming 都必须进入主调用链,而不是挂在外面?
完成这一页时,你应该能:
- 解释
query.ts驱动一轮调用的基本节奏 - 说清模型输出、工具执行、
tool_result回流是如何连成一圈的 - 说清权限检查、流式事件、loop 退出条件各自处在什么层
- 明确知道 memory / runtime branches 还不是这一阶段的重点
如果还说不清,先回去补:
L2,如果你还分不清query和queryLoopL7,如果你还把 security check 和 user approval 混在一起L8,如果你还把 streaming 理解成“边输边打印”
继续去 P3 源码阅读