来源:Claude Code 技术文档 - v2.1.88 源码分析
查询引擎
5.1 QueryEngine 概述
QueryEngine 是 Claude Code 的核心对话引擎,负责管理 AI 对话循环。它是一个有状态的类,持有消息存储、文件缓存和会话配置。
5.2 QueryEngineConfig
type QueryEngineConfig = {
// 环境
cwd: string // 工作目录
tools: Tools // 可用工具集
commands: Command[] // 可用命令列表
mcpClients: MCPServerConnection[] // MCP 连接
agents: AgentDefinition[] // 代理定义
// 权限
canUseTool: CanUseToolFn // 工具使用权限检查
// 状态
getAppState: () => AppState
setAppState: (f: (prev: AppState) => AppState) => void
initialMessages?: Message[] // 初始消息
readFileCache: FileStateCache // 文件读取缓存
// 模型配置
userSpecifiedModel?: string // 用户指定模型
fallbackModel?: string // 后备模型
thinkingConfig?: ThinkingConfig // 思考模式配置
// 预算限制
maxTurns?: number // 最大轮次
maxBudgetUsd?: number // 最大美元预算
taskBudget?: { total: number } // 任务预算
// 输出配置
jsonSchema?: Record<string, unknown> // 结构化输出 Schema
verbose?: boolean // 详细输出
// 高级选项
customSystemPrompt?: string // 自定义系统提示
appendSystemPrompt?: string // 追加系统提示
replayUserMessages?: boolean // 重放用户消息
includePartialMessages?: boolean // 包含部分消息
// 回调
handleElicitation?: ToolUseContext['handleElicitation']
setSDKStatus?: (status: SDKStatus) => void
abortController?: AbortController
orphanedPermission?: OrphanedPermission
// 历史重放
snipReplay?: (
yieldedSystemMsg: Message,
store: Message[]
) => { messages: Message[]; executed: boolean } | undefined
}5.3 核心查询循环
5.3.1 submitMessage() 流程
class QueryEngine {
async *submitMessage(
prompt: string | ContentBlockParam[],
options?: { uuid?: string; isMeta?: boolean }
): AsyncGenerator<SDKMessage, void, unknown>
}详细流程:
submitMessage(prompt)
│
├─ 1. processUserInput(prompt)
│ ├─ 解析斜杠命令 (/command arg)
│ ├─ 检测图片引用 ([Image #N])
│ ├─ 展开粘贴引用 ([Pasted text #N])
│ └─ URL 解析与预处理
│
├─ 2. 系统提示组装
│ └─ fetchSystemPromptParts()
│ ├─ getSimpleIntroSection()
│ ├─ getSimpleSystemSection()
│ ├─ getActionsSection()
│ ├─ getUsingYourToolsSection()
│ ├─ getSessionSpecificGuidanceSection()
│ ├─ getLanguageSection()
│ └─ getOutputStyleSection()
│
├─ 3. wrappedCanUseTool() 权限包装
│ └─ 跟踪权限拒绝,用于诊断
│
├─ 4. 进入 query() 循环
│ │
│ ├─ 构造 API 请求
│ │ ├─ model: 当前模型名
│ │ ├─ messages: 消息历史
│ │ ├─ tools: Zod → API schema 转换
│ │ ├─ system: 系统提示词
│ │ ├─ cache: 1h TTL (3600s)
│ │ ├─ thinking: ThinkingConfig
│ │ └─ effort: Effort 参数
│ │
│ ├─ 处理 API 流式响应
│ │ ├─ message_start → 重置 currentMessageUsage
│ │ ├─ content_block_start → 文本/工具调用/思考
│ │ ├─ content_block_delta → 增量内容
│ │ ├─ message_delta → 累加使用量
│ │ └─ message_stop → 合并到 totalUsage
│ │
│ ├─ 工具调用分发
│ │ ├─ 权限检查 → checkPermissions()
│ │ ├─ 输入验证 → validateInput()
│ │ ├─ 工具执行 → tool.call()
│ │ └─ 结果映射 → mapToolResultToToolResultBlockParam()
│ │
│ └─ yield SDK 消息
│ ├─ assistant 消息
│ ├─ user 消息
│ ├─ progress 进度
│ ├─ attachment 附件
│ └─ stream_event 流事件
│
├─ 5. 预算检查
│ ├─ maxTurns 轮次限制
│ ├─ maxBudgetUsd 美元预算
│ └─ structuredOutputRetries 结构化输出重试
│
├─ 6. 历史录制
│ ├─ recordTranscript() (查询前立即执行,用于 resume)
│ └─ snipReplay() 压缩边界处理
│
└─ 7. Flush 缓冲区
└─ 超出预算时 flush 所有待处理消息5.3.2 ask() 便利函数
async function *ask({
commands, prompt, promptUuid, isMeta, cwd,
tools, mcpClients, verbose, thinkingConfig,
maxTurns, maxBudgetUsd, taskBudget,
canUseTool, mutableMessages, getReadFileCache,
setReadFileCache, customSystemPrompt, appendSystemPrompt,
userSpecifiedModel, fallbackModel, jsonSchema,
getAppState, setAppState, abortController,
replayUserMessages, includePartialMessages,
handleElicitation, agents, setSDKStatus,
orphanedPermission
}): AsyncGenerator<SDKMessage, void, unknown>ask() 是 QueryEngine 的便利包装器,创建 QueryEngine 实例并代理 submitMessage()。
5.4 API 查询层 (claude.ts)
5.4.1 核心查询函数
claude.ts 包含 500+ 行的模型查询逻辑:
async function *query({
model,
messages,
tools,
system,
maxTokens,
thinkingConfig,
effort,
cache,
abortSignal,
// ... 更多参数
}): AsyncGenerator<StreamEvent>5.4.2 缓存策略
// Anthropic API prompt 缓存配置
const cacheConfig = {
ttl: 3600, // 缓存 TTL: 1 小时
// 系统提示词中的动态边界
dynamicBoundary: '__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__'
}缓存稳定性优化:
- 工具列表按名称排序(
assembleToolPool()) - 日期使用
getSessionStartDate()在会话开始时缓存 - 月份使用
getLocalMonthYear()("Month YYYY" 格式)最小化缓存破坏
5.4.3 Zod → API Schema 转换
工具输入的 Zod schema 在发送到 API 之前转换为 JSON Schema 格式:
// 内部转换:
// z.object({ path: z.string() })
// ↓ 转换为 ↓
// { type: 'object', properties: { path: { type: 'string' } } }5.4.4 Thinking 配置
type ThinkingConfig = {
enabled: boolean
budgetTokens?: number
// ... 思考模式参数
}支持 Claude 的 "extended thinking" 模式,允许模型在回答前进行深度推理。
5.5 重试策略 (withRetry.ts)
5.5.1 概述
withRetry.ts 包含 700+ 行的健壮重试逻辑,处理各种 API 错误场景。
5.5.2 重试策略类型
策略
适用场景
行为
前台策略
用户交互模式
短延迟,快速反馈
后台策略
非交互式/后台任务
长延迟,持久重试
快速模式后备
快速模式遇到容量问题
带冷却期的后备
持久重试模式
CLAUDE_CODE_UNATTENDED_RETRY
无限重试直到成功
5.5.3 错误分类
错误分类树
│
├─ 认证错误 (Auth)
│ ├─ API Key 无效
│ ├─ API Key 过期
│ └─ 权限不足
│ → 不重试,立即报告
│
├─ 连接错误 (Connection)
│ ├─ 网络超时
│ ├─ DNS 解析失败
│ └─ 连接拒绝
│ → 指数退避重试
│
├─ 容量错误 (Capacity)
│ ├─ 429 Too Many Requests
│ ├─ 503 Service Unavailable
│ └─ 529 Overloaded
│ → 退避重试 + 快速模式后备
│
└─ 其他错误
├─ 400 Bad Request → 不重试
├─ 500 Internal Error → 有限重试
└─ 未知错误 → 有限重试5.5.4 快速模式后备
当快速模式 (fast mode) 遇到容量限制时,自动降级到标准模式:
快速模式请求
│
├─ 429/503/529 错误?
│ └─ yes → 启动冷却期
│ ├─ 切换到标准模型
│ ├─ 设置冷却计时器
│ └─ 冷却结束后恢复
│
└─ 正常响应 → 继续5.6 Token 预算管理
5.6.1 预算检查
function checkTokenBudget(): BudgetCheckResult {
// 检查条件:
// 1. 90% 阈值 — Token 使用超过限额的 90%
// 2. 递减检测 — 连续 3 次响应 < 500 Token 增量
// (表示模型可能陷入循环)
}5.6.2 递减收益检测
连续 3 次响应中,每次 Token 增量少于 500 Token 时触发:
Turn 1: +2000 tokens ← 正常
Turn 2: +1500 tokens ← 正常
Turn 3: +400 tokens ← 计数器 +1
Turn 4: +300 tokens ← 计数器 +2
Turn 5: +100 tokens ← 计数器 +3 → 触发预算警告5.7 成本跟踪
5.7.1 StoredCostState
type StoredCostState = {
totalCostUSD: number // 总成本(美元)
totalAPIDuration: number // API 总耗时
totalAPIDurationWithoutRetries: number // API 耗时(不含重试)
totalToolDuration: number // 工具执行总耗时
totalLinesAdded: number // 新增代码行数
totalLinesRemoved: number // 删除代码行数
lastDuration: number | undefined // 上次操作耗时
modelUsage: { // 按模型使用量
[modelName: string]: ModelUsage
} | undefined
}5.7.2 成本跟踪流程
API 响应
│
├─ message_start → 重置 currentMessageUsage
├─ message_delta → 累加:
│ ├─ input_tokens
│ ├─ output_tokens
│ └─ cache_read/creation_tokens
│
├─ message_stop → 合并到 totalUsage
│
▼
addToTotalSessionCost(cost, usage, model)
│
├─ 1. addToTotalModelUsage() — 按模型聚合
│ └─ contextWindow + maxOutputTokens
├─ 2. addToTotalCostState() — 更新总成本
├─ 3. getCostCounter().add() — OTel 计量
├─ 4. getTokenCounter().add() — Token 计量(含属性)
└─ 5. 递归处理 advisor 使用 + 遥测记录5.7.3 成本格式化
function formatCost(cost: number): string
// $0.75 → "$0.75"
// $0.003 → "$0.0030"
// $12.50 → "$12.50"
function formatTotalCost(): string
// 输出示例:
// "Total cost: $1.25
// Duration: 45.2s
// Lines: +120 / -30
// Usage by model:
// claude-sonnet-4-20250514: 50K input, 12K output, 30K cache read"
function formatModelUsage(): string
// 按短模型名称聚合显示5.7.4 会话成本持久化
// 恢复上次会话成本(用于 resume)
function restoreCostStateForSession(sessionId: string): boolean
// 保存当前会话成本
function saveCurrentSessionCosts(fpsMetrics?: FpsMetrics): void
// 读取存储的会话成本
function getStoredSessionCosts(sessionId: string): StoredCostState | undefined5.8 历史记录系统
5.8.1 数据结构
type StoredPastedContent = {
id: number
type: 'text' | 'image'
content?: string // 小粘贴 (<1024 字节) 内联
contentHash?: string // 大粘贴的 hash 引用
mediaType?: string
filename?: string
}
type LogEntry = {
display: string
pastedContents: Record<number, StoredPastedContent>
timestamp: number
project: string
sessionId?: string
}5.8.2 历史读写
// 添加历史记录(异步 fire-and-forget)
function addToHistory(command: HistoryEntry | string): void
// → push 到 pendingEntries
// → fire-and-forget flushPromptHistory
// → 注册 cleanup 处理器
// 读取历史(异步生成器 — 懒加载)
async function *getHistory(): AsyncGenerator<HistoryEntry>
// → 最多 100 条
// → 当前会话优先
// → 按显示文本去重5.8.3 大粘贴内容优化
粘贴内容大小检查
│
├─ < 1024 字节
│ └─ 内联存储在 LogEntry.pastedContents
│
└─ >= 1024 字节
├─ 计算 content hash
├─ 异步写入 paste-store 目录
└─ 仅存储 hash 引用5.8.4 引用解析
// 格式化引用
formatPastedTextRef(id: 3, numLines: 50) → "[Pasted text #3 +50 lines]"
formatImageRef(id: 1) → "[Image #1]"
// 解析引用
parseReferences("[Pasted text #3 +50 lines] and [Image #1]")
// → [{ id: 3, match: "...", index: 0 }, { id: 1, match: "...", index: ... }]
// 展开引用为实际内容
expandPastedTextRefs(input, pastedContents)
// → 替换 [Pasted text #N] 为实际文本内容5.9 系统上下文
5.9.1 上下文生成
// Git 状态(memoized)
async function getGitStatus(): Promise<string | null>
// 并行获取: branch, mainBranch, status, log, userName
// 截断: status > 2000 字符时截断
// 跳过: 测试环境或非 Git 仓库
// 系统上下文(memoized)
async function getSystemContext(): Promise<{ [k: string]: string }>
// 返回: { gitStatus?, cacheBreaker? }
// 跳过: CCR 模式 或 gitSettings 禁用
// 用户上下文(memoized)
async function getUserContext(): Promise<{ [k: string]: string }>
// 返回: { claudeMd?, currentDate }
// 跳过: CLAUDE_CODE_DISABLE_CLAUDE_MDS 或 --bare5.9.2 系统提示词组装
async function getSystemPrompt(
tools: Tools,
model: string,
additionalWorkingDirectories?: string[],
mcpClients?: MCPServerConnection[]
): Promise<string[]>
// 分段构成:
// 1. getSimpleIntroSection() — "You are Claude Code..."
// 2. getSimpleSystemSection() — 系统信息
// 3. getSimpleDoingTasksSection() — 任务执行指南
// 4. getActionsSection() — 行为谨慎指南
// 5. getUsingYourToolsSection() — 工具使用指南
// 6. getSimpleToneAndStyleSection() — 风格指南
// 7. getSessionSpecificGuidance() — 会话特定指导
// 8. getLanguageSection() — 语言偏好
// 9. getOutputStyleSection() — 输出样式5.9.3 提示词常量
// 动态边界标记
const SYSTEM_PROMPT_DYNAMIC_BOUNDARY = '__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__'
// 默认前缀
const DEFAULT_PREFIX = "You are Claude Code, Anthropic's official CLI for Claude."
// 代理前缀
const DEFAULT_AGENT_PROMPT = "You are an agent for Claude Code..."
// CLI 前缀集合
type CLISyspromptPrefix =
| "You are Claude Code, Anthropic's official CLI for Claude."
| "You are Claude Code..." // Agent SDK 变体
| "You are a Claude agent, built on Anthropic's Claude Agent SDK."5.9.4 Attribution Header
function getAttributionHeader(fingerprint: string): string
// 返回格式:
// "x-anthropic-billing-header: cc_version=X.Y; cc_entrypoint=Z; ..."
// 用于 API 请求的身份标识和计费归属