返回文章列表

Text2SQL 新范式:可验证问数操作系统

用一条完整流程讲清 Text2SQL:从 schema 准备、RAG 检索、查询计划、SQL 生成、验证纠错,到回答、回放和持续优化。

Text2SQL 新范式:可验证问数操作系统

很多 Text2SQL demo 看起来都很顺:

用户问题 -> prompt -> LLM -> SQL -> execute -> answer

这条链路能跑通演示,却很难撑住真实业务问数。

真实业务里,用户问一句“最近 30 天各渠道的退款率怎么样”,系统要同时解决很多事:

退款率的业务口径是什么?
最近 30 天按哪个时区算?
渠道字段在哪张表?
订单表和退款表怎么 join?
当前用户能看哪些表和列?
应该生成哪种 SQL 方言?
执行失败能不能安全修复?
回答有没有忠于查询结果?
这次错误能不能变成下一轮评测样例?

所以,Text2SQL 的重心要从“让模型现场想出 SQL”,转向一套 Query Operating System:先把数据事实、业务语义、权限边界和历史证据整理成可检索资产,再把用户问题编译成可验证计划、SQL、结果和回放记录。

可以先记住这条主线:

数据源准备
-> schema catalog
-> 用户问题理解
-> RAG 检索候选事实
-> ACL 裁剪和上下文装箱
-> 查询计划
-> 结构化 SQL 生成
-> SQL 验证和有界纠错
-> 安全执行
-> 基于结果回答
-> replay / eval 持续优化
Text2SQL 新范式:可验证问数操作系统 flow 1

下面按这条线讲清楚。


一、完整流程:一次问题怎样变成可信回答

一次成熟的 Text2SQL 运行,大概会经过这些阶段:

Datasource Ready
-> Schema Ingestion
-> Active Schema Catalog
-> User Question
-> Resolve Workspace ACL
-> Extract Query Slots
-> Retrieve Knowledge
-> Build SelectedContextPack
-> Build Intent / Semantic / Physical Plan
-> Generate Structured SQL
-> Validate SQL
-> Correct SQL if Safe
-> Execute SQL
-> Format Grounded Answer
-> Persist Replay and Metrics
Text2SQL 新范式:可验证问数操作系统 flow 8

每一步都要有清楚的输入和输出。

阶段输入输出解决的问题
数据准备datasource、文件、数据库连接active schema catalog系统到底知道哪些表、列、关系和样例
问题理解用户自然语言QuerySlots用户要查指标、明细、趋势、对比还是排行
RAG 检索QuerySlots、schema、业务词、历史样例RetrievalCandidates从大量数据资产里找相关候选
上下文装箱candidates、ACL、budgetSelectedContextPack本轮模型可以看到哪些可信材料
查询计划slots、context、policyIntentPlan / SemanticPlan / PhysicalSqlPlanSQL 生成前先把意图、口径、表列和 join 固定下来
SQL 生成plan、context、output schemaSqlGenerationOutput生成结构化 SQL 草案
验证纠错SQL、schema、ACL、dialectValidationReport / CorrectionAttempt提前发现语法、表列、权限、方言和 limit 问题
执行回答final SQL、rows、columnsGroundedAnswer只基于执行结果回答
回放评测全链路证据Replay / EvalCase找到失败层,推动下一轮优化

这条流程里,LLM 只负责其中几段:理解、计划辅助、SQL 草案、必要时纠错、回答润色。事实源、权限边界、验证、执行和回放,都应该由系统控制。


二、先把数据源变成可问资产

Text2SQL 的准确率,第一步发生在用户提问之前。

如果系统只拿到一个数据库连接字符串,然后临时查几张表塞进 prompt,后面很容易出现表名幻觉、字段误选、指标口径混乱。更好的起点,是先把数据源沉淀成版本化的 schema catalog。

2.1 Schema Ingestion

数据源创建、文件上传、schema 刷新时,触发一次 ingestion:

关系型数据库:
  information_schema
  table comments
  column comments
  primary keys / foreign keys
  indexes
  approximate row count

CSV / Excel:
  headers
  inferred types
  sample values
  null ratio
  date format
  numeric distribution
  sheet metadata

文件型数据源尤其要重视类型推断。时间列、金额列、枚举列如果都被当成 TEXT,模型后面很难写对过滤、排序、聚合和日期函数。

2.2 Catalog Versioning

