009-01:RAG 入门-RAG 优化,查询构建技术详解

发布时间:2026/5/19 18:27:12

009-01:RAG 入门-RAG 优化,查询构建技术详解 本文是 refine-rag 系列教程的第十一篇我们来学习检索前处理的查询构建技术。本文所有代码都在https://github.com/zonezoen/refine-rag往期系列文章• 008-03RAG 入门-向量存储中向量搜索和度量方法• 008-02RAG 入门-向量存储索引算法详解• 008-01RAG 入门-向量存储与企业级向量数据库 milvus• 007RAG 入门-向量嵌入与检索• 006RAG 入门-面试官问你RAG 为什么要切块目录• 前言• 什么是查询构建• 为什么需要查询构建• 三种核心技术• Text-to-SQL自然语言转SQL• Text-to-Cypher自然语言转图查询• 元数据过滤器构建• 技术对比与选择• 最佳实践• 学习路径前言前面我们学习了向量存储和搜索方法知道如何高效地存储和检索向量。但有个问题纯向量检索虽然能理解语义但在处理精确查询时准确率不够高。比如用户问有多少个演员的年龄超过30岁向量检索可能找到相关文档但无法给出精确的数字答案。这时候就需要查询构建技术将自然语言转换为结构化查询结合向量检索的语义理解和结构化查询的精确性。环境准备依赖库安装# Text-to-SQL # 使用 LangChain pip install langchain langchain-community langchain-deepseek # Text-to-Cypher图数据库 pip install neo4j # Neo4j 驱动 pip install langchain-community # GraphCypherQAChain # 元数据过滤器 pip install langchain langchain-core langchain-deepseek pip install langchain-chroma chromadb # 向量数据库 pip install langchain-huggingface sentence-transformers # 嵌入模型 pip install langchain-community # SelfQueryRetriever pip install youtube-transcript-api pytube # YouTube 数据加载示例需要环境变量配置# .env 文件 DEEPSEEK_API_KEYyour_deepseek_api_key # 如果使用 Neo4j NEO4J_URIbolt://localhost:7687 NEO4J_USERNAMEneo4j NEO4J_PASSWORDyour_password数据库准备Text-to-SQL 需要• SQLite 数据库文件90-文档-Data/tourism.db• 或者其他关系型数据库MySQL、PostgreSQL 等Text-to-Cypher 需要• 运行中的 Neo4j 图数据库• 安装docker run -p 7474:7474 -p 7687:7687 neo4j什么是查询构建查询构建就是把自然语言查询转换为结构化查询语言SQL、Cypher、元数据过滤器等。通俗比喻你去餐厅点菜说我要一份不辣的、素的、便宜的菜。服务员把这翻译成SELECT * FROM menu WHERE spicy false AND vegetarian true AND price 30 ORDER BY price ASC为什么需要查询构建向量检索 vs 结构化查询特性向量检索结构化查询数据类型非结构化文本结构化数据表格、图查询方式语义相似度精确匹配、聚合、统计准确性中等85-90%高95-99%适用场景文档检索、语义搜索数据分析、精确查询示例对比查询有多少个演员的年龄超过30岁向量检索• 找到包含演员、年龄、30岁的文档• 可能找到演员的平均年龄是28岁不准确结构化查询SQLSELECT COUNT(*) FROM actors WHERE age 30;• 精确统计结果准确三种核心技术1. Text-to-SQL自然语言转SQL目标将自然语言问题转换为SQL查询语句代码示例Text2SQL/02-Text2SQL-LLM-DeepSeek.py工作原理自然语言: 查询太原市的AAAAA级景区及其当月游客量 ↓ LLM 分析数据库结构 ↓ 理解查询意图: - 目标表: scenic_spots - 过滤条件: city太原市 AND levelAAAAA - 返回字段: scenic_name, monthly_visitors ↓ 生成 SQL: SELECT scenic_name, monthly_visitors FROM scenic_spots WHERE city 太原市 AND level AAAAA; ↓ 执行查询并返回结果核心代码# 准备Schema描述 schema_description 你正在访问一个包含两张表的数据库 1. scenic_spots景区信息表 - scenic_id (INT): 主键 - scenic_name (VARCHAR): 景区名称 - city (VARCHAR): 所在城市 - level (VARCHAR): 景区等级 - monthly_visitors (INT): 当月游客量 # 用户查询 user_query 查询太原市的AAAAA级景区及其当月游客量 # 生成SQL的提示词 prompt f 以下是数据库的结构描述 {schema_description} 用户的自然语言问题{user_query} 请只返回SQL查询语句不要包含任何其他内容。 # 调用LLM生成SQL response client.chat.completions.create( modeldeepseek-chat, messages[ {role: system, content: 你是SQL专家。只返回SQL语句。}, {role: user, content: prompt} ], temperature0 ) sql response.choices[0].message.content.strip() print(f生成的SQL: {sql}) # 执行SQL cursor.execute(sql) results cursor.fetchall()关键技术点1. Schema理解• 提供清晰的表结构描述• 说明字段含义和数据类型• 解释表之间的关系2. SQL验证• 语法检查• 安全性检查防止SQL注入• 权限检查3. 结果转换# 将SQL结果转换为自然语言 nl_prompt f 查询结果{results} 原始问题{user_query} 请将这些数据转换为自然语言描述。 优点与缺点优点• 精确查询准确率95-99%• 支持复杂查询聚合、统计、关联• 适合结构化数据• 可以处理数值比较、范围查询缺点• 需要结构化数据库• LLM 可能生成错误的SQL• 需要理解数据库结构• 不适合非结构化文本适用场景• 数据库问答系统• 商业智能报表• 数据分析助手• 企业数据查询实际效果查询类型向量检索准确率SQL查询准确率提升数值比较45%92%104%聚合统计38%95%150%精确匹配72%98%36%2. Text-to-Cypher自然语言转图查询目标将自然语言转换为Cypher查询语言用于图数据库代码示例Text2Cypher/03-Text2Cypher-SNOMED-v2-成功.py工作原理自然语言: 孙悟空的师傅是谁 ↓ LLM 分析图结构 ↓ 理解查询意图: - 起始节点: 孙悟空 - 关系类型: 师徒关系 - 目标节点: 师傅 ↓ 生成 Cypher: MATCH (悟空:角色 {name: 孙悟空})-[:师徒]-(师傅) RETURN 师傅.name ↓ 执行查询并返回结果核心代码from langchain_community.graphs import Neo4jGraph from langchain.chains import GraphCypherQAChain # 连接Neo4j图数据库 graph Neo4jGraph( urlbolt://localhost:7687, usernameneo4j, passwordpassword ) # 创建Text-to-Cypher链 chain GraphCypherQAChain.from_llm(llm, graphgraph) # 自然语言查询 question 孙悟空的师傅是谁 result chain.invoke({query: question}) print(f答案: {result[result]})适用场景• 知识图谱问答• 社交网络分析• 推荐系统• 关系挖掘3. 元数据过滤器构建目标从查询中提取元数据过滤条件缩小检索范围代码示例构建元数据Filter/02-Query中生成元数据.py工作原理自然语言: 找出2023年发布的观看次数超过100000的Python教程视频 ↓ LLM 分析并提取元数据 ↓ 识别过滤条件: - publish_year: 2023 - view_count: 100000 - 内容关键词: Python教程 ↓ 生成过滤器: { publish_year: 2023, view_count: {$gt: 100000} } ↓ 在向量检索时应用过滤器 ↓ 返回符合条件的结果核心代码from langchain.chains.query_constructor.base import AttributeInfo from langchain.retrievers.self_query.base import SelfQueryRetriever # 定义元数据字段 metadata_field_info [ AttributeInfo( nameauthor, description视频作者名称, typestring ), AttributeInfo( namepublish_year, description视频发布年份, typeinteger ), AttributeInfo( nameview_count, description视频观看次数, typeinteger ), ] # 创建自查询检索器 retriever SelfQueryRetriever.from_llm( llm, vectorstore, document_contentsYouTube视频内容, metadata_field_infometadata_field_info, enable_limitTrue, verboseTrue ) # 使用自动提取元数据过滤条件 query 找出2023年发布的观看次数超过100000的Python教程视频 docs retriever.invoke(query)工作流程详解1. 元数据定义# 定义可过滤的字段 metadata_fields { author: string, # 作者 year: integer, # 年份 views: integer, # 观看次数 duration: integer # 时长 }2. 查询解析查询: 2023年发布的时长超过10分钟的Python视频 ↓ 解析为: - 语义查询: Python视频 - 元数据过滤: year2023 AND duration6003. 组合检索# 先用元数据过滤 filtered_docs filter_by_metadata(docs, metadata_filter) # 再用语义检索 results semantic_search(query, filtered_docs)优点与缺点优点• 显著减少检索范围• 提高检索精度• 支持复杂的过滤条件• 结合语义和精确匹配缺点• 需要预先定义元数据字段• 依赖元数据的质量• LLM 可能提取错误的过滤条件适用场景• 视频/音频检索按时长、发布日期过滤• 文档检索按作者、日期、类型过滤• 产品搜索按价格、品牌、评分过滤• 新闻检索按日期、来源、分类过滤实际效果测试查询找出观看次数超过100000的视频输出查询找出观看次数超过100000的视频 生成的过滤器 { filter: { view_count: {$gt: 100000} } } 结果 标题山西佛光寺 观看次数156789 发布日期2023-05-15技术对比技术数据类型查询类型准确率适用场景Text-to-SQL关系型数据库精确查询、聚合95-99%数据分析Text-to-Cypher图数据库关系查询90-95%知识图谱元数据过滤向量元数据混合查询85-90%文档检索如何选择技术决策树开始 ↓ 数据是否结构化 ├─ 是 ↓ │ 是否是关系型数据 │ ├─ 是 → Text-to-SQL │ └─ 否 → Text-to-Cypher图数据 └─ 否 ↓ 是否有元数据 ├─ 是 → 元数据过滤 └─ 否 → 向量检索场景推荐场景推荐技术理由数据库问答Text-to-SQL精确查询知识图谱Text-to-Cypher关系查询视频检索元数据过滤混合查询文档检索元数据过滤缩小范围实战技巧1. Text-to-SQL 的安全性问题SQL注入风险解决方案# 1. 使用参数化查询 cursor.execute(SELECT * FROM users WHERE id ?, (user_id,)) # 2. SQL验证 def validate_sql(sql: str) - bool: # 检查危险关键词 dangerous_keywords [DROP, DELETE, UPDATE, INSERT] sql_upper sql.upper() for keyword in dangerous_keywords: if keyword in sql_upper: return False return True # 3. 限制权限 # 使用只读数据库用户2. Schema描述优化基础版schema 表名: users, 字段: id, name, age优化版schema 表名: users用户表 字段说明: - id (INT): 用户唯一标识主键 - name (VARCHAR): 用户姓名 - age (INT): 用户年龄 - created_at (DATETIME): 注册时间 示例数据: id1, name张三, age25, created_at2023-01-15 常见查询: - 查询年龄大于30的用户: SELECT * FROM users WHERE age 30 - 统计用户总数: SELECT COUNT(*) FROM users 3. 错误处理和重试def execute_sql_with_retry(query: str, max_retries3): for attempt in range(max_retries): try: sql generate_sql(query) results execute_sql(sql) return results except Exception as e: if attempt max_retries - 1: # 将错误信息反馈给LLM让它重新生成 error_prompt f 生成的SQL执行失败 SQL: {sql} 错误: {str(e)} 请修正SQL语句。 # 重新生成SQL continue else: raise e4. 元数据字段设计好的元数据设计metadata { author: 张三, # 明确的字符串 publish_year: 2023, # 数值类型 category: 技术, # 分类 tags: [Python, AI], # 标签列表 view_count: 10000, # 统计数据 duration: 600 # 时长秒 }避免的设计metadata { info: 张三 2023年 技术类, # 混合信息难以过滤 stats: 10000次观看, # 包含单位难以比较 }常见问题Q1: LLM生成的SQL不正确怎么办答1.提供更详细的Schema描述2.添加示例查询3.使用Few-shot提示prompt f Schema: {schema} 示例1: 问题: 查询年龄大于30的用户 SQL: SELECT * FROM users WHERE age 30 示例2: 问题: 统计用户总数 SQL: SELECT COUNT(*) FROM users 现在请生成: 问题: {user_query} SQL: Q2: 如何处理复杂的多表关联查询答1.提供表关系说明schema 表1: users (用户表) 表2: orders (订单表) 关系: orders.user_id users.id 2.分步查询# 先查询用户 users query(找出年龄30的用户) # 再查询订单 orders query(f找出用户{user_ids}的订单)Q3: 元数据过滤器提取不准确怎么办答1.提供清晰的字段描述2.添加示例AttributeInfo( namepublish_year, description视频发布年份例如2023表示2023年发布, typeinteger )3.验证和修正def validate_filter(filter_dict): # 检查年份范围 if publish_year in filter_dict: year filter_dict[publish_year] if year 2000 or year 2024: # 修正为当前年份 filter_dict[publish_year] 2024 return filter_dictQ4: 如何评估查询构建的效果答# 准备测试集 test_cases [ { query: 查询年龄大于30的用户, expected_sql: SELECT * FROM users WHERE age 30, expected_results: [...] } ] # 评估准确率 correct 0 for case in test_cases: generated_sql generate_sql(case[query]) results execute_sql(generated_sql) # 比较结果 if results case[expected_results]: correct 1 accuracy correct / len(test_cases) print(fSQL生成准确率: {accuracy:.2%})总结核心要点1.Text-to-SQL适合关系型数据库精确查询2.Text-to-Cypher适合图数据库关系查询3.元数据过滤适合混合查询缩小范围选择建议• 结构化数据 精确查询 → Text-to-SQL• 图数据 关系查询 → Text-to-Cypher• 向量数据 元数据 → 元数据过滤• 非结构化文本 → 向量检索效果预期技术准确率提升适用数据量实现难度Text-to-SQL60-80%任意4/5Text-to-Cypher50-70%任意4/5元数据过滤30-50%任意3/5最佳实践1.提供详细的Schema描述2.添加示例查询3.实现错误处理和重试4.验证生成的查询语句5.限制查询权限安全性学习路径1. 简易 RAG 学习2. LCEL 语法学习3. LangChain 读取数据1. LangChain 读取文本数据2. LangChain 读取图片数据3. LangChain 读取 PDF 数据4. LangChain 读取表格数据4. 文本切块5. 向量嵌入与检索6. 向量存储7. 索引算法详解8. 搜索和度量方法9. 检索前处理1. 查询构建技术 ← 当前2. 查询翻译技术3. 查询路由技术10. 索引优化11. 检索后处理12. 响应生成13. 系统评估项目地址本文所有代码示例都在 GitHub 开源https://github.com/zonezoen/refine-rag欢迎 Star 和 Fork一起学习 RAG 技术

相关新闻