Skip to content
ChaixueyuanPublic

About

边做边学 RAG:本地个人笔记 RAG Agent(hybrid 检索 + rerank + chunk 级增量 + 分层 eval + Langfuse 可观测 + Electron 桌面 app)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

learn-rag

边做边学 RAG:一个本地运行的个人笔记 RAG Agent,从朴素向量检索一路迭代到 production-grade 完整链路 —— 每一步优化都有 eval 数据证明它值得做。

A hands-on project for learning production-grade RAG: a local AI agent that answers questions over your own markdown notes, built step by step in TypeScript.

把你的 markdown 笔记目录指给它,跑一次索引,然后用自然语言问它任何笔记里的问题 —— 它会综合多个来源给出统一回答,并告诉你答案来自哪里。提供 CLI(REPL) 和 Electron 桌面 app 两种形态。


它能做什么 ✨

  • 💬 多轮对话:REPL / 桌面 app,持续追问,上下文保留,超长对话自动压缩
  • 🔍 混合检索:bge-m3 向量召回 ⊕ BM25/FTS 全文召回 → RRF 融合 → cross-encoder 精排
  • 🧠 智能综合:Agent 不罗列原文,把多个 chunk 综合成自然回答,带引用溯源
  • ⚡ 流式输出:回答逐字蹦出来,支持 reasoning 思考链展示(桌面 app)
  • 🛠️ 4 个工具:list / read / grep / semantic_search,agent 自主选用
  • 📈 可量化:内置分层 eval(recall@5 / MRR),每个优化都能跑出前后对比
  • 📊 完整可观测:每次调用都在 Langfuse dashboard 看 trace + token 用量

为什么值得学习 📚

这个项目把 RAG 的"教科书优化"逐个真实落地,且都有实测数据:

能力 一句话 实测效果
Contextual Retrieval 每个 chunk 在 embed 前由 LLM 加 50-100 字上下文摘要 召回失败率显著下降(Anthropic 2024 方案)
Hybrid + RRF BM25 补向量对精确术语的盲区,RRF 只看排名绕开量纲 术语类查询 recall@5 0.2 → 0.6
Cross-encoder Rerank bge-reranker-v2-m3 精排,~100ms/次 比 LLM-as-reranker 快 600x
结构感知 chunking 按标题切 + 代码块原子保护 + 三级兜底 稳定 chunk 边界,衍生 chunk 级增量
chunk 级增量索引 稳定 chunk_id + 两级 diff,改一段只重做那段 改 3 文件 → 6 chunk / 4 秒(vs 全量 ~12 分钟)
分层 Eval 语义组 / 术语组分开测,LLM 反向生成考卷 不分层会掩盖 hybrid 的真实价值
失败处理框架 10 类错误分类 + 指数退避 + sanitize + 失败队列 3000+ chunks 实测打磨
意图分类看门人 规则 + LLM hint(非硬路由),闲聊不走 RAG 省钱省延迟

架构 🏗️

用户输入:"我学过哪些 RAG 优化?"
   ↓
[意图分类 intent.ts] 规则 + DeepSeek 判 4 类 → chitchat 直接答,其余注入 hint
   ↓
┌─────────────────────────────────────────────────────────────┐
│ Agent Loop (agent.ts) — 大脑,任意 OpenAI 协议 LLM            │
│ ├─ system prompt 从 Langfuse Prompt Management 动态拉取       │
│ └─ 决定调哪些工具                                              │
│    ┌──────────┬──────────┬──────────┬──────────────┐         │
│    │list_files│search_kb │read_file │semantic_search│         │
│    │ (列目录) │  (grep)  │ (读全文) │   (RAG)       │         │
│    └──────────┴──────────┴──────────┴───────┬──────┘         │
└─────────────────────────────────────────────┬───────────────┘
                                              ↓
                              ┌────────────────────────────────┐
                              │ semantic_search 内部(hybrid):  │
                              │  1. bge-m3 向量召回 top-20      │
                              │  2. FTS/BM25 召回 top-20(ngram)│
                              │  3. RRF 融合 → top-20           │
                              │  4. bge-reranker-v2-m3 → top-5  │
                              └────────────────────────────────┘
                                              ↓
                                       回答流式输出
                                              ↓
                              Langfuse trace 自动嵌套显示

