LlamaIndex SQL 检索器源码级实战指南:NLSQLRetriever / SQLRetriever / SQLParserMode 文本到 SQL 检索全解析

发布时间:2026/9/10 13:39:40
LlamaIndex SQL 检索器源码级实战指南:NLSQLRetriever / SQLRetriever / SQLParserMode 文本到 SQL 检索全解析 LlamaIndex SQL 检索器源码级实战指南NLSQLRetriever / SQLRetriever / SQLParserMode 文本到 SQL 检索全解析【免费下载链接】llama_indexLlamaIndex is the leading document agent and OCR platform项目地址: https://gitcode.com/GitHub_Trending/ll/llama_index本文以 docs/api_reference/api_reference/retrievers/sql.md 所声明的三个公开成员NLSQLRetriever、SQLParserMode、SQLRetriever为核心结合llama-index-core的源码实现系统讲解 LlamaIndex 如何通过检索器Retriever完成自然语言 → SQL → 结构化结果的完整链路。读者读完本文将掌握 SQL 检索器的两类 API 形态、四种解析模式相关机制、提示词定制方法以及如何将 SQL 检索器接入RetrieverQueryEngine构建可用的结构化问答系统。一、SQL 检索器在 LlamaIndex 中的定位在 LlamaIndex 的检索器体系中SQL 检索器专门负责与关系型数据库打交道。它位于 llama-index-core/llama_index/core/indices/struct_store/sql_retriever.py并通过 llama-index-core/llama_index/core/retrievers/init.py第 28–30 行导入、第 80–82 行导出对外公开from llama_index.core.retrievers import ( NLSQLRetriever, # 文本到 SQL 检索器 SQLParserMode, # SQL 解析模式枚举default / pgvector SQLRetriever, # 原始 SQL 检索器 )三者职责清晰公开成员输入输出核心职责SQLRetriever原始 SQL 字符串检索结果 Node 列表直接执行 SQL 并封装结果NLSQLRetriever自然语言问题检索结果 Node 列表LLM 生成 SQL → 解析 → 执行SQLParserMode枚举常量决定使用哪个解析器控制 LLM 响应到 SQL 的解析策略其中NLSQLRetriever内部组合了SQLRetriever与一个由SQLParserMode决定的解析器三者构成一条完整的调用链。二、SQLRetriever以原始 SQL 语句进行检索SQLRetriever源码见 sql_retriever.py是层级最底层的检索器它不生成 SQL只负责把传入的 SQL 字符串在数据库上执行并把结果转成NodeWithScore。2.1 构造参数SQLRetriever( sql_database: SQLDatabase, # SQL 数据库包装对象必需 return_raw: bool True, # 是否返回原始文本结果默认 True callback_manager: Optional[CallbackManager] None, )2.2 核心方法 retrieve_with_metadataretrieve_with_metadataL73-L105是执行主路径将字符串输入统一包装为QueryBundle调用self._sql_database.run_sql(query_bundle.query_str)执行 SQL得到(raw_response_str, metadata)根据return_raw决定返回形态。return_rawTrue默认返回单个NodeWithScore其TextNode.text为结果原始字符串metadata中携带sql_query、result行数据列表、col_keys列名列表并通过excluded_embed_metadata_keys与excluded_llm_metadata_keys排除这些字段避免它们参与后续嵌入与 LLM 上下文。return_rawFalse调用_format_node_resultsL57-L71为每一行结果生成一个TextNodetext留空metadata为{列名: 该行对应值}的字典即一行一个 Node的结构化形态。_retrieveL112-L115只返回 Node 列表并丢弃元数据aretrieve_with_metadataL107-L110目前直接同步委托未做真正异步化。2.3 底层执行器 SQLDatabaserun_sql最终落在SQLDatabase上该类定义于 llama-index-core/llama_index/core/utilities/sql_wrapper.py是 SQLAlchemy Engine 的轻量封装核心能力包括from_uri(database_uri, engine_argsNone, **kwargs)L131-L136从连接串直接构建例如SQLDatabase.from_uri(sqlite:///data.db)dialect属性L138-L141返回数据库方言名如sqlite、postgresql会被注入提示词模板get_usable_table_names()L143-L147返回可用表名集合get_single_table_info(table_name)L153-L194拼装表名 列含类型与注释 外键的表结构描述串作为 LLM 的 schema 上下文run_sql(command)L249-L281执行语句若返回行则对每个单元格按max_string_length默认 300做单词截断返回(结果字符串, {result: 行数据, col_keys: 列名})。构造SQLDatabase时支持include_tables/ignore_tables互斥同时传入会抛ValueError、sample_rows_in_table_info默认 3、indexes_in_table_info、view_support、custom_table_info等参数用于裁剪对 LLM 暴露的表与 schema 信息。三、NLSQLRetriever文本到 SQL 检索器NLSQLRetrieverL179-L456是 SQL 检索家族的核心入口它接受自然语言问题先由 LLM 生成 SQL再交给解析器与SQLRetriever完成执行。它同时继承BaseRetriever与PromptMixin因此既可作为标准检索器使用也支持提示词查询与热更新。3.1 完整构造参数NLSQLRetriever( sql_database: SQLDatabase, # 必需 text_to_sql_prompt: Optional[BasePromptTemplate] None, # 默认 DEFAULT_TEXT_TO_SQL_PROMPT context_query_kwargs: Optional[dict] None, # {表名: 表的补充上下文} tables: Optional[Union[List[str], List[Table]]] None, # 表名或 SQLAlchemy Table 列表 table_retriever: Optional[ObjectRetriever[SQLTableSchema]] None, # 动态选表检索器 rows_retrievers: Optional[dict[str, BaseRetriever]] None, # 表名 → 行向量检索器 cols_retrievers: Optional[dict[str, dict[str, BaseRetriever]]] None, # 表名 → 列 → 检索器 context_str_prefix: Optional[str] None, sql_parser_mode: SQLParserMode SQLParserMode.DEFAULT, llm: Optional[LLM] None, # 默认取 Settings.llm embed_model: Optional[BaseEmbedding] None, # 默认取 Settings.embed_model return_raw: bool True, handle_sql_errors: bool True, sql_only: bool False, callback_manager: Optional[CallbackManager] None, # 默认 Settings.callback_manager verbose: bool False, )关键初始化逻辑L226-L248内部实例化SQLRetriever(sql_database, return_rawreturn_raw)作为执行器llm、embed_model、callback_manager未显式传入时均回落至Settings全局配置通过_load_sql_parserL265-L274按sql_parser_mode选择解析器未知模式抛ValueError通过_load_get_tables_fnL276-L299构造取表函数若传了table_retriever则动态检索SQLTableSchema列表否则基于tables未指定时取全部可用表与context_query_kwargs构建静态 schema 列表。3.2 检索主流程 retrieve_with_metadataretrieve_with_metadataL301-L347完整执行自然语言 → SQL → 结果链路# 1. 包装 QueryBundle query_bundle QueryBundle(query_str) # 2. 组装表上下文schema 补充描述 可选相关行/列值 table_desc_str self._get_table_context(query_bundle) # 3. LLM 依据提示词生成响应 response_str self._llm.predict( self._text_to_sql_prompt, query_strquery_bundle.query_str, schematable_desc_str, dialectself._sql_database.dialect, ) # 4. 解析器提取纯 SQL sql_query_str self._sql_parser.parse_response_to_sql(response_str, query_bundle) # 5. 执行 SQL或仅返回 SQL if self._sql_only: ... # 返回承载 SQL 文本的 Nodemetadata[result] sql_query_str else: retrieved_nodes, metadata self._sql_retriever.retrieve_with_metadata(sql_query_str)_retrieve/_aretrieveL393-L401是对retrieve_with_metadata的薄封装异步版本aretrieve_with_metadataL349-L391使用llm.apredict与sql_retriever.aretrieve_with_metadata走真正异步路径。返回值统一为(retrieved_nodes, {sql_query: sql_query_str, **metadata})——无论是否sql_onlysql_query键都会出现在元数据中方便上层拿到实际生成的 SQL。3.3 提示词管理PromptMixinNLSQLRetriever实现了 PromptMixin 协议L250-L263_get_prompts()返回{text_to_sql_prompt: ...}_update_prompts(prompts)支持在运行时替换text_to_sql_prompt。这意味着可以通过继承类的update_prompts({text_to_sql_prompt: my_prompt})方式热替换提示词而无需重建检索器。四、SQLParserMode 与 SQL 解析器实现SQLParserMode是str与Enum的双重子类L118-L122class SQLParserMode(str, Enum): DEFAULT default # 常规文本到 SQL PGVECTOR pgvector # 支持 pgvector 向量检索的 SQL它决定了_load_sql_parser实例化哪个BaseSQLParser子类。4.1 解析器抽象基类BaseSQLParserL125-L130继承DispatcherSpanMixin具备可观测性 span 能力声明抽象方法parse_response_to_sql(response, query_bundle) - str。开发者可以继承它实现自定义解析逻辑再通过扩展_load_sql_parser接入。4.2 DefaultSQLParser提取标准格式中的 SQLDefaultSQLParserL133-L147的核心逻辑在 LLM 响应中定位SQLQuery:标记截取其后的内容若随后出现SQLResult:标记则截断到该位置即丢弃执行结果与最终答案部分移除sql 与代码块围栏并strip()。该解析器假定 LLM 严格遵循提示词中规定的Question / SQLQuery / SQLResult / Answer四段式输出格式。4.3 PGVectorSQLParser注入查询向量占位符PGVectorSQLParserL150-L176面向 pgvector 扩展它复用与默认解析器相同的定位/截断逻辑但额外做一步——用嵌入模型对当前问题计算查询向量query_embedding self._embed_model.get_query_embedding(query_bundle.query_str) query_embedding_str str(query_embedding) return raw_sql_str.replace([query_vector], query_embedding_str)即 LLM 在 SQL 中以[query_vector]占位符表示查询向量如ORDER BY embedding - [query_vector] LIMIT 5解析器在运行时把占位符替换为真实向量字符串。该模式需要构造时传入或回落到Settings的embed_model。4.4 配套的 pgvector 提示词仓库在 llama-index-core/llama_index/core/prompts/default_prompts.py 中提供了与PGVectorSQLParser配套的DEFAULT_TEXT_TO_SQL_PGVECTOR_TMPL它在常规 text-to-sql 约束只使用 schema 中列、列归属正确表、必要时用表名限定列之上明确告知 LLM 可以使用embedding - [query_vector]这类 pgvector 距离运算符进行最近邻/语义检索并要求不要直接填写向量值而应使用[query_vector]占位符。使用该模式时建议同时传入此提示词保证 LLM 输出格式与解析器预期一致。五、表选择策略与上下文增强_get_table_contextL403-L456负责把给 LLM 看什么组装成schema变量。它有三级增强手段对应三个构造参数1. 静态表清单 表描述tables / context_query_kwargs每个表先通过sql_database.get_single_table_info生成列与外键描述若该表在context_query_kwargs中配置了context_str会追加一句 The table description is: {context_str}帮助 LLM 理解表语义例如该表存储 2019 年各城市人口统计。2. 行级增强rows_retrieversrows_retrievers是{表名: 向量检索器}映射。对每个入选表用当前问题检索出最相关的若干行注入Here are some relevant example rows (values in the same order as columns above)段落。这相当于把少样本示例行喂给 LLM显著提升列值映射准确率例如问题中出现Tokyo时相关行会让 LLM 知道city_name列存的是英文城市名。3. 列值增强cols_retrieverscols_retrievers是{表名: {列名: 检索器}}映射对每个文本列用问题检索相关取值注入Here are some relevant values of text columns段落。适用于枚举值/专有名词列的取值约束如status列的open/closed。关于context_str_prefix该参数在构造函数中被保存为self._context_str_prefixL231但从当前版本_get_table_context的实现看它尚未参与上下文字符串的拼接属于保留参数使用时请以实际版本行为为准。动态选表table_retriever若传入ObjectRetriever[SQLTableSchema]例如基于SQLTableNodeMapping 向量索引构建的检索器则每轮查询先由它从候选表中选出相关表再进入上述上下文组装否则每次把全部指定表塞进上下文。SQLTableSchema定义于 llama-index-core/llama_index/core/objects/table_node_mapping.py仅有table_name与可选context_str两个字段。六、实战示例从建库到检索仓库附带的完整可运行示例位于 docs/examples/index_structs/struct_indices/SQLIndexDemo.ipynb下面按其中思路给出最小可复现片段。6.1 建立 SQLDatabasefrom sqlalchemy import create_engine from llama_index.core import SQLDatabase engine create_engine(sqlite:///:memory:) # 建表、灌入 city_stats 数据 ... sql_database SQLDatabase(engine, include_tables[city_stats])include_tables会把可用表限定为city_stats若该表不存在SQLDatabase构造时会抛ValueError见 sql_wrapper.py。6.2 使用 NLSQLRetrieverreturn_rawTrue默认形态from llama_index.core.retrievers import NLSQLRetriever nl_sql_retriever NLSQLRetriever( sql_database, tables[city_stats], llmllm, # 可显式传入缺省用 Settings.llm return_rawTrue, ) results nl_sql_retriever.retrieve( Return the top 5 cities (along with their populations) with the highest population. ) # results 为单个 Nodetext 形如 [(Tokyo, 13960000), (Seoul, 9776000), ...] # 可通过 results[0].metadata 查看 {sql_query: ..., result: ..., col_keys: ...}6.3 使用 NLSQLRetrieverreturn_rawFalse结构化形态nl_sql_retriever NLSQLRetriever( sql_database, tables[city_stats], return_rawFalse ) results nl_sql_retriever.retrieve( Return the top 5 cities (along with their populations) with the highest population. ) # 每个 Node 一行node.metadata {city_name: Tokyo, population: 13960000}示例 notebook 的输出表明return_rawFalse时所有内容都在 metadata 里配合display_source_node(n, show_source_metadataTrue)即可逐行展示结果。6.4 只拿 SQL 不执行sql_onlyTrue当只需要 LLM 生成的 SQL、无需在数据库上执行时例如先人工审核 SQL 再放行设置sql_onlyTrue此时返回的 Node 文本即为 SQL 字符串metadata中result也直接是该 SQL。结合handle_sql_errors默认TrueSQL 执行失败时返回文本为Error: ...的错误 Node 而非抛出异常设为False则向上抛出原始异常可在生产流程中灵活控制失败行为。七、接入 RetrieverQueryEngine 完成问答SQL 检索器是标准BaseRetriever可直接与RetrieverQueryEngine组合得到与内置 Text-to-SQL 查询引擎效果相当的问答能力notebook 原文即compose our SQL Retriever with our standard RetrieverQueryEnginefrom llama_index.core.query_engine import RetrieverQueryEngine query_engine RetrieverQueryEngine.from_args(nl_sql_retriever, llmllm) response query_engine.query( Return the top 5 cities (along with their populations) with the highest population. ) print(str(response)) # The top 5 cities with the highest populations are: # 1. Tokyo - 13,960,000 # 2. Seoul - 9,776,000 # ...NLSQLRetriever提供的sql_query元数据在retrieve_with_metadata返回的 dict 中在组合到查询引擎后仍可被上层检索便于做结果溯源与 SQL 审计。八、错误处理、调试与可观测性verboseTrueretrieve_with_metadata会打印 Table desc str: ...与 Predicted SQL query: ...L311-L312_get_table_context会打印每个表的 Table Info: ...L452-L453是排查LLM 生成 SQL 不对的第一现场。handle_sql_errors默认捕获执行期异常并降级为错误 Node避免整条查询链路中断。可观测性NLSQLRetriever继承BaseRetriever的CallbackManager链路BaseSQLParser继承DispatcherSpanMixin解析阶段会以 span 形式暴露给 LlamaIndex 的 instrumentation 分发器便于接入 tracing。提示词热更新通过PromptMixin的update_prompts可在不重建实例的情况下替换text_to_sql_prompt。九、总结围绕 docs/api_reference/api_reference/retrievers/sql.md 声明的三个成员LlamaIndex 构建了一条可组合的 SQL 检索链路SQLRetriever提供最底层的执行 SQL → 封装 Node能力return_raw决定返回原始文本还是逐行结构化 NodeNLSQLRetriever在其之上叠加 LLM 生成 SQL、表上下文组装tables/context_query_kwargs/rows_retrievers/cols_retrievers/table_retriever、提示词管理与错误降级是实际业务中最常用的入口SQLParserModedefault/pgvector把LLM 响应 → 可执行 SQL的解析策略抽象为可插拔组件配合DEFAULT_TEXT_TO_SQL_PROMPT与DEFAULT_TEXT_TO_SQL_PGVECTOR_PROMPT两套提示词模板覆盖普通数据库与 pgvector 语义检索两种典型场景。实际落地时建议优先从SQLDatabase的include_tables收窄表范围、用context_query_kwargs补充表语义、再按需叠加rows_retrievers提供少样本示例行调试阶段开启verbose观察表上下文与预测 SQL生产阶段通过sql_only与handle_sql_errors控制 SQL 放行策略与失败行为。相关实现与示例分别位于 sql_retriever.py、sql_wrapper.py、default_prompts.py 与 SQLIndexDemo.ipynb可对照阅读。【免费下载链接】llama_indexLlamaIndex is the leading document agent and OCR platform项目地址: https://gitcode.com/GitHub_Trending/ll/llama_index创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考