Schema catalog 要版本化:

building -> active
building -> failed
active -> archived

查询只读取 active 版本。这样 ingestion 失败时,线上查询仍然使用上一版完整 schema,不会读到半成品上下文。

2.3 Schema Enrichment

只保存表名、列名还不够。RAG 要能搜得准,catalog 里最好补齐这些信息:

displayName / 中文名
字段描述
示例值
空值率
枚举候选
时间字段识别
金额字段识别
主外键
join hint
敏感字段标签
业务标签
指标定义
历史成功 SQL 样例

这些信息会变成后续 RAG 的检索对象。

Text2SQL 新范式:可验证问数操作系统 flow 9

三、问题理解:先抽槽位,再决定是否澄清

用户问题先投影成 QuerySlots,不要直接丢给 SQL 生成器。

interface QuerySlots {
  queryType: "lookup" | "detail" | "aggregate" | "trend" | "compare" | "ranking" | "unknown";
  metrics: string[];
  dimensions: string[];
  filters: Array<{
    field?: string;
    operator?: string;
    value?: string;
  }>;
  timeRange?: {
    start?: string;
    end?: string;
    grain?: "day" | "week" | "month" | "quarter" | "year";
  };
  entities: string[];
  sort?: {
    field?: string;
    direction?: "asc" | "desc";
  };
  limit?: number;
  ambiguities: Array<{
    type: "metric" | "dimension" | "time" | "table" | "filter" | "entity";
    message: string;
    candidates?: string[];
  }>;
  confidence: number;
}

举个例子:

用户:最近 30 天各渠道退款率怎么样?

QuerySlots:
  queryType: compare / trend
  metric: 退款率
  dimension: 渠道
  timeRange: 最近 30 天
  grain: day 或 total,取决于产品默认策略
  ambiguities:
    - 退款率可能有订单口径、金额口径

澄清不应该按问题长短决定。更好的规则是:

情况处理
找不到相关表、指标或业务词追问
一个业务词命中多个指标口径追问
缺少时间范围且问题依赖周期使用产品默认值并说明,或追问
候选唯一且证据高置信继续
轻微歧义但有安全默认继续,并在回答里列出假设
Text2SQL 新范式:可验证问数操作系统 flow 6

四、RAG 搜索:从“找相似文本”升级成“找可执行证据”

Text2SQL 里的 RAG,有一个很容易踩的坑:把向量库当成全部方案。

向量检索能解决自然语言和字段命名不一致的问题,但它只是一条召回通道。Text2SQL 还需要找齐可执行 SQL 所需的事实:

相关表
相关列
列类型
字段含义
业务指标
默认过滤
表关系
join path
历史成功 SQL 样例
当前用户权限
schema 版本

4.1 RAG 索引里应该放什么

建议把知识源拆成几类 chunk,每类都带上 metadata。

chunk 类型内容关键 metadata
TableCard表名、中文名、描述、业务标签、行数datasourceId、catalogVersionId、tableName、ACL scope
ColumnCard列名、类型、描述、示例值、枚举、统计tableName、columnName、semanticTags、sensitivity
RelationshipCardjoin 关系、主外键、人工 join hintfromTable、toTable、confidence、source
MetricCard指标名、同义词、公式、默认过滤、粒度metricId、requiredTables、requiredColumns、status
GlossaryCard业务词、别名、解释、关联字段或指标termId、synonyms、owner
SqlExampleCard历史问题、成功 SQL、使用表列、结果类型runId、schemaVersion、status、qualityScore
FailureCard失败类型、错误 SQL、修复方式errorCode、fixedBy、evalCaseId

一个 ColumnCard 可以长这样:

{
  "sourceType": "column",
  "datasourceId": "shop_prod",
  "catalogVersionId": "v42",
  "table": "orders",
  "column": "channel",
  "dataType": "varchar",
  "text": "orders.channel 渠道。示例值:app、web、mini_program。用于按渠道统计订单、销售额、退款率。",
  "semanticTags": ["channel", "dimension"],
  "acl": {
    "tableVisible": true,
    "columnVisible": true
  }
}

4.2 检索顺序:先权限,再召回,再排序

推荐流程不是一条简单流水线,而是一组安全和质量闸门:

