基于Agentic RAG构建可追问的个人知识工作台实战
1. 为什么我要自己搭一套知识工作台先说结论市面上现成的知识库工具我几乎试了个遍从开源的到商业化的从纯本地的到云端协作的最后发现没有一个能完全满足我的需求。不是它们不好而是我的资料形态太杂了——PDF 论文、Markdown 笔记、网页剪藏、项目文档、会议纪要还有一堆散落在各个文件夹里的代码片段和配置文件。这些东西如果只是丢进一个文件夹里那跟垃圾堆没什么区别如果全部手动整理那我又不是在搞知识管理而是在做图书管理员。我真正想要的东西其实很朴素我把资料扔进去它能自动理解内容我提问的时候它能基于我的资料回答而且回答里要能追溯到原始出处。这个需求听起来简单但真正落地的时候坑一个接一个。这套知识工作台的核心思路就是三个字可追问。不是那种你问一句它答一句就结束的问答机器人而是你可以在它的回答基础上继续深挖像跟一个熟悉你所有资料的人对话一样。比如我先问“这个项目的架构设计是怎样的”它回答之后我可以接着问“那第三层的数据流转为什么要用消息队列而不是直接调用”它应该能基于我上传的项目文档给出有依据的回答而不是胡编。适合谁来参考这套方案我觉得三类人最需要一是手里有大量 PDF 资料需要反复查阅的研究型工作者二是维护着多个项目文档、经常需要跨项目检索的开发或产品人员三是任何觉得“资料存了等于没存”的人。如果你只是偶尔查个东西那用搜索就够了但如果你每天都要跟自己的资料库打交道那这套东西值得花时间搭。2. 整体架构设计与技术选型思路2.1 为什么是 RAG 而不是微调很多人一提到“让 AI 理解我的资料”第一反应是微调模型。我一开始也想过这条路但很快放弃了。原因很简单微调的成本太高而且效果不可控。你每次新增资料都要重新训练训练完了还不一定记得住模型该忘的还是忘。更关键的是微调后的模型你没法追溯它到底是从哪份文件里学到的这个知识出了问题你连排查的方向都没有。RAG 的思路完全不同。它不改变模型本身而是在模型回答问题之前先从你的资料库里检索出最相关的片段把这些片段作为上下文喂给模型让模型基于这些片段来回答。这样做的好处是资料更新只需要更新索引不需要重新训练回答可以附带引用来源你能看到它是根据哪段文字得出的结论而且检索和生成是解耦的哪一步出了问题你都能单独调试。我最终选的是Agentic RAG的架构也就是在传统 RAG 的基础上加了一层 Agent 调度。传统 RAG 是“检索一次生成一次”Agentic RAG 是“检索、判断、再检索、再判断”直到信息足够才生成回答。这个差别在处理复杂问题时特别明显。比如你问“对比 A 项目和 B 项目的技术选型差异”传统 RAG 可能只检索到 A 项目的文档就开始回答了Agentic RAG 会先检索 A再检索 B然后对比最后才生成。2.2 文档解析层的选型与取舍PDF 解析是整个流水线里最脏最累的活。我试过至少五种方案最后定下来的是MinerU PyMuPDF 兜底的组合。MinerU 对学术论文和复杂版式的 PDF 解析效果很好能保留公式、表格和图片的位置信息但它的速度偏慢而且对某些扫描版 PDF 会直接失败。所以我的策略是先用 MinerU 跑一遍如果解析结果为空或者明显异常自动切换到 PyMuPDF 做基础文本提取。Markdown 文件的处理反而简单因为 Markdown 本身就是结构化的。我用markdown-it把 Markdown 解析成 AST然后按标题层级切分成块。这里有个细节代码块要单独处理。如果你把代码块和正文混在一起做向量化检索的时候代码块会干扰正文的语义匹配。我的做法是把代码块提取出来单独存一份检索时如果命中代码块直接返回原始代码而不是让模型重新生成。网页剪藏我用的是Readability Turndown的组合。Readability 负责从网页里提取正文内容去掉导航栏、广告和评论区Turndown 负责把 HTML 转成 Markdown。这个组合的好处是输出的 Markdown 很干净后续切分和向量化都省事。唯一要注意的是有些网站的正文是动态加载的Readability 抓不到这种情况我会手动复制内容再转 Markdown。2.3 向量化与检索策略向量模型我选的是BGE-M3理由是它对中文和英文的支持都很均衡而且支持多粒度检索——你可以用一句话检索也可以用一段话检索效果都不错。更重要的是它是开源的我可以本地部署不用担心数据外泄。检索策略上我做了两层第一层是向量检索用余弦相似度找语义相近的片段第二层是关键词检索用 BM25 算法找字面匹配的片段。然后把两层的结果做倒数排名融合取综合得分最高的几个片段。这样做的好处是既能处理“意思相近但用词不同”的情况也能处理“必须精确匹配某个术语”的情况。分块大小我调了很多次最后定在512 个 token重叠 128 个 token。块太小了语义不完整块太大了检索精度下降。重叠是为了防止关键信息刚好被切在边界上。这个参数不是绝对的你可以根据自己的资料特点调整但 512/128 是一个比较稳妥的起点。3. 核心模块的实操落地细节3.1 文档入库流水线的完整实现整个入库流程我拆成了五个步骤每一步都可以单独重跑这样出问题的时候不用从头再来。第一步是格式识别与路由。我写了一个简单的分发器根据文件扩展名和 MIME 类型决定用哪个解析器。PDF 走 MinerUMarkdown 走 markdown-itHTML 走 Readability纯文本直接读取。这一步的关键是记录原始文件的哈希值后续如果文件没变就跳过避免重复处理。第二步是内容清洗。解析出来的文本往往带着页眉页脚、页码、乱码字符。我用正则表达式做了一轮基础清洗然后过一遍语言检测把非中英文的内容标记出来人工确认。这一步看起来不起眼但如果不做后续检索的时候会经常命中一些无意义的片段。第三步是智能分块。我没有用简单的按字数切分而是按语义边界切分。具体做法是先用标题层级做一级切分然后在每个章节内部按段落切分如果某个段落超过 512 个 token再按句子边界切分。这样切出来的块语义完整性更好。第四步是向量化与索引。每个块同时生成向量索引和关键词索引向量索引用 FAISS关键词索引用 Elasticsearch 的 BM25。两个索引都存了块的 ID检索的时候通过 ID 关联。第五步是元数据注入。每个块都会附带来源文件、章节标题、页码、创建时间等元数据。这些元数据在检索时可以用于过滤比如“只在我最近三个月添加的资料里搜索”。# 分块逻辑的核心代码示意 def semantic_chunk(text, max_tokens512, overlap128): sections split_by_heading(text) chunks [] for section in sections: paragraphs split_by_paragraph(section) current_chunk [] current_len 0 for para in paragraphs: para_len count_tokens(para) if current_len para_len max_tokens: chunks.append(join(current_chunk)) # 保留重叠部分 current_chunk current_chunk[-overlap:] current_len count_tokens(join(current_chunk)) current_chunk.append(para) current_len para_len if current_chunk: chunks.append(join(current_chunk)) return chunks注意分块的时候一定要保留原始文本的位置信息否则后续引用溯源的时候你根本找不到这句话是从哪一页来的。3.2 检索增强的 Agent 调度逻辑Agent 调度的核心是一个状态机。每次用户提问Agent 会经历以下几个状态理解问题、决定检索策略、执行检索、评估检索结果、决定是否继续检索、生成回答。理解问题这一步我会让模型先判断问题的类型。是事实型问题“X 是什么”、对比型问题“A 和 B 有什么区别”、还是分析型问题“为什么 X 会导致 Y”。不同类型的问题检索策略不一样。事实型问题检索一次就够了对比型问题需要分别检索两个对象分析型问题可能需要多轮检索。评估检索结果这一步很关键。我会让模型判断检索到的片段是否足以回答问题。如果不够它会生成一个新的查询词再检索一次。这个循环最多执行三轮防止无限循环。实测下来大部分问题一轮就够复杂问题两到三轮也能覆盖。生成回答的时候我会强制要求模型引用来源。格式是[来源文件名第 X 页]。如果模型没有引用来源我会让它重新生成。这个约束看起来简单但能极大减少胡编的情况。3.3 追问上下文的维护方式追问是这套系统的核心价值但也是最容易出问题的地方。如果只是简单地把历史对话拼接到 prompt 里几轮之后上下文就会爆炸而且模型会混淆不同轮次的信息。我的做法是维护一个对话状态对象里面存三样东西原始问题、已检索到的片段、已生成的回答。每次追问的时候我会先把当前问题跟历史问题做指代消解把“它”“这个”“那个”替换成具体的实体然后再去检索。这样检索的准确率会高很多。举个例子。第一轮我问“MinerU 的解析效果怎么样”第二轮我问“它处理表格的能力呢”。如果不做指代消解第二轮检索“它处理表格的能力”很可能检索不到 MinerU 相关的片段。做了指代消解之后第二轮的实际检索词变成“MinerU 处理表格的能力”命中率就上去了。指代消解我用的是一个轻量级的模型跑在本地的速度很快。你也可以用规则做比如把“它”替换成上一轮问题里的主语但规则覆盖不了复杂情况还是模型靠谱。4. 实际使用中踩过的坑与解决方案4.1 PDF 解析的常见问题与处理PDF 解析的坑我踩得最多这里列几个典型的。扫描版 PDF 解析为空。有些 PDF 是图片扫描的里面根本没有文本层。MinerU 遇到这种会返回空结果。我的处理方式是检测解析结果的长度如果小于阈值就自动切换到 OCR 流程。OCR 我用的是 PaddleOCR对中文的支持很好。但 OCR 的速度慢所以只对扫描版启用。表格解析错位。PDF 里的表格是最难处理的尤其是跨页表格和合并单元格。MinerU 对简单表格处理得不错但复杂表格经常错位。我的做法是把表格单独提取出来转成 Markdown 表格格式如果转换失败就保留原始截图在检索时用图片描述来匹配。公式变成乱码。学术 PDF 里的公式如果解析不当会变成一堆乱码字符。MinerU 对 LaTeX 公式的识别还可以但有些特殊符号还是会丢。我的处理方式是在清洗阶段检测连续的非常规字符如果占比过高就标记这个块为“低质量”检索时降低权重。多栏排版顺序错乱。双栏论文的解析顺序经常是左栏一段右栏一段交替出现读起来完全不通。MinerU 有版面分析功能能识别分栏但偶尔还是会出错。我的兜底方案是按 X 坐标对文本块排序先左后右先上后下。4.2 检索效果不佳的排查思路检索效果不好的时候不要急着换模型先按这个顺序排查。先看分块是否合理。把检索命中的块打印出来看看是不是把不相关的内容切到了一起。如果是调整分块参数。再看查询词是否太短或太长。太短的查询词信息量不够太长的查询词会引入噪声。我的经验是查询词控制在 10 到 30 个字之间效果最好。然后看向量模型是否适合你的领域。通用向量模型在专业领域比如医学、法律的表现会下降这种情况可以考虑用领域数据做微调或者换一个在该领域表现更好的模型。还有一个容易被忽略的点元数据过滤是否过严。如果你设置了时间范围过滤但相关资料都是很久以前添加的那自然检索不到。排查的时候先把所有过滤条件去掉确认是检索本身的问题还是过滤的问题。4.3 回答质量不稳定的应对策略回答质量不稳定通常有三个原因。一是检索到的片段本身质量不高。这种情况要从入库环节解决提高解析和清洗的质量。二是prompt 不够明确。我会在 prompt 里明确要求模型“只基于提供的片段回答如果片段中没有相关信息就直说不知道”。这个约束能减少很多胡编。三是模型本身的能力边界。有些问题就是超出了模型的能力范围这种情况我会在回答里标注“此回答可能不准确建议人工核实”。我还做了一个回答置信度评估的机制。每次生成回答后让模型自己评估一下“这个回答有多大的把握是基于检索片段的”。如果置信度低于阈值就在回答前面加一个提示。这个机制不完美但能帮用户快速判断哪些回答需要重点核实。常见问题排查方向解决方案检索不到相关内容分块是否合理、查询词是否合适调整分块参数、优化查询词检索到不相关内容向量模型是否适合领域换模型或微调回答胡编prompt 约束是否足够加强“不知道就说不知道”的约束追问时答非所问指代消解是否生效检查指代消解模块解析结果乱码PDF 是否为扫描版启用 OCR 兜底5. 知识工作台的扩展方向与个人体会5.1 从个人知识库到团队协作的演进这套东西我一个人用了大半年后来团队里其他人也想用我就做了一些改造让它支持多人协作。核心改动是权限隔离和共享空间。每个人有自己的私有知识库同时可以访问团队共享的知识库。检索的时候会同时搜私有和共享但回答里会标注来源是私有还是共享。权限隔离的实现方式是在元数据里加一个owner字段检索时根据当前用户过滤。共享空间则是单独建一个索引所有人都可以往里加资料。这个改造不复杂但要注意索引更新的并发问题。多人同时上传资料的时候如果两个进程同时写同一个索引文件可能会损坏。我的做法是加了一个简单的队列所有写入操作排队执行。5.2 移动端访问的轻量化方案我经常在外面的时候想查点东西所以做了一个移动端的轻量访问方案。核心思路是把检索和生成分离。移动端只负责发送查询和展示结果检索和生成都在家里的服务器上跑。这样移动端不需要强大的算力只要能联网就行。具体实现是一个简单的 Web 页面用响应式布局适配手机屏幕。查询通过 API 发送到服务器服务器返回结果后渲染。为了减少流量返回的结果只包含回答文本和来源引用不包含原始片段。如果用户想看原始片段再单独请求。这个方案的好处是简单不需要开发 App也不需要处理复杂的同步逻辑。缺点是必须联网才能用离线场景覆盖不了。但对我来说够用了。5.3 我个人的一些使用心得最后分享几个我实际用下来觉得最有价值的点。资料入库要趁早。我一开始想着“等资料攒多了再一起整理”结果攒了半年之后发现整理的工作量巨大而且很多资料已经忘了当初为什么保存。后来我改成“看到有用的就立刻入库”虽然每次只花几分钟但积累起来效果很好。不要追求完美的解析。PDF 解析永远会有瑕疵如果你非要等到解析结果完美了才入库那永远入不了库。我的策略是“先入库后优化”解析质量差的块标记出来后续有空再单独处理。追问比单次问答有价值得多。我大部分有价值的发现都是在追问过程中产生的。第一轮回答往往只是表面信息追问到第三轮第四轮才能挖到真正有用的东西。所以这套系统的追问能力是我最看重的。定期回顾检索日志。我会定期看哪些查询词被搜得最多、哪些资料被引用得最多。这些数据能帮我判断哪些资料真正有价值哪些资料只是占空间。有一次我发现某份文档被引用了上百次但我自己都快忘了它的存在后来把它重新整理了一遍效果很好。这套知识工作台我还在持续迭代最近在尝试把图片和表格的检索也加进去。目前的方案是用多模态模型生成图片描述然后把描述文本做向量化。效果还在验证中等稳定了再单独写一篇分享。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →