2026年6月3日 · 阅读 —

我给测试知识库做智能问答时最重要的一步反而不是调用大模型

测试与评测知识与内容工具

我给测试知识库做智能问答时,最重要的一步反而不是调用大模型

!{{year}}年{六月}月-{{noteName}}.webp

很多朋友一听 AI 问答,第一反应就是,模型用哪个。

Claude 还是 GPT。

要不要上向量库。

embedding 选哪个。

说真的,这些都重要,但我真正在做测试知识库的时候,最折磨我的反而不是模型。

是证据召回。

因为测试人员问的问题,通常不是开放闲聊。

他问「支付超时重试会影响哪些测试范围」,不是想听一段泛泛的支付测试方法论。他想知道在当前项目里,有哪些需求、用例、Bug、模块、状态流和这件事有关。

所以我后来把智能问答链路拆成了一个很笨,但比较可靠的流程。

先找证据。

再让模型说话。

一条问题进来以后发生了什么

当前链路大概是这样。

flowchart TD
    A[用户问题] --> B[关键词和主题词抽取]
    B --> C[本地混合检索]
    B --> D[LLM-Wiki 资料检索]
    B --> E[概念和实体检索]
    B --> F[Graphify 图谱检索]
    B --> G[Fine-Graph 测试图谱检索]
    C --> H[证据池]
    D --> H
    E --> H
    F --> H
    G --> H
    H --> I[去重排序截断]
    I --> J[组装 Prompt]
    J --> K[LLM 生成结论]
    K --> L[答案、证据、检索 trace]

这张图看着像架构图,但它背后的想法非常朴素。

一个问题不要只走一条路。

因为任何单一路径都会漏。

BM25 能找到关键词很准的文档,但不懂同义表达。

图谱能找到关系,但前提是关系已经构出来。

概念页能找到业务主题,但可能缺少具体用例。

历史 queries 能找到之前沉淀的高质量答案,但不能让它压过原始资料。

所以最后只能把多路结果放进同一个证据池,再统一去重和排序。

这个过程看着麻烦,但很值。

本地混合检索为什么要做得这么土

我没有一上来就把所有希望押在语义检索上。

原因也很简单,测试资料里有大量中文业务词、页面名、接口名、状态名。

这些词很多时候不需要理解。

它就是要精确命中。

比如「检测报告」「定金」「留资」「车源详情」「支付超时」。

命中了就是命中了。

这时候 BM25 加精确关键词,反而非常稳。

检索方式它擅长什么它不擅长什么
BM25找关键词相关文档,稳定、可解释不理解同义表达和隐含关系
精确关键词找业务词、文件名、路径命中容易漏掉换一种说法的内容
QMD 语义召回找语义相近资料依赖索引质量,可解释性弱一点
Graphify找节点关系和上下游依赖图谱构建质量
Fine-Graph找测试点、边界、风险需要测试资产本身有结构

这里比较有意思的是 RRF。

RRF 不是召回方式,而是融合排序方式。

BM25 有一个排名,关键词命中也有一个排名。两个排名的分数不是一个量纲,强行相加很怪。

RRF 的做法很粗暴,按名次加分。

score += 1 / (RRF_K + rank)

它不关心你原始分数是多少,只关心你在某一路结果里排得靠不靠前。

所以一个文档如果在 BM25 和精确关键词里都靠前,它就会比较稳定地被选出来。

这种算法没有什么玄学感。

但在工程里,就是好用。

证据不是文本片段这么简单

如果只是召回几段文本,那这个链路还不够。

我希望前端能展示「这条答案到底怎么来的」,所以每个节点都会生成 trace。

也就是 ChatTraceStep。

它大概记录这些东西。

字段作用
name这一步是什么检索节点
status这一步有没有完成
detail召回了多少证据,做了什么处理
keywords本轮使用了哪些关键词
evidence_count这个节点贡献了多少证据

证据本身是 EvidenceItem。

字段作用
title证据标题
path证据来源路径
kind来源类型,比如 wiki_article、graph_node
confidence证据置信度
snippet进入 Prompt 的片段
score排序分数
source_node来自哪个检索节点
selection_reason为什么被选中

我很喜欢 selection_reason 这个字段。

因为它让系统从「我找到了这个」往前走了一步,变成「我为什么认为这个值得给你看」。

这个区别很关键。

测试人员不是不能接受 AI 辅助判断。

测试人员不能接受的是,AI 用一种很自信的语气,说一个你不知道从哪来的结论。

一个问题可以这样跑

假设用户问:

支付超时重试会影响哪些测试范围?

链路可能会拆出这些词。

支付
超时
重试
测试范围
订单
状态流
回归

然后几路证据会同时回来。

来源可能召回的内容
本地混合检索支付相关需求、用例、历史报告
Wiki 资料订单状态、支付规则、异常场景
概念实体支付、订单、超时、回归范围
Graphify支付节点关联的页面、模块、Bug
Fine-Graph对应测试点、边界条件、风险项

最后模型拿到的不是一个空问题。

而是一包整理过的证据。

它的任务也不再是凭空发挥,而是基于证据做归纳。

这时候答案的质量就会稳定很多。

为什么 trace 很重要

我以前觉得 trace 是给开发调试用的。

后来发现不是。

trace 是给用户建立信任用的。

当用户看到系统先做了关键词抽取,再走本地检索,再走图谱,再把证据排出来,他会知道这个答案不是直接拍脑袋。

尤其是测试场景里,用户经常要拿这个结论去做下一步动作。

要不要补用例。

要不要扩大回归范围。

要不要拦提测。

要不要把某个风险写进报告。

这些动作背后都需要依据。

没有 trace,AI 答案就是一段文字。

有 trace,它才开始像一个可复核的工作流。

这篇的结论

我现在越来越觉得,AI 问答系统最核心的体验,不是回答得多漂亮。

而是当它回答完以后,用户能不能顺着证据一路点回去。

能看到它查了什么,漏了什么,哪些是强证据,哪些只是推断。

如果能做到这一点,模型哪怕说得朴素一点,也能用。

如果做不到这一点,模型说得再顺,也只能当聊天。

下一篇就可以继续往下讲了。

当文档能被检索以后,为什么我还要继续做图谱。

因为测试真正想知道的,往往不是某个文件写了什么。

而是这些东西之间,到底有什么关系。