Query 查询函数
概述
query() 是 Claude Code 的核心对话循环函数。它负责将用户消息发送到 Claude API、处理流式响应、执行工具调用,并在工具执行后继续对话——直到 Claude 给出最终回复或达到终止条件。
Source:
src/query.ts(1732 行)
函数签名
typescript
// Source: src/query.ts:219-239
export async function* query(
params: QueryParams,
): AsyncGenerator<
| StreamEvent
| RequestStartEvent
| Message
| TombstoneMessage
| ToolUseSummaryMessage,
Terminal
>关键点:
- Generator 函数 — 使用
async function*语法,允许调用者逐步接收流式事件 - 返回
Terminal— Generator 的最终返回值,包含终止原因
QueryParams 类型
typescript
// Source: src/query.ts:181-199
type QueryParams = {
messages: Message[] // 对话历史
systemPrompt: SystemPrompt // 系统提示词
userContext: { [k: string]: string } // 用户上下文 (CLAUDE.md, 日期等)
systemContext: { [k: string]: string } // 系统上下文 (git status 等)
canUseTool: CanUseToolFn // 权限检查回调
toolUseContext: ToolUseContext // 工具执行上下文
fallbackModel?: string // 降级模型
querySource: QuerySource // 查询来源标识
maxTurns?: number // 最大对话轮数
taskBudget?: { total: number } // Token 预算
deps?: QueryDeps // 依赖注入(用于测试)
}核心循环:queryLoop()
Source:
src/query.ts:241-1731
整个查询过程是一个 while (true) 循环,每次迭代代表一个对话轮次。
状态机
typescript
// Source: src/query.ts:268-279
type State = {
messages: Message[]
toolUseContext: ToolUseContext
autoCompactTracking: AutoCompactTrackingState | undefined
maxOutputTokensRecoveryCount: number
hasAttemptedReactiveCompact: boolean
turnCount: number
pendingToolUseSummary: Promise<...> | undefined
transition: Continue | undefined // 记录为什么继续循环
}每次循环末尾通过 state = next; continue 跳回循环顶部。transition.reason 记录了继续循环的原因:
| transition.reason | 含义 |
|---|---|
next_turn | 工具执行完成,继续下一轮对话 |
max_output_tokens_escalate | 提高 token 上限后重试 |
max_output_tokens_recovery | 多轮恢复尝试 |
reactive_compact_retry | 响应式压缩成功后重试 |
collapse_drain_retry | 上下文折叠排空成功后重试 |
stop_hook_blocking | Stop hook 注入了阻塞错误 |
token_budget_continuation | Token 预算允许继续 |
循环流程图
步骤详解
1. 消息压缩
Source:
src/query.ts:365-447
四级压缩策略,按顺序执行:
每级可能减少 token 数量;后续级别检查是否仍超限。
2. 上下文组装
typescript
// Source: src/query.ts:449-451
const fullSystemPrompt = appendSystemContext(systemPrompt, systemContext);appendSystemContext() 将 git status、日期等信息追加到系统提示词。
prependUserContext() 在消息列表前插入用户上下文(CLAUDE.md 内容等)作为 <system-reminder> 标签。
3. API 调用
Source:
src/query.ts:659-708
typescript
deps.callModel({
messages: prependUserContext(messagesForQuery, userContext),
systemPrompt: fullSystemPrompt,
thinkingConfig,
tools,
signal: toolUseContext.abortController.signal,
options: {
model: currentModel,
fastMode: appState.fastMode,
fallbackModel,
querySource,
agents: toolUseContext.options.agentDefinitions.activeAgents,
mcpTools: appState.mcp.tools,
// ... 更多选项
}
})4. 流式处理
Source:
src/query.ts:708-866
typescript
for await (const message of deps.callModel(...)) {
// 处理降级重试
// 回填 tool_use 输入(SDK 兼容性)
// 扣留可恢复错误(413、max_tokens、media 错误)
// yield 正常消息给调用者
// 提取 tool_use 块
// 喂给流式工具执行器
}5. 错误恢复
Prompt Too Long (413):
- 在流式处理中扣留错误(行 813)
- 尝试上下文折叠排空(行 1097-1120)
- 尝试响应式压缩(行 1122-1169)
- 如果都失败,暴露错误并退出
Max Output Tokens:
- 提升上限:8k → 64k(行 1192-1223)
- 多轮恢复:最多 3 次助推消息(行 1226-1255)
- 耗尽后暴露错误
6-7. Hooks 和预算检查
Stop hooks 可以注入阻塞错误,导致循环继续。Token 预算系统检查是否允许更多轮次。
8. 工具执行
Source:
src/query.ts:1366-1412
两种执行路径:
| 方式 | 特点 |
|---|---|
| StreamingToolExecutor | 工具在流式解析时就开始执行,减少总延迟 |
| runTools() | 流式结束后批量执行所有工具 |
typescript
const toolUpdates = streamingToolExecutor
? streamingToolExecutor.getRemainingResults()
: runTools(toolUseBlocks, assistantMessages, canUseTool, toolUseContext);
for await (const update of toolUpdates) {
if (update.message) {
yield update.message;
toolResults.push(...normalizeMessagesForAPI([update.message]));
}
}9. 循环继续
typescript
// Source: src/query.ts:1717-1731
const next: State = {
messages: [...messagesForQuery, ...assistantMessages, ...toolResults],
toolUseContext: updatedToolUseContext,
turnCount: turnCount + 1,
transition: { reason: 'next_turn' },
// ... 重置恢复计数器
};
state = next;
continue; // 跳回 while(true) 顶部依赖注入
Source:
src/query/deps.ts
typescript
export type QueryDeps = {
callModel: typeof queryModelWithStreaming
microcompact: typeof microcompactMessages
autocompact: typeof autoCompactIfNeeded
uuid: () => string
}
export function productionDeps(): QueryDeps {
return {
callModel: queryModelWithStreaming,
microcompact: microcompactMessages,
autocompact: autoCompactIfNeeded,
uuid: randomUUID,
}
}测试时可以替换 callModel 为假 generator,避免实际 API 调用。
终止条件
| Terminal.reason | 含义 |
|---|---|
completed | 正常完成(无工具调用,无错误) |
aborted_streaming | 用户在流式传输时中断 |
aborted_tools | 用户在工具执行时中断 |
prompt_too_long | 恢复失败 |
max_tokens | 达到最大 token 限制 |
hook_stopped | Hook 阻止继续 |
max_turns | 达到显式轮次限制 |
model_error | 未捕获异常 |
下一步
- QueryEngine 编排器 — 更高层的会话管理
- 上下文构建 — 系统上下文的组装细节