顺序阶段中文含义详细解析
1QuerySlots查询槽位解析先把用户自然语言问题投影成结构化槽位,例如指标、维度、过滤条件、时间范围、排序意图、明细或聚合意图。后续检索不再只依赖原句,而是围绕这些槽位找证据。
2query expansion查询扩展基于槽位扩展同义词、业务别名、缩写、中英文表达和历史常用说法。例如“退款率”可能扩展出“refund_rate”“售后退款占比”“退款订单数除以订单数”等候选表达,用来提升召回覆盖率。
3metadata prefilter元数据预过滤在真正召回前,先用 workspaceIddatasourceIdcatalogVersionId、数据域、表类型、时间分区等元数据缩小搜索范围。这一步既降低噪声,也可以提前排除明显不属于当前用户和当前数据空间的资产。
4multi-lane retrieval多路召回同时从多种通道找候选材料,例如 schema 字段、业务指标定义、join path、历史 SQL 样例、语义 chunk、血缘关系和 dashboard 配置。不同通道解决不同问题:字段名靠 schema,业务口径靠指标定义,复杂连接靠 join path。
5candidate normalization候选归一化把不同通道返回的结果整理成统一结构,包括候选 ID、来源类型、文本内容、相关分数、表列元数据、权限元数据和版本信息。只有格式统一,后续过滤、融合、排序和装箱才不会各做各的。
6ACL filtering访问控制过滤根据用户身份、角色、组织、项目、表权限、列权限和行级策略过滤候选。这里的目标不是等 SQL 执行时再拦截,而是避免未授权 schema、字段说明和业务口径进入模型上下文。
7dedupe候选去重去掉重复表、重复字段说明、同一指标的多份旧版本、多个通道召回的相同样例。去重可以节省 token,也能避免某类候选因为重复出现而在后续排序中被错误放大。
8fusion结果融合将多路召回结果合并,并综合各通道分数。常见做法包括 RRF、加权融合和规则加权,让关键词强匹配、语义强匹配、历史样例强匹配都能被公平纳入候选池。
9rerank重排序用更精细的排序模型或规则重新评估候选价值。召回阶段重在“找得全”,重排序阶段重在“排得准”,通常会综合问题匹配度、字段覆盖度、业务权威性、版本新鲜度和 SQL 可生成性。
10diversity control多样性控制防止最终上下文只来自同一张表、同一种证据或同一批历史样例。Text2SQL 需要覆盖指标定义、相关字段、连接关系、过滤条件和样例 SQL,因此上下文要有结构性多样性。
11token budget packingToken 预算装箱在上下文长度限制内选择最有价值的材料,并决定保留顺序、裁剪方式和摘要粒度。这里追求的是信息密度,而不是把排序最高的候选从上到下直接塞满。
12SelectedContextPack最终上下文包输出本轮 SQL 生成真正可以使用的可信材料包,通常包含选中的 schema、指标定义、join path、样例 SQL、权限状态、来源 ID 和排序依据。后续计划生成和 SQL 生成都应该只依赖这个上下文包。
Text2SQL 新范式:可验证问数操作系统 flow 7

其中最关键的是两层权限:

检索前:用 datasourceId、workspaceId、catalogVersionId 做 metadata prefilter。
检索后:再按 table ACL、column ACL、row policy metadata 做精确过滤。

不要让未授权 schema 进入 selected context。执行阶段拒绝 SQL 只能防止数据泄露,无法防止 schema 泄露。

4.3 多路召回:不要只靠一种检索

Text2SQL 的候选召回建议至少有四路:

召回通道适合解决例子
Lexical / BM25表名、列名、中文名、业务词直接命中“渠道”命中 orders.channel
Dense Embedding用户口语和 schema 描述不一致“从哪里来的订单”命中 channel / source
Graph Retrieval多表 join、邻接表扩展orders -> refunds -> order_items
SQL Example Retrieval复用相似问法和 SQL 结构“各渠道退款率”命中历史退款率 SQL

真实查询经常需要组合:

用户问“各渠道退款率”

Lexical:
  渠道 -> orders.channel
  退款 -> refunds / refund_amount

Dense:
  退款率 -> refund rate metric

Graph:
  orders.id -> refunds.order_id

SQL Example:
  “按渠道看退款金额占比” -> 可复用 group by channel 的 SQL 结构

4.4 Fusion:把多路结果合成候选池

多路召回的分数尺度不同,不能直接相加。可以先用 RRF 做稳定融合:

score(candidate) =
  w_lexical * rrf_rank_lexical
