Pi Agent Harness 源码解析:TypeScript 实现的模块化 Coding Agent

深入剖析 Earendil Works 开源的 Pi Agent Harness------一个基于 TypeScript Monorepo 构建的模块化交互式编程代理

项目简介

Pi Agent Harness 是由 Earendil Works 开发的开源 coding agent 项目。其核心定位是提供一个可扩展的交互式编程代理,能运行在终端中,通过多 LLM provider 驱动,完成代码编写、搜索、编辑等任务。

核心特点

  • 🔌 多 LLM Provider 支持:OpenAI、Anthropic、Google、GitHub Copilot、OpenRouter、xAI、Mistral 等 20+ 个 provider 的统一接口
  • 🧩 自扩展:coding agent 可以通过 skill、extension、prompt template 等方式进行扩展
  • 🖥️ 交互式终端 UI:支持差分渲染的 TUI、alt-screen 全屏模式、overlay、滚动视图等
  • 📦 会话持久化与分支:支持 JSONL 格式的会话存储、会话 fork、恢复、continue
  • 🔗 RPC 模式:支持通过 JSON 行进行程序化控制,可嵌入到其他应用中
  • 🔐 供应链安全:pin 依赖版本、npm shrinkwrap、install lock、lifecycle script 审计

发布包

npm 包名 描述
@earendil-works/pi-coding-agent 交互式 coding agent CLI
@earendil-works/pi-agent-core Agent 运行时(工具调用、状态管理)
@earendil-works/pi-ai 统一多 provider LLM API
@earendil-works/pi-tui 终端 UI 库(差分渲染)

整体架构

复制代码
┌─────────────────────────────────────────────────────────────┐
│                        用户终端                              │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ▼
┌─────────────────────────────────────────────────────────────┐
│   packages/coding-agent (交互式 CLI 主程序)                 │
│   ┌───────────┐  ┌──────────────┐  ┌──────────────────┐   │
│   │ main.ts   │→ │ AgentSession │→ │ SessionManager   │   │
│   │ cli.ts    │  │  Runtime     │  │ (会话持久化)      │   │
│   └───────────┘  └──────┬───────┘  └──────────────────┘   │
│                          │                                  │
│   ┌───────────────────┐  │  ┌────────────────────────┐    │
│   │ Extensions / Skills│←─┘→│ SettingsManager         │    │
│   │ (扩展与技能)       │    │ (配置管理)               │    │
│   └───────────────────┘    └────────────────────────┘    │
└────────────────────────┬────────────────────────────────────┘
                         │
          ┌──────────────┼──────────────┐
          ▼              ▼              ▼
┌──────────────┐  ┌────────────┐  ┌──────────────┐
│ packages/    │  │ packages/  │  │ packages/    │
│ agent        │  │ tui        │  │ protocol     │
│ (Agent 运行时)│  │ (终端 UI)  │  │ (RPC 协议)   │
└──────┬───────┘  └────────────┘  └──────────────┘
       │
       ▼
┌─────────────────────────────────────────────────────────────┐
│   packages/ai (统一 LLM API 层)                            │
│   ┌────────────┐  ┌──────────┐  ┌────────────────┐        │
│   │ stream()   │→ │ Provider │→ │ HTTP Client    │        │
│   │ (统一入口)  │  │ Registry │  │ (fetch/SSE)    │        │
│   └────────────┘  └──────────┘  └────────────────┘        │
└─────────────────────────────────────────────────────────────┘
       │
       ▼
┌─────────────────────────────────────────────────────────────┐
│  OpenAI / Anthropic / Google / Copilot / OpenRouter / xAI   │
│  Mistral / Ollama / Groq / Cerebras / DeepSeek / ...        │
└─────────────────────────────────────────────────────────────┘

分层结构(自底向上):

  1. AI 层packages/ai):统一 LLM API,屏蔽不同 provider 的协议差异
  2. Agent 层packages/agent):Agent 运行时,管理状态、工具调用、消息队列
  3. TUI 层packages/tui):终端 UI 组件库,差分渲染、事件处理
  4. 协议层packages/protocol):RPC 协议的 schema 定义
  5. Client 层packages/client):RPC 客户端 SDK
  6. Server 层packages/server):RPC 服务端框架
  7. Coding Agent 层packages/coding-agent):组装所有模块的 CLI 主程序

