几条并行流水线各自吐出成堆的卡片,堆的尽头站着一个人在逐张翻看

一、数量对比好读,但它读不出判定成本

据公开报道,一项针对编程智能体的公开测试里,多个智能体协作在十一万六千行代码中找出六十余个问题,而单个智能体最好的一次找出二十余个。这两个数字在本篇只作为背景引用一次,我们不由此推出任何一般能力结论,也不推测它的模型配置、提示设置与缺陷构成。想讨论的是那个测试没有量化的部分:候选数量从二十几条涨到六十几条之后,工程上真正变难的是判定,而不是发现。

单智能体时代,缺陷检测的瓶颈确实更接近”找不找得到”。一份清单二十几条,一个熟悉这块代码的人半天能看完,判定顺带在脑子里做了。清单翻倍以后,同一件事变成一条流水线:谁去重、谁定级、谁复现、谁签字、谁记录处置结果。这时候如果组织上没有把判定这件事结构化,多出来的候选不会变成多发现的缺陷,只会变成多消耗的评审人时间,以及一份没人再相信的报告。

所以我们把这类系统的交付目标从”找出多少问题”改写为三条可验收的承诺:每一条候选都能被合并或驳回且有理由;驳回率与误报代价是可观测的;每一次运行的输入、判定与处置能事后复原。这三条比数量难汇报,但它们是这套东西能不能进真实项目的分水岭。

二、四类噪声各有各的识别方法

多智能体产出的清单里,四类东西必须先剔掉,否则会挤占全部判定资源。

第一类是同一根因被多个智能体各报一次。识别方法不是比对文字,而是比对”出错的最小充分条件”:两条报告如果触发输入相同、失败位置在同一个代码路径上、修复动作落在同一处,就是同一个缺陷的两种说法。文字相似度在这一步几乎没有用处,因为不同智能体的措辞习惯差别很大,同一措辞也可能对应不同根因。

第二类是风格与规范问题混进缺陷。它们不是错,是不一致。识别办法是给它一个独立通道:能被静态检查规则覆盖的判断,直接从缺陷清单里摘出去,交给门禁;剩下的”看起来不规范”如果无法写成规则,就标注为主观建议,不进入优先级排序。混在一起的最大代价不是浪费时间,而是让真正的问题被平均化。

第三类是只在特定环境成立的问题。编译器版本、依赖版本、并发时序、时区与区域设置、内存分配器行为都会让它成立或不成立。识别方法是要求报告写清环境,缺环境信息的候选一律退回补充而不是就地判定。

第四类是无实际影响的理论缺陷。逻辑上存在,但调用路径不可达、或者可达路径上的前置条件让后果无法发生。这一类最消耗人的信心,判定它需要一次调用链检查,而且检查结果必须写下来——否则下一轮又会有人报同一个问题。

三、去重要靠字段,不能靠文字

“这里有一个空指针风险”这样的一句话无法合并,也无法分级,因为它不包含任何可比较的结构。候选记录的最小字段是四项:位置、触发条件、受影响代码路径、根因描述。四项缺一,去重就退化成人工读全文。这张表是我们给候选缺陷记录定的字段形状。

字段 内容要求 支撑的后续动作
位置 文件、函数与行范围,标出是报告者认定的根因位置还是失败暴露位置 跨报告合并、按模块抽查
触发条件 能让它发生的输入或状态组合,含前置条件与环境 复现构造、剔除”仅特定环境”类
受影响代码路径 从入口到失败点的调用链,写清哪些调用方真的会走到 判定影响面、驳回不可达
根因描述 一句话说清为什么错,而不是为什么会报错 去重、修复方案归组
来源 哪一次运行、哪一个智能体、哪一轮任务划分产生的 运行质量回溯、召回标定
判定结论与判定人 三级之一,加上人名与时刻 责任落档、误报统计
处置结果 修复、挂起、驳回,或降级为技术债条目 下一轮的对照基线

位置那一项常被写成”根因位置”,但报告者往往只知道失败暴露的位置。两者不分,合并时就会把同一根因的两条不同暴露点当成两个缺陷。要求报告同时填两栏,成本很低,收益是去重准确率明显变化。

四、三级判定,每级的处置不该一样

