如果你最近在折腾 AI 编程代理,大概率会刷到 pi agent 这个词。我花了大约一周时间,把它从安装到定制完完整整走了一遍,最大的感受是:工具本身并不难,难的是怎么把Harness Agent这套思路真正落到项目里。很多人一上来就问 pi agent 和 harness 到底什么关系,然后照着网上零散的配置片段抄作业,结果要么是代理乱改代码,要么上下文直接爆掉,最后还得靠 git 回滚救场。
这篇文章不打算讲玄乎的“范式”,就写我怎么把一个默认的 pi agent,从零打造成一个能稳定处理真实需求的编码代理。整个过程涉及 harness 的设计、工具集定制、长上下文和 CoT(思维链)的配合,以及一堆只有在实战里才会踩到的坑。如果你也想给自己的项目接一个“听话”的 AI 编码代理,这篇应该能帮你省下不少试错的时间。
1. 先搞清楚概念:pi agent、Harness、Agent 之间的关系
1.1 pi agent 到底是什么
按我的理解,pi agent 是一个可定制的编码代理工具,核心作用是让大模型在真实代码仓库里自主完成“读代码 → 定位问题 → 改代码 → 跑测试”这条链路。它和我以前用的那些只能聊天的助手不一样,因为它被设计成能直接和文件系统、Shell 命令行、Git 仓库交互,算是一个真正“动手”的代理。
我把它引入项目的初衷很简单:有些重复性的重构、跨文件的 bug 定位、测试补全,手工做太耗时,而 pi agent 可以把这些任务丢给它,我只需要在旁边监督结果。但这里就引出了另一个问题——它凭什么不乱来?答案就是 harness。
1.2 Harness 和 Agent 不是一个层面的东西
很多人会把 harness 和 agent 混为一谈,包括我在刚开始调研的时候也绕了很久。后来我自己的理解是这样的:
- Agent(代理):指的是那个“决策大脑”,它负责理解用户的意图,思考下一步做什么,调用什么工具,然后根据结果继续推进。pi agent 里真正做决策的,是背后的大模型 + 工具调用循环。
- Harness(缰绳 / 工作框架):指的是包裹在 agent 外面的一整套约束和辅助机制。具体来说包括系统提示词(system prompt)、可用工具清单、工具调用的参数约束、执行流程的编排逻辑、权限控制、以及错误回退策略。
打个比方,agent 是那个开车的人,harness 就是安全带、导航、交规和副驾驶的整套体系。人(模型)负责判断和打方向盘,但能走哪条路、超速会不会被警告、出事故怎么止损,都是 harness 说了算。
1.3 为什么默认配置不够用
我一开始用的是 pi agent 的默认配置,测试的还是一个很小的 demo 仓库。结果它面对一个模块化程度比较高的业务项目时,暴露出好几个问题:上下文窗口被无关文件塞满;工具调用时命令写错格式导致反复重试;改完代码不主动跑测试;更严重的是,在执行一个重构任务时直接把公共工具的签名给改了,连带破坏了好几个调用方。
这些问题的根源,不是模型能力不行,而是harness 没有针对项目做定制。默认的 harness 就像一件均码 T 恤,穿上能遮体,但干活不贴身。所以后面我花了大量精力在设计这套“缰绳”上,这也是整个定制流程里收益最明显的一部分。
2. 动手前的基础准备:安装、模型选型与基线验证
2.1 安装 pi agent 并初始化项目
pi agent 的安装本身比较常规,依赖 git 和运行时环境。我的操作流程是先从 GitHub 拉取源码,然后安装依赖并构建。这一步我强烈建议不要跳过官方文档里的初始化步骤,因为它会帮你生成一个基础的项目配置文件,后面所有定制都基于这个文件展开。
初始化完成后,最好先在一个临时目录里跑一遍内置的 demo 任务,确认整个链路是通的。我当时在这步踩了一个小坑:因为本机全局环境比较乱,构建时缺了一个系统级依赖,导致二进制文件一直没生成成功。解决办法是看日志里给出的缺包提示,用系统包管理器补上,再重新构建。
git clone https://github.com/your-target/pi-agent.git cd pi-agent ./install.sh # 根据官方脚本执行 pi-agent init --workdir ~/projects/my-demo pi-agent run "Add a unit test for the user service"注意:不同分支的安装方式可能有差异,遇到问题时不要硬撑,优先看官方 README 里的 Troubleshooting 部分。
2.2 模型选型:上下文长度决定 harness 设计
在定制 harness 之前,你需要先确定一件事:背后用哪个模型。因为 pi agent 的 harness 里所有和“上下文”相关的策略,都依赖模型本身的上下文窗口大小。
我当时做了个简单的对比:
| 模型类型 | 上下文窗口 | 适合场景 | 备注 |
|---|---|---|---|
| 长上下文模型 | 128K~200K | 大型仓库全局分析 | 价格高,单次成本大 |
| 中长上下文模型 | 32K~64K | 中型项目、模块级任务 | 性价比适中 |
| 短上下文模型 | 8K~16K | 小文件、单函数级任务 | 需要频繁压缩历史 |
我的建议是,除非你的项目真的特别大,否则不要无脑追求 200K 上下文。因为上下文越长,单次请求的 token 消耗越高,而且模型在超长上下文里的注意力容易分散。后面我会讲怎么用“分段聚焦 + 摘要压缩”的方式,让一个 32K 上下的模型也能稳定处理跨文件任务。
2.3 先跑一个“最小闭环”验证基线
定制 harness 之前,最好先记录一下默认行为的表现,这样后面改完才能对比出效果。我通常会准备三个测试任务:
- 修一个已知的小 bug;
- 给一个函数补单元测试;
- 做一次跨文件重命名重构。
每个任务都让它独立跑一遍,记录完成耗时、改动文件数、是否通过测试。我的实测结果是:默认配置下,任务 1 表现还行,任务 2 勉强可用,任务 3 就会开始出现“牵一发动全身”的问题。这个基线数据非常有用,后面每一次调优,我都拿这三个任务做回归,确保没有“拆东墙补西墙”。
3. Harness 定制核心:系统提示词、工具集与安全边界
3.1 系统提示词:不要只写“你是一个助手”
很多人设计 harness 时,最不重视的就是系统提示词,觉得随便写两句就行。但对我来说,系统提示词是整个 harness 的“宪法”,它对 agent 的行为约束力,比任何参数都大。
我总结了一个四段式模板,基本能覆盖大多数编码任务的需求:
# 角色定位 你是一个资深软件工程师,擅长代码阅读、问题诊断和最小化重构。 # 工作准则 - 在修改任何代码前,必须先用工具阅读相关文件,确认调用方和依赖关系。 - 每次只做一件事,修改完成后立即运行相关测试。 - 禁止修改与任务无关的文件;如果必须修改,先向用户说明理由。 - 当遇到不确定的 API 行为时,优先搜索项目内用法,再询问用户。 # 工作流程 1. 阅读任务描述,列出你需要的文件清单。 2. 检查代码索引和项目结构,定位相关模块。 3. 提出修改方案,并明确影响范围。 4. 执行修改,运行测试,查看结果。 5. 输出变更摘要:修改了哪些文件、为什么修改、潜在风险。 # 输出格式 所有回复必须包含 `[Plan]`、`[Action]`、`[Result]` 三个部分,分别对应计划、行动和结果。你可能会问,为什么要专门加输出格式这一条?因为我在实战里发现,如果不约束输出结构,模型很容易把内心想法和实际操作混在一起,导致我无法判断它到底有没有真正执行某个命令。加了[Plan] / [Action] / [Result]这种强制结构后,每一步的行为都变得清晰可追踪。
3.2 工具集定制:少而精,给每个工具明确契约
pi agent 默认会暴露很多工具,但并不是越多越好。工具一多,模型在调用时反而容易选错。我做的第一件事,就是砍掉那些高风险、低频率的工具,把常用工具收敛到下面几张“卡片”里:
| 工具名 | 功能 | 入参 | 出参 |
|---|---|---|---|
| read_file | 读取文件内容 | 文件路径、起始行、结束行 | 代码片段及行号 |
| search_symbol | 在项目内搜索函数/类定义 | 符号名称 | 文件路径、行号、签名 |
| grep_text | 关键词搜索 | 关键词、文件路径过滤 | 匹配列表 |
| write_file | 覆盖写入文件 | 文件路径、完整内容 | 写入结果 |
| run_command | 执行 Shell 命令 | 命令字符串 | 标准输出与退出码 |
| git_diff | 查看当前改动 | 无 | 文件改动列表 |
这里的关键是给每个工具定义好“契约”,尤其是run_command。如果你不加约束,模型可能会跑出rm -rf这种命令。我的做法是在 harness 配置的 tool 定义里,加入一个allowed_prefixes字段,只允许跑白名单里的命令前缀,比如python、pytest、git diff。
tools: run_command: allowed_prefixes: - "python" - "pytest" - "git diff" - "git status" - "npm test"3.3 权限与安全边界:给代理戴上“咬手”的笼头
这部分是我觉得最不能省的地方。AI 代理的能力越强,能造成的破坏也越大。一个没有权限控制的 harness,就像把家门钥匙交给一个陌生人,虽然大多数时候它很乖,但一旦犯傻就是灾难。
我制定了一套安全边界规则:
- 文件路径白名单:除非显式声明,否则代理只能修改
src/、tests/目录下的文件,禁止触碰配置文件、锁文件、CI 配置。 - Git 操作保护:禁止自动执行
git commit和git push,只允许git diff查看改动。最终的提交动作必须由人工确认。 - 命令超时机制:所有 Shell 命令默认 30 秒超时,防止代理陷入死循环。
- 变更预览确认:对于可能影响超过 3 个文件的重构操作,harness 会强制拦截,要求先输出改动计划并等待确认。
这套规则一开始我担心太严,会拖慢代理的执行效率。但实际跑下来发现,它只是拦截了那些“风险动作”,对常规的读代码、改函数、跑测试几乎没有影响。而且因为有了安全边界,我敢把更多长耗时任务交给它挂机执行,省心不少。
4. 工作流编排:长上下文、CoT 与反思机制的实战落地
4.1 长上下文管理:别把整个仓库都塞给模型
刚用 pi agent 的时候,我最常犯的错误就是让它“看看整个项目结构再改代码”。对于一个大仓库,这基本等于自杀式操作,因为 token 很快就用完了,后面的决策全部依赖被截断的上下文,结果自然是胡来。
后来我总结了一套上下文管理的三层策略:
- 项目结构层:只让代理读取目录树和关键配置,比如
package.json、pyproject.toml、README.md,用来建立全局认知。 - 模块聚焦层:根据任务涉及的功能点,把阅读范围限定到相关模块。比如任务是修用户登录逻辑,那就只看
auth/相关目录,而不是整个后端。 - 代码片段层:真正进入阅读代码时,优先读定义和函数签名,有需要再展开函数体,而不是一次性把一个文件几千行全读完。
pi agent 的 harness 里支持设置max_context_ratio参数,我习惯让模型在“已用上下文 + 当前步骤预估消耗”接近阈值前,强制触发一次摘要压缩。这样即使跑一个多小时的长任务,整体 token 用量也不会失控。
4.2 CoT 与 Plan-then-Execute 的落地写法
CoT(思维链)这个词听起来高大上,落到 harness 里其实就是一句话:强制模型在做事之前,先把推理过程写出来。我一直用 Plan-then-Execute 的模式,意思是先让代理输出完整的执行计划,再逐步执行,而不是边想边做。
我在系统提示词里明确要求代理在[Plan]阶段至少包含以下信息:
- 目标是什么;
- 涉及的现有函数/文件有哪些;
- 改动方案是什么,影响面有多大;
- 如何验证改动是否正确。
实际效果非常明显。以前让代理直接改代码,它经常会跳过某些隐式依赖,比如函数 B 的数据来源于函数 A 的返回值,但代理只看函数 B 就动手了。有了 Plan 阶段,它至少会先搜索相关符号,把调用链理清楚再动手,成功率提升了一截。
4.3 反思循环:让代理自己“挑自己的毛病”
只靠 Plan-then-Execute 还不够。真正让代码质量提升的,是在执行阶段之后加一个反思节点。这个节点迫使代理在提交结果前,以“挑剔的代码审查者”身份重新检查自己的改动。
我在 harness 里加了一个伪代码如下:
def reflect_on_changes(patch_file): # 1. 让模型阅读自己的 diff diff_content = read_file(patch_file) # 2. 强制检查三个问题:改动是否最小化?有没有破坏现有测试?有没有遗漏边界条件? prompt = f""" 作为代码审查者,请检查以下 diff: {diff_content} 检查项: - 是否存在无关内容的改动? - 是否处理了空值和异常情况? - 是否会破坏已有测试? 如果发现问题,请列出具体修改建议。 """ feedback = call_model(prompt) return feedback在加入反思环节前,代理经常写出“能用但不谨慎”的代码,比如没有判空、硬编码路径、吞掉异常。加入反思后,它在提交前会主动修正一批低级问题。当然,反思会增加一轮模型调用成本,所以我的策略是只在修改类任务和重构类任务里启用,纯读取类任务不做反思,节省开销。
5. 完整配置文件示例与效果调优对比
5.1 一个可落地的 harness 配置示例
以下是我在项目中实际使用过的 pi agent 配置,去掉了一些内部路径信息,保留核心结构。这种配置本身不绑定特定模型厂商,你可以按自己的环境替换。
project: name: sample-service workdir: /path/to/repo index: enabled: true max_files: 500 allowed_paths: - "src/**" - "tests/**" forbidden_paths: - "*.lock" - ".env" - "deploy/**" model: provider: your-provider name: your-model-name temperature: 0.1 max_tokens: 4096 context: window: 32000 max_ratio: 0.7 compression: summarize harness: role_prompt: | 你是一个严谨的软件工程师... tools: [read_file, search_symbol, grep_text, write_file, run_command, git_diff] command_whitelist: ["python", "pytest", "git diff", "git status"] workflow: plan: true execute: true reflect: true require_confirmation_for_multi_file_changes: true配置里的context.max_ratio: 0.7意思是,当已用上下文达到窗口的 70% 时,就触发摘要压缩。我通常不会设置到 90% 以上,因为压缩本身也需要 token 余量,留点缓冲更安全。
5.2 定制前后的效果对比
我在前面提到的三个基线任务上,分别对比了默认配置和定制 harness 的表现。记录如下:
| 任务 | 默认配置完成时间 | 定制后完成时间 | 是否一次通过测试 | 改动文件数 |
|---|---|---|---|---|
| 修复空指针 bug | 4 分 20 秒 | 3 分 10 秒 | 否 | 2 |
| 补充排序函数测试 | 6 分 15 秒 | 4 分 55 秒 | 是 | 3 |
| 跨文件重命名 | 失败(改坏调用方) | 5 分 30 秒 | 是 | 7 |
可以看到,跨文件重命名这种任务,在默认配置下直接失败了,而定制 harness 后反而变成了耗时最短且一次通过的任务。我认为这主要归功于 Plan 阶段对调用链的梳理,以及多文件变更前置确认机制,让代理在动手前就想清楚了影响面。
5.3 token 成本估算:定制 harness 到底贵不贵
很多人担心加计划、加反思会显著增加 token 消耗。我的实测数据是,定制后平均每轮任务的 token 消耗比默认配置高出 30%~50%,但它解决的问题数量也多不少。换个角度算账:默认配置下,一个任务可能要做三遍才能成功,定制后一遍过,最终总成本反而更低。
我习惯在 harness 配置里给每个任务设定一个max_iterations,比如默认 15 轮。如果代理在 15 轮工具调用内还没有收敛到成功状态,就让它停止并输出当前进度,由人工接管。这个限制能有效防止代理陷入“重试-失败-再重试”的泥潭。
6. 常见问题与排查技巧
6.1 问题速查表
下面这个表格,是我这一周里遇到的最典型的几类问题,每一类都对应一个根因和一个可行的解法。
| 现象 | 可能原因 | 排查与解法 |
|---|---|---|
| 代理改完代码,测试全挂 | 没阅读相关测试文件 | 在系统提示词中强调“修改后先跑相关测试” |
| 上下文迅速耗尽 | 一次性读了多个大文件 | 设置文件读取行数上限,强制摘要压缩 |
| 命令一直失败 | 工具定义里命令白名单太严 | 查看失败日志,按需放宽到具体前缀 |
| 改动影响范围失控 | 缺少 Plan 阶段 | 开启 plan workflow,要求先输出影响面 |
| 代理无限循环调用 | 没有最大迭代数 | 设置max_iterations并添加超时 |
6.2 一个让我印象深刻的排查案例
有一次,代理在执行任务时反复调用同一个工具,明明返回了错误,却还是用相同的参数重试。我一开始以为是模型太笨,后来查日志才发现,是 harness 里工具定义返回的错误格式不规范,模型没办法从错误信息里提取“该改哪个参数”。也就是说,问题出在工具契约,而不是模型推理。
解决方式是在所有工具的错误返回里,强制附加“建议修正字段”。比如read_file报“文件不存在”时,返回信息里要带上“可尝试的相近路径”。这样一来,模型就能从错误反馈里学到东西,而不是原地打转。
6.3 避坑建议
- 不要一开始就把所有工具权限都开放,宁可先用白名单跑通再逐步放宽。
- 每次调优只改一个变量,比如这次只改提示词,下次只改工具,否则出了问题很难定位。
- 一定要记录历史任务日志,pi agent 默认会保留执行轨迹,这些日志是排查问题最重要的线索。
- 遇到大型重构,先用一个小的子任务做验证,不要一上来就扔整个模块进去。
7. 一点个人体会
跑完这一整套流程,我最深的感受是:定制 harness 的核心不是给代理加更多功能,而是给它划清边界、理顺流程。一个好的 harness 能让普通模型做出稳定的结果,而一个混乱的 harness 则会让再强的模型也发挥不出来。pi agent 本身的底子已经很灵活,真正决定它好不好用的,是你愿意花多少精力去设计那圈“缰绳”。
如果你也准备在自己的项目里接入类似方案,建议从一个小场景切入,先用最低配置跑通,再逐步加上 Plan、反思和安全规则。踩过几次坑之后你会发现,AI 编码代理真正能帮你省下的时间,远比你一开始给它的配置时间要多得多。