尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

从Obsidian到AI知识库:Markdown清洗、分块与RAG全流程解析

发布时间:2026/9/24 21:10:05

资讯中心
01
ARTICLE

从Obsidian到AI知识库:Markdown清洗、分块与RAG全流程解析

从Obsidian到AI知识库:Markdown清洗、分块与RAG全流程解析
很多人第一次听到“把 Obsidian 变成 AI 知识库”这个说法第一反应是装个插件点一下同步然后就能跟自己的笔记对话了。我一开始也这么想结果折腾一圈发现事情远没那么简单。真正的核心不在于“对话”而在于“让 AI 能一次性读懂你整个库”。这背后涉及 Markdown 解析、文本分块、向量化、检索链路一整套事情。这篇内容我就把整套流程从零讲透包括我怎么写脚本批量读取 Obsidian 全库、怎么把 Markdown 变成干净的纯文本、怎么选择分块策略以及最后怎么接入本地或云端的 RAG 流水线。如果你手里的笔记已经积累了上千篇想用 AI 直接基于这堆 Markdown 做问答、写周报或者找关联那这篇文章就是为你准备的。现在市面上的知识库方案很多但大部分是给企业文档用的拿到个人 Obsidian 库上往往水土不服。原因很简单Obsidian 里全是 Markdown 文件而且带有自己的一套语法——[[双向链接]]、![[嵌入]]、YAML frontmatter、标签体系这些玩意儿普通解析器根本不认。如果直接把原始.md文件丢给知识库工具轻则格式混乱重则检索结果完全跑偏。所以把整个 Obsidian 变成 AI 知识库本质上是一个“格式归一的工程问题”而不是简单调个 API 的事。1. 先把整个库读出来Markdown 批量扫描与链接语法清理1.1 为什么不能直接拿.md文件当输入Obsidian 的仓库本质上是一个本地文件夹里面散落着几百上千个.md文件。表面上看这些文件都是纯文本好像随便哪个脚本都能读。但问题出在 Obsidian 自己的语法上。比如我随手翻开一篇笔记里面可能是这样--- title: 分布式系统笔记 tags: [分布式, 架构] date: 2024-06-01 --- # CAP 定理 在分布式系统中一致性Consistency、可用性Availability、分区容错性Partition tolerance三者不可兼得。 相关笔记[[BASE 理论]]、[[Paxos 算法]] ![[系统架构图.png]]这一段里有三处普通文本解析器会犯迷糊的地方。第一文件开头的---包裹区叫 YAML frontmatter。它存储的是元数据不是正文内容。如果不加处理直接塞给 AI这些键值对会污染语义。比如date: 2024-06-01可能被 AI 误以为是在讲时间相关的概念。第二[[BASE 理论]]这种双链语法是 Obsidian 用来表示笔记间关联的。但 AI 并不知道[[...]]是什么意思它只会看到一堆方括号。更麻烦的是[[Paxos 算法]]可能是一个尚未创建的笔记名AI 如果去检索这个文件名可能会返回空结果。第三![[系统架构图.png]]是图片嵌入语法。AI 读不到图片内容但会把这行文本当作有效内容导致检索时出现一个永远无法匹配的内容碎片。所以让 AI 读取 Obsidian 的第一步不是“读”而是“翻译”——把 Obsidian 方言翻译成大白话 Markdown。1.2 遍历全库时这些目录必须跳过写脚本遍历目录很简单Python 里一个os.walk()就搞定。但有几个目录必须要过滤掉否则你的向量库里会混进大量垃圾数据。首先是.obsidian目录。这是 Obsidian 的配置目录里面存着你的插件配置、工作区布局、快捷键设置全是 JSON 文件。这些跟你的知识内容没有任何关系纯属噪音。其次是.trash目录。Obsidian 删除笔记时会把它移到这里如果你启用了“软删除”这个目录会越来越大里面全是废弃内容。把它向量化等于 AI 每天在拿你删掉的草稿当记忆。然后是.git目录如果你用了 Obsidian Git 插件做版本管理。这里记录的是仓库的每次 commit 历史体积巨大而且全是重复的文本快照。喂给 AI 不仅浪费 token还会让检索结果被历史版本淹没。最后是附件目录。如果你习惯把图片、PDF、音频都放在仓库里的assets或附件文件夹遍历时一定要按扩展名过滤只处理.md文件。否则遇到 PDF 或者扫描件普通脚本读出来的全是乱码。我自己的过滤逻辑是这样写的import os VAULT_PATH /path/to/your/vault SKIP_DIRS {.obsidian, .trash, .git, node_modules, .smart-env} def find_markdown_files(vault_path): md_files [] for root, dirs, files in os.walk(vault_path): # 原地修改 dirs实现剪枝 dirs[:] [d for d in dirs if d not in SKIP_DIRS] for f in files: if f.endswith(.md): md_files.append(os.path.join(root, f)) return md_files这里有个小坑Python 的os.walk()在遍历时dirs列表直接决定了后续要进入哪些子目录。老老实实在循环里写if d in SKIP_DIRS: continue是没用的必须用dirs[:] [...]这种切片赋值的方式否则 itertools 的遍历顺序会把过滤逻辑打乱还是会走进.git目录。1.3 处理 frontmatter、双链和嵌入标记拿到文件列表后下一步就是逐文件清洗。我整理了一套比较稳妥的清洗规则实测下来能把 Obsidian 的方言语法降噪到一个很干净的程度。YAML frontmatter用正则匹配开头的---到---之间的内容整体删除。但要注意正文中如果用了---做分割线可能被误删。我的做法是只匹配文件开头 200 个字符内的---块避免误伤。内部链接[[笔记名]]转成纯文本。如果是[[笔记名|显示文字]]保留显示文字如果没有显示文字保留笔记名本身。这样 AI 仍能理解这里引用了一篇叫“Paxos 算法”的笔记。嵌入![[xxx.png]]直接删除整行。因为图片信息 AI 读不到留一个空洞的引用没有任何检索价值。外部链接[文字](https://...)保留文字去掉 URL。这样既能保留语义又不会让 AI 陷入一堆网址字符。标签#标签名如果标签是行内的保留词本身但去掉#符号。如果是文件底部的标签列表直接删除。清洗逻辑写成代码大概是这个感觉import re def clean_markdown(text): # 删除 YAML frontmatter if text.startswith(---): parts text.split(---, 2) if len(parts) 3: text parts[2].lstrip() # 处理嵌入图片 text re.sub(r!\[\[.*?\]\], , text) # 处理内部链接 def replace_wikilink(match): target match.group(1) if | in target: return target.split(|)[-1].strip() return target.strip() text re.sub(r\[\[(.*?)\]\], replace_wikilink, text) # 处理外部链接 text re.sub(r\[(.*?)\]\(https?://.*?\), r\1, text) # 处理标签 text re.sub(r#([\w\u4e00-\u9fa5]), r\1, text) return text这一步做完你的 Markdown 文件就从 Obsidian 方言变成了通用 MarkdownAI 能读懂的通用文本格式。2. Markdown 怎么切才不傻分块参数与边界策略2.1 一次性全塞进去不现实暴力拼接的代价是什么在 RAG 方案里切分文档是最容易翻车的一步。很多人上来就把整篇笔记当成一个 chunk几百篇笔记直接拼接成一个大文本扔给 embedding 模型。这样做有两个直接后果。第一是 token 成本。假设你的库有 2000 篇笔记平均每篇 2000 字那总字数就是 400 万字。按一个中国汉字约等于 1.5 个 token 算一次调用 embedding API 的费用虽然不高但如果后面每次问答都要全量检索一遍那响应延迟和费用都会很感人。第二是检索精度。RAG 的原理是先把你问的问题做向量化再在你的知识库里找最相似的片段。如果你把整篇长文当一个片段那这篇长文只要有一个段落跟问题相关整篇就会被检索出来。结果就是AI 收到了一堆不相关的上下文回答质量反而下降。所以正确的做法是把每篇 Markdown 切分成多个语义完整的片段每个片段作为一个独立的知识点参与向量检索。2.2 按标题切分、按段落切分、固定窗口切分的取舍切分策略大致有三种实话说没有绝对的最好只有适不适合。第一种是按标题切分。也就是识别 Markdown 里的##、###标题把每个小标题下的内容作为一个 chunk。这种方案最适合 Obsidian 里的长文笔记比如读书笔记、课程笔记。因为这类笔记通常结构清晰每个小节讲一个独立主题。按标题切能把语义单元保持得比较完整。缺点是如果某篇笔记压根没有二级标题那整个文件就只有一个 chunk起不到切分效果。第二种是按段落切分。用空行作为分隔符把文本切成自然段再按长度合并成 chunk。这种方案比较通用适合大多数场景。但段落长短不一有的段落只有一句话有的段落却是一整篇长文的论证过程。合并的时候如果处理不好容易把两个无关主题拼在一起。第三种是固定窗口切分。不关心标题和段落直接按字符数切比如每 800 个字符一段前后重叠 100 个字符。这种方案的优点是实现简单、逻辑统一、性能稳定。缺点是容易把一句话从中间切断强行拆散语义。我的实践经验是先用标题切如果标题下内容还是太长再用固定窗口兜底。用一个混合策略def smart_chunk(md_text, max_len800, overlap100): sections re.split(r\n(?#{1,6} ), md_text) chunks [] for sec in sections: if len(sec) max_len: chunks.append(sec) else: # 长段落按固定窗口切保留 overlap for i in range(0, len(sec), max_len - overlap): chunks.append(sec[i:imax_len]) return chunks用re.split(r\n(?#{1,6} ), ...)这种正则可以做到在标题前断开保留标题本身作为 chunk 的开头。这样每个 chunk 自带小标题在向量检索时命中率会高不少。2.3 chunk 长度到底该设多少中文场景的特殊考量关于 chunk 大小市面上常见的默认值是 512 token 或者 1024 token。这个值主要受两个因素影响一是 embedding 模型的最大输入长度二是你之后要接的大模型上下文窗口。这里有一个纯中文场景的特殊问题要提一嘴。很多 embedding 模型是按西文 token 训练的中文在分词后 token 数大约是字数的 1.5 倍。假设你设 chunk 大小是 512 token那实际能容纳的中文字符数大约是 340 个也就是三四百字的段落。这个长度对很多笔记来说其实是偏小的一篇 2000 字的深度思考笔记会被切成六块语义可能被拆散。所以我的建议是把 chunk 大小设置在 600 到 800 token 之间overlap 设置为 100 到 150 token。这个区间在主流向量数据库里都能跑而且对中文文档的语义完整性相对友好。另外一个细节是Obsidian 里常见的双链笔记通常都很短几百字一篇。这种短笔记就别切了直接整篇作为一个 chunk因为短笔记语义高度集中拆开反而丢失上下文。3. 向量化与检索链路从文本到可用的 AI 知识库3.1 嵌入Embedding是什么以及为什么它决定知识库的上限聊到知识库离不开“向量化”这个词。我尽量用最直白的方式讲清楚嵌入Embedding就是把一段文字变成一个一串数字组成的向量让语义相近的内容在向量空间里靠得近。比如“分布式系统”和“微服务架构”的向量距离会很近而和技术无关的“番茄炒蛋”就会离得很远。知识库做问答时其实不是你“问”AI而是把你问的问题也变成一个向量然后去向量库里找最接近的片段再把这些片段拼成上下文喂给大模型。嵌入模型选得怎么样直接决定了知识库的上限。这一步选不好后面所有优化都是白费劲。现在主流的选择有三类。第一类是云端 API比如 OpenAI 的text-embedding-3-small、国内的bge-m3系列 API、智谱的 embedding 接口。优点是质量高、不用自己维护模型缺点是收费、有网络依赖。第二类是本地开源模型比如bge-large-zh-v1.5、text2vec-large-chinese用Ollama就能跑。优点是免费、离线可用缺点是 embedding 模型也得吃显存512 维度的模型大概要 1 到 2GB 内存这还好但如果你跑的是更重的模型老机器会吃力。第三类是用大模型自带的 embedding 能力比如本地部署的Qwen-7B本身不做 embedding但你可以在它上面配一个专用的 embedding 微调模型。整体来说个人知识库阶段没必要上那套直接用bge-m3或 OpenAI embedding 就够了。3.2 向量数据库的选型不要一上来就上 Milvus跟嵌入模型配套的是向量数据库。很多文章一上来就推 Milvus说它能支持十亿级向量。我劝你别被带偏。个人 Obsidian 库撑死几万个 chunk用 Milvus 属于高射炮打蚊子运维复杂度反而高。根据我的经验Chroma 是最适合个人知识库起步的选择。它是纯 Python 实现直接 pip install 就能用数据存在本地 SQLite 里。几万个向量在它上面检索延迟是毫秒级完全够用。如果你的知识库将来要几个人协作访问可以换 Qdrant它提供了 Docker 部署方案接口也更规范方便以后迁移到生产环境。说实话个人场景下 Chroma 已经能优雅地撑很长时间了不需要一上来就考虑分布式。3.3 从向量到答案完整的 RAG 检索链路分块完、向量化完最后一步就是 RAG 检索。所谓 RAG就是“检索增强生成”。这步链路如果搭得自然你才会觉得 AI 真的是“懂”你的笔记库的。完整链路是我在代码里封装的一个函数:def ask_vault(question, k5): q_vec embed_model.encode(question) results chroma_collection.query(query_embeddings[q_vec], n_resultsk) context \n\n.join([doc[document] for doc in results[documents]]) prompt f基于以下笔记内容回答问题如果笔记中没有相关信息请直接说不知道。 笔记内容 {context} 问题{question} response llm.chat(prompt) return response关键就在k5这个参数。它决定了每次问答会从知识库里捞出多少个片段。太小了上下文不足太大了噪音会淹没真实答案。我一般在 5 到 8 之间调。另外context拼接时用两个换行分隔是为了避免大模型把一些碎片连读产生幻觉。这整个流程其实可以用 Dify 这种成熟的工具一键跑通自己写一遍代码主要是为了理解原理。后面我会专门讲一下用 Dify 搭流水线的路线两条路径各有利弊。4. 选一条适合自己的搭建路线本地部署 vs 云端工具链4.1 手搓一套方案脚本 Ollama Chroma自己写脚本的路线其实非常适合 Obsidian 用户因为 Obsidian 本身就是一套纯本地工具你的笔记默认存放在本地文件夹不需要额外的同步逻辑。具体流程是先用前面提到的扫描清洗脚本把整个库通过smart_chunk()切块然后调用本地 Ollama 里的 embedding 模型生成向量写入 Chroma。答案生成可以继续用 Ollama 跑一个对话模型也可以外接云端 API。这里分享一个我用得比较顺手的组合本地跑ollama pull bge-m3做 embedding。本地跑ollama pull qwen2.5:14b做对话生成。这个模型对中文的支持非常好生成质量比 7B 强了一个档次显存占用大概 10GB如果你只有 8G 显存可以用qwen2.5:7b代替。Chroma 存向量直接用chromadb0.4.x版本配合langchain的Chroma封装写代码。整个链路用 Python 写起来大概 200 行左右。好处是完全离线、隐私无忧Obsidian 本身就是一个重隐私的工具这算是精神上的匹配。4.2 用 Dify 搭知识库流水线傻瓜式但又不失灵活如果你不想写代码还有一个更省力的方案用 Dify 搭建知识库流水线。Dify 是一个开源的大模型应用开发平台里面专门做了知识库功能。你只需要把清洗后的 Markdown 文件上传Dify 会自动完成分段、向量化、索引入库然后在应用里配置一个“知识库检索 大模型回答”的工作流就能直接对话了。用 Dify 有几个很明显的优势。第一它内置了分段和索引策略你不用自己调参数。第二它支持混合检索也就是向量检索加全文检索。全文检索能解决向量检索常见的“关键词完全匹配但向量距离远”的问题比如你笔记里写的是“Redis”问的是“缓存数据库”向量检索能找到近似语义但全文检索能帮你精确定位到字面匹配的片段。两者结合召回率会高很多。第三Dify 提供了可视化的工作流编排界面你可以在知识库检索后面接一个“重排”Rerank步骤进一步筛掉不相关的片段。我自己实测下来Dify 的分段质量已经比很多手搓方案好很多。它有一个“文档分段”配置支持按标题标记、按段落切分还支持分段长度和重叠长度设置。上传一批 Markdown 后你可以直接在界面上预览切分结果所见即所得比调试 Python 脚本直观得多。4.3 Obsidian Git 和插件生态的配合不管走哪条路线有一点必须重视知识库更新之后向量库必须同步更新否则 AI 回答的还是旧知识。Obsidian 场景下我的同步方案是双保险。第一层保险是 Obsidian Git 插件我设置了每次文件变更后自动 commit 并 push 到远程仓库。这样知识库本身永远有一个可恢复的历史版本。第二层保险是我写了一个定时脚本每天凌晨扫描一次整个库对比文件的修改时间只对变动过的文件重新分块和向量化删除旧的向量记录写入新的。这样增量更新的成本非常低不至于每次全量重跑一遍。增量更新大概的核心逻辑是这样for file_path in changed_files: delete_vectors_by_file(file_path) chunks process_single_file(file_path) add_vectors_to_chroma(chunks)这两层配合下来基本能做到 Obsidian 里改了笔记第二天 AI 就能用上新内容。另外Obsidian 自身的插件生态里还有个 Dataview它可以把笔记的 YAML frontmatter 按条件汇总生成表格相当于一种弱结构化的元数据视图。我在清洗阶段会顺手把每条笔记的标题、标签、创建时间等元数据提取出来存成一个 JSON 文件跟着向量一起入库。这样后续可以对知识库做按标签过滤、按时间过滤的精确检索比纯靠向量相似度靠谱多了。5. 离线部署踩过的坑embedding 模型、编码、显存管理5.1 encoding 与文件读取的隐性坑Obsidian 默认保存的中文内容有些是 UTF-8 无 BOM有些老用户可能开了 GBK 而不自知。Python 读文件如果用默认编码会在中文笔记上直接抛UnicodeDecodeError或者读出一堆乱码。我的建议是读取文件时统一指定with open(file_path, r, encodingutf-8, errorsreplace) as f: content f.read()errorsreplace很关键它保证即使某篇文章有零星几个非法字符也不会导致整个文件读取失败而是用\ufffd替代掉。否则你动不动就会遇到一次任务中断。5.2 本地 embedding 模型的显存与速度权衡本地跑 embedding 模型看着轻巧实际跑起来还是有一些性能细节。bge-m3的模型文件大概 2.3GB用 CPU 跑的话几百个 chunk 的向量化可能要花几分钟。如果你的库有几千个 chunk整个过程会比较磨人。我的做法是把OLLAMA_NUM_CTX调大一些让一次性处理的文本更长减少模型加载次数然后首次全量向量化时找个空闲时段跑完。后续增量更新每次只处理几个文件CPU 模式下也就是几秒钟的事。如果你的机器有 N 卡记得把 Ollama 的gpu开起来向量化速度能快十倍以上。但如果你的显存只有 4GB同时跑 embedding 模型和对话模型会爆显存只能二选一。这时候可以把 embedding 模型放在 CPU 上跑。ollama pull bge-m3之后在代码里用ollama.embed时它会自动选择设备CPU 模式下也就慢一点不影响结果质量。5.3 聊一聊我踩过的最亏的一个坑没有提前清洗数据这是我最后想重点强调的一点。最初我把一个两千多篇笔记的库直接向量化检索的时候发现AI 经常答非所问。查了很久才发现我的 Obsidian 库里有大量自动生成的 MOC 索引笔记这些笔记内容就是一堆[[链接]]的罗列本身没有实质知识。清洗脚本确实会保留链接中的笔记名但 MOC 里成百上千个笔记名组合成的文本在向量检索时会形成“磁铁效应”把其他笔记的向量吸引过来导致上下文严重污染。后来我自己加了一道过滤如果一篇笔记经过清理后有效内容低于 200 个字符就整篇跳过不进知识库。MOC、索引类笔记如果没有补充额外注释就会被自动忽略。这个过滤规则很简单但让检索准确率提升非常明显。所以如果你的库很大建议先做一轮“内容质量预筛”再谈向量化和检索。6. 实战用知识库做的事和想象中不太一样整个链路搭好之后我服务了自己的 Obsidian 库一段时间有些体会还挺反直觉的。第一知识库最擅长的事不是“答疑”而是“找角度”。你问它“我研究过哪些跟缓存有关的内容”它能给你列出一堆从 Redis 到浏览器缓存再到分布式缓存的一致性笔记。这种能力不是靠大模型的记忆力而是靠向量检索帮你把散落各处的相关片段捞出来。同样的问题如果你在 Obsidian 里搜索“缓存”你可能只会搜到标题里带缓存的笔记但语义相似的“写入放大”“Cache Aside”这些概念你可能就错过了。第二知识库的“导入清洗”远比“模型选型”重要。一个干净的知识库配一个中档模型效果永远好于一个脏乱库配最好的模型。原因很简单RAG 的上限不取决于生成模型的聪明程度而取决于检索到的内容有多相关。如果你的库被无效信息淹没再强的模型也只能基于噪音内容作答效果当然差。所以我常跟人说搭知识库的功夫70% 花在清洗上这才是一本万利的事。第三Markdown 结构的保留是个双刃剑。我一开始把标题全删了只留正文结果 AI 回答时经常“断章取义”。后来我把标题作为 chunk 的前缀强制拼上比如把## Redis 持久化这个标题跟下面的正文合并成一个 chunk检索命中率有明显提升。大模型在看到标题和正文的完整组合时对语义的理解会好很多。这些经验放在这里大家搭库的时候可以少走一些弯路。至于要不要追求“一次性读取整个 Obsidian”我的看法是技术上完全可行但前提是你要先想清楚“读取”之后要拿来做什么然后再决定清洗和切分的颗粒度。方向不对跑得再快也是白费功夫。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。