Files
aiagent/clode/dataflow.md

527 lines
18 KiB
Markdown
Raw Normal View History

# Claude Code 数据流与生命周期
## 1. 启动流程
### 1.1 完整启动序列
```
终端执行: bun run dev (→ bun run src/entrypoints/cli.tsx)
├── Phase 0: 预加载
│ ├── globalThis.MACRO 注入 (版本号、构建信息)
│ ├── process.env 初始化 (COREPACK_ENABLE_AUTO_PIN, NODE_OPTIONS)
│ └── Ablation baseline 环境变量设置
├── Phase 1: 快速路径路由
│ ├── --version → console.log(MACRO.VERSION) → 退出
│ ├── --dump-system-prompt → 渲染系统提示词 → 退出
│ ├── --claude-in-chrome-mcp → 启动 Chrome MCP Server
│ ├── --chrome-native-host → 启动 Chrome Native Host
│ └── --computer-use-mcp → 启动 Computer Use MCP
├── Phase 2: CLI 初始化 (cli.tsx main())
│ ├── import startupProfiler → profileCheckpoint('cli_entry')
│ ├── import commander → 解析命令行参数
│ ├── import enableConfigs() → 加载用户配置
│ ├── 注册所有 Command (commands.ts, 60+)
│ ├── 挂载 hook 系统
│ └── commander.parse() → 路由到对应命令
├── Phase 3: REPL 启动 (默认模式)
│ ├── import main.tsx → 启动 Ink 渲染器
│ │ ├── 注册 bun:bundle polyfill (entry.ts)
│ │ └── import App.tsx
│ │
│ ├── <App> 组件挂载
│ │ ├── 检查环境 (Bun版本/终端能力)
│ │ ├── 加载认证状态 (auth.ts)
│ │ ├── 初始化 AppStore (createStore)
│ │ ├── 运行 preflightChecks (网络可达性)
│ │ └── 加载 MCP 服务端配置
│ │
│ └── 进入 REPL 主循环
│ ├── 加载历史会话 (sessionRestore.ts)
│ ├── 显示欢迎信息和系统提示
│ └── 等待用户输入
└── Phase 4: UI 就绪
├── VirtualMessageList 渲染历史消息
├── StatusLine 显示模型/成本/快捷键
└── TextInput 获得焦点,等待输入
```
### 1.2 启动优化策略
- **延迟加载**: `cli.tsx` 中的 `await import()` 确保快速路径不加载不必要模块
- **Feature Flag DCE**: `feature('PROACTIVE')` 在构建时消除死代码
- **Profile Checkpoint**: `startupProfiler.ts` 记录各阶段耗时
- **预连接**: `apiPreconnect.ts` 在启动时预建立 HTTPS 连接
---
## 2. 对话循环(核心数据流)
### 2.1 主循环
```
用户输入 "帮我重构这个函数"
├── Step 1: 输入处理 (handlePromptSubmit.ts)
│ ├── 解析 @file / @agent 等提及
│ ├── 处理剪贴板图片粘贴
│ ├── 处理引用消息 (↑ 历史选取)
│ └── 构建 UserMessage { role: 'user', content: [...] }
├── Step 2: 预处理 Hooks (hooks.ts, 164KB)
│ ├── UserPromptSubmit hook: 修改/拦截用户输入
│ ├── 注入 CLAUDE.md 系统上下文
│ ├── 加载 MEMORY.md 记忆内容
│ └── 加载 Skills 和 MCP 指令
├── Step 3: 构建 Query (QueryEngine.ts)
│ ├── 构建 System Prompt:
│ │ ├── 基础系统提示词 (constants/prompts.ts, 55KB)
│ │ ├── 环境信息 (OS/Shell/Git/日期)
│ │ ├── 工具清单 (所有可用工具的 name + description + schema)
│ │ ├── CLAUDE.md 内容 (项目级 + 用户级)
│ │ ├── MEMORY.md 内容 (自动记忆)
│ │ └── MCP 服务端提供的上下文
│ │
│ ├── 构建 Messages:
│ │ ├── 历史消息 (截断至 Token 预算)
│ │ ├── 当前 UserMessage
│ │ └── Tool Results (若有待回传)
│ │
│ └── 选择模型: getMainLoopModel() → Claude Opus/Sonnet/Haiku
├── Step 4: API 调用
│ ├── POST https://api.anthropic.com/v1/messages
│ ├── Headers: x-api-key, anthropic-version, ...
│ ├── Body: { model, system, messages, tools, max_tokens, ... }
│ ├── Stream: true (SSE 流式响应)
│ │
│ └── 处理响应流 (server-sent events)
│ ├── message_start → 新消息开始
│ ├── content_block_start → 文本块/工具调用块开始
│ ├── content_block_delta → 增量内容 (text_delta / input_json_delta)
│ ├── content_block_stop → 块结束
│ ├── message_delta → 用量/结束原因
│ └── message_stop → 消息完成
├── Step 5: 响应处理
│ ├── 解析 content_block 类型:
│ │ ├── text → 实时渲染 Markdown (打字机效果)
│ │ ├── tool_use → 进入工具调用流程 (见 Step 6)
│ │ └── thinking → 渲染扩展思考 (可选展示)
│ │
│ └── 更新会话状态:
│ ├── 追加 AssistantMessage 到消息列表
│ ├── 更新 Token 使用量 / 成本
│ └── 持久化到 sessionStorage
├── Step 6: 工具调用循环 (Tool Use Loop)
│ ├── 解析 tool_use 参数 (JSON → Zod 校验)
│ ├── 权限检查 (useCanUseTool)
│ │ ├── 需要用户确认 → 弹出权限 UI → 等待用户决策
│ │ │ ├── 批准 → 继续
│ │ │ └── 拒绝 → 返回拒绝消息
│ │ └── 自动批准 → 继续
│ │
│ ├── 执行工具:
│ │ ├── 触发 PreToolUse hooks
│ │ ├── tool.toolUseOutput(params)
│ │ │ ├── BashTool: spawn 子进程 + 流式输出
│ │ │ ├── FileEditTool: 读取文件 + 字符串替换 + 写回
│ │ │ ├── AgentTool: fork 子 Agent + 等待完成
│ │ │ └── WebFetchTool: HTTP GET + HTML→Markdown + AI 提取
│ │ └── 触发 PostToolUse hooks
│ │
│ ├── 构建 ToolResult:
│ │ { role: 'user', content: [{ type: 'tool_result', tool_use_id, content }] }
│ │
│ ├── 回传模型 (回到 Step 4)
│ │ 模型收到工具结果后继续生成回复或调用更多工具
│ │
│ └── 循环终止条件:
│ ├── 模型仅返回 text (不再调用工具)
│ ├── 达到最大工具轮次
│ ├── 用户手动中断 (Ctrl+C)
│ └── Token 预算用尽 (触发自动压缩)
├── Step 7: 响应完成
│ ├── 触发 Stop hooks
│ ├── 自动内存提取 (extractMemories)
│ ├── 文件历史快照 (fileHistoryMakeSnapshot)
│ ├── 更新会话标题 (sessionTitle.ts)
│ └── 统计记录 (成本/时间/工具调用次数)
└── 回到 Step 1: 等待下一条用户输入
```
### 2.2 流式渲染管道
```
API SSE Stream
Stream Processor (query.ts)
│ 解析 SSE events, 拼接 JSON fragments
Message Builder (messages.ts)
│ 构建 Message 对象树
React State Update (useReplBridge.tsx)
│ setState(newMessage) → React re-render
Ink Reconciler (ink/reconciler.ts)
│ Virtual DOM diff
Node → ANSI Renderer (ink/render-node-to-output.ts)
│ React 元素 → ANSI 字符串
Screen Buffer (ink/screen.ts)
│ 计算 diff, 仅输出变化部分
Terminal Output (ink/output.ts)
│ process.stdout.write(ansiString)
用户终端
```
---
## 3. Task 生命周期
### 3.1 主会话任务
```
用户连接 REPL
├── 创建 LocalMainSessionTask
│ ├── status: 'pending'
│ ├── 开始首次对话 → status: 'running'
│ │ ├── 每次对话循环 (QueryEngine)
│ │ └── 工具执行 (Bash/Agent/...)
│ │
│ └── 结束条件:
│ ├── 用户退出 (Ctrl+C / /exit) → status: 'completed'
│ ├── 崩溃/异常 → status: 'failed'
│ └── 用户中断 (Ctrl+C during run) → status: 'cancelled'
└── 会话持久化 (sessionStorage.ts)
└── ~/.claude/projects/<repo>/<session-id>.jsonl
```
### 3.2 子 Agent 任务
```
AgentTool.toolUseOutput()
├── 创建 LocalAgentTask 或 RemoteAgentTask
│ ├── status: 'pending'
│ ├── 分配工具子集 (根据 subagent_type)
│ │ ├── general-purpose → 全部工具
│ │ ├── explore → Glob + Grep + Read (只读)
│ │ └── plan → 仅计划工具
│ │
│ ├── 构建独立系统提示词 (Agent 角色描述)
│ │
│ └── 开始执行:
│ ├── status: 'running'
│ ├── 独立对话循环 (独立的 QueryEngine)
│ │ ├── 上下文隔离 (不共享主会话消息)
│ │ └── 消耗独立 Token 预算
│ │
│ ├── run_in_background: true
│ │ ├── 后台执行,不阻塞
│ │ └── 完成时通过 notification 通知
│ │
│ └── run_in_background: false (默认)
│ ├── 前台阻塞等待
│ └── 完成时返回结果给调用者
└── 结果归并:
├── 成功 → 提取关键产出 + 清理临时上下文
└── 失败 → 错误信息 + 部分产出
```
### 3.3 Shell 后台任务
```
BashTool 长时间命令
├── 创建 LocalShellTask
│ ├── 后台执行: spawn(command, { detached: true })
│ ├── 实时输出: stdout/stderr → stream
│ │ └── UI 实时显示在 TaskListV2 面板
│ │
│ └── 完成: exitCode + 完整输出
└── 生命周期管理:
├── 用户可随时取消: task.kill()
├── 超时自动终止: setTimeout(kill, timeout)
└── 退出清理: gracefulShutdown 等待所有任务完成
```
---
## 4. 会话持久化与恢复
### 4.1 会话存储结构
```
~/.claude/
├── projects/
│ └── <project_hash>/
│ ├── <session_id>.jsonl # 完整对话记录 (JSONL)
│ ├── <session_id>.meta.json # 会话元数据
│ └── memory/
│ └── MEMORY.md # 自动记忆索引
├── settings.json # 用户全局设置
├── credentials.json # API Key 等凭证 (加密)
└── stats/
└── usage.json # 用量统计
```
### 4.2 会话恢复流程
```
claude --resume (或 /resume 命令)
├── listSessionsImpl() 列出所有历史会话
│ └── 解析 <session_id>.meta.json → 标题/时间/模型
├── ResumeConversation 界面
│ └── 用户选择要恢复的会话
├── sessionRestore.ts 加载会话
│ ├── 读取 .jsonl 文件
│ ├── 解析所有 Message 对象
│ ├── 重建 AppState (消息列表/任务状态)
│ └── 恢复上下文 (模型/配置/权限模式)
└── 继续对话 (从上次中断处)
```
### 4.3 会话导出
```
ExportDialog
├── Markdown: 渲染对话为 .md 文件
├── JSON: 原始 JSON 格式
├── HTML: 静态 HTML 页面 (含样式)
└── PNG: 终端截图 (通过 ansiToPng, 215KB)
```
---
## 5. Bridge 模式数据流
### 5.1 Bridge 连接流程
```
本地 Claude Code (Client) 远程环境 (Server)
│ │
├── claude remote-control │
│ (启动 Bridge 监听) │
│ │
├── WebSocket/TCP 连接 ────────────────►│
│ ├── 鉴权 (JWT/TrustedDevice)
│ ├── 环境信息交换
│ ◄── Handshake OK ────────────────│ ├── 创建工作目录
│ │
├── 会话同步 (replBridge.ts, 102KB) │
│ ├── 发送 AppState 快照 ───────────►│ ├── 应用状态到远程
│ ├── 发送用户消息 ──────────────────►│ ├── 执行查询
│ │ │ ├── 工具调用在远程执行
│ │ ◄── 流式响应 ────────────────│ ├── 文件操作在远程执行
│ │ │
│ ◄── 状态同步 ────────────────────│ ├── 远程文件变化回传
│ ◄── 进度通知 ────────────────────│ │
│ │
└── 双向同步维持 │
├── 心跳 (keep-alive) │
├── 断线重连 (replBridgeTransport) │
└── 会话恢复 (sessionRunner) │
```
### 5.2 Bridge 状态机
```
DISCONNECTED
│ (用户触发连接)
CONNECTING
│ (WebSocket 连接建立)
├── 成功 → HANDSHAKE
└── 失败 → DISCONNECTED (显示错误, 可重试)
HANDSHAKE
│ (JWT 验证 + 设备信任)
├── 成功 → CONNECTED
└── 失败 → DISCONNECTED (认证错误)
CONNECTED
│ (会话同步初始化)
SYNCED
│ (正常双向通信)
├── 断线 → RECONNECTING
└── 用户断开 → DISCONNECTED
RECONNECTING
├── 重连成功 → SYNCED
└── 超时 → DISCONNECTED
```
---
## 6. Memory 系统数据流
### 6.1 记忆生命周期
```
会话启动
├── memdir.loadMemoryPrompt()
│ ├── 扫描 ~/.claude/projects/<project>/memory/
│ ├── 读取 MEMORY.md 索引
│ ├── 读取每个 .md 记忆文件
│ │ ├── 解析 frontmatter (name/description/type)
│ │ └── 提取正文内容
│ │
│ └── findRelevantMemories()
│ ├── 基于当前会话上下文匹配相关记忆
│ └── 按新鲜度排序 (memoryAge)
├── 拼入 System Prompt
│ └── "# Memory\n... (相关记忆内容) ..."
└── 会话中
├── 模型使用 Write 工具 → 写入新的 .md 文件
│ └── 更新 MEMORY.md 索引
├── extractMemories 服务 → 自动从对话中提取
│ └── 后台异步写入
└── 会话结束
└── 记忆文件保持不变 (跨会话持久化)
```
### 6.2 四种记忆类型
```markdown
---
name: <名称>
description: <一行描述>
type: user | feedback | project | reference
---
<内容>
```
| 类型 | 用途 | 示例 |
|---|---|---|
| **user** | 用户角色/偏好/知识 | "用户是资深 Go 开发者" |
| **feedback** | 用户反馈的行为规范 | "不要 mock 数据库测试" |
| **project** | 项目上下文/目标 | "下周四冻结合并" |
| **reference** | 外部系统指针 | "Bug 在 Linear INGEST 项目里" |
---
## 7. Hook 系统
### 7.1 可用 Hook 事件
```typescript
interface Hooks {
// 事件触发型
UserPromptSubmit: (messages) => messages // 用户提交前
PreToolUse: (toolName, params) => params // 工具执行前
PostToolUse: (toolName, result) => void // 工具执行后
Stop: (session) => void // 对话完成
// 通知型
Notification: (event) => void // 各类事件通知
SessionStart: (info) => void // 会话启动
// 配置型 (settings.json)
// "hooks": { "PreToolUse": [{ "matcher": "Bash", "command": "..." }] }
}
```
### 7.2 Hook 执行流程
```
事件触发
├── 加载 hooks 配置 (hooks.ts)
│ ├── settings.json 中的 hooks 数组
│ └── 项目级 .claude/settings.json
├── 匹配规则
│ ├── matcher: 工具名/正则/通配符
│ └── 仅匹配的 hook 执行
└── 执行
├── shell command hooks: spawn(command, args)
├── 内置 hooks: 调用内部函数
└── 超时控制: 长时间 hook 被终止
```
---
## 8. 工具结果存储
`toolResultStorage.ts` (39KB) 负责工具调用结果(特别是大文件)的存储策略:
- **小结果** (< 阈值): 直接存在消息对象中
- **大结果**: 写入临时文件,消息中只存文件引用路径
- **图片结果**: 存为 Base64 或文件引用,带缩放缓存
- **清理**: 会话关闭时自动清理临时文件
---
## 9. 错误处理与恢复
### 9.1 API 错误分类
```
API 错误
├── 可重试错误 (retryable)
│ ├── 429 Rate Limit → 指数退避重试
│ ├── 5xx Server Error → 线性退避重试
│ └── 网络超时 → 立即重试 (最多3次)
├── 不可重试错误
│ ├── 401 Unauthorized → 提示检查 API Key
│ ├── 403 Forbidden → 提示检查权限
│ ├── 400 Bad Request → 显示错误详情
│ └── Token 超限 → 触发自动压缩
└── 流式中断
├── SSE 连接断开 → 尝试重连 + 部分结果
└── 解析错误 → 丢弃损坏chunk继续
```
### 9.2 对话崩溃恢复
```
异常发生
├── conversationRecovery.ts
│ ├── 自动保存当前状态 (crash snapshot)
│ ├── 标记会话为 interrupted
│ └── 下次启动时提示恢复
└── 用户重启 Claude Code
├── 检测到未完成会话
├── 显示 ResumeConversation 界面
└── 从崩溃点恢复对话 (最多丢失最后一条消息)
```
---
*本文档基于 2026-06-11 源码状态生成*