Codex 总是把被否的方案也写进交付里?这个 503 星项目想一次性治好它
一周拿下 503 颗星的 no-negative-echo,主打让 Codex 只根据最终拍板的结果生成标题、注释、commit 与 PR,减少被否决方案在交付物里的残留。

适合:用 Codex 类 Agent 写代码、提 PR 的个人开发者和 5–20 人小团队。
先说结论
no-negative-echo 这个项目解决的问题很具体:AI写代码时,被你否掉的方案,往往还藏在标题、注释、commit、PR 描述里,跟着最终交付一起交上去。作者的思路是——喂给模型的上下文,只保留“最终留下来的那一份”,让它照着结果写,不要再去回忆那些被淘汰的思路。
这个方向本身就值得抄:不是给模型加更强的判断力,而是从输入端把噪声砍干净。一周 503 颗星,说明踩中了不少团队的痛点。
真正的问题
Codex、Copilot 这类 Agent 在长任务里,常见一种“回声式残留”:
- 标题里写“尝试用 X 方案重构登录”,结果 X 方案你两小时前已经否了,最终落地的是 Y。
- commit message 像在写开发日记:“先试 A,再试 B,最后用 C”,评审人只关心 C。
- PR 描述把三个备选方案都列上,混淆主线,还会让 reviewer 反复问“为什么不是 A”。
这些残留不是模型“笨”,而是它的工作区里堆满了被否决的中间结果,它分不清哪些该出现在交付物里。对人类开发者来说,这是基本素养;对 LLM 来说,需要显式约束。
怎么做更省力
no-negative-echo 的核心做法可以拆成四步,对应到任何 Agent 工作流里都适用:
1。 在任务结尾,整理一份“最终方案清单”,只保留被采纳的方案及其关键决策点。 2。 把这份清单作为唯一上下文,让模型生成 commit、PR、注释和对外文档。 3。 在 prompt 里显式禁止回写被否决方案,禁止“考虑到我们之前讨论过……”这类过渡。 4。 提交前用 diff 或 lint 自检:标题、注释、PR 描述里出现的方案名,是否都来自最终清单。
第 1 步和第 3 步是收益最大的两步。前者压缩了输入,后者锁死了输出形态。
如果团队已经在用 Codex 处理 PR,可以直接把它当作 PR 阶段的“过滤器”用——Codex 写完代码后,再用一份精简的最终方案清单驱动它重写 commit 和 PR 描述。
| 阶段 | 喂给模型的上下文 | 期望输出 |
|---|---|---|
| 编码中 | 完整历史(含被否方案) | 代码与中间注释 |
| 提交前 | 仅最终方案清单 | commit、PR、对外注释 |
表格前后要交代清楚:上半段保留了探索过程,是为了让模型有足够信息写代码;下半段砍掉历史,是为了让它写出来的交付物不夹带“私货”。
哪些坑要避开
第一,不要把“最终方案清单”做成空壳。如果只丢一句“按最终方案提交”,模型大概率会自己脑补细节,重新引入回声。清单里要包含关键名词、关键路径、关键决策点。
第二,不要在编码阶段就强约束输出风格。Agent 在探索时需要自由度,过早限制会让它不敢试错,最后产出一个保守但平庸的方案。约束要放在交付环节,而不是创作环节。
第三,不要忽视评审反馈本身。Codex 生成的 PR 描述只是“自描述”,真正决定质量的还是 reviewer 的问题。把描述写干净,是为了减少误解,不等于免评审。
第四,别把它当成万能过滤器。如果团队本身就没有清晰的“方案决策记录”,模型再聪明也只能瞎猜。先把人的决策流程理顺,工具才有意义。
第五,注意仓库隐私。no-negative-echo 本身是本地 Skill 思路,但你在生成 commit/PR 时仍要把上下文发给模型,确保不夹带密钥、内部地址、未公开接口。
现在就能动手
今天就可以做的最小一步:
挑一个你最近合并的 PR,打开 commit 历史和 PR 描述,数一下里面出现了多少个被否方案。如果超过 1 个,就说明这个项目解决的问题你也正在踩。
接着建一个文件,命名随意,比如 final-decisions.md,每次任务结束把最终方案的关键点写进去——文件路径、接口名、核心权衡、对外称呼。然后让 Codex 只读这一份文件,生成 commit 和 PR 描述。
跑两三次之后,对比新旧 PR 的描述长度和 reviewer 的提问数量。如果提问明显变少、commit 更干净,说明这条路走通了,可以把规则写进团队的 Agent 使用规范里。
至于是否直接采用 no-negative-echo 的实现,看团队栈。它是 Python 写的 Skill,思路可以照搬,代码可以直接 fork,也可以用知乎高赞回答生成器这类工具把这次改造过程沉淀成团队内部的最佳实践分享,让更多人主动按这套流程走。
工具只是脚手架,核心还是那条简单的纪律:让模型只看到“留下来的”,它就不会把“被否掉的”写进交付。