01-complexity-transfer-cover

02-plan-mode-choice

假设团队准备给 Coding Agent 增加规划能力。第一种方案是在核心里加入 Plan Mode:新的状态机、只读阶段、审批按钮、计划存储和执行切换都由产品统一维护。第二种方案只允许模型先写 PLAN.md,需要只读时切换 Tool Allowlist,需要执行时再由用户授权。两种方案都能产生计划,却不是同一种产品。

第一种把复杂度交给核心维护者,换来一致体验。第二种把复杂度交给配置、Extension 和操作者,换来可替换性。需要判断的是复杂度被放在哪里、由谁维护、失败时由谁恢复。

Pi 的固定版本把这种取舍写得很直接:核心不内置 MCP、Sub-agent、Plan Mode、Todo、后台 Bash 和逐工具权限弹窗,同时允许用 Extension、Skill、Package、文件、tmux、独立进程或容器补齐。这些需求由外部组合负责,核心不替所有用户规定唯一工作流。

这篇文章只回答一个问题:一项 Agent 能力什么时候应该进入核心,什么时候应该外置?外置之后又会产生什么账单?

一、极简的关键是收窄核心边界

03-six-no-evidence

04-extension-escape-evidence

把极简理解成“代码越少越好”,很容易走向两个错误结论:要么把缺少功能当作不成熟,要么把功能少本身当作先进。架构上的极简更严格:核心只承担跨工作流稳定、需要统一生命周期、难以从外部可靠组合的机制。带有明显团队偏好和治理差异的策略,优先留给扩展层。

在 Pi v0.82.1 / b4f2936 的 README 中,这条边界同时出现于两处。一处明确说 Pi 为保持核心精简,不内置 MCP、Sub-agent、Plan Mode、Todo、后台 Bash 和逐工具权限弹窗。另一处又列出 Extension 可以实现自定义 Tool、 Sub-agent、Plan Mode、Permission Gate、Sandbox 和 MCP Server Integration。

所以“不内置”不等于“不支持”。更准确的结构是:

核心提供稳定机制
  ├─ 模型调用与消息转换
  ├─ Agent Loop 与 Tool Runtime
  ├─ Session、事件和基础 UI
  └─ Extension / Skill / Package 入口

工作流策略由外部组合
  ├─ MCP 接入方式
  ├─ 多 Agent 编排
  ├─ 规划与审批阶段
  ├─ Todo 与长期任务状态
  ├─ 后台进程管理
  └─ 权限、隔离和网络策略

这条边界让核心 API 不必同时承诺所有工作流的语义。代价也很明确:没有产品统一承诺的部分,必须由其他人补上。

二、一次“不内置”,会把六种责任一起转移

05-six-responsibility-ledger

外置能力经常被描述成“写个 Extension 就行”。但一个功能从核心移到外部,转移的不只是实现代码,还包括至少六种责任。

转移对象核心内置时外置后
发现产品 UI 和文档统一暴露用户要知道该装哪个 Package、Skill 或 CLI
权限核心可以统一做阶段和操作授权Extension、容器、Shell 与操作者共同承担
观测事件、日志和 UI 可使用同一状态模型tmux、文件、子进程和远程服务各有一套状态
兼容核心版本与功能一起测试扩展作者要跟随 API、平台和依赖变化
升级官方迁移和回滚用户要管理 Package 来源、版本与供应链
恢复核心知道任务状态与生命周期外部进程中断后,恢复协议需要自行设计

极简会改变复杂度的所有权。对能够维护 CLI、容器、配置和 Extension 的团队,这种转移可能非常划算。对只想开箱即用、要求统一审计和支持承诺的团队,它可能只是把产品成本变成了集成成本。

因此,决定外置前先写清六项责任:谁负责发现、权限、观测、兼容、升级和恢复。后文每个能力都用这份责任合同判断,不再把“可由扩展实现”当作完成。

三、六类不内置能力,其实是六种不同决策

06-capability-placement-matrix

Pi 把多个“不做”并列在 Philosophy 中,但它们的理由并不完全相同。不能用一句“外部工具已经解决”概括全部。

1. MCP:标准接入能力是否属于默认核心

固定版本的立场是核心不内置 MCP,优先使用带 README 的 CLI,或者由 Extension 增加 MCP 支持。这里能够确认的是 Pi 的产品边界,不能推导 MCP 协议没有价值,也不能推导所有 MCP Host 都会把全部工具永久塞进上下文。

