
在 AI 智能体的实际落地中,大模型知识过时、事实性幻觉、专业领域回答不精准是三大核心痛点。检索增强生成RAG作为解决上述问题的最优方案,已经成为 AI 智能体的标配能力。RAG 的核心逻辑极简且高效:不依赖大模型记忆,先从外部知识库精准检索相关知识,再让大模型基于检索结果生成答案,从根源上杜绝幻觉,保证知识实时性与专业性。
今天我们结合以往的各类基础知识,基于Qwen本地大模型、FAISS 向量数据库、LangChain 框架以及MiniLM模型,以医疗智能体为场景,深度解析RAG原理、核心组件,提供可完整适配智能体的实践方案。


标准 RAG 五步法:

核心价值:答案可追溯、知识可更新、无幻觉、全本地化部署
模块 | 技术选型 | 核心优势 |
|---|---|---|
大语言模型 | Qwen1.5-1.8B-Chat | 轻量级、CPU 可运行、私有化部署 |
向量模型 | paraphrase-multilingual-MiniLM-L12-v2 | 多语言支持、语义匹配精度高、轻量化 |
向量数据库 | FAISS | Facebook 开源、CPU 本地检索、毫秒级响应 |
开发框架 | LangChain | 标准化 RAG 流程、组件化开发 |
应用场景 | 医疗问答智能体 | 知识合规、答案可追溯、无幻觉 |
核心目标:加载本地大模型 + 向量模型,避免重复下载,保证数据隐私。
# 模型路径配置
model_name = "qwen/Qwen1.5-1.8B-Chat"
embedding_model_id = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
cache_dir = "D:\\modelscope\\hub"
# 加载本地大模型(CPU兼容)
local_model_path = snapshot_download(model_name, cache_dir=cache_dir)
tokenizer = AutoTokenizer.from_pretrained(local_model_path, trust_remote_code=True)
llm_model = AutoModelForCausalLM.from_pretrained(
local_model_path, trust_remote_code=True,
torch_dtype=torch.float32, device_map="cpu"
).eval()
# 加载向量模型
embedding_local_path = snapshot_download(embedding_model_id, cache_dir=cache_dir)
embedding_model = SentenceTransformer(embedding_local_path, device="cpu")重点说明:
向量模型(Embedding)是 RAG 的翻译官,负责把文字转换为计算机可计算的向量。
# --------------------------
# 3. 自定义LangChain Embedding适配器
# 作用:将句子转换为向量,用于语义匹配
# --------------------------
class LocalEmbedding(Embeddings):
def __init__(self, model):
self.model = model
def embed_documents(self, texts: List[str]) -> List[List[float]]:
return self.model.encode(texts, convert_to_numpy=True).tolist()
def embed_query(self, text: str) -> List[float]:
return self.model.encode(text, convert_to_numpy=True).tolist()
embedding = LocalEmbedding(embedding_model)核心作用:

