# Text2SQL-Complexity-Rater **Repository Path**: ppcirgo/Text2SQL-Complexity-Rater ## Basic Information - **Project Name**: Text2SQL-Complexity-Rater - **Description**: 智能问数问题复杂度自动评级工具。基于Spide标准,输入自然语言问题及可选 SQL,自动输出定级及判定依据。适用于 Text-to-SQL 智能体的测试集分级、能力评估与回归测试。 - **Primary Language**: Python - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-28 - **Last Updated**: 2026-09-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Text2SQL-Complexity-Rater 智能问数问题复杂度自动评级工具。基于 **Spider 官方 SQL Hardness Criteria**,输入自然语言问题及可选 SQL,自动输出 **Easy / Medium / Hard / Extra Hard** 四级定级及判定依据。 ## 项目简介 在评估 Text-to-SQL / 智能问数智能体时,一个核心难题是:**如何客观、量化地定义"问题复杂度"?** 仅凭主观感受或句子长度分级,既无法说服业务方,也难以指导模型优化与回归对比。 本工具采用业界成熟基准 **Spider**(EMNLP'18)的官方难度划分逻辑:Spider 依据 SQL 组件、选择列数与条件数,把查询分为 `Easy / Medium / Hard / Extra Hard` 四级。本工具将这套学术标准工程化,实现**自动化、可审计、可复现**的结构复杂度定级,并给出便于汇报的中文别名(简单 / 中等 / 困难 / 极难)。 它解决什么问题: - **测试集分级**:把一堆问数问题自动切成四个难度档,便于按难度分层抽检与统计。 - **能力评估**:给出智能体在不同难度档的表现基线,定位能力短板。 - **回归测试**:把"问题变难了"这一模糊判断,变成可比较的等级与判定依据。 - **人工评审提效**:每条结果都带判定依据,评审可直接引用。 设计原则: - **对齐官方标准**:分级口径逐条对齐 Spider 原论文,不自创阈值。 - **规则优先、模型兜底**:有 SQL 时结构复杂度由规则引擎精准计算,确定且可复现;无 SQL 时才用 LLM 语义推断。 - **可降级、可离线**:LLM 不可用时自动降级规则引擎,`--no-llm` 可完全离线运行。 - **数据不出机**:Web 页面与本地服务默认只监听 `127.0.0.1`,无需外网。 适用对象:Text-to-SQL 智能体研发、数据问答平台、评测与数据标注团队。 ## 特性 - **单轴 Spider 四级定级**:`Easy / Medium / Hard / Extra Hard`,附中文别名(简单 / 中等 / 困难 / 极难)。 - **双模式**: - 提供 SQL 时,结构复杂度由规则引擎(sqlparse)按 Spider 官方判据**精准计算**,不依赖模型。 - 未提供 SQL 时,调用本地 Qwen(OpenAI 兼容接口)做语义推断。 - **规则兜底**:LLM 不可用时自动降级到规则引擎,离线也可运行(`--no-llm`)。 - **批量处理**:支持 CSV / JSONL 输入,输出 CSV / JSON / JSONL,附四级分布统计。 - **可审计**:每条结果都带判定依据 `reason` 与评级路径 `method`。 ## 界面预览 内置的交互式 Web 页面(`t2sql-rater serve`),支持单条评级、批量评级与 LLM 配置。 **单条评级**:输入问题与可选 SQL,展示 Spider 级别、中文别名与判定依据。 ![单条评级](docs/images/ui-single.png) **批量评级**:多行粘贴或导入 CSV/JSONL,输出结果表与四级分布统计。 ![批量评级](docs/images/ui-batch.png) **配置面板**:开关 LLM、设置端点/模型/重试次数与思考模式,配置仅存于浏览器本地。 ![配置面板](docs/images/ui-config.png) **测试结果示例**: | Hard(困难) | Extra Hard(极难) | |---|---| | ![Hard](docs/images/test-hard.png) | ![Extra Hard](docs/images/test-extra.png) | ## Spider 官方分级标准 本工具的评级口径严格对齐 Spider 原始论文(Yu et al., *Spider: A Large-Scale Human-Labeled Dataset for Complex and Cross-Domain Semantic Parsing in Context*, EMNLP 2018, [arXiv:1809.08887](https://arxiv.org/abs/1809.08887)),不做自创。 官方 SQL Hardness Criteria 将 SQL 分为 4 级,依据 SQL 组件、选择列数与条件数。原文明确: > We divide SQL queries into 4 levels: easy, medium, hard, extra hard. We define the difficulty > based on the number of SQL components, selections, and conditions ... a query is considered as > hard if it includes more than two SELECT columns, more than two WHERE conditions, and GROUP BY > two columns, or contains EXCEPT or nested queries. A SQL with more additions on top of that is > considered as extra hard. 一句话概括:**表越多、子句越多、嵌套越深,级别就越高。** ### Easy(简单) - **通俗理解**:一张表,最多加一个简单的过滤条件;不分组、不排序、不嵌套。 - **典型信号**:`SELECT ... FROM 单表 WHERE 一个条件` - **SQL 示例**: ```sql SELECT name FROM student WHERE age > 18; ``` ### Medium(中等) - **通俗理解**:开始有点"花样"了——要么给结果排个序,要么把两张表拼起来查,但还没有分组统计。 - **典型信号**:`ORDER BY`、`DISTINCT`、两表 `JOIN`(不带聚合) - **SQL 示例**: ```sql SELECT c.name, c.age FROM customers c JOIN orders o ON c.id = o.cid ORDER BY c.age; ``` ### Hard(困难) - **通俗理解**:要先分组统计、再筛结果(`GROUP BY` + `HAVING`);或者条件里套了另一条查询(子查询);或者用 `EXCEPT / INTERSECT` 做集合比较;或者"列多 + 条件多 + 分组"同时出现。 - **典型信号**:`GROUP BY` + `HAVING`、嵌套子查询、`EXCEPT / INTERSECT`、>2 列且 >2 条件且分组 - **SQL 示例**: ```sql SELECT dept, AVG(salary) AS avg_sal FROM employee GROUP BY dept HAVING AVG(salary) > 10000; ``` ### Extra Hard(极难) - **通俗理解**:在困难的基础上再套一层。最常见的是"连续 N 天 / 累计 / 排名"这类需要**窗口函数**的题,或者多个子查询层层嵌套,或者集合操作再叠加多表 + 聚合。 - **典型信号**:窗口函数 `OVER(...)`、多层嵌套子查询、集合操作 + 多表 `JOIN` + 聚合 - **SQL 示例**: ```sql -- 每个部门薪资排名前 3 的员工(窗口函数) SELECT name, dept, sal FROM ( SELECT name, dept, sal, RANK() OVER (PARTITION BY dept ORDER BY sal DESC) AS rk FROM employee ) t WHERE rk <= 3; ``` > **小贴士**:把问题里的"每个 / 分别 / 占比"当作**分组信号**,把"连续 / 累计 / 排名 / 环比"当作**窗口信号**;两类同时出现,基本就是**极难**。 ## 在 Spider2-Snow(547 题)上的定级分布 [Spider2-Snow](https://github.com/xlang-ai/Spider2) 是业界公认的高难度 text-to-SQL 基准,共 **547 题**。用本工具按**默认路径**(有标准 SQL 走结构规则、无则走语义兜底)定级,结果如下: | 级别 | 题数 | 占比 | |---|---|---| | Easy(简单) | 34 | 6.2% | | Medium(中等) | 239 | 43.7% | | Hard(困难) | 87 | 15.9% | | Extra Hard(极难) | 187 | 34.2% | | **合计** | **547** | **100%** | 按是否带公开金标 SQL 拆分: | 级别 | 有金标 SQL 的 250 题(结构定级) | 无金标 SQL 的 297 题(语义兜底) | |---|---|---| | Easy(简单) | 3(1.2%) | 31(10.4%) | | Medium(中等) | 1(0.4%) | 238(80.1%) | | Hard(困难) | 75(30.0%) | 12(4.0%) | | Extra Hard(极难) | 171(68.4%) | 16(5.4%) | > **说明**:Spider2-Snow 的 547 题中,公开数据里仅 250 题带金标 SQL,其余 297 题为 test 集、金标未公开。有 SQL 时结构定级精确可靠(Hard + Extra Hard 达 98.4%);无 SQL 时规则兜底偏保守(大量落入 Medium),建议启用 LLM 以获得更贴近真实的语义定级。 复现方式(需自行准备该数据集的 `qa.csv`,含 `nl_prompt` / `sql_query` 列): ```bash # 将 qa.csv 转成 question,sql 两列后批量评级 t2sql-rater batch --input spider2_snow.csv --output out.csv --format csv --no-llm ``` ## 安装 需要 Python >= 3.9。 ```bash # 推荐使用虚拟环境 python -m venv .venv && source .venv/bin/activate pip install -e . ``` 依赖:`sqlparse`、`openai`、`pydantic>=2`、`click`、`pandas`。 ## 构建与部署 ### 本地开发安装 ```bash python -m venv .venv && source .venv/bin/activate pip install -e ".[dev]" # 含 pytest ``` ### 构建发布包 ```bash pip install build python -m build # 生成 dist/*.whl 与 dist/*.tar.gz pip install dist/text2sql_complexity_rater-*.whl # 验证安装 ``` `static/index.html` 通过 `package-data` 一并打包,Web 页面随包分发。 ### 部署为命令行工具 ```bash pip install text2sql-complexity-rater t2sql-rater rate -q "..." -s "..." --no-llm ``` ### 部署 Web 服务 ```bash # 前台运行(开发调试,Ctrl+C 停止) t2sql-rater serve --host 0.0.0.0 --port 8000 # 后台守护运行(启动/停止均在 1s 内返回) t2sql-rater serve --daemon --host 0.0.0.0 --port 8000 t2sql-rater stop --port 8000 ``` 守护模式使用 double-fork 脱离控制终端,PID 与日志默认写入 `/tmp/t2sql-rater-.{pid,log}`,可用 `--pid-file` / `--log-file` 自定义。 - 默认仅监听 `127.0.0.1`;对外暴露时请置于反向代理(Nginx 等)之后,并自行加上鉴权。 - 服务无状态,可用任意进程管理器(systemd / supervisor / docker)托管。 Docker 示例: ```dockerfile FROM python:3.12-slim WORKDIR /app COPY . . RUN pip install --no-cache-dir . EXPOSE 8000 CMD ["t2sql-rater", "serve", "--host", "0.0.0.0", "--port", "8000"] ``` ### 连接本地 Qwen 默认面向 OpenAI 兼容端点(vLLM / Ollama / LM Studio 均可),仅用于「无 SQL」时的语义推断: ```bash # 方式一:命令行参数 t2sql-rater rate -q "..." --base-url http://localhost:8000/v1 --model Qwen2.5-27B-Instruct # 方式二:环境变量 export T2SQL_BASE_URL=http://localhost:8000/v1 export T2SQL_MODEL=Qwen2.5-27B-Instruct export T2SQL_API_KEY=none t2sql-rater rate -q "..." ``` Web 页面同样可在"配置"面板中填写端点信息,无需重启服务。 > **性能**:Qwen3 / QwQ 等模型默认开启 thinking(思考)会明显拉长耗时。本工具默认发送 `chat_template_kwargs={"enable_thinking": false}` 关闭思考;若端点不支持该参数会自动回退,不影响可用性。需要模型深度思考时加 `--enable-thinking`,或在 Web 配置面板勾选"思考模式"。 ## 快速开始 ### 命令行 单条评级(提供 SQL,纯规则、无需模型): ```bash t2sql-rater rate \ -q "查询所有年龄大于18岁的学生姓名" \ -s "SELECT name FROM student WHERE age > 18" \ --no-llm --json-output ``` ```json { "question": "查询所有年龄大于18岁的学生姓名", "sql": "SELECT name FROM student WHERE age > 18", "spider_complexity": "Easy", "reason": "1表,WHERE1条件 → Easy", "method": "sql_rules", "error": null, "complexity_cn": "简单" } ``` 批量评级: ```bash t2sql-rater batch --input examples/sample_input.csv --output out.csv --format csv # 输出格式可选 csv / json / jsonl ``` 未提供 SQL 时调用本地 Qwen: ```bash t2sql-rater rate -q "最近表现好的员工有哪些" \ --base-url http://localhost:8000/v1 \ --model Qwen2.5-27B-Instruct ``` ### Web 测试页面 内置一个零依赖(仅标准库 `http.server`)的交互式页面,用于快速测试与配置: ```bash t2sql-rater serve --host 127.0.0.1 --port 8000 # 浏览器打开 http://127.0.0.1:8000 ``` 页面能力: - **单条评级**:输入问题与可选 SQL,实时展示 Spider 级别(含中文别名)、判定依据与原始 JSON。 - **批量评级**:多行粘贴(`问题 ||| SQL` 或 Tab 分隔),或导入 `.csv` / `.jsonl` 文件;展示结果表、四级分布与一键导出 CSV。 - **配置面板**:开关 LLM、设置 `base_url` / `model` / `api_key` / 重试次数,配置仅存于浏览器 `localStorage`。 - 快捷键 `⌘/Ctrl + Enter` 运行;页面数据全部走本地接口,不出机。 页面接口: | 方法 | 路径 | 说明 | |---|---|---| | GET | `/` | 测试页面 | | GET | `/api/health` | 健康检查 | | POST | `/api/rate` | 单条评级,body `{question, sql?, config?}` | | POST | `/api/batch` | 批量评级,body `{items: [{question, sql?}], config?}` | ### Python API ```python from text2sql_rater import ComplexityRater, QwenRaterClient # 纯规则模式(离线) rater = ComplexityRater(use_llm=False) result = rater.rate("查询所有年龄大于18岁的学生姓名", "SELECT name FROM student WHERE age > 18") print(result.spider_complexity) # SpiderComplexity.EASY print(result.complexity_cn) # 简单 print(result.reason) # 1表,WHERE1条件 → Easy # 启用本地 Qwen 语义推断(无 SQL 时) client = QwenRaterClient(base_url="http://localhost:8000/v1", model="Qwen2.5-27B-Instruct") rater = ComplexityRater(llm_client=client, use_llm=True) print(rater.rate("最近表现好的员工有哪些").spider_complexity) ``` ## 评级路径(method) | method | 触发条件 | 来源 | |---|---|---| | `sql_rules` | 有 SQL | 规则引擎按 Spider 官方判据精准计算 | | `llm` | 无 SQL,LLM 可用 | 本地 Qwen 语义推断 | | `fallback_rules` | 无 SQL,LLM 不可用 | 关键词规则引擎兜底 | > 有 SQL 时结构复杂度**始终**来自规则引擎,不会调用 LLM,以保证"精准计算"。 > > 无 SQL 时以 LLM 推断为主,并叠加**规则安全下限**:若规则引擎识别到更强的窗口/连续/集合等信号,则与 LLM 结果取较难者,避免强语义被低估(reason 中会标注"规则校验提升")。 ## 输入 / 输出格式 输入 CSV 表头需包含 `question`(必填)与 `sql`(可选)。SQL 中含逗号时请用引号包裹。 批量还支持 `.jsonl`,每行一个 `{"question": ..., "sql": ...}` 对象。 输出字段: `question, sql, spider_complexity, complexity_cn, reason, method, error` ## 项目结构 ``` src/text2sql_rater/ ├── schema.py # pydantic 模型、枚举与中文别名 ├── prompt.py # LLM 系统提示词(对齐 Spider 官方定义) ├── rules/ │ ├── sql_features.py # sqlparse 结构特征提取 │ └── spider.py # Spider 官方复杂度规则 ├── llm.py # openai SDK 客户端 + 重试 + 校验 ├── rater.py # 编排与降级 ├── batch.py # 批量 CSV/JSONL 处理 ├── web.py # 零依赖 Web 服务(测试页面 + API) ├── cli.py # click 命令行 └── static/ └── index.html # 交互式测试与配置页面 ``` ## 测试 ```bash pip install -e ".[dev]" pytest ``` ## 已知边界 - Spider 官方仅显式给出 `hard / extra hard` 判据,`easy / medium` 依据官方"组件数"语义按阈值补全。 - 无 SQL 时的规则兜底(`fallback_rules`)是中英双语关键词启发式(含窗口/连续、聚合占比、集合、子查询等信号),精度低于 LLM,仅用于降级场景。建议无 SQL 时启用 LLM 以获得更准的推断。 - 真实 LLM 路径需自行部署 OpenAI 兼容端点;仓库测试全部使用 mock,不依赖网络与模型。 ## License MIT