26 KiB
ML Core Swagger (Сдача)
Актуальный сдаваемый Swagger для ML core backend лежит в файле:
docs/ml_core_backend_openapi.yaml
Что входит в документ:
POST /api/v1/pipelines/generateGET /api/v1/pipelines/dialogsGET /api/v1/pipelines/dialogs/{dialog_id}/historyPOST /api/v1/pipelines/dialog/resetPOST /api/v1/pipelines/{pipeline_id}/runGET /api/v1/executionsGET /api/v1/executions/{run_id}
Что не входит в этот документ:
- auth, actions, capabilities ручки
- demo
/ml/*ручки изdemo-backend/openapi/*
Быстрая проверка локально:
cd backend
pytest -s --capture=no tests/test_ml_openapi_contract.py
Дополнительная валидация OpenAPI (если установлен валидатор):
python3 -m openapi_spec_validator docs/ml_core_backend_openapi.yaml
AI Copilot
Core Concept
Главная задача проекта: Трансформация статической документации API в автономную интеллектуальную систему. Пользователь предоставляет набор OpenAPI файлов и бизнес-логику, которую он хочет получить, чтобы протестировать, а система незамедлительно проектирует и исполняет готовые Pipelines для решения задачи.
The Value Chain
Action → Capability → Pipeline
Система работает по принципу восходящей абстракции:
- Actions (Технический слой): Набор разрозненных эндпоинтов из OpenAPI.
- Capability (Логический слой): Группа из одного или нескольких Actions, объединенных общей бизнес-целью. Это абстракция для AI, который знает, как выполнить конкретную функцию.
- Pipeline (Сценарный слой): Последовательность из Capabilities, решающая задачу пользователя.задачу. | Итоговый план действий. |
ЦА — продакт менеджеры
Проблема: у продакт менеджеров есть гипотеза (например: «Если отправлять пуш тем, кто бросил корзину через 15 минут, конверсия вырастет»), но чтобы её проверить, нужно ставить задачу в спринт, ждать 2 недели и отвлекать бэкенд.
Решение: Наш AI-copilot позволяет проджект менеджеру предоставить системе API маркетинговой платформы и CRM, а затем автоматически создать и запустить Pipeline, решающий бизнес-задачу.
User stories
| Роль | Я хочу (Действие) | Чтобы (Ценность) | Definition of Done (Критерии приемки) | Модуль |
|---|---|---|---|---|
| PM | Загрузить файл OpenAPI (Swagger) | Система получила сырую базу технических методов (Actions). | Файл парсится, эндпоинты сохранены в БД. | Ingestion |
| PM | Объединить несколько Actions в одну Capability | Скрыть техническую сложность и создать «навык» (напр. «Обновить профиль»). | В базе создана сущность Capability, связанная с 1+ Actions. | Capability |
| PM | Получить авто-описание для Capability от AI | Не тратить время на ручное заполнение названий и смыслов для каждого навыка. | Каждой Capability присвоено человекочитаемое имя и описание. | Semantic |
| PM | Описать бизнес-задачу в чате (напр. «Найди новичков и поздоровайся») | AI-копилот сам подобрал нужные Capabilities и построил из них Pipeline. | AI выдает валидный JSON-граф с ID навыков и логическими связями. | Synthesis |
| PM | Видеть сгенерированный сценарий в виде графа | Визуально подтвердить, что данные (напр. user_id) передаются верно. | На канвасе отрисованы ноды и стрелки (React Flow). | Synthesis |
| PM | Вручную отредактировать параметры внутри ноды | Финальный сценарий на 100% соответствовал конкретной задаче. | Форма редактирования корректно сохраняет данные в объект ноды. | Execution |
| PM | Подтвердить запуск Pipeline нажатием кнопки | Контролировать процесс и избежать случайных ошибок в реальных системах. | Запуск происходит только после клика; первая нода переходит в статус active. | Execution |
| PM | Наблюдать за выполнением шагов в реальном времени | Видеть прогресс и понимать, на каком этапе сейчас находится выполнение. | Активная нода подсвечивается; статусы (Success/Fail) обновляются в UI. | Execution |
| PM | Получить лог ответов от всех API по завершении | Убедиться, что гипотеза проверена и данные ушли/пришли корректно. | По завершении выводится панель с результатами (JSON-логи). | Execution |
Domain layer
Все сущности, покрывающие user-stories:
| Сущность | Что это | Зачем нужна |
|---|---|---|
| Action | Инструмент в ящике. | Хранит технические детали одного эндпоинта (URL, метод, JSON-схема). |
Что в модели Action:
| Поле | Тип | Назначение |
|---|---|---|
| id | UUID PK | Первичный ключ |
| operation_id | String | operationId из OpenAPI |
| method | HttpMethod enum | GET / POST / PUT / PATCH / DELETE … |
| path | String | URL-путь, напр. /users/{id} |
| base_url | String | Базовый URL из servers[] спецификации |
| summary | String | Краткое описание из OpenAPI |
| description | Text | Подробное описание из OpenAPI |
| tags | JSON | Теги для группировки |
| parameters_schema | JSON | JSON Schema query/path/header параметров |
| request_body_schema | JSON | JSON Schema тела запроса |
| response_schema | JSON | JSON Schema успешного ответа (2xx) |
| source_filename | String | Имя загруженного Swagger-файла |
| raw_spec | JSON | Оригинальный фрагмент операции из спецификации |
| created_at / updated_at | DateTime | Через TimestampMixin |
Capability|Навык мастера.|Описывает бизнес-логику (связку 1+ Actions) и семантику для AI. Node|Шаг в инструкции.|Конкретный блок в графе. Ссылается на Capability, но хранит индивидуальные настройки (например, текст письма именно для этого шага). Pipeline|Инструкция (чертеж).|Коллекция нод и связей между ними. Хранит общую структуру графа.
Что в модели Pipeline:
| Поле | Тип | Назначение |
|---|---|---|
| id | UUID PK | Первичный ключ |
| name | String | Название пайплайна |
| description | Text | Описание сценария |
| user_prompt | Text | Оригинальный промпт PM из чата |
| nodes | JSON | Список нод графа с параметрами и позициями |
| edges | JSON | Список рёбер графа и порядка выполнения |
| status | PipelineStatus enum | Статус пайплайна: DRAFT / READY / ARCHIVED |
| created_by | UUID FK | Ссылка на пользователя-автора |
| created_at / updated_at | DateTime | Через TimestampMixin |
Execution (Run)|Процесс сборки.|Хранит статус конкретного запуска (ID, время старта, текущий статус всего процесса). Context|Рабочая память.|Временный объект внутри Execution, где лежат результаты выполненных нод для подстановки в следующие.
Infrastructure layer
| Компонент | Технология | Роль и описание |
|---|---|---|
| Database | PostgreSQL | Хранение структурированных данных (Actions, Capabilities, Pipelines). |
| LLM Inference | vLLM / TGI (Text Generation Inference) | Хостинг твоей модели (Llama-3, Mistral и др.). Предоставляет высокопроизводительный OpenAI-совместимый API. |
| ORM | SQLAlchemy + Alembic | Асинхронный маппинг доменных моделей на таблицы БД и управление миграциями без даунтайма. |
| Async Runtime | FastAPI + Uvicorn | Ядро монолита. Обработка запросов, управление жизненным циклом приложения и фоновыми задачами. |
| HTTP Client | HTTPX (Async) | фНеблокирующие вызовы к твоей локальной модели и внешним API сервисов (Slack, CRM и т.д.) внутри ExecutionCore. |
| Cache / State | Redis | Хранение промежуточного состояния (Context) активных пайплайнов и кэширование результатов инференса. |
| Containerization | Docker & Compose | Изоляция сервисов (App, DB, Model Server) и их оркестрация одной командой. |
Service layer
Каждый сервис отвечает за свой этап жизненного цикла — от загрузки API до получения результата.
-
IngestionServiceЗадача: Обработка Swagger/OpenAPI.
Инструменты: Библиотеки prance или openapi-spec-validator.
Логика: Принимает файл через UploadFile, парсит его и сохраняет в БД (PostgreSQL) список Actions.
Метод:
async def ingest_openapi(file: UploadFile) -> List[ActionDomain]: ... -
CapabilityServiceЗадача: Группировка и описание навыков.
Инструменты: LangChain или просто прямой вызов OpenAI SDK.
Логика: Получает ID Actions, делает запрос к LLM для генерации описания Capability, сохраняет результат.
Метод:
async def create_capability(action_ids: list[UUID], name: str) -> CapabilityDomain: ... -
SynthesisServiceЗадача: Сборка Pipeline через LLM.
Логика:
- Получает промпт от PM.
- Выбирает подходящие Capability из библиотеки в БД.
- Формирует промпт для QWEN-2.5, чтобы она вернула JSON-структуру графа.
Метод:
async def synthesize_pipeline(user_query: str) -> PipelineDomain: ... -
ExecutionServiceЗадача: Асинхронное выполнение графа.
Инструменты: httpx (асинхронный клиент для запросов).
Логика:
- Инициализирует Context (Pydantic модель).
- Проходит циклом по нодам Pipeline.
- Заменяет переменные (Variable Injection).
- Делает await client.request(...).
Метод:
async def run_execution(pipeline_id: UUID) -> ExecutionResult: ...
Api layer
Actions
Загрузка и валидация Swagger/OpenAPI.
| Метод | Путь | Описание |
|---|---|---|
| POST | /api/v1/actions/ingest |
Загрузка файла OpenAPI (Multipart form-data). |
| GET | /api/v1/actions |
Получение списка всех импортированных методов с фильтрацией. |
| GET | /api/v1/actions/{id} |
Детальная схема конкретного экшена. |
| DELETE | /api/v1/actions/{id} |
Удаление экшена (если API обновилось или метод больше не нужен). |
Пример запроса ingest
curl -X POST "http://localhost:8000/api/v1/actions/ingest" \
-H "Accept: application/json" \
-F "file=@/app/examples/travel.yaml;type=application/yaml"
Пример ответа ingest
{
"created_actions_count": 5,
"created_capabilities_count": 5,
"capabilities": [
{
"id": "7c1d5c9b-2c9d-4f1c-9d2e-4a8f3e1b7a11",
"action_id": "e4b0bcb6-6a8c-4b0a-8e18-3f44b5f7d1c2",
"name": "get_recent_users",
"description": "Get recent users for travel campaigns",
"input_schema": null,
"output_schema": {
"type": "object"
},
"data_format": {
"parameter_locations": ["query"],
"request_content_types": [],
"request_schema_type": null,
"response_content_types": ["application/json"],
"response_schema_types": ["object"]
},
"created_at": "2026-03-14T12:00:00Z",
"updated_at": "2026-03-14T12:00:00Z"
}
]
}
Capabilities
Превращаем API в способности
| Метод | Путь | Описание |
|---|---|---|
| POST | /api/v1/capabilities/suggest |
AI-powered: Система анализирует Actions и предлагает логические связки. |
| POST | /api/v1/capabilities |
Создание навыка: связка 1+ Action ID, маппинг данных и описание. |
| GET | /api/v1/capabilities |
Список всех навыков для отображения в библиотеке на фронте. |
| GET | /api/v1/capabilities/{id} |
Посмотреть, из каких Actions состоит навык и как внутри ходят данные. |
| DELETE | /api/v1/capabilities/{id} |
Удаление навыка. |
Pipelines
Основная точка входа для чата и канваса (React Flow).
| Метод | Путь | Описание |
|---|---|---|
| POST | /api/v1/pipelines/generate |
AI-powered: Промпт из чата -> AI подбирает Capabilities -> Возвращает JSON-граф. |
| GET | /api/v1/pipelines |
Список всех сохраненных или сгенерированных сценариев. |
| GET | /api/v1/pipelines/{id} |
Загрузка конкретного графа на канвас. |
| PUT | /api/v1/pipelines/{id} |
Сохранение ручных правок: если PM подвигал ноды или изменил параметры. |
| DELETE | /api/v1/pipelines/{id} |
Удаление сценария. |
Вызов чата: POST /api/v1/pipelines/generate
Используйте один и тот же dialog_id для одной цепочки сообщений, чтобы сохранялся контекст.
curl -X POST "http://localhost:8000/api/v1/pipelines/generate" \
-H "Content-Type: application/json" \
-d '{
"dialog_id": "11111111-1111-1111-1111-111111111111",
"message": "Нужно взять 30 последних пользователей, распределить по 5 отелям и отправить email-офферы",
"user_id": null,
"capability_ids": null
}'
Пример ответа чата
{
"status": "ready",
"message_ru": "Пайплайн собран. Можно запускать.",
"chat_reply_ru": "Пайплайн собран. Можно запускать. План шагов: get_users_recent -> get_hotels_top -> post_segments_hotel.",
"pipeline_id": "7b17ac70-3f39-4e70-8f8a-4a2f1fd4ff7e",
"nodes": [
{
"step": 1,
"name": "get_users_recent",
"description": "Отбирает недавних пользователей для travel campaign.",
"input_connected_from": [],
"output_connected_to": [3],
"input_data_type_from_previous": [],
"external_inputs": [],
"endpoints": [
{
"name": "get_users_recent",
"capability_id": "c4be1e66-2e04-4c6f-8d8f-6f39f1f46087",
"action_id": "e4b0bcb6-6a8c-4b0a-8e18-3f44b5f7d1c2",
"output_type": "users[]"
}
]
},
{
"step": 2,
"name": "get_hotels_top",
"description": "Получает список топовых отелей для офферов.",
"input_connected_from": [],
"output_connected_to": [3],
"input_data_type_from_previous": [],
"external_inputs": [],
"endpoints": [
{
"name": "get_hotels_top",
"capability_id": "470ae37e-029e-4c67-a293-acb848675d0b",
"action_id": "96f0bc8f-e294-46a9-9a0e-48e1b8f2a941",
"output_type": "hotels[]"
}
]
}
],
"edges": [
{
"from_step": 1,
"to_step": 3,
"type": "users"
},
{
"from_step": 2,
"to_step": 3,
"type": "hotels"
}
],
"missing_requirements": [],
"context_summary": "Пользователь хочет собрать travel-рассылку из доступных capability."
}
status может быть:
ready— граф построен,pipeline_id/nodes/edgesзаполнены.needs_input— нужно уточнение или добавить Swagger/OpenAPI.cannot_build— с текущими данными сценарий не собирается.
Сброс диалога: POST /api/v1/pipelines/dialog/reset
curl -X POST "http://localhost:8000/api/v1/pipelines/dialog/reset" \
-H "Content-Type: application/json" \
-d '{
"dialog_id": "11111111-1111-1111-1111-111111111111"
}'
Execution
Запуск пайплайна.
| Метод | Путь | Описание |
|---|---|---|
| POST | /api/v1/pipelines/{id}/run |
Запуск пайплайна. Создает объект Execution и запускает цикл выполнения. |
| GET | /api/v1/executions |
История всех запусков (кто, когда и с каким результатом запускал). |
| GET | /api/v1/executions/{run_id} |
Статус в реальном времени: Поллинг для фронта (какая нода сейчас горит зеленым). |
| POST | /api/v1/executions/{run_id}/approve |
Подтверждение «опасного» шага (если нода требует Approval). |
CapabilityService
Задача: Инкапсулировать один или несколько технических Actions (API-эндпоинтов) в единый, понятный для LLM и пользователя бизнес-навык (Capability).
Этап 1: Формирование связки (Action Binding)
На вход сервис получает массив ID Actions и их порядковые номера. Сервис валидирует, что эти эндпоинты существуют в БД.
Внутренний маппинг: Сервис принимает правила передачи данных между экшенами. Например, ответ от GET /users (поле user.id) должен лечь в тело запроса POST /emails (в поле recipient_id).
Абстракция входа/выхода: Сервис вычисляет публичную схему навыка (Input/Output Schema). Он берет все обязательные параметры всех внутренних экшенов, вычитает из них те, которые закрыты внутренним маппингом, и формирует итоговый JSON Schema. Для внешнего мира этот навык теперь выглядит как одна функция.
Этап 2: Семантическое обогащение (LLM Summarization)
Чтобы ИИ в будущем понимал, зачем нужен этот инструмент, сервис обращается к локальной LLM (vLLM/TGI).
Промпт к LLM: Сервис отправляет системный промпт, содержащий URL-адреса, методы и JSON-схемы объединенных экшенов.
Задача LLM: Сгенерировать:
- Короткое название (например, create_refund_ticket).
- Подробное описание (например, «Используется для создания заявки на возврат средств в Zendesk и проверки статуса транзакции в Stripe»).
Этап 3: Сохранение навыка в библиотеку
Текстовое описание и название навыка сохраняются в PostgreSQL вместе с input_schema и output_schema.
Этот шаг делает Capability доступной для последующей сборки пайплайнов без дополнительного индексационного слоя.
SynthesisService
Задача: Принять текстовый запрос пользователя, найти подходящие инструменты (Capabilities) и собрать из них валидный направленный ациклический граф (DAG), готовый к исполнению.
Этот сервис вызывается каждый раз, когда пользователь пишет промпт в чат. Время его работы напрямую влияет на UX, поэтому он должен быть оптимизирован под максимальную скорость.
Этап 1: Отбор доступных навыков (Capability Selection)
Запрос пользователя (например, "Найди последние 5 оплаченных заказов и отправь их в канал #sales") сопоставляется с доступной библиотекой Capability.
Сервис собирает релевантный список навыков из PostgreSQL и готовит их для контекста LLM.
Результат: Вместо сотен API-методов, в prompt передаются только подходящие "строительные блоки" (например, search_orders и send_slack_message).
Этап 2: Сборка контекста для LLM (Prompt Engineering)
Сервис формирует динамический промпт для генерации графа. В него вшиваются:
Инструкция (System Message): Жесткие правила работы («Ты парсер. Возвращай только JSON. Не выдумывай ID навыков»).
Библиотека инструментов: JSON-схемы найденных на предыдущем шаге Capabilities (их id, name, description и input_schema).
Промпт пользователя: Оригинальный текст запроса.
Этап 3: Генерация графа (LLM Inference)
Локальная LLM обрабатывает контекст и возвращает структуру пайплайна в строгом формате.
LLM определяет узлы (Nodes) — какие навыки использовать.
LLM определяет ребра (Edges) — в каком порядке их вызывать.
LLM прописывает переменные (Variable Injection) — как данные перетекают между узлами, используя синтаксис шаблонизатора (например, {{node_1.output.orders_list}}).
Этап 4: Строгая валидация (Sanitization & DAG Check)
LLM склонны к галлюцинациям, поэтому перед сохранением в базу SynthesisService проводит жесткую проверку полученного JSON:
Schema Validation (Pydantic): Проверка, что структура ответа строго соответствует модели Pipeline.
Capability Existence: Проверка, что все capability_id в узлах реально существуют в базе (LLM не выдумала несуществующий навык).
DAG Validation (Топологическая сортировка): Граф проверяется на отсутствие циклов (A -> B -> C -> A), чтобы предотвратить бесконечное выполнение.
Parameter Validation: Проверка, что все обязательные поля из input_schema каждого навыка либо заполнены статичными значениями, либо имеют ссылку-шаблон на предыдущий узел.
Если граф проходит валидацию, он сохраняется в таблицу pipelines и отдается на фронтенд для визуализации на канвасе. Если валидация провалена — сервис просит LLM исправить ошибку (Retry Logic, максимум 2 попытки), передавая ей текст ошибки валидации.