CLI 路线适合已有成熟命令行、输出可检查、需要管道组合的本地工程。MCP 或正式 API Tool 更适合远程 SaaS、跨语言生态、结构化发现和企业系统。究竟选择哪一条,取决于发现、认证、Schema、远程复用和治理要求。这里保留一条边界: Pi 把 MCP 视为可替换接入策略,而不是 Agent Kernel 的必要组成。

2. Sub-agent:启动进程与调度系统不是一回事

Pi 不提供唯一的内置 Sub-agent Tool,但固定文档给出 tmux、独立 Pi 实例、Extension 和第三方 Package 等组合路线。这些路线足以证明“可以组合多个 Agent”,却不自动提供任务所有权、取消传播、工作区隔离、结果协议和独立验收。

把 Sub-agent 外置,核心避免绑定一种编排模型。操作者则必须自己回答:谁能改哪个文件、迟到结果是否可接受、一个子任务失败后主任务如何恢复。能启动多个进程,不能证明已经具备生产调度器。

3. Plan Mode:状态文件与阶段策略不是同一个对象

把计划写入 PLAN.md 有实际优势:可见、可编辑、可 Diff、可跨 Session,也能进入 Git。固定文档还允许用 Tool Allowlist 形成只读探索阶段。但文件只表示“计划是什么”,不会天然保证“规划时禁止写入”“执行前必须批准”“切换后使用哪套权限”。

所以计划文件与 Plan Mode 的关系是:

PLAN.md = 可持久化状态
只读 Tool Profile = 动作边界
Owner Approval = 阶段门
执行 Profile = 下一阶段能力

核心不内置 Plan Mode,意味着团队可以自由组合这四项。也意味着阶段切换和审批一致性不再由产品兜底。

4. Todo:可见清单与调度状态不是一回事

TODO.md 适合单 Agent、中小任务和仓库中心工作流,因为它可以被人编辑、被版本控制、被后续 Session 读取。但当任务涉及依赖、并发、租约、重试和取消时,一份 Markdown 清单没有事务语义。

同时存在多份真相才是风险:文件写“步骤二进行中”,子进程已完成步骤三,用户以为任务准备发布,而 Gate 仍绑定旧产物。规模变大后,结构化任务系统承担状态一致性职责。

5. 后台 Bash:tmux 解决运行,不自动解决任务语义

Pi 建议把后台进程交给 tmux。tmux 确实已经处理 Session、Pane、断开重连、输出观察和人工接管,核心无需重复实现一个终端复用器。但 Agent 任务仍需要进程命名、PID 或 Pane 归属、退出状态、日志截断、端口冲突、清理和恢复规则。

tmux 解决“进程继续跑”。Harness 仍要解决“这个进程属于哪个 Attempt、完成后怎样进入下一状态、取消后如何确认副作用停止”。外置减少核心代码,不会自动得到 Durable Workflow。

6. 权限弹窗:不弹窗不等于没有安全问题

固定版本没有逐工具权限弹窗,并建议使用容器或由 Extension 构造适合环境的确认流。这里必须区分 Project Trust、 Tool Gate、操作系统隔离、网络策略、Secret 隔离和人工审批。它们控制的是不同风险。

权限弹窗不是完整安全边界:被批准的构建命令仍可能执行恶意依赖。它仍能阻止明显越权写入和未经批准的外部动作。把权限策略外置,更适合能够自行设计容器、路径和网络边界的用户,不适合把默认安全责任完全交给普通操作者的产品。

四、哪些能力应该进入核心:四项准入测试

07-four-admission-tests

设计自己的 Harness 时,不应机械复制“不要 MCP”或“只用四个 Tool”。更稳妥的方法是让每项候选能力通过四项测试。

测试一:它是不是跨工作流稳定机制

消息转换、Tool Call 生命周期、Session 身份和事件顺序通常跨工作流存在。用不用 MCP、怎样规划、是否启动 Sub-agent 则高度依赖团队。越接近稳定机制,越适合核心。越接近偏好策略,越适合外置。

测试二:它是否需要统一生命周期