+ w_dense   * rrf_rank_dense
+ w_graph   * rrf_rank_graph
+ w_example * rrf_rank_example
+ boosts
- penalties

常见加分项:

表和列同时命中
命中 active MetricDefinition
命中人工维护的 join hint
命中高质量历史 SQL
候选覆盖 QuerySlots 里的指标、维度、过滤条件

常见扣分项:

schema 版本过旧
metric 已 deprecated
example 执行失败过
候选只命中低价值 sample value
候选表过大且缺少过滤条件
候选之间缺 join path

4.5 Rerank:排序要看“能否生成 SQL”

普通 RAG 的 rerank 常看语义相关性。Text2SQL 的 rerank 还要看可执行性:

这个候选能不能补齐指标?
能不能补齐维度?
表之间有没有 join path?
字段类型是否支持当前操作?
当前用户是否可见?
是否会挤爆 context budget?
是否和其他候选重复?

可以用一个轻量 reranker 给候选打标签:

type RetrievalCandidateScore = {
  candidateId: string;
  relevance: number;
  executableUsefulness: number;
  evidenceQuality: number;
  aclSafe: boolean;
  stale: boolean;
  reason: string;
};

最后进入 prompt 的应该是整理后的 SelectedContextPack,避免直接塞一堆原始 chunk:

interface SelectedContextPack {
  datasourceId: string;
  catalogVersionId: string;
  dialect: "sqlite" | "mysql" | "postgresql";
  selectedTables: Array<{
    name: string;
    description?: string;
    columns: Array<{
      name: string;
      type: string;
      description?: string;
      sampleValues?: string[];
    }>;
  }>;
  relationships: Array<{
    from: string;
    to: string;
    joinHint: string;
    confidence: number;
  }>;
  metrics: Array<{
    name: string;
    formula: string;
    requiredTables: string[];
    requiredColumns: string[];
  }>;
  examples: Array<{
    question: string;
    sql: string;
    tables: string[];
    columns: string[];
    score: number;
  }>;
  evidence: Array<{
    sourceType: "schema" | "relationship" | "metric" | "sql_example" | "glossary";
    sourceId: string;
    reason: string;
    score: number;
  }>;
}

4.6 Context Budget:少而准,比多而杂更重要

建议按优先级装箱:

优先级内容处理
P0当前问题、SQL 方言、安全规则、输出 schema必留
P0ACL 过滤后的候选表列必留
P1命中的指标、业务词、join path优先保留
P1Physical plan 需要的表列优先保留
P2高质量历史 SQL 样例控制数量
P2sample values、枚举值按需保留
P3低分候选、重复描述丢弃

一个实用规则:SQL example 不要超过 2-3 个。样例太多时,模型容易模仿错数据源、错表名或旧口径。

4.7 SQL Example 的过滤规则

历史 SQL 很有价值,也最容易污染上下文。

进入 few-shot 之前至少过滤掉:

执行失败的 run
ACL 拒绝的 run
使用 deprecated schema 的 run
引用当前用户无权表列的 run
人工标记低质量的 run
结果为空但原因不明的 run
来自临时试错会话的 run

可以保留的样例:

执行成功
通过 ACL
schema version 仍兼容
指标口径明确
SQL 可解释
人工或 eval 验证质量高

4.8 RAG 怎么优化

优化 RAG 不能只看“召回相似文本是否变多”。Text2SQL 要按失败层定位:

现象可能原因优化动作
SQL 选错表table recall 低、表描述弱补表中文名、业务标签、BM25 同义词、table-card
SQL 选错列column recall 低、字段解释弱补列描述、示例值、semanticTags、列级 rerank
指标公式错metric 缺失或排序低建 MetricDefinition,给指标更高权重
join 错graph retrieval 弱补 SchemaRelationship、join hint、join eval case
prompt 太长候选太多、去重差dedupe、diversity cap、context budget、example 限量
相似样例误导example 过旧或质量差给 SQL example 加 status、schemaVersion、qualityScore
权限风险过滤只在执行阶段做检索前 metadata filter + 检索后 ACL filter
空结果多时间/过滤槽位默认不清QuerySlots 默认策略、澄清策略、answer explanation

评测也要分层:

table recall@k
column recall@k
metric hit rate
join path hit rate
selected context precision
ACL leakage rate
SQL execution success rate
correction success rate
answer faithfulness

