把多来源产品文档做成可问答知识库:2884 个 chunk 背后的清洗与收敛
- 客户
- 一家 B2B 软件企业,多条产品线、多个交付通道
- 阶段
- 已上线
- 结果
- 入库 2884 个有效 chunk,上线时 13 条抗污染探针全部通过(后续回归基线 12/13,1 条已知失败),端到端问答系统已上线
- 涉及能力
- 企业 RAG知识库治理混合检索多轮对话本地化部署
阅读说明:系统已在客户环境运行。 客户名称与可识别的业务标识已全部隐去;文中的方法、结构与数字为真实记录。
案例速览
| 项 | 内容 |
|---|---|
| 问题 | 多产品线文档散在十余种来源,问答时 A 产品问题拿 B 产品步骤作答(产品污染) |
| 数据规模 | 14 类来源、8 条产品线;清洗后 2884 个 chunk |
| 项目阶段 | 已上线;更换解析层后做过一次全量回归 |
| 验证方法 | 13 条金标准探针,断言检索结果「必须命中 / 必须排除」哪个产品,每次构建后自动跑 |
| 结果 | 上线时 13/13 通过;回归基线 12/13,1 条多轮话题漂移探针为已知失败 |
| 不在验证范围内 | 回答文本准确率(未建立评估题集);权限控制(本项目数据已脱敏,未启用 ACL) |
背景
客户是一家 B2B 软件企业,有多条产品线,覆盖邮件、Web、终端、网络等多个交付通道。产品文档积累了很多年,但散落在十几种不同来源里:管理员手册、售后知识库、功能列表、规格表、端口清单、PoC 实验方案、操作指引、开发接口文档、售前方案、术语表。
售后工程师查一个配置项,常常要在几个系统里翻;新人上手周期长;售前写方案时经常拿到过期的参数。客户希望有一个统一的问答入口。
问题
表面需求是「做个知识库问答」,但真正的困难有三层。
第一层:来源异构。 十几种来源,每种的结构、粒度、命名习惯都不同。手册是章节化的、知识库是问答式的、规格表是 Excel 层级表、端口清单是一行一记录。用同一套切分规则处理它们,结果必然是一锅粥。
第二层:产品污染。 多条产品线的文档措辞高度相似——同样叫「策略配置」,在不同产品里是完全不同的操作。用户问 A 产品,系统拿 B 产品的步骤来回答,这是这类知识库最常见也最致命的失败模式。纯语义检索区分不了,因为语义上它们确实很像。
第三层:数据是脏的,而且脏得不明显。
原有方式为什么不行
项目初期用了常规做法:所有文档统一转成文本、统一切分、统一嵌入、向量检索加 rerank。
结果是语料量看上去很健康——6961 个 chunk,词表 47 万。但问答质量始终上不去,且错得没有规律。
排查下来,问题出在 18 个 .doc 文件上。它们不是 Word 文档,而是某协作平台「导出为 Word」生成的 MHTML——本质是 MIME 封装的 quoted-printable HTML,图片以 base64 内嵌。转换工具把它们当纯文本处理,于是:
- 191 个 chunk 是 MIME 头和编码残留的乱码
- 约 4000 个 chunk 是 base64 图片数据被切成的碎片
这些垃圾 chunk 占了语料的六成,把词表撑到 47 万,稀释了所有真实内容的检索权重。而它们在统计上看起来像是「内容很丰富」。
这类问题的共性:脏数据不是客户维护不当,而是导出工具的产物。任何经过「导出为某格式」这一步的文档,都值得先验证它到底是什么格式,而不是相信文件扩展名。
关键约束
- 索引与检索必须全部在内网完成,生成环节按合规要求可切换本地模型或云端 API
- 模型可替换——嵌入、精排、生成三层都要抽象成接口,不绑定任何一家
- 数据自包含——图、文、索引归集到一处,运行时不依赖原始文档目录,源文档可整体归档
- 可追溯、可回滚——每个 chunk 记录来源;清理只做移动不做删除
做法
一、一来源一提取器,而不是一套通吃
十几种来源各写一个提取器,产出统一结构的 chunk。手册按概念/步骤拆、知识库按故障排查/最佳实践/使用指南分、规格表按(产品,类别)聚合、端口清单一行一记录并额外产出结构化旁路。
针对 MHTML 写了专用转换器:按 MIME 头识别,用邮件解析库处理,而不是走文档转换工具。修复后 22 本手册零 MIME 残留,额外抽出 405 张图,语料从虚高的 6961 回落到 2884 个真实 chunk,词表从 47 万降到 10 万。
二、元数据是一等公民
每个 chunk 带一组决定检索收敛的 facet 字段:内容角色、所属产品、能力、通道、范围(共享/产品专属)、适用范围列表、内容类别、受众。
这些字段还会拼成一段前缀,参与嵌入和关键词检索:
[产品: X] [文档类型: 操作步骤] [能力: 数据识别/指纹] [通道: 邮件]
[适用范围: 共享,适用 X/Y/Z]
检索精度由 facet 决定,不是靠 embedding 猜产品。 这是整个项目最核心的一条设计判断。
三、共享内容存一份,专属内容按产品实例化
跨产品的共用能力只存一份,带「适用哪些产品」的列表;产品专属的操作步骤按产品拆开并标出分歧点。过滤规则是确定性的:内容属于目标产品,或者内容是共享的且适用范围包含目标产品。
四、多轮对话的软继承
用户在多轮中往往不重复说产品名。系统需要判断这一轮该不该沿用上一轮的产品上下文。
做法不是简单继承,而是看证据:如果本轮问的是共享能力,继承;如果检索结果里出现「明确声明了适用产品、但排除继承产品」的内容达到一定数量,说明这个能力不属于继承产品,主动放弃继承走全局检索;如果另一个产品的内容压倒性占优,切换。
实际解决的问题:用户先聊了产品 A,接着问某个存量数据发现功能。该功能是产品 B 和 C 的共享能力、适用范围明确不含 A。软继承判定为「放弃继承」,走全局检索给出正确答案,而不是绑死在 A 上答错。
五、混合检索 + 证据门禁
关键词检索用自实现的中文双字分词倒排(精确命中缩写、端口号、产品名),与向量检索融合,叠加 facet 加权和时效性微调,再用精排模型在候选池内语义排序。
汇总类、端口类、术语类查询跳过精排——这些问题 facet 和结构化查表已经给出正确答案,精排只会引入噪声。
最后一道是证据门禁:用融合分判断证据是否充分,输出「充分 / 部分 / 需要对比 / 需要澄清 / 不相关」五种状态。澄清只在多个产品真正竞争时触发;证据一致指向单一产品时直接作答,不做无谓的反问。
验证方式
13 条抗污染探针——每条是一个金标准查询,断言「必须命中什么、必须排除什么产品」。比如「问数据识别指纹原理时,共享能力块必须进入结果,某产品专属的协议匹配内容必须排除」。这些规则从人的经验变成了可回归的自动断言,每次构建后自动跑。
首要指标是产品污染率,不是回答准确率。 配套指标包括错误产品步骤率、共享内容误判为专属率、结构化问题误走向量率、拒答正确率、澄清正确率。
需要如实说明:这些指标是项目定下的目标口径,目前实测的只有探针这一层。探针验证的是检索结构对不对(拿到的是不是该产品的内容),不是最终回答文本对不对;本项目没有建立成规模的回答评估题集,因此不报告回答准确率。
结果
| 项 | 结果 |
|---|---|
| 入库 chunk | 2884 条(清理前虚高至 6961) |
| 词表规模 | 10 万(清理前 47 万) |
| 抗污染探针 | 上线时 13 / 13 通过;更换解析层后回归基线 12 / 13(1 条已知失败,见下文) |
| 图片资产 | 419 张,归集后原文页可图文渲染 |
| 第三方依赖 | 2 个(pyyaml、openpyxl),无 RAG 框架 |
| 系统状态 | 已上线,支持多用户登录、会话隔离、流式问答、原文回看 |
语料来源构成:手册 656 · 售后知识库 644 · 端口 267 · 用户手册 212 · 售前方案 200 · API 181 · 规格 169 · 功能列表 159 · 操作指引 130 · PoC 121 · 术语 112 · 场景 24 · 汇总 9。
经验与边界
三条可迁移的经验:
- 先验证数据是什么,再决定怎么处理。 六成语料是垃圾这件事,在任何指标上都不会自己浮出来——它表现为「回答质量差且没规律」。
- 多产品知识库的第一指标是污染率,不是准确率。 一个回答得体但张冠李戴的系统,比一个明确说「我不确定」的系统危险得多。
- 把领域规则写进过滤层,不要交给模型猜。 「这个能力适用哪些产品」是确定性事实,应该是一次表查询,不是一次语义判断。
边界:
- 抗污染探针目前 13 条,覆盖的是已知的高风险边界,新增产品线时需要同步扩充
- 仍有一条已知失败:上一轮聊 A 产品、本轮问的其实是 B 产品的功能,而两者文档用词相近时,系统没有从 A 切到 B。回归基线据此冻结为 12/13,没有删掉这条探针
- 只验证了检索层,没有成规模的回答评估集;回答准确率未测
- 本项目数据已脱敏,没有启用权限控制。这个案例证明的是数据治理与检索收敛,不能作为知识库权限安全已验证的证据
- 生成环节仍依赖大模型,证据门禁能控制「拿什么答」,但不能完全消除表述层面的偏差
- 本案例的 facet 体系是按这家企业的产品结构设计的,换一家企业需要重做这一层——这也是这类项目无法产品化交付的原因
相关文章
在做类似的项目?
可以从一个真实业务场景开始,先判断值不值得做、数据是否具备条件、安全和权限问题在哪里,再决定是否进入 PoC。
预约 30 分钟交流