ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Claude-Code源码解读:Agent篇——从queryLoop到AgentTool的配置骨架与验证

Claude-Code源码解读:Agent篇——从queryLoop到AgentTool的配置骨架与验证 1. 从一次“子 Agent 没触发”说起如果你正在读 Claude-Code 的源码大概率会在src/query.ts和AgentTool.tsx之间来回跳。我最初看这段代码时有个很典型的困惑明明在对话框里输入了“帮我搜一下认证模块”为什么日志里只有主 Agent 在调GrepAgent({ subagent_type: Explore })压根没出现后来把queryLoop的调用链捋清楚才明白子 Agent 的创建不是“用户发消息就自动 spawn”而是主 Agent 在某一轮推理里主动发起的一次 tool call。换句话说AgentTool.tsx的call()只负责权限校验、类型解析和 spawn 执行它不负责判断“该不该委派”。真正的决策点在模型的 tool choice 上不在某段 TypeScript 的if/else里。这篇就沿着queryLoop → AgentTool → runAgent → 子 Agent 自己的 query 循环这条链把源码阅读转化成一份能跑通的本地配置。我会给出可复制的settings.json与config.toml骨架把统一 Key 和 API 通道接进去然后演示一次子 Agent 触发与日志验证。目标很直接让你读完能自己复现一次Explore子 Agent 的完整调用。适合谁看已经在用 Claude-Code、想理解子 Agent 调度机制的开发者或者你正准备给自己的 Agent 框架加一层“委派”能力想先看看成熟实现长什么样。前置知识只需要你会读 TypeScript、知道 tool use 的基本流程。2. queryLoop 与 AgentTool 的调用链拆解2.1 queryLoop 是公共引擎restored-src/src/query.ts里的query()和内部的queryLoop是整个产品里“agent 行为”的公共引擎。它做的事可以概括成一个循环一轮轮调模型、解析 tool use、执行工具、把结果写回消息、直到结束。主会话和子 Agent 共用这一份循环区别只在于传进去的 system prompt、工具集和上下文不同。// 简化后的 queryLoop 骨架帮助理解调用顺序 async function queryLoop(messages, tools, systemPrompt) { while (true) { const response await callModel({ messages, tools, systemPrompt }); const toolUses parseToolUse(response); if (toolUses.length 0) break; for (const use of toolUses) { const result await executeTool(use); // Agent 工具也在这里被调用 messages.push({ role: tool, content: result }); } } return messages; }关键点在于executeTool对普通工具和Agent工具是一视同仁的都是 tool call。区别发生在Agent工具内部它不会返回一个原子结果而是启动一个新的queryLoop。2.2 AgentTool 只做三件事AgentTool.tsx的call()职责很窄权限校验、类型解析、spawn 执行。它拿到subagent_type后去查找对应的 Agent 定义比如Explore会命中EXPLORE_AGENT然后交给runAgent.ts组装 prompt、工具和上下文再进入子 Agent 自己的query()循环。// AgentTool.call() 的职责边界示意 async call(input: { subagent_type: string; prompt: string }) { await checkPermission(input.subagent_type); // 1. 权限校验 const agentDef resolveAgentDef(input.subagent_type); // 2. 类型解析 return await runAgent(agentDef, input.prompt); // 3. spawn 执行 }所以如果你要改Explore的行为通常不是动AgentTool.tsx而是从exploreAgent.ts的getExploreSystemPrompt()和disallowedTools入手。业务逻辑在exploreAgent.ts执行在runAgent.ts的通用子 Agent 循环里runAgent.ts和agentToolUtils.ts里只有少量 Explore/Plan 专属的上下文与工具过滤优化。2.3 子 Agent 与普通 Tool 的本质差异把两者放在一起对照差异会非常清楚维度普通 Tool子 AgentAgent 工具本质单次能力调用派生一个会自己多轮推理的小 agentLLM 推理无纯代码执行有完整 query() 循环System prompt无有如 Explore 的只读 specialist prompt对话历史不参与有独立消息链fork 除外会继承主上下文上下文成本一次 tool result多轮 tool 输出主 Agent 通常只收最终摘要工具集固定单一能力可调多种 tool按类型限制续跑不支持异步子 Agent 可用 SendMessage resume一句话概括普通 tool 是主 Agent 的“手脚”单次动作子 Agent 是主 Agent 雇的“临时工”有自己的判断和多轮工具使用通过Agent这个元工具接入结果以 tool result 形式回到主 Agent。2.4 三层 Prompt 别搞混读源码时最容易混淆的是 prompt 的层级。FileEditTool/prompt.ts、FileReadTool/prompt.ts属于 Tool description是给主 Agent 看“怎么调用这个工具”的不是子 Agent 的 System prompt。层级给谁看作用普通 Tool子 Agent① 主 Agent System prompt主 Agent定义主会话身份、规范共享共享fork 可继承② Tool description主 Agent教如何调用工具有Agent 工具也有③ 子 Agent 专属 System prompt子 Agent 的 query 循环定义子 Agent 身份、行为、约束无有理解了这三层你再看exploreAgent.ts里那份只读 specialist prompt就知道它属于第③层是子 Agent 自己的“大脑设定”。3. 前置把统一 Key 与 API 通道接好在跑通子 Agent 之前得先让 Claude-Code 能正常调模型。这里用统一 Key 和 API 通道的方式接入避免每个工具单独配一套凭证。你需要先拿到一个可用的 Key然后把它写进环境变量或配置文件。第一步打开控制台创建 Key。地址是https://taotoken.net/api-keys登录后新建一个 Key复制出来。这个 Key 后面会同时给主 Agent 和子 Agent 用因为它们共用同一个 API 通道。第二步确认 API 基地址。统一通道的地址是https://taotoken.net/api注意这里不带任何查询参数保持干净。如果你在文档里看到带 UTM 的链接那是给网页访问用的配置里不要带。第三步把 Key 写进环境变量。Linux/macOS 下可以这样export TAOTOKEN_API_KEYsk-你的Key export ANTHROPIC_BASE_URLhttps://taotoken.net/apiWindows PowerShell 用$env:TAOTOKEN_API_KEYsk-你的Key $env:ANTHROPIC_BASE_URLhttps://taotoken.net/api如果你更习惯用配置文件Claude-Code 支持settings.json把凭证和模型参数放进去团队协作时也方便统一。具体字段在下一节给出。注意Key 属于敏感凭证不要提交到 Git 仓库。建议用.env或系统环境变量管理配置文件里只放非敏感的模型和工具参数。4. 可复制的 settings.json 与 config.toml 骨架4.1 settings.json主 Agent 与子 Agent 共用Claude-Code 的settings.json通常放在项目根目录的.claude/下或者用户级配置目录。下面这份骨架把 API 通道、模型、以及子 Agent 相关的开关都列出来了你可以直接复制后改 Key。{ apiKey: sk-你的Key, baseUrl: https://taotoken.net/api, model: claude-sonnet-4-20250514, subagents: { enabled: true, defaultType: general-purpose, types: { Explore: { description: 只读探索型子 Agent适合大范围搜索与多轮检索, disallowedTools: [Write, Edit, Bash], model: claude-sonnet-4-20250514 }, Plan: { description: 规划型子 Agent只读用于拆解任务, disallowedTools: [Write, Edit], model: claude-sonnet-4-20250514 }, verification: { description: 验证型子 Agent改完代码后独立验证, disallowedTools: [Write, Edit], model: claude-sonnet-4-20250514 } } }, permissions: { allow: [Read, Grep, Glob, Agent], deny: [] } }几个字段说明一下。baseUrl指向统一 API 通道主 Agent 和子 Agent 都走这里。subagents.types里给每个类型配了disallowedTools比如Explore禁掉Write/Edit/Bash保证它只读。permissions.allow里显式放行Agent否则主 Agent 可能因为权限不足而无法发起委派。4.2 config.toml给自定义子 Agent 用如果你要加自定义子 Agent除了在settings.json里注册类型还可以用config.toml定义更细的行为。下面这份骨架演示了一个自定义的code-reviewer子 Agent。[agent] name code-reviewer description 对指定文件做只读代码审查输出问题清单 model claude-sonnet-4-20250514 system_prompt 你是一个只读代码审查子 Agent。你只能使用 Read、Grep、Glob 工具。 你的任务是审查用户指定的文件找出潜在的空指针、边界条件和资源泄漏问题。 输出格式按文件分组每条问题给出行号和简短说明。 不要修改任何文件。 [agent.tools] allow [Read, Grep, Glob] deny [Write, Edit, Bash] [agent.limits] max_turns 12 timeout_seconds 180max_turns限制子 Agent 自己的 query 循环轮数避免它无限探索。timeout_seconds是兜底防止某个子 Agent 卡住拖垮主流程。这两个参数在runAgent.ts里会被读取用来约束子 Agent 的执行。4.3 把 Explore 的 prompt 与工具过滤对齐源码源码里Explore的业务逻辑主要在exploreAgent.ts核心是getExploreSystemPrompt()和disallowedTools。你在配置里对齐这两点行为就基本一致了。Explore没有单独的“Explore 引擎”它走的是通用 Subagent 管线所以配置里不需要额外写执行逻辑只要把 prompt 和工具白/黑名单配好。{ subagents: { types: { Explore: { systemPromptFile: .claude/prompts/explore.md, disallowedTools: [Write, Edit, Bash], oneShot: true } } } }oneShot: true对应源码里 Explore 的 one-shot 特性返回报告后不保留 resume 提示。如果你需要能续跑的子 Agent就别开这个开关改用异步子 Agent 加SendMessageresume。5. 验证触发一次子 Agent 并看日志5.1 触发动作配置写好后启动 Claude-Code在对话框里输入一个需要大范围探索的任务比如在 src/auth/ 目录下找 session 处理逻辑报告文件路径和调用链。主 Agent 收到后会先读 system prompt 和 Agent 工具描述然后决定是直接Grep还是委派给Explore。如果它判断需要多轮搜索就会发起Agent({ subagent_type: Explore, prompt: ... })。5.2 日志里该看到什么打开调试日志你应该能看到类似这样的调用链[main] queryLoop turn 1 [main] tool_call: Agent({ subagent_type: Explore, description: 搜认证模块 }) [agent:Explore] runAgent start, agentIdxxx [agent:Explore] queryLoop turn 1 - Glob(src/auth/**/*.ts) [agent:Explore] queryLoop turn 2 - Grep(session, src/auth/) [agent:Explore] queryLoop turn 3 - Read(src/auth/session.ts) [agent:Explore] report ready, returning to main [main] tool_result received, length...关键验证点有三个。第一Agent工具被调用说明主 Agent 决定委派。第二[agent:Explore]前缀的日志里出现了多轮Glob/Grep/Read说明子 Agent 有自己的 query 循环。第三主 Agent 收到的是最终报告而不是子 Agent 每一轮的原始输出这对应“上下文成本”那一栏的描述。5.3 用一次成功结果确认链路如果日志里能看到子 Agent 返回的报告并且主 Agent 基于报告继续回答那整条链路就通了。你可以故意把permissions.allow里的Agent去掉再跑一次会看到主 Agent 无法发起委派只能自己Grep。这个对照实验能帮你确认权限配置确实生效。6. 本篇常见错排查6.1 子 Agent 一直不触发最常见的原因是主 Agent 判断不需要委派。它可能觉得直接Grep就够了。你可以把任务描述得更“探索型”比如加上“大范围”“多轮搜索”“不确定路径”这类词。另一个原因是permissions.allow里没放行Agent主 Agent 想调也调不了。检查settings.json的permissions.allow是否包含Agent。6.2 子 Agent 报权限错误如果日志里出现permission denied for Write说明子 Agent 的工具白名单没配好。Explore应该禁掉Write/Edit/Bash但如果你自定义的子 Agent 需要写文件就要在allow里显式加上。注意disallowedTools和allow的优先级通常 deny 优先。6.3 日志里看不到子 Agent 的轮次如果只看到Agent被调用但没有[agent:xxx]的后续日志可能是日志级别不够。把调试级别调到debug或verbose再看runAgent.ts相关的输出。另外如果子 Agent 是 one-shot 且很快返回日志可能被合并检查一下是否有report ready这一行。6.4 API 通道报 401 或 404401 通常是 Key 不对或没带上。检查apiKey字段和环境变量是否一致。404 多半是baseUrl写错了确认是https://taotoken.net/api不要多加路径或参数。如果你在配置文件里写了带 UTM 的地址去掉查询串再试。6.5 子 Agent 卡住不返回看max_turns和timeout_seconds是否设置合理。如果子 Agent 陷入多轮搜索max_turns会强制它停下。如果没设它可能一直调到模型自己觉得够了为止。建议给每个子 Agent 类型都配上这两个限制尤其是Explore这种容易“越搜越深”的类型。7. 把源码阅读变成可跑通的配置回到开头那个困惑子 Agent 不是自动触发的它是主 Agent 在queryLoop里通过Agent工具显式创建的一次 tool call。AgentTool.tsx只做权限校验、类型解析、spawn 执行决策点在模型的 tool choice 上。理解这一点你再看exploreAgent.ts的 prompt 和runAgent.ts的通用循环就知道该改哪里、不该改哪里。如果你想把这条链路接到自己的项目里建议先从settings.json的最小配置开始跑通一次Explore子 Agent再逐步加自定义类型和config.toml。需要长期跑编码任务或 Agent 工作流的话可以看看 Coding Plan 的配置方式把子 Agent 的模型和轮数限制统一管理起来。配置过程中遇到接入问题API Keys 页面和接入文档里有更细的字段说明。
返回列表