做 Qasey 的 QA Agent 架构评审时,我拿到的最有说服力的一份证据不是某个崩溃日志,是两份「都成功」的执行结果。

同一个需求 FIN-6202,同一个 Agent,跑两遍:第一遍产出 10 条测试用例,第二遍产出 12 条。数量不同也就罢了,模块名对不上、粒度对不上——同样一段业务逻辑,一遍拆成两条用例、一遍揉成一条;Figma 里的节点名和 Jira 主题不完全一致时,两遍各选了一种语义,谁也不打招呼。没有报错,没有异常,两遍都「顺利完成」,只是交付的是两个平行宇宙里的测试计划。

更值得玩味的是这两份输出的「质量」:逐条看,两遍的用例都写得像模像样——格式规范、断言清晰、措辞专业。拿去评审,任何单一份都能过「感觉上不错」那关。只有并排摆在一起,才能看见它们其实在描述两个略有不同的功能理解。单看每份都成立,合起来互相拆台——这正是「随机性包装成专业性」最危险的地方:它永远不会以 bug 的形态出现,只会以「这次的结果和上次的怎么对不上」的形态,缓慢侵蚀你对系统的信任。

这个症状诊断起来其实不难:问题不在模型想不出测试点,在结论不稳定、来源不可追溯、风险假设可能伪装成硬性预期,而且整条链路没有第二遍质量控制。 生成能力从来不是瓶颈,证据治理才是。

生成不稳定是表象,证据无账本是病根

往链路上游追,输入是五路来源:Jira ticket、Figma 设计稿、代码、Slack 讨论、QA Experience 检索回来的历史经验。五路证据各自带着自己的版本、口径和置信度,流进一个 prompt 后被模型「无痕融合」成一份流畅的输出——流畅正是问题所在:事实、推断、假设在输出里穿着同一件衣服,你无从分辨哪条用例背后站的是 PRD 原文,哪条背后站的是模型脑补。

Figma 节点名和 Jira 主题不一致这种小冲突最典型。人类 QA 会停下来问一句「这俩是一个东西吗」,模型不会——它把两种表述当同义词吸收了,哪一遍吸收成哪种语义,取决于 prompt 上下文里哪个先出现。两次执行两种语义,于是模块名变了、用例集变了,而流程里的每个环节都觉得自己做了正确的事。

过度合并是同一病灶的另一个出口:多个触发条件、多种故障原因,本该拆成独立用例的,被模型揉成一条「大而全」——看着覆盖了,实际断言模糊到没法执行。还有风险假设的位阶错乱:「导出超过 10k 行可能异步化」这种从历史经验召回的推测,被写成和需求事实同等强硬的预期——假设穿事实的衣服,是最危险的一种输出:它不是错误,是比错误更隐蔽的「半真」,执行者拿它当验收标准去跑,跑出来的失败其实是系统在按另一套预期工作。

解法:在写入之前,先让证据成为一等公民

收敛出来的架构是 Evidence Model + Critic Pass:不让模型直接把「读到的多源材料」翻译成「写入系统的用例」,中间强制经过一层显式的证据登记和一遍独立的审查。

Evidence Agent 先把材料消化成结构化条目:事实 F01、风险 R03、冲突 C02——每条都带稳定 id、sourceconfidence 和验证状态。「结算导出月末锁定后只读」是 PRD 原文、高置信、已确认,登记为 fact;「10k 行以上可能异步」是经验召回、中置信、待确认,登记为 risk——位阶在 schema 层就分开了,不存在假设混进事实的物理通道。 Figma 和 Jira 的命名分歧登记为 conflict,状态 pending-human,明确禁止无痕合并。

QA Planner 生成用例时不许再自由发挥:每条 case 必须通过 F/R id 形成覆盖关系——这条用例覆盖哪条事实、验证哪条风险,写进结构里。这一步把「需求到用例」从一次性的创作变成可核对的对账:有没有 F/R 没有任何 case 认领,一眼可查;反过来任何一条 case 都能回答「你凭什么存在」。

Critic Pass 是第二遍独立审查,查六个问题:遗漏(没覆盖的 F/R)、重复(两条断言同一件事)、假设(needs-confirmation 被当成硬预期写死)、粒度(多触发条件揉进单条)、可观察性(断言结果外部根本看不到)、优先级(排序没对齐风险严重度)。前两个检查基本是机械的——对着覆盖矩阵跑集合运算就能出结果;后四个要带判断力,因为它们问的是「作为测试是否成立」而不是「作为文本是否合规」。Critic 不只是挑文案毛病——它查的是「这条用例作为测试,能不能被执行、被观察、被追责」。过了 Critic 的 canonical cases 才轮到 Writer 写进 MeterSphere,Writer 只消费审查后的集合,没有「模型觉得行就直接入库」这条通道

