1. 项目概述:为什么“答非所问”不是模型的错,而是整个知识链路的失守
“AI知识库总是答非所问?”——这句话最近在技术团队晨会、客户支持群、甚至产品经理的OKR复盘里高频出现。我上个月帮三家不同行业的客户做知识库上线后的效果诊断,无一例外,他们第一句抱怨都是:“我们喂了2000份产品手册、300条FAQ、50个内部SOP,结果用户问‘怎么重置密码’,它回了一段关于‘密码学哈希算法演进史’。”听起来荒谬,但背后是真实存在的系统性断层。这不是大模型本身“变笨了”,而是从原始数据进入知识库,到最终生成答案的整条链路中,至少有7个关键节点可能悄然失效。我把这个过程比作一条精密流水线:上游原料(PDF/Word/网页)如果含沙量高、杂质多,中游清洗(切片、向量化)若参数设置不当,下游组装(检索+生成)再先进,也只会把错误信息包装得更漂亮。真正的问题往往藏在“看不见”的环节——比如你用默认的512字符切片去处理一份带复杂表格的财务制度文档,表格被硬生生劈成两半,语义彻底断裂;又比如你把所有文档不加区分地扔进同一个向量库,销售话术和API接口文档混在一起,检索时根本分不清用户是在问“怎么报价”还是“怎么调用token接口”。这篇文章不讲空泛原理,只记录我过去三个月在6个真实项目中逐层拆解、定位、修复“答非所问”问题的完整过程。你会看到具体到某一行代码的参数调整、某一个chunk的切片效果对比图、某一次RAG日志里暴露出的检索失败路径。适合正在搭建或优化知识库的工程师、AI产品经理、以及被业务方反复追问“为什么AI总说不到点子上”的技术负责人。如果你只想要一个“一键修复”的按钮,那抱歉,这不存在;但如果你愿意花45分钟,跟着我把这条链路从头摸一遍,下次再遇到类似问题,你就能在15分钟内锁定故障点。
2. 知识链路全景拆解:从原始数据到生成答案的7个关键断点
要解决“答非所问”,必须先画出这张链路图。我把它拆成7个不可跳过的环节,每个环节都对应一个明确的“责任主体”和一套可验证的检查方法。这不是理论模型,而是我在现场用日志、截图、A/B测试反复验证过的实战地图。
2.1 断点1:原始数据质量——90%的“答非所问”始于源头污染
很多人以为知识库效果差是因为模型不够强,其实第一步就错了。上周我接手一个医疗知识库项目,客户提供了800份PDF格式的诊疗指南,表面看很规范。但当我随机抽样打开10份,发现3份是扫描件(OCR识别错误率超40%),2份是加密PDF(文字层完全丢失),还有4份在页眉页脚嵌入了大量医院LOGO矢量图(导致文本提取时插入乱码字符)。这些“脏数据”直接进入后续流程,等于往发动机里灌沙子。更隐蔽的是语义污染:一份《高血压用药指南》里混着3页广告页,标题写着“XX药企学术支持”,内容却是药品推广话术。当用户问“氨氯地平禁忌症”,向量检索可能优先匹配到广告页里高频出现的“氨氯地平”字样,却忽略了正文里真正的禁忌列表。检查方法很简单:写个脚本,对所有原始文件做三件事——① 检测文件是否可提取纯文本(pdfplumber+try/except捕获异常);② 统计每页有效文本占比(剔除页眉页脚后剩余字符数/总字符数);③ 对提取文本做关键词密度分析(用jieba分词后统计TOP20词,人工核对是否符合文档主题)。我给客户的整改清单第一条就是:“停掉所有扫描PDF入库,全部重扫+人工校对;删除所有含商业推广内容的页面;对页眉页脚超过15%的文档,手动调整提取区域。”
2.2 断点2:文本预处理——切片不是越小越好,而是要“语义完整”
切片(chunking)常被当成技术细节忽略,但它决定着知识能否被正确理解。默认的512字符切片在处理技术文档时简直是灾难。举个真实例子:一份Kubernetes配置YAML文档,其中一段定义了Pod的健康检查探针:
livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 30 periodSeconds: 10如果按字符切片,这段代码可能被切成两半——前半段在chunk A,后半段在chunk B。当用户问“livenessProbe的initialDelaySeconds是多少”,检索系统只能匹配到包含livenessProbe的chunk A,却找不到initialDelaySeconds的值,因为后者在另一个chunk里。我的解决方案是“语义感知切片”:对代码块用tree-sitter解析AST节点,确保整个livenessProbe对象不被拆分;对Markdown文档用markdown-it解析标题层级,以##为最小切片单元;对普通段落则用NLTK的句子分割器,以完整句子为单位。参数选择上,我放弃固定长度,改用动态阈值:单个chunk最大长度设为1024字符,但强制要求“最后一个句子必须完整结束”。实测下来,技术类文档的问答准确率从58%提升到82%。这里有个反直觉经验:切片越细,召回率可能越高,但精确率必然暴跌。因为用户问题往往需要跨多个句子才能理解上下文,碎片化切片让模型失去推理依据。
2.3 断点3:向量化嵌入——同一份文档,不同嵌入模型给出的答案天差地别
向量数据库不是“存储容器”,而是知识的“语义索引器”。用text-embedding-ada-002处理中文法律条文,效果远不如bge-zh-v1.5。上周一个金融客户用OpenAI嵌入模型处理《证券投资基金法》,用户问“私募基金备案需要哪些材料”,检索返回的top3 chunk全是“基金”“管理”“投资”等宽泛词汇匹配的结果,而真正讲备案材料的条款(第32条)因用词严谨(如“私募基金管理人应当向基金业协会履行登记手续”)反而排在第17位。根本原因是嵌入模型的训练语料偏差:OpenAI模型在英文法律语料上训练充分,但对中文法律术语的向量空间分布建模不足。我的排查步骤是:① 抽取10个典型用户问题,人工标注“应匹配的黄金chunk”;② 用不同嵌入模型(bge-zh,m3e,text2vec)对同一份文档向量化;③ 计算每个模型下,黄金chunk在top5中的命中率。结果bge-zh-v1.5命中率92%,text-embedding-ada-002仅38%。选型原则很务实:中文场景闭源模型慎用,优先选HuggingFace上下载量超5万、且有中文评测榜单(如MTEB)排名前3的开源模型。部署时还要注意:不要直接用模型默认的max_length=512,对长文档需启用truncation=True并配合stride=128滑动窗口,否则末尾关键信息永远进不了向量。
2.4 断点4:向量数据库配置——相似度阈值不是玄学,而是业务精度的开关
很多团队把向量数据库当黑盒,调参全靠“感觉”。实际上,similarity_threshold(相似度阈值)直接决定知识库是“过度联想”还是“死板僵硬”。设太高(如0.85),系统只返回高度匹配的chunk,但用户口语化提问(如“那个管登录的接口咋调用?”)可能因用词差异被过滤;设太低(如0.3),又会塞进大量噪声chunk,让LLM在无关信息里挣扎。我的做法是用业务指标反推阈值:先定义“有效回答”的标准——答案必须包含用户问题中的核心实体(如“重置密码”“API密钥”)且引用原文位置(如“见《用户手册》第3.2节”)。然后用100个历史工单问题做A/B测试,在不同阈值下统计“有效回答率”。结果发现,对客服类知识库,最优阈值是0.62;对开发文档类,因术语严谨,可提高到0.71。更关键的是k值(检索返回chunk数量)。盲目设k=5很危险——当用户问“如何配置SSL证书”,如果返回5个chunk里有3个讲Nginx、2个讲Apache,LLM可能混淆指令。我强制要求:按文档类型分库(Nginx库/Apache库/通用库),每个库独立设置k,且对技术类问题,k不超过3,逼迫系统精准聚焦。
2.5 断点5:检索增强生成(RAG)提示词——不是模板越长越好,而是要“约束幻觉”
RAG的提示词常被写成一篇小作文:“你是一个专业助手,请基于以下上下文回答问题……”这种写法在简单问答中尚可,但面对复杂需求立刻失效。上个月一个电商客户,用户问“大促期间订单超时未支付,系统会自动取消吗?取消后库存怎么释放?”,提示词里只写了“请根据上下文回答”,结果模型编造出“系统会在15分钟后自动释放库存”,而真实规则是“订单取消后,库存释放需人工审核”。问题出在提示词缺乏“幻觉约束”和“溯源要求”。我现在的标准提示词结构是三段式:①角色锚定:“你是一个严格遵循《订单中心操作规范V2.3》的客服机器人,所有回答必须基于该文档,禁止推测、禁止补充外部知识”;②输出约束:“如果上下文未提及某信息,必须回答‘文档未说明’,禁止用‘通常’‘一般’等模糊表述”;③溯源强制:“每个答案后必须标注来源,格式为【见《文档名》第X章第Y节】”。实测数据显示,加入溯源强制后,幻觉率下降67%。还有个隐藏技巧:在提示词开头插入一段“元指令”:“请先通读所有提供的上下文,再思考答案。思考过程不得输出”。这能避免模型在生成中途被截断,导致逻辑断裂。
2.6 断点6:大模型选型与微调——不是越大越好,而是要“懂行”
用GPT-4处理内部IT运维知识库,效果未必比Qwen1.5-7B好。原因在于领域适配度:GPT-4的通用知识太强,容易覆盖掉你精心注入的领域规则。一个典型场景:用户问“服务器CPU使用率持续100%怎么办?”,GPT-4可能给出“检查进程、优化代码、升级硬件”等通用建议,而Qwen1.5-7B经微调后,能精准调用知识库里的《Linux服务器故障排查手册》第5.3节:“执行top -Hp <PID>查看线程级占用,并对照手册附录B的常见高CPU进程表”。我的选型逻辑很直接:对内部知识库,优先选7B级别、支持中文、且有丰富LoRA微调案例的模型(如Qwen、ChatGLM3)。微调不是为了提升通用能力,而是教会模型“什么时候该查知识库,什么时候该拒绝回答”。具体做法:用真实对话日志构造三类样本——① 正样本(问题+知识库匹配chunk+标准答案);② 负样本(问题+无关chunk+答案‘文档未说明’);③ 拒绝样本(问题明显超出知识库范围,如“明天股市会涨吗?”)。微调后,模型在“知识边界识别”上的准确率从61%升至94%。
2.7 断点7:效果监控闭环——没有监控的优化,都是自我感动
最后也是最容易被忽视的一环:你怎么知道优化真的生效了?很多团队只看“平均响应时间”或“用户点赞率”,这完全失真。上周一个客户上线新版本后,点赞率从72%升到85%,但深入分析发现,点赞的全是简单问题(如“密码忘了怎么办?”),而复杂问题(如“多租户环境下如何隔离数据库连接池?”)的投诉率反而上升了12%。我建立的监控体系有三个硬指标:① “黄金路径命中率”——用户问题触发的检索,其top1 chunk是否为人工标注的黄金chunk;② “答案溯源率”——回答中明确标注来源的比例;③ “幻觉率”——通过正则匹配检测答案中是否出现“可能”“大概”“通常”等模糊词,或未标注来源的断言。每天自动生成报告,当任一指标连续3天低于阈值(如黄金路径命中率<80%),自动触发告警并推送问题样本。这才是可持续优化的基础。
3. 实战排查四步法:从现象到根因的标准化诊断流程
有了链路图,下一步是落地动作。我总结出一套可复制的四步排查法,已在6个项目中验证有效。它不依赖专家直觉,而是用数据说话,让初级工程师也能快速上手。
3.1 第一步:现象归类——先分清是“没找到”还是“找错了”
所有“答非所问”问题,本质只有两类:检索失败(Retrieval Failure)和生成失真(Generation Distortion)。这是诊断的起点,必须严格区分。
- 检索失败:模型答案明显偏离主题,且不引用任何知识库内容。例如用户问“报销发票抬头要求”,答案却是“如何申请加班费”。此时问题一定出在链路前半段(数据→切片→向量化→检索)。
- 生成失真:模型答案引用了知识库(如“见《财务制度》第2.1条”),但内容与原文矛盾,或添加了原文没有的信息。例如原文写“电子发票需加盖发票专用章”,模型答“电子发票无需盖章,系统自动验真”。此时问题在链路后半段(RAG提示词→模型生成)。
提示:快速判断方法——在调试模式下开启
verbose=True,查看RAG日志中retrieved_chunks字段。如果为空或全是无关chunk,属检索失败;如果chunk内容正确但答案错误,属生成失真。
我给团队的检查清单第一项就是:“请提供问题、模型原始输出、以及RAG日志中的retrieved_chunks内容”。上周一个案例中,客户只发来“用户问‘怎么退订会员’,AI答‘请联系客服’”,我让他们补日志后发现,retrieved_chunks里确实有《会员服务协议》第4.2条“自动续费取消流程”,但模型生成时忽略了关键条件“需在扣费日前72小时操作”。这属于典型的生成失真,后续优化聚焦在提示词约束上。
3.2 第二步:链路快照——用“三明治日志”锁定故障节点
传统日志只记录最终输出,无法定位中间环节。我设计的“三明治日志”在每个关键节点插入标记,形成完整证据链:
- 输入层:记录原始用户问题(
user_query)、问题ID、时间戳; - 检索层:记录
retrieved_chunks(含chunk ID、来源文档、相似度分数、前50字符摘要); - 生成层:记录
prompt全文(含系统指令、上下文、用户问题)、model_output、response_time。
注意:
retrieved_chunks必须包含相似度分数!很多团队只存chunk内容,导致无法判断是“没检索到”还是“检索到了但分数太低被过滤”。我要求所有chunk按分数降序排列,且保留score字段。
实战中,这套日志帮我们发现一个隐蔽问题:某次更新后,retrieved_chunks里总有一个chunk的相似度分数异常高(0.98),但内容却是文档的版权声明页。追查发现,预处理时未过滤页眉页脚,版权声明页因重复出现“版权所有”“2024”等高频词,在向量空间中形成了强聚类中心,把所有问题都拉向它。解决方案很简单:在切片后增加一道“版权页过滤”,用正则r'版权所有.*?(\d{4})'匹配并丢弃。
3.3 第三步:根因验证——用“最小化复现”排除干扰
一旦锁定疑似故障节点,必须用最小化复现(Minimal Reproducible Example)验证。不能停留在“可能”“大概”,要拿到铁证。
- 针对检索失败:取一个典型问题,绕过前端,直接调用向量数据库的
search接口,传入相同query和filter参数,观察返回结果。如果数据库返回正确chunk,说明问题在前端传参或RAG集成层;如果数据库也返回错误结果,则聚焦向量化或数据源。 - 针对生成失真:将
retrieved_chunks和user_query拼成静态prompt,用curl直接调用大模型API,对比输出与线上结果。如果一致,说明模型层无问题;如果不一致,则检查线上环境是否有额外的后处理(如敏感词过滤、答案截断)。
上周一个案例中,线上环境对“退款”相关问题总返回“请联系客服”,但最小化复现时模型能给出详细流程。最终发现,是前端SDK有个bug:当检测到问题含“退款”“赔偿”等词时,自动注入了一段system_prompt:“由于涉及资金安全,所有相关问题必须引导至人工客服”。这个隐藏逻辑在日志里完全不体现,只有最小化复现才能暴露。
3.4 第四步:效果度量——用“业务问题集”替代技术指标
技术团队爱看recall@5、mrr,但业务方只关心“用户问什么,AI答什么”。我坚持用真实业务问题构建测试集,每月更新。
- 问题来源:从客服系统导出近30天TOP100未解决工单;从搜索框日志提取100个“零结果”查询;从业务部门收集20个高频政策咨询问题。
- 评估标准:由2名业务专家盲评,按三级制打分:
✅优秀:答案准确、完整、标注来源,且语言符合业务习惯(如客服场景用“您好,请按以下步骤操作…”);
⚠️合格:答案基本正确,但缺少来源标注,或存在轻微歧义;
❌失败:答案错误、编造、或完全偏离主题。
实操心得:评估时必须“隔离上下文”。让专家只看用户问题和AI答案,不看知识库原文。这样才能模拟真实用户体验。我们曾发现一个模型在“有上下文”时准确率95%,但“无上下文”评估时仅68%,说明它过度依赖提示词中的暗示,而非真正理解知识。
这套方法让优化效果可衡量。上个月某客户优化后,“优秀”率从31%升至79%,业务部门主动要求将知识库接入更多渠道。
4. 核心环节深度实现:从代码到配置的完整可复现方案
光有方法论不够,必须落到具体实现。以下是我在生产环境中验证过的、可直接抄作业的代码片段和配置方案,覆盖最关键的三个环节。
4.1 语义感知切片:用tree-sitter处理技术文档
对代码、配置文件等结构化文本,字符切片必然失败。tree-sitter能精准解析语法树,确保逻辑单元完整。以YAML为例:
# 安装:pip install tree-sitter py-tree-sitter import tree_sitter from tree_sitter import Language, Parser # 加载YAML语言(需提前编译:https://github.com/tree-sitter/tree-sitter-yaml) YAML_LANGUAGE = Language('build/my-languages.so', 'yaml') parser = Parser() parser.set_language(YAML_LANGUAGE) def parse_yaml_chunks(content: str, max_chunk_size: int = 1024) -> list: """将YAML内容按语义节点切片,确保livenessProbe等对象不被拆分""" tree = parser.parse(bytes(content, "utf8")) root_node = tree.root_node chunks = [] # 遍历所有block节点(对应YAML中的key-value块) for node in root_node.children: if node.type == "block_mapping": # 获取该block的完整文本 block_text = content[node.start_byte:node.end_byte] # 如果超出长度,递归切分子节点 if len(block_text) > max_chunk_size: sub_chunks = _split_block_recursively(node, content, max_chunk_size) chunks.extend(sub_chunks) else: chunks.append(block_text) return chunks def _split_block_recursively(node, content, max_size): """递归切分过大的block,优先按mapping_pair切分""" sub_chunks = [] for child in node.children: if child.type == "mapping_pair": pair_text = content[child.start_byte:child.end_byte] if len(pair_text) <= max_size: sub_chunks.append(pair_text) else: # 对value部分进一步切分(如长字符串) value_node = child.child_by_field_name("value") if value_node and value_node.type == "string": sub_chunks.extend(_split_long_string(value_node, content, max_size)) return sub_chunks关键参数说明:max_chunk_size=1024不是拍脑袋定的。我统计了1000份技术文档中,livenessProbe、env、volumes等常见K8s对象的平均长度,中位数是892字符,设1024留出缓冲。实测效果:对一份含5个Probe定义的deployment.yaml,传统切片产生17个碎片,tree-sitter切片仅4个,且每个都是完整对象。
4.2 向量数据库优化:Milvus中的动态阈值配置
Milvus是当前最主流的向量数据库之一,但其search参数常被误用。重点配置如下:
# Milvus 2.4+ Python SDK from pymilvus import Collection, connections connections.connect("default", host="localhost", port="19530") collection = Collection("knowledge_base") # 已创建的集合 # 关键:使用ANN搜索时,必须指定consistency_level # 用"Strong"保证每次查询看到最新数据,避免缓存导致的旧结果 collection.load(consistency_level="Strong") # 搜索参数——这才是性能与精度的平衡点 search_params = { "metric_type": "IP", # 内积,比L2更适配余弦相似度 "params": { "nprobe": 64, # 增加nprobe提升精度,但降低速度;64是实测平衡点 "ef": 128 # HNSW索引的ef参数,越大越准,128在P95延迟<100ms内 } } # 执行搜索——注意:results是二维列表,[0]对应第一个query results = collection.search( data=[query_embedding], # 单个查询向量 anns_field="embedding", # 向量字段名 param=search_params, limit=3, # 严格限制k=3,避免噪声 output_fields=["doc_id", "chunk_id", "source_file"] # 必须返回来源,用于溯源 ) # 后处理:动态过滤低分结果 for hits in results: filtered_hits = [] for hit in hits: # 动态阈值:对技术文档设0.71,客服文档设0.62 threshold = 0.71 if is_tech_doc else 0.62 if hit.score >= threshold: filtered_hits.append(hit) # 只返回过滤后的结果 final_results = filtered_hits[:3]为什么nprobe=64?我在200GB知识库上做了压测:nprobe=32时P95延迟42ms,但黄金chunk命中率仅73%;nprobe=128时命中率89%,但P95延迟飙升至156ms。64是命中率85%与延迟88ms的最佳交点。consistency_level="Strong"至关重要:某次上线后问题频发,最终发现是默认Bounded一致性导致查询偶尔读到旧向量,切换后故障归零。
4.3 RAG提示词工程:防幻觉的三重约束模板
这是经过200+次A/B测试验证的提示词结构,直接可用:
# 系统指令(严格锚定角色与边界) 你是一个专注解答《[文档名称]》的AI助手。你的知识仅限于该文档内容,禁止使用任何外部知识、常识或推测。如果文档未提及某信息,必须回答“文档未说明”,禁止使用“可能”“一般”“通常”等模糊表述。 # 上下文(RAG注入的chunk,按相似度降序排列) [上下文1:相似度0.82,来源《用户手册》第3.2节] 用户可通过APP首页右上角“设置”图标进入账户管理... [上下文2:相似度0.75,来源《API文档》第5.1节] POST /v1/users/{id}/password/reset 接口用于重置用户密码... # 用户问题 怎么在APP里重置密码? # 输出要求(强制约束生成行为) 1. 答案必须基于且仅基于以上上下文; 2. 每个事实性陈述后必须标注来源,格式为【见《文档名》第X章第Y节】; 3. 如果上下文未提供完整答案,需明确指出缺失部分【文档未说明】; 4. 禁止添加任何解释、建议或额外步骤。关键设计点:
- 来源标注强制:用
【】符号包裹,区别于普通括号,便于后续正则提取验证; - 缺失声明标准化:统一用“文档未说明”,避免模型用“不清楚”“暂无信息”等变体;
- 禁用词列表:在部署时,用
post_process函数扫描输出,匹配r'(可能|大概|通常|一般|建议|可以试试)',匹配则替换为“文档未说明”。
实测显示,此模板将幻觉率从34%压至5%以下,且人工审核耗时减少70%。
5. 常见问题与独家避坑指南:那些文档里不会写的血泪教训
最后分享我在实战中踩过的坑,以及对应的速查解决方案。这些都是文档里找不到,但能让你少走半年弯路的经验。
5.1 问题速查表:高频故障与一键定位法
| 现象 | 可能根因 | 一键定位法 | 解决方案 |
|---|---|---|---|
| 所有问题都答“请联系客服” | 前端SDK注入了全局system_prompt | 在浏览器控制台Network标签页,抓取API请求,检查messages[0].content是否含隐藏指令 | 审查前端代码,移除自动注入逻辑;或在后端增加prompt清洗 |
| 技术问题答得准,客服问题全错 | 向量库未分库,技术文档淹没客服文档 | 用collection.query查source_file字段分布,看客服类文档是否在top100检索中占比<5% | 按文档类型建多个collection,查询时路由到对应库 |
| 答案总带“根据文档”但内容错误 | RAG提示词未禁用模型自身知识 | 将retrieved_chunks和user_query拼成prompt,用curl直连模型API,关闭temperature=0 | 在system_prompt开头加:“你是一个严格的文档复读机,禁止任何创造性发挥” |
| 响应时间忽快忽慢(200ms~3s) | Milvus缓存未预热,首次查询加载索引 | 重启Milvus后,立即执行collection.load(),观察collection.num_entities是否为0 | 上线前执行collection.load()预热;监控system_cache_hit_rate指标 |
| 中文标点被识别为乱码 | PDF提取时编码未指定为utf-8 | 用pdfplumber打开PDF,打印page.chars[0]的fontname和unicode属性 | 提取时强制encoding='utf-8',对特殊字体用fontmap映射 |
5.2 那些没人告诉你的“反直觉”真相
- “高质量数据”可能比“海量数据”更致命:一个客户精心整理了500份“高质量”FAQ,每份都经过法务审核。结果发现,这些FAQ为规避风险,大量使用“原则上”“一般情况下”“视具体情况而定”等模糊表述。模型学到的不是规则,而是模糊话术。对策:对FAQ做“确定性增强”,用正则将
r'原则上.*?([。!?])'替换为r'必须\1'(需人工复核)。 - 向量维度不是越高越好:用
text2vec-large-chinese(1024维)比bge-small-zh-v1.5(384维)在小知识库上效果更差。原因在于高维向量在小数据集上易过拟合,相似度计算更敏感。对策:数据量<10万chunk时,优先选384维模型;>50万chunk再考虑768/1024维。 - “实时更新”可能是伪需求:某客户坚持要求“文档修改后1秒内生效”。实测发现,频繁的
upsert操作导致Milvus索引重建,反而使P99延迟从120ms升至2.3s。对策:改为每小时批量更新,用delete+insert替代upsert,并设置index_task_timeout=300。
5.3 我的终极检查清单(上线前必做)
每次知识库上线前,我都会带着这份清单逐项核验,漏一项都可能引发线上事故:
- 数据层:随机抽10份原始PDF,用
pdfplumber提取后,肉眼检查是否有乱码、缺失段落、页眉页脚污染; - 切片层:对一份含代码块的文档,对比
tree-sitter切片与字符切片结果,确认关键对象是否完整; - 向量层:用10个问题测试
search,检查retrieved_chunks中是否有版权页、目录页等无效chunk; - RAG层:运行3个典型问题,检查输出中是否100%包含
【见...】标注,且无模糊词; - 监控层:确认
gold_path_hit_rate、source_citation_rate、hallucination_rate三个指标已接入Prometheus,并设置告警阈值。
最后一句真心话:知识库不是“建完就完”,而是“上线即开始”。我见过太多项目,花了3个月搭建,上线后没人看监控,两周后问题堆积如山。真正的终点,是建立一个每天自动推送问题样本、每周生成优化建议的闭环系统。当你不再需要手动排查,而是等着系统告诉你“第3.2节的表述需要更明确”,那时,知识库才算真正活了过来。