# ML Core Swagger (Сдача) Актуальный сдаваемый Swagger для ML core backend лежит в файле: - `docs/ml_core_backend_openapi.yaml` Что входит в документ: - `POST /api/v1/pipelines/generate` - `GET /api/v1/pipelines/dialogs` - `GET /api/v1/pipelines/dialogs/{dialog_id}/history` - `POST /api/v1/pipelines/dialog/reset` - `POST /api/v1/pipelines/{pipeline_id}/run` - `GET /api/v1/executions` - `GET /api/v1/executions/{run_id}` Что не входит в этот документ: - auth, actions, capabilities ручки - demo `/ml/*` ручки из `demo-backend/openapi/*` Быстрая проверка локально: ```bash cd backend pytest -s --capture=no tests/test_ml_openapi_contract.py ``` Дополнительная валидация OpenAPI (если установлен валидатор): ```bash 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 до получения результата. 1. `IngestionService` Задача: **Обработка Swagger/OpenAPI.** Инструменты: Библиотеки prance или openapi-spec-validator. Логика: Принимает файл через UploadFile, парсит его и сохраняет в БД (PostgreSQL) список Actions. Метод: `async def ingest_openapi(file: UploadFile) -> List[ActionDomain]: ...` 2. `CapabilityService` Задача: **Группировка и описание навыков.** Инструменты: LangChain или просто прямой вызов OpenAI SDK. Логика: Получает ID Actions, делает запрос к LLM для генерации описания Capability, сохраняет результат. Метод: `async def create_capability(action_ids: list[UUID], name: str) -> CapabilityDomain: ...` 3. `SynthesisService` Задача: **Сборка Pipeline через LLM.** Логика: - Получает промпт от PM. - Выбирает подходящие Capability из библиотеки в БД. - Формирует промпт для QWEN-2.5, чтобы она вернула JSON-структуру графа. Метод: `async def synthesize_pipeline(user_query: str) -> PipelineDomain: ...` 4. `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 ```bash curl -X POST "http://localhost:8000/api/v1/actions/ingest" \ -H "Accept: application/json" \ -F "file=@/app/examples/travel.yaml;type=application/yaml" ``` #### Пример ответа ingest ```json { "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` для одной цепочки сообщений, чтобы сохранялся контекст. ```bash 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 }' ``` #### Пример ответа чата ```json { "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` ```bash 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 попытки), передавая ей текст ошибки валидации.