first commit
This commit is contained in:
813
claude-code-中文Wiki/02-核心模块详解.md
Normal file
813
claude-code-中文Wiki/02-核心模块详解.md
Normal file
@@ -0,0 +1,813 @@
|
||||
# 核心模块详解
|
||||
|
||||
本文件说明:详细分析 Claude Code 的核心源文件,理解其实现原理。
|
||||
|
||||
## 1. main.tsx - CLI 入口点
|
||||
|
||||
### 1.1 功能概述
|
||||
|
||||
`main.tsx` 是 Claude Code 应用程序的入口点,负责:
|
||||
|
||||
- CLI 参数解析
|
||||
- 应用程序初始化
|
||||
- REPL(Read-Eval-Print Loop)启动
|
||||
- 各种启动前检查和配置
|
||||
|
||||
### 1.2 启动流程
|
||||
|
||||
```typescript
|
||||
// 1. 启动性能分析
|
||||
profileCheckpoint('main_tsx_entry')
|
||||
|
||||
// 2. MDM 设置预读取(并行)
|
||||
startMdmRawRead()
|
||||
|
||||
// 3. Keychain 凭据预读取(并行)
|
||||
startKeychainPrefetch()
|
||||
|
||||
// 4. 初始化遥测
|
||||
initializeTelemetryAfterTrust()
|
||||
|
||||
// 5. 获取引导数据
|
||||
const bootstrapData = await fetchBootstrapData()
|
||||
|
||||
// 6. 启动 REPL
|
||||
launchRepl()
|
||||
```
|
||||
|
||||
### 1.3 关键导入
|
||||
|
||||
```typescript
|
||||
import { Command as CommanderCommand } from '@commander-js/extra-typings'
|
||||
import React from 'react'
|
||||
import chalk from 'chalk'
|
||||
import { getTools } from './tools.js'
|
||||
import { getCommands, filterCommandsForRemoteMode } from './commands.js'
|
||||
import { getSystemContext, getUserContext } from './context.js'
|
||||
```
|
||||
|
||||
### 1.4 核心配置初始化
|
||||
|
||||
```typescript
|
||||
// 权限模式设置
|
||||
const initialPermissionMode = await initialPermissionModeFromCLI()
|
||||
|
||||
// 工具权限上下文初始化
|
||||
initializeToolPermissionContext({
|
||||
mode: initialPermissionMode,
|
||||
additionalWorkingDirectories: new Map(),
|
||||
alwaysAllowRules: {},
|
||||
alwaysDenyRules: {},
|
||||
alwaysAskRules: {},
|
||||
isBypassPermissionsModeAvailable: false,
|
||||
})
|
||||
|
||||
// 获取工具列表
|
||||
const tools = getTools(appState.toolPermissionContext)
|
||||
|
||||
// 获取命令列表
|
||||
const commands = await getCommands(cwd)
|
||||
```
|
||||
|
||||
## 2. QueryEngine.ts - LLM 查询引擎核心
|
||||
|
||||
### 2.1 功能概述
|
||||
|
||||
`QueryEngine` 是 Claude Code 的核心查询处理类,负责:
|
||||
|
||||
- 管理对话生命周期(每个对话一个 QueryEngine 实例)
|
||||
- 处理用户消息提交
|
||||
- 执行 API 调用循环
|
||||
- 处理流式响应
|
||||
- 编排工具执行
|
||||
- 管理会话状态(消息、Token 使用等)
|
||||
|
||||
### 2.2 类结构
|
||||
|
||||
```typescript
|
||||
export class QueryEngine {
|
||||
private config: QueryEngineConfig
|
||||
private mutableMessages: Message[] // 对话消息列表
|
||||
private abortController: AbortController // 中断控制器
|
||||
private permissionDenials: SDKPermissionDenial[] // 权限拒绝记录
|
||||
private totalUsage: NonNullableUsage // 累计 Token 使用
|
||||
private hasHandledOrphanedPermission = false
|
||||
private readFileState: FileStateCache // 文件读取缓存
|
||||
private discoveredSkillNames = new Set<string>()
|
||||
private loadedNestedMemoryPaths = new Set<string>()
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 核心方法:submitMessage()
|
||||
|
||||
这是处理用户输入的主要方法:
|
||||
|
||||
```typescript
|
||||
async *submitMessage(
|
||||
prompt: string | ContentBlockParam[],
|
||||
options?: { uuid?: string; isMeta?: boolean },
|
||||
): AsyncGenerator<SDKMessage, void, unknown>
|
||||
```
|
||||
|
||||
**执行流程**:
|
||||
|
||||
1. **初始化阶段**
|
||||
```typescript
|
||||
this.discoveredSkillNames.clear()
|
||||
setCwd(cwd)
|
||||
|
||||
// 获取系统提示词
|
||||
const { defaultSystemPrompt, userContext, systemContext } =
|
||||
await fetchSystemPromptParts({ tools, mainLoopModel, ... })
|
||||
```
|
||||
|
||||
2. **处理用户输入**
|
||||
```typescript
|
||||
const { messages, shouldQuery, allowedTools, model, resultText } =
|
||||
await processUserInput({
|
||||
input: prompt,
|
||||
mode: 'prompt',
|
||||
context: processUserInputContext,
|
||||
})
|
||||
|
||||
// 更新权限规则
|
||||
setAppState(prev => ({
|
||||
...prev,
|
||||
toolPermissionContext: {
|
||||
...prev.toolPermissionContext,
|
||||
alwaysAllowRules: { command: allowedTools }
|
||||
}
|
||||
}))
|
||||
```
|
||||
|
||||
3. **构建系统提示**
|
||||
```typescript
|
||||
const systemPrompt = asSystemPrompt([
|
||||
...(customPrompt ?? defaultSystemPrompt),
|
||||
...(memoryMechanicsPrompt ?? []), // 记忆机制提示
|
||||
...(appendSystemPrompt ?? [])
|
||||
])
|
||||
```
|
||||
|
||||
4. **查询循环**
|
||||
```typescript
|
||||
for await (const message of query({
|
||||
messages,
|
||||
systemPrompt,
|
||||
userContext,
|
||||
systemContext,
|
||||
canUseTool: wrappedCanUseTool,
|
||||
})) {
|
||||
// 处理各种消息类型
|
||||
switch (message.type) {
|
||||
case 'assistant': /* 记录助手消息 */ break
|
||||
case 'progress': /* 进度更新 */ break
|
||||
case 'user': /* 用户消息 */ break
|
||||
case 'stream_event': /* 流式事件 */ break
|
||||
case 'attachment': /* 附件处理 */ break
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 流式响应处理
|
||||
|
||||
QueryEngine 处理多种流式事件:
|
||||
|
||||
```typescript
|
||||
if (message.type === 'stream_event') {
|
||||
// message_start: 新消息开始
|
||||
if (message.event.type === 'message_start') {
|
||||
currentMessageUsage = EMPTY_USAGE
|
||||
currentMessageUsage = updateUsage(currentMessageUsage, message.event.message.usage)
|
||||
}
|
||||
|
||||
// message_delta: 消息增量更新
|
||||
if (message.event.type === 'message_delta') {
|
||||
currentMessageUsage = updateUsage(currentMessageUsage, message.event.usage)
|
||||
if (message.event.delta.stop_reason != null) {
|
||||
lastStopReason = message.event.delta.stop_reason
|
||||
}
|
||||
}
|
||||
|
||||
// message_stop: 消息结束
|
||||
if (message.event.type === 'message_stop') {
|
||||
this.totalUsage = accumulateUsage(this.totalUsage, currentMessageUsage)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5 思考模式 (Thinking Mode)
|
||||
|
||||
Claude Code 支持 Claude 的思考模式:
|
||||
|
||||
```typescript
|
||||
const initialThinkingConfig: ThinkingConfig = thinkingConfig
|
||||
? thinkingConfig
|
||||
: shouldEnableThinkingByDefault() !== false
|
||||
? { type: 'adaptive' } // 自适应思考
|
||||
: { type: 'disabled' } // 禁用
|
||||
```
|
||||
|
||||
思考规则(注释中的说明):
|
||||
1. 包含思考或编辑块的消息必须在 `max_thinking_length > 0` 的查询中
|
||||
2. 思考块不能是消息块中的最后一个
|
||||
3. 思考块必须在整个助手轨迹中保留
|
||||
|
||||
### 2.6 错误恢复机制
|
||||
|
||||
```typescript
|
||||
// USD 预算超限检查
|
||||
if (maxBudgetUsd !== undefined && getTotalCost() >= maxBudgetUsd) {
|
||||
yield {
|
||||
type: 'result',
|
||||
subtype: 'error_max_budget_usd',
|
||||
...
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 最大轮次检查
|
||||
if (maxTurns && nextTurnCount > maxTurns) {
|
||||
yield { type: 'result', subtype: 'error_max_turns', ... }
|
||||
return
|
||||
}
|
||||
|
||||
// 结构化输出重试限制
|
||||
if (callsThisQuery >= maxRetries) {
|
||||
yield { type: 'result', subtype: 'error_max_structured_output_retries', ... }
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Tool.ts - 工具基类和接口定义
|
||||
|
||||
### 3.1 工具接口定义
|
||||
|
||||
```typescript
|
||||
export type Tool<
|
||||
Input extends AnyObject = AnyObject,
|
||||
Output = unknown,
|
||||
P extends ToolProgressData = ToolProgressData,
|
||||
> = {
|
||||
// 工具名称
|
||||
readonly name: string
|
||||
|
||||
// 可选别名(用于工具重命名后的向后兼容)
|
||||
aliases?: string[]
|
||||
|
||||
// 工具搜索提示词
|
||||
searchHint?: string
|
||||
|
||||
// 核心执行方法
|
||||
call(
|
||||
args: z.infer<Input>,
|
||||
context: ToolUseContext,
|
||||
canUseTool: CanUseToolFn,
|
||||
parentMessage: AssistantMessage,
|
||||
onProgress?: ToolCallProgress<P>,
|
||||
): Promise<ToolResult<Output>>
|
||||
|
||||
// 获取工具描述
|
||||
description(
|
||||
input: z.infer<Input>,
|
||||
options: {...}
|
||||
): Promise<string>
|
||||
|
||||
// 输入 Schema
|
||||
readonly inputSchema: Input
|
||||
|
||||
// 输入 JSON Schema(用于 MCP 工具)
|
||||
readonly inputJSONSchema?: ToolInputJSONSchema
|
||||
|
||||
// 输出 Schema
|
||||
outputSchema?: z.ZodType<unknown>
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 工具权限方法
|
||||
|
||||
```typescript
|
||||
// 验证输入是否有效
|
||||
validateInput?(
|
||||
input: z.infer<Input>,
|
||||
context: ToolUseContext,
|
||||
): Promise<ValidationResult>
|
||||
|
||||
// 检查权限
|
||||
checkPermissions(
|
||||
input: z.infer<Input>,
|
||||
context: ToolUseContext,
|
||||
): Promise<PermissionResult>
|
||||
|
||||
// 准备权限匹配器
|
||||
preparePermissionMatcher?(
|
||||
input: z.infer<Input>,
|
||||
): Promise<(pattern: string) => boolean>
|
||||
```
|
||||
|
||||
### 3.3 工具渲染方法
|
||||
|
||||
```typescript
|
||||
// 渲染工具使用消息
|
||||
renderToolUseMessage(
|
||||
input: Partial<z.infer<Input>>,
|
||||
options: { theme: ThemeName; verbose: boolean; commands?: Command[] }
|
||||
): React.ReactNode
|
||||
|
||||
// 渲染工具结果消息
|
||||
renderToolResultMessage?(
|
||||
content: Output,
|
||||
progressMessagesForMessage: ProgressMessage<P>[],
|
||||
options: {...}
|
||||
): React.ReactNode
|
||||
|
||||
// 渲染工具使用标签
|
||||
renderToolUseTag?(input: Partial<z.infer<Input>>): React.ReactNode
|
||||
|
||||
// 渲染进度消息
|
||||
renderToolUseProgressMessage?(
|
||||
progressMessagesForMessage: ProgressMessage<P>[],
|
||||
options: {...}
|
||||
): React.ReactNode
|
||||
```
|
||||
|
||||
### 3.4 buildTool 工厂函数
|
||||
|
||||
```typescript
|
||||
const TOOL_DEFAULTS = {
|
||||
isEnabled: () => true,
|
||||
isConcurrencySafe: (_input?: unknown) => false,
|
||||
isReadOnly: (_input?: unknown) => false,
|
||||
isDestructive: (_input?: unknown) => false,
|
||||
checkPermissions: async () => ({ behavior: 'allow', updatedInput: input }),
|
||||
toAutoClassifierInput: (_input?: unknown) => '',
|
||||
userFacingName: (_input?: unknown) => '',
|
||||
}
|
||||
|
||||
export function buildTool<D extends AnyToolDef>(def: D): BuiltTool<D> {
|
||||
return {
|
||||
...TOOL_DEFAULTS,
|
||||
userFacingName: () => def.name,
|
||||
...def,
|
||||
} as BuiltTool<D>
|
||||
}
|
||||
```
|
||||
|
||||
### 3.5 工具权限上下文
|
||||
|
||||
```typescript
|
||||
export type ToolPermissionContext = {
|
||||
mode: PermissionMode // 'default' | 'auto' | 'bypass' | 'plan'
|
||||
additionalWorkingDirectories: Map<string, AdditionalWorkingDirectory>
|
||||
alwaysAllowRules: ToolPermissionRulesBySource
|
||||
alwaysDenyRules: ToolPermissionRulesBySource
|
||||
alwaysAskRules: ToolPermissionRulesBySource
|
||||
isBypassPermissionsModeAvailable: boolean
|
||||
isAutoModeAvailable?: boolean
|
||||
strippedDangerousRules?: ToolPermissionRulesBySource
|
||||
shouldAvoidPermissionPrompts?: boolean
|
||||
awaitAutomatedChecksBeforeDialog?: boolean
|
||||
prePlanMode?: PermissionMode
|
||||
}
|
||||
```
|
||||
|
||||
## 4. commands.ts - 命令注册中心
|
||||
|
||||
### 4.1 功能概述
|
||||
|
||||
`commands.ts` 是 Claude Code 的命令注册中心,管理所有 slash commands(以 `/` 开头的命令)。
|
||||
|
||||
### 4.2 命令类型
|
||||
|
||||
```typescript
|
||||
type CommandType =
|
||||
| 'prompt' // 展开为提示发送给模型
|
||||
| 'local' // 本地执行,返回文本
|
||||
| 'local-jsx' // 本地执行,渲染 Ink UI
|
||||
```
|
||||
|
||||
### 4.3 命令定义结构
|
||||
|
||||
```typescript
|
||||
interface Command {
|
||||
type: CommandType
|
||||
name: string
|
||||
aliases?: string[]
|
||||
description: string
|
||||
contentLength: number
|
||||
source: 'builtin' | 'plugin' | 'bundled' | 'mcp' | ...
|
||||
availability?: ('claude-ai' | 'console')[]
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 命令加载流程
|
||||
|
||||
```typescript
|
||||
// 1. 获取技能目录命令
|
||||
const skillDirCommands = await getSkillDirCommands(cwd)
|
||||
|
||||
// 2. 获取插件命令
|
||||
const pluginCommands = await getPluginCommands()
|
||||
|
||||
// 3. 获取内置命令
|
||||
const builtinCommands = COMMANDS()
|
||||
|
||||
// 4. 合并并过滤
|
||||
const allCommands = [
|
||||
...bundledSkills,
|
||||
...builtinPluginSkills,
|
||||
...skillDirCommands,
|
||||
...workflowCommands,
|
||||
...pluginCommands,
|
||||
...pluginSkills,
|
||||
...COMMANDS(),
|
||||
]
|
||||
|
||||
// 5. 检查可用性要求
|
||||
const filteredCommands = allCommands.filter(cmd =>
|
||||
meetsAvailabilityRequirement(cmd)
|
||||
)
|
||||
|
||||
// 6. 检查是否启用
|
||||
.filter(cmd => isCommandEnabled(cmd))
|
||||
```
|
||||
|
||||
### 4.5 远程安全命令
|
||||
|
||||
```typescript
|
||||
export const REMOTE_SAFE_COMMANDS: Set<Command> = new Set([
|
||||
session, exit, clear, help, theme, color, vim,
|
||||
cost, usage, copy, btw, feedback, plan,
|
||||
keybindings, statusline, stickers, mobile,
|
||||
])
|
||||
```
|
||||
|
||||
### 4.6 命令查找
|
||||
|
||||
```typescript
|
||||
export function findCommand(
|
||||
commandName: string,
|
||||
commands: Command[],
|
||||
): Command | undefined {
|
||||
return commands.find(
|
||||
_ =>
|
||||
_.name === commandName ||
|
||||
getCommandName(_) === commandName ||
|
||||
_.aliases?.includes(commandName)
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## 5. context.ts - 系统/用户上下文收集
|
||||
|
||||
### 5.1 功能概述
|
||||
|
||||
`context.ts` 负责收集系统上下文和用户上下文,这些上下文会被预置到每个对话的开头。
|
||||
|
||||
### 5.2 Git 状态收集
|
||||
|
||||
```typescript
|
||||
export const getGitStatus = memoize(async (): Promise<string | null> => {
|
||||
// 检查是否是 Git 仓库
|
||||
const isGit = await getIsGit()
|
||||
if (!isGit) return null
|
||||
|
||||
// 并行获取多个 Git 信息
|
||||
const [branch, mainBranch, status, log, userName] = await Promise.all([
|
||||
getBranch(),
|
||||
getDefaultBranch(),
|
||||
execFileNoThrow(gitExe(), ['status', '--short'], ...),
|
||||
execFileNoThrow(gitExe(), ['log', '--oneline', '-n', '5'], ...),
|
||||
execFileNoThrow(gitExe(), ['config', 'user.name'], ...),
|
||||
])
|
||||
|
||||
return [
|
||||
`Current branch: ${branch}`,
|
||||
`Main branch: ${mainBranch}`,
|
||||
`Git user: ${userName}`,
|
||||
`Status:\n${truncatedStatus}`,
|
||||
`Recent commits:\n${log}`,
|
||||
].join('\n\n')
|
||||
})
|
||||
```
|
||||
|
||||
### 5.3 系统上下文
|
||||
|
||||
```typescript
|
||||
export const getSystemContext = memoize(async () => {
|
||||
const gitStatus = await getGitStatus()
|
||||
|
||||
// 缓存破坏注入(仅用于调试)
|
||||
const injection = feature('BREAK_CACHE_COMMAND')
|
||||
? getSystemPromptInjection()
|
||||
: null
|
||||
|
||||
return {
|
||||
...(gitStatus && { gitStatus }),
|
||||
...(injection && { cacheBreaker: `[CACHE_BREAKER: ${injection}]` }),
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 5.4 用户上下文
|
||||
|
||||
```typescript
|
||||
export const getUserContext = memoize(async () => {
|
||||
// 获取 CLAUDE.md 文件内容
|
||||
const shouldDisableClaudeMd = isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_CLAUDE_MDS)
|
||||
|| (isBareMode() && getAdditionalDirectoriesForClaudeMd().length === 0)
|
||||
|
||||
const claudeMd = shouldDisableClaudeMd
|
||||
? null
|
||||
: getClaudeMds(filterInjectedMemoryFiles(await getMemoryFiles()))
|
||||
|
||||
// 缓存 CLAUDE.md 内容供自动模式分类器使用
|
||||
setCachedClaudeMdContent(claudeMd || null)
|
||||
|
||||
return {
|
||||
...(claudeMd && { claudeMd }),
|
||||
currentDate: `Today's date is ${getLocalISODate()}.`,
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### 5.5 上下文缓存
|
||||
|
||||
所有上下文函数都使用 `memoize` 进行缓存,确保在一次对话中只计算一次:
|
||||
|
||||
```typescript
|
||||
export const getSystemContext = memoize(async () => {...})
|
||||
export const getUserContext = memoize(async () => {...})
|
||||
export const getGitStatus = memoize(async () => {...})
|
||||
```
|
||||
|
||||
## 6. cost-tracker.ts - Token 成本追踪
|
||||
|
||||
### 6.1 功能概述
|
||||
|
||||
`cost-tracker.ts` 负责追踪和计算 API 调用的 Token 使用量和成本。
|
||||
|
||||
### 6.2 核心数据结构
|
||||
|
||||
```typescript
|
||||
type StoredCostState = {
|
||||
totalCostUSD: number
|
||||
totalAPIDuration: number
|
||||
totalAPIDurationWithoutRetries: number
|
||||
totalToolDuration: number
|
||||
totalLinesAdded: number
|
||||
totalLinesRemoved: number
|
||||
lastDuration: number | undefined
|
||||
modelUsage: { [modelName: string]: ModelUsage }
|
||||
}
|
||||
|
||||
type ModelUsage = {
|
||||
inputTokens: number
|
||||
outputTokens: number
|
||||
cacheReadInputTokens: number
|
||||
cacheCreationInputTokens: number
|
||||
webSearchRequests: number
|
||||
costUSD: number
|
||||
contextWindow: number
|
||||
maxOutputTokens: number
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 成本计算
|
||||
|
||||
```typescript
|
||||
export function addToTotalSessionCost(
|
||||
cost: number,
|
||||
usage: Usage,
|
||||
model: string,
|
||||
): number {
|
||||
const modelUsage = addToTotalModelUsage(cost, usage, model)
|
||||
addToTotalCostState(cost, modelUsage, model)
|
||||
|
||||
// 发送到 Statsig 计数器
|
||||
getCostCounter()?.add(cost, { model })
|
||||
getTokenCounter()?.add(usage.input_tokens, { ...attrs, type: 'input' })
|
||||
getTokenCounter()?.add(usage.output_tokens, { ...attrs, type: 'output' })
|
||||
|
||||
// 处理 Advisor 使用量
|
||||
for (const advisorUsage of getAdvisorUsage(usage)) {
|
||||
const advisorCost = calculateUSDCost(advisorUsage.model, advisorUsage)
|
||||
totalCost += addToTotalSessionCost(advisorCost, advisorUsage, advisorUsage.model)
|
||||
}
|
||||
return totalCost
|
||||
}
|
||||
```
|
||||
|
||||
### 6.4 成本格式化
|
||||
|
||||
```typescript
|
||||
export function formatTotalCost(): string {
|
||||
const costDisplay = formatCost(getTotalCostUSD())
|
||||
|
||||
return chalk.dim(`
|
||||
Total cost: ${costDisplay}
|
||||
Total duration (API): ${formatDuration(getTotalAPIDuration())}
|
||||
Total duration (wall): ${formatDuration(getTotalDuration())}
|
||||
Total code changes: ${linesAdded} lines added, ${linesRemoved} lines removed
|
||||
${formatModelUsage()}
|
||||
`)
|
||||
}
|
||||
```
|
||||
|
||||
### 6.5 会话成本保存/恢复
|
||||
|
||||
```typescript
|
||||
// 保存当前会话成本到项目配置
|
||||
export function saveCurrentSessionCosts(fpsMetrics?: FpsMetrics): void {
|
||||
saveCurrentProjectConfig(current => ({
|
||||
...current,
|
||||
lastCost: getTotalCostUSD(),
|
||||
lastAPIDuration: getTotalAPIDuration(),
|
||||
lastLinesAdded: getTotalLinesAdded(),
|
||||
lastLinesRemoved: getTotalLinesRemoved(),
|
||||
lastModelUsage: Object.fromEntries(...),
|
||||
lastSessionId: getSessionId(),
|
||||
}))
|
||||
}
|
||||
|
||||
// 恢复会话成本
|
||||
export function restoreCostStateForSession(sessionId: string): boolean {
|
||||
const data = getStoredSessionCosts(sessionId)
|
||||
if (!data) return false
|
||||
setCostStateForRestore(data)
|
||||
return true
|
||||
}
|
||||
```
|
||||
|
||||
## 7. query.ts - 查询管道
|
||||
|
||||
### 7.1 功能概述
|
||||
|
||||
`query.ts` 是核心的查询处理管道,负责:
|
||||
|
||||
- 消息流式 API 调用
|
||||
- 工具调用编排
|
||||
- 自动压缩
|
||||
- 错误恢复
|
||||
|
||||
### 7.2 主查询循环
|
||||
|
||||
```typescript
|
||||
export async function* query(
|
||||
params: QueryParams,
|
||||
): AsyncGenerator<...> {
|
||||
const consumedCommandUuids: string[] = []
|
||||
const terminal = yield* queryLoop(params, consumedCommandUuids)
|
||||
return terminal
|
||||
}
|
||||
|
||||
async function* queryLoop(
|
||||
params: QueryParams,
|
||||
consumedCommandUuids: string[],
|
||||
): AsyncGenerator<...> {
|
||||
let state: State = {
|
||||
messages: params.messages,
|
||||
toolUseContext: params.toolUseContext,
|
||||
autoCompactTracking: undefined,
|
||||
maxOutputTokensRecoveryCount: 0,
|
||||
// ...
|
||||
}
|
||||
|
||||
while (true) {
|
||||
// 1. 上下文压缩
|
||||
const { compactionResult } = await deps.autocompact(...)
|
||||
|
||||
// 2. API 调用循环
|
||||
for await (const message of deps.callModel({...})) {
|
||||
if (message.type === 'assistant') {
|
||||
assistantMessages.push(message)
|
||||
// 处理工具调用块
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 工具执行
|
||||
if (needsFollowUp) {
|
||||
const toolUpdates = runTools(toolUseBlocks, ...)
|
||||
for await (const update of toolUpdates) {
|
||||
// 处理工具结果
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 递归继续
|
||||
state = { ...state, messages: [...messages, ...assistantMessages, ...toolResults] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7.3 工具执行编排
|
||||
|
||||
```typescript
|
||||
// 两种工具执行模式
|
||||
if (streamingToolExecutor) {
|
||||
// 流式工具执行:在模型流式输出的同时执行工具
|
||||
logEvent('tengu_streaming_tool_execution_used', {...})
|
||||
} else {
|
||||
// 批量工具执行:等待模型完全响应后执行
|
||||
logEvent('tengu_streaming_tool_execution_not_used', {...})
|
||||
}
|
||||
|
||||
const toolUpdates = streamingToolExecutor
|
||||
? streamingToolExecutor.getRemainingResults()
|
||||
: runTools(toolUseBlocks, assistantMessages, canUseTool, toolUseContext)
|
||||
```
|
||||
|
||||
### 7.4 错误恢复机制
|
||||
|
||||
```typescript
|
||||
// 1. 模型回退
|
||||
if (innerError instanceof FallbackTriggeredError && fallbackModel) {
|
||||
currentModel = fallbackModel
|
||||
attemptWithFallback = true
|
||||
continue
|
||||
}
|
||||
|
||||
// 2. Prompt Too Long 恢复
|
||||
if (isWithheld413) {
|
||||
// 尝试排空上下文折叠
|
||||
const drained = contextCollapse.recoverFromOverflow(...)
|
||||
if (drained.committed > 0) {
|
||||
state = { ...state, messages: drained.messages }
|
||||
continue
|
||||
}
|
||||
|
||||
// 尝试响应式压缩
|
||||
const compacted = await reactiveCompact.tryReactiveCompact(...)
|
||||
if (compacted) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Max Output Tokens 恢复
|
||||
if (maxOutputTokensRecoveryCount < MAX_OUTPUT_TOKENS_RECOVERY_LIMIT) {
|
||||
const recoveryMessage = createUserMessage({
|
||||
content: `Output token limit hit. Resume directly...`
|
||||
})
|
||||
state = { ...state, messages: [...messages, recoveryMessage] }
|
||||
continue
|
||||
}
|
||||
```
|
||||
|
||||
### 7.5 上下文压缩
|
||||
|
||||
```typescript
|
||||
// Snip(去除)压缩
|
||||
if (feature('HISTORY_SNIP')) {
|
||||
const snipResult = snipModule!.snipCompactIfNeeded(messagesForQuery)
|
||||
messagesForQuery = snipResult.messages
|
||||
snipTokensFreed = snipResult.tokensFreed
|
||||
}
|
||||
|
||||
// 微压缩
|
||||
const microcompactResult = await deps.microcompact(messagesForQuery, ...)
|
||||
messagesForQuery = microcompactResult.messages
|
||||
|
||||
// 自动压缩
|
||||
const { compactionResult } = await deps.autocompact(
|
||||
messagesForQuery,
|
||||
toolUseContext,
|
||||
{ systemPrompt, userContext, systemContext, toolUseContext },
|
||||
querySource,
|
||||
tracking,
|
||||
snipTokensFreed,
|
||||
)
|
||||
```
|
||||
|
||||
### 7.6 工具使用摘要
|
||||
|
||||
```typescript
|
||||
// 为耗时的 Haiku 调用生成工具使用摘要
|
||||
if (config.gates.emitToolUseSummaries && toolUseBlocks.length > 0) {
|
||||
const summary = await generateToolUseSummary({
|
||||
tools: toolInfoForSummary,
|
||||
signal: toolUseContext.abortController.signal,
|
||||
lastAssistantText,
|
||||
})
|
||||
|
||||
// 异步生成,不阻塞下一个 API 调用
|
||||
nextPendingToolUseSummary = summary.then(s =>
|
||||
s ? createToolUseSummaryMessage(summary, toolUseIds) : null
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## 8. 总结
|
||||
|
||||
这七个核心文件共同构成了 Claude Code 的核心架构:
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `main.tsx` | CLI 入口,应用程序初始化 |
|
||||
| `QueryEngine.ts` | 对话生命周期管理,消息处理 |
|
||||
| `Tool.ts` | 工具基类和接口定义 |
|
||||
| `commands.ts` | Slash 命令注册和管理 |
|
||||
| `context.ts` | 系统/用户上下文收集 |
|
||||
| `cost-tracker.ts` | Token 成本追踪 |
|
||||
| `query.ts` | API 调用管道,工具编排 |
|
||||
|
||||
理解这些核心模块是深入研究 Claude Code 源码的基础。
|
||||
Reference in New Issue
Block a user