技术栈 📦

层 选型 说明
语言 TypeScript + Node 20+ —
主对话 LLM 自配(任意 OpenAI 协议:Kimi / DeepSeek / GLM / Grok…) baseURL + model + key 三项配置
Contextual LLM(索引时) DeepSeek-chat 便宜 + 自动 Prompt Caching
Embedding bge-m3(SiliconFlow 云,默认;或 Ollama 本地) 1024 维,多语言,两家同模型可互换
Reranker bge-reranker-v2-m3 via SiliconFlow Cross-encoder,~100ms
Vector DB LanceDB(嵌入式) 无 Docker,数据存本地
Observability Langfuse v4(OpenTelemetry) Trace + Prompt Management
Agent 框架 自写 agent loop streaming + tool use + 上下文压缩,无框架依赖
桌面 app Electron + React 18 + Vite 主进程直接 import 现有 .ts,零重写

安装 🚀

0. 前置

  • Node.js 20+
  • 一个 markdown 笔记目录(Obsidian vault、普通 md 文件夹都行)

1. Clone + 装依赖

git clone https://github.com/Chaixueyuan/learn-rag.git
cd learn-rag
npm install

2. 准备 API keys(3 个必须 + 1 个自选)

用途 哪里申请 必须?
DeepSeek(Contextual 索引 + 意图分类) https://platform.deepseek.com/api_keys ✅
SiliconFlow(embedding + reranker) https://cloud.siliconflow.cn/account/ak ✅
Langfuse(observability,免费版够用) https://cloud.langfuse.com → Project → API Keys ✅
主对话 LLM(任意 OpenAI 协议供应商) 例如 Kimi:https://platform.moonshot.cn/console/api-keys ✅(选一家即可)

不想用云端 embedding?装 Ollama + ollama pull bge-m3,然后 .env 里 EMBED_PROVIDER=ollama。两家都是 bge-m3、1024 维,切换不用重建索引。

3. 配置 .env

cp .env.example .env
# 编辑 .env,填入 keys + 你的笔记目录

最小可用配置:

CHAT_BASE_URL=https://api.moonshot.cn/v1      # 主对话 LLM(任意 OpenAI 协议)
CHAT_MODEL=moonshot-v1-32k
CHAT_API_KEY=sk-...

DEEPSEEK_API_KEY=sk-...
SILICONFLOW_API_KEY=sk-...

LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_SECRET_KEY=sk-lf-...
LANGFUSE_BASE_URL=https://cloud.langfuse.com

KB_ROOT=/path/to/your/markdown/notes          # 你的笔记目录

4. 在 Langfuse 创建 system prompt

在 Langfuse → Prompts → New Prompt:

  • Name: kb-agent-system(agent 按这个名字拉取)
  • Type: text
  • Content: 你的 agent 人设 + 回答规则(例:「你是用户的个人知识库助手,基于检索到的笔记内容综合回答,标注来源,检索不到就明说」)
  • Labels: 加 production

Prompt 放 Langfuse 而不是硬编码,是为了体验 production 标配的 Prompt Management:UI 改 prompt、切版本,不用动代码。


用法 🎬

索引你的笔记

npm run index       # 首次全量(耗时/成本与笔记量成正比);之后增量,只处理改动
npm run build-fts   # 建 FTS 全文索引(hybrid 检索用,建一次)

对话

npm run chat

> 我学过哪些 RAG 优化?
🤖 (流式输出综合回答...)

> /exit   退出
> /reset  清空对话历史

桌面 app 🖥️

cd desktop
npm install
npm run dev

