TEXT → SQL六个数据集 · 浏览器内运行
把中文问题
变成可审计的 SQL
语义层把业务口径编译成确定的 SQL,覆盖不到的长尾问题交给带工具的 SQL 智能体。每条 SQL 执行前过语法树安全门和代价预估,执行后做结果质量检查。下面的控制台运行在你的浏览器里:Python 与 DuckDB 编译为 WebAssembly,跑的是与服务端同一份代码。可以问合成的企业库、四个开源数据集,也可以拖进自己的表格;结果能换图表、改 SQL 重跑、钉到看板。
- 运行位置
- 浏览器 Web Worker · WebAssembly
- 引擎
- Python 3.14 · DuckDB · sqlglot
- 模型
- Cloudflare Workers AI(长尾问题)
- 数据
- 按生产表结构生成的合成库
- 评测
- —
- 测试
- —
问数
企业库的问题先走手写语义层,在浏览器里直接编译、校验、执行,零模型调用;语义层放弃的长尾问题、开源数据集和你上传的文件交给 SQL 智能体,模型经本站 Worker 调用 Cloudflare Workers AI。
看板
问数结果上点「钉到看板」,图表连同数据快照一起保存在本机浏览器里,不经过服务器。「刷新」用当前页面的引擎重新执行 SQL,换数据集的卡片会先载入对应数据集。
还没有卡片。先在上面问一个问题,比如在 Northwind 里问“1997 年各产品类别的销售额”,再点结果下方的「钉到看板」。
评测
判分执行两边的 SQL、比较结果集,不比较 SQL 文本。未作答的题目是语义层主动放弃,不计为答错。
比较方式
按列签名匹配投影列,列名和列顺序无关;数值保留两位小数;除排名类题目外忽略行序。
指标口径
覆盖率 = 作答 / 应答;作答精确率 = 作答且正确 / 作答;执行准确率 = 正确 / 应答;拒答准确率 = 正确拒答 / 应拒。
局限
评测集与语义层出自同一作者,失败题只通过通用规则修复。这组数字适合做回归门禁,不代表对陌生问题的泛化能力。
语义层
业务口径写在 YAML 里:什么算存续企业,融资金额为空时怎么补齐,哪些宽表带占位行。解析器要求问题里的每个实义词都被解释,解释不了就放弃,不去猜。
架构
一次提问是一次状态机运行,像一条生产线:节点是工位,安全门和质量检查是质检,报错或可疑结果进入返工。服务端用 LangGraph 编排,浏览器里用顺序执行器,两者的输出由测试逐项核对。
| 服务端 · CLI / FastAPI / MCP / Streamlit | 本页 · 浏览器 | |
|---|---|---|
| 编排 | LangGraph,检查点按会话保存历史,SSE 推送节点事件 | 顺序执行器,同一组节点与路由表,会话保存在页面内存 |
| 规划 | 语义层编译 + SQL 智能体(OpenAI 兼容接口,函数调用) | 语义层编译 + SQL 智能体(同源 Worker 转发到 Workers AI,限流、只收同源请求,页面不持有密钥) |
| 数据 | MySQL 只读会话,或 DuckDB 演示库 | DuckDB(WebAssembly):合成演示库、按提交固定并校验过的开源数据集(Parquet)、你上传的文件 |
| 安全 | 语法树安全门、EXPLAIN 代价预估、查询超时中断 | 同一安全门与代价预估;WebAssembly 没有线程,不做超时中断 |
宁可放弃,也不给看似合理的错误结果
覆盖检查之外,还有结构性拒答:分组内取排名、在排名集合里再计数,这类问题照常编译会得到数字漂亮但口径错误的答案。评测里未作答的题目全部来自主动放弃。
安全门在语法树上判断
用 sqlglot 解析:只允许单条只读语句,表走白名单,拦截跨库与系统函数、SELECT * 和个人信息字段,逐列校验字段是否存在并给出相近字段,收紧 LIMIT。MySQL 会话本身也设为只读。
条件取值要落在真实数据上
值索引扫描低基数列的真实取值。结果为空且条件里的取值在库中不存在时,把真实取值反馈给 SQL 智能体重写,例如把 status = '存续' 改成 LIKE '存续%'。
执行成功不等于答对
结果画像之后做 8 项质量检查:截断、排名数量、时间顺序、宽表占位行等。解读里的每个数字都必须能在结果中找到,否则退回由结果直接生成的解读。
编排器可以替换
LangGraph 依赖的原生扩展装不进 Pyodide。节点逻辑与编排拆开之后,浏览器端改用顺序执行器,用 62 道评测题、多轮追问和修复回路核对两种编排器输出一致。
语义层是配置,不是代码
领域词、表召回规则、业务口径都写在 YAML 里。新数据集导入时先自动画像(字段角色、真实取值、疑似个人信息),开源数据集再叠一张人写的数据卡片(中文字段名、表间关联、口径),接入四个开源库没有改一行智能体代码。