用 AI 给自己搭建“第二大脑”,终于不再读完就忘

0 阅读8分钟

读完文章转眼就忘,笔记散落各处找不到——这是大多数程序员的痛点。本文记录我如何用本地大模型+向量数据库搭建个人知识库,三个月后检索效率提升明显,信息焦虑也缓解了不少。

image_5379bd11.png

我受够了“松鼠症”式的学习

过去五年,我收藏了 3000 多篇文章,记了 2000 多条笔记,散落在各种云笔记、浏览器书签、甚至微信“文件传输助手”里。每次想找一个半年前看过的技术方案,翻箱倒柜十几分钟,最后往往还是重新 Google。

更让我焦虑的是,明明花了很多时间学习,但被问到“你了解 XXX 吗”的时候,脑子里只有模糊的印象,说不清原理,更别提落地经验。这种感觉就像往一个漏水的桶里灌水——灌得越多,挫败感越强。

今年初,我决定用 AI 工具给自己搭一个真正的“第二大脑”。三个月的打磨迭代,现在这个系统已经成了我日常开发中最依赖的工具。这篇文章是完整的搭建记录,包含工具选型、配置步骤、踩坑复盘,以及心态上的变化。


整体架构:所有数据留在本地,AI 只做理解和检索

我的核心诉求有两个:数据隐私(笔记里可能有公司项目的代码片段)和检索精准(不能比 Ctrl+F 更差)。

最终选型:

组件选型理由
笔记仓库Obsidian(本地 Markdown)纯文本,Git 版本控制,无平台锁定
嵌入模型BGE-M3(通过 Ollama 本地运行)中文+代码混合场景表现好,免费
向量数据库ChromaPython 一键启动,零配置
检索与对话本地脚本 + Claude Sonnet 4 API只传检索到的摘要,原文不出本地
前端命令行 REPL程序员不需要花里胡哨的界面

为什么不直接用 ChatGPT 或 Claude 的 Projects 功能?因为它们会存储我的数据到云端。而本地嵌入+云端推理的组合,既能保护隐私,又能用上最强的模型——检索出来的只是文本片段摘要,不包含完整原文。


搭建步骤:分四步,每一步都有坑

第一步:笔记清洗与分块

Obsidian 仓库里 2000 多个 .md 文件,混杂着技术笔记、读书摘抄、个人碎碎念、甚至购物清单。直接全部灌进向量库,检索质量会一塌糊涂。

我的处理脚本(Python 核心逻辑):

import os
import re
from pathlib import Path

def clean_and_chunk(md_content: str, file_path: str) -> list[dict]:
    """清洗 Markdown 并按标题分块"""
    # 去除图片链接
    content = re.sub(r'!\[.*?\]\(.*?\)', '', md_content)
    # 去除 HTML 标签
    content = re.sub(r'<[^>]+>', '', content)
    # 去除 front matter
    content = re.sub(r'^---.*?---', '', content, flags=re.DOTALL)
    
    chunks = []
    # 按 ## 标题拆分
    sections = re.split(r'\n(?=## )', content)
    
    for i, section in enumerate(sections):
        # 跳过太短的片段
        if len(section) < 100:
            continue
        # 生成唯一 ID
        chunk_id = f"{file_path}#chunk-{i}"
        chunks.append({
            "id": chunk_id,
            "content": section.strip(),
            "source": file_path
        })
    
    return chunks

踩的坑:一开始用固定 500 字符切分,把大量代码块拦腰截断,导致嵌入质量极差。后来改为按标题切分,代码块保持完整,检索准度立刻上了一个台阶。


第二步:生成嵌入并存入 Chroma

BGE-M3 模型通过 Ollama 本地运行,一张 RTX 4060 就够用。

import chromadb
import ollama

# 初始化 Chroma
client = chromadb.PersistentClient(path="./my-knowledge-base")
collection = client.get_or_create_collection("tech-notes")

def embed_and_store(chunks: list[dict]):
    """批量生成嵌入并存入 Chroma"""
    for chunk in chunks:
        # 调用 Ollama 本地生成嵌入
        response = ollama.embeddings(
            model="bge-m3",
            prompt=chunk["content"]
        )
        embedding = response["embedding"]
        
        # 存入 Chroma
        collection.add(
            ids=[chunk["id"]],
            embeddings=[embedding],
            metadatas=[{"source": chunk["source"], "content": chunk["content"]}],
            documents=[chunk["content"]]
        )

2000 多个文本块,嵌入生成大约跑了 15 分钟。之后就可以用来检索了。


第三步:构建检索+重排序流水线

直接用向量相似度召回 Top 10,再交给 Claude 重排序,挑出最相关的 3 条。

