RAG与Agent性能调优13.稀疏查询召回差HyDE伪文档深层技术解决冷启动问题Gitee地址https://gitee.com/agiforgagaplus/OptiRAGAgent文章详情目录RAG与Agent性能调优上一节第12节用户查询太模糊通过查询扩展提升语义匹配能力下一节HyDE通过假想文档增强语义匹配能力核心思想HYDE的核心思想在于先假想答案再通过这个假设去匹配真实的文档。具体流程如下使用大模型基于用户问题生成一段假想的答案文档将该假想文档用于向量检索找到最相似的真实文档基于这些真实文档生成最终回答代码实现import logging from sentence_transformers import SentenceTransformer from sklearn.metrics.pairwise import cosine_similarity from llama_index.core.indices.query.query_transform.base import HyDEQueryTransform from llama_index.core.query_engine import TransformQueryEngine from llama_index.core import VectorStoreIndex, SimpleDirectoryReader # 配置日志记录显示信息级别日志方便调试和观察模型行为 logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) class HyDEQueryEvaluator: def __init__(self, data_dir./12/data/, include_originalFalse, top_k10): 初始化 HyDE 查询评估器。 这个类负责加载数据、构建索引、设置 HyDE 查询转换以及运行评估。 :param data_dir: 存储文档的目录路径。 :param include_original: 布尔值指示 HyDE 是否在生成假设性文档时包含原始查询。 设置为 True 可以让模型同时考虑原始意图和假设性内容。 :param top_k: 相似度查询时返回的前 k 个最相关结果。 logging.info(f正在初始化 HyDEQueryEvaluator数据目录: {data_dir}) # 1. 加载文档从指定目录读取所有文档。SimpleDirectoryReader 会自动处理多种文件类型。 self.documents SimpleDirectoryReader(data_dir).load_data() logging.info(f已加载 {len(self.documents)} 份文档。) # 2. 初始化向量存储索引将文档转换为向量并存储以便进行高效的相似度检索。 # 默认使用 OpenAI 的嵌入模型。 self.sentence_index VectorStoreIndex.from_documents(self.documents) logging.info(已创建文档的向量索引。) # 3. 初始化 HyDE 查询转换器这是 HyDE 的核心部分它会根据原始查询生成一个假设性文档。 # include_originalTrue 表示在检索时会将原始查询和生成的假设性文档都用于查找。 self.hyde HyDEQueryTransform(include_originalinclude_original) logging.info(fHyDE 查询转换器已初始化include_original: {include_original}。) # 4. 构建基础查询引擎用于执行实际的向量相似度搜索。 # streamingTrue 可以在响应生成时逐步输出提升用户体验。 # similarity_top_k 决定了每次查询返回多少个最相关的文档片段。 self.query_engine self.sentence_index.as_query_engine( streamingTrue, similarity_top_ktop_k ) logging.info(f基础查询引擎已构建top_k: {top_k}。) # 5. 包装成支持 HyDE 的查询引擎将基础查询引擎与 HyDE 转换器结合。 # 这样每次查询都会先通过 HyDE 进行转换然后再由基础引擎执行。 self.hyde_query_engine TransformQueryEngine( self.query_engine, query_transformself.hyde ) logging.info(HyDE 查询引擎已准备就绪。) # 6. 初始化语义相似度模型用于计算查询结果与真实答案之间的语义相似度。 # paraphrase-multilingual-MiniLM-L12-v2 是一个多语言模型适合处理中文。 self.model SentenceTransformer(paraphrase-multilingual-MiniLM-L12-v2) logging.info(语义相似度模型 paraphrase-multilingual-MiniLM-L12-v2 已加载。) def evaluate_similarity(self, response_text, ground_truth): 使用 SentenceTransformer 模型计算两个文本之间的余弦相似度。 用于量化检索到的信息与真实答案的匹配程度。 :param response_text: 从模型响应中提取的文本片段。 :param ground_truth: 预设的真实答案或标准参考文本。 :return: 两个文本嵌入向量之间的余弦相似度得分范围通常在 -1 到 1 之间1 表示完全相同。 # 如果任一文本为空则直接返回0避免编码错误 if not response_text or not ground_truth: return 0.0 # 将文本编码为向量嵌入。模型会捕获文本的语义信息。 response_embedding self.model.encode([response_text], convert_to_tensorTrue) ground_truth_embedding self.model.encode([ground_truth], convert_to_tensorTrue) # 计算余弦相似度。余弦相似度衡量两个向量方向的相似性与向量长度无关。 similarity cosine_similarity(response_embedding.cpu(), ground_truth_embedding.cpu())[0][0] return float(similarity) def evaluate_result(self, question, response, ground_truth): 评估一次查询结果的质量主要是通过计算检索到的文档片段与真实答案的相似度。 :param question: 用户输入的查询问题。 :param response: LlamaIndex 查询引擎返回的响应对象包含源文档节点。 :param ground_truth: 问题的真实答案。 :return: 检索到的文档片段与真实答案的相似度得分。 # 从响应的源节点中提取所有文本拼接成一个字符串。 # 这些源节点是 RAG 模型用来生成回答的原始信息。 response_text .join([node.text for node in response.source_nodes]) logging.info(f正在评估查询 {question} 的结果...) # 调用语义相似度评估函数 score self.evaluate_similarity(response_text, ground_truth) logging.info(f评估完成得分: {score:.4f}) return score def run_query(self, question, ground_truth, num_queries3): 执行多次查询并输出详细结果包括 AI 回答、参考文档和评估得分。 这个方法主要用于演示 HyDE 如何影响查询结果和检索到的信息。 :param question: 用户要查询的问题。 :param ground_truth: 该问题的标准真实答案用于评估。 :param num_queries: 执行查询的次数用于观察结果的稳定性通常 HyDE 每次结果不会有太大变化。 print(f\n--- HyDE 查询演示开始 ---\n) print(f❓ 用户问题: {question}\n) print( AI 正在通过 HyDE 分析并生成回答...\n) scores [] for i in range(num_queries): if num_queries 1: print(f 第 {i1}/{num_queries} 次查询中...) # 执行 HyDE 增强的查询。这一步会先生成假设性文档然后用它来检索。 response self.hyde_query_engine.query(question) # 首次查询时详细输出 AI 的回答和作为参考的源文档片段。 if i 0: print( AI 回答:) print(- * 40) # print_response_stream() 可以逐步打印回答提供更好的用户体验。 response.print_response_stream() print(\n 参考依据 (Source Nodes):) print(- * 40) # 遍历并打印出模型用来生成回答的原始文档片段。 for idx, node in enumerate(response.source_nodes, 1): print(f\n文档片段 {idx}:) print(- * 30) print(node.text) # 评估当前查询的相似度得分。 score self.evaluate_result(question, response, ground_truth) scores.append(score) if num_queries 1: print(f✅ 第 {i1} 次查询得分: {score:.4f}) # 计算并打印所有查询的平均得分。 average_score sum(scores) / len(scores) print(\n HyDE 查询评估结果:) print(- * 40) print(f平均相似度得分: {average_score:.4f}) print(f\n--- HyDE 查询演示结束 ---\n) return average_score if __name__ __main__: # 初始化评估器实例。可以根据需要调整 data_dir, include_original 和 top_k。 # data_dir 指向你的文档存储位置。 # include_originalTrue 会让 HyDE 同时考虑原始查询和假设性查询。 # top_k5 表示检索最相关的 5 个文档片段。 evaluator HyDEQueryEvaluator(data_dir./12/data/, include_originalTrue, top_k5) # 示例问题。请确保你的文档中包含能够回答这个问题的相关信息。 question 适合亲子游的地方有哪些 # 预设的真实答案Ground Truth。这是用于评估模型检索效果的基准。 # 请根据你实际文档中的内容提供一个准确且简洁的答案。 ground_truth 适合亲子游的地方有上海迪士尼乐园、北京环球影城和广州长隆欢乐世界等主题公园。 # 执行查询。num_queries1 更适合演示因为它会打印详细的 AI 回答和参考文档。 # 如果想观察 HyDE 在多次查询中的稳定性可以增加 num_queries。 evaluator.run_query(question, ground_truth, num_queries1) # 演示时通常一次就够了好处1.提升检索相关性获取精准答案HyDE通过生成与问题相关的假设性文档帮助系统更准确地理解用户查询的意图原始问题适合亲子游的地方有哪些HyDE生成的假设文档可能包含类似于主题公园、儿童设施等关键语义最终系统准确返回了文档中的上海迪士尼乐园北京环球影视城。核心结果完全匹配文档中的内容避免了无关文档的干扰比如北京火锅推荐等片段2.增强结果稳定性降低随机误差通过三次重复查询测试HyDE展现了优秀的结果一致性三次查询得分语义相似度均为0.7249回答内容完全一致未出现因检索随机性导致的答案波动这表明HYDE生成的假设文档能够稳定引导检索系统聚焦高质量文档3.优化语义匹配能力超越关键词检索传统关键词检索可能受字面匹配而HyDE通过以下机制提升语义理解假设文档生成通过简短查询扩展为包含丰富语义的假设文档深层语义对齐通过HyDEQueryTransfrom实现查询与文档的语义级匹配结果即使文档中未出现亲子游相关关键词仍能精准定位其相关内容4.提升复杂查询处理能力对于开放式问题如推荐类比较类问题HyDE展现出了独特优势自动补充查询上下文帮助系统从多个文档片段中聚焦信息平均相似度得0.7249表达回答与真实答案高度一致通过输出可预见HYDE解决了传统RG系统中查询意图不足或检索结果不太稳定的两大问题。虽然这个假设文档是虚构的但它的风格和内容上与真实旅游攻略非常相近从而提升了检索的相关性提取标签增强检索在向量检索的基础上通过标签过滤机制能够显著提升检索精度。这一方面就如图书馆不仅有书名检索还分配分类编号双管齐下让检索结果更加精确标签提取主要应用在以下两个场景建立索引时从文档切片中提取结构化标签将其与文档切片一同存储检索时从用户问题中提取对应的标签进行初步过滤再结合向量检索获取最终结果import os from openai import OpenAI client OpenAI(api_keyos.getenv(DASHSCOPE_API_KEY), base_urlhttps://dashscope.aliyuncs.com/compatible-mode/v1) system_message 你是一个标签提取专家。请从旅游相关文本中提取结构化信息并按要求输出标签。 --- 【支持的标签类型】 - 旅游目的地 - 景点名称 - 旅游活动 - 最佳旅游时间 - 旅游花费 --- 【输出要求】 1. 请用 JSON 格式输出如[{key: 旅游目的地, value: 北京}] 2. 如果某类标签未识别到则不输出该类 --- 待分析文本如下 def extract_tags(text): completion client.chat.completions.create( modelqwen-turbo, messages[ {role: system, content: system_message}, {role: user, content: text} ], response_format{type: json_object} ) return completion.choices[0].message.content # 示例1城市旅游介绍 city_text 上海是一个充满魅力的旅游城市外滩是必去的景点之一晚上可以欣赏美丽的夜景灯光秀。最佳旅游时间是春秋两季参观外滩无需门票。 print(城市旅游介绍标签提取结果) print(extract_tags(city_text)) # 示例2主题公园游玩指南 park_text 广州长隆欢乐世界是亲子游玩的好去处这里有各种刺激的游乐设施如垂直过山车。周末和节假日是游玩高峰全票价格约300元。 print(\n主题公园游玩指南标签提取结果) print(extract_tags(park_text)) # 示例3自然风光旅游推荐 nature_text 九寨沟的秋天是最美的五彩斑斓的湖水和山林构成了绝美的画卷。在这里可以徒步欣赏风景门票价格旺季220元。 print(\n自然风光旅游推荐标签提取结果) print(extract_tags(nature_text))结论功能实现代码成功从旅游相关文本中提取结构化标签输出符合jason格式的要求覆盖了旅游目的地、景点名称、旅游活动最佳旅行路线和旅行花费等信息输出结果针对不同类型的旅游文本都能提确出对应的标签验证了代码的有效性扩展性系统提示词和知识的标签类型可以灵活调整方便。根据不同需求扩展标签类型的处理更多种类的旅游文本标签的价值精准检索企业可利用提取的标签建立文档索引在用户检索时先通过标签过滤出相关文档切片再结合相似度检索能大幅度提升检索的准确率和效率快速定位用户所需要的信息列如用户搜索藏海景点可以快速筛选出上海相关的文档标签个性化推荐基于标签信息企业可为用户提供个性化旅游推荐。例如根据用户的旅游偏好和历史记录再结合景点的标签信息为用户推荐符合其需求的旅行目的地和活动数据分析标签数据可用于企业进行数据分析了解热门旅行的目的地、旅游活动和用费交付习惯。企业可以根据这些数据优化旅游产品和服务制定更具有针对性的营销策略内容管理标签能够帮助企业对大量的旅游文档进行分类和管理提高文档的组织性和可维护性。企业可以通过标签对文档进行分组排序和筛选方便员工查找和使用相关信息提升用户体验精确的检索结果和个性化的推荐服务能够提升用户体验增加用户对企业的满意度和忠诚度从而促进业务增长