Monorepo 结构

复制代码
pi-monorepo/
├── packages/
│   ├── ai/                 # 统一多 provider LLM API
│   ├── agent/              # Agent 运行时核心
│   ├── coding-agent/       # 交互式 coding agent CLI(主程序)
│   ├── tui/                # 终端 UI 库
│   ├── protocol/           # RPC 协议定义
│   ├── client/             # RPC 客户端 SDK
│   ├── server/             # RPC 服务端框架
│   ├── evals/              # 评估基准
│   └── storage/            # 存储后端(sqlite-node 等)
├── scripts/                # 构建、发布、检查脚本
├── .pi/                    # Pi 自身配置
├── package.json            # Monorepo 根配置
├── tsconfig.json           # TypeScript 配置
├── biome.json              # Biome linter/formatter 配置
└── vitest.base.ts          # 测试配置

构建顺序npm run build 的依赖链):

复制代码
tui → ai → agent → storage/sqlite-node → protocol → client → coding-agent → server

这种依赖链设计非常清晰------底层模块不依赖上层,每一层只依赖其下的层。


核心模块详解

1. coding-agent:交互式 CLI 主程序

这是整个项目的主程序包,将 agent 运行时、TUI、会话管理、配置管理、工具系统等组合为一个完整的交互式 coding agent。

入口文件

src/main.ts --- main() 函数是整个 CLI 的入口点。

启动流程概览

复制代码
main()
 ├── 1. 初始化
 │    ├── resetTimings()               // 重置性能计时
 │    ├── 合并 builtInExtensions        // 注册内置扩展
 │    ├── 检查 offline 模式             // --offline 或 PI_OFFLINE
 │    └── applyHttpProxySettings()      // 应用 HTTP 代理配置
 │
 ├── 2. 命令处理(短路)
 │    ├── handlePackageCommand()        // pi install/uninstall/update/...
 │    ├── handleConfigCommand()         // pi config ...
 │    └── runCredentialPrintCommand()   // pi credential ...
 │
 ├── 3. 参数解析
 │    ├── parseArgs()                  // CLI 参数解析
 │    └── resolveAppMode()             // 确定运行模式(interactive/rpc/print)
 │
 ├── 4. 迁移与设置
 │    ├── runMigrations()              // 执行配置迁移
 │    └── SettingsManager.create()     // 创建设置管理器
 │
 ├── 5. 会话管理
 │    ├── createSessionManager()       // 创建/恢复/继续会话
 │    └── getMissingSessionCwdIssue()  // 检查会话 CWD 一致性
 │
 ├── 6. 运行时创建
 │    ├── createRuntime()              // 创建 AgentSessionRuntime
 │    │    ├── ModelRuntime            // 模型运行时
 │    │    ├── ResourceLoader          // 资源加载器
 │    │    ├── AuthStorage             // 认证存储
 │    │    ├── ExtensionManager        // 扩展管理器
 │    │    └── SkillRegistry           // 技能注册表
 │    └── createAgentSession()         // 创建 Agent 会话
 │
 └── 7. 运行模式分派
      ├── runInteractiveMode()         // 交互式 TUI 模式
      ├── runRpcMode()                 // RPC(JSON-lines)模式
      └── runPrintMode()               // 单次打印模式
核心子模块

AgentSessionsrc/core/agent-session.ts

编码 agent 的会话对象,封装了:

  • 底层 Agent(来自 packages/agent)实例
  • 系统提示词管理(system prompt)
  • 工具注册(内置工具 + 扩展工具)
  • 模型切换(Ctrl+P 循环)
  • 自动压缩(auto-compaction,当 token 接近上限时自动摘要)
  • 会话事件分发

AgentSessionRuntimesrc/core/agent-session-runtime.ts

