一、前言
在 AI 智能体的实际落地中,大模型知识过时、事实性幻觉、专业领域回答不精准是三大核心痛点。检索增强生成RAG作为解决上述问题的最优方案,已经成为 AI 智能体的标配能力。RAG 的核心逻辑极简且高效:不依赖大模型记忆,先从外部知识库精准检索相关知识,再让大模型基于检索结果生成答案,从根源上杜绝幻觉,保证知识实时性与专业性。
今天我们结合以往的各类基础知识,基于Qwen本地大模型、FAISS 向量数据库、LangChain 框架以及MiniLM模型,以医疗智能体为场景,深度解析RAG原理、核心组件,提供可完整适配智能体的实践方案。
二、RAG 核心原理
1. 大模型的缺陷
- 知识截止:模型训练数据固定,无法获取最新知识,如最新医疗指南
- 事实幻觉:凭空编造不存在的医疗建议、数据,存在安全风险
- 专业不足:通用大模型在医疗、法律等垂直领域精度不足
2. RAG 工作流程
标准 RAG 五步法:
- 1. 用户提问:输入医疗相关问题
- 2. Embedding 向量化:将问题转换为数值向量
- 3. FAISS 语义检索:在向量库中匹配最相关的专业知识
- 4. 知识召回:筛选高相似度可信知识
- 5. 大模型生成:严格基于召回知识生成合规答案
核心价值:答案可追溯、知识可更新、无幻觉、全本地化部署
3. 技术选型说明
| 模块 | 技术选型 | 核心优势 |
| 大语言模型 | Qwen1.5-1.8B-Chat | 轻量级、CPU 可运行、私有化部署 |
| 向量模型 | paraphrase-multilingual-MiniLM-L12-v2 | 多语言支持、语义匹配精度高、轻量化 |
| 向量数据库 | FAISS | Facebook 开源、CPU 本地检索、毫秒级响应 |
| 开发框架 | LangChain | 标准化 RAG 流程、组件化开发 |
| 应用场景 | 医疗问答智能体 | 知识合规、答案可追溯、无幻觉 |
三、RAG 实践案例
1. 环境与模型初始化
核心目标:加载本地大模型 + 向量模型,避免重复下载,保证数据隐私。
# 模型路径配置 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")
重点说明:
- 模型路径配置:指定大模型与向量模型名称及本地缓存目录,统一管理资源下载路径,便于离线部署与维护。
- 大模型本地加载:通过snapshot_download获取模型文件,利用CPU加载量化后的对话模型,降低硬件门槛并保障隐私。
- 推理环境优化:设置float32精度与CPU设备映射,调用eval模式冻结参数,确保在无GPU环境下稳定进行推理。
- 向量模型初始化:独立加载多语言句子变换模型至CPU,专用于文本向量化,实现语义检索与大模型生成的解耦。
2. 自定义向量适配器
向量模型(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)
核心作用:
- LangChain协议适配:继承Embeddings基类,封装本地模型接口,使其兼容LangChain生态,实现无缝集成。
- 批量文档嵌入:embed_documents方法支持文本列表批量编码,高效处理知识库构建时的大规模数据向量化。
- 单句查询嵌入:embed_query方法专为用户提问设计,将单条文本转为向量,用于实时检索时的语义匹配。
- 本地模型封装:直接调用本地模型encode接口并转换格式,避免网络依赖,保障医疗数据隐私与低延迟。
3. 自定义大模型适配器
将 Qwen 大模型封装为 LangChain 标准 LLM,实现可控、可追溯、无幻觉的生成能力。
class LocalQwenLLM(LLM): tokenizer: Any model: Any max_new_tokens: int = 512 temperature: float = 0.1 # 低温度=高确定性,杜绝幻觉 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)
重点说明:
- LangChain接口封装:继承LLM基类并定义类型,将本地Qwen模型包装为标准组件,无缝接入LangChain调用链。
- 低温度防幻觉:设置0.1低温与贪婪解码,抑制随机性,确保医疗回答严谨确定,最大限度减少模型幻觉。
- 无梯度推理执行:使用no_grad上下文阻断梯度计算,调用generate生成文本,显著降低显存占用并提升推理速度。
- 精准回复提取:解码时自动剔除输入提示词部分,仅返回模型新生成的内容,保证输出结果纯净且可直接使用。
关键参数解析:
- temperature=0.1:降低生成随机性,保证医疗答案严谨性
- do_sample=False:禁用采样,生成确定性结果
- torch.no_grad():推理禁用梯度计算,提速 50% 以上
4. FAISS 向量库构建
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")
重点说明:
- 数据结构化存储:采用Document对象封装内容与元数据,清晰分类疾病与类别,便于后续精准检索与过滤。
- 知识库可扩展性:列表结构支持动态追加,可轻松接入PDF、Excel等多源医疗数据,灵活构建大规模知识库。
- FAISS向量库构建:利用FAISS将文档向量化并建立索引,实现毫秒级高维语义搜索,大幅提升检索效率。
- 本地持久化保存:将构建好的向量索引保存至本地磁盘,避免重复计算,支持系统重启后快速加载与复用。
5. 问答相似度匹配计算
主要用于调试和分析检索增强生成(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()
重点说明:
- 向量嵌入计算:将文本问题与文档转化为高维向量,使计算机能数值化地衡量语义相似度,是检索的基础。
- 余弦相似度算法:通过计算向量夹角余弦值量化相关性,结果越接近1表示语义越相似,用于精准匹配文档。
- 阈值判定机制:设定0.6为界限,自动标记命中或未命中文档,快速筛选高相关素材,辅助调整检索策略。
- 元数据展示:提取疾病名称等关键标签,直观显示匹配来源,帮助开发者在医疗场景中快速定位错误关联。
- 调试与可视化:遍历打印所有得分与摘要,专为开发调优设计,便于观察模型表现并优化知识库切片策略。
6. RAG 核心查询函数
这是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}
重点说明:
- 余弦相似度计算:语义匹配最常用算法,数值越接近 1 越相关
- 相似度阈值(0.6):过滤无关知识,避免干扰生成
- Top-K 召回(2 条):平衡精度与效率
- 约束提示词:从机制上杜绝大模型幻觉
- 可追溯:返回来源文档,答案有据可查
7. 运行结果输出
加载本地大模型+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系统部署完成!
结果分析:检索模块表现优异,精准匹配
- 语义识别准确:系统能精准区分不同疾病场景。
- 问“高血压饮食”时,仅召回高血压文档(相似度0.8282)。
- 问“感冒”相关时,准确锁定感冒文档(相似度0.63~0.76),并有效过滤了高血压、糖尿病等无关文档(相似度均<0.41)。
- 阈值控制有效:设定的 0.6 相似度阈值成功拦截了低相关性内容,保证了输入给大模型的上下文质量。
四、总结
总的来说,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 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系统部署完成!")