def search(query: str, top_k: int = 3) -> list[dict]:
    """检索+重排序"""
    # 1. 向量检索
    query_emb = ollama.embeddings(model="bge-m3", prompt=query)["embedding"]
    results = collection.query(
        query_embeddings=[query_emb],
        n_results=10
    )
    
    # 2. 用 Claude 重排序
    candidates = results["metadatas"][0]
    rerank_prompt = f"""从以下 10 个片段中,选出与问题最相关且能回答的 3 个。
只返回选中的片段 ID,逗号分隔,不要解释。

问题:{query}

片段列表:
"""
    for i, c in enumerate(candidates):
        rerank_prompt += f"\n[{i}] 来源: {c['source']}\n内容: {c['content'][:300]}...\n"
    
    # 调用 Claude(省略具体 API 代码,参考前文)
    selected_ids = call_claude(rerank_prompt)
    
    # 3. 返回最终结果
    final_results = []
    for idx in selected_ids:
        meta = candidates[idx]
        final_results.append({
            "source": meta["source"],
            "content": meta["content"]
        })
    return final_results

为什么加重排序? 纯向量检索对“语义相似但答非所问”的内容误召回率很高。加上 Claude 重排序后,命中率提升了大约 35%,而且每次重排序消耗不到 0.01 美元。


第四步:搭一个命令行对话界面

我用 Python 的 argparse + rich 库写了个简单的终端 REPL,支持:

  • search <关键词>:检索笔记
  • ask <问题>:基于检索结果生成回答
  • recent:查看最近添加的笔记
  • stats:查看知识库统计

效果:

> ask Go context 超时控制的最佳实践

📖 基于你的笔记,找到 3 条相关内容:

1. [Go/并发/context包避坑.md]
   context.WithTimeout 的坑:如果父 context 已经超时,
   子 context 会立即收到取消信号。建议在 RPC 调用时
   单独设置 timeout,不要复用上层 context 的 deadline。

2. [踩坑记录/2025-06-微服务超时排查.md]
   那次支付回调丢失的原因:上游设置了 3s 超时,但我们
   的签名验证需要 2.8s,网络抖动直接超时。后来把验证
   改为异步,先回 200 再处理。

3. [Go/并发/信号处理.md]
   优雅关闭时的超时处理:用 select + time.After 设置
   最大等待时间,配合 sync.WaitGroup 等待协程退出。

💡 建议:根据你 20256 月踩过的坑,关键点是"不要复用
上层 context 的 deadline,为每个外部调用单独设置 timeout"。

这一刻,我觉得所有搭建成本都值了。 那些曾经花费数小时排查后写下的笔记,不再躺在文件夹里吃灰,而是在我再次面临同样问题时主动跳出来。


三个月后的真实变化

数据层面

指标搭建前搭建后
找到目标笔记的时间平均 8 分钟平均 15 秒
笔记复用率<5%(几乎不复习)>40%(被动触达)
写技术文章时引用旧笔记靠记忆翻找直接检索引用
碎片信息丢失率高(忘了就真忘了)低(只要记过就能找到)

心理层面

比数据更重要的是心理状态的变化。

以前看到一篇好文章,我会焦虑:“又要花时间看完,看完也记不住,不看完又觉得亏了。” 现在我的心态变成了:“看过、标注过、索引过,就已经是我的了。需要的时候能找回来就行。”

知识库的意义不是让你记住一切,而是让你放心地忘记。 当你知道任何曾经学过的内容都能在 15 秒内找回时,信息焦虑会自然消退,学习也从“囤积”回归到了“理解”。


几个你可能需要的提醒

1. 不用追求全自动

我一开始试图写个文件监控脚本,自动增量索引。结果 Obsidian 的自动保存和频繁切换文件,产生了大量脏数据。手动触发索引,每次只索引加了 #published 标签的笔记,效果反而更好。

2. 代码块保留原样,不要摘要化

嵌入模型对代码的理解已经很强了,不要在索引前把代码“翻译”成自然语言——那样反而丢失细节。BGE-M3 对代码段的语义理解足够支撑检索。

3. 先跑通 MVP,再考虑优化

先用命令行玩起来,不要一上来就搭 Web UI。程序员用命令行的效率远高于点鼠标。等确定这套流程真的适合你,再考虑把它做成浏览器插件或 VS Code 扩展。

4. 这不能替代“理解”,只能辅助“检索”

AI 知识库帮你找到碎片,但真正的理解仍然需要你自己花时间去消化。它是外挂,不是替代品。把检索当成“快速回忆起自己曾经学过的内容”,而不是“AI 帮你学习新东西”。


可以抄的作业

如果你也想搭一套,最小启动成本是:

  1. 装好 Ollama,拉取 bge-m3 模型
  2. pip install chromadb ollama rich
  3. 写一个不到 200 行的 Python 脚本(核心逻辑都在这篇文章里了)
  4. 选一个周末的下午,把最有价值的笔记跑一遍索引
  5. 在终端里打出第一句 ask 命令

然后,你会发现自己再也回不到靠文件夹和文件名搜索笔记的日子。


你们有没有搭建过自己的知识库?用的什么方案?或者有哪些笔记管理的独门技巧?欢迎评论区讨论。