多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

AI FDE 如何设计企业知识库检索链路

AI FDE 如何设计企业知识库检索链路 核心命题检索链路决定回答质量模型只是最后一环。 一句话带走每一环都可观测、可替换问题才能定位到具体一环。企业知识库问答的效果往往不是由模型大小单独决定的。模型拿到什么资料、资料是否最新、资料之间是否冲突、用户是否有权访问都会直接影响回答。大模型有知识截止日期不知道企业内部的最新制度和数据。检索增强生成RAG的思路是用户提问时先从企业知识库检索相关内容再把这些内容作为上下文交给模型让模型基于资料回答。这样回答就能引用最新的企业知识而不是靠模型记忆瞎编。但 RAG 引入了新的工程问题•召回不准检索回来的内容和问题不相关模型基于错误内容回答•排序不对相关内容排到了后面没进入模型上下文•无答案处理知识库里没有相关内容模型应该说「不知道」而不是编•引用溯源回答的内容来自哪里用户能不能验证两个典型症状•用户问「报销审批需要几级签字」AI 回答了但引用的是差旅报销制度不是费用报销制度——检索召回了错误的文档•用户问「2024 年的年假政策」AI 引用了 2022 年的旧政策——没有做版本过滤本章讲清楚五件事检索链路的基本结构、关键词与向量检索的取舍、Hybrid Search 怎么实现、切分怎么选、无答案和冲突怎么处理。一、检索链路的基本结构从提问到回答的完整链路整条链路可以画成这样每一层都在收窄候选集同时附加新的约束用户提问 ↓ 权限前置过滤注入 tenant_id、team_id 等条件 ↓ 查询改写Query Rewrite把用户的口语化问题改写成适合检索的查询 ↓ 多路召回Hybrid Search关键词检索 向量检索各取 Top-K ↓ 结果融合Fusion把两路结果合并去重归一化分数 ↓ 重排Rerank用交叉编码器或 LLM 对候选结果重新排序 ↓ 上下文拼装把 Top-N 文档拼成 Prompt 上下文 ↓ 模型生成基于检索到的内容生成回答 ↓ 引用展示回答中标注来源用户可点击跳回原文权限过滤应尽量在检索请求中前置。只有经过访问控制的候选内容才进入融合、重排和 Prompt。这一步如果没做好后面所有环节都可能泄露未授权内容。每个环节的作用•查询改写用户问「报销怎么弄」改写为「费用报销审批流程 签字级别」提高召回准确率•多路召回关键词检索适合精确匹配如制度编号、专有名词向量检索适合语义匹配如「不续约」和「合同到期挽留」•结果融合两路结果的分数不在同一量纲需要归一化后再加权合并•重排召回到的 Top-K 可能有 20-50 条用更精准的模型选出最相关的 Top-N通常 3-5 条送给模型•引用展示回答中的每句话都要能溯源到具体文档用户可以验证链路设计的核心原则每个环节都应该可观测、可替换。如果某个环节出了问题比如召回不准你需要能通过日志定位到是查询改写的问题还是切分的问题如果某个环节需要升级比如换更好的重排模型你需要能替换它而不影响其他环节。这就是为什么链路追踪和结构化日志在检索系统中如此重要——它们不是锦上添花而是排查问题和持续优化的基础设施。二、关键词检索与向量检索什么时候用哪个关键词检索适合以下情况•问题包含编号、专有名词、产品型号如「GB/T 22239-2019 是什么」•用户用的是和文档完全一致的术语如「年假政策」•需要精确匹配不能用语义相近的内容替代如法律条款编号向量检索适合表达不同但含义相近的问法。例如用户问「客户不续约怎么办」资料标题写的是「合同到期挽留流程」。关键词检索搜「不续约」可能搜不到「挽留」但向量检索能理解两者语义相近。两者结合通常比单独使用一种检索更稳健但需要注意分数归一化和权重调节。关键词得分如 BM25 分数和向量相似度0-1 之间的余弦相似度不在同一个量纲上不能直接相加而不做处理。需要先分别归一化到 [0,1]再加权求和。检索方式优势劣势适用场景关键词检索BM25精确匹配、可解释、成本低无法理解语义、对同义词不敏感编号、专有名词、法律条款向量检索Dense语义理解、同义词匹配可能召回语义相近但不相关的内容、成本较高口语化提问、FAQ、语义模糊的问题混合检索Hybrid兼顾精确和语义实现复杂、需要调权重企业知识库推荐默认一个常见的误区有些团队认为向量检索「更先进」所以只用向量检索就够了。实际上在企业知识库场景中大量问题是精确匹配型的——用户问的是具体的制度条款、流程步骤、编号规则这些内容语义上可能和其他文档很接近但只有精确匹配的那份文档才是正确答案。纯向量检索在这种情况下容易召回「语义相近但内容不对」的文档。混合检索是更稳健的默认选择。三、Hybrid Search 的教学示例怎么实现混合检索下面的函数只是用关键词命中模拟「检索候选」步骤不是真实的向量混合检索。生产系统应替换为全文索引如 Elasticsearch、向量数据库如 Milvus、pgvector和 Rerank 服务并在查询层注入权限条件。from typing import List, Dict def keyword_search(query: str, docs: List[Dict], top_k: int 10) - List[Dict]: 关键词检索用 BM25 或简单的词频匹配返回带分数的候选 results [] query_terms set(query.lower().split()) for doc in docs: # 简单实现统计查询词在文档中出现的次数 score sum(1 for term in query_terms if term in doc[content].lower()) if score 0: results.append({doc: doc, score: score, type: keyword}) results.sort(keylambda x: x[score], reverseTrue) return results[:top_k] def vector_search(query: str, docs: List[Dict], top_k: int 10) - List[Dict]: 向量检索用 Embedding 计算余弦相似度返回带分数的候选 # 实际实现需要调用 Embedding 模型这里用随机分数模拟 import random results [] for doc in docs: score random.uniform(0.5, 0.95) # 模拟余弦相似度 results.append({doc: doc, score: score, type: vector}) results.sort(keylambda x: x[score], reverseTrue) return results[:top_k] def normalize_scores(results: List[Dict]) - List[Dict]: 把分数归一化到 [0, 1] if not results: return results max_score max(r[score] for r in results) min_score min(r[score] for r in results) if max_score min_score: for r in results: r[norm_score] 1.0 else: for r in results: r[norm_score] (r[score] - min_score) / (max_score - min_score) return results def hybrid_search(query: str, docs: List[Dict], alpha: float 0.5, top_k: int 10) - List[Dict]: 混合检索关键词检索 向量检索加权融合 alpha: 关键词检索的权重0-1 之间 keyword_results normalize_scores(keyword_search(query, docs, top_k)) vector_results normalize_scores(vector_search(query, docs, top_k)) # 合并两路结果按 doc_id 去重分数加权求和 merged {} for r in keyword_results: doc_id r[doc][doc_id] merged[doc_id] {doc: r[doc], final_score: alpha * r[norm_score]} for r in vector_results: doc_id r[doc][doc_id] if doc_id in merged: merged[doc_id][final_score] (1 - alpha) * r[norm_score] else: merged[doc_id] {doc: r[doc], final_score: (1 - alpha) * r[norm_score]} results sorted(merged.values(), keylambda x: x[final_score], reverseTrue) return results[:top_k]正式的混合检索可以抽象成final_score alpha * norm(keyword_score) (1 - alpha) * norm(vector_score)alpha不是固定常量。包含编号和专有名词的问题通常提高关键词权重alpha0.7表达模糊意图的问题可以提高向量权重alpha0.3。这个权重应通过评测集验证而不是凭感觉设定。怎么调 alpha准备 30-50 条真实问题的评测集每条标注正确答案所在的文档。分别用 alpha0.3/0.5/0.7 跑一遍看哪个 alpha 的证据召回率最高。进阶动态 alpha 策略。更成熟的做法是根据问题类型自动调整 alpha。可以训练一个简单的分类器判断用户问题属于「精确匹配型」还是「语义匹配型」然后动态选择 alpha。比如问题中包含编号、引号、专有名词时自动提高关键词权重问题以「怎么」「如何」「为什么」开头时适当提高向量权重。这种策略不需要用户手动选择对终端用户完全透明。四、重排为什么召回了还不算数融合之后拿到的是一批「可能相关」的候选通常 20-50 条。这批量不能直接送给模型——模型的上下文有限塞太多反而稀释重点而且候选里必然混着大量弱相关内容。重排Rerank的任务是把这 20-50 条按真实相关性重新排序选出 3-5 条真正该进上下文的。为什么不能直接用召回分数排序召回阶段的关键词分数和向量分数都是把问题和文档分开算再比较的——文档的向量在入库时就算好了不用看问题。这种做法快但它只能粗略判断「像不像」判断不了「这段话到底有没有回答问题」。重排阶段反过来把问题和文档拼在一起送进模型逐条判断这条文档对这个问题有没有用。慢但准。阶段计算方式问题与文档是否同时参与速度精度召回双塔文档预先编码问题单独编码算向量距离否两者各自独立编码快可预计算粗只能判断语义相近重排交叉编码器问题与文档拼接后一起送进模型打分是逐条联合编码慢无法预计算细能判断是否真的回答了问题这个差别决定了两者不能互相替代。用重排做全量召回复杂度是 O(文档总数)一个百万级知识库每次查询要跑一百万次模型推理不可行用召回直接定序就会出现「语义相近但答非所问」的文档挤掉正确答案。所以标准做法是两段式召回负责从百万量级快速筛到几十条重排负责从几十条精准选出三五条。前面第三节讲的alpha调的是召回段这一节讲的是第二段。重排最典型的救命场景重复内容和近似内容占位。同一份制度的三个版本、同一段说明在五份文档里各出现一次——这些内容在向量空间里极其接近会一起挤进 Top-K。召回阶段的分数区分不了它们的「该不该用」只有重排把问题和文档合起来看才能把「现行版且直接回答问题的」那一条挑到最前面。这也是为什么重排之前必须先去重见第 95 章 RAG 的核心流程。选哪种重排方案特点适用交叉编码器Cross-Encoder专门的排序模型精度高延迟可控默认选择候选 20-50 条时最合适LLM 重排用大模型直接排序或打分零样本可用、可解释候选少、需要解释排序理由、或没有训练好的排序模型时规则加权按字段权重如条款号精确匹配加分提权作为补充不单独承担排序重排的工程约束第一候选数不能无限加大——重排是逐条推理候选从 50 加到 200延迟会线性上涨而收益递减通常 20-50 条就够了。第二重排前必须去重否则同一文档的多个版本会占满候选位既浪费重排算力又干扰排序。第三重排也要可观测——记录每条候选的召回分和重排分两者排名差异大的条目特别值得看那里往往藏着召回阶段的系统性偏差。怎么判断重排有没有用对比重排前后的同一批查询看正确答案在最终 Top-N 里的命中率变化。如果加了重排命中率几乎不动说明召回段已经把正确答案排得很靠前重排的边际收益低如果动得很明显说明召回段的排序不可信这时候重点要放在重排上而不是继续调alpha。五、切分与上下文组织chunk 大小和方式直接影响检索质量检索质量不仅由索引决定也由切分决定。一个 chunk 太长容易同时包含多个主题和权限边界一个 chunk 太短语义不完整模型无法理解上下文。建议根据内容类型选择切分方式内容类型推荐切分方式chunk 大小原因制度、手册、Wiki有标题层级递归字符切分300-500 token标题天然是语义边界顺着结构切最稳合同条款、FAQ短句密集句窗切分1-3 句 前后文句子之间强依赖需要上下文才能理解访谈纪要、邮件、会议纪要无标题语义切分话题突变处断开没有标题层级靠语义相似度判断边界操作步骤、流程文档按步骤序号切分1-3 步一个 chunk步骤不能被切断否则答案不完整每个上下文块都应携带来源、版本和更新时间。回答中的引用 ID 应映射到用户可以再次访问且经过授权检查的原始资料。如果 chunk 没有元数据引用溯源就做不了用户无法验证回答的正确性。案例某银行操作规程库首版上线切分器只看字符长度每 800 字一刀切结果「大额转账复核」流程的第 3、4 步之间被切开。柜员问「大额转账复核流程」AI 只检索到前 3 步回答漏掉了「双人复核」这一关键控制点。后来改成「标题 步骤序号」组合切强制同一步骤不切断命中率从 0.64 升到 0.86。切分与权限的交叉问题如果一个 chunk 横跨了两个权限边界比如前半部分属于「内部」密级后半部分属于「机密」密级应该怎么处理答案是切分时就要考虑权限边界一个 chunk 不能横跨不同的密级或团队归属。否则检索时要么按高密级处理导致低密级用户看不到本来能看的内容要么按低密级处理导致高密级内容泄露给低密级用户。这就是为什么数据接入阶段第三章的权限打标必须在切分之前完成。六、无答案和冲突答案允许说「不知道」比硬编更安全一个可信的知识库系统必须允许回答「当前资料不足」。如果强行让模型回答模型会基于不相关的内容编造比直接说「不知道」更危险。建议设置以下降级规则场景判断条件系统行为无相关内容检索结果的最高相似度 阈值如 0.5返回「当前知识库中没有找到相关内容请尝试换个问法或联系人工」内容冲突检索到的文档中存在相互矛盾的表述返回「知识库中存在不一致的信息建议以最新版本为准引用最新文档」置信度低重排后的 Top-1 分数 阈值返回「以下回答仅供参考建议核对原文附引用」权限不足用户无权访问相关文档返回「您没有权限查看相关内容请联系管理员」不暴露文档存在文档过期检索到的文档已过期优先返回最新版本如果只有过期版本标注「该文档可能已过期」怎么设置阈值用评测集跑一遍看正确答案的相似度分布和错误答案的相似度分布找到一个能把两者分开的阈值。比如正确答案的相似度都 0.6错误答案都 0.5那阈值就设在 0.55。案例某客服知识库问答早期没有无答案处理用户问「你们公司的 CEO 是谁」知识库里没有模型编造了一个名字。后来加了相似度阈值最高相似度 0.5 时返回「没有找到相关内容」这类编造问题就消失了。降级规则的优先级当多个降级条件同时触发时需要明确优先级。建议的顺序是权限不足 文档过期 内容冲突 无相关内容 置信度低。权限不足必须最先处理因为如果用户无权访问相关文档系统不应该告诉用户「没有找到相关内容」——这会暴露文档的存在性。正确的做法是直接返回「您没有权限查看相关内容」不透露任何其他信息。七、查询改写用户的原话往往不能直接拿去检索检索链路的第一道加工是查询改写但它最容易被跳过——因为用户的原句看起来「就是问题本身」。实际上用户提问用的是口语和简称知识库里的文本用的是术语和全称两者之间的词面差距会直接吃掉召回率。四类常见改写动作改写动作解决的问题例子术语归一口语简称对上文档全称「报销怎么弄」 → 「费用报销审批流程」同义词扩展同一概念的不同说法「不续约」 → 「不续约 OR 合同到期 OR 挽留」指代消解多轮对话里的「它」「这个」「它的审批人是谁」 → 补全为上一轮提到的单据名意图澄清问题过短、歧义大「年假」 → 追问或补全为「年假 天数 计算方法」什么情况下不该改写改写不是越多越好。如果用户的问题里含编号、专有名词、法条号改写反而可能引入同义词噪声把精确匹配的优势稀释掉。这类问题应当原样保留并提高关键词权重对应第三节的alpha调高。判断规则可以写死正则命中GB/T、版本号、工单号、型号格式时跳过同义词扩展。改写本身也可能引入错误所以要可观测。每次改写都要记录「原查询 → 改写查询」并允许在评测集上单独度量改写环节的收益。如果改了写反而让召回率下降说明改写规则和业务术语不对齐——这时候要改的是规则不是模型。查询改写是规则问题不是模型问题这句话在客户现场被验证过很多次。八、引用溯源用户能不能验证决定他敢不敢用前七节都在解决「回答得准不准」这一节解决「用户凭什么相信」。一个知识库产品的信任门槛不是回答的正确率而是用户能不能自己核实——答对了但无法验证用户仍然不敢拿它当依据。引用要做到什么粒度只说「来自知识库」是不够的。合格的引用至少要能让用户点击后落到原文的具体位置。粒度用户能做什么是否合格无引用只能选择信或不信不合格文档级文档名知道来自哪份文档还要自己翻勉强段落级文档 chunk能快速定位到相关段落合格段落级 页码/锚点一步跳到原文位置合格推荐引用要经过和检索同样的权限检查。这是最容易被忽略的一点引用链接必须走和检索一致的授权判断。如果引用链接是一个可以直接访问的 URL用户就能通过点击引用绕过权限——检索阶段挡住了文档引用链接又把它打开了。正确做法是引用链接指向一个受控跳转接口该接口重新校验当前用户权限无权则返回提示而不是原文。引用缺失时宁可少答。如果某个结论无法对应到任何 chunk正确的处理是不把这个结论写进回答而不是照样写出来、只是不标注来源。标注是承诺不是装饰——没有来源的句子在用户眼里和编造没有区别。引用覆盖率应作为验收指标。可以这样定义引用覆盖率 带引用的句子数 / 总句子数目标值通常在 90% 以上。低于这个值说明生成环节没有把证据约束住模型正在用训练记忆补全。把本章翻译成 AI FDE hub 工程契约AI FDE 如何设计企业知识库检索链路 这件事最终要落到一份能被四方签收的契约上。下面这份 YAML 就是它的字段模板# AI FDE hub · 检索链路 · 工程契约示例 chapter: 005 framework: AI FDE hub 三段式交付入场 / 搭建 / 离场 tags: [AI, RAG, Hybrid, Rerank] scope: role: AI FDE 工程师 boundary: 对接客户业务负责人、合规、研发、测试四方共同验收 retrieval_pipeline: steps: [权限前置过滤, 查询改写, 多路召回, 结果融合, 重排, 上下文拼装, 生成, 引用展示] hybrid_search: keyword_weight: alpha0.5可按问题类型调整 fusion: 归一化后加权求和 rerank: Top-50 → Top-5 fallback_rules: - 最高相似度 0.5返回无答案 - 内容冲突标注不一致优先最新版本 - 置信度低标注仅供参考 - 权限不足不暴露文档存在和相邻章节的关系本章与前后相邻的第 4 章《企业 AI 应用的权限治理别让 RAG 成为越权入口》、第 6 章《从问答到执行AI Agent 的现场交付方法》同处一个主题段共用同一套「产物 退出条件 决策门」语言——第 4 章保证候选集里只有合法文档本章保证这些文档被准确捞出并正确排序第 6 章再把链路从「回答」推进到「执行」。Toy Project vs. 生产级系统 · 8 项关键差异这张表聚焦检索链路上最容易「演示时看不出、上线后处处不对劲」的地方。它的特点是这里每一项的差距都不会让系统报错只会让回答悄悄变差——所以只能靠评测和 trace 发现不能靠客户投诉发现。维度Toy Project生产级系统差距代价检索方式纯向量检索单一召回路径Hybrid Search 动态 alpha 调节编号类问题召回错文档用户拿到语义相近但不对的答案分数融合直接取 Top-K不做归一化两路分数归一化后加权融合量纲差一个数量级融合排序实际只被一路主导重排能力无重排检索即最终结果交叉编码器或 LLM Rerank最相关的答案排在 20 名开外被 Top-N 截断切分策略固定字符长度一刀切按内容类型选择切分方式步骤被切断答案漏掉关键控制点无答案处理模型硬编什么都敢答相似度阈值 分级降级规则编造不存在的制度用户照做造成实际损失引用溯源无引用用户无法验证每个回答标注来源文档 段落出事无法定位责任用户不敢信也不敢用查询改写用户原句直接检索Query Rewrite 同义词扩展口语化提问召不回来用户以为系统「没有这个资料」评测体系凭感觉判断「答得好不好」30-50 条标注评测集 召回率指标每次调整都是盲改改好改坏说不清客户现场最常见的三种反模式这三种做法在检索链路调优时反复出现共同点是都在用「更先进的组件」替代「更准确的定位」——链路本身没被当作一个整体来看。提前识别比事后补救成本低一个数量级。反模式典型表现正确做法不推荐原因只用一种检索方式「向量检索最先进只用向量就够了」结果精确匹配型问题大量召回错误文档默认 Hybrid Search根据评测集调 alpha编号和专有名词类问题系统性失效且失效得不像故障不切分直接灌入整份文档作为一个 chunk 入向量库一个 chunk 包含多个主题和权限边界按内容类型选择切分方式chunk 不横跨权限边界答案只覆盖文档开头后文形同不存在不允许说「不知道」模型什么都答知识库里没有的内容也编造出来设置相似度阈值 分级降级规则宁可说「没找到」也不编一次编造就摧毁用户对全部回答的信任附录检索链路速查一句话记法检索链路的价值不在于每一环用了多先进的模型而在于能沿着链路把问题定位到具体一环。链路八环分工表环节主要职责失效后果权限前置过滤只让经授权的候选进入融合、重排和 Prompt未授权内容泄露查询改写把口语问题改写成适合检索的查询口语化提问召不回来多路召回关键词 向量各取 Top-K精确匹配或语义匹配一类问题失效结果融合两路分数归一化后加权合并融合排序被单一量纲主导重排用交叉编码器从 20-50 条选出 3-5 条正确答案被截断在 Top-N 之外上下文拼装把 Top-N 文档拼成 Prompt 上下文重点被稀释模型生成基于检索内容生成回答靠训练记忆补全、编造引用展示标注来源链接走受控跳转用户无法验证不敢用边界表降级规则边界场景触发条件系统行为无相关内容最高相似度 阈值如 0.5返回「没有找到相关内容」内容冲突文档间存在矛盾表述标注不一致优先最新版本置信度低重排后的 Top-1 分数 阈值标注「仅供参考」附引用权限不足用户无权访问相关文档不暴露文档存在返回无权限文档过期检索到的文档已过期优先最新版本否则标注「可能已过期」现场速查现场症状根因判断先做什么不该做什么引用了错误制度的文档召回或重排环节看 trace 日志确认召回了哪些文档不要直接换更大的模型引用了旧版政策缺版本过滤或无答案规则检查文档元数据与降级条件不要让模型自行判断版本步骤答案漏掉关键控制点切分把步骤切断改按「标题 步骤序号」组合切不要只加长 chunk 了事编号类问题答错纯向量召回默认 Hybrid调高关键词权重不要迷信「向量最先进」编造不存在的制度无答案阈值缺失设相似度阈值 分级降级规则不要只加提示词约束引用链接可绕过权限引用未走受控跳转改为重新校验权限的跳转接口不要直接暴露原文 URL判断顺序先看 trace 日志 → 确认召回了哪些文档 → 判断是查询改写、切分还是重排的问题 → 再针对性优化对应环节。一句收尾回答不准时最怕的不是链路简单而是链路是个黑盒——不知道该改哪一环就只能反复换模型。和相邻章节的关系本章与第 4 章《企业 AI 应用的权限治理别让 RAG 成为越权入口》、第 6 章《从问答到执行AI Agent 的现场交付方法》同处一个主题段共用同一套「产物 退出条件 决策门」语言——第 4 章保证候选集里只有合法文档本章保证这些文档被准确捞出并正确排序第 6 章再把链路从「回答」推进到「执行」。思考题用户问「报销审批需要几级签字」AI 回答了但引用的是差旅报销制度。你觉得问题出在检索链路的哪一环怎么排查参考答案问题可能出在召回或重排环节。排查步骤1看日志确认检索召回了哪些文档有没有费用报销制度2如果没召回费用报销制度可能是查询改写的问题用户的问题没被改写到「费用报销」关键词或切分的问题费用报销制度被切散了没被检索到3如果召回了但排到后面可能是重排的问题重排模型把差旅报销排到了前面4如果召回和排序都对可能是上下文拼装的问题费用报销制度没被选进 Top-N。解决方法先看 trace 日志定位具体环节再针对性优化——查询改写加同义词、切分按标题层级、重排用更精准的模型。换个说法检索链路的价值不在于每一环用了多先进的模型而在于能沿着链路把问题定位到具体一环。回答不准时最怕的不是链路简单而是链路是个黑盒——不知道该改查询改写、切分还是重排就只能反复换模型每次都像重新掷一次骰子。AI FDE hub本文由 AI FDE hub 出品关注「AI PDE 陪跑计划」获取 AI 现场交付方法论
返回列表