ARTICLE · 1326061
急诊科 AI 极客实战教程 · 第 8 章
急诊科 AI 极客实战教程 · 第 8 章
本地 RAG 知识库:让 AI 带着指南开卷考试
搭一个只基于你的指南和文献回答的循证秘书。预计用时 120-150 分钟。
本章目标 把“凭记忆回答”的 AI,改造成“只基于你的资料回答、说不出就敢说不知道”的助手。 |
前置条件:完成第 2、5 章,会运行 Python,准备若干 PDF 或 TXT 格式的指南与文献。本章可交付成果:一个本地知识库 + 一个可查询的 rag_qa.py + 一份 20 题测试报告。
一、开场病例:一条推荐剂量,查了半小时
夜班,一个创伤性凝血病(TIC)的患者,血压在往下掉。你记得氨甲环酸(TXA)有用,但不确定:推荐剂量是多少?伤后多长时间内给药?哪些指南支持?肾功能不全要不要减量?
你打开手机,搜到一堆互相矛盾的内容:有的说 1 g,有的说 1 g 加 1 g;有的说 3 小时内,有的说 8 小时内。你打开一个网页版 AI,它很自信地给你一段“指南推荐”,还附了一个看起来很像真的参考文献。你差点就信了。
问题是:AI 不是在查指南,它是在“接龙”。它生成的是“最像答案的文本”,不是“最真实的文本”。
这一章要做的,就是给 AI 配一个本地开卷考试的书包:你提问,系统先在你的指南和文献里检索相关原文,把原文和问题一起交给 AI,再要求它“只根据原文回答,并标注出处”。这就是 RAG(Retrieval-Augmented Generation,检索增强生成)。
二、核心概念:六个词,一张表
1. RAG 的基本流程
# 离线阶段 PDF/TXT -> 解析文本 -> 分块 -> 向量化 -> 存入向量库 # 在线阶段 用户问题 -> 向量化 -> 相似度检索 -> 取 Top-K 片段 → 拼成上下文 -> 交给 LLM -> 带引用回答 |
关键区别:普通 LLM 是“闭卷考试”,靠参数里的记忆;RAG 是“开卷考试”,靠你给它的资料。开卷考试也会答错,但至少你知道它看的是哪一页。
2. 为什么大模型会“幻觉”
第 1 章讲过:LLM 的训练目标是“预测下一个 token”,不是“保证事实正确”。具体到医学指南,有四条路径通向幻觉:知识可能过时,模型的训练有截止日期,新指南可能没学过;知识可能混淆,不同指南、不同年份、不同病种的推荐容易串;上下文里没有答案时它会“补全”,为了完成任务编一个看起来合理的答案;它无法区分“我记得”和“我猜的”,语气都很自信。
RAG 的作用,是给它一个可核查的证据来源。但它不能把幻觉降到零:检索可能没找到正确片段;找到了,模型可能忽略;模型可能错误引用;资料本身也可能过时或出错。
正确期待:RAG 把“凭空编造”变成“有据可查”,把幻觉率显著降低,但仍然需要人工核对引用。 |
3. Chunking:分块的艺术
分块太大,检索到一大段,模型要自己找重点,上下文浪费,还容易把不相关内容混进来。分块太小,上下文丢失,一个推荐被切成几段检索不全,指代关系也断了(“该药”“上述剂量”)。急诊指南还有个特殊性:推荐剂量、时间窗、禁忌证往往集中在一个小节,分块时最好保留章节标题和页码,否则检索到“1 g”却不知道是哪个药。
4. Embedding:文字怎么变成向量
Embedding(嵌入)把一段文字映射成一个高维向量,比如 384 维或 768 维,语义相近的文字,向量也相近。你可以把它想成“语义指纹”:“氨甲环酸”和“TXA”向量很近;“氨甲环酸”和“对乙酰氨基酚”向量较远;“推荐剂量”和“用法用量”向量较近。
similarity = (A · B) / (|A| × |B|) |
余弦相似度等于 1 表示方向完全相同,等于 0 表示无关,等于负 1 表示方向相反。注意:Embedding 不是“理解”,它捕获的是训练语料中的共现模式。缩写、否定、单位、数字,它可能处理不好,所以检索结果必须人工看。
5. 向量数据库:按“意思”搜索
传统搜索按关键词匹配:你搜“TXA”,找不到只写“氨甲环酸”的段落;你搜“肾功能不全”,找不到“eGFR 小于 30”的段落。向量搜索按语义相似度:你搜“TXA 剂量”,能找到“氨甲环酸 1 g 静脉注射”;你搜“肾不好能不能用”,能找到“严重肾功能不全患者慎用”。
本书用 FAISS 加 sentence-transformers,因为依赖少、可离线、适合个人电脑。
6. 检索之后:Prompt 决定成败
你是循证医学助手。请只根据下面提供的资料回答问题。 要求: 1. 每个关键结论后标注来源,格式:[文件名, 第X页] 2. 如果资料中没有明确答案,回答“库中无证据”,不要推断。 3. 如果资料之间有冲突,列出冲突并说明各自来源。 4. 不要使用资料之外的知识。 |
没有这几句话,模型很可能“看了资料,但继续自由发挥”。
三、底层原理:RAG 为什么会失败
知道失败模式,才知道怎么调。
RAG 的质量上限,取决于知识库的质量。垃圾进,垃圾出。 |
1. 混合检索:向量 + 关键词
纯向量检索对缩写、数字、专有名词可能不敏感。实践中常用混合检索:向量检索负责语义相似,关键词检索(BM25)负责精确匹配“TXA”“1 g”“3 h”,两者合并结果之后再做 rerank。本书的最小实现只做向量检索;如果你要做生产级知识库,建议加 BM25。
2. 元数据过滤
给每个 chunk 加元数据:文件名、页码、章节、指南版本或年份、病种、证据等级。检索时可以过滤,比如“只查 2024 年以后的指南”“只查创伤相关章节”。这能显著减少无关结果。
四、实操:搭一个最小可用的本地 RAG
步骤 1 安装依赖
conda activate ai-ed pip install pypdf sentence-transformers faiss-cpu openai numpy pandas |
Windows 提示:faiss-cpu 如果安装失败,可以尝试 pip install faiss-cpu --only-binary=:all:,或者改用 chromadb。本书以 FAISS 为例,概念是通用的。如果安装 sentence-transformers 后 import torch 报 shm.dll 或 c10.dll 找不到,通常是缺少最新的 Microsoft Visual C++ Redistributable(x64),安装后重启终端即可,详见附录 C.5.4。 |
步骤 2 准备资料
在 D:\AI急诊实验室\07_知识库\docs 下放入三类文件:公开版本的指南 PDF(例如 AHA、SSC、ATLS)、综述 PDF、你自己整理的指南摘要 TXT 或 MD。
安全提醒 不要放入含患者信息的文档; 不要放入未经授权的内部文件; 如果文档受版权保护,仅限个人学习使用。 |
步骤 3 运行建库脚本
python "...\ch08_rag_minimal.py" build ^ --docs "D:\AI急诊实验室\07_知识库\docs" ^ --index "D:\AI急诊实验室\07_知识库\index" |
PowerShell 的续行符是反引号,不是 ^。上面的 ^ 是 CMD 写法。在 PowerShell 里写成一行,或者用反引号续行。 |
脚本会读取 .pdf、.txt、.md,按页提取文本,分块(默认 800 字,重叠 120 字),用 BAAI/bge-small-zh-v1.5 生成向量,建立 FAISS 索引,最后保存 index.faiss 和 metadata.json。第一次运行会下载 embedding 模型(几百 MB),需要网络。
步骤 4 查询
python "...\ch08_rag_minimal.py" query ` --index "D:\AI急诊实验室\07_知识库\index" ` --question "创伤性凝血病使用氨甲环酸的推荐剂量和时间窗是什么?" |
脚本会把问题向量化,检索 Top-K(默认 5)片段,打印片段来源(文件名、页码、相似度);如果设置了 DEEPSEEK_API_KEY,就调用 DeepSeek 生成带引用的回答;如果没有 API Key,只打印检索结果,方便你人工阅读。
$env:DEEPSEEK_API_KEY = "sk-你的密钥" |
不要把密钥写进脚本。用环境变量,或者 .env 加 python-dotenv,并把 .env 加入 .gitignore。
步骤 5 测试一个“库中没有”的问题
python "...\ch08_rag_minimal.py" query ` --index "D:\AI急诊实验室\07_知识库\index" ` --question "急诊成人咯血使用氨甲环酸的剂量是多少?" |
如果库里只有创伤指南、没有咯血指南,正确行为就是回答“库中无证据”。如果它还是给了你一个剂量,说明 prompt 或检索出了问题。
“敢于说不知道”是 RAG 最重要的能力。一个什么都敢答的医疗 AI,比一个答不出来的危险得多。 |
步骤 6 评估你的知识库
准备 20 个问题:10 个“库中明确有答案”,5 个“需要跨文档综合”,5 个“库中没有答案”。然后按四个指标打分:
把结果存到 07_知识库\RAG测试报告.md。这一步最容易被跳过,但它恰恰是判断“你的知识库能不能用”的唯一依据。
五、代码结构:你不需要从零写
配套脚本 ch08_rag_minimal.py 包含五个函数:load_documents() 读取 PDF、TXT、MD,split_sentences() 按句子切分,chunk_text() 分块并做重叠,build_index() 向量化并建 FAISS 索引,query() 负责检索与生成。
核心逻辑只有两段,建库和查询:
# 1. 建库 chunks = [] for doc in documents: for chunk in chunk_text(doc["text"]): chunks.append({"text": chunk, "file": doc["file"], "page": doc["page"]}) vectors = model.encode( [c["text"] for c in chunks], normalize_embeddings=True) index = faiss.IndexFlatIP(vectors.shape[1]); index.add(vectors) # 2. 查询 q_vec = model.encode([question], normalize_embeddings=True) scores, ids = index.search(q_vec, top_k) context = "\n\n".join( f"[{i+1}] 文件:{chunks[idx]['file']} 第{chunks[idx]['page']}页\n{chunks[idx]['text']}" for i, idx in enumerate(ids[0])) |
为什么用 IndexFlatIP?因为 embedding 已经归一化,内积就等于余弦相似度。数据量小(几万条)时 Flat 索引足够快,数据量大了再考虑 IVF 或 HNSW。
六、把 RAG 接进 Claude Code 的工作流
用法 B 的操作很简单,在 Claude Code 里直接说:
请运行 D:\AI急诊实验室\07_知识库\rag_qa.py, 查询“脓毒症 1 小时 bundle 的推荐内容”, 把检索到的原文和回答一起给我,并标注文件名和页码。 # 进阶:封装成 MCP 工具(第 13 章),像调用本地文件一样调用知识库 |
Claude Code 会执行脚本、读取输出,再帮你整理。这样你既保留了 RAG 的可追溯性,又利用了 Agent 的整理能力。
七、提示词模板:带引用、敢拒答
【角色】你是一名循证医学助手,擅长从指南原文中提取推荐意见。 【背景】下面是从本地知识库检索到的资料片段,每段都有文件名和页码。 【任务】请只根据这些资料回答用户问题。 【边界】 1. 每个关键结论后标注来源,格式:[文件名, 第X页]。 2. 如果资料中没有明确答案,回答“库中无证据”,不要推断。 3. 如果不同资料冲突,列出冲突点及各自来源。 4. 不要使用资料之外的知识。 5. 不要编造页码、指南名称或推荐等级。 【格式】 - 先给出一句话结论; - 再列出证据要点(每条带引用); - 最后列出“不确定、冲突、缺失”的部分。 |
一个“防幻觉”的追问
回答生成之后,再加一句追问:请把你回答中的每一条引用逐条对照原文,确认原文是否真的包含该结论、页码是否准确、是否有断章取义;如果某条引用无法核实,请删除该结论并说明。
这一步会让模型自己删掉几条结论。别嫌它变得“保守”——被删掉的,本来就是你最不该相信的部分。
八、安全与合规
1. 数据不出院
2. 版权
指南 PDF 可能受版权保护,个人学习使用通常可以,但不能公开分发;不要把整本教科书上传到公开网盘;商用知识库需要获得授权。
3. 版本管理
每份文档记录版本或年份;指南更新后重建索引;在回答中标注指南版本,避免“用旧指南回答新问题”。
九、可交付成果与过关自检
07_知识库/ ├─ docs/ # 你的指南与文献 ├─ index/ # index.faiss + metadata.json ├─ rag_qa.py # 查询脚本 └─ RAG测试报告.md # 20 题评估 |
验收标准:能成功建库、索引文件存在;查询时能返回文件名、页码、相似度;回答中的引用能回到原文核对;库中无答案时能正确回答“无证据”;20 题测试的检索命中率不低于 80%、拒答正确率不低于 90%;没有把患者数据或密钥写进知识库和代码。
过关自检七项:能用自己的话解释 RAG 的开卷考试流程;能说出 RAG 不能把幻觉降到零的三个原因;知道分块太大和太小分别有什么问题;能解释 embedding 和余弦相似度的直觉;知道检索失败的常见原因;能写出“只基于资料回答、无证据就拒答”的 prompt;知道如何评估检索命中率和引用准确率。
延伸练习:把 chunk_size 从 500 改到 1500,观察检索结果的变化;加入 BM25 关键词检索,比较纯向量与混合检索的命中率;给文档加“年份、病种”元数据,只检索 2024 年以后的指南;故意问一个库中没有的罕见病剂量,观察模型是否拒答;随机抽 10 条引用逐条回到原文,计算引用准确率。
本章速查卡 RAG 等于检索 + 增强 + 生成,本质是开卷考试 流程:文档 → 分块 → 向量化 → 向量库 → 检索 → 拼上下文 → LLM 分块:500 到 1000 字,重叠 100 到 200 字,保留文件名、页码、章节 向量:语义指纹;余弦相似度;归一化后用内积 Prompt:只基于资料;每条带引用;无证据就拒答 评估:检索命中率、引用准确率、拒答正确率 安全:患者数据不出院;密钥不进代码;指南注意版权 |
下一章预告:你有了干净数据、统计图表、循证知识库。接下来要把这些素材变成论文和标书——IMRAD 的底层逻辑、如何写出没有 AI 味的 Results,以及国自然标书“痛点、空白、假说、路线”的四段结构。 |
本文为《急诊科 AI 极客实战教程》第 8 章排版版。代码请以配套仓库的完整脚本为准,文中代码为讲解节选。
我是老陆,热衷于分享医生 × AI 的实战干货。
如果这篇对你有用,欢迎点赞、在看、转发三连,我们下篇见。















































































