ИИ-агент для автоматизации первой линии технической поддержки. Принимает тикеты, классифицирует их, ищет решение в базе знаний (RAG) и эскалирует сложные задачи на L2 или разработчиков. Реализует механизм self-learning: пополняет базу знаний решениями закрытых багов.
- Архитектура
- Стек технологий
- Как это работает (Workflow)
- Архитектурные решения
- Структура проекта
- Быстрый старт
- Логи
Проект состоит из двух независимых процессов: самого агента (l1-agent) и симулятора второй линии/разработки (mock-expert). Они общаются исключительно через внешний mock-API.
┌─────────────────────┐
│ mockapi.io │
│ /tickets │
└─────────┬───────────┘
│ poll
▼
┌──────────────────────────────────────────────────────────────┐
│ l1-agent │
│ │
│ poller.py ──sync──▶ SQLite (state.db) │
│ │ │
│ ├─▶ sort.py (LLM) Классификация (категория/вес) │
│ │ │
│ ├─▶ router.py │
│ │ ├─▶ rag.py (Chroma) ── найдена статья ──▶ Закрыт │
│ │ │ │
│ │ └─▶ sort.py (LLM) classify_escalation │
│ │ ├─ infra ──▶ Эскалация L2 │
│ │ ├─ bug ──▶ Эскалация Dev │
│ │ └─ info ──▶ Консультация │
│ │ │
│ └─▶ watcher Ожидание ответа │
│ │ ответ получен ──▶ Возврат в очередь │
│ └─▶ self-learning: bug ──▶ Добавление в KB │
└──────────────────────────────┬───────────────────────────────┘
│ POST/GET/PATCH
▼
┌───────────────────────┐
│ mockapi.io │
│ /escalations │
└───────────┬───────────┘
│ poll
▼
┌───────────────────────┐
│ mock-expert │
│ (симулятор L2/dev) │
└───────────────────────┘
| Компонент | Технология | Обоснование |
|---|---|---|
| LLM | Gemini API (OpenAI-совместимый) | Structured output устойчив к опечаткам. Легко заменить провайдера через .env. |
| База данных | SQLite | Идеально для single-process агента. Дает SQL-очередь (ORDER BY) из коробки. |
| Векторная БД | ChromaDB (embedded) | Не требует отдельного сервера для RAG. |
| Эмбеддинги | paraphrase-multilingual-MiniLM |
Компактная локальная модель с поддержкой русского языка. |
| Интеграции | mockapi.io | Замена реальных API трекеров и мессенджеров для тестового контура. |
- Сбор (Polling): Агент забирает новые тикеты из mock-API и сохраняет в локальную SQLite.
- Классификация (Sort): LLM определяет категорию и приоритет заявки.
- Поиск (Router + RAG): Chroma ищет релевантную статью. Если similarity score >
0.6— тикет закрывается автоматическим ответом (Сценарий А). - Эскалация: Если решения нет, LLM маршрутизирует тикет:
infra: падение сервисов (Сценарий Б -> L2)bug: ошибки в коде (Сценарий В -> Разработка)info: вопросы и консультации (отдельный канал)
- Ожидание ответа: Агент мониторит эскалированные тикеты. Как только
mock-expertдает ответ, тикет получает статусresumed=1(высший приоритет в очереди). - Обучение (Self-learning): Тикет закрывается. Важно: в базу знаний (KB) сохраняются только ответы типа
bug, так как фикс бага перманентен, в отличие от временных инфраструктурных рестартов или разовых консультаций.
- SQLite вместо очередей (Kafka/Redis/Celery): Архитектура в один процесс делает распределенные брокеры избыточными. Очередь реализована через SQL (
ORDER BY priority, resumed, created_at). - Изоляция Mock-Expert: Скрипт-симулятор вынесен в отдельный процесс и отвечает по заготовленным ключевым словам, а не через LLM. Это сделано для четкого разделения: скрипт симулирует человека, а не является частью тестируемого ИИ-агента.
- Ограничения LLM API: При использовании Free Tier квота запросов (RPM) может быстро исчерпаться из-за 2 вызовов на тикет. В поллере реализован backoff.
- Качество тестовых данных: Генератор mockapi иногда создает несогласованные тикеты (название про принтер, описание про зависание ОС). Агент ориентируется на описание.
- Ограничение решений: Сделал всего 5 первоначальных инструкций, при первоначальном расширении бд (а также более широким ответам от L2/dev) агент будет ориентироваться намного лучше
├── l1/ # L1-агент
│ ├── agent/ # Основная логика (poller, router, sort)
│ ├── db/ # Работа с SQLite и .sql запросы
│ ├── rag/ # ChromaDB, эмбеддинги, markdown-статьи
│ └── integrations/ # Клиенты к mockapi
├── mock-expert/ # Симулятор второй линии
│ ├── responder.py # Логика ответов и задержек
│ └── responses.py # Шаблоны ответов
└── README.md
Откройте два терминала (для агента и симулятора).
python3 -m venv l1/venv && source l1/venv/bin/activate
pip install -r l1/requirements.txt python3 -m venv mock-expert/venv && source mock-expert/venv/bin/activate
pip install -r mock-expert/requirements.txtСоздайте файлы .env в папках l1/ и mock-expert/:
LLM_API_KEY=...
ESCALATIONS_URL=https://<project-id>.mockapi.io/escalations
ESCALATIONS_URL=https://<project-id>.mockapi.io/escalationsВыполняется один раз перед первым запуском (из корня репозитория, в окружении l1):
python -m l1.rag.rag python -m l1.agent.poller python -m mock-expert.responderProcessing ticket 27: Не могу войти в систему
-> Доступ и авторизация / Средний (Запрос касается прав доступа пользователя к функционалу системы, что не является критической ошибкой, но требует администрирования.)
[RAG] Лучшее совпадение: «Не могу войти в систему, забыл пароль» score=0.368
[L2] Тикет 27 отправлен сисадмину: Не могу войти в систему: Нужно выдать права на чтение для модуля "Отчеты".
Processing ticket 68: Не работает принтер
-> Оборудование / Средний (Проблема связана с физической неисправностью оргтехники, препятствующей выполнению рабочих задач.)
[RAG] Лучшее совпадение: «Ошибка 500 при формировании отчёта» score=0.334
[L2] Тикет 68 отправлен сисадмину: Не работает принтер: Принтер в кабинете 204 жует бумагу.
Processing ticket 56: Забыл пароль
-> Доступ и авторизация / Средний (Пользователь не может войти в систему из-за блокировки учетной записи, что требует сброса пароля.)
[RAG] Лучшее совпадение: «Не могу войти в систему, забыл пароль» score=0.691
[A] Тикет 56 закрыт по статье «Не могу войти в систему, забыл пароль» (score=0.69)
Ответ пользователю:
Не могу войти в систему, забыл пароль
Решение:
1. На странице входа нажать "Забыли пароль?".
2. Ввести корпоративную почту, дождаться письма со ссылкой сброса.
3. Перейти по ссылке (действует 30 минут), задать новый пароль.
4. Если письмо не пришло за 5 минут — проверить папку "Спам".
5. Если аккаунт заблокирован после нескольких неудачных попыток — эскалировать на L2 для разблокировки вручную.
[WATCHER] Пришёл ответ по тикету 68, возвращаю в очередь
[CLOSE] Тикет 68 (infra) закрыт без записи в KB. Ответ пользователю:
Переустановил драйвер печати на сервере печати, очередь заданий сброшена.
[mock-expert] 27: ответ запланирован через 44с
[mock-expert] 68: ответ запланирован через 15с
[mock-expert] 68 (infra): ответ отправлен -> Переустановил драйвер печати на сервере печати, очередь заданий сброшена.
[mock-expert] 27 (infra): ответ отправлен -> Разобрался с проблемой на инфраструктурном уровне, всё должно работать. Дайте знать, если повторится.