Skip to content

Контракт EACP ​

Протокол управления внешними агентами: версии, авторизация, все вызовы, формат описания агента, формат ошибок и правила идемпотентности.

Что это ​

EACP это EVA Agent Control Protocol, универсальный контракт между платформой и внешним агентом. Название задано в самом описании протокола.

ВерсияЧто покрывает
1.0Базовый протокол: описание, состояние, настройки, задания, события, команды, спорные случаи
1.1Расширение интерфейса: объявляемые агентом разделы

Описание протокола лежит в платформе в каталоге contracts/eacp/, хеши файлов зафиксированы отдельным файлом и проверяются тестом. То есть незамеченное изменение контракта невозможно.

Платформа принимает только версию 1

При подключении проверяется версия протокола из описания агента. Если она не начинается с 1., подключение отклоняется с ошибкой о неподдерживаемой версии.

Авторизация ​

Один механизм, других нет.

Authorization: Bearer <service token>

К каждому обращению платформа добавляет ещё два заголовка:

ЗаголовокЗначение
X-Request-IDУникальный идентификатор обращения
Acceptapplication/json

Токен задаётся при подключении агента, хранится в базе в зашифрованном виде и наружу не отдаётся.

Ошибки авторизации маскируются

Ответы агента 401 и 403 платформа наружу отдаёт как 502 с отдельными кодами о неудачной аутентификации и неудачной авторизации. Сделано, чтобы не путать отказ агента с отказом самой платформы.

Требования к адресу агента ​

ПравилоПодробности
СхемаТолько http или https
Данные для входа в адресеЗапрещены
Параметры запросаЗапрещены
ЯкорьЗапрещён
Косая черта на концеСрезается автоматически

Ожидаемый вид адреса оканчивается на /agent-api/v1.

Описание агента ​

Первое, что платформа запрашивает при подключении.

json
{
  "protocol_version": "1.0",
  "agent": {
    "type": "mail-processor",
    "instance_id": "mail-processor-01",
    "name": "Обработка почты",
    "version": "0.1.0"
  },
  "capabilities": {
    "settings": true,
    "settings_schema": true,
    "event_history": true,
    "event_stream": true,
    "jobs": true,
    "llm_via_eva": true,
    "commands": []
  },
  "links": {
    "status": "/agent-api/v1/status"
  }
}

Раздел возможностей определяет, какие вкладки появятся в карточке агента и будет ли платформа читать его поток событий.

Формат ошибок ​

Ошибки приходят по стандарту описания проблем.

json
{
  "type": "urn:eva:error:unauthorized",
  "title": "Unauthorized",
  "status": 401,
  "detail": "Invalid token",
  "code": "UNAUTHORIZED",
  "request_id": "0f3c..."
}

Базовый протокол ​

Состояние и описание ​

МетодПутьНазначение
GET/manifestОписание агента
GET/health/readyГотовность
GET/statusЖивое состояние, активные и очередные задачи

Настройки ​

МетодПутьНазначение
GET/settings/schemaСхема настроек, по ней платформа строит форму
GET/settingsТекущие значения
PATCH/settingsИзменение значений
GET/settings/historyИстория изменений, постранично

Задания и реестр операций ​

МетодПутьНазначение
GET/jobsСписок заданий, фильтр только по состоянию
GET/jobs/{jobId}Одно задание
GET/ledgerРеестр операций, постранично
GET/ledger/exportВыгрузка реестра файлом

Команды и операции ​

МетодПутьНазначение
POST/commandsПослать команду, обязателен ключ идемпотентности
GET/operations/{operationId}Состояние операции

Команда принимается кодом 202 и возвращает идентификатор операции, состояние и время принятия. Выполнение асинхронное.

События ​

МетодПутьНазначение
GET/eventsПостраничное чтение истории
GET/events/streamПоток событий, постоянное соединение

Оба принимают номер, после которого читать. Так платформа продолжает с той позиции, на которой остановилась.

Требование к событиям

Каждое событие обязано быть помечено агентом как прошедшее маскирование персональных данных. Событие без такой пометки платформа отклоняет и в базу не пишет.

Спорные случаи ​

МетодПутьНазначение
GET/attention-casesОчередь, по умолчанию открытые
GET/attention-cases/{caseId}Один случай
GET/attention-cases/{caseId}/source-fileИсходный файл случая
POST/attention-cases/{caseId}/correctionsИсправление полей
POST/attention-cases/{caseId}/select-...Выбор кандидата
POST/attention-cases/{caseId}/resumeПродолжить обработку
POST/attention-cases/{caseId}/resolveЗавершить вручную
POST/attention-cases/{caseId}/replacement-documentЗагрузить замену файла

Все изменяющие обращения обязаны содержать четыре вещи:

ПолеЗачем
expected_versionЗащита от одновременной правки: если версия не совпала, изменение отклоняется
actorКто выполняет действие
reasonОбязательная причина, попадает в аудит
Idempotency-KeyЗащита от повторного применения

Пример обращения:

POST https://agent.internal/agent-api/v1/attention-cases/case-1/select-program
Idempotency-Key: action-key-1

{
  "expected_version": 3,
  "actor": "eva:user/42",
  "reason": "Проверено",
  "candidate_id": "p-1"
}

Расширение интерфейса, версия 1.1 ​

Позволяет агенту объявить свои разделы, которые платформа нарисует сама.

МетодПутьОсобенности
GET/ui/manifestУсловный запрос по метке, поддерживается ответ «не изменилось», предел 512 КБ
GET/ui/views/{viewId}/dataДанные раздела, повторяющиеся параметры для нескольких значений фильтра
GET/ui/views/{viewId}/records/{recordId}Одна запись
POST/ui/views/{viewId}/actions/{actionId}Нажатие объявленной кнопки, нужен ключ идемпотентности
GET/ui/views/{viewId}/exportВыгрузка раздела файлом

При запросе описания разделов платформа сообщает поддерживаемую версию расширения отдельным заголовком.

Ключевое решение по безопасности

Агент объявляет идентификаторы разделов, а не адреса. Путь обращения платформа собирает сама из идентификатора. В коде это прокомментировано прямо: маршрут выводится из идентификатора раздела, поэтому агент никогда не передаёт платформе адрес, по которому ей идти.

Там же отмечено, что в этом слое сознательно нет ни одного упоминания конкретного раздела, столбца или фильтра какого-либо агента.

Выдача модели ​

МетодПутьНазначение
GET/llm-bindingТекущая привязка
PUT/llm-bindingУстановить привязку
DELETE/llm-bindingУдалить привязку

Платформа сообщает агенту адрес своего OpenAI-совместимого шлюза, служебный ключ и разрешённую модель.

Идемпотентность ​

Правило простое: всякое изменяющее обращение снабжается ключом идемпотентности. Это касается команд, действий над спорными случаями и нажатий объявленных кнопок.

Платформа хранит эти ключи в аудите, поэтому повторная отправка того же действия не приводит к двойному выполнению.

Таймауты и пределы ​

ОграничениеЗначениеГде задаётся
Обычное обращение15 секундEACP_TIMEOUT_MS
Выгрузка120 секундEACP_EXPORT_TIMEOUT_MS
Размер описания разделов512 КБЗадано в коде, не настраивается
Размер выгрузки32 МБMANAGED_AGENT_UI_EXPORT_MAX_BYTES
Страница списков100 записей по умолчаниюПараметр обращения

Документация платформы EVA