AI-агент для интеллектуальной работы с Excel-файлами металлургической компании ЕВРАЗ.
Система позволяет загружать Excel-файлы с ценами на металлы, автоматически нормализовать их в
факт-таблицу (mart.price_facts) и отвечать на вопросы на естественном языке через
LangGraph-агент с генерацией SQL.
Архитектура ориентирована на prod-ready: лёгкое entity-resolution по справочникам, read-only доступом к БД для генерации SQL, асинхронный ingestion, наблюдаемость (Prometheus), auth и rate limiting.
- Архитектура
- Возможности
- Стек технологий
- Быстрый старт
- Конфигурация
- API
- Асинхронный ingestion
- Наблюдаемость
- Auth и rate limiting
- Безопасность БД (read-only роль)
- Schema Inference
- Golden dataset
- Секреты
- Структура проекта
- Лицензия
Excel файл
│
▼
Schema Inference (LLM, разово на новый формат)
│ → Template Fingerprint (кэш схем в mart.sheet_templates)
│ → Human confirmation (для новых форматов)
▼
Normalize → raw.cells (аудит) + mart.price_facts (плоская факт-таблица)
│
▼
Entity Resolution (item_name/supplier/sheet_period из mart.price_facts)
│ + pg_trgm fuzzy-поиск (similarity()/%) по item_name/supplier
▼
LangGraph агент: Classifier → Planner → CodeGen → Executor → Verifier → Answer
│ (entity-resolution выполняется внутри Classifier/Planner)
▼
PostgreSQL (mart.* — SQL выполняет Executor-узел)
Архитектура построена вокруг нормализованной факт-таблицы и генерации SQL без тяжёлых векторных поисков:
- Entity-resolution: на ingestion собираются уникальные значения справочников
(
item_name,supplier,sheet_period) напрямую изmart.price_facts(десятки–сотни записей). В рантайме вопрос сопоставляется с кандидатами через pg_trgmsimilarity()/%(без эмбеддинг-модели, без BM25-индекса, без RU-лемматизации). - Нормализованная схема:
raw.*(files/sheets/columns/cells — аудит) иmart.price_facts(long-таблица фактов), на которой агент генерирует SQL. - Кэш ответов: нормализованный вопрос → SQL → результат хранится в
query_cache.
- Загрузка Excel-файлов (
.xlsx/.xls) с асинхронной фоновой обработкой и опросом статуса. - Нормализация в
mart.price_facts(идемпотентная, перезалив файла не дублирует факты). - LangGraph-агент: Classifier → Disambiguation → Planner → CodeGen → Executor → Verifier → Answer.
- Генерация безопасного SQL с keyword-blacklist валидацией в
codegen_node. - Entity-resolution по справочникам + pg_trgm fuzzy-поиск.
- Schema Inference (LLM) + Template Fingerprint для разнородных форматов таблиц.
- API-key auth + rate limiting (slowapi).
- Prometheus-метрики (
/metrics) и расширенные query_logs. - Golden dataset (pytest, marker
golden) для регрессионного тестирования.
- FastAPI — веб-фреймворк.
- LangGraph — явный StateGraph агента (без LangChain-обёртки).
- PostgreSQL +
pg_trgm(fuzzy-поиск для entity-resolution). - SQLAlchemy 2.0 (async, asyncpg) + Alembic.
- prometheus-client, slowapi, pytest.
cp .env.example .env
# заполните .env (LLM, Postgres)
docker compose up --build
# миграции
docker compose exec service alembic upgrade headСервис поднимется на :8000, фронтенд — на :8080.
Основные переменные (полный список — в .env.example):
| Переменная | Описание |
|---|---|
LLM_BASE_URL, LLM_API_KEY, LLM_MODEL_PRIMARY, LLM_MODEL_CHEAP |
LLM-клиент |
TRIGRAM_THRESHOLD |
порог pg_trgm similarity для fuzzy-сопоставления сущностей |
DB_STATEMENT_TIMEOUT_MS |
statement_timeout для БД (отдельно от REQUEST_TIMEOUT_S) |
API_KEY |
API-ключ для /files/* и /ask/* (пусто = dev) |
RATE_LIMIT_ASK, RATE_LIMIT_UPLOAD |
rate limiting (slowapi) |
INGESTION_QUEUE_MODE |
inproc / celery / arq |
POST /files/upload— загрузка (асинхронно, возвращаетfile_id).GET /files/{id}/status— опрос статуса обработки.GET /files— список.GET /files/{id}— детали.GET /files/{id}/sheets,/columns,/cells— просмотр raw-структуры.POST /files/{id}/sheets/{sheet_id}/infer-schema— Schema Inference (LLM).POST /files/{id}/sheets/{sheet_id}/confirm-schema— подтверждение схемы.
POST /ask— вопрос к агенту (mode:auto|agent).
GET /trace/...— трассировка выполнения графа.
Парсинг + нормализация + entity-resolution вынесены из синхронного /files/upload в фоновую
очередь (inproc по умолчанию; интерфейс совместим с celery/arq). Клиент получает file_id
и опрашивает статус через GET /files/{id}/status.
Статусы: uploaded → processing → ready | failed.
GET /metrics отдаёт метрики Prometheus:
- RPS и латентность
/ask(по статусу). - Per-node latency графа (classifier/planner/codegen/executor/verifier/answer).
- Доля
failed/low_confidence. - Token usage LLM.
query_logs расширены полями: latency по узлам, token usage/стоимость, текст ошибки при failed.
/files/*и/ask/*защищены API-ключом (заголовокX-API-Key), проверка черезverify_api_key. ЕслиAPI_KEYпуст — auth отключён (dev-режим).- Rate limiting через slowapi (
RATE_LIMIT_ASK,RATE_LIMIT_UPLOAD).
Executor-узел выполняет сгенерированный SQL под основной ролью БД. Защита от опасных
запросов обеспечивается keyword-blacklist валидацией SQL в codegen_node (блокировка
INSERT/UPDATE/DELETE/DROP и не-mart сущностей). statement_timeout задаётся на уровне
сессии (DB_STATEMENT_TIMEOUT_MS).
Для разнородных форматов таблиц (сдвинутые шапки, вложенные заголовки, merged cells):
template_fingerprint.compute_sheet_fingerprintсчитает отпечаток структуры листа.- Если отпечаток совпадает с подтверждённым шаблоном в
mart.sheet_templates— схема применяется без вызова LLM. - Иначе
schema_inference.schema_inference_service.inferвызывает LLM со структурным выводом (PydanticSheetSchema), результат сохраняется какpending_confirmation. - Пользователь подтверждает/правит схему через
confirm-schema(статус →confirmed).
tests/golden_questions.json — набор вопросов с ожидаемыми SQL-сигнатурами/результатами.
tests/test_golden_dataset.py прогоняет их через агента (marker golden, включается --golden).
pytest tests/ # юнит-проверки (без LLM)
pytest --golden tests/ # интеграционные golden-тесты (требуют LLM и данные)Запускайте в CI при каждом изменении промптов/схемы.
Прод-секреты (LLM API-ключ, пароли БД, API_KEY) не должны лежать в .env в пайплайне
деплоя. Интеграция с secrets-менеджером (Hashicorp Vault / облачный аналог):
- Секреты загружаются в рантайм из Vault (или env-injection в CI/CD).
- Пример (Vault): приложение читает секреты по пути
secret/evraz/prodи передаёт их вSettingsдо создания движков БД.
src/
main.py # FastAPI app, lifespan, /metrics, /health
api/ # роутеры (files, agent, trace, schema, security, ratelimit)
core/
config.py # настройки (rate limits, timeout, trgm threshold)
db/ # engine, session, models (raw + mart)
excel/ # parser, normalize, schema_inference, template_fingerprint
metrics.py # Prometheus-метрики
services/
agent/ # LangGraph: graph.py, nodes (classifier/planner/codegen/executor/verifier)
mart/ # normalizer raw->mart
excel/ # ingestion_service, ingestion_queue, repository
entity_resolution/ # entity-resolver, query_cache (pg_trgm, без эмбеддингов)
tests/ # golden dataset + pytest
alembic/ # миграции
scripts/init-db/ # расширения Postgres (pg_trgm)
Проприетарная (внутренний проект).