Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

最终 API 请求与源码索引

这一章回答最具体的问题:Claude Code 最后到底把什么发给模型,Plan Mode 和工具回合在请求中长什么样,以及这条边界在源码哪里。

先区分三种形状

形状用途是否直接出网
内部 transcriptUI、恢复、进度、附件和 sidechain 的完整事件账本
归一化消息已过滤、重排、合并并修复工具配对的 messages[]作为请求的一部分
Messages API payloadsystemmessagestools、模型与控制字段的最终快照

权限规则、Sandbox、读取缓存、任务表、取消器和 UI 状态不会因为影响 Agent 行为,就自动成为 API 顶层字段。

最终出网边界在哪里

主查询循环在 src/query.ts 维护一次 agentic turn;src/utils/messages.ts 投影内部消息,src/utils/api.ts 投影 system 与工具;最终请求在 src/services/api/claude.ts 汇合。

该文件先形成请求参数,再在流式主路径调用 Anthropic Beta Messages API,并在最后一层加入 stream: true。因此:

src/services/api/claude.ts 中的 anthropic.beta.messages.create(...) 是本教程所说的最终 API 调用位置。

它是逻辑出网边界,不等于永远直连固定域名;Bedrock、Vertex、第一方或其他兼容提供方仍可由客户端配置决定物理传输路径。

一份完整请求大致长这样

下面选择一个明确分支作为架构等价的脱敏示意:正常主线程、Prompt caching 开启、ToolSearch 已启用、thinking 开启且模型支持 adaptive thinking。实际文本、模型名、工具数量和字段会随入口、Agent、功能开关和提供方变化。

{
  "model": "当前迭代使用的模型标识",
  "max_tokens": 32000,
  "system": [
    {
      "type": "text",
      "text": "相对稳定的身份、原则与工具使用规则",
      "cache_control": {
        "type": "ephemeral"
      }
    },
    {
      "type": "text",
      "text": "当前工作目录、平台、风格、记忆机制等动态系统上下文"
    }
  ],
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "<system-reminder>当前项目适用的 CLAUDE.md 与日期</system-reminder>"
        },
        {
          "type": "text",
          "text": "用户的真实需求",
          "cache_control": {
            "type": "ephemeral"
          }
        }
      ]
    }
  ],
  "tools": [
    {
      "name": "Read",
      "description": "读取文件",
      "input_schema": {
        "type": "object",
        "properties": {
          "file_path": {
            "type": "string"
          }
        },
        "required": [
          "file_path"
        ]
      }
    },
    {
      "name": "ToolSearch",
      "description": "发现延迟加载的可调用工具",
      "input_schema": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string"
          }
        },
        "required": [
          "query"
        ]
      }
    }
  ],
  "thinking": {
    "type": "adaptive"
  },
  "metadata": {
    "user_id": "包含脱敏设备、账户与会话关联信息的序列化字符串"
  },
  "stream": true
}

这份示意最重要的不是示例数字,而是四条通道同时存在:system[] 放基础与系统上下文,messages[] 放时序化对话,tools[] 放结构化能力,其他顶层字段控制本次推理与传输。

这里同时选择了缓存开启分支,所以 system 和 message 都展示了缓存标记。正常主请求会在最后一条 message 的最后一个内容块放置恰好一个 message 级断点;不写缓存的 Fork 等特殊路径会把它移到最后一个共享前缀点。system[] 也不是固定两块:它可因归因前缀、默认 Prompt 动静边界和 global-cache 资格形成不同数量的块,缓存作用域可能是组织级、全局级或无标记。上面的两块只是一种代表快照。

若 ToolSearch 条件不成立,示例中的 ToolSearch 会从 tools[] 移除,其他工具直接带完整 schema;若 thinking 关闭或模型不支持,该字段缺席,支持预算式 thinking 的模型则使用带 Token 预算的 enabled 形态。

主 Agent 已处于五阶段 Plan 分支时,请求尾部是什么样

如果主 Agent 的用户回合开始时已经处于标准五阶段 Plan 分支,运行时会收集 Plan 附件,转成 <system-reminder>,再与相邻 user 内容归一化。忽略更早历史后,请求尾部大致是:

{
  "role": "user",
  "content": [
    {
      "type": "text",
      "text": "用户的真实需求"
    },
    {
      "type": "text",
      "text": "<system-reminder>Plan Mode 已激活:除计划文件外只读;按理解、设计、复核、定稿、ExitPlanMode 五阶段完成规划</system-reminder>"
    }
  ]
}

这正是最容易误解的地方:五阶段 Plan 指令没有替换顶层 system,也没有成为一个新的 API role;它仍是 user.content[] 中由运行时追加的文本块。访谈式 Plan 实验会换成迭代规划提醒,Subagent 也有自己的 Plan 提醒,不能把这段五阶段文本视为所有 Plan 请求的固定模板。

完整提醒不会在每个工具回合重复。后续会按人类回合节流,并在完整与稀疏提醒之间切换;压缩后仍会重建有效的 Plan 状态。

模型先调用 EnterPlanMode 时,会多一个工具回合

若当前并不在 Plan Mode,模型先从 tools[] 中调用 EnterPlanMode。它走普通权限管线:默认交互策略下可能询问并获得许可,allow/bypass 条件也可能直接放行。调用成功后,下一次 API 请求的消息尾部大致如下;示例继续选择主 Agent 的五阶段分支:

