从零定制pi agent:打造稳定可靠的AI编码代理Harness
如果你最近在折腾 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 test3.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 的表现。记录如下任务默认配置完成时间定制后完成时间是否一次通过测试改动文件数修复空指针 bug4 分 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 编码代理真正能帮你省下的时间远比你一开始给它的配置时间要多得多。