从消息处理到最终请求参数的完整分析
scss
用户输入 (REPL.tsx)
→ onQuery (2855)
→ onQueryImpl (2661)
→ query() (query.ts:219)
一、最终 API 请求体结构
在 claude.ts:1699-1728 的 paramsFromContext() 中构建,通过 anthropic.beta.messages.create({ ...params, stream: true }) 发送。
ts
{
model: string, // 如 "claude-sonnet-4-20250514"
messages: MessageParam[], // 用户/助手对话历史
system: TextBlockParam[], // 系统提示(分块 + cache_control)
tools: ToolUnion[], // 声明所有可用工具
tool_choice?: object, // 工具选择策略
max_tokens: number, // 最大输出 token 数
temperature?: number, // 采样温度
thinking?: object, // 思考配置
betas?: string[], // Beta 功能标头
metadata?: object, // 请求元数据
context_management?: object, // 服务端上下文管理
output_config?: object, // 输出格式控制
speed?: string, // 快速模式
}
二、最终发给 API 的消息格式
ts
messages = [
{ role: "user", content: string | ContentBlockParam[] },
{ role: "assistant", content: ContentBlockParam[] },
{ role: "user", content: ContentBlockParam[] }, // tool_result
...
]
每条消息只有 role 和 content 两个字段(OpenAI 兼容格式)。最后一条消息上附加 cache_control: { type: "ephemeral" }。
三、各字段详解
3.1 tool_choice
控制模型如何选择工具。
取值来源(query.ts:674):
| 取值 | 位置 | 含义 |
|---|---|---|
undefined |
所有常规调用(主对话、压缩、hook、子代理等) | 不发送此字段,模型自主决定 |
{ type: "tool", name: "web_search" } |
仅 WebSearchTool.ts:281(Haiku 子查询) |
强制 Haiku 只能调用 web_search |
{ type: "auto" } 在整个代码库中未使用 。undefined 与 auto 效果等同,显式不发送是为了避免冗余字段。
主对话流中模型能调用工具,是因为 tools 字段传入了完整的工具定义列表,而非 tool_choice 的配置。
3.2 max_tokens
模型单次回复的最大输出 token 数。
计算链(claude.ts:3399-3419 → claude.ts:1591-1594)
scss
getMaxOutputTokensForModel(model)
│
├─ 模型能力表 → { default, upperLimit }
│
├─ Slot cap (tengu_otk_slot_v1) → min(default, 8_000)
│ ↓
├─ CLAUDE_CODE_MAX_OUTPUT_TOKENS → clamp(1 ~ upperLimit)
│ ↓
├─ maxOutputTokensOverride → 覆盖最终值
└─ retryContext.maxTokensOverride → PTL 重试覆盖
模型能力表(src/utils/context.ts:149):
| 模型 | default | upperLimit |
|---|---|---|
| Opus 4.6 | 64,000 | 128,000 |
| Sonnet 4.6 | 32,000 | 128,000 |
| Sonnet 4 / Haiku 4 | 32,000 | 64,000 |
| Claude 3 Opus | 4,096 | 4,096 |
| Claude 3 Sonnet | 8,192 | 8,192 |
Slot cap (tengu_otk_slot_v1):压低默认值到 8K。BQ p99 实际输出仅 4,911 tokens,8K+ 的默认值会过度预留 8-16 倍算力槽。不到 1% 的请求会触发上限。
截断恢复机制(query.ts:1188-1252)
当模型返回 stop_reason === 'max_tokens' 时,分两阶段恢复:
阶段 1 --- 静默升级:如果当前是 8K 封顶且无覆盖,自动以 64K 重发同一次请求,用户无感知。
阶段 2 --- 多轮恢复:如果 64K 也被截断,注入元消息告诉模型"接着继续,别总结"。最多尝试 3 次,超过后向用户展示错误。
3.3 temperature
控制生成随机性(claude.ts:1693-1695)。
ts
const temperature = !hasThinking
? (options.temperatureOverride ?? 1) // 默认 1.0
: undefined // thinking 启用时不发送
- thinking 禁用 :默认 1.0,可被
temperatureOverride覆盖 - thinking 启用:不发送。API 要求 thinking 模式下 temperature 必须为 1,这正是 SDK 默认值
3.4 thinking
控制 Claude 的内部思维链(claude.ts:1599-1630)。
禁用条件:
thinkingConfig.type === 'disabled'- 环境变量
CLAUDE_CODE_DISABLE_THINKING
启用时两种模式:
ts
if (模型支持自适应思考 && 未禁用) {
thinking = { type: "adaptive" }
// 新版:模型自主分配思考预算,无需指定 budget_tokens
} else {
thinking = {
type: "enabled",
budget_tokens: Math.min(maxOutputTokens - 1, thinkingBudget)
// 旧版:固定预算,默认来自 getMaxThinkingTokensForModel(model)
}
}
自适应思考是新模型的默认行为,用户可通过设置关闭。
3.5 betas
启用 API 实验性功能的 beta 标头数组(claude.ts:1539-1557)。
基础 betas :来自 getMergedBetas(options.model),包含模型所需的标头(如 prompt caching)。
运行时动态追加(视功能和条件添加):
| Beta 标头 | 条件 | 说明 |
|---|---|---|
CONTEXT_1M_BETA_HEADER |
Sonnet 1M 实验 | 启用 1M 上下文窗口 |
| 工具搜索标头 | 工具搜索启用 | 允许 tool_reference 块 |
FAST_MODE_BETA_HEADER |
快速模式 | 加速响应 |
AFK_MODE_BETA_HEADER |
自动模式 | 自动执行 |
| 缓存编辑标头 | cached MC 启用 | 服务端缓存编辑 |
| Bedrock betas | Bedrock 提供商 | 平台特定参数 |
粘性闭锁设计 (claude.ts:1405-1410):动态 beta 标头一旦首次发送,就在整个会话中持续发送。因为中途切换 beta 标头会破坏服务端缓存键 ,导致 ~50-70K token 的缓存前缀失效。闭锁在 /clear 和 /compact 时清除。
useBetas 开关 :如果 betas.length === 0(第三方提供商),整个 betas 字段不发送。
3.6 metadata
请求的附加元数据,用于 API 监控和用量统计(claude.ts:503-528)。
ts
{
user_id: JSON.stringify({
device_id: getOrCreateUserID(), // 设备唯一标识
session_id: getSessionId(), // 当前会话 ID
account_uuid: getOauthAccountInfo()?.accountUuid ?? '',
...CLAUDE_CODE_EXTRA_METADATA, // 环境变量注入的额外数据
})
}
所有字段序列化到 user_id 中。CLAUDE_CODE_EXTRA_METADATA 环境变量允许注入自定义 JSON 对象。
3.7 context_management
服务端上下文管理(ant-only)。让 API 服务器在渲染提示词时透明地清理旧内容,无需客户端做摘要压缩。
配置在 claude.ts:1633-1637,实现在 apiMicrocompact.ts:
ts
context_management: {
edits: [
{ type: "clear_thinking_20251015" },
{
type: "clear_tool_uses_20250919",
max_input_tokens: 180_000,
target_input_tokens: 40_000,
},
]
}
需要 beta 标头 context-management-2025-09-19。
两种策略:
clear_thinking_20251015:清除旧 thinking 块。如果clearAllThinking设置了,至少保留 1 轮思考;否则保留所有。clear_tool_uses_20250919:输入超过 180K token 时,自动删除旧工具结果,目标压到 ~40K token。
与服务端压缩(autoCompact)的区别:客户端压缩用 LLM 做摘要 ,走一次 API 调用;服务端上下文管理直接在提示词渲染时丢弃旧 block,零额外成本。
三、完整请求构建过程
scss
query() → queryModel()
│
├─ normalizeMessagesForAPI(messages, tools)
│ ├─ 过滤 progress/system/virtual 消息
│ ├─ 合并连续 user 消息
│ ├─ 拆分 assistant 多 block 消息
│ ├─ 标准化工具输入
│ └─ 限制媒体数量 ≤ 100
│
├─ ensureToolResultPairing() ← 修复 tool_use/tool_result 配对
├─ stripAdvisorBlocks() ← 移除 advisor 内容
├─ stripExcessMediaItems() ← 裁剪超量媒体
│
├─ 构建 system prompt
│ ├─ 添加归因标头 + CLI 前缀
│ ├─ 合并系统提示部分
│ └─ buildSystemPromptBlocks() → 分块 + cache_control
│
├─ 构建 tools 数组
│ ├─ toolSchemas(主要工具定义)
│ └─ extraToolSchemas(advisor 等)
│
├─ paramsFromContext() ← 组装所有参数
│ ├─ model / messages / system / tools
│ ├─ max_tokens / temperature / thinking
│ ├─ tool_choice / betas / metadata
│ ├─ context_management / output_config
│ └─ addCacheBreakpoints() ← 添加缓存断点和 cache_edits
│
└─ anthropic.beta.messages.create({ ...params, stream: true })
针对 messages、system、tools、output_config 这几个重要参数,接下来重点分析: