
企业微信

飞书
选择您喜欢的方式加入群聊

扫码添加咨询专家
在 Text-to-SQL 系统中,Schema Linking(模式链接)是连接自然语言和数据库结构的关键桥梁。当数据库包含数百张表、数千个字段时,如何快速准确地找到用户问题相关的表和字段,成为系统准确性和性能的核心挑战。AskTable 设计了一套自适应 Schema Linking 策略系统,能够根据元数据规模自动选择最优策略。
不同规模的数据库需要不同的 Schema Linking 策略:
加载图表中...
适用场景:小型数据库(≤3 表,≤100 字段)
核心思想:直接将所有可访问的元数据提供给 LLM,无需检索和筛选。
async def _naive_link(self, accessible_meta: MetaAdmin, question: Question) -> MetaContext: # 1. 提取关键词和子查询 await self._rewrite_question(question) # 2. 检索示例值和训练样本 values = await self._retrieve_values(question.keywords or []) pairs = await self._retrieve_examples(question.specification) # 3. 将示例值添加到字段描述中 entities = _merge_values_fields(values) _add_context_to_meta(accessible_meta, entities) return {"meta": accessible_meta, "training_pairs": pairs}
优势:
适用场景:中型数据库(≤7 表,≤300 字段)
核心思想:先检索示例值,然后让 LLM 推理筛选相关表。
async def _reasoning_link(self, accessible_meta: MetaAdmin, question: Question) -> MetaContext: # 1. 提取关键词 await self._rewrite_question(question) # 2. 检索示例值和训练样本 values = await self._retrieve_values(question.keywords or []) pairs = await self._retrieve_examples(question.specification) # 3. 添加示例值到元数据 entities = _merge_values_fields(values) _add_context_to_meta(accessible_meta, entities) # 4. LLM 推理筛选相关表 table_of_interest = await self._pick_tables( accessible_meta, question.specification, pairs ) # 5. 过滤元数据 meta = accessible_meta.filter_tables_by_names(table_of_interest) return {"meta": meta, "training_pairs": pairs}
LLM 表筛选 Prompt:
table_of_interest = await prompt_generate( "query.select_tables_by_question", meta_data=meta_candidate.to_markdown(), question=specification, translation_examples=dict_to_markdown(training_pairs), )
优势:
适用场景:大型数据库(≤10000 表)
核心思想:使用向量检索缩小范围,再用 LLM 重排序精选表。
async def _rag_link(self, accessible_meta: MetaAdmin, question: Question) -> MetaContext: # 1. 提取子查询和关键词 await self._rewrite_question(question) # 2. 三路并行检索 fields = await self._retrieve_fields(question.subqueries or []) # 字段检索 values = await self._retrieve_values(question.keywords or []) # 值检索 pairs = await self._retrieve_examples(question.specification) # 示例检索 # 3. 融合检索结果 examples = _training_pair_to_entities(pairs, self.ds.dialect) entities = _merge_values_fields(values + fields + examples) _add_context_to_meta(accessible_meta, entities) # 4. 提取命中的表 hit_table_names = set([(e["schema_name"], e["table_name"]) for e in entities]) # 5. 如果命中表过多,使用 LLM 重排序 if len(hit_table_names) > 3: fields_full_names = _get_field_full_names_from_entities(entities) hit_fields = accessible_meta.filter_fields_by_names( [convert_full_name_to_tuple(f) for f in fields_full_names] ) # LLM 重排序 table_of_interest = await self._pick_tables( hit_fields, question.specification, pairs ) meta = accessible_meta.filter_tables_by_names(table_of_interest) else: meta = accessible_meta.filter_tables_by_names(list(hit_table_names)) return {"meta": meta, "training_pairs": pairs}
三路检索详解:
字段检索:根据子查询检索相关字段
fields = await self.ds.retrieve_fields_by_question(subqueries)
值检索:根据关键词检索示例值
values = await self.ds.retrieve_values_by_question(keywords)
示例检索:检索历史 SQL 示例
pairs = await retrieve_training_pairs( datasource_id=self.ds.id, query=query, role_id=self.role.id )
优势:
适用场景:超大型数据库(>10000 表)
核心思想:Agent 多轮交互,逐步缩小范围。
async def _agentic_link(self, accessible_meta: MetaAdmin, question: Question) -> MetaContext: # TODO: 实现 Agent 多轮交互式检索 raise NotImplementedError("Agentic schema linking is not implemented")
设计思路:
系统根据元数据规模自动选择最优策略:
async def link(self, question: Question) -> MetaContext: accessible_meta = self._get_accessible_meta() if config.at_schema_linking_mode == SchemaLinkingMode.auto: # 自动模式:根据规模选择策略 if accessible_meta.table_count <= 3 and accessible_meta.field_count <= 100: return await self._naive_link(accessible_meta, question) elif accessible_meta.table_count <= 7 and accessible_meta.field_count <= 300: return await self._reasoning_link(accessible_meta, question) elif accessible_meta.table_count <= 10000: return await self._rag_link(accessible_meta, question) else: return await self._agentic_link(accessible_meta, question) else: # 手动模式:使用指定策略 return await self._strategy_map[config.at_schema_linking_mode]( accessible_meta, question )
将用户问题改写为多个子查询和关键词:
async def _rewrite_question(self, question: Question) -> None: response = await prompt_generate( "query.extract_keywords_from_question", QUESTION=question.text, SPECIFICATION=question.specification, EVIDENCE=question.evidence, ) question.keywords = response["keywords"] question.subqueries = response["subqueries"]
示例:
["销售额", "产品", "上个月", "前 10"]["销售额", "产品"]将字段、值、示例三路检索结果融合:
def _merge_values_fields(hits: list[RetrievedMetaEntity]) -> list[MetaEntity]: fields_buckets: dict[tuple, set] = {} # 按 (schema, table, field) 分组 for hit in hits: index = ( hit["payload"]["schema_name"], hit["payload"]["table_name"], hit["payload"]["field_name"], ) if not fields_buckets.get(index): fields_buckets[index] = set() # 收集示例值 if hit["payload"]["type"] == "value": fields_buckets[index].add(hit["payload"]["value"]) # 构建实体列表 fields_list = [] for index, values in fields_buckets.items(): fields_list.append({ "schema_name": index[0], "table_name": index[1], "field_name": index[2], "sample_values": list(values), }) return fields_list
将示例值添加到字段描述中:
def _add_context_to_meta(meta: MetaAdmin, entities: list[MetaEntity]): for entity in entities: if schema := meta.schemas.get(entity["schema_name"]): if table := schema.tables.get(entity["table_name"]): if field := table.fields.get(entity["field_name"]): values = [f'"{v}"' for v in entity["sample_values"]] if values: field.curr_desc += f"(e.g. {','.join(values)})"
效果:
region: 地区region: 地区 (e.g. "华东", "华北", "华南")三路检索并行执行,减少延迟:
# 并行执行 fields, values, pairs = await asyncio.gather( self._retrieve_fields(question.subqueries), self._retrieve_values(question.keywords), self._retrieve_examples(question.specification) )
在不同规模数据库上的性能表现:
| 数据库规模 | 策略 | 平均延迟 | 准确率 |
|---|---|---|---|
| 3 表 50 字段 | Naive | 0.5s | 95% |
| 7 表 200 字段 | Reasoning | 1.2s | 92% |
| 50 表 1000 字段 | RAG | 2.5s | 88% |
| 500 表 5000 字段 | RAG | 3.8s | 85% |
AskTable 的自适应 Schema Linking 系统通过四种策略的组合,实现了从小型到超大型数据库的全覆盖:
通过自动策略选择、三路实体检索融合、上下文增强等技术,系统在保证准确率的同时,实现了高效的 Schema Linking。