Skip to content

Pi Monorepo 源码调试指南

本文档覆盖 5 个包的调试方法:pi-ai、pi-tui、pi-agent-core、pi-coding-agent、pi-web-ui。 每个包都包含:环境准备、测试分类(是否需要 API Key)、具体调试命令、断点调试配置、推荐的学习路径。


目录


通用环境准备

1. 安装依赖与构建

bash
cd /Users/bytedance/Desktop/ai-study/pi-mono
npm install
npm run build

2. 验证构建成功

bash
npm run check

如果有报错,先解决构建问题再进行调试。

3. 了解测试框架差异

测试框架运行方式
pi-aivitestnpx tsx ../../node_modules/vitest/dist/cli.js --run test/xxx.test.ts
pi-agent-corevitest同上
pi-coding-agentvitest同上
pi-tuinode --testnode --test --import tsx test/xxx.test.ts
pi-web-ui无自动化测试Vite dev server + 手动测试

关键规则:所有测试都必须在包根目录下运行,不是 monorepo 根目录。

![Pi Monorepo 包结构与测试框架总览](https://qn.huat.xyz/blog/article-Illustration/Pi 源码调试指南/01-infographic-monorepo-overview-1775150081049.png)

4. 调试工具选择

  • 命令行调试:直接运行测试看输出
  • VSCode 断点调试:通过 launch.json 配置(见附录 A)
  • console.log 调试:在源码中临时添加日志
  • Node.js Inspectornode --inspect-brk + Chrome DevTools

一、pi-ai 调试

包路径packages/ai/测试框架:vitest vitest 配置globals: true, environment: "node", testTimeout: 30000

1.1 测试文件分类

不需要 API Key 的测试(可直接运行)

这些测试使用 mock 或纯逻辑验证,不调用真实 LLM API:

测试文件测试内容学习价值
validation.test.ts工具参数校验降级逻辑理解 TypeBox schema 校验机制
lazy-module-load.test.ts懒加载机制验证理解 Provider 注册与按需加载
json-parse.test.ts流式 JSON 解析理解 tool_call 参数的增量解析
sanitize-unicode.test.tsUnicode 代理对清洗理解流式文本的边界处理
hash.test.ts哈希工具简单工具函数
typebox-helpers.test.tsTypeBox schema 辅助工具理解 StringEnum 等自定义 schema
overflow.test.ts上下文溢出检测理解 token 计算与 context window 管理
models.test.ts模型注册表查询理解 getModel/getProviders/calculateCost

需要 API Key 的测试(集成测试)

这些测试会真实调用 LLM API,需要对应的环境变量:

测试文件所需环境变量测试内容
stream.test.ts多个 Provider 的 Key所有 Provider 的流式调用
tokens.test.ts多个 Provider 的 KeyToken 计算准确性
total-tokens.test.ts多个 Provider 的 KeytotalTokens 字段验证
abort.test.ts至少一个 Provider 的 Key流式请求取消
empty.test.ts至少一个 Provider 的 Key空响应处理
context-overflow.test.ts至少一个 Provider 的 Key上下文溢出行为
cross-provider-handoff.test.ts至少两个 Provider 的 Key跨 Provider 交接
unicode-surrogate.test.ts多个 Provider 的 KeyUnicode 处理(带 skipIf)
xhigh.test.tsOPENAI_API_KEYxhigh 思考等级

skipIf 的测试会在缺少对应 Key 时自动跳过,不会报错。

![pi-ai 测试分类:无需 API Key vs 需要 API Key](https://qn.huat.xyz/blog/article-Illustration/Pi 源码调试指南/02-infographic-pi-ai-test-classification-1775150081627.png)

1.2 运行命令

bash
cd packages/ai

# 运行单个不需要 API Key 的测试
npx tsx ../../node_modules/vitest/dist/cli.js --run test/validation.test.ts
npx tsx ../../node_modules/vitest/dist/cli.js --run test/lazy-module-load.test.ts
npx tsx ../../node_modules/vitest/dist/cli.js --run test/json-parse.test.ts
npx tsx ../../node_modules/vitest/dist/cli.js --run test/overflow.test.ts
npx tsx ../../node_modules/vitest/dist/cli.js --run test/models.test.ts

# 运行需要 API Key 的测试(会自动跳过没有 Key 的 Provider)
OPENAI_API_KEY=sk-xxx npx tsx ../../node_modules/vitest/dist/cli.js --run test/stream.test.ts

# 运行所有测试(需要 API Key 的会被跳过)
npx tsx ../../node_modules/vitest/dist/cli.js --run

1.3 推荐的调试学习路径

第一步:纯逻辑测试(无需 API Key)

bash
cd packages/ai

# 1. 模型注册表 — 理解 getModel, getProviders, calculateCost
npx tsx ../../node_modules/vitest/dist/cli.js --run test/models.test.ts

# 2. 参数校验 — 理解 TypeBox 在 tool calling 中的作用
npx tsx ../../node_modules/vitest/dist/cli.js --run test/validation.test.ts

# 3. 懒加载 — 理解 Provider SDK 的按需加载机制
npx tsx ../../node_modules/vitest/dist/cli.js --run test/lazy-module-load.test.ts

# 4. JSON 解析 — 理解流式 tool_call 参数的增量解析
npx tsx ../../node_modules/vitest/dist/cli.js --run test/json-parse.test.ts

# 5. 溢出检测 — 理解 context window 管理
npx tsx ../../node_modules/vitest/dist/cli.js --run test/overflow.test.ts

第二步:集成测试(需要 API Key)

建议先只配置一个 Provider 的 Key(推荐 OpenAI 或 Anthropic),逐步添加:

bash
# 只测试 OpenAI 相关用例(其他 Provider 会被自动跳过)
OPENAI_API_KEY=sk-xxx npx tsx ../../node_modules/vitest/dist/cli.js --run test/stream.test.ts

1.4 断点调试要点

stream.ts 中设断点,观察调用链:

stream() → resolveApiProvider(model.api) → provider.stream() → SSE 事件流解析

关键断点位置:

  • src/stream.tsstream() 函数入口
  • src/api-registry.tsgetApiProvider() — 观察 Provider 查找
  • src/providers/openai-completions.ts 的流式解析循环 — 观察事件发射
  • src/providers/transform-messages.ts — 观察消息格式转换

![pi-ai 流式调用链与断点位置](https://qn.huat.xyz/blog/article-Illustration/Pi 源码调试指南/03-infographic-pi-ai-call-chain-1775150082122.png)


二、pi-tui 调试

包路径packages/tui/测试框架node --test(Node.js 内置测试框架) 特殊依赖@xterm/headless(VirtualTerminal 用于测试中模拟终端)

2.1 测试文件分类

tui 的所有测试都不需要 API Key,它们使用 VirtualTerminal(基于 @xterm/headless)模拟终端环境:

测试文件测试内容学习价值
keys.test.ts键盘输入解析(Kitty 协议、西里尔字母等)理解终端键盘输入底层机制
editor.test.ts编辑器组件(历史导航、自动补全、光标)理解复杂组件的实现
tui-render.test.tsTUI 渲染引擎(resize、差分更新、样式)理解核心渲染机制
markdown.test.tsMarkdown 渲染(代码块、列表、样式)理解文本处理与 ANSI 输出
text.test.tsText 组件渲染理解最基础的组件模型
utils.test.tsvisibleWidth、truncateToWidth 等理解 ANSI 感知的字符串处理
select-list.test.ts列表选择组件理解键盘交互组件
editor-cjk.test.tsCJK 字符编辑理解东亚宽字符处理
editor-bracketed-paste.test.ts粘贴处理理解终端粘贴协议
fuzzy.test.ts模糊匹配算法理解自动补全的核心算法
autocomplete.test.ts自动补全 Provider理解补全系统架构
stdin-buffer.test.ts输入缓冲理解输入事件的批处理

2.2 交互式 Demo(可视化调试)

tui 包提供了多个可直接运行的交互式 Demo,非常适合可视化学习:

Demo 文件用途运行命令
test/chat-simple.ts简单聊天界面npx tsx test/chat-simple.ts
test/key-tester.ts键盘按键代码查看器npx tsx test/key-tester.ts
test/image-test.ts终端图片渲染测试npx tsx test/image-test.ts [图片路径]

2.3 运行命令

bash
cd packages/tui

# === 运行单元测试 ===

# 键盘输入解析测试
node --test --import tsx test/keys.test.ts

# 编辑器组件测试
node --test --import tsx test/editor.test.ts

# TUI 渲染引擎测试
node --test --import tsx test/tui-render.test.ts

# Markdown 渲染测试
node --test --import tsx test/markdown.test.ts

# 工具函数测试
node --test --import tsx test/utils.test.ts

# 模糊匹配测试
node --test --import tsx test/fuzzy.test.ts

# 运行全部测试
node --test --import tsx test/*.test.ts

# === 运行交互式 Demo ===

# 聊天界面 Demo(Ctrl+C 退出)
npx tsx test/chat-simple.ts

# 键盘测试器(Ctrl+C 退出)
npx tsx test/key-tester.ts

# 图片渲染测试
npx tsx test/image-test.ts /path/to/image.png

2.4 推荐的调试学习路径

第一步:工具函数(最基础)

bash
cd packages/tui

# 理解 ANSI 字符串的可见宽度计算
node --test --import tsx test/utils.test.ts

src/utils.tsvisibleWidth() 函数设断点,理解如何处理 ANSI 转义码、东亚宽字符、零宽字符。

第二步:键盘输入(终端交互基础)

bash
# 先运行 key-tester 看看真实按键产生什么序列
npx tsx test/key-tester.ts

# 然后运行测试,理解解析逻辑
node --test --import tsx test/keys.test.ts

src/keys.tsparseKey() 函数设断点,观察不同按键序列是如何被解析为 Key 对象的。

第三步:渲染引擎(核心)

bash
node --test --import tsx test/tui-render.test.ts

src/tui.tsrender() 方法(TUI 类的渲染循环)设断点,观察:

  1. 组件的 render(width) 如何输出行数组
  2. 新旧行如何比较
  3. 哪些行需要更新
  4. CSI 2026 同步输出如何工作

第四步:交互式 Demo(完整体验)

bash
npx tsx test/chat-simple.ts

在 demo 运行时,在 Editor 组件的 handleInput() 设断点,观察输入如何流转为 UI 更新。

2.5 关键源文件断点位置

文件断点建议观察目的
src/tui.ts TUI.render()渲染循环入口理解三策略差分渲染
src/tui.ts TUI.handleInput()输入处理入口理解输入→焦点组件的分发
src/keys.ts parseKey()键盘解析理解原始字节→Key 对象
src/components/editor.ts handleInput()编辑器输入处理理解复杂组件的交互逻辑
src/utils.ts visibleWidth()宽度计算理解 ANSI 感知的文本处理

2.6 VirtualTerminal 的作用

test/virtual-terminal.ts 是测试的关键基础设施。它基于 @xterm/headless(xterm.js 的无头版本)实现了 Terminal 接口,允许在测试中:

  • 模拟一个 80x24 的终端
  • 写入 ANSI 控制序列并正确处理
  • 读取终端 buffer 中的内容验证渲染结果
  • 模拟键盘输入
  • 检查单元格样式(粗体、斜体、颜色等)

理解 VirtualTerminal 是理解 tui 测试的前提。

![pi-tui 架构层次:从输入到渲染](https://qn.huat.xyz/blog/article-Illustration/Pi 源码调试指南/04-infographic-pi-tui-architecture-1775150082728.png)


三、pi-agent-core 调试

包路径packages/agent/测试框架:vitest vitest 配置globals: true, environment: "node", testTimeout: 30000

3.1 测试文件分类

不需要 API Key 的测试(使用 MockAssistantStream)

测试文件测试内容学习价值
agent.test.tsAgent 类状态管理、事件订阅、prompt 执行理解 Agent 类的完整生命周期
agent-loop.test.tsAgent Loop 事件序列、工具调用、多轮循环最核心,理解 Agent 循环机制

这两个文件使用 MockAssistantStream 模拟 LLM 响应,完全不需要网络或 API Key。

需要 API Key 的测试

测试文件所需环境变量测试内容
e2e.test.ts多个 Provider 的 Key端到端集成测试(带 skipIf)
bedrock-models.test.tsAWS Bedrock 凭证Bedrock 特定的模型测试

3.2 运行命令

bash
cd packages/agent

# 运行 Agent 类测试(不需要 API Key)
npx tsx ../../node_modules/vitest/dist/cli.js --run test/agent.test.ts

# 运行 Agent Loop 测试(不需要 API Key)
npx tsx ../../node_modules/vitest/dist/cli.js --run test/agent-loop.test.ts

# 运行 E2E 测试(需要 API Key,会自动跳过没有 Key 的 Provider)
OPENAI_API_KEY=sk-xxx npx tsx ../../node_modules/vitest/dist/cli.js --run test/e2e.test.ts

3.3 推荐的调试学习路径

第一步:Agent 类基础

bash
cd packages/agent
npx tsx ../../node_modules/vitest/dist/cli.js --run test/agent.test.ts

在以下位置设断点:

  • src/agent.tsAgent 构造函数 — 观察默认状态初始化
  • src/agent.tsprompt() 方法 — 观察消息队列机制
  • src/agent.tssubscribe() — 观察事件发布/订阅

第二步:Agent Loop 核心循环

bash
npx tsx ../../node_modules/vitest/dist/cli.js --run test/agent-loop.test.ts

这是最重要的调试目标。在以下位置设断点:

文件位置观察目的
src/agent-loop.tsrunAgentLoop() 入口理解循环的启动
src/agent-loop.tsemit({ type: "agent_start" })观察事件发射
src/agent-loop.tsconvertToLlm(messages)观察 AgentMessage → Message 转换
src/agent-loop.tsstreamFn(...) 调用点观察 LLM 调用时机
src/agent-loop.ts — tool_call 处理分支观察工具执行与结果注入
src/agent-loop.ts — 循环终止条件理解何时退出循环

测试中的 Mock 机制

agent-loop.test.ts 中的 MockAssistantStream 模拟了 LLM 的流式响应。理解它的工作方式是理解测试的关键:

typescript
class MockAssistantStream extends EventStream<AssistantMessageEvent, AssistantMessage> {
  constructor() {
    super(
      (event) => event.type === "done" || event.type === "error",  // 终止条件
      (event) => {                                                  // 结果提取
        if (event.type === "done") return event.message;
        if (event.type === "error") return event.error;
      },
    );
  }
}

通过 queueMicrotask() 异步发射事件,模拟真实的流式行为。

![MockAssistantStream 模拟 LLM 流式响应](https://qn.huat.xyz/blog/article-Illustration/Pi 源码调试指南/06-infographic-mock-mechanism-1775150083264.png)

第三步:E2E 集成测试(需要 API Key)

bash
OPENAI_API_KEY=sk-xxx npx tsx ../../node_modules/vitest/dist/cli.js --run test/e2e.test.ts

stream.ts(pi-ai 包)和 agent-loop.ts 同时设断点,观察完整的端到端调用链。

3.4 关键概念可视化

调试时重点观察这个事件序列:

agent_start
  → turn_start
    → message_start (user)
    → message_end (user)
    → [LLM 调用]
    → message_start (assistant)
    → message_update (streaming...)
    → message_end (assistant)
    → [如果有 tool_call]
      → tool_execution_start
      → tool_execution_end
      → message_start (toolResult)
      → message_end (toolResult)
  → turn_end
  → [如果有 tool_call,开始新 turn...]
  → turn_start
    → ...
  → turn_end
agent_end

![Agent Loop 事件生命周期](https://qn.huat.xyz/blog/article-Illustration/Pi 源码调试指南/05-infographic-agent-loop-events-1775150083759.png)

agent-loop.test.ts 中添加如下代码可以打印完整的事件序列:

typescript
const events: AgentEvent[] = [];
const stream = agentLoop([userPrompt], context, config);
for await (const event of stream) {
  events.push(event);
  console.log(`Event: ${event.type}`);
}

四、pi-coding-agent 调试

包路径packages/coding-agent/测试框架:vitest vitest 配置globals: true, environment: "node", testTimeout: 30000

这是最大的包(62 个测试文件),分类如下。

4.1 测试文件分类

不需要 API Key 的测试(推荐优先调试)

工具测试

测试文件测试内容学习价值
tools.test.ts所有内置工具(read/write/edit/bash/grep/find/ls)强烈推荐 — 理解 Agent 工具的实现
file-mutation-queue.test.ts文件修改队列的串行化理解并发文件操作的安全性
path-utils.test.ts路径工具函数理解路径解析和安全校验

核心逻辑测试

测试文件测试内容学习价值
test-harness.test.ts测试辅助框架自身的验证推荐先读 — 理解 mock 机制
compaction.test.ts上下文压缩逻辑(纯逻辑部分不需要 Key)理解压缩策略
compaction-serialization.test.ts压缩数据序列化理解压缩数据持久化
compaction-summary-reasoning.test.ts压缩摘要推理理解摘要生成逻辑
system-prompt.test.ts系统提示词构建理解提示词组装逻辑
model-resolver.test.ts模型解析理解模型选择机制
model-registry.test.ts模型注册表理解自定义 Provider 注册

会话管理测试

测试文件测试内容学习价值
settings-manager.test.ts设置管理器理解配置持久化
settings-manager-bug.test.ts设置管理器 bug 修复理解边界情况
auth-storage.test.ts认证存储理解凭证管理
session-selector-rename.test.ts会话重命名理解会话管理
session-selector-search.test.ts会话搜索理解会话检索
session-selector-path-delete.test.ts会话路径删除理解会话清理
session-info-modified-timestamp.test.ts会话时间戳理解会话元数据
sdk-session-manager.test.tsSDK 会话管理理解 SDK 层会话 API

扩展系统测试

测试文件测试内容学习价值
extensions-runner.test.ts扩展运行器理解扩展的执行机制
extensions-discovery.test.ts扩展发现理解扩展的加载流程
extensions-input-event.test.ts扩展输入事件理解扩展的事件拦截
resource-loader.test.ts资源加载器理解统一的资源加载

其他测试

测试文件测试内容
args.test.tsCLI 参数解析
skills.test.ts技能系统
sdk-skills.test.tsSDK 技能 API
prompt-templates.test.ts提示词模板
frontmatter.test.tsFrontmatter 解析
initial-message.test.ts初始消息构建
plan-mode-utils.test.tsPlan 模式工具
rpc-jsonl.test.tsRPC JSON-L 协议
footer-data-provider.test.ts底栏数据
footer-width.test.ts底栏宽度
tree-selector.test.ts文件树选择器
truncate-to-width.test.ts文本截断
git-ssh-url.test.tsGit SSH URL 解析
git-update.test.tsGit 更新
image-processing.test.ts图片处理
image-resize-callers.test.ts图片缩放
clipboard-image.test.ts剪贴板图片
clipboard-image-bmp-conversion.test.tsBMP 转换
block-images.test.ts图片阻止
package-manager.test.ts包管理器
package-manager-ssh.test.tsSSH 包管理
package-command-paths.test.ts包命令路径
keybindings-migration.test.ts快捷键迁移
interactive-mode-status.test.ts交互模式状态
interactive-mode-suspend.test.ts交互模式挂起
tool-execution-component.test.ts工具执行组件
stdout-cleanliness.test.tsstdout 干净度
bash-close-hang-windows.test.tsWindows bash 挂起(仅 Windows)

Agent Session mock 测试

测试文件测试内容学习价值
agent-session-retry.test.ts重试机制理解错误恢复
agent-session-concurrent.test.ts并发 prompt理解并发安全
agent-session-auto-compaction-queue.test.ts自动压缩队列理解压缩触发
agent-session-dynamic-tools.test.ts动态工具理解运行时工具变更
agent-session-dynamic-provider.test.ts动态 Provider理解运行时 Provider 切换
agent-session-model-switch-thinking.test.ts模型切换思考理解模型热切换

需要 API Key 的测试

测试文件所需环境变量测试内容
agent-session-branching.test.tsANTHROPIC_API_KEY会话分叉
agent-session-compaction.test.tsANTHROPIC_API_KEY压缩 E2E
agent-session-tree-navigation.test.tsANTHROPIC_API_KEY树导航 E2E
compaction-thinking-model.test.tsANTHROPIC_API_KEY思考模型压缩
compaction-extensions.test.tsANTHROPIC_API_KEY压缩扩展
compaction-extensions-example.test.tsANTHROPIC_API_KEY压缩扩展示例
rpc.test.tsANTHROPIC_API_KEYRPC 模式

4.2 Test Harness — 核心 Mock 机制

test/test-harness.ts 是 coding-agent 测试的核心基础设施,理解它是调试的前提。

关键组件

createHarness(options)
    ├─ FauxStreamFn — 模拟 LLM 的流式响应函数
    │   ├─ 接受 responses 数组(字符串 / toolCall / error)
    │   └─ 返回 MockAssistantStream
    ├─ fauxModel — 虚拟模型定义
    ├─ AgentSession — 真实的 AgentSession 实例
    └─ Harness — 组合以上组件的测试夹具

使用示例

typescript
// 创建一个会回复 "hello world" 的 mock Agent
const harness = createHarness({ responses: ["hello world"] });

// 发送 prompt 并等待完成
await harness.session.prompt("hi");

// 检查结果
expect(harness.faux.callCount).toBe(1);

// 清理
harness.cleanup();

这意味着你可以完全不需要 API Key 就能调试 Agent 的完整行为,包括工具调用、重试、错误处理等。

![Test Harness 核心组件结构](https://qn.huat.xyz/blog/article-Illustration/Pi 源码调试指南/07-infographic-test-harness-1775150084270.png)

4.3 运行命令

bash
cd packages/coding-agent

# ========= 推荐最先运行的测试 =========

# Test Harness 本身的验证(理解 mock 机制)
npx tsx ../../node_modules/vitest/dist/cli.js --run test/test-harness.test.ts

# 工具测试(理解 read/write/edit/bash 的实现)
npx tsx ../../node_modules/vitest/dist/cli.js --run test/tools.test.ts

# 系统提示词构建(理解提示词组装)
npx tsx ../../node_modules/vitest/dist/cli.js --run test/system-prompt.test.ts

# ========= 核心逻辑测试 =========

# 上下文压缩(纯逻辑部分)
npx tsx ../../node_modules/vitest/dist/cli.js --run test/compaction.test.ts

# 扩展系统
npx tsx ../../node_modules/vitest/dist/cli.js --run test/extensions-runner.test.ts
npx tsx ../../node_modules/vitest/dist/cli.js --run test/extensions-discovery.test.ts

# 会话管理
npx tsx ../../node_modules/vitest/dist/cli.js --run test/settings-manager.test.ts
npx tsx ../../node_modules/vitest/dist/cli.js --run test/auth-storage.test.ts

# Agent Session mock 测试
npx tsx ../../node_modules/vitest/dist/cli.js --run test/agent-session-retry.test.ts
npx tsx ../../node_modules/vitest/dist/cli.js --run test/agent-session-concurrent.test.ts
npx tsx ../../node_modules/vitest/dist/cli.js --run test/agent-session-dynamic-tools.test.ts

# ========= 需要 API Key =========

ANTHROPIC_API_KEY=sk-xxx npx tsx ../../node_modules/vitest/dist/cli.js --run test/agent-session-branching.test.ts

4.4 SDK 示例调试

SDK 示例是理解 coding-agent 对外 API 的最佳方式(需要 API Key):

bash
cd packages/coding-agent

# 最简示例 — 理解 createAgentSession
npx tsx examples/sdk/01-minimal.ts

# 工具配置 — 理解工具选择与 cwd 绑定
npx tsx examples/sdk/05-tools.ts

# 扩展系统 — 理解 Extension API
npx tsx examples/sdk/06-extensions.ts

# 完整列表:
# 01-minimal.ts       — 最简用法
# 02-model.ts         — 模型选择
# 03-prompts.ts       — 提示词配置
# 04-sessions.ts      — 会话持久化
# 05-tools.ts         — 工具配置
# 06-extensions.ts    — 扩展系统
# 07-events.ts        — 事件监听
# 08-print-mode.ts    — 打印模式
# 09-thinking.ts      — 思考等级
# 10-custom-model.ts  — 自定义模型
# 11-rpc-client.ts    — RPC 客户端
# 12-full-control.ts  — 完全控制

4.5 推荐的调试学习路径

1. test-harness.test.ts      → 理解 mock 机制
2. tools.test.ts             → 理解四大工具
3. system-prompt.test.ts     → 理解提示词构建
4. compaction.test.ts        → 理解上下文压缩
5. extensions-runner.test.ts → 理解扩展系统
6. agent-session-retry.test.ts → 理解 AgentSession 错误恢复
7. examples/sdk/01-minimal.ts  → 理解完整 SDK 用法(需要 Key)

![pi-coding-agent 调试学习路径](https://qn.huat.xyz/blog/article-Illustration/Pi 源码调试指南/08-infographic-coding-agent-learning-path-1775150084978.png)

4.6 关键源文件断点位置

文件断点建议观察目的
src/core/sdk.ts — createAgentSession()SDK 工厂入口理解初始化全流程
src/core/agent-session.ts — prompt()消息发送入口理解消息队列
src/core/system-prompt.ts — buildSystemPrompt()系统提示词组装理解提示词结构
src/core/tools/read.ts — execute()read 工具执行理解工具的输入输出
src/core/tools/edit.ts — execute()edit 工具执行理解 search-and-replace
src/core/tools/bash.ts — execute()bash 工具执行理解命令执行安全性
src/core/compaction/compaction.ts — compact()压缩主逻辑理解压缩策略
src/core/extensions/runner.ts — emit()扩展事件分发理解 hook 机制
test/test-harness.ts — createFauxStreamFn()Mock 流函数理解 mock 如何模拟 LLM

五、pi-web-ui 调试

包路径packages/web-ui/测试框架:无自动化测试 调试方式:Vite dev server + 浏览器 DevTools

5.1 启动示例应用

bash
cd packages/web-ui/example

# 安装依赖(如果还没装)
npm install

# 启动开发服务器
npm run dev

默认会在 http://localhost:5173 启动。

5.2 示例应用架构

![pi-web-ui 示例应用架构](https://qn.huat.xyz/blog/article-Illustration/Pi 源码调试指南/09-infographic-web-ui-architecture-1775150085513.png)

示例应用 (packages/web-ui/example/) 是一个完整的 AI 聊天应用,展示了 web-ui 包的所有核心功能:

入口example/src/main.ts

初始化流程

  1. 创建四个 Store(settings, providerKeys, sessions, customProviders)
  2. 创建 IndexedDBStorageBackend
  3. 创建 AppStorage 统一管理
  4. 创建 Agent(默认使用 claude-sonnet-4-5)
  5. 创建 ChatPanel 组件
  6. 渲染 UI(使用 lit 的 html 模板)

功能演示

  • 聊天会话管理(新建 / 历史 / 加载)
  • 自定义消息类型(SystemNotification — 通过 declaration merging 扩展)
  • API Key 对话框
  • 模型选择
  • 设置面板
  • 自动保存到 IndexedDB

5.3 调试方式

方式一:浏览器 DevTools

  1. 启动 npm run dev
  2. 打开 http://localhost:5173
  3. 打开浏览器 DevTools (F12)
  4. 在 Sources 面板中找到 src/ 目录下的源文件
  5. 直接在浏览器中设断点

推荐断点位置

  • main.tscreateAgent() 函数:观察 Agent 初始化
  • main.ts — Agent subscribe 回调:观察事件流
  • main.tssaveSession() 函数:观察 IndexedDB 持久化

方式二:查看 Web Components 源码

在浏览器 DevTools 的 Elements 面板,找到以下自定义元素:

元素对应源文件职责
<chat-panel>ChatPanel.ts顶层聊天容器
<agent-interface>components/AgentInterface.tsAgent 交互界面
<message-list>components/MessageList.ts消息列表
<sandboxed-iframe>components/SandboxedIframe.tsArtifact 沙箱

方式三:IndexedDB 调试

在浏览器 DevTools → Application → Storage → IndexedDB → pi-web-ui-example,可以直接查看:

  • settings — 用户设置
  • provider-keys — API Key 存储
  • sessions — 会话数据
  • custom-providers — 自定义 Provider

5.4 自定义消息类型示例

example/src/custom-messages.ts 展示了 web-ui 最强大的扩展机制——通过 TypeScript declaration merging 添加自定义消息类型:

typescript
// 1. 定义自定义消息接口
interface SystemNotificationMessage {
  role: "system-notification";
  message: string;
  variant: "default" | "destructive";
  timestamp: string;
}

// 2. 通过 declaration merging 注册到类型系统
declare module "@mariozechner/pi-agent-core" {
  interface CustomAgentMessages {
    "system-notification": SystemNotificationMessage;
  }
}

// 3. 创建渲染器
const renderer: MessageRenderer<SystemNotificationMessage> = {
  render: (msg) => html`<div>${msg.message}</div>`,
};

// 4. 注册渲染器
registerMessageRenderer("system-notification", renderer);

// 5. 自定义 convertToLlm(过滤自定义消息,不发送给 LLM)
function customConvertToLlm(messages: AgentMessage[]): Message[] {
  return defaultConvertToLlm(
    messages.filter((m) => m.role !== "system-notification")
  );
}

在断点调试这个流程,可以深入理解 web-ui 的消息扩展机制。

5.5 推荐的调试学习路径

1. 启动 example/npm run dev,浏览整体界面
2. 在 DevTools 中检查 IndexedDB 存储结构
3. 在 main.ts 的 createAgent() 设断点,理解初始化
4. 发送一条消息,观察 Agent subscribe 回调中的事件流
5. 查看 custom-messages.ts,理解自定义消息扩展
6. 在 ChatPanel.ts 源码中设断点,理解组件渲染
7. 检查 SandboxedIframe,理解 Artifact 的沙箱机制

5.6 不需要 API Key 的调试场景

即使没有 API Key,你仍然可以:

  • 启动应用并查看 UI 结构
  • 查看 IndexedDB 存储机制
  • 研究自定义消息类型的 declaration merging
  • 查看组件的渲染逻辑
  • 测试 API Key 输入对话框的流程

但如果要真正发送消息并看到 Agent 回复,需要至少配置一个 Provider 的 API Key。


附录 A:VSCode 调试配置模板

在 monorepo 根目录创建 .vscode/launch.json

json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Debug pi-ai test (vitest)",
      "type": "node",
      "request": "launch",
      "runtimeExecutable": "npx",
      "runtimeArgs": [
        "tsx",
        "../../node_modules/vitest/dist/cli.js",
        "--run",
        "--no-file-parallelism",
        "${file}"
      ],
      "cwd": "${workspaceFolder}/packages/ai",
      "console": "integratedTerminal",
      "env": {
        "OPENAI_API_KEY": "${env:OPENAI_API_KEY}",
        "ANTHROPIC_API_KEY": "${env:ANTHROPIC_API_KEY}",
        "GEMINI_API_KEY": "${env:GEMINI_API_KEY}"
      },
      "skipFiles": ["<node_internals>/**"]
    },
    {
      "name": "Debug pi-agent test (vitest)",
      "type": "node",
      "request": "launch",
      "runtimeExecutable": "npx",
      "runtimeArgs": [
        "tsx",
        "../../node_modules/vitest/dist/cli.js",
        "--run",
        "--no-file-parallelism",
        "${file}"
      ],
      "cwd": "${workspaceFolder}/packages/agent",
      "console": "integratedTerminal",
      "env": {
        "OPENAI_API_KEY": "${env:OPENAI_API_KEY}"
      },
      "skipFiles": ["<node_internals>/**"]
    },
    {
      "name": "Debug pi-tui test (node --test)",
      "type": "node",
      "request": "launch",
      "runtimeArgs": [
        "--test",
        "--import",
        "tsx",
        "${file}"
      ],
      "cwd": "${workspaceFolder}/packages/tui",
      "console": "integratedTerminal",
      "skipFiles": ["<node_internals>/**"]
    },
    {
      "name": "Debug pi-tui demo",
      "type": "node",
      "request": "launch",
      "runtimeExecutable": "npx",
      "runtimeArgs": [
        "tsx",
        "${file}"
      ],
      "cwd": "${workspaceFolder}/packages/tui",
      "console": "externalTerminal",
      "skipFiles": ["<node_internals>/**"]
    },
    {
      "name": "Debug pi-coding-agent test (vitest)",
      "type": "node",
      "request": "launch",
      "runtimeExecutable": "npx",
      "runtimeArgs": [
        "tsx",
        "../../node_modules/vitest/dist/cli.js",
        "--run",
        "--no-file-parallelism",
        "${file}"
      ],
      "cwd": "${workspaceFolder}/packages/coding-agent",
      "console": "integratedTerminal",
      "env": {
        "ANTHROPIC_API_KEY": "${env:ANTHROPIC_API_KEY}"
      },
      "skipFiles": ["<node_internals>/**"]
    },
    {
      "name": "Debug pi-coding-agent SDK example",
      "type": "node",
      "request": "launch",
      "runtimeExecutable": "npx",
      "runtimeArgs": [
        "tsx",
        "${file}"
      ],
      "cwd": "${workspaceFolder}/packages/coding-agent",
      "console": "integratedTerminal",
      "env": {
        "OPENAI_API_KEY": "${env:OPENAI_API_KEY}",
        "ANTHROPIC_API_KEY": "${env:ANTHROPIC_API_KEY}"
      },
      "skipFiles": ["<node_internals>/**"]
    }
  ]
}

使用方式

  1. 在 VSCode 中打开要调试的测试文件
  2. 在源码中设置断点
  3. 按 F5 选择对应的调试配置
  4. 调试器会停在断点处

注意:tui demo 建议使用 externalTerminal,因为 TUI 需要真实的终端环境。


附录 B:API Key 配置参考

环境变量设置

在 shell profile(~/.zshrc~/.bashrc)中添加:

bash
# 推荐至少配置一个(OpenAI 或 Anthropic)
export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."

# 可选
export GEMINI_API_KEY="..."
export XAI_API_KEY="..."
export GROQ_API_KEY="..."
export MISTRAL_API_KEY="..."

临时设置(单次运行)

bash
OPENAI_API_KEY=sk-xxx npx tsx ../../node_modules/vitest/dist/cli.js --run test/stream.test.ts

.env 文件方式

在包根目录创建 .env 文件(vitest 会自动加载):

OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...

注意:.env 文件已在 .gitignore 中,不会被提交。


附录 C:常见问题排查

问题 1:测试报错 "Cannot find module"

原因:没有先构建。 解决

bash
cd /Users/bytedance/Desktop/ai-study/pi-mono
npm run build

问题 2:tui 测试报错 "Cannot use import statement outside a module"

原因:缺少 tsx loader。 解决:确保使用 --import tsx 参数:

bash
node --test --import tsx test/keys.test.ts

问题 3:vitest 测试超时

原因:默认 30 秒超时,部分集成测试(特别是带 API 调用的)可能需要更长时间。 解决

bash
npx tsx ../../node_modules/vitest/dist/cli.js --run --testTimeout=60000 test/stream.test.ts

问题 4:web-ui example 启动失败

原因:可能缺少 example 的依赖。 解决

bash
cd packages/web-ui/example
npm install
npm run dev

问题 5:API Key 相关的测试全部跳过

原因:没有设置对应的环境变量。 预期行为:这是正常的——带 skipIf 的测试会在缺少 Key 时自动跳过,不会报错。如果你只想运行纯逻辑测试,这些跳过是完全正确的。

问题 6:断点不生效

可能原因

  1. 断点设在了编译后的 .js 文件而不是源 .ts 文件
  2. source map 没有正确生成
  3. 使用了 console 模式而不是 integratedTerminal

解决

  • 确保断点设在 src/ 目录下的 .ts 文件
  • VSCode 配置中添加 "skipFiles": ["<node_internals>/**"]
  • 使用 integratedTerminal 作为控制台类型

快速开始清单

![快速开始清单:7 步调试入门](https://qn.huat.xyz/blog/article-Illustration/Pi 源码调试指南/10-infographic-quick-start-flow-1775150086043.png)

如果你想现在就开始调试,按以下顺序执行:

bash
# 1. 构建项目
cd /Users/bytedance/Desktop/ai-study/pi-mono
npm run build

# 2. 从 tui 开始 — 最直观,不需要 API Key
cd packages/tui
npx tsx test/chat-simple.ts    # 看看交互式界面长什么样
npx tsx test/key-tester.ts     # 按按键看看终端接收到什么

# 3. tui 单元测试
node --test --import tsx test/keys.test.ts
node --test --import tsx test/editor.test.ts

# 4. pi-ai 纯逻辑测试
cd ../ai
npx tsx ../../node_modules/vitest/dist/cli.js --run test/validation.test.ts
npx tsx ../../node_modules/vitest/dist/cli.js --run test/lazy-module-load.test.ts
npx tsx ../../node_modules/vitest/dist/cli.js --run test/models.test.ts

# 5. agent-core mock 测试
cd ../agent
npx tsx ../../node_modules/vitest/dist/cli.js --run test/agent.test.ts
npx tsx ../../node_modules/vitest/dist/cli.js --run test/agent-loop.test.ts

# 6. coding-agent mock 测试
cd ../coding-agent
npx tsx ../../node_modules/vitest/dist/cli.js --run test/test-harness.test.ts
npx tsx ../../node_modules/vitest/dist/cli.js --run test/tools.test.ts
npx tsx ../../node_modules/vitest/dist/cli.js --run test/system-prompt.test.ts

# 7. web-ui 示例应用
cd ../web-ui/example
npm run dev
# 打开 http://localhost:5173

以上所有命令都不需要 API Key(web-ui 启动后发消息需要 Key,但查看 UI 结构不需要)。

读文档、看源码、写代码,理解 AI Agent 本质 🤖