提供一条较为完整的工程闭环:
自然语言问题 -> Schema 理解 -> SQL 生成 -> 执行反馈 -> 结果校验 -> 轨迹留存
当前仓库已经具备可运行的 Web 入口、API 通路、Agent 通路、评测通路,以及多数据源扩展与结果追踪能力。
这个项目聚焦 DB-NLI(Database Natural Language Interaction),即让用户直接使用自然语言完成数据库查询、统计分析和结果呈现准备。
相对“单次生成 SQL”的轻量方案,本项目更强调以下工程特性:
- 可执行:生成结果最终要落到数据库执行;
- 可修复:执行失败或结构不匹配时可进入修复链路;
- 可约束:通过输出合同约束列名、顺序和结构;
- 可回归:支持批量评测与结果对比;
- 可扩展:支持多数据集注册、多数据库适配与公开代理数据接入;
- 可追踪:保留 grounding、生成、修复、执行、评测等关键轨迹。
- 自然语言问题理解与意图分析
- Schema grounding / 表列召回
- SQL 自动生成与非 SQL 输出重试
- 多候选 SQL 生成与重排
- 执行失败自动修复
- 输出列合同校验与结构修复
- 结果规范化与最终产物落盘
- Flask Web 服务入口
- Vue 前端壳层
- 单轮查询与多步 Agent 两条运行通路
- 本地 mini-eval 与专项评测
- 审计日志与历史记录存储
- RBAC / 语义视图 / 治理层基础设施
- 多数据库适配层与外部数据集注册机制
当前仓库存在 4 条主要通路:
| 通路 | 入口 | 适用场景 | 核心特点 |
|---|---|---|---|
| 遗留聊天通路 | /chat |
快速体验 | 基于 Vanna 原生页面,接入成本低 |
| Fast 查询通路 | POST /api/query + mode=fast |
常规查询 | 单轮闭环,响应直接 |
| Agent 查询通路 | POST /api/query + mode=agent |
复杂问题 | 多步观察、诊断、修复 |
| Eval 通路 | eval/run_mini_eval.py |
回归验证 | 批量执行、统一报告输出 |
- 入口:
app.py中的main()→VannaFlaskApp(vn) - 触发方式:用户直接在
/chat的 Vanna 内置聊天框输入问题 - 典型内建路由:
/generate_sql、/run_sql
数据流:
用户问题
-> Vanna.generate_sql(question)
-> Vanna.run_sql(sql)
-> 返回 SQL + 表格 + 图表
关键代码:
| 位置 | 作用 |
|---|---|
app.py JunoVanna |
组合 ChromaDB_VectorStore 与 OpenAI_Chat |
app.py create_vanna() |
创建 Vanna 实例并连接 SQLite |
app.py generate_questions() |
提供默认建议问题 |
app.py main() |
组装 VannaFlaskApp 并启动服务 |
这条通路的特点是:
- 使用门槛最低;
- 适合快速验证基础问答;
- 返回结果更接近“原生聊天 + SQL 展示”;
- 不经过统一的 Intent / Contract / Governance / Audit 链路;
- 没有统一审计、RBAC、语义视图和结构化追踪约束。
- 入口:
POST /api/query - API 路由:
nldb/api/query_routes.py - 核心方法:
nldb/query_tool.py中的JunoQueryTool.run_question() - 请求模式:
mode=fast
示例请求:
{
"question": "按区域统计事件数",
"mode": "fast",
"dataset_name": "juno_demo",
"user_role": "analyst",
"num_candidates": 1
}8 阶段完整链路:
| 阶段 | 核心动作 | 主要产物 |
|---|---|---|
| 1. Intent Analysis | analyze_question() |
IntentAnalysis,包含意图类型、预期列、top-k 等信息 |
| 2. Schema Linking & Grounding | ground_question() |
grounded_prompt、grounding_trace |
| 3. Output Contract | build_output_contract() |
OutputContract,约束列名、顺序、额外列策略 |
| 4. SQL Generation | generate_sql() / generate_sql_candidates() |
单候选或多候选 SQL |
| 5. Governed Execution | governed_execute() |
SQLResult(columns, rows) |
| 6. Execution Repair Loop | build_execution_repair_prompt() + 再生成 |
执行失败后的修复 SQL |
| 7. Contract Validation & Repair | validate_output_contract() + build_contract_repair_sql() |
列结构校验与结构修复 |
| 8. Value Normalization | build_value_normalization_sql() |
对数值、排序、维度列做规范化输出 |
关键类与数据结构:
| 类/函数 | 作用 |
|---|---|
JunoQueryTool |
Fast 通路核心类 |
RunContext |
单次调用上下文缓存,避免重复取 schema / profile |
QueryRunResult |
统一结果对象,包含 SQL、trace、结果表、状态 |
OutputContract |
输出结构合同 |
SQLResult |
执行结果容器 |
关键参数:
top_k_tables:Schema grounding 时选取的表数量;num_candidates:候选 SQL 数量,>1时进入多候选重排;max_generation_retries:生成失败后的重试次数;max_execution_repairs:执行修复次数;disable_grounding:关闭 schema grounding;disable_value_probe:关闭值域探测;forced_sql:跳过生成,直接验证指定 SQL;allow_extra_columns:放宽输出列严格匹配;user:RBAC / 治理上下文;extra_context:附加上下文知识。
状态值:
| 状态 | 含义 |
|---|---|
success |
执行成功且结构通过 |
contract_mismatch |
执行成功但结果列结构仍不匹配 |
generation_error |
生成 SQL 失败 |
execution_error |
SQL 执行失败且修复未成功 |
validation_error |
forced_sql 非合法 SELECT |
API 层集成过程:
- 在
query_routes.py中解析请求; - 根据数据集构造
JunoQueryTool.from_dataset(); - 调用
tool.run_question(); - 将结果写入
AuditStore; - 返回统一 artifact 响应。
- 入口:
POST /api/query - API 路由:
nldb/api/query_routes.py - 核心方法:
nldb/agent_loop.py中的AgentLoop.run() - 请求模式:
mode=agent
这条通路适合复杂问题,因为它不是“一次生成然后执行”,而是一个多步状态机。
示例请求:
{
"question": "按区域统计事件数",
"mode": "agent",
"config": {
"round_budget": 3,
"disable_value_probe": true
}
}7 个主要阶段:
| 阶段 | 主要动作 | 典型产物 |
|---|---|---|
PLANNING |
解析问题,建立意图与预算 | IntentAnalysis |
LINKING |
做 schema grounding 与合同构建 | grounded_prompt、contract |
GENERATING |
生成 SQL | current_sql |
EXECUTING |
执行 SQL | current_result 或 current_error |
OBSERVING |
诊断错误或结果异常 | ErrorDiagnosis |
REPAIRING |
基于诊断重写 SQL | 修复后的 current_sql |
COMPLETED / FAILED |
进入终态 | final_sql、final_result 或失败原因 |
状态机形态:
PLANNING
-> LINKING
-> GENERATING
-> EXECUTING
-> OBSERVING
-> REPAIRING -> EXECUTING
-> LINKING -> GENERATING
-> COMPLETED
-> FAILED
两级诊断机制:
Level 1:硬错误诊断
| 错误类别 | 典型问题 | 恢复动作 |
|---|---|---|
SYNTAX_ERROR |
SQL 语法错误 | REPAIR_SQL |
TABLE_NOT_FOUND |
表不存在 | RETRY_LINK |
COLUMN_NOT_FOUND |
列不存在 | REPAIR_SQL |
TYPE_MISMATCH |
类型不匹配 | REPAIR_SQL |
TIMEOUT |
执行超时 | RETRY_GENERATE |
UNKNOWN |
未知错误 | REPAIR_SQL |
Level 2:软异常诊断
| 错误类别 | 检测条件 | 恢复动作 |
|---|---|---|
CONTRACT_MISMATCH |
实际列与期望列不一致 | CONTRACT_REPAIR |
EMPTY_RESULT |
返回 0 行 | RETRY_GENERATE |
SUSPICIOUS_RESULT |
全 NULL、单列单行等可疑结果 | RETRY_GENERATE |
终止规则:
- 相同错误连续出现两次时中止;
- 超过
round_budget时中止; - 生成到非
SELECTSQL 时直接阻断; - 预算耗尽时状态记为
max_rounds_reached。
关键类:
| 类 | 作用 |
|---|---|
AgentConfig |
控制轮数预算、候选数量、观察开关等 |
AgentLoop |
Agent 核心执行器 |
AgentPhase |
7 阶段枚举 |
AgentStep |
单步记录,保存动作、观察与产物 |
AgentState |
跨步状态容器与最终 trace 出口 |
ErrorCategory |
错误类型枚举 |
RecoveryAction |
恢复动作枚举 |
ErrorDiagnosis |
诊断结果对象 |
返回形态:
AgentState.to_trace() 会输出统一 trace,通常包含:
questionstatusround_countround_budgetfinal_sqlfinal_resultstepsintentcontractgrounding_trace
- 入口:
eval/run_mini_eval.py - 核心作用:
- 校验整条查询链路是否可回归
- 批量输出明细、汇总和报告
- 做 schema linking 专项评测
内置 preset:
| Preset | 数据集 | 数据库 | 条数 |
|---|---|---|---|
juno_demo |
eval/mini_eval_v1.jsonl |
demo.db |
30 |
public_proxy |
eval/public_proxy_eval_v1.jsonl |
public_proxy.db |
20 |
juno_demo_v2 |
eval/mini_eval_v2.jsonl |
demo.db |
45 |
public_proxy_v2 |
eval/public_proxy_eval_v2.jsonl |
public_proxy.db |
40 |
internal_v1 |
eval/internal_eval_v1.jsonl |
demo.db |
10 |
常用命令:
python eval/run_mini_eval.py --mode gold
python eval/run_mini_eval.py --mode model --use-query-tool
python eval/run_schema_linking_eval.py --top-k-list 1,2,3两种运行模式:
gold_sql
-> execute_sql()
-> 直接作为 prediction
-> compare_results()
特点:
- 不需要 API key;
- 用于验证执行层、修复层、规范化层是否稳定;
- 适合做基线回归。
有两种子路径:
Path A:统一查询运行时
question
-> JunoQueryTool.run_question()
-> 提取 final_sql / result_rows / result_columns
-> compare_results()
Path B:直接评测流水线
question
-> ground_question()
-> infer_expected_output_columns()
-> generate_sql() / generate_sql_candidates()
-> execute_sql()
-> build_contract_repair_sql() (if needed)
-> compare_results()
结果比对逻辑:
- 浮点值统一保留 6 位小数;
- 字符串统一做 trim;
- 比较列名;
- 比较行数;
- 根据
ordered决定是否按顺序比较行值。
主要失败分类:
correctgeneration_errorexecution_errorschema_mismatchrow_count_mismatchvalue_mismatchgold_execution_error
输出产物:
| 文件 | 说明 |
|---|---|
details.jsonl |
每条样例的完整 trace |
summary.json |
汇总指标与分类统计 |
report.md |
人类可读报告 |
补充说明:
- 评测路径本身也支持多候选;
- 评测里的多候选选择逻辑可以独立于运行时候选打分存在;
- 这使评测既能复用统一主链,也能单独做方法分析。
主链路可以概括为:
Question
-> Intent Analysis
-> Schema Grounding
-> Output Contract
-> SQL Generation
-> Candidate Reranking
-> Execution / Repair
-> Result Normalization
-> Artifact / Trace
对应核心模块:
nldb/intent.pynldb/schema_linker.pynldb/output_contract.pynldb/generation_runtime.pynldb/candidate_scorer.pynldb/query_tool.pynldb/agent_loop.pynldb/governance.pynldb/dataset_registry.pynldb/audit_store.py
不同通路并不是各自孤立实现,而是复用同一批核心模块:
| 模块 | 遗留聊天通路 | Fast 查询通路 | Agent 查询通路 | Eval 通路 |
|---|---|---|---|---|
intent.py |
— | Y | Y | — |
schema_linker.py |
— | Y | Y | Y(model) |
generation_runtime.py |
— | Y | Y | Y(model) |
candidate_scorer.py |
— | Y | — | — |
output_contract.py |
— | Y | Y | — |
sql_utils.py |
— | Y | Y | Y |
governance.py |
— | Y | Y | — |
agent_state.py / error_router.py |
— | — | Y | — |
audit_store.py |
— | Y(API) | Y(API) | — |
dataset_registry.py |
— | Y(API) | Y(API) | — |
几个关键约束:
sql_utils.py是叶模块,尽量保持零运行时依赖;agent_loop.py与query_tool.py平行存在,不相互整包依赖;- 跨层引用尽量通过
TYPE_CHECKING守卫降低循环依赖风险。
当前仓库存在两类生成器实现:
| 维度 | VannaGenerator |
DirectAPIGenerator |
|---|---|---|
| 核心机制 | 复用 Vanna 检索与生成 | 直接调用 OpenAI-compatible API |
| 向量检索 | 有 | 无 |
| System Prompt | Vanna 内部管理 | 可通过 GeneratorConfig.extra 配置 |
| 重试 | 依赖 Vanna 与外层运行时 | 由统一运行时控制 |
| 适用场景 | 原型主路径 / 带检索增强 | 轻量测试 / 纯 prompt 路径 |
| 与 VannaFlaskApp 兼容 | 兼容 | 不兼容 |
这两类生成器共用统一的运行时接口,因此上层查询链路不需要知道底层模型调用细节。
- Python 3.12+
- Flask
- SQLite / DuckDB
- OpenAI-compatible API
- Vanna
- Vue 3
- 原生 JS / CSS
- pytest
- JSONL/CSV 报告产物
- 本地回归脚本
- 配置文件驱动的数据集注册
.
├─ app.py
├─ init_db.py
├─ run_query.py
├─ run_public_proxy_event_demo.py
├─ train_vanna.py
├─ README.md
├─ requirements.txt
├─ pytest.ini
├─ config/
├─ eval/
├─ nldb/
├─ static/
├─ templates/
└─ tests/
关键目录说明:
nldb/:核心查询管线、Agent、治理、数据集注册、适配层eval/:本地评测与专项评测config/:开发与生产配置tests/:自动化测试
python -m venv venv
.\venv\Scripts\Activate.ps1
pip install -r requirements.txt$env:OPENAI_API_KEY="your-key-here"
$env:OPENAI_BASE_URL="https://api.117911.xyz/v1"
$env:OPENAI_MODEL="gpt-5.3-codex"如果已经在 .env 中配置,可直接跳过。
python init_db.pypython app.py默认入口:
http://localhost:8084- 'http://localhost:8084/chat'
http://localhost:8084/config
python app.py
python run_query.py --question "统计每个 run 的触发率"
python run_query.py --question "统计 A 区所有 PMT 的触发事件总数" --sql "SELECT COUNT(*) AS total_triggered_events FROM pmt_events WHERE zone = 'A' AND is_triggered = 1"python eval/run_mini_eval.py --mode gold
python eval/run_mini_eval.py --mode model --use-query-tool
python eval/run_schema_linking_eval.py --top-k-list 1,2,3python run_public_proxy_event_demo.py --init-demo-db --db-path runs/public_proxy_demo.db
python run_public_proxy_event_demo.py --db-path runs/public_proxy_demo.db默认页面:
http://127.0.0.1:8091
run_query.py 默认会在 runs/ 下生成:
result.jsonresult.csv
这些文件包含:
- grounding 轨迹
- 生成轨迹
- 修复轨迹
- 执行结果
- 最终结果表
eval/run_mini_eval.py 默认会在 eval/results/ 下生成:
details.jsonlsummary.jsonreport.md
当前主要依赖三类数据源验证系统能力:
- 内置示例数据库
- 公开代理数据
- 外部多库错例与评测集
当前支持两类核心评测:
- mini-eval
- Schema Linking Eval
评测价值主要体现在:
- 支持批量回归
- 支持错误分类统计
- 支持对 grounding 与生成质量做拆分分析
更详细的评测说明见 eval/README.md。
当前仓库已经实现:
- 可运行的自然语言查询主链路
- VannaFast / Agent / Eval 核心工程通路
- 多候选重排与执行修复
- 多数据集注册与多数据库扩展基础
- 事件可视化原型页
- 本地自动化评测与测试基础设施
当前仍有几个明确边界:
- 尚未接入真实内部业务数据
- 部分脚本与模块保留历史命名
- 遗留聊天通路与统一查询通路并存
- 当前重点仍是系统能力验证,不是特定业务库上的最终准确率
当前仓库保留两类核心入口文档:
README.md:项目总览、系统通路、运行方式eval/README.md:评测模式、参数与输出说明
在不引入真实内部数据之前,后续最值得推进的方向包括:
- 建立更完整的问题模板集
- 为每类问题定义标准输出结构
- 覆盖统计、过滤、时间趋势、多表关联、异常定位等任务类型
- 强化表级召回
- 做值域感知的列选择
- 建立更细的业务词典与别名映射
- 降低无关 schema 对模型上下文的干扰
- 更稳健的多候选共识
- 更细粒度的执行反馈修复
- 面向复杂问题的分步 SQL / CTE 修复
- 从结果表升级到结果表 + 图表规格
- 在 UI 中呈现 grounding、候选、修复与最终结果之间的关系
- 定义影子 schema 与字段口径
- 设计权限、脱敏、审计和结果上限
- 让系统从原型验证平滑过渡到场景验证