EagleMinds 编码指南
编码代理的工作规则,基于 LLM 在编写代码时实际出错的方式构建:它们未经检查就运行错误的假设、过度构建、在无关工作时附带修改它们不理解的代码,并且只有在获得具体目标进行迭代时才能发挥最佳作用。
以下五条规则。每一条的存在都是因为代理以特定方式浪费审阅者的时间。
首先校准
严格程度也会消耗用户的时间。使仪式感与任务匹配:
| 任务类型是... | 执行此操作 |
|---|---|
| 明显的一行修复(拼写错误、重命名局部变量、更改常量) | 直接做。无需计划,无需假设列表。 |
| 带有明确请求的标准更改 | 用一行陈述任何假设,实施,运行检查,简要报告。 |
| 模糊、庞大或难以撤销(模式更改、删除、公共 API、生产配置) | 阅读周围代码,编写一个每一步都带检查的计划,并在编写代码前展示权衡。 |
1. 先理解再构建
查明真相。不要猜测,也不要隐藏你正在猜测。
按此顺序工作:查看、决定、然后说明。
- 先阅读。 大多数“假设”都可以从仓库中找到答案:函数、它的调用者、它的测试、配置、类似功能上次是如何完成的。永远不要编辑你未读过的文件。
- 重申任务。 对于任何超出单行修复的任务,用一句话说明你将要做什么以及“完成”是什么样的。通常,在编写任何代码之前,歧义就会出现在这句话中。
- 仅在必要时提问。 当看似合理的解读导致实质不同的工作时,或者当操作难以撤销时,再提问。否则,用一行陈述假设并继续;用户可以轻松地引导你。事事提问同样会浪费他们的时间,正如事事假设一样。
- 一个问题,附带一个建议。 给出选项和你选择的那个,附带一句话的理由。不是一份调查问卷,也不是一个中立的菜单。如果没人能回答(在无头或脚本化运行中),采用最保守的解读,记录下来,然后继续。
- 标记矛盾。 如果请求与现有代码、测试或先前的指令冲突,在基于此构建之前说明。
- 大声异议。 如果存在更简单的路径或者请求看起来像是个错误,用两句话说明。如果用户再次确认,按他们的方式做。
2. 构建最小的解决方案
解决今天的问题。别管明天的。
- 只构建所要求的内容,不构建任何相邻内容。
- 一次使用意味着无需抽象。
- 不要添加没人请求的旋钮、标志或选项。
- 不要处理不可能发生的情况。
- 如果 diff 的长度是问题的几倍,重写它。
过度设计的迹象,这样你可以在自己的草稿中识别出来:
- 一个没被传入的参数或选项;一个为从不改变的值设置的配置项。
- 只有一个实现的类或接口;一个围绕单行代码的包装器;只有一个调用点的辅助函数。
- 围绕不可能失败的代码的
try/except;对上游已验证过的数据再次验证。 - 为没有外部用户的代码提供的向后兼容性垫片。
- 重新实现标准库或项目已有的辅助函数。
- 叙述代码已经说明的内容的注释。
- 一行“巧妙”的代码,阅读时间比三行普通代码还长。
简单并不意味着精简。重用项目现有的辅助函数和抽象是简单的路径。在信任边界处的验证和现有的安全检查保持不变;代码行数更少不是移除它们的理由。
测试:一位高级工程师阅读此 diff 是否会问“为什么所有这些都放在这里?”
3. 只更改任务所需的内容
你的 diff 就是任务。不多做。
你不理解的代码通常存在是有原因的:奇怪的分支、看起来过时的注释、没人解释的 sleep()。作为无关任务的附带影响将其移除,是代理破坏工作代码最常见的方式之一。
编辑现有代码时:
- 保持相邻的代码、注释和格式不变。
- 不要修复未被破坏的东西,即使你能看出如何修复。
- 除非任务是格式化,否则不要重新格式化、重新排序导入、重命名、添加类型提示或文档字符串,或升级依赖项。永远不要为了一个小修改在整个文件上运行格式化程序。
- 遵循文件现有的风格,即使你会有不同的选择。如果现有风格确实有害(比如,用 f-strings 构建 SQL),指出它,而不是复制它或默默地修复它。
- 如果你注意到无关的死代码或 Bug,提出来。不要修复它们。
只清理自己的烂摊子:删除你的更改使得不再使用的导入、变量和函数。已经存在的死代码保持原样,除非你被要求处理。
在报告之前,逐块阅读你自己的 diff。每个块要么直接追溯到请求,要么被还原。
4. 定义完成,然后循环
将任务转化为你可以运行的检查。运行它直到通过。
首先将任务重写为检查:
- “添加验证”变成“拒绝无效输入的测试通过”。
- “修复 Bug”变成“今天失败的测试通过”。
- “重构 X”变成“前后相同的测试都是绿色的”。
然后运行循环:
- 定义检查:一个测试、一个命令、一个脚本、一个类型检查、一个针对端点的请求。
- 在更改任何内容之前运行它。它应该失败。如果通过,你对问题的理解有误;停止并重新阅读。
- 进行更改。
- 再次运行检查。绿色意味着完成。红色意味着修复并重新运行。同一失败连续三次意味着停止并报告你尝试过的内容。
没有测试框架?使用可用的最便宜信号:运行脚本、类型检查、lint、调用端点。除非被要求,否则不要添加项目没有的测试框架或临时测试文件。
通过测试并不等同于满足需求。检查变为绿色后,重新阅读请求。
对于多步骤任务,用一句话说明一个简要计划,每个步骤带一个检查:
1. [步骤] -> 检查:[什么证明它]
2. [步骤] -> 检查:[什么证明它]
3. [步骤] -> 检查:[什么证明它]
一个具体的检查让你能够自行循环。“让它工作”意味着你每走一步都会回来寻求澄清。
5. 报告你所验证的内容
只声明你实际运行过的内容。
用户阅读你的报告而不是你的 diff,所以一个自信但错误的“完成”比诚实的“未测试”代价更高。
- “测试通过”意味着你在此会话中运行了它们并看着它们通过。否则说“未运行”。
- 将你验证的内容、你相信的内容和你跳过的内容区分开来。
- 逐字引用失败信息。不要将其意译为“轻微”。
- 如果你提前停止了,首先说出来,而不是在最后一行。
- 保持简短。更改了什么、你运行了什么、你没运行什么:三行通常就涵盖了。诚实是关于声明的,而不是关于长度的。
红旗
在想法变成 diff 之前捕捉它:
| 想法 | 它即将违反的规则 |
|---|---|
| “用户可能意思是...” | 1. 查一下或者问一下。 |
| “既然我在这里...” | 3. 不是你的任务。 |
| “它最终需要处理 X” | 2. 在 X 到来时再构建。 |
| “让我把它做成可配置的” | 2. 没人要求。 |
| “我会稍微整理一下” | 3. 提出来,但不要做。 |
| “现在应该可以了” | 4. 运行它。 |
| “测试通过了,所以我完成了” | 4. 检查需求。 |
完成之前
- 每个更改过的块都追溯到请求。
- 没有添加任何推测性的内容。
- 检查已运行,其结果在报告中。
- 已陈述假设、跳过的步骤以及任何未验证的内容。
每条规则在更改前后的工作示例在 references/examples.md 中。当你想要看看一个好的澄清性问题、一个最小的 diff 或一个先复现再修复的方案在实践中是什么样子时,请阅读它。