{
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "请先设计一个可靠方案,不要开始实现"
        }
      ]
    },
    {
      "role": "assistant",
      "content": [
        {
          "type": "tool_use",
          "id": "toolu_enter_plan",
          "name": "EnterPlanMode",
          "input": {}
        }
      ]
    },
    {
      "role": "user",
      "content": [
        {
          "type": "tool_result",
          "tool_use_id": "toolu_enter_plan",
          "content": "已经进入 Plan Mode,接下来应专注于探索与设计。\n\n<system-reminder>Plan Mode 已激活及五阶段规划指令</system-reminder>"
        }
      ]
    }
  ]
}

本地权限界面是否要求用户点击,不会被伪装成一句“用户已同意”发给 API。成功工具结果表达的是模式已经进入;Plan 附件在工具批次完成后出现。对上面这条普通字符串结果,纯文本 reminder 会直接拼入同一个 tool_result.content 字符串。其他工具结果形状或折叠受限时,reminder 才可能表现为内容数组或同一 user 消息中的相邻块。

普通工具调用如何形成下一次请求

以 Read 为例,模型先产生结构化意图,本地读取文件,再把结果作为 user 角色的 tool_result 回填:

{
  "messages": [
    {
      "role": "assistant",
      "content": [
        {
          "type": "text",
          "text": "我先读取架构入口。"
        },
        {
          "type": "tool_use",
          "id": "toolu_read_arch",
          "name": "Read",
          "input": {
            "file_path": "/项目/ARCHITECTURE.md"
          }
        }
      ]
    },
    {
      "role": "user",
      "content": [
        {
          "type": "tool_result",
          "tool_use_id": "toolu_read_arch",
          "content": "文件内容或经过预算处理后的读取结果"
        }
      ]
    }
  ]
}

下一轮是否继续不靠“看起来像工具调用”的文本,而是靠真实 tool_use 内容块。权限拒绝、输入错误、取消和执行异常也应形成对应的错误 tool_result,以保持协议闭环。

哪些顶层字段是条件出现的

字段何时出现或变化
tool_choice宿主需要限制或指定工具选择时
betas当前提供方和功能需要相应实验能力时
temperaturethinking 关闭时发送,默认 1 且可覆盖;thinking 开启时省略
context_management上下文管理能力、beta 和当前策略同时满足时
output_config推理力度、任务 Token 预算或结构化输出启用时
speed快速模式当前可用、受支持且未处于冷却时

同一模型内的 request retry 可以重新计算最大输出、快速模式等允许变化的动态字段。持续合资格过载触发模型 fallback 时,请求层抛出转移信号,由主查询循环更新模型并重新构造一次模型调用;它不是同一个请求闭包里悄悄改 model。两条路径都不会因此丢弃外层用户回合状态。

全书源码证据索引

章节核心证据
01 总体架构src/main.tsxsrc/screens/REPL.tsxsrc/query.ts
02 Agent 生命周期src/bootstrap/src/query.tssrc/services/tools/
03 Agent 构造src/tools/AgentTool/runAgent.tssrc/utils/forkedAgent.ts
04 系统 Promptsrc/utils/systemPrompt.tssrc/constants/prompts.tssrc/utils/api.ts
05 上下文编译src/context.tssrc/utils/attachments.tssrc/utils/messages.ts
06 工具能力图src/tools.tssrc/tools/AgentTool/agentToolUtils.tssrc/utils/toolSearch.ts
07 状态模型src/bootstrap/state.tssrc/utils/agentContext.tssrc/utils/cwd.ts
08 主查询循环src/query.tssrc/QueryEngine.ts
09 API 请求编译src/services/api/claude.tssrc/utils/api.ts
10 工具回合src/services/tools/toolExecution.tssrc/services/tools/StreamingToolExecutor.ts
11 并行与取消src/services/tools/toolOrchestration.tssrc/utils/abortController.ts
12 错误恢复src/services/api/withRetry.tssrc/services/api/errors.tssrc/query.ts
13 压缩src/services/compact/src/utils/messages.ts
14 内置工具src/tools.tssrc/tools/src/constants/tools.ts
15 搜索体系src/tools/GlobTool/src/tools/GrepTool/src/tools/ToolSearchTool/src/services/mcp/
16 扩展系统src/commands/src/skills/src/plugins/src/services/mcp/src/hooks/
17 Memorysrc/utils/claudemd.tssrc/memdir/src/services/SessionMemory/
18 安全边界src/utils/permissions/src/utils/sandbox/src/utils/worktree.ts
19 Subagent 构造src/tools/AgentTool/src/tasks/LocalAgentTask/
20 多代理协作src/utils/swarm/src/utils/tasks.tssrc/utils/teammateMailbox.ts
21 共享与隔离src/utils/forkedAgent.tssrc/utils/cwd.tssrc/utils/sessionStorage.ts
22 精妙设计src/query.tssrc/services/api/claude.tssrc/tools/AgentTool/
23 最终 APIsrc/services/api/claude.tssrc/utils/messages.tssrc/utils/api.ts

最后用一句话收束

Claude Code 每轮真正发出的不是“用户问题 + 一段大 Prompt”,而是:

经过当前 Agent 身份、上下文编译、能力裁剪、缓存策略和恢复状态共同生成的一份 Messages API 请求快照。

模型返回结构化意图,本地运行时执行并观察,再把结果编译进下一份请求;Agent 就在这个闭环中持续运行。

源码定位

  • 请求状态来源:src/query.ts
  • 消息归一化:src/utils/messages.ts
  • system 与工具投影:src/utils/api.ts
  • 最终参数与 Messages API 调用:src/services/api/claude.ts
  • Plan Mode 附件和五阶段提醒:src/utils/attachments.tssrc/utils/messages.ts