核心架构:一个循环解决所有问题

Pi的开发者Armin Ronacher和Mario Zechner做了一个关键判断——现代顶尖大语言模型(如Claude Sonnet)已经具备足够的推理和工具使用能力,框架层面不需要再做复杂的编排。基于这个判断,Pi的核心代码就是一个while循环,位于 packages/agent/src/agent-loop.ts

循环逻辑可以拆解为四步:

  1. 把用户消息发给LLM
  2. LLM返回"我要用工具" → 执行工具 → 把结果返回给LLM
  3. LLM继续思考,可能还要调用工具 → 重复步骤2
  4. LLM返回"我完成了" → 检查有没有新的用户消息 → 有就继续,没有就结束

没有状态机,没有DAG,没有递归调用。整个Agent的行为完全由LLM自身的推理能力驱动,框架只负责"对话-执行-反馈"这个最小闭环。

四个工具,覆盖全部操作

packages/coding-agent/src/core/tools/index.ts 中,Pi只定义了四个核心工具:

工具 功能 用途
read 读取文件内容 AI需要了解代码现状
bash 执行shell命令 运行git、npm、测试、grep、find等
edit 精确修改文件(查找替换) 修改现有代码
write 写入新文件 创建新文件

这里最值得注意的是bash工具的设计思路。Pi的哲学是"Bash就是你所需要的一切"——通过bash,AI可以调用grep、find、ls、git、npm,整个Unix工具链都成为Agent的能力延伸。不需要为每个功能单独封装一个工具,让LLM自己组合系统已有的命令行工具。

这个设计遵循的是经典的Unix哲学:每个工具做一件事,通过组合完成复杂任务。对独立开发者来说,这意味着你不需要花大量时间开发自定义工具——先看看系统里已经有什么。

技能系统:文档即能力

Pi大约25%的系统提示词用于"元文档"——教AI如何扩展自己的能力。这通过Skill系统实现,代码在 packages/coding-agent/src/core/skills.ts

工作流程是这样的:

  • 启动时:Pi扫描指定目录下的所有SKILL.md文件
  • 系统提示词中:只告诉AI有哪些技能可用(名称+描述),不包含详细内容
  • 按需加载:当任务匹配某个技能描述时,AI主动用read工具读取完整的SKILL.md
  • SKILL.md内容:包含具体操作步骤、脚本命令、示例

一个典型的SKILL.md可能长这样:

Usage
./extract-text.js <input.pdf>     # Extract text
./extract-tables.js <input.pdf>   # Extract tables

这个设计有两个精妙之处。第一,"渐进式披露"——系统提示词里只放索引信息,详细内容按需加载,避免token浪费。第二,热重载——Skill就是Markdown文件,修改后AI下次读取时立即生效,没有注册流程,不需要重启。编写即用,修改即生效。

转向队列:实时干预正在执行的Agent

这是Pi最有特色的交互设计,位于 packages/agent/src/agent.ts

设想一个场景:AI正在按计划执行一系列操作(工具A → 工具B → 工具C),你发现它走错方向了,发送一条消息"等等,不要用那个方法"。Pi设计了两种队列来处理这种情况:

  • 转向队列(steer):当前工具执行完后,立即处理你的消息,跳过剩余计划中的工具调用
  • 后续队列(followUp):等AI这一轮完全结束后再处理

这就像开车时"立刻转向"和"下一个路口再转"的区别。对于任何需要人机协作的Agent场景,这种分级干预机制都值得借鉴——既给用户实时纠正的能力,又不会因为强制终止导致状态不一致。

上下文管理:拒绝向量数据库

Pi明确拒绝了向量数据库、记忆银行等外部记忆系统。它的"记忆"策略在 compaction/compaction.ts 中实现,逻辑很直接:

  • 优先用真实代码:AI直接用read/grep读取项目文件,而不是依赖可能过时的摘要或嵌入
  • 对话历史管理:当上下文接近窗口上限时,把较早的对话压缩成结构化摘要
  • 摘要格式:保留目标、约束、进度、关键决策、下一步——是人类可读的结构化信息,不是不可解释的向量嵌入

这个选择背后有一个务实的判断:对于编码场景,源码本身就是最权威的"记忆",任何中间层的缓存和索引都可能过期。与其维护一套复杂的记忆系统,不如让AI每次都直接读取最新的源文件。

对独立开发者的实践意义

Pi的设计可以提炼出几条可直接应用的原则:

  • 工具设计做减法:不要为每个功能写一个自定义工具,先评估bash + 系统命令能否覆盖需求
  • 文档驱动能力扩展:用Markdown文件定义Agent的技能,比硬编码更灵活,迭代成本接近零
  • 渐进式加载提示词:系统提示词只放索引,详细指令按需读取,在有限的上下文窗口里最大化信息密度
  • 结构化摘要优于向量检索:用LLM生成人类可读的摘要来管理长对话,比嵌入方案更可控、更可调试
  • 设计干预机制:任何面向用户的Agent都应该允许实时纠正,而不是只能等它跑完

Pi的整体代码量和架构复杂度,完全在一个人可以掌控的范围内。它证明了一件事:当底层LLM足够强时,框架层的核心竞争力不是功能的丰富度,而是约束的精准度。在AI Agent领域,知道什么不做,可能比知道做什么更重要。