本文是 Claude Code 源码逆向系列 的第三篇,聚焦 Agent Runtime 的核心执行循环与子代理协作机制。
第二块是我认为最有"框架味"的部分:Agent 不是单次调用,而是一个带状态的循环执行体。
恢复后的模块拆分如下:
src/core/agent/runtime.ts:核心循环,负责模型调用、tool_use 执行、结果回填 src/core/agent/types.ts:运行时消息、事件、配置类型 src/core/agent/mailbox.ts:队友/子代理消息邮箱(内存实现) src/core/agent/manager.ts:管理多个 in-process teammate src/core/agent/protocol.ts:控制消息协议(如 shutdown) src/core/agent/inProcessRunner.ts:轮询邮箱并驱动 runtime src/core/agent/run.ts:对外暴露的便捷入口,创建 runtime 并执行 src/core/agent/options.ts:解析 teammate 选项 一、核心循环:AgentRuntime.submitMessage 整个 Agent Runtime 的灵魂是 AgentRuntime 类的 submitMessage 方法。它是一个 AsyncGenerator——不是简单的 async 函数,而是调用者可以按需消费每一步事件的异步迭代器。
核心循环可概括为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 // src/core/agent/runtime.ts (简化示意) async *submitMessage(input: string): AsyncGenerator<AgentRuntimeEvent> { // 1. 首次调用时发送 init 事件 yield { type: "system", subtype: "init", ... }; // 2. 用户消息入队 this.mutableMessages.push({ role: "user", content: input }); yield { type: "user", message: userMessage, ... }; // 3. 核心循环:最多 maxTurns 轮 for (let turn = 0; turn < maxTurns; turn++) { const response = await callModel(client, { model, messages, tools, system, signal, skills }); this.mutableMessages.push(assistantMessage); yield { type: "assistant", message: assistantMessage, ... }; // 无 tool_use → 任务完成 const toolUses = extractToolUses(response.content); if (toolUses.length === 0) { yield successResult(...); return; } // 逐个执行 tool,回填结果 for (const toolUse of toolUses) { toolResults.push(await this.runLocalTool(toolUse)); } this.mutableMessages.push({ role: "user", content: toolResults }); yield { type: "tool_use_summary", ... }; // 预算超限检查 if (this.estimateCostUsd() > maxBudgetUsd) { yield { type: "result", subtype: "error_max_budget_usd", ... }; return; } } // 达到最大轮次 yield { type: "result", subtype: "error_max_turns", ... }; } 对应伪代码:
...