ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

企业知识库Agent检索评测:从杂乱数据到端到端指标

企业知识库Agent检索评测:从杂乱数据到端到端指标 这两年做企业知识库Agent项目的同事几乎都会被同一个问题卡住检索系统在demo里跑得飞快一问一个准可一旦接到真实的公司知识库上效果就像换了个人。文档一多、格式一杂、版本一乱召回就开始飘Agent拿到一堆上下文胡言乱语。问题不在于模型不够强而在于我们对检索这件事的理解太理想化了。这篇文章想聊的就是围绕Benchmarking retrieval for agents on messy real-world company knowledge这个主题总结的一套检索评测方案。它解决的核心问题很简单如何用一套可复现、贴近真实业务分布的评测方法量化地判断哪个检索策略在Agent场景下真的更好。我会从评测集的搭建思路、指标体系的取舍、基线选型到具体管线代码和踩坑实录都过一遍。适合正在做企业知识库问答、RAG应用、Agent工具链的工程师或技术负责人参考也适合那些被效果说不清困扰的团队拿来当起点。1. 项目背景Agent检索面对的不是干净文档而是messy的公司知识1.1 企业知识库里到底有多乱很多人以为企业知识库就是一堆写得规规矩矩的Word和PDF标题清晰、层级分明切一切扔进向量库就能用。真实情况远没有这么美好。我接触过的真实企业知识库通常混合着产品规格书、销售PPT、售后工单、客户邮件、会议纪要、内部Wiki甚至还有Excel里零散记录的业务规则。这些资料的格式千奇百怪PPT里大量文字是图片格式PDF有扫描件也有加密件Word里嵌套表格Excel里一个单元格塞了上千字说明。更麻烦的是同一主题往往存在多版本不同时期、不同部门维护的文档里同一个术语可能代表完全不同的东西同一个流程的步骤描述还互相冲突。这种messy还体现在信息密度不均衡上。有些文档被反复复制粘贴大量重复内容有些则高度浓缩一句话包含三个专业缩写。语言混杂也常见中英文术语混排、部门黑话满天飞。这些信息进了向量库之后检索系统根本分不清哪些是权威版本哪些是废弃内容哪些只是聊天记录里的随口一提。第一次跑评测时你会发现检索结果表面上related细看却答非所问这就是企业知识库的常态。1.2 Agent检索和普通RAG的差异决定了评测方式完全不同做传统RAG问答时用户的提问通常是一句话检索系统只要把相关段落捞出来大模型综合成答案就行。但Agent不是这么工作的。第一个差异是多轮多步检索。Agent解决一个任务往往要拆解成多个子步骤每一步都可能触发一次或多次检索。比如一个帮我写一份竞品分析报告的任务Agent可能需要先检索公司内部的产品资料再检索销售团队整理的客户反馈然后检索竞争对手的公开信息。每一次检索都是独立动作但上下文却一脉相承上一轮的答案会成为下一轮检索的背景。评测这种链条时只看单次检索的质量是不够的还要看整个决策链条的稳定性和可追溯性。第二个差异是证据的锚定作用。Agent拿到检索结果后会试图把几个片段组合起来推理。如果其中一段检索错了、过期了、或者根本是不同语境下的同名概念Agent不会像人一样停下来质疑而是会顺着错误信息往下编。这个特性决定了在Agent场景里检索出来的内容是精但少好还是多而杂好结论和传统RAG完全不同。第三个差异是Token预算约束。Agent通常有一个整体上下文窗口检索占用的Token越多留给推理和指令执行的空间就越少。很多时候评测系统唯独没有考虑这个指标导致一个检索策略在Recall上分数高放到Agent里却因为上下文过长把推理质量拖垮了。这三个差异叠加起来决定了专门为Agent设计一套检索评测体系是有必要且绕不开的——直接沿用传统RAG的评测方法结果会失真。1.3 没有基准就无法回答哪个方案更好这个基本问题在做这个项目之前团队内部的检索方案选型基本靠拍脑袋。有人说BM25稳有人说向量检索效果好有人说必须上重排。每个人都能举出自己遇到过的一两个成功案例但谁也说服不了谁。这种情况下唯一的仲裁者就是用统一数据、统一指标、统一流程跑出来的评测结果。评测基准的意义不只是选型。它还是优化方向的指南针。没有基准的时候改检索策略就像蒙着眼睛调参数改了embedding模型、调了chunk大小、加了重排效果到底有没有变好全靠感觉。有了可复现的评测集和指标之后每次改动都能量化对比团队才能真正进入优化—验证—再优化的正循环。这条路径值得完整走一遍因为它在公司内部也是投入产出比极高的基建。2. 评测方案设计从评测集构建到指标体系2.1 评测集怎么搭才能还原真实杂乱搭建评测集是这个项目里最花时间、也最容易草率的部分。很多人为了省事从网上找公开问答对直接评测结果模型表现很好一上生产就翻车。原因很简单公开数据集是干净的而你的知识库是杂乱的二者分布完全不同。我的做法是把评测集分成三个子集分别对应企业知识库最常见的三类脏乱形态。第一个子集叫碎片化与噪音集。模拟的是知识库里大量存在的半结构化文档比如会议纪要里随手记的一句话、工单系统里充斥着这个那个指代不明的记录、PPT里从别处复制粘贴过来的残缺段落。这类数据的特点是信息片段多、上下文不完整、口语化表达多测试的是检索系统在低信息密度环境里找关键点的能力。第二个子集叫多版本冲突集。同一个产品、同一项制度在知识库里存在多个版本。有的版本被标记为草案有的已经作废但没人删有的内容虽然新但格式不规范导致元信息丢失。这个子集的答案通常需要对多个文档进行交叉验证才能确定哪个版本是有效的回答依据。第三个子集叫跨语言与缩写集。模拟跨国企业里中英文混排、缩写满天飞的情况。比如CRM在一份文档里是客户关系管理系统在另一份文档里是客户续约管理流程的缩写在第三份文档里甚至是一个员工名字的首字母。这个子集专门测试检索系统对多义词和上下文的敏感度。每个子集我准备了100到150条测试问题其中约60%来自真实业务场景采样40%是人工合成的对抗性样本。合成样本的设计原则是不追求刁钻只追求真实。比如把一段正常的业务规则改写成聊天记录风格把一个术语的解释从文档末尾移到文档开头制造位置偏差。这样的评测集才能让检索系统的弱点暴露出来。2.2 指标设计不能只看RecallK传统RAG评测里最常用的两个指标是RecallK和nDCGK。它们统计的是检索出来的K个片段里有没有相关文档以及相关文档的排序质量。这两个指标在传统RAG里够用但在Agent场景里不够因为它们完全忽略了检索结果被消耗之后的下游表现。我在评测方案里把指标分成两组离线检索指标和端到端Agent指标。离线检索指标还是保留RecallK和nDCGK用来快速衡量检索系统本身的优劣。端到端Agent指标增加了三个Answer Success RateASRAgent用检索结果回答问题时答案被判定为正确或可接受的比例。这是最重要的一个指标因为它直接反映了检索结果能不能支撑Agent完成任务。Faithfulness忠实度答案内容是不是严格基于检索文档有没有出现文档里没有的信息或与文档冲突的信息。这个指标在检索评测里尤其关键因为很多检索方案Recall很高但召回的片段里有误导性的旧版本内容Agent基于它编出了与事实不符的答案。Tool Call Efficiency工具调用效率Agent为完成一个任务平均触发了多少次检索。这个指标衡量的是检索结果的一次到位率。如果检索结果质量高Agent一次检索就能继续后续动作如果质量差Agent会反复检索、反复修正整个系统的延迟和成本都会上涨。之所以要同时保留两组指标是因为它们回答的问题不同。离线指标回答这个检索器本身怎么样端到端指标回答这个检索器放进Agent后整体表现如何。我见过不少方案离线指标高得离谱端到端表现却很糟糕问题就出在评测维度不完整。下表可以看出它们的视角差异评测维度离线检索指标端到端Agent指标考察对象检索器检索器 Agent 模型主要指标RecallK、nDCGKASR、Faithfulness、Tool Call Efficiency回答的问题相关文档是否被召回排序是否合理检索结果是否支撑任务完成是否引入幻觉评测成本低可自动化批量跑高需要逐条判读或多模型评估适用阶段检索方案快速迭代、参数粗调上线前验收、跨方案终选2.3 基线怎么选对标对象决定结论上限评测如果没有基线数字就失去了意义。但基线选得不对同样会得出误导性结论。常见的错误是把所有方案都跟无检索比或者只跟自己以前的旧方案比这样得出的提升很容易让人盲目自信。我选择基线时遵循三个原则。第一必须包含一个传统关键词检索基线。BM25这种稀疏检索虽然看起来古老但在企业知识库这种大量专有名词、正式文档的场景里它的表现往往出人意料地好尤其是在缩写集和版本冲突集上。跳过这个基线你会高估向量检索的真实优势。第二必须包含一个最朴素的向量检索基线比如用通用的bge或text-embedding-3-small直接embedding余弦相似度检索。这个基线用来锚定最低限度可用水平所有花哨的优化方案都要能明显超过它。第三必须建立一个理想上界。我通常用人工标注的答案文档作为参考假设如果检索系统每次都完美地召回这些文档端到端指标能到多少。这个上界告诉我们评测集本身的难度上限避免出现所有方案都达不到80分但80分本来就是这个数据集的极限这种误判。选好基线和指标之后评测才算有一个可以反复执行的标尺。接下来要做的是把这套流程工程化让它能自动化、可复现地跑起来。3. 实操过程搭一套可复现的检索评测管线3.1 数据准备与切分策略评测管线的第一步是数据准备。这里有一个经常被忽略的关键点训练语料和评测语料必须物理隔离。也就是说用来建索引的知识库文档和用来构造测试问题的背景文档不能是同一批文件否则检索系统只要记住了文档片段就能轻松拿到高分真正考察的泛化检索能力完全失灵。实际操作中我从公司知识库里挑出大约500篇文档作为评测范围其中300篇用于构建索引200篇留作隐藏文档人工基于隐藏文档生成测试问题。这样评测时Agent必须检索到那些 没见过 的文档才能答对问题更贴近真实场景中知识库在持续增长、模型不可能见过所有内容的实际情况。切分策略对评测结果的影响非常大。我刚开始用固定字数切分比如每段512字符结果在碎片化集上Recall惨不忍睹。原因很简单会议纪要这种半结构化文本里一个完整意思往往跨好几个自然段固定字数切分把上下文腰斩了。后来的做法是结构感知切分 语义切分兜底。具体来说对PDF和Word文档先解析标题层级按章节切分再对过长章节按段落边界二次切分对没有明确结构的文本比如聊天记录、工单使用基于embedding的语义切分器让语义相近的句子自然聚到一个块里最终统一设定一个chunk_size区间如300到800字符防止向量索引某个块的尺寸过大影响检索精度。这一步做完后续所有评测才能稳定复现。切分的品味直接决定了检索系统的上限值得多花时间打磨。3.2 检索评测管线代码实现评测管线我用Python实现整体分成四步构建索引、离线检索、端到端Agent执行、结果评估。这里给一个可运行的简化版本关键技术点的注释我都写在了代码里。import json import numpy as np from langchain.embeddings import HuggingFaceEmbeddings from langchain.vectorstores import Qdrant from langchain.retrievers import BM25Retriever from langchain.retrievers.ensemble import EnsembleRetriever from langchain.schema import Document from qdrant_client import QdrantClient # ---------- 1. 构建索引 ---------- # 注意这里加载的是训练语料评测问题对应的文档不参与建索引 def build_index(docs, collection_namecompany_kb): embeddings HuggingFaceEmbeddings( model_nameBAAI/bge-m3, # 支持多语言适合中英混合的企业知识 model_kwargs{device: cuda}, encode_kwargs{normalize_embeddings: True}, ) client QdrantClient(path./qdrant_db) vectorstore Qdrant( clientclient, collection_namecollection_name, embeddingsembeddings, ) # docs: List[Document] vectorstore.add_documents(docs) return vectorstore # ---------- 2. 离线检索 ---------- def offline_eval(retriever, eval_questions): eval_questions: [{question, expected_doc_ids, subset}] 分别计算 Recall5 和 nDCG5 results [] for item in eval_questions: hits retriever.get_relevant_documents(item[question]) hit_ids [d.metadata[doc_id] for d in hits[:5]] hits_set set(hit_ids) expected_set set(item[expected_doc_ids]) recall len(hits_set expected_set) / len(expected_set) # nDCG 简单计算只考虑命中位置折扣 dcg 0.0 idcg 0.0 for idx, doc_id in enumerate(hit_ids): if doc_id in expected_set: dcg 1.0 / np.log2(idx 2) for idx in range(min(len(expected_set), len(hit_ids))): idcg 1.0 / np.log2(idx 2) ndcg dcg / idcg if idcg 0 else 0.0 results.append({question: item[question], recall5: recall, ndcg5: ndcg, subset: item[subset]}) return results # ---------- 3. 端到端Agent评测 ---------- from langchain.agents import create_react_agent from langchain.tools import Tool from langchain.llms import OpenAI def build_agent_tool(retriever): def retrieve_tool(query: str) - str: docs retriever.get_relevant_documents(query) return \n\n.join([d.page_content[:500] for d in docs[:3]]) return Tool( namecompany_kb_search, description通过检索公司知识库获取业务信息。输入为查询问题。, funcretrieve_tool, ) def end_to_end_eval(retriever, eval_questions, llm): 对每个问题让Agent基于检索结果作答统计ASR和Faithfulness。 这里用简化逻辑Agent每次只调用一次检索然后直接生成答案。 tool build_agent_tool(retriever) prompt (你是公司内部知识问答助手。基于提供的知识库片段回答问题。 如果片段中没有足够信息请回答知识库中未找到相关信息。不要编造。) answers [] for item in eval_questions: docs retriever.get_relevant_documents(item[question]) context \n\n.join([d.page_content[:800] for d in docs[:4]]) user_question f问题{item[question]}\n\n资料\n{context} answer llm.invoke(prompt \n\n user_question) answers.append({question: item[question], answer: answer, expected_answer: item[expected_answer], subset: item[subset]}) return answers这段代码体现了评测的两种模式离线模式批量算Recall端到端模式跑Agent对话。实际评测中我会对每个方案先跑离线再跑端到端最后把两组结果汇总对比。其中第四步答案判分没有放进代码里因为它需要结合人工标注或使用一个独立的裁判模型属于另一个复杂话题后面会在常见问题里展开说。3.3 运行评测与结果对比评测跑完之后我把数据按子集维度聚合得到了下面这张简化版的对比表。它直观展示了为什么不能只信一个指标。评测策略碎片化集 Recall5多版本集 Recall5缩写集 Recall5端到端 ASRTool Call次数/任务仅BM250.610.420.4744.2%3.8仅向量检索(bge-m3)0.660.510.5849.6%3.5混合检索(加权)0.730.580.6256.1%2.9混合检索重排(bge-reranker)0.780.640.6861.7%2.1看到这个结果后有几点值得展开。首先BM25在碎片化集上的Recall虽然低于向量检索但差距并不悬殊。进一步分析发现BM25对专有名词和缩写非常敏感比如输入CRM这个词BM25能直接命中包含CRM的文本但向量检索容易把它匹配到客户管理等语义相近却不精确的内容上。这说明在企业知识库场景里稀疏检索和稠密检索是互补关系而不是替代关系。其次加上重排之后Recall提升最明显的是多版本冲突集从0.58涨到0.64。原因是重排模型对语义相关性的判断比向量距离更细粒度能够区分同样讲了折扣策略但一个明确标注了2023年作废另一个是2024年现行版这种细微差异。最后端到端ASR和Tool Call Efficiency的变化趋势高度一致检索质量越好ASR越高Agent需要触发的检索次数越少。这个现象很有说服力因为它在业务上的直接收益是可量化的——比如一个每天处理10万次请求的Agent服务每次任务减少0.8次检索总延迟和API成本下降是肉眼可见的。4. 常见问题与排查实录4.1 检索失败典型模式速查表评测过程中我整理了四类最典型的检索失败模式每类都对应一个可以执行的优化动作做成速查表如下。失败模式典型表现根本原因排查方法解决方案语义漂移检索结果相关但不精确比如查2024年报销标准召回一堆报销流程向量距离无法捕捉细粒度差异对比BM25召回检查高分片段的标题和上下文加入关键词软匹配信号使用重排模型精排新旧版本混答Agent回答里出现两个互相矛盾的版本索引中同时存在多个版本的文档检索结果中检查是否有明确版本号/日期字段按文档元数据做时间过滤或版本优先级加权缩写歧义查CRM返回客户关系管理连续续约管理员工姓名embedding对短查询缺乏上下文查看召回的Top 3文档内容差异查询侧加入扩充词如CRM 客户关系管理或者用HyDE生成查询扩展信息切碎关键信息被横切到多个chunk每个chunk单独看都答不了切分策略破坏了完整语义检查召回chunk的上下文完整性允许检索TopK时附带相邻chunk做上下文修复这套速查表的价值在于它不是只给出解决方案而是先教你怎么从现象定位根因。日常优化时我遇到效果变差的第一反应不是调参数而是先判断它属于哪一类失败模式然后对症下药。4.2 评测工程本身的坑评测管线搭建过程中踩过的坑也值得记录。第一个坑是embedding模型版本漂移。有一次我在评测过程中升级了embedding模型版本结果所有历史评测数据都失去了可比性。后来制定了规则切换embedding模型时必须重新索引、重新跑一轮完整评测并存档旧结果不能直接拿去跟新结果比较。第二个坑是评测集污染。用来生成评测问题的文档如果被不小心放进了索引目标库里问题就会被读过答案的文档污染导致Recall虚高。我在构建评测集时严格采用了先划分文档再基于划分结果生成问题的流程并且在索引构建前加一道校验确保评测问题关联的文档ID集合和索引文档ID集合交集为空。第三个坑是人工判分标准不一致。端到端ASR的判分如果完全靠人工标准很容易漂移上午觉得满意的答案下午可能觉得不行。后来我改用双人标注仲裁即两个标注者背靠背判分不一致的样本由第三个人仲裁。同时针对Agent答案的模糊性我还要求标注者在判对的时候必须标注依据的答案要点比如答对了产品和金额但日期写错了这样错误类型还能沉淀成新的优化方向。第四个坑更隐蔽评测的顺序偏差。如果评测集里的问题按子集顺序排列比如前100条全是碎片化集后100条全是多版本集那么大型语言模型作为裁判时的评分会受比较效应影响。后来我把问题顺序随机打乱并且每个方案评测前都重新洗牌确保子集间的横向对比公平。4.3 让评测持续发挥价值的三个建议评测体系建好之后最大的风险是沦为一次性项目跑完一轮就束之高阁。我这里有三个实际有效的建议都是踩过坑之后的经验。第一把评测嵌进CI流水线。每次改动检索策略、切分参数或embedding模型自动触发一轮回归评测把关键指标的变化直接报告到团队群。这比任何code review都管用因为指标下滑会在合并代码前就被拦截。第二保留历史评测结果存档。每轮评测跑完把评测集版本号、索引版本号、模型版本号、指标明细完整存档。这样当线上效果突然异常时可以回溯到上一次一切正常的时间点快速定位是哪个环节发生了漂移。第三评测集要随知识库一起生长。公司知识库在持续更新评测集也必须持续补充新样本否则检索系统会过拟合到旧的评测集上。我的做法是每两周抽一次线上真实用户反馈中答错了的案例人工审核后加入评测集。这样一来评测集永远比线上真实问题慢半拍但它始终在逼近真实分布。说实话从零搭这套评测体系花了我大概三周时间。如果当时只是想先上线跑起来也许第一周就能出结果但后续每次方案争论都要靠运气和嘴皮子才能推进。有了这套评测管线之后团队讨论的语言统一了指标成为公认的仲裁标准。这种隐性的协作效率提升往往比评测本身带来的检索效果提升更值钱。如果你也在被检索方案选型难、效果说不清困扰我建议你在优化检索算法之前先花时间把评测这件事做扎实。它看起来慢实际上是最快的一条路。
返回列表