9.7 KiB
eva-agent-mvp — решения агента
Спека и дизайн — в jira-hub/openspec/changes/wiki-module/ (issue aep/jira-hub#91, PR aep/jira-hub#92):
- решения 10, 11, 14 и Context (форматы ссылок Eva);
- требования
wiki«Синхронизация вики» и «Вложения вики по запросу»; modules«Агенты модулей»;hub-api«Маршруты агента» (общие/agent/*).
Issue агента — aep/eva-agent#1. Версия агента — 1. Агент работает за модуль wiki: имя агента в хабе wiki, токен —
jira-hub tokens → «агент wiki».
Владелец 24.10.2026 разрешил делать агента ночью без обсуждения («завтра хочу уже иметь рабочий mvp»). Поэтому все решения ниже приняты агентом и перечислены в разделе «Decisions made without discussion».
Context
- Источник для копирования — jira-agent v14 (
../jira-agent/src/agent.py,run.py). Его части, не связанные с Jira, отлажены с v2 по v14:- HTTP-слой:
Http,HttpError,RedirectAway,TooBig,_SameOriginRedirect; - транспорт
FolderTransportи клиент хабаHubClient; - журнал
Journal, циклRunner, самообновлениеUpdater; acquire_lock, CLIrun|check|selftest|prune,launcher_version.
- HTTP-слой:
- Клиент Eva и обход дерева — по образцу
../eva_wiki_export.py:- JSON-RPC 2.2:
POST <base>/api/?m=<метод>,Authorization: Bearer, тело{jsonrpc: "2.2", method, callid, kwargs}; - повторы на 429/502/503/504 и сетевых сбоях;
CmfDocument.get/list, страницы списка по 200;- дерево — по
parent/tree_parentиз списка документов проекта (project.code), запасной путь — обход черезtree_parent.code.
- JSON-RPC 2.2:
- Хаб принимает:
- пачки
POST /agent/wiki/pages {command_id, pages}— до 50 страниц, тело до 16 МБ (MAX_BODYхаба); - форму страницы проверяет
modules/wiki/stage.py; - отчёт
wiki_sync done {root_title, codes, eva_calls}:codes[0]должен быть кодом корня; - отчёт
wiki_fetch done {file, size}: файл должен лежать в хранилище хаба с этим размером.
- пачки
Decisions made without discussion
Дата всех решений — 2026-10-24.
-
Скопировать общие части jira-agent v14, а не выделять библиотеку. Обоснование: решение владельца 23.10 — однофайловый агент на stdlib на виртуалке. Копия сохраняет отлаженное поведение. Цена ошибки: правки общих частей придётся повторять в двух агентах.
-
run.py— дословная копия запускателя jira-agent (LAUNCHER_VERSION = 2), вместе с переменной окруженияJIRA_AGENT_LAUNCHER. Обоснование: в протоколе самообновления важны именаagent.py,update.jsonиAGENT_VERSION; переменная — внутреннее дело пары запускатель–агент. Цена ошибки: переименование переменной в обоих файлах. -
Конфиг
agent.json:- обязательны
hub_url,hub_token,eva.base_url,eva.token; - необязательны
verify_ssl,ca_bundle,timeout(60),poll_seconds(5),auto_update(true).
Обоснование: формат jira-agent, где ключ
jiraзаменён наeva. Цена ошибки: правка шаблона иload_config. - обязательны
-
Пауза между вызовами Eva —
wiki_pacingизGET /agent/settings(по умолчанию 0.2 с).- 429/502/503/504 и сетевые сбои: до 4 попыток с ожиданием 1, 2, 4 с.
- При 429 учитывается
Retry-After(не больше 60 с). - Число 429 уходит в
rate_limitedотчёта, число вызовов Eva — вjira_calls: поле протокола хаба, имя общее для всех агентов.
Цена ошибки: Eva режет запросы; правится настройкой без выпуска агента.
-
wiki_sync— порядок работы:- Корень берётся через
CmfDocument.getпо коду изparams.root. Нет корня —failed«страница <код> не найдена или нет прав». - Дерево строится как в
collect_subtree. - Обход в глубину от корня с защитой от циклов (
seenпоid). Соседи — по(orderno is None, orderno, name). - По каждой странице отдельный
CmfDocument.get(fields=["code","name","text"]). - Страницы уходят пачками по мере обхода: 50 страниц или 4 МБ JSON, что наступит раньше.
- Отчёт
codes— коды в порядке обхода, корень первым.
- Поля страницы:
eva_id = str(id),parent_code— код родителя по обходу (у корняnull),orderno— целое илиnull,url = urljoin(base_url + "/", "project/Document/<code>").
Цена ошибки: дерево не совпадёт с Eva; лечится правкой обхода и новой версией через самообновление.
- Корень берётся через
-
Ссылки разбирает
html.parser.HTMLParserиз stdlib.- Берутся
img[src]иa[href]. - Адрес разрешается
urljoin(base_url + "/", ref). «Свой» — тот жеschemeиnetloc, что уeva.base_url. - Вложение — свой адрес с путём, начинающимся на
/files/. Изurlубираются query и fragment. Миниатюра<путь>.meta/thumbnail.jpgозначает файл<путь>. - Ссылка на страницу — свой адрес с
DOC-<цифры>в пути (код в верхнем регистре). static/…и прочие ссылки не передаются: значки убирает хаб, прочие остаются в тексте.- Имя вложения —
altилиtitle, затем текст ссылки, затем последний сегмент пути послеunquote. - Каждая ссылка передаётся отдельной записью
{ref, url, name}или{ref, code},ref— ровно значение атрибута. Повторы поurlсклеивает хаб.
Обоснование: форматы из выгрузки ТЗ АРМ КА (design хаба, Context). Цена ошибки: вложение продублируется или потеряется; проверяется на первой живой синхронизации, правится агентом.
- Берутся
-
Без
?version=1вurlвложения. Обоснование: миниатюра и файл дают один адрес, хаб склеивает вложение. Цена ошибки: если Eva безversionне отдаёт файл,wiki_fetchпадает; правка одной строки. -
wiki_fetch:- адрес не своего хоста —
failed«чужой хост», запроса нет; - файл скачивается с Bearer-токеном Eva потоком до 10 МБ + 1 байт;
Content-Lengthбольше 10 МБ или прочитано больше —failed«файл больше 10 МБ — откройте в Eva»;- иначе
POST /agent/filesиdone {file, size}; - HTTP 404 —
failed«файл не найден в Eva».
Цена ошибки: большие файлы только ссылкой.
- адрес не своего хоста —
-
Обе команды только читают Eva, поэтому восстановление после перезапуска выполняет их заново. Журнал нужен только для досылки отчёта;
wiki_syncне продолжается с середины. Хаб очищает стадию при повторной выдаче. Цена ошибки: лишняя полная перекачка после перезапуска. -
check:- хаб:
GET /agent/settingsсвоим токеном; - Eva:
CmfDocument.list(filter=[], fields=["code"], slice=[0, 1]); - вывод «хаб: ok», «eva: ok» или текст ошибки, код 0 или 3, как у jira-agent;
selftest— версия плюсcheck.
- хаб:
-
Тесты:
unittestна/usr/bin/python3(3.9);- фейки
tests/fake_hub.py(как у jira-agent, плюсPOST /agent/wiki/pages),tests/fake_eva.py(JSON-RPC наhttp.server),tests/fake_relay.py; tests/test_py39.py— копия.- Тесты общих частей переносятся из jira-agent и адаптируются: журнал, транспорт, самообновление, запускатель.