这套结构里最不起眼但最关键的设计是稳定 idF01R03 不是编号装饰——它让「哪条 case 覆盖哪条证据」变成可 join 的关系数据,而不是一段需要在字里行间做阅读理解的散文。两次执行差异大,恰恰是因为没有这层引用:输出是「一堆用例」,没法问它们各自站在哪条证据上;有了 id 引用,差异分析直接变成集合运算——run A 覆盖了 {F01,F02,R01},run B 覆盖了 {F01,R01,R03},差集一目了然,分歧点定位到证据粒度而不是「感觉不一样」。

证据条目与覆盖矩阵

为什么不是更简单地修一修

评审时摆过三个更省事的方案,都值得说说否决的理由,因为它们是这类问题的标准诱惑。

继续强化单一 prompt——改动最小,但「把规则写进 prompt」不等于「规则可机器验证」,随机性原封不动留着,而且规则越长漏遵循概率越高(认知预算那笔账我在别处算过一次)。直接减少用例数量——输出是简洁了,代价是把不同触发条件和故障原因揉进同一条,病灶直接加重。只增加更多工具——证据是多了,但冲突归属和来源追溯依然没人管,等于给没有账本的商店继续进货。

三个方案共享同一个误判:把「生成质量」当成问题本身,而实际上问题是「生成前的证据没有被治理」。 生成器再好,喂给它一锅没分层的原料,出来的还是每周一次的抽签。

顺手牵出的第二个问题:重型链路接待了问候

评审里还有个啼笑皆非的发现:用户对 Agent 说一句「你好」「现在几点」,整条重型 QA 链路照样全量加载——约 17k prompt token 就为回一句寒暄。

这催生了架构里的第一个组件:Intent Router。轻量问答走轻路径,重型需求分析才进 Evidence→Planner→Critic→Writer 的全链。路由前置不是优化癖——重型链路不光是贵,它对简单意图的处理质量反而更差:17k token 的测试方法论灌进「现在几点」的上下文里,模型反倒要在无关规则里找出口。意图决定路径深度,这是认知预算在 QA Agent 上的又一次应用。

上下游还要接什么

这套中间模型只管「写入之前」。写入之后还有一层互补机制值得提一句(它在别处展开过):评审通过的用例集合在执行前冻结为不可变 CasePlan,绑定 planHash,retry 时恢复持久计划而不是让模型重新生成——因为只要允许 retry 重新抽签,「写入的集合」和「验证的集合」就可能不是同一个,部分验证冒充整批完成的事故就是这么来的。写入前用 Evidence+Critic 稳一次,执行时用冻结计划冻一次,两端掐住,漂移才没有藏身之处。

任务上下文的所有权也顺手定了调:以 ticket/requirement 为单位,而不是以 Slack thread 为单位——thread 是对话容器,requirement 才是事实容器,证据条目挂后者才能跨会话复用和追责。工具失败策略则是「发现签名→调用一次→修正一次→降级停止」:识别失败模式、给一次纠正机会、还不行就降级记录,而不是让 Agent 在同一个签名上无限重试放大成本。

怎么验证它真的稳了

这套设计的验证口径和普通功能不一样——它不是「跑通就行」,是要证明可重复。同一个需求多次回放:case 数量、模块归属、覆盖映射应该收敛到一致或显式可解释的差异——差异存在没关系,但必须能追到哪条证据导致它,而不是「模型心情」。

第二个口径是可回答性:随机抽任何一条 canonical case,系统必须能回答「它覆盖哪条 F/R、该 F/R 来自哪个来源、置信度多少、有无挂起冲突」。以前 prompt 里明明要求了证据字段,产物却回答不了这些——字段要求不等于结构约束,结构约束才造得出可回答的产物。

第三个口径是 Critic 的拦截率要有账:它拦下来的用例里,多少是真的问题(可执行性、可观察性缺陷)、多少是误伤。一个没有拦截记录的 Critic 和一个 100% 拦截的 Critic 一样可疑——前者可能在睡大觉,后者可能在扼杀所有输出。

第四个口径是把「评审标准」固化成回归资产:我们把 18 段真实 QA 会话处置成 32 条 Golden Dataset case,配逐 Case Review 和分项 Scorer——「是否覆盖关键行为、是否触碰禁止行为、执行轨迹是否可信」三条各打各的分。这一步的意义在于:Critic 的判断标准不再是某次评审时我脑子里的感觉,而是可被回放、可被 diff、可进 CI 的显式基准。治理层自己的质量也要有回归,否则 Critic 本身就是下一个不稳定源。

收个尾

「同一需求两次跑出不同结果」表面上是稳定性问题,骨子里是证据链问题:多源材料没分层、冲突被无痕融合、假设没有位阶、输出没人复审。Evidence Model 给每条结论上户口,Critic Pass 给每次交付设岗哨——把「模型觉得」降级为输入之一,把「可追溯、可复审」升级为交付的及格线。

QA Agent 想被人信任,靠的不是它多会写用例,是每条用例都能说出自己从哪来、经过了谁的审查——户口和岗哨,一个都不能少。