ai-l1-support-agent
# AI L1 Support Agent
Контролируемый AI-native процесс первой линии поддержки. Текущий статус:
- этап 1: архитектура и доменные контракты — готово;
- этап 2: MCP-инструменты и тестовые интеграции — готово;
- этап 3: первичная классификация (triage), правила безопасности, ответы из KB и оценка качества — готово;
- этап 4: LangGraph-оркестратор, контрольные точки и полный процесс обработки — готово;
- этап 5: E2E-демонстрация, безопасная наблюдаемость и расширенная оценка качества — готово;
- этап 6: формальный критерий качества, проверка стабильности и тестовая матрица Linux — готово;
- этап 7: автономный wheel, CI и подготовка публичного репозитория — готово.
По умолчанию проект работает полностью локально: читает зафиксированный снимок тикетов,
пишет только в `data/runtime/` и не изменяет публичный MockAPI.
## Быстрый запуск: два сценария
Требования: Python 3.10–3.13 и [uv](https://docs.astral.sh/uv/).
```powershell
uv sync --locked
```
### Сценарий 1: воспроизводимый локальный запуск
Это основной режим для проверки логики, демонстрации и разработки. Он читает зафиксированный
снимок заявок, не требует сети и сохраняет все изменения только в `data/runtime/`.
`TICKET_SOURCE=fixture` уже является безопасным значением по умолчанию.
```powershell
# Обработать одну заявку из локального снимка
uv run support-agent-workflow poll --limit 1
# Прогнать пять изолированных сквозных сценариев
uv run support-agent-demo --run-id quick-start
```
Повтор с тем же рабочим каталогом восстанавливает состояние из SQLite и не дублирует
побочные действия. Для полностью нового прогона используйте другой `--run-id` демонстрации
или задайте новые `RUNTIME_PATH` и `SQLITE_PATH`.
### Сценарий 2: чтение реальной очереди MockAPI
Этот режим подтверждает интеграцию с открытым API из задания. Агент получает настоящую
заявку по сети, но ответы, статусы, Telegram-сообщения и GitHub Issues по-прежнему записывает
только локально. Удалённая очередь не изменяется.
```powershell
$env:TICKET_SOURCE = "remote"
$env:TICKETS_API_URL = "https://6a7ad74c8c69b3eb4a179621.mockapi.io/tickets/tickets"
$env:ALLOW_REMOTE_WRITES = "false"
$env:RUNTIME_PATH = "data/runtime/remote-quick-start"
$env:SQLITE_PATH = "data/runtime/remote-quick-start/agent.sqlite3"
uv run support-agent-workflow poll --limit 1
```
Для следующего запуска в том же терминале можно вернуться к локальному снимку:
```powershell
$env:TICKET_SOURCE = "fixture"
```
`RemoteTicketSource` намеренно поддерживает только чтение. `ALLOW_REMOTE_WRITES=false`
зафиксирован как дополнительный предохранитель; удалённый адаптер записи в проекте вообще
не реализован. Локальный overlay не синхронизируется с MockAPI, поэтому состояние разных
экземпляров агента не является общим.
MockAPI является внешним сервисом. Если он или сеть недоступны, команда намеренно завершится
ошибкой `ticket API is unavailable`, не переключаясь незаметно на локальные данные. В таком
случае проверьте URL и повторите сетевой запуск позднее; автономный сценарий остаётся доступен.
### Запуск MCP-сервера
```powershell
uv run support-agent-mcp
```
Команда запускает MCP-сервер `support-tools` через stdio. Для локальной разработки `.env`
необязателен: безопасные значения уже являются значениями по умолчанию. Для изменения
настроек скопируйте `.env.example` в `.env`; секреты и рабочие файлы исключены из Git.
## Установка собранного пакета
Wheel содержит локальные тестовые заявки, набор оценочных сценариев, демонстрацию и
тестовую базу знаний, поэтому команды работают и вне исходного репозитория. Изменяемое
состояние создаётся в `data/runtime/` текущего каталога или по путям из окружения.
```powershell
uv build
uv venv .wheel-venv
uv pip install --python .wheel-venv/Scripts/python.exe dist/*.whl
.wheel-venv/Scripts/support-agent-eval.exe --runs 2
```
Исходный архив и wheel собираются в `dist/`; каталог намеренно не хранится в Git.
## Полный процесс обработки
Команда `support-agent-workflow` запускает LangGraph с сохраняемыми контрольными точками
поверх тех же сервисов, которые опубликованы как MCP-инструменты:
```powershell
# Один тикет или вся очередь новых тикетов
uv run support-agent-workflow start 3
uv run support-agent-workflow poll --limit 10
# Состояние после рестарта процесса
uv run support-agent-workflow status 3
# Безопасная хронология метаданных контрольных точек и инструментов без содержимого заявки
uv run support-agent-workflow trace 3
# Возобновление ожидания пользователя или инженера
uv run support-agent-workflow user <ticket-id> "Уточнённые симптомы"
uv run support-agent-workflow engineer <ticket-id> "Подтверждённое решение" --author l2 --confirmed
```
Одна заявка всегда использует идентификатор потока `support-ticket:<ticket-id>`.
Контрольные точки хранятся в SQLite, а все записи в заявки, Telegram, GitHub и черновики
дополнительно защищены стабильными ключами идемпотентности. Поэтому восстановление
контрольной точки и повтор узла не создают дубликаты побочных действий.
## Воспроизводимая демонстрация
`support-agent-demo` запускает пять синтетических E2E-сценариев в отдельном каталоге,
не затрагивая обычный `data/runtime/agent.sqlite3`:
```powershell
uv run support-agent-demo
uv run support-agent-demo --run-id interview-demo
```
Покрыты ответ из KB, эскалация L2 и одноразовое решение, отчёт об ошибке и переиспользуемое
решение, уточнение пользователя с повторной классификацией, низкая уверенность и
`safe_review`. Завершённый запуск повторно читает сохранённый `demo-report.json`;
незавершённый каталог с тем же ID не перезаписывается. В отчёте есть фазы, количество
контрольных точек и событий аудита, но нет текстов заявок и содержимого вызовов инструментов.
## Проверка
```powershell
uv run ruff format --check .
uv run ruff check .
uv run mypy
uv run pytest --cov --cov-report=term-missing
uv run support-agent-eval --runs 3 --output data/runtime/evaluation.json
uv run support-agent-eval --case-id http-500-software-bug
```
GitHub Actions проверяет форматирование, Ruff, строгий mypy, pytest с покрытием ветвей и
стабильность тестового провайдера на Python 3.10 и 3.13. Отдельное задание собирает
sdist/wheel, устанавливает wheel вне исходного репозитория и запускает оценку качества,
демонстрацию и CLI процесса. Проверка через LM Studio не входит в CI, потому что требует
локальную модель; её зафиксированный прогон описан ниже.
Тесты используют официальный MCP-клиент в памяти и отдельно запускают точку входа stdio.
Оценка качества прогоняет 20 подготовленных бизнес-сценариев классификации и решения через
выбранный `LLM_PROVIDER`, измеряет время каждого сценария и всего запуска; по умолчанию
используется воспроизводимый провайдер на основе правил.
Из 20 сценариев 13 помечены как `safety_critical`. Критерий качества выполнен, только если
доля успешных попыток не ниже 90%, все критические попытки успешны, ошибки провайдера и
схемы отсутствуют, а повторные запуски каждого сценария дают одинаковый смысловой
результат. `--runs 3` проверяет не 20, а 60 попыток; хэш исходного набора сохраняется в
JSON-отчёте.
## Первичная классификация и база знаний
Первичная классификация возвращает строгий `TriageDecision`: достаточность и перечень
недостающих фактов, категорию, приоритет, маршрут, оценку уверенности (`confidence`),
извлечённые факты и флаги риска. После ответа
модели детерминированная политика отдельно проверяет:
- недостаток фактов — запросить конкретное уточнение;
- низкую уверенность при достаточных фактах — `safe_review`;
- модельный priority — нормализовать независимой бизнес-политикой по явным признакам;
- инъекцию инструкций — проигнорировать и отметить флагом;
- лимит уточнений — передать в безопасную очередь исключений.
Поиск в KB выполняется до эскалации. Автоматический ответ разрешён только при совпадении
категории, порога релевантности и отсутствии exclusions статьи. Ответ состоит из текста
статьи и обязательной ссылки `KB:<article_id>`; свободной генерации решения нет.
Проверенные ответы пользователя не теряются после повторной классификации: они участвуют
в поиске и проверке применимости статьи, добавляются в выжимку для L2, отчёт об ошибке и
контекст будущего черновика статьи. Telegram-эскалация и GitHub Issue всегда содержат ссылку
на исходную заявку, построенную от настроенного `TICKETS_API_URL`.
## LM Studio
Адаптер LM Studio уже реализован через совместимый с OpenAI строго структурированный ответ:
```dotenv
LLM_PROVIDER=lmstudio
LLM_BASE_URL=http://127.0.0.1:1234/v1
LLM_MODEL=google/gemma-4-12b-qat
LLM_API_KEY=
LLM_REASONING=none
```
После запуска локального сервера выбранную модель можно проверить командой
`uv run support-agent-eval`. `google/gemma-4-12b-qat` Q4_0 прошла текущий набор 20/20
за 74,9 секунды и E2E-демонстрацию 5/5 за 22,9 секунды при контексте 13 056 токенов.
Провайдер на основе правил остаётся безопасным значением по умолчанию для тестов и
воспроизводимой демонстрации. Подробнее:
[проверка LM Studio](docs/lm-studio-evaluation.md).
## MCP-инструменты
| Группа | Инструменты |
|---|---|
| Заявки | `tickets_list_new`, `tickets_get`, `tickets_update_classification`, `tickets_add_reply`, `tickets_change_status` |
| База знаний | `knowledge_search`, `knowledge_get_article`, `knowledge_create_draft` |
| Эскалация | `telegram_notify_l2`, `github_create_bug` |
| Обратная связь | `feedback_add`, `feedback_list` |
Входные и выходные JSON Schema формируются MCP SDK из строгих Pydantic-моделей. Все
изменяющие инструменты требуют стабильный `idempotency_key`, журналируются в SQLite и
имеют соответствующие MCP annotations. `knowledge_create_draft` создаёт только черновик
со статусом `pending_human_review`; инструмента автоматической публикации намеренно нет.
## Данные и безопасность
- `TICKET_SOURCE=fixture` — воспроизводимый режим по умолчанию.
- `TICKET_SOURCE=remote` — чтение публичного MockAPI без права записи.
- `ALLOW_REMOTE_WRITES=false` — зарезервированный явный предохранитель; текущий адаптер
вообще не реализует удалённые записи.
- `data/runtime/` — SQLite, локальные изменения, исходящие сообщения и тестовые Issues;
каталог исключён из Git.
- `data/knowledge_base/` — опубликованные тестовые статьи, которые входят в репозиторий.
- `data/fixtures/` — зафиксированные входные данные для тестов.
- `data/demo/` — пять синтетических заявок для изолированной E2E-демонстрации.
- `trace` намеренно возвращает только метаданные. Полное состояние контрольной точки остаётся
локально в SQLite и должно защищаться как рабочие данные поддержки.
## Документы
- [Финальная архитектура](docs/architecture.md)
- [Устройство проекта и руководство по защите](docs/project-and-presentation-guide.md)
- [ADR-001: управляемый процесс и модульный MCP-сервер](docs/decisions/ADR-001-controlled-workflow-mcp.md)
- [Контракты домена](docs/contracts/domain-contracts.md)
- [Правила первичной классификации и маршрутизации](docs/rules/triage-routing.md)
- [Отчёт по этапу 1](docs/stage-1-report.md)
- [Отчёт по этапу 2](docs/stage-2-report.md)
- [Отчёт по этапу 3](docs/stage-3-report.md)
- [Отчёт по этапу 4](docs/stage-4-report.md)
- [Отчёт по этапу 5](docs/stage-5-report.md)
- [Отчёт по этапу 6](docs/stage-6-report.md)
- [Отчёт по этапу 7](docs/stage-7-report.md)
- [Сценарий демонстрации и сдачи](docs/demo-guide.md)
- [Проверка модели в LM Studio](docs/lm-studio-evaluation.md)
- [ADR-002: LangGraph и сохраняемые контрольные точки](docs/decisions/ADR-002-langgraph-checkpoints.md)
Проект распространяется по [лицензии MIT](LICENSE). Правила безопасного сообщения об
уязвимостях описаны в [SECURITY.md](SECURITY.md).
## Машиночитаемые контракты
- [Нормализованная заявка](contracts/ticket.schema.json)
- [Результат первичной классификации](contracts/triage-decision.schema.json)
- [Кандидат в базу знаний](contracts/knowledge-candidate.schema.json)
Локальный снимок данных находится в `data/fixtures/tickets.snapshot.json`. Он используется
для воспроизводимых тестов; удалённый MockAPI рассматривается как источник только для чтения.
TDQS
Scored across 12 tools
Each tool targets a distinct action within its domain: ticket management, knowledge lookup, notification, bug creation, and feedback. The descriptions clearly differentiate overlapping concepts like tickets_add_reply vs feedback_add and update_classification vs change_status.
Tools largely follow a domain_verb_noun pattern (tickets_add_reply, knowledge_get_article, github_create_bug). Minor exceptions like feedback_add/feedback_list (implicit object) and tickets_list_new (adjective instead of noun) create slight inconsistency but remain predictable.
The 12 tools cover a well-scoped support agent workflow: ticket triage, knowledge access, escalation, bug reporting, and feedback. The count is within the ideal range and each tool serves a clear purpose.
Core ticket operations are covered, but there is no way to list tickets by status other than 'new', which prevents the agent from resuming work on in-progress or waiting tickets. Knowledge draft creation exists without management (list/update), leaving an incomplete workflow.