Skip to main content
Glama
Eshanya1

sql-specialist-mcp

by Eshanya1

sql-specialist-mcp

一个针对 SQLite 数据库的自然语言问答而微调的小型开源权重模型,以 MCP 工具 的形式提供服务,任何 MCP 客户端(Claude Desktop、Claude Code、自定义代理)都可以直接调用——并配有一个基于执行准确率的评估框架,用于将专家模型与提示词调用前沿模型在准确率、延迟和成本方面进行对比。

这个项目的重点不是“构建一个 text-to-SQL 演示”,而是展示 LLM 工程中位于提示词 之下 的部分:采用一个小模型,通过 LoRA 将其适配到单一任务,高效地提供服务,并通过一个真实的、基于执行的评估证明,一个廉价的专家模型在这个狭窄任务上可以与提示词调用前沿模型相媲美(甚至更优)。

试试交互式演示 ——点击浏览全部 28 个真实评估问题,查看专家模型实际生成的 SQL、延迟和结果行,并与前沿基线进行对比。无需安装。

为什么做这个项目

大多数“AI 作品集”式的 text-to-SQL 项目都是 LangChain 快速入门。这里有两件事旨在与众不同:

  1. 评估是严谨的,而不是凭感觉。 每条黄金查询在数据集构建时都会针对数据库执行(139/139 已验证),评分比较的是 结果集,而不是查询文本——一个语义正确但列顺序不同的查询仍然算正确。一个只是回显黄金 SQL 的预测器得分为 100%;一个总是返回平凡错误查询的预测器得分为 0%。两者都作为健全性测试(tests/test_harness_oracle.py)被检查,因此评估框架自身的正确性不是假设的。

  2. 它交付的是可用的东西,而不仅仅是一个演示仓库。 微调后的模型以真正的 MCP 工具(nl_to_sql)形式暴露——将 Claude Desktop 或 Claude Code 指向 mcp_server/server.py,它就能在对话中实际查询数据库。

Related MCP server: mcp-sqlite-chat

结果

完整流程已在真实硬件上端到端运行,双方都是真实的:真实的 LoRA 微调、真实的合并、真实的 GGUF 量化、真实的 Ollama 服务、真实的评估——以及针对实时 Claude API 的真实前沿基线。 基础模型:Qwen/Qwen2.5-Coder-0.5B-Instruct(选择它是因为在笔记本电脑上迭代速度快;关于 1.5B 路径,请参阅 微调 部分)。

预测器

准确率

n

p50 延迟

p95 延迟

成本 / 1k 次调用

前沿:Claude Haiku 4.5(提示词)

53.6%

28

1055ms

1884ms

$1.06

sql-specialist(微调、量化、本地)

92.9%

28

207ms

371ms

$0.00

请带着保留态度阅读,而不仅仅是看标题。 我手动审计了 Claude Haiku 在此评估集上的 13 个测量“失败”中的每一个:零个是 SQL 逻辑错误。 全部 13 个都是列选择或行顺序约定不匹配——例如,当黄金答案是仅 (name) 时返回了 (name, email),或者正确的行顺序与原始问题从未指定的 ORDER BY 不同。严格的执行准确率指标(eval/execution.py 逐列比较结果行)将这些与真正错误的查询同等评分,而微调后的专家模型永远不会产生这种错误,因为它从 111 个训练示例中记住了这个数据集的确切约定——而零样本提示的前沿模型无法知道这些。完整的逐失败分类见 COMPARISON.md。

所以:准确率差距是真实的,但部分是评估所奖励的产物,而不仅仅是推理差距。延迟和成本差距不是产物——207ms/本地/免费 vs. 1055ms/$1.06-per-1k-calls 是在本地运行量化 0.5B 模型而不是调用 API 的实际、未对冲的结果,而这个项目的核心前提正是建立在这个对比之上。

专家模型自身的 2 个失败(共 28 个)是真正的逻辑错误,而不是格式不匹配——幻觉出一个在此模式中不存在的看似合理的 orders.total 列,以及在一个多表 SELECT 中丢失了一个表限定符。训练在 3 个 epoch 中干净地收敛(评估损失 0.060 → 0.048 → 0.008),量化模型(988MB f16 → 373MB q4_k_m)通过 Ollama 在约 200ms 内提供服务。

这里什么是真实的

