基于 earendil-works/pi 的
@earendil-works/pi-agent-core, 用 LangChain 替代pi-ai作为 LLM 层的 Python 移植。本文是项目整体设计文档,涵盖已完成的核心运行时、生产化增强、AgentHarness、工具生态, 以及 Phase 4 Coding Agent CLI(fork grok TUI + 标准 ACP)。各阶段的完整详细设计见独立 spec 文档(§11 索引)。
| 项 | 说明 |
|---|---|
| 定位 | 非官方 Python 移植,忠实复现 pi 的 agent 循环语义,最终目标是构建完整的 Coding Agent CLI |
| LLM 层 | LangChain BaseChatModel.astream(),不移植 pi-ai 多厂商 registry |
| 核心原则 | 忠实移植 pi 循环语义;LangChain 仅作 StreamFn 边界适配——工具执行、turn 管理、事件协议均在 agent_loop.py,不委托 LangChain agents/ToolNode |
┌─────────────────────────────────────────────────────────────────┐
│ Coding Agent CLI (Phase 4, P0–P2 landed) │
│ forked grok TUI (ACP client) · pi_agent_cli (ACP agent) │
│ 标准 ACP stdio · 权限确认 · JSONL v3 │
├─────────────────────────────────────────────────────────────────┤
│ AgentHarness (Phase 3) │
│ Session 树 · phase 锁 · 三队列 · 写缓冲 · 19 种事件/hook │
│ Compaction · Skills / Templates · SystemPrompt · ExecutionEnv │
├─────────────────────────────────────────────────────────────────┤
│ Tool Ecosystem (Phase 3.5) │
│ read · bash · edit · write · grep · find · ls │
│ LangChain BaseTool 适配器 · MCP (via langchain-mcp-adapters) │
├─────────────────────────────────────────────────────────────────┤
│ Core Runtime (Phase 1–2.5) │
│ Agent · agent_loop · 事件协议 · StreamFn │
│ transform_messages · usage/cost · thinking/reasoning │
│ retries · max_turns · guardrail hooks · ContextBudget │
│ structured output · tool-result images │
├─────────────────────────────────────────────────────────────────┤
│ LangChain Adapter │
│ langchain_stream · langchain_convert · resolve_chat_model │
│ OpenAI / Anthropic / DeepSeek / init_chat_model │
└─────────────────────────────────────────────────────────────────┘
AgentMessage[] → transform_context() → convert_to_llm() → transform_messages()
↓
convert_to_langchain()
↓
stream_fn (LangChain astream)
↓
AssistantMessageEvent → AgentEvent
| Python 模块 | 职责 | pi (TS) 对应 |
|---|---|---|
pi_agent_core/messages.py |
规范消息(user/assistant/toolResult)、content blocks、Usage |
pi-ai messages |
pi_agent_core/types.py |
Model、AgentTool、contexts、event types、AgentLoopConfig |
packages/agent/src/types.ts |
pi_agent_core/event_stream.py |
EventStream / AssistantMessageEventStream |
pi-ai EventStream |
pi_agent_core/agent_loop.py |
核心循环:turns、工具执行、hooks、事件发射 | packages/agent/src/agent-loop.ts |
pi_agent_core/agent.py |
有状态 Agent 封装:prompt/steer/follow-up 队列、abort |
packages/agent/src/agent.ts |
pi_agent_core/transform.py |
跨 provider 回放(tool-call id 规范化、thinking 降级、图片剥离) | pi-ai transforms |
pi_agent_core/tools.py |
SimpleTool 辅助工厂 |
— |
pi_agent_core/validation.py |
Pydantic 参数校验(替代 pi 的 TypeBox) | — |
pi_agent_core/queues.py |
steering / follow-up 消息队列 | — |
pi_agent_core/adapters/langchain_stream.py |
StreamFn:LangChain astream() → pi 事件 |
packages/ai/src/stream.ts |
pi_agent_core/adapters/langchain_convert.py |
pi ⇄ LangChain 消息互转 | packages/ai/src/stream.ts |
pi_agent_core/adapters/langchain_tools.py |
LangChain BaseTool → AgentTool 适配 |
无(Python 特有) |
pi_agent_core/coding_tools/ |
7 个内置编码工具 + 截断/路径/互斥公共设施 | packages/coding-agent/src/core/tools/ |
pi_agent_harness/ |
Session 树、AgentHarness、Compaction、Skills/Templates、Env | packages/agent/src/harness/ |
prompt("Hello") 无工具时:
agent_start → turn_start → message_start(user) → message_end(user)
→ message_start(assistant) → message_update* → message_end(assistant)
→ turn_end → agent_end
有工具时:在 message_end(assistant) 后插入 tool_execution_* 与 toolResult message events,可能跨多轮 turn_start。
message_update 包装粒度 assistant-stream 事件:text_start/delta/end、thinking_start/delta/end、toolcall_start/delta/end。end 事件携带聚合结果。
- 并行工具顺序 —
tool_execution_end按完成顺序发射;toolResult消息按 tool call 源顺序持久化。 - terminate 语义 — 仅当同一批次全部 finalized 工具结果的
terminate=True时,跳过下一轮 LLM。 - StreamFn 契约 — 不向调用方抛异常;失败编码为
error事件 +stop_reason=error|aborted。 - Thinking/reasoning 门控 — reasoning 参数仅在
Model.reasoning=True且thinking_level != "off"时注入;同一标志驱动transform_messages的 thinking 历史剥除。 - Usage 累积取 per-field max — 不求和(避免累积快照被放大)。
- 结构化输出不破坏流式 —
response_schema走 prompt 注入 + 原生response_format;不用with_structured_output。
详细设计原稿:
docs/PLAN/PLAN-PHASE1.md
- 三种规范消息:
UserMessage、AssistantMessage、ToolResultMessage - Content blocks:
TextContent、ThinkingContent、ToolCallContent、ImageContent - 事件联合类型:
AgentEvent(agent_start/end、turn_start/end、message_*、tool_execution_*) - Model dataclass:
provider、model_id、context_window、supports_images、reasoning、base_url - AgentTool Protocol:
name/description/label/parameters/execute/execution_mode/prepare_arguments
忠实移植 pi agent-loop.ts 的双层循环:
- 内层:LLM 流式响应 → 解析 tool calls → validate →
before_tool_callhook → 并行/串行执行 →after_tool_call→ terminate 判定 - 外层:steering 消息注入(turn 边界)→ follow-up 消息注入(无 tool/steering 时)
- 关键 hooks:
should_stop_after_turn、prepare_next_turn、before/after_tool_call
有状态封装:prompt()/continue_()/abort()/wait_for_idle()/reset()/steer()/follow_up()。subscribe() 监听器按注册顺序 await,agent_end 为 settlement 屏障——is_streaming 在全部监听器完成后才清零。
langchain_stream.py:StreamFn实现——resolve_chat_model(model)构造ChatOpenAI/ChatAnthropic/ChatDeepSeek(或init_chat_model通用路径);bind_tools绑定工具 schema;astream流式映射text_delta/toolcall_delta/done/errorlangchain_convert.py:piMessage⇄ LangChainBaseMessage双向转换;system prompt 注入;tool call id/content 规范化
详细设计:
docs/specs/2026-05-25-phase2-production-enhancements-design.md审计与 P2.5 增强记录:docs/AUDIT/AUDIT-2026-07-02.md
从 LangChain 流式 chunk 的 usage_metadata 逐 chunk 累加(对齐 LangChain add_usage 语义),全 provider 统一读标准化字段。cost_calculator 回调将 token 数转为金额。三种真实 provider 报告形态(单次终报、互补分片、累积快照)均正确处理。
ThinkingLevel(off/minimal/low/medium/high/xhigh)映射为 provider 参数:Anthropic thinking.budget_tokens + 联动 max_tokens;OpenAI reasoning_effort;DeepSeek 官方 API 经 extra_body 发 thinking: enabled|disabled 与 reasoning_effort(它默认开启思考,所以闸门关闭时显式发 disabled;网关不发。见 Phase 6 规格 §5.4)。Anthropic thinking 块流式捕获为 ThinkingContent(含 signature 用于多轮工具回放),以 thinking_delta 事件实时发射。DeepSeek 风格网关流式返回的 reasoning_content(或 reasoning)同样捕获:provider="deepseek" 由 ChatDeepSeek 保留,provider="openai" 由 adapters/openai_reasoning.py 的 ChatOpenAI 子类保留(普通 ChatOpenAI 会丢掉它;冻结版只有 openai provider,靠这个才能显示思考;该子类只保留流,不回传)。DeepSeek 官方 API 开启思考时,之前 assistant 消息的这部分在后续请求里按 reasoning_content 回传(不回传会 400;adapters/deepseek_replay.py,Phase 6 规格 §5.5),网关不回传。
messages → normalize_tool_call_ids → downgrade_thinking → strip_unsupported_images → result
- normalize_tool_call_ids:OpenAI
call_xxx⇄ Anthropictoolu_xxx,维护映射保证 toolCall/toolResult 配对 - downgrade_thinking:
target_model.reasoning=False时剥除ThinkingContent - strip_unsupported_images:
supports_images=False时移除ImageContent,纯图片消息替换为占位文本
- 重试:首 token 前指数退避 + 抖动(
Retry-After感知),可配max_retries max_turns:超限抛MaxTurnsExceededError,Agent 转为 error-stop assistant messagetool_timeout:单工具超时 → error tool result(LLM 可见可自纠)
before_llm_call(context, budget):ContextBudget token 信号——压缩钩子挂载点after_llm_call(context, message):tripwire on raise(内容审查等)on_agent_end(context, messages):run 结束后收尾on_payload/on_response:观测裸请求/响应- 每个事件携带
run_id/ 1-basedturn_id
response_schema(pydantic model 或 JSON schema dict)→ prompt 注入 + OpenAI-style 原生 response_format → 解析结果存 AssistantMessage.structured_output。流式不受影响——JSON 文本仍以 text_delta 发射。
Anthropic 原生 image blocks;其他 provider 回退为 user-message 注入;supports_images=False 时自动剥离。
详细设计:
docs/specs/2026-07-03-phase3-agent-harness-design.md审计:docs/AUDIT/AUDIT-H1.md~docs/AUDIT/AUDIT-H4.md
独立发行包 pi-agent-harness(packages/pi-agent-harness/pi_agent_harness/),依赖 pi-agent-core。
- 与 pi v3 字节兼容的 append-only JSONL 格式:首行 header,之后每行一个 entry
- 11 种 SessionTreeEntry(pydantic discriminated union):message、thinkingLevelChange、modelChange、activeToolsChange、compaction、branchSummary、custom、customMessage、label、sessionInfo、leaf
- 树结构:
parentId构成树;分支 = 从任意历史 entry 续写;leaf entry 实现叶子指针移动 - 两种存储:
JsonlSessionStorage(文件 I/O + 内存索引)、MemorySessionStorage(纯内存,测试用) - 延迟写(Python 一侧的扩展,默认不开):
JsonlSessionRepo.create({"deferred": True})返回DeferredJsonlSessionStorage(JsonlSessionStorage的子类)。第一条messageentry 到来之前,entry 只记在内存里;那时才一次写下 header、攒下的 entry 与这条消息。没发过消息的会话不留文件,list、find、has_session也看不到它。pi-agent-cli的session/new用它(计划决策 17)。 - Repo:create/open/list/delete/fork;fork 语义忠实对齐 pi
getEntriesToFork
平行于 Agent,不包 Agent 类——两者都直接驱动 run_agent_loop。
- Phase 锁:
idle/turn/compaction/branch_summary/retry——单占用模型 - 三队列:steer、follow_up(drain 时发
queue_update)、next_turn(任何时刻可入队,下次prompt()时注入) - 写缓冲:turn 中 setter 不直接写 session,flush 在 turn 边界按序重放
- 事件系统:11 种 broadcast + 8 种 hook(互斥通道,hook 带返回值)
- 持久化时序不变量:
message_end→ 先落盘后广播;turn_end→ 先广播再 flush 再save_point;agent_end→ flush + idle +settled - Run 失败合成:loop 异常逃逸时构造失败 assistant message,完整走
_handle_agent_event保证事件流闭合
- token 估算:
estimate_context_tokens混合真实 usage + chars/4 启发式 - Cut point 算法:从尾部向前累积直到
keep_recent_tokens,找合法切点(toolResult 不可切);split-turn 检测 + 前缀单独摘要 - 结构化摘要:Goal / Constraints / Progress / Key Decisions / Next Steps / Critical Context;迭代更新模式保留旧信息
- auto_compact(Python 扩展):
turn_end后检查should_compact,命中则在 turn 间隙执行——失败不打断 run
求两路径的最深公共祖先,收集被放弃分支 entries 生成 branch summary。session_before_tree hook 可 cancel / 自供 summary / 打 label。
- Skills:递归扫描目录,
SKILL.md为叶子;YAML frontmatter(name/description/disable-model-invocation);.gitignore/.ignore/.fdignore叠加忽略;诊断模型(warning 不阻断) - Prompt Templates:目录取
.md子文件;$1..$n/$@/$ARGUMENTS/${@:N:L}参数替换;shell 风格引号分词 - 系统提示注入:
format_skills_for_system_prompt输出 agentskills.io 风格 XML 块
- FileSystem + Shell 两个 Protocol(
@runtime_checkable);Python 化:异常代替 pi 的Result<T,E> - LocalExecutionEnv:
pathlib+asyncio.to_thread(FS);asyncio.create_subprocess_shell(Shell,Windows kill 进程组) - 错误层级:
FileError/ExecutionError/SessionError/CompactionError/BranchSummaryError/AgentHarnessError,归一化函数normalize_harness_error
四种自定义 role:bashExecution、custom、branchSummary、compactionSummary。harness_convert_to_llm 决定各 role 如何到达 LLM(bashExecution → user 消息含代码块;未知 role → 丢弃)。
详细设计:
docs/specs/2026-07-03-phase3.5-tool-ecosystem-design.md
归层在 pi_agent_core/coding_tools/——工具是 loop 的消费者,不是 loop 的一部分。7 个工具均实现 AgentTool 协议,不反向依赖 agent_loop/Agent。零新增运行时依赖。
| 工具 | 归组 | 核心行为 |
|---|---|---|
read |
编程 + 只读 | 文本 2000 行/50KB 截断 + offset/limit 分页;图片魔数嗅探 → ImageContent |
bash |
编程 | 真实 shell(bash -c / Git Bash on Windows);合并 stdout+stderr 尾部截断;超时/abort 杀进程树;100ms 节流流式更新 |
edit |
编程 | 精确文本替换(唯一匹配保证、重叠拒绝、CRLF/BOM 往返);unified-diff details |
write |
编程 | 创建/覆盖 + 自动递归建目录;经 mutation_queue 串行 |
grep |
只读 | ripgrep 优先(--json 流式解析);纯 Python 回退;单行 500 字符截断 |
find |
只读 | 纯 Python glob walk;basename vs 路径 pattern 语义对齐 pi 对 fd 的调用 |
ls |
只读 | 大小写不敏感排序,/ 目录后缀,含 dotfiles |
truncate.py:truncate_head/truncate_tail/truncate_line+TruncationResult,数值与 pi 逐项相同mutation_queue.py:按realpath归一的asyncio.Lock注册表,write/edit 同文件互斥path_utils.py:resolve_to_cwd、glob→regex 翻译、图片魔数嗅探(stdlib 实现,不依赖 Pillow)
create_coding_tools(cwd) # read/bash/edit/write(pi 默认组)
create_read_only_tools(cwd) # read/grep/find/ls(无修改保证)
create_tool(name, cwd) # 按名构造单工具
create_all_tools(cwd) # 全部 7 个from_langchain_tool(tool: BaseTool) -> AgentTool:schema 提取(tool_call_schema 优先,剔除 LangChain 注入参数)、结果归一(str → text 块、content blocks 列表 → text/image 映射、artifact → details)、异常冒泡为 error tool result。MCP 工具经 langchain-mcp-adapters 产出的 BaseTool 走同一适配器。
详细设计:
docs/specs/2026-08-25-phase4-coding-agent-cli-design.mdTUI 上游:xai-org/grok-build(Apache-2.0);协议:ACP
不自研 Python TUI。 Fork grok-build 的 pi-pager 作 ACP Client;本仓库 Python 作 ACP Agent,只走标准 ACP,不实现 x.ai/*。
pi (Rust TUI) --stdio 标准 ACP--> python -m pi_agent_cli --> AgentHarness
pi:全屏 TUI(Markdown/diff/权限 modal),spawn Python agent。pi acp/ Zed:同一 agent 给编辑器。pi -p:纯 Python headless。- 会话真源:JSONL v3。TUI 本地缓存不是真源。
整棵 grok-build Cargo workspace 已放进 tui/(不要只拷 pager 两 crate)。tui/ Apache-2.0,不进 Python sdist/wheel。它现在是硬分叉:不再合并上游,crate 名与行为都已与上游不同(tui/NOTICE)。
spawn 默认为 python -m pi_agent_cli(PI_AGENT_COMMAND / PI_PYTHON 可覆盖)。acp_send 丢弃 x.ai/* 扩展 RPC;空 auth_methods 跳过 xAI 登录;auto-update 关闭;交互路径不装 otel。家目录 PI_HOME / ~/.pi-python。斜杠菜单只有 /new /resume /model /exit(别名 /quit)与本地命令(/help /theme /settings /multiline /home);/resume 走标准 session/list + session/load,/model 走 ACP Session Config Options(session/set_config_option)。pi -p 转发到 python -m pi_agent_cli -p,不复用 grok headless。
before_tool_call 在 ask 模式下对每个工具调用发 ACP session/request_permission,除非该工具自己声明无害(MCP 风格的 annotations:readOnlyHint,或 destructiveHint 与 openWorldHint 都为 false;没声明就问,所以 bash/edit/write/workflow 与不带 annotations 的扩展工具都会问)。workflow 子代理的每次工具调用经桥接的 tool_call_gate 走同一权限层,按父会话里同名工具的 annotations 判定(见 Phase 7 spec §12「权限模型」)。Python agent:~/.pi-python/agent.toml(model、permission、skills、[agent].command)。Rust TUI:config.toml 由 TUI 自己的类型化 schema 解析——认识的键写错类型会让 zypi 以退出码 1 退出(顶层 permission = "ask"、[[models]]、不合法的 TOML),不认识的键被忽略;勿写 Python 的配置。示例见 packages/pi-agent-cli/agent.example.toml;Windows 见 docs/WINDOWS.md。
TUI 与 agent 之间的线上协议是标准 ACP,加上登记在册的 pi/ 扩展。新增一个 pi/ 方法、通知或 _meta 键,必须同时满足五条:
- 没有标准等价。 以 ACP 现行 schema / RFD 为准。例如模型选择走 Session Config Options,
session/set_model已被上游移除,所以不能再为它造pi/方法。 - Python 侧有生产者与测试。
- 在
docs/specs/登记方法名、方向、schema 和能力键(登记处:ACP 扩展登记)。 - wire 类型只定义一次,放在
pi-acp-lib(或同级的协议 crate),不留在 runtime crate 里。 - 经
initialize协商,对 Zed 等第三方客户端无害:Python agent 仍是通用的 ACP agent。
五条缺一,就不加;已有的、不满足的,按「降级 / 隐藏 / 删除」处理(计划 ADR3)。TUI 一侧不实现 x.ai/*:入口处丢弃以 x.ai/ 或 _x.ai/ 开头的扩展通知。
现状:登记处是 ACP 扩展登记(r44)。它登记八项在用的扩展(X1–X8:pi/session/delete、_meta.sessionId、_meta["pi/cwd"]、模型提示键、_meta.isReplay、终端的 _meta 键、_meta.yoloMode 与 _meta.autoMode、pi/yolo_mode_changed),每项写了名字、方向、schema、发送方、读取方、测试与状态,并逐条对照五条。结果:四项过不了第 ① 条(X1、X4、X7、X8),它们都有保留的决定(X1 与 X4 是计划的 ADR4;X7 与 X8 是 Auto 模式设计的 D5,加上用户在 2026-11-04 的决定:pager 继续用它们,不改走标准通道,计划 1.P3 关闭);没有一项在 initialize 里协商(第 ⑤ 条);有 Rust 一侧的六项,类型都不在 pi-acp-lib(第 ④ 条)。第 ④、⑤ 条只对新项适用;已在用的这八项保持原样,登记就是缺口的记录(用户,2026-11-05;登记的 §7 Q1)。packages/pi-agent-cli/AGENTS.md 只留 agent 一侧的实现说明(「Extensions beyond ACP v1」一节)。
全量 Mock stream_fn,无需 API key,镜像 pi agent-loop.test.ts 场景。
| 域 | 覆盖要点 |
|---|---|
| 核心循环 | 事件序列(纯对话/单工具/多工具/parallel/sequential)、steering、follow-up、abort、should_stop_after_turn |
| Usage/Thinking | mock chunk 带 usage_metadata/thinking 块,验证提取与累积 |
| Transform | tool-call id 跨 provider 重写、thinking 降级、图片剥离、端到端混合场景 |
| Harness | JSONL 往返(与 pi v3 fixture 互读)、持久化时序不变量、hook/队列、turn 边界 setter、run 失败合成 |
| Compaction | cut point / toolResult 非切点 / LLM 摘要 / hook 自供 / branch summary / auto_compact |
| Skills/Templates | frontmatter 解析、ignore 规则、校验诊断、参数替换 |
| 编码工具 | 截断边界、offset/limit 组合、图片魔数、edit 唯一匹配/重叠/CRLF 往返、bash 三终态、grep rg+回退、mutation queue 串行 |
| LangChain 适配 | str/blocks/artifact 返回归一、schema 提取、异常冒泡 |
| Real-API 冒烟 | scripts/smoke_real_api.py 对接 SiliconFlow(OpenAI 兼容) |
pydantic>=2.0、langchain-core>=0.3.0、typing-extensions>=4.6- 可选 providers:
langchain-openai、langchain-anthropic、langchain-deepseek
- 依赖
pi-agent-core-lc PyYAML>=6(skills frontmatter)、pathspec>=0.12(gitignore 匹配)
- 依赖
pi-agent-core-lc、pi-agent-harness-lc、agent-client-protocol>=0.12,<1(1.0 删了acp.helpers与text_block,还没有移植) - PyPI 发布(
.github/workflows/release.yml);RustzypiTUI 不在 PyPI
pytest>=8.0、pytest-asyncio>=0.24、ruff>=0.16
| 文档 | 内容 |
|---|---|
AGENTS.md |
项目总览、模块表、不变量、当前状态 |
README.md |
公开 README、Quick start、Roadmap |
docs/DESIGN.md(本文) |
项目整体设计方案 |
docs/PLAN/PLAN-PHASE1.md |
Phase 1 原始规划(历史) |
docs/AUDIT/AUDIT-2026-07-02.md |
核心层审计:缺陷修复 + Phase 2.5 增强追踪 |
docs/AUDIT/AUDIT-H1.md ~ docs/AUDIT/AUDIT-H4.md |
Harness 各批次审计 |
docs/AUDIT/SPIKE-P0-GROK-TUI.md |
Phase 4 P0:pager x.ai/* 拆除清单 |
docs/specs/2026-05-25-phase2-*.md |
Phase 2 详细设计:usage/cost、thinking、transform |
docs/specs/2026-07-03-phase3-*.md |
Phase 3 详细设计:Session 树、AgentHarness、Compaction、Skills、Env |
docs/specs/2026-07-03-phase3.5-*.md |
Phase 3.5 详细设计:7 个内置工具 + LangChain 适配器 |
docs/specs/2026-08-25-phase4-coding-agent-cli-design.md |
Phase 4:fork grok TUI + 标准 ACP agent |
docs/specs/2026-09-02-phase5-prompt-engine-design.md |
Phase 5:system prompt 装配 |
docs/specs/2026-09-24-phase6-extended-integrations-design.md |
Phase 6:Git 上下文与多提供商测试矩阵 |
docs/specs/2026-09-22-phase7-extension-api-design.md |
Phase 7:ExtensionAPI 与扩展包 |