sql-specialist-mcp
sql-specialist-mcp
Небольшая дообученная модель с открытыми весами, которая отвечает на вопросы на естественном языке о базе данных SQLite и работает как MCP-инструмент — любой MCP-клиент (Claude Desktop, Claude Code, кастомные агенты) может вызывать его напрямую. В основе лежит eval-харнесс, измеряющий точность исполнения: он сравнивает специалиста с запросами к frontier-модели по точности, задержке и стоимости.
Смысл проекта не в том, чтобы «сделать демо text-to-SQL» — а в том, чтобы показать части LLM-инжиниринга, которые лежат ниже промптирования: взять маленькую модель, адаптировать её под одну задачу через LoRA, эффективно обслуживать и доказать с помощью настоящего eval-а, основанного на исполнении, что дешёвый специалист конкурентоспособен с запросами к frontier-модели (или превосходит их) для этой узкой задачи.
Попробуйте интерактивное демо — пройдите все 28 реальных eval-вопросов и посмотрите, какой SQL реально сгенерировал специалист, его задержку и результирующие строки рядом с frontier-базлайном. Устанавливать ничего не нужно.
Зачем это существует
Большинство «AI-портфолио» проектов text-to-SQL — это быстрые старты на LangChain. Здесь два отличия:
Eval строгий, а не «по ощущениям». Каждый золотой запрос исполняется против базы данных на этапе сборки датасета (139/139 проверено), а скоринг сравнивает результирующие наборы, а не текст запроса — семантически корректный запрос с другим порядком колонок всё равно получает «верно». Предиктор, который просто повторяет золотой SQL, показывает результат 100%; предиктор, который всегда возвращает заведомо неверный запрос, показывает 0%. Оба проверены как sanity-тесты (
tests/test_harness_oracle.py), так что корректность самого харнесса не постулируется.Это полезный инструмент, а не просто демо-репозиторий. Дообученная модель доступна как настоящий MCP-инструмент (
nl_to_sql) — наведите Claude Desktop или Claude Code наmcp_server/server.py, и он сможет реально выполнять запросы к базе данных в ходе разговора.
Related MCP server: mcp-sqlite-chat
Результаты
Полный конвейер запущен end-to-end на реальном железе с обеих сторон: реальное LoRA-обучение, реальное слияние, реальная GGUF-квантование, реальный Ollama-сервер, реальный eval — а также настоящий фронтир-базлайн через живой Claude API.
Базовая модель: Qwen/Qwen2.5-Coder-0.5B-Instruct (выбрана ради быстрой итерации на ноутбуке; см. Fine-tuning ниже о пути к 1.5B).
Предиктор | Точность | n | p50 задержка | p95 задержка | Стоимость / 1 тыс. вызовов |
frontier: Claude Haiku 4.5 (промпт) | 53.6% | 28 | 1055ms | 1884ms | $1.06 |
sql-specialist (дообучен, квантован, локально) | 92.9% | 28 | 207ms | 371ms | $0.00 |
Читайте это с оговоркой, а не с заголовком. Я вручную просмотрел все 13 измеренных «провалов» Claude Haiku на этом eval-наборе: ни один из них не был логической ошибкой в SQL. Все 13 — рассинхрон по выбору колонок или порядку строк: например, вывод (name, email), когда золотым ответом был только (name), или верные строки в другом порядке, чем курс по ORDER BY, которого исходный вопрос вообще не задавал. Строгая метрика execution-accuracy (в eval/execution.py сравниваются строки результата колонка-к-колонке) оценивает это точно так же, как действительно неверный запрос, чего дообученный специалист никогда не дасть, потому что он запомнил стандарты этого датасета из 111 обучающих примеров — а фронтир-модель, вызванная zero-shot, просто не может об этом знать. Полная порций-по-провалам таксономия в COMPARISON.md.
Итак: разрыв по точности реален, но отчасти это артефакт того, что оценивает eval, а не чисто разрыв в рассуждениях. Разрыв по задержке и стоимости — не артефакт — 207ms/локально/бесплатно против 1055ms/$1.06 за 1 тыс. вызовов — это фактический результат запуска локально-квановой 0.5B модели вместо вызова API, и именно на этом сравнении основана посылка проекта.
Собственные 2 провала специалиста (из 28) были настоящими логическими ошибками, а не несоответствием форматирования: галлюцинирование правдоподобной колонки orders.total, которой нет в этой схеме, и исключение квалификатора таблицы в одном многократном SELECT. Обучение сходилось стабильно на 3 эпохах (eval loss 0.060 → 0.048 → 0.008), а квантованизация (988MB f16 → 373MB q4_k_m) обслуживается через Ollama примерно за 200мс..
Что здесь реально
Здесь важно быть честным больше, чем может показаться — это разница между проектом, которому рекрутёр может доверять, и тем, что выглядит как маркетинг.
Генерация базы и датасета доказуемо корректны.
shopsphere.dbсоздаётся детерминированно (сид = 42); каждая из 139 золотых пар (вопрос, SQL) вdata/*.jsonlсоздаётся из параметрических шаблонов и выполняется против реальной базы на этапе сборки — шаблон, дающий невалидный SQL, проваливает сборку, а не молча выдаёт неверную метку.Корректность eval-харнеса сама протестирована, а не принята постулатом —
tests/test_harness_oracle.pyутверждает, что оракуляровый предиктор (возвращающий золотой SQL дословно) даёт ровно 100%, а умышленно неверный предикков даёт ~0%, прежде чем чьему-то числу доверяют.Execution-accuracy, а не сравнение строк.
eval/execution.pyсравнивает результирующие множества (не учитывая порядок, если только в золотом запросе нетORDER BY) — запрос, написанный иначе, но семантически эквивалентный, всё равно засчитывается как корректный.Дообучение реальное, на этой машине, с подтверждённой сходимости. LoRA (8.8M обучаемых параметров, 1.75% от модели) за 3 эпохи, eval loss монотонно снижается на каждой эпохе. См. Engineering notes ниже о двух реальных багах, найденных и исправленных по пути.
SQL-выполнение настоящим образом песорчено, а не просто проинструктировано «вести себя хорошо»: read-запросы валидируются с помощью regex-списка разрешённых и выполняются на настоящем read-only SQLite-соединении (
mode=roна уровне ОС) — даже баг в regex-защите не приведёт к записи. Это важно не только для eval-харнеса, потому что та же защита работает в MCP-сервере, где SQL приходит от модели, отвечающей на вопрос агента, а не из курируемого eval-набора.MCP-сервер — это реальный вызываемый инструмент с настоящим дообученной моделью, проверено end-to-end:
nl_to_sql("Какие сотрудники не имеют назначенного менеджера?")→ генерация SQL через квантованную модель на Ollama → выполнение запроса read-only → возвращает настоящие строки → пишет задержку/стоимость в observability.Observability реализована самостоятельно и не имеет зависимостей —
observability/logger.pyлогирует каждый вызов (задержку, токены, примерную стоимость, успех/неуспех) в локальный файл SQLite, без стороннего аккаунта, по той же схеме, что иpr-review-agent.Frontier-база тоже настоящая —
eval/baseline_frontier.pyделал запросы к живому Claude API (Claude Haiku 4.5), а не только через красивый импорт. Его «провалы» оказались реальной находкой для методологии evala — см. Результаты выше иождения.
Инженерные заметки: два реальных бага, найденных при реальном запуске
Фактическое выполнение дообучения (а не оставление его в теории) выявило два настоящих бага с памятью в PyTorch, оба исправлены в текущем коде:
Бесконечный рост кэшем MPS. Обучение на Apple Silicon через MPS-задний план
transformers.Trainerприводило к раздуванию процесса до 23GB RSS и зависание, из-за динамического добавления padding по батчам — каждая уникальная форма (batch, seq_len) получает собственный памятьpool в MPS-аллокаторе PyTorch, который не возвращает OSS-память. Исправление:--device cpuвfinetune.py, и, в фундаменталь, фиксированная длина padding (ниже), чтобы этот класс багов не повторился на любом бакенде.Обращение с Trainer/DataLoader, а не сама модель. Непосредственнная прямая propagation+обратная propagation показала 1.6 секунды на примере; тот же расчёт через
transformers.Trainerпростаивал по несколько минут между логами без соответствующей нагрузки. Причина — изолировать фактическую модель+LoRA с ручным замером времени, прежде чем принять, что баг в коде модели. Исправление: заменитьTrainerна цикл обучения околоставим вручную (~40 строк) вtraining/finetune.py— та же LoRA-установка, прямой контроль над циклом батчей, без запредельных оверхейдов. Также перевели сборку батчей с динамического на фикированную длину padding (все батчи одинаковой формы) — это отдельно исправило паттерн фрагментации аллокатора из бага #1.
Ни то, ни другое — не в обходит и not workaround — оба видны в 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 soundrequirements-train.txt добавляет torch/transformers/peft/trl для пути fine-tuning — тяжелее, держится отдельно, чтобы путь eval/serving/MCP ставился быстро.
Запуск полного пайплайна
1. Baseline на fронтире (требуется ANTHROPIC_API_KEY):
export ANTHROPIC_API_KEY="..."
python -m eval.baseline_frontier --model claude-haiku-4-5
# writes eval/results_claude-haiku-4-5.json2. Fine-tune специалиста (это то, что реально было запущено для получения результатов выше — ~15 минут активного вычисления на CPU ноутбука, хотя wall clock сильно depends на нагрузке; 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 instructions3. Замерить специалиста так же, как бейзлайновый путь:
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Тюнинг: масштабирование
Uказанные выше результаты используют Qwen2.5-Coder-0.5B-Instruct на CPU — для быстрой локальной итерации. training/finetune.py --base-model принимает любую HF causal-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 определяется автоматически, но пока не рекомендуется для этой задачи — см. Engineering notes выше.
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Добавьте в MCP-конфиг Claude Desktop (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-выполнение доступно только для чтения на двух независимых уровнях: regex-правило, разрешающее только
SELECT/WITH, а также настоящая уровня ОС за RZ-соединение SQLite (file:...?mode=ro) — второй уровень надёжности.MCP-сервер никогда не выполняет запрос, если он не прошёл проверку, независимо от того, что попросили модель или вызывающий агент.
В репозитории нет секретов.
ANTHROPIC_API_KEYчитается только из окружения.
Что я построил бы дальше
Нормализовать eval для superset-колонок — считать предсказание корректным, если значения требуемых золотым колонок присутствуют, а не требовать точного совпадения колонка-к-колонке. Это фикс, который заявлен в «Таксономия ошибок» в
COMPARISON.md; он очень наверноем закроет большую часть измеренного разрыва 53.6% → 92.9% и даст сравнение, которое изолирует собственно способность рассуждать от подгонки под стандарты.Запустить
eval/baseline_frontier.pyи против Claude Sonnet — чтобы точка сравнения была сильнее (Haiku — это дешёвый/быстрый уровень; Sonnet — вопрос «насколько сама по себе сила модели закрывает разрыв»).Дообучить
Qwen2.5-Coder-1.5B-Instructна GPU и сравнить точность с 0.5B результатом 92.9% — чтобы напрямую количественно оценить tradeoff размера и качества.DPO по направлению двух известных fail-мод специалиста (галлюцинируемые колонки, пропажа квалаifiers таблиц в multi-join) — теперь, когда реальные данные про провалы естьи-точается.
vLLM-serving для измерения пропускной способности в сравнении с каналом Ollama/GGUF.
This server cannot be deployed
Maintenance
Related MCP Connectors
Query your warehouse or a CSV with Claude/ChatGPT over MCP, governed by table-level ACL + audit.
- mcpOAuthcom.gibsonai
GibsonAI MCP server: manage your databases with natural language
Analytical memory for AI agents: a real Postgres queried in plain English over MCP. One command.
Query your org's data in natural language — read-only MCP access to SQL, NoSQL, files & warehouses.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables natural language database queries by combining Ollama's language models with SQLite database access through an MCP server.-
- FlicenseNot gradedqualityDmaintenanceEnables querying and managing a SQLite database using natural language, with an MCP server and Groq LLM.-
- FlicenseNot gradedqualityBmaintenanceEnables AI agents to query a SQLite database using natural language through the Model Context Protocol (MCP). Includes security guardrails that block destructive SQL operations.-
- AlicenseNot gradedqualityDmaintenanceMCP tool server providing SQLite database access for AI agents.MIT