nl-sql / docs /01_architecture.md
liovina's picture
Deploy NL_SQL HEAD to HF Space
942050b verified
|
Raw
History Blame Contribute Delete
20.8 kB
# NL→SQL Assistant — архитектура (максимально нагруженный вариант)
**Дата:** 2026-05-10
**Статус:** v1 — superseded after CX/KM review (2026-05-10). Документ оставлен как исторический. Действующая baseline: `02_architecture_v2.md`.
> ⚠️ **Известные ошибки v1**, исправлены в v2:
> - **BIRD Mini-Dev = 500 примеров** (не 1500/11 БД, как ниже в разделе 5)
> - **codestral-2501 deprecated** с ноября 2025 → переход на `codestral-latest` (v25.08)
> - 11-узловой pipeline → 6 узлов
> - 4 коллекции Chroma → 2
> - стек: убраны Prometheus + OTel + Redis (избыточно для solo-demo)
> - Vega-Lite от LLM → детерминированный chart picker + Plotly шаблоны
> - Mistral-only → provider abstraction + 30-question bakeoff
> - eval target: 50% EA → baseline 35-40%, stretch 50%, hard checkpoint неделя 3
> «Максимально нагруженный» здесь = всё, что реально нужно для серьёзного
> демо-проекта уровня Senior Data Engineer, без фейкового overengineering'а.
> Каждый компонент обоснован задачей; ничего «на будущее».
---
## 1. Системная диаграмма
```text
┌──────────────────────────┐
│ Web UI (Next.js + React)│
│ ─ chat input │
│ ─ table / chart / SQL │
│ ─ history + bookmarks │
└────────────┬─────────────┘
│ HTTPS
┌──────────────────────────────────────────┐
│ FastAPI gateway (auth, rate-limit, CORS)│
│ /ask, /databases, /history, /eval/run │
└────────────┬─────────────────────────────┘
┌────────────────────┼────────────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌────────────────┐ ┌────────────────────┐
│ LangGraph │ │ Eval harness │ │ Schema indexer │
│ NL→SQL graph │ │ (BIRD/Spider) │ │ (offline pipeline) │
└──────┬───────┘ └────────┬───────┘ └─────────┬──────────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────────────────────────┐
│ Shared services layer │
├──────────────┬──────────────┬─────────────┬──────────────────┤
│ Mistral API │ Chroma DB │ Postgres │ Redis │
│ codestral │ schema chunks│ target DBs │ result cache │
│ large-2 │ few-shot Q→S │ (multi-DB) │ rate-limit state │
│ mistral-embed│ │ + traces DB │ │
└──────────────┴──────────────┴─────────────┴──────────────────┘
┌──────────────────────────┐
│ Observability │
│ Prometheus + OpenTelemetry│
│ Langfuse traces │
└──────────────────────────┘
```
## 2. LangGraph pipeline
Реиспользуем структуру из RAG_Support_Assistant
(`classify → retrieve → rerank → generate → verify → evaluate`),
но узлы заточены под NL→SQL:
```text
┌────────────────┐
│ classify_intent│ intent = aggregation | ranking | filter |
└────────┬───────┘ time_series | comparison | lookup |
│ distribution
┌────────────────┐
│ select_database│ если в системе несколько БД — выбрать целевую
└────────┬───────┘ по интенту + ключевым словам
┌────────────────┐
│ retrieve_schema│ Chroma: relevant tables + columns + value samples
└────────┬───────┘
┌────────────────┐
│ retrieve_examples│ Chroma: top-k похожих Q→SQL пар (few-shot)
└────────┬───────┘
┌────────────────┐
│ generate_sql │ codestral-2501 + structured output (JSON-mode)
└────────┬───────┘ { "sql": "...", "rationale": "..." }
┌────────────────┐
│ static_validate│ sqlglot parse → SELECT-only guard → schema check
└────────┬───────┘ (table/column existence vs catalog)
│ FAIL ──────────► retry_loop (max 2)
│ OK
┌────────────────┐
│ explain_plan │ EXPLAIN на целевой БД, отказ если cost > threshold
└────────┬───────┘ (защита от full-scan на больших таблицах)
┌────────────────┐
│ execute │ read-only коннект, statement_timeout, LIMIT-guard
└────────┬───────┘
┌────────────────┐
│ verify_result │ проверки: непустой? типы соответствуют интенту?
└────────┬───────┘ аномалии? (нулей/null'ов слишком много)
│ FAIL ──────────► retry_loop
│ OK
┌────────────────┐
│ choose_format │ intent + result shape →
└────────┬───────┘ scalar | sentence | table | chart
┌────────────────┐
│ render_answer │ mistral-large-2: NL-объяснение + chart-spec (Vega-Lite)
└────────┬───────┘
┌────────────────┐
│ persist_trace │ sqlite traces + Langfuse span + Prometheus counter
└────────────────┘
```
**Retry loop:** при фейле узлов `static_validate`, `execute`, `verify_result`
граф возвращается к `generate_sql` с приклеенным error-context'ом
(текст ошибки + предыдущий SQL + разъяснение что не так). Лимит — 2 попытки,
после чего отдаётся диагностический ответ.
## 3. Schema-RAG: устройство индекса
**Проблема:** в BIRD есть БД с 50+ таблицами. Полная схема в промпт не лезет
и зашумляет генерацию.
**Решение:** offline-пайплайн `Schema indexer` строит несколько коллекций в Chroma:
| Коллекция | Чанк | Эмбеддится |
|---|---|---|
| `schema_tables` | таблица | имя + описание + список колонок + 3 sample строки |
| `schema_columns` | колонка | имя + тип + описание + min/max/nunique + 5 sample значений |
| `fewshot_qsql` | Q→SQL пара | вопрос + аннотация интента (SQL не эмбеддится) |
| `relations` | FK-связь | from_table.col → to_table.col + семантика |
При вопросе `retrieve_schema` делает гибрид BM25 + dense на `schema_tables`,
дотягивает топ-N колонок из `schema_columns` для отобранных таблиц,
добавляет связи между ними. Получается компактный «срез схемы» под вопрос —
обычно 5-15 таблиц вместо 50+.
`retrieve_examples` достаёт из `fewshot_qsql` 3-5 наиболее похожих
вопросов с эталонными SQL — это мощно поднимает качество на сложных диалектах.
## 4. Безопасность исполнения SQL
Read-only — это не «обещание промптом», а реальные гарды на четырёх уровнях:
1. **БД-роль:** отдельный postgres-пользователь с GRANT SELECT ONLY,
без CREATE/INSERT/UPDATE/DELETE/TRUNCATE/ALTER.
2. **Парсер:** `sqlglot` AST-валидация — отказ при не-SELECT, при множественных
стейтментах, при наличии CTE с DML, при `pg_*`/`information_schema` без whitelist.
3. **EXPLAIN-gate:** `EXPLAIN (FORMAT JSON)` перед `EXECUTE`,
отказ если `Total Cost > X` (порог настраивается на БД).
4. **Runtime:** `SET statement_timeout = 30s`, обязательный `LIMIT 10000`
если в запросе нет агрегации.
## 5. Eval harness
Отдельный модуль `eval/`, не часть онлайн-пайплайна:
```text
eval/
├── datasets/
│ ├── bird_mini.jsonl # 1500 Q→SQL пар, 11 БД
│ └── stackexchange_gold.jsonl # 20 наших эталонных вопросов
├── runner.py # прогон через граф, сравнение
├── metrics/
│ ├── execution_accuracy.py # сравнение result-set'ов
│ ├── exact_match.py # SQL string match (слабая метрика)
│ └── component_match.py # сравнение по AST-компонентам
└── reports/
└── 2026-05-10-baseline.html # отчёт по прогонам
```
CI прогоняет smoke-eval на 50 примерах при каждом merge в main.
Полный прогон — вручную или nightly.
**Целевое число:** Execution Accuracy ≥ 50% на BIRD-mini dev.
Опубликованные результаты codestral-2501 на BIRD ~57%, так что 50%
своими силами на узком сабсете — реалистично.
## 6. Multi-DB switching
В `config/databases.yml` описаны подключения:
```yaml
databases:
- id: stackexchange
dsn: postgresql://nlsql_ro@localhost/stackexchange
description: "StackOverflow public data — posts, users, votes"
schema_index: chroma://stackexchange
sample_questions: ["топ-10 тегов...", "распределение..."]
- id: bird_california_schools
dsn: sqlite:///data/bird/california_schools.sqlite
description: "California schools — performance, demographics"
schema_index: chroma://bird_california_schools
- id: chinook
dsn: sqlite:///data/chinook.sqlite
description: "Music store — invoices, tracks, customers"
schema_index: chroma://chinook
```
UI даёт переключатель «target DB», граф читает её из state.
## 7. UI (Next.js + React)
Минимально, но без обрезков:
- **Chat-style вход** с подсветкой SQL в ответе и copy-кнопкой.
- **Multi-format ответ** — компонент сам решает, что рендерить
(scalar / sentence / DataGrid / Vega-Lite chart).
- **«Show working»**: разворачивающийся блок с retrieved schema, few-shot,
rationale, EXPLAIN-планом, временем выполнения.
- **History + bookmarks** в localStorage + опционально на бэке.
- **DB switcher** + список sample-вопросов под каждую БД.
Отдельная страница **`/eval`** — таблица результатов eval-прогонов,
графики динамики Execution Accuracy по коммитам.
## 8. Стек целиком
| Слой | Технология | Почему |
|---|---|---|
| LLM | Mistral API: codestral-2501, mistral-large-2, mistral-embed | Жёсткое требование задачи |
| Orchestration | LangGraph | Уже знакома по RAG_SA, retry-loop из коробки |
| API | FastAPI + Pydantic v2 | Стандарт, типобезопасность |
| Vector DB | ChromaDB | Уже знакома, локально без отдельного сервиса |
| SQL parser | sqlglot | Multi-dialect, AST-валидация, dialect translation |
| Target DB | Postgres 16 (StackExchange) + SQLite (BIRD, Chinook) | Реализм + простота |
| Cache | Redis 7 | Кэш результатов SQL, rate-limit |
| Charting | Vega-Lite (через спеку из LLM) + Plotly fallback | LLM хорошо генерит Vega-spec'и |
| Frontend | Next.js 15 + Tailwind + shadcn/ui | Быстрый красивый UI |
| Observability | Prometheus + OpenTelemetry + Langfuse | Стандартный стек, переиспользуется из RAG_SA |
| Tests | pytest + httpx + testcontainers (Postgres) | Реальная БД в CI |
| Lint/Type | ruff + mypy strict (api/, agent/) | Как в DE_project |
| CI | GitHub Actions | smoke-eval + pytest + ruff + mypy |
| Deploy | docker-compose (dev) + Dockerfile multi-stage (prod) | Достаточно для демо |
## 9. Структура репозитория
```
NL_SQL/
├── api/ # FastAPI app, routers, middleware
├── agent/ # LangGraph nodes, prompts, state
│ ├── graph.py
│ ├── nodes/
│ │ ├── classify.py
│ │ ├── retrieve_schema.py
│ │ ├── retrieve_examples.py
│ │ ├── generate_sql.py
│ │ ├── validate.py
│ │ ├── execute.py
│ │ ├── verify.py
│ │ ├── render.py
│ │ └── retry.py
│ └── prompts/
├── llm/ # Mistral provider, retry, cost guard
├── schema_index/ # offline indexer for Chroma
│ ├── extractor.py # introspect Postgres/SQLite catalog
│ ├── enricher.py # описания, sample values, stats
│ └── builder.py # build Chroma collections
├── execution/ # SQL guards, EXPLAIN gate, runner
├── eval/ # см. раздел 5
├── frontend/ # Next.js UI
├── config/ # databases.yml, prompts.yml
├── data/ # BIRD dump, Chinook, sample dumps (gitignore)
├── tests/
├── docker-compose.yml
├── Dockerfile
└── docs/
├── 00_task.md
├── 01_architecture.md ← вы здесь
├── 02_eval_methodology.md ← TODO
└── 03_demo_questions.md ← TODO
```
## 10. Roadmap (этапы)
| # | Этап | DoD |
|---|---|---|
| 1 | **Bootstrap** | poetry/uv проект, FastAPI hello, Mistral provider, тесты на провайдер с моком |
| 2 | **Target DBs ready** | docker-compose поднимает Postgres со StackExchange dump + SQLite Chinook + BIRD dump в `data/` |
| 3 | **Schema indexer** | offline скрипт строит Chroma-коллекции, smoke-тест на retrieval |
| 4 | **Pipeline v1** | LangGraph граф работает на Chinook (простая БД), single-shot без retry |
| 5 | **Guards & verify** | sqlglot guard, EXPLAIN gate, retry-loop, тесты |
| 6 | **Eval harness** | runner + execution_accuracy метрика, baseline на BIRD-mini |
| 7 | **Multi-format render** | scalar/sentence/table/chart с автоопределением + Vega-Lite spec'и |
| 8 | **UI v1** | chat + DB switcher + history, end-to-end на 3 БД |
| 9 | **Polish & deploy** | docker-compose prod-like, README, демо-видео, eval-страница |
Этапы 1-3 — фундамент (~неделя на каждом темпе).
Этапы 4-6 — суть проекта (~2 недели).
Этапы 7-9 — витрина (~неделя).
Итого: ~5-6 рабочих недель в спокойном темпе или 2-3 в плотном.
## 11. Риски
| Риск | Вероятность | Митигация |
|---|---|---|
| codestral-2501 даёт <40% на BIRD | средняя | улучшить few-shot retrieval, добавить chain-of-thought, schema-linking шаг |
| StackExchange dump слишком большой для локалки (≥100GB) | высокая | взять mini-dump (`gaming.stackexchange.com`, ~1GB) — реализм без боли |
| EXPLAIN-gate ломает легитимные тяжёлые запросы | средняя | tune порог на БД, дать override-флаг для админа |
| BIRD dataset лицензия | низкая | CC-BY-SA-4.0, для демо OK |
| Mistral API rate limits на eval-прогоне | средняя | local cache на (prompt → response), батчинг, exponential backoff |
## 12. Что в этой архитектуре «нагруженного»
Если сравнить с минимальным NL→SQL (один промпт + один вызов LLM + execute):
- **+ LangGraph pipeline на 10+ узлов** с retry-loop и error-context.
- **+ Schema-RAG из 4 коллекций** вместо «вся схема в промпт».
- **+ Few-shot retrieval** из эталонных Q→SQL пар.
- **+ Static validate (sqlglot AST) + EXPLAIN-gate + 4-уровневая защита**.
- **+ Multi-DB** с переключателем и per-DB индексами.
- **+ Eval harness на публичном бенчмарке** с измеримой метрикой.
- **+ Multi-format рендер** (4 формата + auto-выбор графика).
- **+ Полноценный observability stack** (Prom + OTel + Langfuse).
- **+ Web-UI** с историей, eval-страницей, «show working».
Это потолок того, что осмысленно делать для демо-проекта без скатывания
в production-overhead (мульти-тенант, RBAC, OIDC, freshness monitor и т.д. —
всё то, что в RAG_SA уместно, а здесь было бы фейк-нагрузкой).