将 Qwen 大模型封装为 LangChain 标准 LLM,实现可控、可追溯、无幻觉的生成能力。
class LocalQwenLLM(LLM):
tokenizer: Any
model: Any
max_new_tokens: int = 512
temperature: float = 0.1 # 低温度=高确定性,杜绝幻觉
@property
def _llm_type(self) -> str:
return "qwen_local"
def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
inputs = self.tokenizer(prompt, return_tensors="pt")
with torch.no_grad():
outputs = self.model.generate(
**inputs, max_new_tokens=self.max_new_tokens,
temperature=self.temperature, do_sample=False
)
return self.tokenizer.decode(outputs[0][len(inputs["input_ids"][0]):], skip_special_tokens=True)重点说明:
关键参数解析:
FAISS 是 RAG 的检索核心,负责海量知识的快速语义匹配。
# 医疗专业知识库(可扩展PDF/Excel/数据库)
medical_knowledge = [
Document(page_content="高血压饮食建议:低盐饮食,每日食盐不超过5克...", metadata={"category": "饮食", "disease": "高血压"}),
Document(page_content="糖尿病饮食建议:控制碳水化合物摄入量...", metadata={"category": "饮食", "disease": "糖尿病"}),
Document(page_content="感冒护理建议:保证充足睡眠,多饮温水...", metadata={"category": "护理", "disease": "感冒"}),
Document(page_content="冠心病预防:戒烟限酒,规律运动...", metadata={"category": "预防", "disease": "冠心病"})
]
# 构建FAISS向量库 + 本地持久化
vector_store = FAISS.from_documents(medical_knowledge, embedding)
vector_store.save_local("D:\\medical_rag_faiss_db", "medical_index")重点说明:
主要用于调试和分析检索增强生成(RAG)系统中“问题”与“医疗知识库文档”之间的匹配程度。通过计算余弦相似度,量化用户提出的问题与库中每一篇文档的相关性,并打印出得分和简要内容。
def show_similarity_scores(question: str):
"""展示问题与所有文档的相似度得分"""
print(f"\n[向量相似度分析] 问题: {question}")
print("-" * 50)
# 计算问题向量
query_vector = embedding.embed_query(question)
# 计算与每个文档的相似度
import numpy as np
for i, doc in enumerate(medical_knowledge):
doc_vector = embedding.embed_query(doc.page_content)
# 余弦相似度
query_np = np.array(query_vector)
doc_np = np.array(doc_vector)
similarity = np.dot(query_np, doc_np) / (np.linalg.norm(query_np) * np.linalg.norm(doc_np))
# 标记是否超过阈值
status = "✓" if similarity > 0.6 else "✗"
print(f"{status} 文档{i+1} ({doc.metadata['disease']}): {similarity:.4f}")
print(f" 内容: {doc.page_content[:50]}...")
print()重点说明:

