通过 Webhook 触发 diff 总结,用免费模型生成带置信度的 changelog JSON,schema 驱动而非 prompt 驱动,48 小时验证了可行性。
每次发布日志都是一次小小的信任行为,但当作者写得信心满满,而唯一的上下文就是你刚刚给它的那些内容时,这种信任究竟从何而来?我花了 48 小时回答这个问题,工具是 MonkeyCode 的免费模型访问权限,以及一个免费服务器选项,用来运行每次合并时触发的定时任务。
声明:本文是 MonkeyCode 产品推广的一部分。计划很简单:把真实 diff 转成 changelog 条目,然后逐条对照 diff 核验每个声明。
整个流水线刻意做得很无聊:合并触发 webhook,免费服务器拉取新标签,脚本把压缩后的 diff 摘要交给模型。模型只有一项工作:返回一个 JSON 对象,包含发布摘要、变更文件列表,以及建议的 semver 升级级别。我没有让它写代码,也没有让它审查代码;只让它描述发生了什么,别的不管。
结果这个 JSON schema 成为整个系统两天下来唯一没有修改过的部分:
{
"summary": "short release summary",
"changed_files": ["path/to/file.py"],
"semver_bump": "major|minor|patch|none",
"confidence": "high|medium|low"
}
前六小时内,模型在一个根本不存在 src/analytics.ts 这个文件的仓库里引用了它,而且语法完美、毫无犹豫。可怕的不是幻觉本身;可怕的是输出读起来像一份完全正常的发布日志。人类审核者会略读一遍,点点头,然后把它发给客户。
就在这时我加入了 grounding checker,一个拒绝信任模型任何输出的小脚本——除非它能把声明追溯到 diff。它把每个变更文件与 git diff --name-only 做比对,在变更文件中 grep 摘要里提到的标识符,一旦有任何一个符号缺失就拒绝整条条目:
def ground_check(model_output, diff_files):
missing_files = set(model_output["changed_files"]) - set(diff_files)
if missing_files:
return False, f"files not in diff: {missing_files}"
for mention in extract_identifiers(model_output["summary"]):
if not grep_changed_files(mention, diff_files):
return False, f"no match for: {mention}"
return True, "grounded"
这个检查器在两天里标记出了多条无根据的声明,而它们每一份初读起来都是可信的。
同一个 diff 在 09:00 产生了 patch 升级,到 16:30 却产生了 minor 升级,而两次运行之间唯一变化的是请求的批次。更糟的是,一段包含注释 TODO: remove this entire function 的删除代码块就足以让模型转向 major 升级,因为它把 "remove" 这个词读成了破坏性变更。我用 semver.org 的规则作为升级决策的参考,而模型在这两天里不止一次悄悄违背了这些规则。
解决办法是把模型不再当作决策者,而是当作提议者。一条基于规则的函数从已知标记重新推导升级级别,changelog 只在规则同意时才使用模型的措辞;当两者不一致时,条目被路由给人工处理,而不是直接发布。
整个栈在没有任何人工帮助下唯一自动捕获的失败是截断,而这恰恰是因为我把输入范围定在了每个合并而不是每个发布。如果你给模型喂一个包含 300 个文件的 diff,你不是在测试它的摘要能力;你是在测试它的痛苦承受能力。
保留 grounding checker,因为无法追溯到某行 diff 的 changelog 声明就是噪音,发布日志里的每个句子都应该是证据。
保留 idempotency key,因为免费服务器在午夜前后重启了,如果没有它这个任务会写出两条相同的条目。
保留人工把控 semver 分歧的环节,因为模型的置信度分数在升级级别出错时依然很高。
砍掉直接把摘要拿来用的想法,因为模型最好的产出是一份草稿,而不是交付物。
砍掉任何把安全敏感的发布日志送进免费层的野心,因为那里一个幻觉出的文件名代价不止五分钟。
这个工作流适用于内部 changelog、周报摘要和演示流水线——错误的文件名只会让你轻度头疼。如果你的发布日志面向客户、如果它们描述安全修复、或者你的合规团队会阅读它们,就把免费模型从最后一步拿开,在发布按钮上留一个有名字的人。我也不建议在大单体仓库里用这种方法,因为当一个符号在树的其他地方合法存在时,grounding check 就失去了效力。
两天后,我依然更信任 diff 而不是摘要,而且我确信这是正确的操作顺序。模型让我的初稿更快了,但 diff 一直是地面真相;changelog 只是在有了裁判之后才变好的。如果你做同样的实验,在解析之前保存原始模型输出,因为那个原始文件是服务器重启后你唯一拥有的证据。