如果能力必须与任务版本、取消、重试、幂等和恢复共享状态,只放在松散脚本里会产生双重真相。此时即使 UI 可以外置,核心也应提供最小状态契约和事件接口。

测试三:外部组合是否真的可靠

“可以用 Shell 拼起来”只证明可调用,不证明错误传播、跨平台行为、输出上限和恢复语义可靠。若团队每次都要重复实现同一套脆弱胶水,外置成本可能已经高于核心抽象成本。

测试四:默认开启的风险是否可接受

进入核心通常意味着更容易被默认使用。远程 Tool、后台进程和高权限 Shell 需要清楚的权限与审计边界。无法提供安全默认值时,保持显式安装和显式授权可能更合理。

四项测试可以形成一张决策表:

结论适合位置典型条件
稳定、共享生命周期、外部组合脆弱、风险可控核心机制Session ID、Tool 生命周期、事件与取消协议
需要执行权限和事件 Hook,但工作流有差异Extension自定义 Tool、Permission Gate、MCP Adapter
主要提供知识、步骤和按需说明Skill / READMECLI 使用方法、项目流程、检查清单
主要保存人机可见状态文件PLAN、TODO、交接记录,但不冒充事务系统
已有成熟运行与隔离系统外部工具tmux、容器、Git、CI
跨团队远程复用且需独立治理独立服务企业 API、MCP Server、Durable Workflow

五、极简 Harness 的收益必须和代价成对出现

08-benefit-cost-pair

Pi 这条路线的真实收益是控制力。默认核心较窄,用户更容易看到模型收到了什么、哪些能力来自 Extension、状态存在哪里,也更容易替换某一层而不接受完整产品工作流。对研究 Harness、嵌入自有产品、已有成熟 CLI 和平台治理的团队,这种透明度很有价值。

对应代价是组合责任。扩展越自由,来源、版本、权限和兼容越分散。外部状态越多,恢复与审计越难统一。默认保护越少,操作者越需要理解终端、依赖和隔离风险。较弱模型也可能更依赖专用 Tool 与结构化反馈,不能因为强模型能熟练使用 Bash,就把四 Tool 路线当成普遍最优。

本文没有运行极简与产品型 Harness 的受控对照,无法给出成功率、Token、速度、维护工时或安全收益数字。作者早期博客中的历史数字和个人体验只能解释动机,不能当作本文环境的实测结果。

六、最终产物:一份复杂度归属合同

09-ownership-contract-close

团队决定不内置某项功能时,不能只写一句“以后用 Extension”。至少应留下以下合同:

capability: plan-and-approval
core_mechanism:
  - task_version
  - tool_profile
  - owner_gate
external_strategy:
  state: PLAN.md
  implementation: project_extension
responsibility:
  discovery: platform_team
  permission: security_team
  observability: extension_events
  compatibility: extension_owner
  recovery: workflow_owner
fallback:
  disable_extension: true
  preserve_plan_file: true
evidence_required:
  - read_only_stage_test
  - approval_bypass_test
  - stale_plan_rejection

这份合同用于防止“外置”变成无人负责。每次把能力移出核心,都应明确谁提供发现、权限、观测、兼容、升级和恢复。否则核心看起来很小,系统复杂度只是散落到了没人维护的角落。

Pi 的极简主义提供了一组具体追问:这项能力为什么必须进入核心?如果不进入,逃生口在哪里?复杂度会转移给谁?失败后谁能证明状态仍然可信?

四个问题都有答案时,极简是一项可验收的架构选择。没有答案时,团队只是把产品缺口转移给使用者。

参考资料与证据边界

  1. Pi Coding Agent README,固定 Tag v0.82.1:https://github.com/earendil-works/pi/blob/v0.82.1/packages/coding-agent/README.md
  2. Pi Extension examples,固定 Tag v0.82.1:https://github.com/earendil-works/pi/tree/v0.82.1/packages/coding-agent/examples/extensions
  3. Mario Zechner,What I learned building an opinionated and minimal coding agent:https://mariozechner.at/posts/2025-11-30-pi-coding-agent/

固定 Tag 用于证明 Pi 当时明确列出的“不内置”能力与替代路线。作者博客只用于解释设计动机。内置/外置决策表、复杂度转移账单和 YAML 合同是本文作者综合,不是 Pi 官方协议。本文未运行极简与产品型 Harness 的受控比较,真实效果幅度未知。