这是AI 智能体的 RAG 大脑,实现:检索→过滤→提示词构建→生成全流程。
def rag_query(question: str, k: int = 2, threshold: float = 0.6) -> dict:
# 1. 计算问题向量
query_vector = embedding.embed_query(question)
# 2. 余弦相似度检索 + 阈值过滤
scored_docs = []
for doc in medical_knowledge:
doc_vector = embedding.embed_query(doc.page_content)
similarity = np.dot(query_np, doc_np) / (np.linalg.norm(query_np) * np.linalg.norm(doc_np))
if similarity > threshold:
scored_docs.append((doc, similarity))
# 3. 按相似度排序,取Top2
scored_docs.sort(key=lambda x: x[1], reverse=True)
docs = [doc for doc, score in scored_docs[:k]]
# 4. 构建约束型提示词(强制基于知识回答)
prompt = f"""你是专业医疗顾问,严格基于知识库回答,不编造信息。
知识库:{context}
用户问题:{question}"""
# 5. 大模型生成答案
answer = local_llm._call(prompt)
return {"question": question, "answer": answer, "source_documents": docs}重点说明:
加载本地大模型+Embedding模型... 构建医疗知识库... 构建FAISS向量检索库... ✅ 检索流程图已保存:medical_rag_process.png ================================================== 医疗问答测试 ================================================== 用户问题:高血压患者平时应该怎么吃饭? [调试] 检索到 1 条相关文档(阈值: 0.6): 文档1 (相似度: 0.8282): 高血压饮食建议:低盐饮食,每日食盐不超过5克;多吃蔬菜、全谷物;减少油炸食品、动物内脏摄 入 元数据: {'category': '饮食', 'disease': '高血压'} 模型答案: 高血压患者在日常饮食上应遵循以下原则: 1. **低盐饮食**: - 每日食盐量不超过5克(约0.9克)。 - 食盐的主要成分是氯化钠,过多摄入可能导致血压升高。因此,高血压患者应尽量避免或限制食用含盐较高的食物,如腌制 食品、咸菜、方便面等。 - 增加蔬菜和全谷物的摄入,这些食物富含钾离子,有助于降低血压。例如,新鲜的绿叶蔬菜(如菠菜、芹菜)、土豆、糙米 等都是良好的选择。 - 减少油炸食品、动物内脏等高脂肪、高胆固醇的食物的摄入,因为它们会增加血液中的甘油三酯水平,进一步加重高血压症 状。 2. **多吃蔬菜、全谷物**: - 蔬菜和全谷物富含膳食纤维,能够帮助控制血糖和血脂,从而降低血压。同时,它们还含有丰富的维生素和矿物质,对身体 健康有益。 - 可以选择各种颜色的蔬菜,如深绿色叶菜(如菠菜、羽衣甘蓝)、红色辣椒、黄色南瓜等,这些色彩丰富的蔬菜可以提供多 种营养素,满足身体对不同营养的需求。 - 全谷物包括燕麦、糙米、全麦面包等,它们的碳水化合物含量较高,但相对于精制白米, ....... ================================================== 用户问题:感冒了需要注意什么? [对比测试] 直接查看知识库中的感冒文档: 找到:感冒护理建议:保证充足睡眠,多饮温水,症状严重时可服用解热镇痛药,病程一般3-7天 [向量相似度分析] 问题: 感冒了需要注意什么? -------------------------------------------------- ✗ 文档1 (高血压): 0.2543 内容: 高血压饮食建议:低盐饮食,每日食盐不超过5克;多吃蔬菜、全谷物;减少油炸食品、动物内脏摄入... ✗ 文档2 (糖尿病): 0.2573 内容: 糖尿病饮食建议:控制碳水化合物摄入量,优先选择低升糖指数食物,定时定量进餐,避免含糖饮料... ✓ 文档3 (感冒): 0.6377 内容: 感冒护理建议:保证充足睡眠,多饮温水,症状严重时可服用解热镇痛药,病程一般3-7天... ✗ 文档4 (冠心病): 0.2835 内容: 冠心病预防:戒烟限酒,规律运动,控制血压血脂,避免情绪剧烈波动,定期体检... [调试] 检索到 1 条相关文档(阈值: 0.6): 文档1 (相似度: 0.6377): 感冒护理建议:保证充足睡眠,多饮温水,症状严重时可服用解热镇痛药,病程一般3-7天 元数据: {'category': '护理', 'disease': '感冒'} 模型答案: 知识1: 感冒护理建议: 1. **保证充足睡眠**:充足的休息可以帮助身体恢复和抵抗病毒入侵,提高免疫力。 2. **多饮温水**:保持水分摄入有助于稀释黏液,减轻鼻塞、喉咙痛等症状,同时也能帮助缓解咳嗽和发热。 。。。。。。 总之,感冒期间应注意休息、补充水分、合理用药、加强个人卫生、调整饮食、保持室内空气流通以及适当运动,以帮助身体尽快恢复健康。若症状持续不减或加重,应及时就医并遵循医生的指导进行治疗。 ================================================== 用户问题:感冒护理建议 [向量相似度分析] 问题: 感冒护理建议 -------------------------------------------------- ✗ 文档1 (高血压): 0.2983 内容: 高血压饮食建议:低盐饮食,每日食盐不超过5克;多吃蔬菜、全谷物;减少油炸食品、动物内脏摄入... ✗ 文档2 (糖尿病): 0.3093 内容: 糖尿病饮食建议:控制碳水化合物摄入量,优先选择低升糖指数食物,定时定量进餐,避免含糖饮料... ✓ 文档3 (感冒): 0.7600 内容: 感冒护理建议:保证充足睡眠,多饮温水,症状严重时可服用解热镇痛药,病程一般3-7天... ✗ 文档4 (冠心病): 0.4085 内容: 冠心病预防:戒烟限酒,规律运动,控制血压血脂,避免情绪剧烈波动,定期体检... [调试] 检索到 1 条相关文档(阈值: 0.6): 文档1 (相似度: 0.7600): 感冒护理建议:保证充足睡眠,多饮温水,症状严重时可服用解热镇痛药,病程一般3-7天 元数据: {'category': '护理', 'disease': '感冒'} 模型答案: - 睡眠是身体恢复和免疫力增强的重要环节,充足的睡眠有助于身体修复受损的组织、提高免疫系统的功能。 - 患者应尽量保持规律的作息时间,避免熬夜或过度劳累,因为这些都可能影响身体对病毒的抵抗力。 - 对于儿童和青少年,家长可以适当调整他们的作息时间,确保他们有足够的休息时间,以应对可能出现的感冒症状。 - 多喝水还可以帮助身体排除体内的毒素,促进新陈代谢,有利于病情的康复。 ...... 总之,感冒期间应注重休息、饮食调节以及适当的药物治疗,以促进身体的康复和减少并发症的发生。如果症状持续不减或者加重,应及时就医,遵医嘱进行治疗。同时,保持良好的个人卫生习惯,如勤洗手、戴口罩、避免人群聚集等,也是预防感冒的重要手段。 ✅ 医疗知识库RAG系统部署完成!
结果分析:检索模块表现优异,精准匹配

