摘要:于是我给自己定了个五一假期的目标:做一个能把自己的私有笔记喂给 AI 助手的 RAG 系统。发布到 PyPI——踩过的坑
license = {text = "MIT"} 是新版 setuptools 不支持的旧写法,必须改成 SPDX 表达式 license = "MIT",同时删除旧的 License :: OSI Approved :: MIT License classifier——两者共存直接报 InvalidConfigError
torch/torchvision 版本不匹配——sentence-:Markdown + 纯文本(后续可扩展 PDF、DOCX)
从零到发布,五一假期 3 天搞定。
一句话:pip install 一个包,你的 Markdown 笔记就能被 AI 助手自动检索并引用。
我平时在本地写了大量 Markdown 笔记——前端面试题、CSS 知识、Python 性能优化、学习计划……但每次和 AI 助手(Claude Code)对话时,它根本不知道我有这些笔记。我想问"我笔记里的 CSS 第一题是什么",它只能瞎猜。
于是我给自己定了个五一假期的目标:做一个能把自己的私有笔记喂给 AI 助手的 RAG 系统。而且要简单——装个包、配一行配置就能用。
整个系统的核心链路只有 7 步:
本地 .md/.txt 文件 → Markdown 解析器(markdown-it-py) → 按标题分块(Chunker) → 文本向量化(BGE-small-zh-v1.5) → 存入向量数据库(LanceDB) → 封装成 MCP 服务(FastMCP) → Claude Code 自动调用 → 检索结果注入上下文,生成回答
其中,MCP(Model Context Protocol)是 AI 助手和外部工具之间的标准通信协议——想象一下,它就像 USB 接口,你的工具只要实现这个协议,任何 AI 助手都能"即插即用"。这就是为什么 Claude Code、Cursor 都能直接调用我的知识库。
这些选型有一个共同的考量:零外部依赖部署。除了 Python 本身,用户不需要装 Docker、不需要起数据库、不需要配云服务。
最初我用 markdown-it-py 的 SyntaxTreeNode 构建文档树,调试了好久一直返回 token=None。后来才搞清楚:markdown-it-py 的树形节点会在非叶子节点上把 token 设为 None——所以你直接读 token 属性就崩了。
SyntaxTreeNode
token=None
解决方案是放弃高层 API,直接遍历底层 Token Stream:
# 不靠谱的高层 API tokens = md.parse(content) node = SyntaxTreeNode(tokens) # parent nodes 的 .token 是 None! # 靠谱的底层实现 for token in md.parse(content): if token.type == "heading_open": # 手动追踪标题层级,构建 Section 树
踩过这个坑之后,"别信框架给你包装好的结构"成了我写解析器的一条原则。
分块的核心矛盾:块太小则丢失上下文,块太大则检索精度下降。
我的策略:
(\
# 核心逻辑 if len(section_content) <= 1000: chunks.append(section_content) # 整个 Section 作为一个 chunk else: # 按段落切分,但代码块保持完整 parts = re.split(r'(\`\`\`[\s\S]*?\`\`\`)', section_content) for part in parts: if part.startswith('```'): chunks.append(part) # 代码块原封不动 else: # 长文本按段落补充切分
BGE 模型有一个设计细节很多人不知道:文档向量和查询向量应该用不同的方式生成。
model.encode(text)
"为这个句子生成表示以用于检索相关文章:"
这个前缀告诉 BGE 模型"接下来的是查询,不是文档",能显著提升中文检索质量。
增量索引的实现很简单——算文件 SHA256,跟库里存的 content_hash 比对,一样就跳过:
content_hash
file_hash = hashlib.sha256(file_bytes).hexdigest() if db.has_hash(file_hash): return "skipped" # 文件没变,跳过
我注册了 5 个 Tool:
knowledge_search
knowledge_index
knowledge_list
knowledge_remove
knowledge_stats
标签系统是 FastMCP 内置的——readOnlyHint 告诉 AI "这个操作是安全的";destructiveHint 告诉 AI "这个会改数据"。Claude Code 会根据这些标签决定要不要弹出确认框。
readOnlyHint
destructiveHint
一个 MCP Server 注册多个 Tool 的概念,类似于一个 HTTP 服务暴露多个 API 端点——启动的是同一个进程,通过同一根 stdio 管道通信,但 5 个 Tool 各自独立可调用。
这是上线后遇到的一个有意思的问题。我在新环境装好包后,问了一个知识库相关的问题,Claude Code 直接用自己的训练数据回答了——完全没有调用 knowledge_search。
原因有二:
解决方案是创建一个 CLAUDE.md:
当用户询问与个人笔记、学习记录相关的问题时: 1. 优先使用 knowledge_search 检索知识库 2. 即使用户提到了具体文件名,也先用 knowledge_search 搜索
这个规则文件是给 Claude Code 看的"行为指南",告诉它遇到哪类问题该走知识库路线。
license = {text = "MIT"}
license = "MIT"
License :: OSI Approved :: MIT License
InvalidConfigError
torch/torchvision 版本不匹配——sentence-transformers 会拉 torch 2.11.0,但旧版 torchvision 0.22.1 不兼容,报 RuntimeError: operator torchvision::nms does not exist。pip install torch torchvision --force-reinstall 解决
sentence-transformers
RuntimeError: operator torchvision::nms does not exist
pip install torch torchvision --force-reinstall
包名和模块名是两回事——改 pyproject.toml 的 name 字段改了 pip install 的名字,但 Python 模块名永远是 src/ 下的目录名 knowledge_mcp
pyproject.toml
name
pip install
src/
knowledge_mcp
pip install lxd-knowledge-test-mcp
从零到发布,五一假期 3 天搞定。核心体感是:MCP 协议把"AI 调用工具"这件事做得极其丝滑——你写完 Tool 的定义,AI 就能自动发现并调用,不用写任何对接代码。
暂无回复,快来抢沙发吧!
本次需消耗银元:
100
当前账户余额: 0 银元
license = {text = "MIT"} 是新版 setuptools 不支持的旧写法,必须改成 SPDX 表达式 license = "MIT",同时删除旧的 License :: OSI Approved :: MIT License classifier——两者共存直接报 InvalidConfigError
torch/torchvision 版本不匹配——sentence-:Markdown + 纯文本(后续可扩展 PDF、DOCX)
从零到发布,五一假期 3 天搞定。
五一花了 1 天,我把个人知识库做成了可"对话检索"的 MCP 服务
起因
我平时在本地写了大量 Markdown 笔记——前端面试题、CSS 知识、Python 性能优化、学习计划……但每次和 AI 助手(Claude Code)对话时,它根本不知道我有这些笔记。我想问"我笔记里的 CSS 第一题是什么",它只能瞎猜。
于是我给自己定了个五一假期的目标:做一个能把自己的私有笔记喂给 AI 助手的 RAG 系统。而且要简单——装个包、配一行配置就能用。
总体设计思路
整个系统的核心链路只有 7 步:
其中,MCP(Model Context Protocol)是 AI 助手和外部工具之间的标准通信协议——想象一下,它就像 USB 接口,你的工具只要实现这个协议,任何 AI 助手都能"即插即用"。这就是为什么 Claude Code、Cursor 都能直接调用我的知识库。
为什么选这些技术
这些选型有一个共同的考量:零外部依赖部署。除了 Python 本身,用户不需要装 Docker、不需要起数据库、不需要配云服务。
把一篇 Markdown 变成可检索的知识——核心实现
1. 解析(你猜我绕过了哪个坑)
最初我用 markdown-it-py 的
SyntaxTreeNode构建文档树,调试了好久一直返回token=None。后来才搞清楚:markdown-it-py 的树形节点会在非叶子节点上把 token 设为 None——所以你直接读 token 属性就崩了。解决方案是放弃高层 API,直接遍历底层 Token Stream:
# 不靠谱的高层 API tokens = md.parse(content) node = SyntaxTreeNode(tokens) # parent nodes 的 .token 是 None! # 靠谱的底层实现 for token in md.parse(content): if token.type == "heading_open": # 手动追踪标题层级,构建 Section 树踩过这个坑之后,"别信框架给你包装好的结构"成了我写解析器的一条原则。
2. 分块(别把代码块切碎了)
分块的核心矛盾:块太小则丢失上下文,块太大则检索精度下降。
我的策略:
(\``[\s\S]*?```)` 识别代码块,分块时绕开它们,绝不切割# 核心逻辑 if len(section_content) <= 1000: chunks.append(section_content) # 整个 Section 作为一个 chunk else: # 按段落切分,但代码块保持完整 parts = re.split(r'(\`\`\`[\s\S]*?\`\`\`)', section_content) for part in parts: if part.startswith('```'): chunks.append(part) # 代码块原封不动 else: # 长文本按段落补充切分3. 向量化与增量索引
BGE 模型有一个设计细节很多人不知道:文档向量和查询向量应该用不同的方式生成。
model.encode(text)"为这个句子生成表示以用于检索相关文章:"再编码这个前缀告诉 BGE 模型"接下来的是查询,不是文档",能显著提升中文检索质量。
增量索引的实现很简单——算文件 SHA256,跟库里存的
content_hash比对,一样就跳过:file_hash = hashlib.sha256(file_bytes).hexdigest() if db.has_hash(file_hash): return "skipped" # 文件没变,跳过MCP 协议——让 AI 助手"看懂"你的工具
我注册了 5 个 Tool:
knowledge_searchknowledge_indexknowledge_listknowledge_removeknowledge_stats标签系统是 FastMCP 内置的——
readOnlyHint告诉 AI "这个操作是安全的";destructiveHint告诉 AI "这个会改数据"。Claude Code 会根据这些标签决定要不要弹出确认框。一个 MCP Server 注册多个 Tool 的概念,类似于一个 HTTP 服务暴露多个 API 端点——启动的是同一个进程,通过同一根 stdio 管道通信,但 5 个 Tool 各自独立可调用。
为什么装好了 MCP,问问题却不自动检索?
这是上线后遇到的一个有意思的问题。我在新环境装好包后,问了一个知识库相关的问题,Claude Code 直接用自己的训练数据回答了——完全没有调用
knowledge_search。原因有二:
解决方案是创建一个 CLAUDE.md:
当用户询问与个人笔记、学习记录相关的问题时: 1. 优先使用 knowledge_search 检索知识库 2. 即使用户提到了具体文件名,也先用 knowledge_search 搜索这个规则文件是给 Claude Code 看的"行为指南",告诉它遇到哪类问题该走知识库路线。
发布到 PyPI——踩过的坑
license = {text = "MIT"}是新版 setuptools 不支持的旧写法,必须改成 SPDX 表达式license = "MIT",同时删除旧的License :: OSI Approved :: MIT Licenseclassifier——两者共存直接报InvalidConfigErrortorch/torchvision 版本不匹配——
sentence-transformers会拉 torch 2.11.0,但旧版 torchvision 0.22.1 不兼容,报RuntimeError: operator torchvision::nms does not exist。pip install torch torchvision --force-reinstall解决包名和模块名是两回事——改
pyproject.toml的name字段改了pip install的名字,但 Python 模块名永远是src/下的目录名knowledge_mcp成果
pip install lxd-knowledge-test-mcp从零到发布,五一假期 3 天搞定。核心体感是:MCP 协议把"AI 调用工具"这件事做得极其丝滑——你写完 Tool 的定义,AI 就能自动发现并调用,不用写任何对接代码。
后续方向