gte-base-zh构建个人知识库:联动Typora管理Markdown笔记

张开发
2026/4/11 8:19:24 15 分钟阅读

分享文章

gte-base-zh构建个人知识库:联动Typora管理Markdown笔记
gte-base-zh构建个人知识库联动Typora管理Markdown笔记你是不是也有过这样的经历电脑里存了几百篇用Typora写的Markdown笔记从技术心得、读书摘要到项目复盘什么都有。想找某个知识点时却只能对着文件夹列表发呆要么靠模糊的记忆搜索文件名要么就得一篇篇点开看效率低得让人抓狂。我之前也这样直到我开始琢磨能不能让这些笔记“活”起来能不能像问一个助手一样用自然语言直接问“我去年写的关于Python异步编程的笔记里是怎么处理异常重试的”然后它就能立刻给我找出来。今天要聊的就是这么一个我自己折腾出来的方案。它不复杂核心就是用开源的gte-base-zh模型把Typora写的Markdown笔记变成机器能理解的“向量”存起来。以后你想找什么直接问就行它会从你的笔记海洋里把最相关的那几篇捞出来给你看。1. 为什么需要个人知识库以及为什么是现在我们积累笔记本质上是在构建自己的“第二大脑”。但传统文件夹式的管理让这个大脑变成了一个杂乱无章的仓库存进去容易取出来难。知识没有被连接价值就大打折扣。最近几年基于向量检索的智能知识库技术成熟了而且门槛大大降低。像gte-base-zh这样的中文文本表示模型效果不错还能在普通电脑上跑起来。Typora又是大家写Markdown的利器格式干净。把它们俩结合起来给自己搭一个私人的、智能的笔记检索系统这件事就变得非常可行了。这个方案能帮你解决几个实实在在的痛点告别“记得有但找不到”不用再精确回忆文件名或关键词。实现“模糊查找”即使你的问题和笔记里的原话不完全一样基于语义相似度也能找到。深度关联碎片知识你可能在不同笔记里零散地记录了同一个主题的内容系统能帮你一起找出来拼凑出更完整的图景。完全私有化所有数据都在自己电脑上不用担心隐私泄露。2. 方案核心gte-base-zh模型与向量检索先简单说说核心是怎么工作的不用担心我用大白话解释。gte-base-zh模型是干什么的你可以把它理解成一个“文本理解器”。它读一段中文文本比如你的一篇笔记然后把它转换成一串长长的数字比如768个这串数字就叫“向量”或“嵌入”。关键的是如果两段话意思相近它们转换出来的两串数字也会很“像”在数学上距离很近。如果意思不相关数字串就“不像”距离远。向量检索又是怎么回事就是把所有笔记都用这个模型变成一串串数字然后存到一个专门的数据库向量数据库里。当你想提问时你的问题也会被模型变成一串数字。接着数据库会飞快地计算你的问题数字和所有笔记数字之间的“像不像”程度相似度然后把最“像”的、也就是最相关的几篇笔记找出来还给你。整个过程可以概括为三步笔记向量化 - 存入向量库 - 提问并检索。我们接下来要做的所有事情都是围绕这三步展开的。3. 动手搭建从Typora笔记到可检索知识库下面我们一步步来把想法变成现实。你需要准备一个Python环境建议3.8以上以及安装一些必要的库。3.1 第一步环境与模型准备首先安装核心的库。打开你的命令行工具执行以下命令pip install sentence-transformers chromadbsentence-transformers一个很好用的库封装了包括gte-base-zh在内的众多文本表示模型调用起来特别简单。chromadb一个轻量级、易用的向量数据库可以持久化存储在本机。然后我们来准备模型。gte-base-zh模型可以从Hugging Face上下载。sentence-transformers库会自动处理下载。我们写一个简单的脚本来初始化它。创建一个Python文件比如叫knowledge_base_init.py写入以下代码from sentence_transformers import SentenceTransformer # 初始化模型这里指定使用gte-base-zh # 第一次运行会自动从网络下载模型文件请保持网络通畅 print(正在加载gte-base-zh模型首次使用下载时间可能较长...) model SentenceTransformer(thenlper/gte-base-zh) print(模型加载成功) # 简单测试一下模型是否工作 test_sentences [今天天气真好, 这是一个晴朗的日子] embeddings model.encode(test_sentences) print(f测试句子向量维度{embeddings.shape}) # 应该输出 (2, 768)运行这个脚本确保模型能成功加载。看到输出向量维度是(2, 768)就说明没问题了。3.2 第二步处理你的Typora笔记库假设你的所有Markdown笔记都放在一个目录下比如D:/MyNotes。我们需要遍历这个目录读取所有.md文件。Markdown文件里有标题、正文、代码块等各种元素。为了检索效果更好我们通常不会把整篇长文档直接扔进去而是进行“分块”。比如按二级标题切分或者按固定长度例如500字切分这样检索会更精准。我们来写一个处理笔记的函数创建新文件process_notes.pyimport os import re from pathlib import Path def read_markdown_files(notes_dir): 遍历目录读取所有Markdown文件内容 md_files [] for root, dirs, files in os.walk(notes_dir): for file in files: if file.endswith(.md): file_path Path(root) / file try: with open(file_path, r, encodingutf-8) as f: content f.read() # 这里可以获取文件的相对路径方便后续定位 rel_path os.path.relpath(file_path, notes_dir) md_files.append({ path: rel_path, content: content }) except Exception as e: print(f读取文件 {file_path} 时出错{e}) return md_files def split_content_by_heading(content, file_path): 一个简单的按二级标题##分割内容的分块函数 chunks [] # 使用正则表达式找到所有二级标题的位置 headings re.finditer(r^##\s(.)$, content, re.MULTILINE) last_pos 0 heading_texts [] for match in headings: start_pos match.start() if start_pos last_pos: # 截取上一个标题到当前标题之间的内容作为一个块 chunk_content content[last_pos:start_pos].strip() if chunk_content: # 确保内容不为空 # 记录这个块所属的源文件路径和最近的标题 chunk_source f{file_path} - { .join(heading_texts[-1:])} if heading_texts else file_path chunks.append({ text: chunk_content, source: chunk_source }) heading_texts.append(match.group(1).strip()) last_pos start_pos # 处理最后一个标题之后的内容 if last_pos len(content): chunk_content content[last_pos:].strip() if chunk_content: chunk_source f{file_path} - { .join(heading_texts[-1:])} if heading_texts else file_path chunks.append({ text: chunk_content, source: chunk_source }) # 如果没有任何二级标题就把整篇文档作为一个块可以进一步按段落或固定长度切分 if not chunks: # 这里可以添加更精细的无标题分块逻辑例如按段落或固定字符数分割 # 为了简单起见我们暂时将整篇文档作为一个块 if content.strip(): chunks.append({ text: content.strip(), source: file_path }) return chunks # 使用示例 if __name__ __main__: notes_directory D:/MyNotes # 请替换为你的笔记目录实际路径 all_files read_markdown_files(notes_directory) print(f共找到 {len(all_files)} 个Markdown文件。) all_chunks [] for file_info in all_files[:3]: # 先处理前3个文件作为演示 chunks split_content_by_heading(file_info[content], file_info[path]) print(f文件 {file_info[path]} 被分割为 {len(chunks)} 个块。) all_chunks.extend(chunks) print(f\n总共生成 {len(all_chunks)} 个文本块。)这个脚本提供了基础的读取和分块功能。你可以根据自己笔记的结构调整split_content_by_heading函数比如改成按三级标题、按固定长度例如用textwrap或按段落分块。3.3 第三步构建向量数据库并存入笔记现在我们有了文本块也有了模型接下来就是把它们变成向量存进ChromaDB。创建一个新文件build_vector_db.pyfrom sentence_transformers import SentenceTransformer import chromadb from chromadb.config import Settings from process_notes import read_markdown_files, split_content_by_heading # 导入上一步的函数 import hashlib # 1. 初始化模型和客户端 model SentenceTransformer(thenlper/gte-base-zh) # 指定持久化路径。chroma会在该目录下创建数据库文件。 chroma_client chromadb.PersistentClient(path./my_knowledge_db) # 2. 创建或获取一个集合Collection相当于一个表 collection_name my_notes try: collection chroma_client.get_collection(namecollection_name) print(f集合 {collection_name} 已存在将添加新数据。) except: collection chroma_client.create_collection(namecollection_name) print(f创建新集合 {collection_name}。) # 3. 处理笔记并添加到数据库 def add_notes_to_db(notes_dir): all_files read_markdown_files(notes_dir) all_chunks [] chunk_sources [] for file_info in all_files: chunks split_content_by_heading(file_info[content], file_info[path]) for chunk in chunks: all_chunks.append(chunk[text]) # 在元数据中存储来源信息方便我们后续定位原文 chunk_sources.append({source: chunk[source]}) if not all_chunks: print(未找到任何可处理的文本块。) return print(f正在为 {len(all_chunks)} 个文本块生成向量...) # 批量生成向量效率更高 embeddings model.encode(all_chunks, normalize_embeddingsTrue, show_progress_barTrue) # 为每个块生成一个唯一的ID这里用内容哈希 ids [hashlib.md5(chunk.encode(utf-8)).hexdigest() for chunk in all_chunks] # 添加到集合中 collection.add( embeddingsembeddings.tolist(), # ChromaDB接受列表形式的向量 documentsall_chunks, # 原始文本 metadataschunk_sources, # 来源信息 idsids # 唯一ID ) print(f成功将 {len(all_chunks)} 个文本块存入向量数据库。) if __name__ __main__: notes_directory D:/MyNotes # 请替换为你的笔记目录实际路径 add_notes_to_db(notes_directory)运行这个脚本它会遍历你的笔记目录分块、生成向量然后存储到本地的./my_knowledge_db文件夹中。第一次运行可能会花些时间取决于你的笔记数量和电脑性能。3.4 第四步实现自然语言查询数据库建好了最激动人心的部分来了怎么问它问题我们写一个查询函数。创建文件query_knowledge.pyfrom sentence_transformers import SentenceTransformer import chromadb from chromadb.config import Settings # 加载模型和数据库 model SentenceTransformer(thenlper/gte-base-zh) chroma_client chromadb.PersistentClient(path./my_knowledge_db) collection chroma_client.get_collection(namemy_notes) def ask_question(question, top_k5): 向知识库提问 :param question: 你的问题自然语言 :param top_k: 返回最相关的几条结果 # 将问题转换为向量 question_embedding model.encode([question], normalize_embeddingsTrue) # 在集合中查询最相似的文档 results collection.query( query_embeddingsquestion_embedding.tolist(), n_resultstop_k, include[documents, metadatas, distances] # 返回文档内容、元数据和相似度距离 ) if results and results[documents]: print(f\n问{question}) print(f\n找到了 {len(results[documents][0])} 条相关笔记\n) for i, (doc, meta, dist) in enumerate(zip(results[documents][0], results[metadatas][0], results[distances][0])): # 距离越小越相似我们将其转换为一个更易读的分数可选 score 1 - dist # 近似相似度分数仅作参考 print(f【结果 {i1} | 相关度{score:.3f}】) print(f来源{meta[source]}) print(f内容预览{doc[:200]}...) # 只打印前200字符预览 print(- * 50) else: print(未找到相关结果。) # 交互式提问 if __name__ __main__: print(个人知识库问答系统已启动输入 quit 退出) while True: user_input input(\n请输入你的问题).strip() if user_input.lower() in [quit, exit, q]: print(再见) break if user_input: ask_question(user_input)运行这个脚本你就可以开始用自然语言提问了。它会从你的笔记库里找出最相关的几个片段并告诉你是从哪篇笔记的哪个部分找到的。4. 实际应用与效果体验搭建好之后我用自己的技术笔记库试了试。我的笔记大概有300多篇涉及Python、后端架构、机器学习等多个主题。场景一查找具体技术点我问“Python中如何优雅地合并两个字典”系统返回了3篇相关笔记。一篇是专门讲字典操作的里面详细记录了{**d1, **d2}和d1.update(d2)等方法另一篇是在项目总结里提到了字典合并的坑。它甚至找到了一篇我在学习collections.ChainMap时的笔记虽然问题里没提这个但语义上高度相关。场景二模糊回忆与概念关联我问“我记过哪些关于‘缓存雪崩’的内容”结果把我分散在不同时间、不同项目复盘里的关于缓存雪崩、缓存击穿、缓存穿透的笔记都找出来了。有些笔记文件名根本没提“缓存”二字但内容相关也被检索到了。这帮我重新串联起了这个知识网络。场景三寻找特定项目的决策依据我问“我们当初为什么选择用RabbitMQ而不是Kafka”它从一篇名为“消息队列选型评估.md”的笔记中精准定位到了对比表格和分析结论部分。这比我去翻会议记录或者聊天记录快多了。效果上gte-base-zh模型对中文的语义理解确实不错大部分时候都能准确命中。检索速度也很快基本上是毫秒级响应。当然它也不是万能的对于特别口语化或者包含大量专有名词缩写的问题偶尔会有些偏差但这完全可以通过优化提问方式或者后续引入更专业的模型来改善。5. 后续优化与实践建议这个基础版本跑起来之后你可以根据自己的需求把它变得更好用自动化更新写了一个新笔记怎么自动加到知识库里可以写一个简单的监控脚本利用Typora的“保存”动作触发或者定时比如每天一次扫描笔记目录增量更新向量数据库。分块策略优化现在的按标题分块比较简单。对于很长的无标题文档可以尝试按固定token数比如500字重叠分块避免丢失上下文。langchain等框架里有现成的文本分割器可以用。前端界面总是用命令行提问不太方便。可以用Gradio或Streamlit快速搭一个简单的网页界面输入框在上面结果展示在下面体验更好。结合Typora生态更进一步甚至可以开发一个Typora插件在编辑器侧边栏直接集成查询功能一边写笔记一边查找关联的旧笔记体验更无缝。元数据增强除了内容存储笔记的创建时间、标签等信息查询时不仅可以按语义还可以按时间、标签过滤。用下来最大的感受是这个方案真正把沉淀的笔记价值释放出来了。它不是一个复杂的工程系统而是一个轻巧实用的个人工具。技术本身不是目的能高效地服务于自己的学习和工作才是关键。如果你也受困于日益膨胀的笔记库不妨花点时间试试这个方案亲手打造一个专属于你的智能知识助手。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。

更多文章