判定分三级,价值在于让优先级规则机械化,而不是靠个人判断力的稳定性。

  1. 可复现并给出复现步骤。这一级直接进入修复排程,按影响面定优先级。复现证据的最低要求是三件:触发输入、运行环境、可观察的失败表现。缺任意一件就不算确认,只能降到第二级——这条规则要严格执行,否则第三级会被掏空,判定名义上很轻松,实际全是待验证。
  2. 静态成立但无法构造触发。处置是补齐可达性判断而不是立即修。常见结果是这一级的多数条目在调用链检查后被驳回,因此它的正确动作是”指派一次检查”,指派对象是熟悉该模块的人,而不是报告者自己。
  3. 需人工裁定。典型是并发时序、资源释放顺序、权限边界这类难以机械验证的判断。这一级的处置是转人工主导的测试任务,而不是继续加候选。

三级之间的升降要有明确条件:第二级在补出复现输入后升到第一级,第三级在检查规则可写成静态断言后降到第一级。没有升降条件,级别就会固化成三个互不相干的堆。

五、漏检不可知,那就用已知缺陷做标定

多智能体清单最危险的性质不是误报,而是你永远不知道它漏了什么。一份六十条的报告看起来工作很充分,但没人知道还有多少条根本不在纸面上。不可知不等于没办法,可做的有三件事。

第一件是用已知缺陷集做召回标定。把历史上真实造成过线上问题、并且有明确修复提交的缺陷整理成一套对照集,每次方法或配置变更后重跑一遍,看这套已知缺陷被报出来多少。它不需要新数据,素材就是从前的故障复盘记录。第二件是把”未报出已知问题”设为一次运行质量的否决项——只要对照集里某条已知缺陷在这次运行里消失,这次产出的全部结果都不能作为发布依据,而不是按比例打折。这一条听起来苛刻,但它是唯一能让召回率不被候选数量掩盖的规则。第三件是按模块抽查,抽查对象选那些从未出现在候选清单里的模块,而不是问题最多的模块,因为后者本来就是人工评审的重点。

误报的代价也要算,不能只说”有误报”。三个组成部分:评审人逐条判定的耗时,对报告可信度的侵蚀(这是不可逆的,一旦团队习惯性地认为清单一半是噪声,真问题也会被判第三级挂起),以及错误修复带来的返工与新增缺陷。量化办法不需要精确工时,用抽样记录即可:每周固定抽若干条判定,记下判定耗时、结论与是否修复。误报预算的设定方式由此而来——先定一个团队能承受的每周判定工时,再用平均判定耗时折算出每周可处理的候选上限,超出上限就要求上游提高去重与噪声剔除强度,或者收紧报告门槛。把候选数量当成产能指标来管理,比把它当成成果指标健康得多。

六、落档字段与哪些工作不该交给候选清单

多智能体运行结果要能被审计,字段和测试报告一样要固定:本次运行的模型与配置标识、任务是如何划分的(哪些范围给了哪个智能体、是否重叠)、每一条发现的来源、判定人与判定结论、处置结果,以及标定集的结果。落档不是为了追责,是为了让”下次少犯同样的错”有依据:只有知道某个模块在上一轮根本没被任何智能体覆盖,才谈得上调整任务划分;只有知道误报集中在某一类断言,才谈得上给那一类加静态检查。

组织上的分工建议同样明确。适合候选清单模式的是可以被局部验证的问题类型:边界与异常路径遗漏、明显的错误处理缺失、资源配对使用不当、输入校验缺项。这些问题不需要全局理解,清单模式收益最大。必须人工主导的是三类:并发与时序、资源释放与生命周期、权限边界与信任假设。它们的共同点是判断依据不在被检查的那段代码里,而在系统其余部分的行为约定中,候选模式在这里给出的不是答案而是噪声,反而会把判定资源抽干。

洹拓在把这类能力接进交付流程时,验收看的是上面这些字段能不能被第三方复核,而不是清单长度。一份六十条但每条都带触发条件、复现结果和判定人的报告,价值高于两份加起来一百二十条、每一条都要重读一遍才能确定是不是同一件事的报告。这个差别在项目里第一次出现时很便宜,出现三次之后,团队就再也不看这类清单了。