Тема
ИИ-агенты
Эндпоинты агентов (/api/agents, /api/ai, /api/repo, /api/regression, /api/sources и связанные с ними) входят в модуль agents (тариф Pro и выше); отдельные части требуют модулей eval и healing (Enterprise) — см. Модули и тарифы. Запросы, кроме GET, под /api/agents подпадают под лимит ИИ-запросов — см. Ограничения частоты запросов.
Обзор
GET /api/agents/overview — статус агентов, метрики и матрица трассируемости, построенная движком покрытия.
Задачи агентов
POST /api/agents/jobs { type, params } — запустить задачу агента (agent0_coverage, agent1_generate, agent2_codegen, agent2_verify, agent3_triage, agent4_execute, custom_agent); ответ — 202 с jobId.
- Тот же запрос, пока его задача ещё выполняется, отвечает
200с этой задачей (reused: true). - В проекте одновременно может выполняться 3 задачи: четвёртая получает
429сRetry-After. - Задача, которая всё ещё выполняется через 5 минут, завершается с ошибкой, а её вызовы модели отменяются; у
agent2_verifyесть 40 минут, уagent4_execute— час.
Остальные эндпоинты задач:
GET /api/agents/jobs— недавние задачи проекта.GET /jobs/:jobId— одна задача.GET /jobs/:jobId/stream— следить за задачей через SSE.POST /api/agents/jobs/:jobId/cancel(agents:write) — остановить ещё выполняющуюся задачу: её вызовы модели прекращаются, задача завершается как отменённая, и освобождается одно из трёх мест проекта.GET /api/agents/jobs/:jobId/llm-calls(agents:write) — ответ{ calls, keptDays }: все вызовы модели в задаче по порядку, с системным промптом, промптом, ответом или ошибкой, токенами и длительностью (отправленные изображения учитываются, но не хранятся). Ход беседы с инструментами (обход стенда Агентом 2) хранится как то, что появилось с последней реплики модели — ответы инструментов, — плюс текст модели и вызовы инструментов. Вызовы хранятсяKVALLI_LLM_CALL_LOG_DAYSдней (7;0— не хранить) в таблицеllm_callsи только для вызовов, сделанных задачей.
Завершённая задача несёт usage — токены, потраченные её вызовами модели (они также учитываются по проекту в agent_usage; cacheReadTokens и cacheWriteTokens говорят, сколько токенов промпта обслужил кеш промптов провайдера или сколько в него записано). Ответ модели, обрезанный по лимиту токенов, помечается как обрезанный, а не используется. Промпт длиннее контекстного окна модели завершает задачу с 413 и тем, что сказал провайдер, — или, для Ollama, которая оставляет то, что помещается в окно, и молча отбрасывает остальное, — с указанием, сколько символов было прочитано как сколько токенов; шаблон вместо ответа не подставляется.
Все агенты запускаются этим способом: синхронные формы POST /api/agents/run-agent0-coverage, run-agent1-generate, run-agent2-codegen и run-agent3-triage удалены. Остался только POST /api/agents/run-custom (agents:write) { slug, runtimeInputs? } (или agent — определение агента прямо в запросе).
Агент 4: выполнение ручного прогона
agent4_execute { runId, itemIds?, only? } выполняет ручной прогон на тестовом стенде проекта вместо тестировщика (Pro, модуль browser и браузерный воркер). Нужно также право runs:write (403 без него).
- Каждый результат, который ещё
untested(only: "all"выполняет заново и результаты с вердиктом;itemIdsназывает конкретные результаты), проходится в отдельной браузерной сессии: модель выполняет шаги в том виде, в каком их скопировал прогон, с параметрами строки, а воркер проверяет каждый ожидаемый результат. - Файлы кейса (до 10 файлов, 10 МБ) передаются в браузер для шага, который загружает файл; модель может менять размер окна и спрашивать, как выглядит страница (ответ даётся словами по скриншоту).
- Шаг, отмеченный как упавший, ещё раз сверяется со скриншотом, сделанным в момент отметки; падение, которое скриншот не подтверждает, проходится заново, а не записывается.
- На стенде, настройки которого это разрешают (
prepareData, по умолчанию выключено), предусловия кейса сначала выполняются через страницы самого стенда: по рецепту, сохранённому с прошлой подготовки и проигрываемому на стенде как код, или обходом, который может следовать кейсам проекта с теми же словами в предусловии. Всё созданное называется по прогону ([A4 RUN-12] …), а комментарий результата говорит, что было подготовлено; кейс, предусловия которого не удалось выполнить, получаетblockedи не проходится. - Статус шага и, если отличалось, наблюдаемое записываются по мере отметки шагов. Скриншот упавшего шага (или последнего шага пройденного результата) сохраняется у результата как
agent4-step-<n>[-failed].png. Вердикт (passed;failed, если шаг отличался;blocked, если шаг нельзя было выполнить здесь или у кейса нет шагов) записывается от имениAgent 4с итогом обхода в комментарии. - Прогон не закрывается. Одна задача выполняет не больше 50 результатов; отмена или дедлайн срабатывают между результатами, и уже записанное остаётся.
Результат задачи: { runId, executed, passed, failed, blocked, left, items: [{ itemId, name, caseRef?, status, steps: [{ index, outcome, note? }], rounds?, reason? }], stopped? }. Закрытый прогон или прогон, импортированный из CI, отклоняется.
Агент 2: проверка в CI
agent2_verify { artifactId, sourceId?, workflowFile? } запускает автотест Агента 2 в собственном CI проекта, на ветке его открытого merge request. Нужно также право pipelines:trigger (403 без него).
- Merge request должен быть в репозитории Агента 2; на GitHub воркфлоу (параметр или первый файл из
ciWorkflowFilesисточника) должен быть в этой ветке, иначе задача предложит влить в неё ветку по умолчанию. - Пайплайн записывается в
GET /api/pipelinesсtrigger: "agent2_verify". - Падение читается из загруженного результата самого теста, если CI отправляет результаты в Kvalli, иначе — из конца логов упавших джобов (не больше трёх, джобы, которым разрешено падать, не учитываются).
- Модель, читая это вместе с шагами и ожидаемым результатом тест-кейса, решает, что упало:
test_code— тест неверен или делает не то, что говорит кейс; он исправляется, коммитится в ту же ветку какtest(codegen): fix <case> after CI run <PIP> [Kvalli Agent 2]после обычной проверки кода и запускается снова — не больше двух раз;product— тест верен; он не меняется;environment— пайплайн не дошёл до теста;unrelated— упали другие тесты.
Артефакт сохраняет итог как ciVerification: { status: "running" | "passed" | "failed" | "inconclusive", verdict?, reason?, attempts: [{ pipelineId, url, status, failureSource?, verdict?, reason?, fixCommitted? }], checkedAt } и ciStatus. passed — только зелёный пайплайн или собственный пройденный результат теста; environment и unrelated дают inconclusive.
Агент 1: генерация кейсов
agent1_generate принимает sourceRef (jira:KVQA-2, youtrack:SHOP-12 или confluence:98307) и sourceVersion (когда задача последний раз менялась, номер версии страницы), если текст получен из этого источника. Каждый черновик кейса несёт их, и утверждённый кейс сохраняет их для отслеживания изменившихся требований. Каждый черновик также говорит, кто его написал, — draftOrigin: { provider, model } (provider: "parser" для кейса, выведенного парсером спецификаций), а кейс парсера, проверяющий то же, что уже проверяет один из кейсов модели, не включается.
Агент 3: разбор падений
agent3_triage с runId и itemId упавшего результата сообщает модели, как тот же тест проходил в пяти импортированных прогонах до этого на той же ветке («прошёл в 4 из последних 5») или что истории у него нет.
- Он также читает, что оставило падение: страницу, которую Playwright сохранил в месте падения (самое новое вложение результата
error-context.md, 8000 символов), вывод теста (output— конец stdout/stderr или system-out/system-err упавшего результата в том виде, в каком их нёс отчёт), и конец логов упавших джобов CI, если к прогону привязан пайплайн, известный Kvalli. - Для ручного результата падением считается его комментарий.
- Скриншоты, видео и трассы не читаются: подключения к моделям передают только текст.
- Модели также сообщается, где ещё встречается то же падение: другие упавшие результаты прогона, сообщение которых совпадает, если отбросить время, id, числа (HTTP-статусы сохраняются) и пути URL, а также импортированные прогоны проекта за предыдущие 14 дней. Тогда разбор несёт
sharedFailure{ signature, inRun, earlierRuns, earlierTests }; если падение есть только у этого теста, поля нет. Экран прогона группирует упавшие результаты по той же сигнатуре.
Полезность агентов
GET /api/agents/usefulness?days=90 (1–365) — что стало с черновиками агентов: byModel (key: "provider / model") и bySkill, каждая строка с proposed, accepted, edited, discarded и процентами.
- Агент 1: кейс утверждён в виде черновика, утверждён после правки на ревью или отброшен из очереди (
acceptedPct,editedPct,discardedPctот решённых). - Агент 2: автотест, сгенерированный моделью, —
proposed, иaccepted, когда открыт его первый merge request (acceptedPctот предложенных).
Записывается по каждому черновику в agent_outcomes; кейсы парсера спецификаций считаются как parser / spec-parser. Строка bySkill несёт revisions (те же счётчики по каждой ревизии, v<version>-<hash> текста навыка, сначала старые), если черновики писались по двум или более ревизиям навыка, — так правку навыка можно оценить по тому, что она изменила. corrections (Агент 1) считает editedFields — поля, которые ревьюеры изменили перед принятием, по сравнению с кейсом в том виде, в каком его написал агент, — и discardReasons с discardedWithoutReason; каждое «до» и «после» правки и каждая причина хранятся в agent_outcomes.details.
Навыки
GET /api/agents/skills— встроенные навыки, поставляемые с Kvalli (только для чтения,isBuiltIn), и собственные навыки проекта, хранящиеся в базе данных. Навык проекта с id встроенного навыка заменяет его (overridesBuiltIn).POST /api/agents/skillsсохраняет навык проекта: сidправит этот навык, безidсоздаёт новый и отвечает409, если ключ (filenameили slug отnameна любом алфавите) занят.DELETE /api/agents/skills/:filenameудаляет навык (409для встроенного).POST /api/agents/skills/bootstrap{ appliesTo? }предлагает до трёх навыков на основе кейсов проекта и ничего не сохраняет: отвечаетdrafts, которые нужно просмотреть и сохранить; без модели —502.POST /api/agents/skills/eval-test,POST /api/agents/eval/run(agents:write),GET /api/agents/eval/report— оценка на заложенных дефектах;/api/agents/evalтребует модульeval(Enterprise).
Очередь ревью Агента 1
GET /api/agents/agent1/drafts— очередь черновиков.PUT /api/agents/agent1/drafts/:id— изменить черновик (сversion).POST /api/agents/agent1/drafts/approveиPOST /api/agents/agent1/drafts/discard(cases:write) — решить по нескольким сразу: принимают id и отвечают, что принято и что отклонено. Отбрасывание может указать причину,reason:duplicate,not_in_requirement,wrong,too_vagueилиother(любое другое значение —400).DELETE /api/agents/agent1/drafts/:id— удалить черновик.DELETE /api/agents/agent1/drafts— очистить очередь.
Первая правка черновика на ревью сохраняет кейс в том виде, в каком его написал агент, — с ним сравнивается утверждение. Каждое решение записывается к черновику в agent_outcomes, откуда берутся цифры полезности выше; очистка очереди выбрасывает это измерение, а не считает черновики отброшенными.
Свои агенты
Свои агенты хранятся в каждом проекте:
GET|POST /api/agents/custom,GET|DELETE /api/agents/custom/:slug;POST /api/agents/custom/import(Markdown);GET /api/agents/custom/:slug/export;POST /api/agents/custom/:slug/verify— пробный запуск, который агент должен пройти.
Источники требований для Агента 1
POST /api/sources/requirements-bundle{ openapi?, issues?, confluencePages? }(не больше 20 задач и 20 страниц).POST /api/sources/swagger/fetch({ url }требуетcases:write; доверенные хосты применяются только к источнику, зарегистрированному в проекте).POST /api/sources/confluence/fetch,POST /api/sources/task-manager/fetch.POST /api/sources/task-manager/issues(cases:read) — задачи трекера, чтобы выбрать одну из списка, а не по ключу.GET|POST /api/sources— реестр источников проекта (запись требуетcases:write).PUT /api/sources/:id— один источник, создаётся (201) или заменяется (200); название, которое уже есть у другого источника, — 409.DELETE /api/sources/:id— удалить источник. Источники-репозитории по-прежнему добавляет, меняет и удаляет только администратор.GET /api/contracts— спецификации из реестра.
Каждый маршрут этой группы доступен по двум путям. Маршруты получения и сборки работают и без префикса /sources (POST /api/swagger/fetch, /api/confluence/fetch, /api/task-manager/fetch, /api/task-manager/issues, /api/requirements-bundle), а реестр и контракты — наоборот: GET|POST /api/sources/list — тот же обработчик, что GET|POST /api/sources, а GET /api/sources/contracts — то же, что GET /api/contracts. Это псевдонимы, а не отдельные эндпоинты: используйте любой и ожидайте любой в старых клиентах.
Результаты агентов
GET /api/agents/requirements,GET /api/agents/traceability— граф требований и матрица, построенные последним прогоном покрытия.GET /api/agents/last-result/:agent(cases:read) — последний результат названного агента в этом проекте в сохранённом виде, чтобы панель могла показать предыдущий ответ, не запуская задачу.
Код автотестов и репозиторий
Артефакты кода
GET /api/agents/code-artifacts — сгенерированные артефакты кода этого проекта. Агент 2 проверяет код, прежде чем сохранить его:
- TypeScript и JavaScript — компилятором TypeScript по спеке и её page object вместе (синтаксис, неопределённые имена, собственные типы файлов; типы тестового фреймворка не установлены, поэтому его импорты считаются нетипизированными);
- Python — парсером интерпретатора, если он установлен.
Код, который не компилируется, возвращается модели с сообщениями компилятора, не больше двух раз. Артефакт несёт результат как codeCheck: { status: "passed" | "failed" | "unchecked", checker, issues: [{ file, line, column, message }], note, fixAttempts }; Java и Go — unchecked, никогда не passed. Код, который всё равно не проходит проверку, сохраняется как артефакт, но не записывается в кейс.
Merge request
POST /api/repo/merge-request проверяет файлы, которые собирается отправить, включая правки, тем же способом: код, который не компилируется, получает 409 с codeCheck, если не передано force: true. Ответ в любом случае несёт codeCheck.
- Отправка идёт только в ветку под
autotest/, не больше 20 файлов по 512 КБ, и только тестовые файлы: пути под тестовыми globs репозитория или там, куда пишет Агент 2 (tests/**,e2e/**,**/*.spec.*, …). - Скрытые пути (
.github/,.gitlab-ci.yml), конфигурация сборки и запуска (package.json,playwright.config.ts,conftest.py, Dockerfile, shell-скрипты …) иscripts/,ci/,bin/верхнего уровня отклоняются с400.
Вылеченный тест (purpose: "healing", один файл) получает один merge request на файл. Его ветка — autotest/heal-<file path as a slug>-<8 hex of the path's hash> (или переданный branchName, под autotest/). Пока в том же репозитории открыт запрос из этой ветки (или из неё с суффиксом, который получает занятое имя) в ту же целевую ветку, у провайдера запрашивается этот запрос, и вылеченные строки идут туда, а не в новый: они вносятся в копию файла в этой ветке, так что отправленное ранее исправление остаётся, а ответ содержит updated: true, и ещё unchanged: true, если в ветке они уже были (тогда ничего не коммитится). Иначе открывается новый запрос (updated: false). В запросы из форков ничего не пишется. Поиск одинаков для любого провайдера (merge requests GitLab, pull requests GitHub); без токена ничего не ищется, а запрос симулируется.
Репозиторий
Остальные эндпоинты /api/repo:
GET /api/repo/status(settings:read) — настроен ли репозиторий и какая его часть проиндексирована.POST /api/repo/sync(agents:write) — см. ниже.- Три эндпоинта, которые читают или используют сохранённые учётные данные и поэтому требуют
settings:writeи администратора установки:POST /api/repo/test,GET /api/repo/projects(репозитории, которые видит токен, — для выбора в настройках) иGET /api/repo/branches(?project=— ветки одного из них).
Весь префикс ограничен 60 запросами в минуту и требует модуля agents.
POST /api/repo/sync { force? } индексирует головной коммит ветки: маршруты, которые объявляет каждый исходный файл, и тесты в каждом тестовом файле, — это то, что читают Агент 0 и свои агенты с входом repo. Коммит скачивается один раз как архив (по файлам через API, если провайдер не отдаёт архивы), а файл, git blob id которого не изменился с прошлой индексации, повторно не анализируется; коммит, уже проиндексированный с теми же globs, отвечает unchanged: true и ничего не скачивает. Статус говорит, сколько файлов, подходящих под globs, проиндексировано (filesScanned из filesMatched), с truncated и notes, если лимит (50 000 файлов, 1 МБ на файл) что-то отбросил. Агенты обновляют индекс до головы ветки, прежде чем его читать.
Подбор кейсов для регрессии
POST /api/regression/suggest{ planKey?, folder?, focus?, issueKey?, limit? (30), windowDays? (30) } — какие кейсы выполнить сейчас и почему каждый. Оценка строится по истории проекта в окне и никогда — моделью. Причины:
- упал или заблокирован в последнем прогоне (и заведён дефект);
- часто падает;
- переключается между прохождением и падением на одном окружении;
- нет вердикта в окне или нет его 14 дней;
- изменён после последнего прогона;
- покрывает требование, кейсы которого падают, или единственный покрывает требование;
- слова изменения (
focusили название и описание задачи трекераissueKey) найдены в его названии, папке, тегах, эндпоинте или шагах.
Критический и высокий приоритет добавляют очки, но сами по себе не причина; заглушённый кейс теряет очки; устаревшие кейсы не учитываются. Ответ: picks (сначала с наибольшей оценкой, каждый с reasons: [{ kind, points, text }]), nextBest ниже отсечки, leftOut, considered, quiet (нет причины запускать), счётчики byReason и basis — использованные результаты и прогоны. Одна и та же история всегда даёт один и тот же ответ.
Эндпоинты /api/ai
GET /api/ai/providers(cases:read) — провайдеры и модели, которые может использовать установка, для выбора в интерфейсе. Ключей в ответе нет.POST /api/ai/smart-search(cases:read) — старый адресPOST /api/testcases/best-match, отвечает так же; модель не используется. Работает только там, где лицензирован модуль агентов: на Starter используйте новый адрес.POST /api/ai/generate-cases(cases:write,models:use) — сгенерировать кейсы и вернуть их в том же запросе, а не запускать задачу. Для небольшой немедленной генерации; всё более долгое — черезPOST /api/agents/jobs, за которым можно следить и который можно отменить.POST /api/ai/generate-testcases— тот же маршрут под старым названием.POST /api/ai/analyze-failure,POST /api/ai/generate-report(runs:read,models:use) — разбор падения и сводка по прогону. Отчёт принимает{ runId }и сам читает прогон; модели передаются его счётчики и первые 50 падений с сообщениями, но не загруженный отчёт, стеки и пройденные тесты (целый{ run }от старого клиента сводится так же). Текст падения обрезается до 20 000 символов — его начало и конец. Без настроенной модели падение читается по трассе, а отчёт — это собственные цифры прогона; ни то ни другое не выдумывает классификацию, уверенность или рекомендацию по релизу.
Эндпоинты /api/ai/test-llm и /api/ai/ollama-models описаны в разделе Конфигурация и система.
Починка локаторов
POST /api/healing/heal (healing:write; модуль healing, Enterprise) восстанавливает сломанный локатор по снимку DOM и возвращает исправленный код с оценёнными кандидатами. POST /api/healing/apply (те же требования) возвращает этот код: в репозиторий здесь ничего не пишется. Предложить его как merge request — POST /api/repo/merge-request с purpose: "healing" (один запрос на файл, переиспользуется, пока открыт; см. Merge request).