Skip to content

来源: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 | undefined

5.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 或 --bare

5.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 请求的身份标识和计费归属

MIT