
1. 从一堆散落的 PDF 和 Markdown 说起为什么通用笔记软件救不了你我自己的资料库大概从五年前开始失控。那时候还天真地以为只要把 PDF 丢进某个网盘、把 Markdown 笔记塞进某个笔记软件知识就算沉淀下来了。结果呢写方案的时候想引用半年前看过的一篇论文只记得里面有个分层检索的架构图具体在哪份 PDF、第几页完全想不起来。打开笔记软件搜关键词搜出来的是一堆标题相似但内容无关的条目打开网盘搜文件名搜出来的是一堆final_v3_真的最终版.pdf。这个问题的本质不是资料没存下来而是资料之间没有建立可被追问的关联。传统的文件夹和标签体系是给人看的人靠记忆去导航但人的记忆是模糊的、联想的而文件夹是精确的、树状的。这两者天然不匹配。你记得的是那个讲 RAG 分块策略的段落但文件夹要求你记得的是它存在技术/LLM/检索/下面。所以当我看到把 PDF、Markdown 和项目资料沉淀为个人知识库这个需求时我第一反应不是去找一个更好的笔记软件而是去想能不能让机器替我记住那些关联我只负责提问这就是 RAGRetrieval-Augmented Generation检索增强生成这套思路对个人知识管理的价值所在——它不要求你提前把知识组织成完美的树它允许你用自然语言去追问你的资料库。这篇文章适合三类人看一是手里攒了几百份 PDF 和一堆 Markdown 笔记、但从来没真正用起来的人二是想自己搭一套本地知识库、又不想被复杂工程劝退的人三是已经在用某些工具、但发现问两句就答非所问、想搞清楚瓶颈在哪的人。我会把整套工作台的搭建逻辑、关键取舍、以及我踩过的坑尽量讲透。2. 拆解可持续追问这个核心诉求它到底难在哪2.1 一次性问答和可持续追问是两码事很多人对知识库 AI 的想象是这样的我把资料喂进去然后问它问题它给我答案。这个描述没错但它描述的是一次性问答。而可持续追问要求的是我问了第一个问题拿到答案后能基于这个答案继续问那第二点展开说说这个结论的依据是哪份文档如果换个前提会怎样并且整个对话过程中系统始终锚定在我的资料库上而不是聊着聊着就开始自由发挥。这两者的工程难度差了一个量级。一次性问答只需要检索 生成两步可持续追问需要额外解决三件事上下文如何跨轮次保持、检索结果如何随追问动态调整、如何避免多轮对话中检索目标漂移。我见过太多 demo第一问很惊艳第二问就开始胡编第三问直接忘了自己在聊什么。这不是模型不行是检索层和对话层没有打通。2.2 三类资料的处理逻辑完全不同PDF、Markdown、项目资料我理解为代码片段、配置文件、会议记录这类半结构化文本这三者的处理方式必须区别对待不能一把梭。PDF 的麻烦在于它是版面而不是文本。一份双栏排版的论文如果你按阅读顺序提取文本很可能把左栏和右栏的内容交错在一起导致语义断裂。表格、公式、图注更是重灾区。Markdown 相对友好但它的结构信息标题层级、代码块、列表如果被当成纯文本切碎就浪费了。项目资料则往往很短、很碎单独一块没有意义必须带上足够的上下文。我自己的做法是PDF 走版面感知的解析路径Markdown 走结构感知的切分路径项目资料走文件级 摘要的路径。下面会分别展开。2.3 追问对检索质量的要求比问答高得多一次性问答检索错了用户可能看不出来因为答案读起来通顺。但追问场景下用户会顺着答案往下挖一旦检索层给的是无关内容模型就会开始圆越圆越离谱。所以可持续追问的隐性门槛是检索的召回率和精确率都得在线而且要有机制让用户看到答案是从哪来的这样才能建立信任、也才能让用户自己判断该不该继续追问。提示如果你搭的知识库只能回答是什么、不能回答为什么和依据在哪那它本质上还是个高级搜索框不是知识工作台。3. 工作台的整体架构把资料进和问题出拆成两条流水线3.1 一张图看清数据流用文字描述不画图整套系统我分成两条独立的流水线。入库流水线负责把原始资料变成可检索的向量和元数据解析 → 清洗 → 切分 → 向量化 → 存储。问答流水线负责把用户问题变成答案问题改写 → 检索 → 重排 → 组装上下文 → 生成 → 引用回溯。两条线之间通过一个统一的存储层向量库 元数据库连接。这么拆的好处是入库可以离线批量跑慢一点没关系问答必须在线快速响应所以要做缓存和预计算。如果混在一起每次提问都重新解析 PDF那体验会烂到没法用。3.2 为什么我坚持本地优先市面上有不少托管型知识库服务上传即用省事。但我最后选了本地优先的方案原因有三个。第一是隐私项目资料里经常有客户信息、内部设计传到别人服务器上我不放心。第二是可控检索参数、切分策略、模型选择我想调就能调托管服务往往只给你几个开关。第三是成本资料库一旦上万份文档按量计费的托管服务账单会很难看本地跑一次向量化之后检索几乎零边际成本。本地优先的代价是要自己维护环境但现在开源工具已经足够成熟这个代价我认为值得。3.3 组件选型每个位置都有取舍环节我的选择备选选择理由PDF 解析版面感知解析器纯文本提取双栏、表格场景下语义完整度高切分语义 结构混合切分固定长度切分避免句子被拦腰截断向量模型中英双语嵌入模型纯英文模型资料中英混杂纯英文模型中文召回差向量库本地轻量向量库云端向量服务隐私 零边际成本生成模型本地可跑的中等规模模型大参数模型追问场景对延迟敏感本地响应快重排轻量交叉编码重排不重排重排能把 top10 里的相关项提到 top3这张表里的每个选择都不是绝对的比如你资料全是英文、且对延迟不敏感那向量模型和生成模型都可以换。关键是理解每个位置在解决什么问题而不是照抄。4. 入库流水线PDF、Markdown、项目资料分别怎么处理4.1 PDF 解析别急着提取文本先看版面我最早的做法是用最朴素的文本提取结果一份三栏排版的综述提取出来的文字读起来像乱码。后来换成版面感知的解析方式先识别出这是标题、这是正文块、这是表格、这是图注再按阅读顺序重组质量立刻上了一个台阶。具体操作上我会先跑一遍解析把结果按页存成中间格式比如每页一个结构化对象然后人工抽查几页。抽查的重点是跨栏的段落有没有被正确合并、表格有没有被拆散、公式有没有变成乱码。这一步花十分钟能省掉后面几小时的调试。对于扫描版 PDF本质是图片必须走 OCR。OCR 的坑在于中文识别率参差尤其是小字号和特殊字体。我的经验是如果一份扫描件很重要宁可手动校对关键页也不要完全信任 OCR 结果因为错误的文字会污染整个检索库。4.2 Markdown 切分保留结构别当纯文本Markdown 的价值在于它自带结构。一个##标题下面的内容天然就是一个语义单元。所以我切分 Markdown 时优先按标题层级切而不是按字符数切。具体规则是遇到一级标题切一刀遇到二级标题再切一刀如果某个二级标题下的内容超过阈值我设的是 800 字左右再按段落细分。代码块要特殊处理。一个代码块如果被切碎检索出来就是残缺的没法用。我的做法是代码块整体保留不参与切分并且给它打上代码标签检索时可以按类型过滤。表格同理整表保留。还有一个细节Markdown 里的链接和图片引用切分时要保留在对应的块里否则你检索到一段文字却不知道它引用了哪张图。4.3 项目资料的文件级摘要 块级检索双轨制项目资料代码、配置、会议记录的特点是单个文件可能很短但文件之间的关联很强。比如一个配置文件和一个说明文档单独看都没什么合起来才完整。我的处理是双轨文件级生成一段摘要存进元数据用于粗筛文件内部再切块用于精确定位。检索时先用问题去匹配文件摘要锁定几个候选文件再在候选文件内部做块级检索。这样既保证了召回不会漏掉整个文件又保证了精确能定位到具体段落。会议记录这类资料还有个特殊点它有时间属性。我会在元数据里加上日期检索时支持最近三个月的讨论这种时间过滤。4.4 向量化与存储批处理、去重、增量更新向量化是个体力活尤其是首次入库。我的做法是分批跑每批几百个块跑完记录进度断了能续。一定要做去重同一份文档重复入库会导致检索结果里出现多个几乎一样的块浪费上下文窗口。去重我按内容哈希做简单有效。增量更新是很多人忽略的。资料库不是一次建完就完事你每天都在往里加东西。所以入库脚本要支持只处理新增和修改的文件靠文件修改时间和哈希判断。我一开始没做这个每次加一份文档就全量重跑浪费了大量时间。注意向量化之前一定要确认切分质量。切分错了后面检索再优化也救不回来。这是整条流水线里最值得花时间的一步。5. 问答流水线让追问不跑偏的几个关键设计5.1 问题改写把口语化的追问变成可检索的查询用户追问时说的话往往很口语那第二点呢这个有例子吗换个场景呢。这些话直接拿去检索向量模型根本不知道你在问什么。所以中间要加一层问题改写结合对话历史把当前问题补全成一个完整的、可检索的查询。比如上一轮在聊分块策略用户问那重叠部分呢改写后应该变成文本分块时的重叠窗口设置策略。这一步用一个小模型就能做成本很低但对检索质量的提升非常明显。5.2 检索策略混合检索比纯向量检索稳纯向量检索的问题是它对精确匹配不敏感。你搜一个专有名词向量检索可能给你一堆语义相近但没提到这个词的文档。所以我用的是混合检索向量检索负责语义召回关键词检索比如 BM25负责精确召回两路结果合并后再重排。重排这一步很关键。它用一个交叉编码模型把问题和候选块放一起打分比单纯的向量相似度准得多。代价是慢所以只对前几十个候选做重排不重排全库。5.3 上下文组装给模型的信息要够用但不冗余检索回来一堆块不能全塞给模型上下文窗口有限塞太多反而稀释了重点。我的组装策略是按重排分数排序取前 N 个块每个块带上来源信息文件名、页码/标题然后拼成一段结构化的上下文。这里有个经验块与块之间要加分隔标记让模型知道这是不同来源的内容避免它把不同文档的内容混在一起当成一个事实。我见过模型把 A 文档的结论和 B 文档的数据拼在一起得出一个两边都不成立的结论就是因为上下文没分隔清楚。5.4 引用回溯让每个答案都能点回去这是建立信任的关键。答案生成时要求模型在引用某段内容时标注来源编号前端把这些编号渲染成可点击的链接点进去直接跳到原文对应位置。这样用户追问时心里有底我知道这个答案是从哪来的我可以自己去核对。实现上我在组装上下文时给每个块编了号生成时要求模型输出形如[1][2]的引用标记后处理时再映射回真实的文件路径和位置。5.5 多轮对话的状态管理别让历史拖垮检索多轮对话有个陷阱历史越长问题改写时越容易被无关的历史带偏。我的做法是滑动窗口 摘要保留最近几轮完整对话更早的对话压缩成一段摘要。这样既保留了上下文又不会让历史无限膨胀。另外我加了一个话题切换检测如果当前问题和历史话题的相似度低于阈值就重置对话状态当成新问题处理。这能避免聊着聊着串台。6. 实测中暴露的问题检索不准、答非所问、越问越偏6.1 检索不准的三种典型表现和排查路径第一种表现是答非所问问 A答 B。排查时先看检索结果如果检索回来的块本身就和问题无关那是检索层的问题如果检索回来的块是对的但答案跑偏了那是生成层的问题。这个二分法能帮你快速定位。第二种表现是答案正确但来源不对内容对但引用的是另一份文档。这通常是重排没做好或者多个块内容相似导致模型选错了引用。解决办法是提高重排权重或者在上下文里强化来源标记。第三种表现是该召回的没召回明明库里有就是搜不出来。这往往是切分问题——相关内容被切散了或者被切进了代码块标签里检索时被过滤掉了。回头检查切分策略。6.2 追问跑偏的根因检索目标漂移我遇到过一个典型场景第一问这套系统的架构是什么答得挺好第二问它的存储层怎么设计的答得也行第三问那成本呢模型开始聊通用云存储成本完全脱离了我的资料库。根因是第三问太短、太泛问题改写时没有正确继承前两轮的存储层这个限定导致检索目标漂移到了通用话题。修复方法是问题改写时强制继承上一轮的核心实体如果当前问题里没有明确的主语就默认沿用上一轮的主语。这个规则很简单但效果立竿见影。6.3 一个反直觉的发现块不是越小越好刚开始我追求小块觉得块小检索精确。结果发现块太小单块信息不完整模型拿到手也不知道在说什么。后来我把块大小调大并且加了重叠窗口检索质量反而提升了。原因是检索的单元和生成的单元应该匹配——检索需要精确但生成需要上下文块太小就满足不了生成的需求。我最后的参数是块大小 500 到 800 字重叠 100 到 150 字。这个范围对不同资料类型略有调整但大体在这个区间。6.4 性能瓶颈重排和生成是两大耗时点实测下来一次完整问答的耗时分布大概是检索 10%、重排 30%、生成 60%。重排和生成是大头。优化手段重排只对 top20 做生成用流式输出让用户先看到字。另外常见问题可以缓存答案命中缓存直接返回。7. 让知识库真正活起来的几个习惯7.1 入库不是终点定期回访检索质量我每个月会抽十几个问题手动跑一遍看检索结果和答案质量有没有退化。资料库是动态的新加的内容可能和旧内容冲突或者改变了某些概念的分布定期回访能及早发现问题。7.2 给资料打可信度标签不是所有资料都同等可信。正式发表的论文、官方文档和随手记的草稿可信度不一样。我在元数据里加了可信度字段检索时可以加权生成时也可以提示模型优先采信高可信度来源。这个习惯让答案的可靠性提升了不少。7.3 保留原始文件这条退路无论 AI 答得多好我始终保留原始文件的完整访问路径。因为 AI 会出错而原始文件不会骗人。当我对某个答案存疑时点开引用直接读原文这是最后的防线。7.4 把高频追问沉淀成预设问题有些问题我会反复问比如这个项目的关键决策点有哪些。与其每次重新检索不如把这类高频追问做成预设提前算好答案缓存起来。这既是性能优化也是一种知识提炼——你反复问的往往就是你最关心的。8. 关于工具选型和长期维护的一点个人体会搭这套东西的过程中我最大的体会是工具是次要的流程和习惯才是主要的。我见过有人用着最先进的工具资料库却一团糟也见过有人用很朴素的方案但资料整理得井井有条检索效果反而更好。工具能帮你降低门槛但替代不了你对资料的判断。选型上我的建议是从最小可用开始别一上来就追求全功能。先跑通一份 PDF 进、一个问题出的闭环再逐步加 Markdown、加项目资料、加重排、加引用。每加一个环节都回头验证一下整体效果有没有提升。很多功能听起来很美实际用起来可能是负优化。维护上把入库脚本当成代码来管理用版本控制写清楚每个参数为什么这么设。半年后你回头看会感谢当时的自己。资料库是会陪你很多年的东西值得认真对待。最后分享一个我一直在用的小技巧每次往库里加新资料我都会顺手问它一个这份资料讲了什么的问题用它的回答来检验入库质量。如果它答得含糊说明解析或切分有问题当场就修。这个习惯让我避免了很多入库了但其实搜不到的隐形故障。