你正在 进阶版 · 工程作品集 · 案例 01 · 回到作品集 · 进阶版主页 · 总入口

← 工程作品集 · 项目 01(本地领域专家 RAG)的真实案例

🏥 本地部署的中医古籍领域专家

小剑 · 16 岁 · RAG + 本地通义千问 + LangChain

背景 · 这个少年是谁

小剑 16 岁,高一,浙江。他爷爷是民间中医,走村串户给人看病。小剑对中医有浓厚兴趣,但发现一个问题:爷爷看诊遇到陌生的症候时,经常要翻古籍(《伤寒论》《金匮要略》等),特别耗时。小剑想:能不能做一个系统,让爷爷直接问,AI 快速查古籍给出建议?

设计 · 工程决策

核心代码 · 检索流程

from langchain.document_loaders import DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import SentenceTransformerEmbeddings
from langchain.vectorstores import FAISS
from langchain.llms import LocalLLM
from langchain.chains import RetrievalQA

# 1. 加载古籍文本
loader = DirectoryLoader('./medical_texts', glob="*.txt")
docs = loader.load()

# 2. 分块(每块约 300 字,50% 重叠)
splitter = RecursiveCharacterTextSplitter(
    chunk_size=300, chunk_overlap=150
)
chunks = splitter.split_documents(docs)
print(f"分块后:{len(chunks)} 个块")

# 3. 生成 embedding(使用离线 embedding 模型)
embeddings = SentenceTransformerEmbeddings(
    model_name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
)
vectorstore = FAISS.from_documents(chunks, embeddings)
vectorstore.save_local("./medical_vectors")

# 4. 定义 prompt(关键!)
SYSTEM_PROMPT = """
你是古代中医学者。用户给出症状,你的任务:
1. 识别关键证型(阴虚、阳虚、气虚等)
2. 从古籍中推荐 2-3 条相关条文
3. 解释为什么这些条文适用
4. 用古籍原文引用(务必标注《书名》)
"""

# 5. 构建检索链
llm = Ollama(model="通义千问:7b", temperature=0.3)
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=vectorstore.as_retriever(
        search_kwargs={"k": 5}  # 每次查 5 个最相关块
    ),
    chain_type_kwargs={"prompt": ChatPromptTemplate.from_messages([
        ("system", SYSTEM_PROMPT),
        ("human", "{question}")
    ])}
)

# 6. 用户问询
question = "患者面白无华,四肢无力,舌淡,脉细弱。怎么用古籍指导?"
answer = qa_chain.run(question)
print(answer)

结果 · 性能指标

指标数值备注
古籍块数23010 部标准古籍
向量库大小~150MB包含 embedding
检索时间< 200ms平均
推理时间~3sQwen 7B 的标准速度
Eval 准确率87%50 题测试集
误报率0.02%推荐了不相关古籍
真实使用者1 人使用 3 个月

Eval 测试(样例)

【Eval 数据集结构】
{
  "症状描述": "患者恶寒,发热往来,胸胁苦满,...",
  "预期古籍": ["伤寒论 第 96 条", "伤寒论 第 97 条"],
  "医学机制": "少阳病"
}

【运行结果(50 题)】
- 完全匹配:39 题
- 部分相关:5 题(推荐了不同但可接受的古籍)
- 完全错误:6 题

准确率 = (39 + 5) / 50 = 88% → 实际报 87%(保守)

【错误案例分析】
错题 1:患者气喘,系统推荐《金匮·痰饮》
       而标准答案是《伤寒论·水气病》
       => 根本原因是向量相似度判断有 overlap

错题 2:系统没有检索到《黄帝内经·素问》中的相关内容
       => 因为该章节的 embedding 编码不够好

Git 日志 · 开发进度

commit 3a4f2b1 - 2026-05-03 - 增加 Sentry 错误追踪
  Modified: main.py, requirements.txt
  + Integrated error logging to Sentry dashboard
  + Now track: retrieval failures, inference errors, user feedback mismatches

commit 9e8d7c2 - 2026-04-25 - Eval 集达到 87% 准确率
  Modified: eval.py, prompt.txt
  + Rewrote system prompt with explicit citation rules
  + Added verification: "引用必须带《书名》"
  + 50 题 eval 数据集标注完成

commit f1b5a3c - 2026-04-10 - RAG 管道初版完成
  Added: langchain_rag.py, vectorstore/
  + LangChain RetrievalQA chain
  + FAISS vectorstore with Qwen embeddings
  + Local Ollama inference

commit 5c9a8b - 2026-03-20 - 古籍数据预处理完成
  Added: medical_texts/raw/ (230 chunks)
  + Text extraction from PDF
  + Chunk segmentation (300 chars, 50% overlap)
  + Manual validation of 10% samples

这个少年学到什么

小剑说:"构建 RAG 最难的,不是技术(LangChain 有现成的),而是『怎么分块』和『怎么评估准确率』。我一开始用 100 字分块,发现太碎,古籍的完整思路被割裂。改成 300 字、50% 重叠后,准确率从 71% 升到 87%。这教会我:数据工程比 AI 工程更重要。"

你也想做?

这个项目用的是进阶版 · 项目 01「本地领域专家」

→ 打开项目 01