只要能把每次失败归到这些指标上,RAG 优化就会从“调 prompt”变成“修资产、修召回、修排序、修验证”。


五、从上下文到 SQL:计划先行

拿到 SelectedContextPack 后,不要直接让模型写 SQL。先生成计划。

QuerySlots
-> IntentPlan
-> SemanticPlan
-> PhysicalSqlPlan
-> SqlGenerationOutput
Text2SQL 新范式:可验证问数操作系统 flow 11

5.1 IntentPlan:确定查询形态

interface IntentPlan {
  queryType: "detail" | "aggregate" | "trend" | "compare" | "ranking";
  metrics: string[];
  dimensions: string[];
  filters: string[];
  timeRange?: string;
  sort?: string;
  limit?: number;
}

5.2 SemanticPlan:把业务词绑定证据

resolvedMetrics:
  - userTerm: "退款率"
    metricName: "refund_rate"
    formula: "SUM(refunds.amount) / SUM(orders.amount)"
    evidenceRefs:
      - metric:refund_rate
      - column:refunds.amount
      - column:orders.amount

resolvedDimensions:
  - userTerm: "渠道"
    table: "orders"
    column: "channel"
    evidenceRefs:
      - column:orders.channel

这里的重点是 evidence。没有 evidence 的语义解析,很容易变成模型现场猜。

5.3 PhysicalSqlPlan:约束 SQL 的形状

interface PhysicalSqlPlan {
  dialect: string;
  tables: string[];
  columns: string[];
  joins: Array<{
    type: "inner" | "left";
    leftTable: string;
    rightTable: string;
    on: string;
  }>;
  aggregations: string[];
  filters: string[];
  groupBy: string[];
  orderBy: string[];
  limit: number;
}

生成 SQL 前先校验 plan:

表是否存在?
列是否存在?
join 是否有依据?
metric formula 是否有字段支持?
是否引用未授权对象?
limit 是否符合策略?
dialect 是否匹配?

Plan 合法,再进入 SQL 生成。


六、SQL 生成、验证和有界纠错

SQL 生成不要依赖 Markdown code block。让模型输出结构化 JSON:

interface SqlGenerationOutput {
  sql: string;
  explanation: string;
  referencedTables: string[];
  referencedColumns: Record<string, string[]>;
  assumptions: string[];
  confidence: number;
  cannotAnswerReason?: string;
}

模型必须遵守这些约束:

只能使用 selected context 中的表、列、指标和关系。
上下文不足时返回 cannotAnswerReason。
SQL 必须只读。
SQL 必须符合目标 dialect。
明细查询必须包含 LIMIT。
referencedTables / referencedColumns 要和 SQL parser 结果一致。

SQL 出来后进入 validator:

SQL text
-> normalize
-> dialect parse
-> readonly check
-> single statement check
-> referenced object extraction
-> schema existence check
-> ACL check
-> limit policy
-> dry-run / explain
-> executable SQL

错误要分类。安全错误直接拒绝,可修复错误进入有界纠错:

错误类型处理
SQL_SYNTAX_ERROR纠错
UNKNOWN_TABLE / UNKNOWN_COLUMN回到 selected context 纠错
AMBIGUOUS_COLUMN补表别名
GROUP_BY_ERROR修聚合
DIALECT_ERROR按方言修复
LIMIT_POLICY_VIOLATION注入或裁剪 limit
READONLY_VIOLATION拒绝
ACL_FORBIDDEN拒绝
EMPTY_RESULT进入回答解释

纠错最多 1-2 次,输入必须包含原问题、selected context、原 SQL、验证错误和方言规则。纠错 prompt 还要明确限制:

只在 selected context 内修复。
不能新增未授权表列。
不能绕过 readonly 和 limit 策略。
不能把 ACL 错误当成可修复错误。
Text2SQL 新范式:可验证问数操作系统 flow 12

七、回答也要基于证据

很多系统前面做对了,最后一步回答又开始自由发挥。

Answer Formatter 的输入应该收窄到:

question
finalSql
columns
rows
assumptions
metric definitions used
execution status

回答规则:

不能补充 rows/columns 里没有的数据。
不能把 assumptions 写成事实。
不能隐藏权限拒绝、执行失败或空结果。
趋势、对比、排名要说明口径和排序依据。
空结果要解释当前条件下没有匹配数据,并建议用户调整时间或过滤条件。

