
1. 为什么我要从 Claude Code 转向 pi1.1 一个“全能选手”带来的真实困扰Claude Code 刚出来那阵子我几乎是第一时间就装上了。原因很简单它能直接读写文件、跑终端命令、理解整个项目结构还能在 VS Code 里当插件用确实把“命令行里的 AI 编程助手”这件事做到了一个新高度。那段时间我逢人就推荐觉得这就是 Coding Agent 的终极形态。但用久了之后一些问题开始暴露出来。最直接的感受是它太重了。每次启动要加载一大堆上下文工具调用链路很长遇到稍微复杂一点的任务它会在“思考—调用工具—再思考”之间反复横跳token 消耗肉眼可见地往上涨。我做过一个粗略统计同样一个“重构某个模块并补测试”的任务Claude Code 平均要消耗 3 到 5 倍于一个轻量 Agent 的 token 量。这不是钱的问题而是响应速度和可控性的问题。另一个让我不舒服的点是黑盒感。Claude Code 内部到底怎么调度工具、怎么决定下一步做什么用户基本看不到。它像一个很聪明的实习生你交代一件事它自己闷头干完中间过程你插不上手。对于简单任务这很爽但对于需要精细控制的场景比如“只改这个文件的第 30 到 50 行别动其他任何东西”它经常会“顺手”帮你多改几处你还得回头 review 半天。1.2 pi 的出现四个工具够用就好后来我在社区里看到有人提到pi这个 Coding Agent。第一眼吸引我的是它的宣传语只用 4 个工具。我当时的第一反应是“这能干啥”第二反应是“这不就是我要的么”。pi 的设计哲学和 Claude Code 完全相反。Claude Code 是“我什么都能做你别管我怎么做的”pi 是“我只做四件事但每件事你都看得清清楚楚”。这四个工具分别是读文件读取指定路径的文件内容支持按行范围读取。写文件创建或覆盖文件支持精确到行的修改。执行命令在终端里跑命令返回标准输出和错误输出。搜索在项目里按关键词或正则搜索快速定位代码。就这四个。没有花哨的“规划器”没有复杂的“记忆系统”没有内置的“代码理解引擎”。pi 把所有的决策权交还给模型本身而模型要做的就是在这四个工具之间做选择。我一开始担心这样会不会太简陋但实际用下来发现对于 80% 的日常编码任务这四个工具已经足够了。读文件让你了解现状搜索让你定位问题写文件让你修改代码执行命令让你验证结果。一个完整的“理解—定位—修改—验证”闭环四个工具全部覆盖。1.3 oh-my-pi给极简 Agent 配个全家桶pi 本身很轻但轻意味着很多东西要自己配。比如你想让它支持不同的模型提供商想让它有更好的终端交互体验想让它能记住一些常用配置这些 pi 本身都不管。于是就有了oh-my-pi可以理解为 pi 的“全家桶”配置层。oh-my-pi 做的事情很实在它把 pi 的安装、配置、模型接入、主题、快捷键、常用命令别名全部打包好你装完就能用不用自己一个个去折腾。它有点像 zsh 和 oh-my-zsh 的关系——pi 是那个简洁的 shelloh-my-pi 是让你用起来更顺手的配置框架。我现在的日常组合就是pi 作为核心 Agentoh-my-pi 作为配置和体验层。这套组合跑下来我的感受是它没有 Claude Code 那么“聪明”但它的可预测性、可控性和响应速度让我在大多数场景下更愿意用它。2. pi 的四个工具到底怎么用2.1 读文件不只是 cat 那么简单pi 的读文件工具表面上看就是“把文件内容读出来”但实际用起来有几个细节很关键。第一它支持按行范围读取。比如一个 2000 行的文件你只想看第 100 到 150 行直接指定范围就行不用把整个文件塞进上下文。这个设计在大型项目里非常实用因为很多文件的绝大部分内容和你当前的任务无关全读进来纯属浪费 token。第二它返回的内容会带行号。这个细节看起来不起眼但当你需要让模型精确修改某几行时行号就是坐标。模型可以说“把第 42 行改成 xxx”而不是“把那个函数里的某一行改成 xxx”精确度完全不一样。第三它不会自动截断。有些工具为了省 token会默认只读前 N 行导致模型看不到文件后半部分的关键逻辑。pi 的读文件是你指定多少就读多少读多少由你控制。实操心得我通常会让模型先读文件的前 50 行了解这个文件的整体结构和导入依赖然后再根据需要在后续步骤里读具体段落。这样比一次性读整个文件更高效模型也更容易聚焦。2.2 写文件精确到行的修改能力写文件是 pi 最核心的工具也是它和 Claude Code 差异最大的地方。Claude Code 的写文件通常是“整文件替换”或者“大段替换”你很难让它只改某一行。pi 的写文件支持三种模式整文件写入适合新建文件或完全重写。按行替换指定起始行和结束行用新内容替换这一段。按行插入在指定行号后插入新内容不覆盖原有内容。这三种模式组合起来就能实现非常精细的修改。比如你要在一个函数中间加一行日志用“按行插入”就行不用把整个函数重写一遍。再比如你要删掉某个废弃的配置项用“按行替换”把那一行替换成空行即可。这种精细控制带来的好处是diff 非常干净。你 review 代码时看到的就是你期望的那几行改动不会出现“模型顺手把缩进改了”“把不相关的注释删了”这类意外。注意事项按行修改的前提是你知道确切的行号。如果文件在你读取之后被其他操作修改过行号可能会偏移。所以我的习惯是读文件和写文件之间不要插入其他修改操作确保行号一致。2.3 执行命令终端就是你的验证器pi 的执行命令工具本质上就是帮你跑 shell 命令。但它的价值不在于“能跑命令”而在于跑完之后的反馈闭环。举个例子你让 pi 修改一个 Python 文件它改完之后你可以让它跑python -m pytest tests/test_xxx.py。如果测试通过说明修改没问题如果失败错误信息会直接返回给模型模型可以基于错误信息继续修改。这个“修改—验证—再修改”的循环是 Coding Agent 最核心的工作模式。pi 在执行命令时有两个细节做得很好超时控制默认有超时限制避免某个命令卡死导致整个 Agent 挂起。你可以根据需要调整超时时间。输出截断如果命令输出特别长比如跑了一个输出几千行的测试pi 会智能截断只保留关键部分比如错误摘要和最后几行避免上下文被撑爆。实操心得我习惯在让 pi 执行命令时加上21 | tail -50这样的管道确保错误输出也能被捕获同时只保留最后 50 行。这样既能看到关键信息又不会浪费 token。2.4 搜索快速定位不做无用功搜索工具是 pi 四个工具里最容易被低估的一个。很多人觉得“搜索嘛不就是 grep”但实际上它在 Agent 工作流里的作用非常大。当模型面对一个陌生项目时它不可能把每个文件都读一遍。这时候搜索就是它的“眼睛”。比如你要改一个函数但不知道这个函数在哪些地方被调用了直接搜函数名就能快速定位所有调用点。再比如你要找一个配置项的定义位置搜关键词就能直接跳到目标文件。pi 的搜索支持正则表达式这意味着你可以做更复杂的匹配。比如搜def\shandle_.*_request就能找到所有处理请求的函数定义。这个能力在重构和代码审查场景下特别有用。常见问题搜索时如果关键词太宽泛会返回大量结果反而干扰模型判断。我的经验是先用宽泛关键词定位大致范围再用精确关键词缩小目标。比如先搜config找到相关文件后再搜database_url定位具体配置项。3. oh-my-pi 全家桶配置实战3.1 安装与初始化十分钟搞定oh-my-pi 的安装过程比我预想的简单。它本质上是一个配置脚本集合你把它 clone 下来跑一个安装脚本它就会帮你把 pi 装好并把配置文件放到正确的位置。我是在 macOS 上操作的步骤大致如下# 克隆 oh-my-pi 仓库 git clone https://github.com/example/oh-my-pi.git ~/.oh-my-pi # 运行安装脚本 cd ~/.oh-my-pi ./install.sh安装脚本会做几件事检查系统环境确认 pi 的依赖是否满足。下载并安装 pi 二进制文件到~/.local/bin或/usr/local/bin。把默认配置文件复制到~/.config/pi/目录。在 shell 配置文件如.zshrc或.bashrc里添加环境变量和别名。装完之后重新加载 shell 配置输入pi --version能看到版本号就说明安装成功了。注意事项如果你之前已经装过 pi安装脚本会提示你是否覆盖现有配置。建议先备份~/.config/pi/目录再选择覆盖避免丢失自定义配置。3.2 模型接入不绑定任何一家pi 本身不绑定任何模型提供商你可以通过配置文件接入不同的模型。oh-my-pi 默认提供了一套配置模板你只需要填入自己的 API Key 和模型名称即可。配置文件通常长这样# ~/.config/pi/config.yaml providers: - name: openai api_key: ${OPENAI_API_KEY} base_url: https://api.openai.com/v1 models: - gpt-4o - gpt-4o-mini - name: anthropic api_key: ${ANTHROPIC_API_KEY} base_url: https://api.anthropic.com models: - claude-sonnet-4-20250514 - claude-opus-4-20250514 - name: deepseek api_key: ${DEEPSEEK_API_KEY} base_url: https://api.deepseek.com/v1 models: - deepseek-chat - deepseek-coder default_model: claude-sonnet-4-20250514这个配置的好处是你可以随时切换模型。比如日常任务用便宜快速的模型复杂重构用能力更强的模型。切换只需要改一行default_model或者用命令行参数临时指定。实操心得我通常会把default_model设为一个性价比高的模型比如 deepseek-chat然后在遇到复杂任务时用pi --model claude-opus-4-20250514临时切换到更强的模型。这样既控制了成本又保证了关键任务的质量。3.3 主题与快捷键让终端用起来更顺手oh-my-pi 自带了几套终端主题你可以根据自己的喜好切换。我比较喜欢暗色主题因为长时间盯着终端亮色背景容易疲劳。快捷键方面oh-my-pi 预设了一些常用操作的绑定快捷键功能CtrlC中断当前操作CtrlL清屏CtrlR搜索历史命令Tab自动补全文件路径CtrlO打开配置文件这些快捷键和常见的终端工具保持一致上手成本很低。如果你有自己习惯的快捷键也可以在配置文件里覆盖。3.4 常用命令别名少打几个字oh-my-pi 还预设了一些命令别名比如alias ppi alias pcpi --model claude-sonnet-4-20250514 alias pdpi --model deepseek-chat alias prpi --resume这些别名看起来不起眼但每天用下来能省不少打字时间。特别是pr恢复上次会话在调试过程中经常需要反复回到同一个上下文有这个别名就方便很多。4. 实际工作流从零开始完成一个任务4.1 场景设定给一个 Flask 项目加接口为了让你更直观地理解 pi 的工作方式我拿一个真实场景来演示给一个 Flask 项目加一个“获取用户订单列表”的接口。项目结构大致如下myapp/ ├── app.py ├── models.py ├── routes/ │ ├── __init__.py │ ├── user.py │ └── order.py ├── tests/ │ ├── test_user.py │ └── test_order.py └── requirements.txt目标是在routes/order.py里加一个GET /orders/user_id接口返回该用户的订单列表并补上对应的测试。4.2 第一步让 pi 了解项目结构我首先让 pi 读一下项目根目录和关键文件pi 读一下 app.py 和 routes/order.py告诉我这个项目怎么组织路由的pi 会调用读文件工具把这两个文件的内容读出来然后基于内容回答。它会告诉你app.py里注册了蓝图routes/order.py里定义了一个蓝图对象order_bp目前只有一个GET /orders接口。这一步的目的是建立上下文。模型需要知道现有的代码风格、导入方式、错误处理模式才能写出风格一致的代码。4.3 第二步搜索现有模式接下来我让 pi 搜索一下现有的接口是怎么写的pi 搜索一下 routes 目录下所有 order_bp.route 的定义pi 会调用搜索工具返回类似这样的结果routes/order.py:12: order_bp.route(/orders, methods[GET]) routes/order.py:25: order_bp.route(/orders/int:order_id, methods[GET])看到这个结果模型就知道现有的接口用order_bp.route装饰器路径参数用int:order_id这种 Flask 风格。新接口也应该保持一致。4.4 第三步读具体文件确定插入位置然后我让 pi 读一下routes/order.py的完整内容确定新接口应该加在哪里pi 读一下 routes/order.py 的第 1 到 40 行pi 返回带行号的内容模型可以看到第 25 到 35 行是GET /orders/int:order_id接口的实现。新接口可以加在这个接口之后保持相关接口聚在一起。4.5 第四步写代码现在模型已经掌握了足够的信息可以开始写代码了。我给出具体指令pi 在 routes/order.py 的第 36 行后插入一个新接口 GET /orders/user/int:user_id 返回该用户的所有订单用 JSON 格式 参考现有接口的错误处理方式pi 会调用写文件工具在指定行号后插入新代码。插入的内容大致如下order_bp.route(/orders/user/int:user_id, methods[GET]) def get_user_orders(user_id): user User.query.get(user_id) if not user: return jsonify({error: User not found}), 404 orders Order.query.filter_by(user_iduser_id).all() return jsonify([order.to_dict() for order in orders]), 200这段代码的风格和现有接口保持一致用jsonify返回 JSON用User.query.get查询用户错误时返回 404。4.6 第五步执行测试验证代码写完后我让 pi 跑一下现有测试确保没有破坏其他功能pi 执行 pytest tests/ -vpi 会调用执行命令工具跑测试并返回结果。如果所有测试通过说明新代码没有引入回归问题。如果有测试失败错误信息会返回给模型模型可以继续修改。4.7 第六步补测试最后我让 pi 给新接口补一个测试pi 在 tests/test_order.py 里加一个测试验证 GET /orders/user/1 返回 200 和正确的订单列表pi 会先读tests/test_order.py了解现有测试的写法然后在合适的位置插入新测试函数。插入的测试大致如下def test_get_user_orders(client, init_database): response client.get(/orders/user/1) assert response.status_code 200 data response.get_json() assert isinstance(data, list) assert len(data) 0然后再跑一次测试确认新测试通过。整个流程下来pi 一共调用了大约 8 到 10 次工具消耗的 token 量比我用 Claude Code 做同样任务少了将近一半。更重要的是每一步我都能看到它在做什么如果某一步方向不对我可以立刻打断并纠正。5. 常见问题与排查技巧5.1 模型不按预期调用工具怎么办这是最常见的问题。有时候你让模型读文件它却去执行命令你让它写文件它却只返回一段代码文本而不实际写入。原因通常是提示词不够明确。pi 的模型需要明确的指令来触发工具调用。比如“看一下这个文件”可能被理解为“描述一下这个文件”而“读一下这个文件的内容”就更明确。我的经验是在指令里直接使用工具名称。比如不要说“看看 order.py”而要说“读一下 order.py”。不要说“改一下这个函数”而要说“用写文件工具把第 30 到 35 行替换成以下内容”。不要说“跑一下测试”而要说“执行 pytest 命令”。这样模型更容易理解你的意图工具调用的准确率会大幅提升。5.2 行号偏移导致修改错位前面提到过按行修改的前提是行号准确。如果读文件和写文件之间文件被其他操作修改过比如你手动改了文件或者另一个 Agent 改了文件行号就会偏移导致修改错位。排查方法在写文件之前重新读一次目标文件的相关段落确认行号没有变化。如果变化了重新计算行号再写入。避坑技巧我习惯在让 pi 写文件之前先让它读一下目标行附近的内容并在指令里带上“确认第 X 行是 YYY如果不是请先告诉我”。这样如果行号偏移模型会先反馈而不是直接写错。5.3 命令执行超时或卡死有些命令比如启动一个服务器、跑一个长时间的训练任务会一直运行不退出导致 pi 的执行命令工具超时。解决方法给命令加上超时参数或者用timeout命令包裹。比如timeout 30s python -m pytest tests/这样即使测试卡住30 秒后也会自动终止pi 能拿到超时信息并继续处理。5.4 搜索结果太多模型无法聚焦搜索关键词太宽泛时会返回大量结果模型可能被淹没在信息里反而找不到重点。解决方法分两步搜索。先搜一个宽泛的关键词定位文件范围再在指定文件里搜精确关键词。比如pi 先在 routes/ 目录下搜索 order pi 然后在 routes/order.py 里搜索 def get_这样逐步缩小范围模型更容易聚焦到目标代码。5.5 模型切换后行为不一致不同模型对工具调用的理解能力不一样。比如某些模型更擅长写代码但不太会主动调用工具某些模型工具调用很积极但代码质量一般。解决方法根据任务类型选择模型。日常的读文件、搜索、简单修改用快速便宜的模型复杂的重构、架构设计用能力更强的模型。oh-my-pi 的配置支持随时切换不用重启。问题现象可能原因解决方法模型只返回文本不调用工具指令不够明确在指令里直接使用工具名称修改错位行号偏移写之前重新读目标段落确认行号命令超时命令未设置超时用 timeout 包裹命令搜索结果过多关键词太宽泛分两步搜索先宽后窄模型行为不一致模型能力差异按任务类型切换模型6. 我为什么最终选择 pi oh-my-pi6.1 可控性比智能更重要用了几个月 pi 之后我最大的感受是对于日常编码任务可控性比智能更重要。Claude Code 确实更“聪明”它能理解更复杂的指令能自己规划多步操作能处理更模糊的需求。但这种“聪明”是有代价的你不太清楚它下一步要做什么它的决策过程对你来说是个黑盒。当它做对时你很爽当它做错时你很难纠正只能重新描述需求希望它这次能理解对。pi 不一样。它的四个工具就是它的全部能力边界你知道它只能做这四件事。当它做错时你能清楚地看到是哪一步错了是读错了文件还是搜错了关键词还是写错了行号。你可以精确地纠正那一步而不是推翻重来。这种可预测性在长期使用中带来的效率提升远比“偶尔很聪明”更有价值。6.2 轻量带来的速度优势pi 的启动速度明显快于 Claude Code。Claude Code 启动时要加载大量内置工具和上下文而 pi 只需要加载四个工具的定义启动几乎是瞬时的。这个速度优势在频繁切换任务时特别明显。比如你正在写代码突然需要查一个 API 的用法用 pi 几秒钟就能问完而 Claude Code 可能要等十几秒才能开始响应。日积月累这个时间差很可观。6.3 oh-my-pi 补齐了体验短板pi 本身很简陋但 oh-my-pi 把体验层面的东西都补齐了安装脚本、配置模板、主题、快捷键、命令别名。这些东西单独看都不复杂但组合起来就让 pi 从一个“能用”的工具变成了“好用”的工具。我特别喜欢 oh-my-pi 的配置分层设计默认配置提供合理的起点用户配置覆盖默认值项目级配置再覆盖用户配置。这样你可以有全局的默认模型也可以为特定项目指定不同的模型和参数灵活性很高。6.4 适合谁不适合谁这套组合适合喜欢精细控制、不愿意把决策权完全交给 AI 的开发者。经常做小步修改、需要干净 diff 的场景。对token 消耗敏感、希望控制成本的用户。喜欢终端工作流、不想离开命令行的开发者。不太适合需要 AI自主规划复杂任务的场景比如“帮我重构整个项目”。不熟悉命令行、希望有图形界面的用户。需要内置代码理解能力比如自动分析依赖关系的场景。最后分享一个我自己的小习惯我会在项目根目录放一个.pi/instructions.md文件里面写这个项目的编码规范、常用命令、注意事项。每次启动 pi 时它会自动读取这个文件作为上下文。这样不用每次都重复交代项目背景模型一上来就知道该怎么做。这个技巧在多个项目之间切换时特别有用强烈推荐你试试。