对此坦诚比看起来更重要——这是招聘人员可以信任的项目与读起来像营销的项目之间的区别。

  • 合成数据库和数据集是可证明正确的。 shopsphere.db 是确定性播种的(seed=42);data/*.jsonl 中的全部 139 个黄金(问题,SQL)对都是从参数化模板生成的,并在构建时 针对真实数据库执行——产生无效 SQL 的模板会导致构建失败,而不是静默地发布一个坏标签。

  • 评估框架自身的正确性也经过测试,而不是假设——tests/test_harness_oracle.py 断言一个 oracle 预测器(逐字返回黄金 SQL)恰好得分为 100%,而一个故意错误的预测器得分约为 0%,然后才信任任何真实预测器的数字。

  • 执行准确率,而不是字符串匹配。 eval/execution.py 比较结果 集(除非黄金查询有 ORDER BY,否则顺序不敏感),因此一个写法不同但语义等价的查询仍然算正确。

  • 微调是真实的,在这台机器上,已验证收敛。 LoRA(880 万可训练参数,占模型的 1.75%)在 3 个 epoch 上,评估损失每个 epoch 单调下降。有关过程中遇到并修复的两个真实 bug,请参阅下面的 工程笔记。

  • SQL 执行是真正沙箱化的,而不仅仅是提示词要求其行为:读查询通过正则表达式白名单验证 并且 针对真正的只读 SQLite 连接(OS 级别的 mode=ro)执行——即使正则表达式守卫有 bug,也无法导致写入。这超出了评估框架的范围,因为相同的守卫在 MCP 服务器中运行,那里的 SQL 来自响应代理问题的模型,而不是精心策划的评估集。

  • MCP 服务器是一个真实的、可调用的工具,服务于真实的微调模型,并经过端到端验证:nl_to_sql("Which employees have no manager assigned?") → 通过 Ollama 使用量化模型生成 SQL → 以只读方式执行 → 返回真实行 → 将延迟/成本记录到可观测性系统。

  • 可观测性是自建且无依赖的——observability/logger.py 将每次调用(延迟、令牌、估计成本、成功/失败)记录到本地 SQLite 文件,无需外部账户,与 pr-review-agent 的模式相同。

  • 前沿基线也是真实的——eval/baseline_frontier.py 针对实时 Claude API(Claude Haiku 4.5)运行,而不仅仅是干净地导入。它的“失败”结果揭示了一个真实的评估方法论发现——请参阅上面的 结果 和 COMPARISON.md 中的完整手动失败审计。

工程笔记:实际运行中发现的两个真实 bug

实际执行微调(而不是将其留作“理论上应该可行”)暴露了两个真正的 PyTorch 内存 bug,两者都已在当前代码中修复:

  1. MPS 缓存分配器失控。 在 Apple Silicon 的 MPS 后端上通过 transformers.Trainer 进行训练,由于动态的每批次填充,导致进程膨胀到 23GB RSS 并挂起——每个不同的(batch, seq_len)形状在 PyTorch 的 MPS 分配器中都有自己的内存池,而该分配器不会将释放的内存返回给操作系统。修复:在 finetune.py 中覆盖 --device cpu,更根本的是,使用固定长度填充(见下文),这样这类 bug 在任何后端上都不会再发生。

  2. Trainer/DataLoader 开销,而不是模型本身。 直接的前向+反向传播计时为 1.6s/示例;相同的计算通过 transformers.Trainer 时,进程在记录步骤之间空闲数分钟,且没有相应的计算。通过在实际假设 bug 在模型代码中之前,隔离实际的模型+LoRA 前向/反向传播并手动计时,找到了根本原因。修复:用约 40 行的手动训练循环(training/finetune.py)替换了 Trainer——相同的 LoRA 设置,直接控制批次循环,没有无法解释的开销。还将批次整理从动态每批次改为固定长度填充(每个批次形状相同),这独立地修复了 bug #1 中的分配器碎片化模式。

这两个修复都不是事后修补——它们都作为唯一实现出现在 training/finetune.py 中,而不是替代路径。

架构

data/build_dataset.py ──▶ data/{train,eval}.jsonl   (139 examples, template-generated,
                                                       every gold SQL executed at build time)
                              │
        ┌─────────────────────┼─────────────────────┐
        ▼                     ▼                      ▼
training/finetune.py   eval/baseline_frontier.py   tests/test_harness_oracle.py
  (LoRA on a small        (prompt Claude Haiku/       (sanity-checks the harness
   open model)             Sonnet as the baseline)     itself before trusting scores)
        │                     │
        ▼                     │
training/merge_and_quantize.py
        │                     │
        ▼                     ▼
serving/ollama_predictor.py ──┴──▶ eval/harness.py ──▶ eval/report.py ──▶ COMPARISON.md
        │                          (execution-accuracy scoring,
        │                           same logic for every predictor)
        ▼
mcp_server/server.py  (nl_to_sql tool -- installable in Claude Desktop/Code)
        │
        ▼
observability/logger.py  (latency, tokens, cost -- local SQLite, no external account)

项目结构

schema/           synthetic "ShopSphere" e-commerce DB (7 tables) + seeded generator
data/              templated gold (question, SQL) dataset -- every query build-time validated
eval/              execution-accuracy harness, frontier baseline, comparison report
training/          LoRA fine-tuning pipeline + LoRA-merge/quantize script
serving/           Ollama-backed and in-process HF predictors, Ollama Modelfile template
mcp_server/        the installable MCP tool (nl_to_sql)
observability/     self-built call logging (latency/tokens/cost), no external account
tests/             harness sanity checks (oracle predictor must score 100%)

设置

python3.11 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt          # base: anthropic, mcp, requests
python schema/generate_data.py           # build the seeded database
python data/build_dataset.py             # build + validate the gold dataset
python tests/test_harness_oracle.py      # confirm the eval harness itself is sound

requirements-train.txt 为微调路径添加了 torch/transformers/peft/trl——更重,单独保留,以便评估/服务/MCP 路径快速安装。

运行完整流程

1. 前沿基线(需要 ANTHROPIC_API_KEY):

export ANTHROPIC_API_KEY="..."
python -m eval.baseline_frontier --model claude-haiku-4-5
# writes eval/results_claude-haiku-4-5.json

2. 微调专家模型(这是实际运行以产生上述结果的过程——在笔记本电脑 CPU 上大约需要 15 分钟的活动计算,但墙钟时间因系统负载而异;GPU 会快得多,见下文):

pip install -r requirements-train.txt
python -m training.finetune --base-model Qwen/Qwen2.5-Coder-0.5B-Instruct --device cpu
python -m training.merge_and_quantize --base-model Qwen/Qwen2.5-Coder-0.5B-Instruct
# then follow the printed llama.cpp + ollama create instructions

3. 以与基线相同的方式为专家模型评分:

python -c "
from eval.harness import run_eval, report_to_dict
from serving.ollama_predictor import OllamaPredictor
import json
report = run_eval(OllamaPredictor())
json.dump(report_to_dict(report), open('eval/results_specialist.json', 'w'), indent=2)
"

4. 生成比较报告:

python -m eval.report eval/results_claude-haiku-4-5.json eval/results_specialist.json

微调:扩展规模

上述结果使用 Qwen2.5-Coder-0.5B-Instruct 在 CPU 上运行,以便快速本地迭代。training/finetune.py --base-model 接受任何 HF 因果 LM 仓库(或本地目录)——Qwen2.5-Coder-1.5B-Instruct 是一个直接替换,可获得更好的质量,而单个云 GPU(对于此数据集大小,T4 就足够了)可以在几分钟内训练任一大小,而不是约 15 分钟:

pip install -r requirements-train.txt
python -m training.finetune \
  --base-model Qwen/Qwen2.5-Coder-1.5B-Instruct \
  --epochs 3

--device {cuda,mps,cpu} 覆盖自动检测。MPS 在 Apple Silicon 上会自动检测,但尚不建议用于此任务——请参阅上面的 工程笔记。

MCP 服务器

# Backend defaults to a locally-served model via Ollama:
python -m mcp_server.server

# Or run against a prompted frontier model instead (no fine-tune needed --
# useful for trying the tool before training anything):
SQL_SPECIALIST_BACKEND=frontier SQL_SPECIALIST_MODEL=claude-haiku-4-5 \
  ANTHROPIC_API_KEY=... python -m mcp_server.server

添加到 Claude Desktop 的 MCP 配置(claude_desktop_config.json):

{
  "mcpServers": {
    "sql-specialist": {
      "command": "/absolute/path/to/sql-specialist-mcp/.venv/bin/python",
      "args": ["-m", "mcp_server.server"],
      "cwd": "/absolute/path/to/sql-specialist-mcp"
    }
  }
}

然后问 Claude 类似 “使用 sql-specialist 工具,哪些客户从未下过订单?”——它会调用 nl_to_sql,从数据库获取真实行,并基于实际数据回答。

安全说明

  • SQL 执行在两个独立层上是只读的:一个正则表达式守卫拒绝除 SELECT/WITH 之外的任何内容,以及一个真正的 OS 级只读 SQLite 连接(file:...?mode=ro)作为后备。

  • MCP 服务器永远不会执行守卫拒绝的任何内容,无论模型或调用代理要求什么。

  • 此仓库中不存储任何秘密。ANTHROPIC_API_KEY 仅从环境中读取。

我接下来会构建什么

  • 为列超集规范化评估——如果黄金请求的列的值存在,则预测得分正确,而不是要求逐列精确匹配。这是 COMPARISON.md 中失败分类所暗示的修复;它很可能会缩小大部分测量的 53.6%→92.9% 差距,并产生一个将实际推理能力与约定匹配隔离开来的比较。

  • 也针对 Claude Sonnet 运行 eval/baseline_frontier.py,以获得更强模型的比较点(Haiku 是廉价/快速层;Sonnet 是“仅模型强度能缩小多少差距”的问题)。

  • 在 GPU 上微调 Qwen2.5-Coder-1.5B-Instruct,并与 0.5B 结果(92.9%)比较准确率,以直接量化大小/质量权衡。

  • 针对专家模型的两个已知失败模式(幻觉列、多连接中丢失表限定符)进行 DPO,现在已有真实失败数据。

  • vLLM 服务路径,用于与 Ollama/GGUF 路径进行吞吐量比较。

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables querying and managing a SQLite database using natural language, with an MCP server and Groq LLM.
    -
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to query a SQLite database using natural language through the Model Context Protocol (MCP). Includes security guardrails that block destructive SQL operations.
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    MCP tool server providing SQLite database access for AI agents.
    MIT