# M1.5 个人知识库（RAG 初体验）

> 目标：直观理解"RAG（检索增强生成）"是什么，并亲手做一个最小版。

## 1. 什么是 RAG

**RAG = Retrieval-Augmented Generation（检索增强生成）**。

普通大模型：只靠自己"背过的知识"回答，容易过时/瞎编。
RAG：先**去你的资料里检索相关片段** → 再把片段喂给模型 → 模型基于"真实资料"回答。

```
用户提问
   ↓
[检索] 在你的文档里找最相关的几段
   ↓
[拼接] 问题 + 检索到的片段 → 组成新提示词
   ↓
[生成] 大模型基于"真实资料"作答
```

核心价值：**让模型"只说你资料里有的事"，大幅减少幻觉。**

## 2. 把自己的文档变成 Agent 的"记忆"

在 Coze/Dify 里，知识库就是这么用的：
1. 上传 PDF/Word/网页。
2. 工具会自动把文档**切片**（切成小段）并建立**索引**。
3. 问答时，先检索再回答。

你不用懂底层，但要记住一句真理：**RAG 的效果 = 资料质量 + 切片策略 + 检索准确度**，模型和提示词反而是次要的。

## 3. 动手：用 Python 做一个"极简 RAG"

不依赖任何框架，几十行就能体会原理（用关键词匹配代替向量检索，够直观）。

```python
import re

# 1) 你的"知识库"文档（实际里可能是多份文件）
docs = [
    "公司报销流程：单笔 500 元以下部门经理审批，以上需财务总监审批。",
    "年假政策：入职满 1 年享 5 天年假，每多 1 年加 1 天，上限 15 天。",
    "服务器 IP：47.110.93.196，部署在阿里云，系统为 Ubuntu 22.04。",
]

def retrieve(question, docs, top_k=1):
    # 极简"检索"：用问题里的词去文档里找命中最多的
    words = set(re.findall(r"\w+", question))
    scored = []
    for d in docs:
        hit = len(words & set(re.findall(r"\w+", d)))
        scored.append((hit, d))
    scored.sort(reverse=True)
    return [d for _, d in scored[:top_k] if _ > 0]

def rag_answer(question):
    ctx = "\n".join(retrieve(question, docs))
    prompt = f"请只根据下面资料回答问题，资料没有就答不知道。\n资料：\n{ctx}\n问题：{question}"
    return llm(prompt)

print(rag_answer("报销 800 块要谁批？"))      # 会基于文档作答，而非瞎编
```

> 真实 RAG 用"向量相似度"而非关键词，效果远好于上面，但**流程一模一样**。M3.4 会做真·向量版。

## 动手任务

1. 在 Coze/Dify 上传一份你自己的文档（产品手册/制度/笔记），问一个"只有文档里才有的细节"，确认它答得上。
2. 跑上面的最小 RAG 代码，把 `docs` 换成你自己的 3 段资料，问它问题。

## 自测

1. 一句话解释 RAG 和普通"直接问大模型"的区别。
2. 为什么说"RAG 效果主要看资料质量"？举一个资料烂导致答错的例子。
3. 上面代码里 `retrieve` 用的是关键词匹配，它有什么明显缺陷？（提示：同义词、长句）