空结果尤其要单独处理。rows.length === 0 代表 SQL 成功执行但没有匹配数据,它应该进入回答解释,不能自动触发重写 SQL。


八、Replay:让错误变成下一轮能力

Text2SQL 很难靠一次 prompt 写准。它要靠 replay 把失败变成资产。

每次运行都应该保存:

runId
question
actor / workspace / datasource
schema catalog version
query slots
retrieval queries
retrieval candidates
selected context
intent / semantic / physical plan
generated SQL
validation results
correction attempts
final SQL
execution result
answer
latency / token / cost
final status
Text2SQL 新范式:可验证问数操作系统 flow 13

Replay 的关键价值是定位失败层:

意图理解错?
RAG 没召回正确表?
RAG 召回了但 rerank 排低?
SelectedContextPack 漏了 join path?
MetricDefinition 口径错?
SQL 生成没遵守 plan?
Validator 漏掉了未知列?
纠错引入了新错误?
Answer Formatter 超出结果集?

每类失败都能转成不同资产:

失败层沉淀资产
问题理解错QuerySlots 训练样例、澄清规则
表列召回失败schema chunk、同义词、BM25 权重
指标口径错MetricDefinition、业务词典
join 错SchemaRelationship、join hint
SQL 生成错few-shot example、output contract、plan validation
纠错失败correction eval case、error taxonomy
回答幻觉answer faithfulness test

九、落地顺序:先做能闭环的 MVP

别一开始就做完整混合检索和自动优化。第一版先把边界立住。

P0:Schema Catalog + ACL Context Pack

目标:降低表名、列名幻觉,防止未授权 schema 进入 prompt。

交付:

SchemaCatalogVersion / SchemaTable / SchemaColumn / SchemaRelationship
数据源创建后自动 schema ingestion
文件型数据源 type inference
ContextPackBuilder
workspace / table / column ACL 裁剪
Prompt Builder 注入 selected context
SQL validator 检查表列存在性
trace 记录 contextPackSummary 和 evidence

验收:

指标目标
未授权表进入 prompt0
表名幻觉率明显下降
列名幻觉率明显下降
SQL 执行成功率提升
runId 可查看 context summary

P1:QuerySlots + Plan Validation + SQL Correction

目标:让系统知道自己缺什么,也知道哪些 SQL 错误可以修。

交付:

QuerySlots
schema-aware clarification
IntentPlan / SemanticPlan / PhysicalSqlPlan
PlanValidation
SQL parser / dialect parser
dry-run / explain
execution error taxonomy
diagnose-sql / correct-sql,最多 2 次

P2:Lexical Retrieval + SQL Examples

目标:解决 schema 变大后的上下文选择问题。

交付:

TableCard / ColumnCard / MetricCard / SqlExampleCard
lexical retrieval
metadata prefilter
ACL filter
simple rerank
selected context replay

P3:Hybrid Retrieval + Semantic Model

目标:解决口语表达、复杂指标、多表 join。

交付:

dense embedding
lexical + dense + graph 多路召回
RRF fusion
rerank
MetricDefinition / GlossaryTerm / SemanticJoinPath
retrieval metrics

P4:Eval Replay + 自动优化

目标:把运行日志变成回归测试和知识资产。

交付:

Text2SqlReplay
EvalCase / EvalDataset
runId replay API
promote failed run to eval case
table / column / metric / join hit rate
answer faithfulness check

十、架构边界建议

最后,模块边界可以这样分:

API / Chat Controller
-> Text2SQL Agent Runtime
   -> Intent Service
   -> Knowledge / Retrieval Service
   -> Policy Service
   -> Query Compiler
   -> Planner Service
   -> SQL Generator
   -> SQL Validator
   -> Query Executor
   -> Answer Formatter
   -> Replay / Eval Service
Text2SQL 新范式:可验证问数操作系统 flow 14

其中最容易长成泥球的是 ChatService、PromptBuilder 和 RetrievalService。一个简单判断方法:

ChatService 只管会话和编排。
Knowledge Service 管 schema、指标、样例和检索。
Policy Service 管可见性和执行授权。
Query Compiler 管 selected context。
Planner 管意图、语义和物理计划。
Validator 管 SQL 是否安全可执行。
Replay 管证据链和评测样例。

做到这里,Text2SQL 就会从“自然语言转 SQL 功能”,升级成一套能解释、能验证、能修复、能持续变准的问数操作系统。