三栏布局:文件树 | 文档(读/写,自动保存 + 自动增量索引)| AI 助手(对话/搜索)。检索过程可视化:每条答案可展开 4 阶段检索详情(向量/FTS/RRF/rerank 双分数),引用点击跳转原文高亮。详见 desktop/README.md。

检索调试 / 质量评估

npm run search "RRF"            # 三路对比:纯向量 / FTS / RRF+rerank
npm run sample 10               # 抽 10 个 chunk 审查 Contextual 摘要质量
npm run intent "你好"            # 看意图分类走规则层还是 LLM 层
npm run gen-eval 15 semantic    # LLM 反向生成 eval 考卷(语义组:换说法)
npm run gen-eval 15 term        # 术语组:保留精确术语
npm run eval                    # 分层 eval:纯向量 vs hybrid(recall@5 + MRR)
npm run eval-langfuse           # eval 上报 Langfuse(Datasets + Experiments)
npm run eval-quality            # 答案质量 eval(需先跑 gen-eval term + eval-langfuse)
npm run retry-failed            # 修复索引失败的 chunks

Production 特性 ⭐

1. Contextual Retrieval

每个 chunk 在 embedding 前先让 LLM 生成 50-100 字"上下文摘要",拼接后再 embed(来源:Anthropic 2024):

// indexer.ts
const augmented = `[上下文]\n${context}\n\n[原文]\n${chunk}`;
const vec = await embed(augmented);

2. Hybrid 检索 + RRF

向量检索对"换说法"的语义查询强,但对精确术语(函数名、缩写)有盲区;BM25 恰好相反。RRF 融合只看排名不看分数,天然绕开两路分数量纲不可比的问题。分层 eval 实测:术语组 recall@5 从 0.2 → 0.6。

3. 失败处理 4 件套(errors.ts)

generateContext()
  → Pass 1: 正常调用(自动重试 rate_limit / 5xx)
  → Pass 2: encoding 错 → sanitize 后重试
  → Pass 3: context 超长 → 截短后重试
  → fail-fast: auth / balance 立刻退出
  → 其他失败:记录进 failed_chunks.json 降级,继续跑,事后 retry-failed

4. chunk 级增量索引

文件级 SHA256 判"哪些文件动了" → chunk 集合 diff 判"哪些段动了" → 按稳定 chunk_id 精准增删:

场景 代价
什么都没改再跑 秒级,0 成本
改 3 个文件里各一段 6 chunk / 4 秒
换 chunking 策略 自动判定全量重建(configHash 守卫)

5. Langfuse Observability + Prompt Management

  • 交互链路(对话/检索/意图)全量 trace,自动嵌套;离线批处理(索引/eval 生成)刻意不 trace,避免淹没面板
  • observeOpenAI() 自动捕获 model / tokens / latency
  • System prompt 在 Langfuse UI 动态改,不用发版

6. 分层 Eval

用 LLM 反向生成两组考卷(语义组=换说法问、术语组=保留精确术语),分别测纯向量 vs hybrid+rerank 的 recall@5 / MRR。不分层的平均数会掩盖真相:hybrid 对语义组几乎没提升甚至略降,对术语组是质变 —— 这正是它存在的理由。


Roadmap 📋

  • 意图分类 / Hybrid+RRF / 分层 Eval / Stable chunking / chunk 级增量 / 答案质量 eval
  • Electron 桌面 app:三栏布局、chat streaming + 工具可视化、检索详情 fold、引用跳转高亮、markdown 编辑 + 自动保存 + 自动增量、多会话持久化、wikilink/本地图片渲染、上下文压缩
  • chunking 边界:表格 / 数学公式 / 图片(难点清单见 CLAUDE.md)
  • 桌面 app 打包分发(electron-builder + LanceDB asarUnpack,见 APP_PLAN_v2.md §11)
  • MCP Server:把检索后端暴露给 Claude Code / 其他 agent 消费

License

MIT

About

边做边学 RAG:本地个人笔记 RAG Agent(hybrid 检索 + rerank + chunk 级增量 + 分层 eval + Langfuse 可观测 + Electron 桌面 app)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages