ARTICLE DETAIL

资讯详情

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

RAG与Wiki协同:从知识沉淀到智能问答的落地实践

RAG与Wiki协同:从知识沉淀到智能问答的落地实践 我那个 Wiki 从几十个页面写到几百个页面真正被人点开看的频率却一路走低。后来我给它套了一层 RAG团队问问题变成了聊天答案直接给出来还带着原文出处链接很多同事才重新把知识库用起来。这件事用一句话概括就是“RAG 找答案Wiki 长知识”听起来像个口号但背后牵扯的是两类完全不同的工作知识沉淀和知识消费。这篇文章就围绕这句话展开聊聊 Wiki 为什么越厚越没人翻RAG 到底解决了哪一环以及我落地过程中踩过的坑和摸出来的可复现方案。1. 知识越厚越难找Wiki 的价值与使用门槛先讲一个扎心的观察。Wiki 的核心理念是“长知识”也就是把散落在人脑、聊天记录、文档、邮件里的信息沉淀成有结构的页面并且通过目录、父子页面、标签、交叉引用形成一个体系。这本身没有问题问题出在“知识变厚”之后消费它的成本也在变高。1.1 知识库的悖论越厚越没人用当 Wiki 只有 30 个页面时你大概能记住每个页面在哪个目录下搜索一两次就能找到。当它涨到 300 个页面而且经历了多次组织架构调整、项目改名、术语变更之后事情就失控了。你明明知道某个结论写在 Wiki 里但具体在哪个页面、哪一层目录、哪个历史版本里搜索引擎关键词匹配经常给不准确的结果。我见过最典型的一幕新同事问“这个接口的重试策略到底有没有限制次数”答案是“有之前评审的时候定了 3 次”但没人记得写在哪。于是群里炸出三四个老同事开始回忆最后有人翻出某个月度总结页面里夹着的一段文字。问题倒不是知识不存在而是知识沉淀了却无法被低成本找回。这种情况不是 Wiki 本身错了而是我们把它当成了“最终存档地”却没有给它配一个适合“即时提取”的入口。Wiki 擅长的是让人慢慢读、按结构浏览、理解上下文而不是让人快速拿到一个精确答案。一旦页面数量上去上下文路径变深关键词搜索就扛不住了。1.2 “长知识”和“找答案”是两种能力“长知识”的核心动作是整理、判断、归类。你需要决定哪句话够资格写进页面哪条信息和另一条信息该建立链接哪个页面应该拆成多个子页。这个过程靠的是人的领域判断力也是 Wiki 体系中最值钱的部分。“找答案”的核心动作是定位、提取、组合。用户想要的不是从头读到尾而是直接得到“重试次数是 3 次依据是某某评审结论”这样一句话并且最好能回溯到原始页面。这两件事的节奏完全不同。沉淀知识是低频重脑力活提取答案是高频轻量活。过去我们试图让 Wiki 一个系统同时干两件事自然就会顾此失彼。RAG 的角色不是替代 Wiki而是把“找答案”这件事从人肉浏览里解放出来。1.3 两者的分工边界维度WikiRAG核心职责沉淀、组织、维护知识提取、组合、生成答案更新节奏低频、人工编辑、有评审随语料更新自动重建索引用户入口目录、搜索、链接跳转问答式对话、统一查询接口质量核心内容准确、结构清晰、定义明确检索命中率高、生成有据可依失败成本没人看、没人更新、内容腐烂答非所问、编造答案、误导决策我对两者的定位是Wiki 是知识资产的仓库仓库里必须码放整齐RAG 是仓库门口的智能导购它不负责进货也不负责分类只负责把你想要的东西准确递到你手里。仓库乱糟糟的导购再聪明也没办法导购不存在仓库里的好东西就只能落灰。2. RAG 的工作链路拆开看切分、检索、生成的每一环都在影响答案质量RAGRetrieval-Augmented Generation检索增强生成这个名字已经把这套方法说得很明白了先检索再生成。很多第一次接触的人以为重点在后面那个“生成”实际上决定体验上限的往往是前面那个“检索”。一个答案好不好首先取决于有没有把对的片段捞出来。2.1 从 Wiki 到文本片段切分策略是第一道关卡Wiki 页面是给人类阅读的一个页面可能有几百行包含背景、步骤、注意事项、FAQ。如果把它整页塞进大模型的上下文又会超出窗口限制或者在检索阶段因为向量粒度太粗而找不到精确内容。所以第一步是把一个长页面切成若干块有独立语义的片段。常见的切分方式有三种按固定长度切分比如每 512 个 token 一块相邻块重叠 50。实现简单但容易把一段完整结论拦腰截断。按标题结构切分用 Markdown 的#、##层级作为边界每个标题下的内容形成一个块。对 Wiki 这种层级分明的文档非常合适因为标题本身就概括了内容主题。语义切分用模型判断句子间的语义边界实现复杂适合杂糅严重的页面。我比较推荐对 Wiki 内容优先做结构化切分。因为 Wiki 页面本来就有标题树你只需要按#和##把页面拆开保留标题作为 chunk 的前缀检索效果通常比盲目固定长度切好很多。只有遇到没有标题层级的自由文本时再退回到固定长度加重叠。切分粒度也要注意。chunk 太短信息不完整chunk 太长向量语义容易被稀释而且会占用过多的上下文空间。我常用的起点是 500 到 800 个 token重叠 50 到 100然后根据真实查询的命中情况迭代调整。2.2 向量化与混合检索为什么不能只靠向量切分完成后每个片段会经过嵌入模型变成向量存入向量数据库。查询的时候把用户的问题也转成向量然后做相似度检索。这是 RAG 的基础形态但如果你只依赖向量检索很快会碰壁。原因在于向量检索擅长处理“语义相似”但不擅长处理“关键词精确匹配”。举个例子Wiki 里写的是“限流阈值 QPS 500”用户问“每秒最多请求多少个”向量上能对应上但如果用户问“qps 阈值”而你的索引里恰好事后把“QPS”写成了“每秒查询数”向量可能仍然能找到却不如关键词 BM25 来得稳定。所以我的做法是混合检索向量检索负责召回语义相近的内容BM25 负责召回关键词精确命中的内容两者结果合并后去重再做重排。这个改动在不少场景下能把命中率明显拉起来尤其是中文 Wiki 里充满专业缩写和英文术语的时候。2.3 重排和上下文构造让大模型拿到高质量的素材从候选片段中取回的前几十条结果不能直接全部塞给大模型。正确的流程是用一个重排模型reranker对候选做精细打分选出最相关的三到五条再拼接成上下文。重排模型通常比嵌入模型更重但精度更高。它做的事情就是给定“问题-片段”pair输出一个相关性分数。没有条件部署重排模型时也可以先用简单的规则优先保留包含问题关键词的片段优先保留标题层级较浅、来源页面权重较高的片段。上下文构造也有讲究。我会给每个片段前面加上来源页面标题和页面路径让大模型知道这段话的出处同时在提示词里明确要求只基于提供的片段作答如果片段中没有依据就直接说不知道不要自行推测。2.4 评估指标先看 hit rate再看答案对不对“RAG hit rate”是评测检索质量的关键指标。它的定义很简单在一组测试问题上正确的答案片段是否出现在检索结果的前 k 条里。如果 hit rate 很低说明问题根本不在生成而是检索环节就没把对的材料捞出来。这时候换再大的模型、写再漂亮的提示词都没用。我维护知识库的习惯是建一个“验证问题集”覆盖三类问题直接事实型“超时时间是多少”、跨页面组合型“A 服务和 B 服务之间的鉴权流程”、边界状态型“什么情况下不允许重试”。每次改动切分策略或索引都先跑一遍验证问题集统计 top-5 命中率再挑几个案例人工看答案质量。没有这个验证集你很难知道优化到底是在前进还是倒退。3. 让 Wiki 先变成 RAG 喜欢的样子内容结构与元数据改造很多团队接 RAG 时只想着把 Wiki 导出、切分、灌进向量库结果上线后答非所问最后得出结论“RAG 不行”。但真实原因往往是 Wiki 内容本身对机器不友好。想让 RAG 找准答案先得让 Wiki 的结构更适合被检索和被引用。3.1 一个页面只讲一件事标题要承载信息Wiki 常见的坏味道是“综合页”标题叫“支付系统架构”页面里又从网关到对账写到数据库分表。这种页面作为人读的文档没问题作为 RAG 的语料就麻烦了因为一个片段里可能混着三四个主题query 向量会“平均化”导致哪个主题都命中得不够精准。改造方向是拆分一个页面解决一个核心问题标题本身就是对答案的概括。比如“支付系统架构”拆成“支付网关职责”“对账流程”“支付库分表策略”用户问“对账怎么做的”时检索到的片段会干净得多。RAG 的命中率很大程度上在写标题的那一刻就决定了。3.2 把“依赖于阅读顺序”的表述改成“自包含”表述Wiki 里经常出现这类句子“如上所述重试策略定为 3 次。”这个“如上所述”对人类读者完全没问题但对 RAG 是灾难。因为系统切分出来的某个片段可能只包含“如上所述”后面的结论前面被切走的上下文根本不在里面大模型只能看到半截信息。更友好的写法是让每段关键结论独立成块“根据 XX 评审结论接口重试策略为最多 3 次每次间隔 5 秒。”第一句话就交代适用范围、结论、依据后续哪怕只检索到这一段也能给出完整答案。缩写词、业务代号第一次出现时顺手写全称或直接给一句定义成本很低收益却很大。3.3 表格、图片、代码块的预处理热词里有个问题很典型“RAG 知识库能存储图片吗”直接答案是图片本身不能直接进向量库参与语义检索但图片背后的信息可以。对 Wiki 中每个图片至少补一个 alt 文本或一句话描述重要的截图建议配一段要点讲解。表格要先转成 Markdown 或 CSV 再入库否则按纯文本切分后表头和数据列会各奔东西。代码块保留语言标注检索到之后原样丢给大模型代码解释类问题才会准。市面上也有专门的文本拆解工具比如 unstructured、textract、markdown-it 这类库能把 PDF、Word、HTML 转成结构化文本。Wiki 如果本身是 Markdown 编辑的直接解析标题结构就行不需要走复杂的文档解析流程。3.4 同义词、别名与版本Wiki 作为知识字典同一个东西在不同人嘴里叫法不一样技术文档里叫“订单中心”业务那边叫“交易中台”老系统文档里叫“OMS”。如果 Wiki 里各写各的RAG 检索时经常因为术语对不上而漏召回。我的办法是在 Wiki 里维护一个“术语别名表”并在页面元数据里标注该页面的若干别名。切分时把别名拼到片段前面等于给每个片段多了一组“检索钥匙”。查询侧再做一次简单的问句改写把用户的说法映射到 Wiki 的标准叫法。这个方案比单纯堆 embedding 模型更有效因为它是基于你的业务知识做的精确对应。4. 一次最小的本地落地实践Ollama 加向量库跑通 Wiki 问答聊了很多原理现在给出一套我自己在本地就能跑通的方案。它不需要 GPU不需要公司级基础设施一台普通电脑就能完成适合前期验证“RAG Wiki”到底适不适合你的场景。跑通之后再谈往生产环境迁移。4.1 为什么先从本地开始我坚持先用本地模型跑通主要有三个原因一是 Wiki 内容往往包含内部敏感信息直接调云端大模型会有合规压力二是本地跑环节少了网络波动和接口费用迭代切分策略和查询改写时反馈速度明显更快三是 Ollama 这类工具把模型部署简化成了几条命令零基础也能快速上手。这里的“本地”包括两件事本地部署大模型做生成本地搭建向量库做检索。生成模型我用 Qwen 系列的 7B 或 14B 量化版本嵌入模型用 nomic-embed-text 或 bge-m3都是社区验证过、资源开销可控的选择。4.2 环境准备与依赖安装首先是安装 Ollama然后拉取模型ollama pull qwen2.5:7b ollama pull nomic-embed-text向量库我用 Chroma纯本地免运维。Python 依赖方面只需要 sentence-transformers 或直接用 Ollama 的 embedding 接口再加上 chromadb 和 langchain-text-splitters 里的 MarkdownHeaderTextSplitter。如果你偏好更重的框架LangChain、LlamaIndex、RAGFlow 也可以但前期验证阶段我建议少上依赖自己写两层脚本反而更容易定位问题。4.3 导入 Wiki 并建立索引假设你的 Wiki 已经导出为一批 Markdown 文件结构类似wiki/支付/重试策略.md。下面这个 Python 脚本的思路是遍历文件按 Markdown 标题切分拼上文件路径作为元数据写入 Chroma。from pathlib import Path from langchain_text_splitters import MarkdownHeaderTextSplitter import chromadb client chromadb.PersistentClient(path./wiki_rag_db) collection client.get_or_create_collection(wiki_docs) splitter MarkdownHeaderTextSplitter(headers_to_split_on[(#, H1), (##, H2)]) for md_file in Path(./wiki).rglob(*.md): text md_file.read_text(encodingutf-8) for chunk in splitter.split_text(text): content f{chunk.metadata.get(H1, )} {chunk.metadata.get(H2, )}\n{chunk.page_content} doc_id f{md_file}:{chunk.metadata} collection.upsert( documents[content], ids[doc_id], metadatas[{source: str(md_file)}] )这里有个细节值得强调把标题拼回 chunk 内容里。单独存标题和正文会让标题信息在向量化时被浪费拼进去之后检索片段自带上下文命中率会好很多。4.4 检索与生成生成阶段我用 Ollama 的 Python 接口先根据用户问题从 Chroma 中检索 top-k 片段再拼成提示词交给本地模型import ollama def ask(query, top_k5): q_emb ollama.embeddings(modelnomic-embed-text, promptquery)[embedding] result collection.query(query_embeddings[q_emb], n_resultstop_k) context \n\n.join(result[documents][0]) prompt f请只根据下面的知识片段回答问题不要编造。 如果片段不足以回答请直接说明。 片段 {context} 问题{query} return ollama.chat(modelqwen2.5:7b, messages[{role: user, content: prompt}])跑通之后你会立刻遇到一个情况有些问题回答得不错有些问题明显“没找到”。这时候不要急着调大模型先把检索结果打印出来看。如果 top-5 里根本没有包含正确答案的片段问题出在索引或切分而不是生成。4.5 准备一组验证问题按真实使用场景来测我建议维护 20 到 50 条问题来自真实使用场景而不是你自己临时想的。标准很简单这些问题必须是过去一个月内你在群里、工单里、会议上回答过的“重复问题”。把这些问题整理成验证集每次改动切分参数、换 embedding 模型、调整索引之后重跑一遍记录命中率和答案质量。我自己会用一张表来记录问题、期望答案关键字、hit top-5 是否命中、生成答案是否符合预期、失败原因检索失败/上下文不足/生成错误。这张表就是后续一切优化的指南。5. 上线之后我踩过的坑从检索不到到知识打架实践和想象之间永远隔着几条沟。RAG 落地过程中最常见的不是单个技术难点而是一连串看起来很小、叠加起来却很致命的内容工程问题。5.1 先判断瓶颈打印检索片段别猜当答案不对时第一件事是看检索结果。大多数 RAG 调试都是从这里开始的把用户问题、top-5 片段、生成答案三样东西并排打印出来。如果片段里有正确答案但模型答错那是提示词或模型问题如果片段里根本没有正确答案那是切分、索引或检索问题。千万不要跳过这一步直接换大模型。很多团队花了大价钱把 7B 换成 70B结果答错的还是那几道题因为根子不在生成而在没检索到材料。打印检索片段是个五秒钟就能完成、却能省下大量无效调优的动作。5.2 切分恰好切断了关键信息有过一次很崩溃的经历用户问“某个异常会自动重试吗”检索出来的片段是“重试策略见下方表格”而真正的表格内容在下一个 chunk 里。模型只看到提示没看到表格只能编一个答案。解决方案分三层第一层切分时给相邻 chunk 做 50 到 100 token 的重叠第二层尽量用标题结构切分避免把表格和它上方的说明文字分隔开第三层遇到关键表格手动在页面里加一句结论性的文字作为表格摘要比如“重试策略总结普通异常最多重试 3 次致命异常不重试”。这句摘要会随着表格一起被检索模型就能给出正确答案。5.3 多条知识源说法不一致模型会“和稀泥”Wiki 维护久了同一个问题在不同页面里可能有两个版本一个页面说超时时间 3 秒另一个页面说 5 秒。RAG 把两个片段都检索出来大模型很可能给出一个模棱两可的答案甚至取错的那一个。我的应对办法是引入“权威来源”元数据。在索引阶段给每个 chunk 打上标签比如官方文档、预案记录、旧系统文档并在检索后对来源权重做排序。更根本的办法是在 Wiki 里明确标注“本页面已废弃以 XX 页面为准”并把旧页面标记为不参与索引。知识打架的解法永远应该在知识源头解决而不是在 RAG 侧靠猜。5.4 更新索引Wiki 改了答案不能还停在旧版本Wiki 每天都在变但向量索引不会自己感知。最常见的坑是产品经理改了策略Wiki 已更新RAG 还按旧文本回答。刚上线时没人注意等有人较真追问出处问题才暴露出来。现在的做法是每两小时做一次增量扫描把修改过的文件重新切分、重新 upsert并清理掉旧版本的向量记录。本地小规模场景下甚至可以每天全量重建一次索引成本很低。关键是版本要可控向量库里不能同时存在“旧说”和“新说”否则就会掉进前面提到的知识打架问题。5.5 表格被切碎、图片没有描述、代码被折叠Wiki 里富文本形式的内容是 RAG 的天然难点。表格一旦被切分切到一半缺失的列会让内容语义崩坏图片没加描述检索时图片那一块几乎是废信息代码块被折叠时大模型可能只看到“具体代码如下”几个字而看不到代码本身。整理 Wiki 时就要考虑消费场景表格尽量转成 Markdown 或 CSV图片必须补上文字说明代码块保持完整并用语言标记包裹。这些改动看起来是内容编辑工作不是技术工作但对 RAG 的影响比换任何模型都大。RAG 的上限从源头上说是 Wiki 的内容质量决定的。6. 再往前一步本体映射、GraphRAG 与 Agentic RAG当基础问答跑通之后你会发现“找答案”这件事还有两个更高级的目标一是跨页面组合二是多轮任务执行。这也是 Ontology RAG、GraphRAG、Agentic RAG 这些热词背后真正在解决的问题。6.1 Wiki 的目录结构本身就是一种本体Wiki 的父子页面、分类标签、交叉链接本质上就是在描述“实体之间的关系”。这个关系网络对单一问题检索帮助不大但对“组合型问题”至关重要。比如“订单退款失败后应该通知哪个系统”答案可能在订单系统的页面里也可能在消息中心页面的依赖关系说明里。简化的做法是把 Wiki 的分类关系提取出来作为过滤条件或上下文增强信息。比如用户问的是订单相关的问题就优先检索订单分类下的页面再扩展到关联分类。这样能在不引入复杂图谱的情况下利用 Wiki 已有的本体结构。完整的 GraphRAG 会把实体和关系抽取成图让模型沿着关系路径找答案。优势是处理关系密集的知识非常强代价是构建成本高、更新复杂。我的建议是如果你的问题大多是单点事实型不需要上 GraphRAG如果大量问题是“系统间依赖”“组织架构”“流程链路”这类关系型问题可以考虑在关键子领域先试点。6.2 从单轮问答到 Agentic RAG单轮问答只是 RAG 的第一阶段。真实使用场景里用户的问题经常是模糊的、多跳的、需要上下文确认的。Agentic RAG 的做法是让一个智能体负责拆解任务先判断问题需要哪些知识再决定调用哪个检索工具、查哪个索引必要时追问澄清最后把多轮检索的结果组合成答案。我在实践中的体会是Agentic RAG 的收益需要建立在单轮 RAG 已经足够可靠的基础上。如果基础检索率就不高智能体只是在错误的地基上做复杂的导航。先解决“每次检索都准”再考虑“自动决定检索哪里”。6.3 长期维护的节奏让两个动作形成闭环“RAG 找答案Wiki 长知识”不只是一句口号它应该变成一条可持续运行的闭环用户提问暴露知识盲区RAG 答错说明 Wiki 内容有缺口或歧义维护者根据这些问题反过来补充、拆分、更新 Wiki 页面下一轮问答就会更准。操作层面我会定期拉取 RAG 的失败案例把它们按主题归类输出一份“Wiki 待更新清单”。这份清单不是给你增加额外工作量而是把沉淀知识的优先级排出来最高优先级的正是那些被反复提问却答得不好的内容。知识管理最难的不是写而是知道该写什么RAG 恰恰能暴露这个需求。7. 写在最后团队需要“长知识的人”也要“找答案的路”这套方案跑下来我最大的体会是RAG 不会让烂 Wiki 自动变好也不会替代知识维护者。它只是把“找答案”的成本大幅降低让“长知识”这件事的价值更容易被看见。当同事开始频繁使用问答机器人、开始顺着答案点进原始页面查看时Wiki 的更新动力反而会增强这是一个正向循环。如果你现在正维护着一个内容不少但使用率不高的 Wiki我的建议是别急着买大模型服务也别一上来就搭复杂的框架。先把 Wiki 的目录和页面结构梳理一遍挑出三个最常被问到的问题类型用本地模型加向量库跑一个最小原型打印出检索片段亲眼看看知识在切分和检索之后变成了什么样子。这一套走完你自然知道下一步该优化的是模型、切分、还是写作规范。最后分享一个小技巧在 Wiki 页面顶部加一段“一句话摘要”写明本页面回答的核心问题。这段摘要会随着标题一起进入索引成为 RAG 最可靠的命中目标。你别小看这几十个字它常常比调整半天的 embedding 参数更能直接提升命中率。
返回列表