总的来说,RAG 才是 AI 智能体的硬核标配,说到底,RAG就是A 智能体的专属知识底座,更是根治大模型通病的关键方案,完美解决知识过时、事实幻觉、垂直领域不专业三大行业痛点,让智能体告别胡编乱造,回答精准又靠谱。它的核心逻辑“先检索、再生成”,对比模型微调成本更低、落地更快,知识库还能随时更新,不用反复训练模型,实用性拉满。
如今 AI 智能体早已从通用化走向垂直深耕,RAG 早就不是可有可无的加分项,而是打造安全、可靠、可信专业智能体的必备能力,想让AI落地更加务实好用,RAG必须安排到位。
import os
import torch
import matplotlib.pyplot as plt
from modelscope import snapshot_download
from transformers import AutoTokenizer, AutoModelForCausalLM
from sentence_transformers import SentenceTransformer
# LangChain 核心模块
from langchain_core.embeddings import Embeddings
from langchain_community.vectorstores import FAISS
from langchain_core.documents import Document
from langchain_core.language_models.llms import LLM
from langchain_core.callbacks.manager import CallbackManagerForLLMRun
from typing import Any, List, Optional
# --------------------------
# 1. 配置路径(复用你的本地模型)
# --------------------------
model_name = "qwen/Qwen1.5-1.8B-Chat"
embedding_model_id = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
cache_dir = "D:\\modelscope\\hub"
faiss_db_path = "D:\\medical_rag_faiss_db" # 向量库持久化路径
os.makedirs(faiss_db_path, exist_ok=True)
# --------------------------
# 2. 下载/加载本地模型(无重复下载)
# --------------------------
print("="*50)
print("加载本地大模型+Embedding模型...")
# 加载对话大模型
local_model_path = snapshot_download(model_name, cache_dir=cache_dir)
tokenizer = AutoTokenizer.from_pretrained(local_model_path, trust_remote_code=True)
llm_model = AutoModelForCausalLM.from_pretrained(
local_model_path,
trust_remote_code=True,
torch_dtype=torch.float32, # CPU兼容
device_map="cpu"
).eval()
# 加载Embedding模型
embedding_local_path = snapshot_download(embedding_model_id, cache_dir=cache_dir, revision="master")
embedding_model = SentenceTransformer(embedding_local_path, device="cpu")
# --------------------------
# 3. 自定义LangChain Embedding适配器
# 作用:将句子转换为向量,用于语义匹配
# --------------------------
class LocalEmbedding(Embeddings):
def __init__(self, model):
self.model = model
def embed_documents(self, texts: List[str]) -> List[List[float]]:
return self.model.encode(texts, convert_to_numpy=True).tolist()
def embed_query(self, text: str) -> List[float]:
return self.model.encode(text, convert_to_numpy=True).tolist()
embedding = LocalEmbedding(embedding_model)
# --------------------------
# 4. 自定义LangChain LLM适配器
# 作用:对接本地Qwen大模型,实现生成能力
# --------------------------
class LocalQwenLLM(LLM):
tokenizer: Any
model: Any
max_new_tokens: int = 512
temperature: float = 0.1
@property
def _llm_type(self) -> str:
return "qwen_local"
def _call(
self,
prompt: str,
stop: Optional[List[str]] = None,
run_manager: Optional[CallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> str:
inputs = self.tokenizer(prompt, return_tensors="pt")
with torch.no_grad():
outputs = self.model.generate(
**inputs,
max_new_tokens=self.max_new_tokens,
temperature=self.temperature,
do_sample=False,
pad_token_id=self.tokenizer.eos_token_id
)
response = self.tokenizer.decode(outputs[0][len(inputs["input_ids"][0]):], skip_special_tokens=True)
return response
# 初始化本地大模型包装器
local_llm = LocalQwenLLM(tokenizer=tokenizer, model=llm_model)
# --------------------------
# 5. 构建医疗知识库(可扩展为Excel/文档读取)
# --------------------------
print("构建医疗知识库...")
medical_knowledge = [
Document(
page_content="高血压饮食建议:低盐饮食,每日食盐不超过5克;多吃蔬菜、全谷物;减少油炸食品、动物内脏摄入",
metadata={"category": "饮食", "disease": "高血压"}
),
Document(
page_content="糖尿病饮食建议:控制碳水化合物摄入量,优先选择低升糖指数食物,定时定量进餐,避免含糖饮料",
metadata={"category": "饮食", "disease": "糖尿病"}
),
Document(
page_content="感冒护理建议:保证充足睡眠,多饮温水,症状严重时可服用解热镇痛药,病程一般3-7天",
metadata={"category": "护理", "disease": "感冒"}
),
Document(
page_content="冠心病预防:戒烟限酒,规律运动,控制血压血脂,避免情绪剧烈波动,定期体检",
metadata={"category": "预防", "disease": "冠心病"}
)
]
# --------------------------
# 6. 构建FAISS向量库(核心检索模块)
# 技术细节:FAISS使用L2距离进行语义相似度计算
# --------------------------
print("构建FAISS向量检索库...")
vector_store = FAISS.from_documents(medical_knowledge, embedding)
# 持久化保存向量库(下次直接加载,无需重新构建)
vector_store.save_local(faiss_db_path, "medical_index")
# --------------------------
# 7. 生成FAISS检索流程图(可视化输出图片)
# --------------------------
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.figure(figsize=(10, 6))
plt.title("医疗知识库RAG检索流程", fontsize=16, pad=20)
# 流程节点
steps = [
"用户问题",
"Embedding向量化",
"FAISS语义检索",
"召回相关医疗知识",
"大模型生成答案"
]
# 节点坐标
x = [1, 2, 3, 4, 5]
y = [1, 1, 1, 1, 1]
# 绘制流程
plt.plot(x, y, "o-", linewidth=3, markersize=15, color="#2E86AB")
for i, step in enumerate(steps):
plt.text(x[i], y[i]+0.1, step, ha="center", fontsize=12, bbox=dict(boxstyle="round,pad=0.3", facecolor="#A2D6F9"))
plt.ylim(0.5, 1.5)
plt.axis("off")
plt.tight_layout()
plt.savefig("medical_rag_process.png", dpi=300, bbox_inches="tight")
print("✅ 检索流程图已保存:medical_rag_process.png")
# --------------------------
# 8. 手动构建RAG问答链(简化版,避免LangChain兼容性问题)
# 检索参数:top_k=2 召回最相关的2条知识
# --------------------------
def rag_query(question: str, k: int = 2, threshold: float = 0.6) -> dict:
"""RAG查询函数"""
# 1. 检索相关文档并计算相似度
import numpy as np
# 获取所有文档的相似度
query_vector = embedding.embed_query(question)
scored_docs = []
for doc in medical_knowledge:
doc_vector = embedding.embed_query(doc.page_content)
query_np = np.array(query_vector)
doc_np = np.array(doc_vector)
similarity = np.dot(query_np, doc_np) / (np.linalg.norm(query_np) * np.linalg.norm(doc_np))
# 只保留相似度大于阈值的文档
if similarity > threshold:
scored_docs.append((doc, similarity))
# 按相似度排序,取top_k
scored_docs.sort(key=lambda x: x[1], reverse=True)
docs = [doc for doc, score in scored_docs[:k]]
# 打印检索到的文档(验证RAG是否生效)
print(f"\n[调试] 检索到 {len(docs)} 条相关文档(阈值: {threshold}):")
for i, (doc, score) in enumerate(scored_docs[:k]):
print(f" 文档{i+1} (相似度: {score:.4f}): {doc.page_content}")
print(f" 元数据: {doc.metadata}")
# 2. 判断是否有检索结果
if len(docs) == 0:
answer = f"抱歉,知识库中没有与您的问题相关的信息(最高相似度低于{threshold})。"
return {
"question": question,
"answer": answer,
"source_documents": []
}
# 3. 构建提示词
context = "\n\n".join([f"知识{i+1}: {doc.page_content}" for i, doc in enumerate(docs)])
prompt = f"""你是一名专业的医疗顾问。请**严格基于以下知识库内容**回答用户问题。
【重要要求】
1. 你的答案必须来自下方提供的知识库内容
2. 不要编造知识库以外的信息
3. 如果知识库中没有相关信息,请明确说明"知识库中没有相关信息"
【知识库内容】
{context}
【用户问题】
{question}
【请基于知识库内容回答】"""
# 4. 调用大模型生成答案
answer = local_llm._call(prompt)
return {
"question": question,
"answer": answer,
"source_documents": docs
}
def show_similarity_scores(question: str):
"""展示问题与所有文档的相似度得分"""
print(f"\n[向量相似度分析] 问题: {question}")
print("-" * 50)
# 计算问题向量
query_vector = embedding.embed_query(question)
# 计算与每个文档的相似度
import numpy as np
for i, doc in enumerate(medical_knowledge):
doc_vector = embedding.embed_query(doc.page_content)
# 余弦相似度
query_np = np.array(query_vector)
doc_np = np.array(doc_vector)
similarity = np.dot(query_np, doc_np) / (np.linalg.norm(query_np) * np.linalg.norm(doc_np))
# 标记是否超过阈值
status = "✓" if similarity > 0.6 else "✗"
print(f"{status} 文档{i+1} ({doc.metadata['disease']}): {similarity:.4f}")
print(f" 内容: {doc.page_content[:50]}...")
print()
# --------------------------
# 9. 医疗问答测试(实战演示)
# --------------------------
print("\n" + "="*50)
print("医疗问答测试")
print("="*50)
# 测试问题1 - 高血压(应该能准确检索)
query1 = "高血压患者平时应该怎么吃饭?"
print(f"\n用户问题:{query1}")
result1 = rag_query(query1)
print(f"模型答案:{result1['answer']}")
# 测试问题2 - 感冒(向量检索可能不精确)
print("\n" + "="*50)
query2 = "感冒了需要注意什么?"
print(f"\n用户问题:{query2}")
print("\n[对比测试] 直接查看知识库中的感冒文档:")
for doc in medical_knowledge:
if "感冒" in doc.page_content:
print(f"找到:{doc.page_content}")
# 先展示向量相似度分析
show_similarity_scores("感冒了需要注意什么?")
result2 = rag_query(query2)
print(f"模型答案:{result2['answer']}")
# 测试问题3 - 直接询问感冒护理(使用更精确的关键词)
print("\n" + "="*50)
query3 = "感冒护理建议"
print(f"\n用户问题:{query3}")
show_similarity_scores("感冒护理建议")
result3 = rag_query(query3)
print(f"模型答案:{result3['answer']}")
# --------------------------
# 10. 加载已有向量库(生产环境使用)
# --------------------------
# vector_store = FAISS.load_local(faiss_db_path, embedding, allow_dangerous_deserialization=True)
# 然后重新调用rag_query函数
print("\n✅ 医疗知识库RAG系统部署完成!")原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。