与特定 CWD 绑定的运行时环境,包含:

  • ModelRuntime:模型查询、切换、API 兼容性处理
  • SettingsManager:全局/项目/会话级设置
  • ResourceLoader:资源文件(CLAUDE.md 等)加载
  • AuthStorage:认证凭据存储
  • ExtensionManager:扩展加载与管理
  • SkillRegistry:技能(/command)注册

SessionManagersrc/core/session-manager.ts

会话的生命周期管理:

  • 创建新会话(create()
  • 打开已有会话(open()
  • 继续最近会话(continueRecent()
  • Fork 会话(forkSessionOrExit()
  • 列出所有会话(list()listAll()

会话文件存储在 ~/.pi/agent/sessions/<encoded-cwd>/ 下,格式为 JSONL。

运行模式

交互式模式src/modes/interactive/):

  • 使用 TuiMainScreen 进行全屏或滚动渲染
  • 支持键盘快捷键(Ctrl+C、Ctrl+P、Escape 等)
  • 输入编辑器、自动补全、overlay 弹窗
  • 流式输出渲染

RPC 模式src/modes/rpc/):

  • 通过 stdin/stdout 交换 JSON-lines 消息
  • 支持 get_statepromptsteerabortset_model 等命令
  • 可嵌入到 IDE、web 应用等外部宿主

Print 模式src/modes/print/):

  • 非交互式单次执行
  • 输出到 stdout,适合脚本和管道

2. agent:Agent 运行时

Agent 运行时是与 UI 无关的核心引擎,负责:

  • 管理 Agent 状态(消息、工具、模型)
  • 驱动 Agent Loop(LLM 调用 → 工具执行 → 继续)
  • 消息队列(steering queue、follow-up queue)
  • 事件分发
  • 会话持久化抽象
Agent 类
typescript 复制代码
class Agent {
    // 状态
    state: AgentState;  // 包含 systemPrompt, model, tools, messages, isStreaming 等

    // 消息队列
    steer(message: AgentMessage): void;      // 在当前 turn 结束后注入消息
    followUp(message: AgentMessage): void;   // 在 agent 即将停止时注入消息

    // 主入口
    prompt(input: string | AgentMessage): Promise<void>;  // 开始一个新 prompt
    continue(): Promise<void>;                            // 继续运行(处理队列)
    abort(): void;                                        // 中止当前运行

    // 事件
    addListener(listener: (event: AgentEvent) => void): void;
    // AgentEvent: assistant_start, text_delta, toolcall_start, tool_execution_end, agent_end, ...
}

状态模型(AgentState 接口):

typescript 复制代码
interface AgentState {
    systemPrompt: string;           // 系统提示词
    model: Model;                   // 当前模型
    thinkingLevel: ThinkingLevel;   // 推理等级(off/minimal/low/medium/high/xhigh/max)
    tools: AgentTool[];             // 可用工具列表
    messages: AgentMessage[];       // 对话历史
    readonly isStreaming: boolean;  // 是否正在流式输出
    readonly streamingMessage?: AgentMessage;  // 当前部分消息
    readonly pendingToolCalls: ReadonlySet<string>;  // 正在执行的工具调用 ID
    readonly errorMessage?: string; // 最近的错误消息
}
Agent Loop

Agent Loop 是整个系统的核心循环 ,位于 src/agent-loop.ts

复制代码
runAgentLoop(context, streamFn, config)
 │
 ├── 1. 准备上下文
 │    ├── transformContext()          // 消息转换(过滤自定义消息)
 │    ├── convertToLlm()             // AgentMessage → LLM Message
 │    └── 构建 streamFn 参数
 │
 ├── 2. 调用 LLM
 │    ├── streamFn(model, context)    // 调用 LLM API,返回流
 │    └── 处理流事件:
 │         ├── text_delta             // 文本增量
 │         ├── thinking_delta         // 思考增量
 │         ├── toolcall_start/delta/end // 工具调用
 │         └── done / error           // 结束/错误
 │
 ├── 3. 工具执行
 │    ├── 识别工具调用(从 assistant message 中)
 │    ├── prepareToolCall()           // 参数准备和验证
 │    ├── beforeToolCall()            // 工具执行前钩子
 │    ├── 并行/串行执行工具           // 根据 executionMode
 │    │    ├── tool.execute()         // 执行工具
 │    │    └── 收集 AgentToolResult
 │    ├── afterToolCall()             // 工具执行后钩子
 │    └── 将工具结果追加到 messages
 │
 ├── 4. 检查终止条件
 │    ├── stopReason === "stop"       // LLM 决定停止
 │    ├── terminate === true          // 工具请求终止
 │    ├── 队列消息处理                 // drain steering/followUp queue
 │    └── prepareNextTurn()           // 下一轮准备钩子
 │
 └── 5. 继续或结束
      ├── 有工具结果 → 继续循环
      ├── 有队列消息 → 注入消息继续
      └── 否则 → 结束循环

关键设计决策

  • 工具执行模式 :支持 sequential(逐个执行)和 parallel(并行执行)两种模式,每个工具可通过 executionMode 属性覆盖默认行为
  • 消息队列steering 队列在每轮结束后注入,followUp 队列在 agent 即将停止时注入,支持不同的 QueueModeall / one-at-a-time
  • 流式处理 :LLM 返回的流通过 AssistantMessageEventStream 处理,支持增量更新 UI
工具接口
typescript 复制代码
interface AgentTool<TParameters, TDetails> {
    name: string;               // 工具名称
    description: string;        // 工具描述(给 LLM 看的)
    parameters: TSchema;        // JSON Schema 参数定义
    label: string;              // 人类可读标签(给 UI 看的)
    executionMode?: ToolExecutionMode;  // 执行模式

    // 参数预处理(可选)
    prepareArguments?: (args: unknown) => Static<TParameters>;

    // 执行函数
    execute: (
        toolCallId: string,
        params: Static<TParameters>,
        signal?: AbortSignal,
        onUpdate?: AgentToolUpdateCallback<TDetails>,
    ) => Promise<AgentToolResult<TDetails>>;
}

3. ai:统一多 Provider LLM 接口

AI 层是 LLM 调用的统一抽象层,将不同 provider 的协议差异屏蔽,为上层提供一致的 API。

核心 API
typescript 复制代码
// 简化的流式调用
streamSimple(model, context, options): AssistantMessageEventStream

// 完整的流式调用
stream(model, context, options): AssistantMessageEventStream
API 实现

每种 API 协议有一个独立的实现文件:

文件 协议 使用的 Provider
src/api/openai-completions.ts OpenAI Chat Completions OpenAI、Azure、Mistral、Groq、Cerebras、DeepSeek、xAI 等
src/api/openai-responses.ts OpenAI Responses API OpenAI (GPT-5 等)
src/api/openai-codex-responses.ts OpenAI Codex Responses Codex
src/api/anthropic.ts Anthropic Messages Anthropic (Claude)
src/api/google.ts Google Gemini Google
src/api/bedrock.ts AWS Bedrock Amazon
src/api/ollama.ts Ollama Ollama (本地)
src/api/github-copilot.ts GitHub Copilot GitHub Copilot
流式处理流程(以 OpenAI Completions 为例)
复制代码
stream(model, context, options)
 │
 ├── 1. 初始化
 │    ├── getClientApiKey()           // 获取 API Key
 │    ├── getCompat()                 // 获取 provider 兼容性配置
 │    ├── createGrammarToolInputProperties()  // 语法工具处理
 │    ├── resolveCacheRetention()     // 缓存保留策略
 │    └── createClient()              // 创建 OpenAI SDK 客户端
 │
 ├── 2. 构建请求参数
 │    └── buildParams()
 │         ├── convertMessages()      // 消息格式转换
 │         ├── 设置 prompt_cache_key  // 缓存键
 │         ├── 设置 tools             // 工具定义
 │         └── 设置 reasoning_effort  // 推理等级
 │
 ├── 3. 发送请求
 │    ├── retryProviderRequest()      // 带重试的请求
 │    └── client.chat.completions.create()  // OpenAI SDK 调用
 │
 ├── 4. 处理流式响应
 │    └── for await (chunk of openaiStream)
 │         ├── 解析 usage             // token 用量
 │         ├── 解析 finish_reason     // 停止原因
 │         ├── 解析 text delta        // 文本增量
 │         ├── 解析 reasoning delta   // 推理增量(thinking)
 │         ├── 解析 tool_calls delta  // 工具调用增量
 │         └── 解析 reasoning_details // 加密推理详情
 │
 └── 5. 完成/错误处理
      ├── finishBlock()              // 完成每个内容块
      ├── stream.push(done)          // 推送完成事件
      └── stream.end()               // 结束流
模型管理
typescript 复制代码
interface Model<TApi> {
    id: string;           // 模型 ID
    provider: string;     // Provider ID
    api: TApi;            // API 类型
    baseUrl?: string;     // 自定义 base URL
    headers?: Record<string, string>;  // 自定义请求头
}

ModelsRuntime 负责:

  • 内置模型数据(编译时生成)
  • 远程 catalog 更新(运行时刷新)
  • 模型查询和匹配

4. tui:终端 UI 库

TUI 是一个独立的终端 UI 库,提供类似 React 的组件化开发模型,支持差分渲染。

核心接口
typescript 复制代码
interface Component {
    // 渲染组件为行数组
    render(width: number): string[];

    // 处理键盘输入(当组件有焦点时)
    handleInput?(data: string): void;

    // 是否接收按键释放事件(Kitty 协议)
    wantsKeyRelease?: boolean;

    // 使缓存失效(主题变化等)
    invalidate(): void;
}

interface TUI extends Component {
    children: Component[];       // 子组件
    terminal: Terminal;          // 终端抽象

    // 组件管理
    addChild(component: Component): void;
    removeChild(component: Component): void;
    clear(): void;

    // 焦点管理
    setFocus(component: Component | null): void;

    // Overlay(弹窗/浮层)
    showOverlay(component: Component, options?: OverlayOptions): OverlayHandle;
    hideOverlay(): void;

    // 生命周期
    start(): void;
    stop(): void;

    // 渲染
    requestRender(force?: boolean): void;

    // 输入监听
    addInputListener(listener: TuiInputListener): () => void;

    // 终端颜色
    queryTerminalBackgroundColor(options): Promise<RgbColor | undefined>;
    queryTerminalColorScheme(options): Promise<TerminalColorScheme | undefined>;
}
TUI 实现层次
复制代码
TUI (interface)
 └── TuiBase (abstract class, extends Container)
      ├── TuiMainScreen      // 主屏幕模式(终端滚动缓冲区)
      └── TuiAltScreen       // 替代屏幕模式(全屏,应用控制视口)

TuiMainScreen

  • 使用终端的主屏幕(normal screen buffer)
  • 输出追加到终端滚动缓冲区
  • 支持差分渲染:只重绘变化的行
  • 适用于常规 chat 模式

TuiAltScreen

  • 使用终端的替代屏幕 (alternate screen buffer,ESC[?1049h
  • 应用完全控制视口内容
  • 支持滚动、鼠标事件、文本选择
  • 适用于全屏模式
差分渲染机制

TUI 的核心性能优化是差分渲染

复制代码
requestRender(force?)
 │
 ├── 1. 收集所有子组件的 render() 输出
 │    └── 合并为完整的文档行数组
 │
 ├── 2. 与上一帧对比
 │    ├── 找到变化的行范围
 │    └── 处理 Kitty 图片(特殊协议)
 │
 ├── 3. 生成终端指令
 │    ├── 移动光标到变化区域
 │    ├── 输出变化的行
 │    └── 清理多余行
 │
 └── 4. 更新缓存状态
      ├── lastDocument = newDocument
      └── fullRedraws++(如果是强制重绘)
Overlay 系统

Overlay 是 TUI 的浮层/弹窗机制:

typescript 复制代码
interface OverlayOptions {
    width?: SizeValue;           // 宽度(数字或百分比)
    maxHeight?: SizeValue;       // 最大高度
    anchor?: OverlayAnchor;      // 锚点(center/top-left/...)
    offsetX?: number;            // 水平偏移
    offsetY?: number;            // 垂直偏移
    margin?: OverlayMargin | number;  // 边距
    visible?: (w, h) => boolean; // 可见性条件
    nonCapturing?: boolean;      // 是否不捕获焦点
}

interface OverlayHandle {
    hide(): void;                // 永久隐藏
    setHidden(hidden: boolean): void;  // 临时隐藏/显示
    focus(): void;               // 获取焦点
    unfocus(options?): void;     // 释放焦点
    isFocused(): boolean;        // 是否获得焦点
}

5. server / client / protocol

这三个包提供了 RPC(远程过程调用) 能力,允许外部程序通过 JSON-lines 协议控制 agent。

  • protocol:定义了 RPC 协议的消息格式(请求、响应、事件)
  • server :RPC 服务端框架(PiSessionBackendPiSessionRuntimeLiveSession
  • client :RPC 客户端 SDK(PiClientRemoteSession,支持自动重连、状态同步)

工具系统

内置工具

coding-agent 提供了一组内置工具(packages/agent/src/harness/tools/):

工具 文件 功能
Bash bash.ts 执行 shell 命令
Edit edit.ts 文本替换编辑
Read read.ts 读取文件内容
Write write.ts 写入文件
Find find.ts 文件搜索(按名称/glob)
Grep grep.ts 内容搜索(正则表达式)
Ls ls.ts 列出目录内容
NotebookEdit notebook-edit.ts Jupyter notebook 编辑
TodoRead todo-read.ts 读取待办事项
TodoWrite todo-write.ts 写入待办事项

Edit 工具的模糊匹配

Edit 工具(packages/agent/src/harness/tools/edit-diff.ts)实现了模糊匹配机制:

复制代码
applyEditsToNormalizedContent(content, edits)
 │
 ├── 1. 尝试精确匹配
 │    └── content.indexOf(oldText)
 │
 ├── 2. 如果失败,尝试模糊匹配
 │    ├── normalizeForFuzzyMatch(content)
 │    │    ├── 标准化 Unicode 引号
 │    │    ├── 标准化 Unicode 破折号
 │    │    ├── 标准化特殊空格
 │    │    └── 去除行尾空白
 │    └── fuzzyContent.indexOf(fuzzyOldText)
 │
 ├── 3. 检查重复匹配
 │    └── countOccurrences() > 1 → 报错
 │
 ├── 4. 检查重叠
 │    └── 排序后检查相邻 edit 是否重叠
 │
 └── 5. 应用替换
      ├── 精确匹配:直接替换
      └── 模糊匹配:保留原始行的未变部分

这个设计很巧妙------AI 生成的代码经常会有 Unicode 引号(如 "")或行尾空白不一致的问题,模糊匹配能有效解决这些场景。


会话管理与持久化

会话文件格式

会话文件采用 JSONL 格式(每行一个 JSON 对象),存储在 ~/.pi/agent/sessions/<encoded-cwd>/ 下。

文件命名:<timestamp>_<session-id>.jsonl

jsonl 复制代码
{"type":"session","version":3,"id":"<uuid>","timestamp":"2026-08-02T...","cwd":"/path/to/project"}
{"type":"message","id":"msg_001","message":{"role":"user","content":[{"type":"text","text":"Hello"}]}}
{"type":"message","id":"msg_002","message":{"role":"assistant","content":[{"type":"text","text":"Hi!"}]}}
{"type":"label","id":"lbl_001","targetId":"msg_002","label":"Greeting"}
{"type":"compaction","id":"cmp_001","usage":{"input":1000,"output":200,...}}

条目类型

类型 说明
session 会话头(必须第一行)
message 对话消息
label 消息标签(可引用其他条目)
compaction 压缩摘要记录
branch_summary 分支摘要
session_info 会话元信息(名称等)

自动压缩

当 token 使用接近上下文窗口限制时,系统会自动触发压缩

复制代码
检查 token 使用量
 │
 ├── 如果超过阈值:
 │    ├── 保留最近 N 条消息
 │    ├── 将早期消息摘要为压缩文本
 │    ├── 将摘要作为新的 system prompt 前缀
 │    └── 记录 compaction 条目
 │
 └── 设置:
      ├── compaction.enabled(默认 true)
      ├── compaction.reserveTokens(默认 16384)
      └── compaction.keepRecentTokens(默认 20000)

配置系统

三级配置合并

复制代码
全局设置 (~/.pi/settings.json)
    ↓ 合并
项目设置 (<project>/.pi/settings.json)
    ↓ 合并
运行时覆盖 (CLI 参数 / 会话内设置)

合并策略:深度合并(deepMergeSettings),嵌套对象递归合并,后者覆盖前者。

主要配置项

配置项 类型 默认值 说明
defaultProvider string - 默认 LLM provider
defaultModel string - 默认模型
defaultThinkingLevel ThinkingLevel "off" 默认推理等级
transport TransportSetting "auto" 传输协议
steeringMode "all" | "one-at-a-time" - 转向队列模式
followUpMode "all" | "one-at-a-time" - 后续队列模式
theme string - UI 主题
uiMode "regular" | "fullscreen" "regular" UI 模式
compaction.enabled boolean true 是否启用自动压缩
compaction.reserveTokens number 16384 预留 token 数
compaction.keepRecentTokens number 20000 保留最近消息的 token 数
terminal.showImages boolean true 是否显示图片
httpProxy string - HTTP 代理 URL
packages PackageSource\[\] - npm/git 包源
extensions string\[\] - 扩展文件路径
skills string\[\] - 技能文件路径
enabledModels string\[\] - 可循环的模型模式

扩展机制

Extensions(扩展)

扩展是 TypeScript 文件,可以:

  • 注册新工具
  • 添加 UI 组件(overlay)
  • 修改系统提示词
  • 注册新的 LLM provider
  • 添加自定义消息类型
typescript 复制代码
interface Extension {
    name: string;
    tools?: AgentTool[];
    uiComponents?: UIComponent[];
    systemPrompt?: string;
    providers?: Provider[];
}

扩展加载顺序

  1. 内置扩展(builtInExtensions
  2. Settings 中 extensions 配置的扩展
  3. CLI --extensions 参数指定的扩展

Skills(技能)

技能是可以通过 /skill:name 命令调用的功能模块:

  • 技能文件可以是 TypeScript 或 Markdown
  • 支持参数传递
  • settings.skills 中配置路径

Prompt Templates(提示词模板)

可复用的提示词模板:

  • 存储在 settings.prompts 指定的路径
  • 支持变量替换
  • 通过 /prompt 命令或 API 调用

Themes(主题)

UI 主题定制:

  • 定义颜色、样式
  • 存储在 settings.themes 指定的路径
  • 通过 settings.theme 激活

关键数据流

用户输入到 LLM 响应的完整流程

复制代码
用户输入 "帮我写一个函数"
 │
 ├── TUI 层
 │    ├── 键盘事件 → InputHandler
 │    ├── 输入编辑器收集文本
 │    └── 按 Enter 提交
 │
 ├── AgentSession
 │    ├── 创建 UserMessage
 │    ├── 追加到 state.messages
 │    └── 调用 agent.prompt()
 │
 ├── Agent
 │    ├── 将消息放入队列
 │    └── 启动 Agent Loop
 │
 ├── Agent Loop
 │    ├── convertToLlm(messages)  // AgentMessage[] → Message[]
 │    ├── transformContext()       // 消息转换
 │    └── streamFn(model, context) // 调用 LLM
 │
 ├── AI 层
 │    ├── 选择 API 实现(如 openai-completions)
 │    ├── buildParams()           // 构建请求参数
 │    ├── client.chat.completions.create()  // HTTP 请求
 │    └── 返回 AssistantMessageEventStream
 │
 ├── 流处理
 │    ├── text_delta → AgentEvent → TUI 渲染
 │    ├── toolcall_start → 工具准备
 │    ├── tool_execution → 工具运行
 │    ├── tool_result → 追加到 messages
 │    └── done → 检查是否继续
 │
 └── 输出渲染
      ├── 文本增量 → 差分渲染到终端
      ├── 工具调用 → overlay/状态更新
      └── 完成 → 恢复输入

关键文件索引

入口文件

文件 说明
packages/coding-agent/src/main.ts CLI 主入口(main() 函数)
packages/coding-agent/src/cli.ts CLI 分派入口
packages/coding-agent/src/rpc-entry.ts RPC 模式入口

核心模块

文件 说明
packages/agent/src/agent.ts Agent 核心类
packages/agent/src/agent-loop.ts Agent Loop 实现
packages/agent/src/types.ts Agent 类型定义
packages/coding-agent/src/core/agent-session.ts AgentSession 实现
packages/coding-agent/src/core/agent-session-runtime.ts AgentSessionRuntime
packages/coding-agent/src/core/session-manager.ts SessionManager
packages/coding-agent/src/core/settings-manager.ts SettingsManager
packages/coding-agent/src/core/model-runtime.ts ModelRuntime

AI 层

文件 说明
packages/ai/src/index.ts AI 包入口
packages/ai/src/api/openai-completions.ts OpenAI Completions API
packages/ai/src/api/anthropic.ts Anthropic Messages API
packages/ai/src/api/google.ts Google Gemini API
packages/ai/src/models.ts Model 类型和管理
packages/ai/src/models-runtime.ts ModelsRuntime

TUI 层

文件 说明
packages/tui/src/tui.ts TUI 接口和基础类
packages/tui/src/tui-main-screen.ts 主屏幕 TUI
packages/tui/src/tui-alt-screen.ts 替代屏幕 TUI
packages/tui/src/terminal.ts 终端抽象

工具

文件 说明
packages/agent/src/harness/tools/bash.ts Bash 工具
packages/agent/src/harness/tools/edit.ts Edit 工具
packages/agent/src/harness/tools/edit-diff.ts Edit 模糊匹配算法
packages/agent/src/harness/tools/read.ts Read 工具
packages/agent/src/harness/tools/find.ts Find 工具
packages/agent/src/harness/tools/grep.ts Grep 工具

总结

Pi Agent Harness 是一个工程质量极高的 TypeScript Monorepo 项目,几个亮点:

  1. 模块化极致:7 个独立 npm 包,每一层职责清晰
  2. 差分渲染 TUI:独立的终端 UI 库,性能优化到位
  3. 模糊匹配 Edit:解决 AI 生成代码的 Unicode 引号/空白问题
  4. RPC 模式:让 agent 可以被嵌入到 IDE、Web 应用等外部宿主
  5. 三级配置合并:全局 + 项目 + 运行时的灵活配置
  6. 会话 Fork 与分支:支持树形会话结构
  7. 供应链安全:pin 依赖、shrinkwrap、lifecycle script 审计

对于想要用 TypeScript 构建 AI Agent 工具的开发者来说,Pi 的架构设计(尤其是分层结构、Agent Loop、TUI 差分渲染、RPC 模式)非常值得学习。


项目地址https://github.com/earendil-works/pi-monorepo

许可证:开源

相关推荐
kyriewen3 小时前
别再这样写TypeScript了——Code Review中最常见的8个反模式
前端·javascript·typescript
用户09340777351413 小时前
HarmonyOS WPS Open SDK 实践:enableEdit 怎么映射预览与编辑
typescript
鱼饼Y13 小时前
AI时代,使用大模型学习LangChain (4)——LangGraph
typescript·langchain
带娃的IT创业者15 小时前
PostHog 的 TypeScript 原生移植:一场开源产品工程化的自我革命
javascript·typescript·开源·开源软件·posthog·技术重构
濮水大叔2 天前
不必把 Vue3 写成“麻花”:从状态碎片到对象协作,重新理解 Zova 的前端心智模型
前端·typescript·vue3·ioc·tsx·zova
退休倒计时3 天前
【每日一题】LeetCode 45. 跳跃游戏 II TypeScript
算法·leetcode·typescript
TunerT_TQ3 天前
GitHub深度工程评测:Cline 源码级工程评测|65k Star 开源AI编程助手的工程全景
typescript·开源·github·vs code·ai编程助手·开源供应链安全·静态源码审计
贩卖黄昏的熊3 天前
NestJS简明教程——安全
开发语言·javascript·安全·typescript·nest.js
theoweb33 天前
Typescript关于sort需要注意的地方
typescript