Измерение маршрутизатора инструментов Jev
AT-237 (EP-029, HS-063). Измерение вне продукта: ничто в
apps/не импортирует этот код, ничего не пишется в базу, и никакой выгоды продукта здесь не утверждается — это задача AT-239, которая сравнивает ходы с маршрутизатором и без него. Измеряемый маршрутизатор — тот, что специфицирует AT-235; решения, от которых он зависит, — из AT-236.Измерено 2026-09-29 против локального экземпляра dev (проект compose
brabo-dev), репозиторий наd48f99f8ec. Второй раунд — вариантыstate, классы эквивалентности, разделение настройка/проверка, top-2 и расстояние по причинам — ниже.
Что существует
Всё живёт в scripts/jev/ и запускается Node репозитория (type stripping,
без шага сборки, без новых зависимостей).
| файл | роль |
|---|---|
catalogo.exs | выгружает из самого кода engine каталог инструментов каждого агента (spec/0 каждого инструмента: имя и описание) и идентичность (Engine.Harness.Agents.identity/1) |
catalogo.json | выгрузка, версионируемая — перегенерируйте при изменении каталога (команда в заголовке .exs) |
passos.ts | чистый: event log → шаги и state каждого шага |
jev.ts | чистый: запрос к Decisions API и чтение ответа |
medicao.ts | чистый: согласие, интервалы Уилсона, кривая уверенности, задержка, стоимость, отчёт в Markdown |
replay.ts | CLI первого раунда: три SELECT, один запрос на шаг, JSONL вне checkout |
dados.ts | второй раунд: сырые данные (события, строки расхода, задачи, истории, карты модулей, исходы действий) и кеш снимка |
equivalencia.ts | второй раунд: классы эквивалентности E1–E3, зафиксированные до запуска, и классификатор терминальных команд |
kickoff.ts | второй раунд: первое сообщение цикла dev agents и gates, перенесённое из Elixir engine (spec сверяет фиксированный текст с файлами .ex) |
variantes.ts | второй раунд: варианты state и граница исполнения; каждое поле называет свой источник в engine |
divisao.ts | второй раунд: разделение настройка/проверка, по исполнению |
cliente.ts, medicao2.ts, analise.ts | второй раунд: вызов (сохраняет вероятности), таблицы top-k / каскада / по причинам и CLI jev:analise |
Шаг, а не вызов инструмента
Маршрутизатор опрашивается один раз на вызов чат-модели, перед
provider.chat (AT-235). Шаг может нести несколько вызовов инструментов —
десять emit_artifact в ответе Criativo, terminal + read_file в ответе
dev, — поэтому опрос Jev по разу на tool.call задавал бы вопросы, которых
продукт никогда не задаёт, с state, уже содержащим соседние вызовы того же
шага.
У event log нет маркера шага (AT-235, пробел 1), поэтому граница берётся из
token_usage: api записывает строку на вызов LLM до того, как engine
запишет tool.call этого ответа. Каждый tool.call той же сессии и того же
актора между двумя последовательными строками — один и тот же шаг. Отсюда
три метки:
- шаг с одним инструментом (один или несколько его вызовов): метка = этот инструмент;
- разнородный шаг (два разных инструмента): попадание = выбор Jev — один из них, — но ограничение меню одним инструментом отрезало бы остальное;
- шаг без
tool.callпосле своей строки: модель ответила текстом (или упала). Это единственная метка, которуюresponder_sem_ferramentaможет получить этим методом, — бриф не ожидал ни одной; граница шага даёт семь.
Моменты сравниваются как текст UTC фиксированной ширины с
микросекундами: строка расхода и первый tool.call шага иногда попадают в
одну и ту же миллисекунду.
Отправляемый state
Формат AT-235, вырезанный из того, что существовало до шага: agente;
pedido — для разговорных агентов последнее chat.message, написанное
пользователем; для dev agents и gates задач — заголовок задачи из
dev.working; contexto — идентичность агента плюс строка
agent_instructions проекта, когда она есть (два слоя системного промпта, а
не весь собранный промпт); passos_recentes — последние 6 вызовов этого
актора в этой сессии, с аргументами и результатом, обрезанными до 500
символов. Вопрос — choice, чьи criteria — это {name: description}
каталога плюс зарезервированный responder_sem_ferramenta.
Запрос идёт в POST https://openrouter.ai/api/alpha/decisions с моделью
typesafe/jev-1.13. Исправление к AT-235, измеренное: questions — объект,
индексированный по id вопроса, а не список — списочная форма получает
400 expected record, received array. Ответ —
answers.<id>.{choice, probabilities, confidence}, usage.{input_tokens, output_tokens, cost}, id (gen-dec-…) и датированный снимок в model
(typesafe/jev-1.13-20260917 в каждом ответе этого исполнения).
Выборка
| экземпляр | локальный dev (brabo-dev), проект test, 4 сессии с шагами (у пятой сессии, core-api, нет ни одного) |
| период | 2026-09-29 06:19:11 → 07:57:22 UTC |
| прочитано | 879 событий (502 tool.call), 307 строк token_usage агентов |
| шаги | 305 от агентов каталога; 302 опрошено (3 шага Psicólogo имеют каталог из одного инструмента и никогда не маршрутизировались бы — AT-235, условие c) |
| метки | 295 шагов с инструментом (36 разнородных), 7 без |
| подозрительные метки | у 157 из 295 шагов есть вызов, результат которого показывает сбой (ok: false, текст, начинающийся с falhou/erro, exit ≠ 0), или который так и не получил результата, — вызванный инструмент мог быть неправильным |
Было сделано два полных исполнения (сообщается второе; между ними в базе
появился шаг Anamnese). Между исполнениями, на 301 шаге, общем для обоих, Jev
дал один и тот же выбор в 283 (94%); средняя |Δ уверенности| 0,029,
максимальная 0,16. Выборка — небольшой проект, один день, одна чат-модель
(~deepseek/deepseek-flash-latest); каждая доля ниже несёт свой 95%
интервал Уилсона, и большинство интервалов по агентам слишком широки, чтобы
упорядочить агентов.
Согласие по агентам
«Согласие» = выбор Jev — инструмент, который модель вызвала на этом шаге. «Повторить предыдущий» — бесплатная базовая линия: предложить последний инструмент, который тот же актор использовал на предыдущем шаге.
| агент | вариантов | шагов с инструментом | разнородных | согласие (95% ДИ) | без подозрения | когда Jev выбрал инструмент | базовая линия «повторить предыдущий» | шагов без инструмента |
|---|---|---|---|---|---|---|---|---|
| anamnese | 5 | 8 | 0 | 3/8 = 38% (14–69%) | 3/8 | 3/3 | 4/4 | — |
| appsec | 6 | 6 | 2 | 0/6 = 0% (0–39%) | 0/6 | 0/1 | 0/5 | — |
| arquiteto | 9 | 5 | 2 | 1/5 = 20% (4–62%) | 1/5 | 1/2 | 1/4 | 3/3 |
| criativo | 3 | 5 | 0 | 3/5 = 60% (23–88%) | 3/5 | 3/4 | 1/4 | 2/2 |
| dev-board-engine | 9 | 24 | 3 | 12/24 = 50% (31–69%) | 1/3 | 12/24 | 20/23 | — |
| dev-game-session | 9 | 38 | 2 | 20/38 = 53% (37–68%) | 5/14 | 20/38 | 25/37 | — |
| dev-input-keyboard | 9 | 25 | 3 | 12/25 = 48% (30–67%) | 5/10 | 12/25 | 18/24 | — |
| dev-lead | 4 | 1 | 1 | 0/1 | — | — | — | — |
| dev-persistence | 9 | 34 | 3 | 24/34 = 71% (54–83%) | 4/12 | 24/34 | 26/33 | — |
| dev-piece-catalog | 9 | 37 | 5 | 21/37 = 57% (41–71%) | 4/11 | 21/37 | 31/36 | — |
| dev-retro-renderer | 9 | 26 | 0 | 13/26 = 50% (32–68%) | 5/14 | 13/26 | 19/25 | — |
| dev-scoring | 9 | 38 | 1 | 19/38 = 50% (35–65%) | 4/15 | 19/38 | 29/37 | — |
| infra | 5 | 2 | 1 | 0/2 | 0/1 | — | 1/1 | 0/1 |
| infra-workflows | 3 | 2 | 0 | 0/2 | 0/2 | — | 0/1 | — |
| po | 10 | 8 | 2 | 6/8 = 75% (41–93%) | 6/8 | 6/6 | 0/7 | 1/1 |
| qa-automacao | 7 | 20 | 5 | 4/20 = 20% (8–42%) | 2/9 | 4/20 | 14/19 | — |
| qa-estrategia | 6 | 16 | 6 | 0/16 = 0% (0–19%) | 0/15 | — | 7/15 | — |
| все | 295 | 36 | 138/295 = 47% (41–52%) | 43/138 = 31% (24–39%) | 138/258 = 53% (47–59%) | 196/275 = 71% (66–76%) | 6/7 = 86% (49–97%) |
Кривая уверенности по принятому правилу
AT-236 решила (ответы 7 и 12), что маршрутизатор только ограничивает
меню и что responder_sem_ferramenta — с любой уверенностью — отправляет
весь каталог в v1. Поэтому шаг ограничивается, только когда уверенность
на пороге или выше и выбор — инструмент; всё остальное ведёт себя как
сегодня. Дорого стоит ошибка ограничения не тем инструментом.
| порог | меню ограничено до 1 | падает на весь каталог | верных среди ограниченных (95% ДИ) | ограничено не тем инструментом | разнородных шагов отрезано |
|---|---|---|---|---|---|
| 0.00 | 258 | 37 | 138/258 = 53% (47–59%) | 120 | 18 |
| 0.30 | 191 | 104 | 110/191 = 58% (51–64%) | 81 | 13 |
| 0.40 | 126 | 169 | 76/126 = 60% (52–68%) | 50 | 10 |
| 0.50 | 73 | 222 | 50/73 = 68% (57–78%) | 23 | 5 |
| 0.60 | 43 | 252 | 29/43 = 67% (53–80%) | 14 | 4 |
| 0.70 | 17 | 278 | 14/17 = 82% (59–94%) | 3 | 2 |
| 0.80 | 7 | 288 | 7/7 = 100% (65–100%) | 0 | 0 |
| 0.90 | 2 | 293 | 2/2 = 100% (34–100%) | 0 | 0 |
Отчёт печатает ту же кривую по группам (dev agents, разговорные агенты,
gates и прочие агенты ToolLoop). Dev agents следуют таблице выше (0.70:
11/14; 0.80: 6/6). Разговорные агенты, когда Jev выбирал инструмент с любой
уверенностью, были правы в 10/12 шагах; он выбирал
responder_sem_ferramenta в 9 из их 21 шага с инструментом, что по ответу 12
безвредно.
Порог. Ни у одного порога сетки нижняя граница интервала не достигает 90% или выше — ни в целом, ни в какой-либо группе. Единственная точка без неверного ограничения — 0.80, и там маршрутизатор действует на 7 из 295 шагов (2%) — с нижней границей 65%. Эта выборка не поддерживает порог; если он будет зафиксирован по ней, 0.80 — значение, на которое она указывает, с таким покрытием. Ответ 4 AT-236 просит стоимость каждого типа ошибки, которую этот повтор оценить не может.
Задержка и стоимость
| задержка (302 запроса, последовательно, с машины сопровождающего) | p50 306 мс, p95 410 мс, макс 550 мс; 0 выше 2 000 мс (первое исполнение: 286 / 409 / 512) |
| входных токенов на запрос | от 538 до 3 220, медиана 1 710 |
стоимость по usage.cost | US$ 0,021964 за 302 шага — US$ 0,0000727 за шаг |
| списанная стоимость | GET /api/v1/generation для каждого из 302 id: total_cost даёт в сумме те же US$ 0,021964 (отношение 1,000) |
Дельта GET /api/v1/key вокруг каждого исполнения не совпала: +US$ 0,0349
против заявленных 0,0219 в первом исполнении, +0,0185 против 0,0220 во
втором. Ключ общий, и его счётчик запаздывает: за всю сессию AT-237 его
usage вырос с US$ 1,830 до US$ 2,052 (+0,222), тогда как генерации этого
повтора были списаны на US$ 0,044, значит, другая работа тратила на том же
ключе в то же время. Дельта — не измерение одного исполнения; запись по
генерации — да. Всего потрачено AT-237, два исполнения и пробы формата: около
US$ 0,044, против потолка US$ 2,00.
Чего повтор не видит
- Первое сообщение цикла. Агент
ToolLoop(dev agents, gates, Anamnese, Infra-Workflows) начинает с пользовательского сообщения, собранного в коде, — задача, история, карта модулей, — которое никогда не доходит до event log. У повтора есть лишь заголовок задачи для dev agents и gates задач и ничего дляqa-estrategia,appsec,anamneseиinfra-workflows.stateпродукта нёс бы это сообщение. 0/16 уqa-estrategia(Jev каждый раз отвечалresponder_sem_ferramenta) измеряет слепоту повтора так же, как и Jev; эти строки — нижняя граница и ничего не должны решать. - Остальной системный промпт.
contexto— это идентичность и строка инструкции проекта, а не собранный промпт (бизнес-правила, состояние задачи, файлы). - Верна ли метка. Модель, выбравшая инструмент, — не эталон; 157 шагов
помечены как подозрительные и сообщаются отдельно («без подозрения»).
Согласие ниже на чистых шагах (31%), чем в целом (47%): подозрительные шаги
— в основном падающие вызовы
terminalот dev agents, которые Jev тоже склонен выбирать, — поэтому плохо размеченные шаги не объясняют несогласия. - Эффект. Ограниченное меню меняет то, что модель делает дальше; повтор лишь сравнивает выбор с записанным. Становятся ли ходы короче, быстрее или дешевле — это AT-239.
Две находки вне чисел
- Разнородные шаги часты: 36 из 295 (12%). Ограничение меню одним инструментом превращает каждый из них как минимум в два шага — пробел 1 AT-235, теперь подсчитанный.
- Риск синхронизации каталога из AT-235 (пробел 3) реален, под другим
именем:
GET /api/v1/modelsне перечисляетtypesafe/jev-1.13, но перечисляетtypesafe/jev-router— чат-маршрутизатор с ценой-1. Синхронизация моделей импортировала бы его как чат-модель (неактивную). Записано здесь, не изменено.
Второй раунд (2026-09-29): где расстояние
Запрошен владельцем после первого раунда: повторить измерение, попытаться дойти хотя бы до 90% и сказать, где именно расстояние. Не достигнуто. На шагах проверки top-1 Jev — 73% по эквивалентности (95% ДИ 66–79%, n = 160) и 66% строго (58–73%); его top-2 доходит до 90% (84–94%). Остальная часть раздела — протокол, числа и ручное прочтение 43 оставшихся ошибок top-1. Тот же экземпляр, та же оговорка, что и в первом раунде: небольшой проект, один день, одна чат-модель; никакой выгоды продукта не утверждается (это AT-239). Репозиторий на
bc7f4c0738, веткаtest/replay-do-jev-v2.
Протокол (записан до любого исполнения)
- Только то, что продукт может произвести во время выполнения, до шага.
Ничто от целевого шага не попадает в
state: ни инструмент, который он вызвал, ни аргументы, ни результат, ни текст, который модель написала на том же шаге (Passo.textoчитается только из предыдущих шагов;variantes.spec.tsутверждает для каждого варианта, что вызов, аргумент, результат и текст целевого шага отсутствуют). Каждое новое поле имеет источник в engine, процитированный в заголовкеscripts/jev/variantes.ts. - Классы эквивалентности зафиксированы в
scripts/jev/equivalencia.tsдо запуска, исходя из семантики инструментов, и сообщаются рядом со строгим правилом (правилом первого раунда, чтобы 47% оставались сопоставимыми). Три накопительных слоя: E1terminal, чья команда лишь читает/перечисляет/ищет (ls,find,cat,pwd,grep,head,git status/log/diff… — составная команда считается по самому сильному сегменту, исполнение > запись > чтение) ≡read_file/search_workspace; E2read_file≡search_workspace; E3terminal, пишущий файл (перенаправление, heredoc,tee,sed -i,cp,mv,touch) ≡write_file. Асимметричны намеренно: выбор Jevterminalподходит только к вызовуterminal, а команда, которая исполняет (npm,node,rm,mkdir,git commit…), не эквивалентна ничему, кромеterminal. Заголовок — полное правило (E1+E2+E3), строгое правило рядом. Ничего не переклассифицировалось после просмотра результата; 3 ошибки, которые классы всё ещё допускают, посчитаны как таковые ниже. - Настройка × проверка по исполнению, а не по шагу. Группа — это
исполнение
ToolLoop(задача dev agent, раунд gate) или, для разговорных агентов, вся сессия агента. Группы упорядочиваются по хешу имени, и каждая идёт в ту половину, где к этому моменту меньше шагов, — детерминированно и слепо к любому результату. 328 пригодных шагов: 167 настройки, 161 проверки (n ниже считает шаги с инструментом: 161 и 160). Варианты итерировались только на настройке; проверка опрашивалась потом, по разу на вариант. Один инцидент, заявленный: после первых исполнений настройки я обнаружил, чтоskip_proficiency/emit_proficiencyAnamnese тоже завершают цикл, и добавил их в список границ исполнения, используемый для сборки state. Использование исправленного списка для разделения также переместило бы 154 из 167 шагов настройки в проверку, поэтому разделение сохраняет список, который был до первого запроса (FERRAMENTAS_DE_FIM_DA_DIVISAO), и только state использует исправленный. Четыре варианта state, зависящие от границы, были опрошены на настройке заново (US$ 0,075, сохранены как.descartadoв журнале расходов). - Замороженный снимок. База dev продолжала получать шаги от другой работы
во время сессии (335 строк расхода в конце против 333 в начале); анализ
выполняется по снимку из 333 строк и событиям до тех же порядковых номеров
(
--dados-cache). 26 новых шагов с первого раунда (302 отвеченных → 328 пригодных) — все шаги Anamnese из половины настройки. - Потолок расходов US$ 1,00 по сумме
usage.cost, проверяемый перед каждым запросом против накопленного итога каталога вывода (.jsonlи.descartado). Потрачено: US$ 0,414 на 4 604 запроса (см. «Задержка и стоимость»).
Выборка
| настройка | проверка | |
|---|---|---|
| шаги (с инструментом / без) | 167 (161 / 6) | 161 (160 / 1) |
dev agents (ToolLoop) | dev-piece-catalog 37, dev-retro-renderer 26, dev-input-keyboard 25 | dev-game-session 38, dev-scoring 38, dev-persistence 34, dev-board-engine 24 |
gates и прочие ToolLoop | anamnese 32, qa-automacao 20, appsec 3 | qa-estrategia 16, appsec 3, infra-workflows 2, anamnese 2 |
| разговорные | po 8, criativo 5, arquiteto 5 | infra 2, dev-lead 1 |
В проверке доминируют dev agents (134 из 160 шагов с инструментом) и один
gate (qa-estrategia, 16). Половина настройки несёт 32 шага Anamnese, на
которые Jev отвечает responder_sem_ferramenta, когда агент использовал
skip_proficiency (30 из 32): без них настройка даёт 94/129 = 73% по
эквивалентности, как и проверка.
Опробованные варианты (все)
tokens — медианный размер входа; задержка в мс (p50 / макс),
последовательные запросы с машины сопровождающего. Только шаги с
инструментом.
| вариант | что добавляет к предыдущему | настройка строго → эквивалентность (top-2) | проверка строго → эквивалентность (top-2) | токены | задержка |
|---|---|---|---|---|---|
original | ничего — state первого раунда | 39% → 48% (83%) | 49% → 58% (82%) | 1 687 | 322 / 780 |
kickoff | pedido = первое сообщение цикла, восстановленное из кода engine | 55% → 58% (89%) | 65% → 73% (91%) | 1 867 | 318 / 612 |
escopo | недавние шаги только текущего исполнения (ctx.messages начинается заново при каждом ToolLoop.run) | 57% → 59% (89%) | 66% → 73% (90%) | 1 867 | 312 / 814 |
resultados | результаты/аргументы до 2 000 символов | 53% → 55% (91%) | 62% → 69% (88%) | 2 011 | 314 / 699 |
texto | текст модели из предыдущих шагов | 53% → 55% (91%) | 64% → 73% (93%) | 2 090 | 315 / 685 |
progresso | выведенные счётчики (шагов к этому моменту, вызовов по инструменту, последний инструмент, запущенные тесты…) | 55% → 57% (91%) | 65% → 70% (93%) | 2 224 | 309 / 817 |
trilha | escopo + вся последовательность инструментов исполнения (только имена) | 53% → 57% (91%) | 64% → 71% (89%) | 1 936 | 310 / 614 |
fluxo | trilha + вопрос, говорящий, что агенты работают рывками | 49% → 55% (92%) | 64% → 75% (93%) | 2 014 | 309 / 586 |
enxuto | trilha только с 2 последними вызовами | 50% → 54% (89%) | 63% → 74% (91%) | 1 429 | 307 / 920 |
so_trilha | trilha без единого недавнего вызова | 39% → 44% (80%) | 33% → 56% (83%) | 1 168 | 307 / 480 |
acoes | escopo + результат команд, ожидавших одобрения (восстановленный из исхода действия) | 50% → 53% (91%) | 53% → 61% (89%) | 2 333 | 348 / 1 352 |
acoes_completo | acoes + 2 000 символов + текст + прогресс | 47% → 51% (92%) | 51% → 58% (92%) | 3 360 | 326 / 845 |
Шесть строк от original до progresso (каскад ниже) были написаны до
первого запроса. Остальные шесть написаны позже, в ответ на то, что показала
настройка, и представляют собой обычный поиск на половине настройки:
trilha, fluxo, enxuto и so_trilha пытаются дать Jev шаблон исполнения
в более лёгкой для чтения форме; acoes и acoes_completo появились после
того, как стало видно, что многие недавние шаги читали
(sem resultado gravado) (команды, ожидавшие одобрения).
Победитель — escopo, выбранный на настройке (лучший top-1, строго и по
эквивалентности); проверка для выбора не использовалась. Две вещи, которые
стоит прочесть в таблице: различия между kickoff … progresso лежат в
пределах доверительных интервалов (±7 пунктов), поэтому не показано, что
«больше входа» мешает или помогает после первого шага; а у fluxo лучшее
число проверки (75%), но он ниже победителя на настройке (55% против 59%), и
именно поэтому он не стал победителем — выбрать его сейчас значило бы
выбирать по проверке.
Откуда берётся каждое новое поле в engine (apps/engine/lib/engine/…):
| поле | источник |
|---|---|
pedido (dev) | initial_message/2, dev/dev_agent_server.ex:484; заголовки задачи и истории из tasks/stories |
pedido (qa-automacao) | initial_message/2, gates/qa_automacao_agent.ex:108; задача из последнего dev.awaiting_gate, требования истории |
pedido (qa-estrategia, appsec) | gates/qa_estrategia_agent.ex:93, gates/appsec_agent.ex:79; история не восстанавливается из лога, поэтому сообщение говорит об этом и несёт карту модулей |
| недавние шаги по исполнению | ToolLoop.Default.init/1 и loop/1 (harness/tool_loop.ex): messages собирается run |
полные результаты, texto | сообщения tool и assistant, которые добавляет loop/1 (concluir_despacho/5, append(ctx, message)); лог обрезает результаты до 2 000 |
результаты одобренных команд (acoes) | texto_do_desfecho/1, dev/dev_agent_server.ex:460; в логе для них нет tool.result, только proposed_actions.execution_result |
progresso, trilha | счётчики по предыдущим вызовам исполнения в тех же messages |
Не перенесены, и это сказано в kickoff.ts: Anamnese (её сообщение
собирается из окна лога и списка членов, которых лог не хранит),
infra-workflows (карта контекста) и разговорные агенты (их pedido
по-прежнему последнее сообщение пользователя).
Каскад (проверка, шаги с инструментом)
Каждая строка — предыдущая плюс одна деталь, на тех же 160 шагах.
| шаг | n | top-1 (95% ДИ) | top-2 (95% ДИ) |
|---|---|---|---|
| вход первого раунда, строгое правило | 160 | 78/160 = 49% (41–56%) | 118/160 = 74% (66–80%) |
| + E1 терминал-чтение ≡ чтение | 160 | 92/160 = 57% (50–65%) | 125/160 = 78% (71–84%) |
+ E2 read_file ≡ search_workspace | 160 | 93/160 = 58% (50–65%) | 131/160 = 82% (75–87%) |
+ E3 терминал, который пишет ≡ write_file | 160 | 93/160 = 58% (50–65%) | 131/160 = 82% (75–87%) |
+ pedido = первое сообщение цикла | 160 | 116/160 = 73% (65–79%) | 145/160 = 91% (85–94%) |
| + недавние шаги только текущего исполнения | 160 | 117/160 = 73% (66–79%) | 144/160 = 90% (84–94%) |
| + результаты до 2 000 символов | 160 | 110/160 = 69% (61–75%) | 141/160 = 88% (82–92%) |
| + текст модели из предыдущих шагов | 160 | 116/160 = 73% (65–79%) | 149/160 = 93% (88–96%) |
| + выведенные счётчики | 160 | 112/160 = 70% (62–77%) | 148/160 = 93% (87–96%) |
Всё движение несут две строки: эквивалентности (+9 пунктов) и первое
сообщение цикла (+15). После escopo ничто не выводит top-1 за пределы
интервала, а восстановленные результаты одобренных команд (acoes) его
понижают. 47% первого раунда на том же входе — это 58% при объявленных
эквивалентностях.
Заголовок
Вариант escopo, проверка, 160 шагов с инструментом (плюс 1 без, на котором
Jev ошибся):
| top-1, правило эквивалентности | 117/160 = 73% (66–79%) |
| top-1, строгое правило (сопоставимо с 47%) | 105/160 = 66% (58–73%) |
| top-2 (меню ограничено до 2), эквивалентность / строго | 144/160 = 90% (84–94%) / 134/160 = 84% (77–89%) |
| top-3, эквивалентность | 148/160 = 93% (87–96%) |
| базовая линия «повторить предыдущий инструмент» (бесплатная), эквивалентность / строго | 122/149 = 82% (75–87%) / 108/149 = 72% (65–79%) |
| меню из 2 без второго вызова, {выбор Jev, предыдущий инструмент}, эквивалентность / строго | 140/160 = 88% (81–92%) / 125/160 = 78% (71–84%) |
| без Anamnese (kickoff не восстановлен; приостановлена в продукте) | 115/158 = 73% (65–79%) |
Jev не обыгрывает бесплатную базовую линию. На тех же шагах проверки «повторить предыдущий инструмент» даёт 82% (72% строго), выше 73% (66%) Jev. Его единственное преимущество проявляется в объединении: предложение выбора Jev и предыдущего инструмента доходит до 88% (78% строго), на 15 пунктов выше одного Jev, без лишнего вызова. Ни одна конфигурация не доходит до 90% в top-1, и ни у одной нижняя граница не 90%.
По агентам (проверка, эквивалентность): dev-board-engine 23/24 = 96% (80–99%),
dev-persistence 28/34 = 82% (66–92%), dev-scoring 31/38 = 82% (67–91%),
dev-game-session 28/38 = 74% (58–85%), qa-estrategia 4/16 = 25% (10–49%),
appsec 1/3, infra 0/2, infra-workflows 0/2, dev-lead 0/1 (у
qa-estrategia и appsec первое сообщение восстановлено, но без истории; у
остальных трёх оно вообще не восстановлено).
Уверенность по-прежнему не отделяет верное от неверного:
| порог | меню ограничено до 1 | верных среди ограниченных (95% ДИ) |
|---|---|---|
| 0.00 | 155/160 | 117/155 = 75% (68–82%) |
| 0.40 | 123/160 | 95/123 = 77% (69–84%) |
| 0.60 | 55/160 | 44/55 = 80% (68–88%) |
| 0.70 | 32/160 | 23/32 = 72% (55–84%) |
| 0.80 | 16/160 | 8/16 = 50% (28–72%) |
| 0.90 | 10/160 | 4/10 = 40% (17–69%) |
Выше 0,7 точность падает: уверенные ошибки — те, что описаны ниже (Jev уверен, что после написания тестов идёт их запуск).
Расстояние по причинам
82 ошибки проверки по правилу первого раунда и то, что с ними сделала каждая деталь каскада (шаги с инструментом, n = 160):
| причина | шагов | пунктов | доля расстояния |
|---|---|---|---|
| мерка — выбор Jev был эквивалентным (E1–E3, объявленные заранее) | 15 | 9.4 | |
| мерка — остаток: классы всё ещё упускают случай (3 шага, ниже) | 3 | 1.9 | 22% (18 шагов) |
вход — первое сообщение цикла (kickoff) | 23 | 14.4 | |
вход — escopo | 1 | 0.6 | |
| вход — остаток: невосстановленные первые сообщения или результат вне лога (7) | 7 | 4.4 | 38% (31 шаг) |
| темп агента, а не поле state (ниже) | 29 | 18.1 | |
| Jev расходится без того, чтобы state это оправдывал (4) | 4 | 2.5 | 40% (33 шага) |
| итого | 82 | 51.3 |
43 ошибки, оставшиеся после последней восстановленной детали, прочитаны по одной (не выборка): отправленный state, вызванные инструменты, собственный текст модели на этом шаге (который Jev никогда не видит), а также выбор и уверенность Jev. Результат:
- Мерка, 3.
dev-board-engineterminal→search_workspace: команда агента былаfind … | sort; ls; for f in docs/*; do …cat— только чтение, но циклаforнет в списке «только чтение», поэтому он оценивается как исполнение. Плюс дваread_file→terminal, где агент всё время читал черезterminal: эквивалентны при любом прочтении, но классы асимметричны намеренно, и это не переоценивалось. - Вход, 7. Все ошибки
infra(2 шага),infra-workflows(2) иdev-lead(1) — пять шагов, первое сообщение которых перенос не восстанавливает, — плюс дваreport_done→terminal, где результата последней команды («exit 0, a suíte passou») нет в логе. Вариантacoesвосстанавливает эти результаты и в целом сделал Jev хуже (61%): перед падающей песочницей он говоритreport_blockedтам, где агент продолжал пытаться. - Темп агента, 29. Jev называет следующий этап потока, а агент остаётся
ещё на шаг в текущем:
14 ×
write_file→terminal(Jev: «тесты написаны, запустите их», 8 из них с уверенностью ≥ 0,6, до 0,98; агент написал ещё два-три файла до этого), 12 ×qa-estrategiaread/search→emit_plano_de_teste(Jev перескакивает к финальному инструменту kickoff с уверенностью 0,25–0,56; агент исследовал ~15 шагов, прежде чем выпустить), 2 ×appsecто же самое, 1 ×terminal→report_blockedпосле отклонённого действия. Во всех них выбор Jev — то, что читатель лога назвал бы естественным следующим шагом. То, что решает «сколько ещё файлов, сколько ещё чтений», — это план модели, которого нет ни в одном поле, которое продукт мог бы передать маршрутизатору, — он существует, один раз, в самом тексте предсказываемого шага. - Jev расходится, 4.
write_file→search_workspaceдважды с уверенностью 0,25–0,26,rag_search→terminalдважды (0,24, 0,59). Это единственные шаги, где state не объясняет выбор: 2,5% шагов.
Итак: из расстояния в 51 пункт между 49% первого раунда и 100% примерно пятая часть была меркой, около двух пятых — входом, которого не хватало повтору (а восстановимая его часть восстановлена), и две пятых — темп агента, причём 5% шагов — чистая ошибка Jev. От входа остаётся максимум 4 пункта, и это не то, что отделяет 73% от 90%.
Что понадобилось бы для 90% и сколько стоит каждая деталь
Шаги проверки, 43 ошибки; 90% — это 144/160, то есть на 27 попаданий больше.
| деталь | шагов максимум | примечание |
|---|---|---|
завершить перенос входа (Anamnese, infra-workflows, первые сообщения разговорных; история qa-estrategia) | до +7 → 79% | верхняя граница: с полным входом Jev здесь лучше не стал, а восстановление результатов его ухудшило |
ужесточить классы (циклы for, терминал-как-чтение в обратном направлении) | +3 → 75% | это мерка, а не точность; решать это после просмотра ошибок — именно то, что запрещает протокол, поэтому этого нет в заголовке |
| темп агента | до +29 | это не поле state; единственные входные двери — меню из 2, включающее предыдущий инструмент (измерено: 88%, 81–92%), или top-2 (90%, 84–94%) |
Если читать как продуктовое решение, а это дело AT-239: маршрутизатор, которому нужно выбрать один инструмент, остаётся около трёх четвертей и ниже бесплатной базовой линии; маршрутизатор, который ограничивает меню до двух, доходит до девяти из десяти, а предыдущий инструмент — второе имя не хуже второго варианта самого Jev. Этот раздел не утверждает ни одной из двух выгод.
Задержка и стоимость (вариант escopo)
| входных токенов на запрос | p50 1 866, p95 2 557, макс 3 220 (первый раунд: медиана 1 710, макс 3 220) |
| задержка (328 запросов, последовательно) | p50 317 мс, p95 449 мс, макс 814 мс; 0 выше 2 000 мс |
| стоимость | US$ 0,0000769 за шаг (US$ 0,025233 за 328), и GET /api/v1/generation для каждого из 328 id даёт в сумме те же US$ 0,025233 (отношение 1,000) |
| весь этот раунд | US$ 0,414 по usage.cost — 12 вариантов, 3 из них дважды на части половины настройки — против потолка US$ 1,00 |
Более богатые варианты стоят больше входа (acoes_completo: медиана 3 360
токенов, около US$ 0,00014 за шаг против 0,00008) и ничего не покупают;
побеждающий state — ещё и дешёвый. У потолка 2 000 мс из AT-236 запас в два
раза.
Чего этот раунд всё ещё не видит
- Историю двух gates, которые работают до любого кода (
qa-estrategia,appsec): запрос, запускающий их, внутренний и не идёт в лог. - Первое сообщение Anamnese,
infra-workflowsи разговорных агентов; и системный промпт заcontexto. - Эталон. Метка по-прежнему — инструмент, который вызвала чат-модель, и большинство из 29 ошибок темпа — выборы, которые человек-читатель назвал бы разумными, и именно поэтому top-2 и объединение говорят больше, чем top-1.
- Эффект. Ограниченное меню меняет то, что модель делает дальше; AT-239.
Меню из 2 вариантов (2026-09-29): ограничивать вместо выбора
Запрошено владельцем после второго раунда, где top-1 Jev (73%) остался ниже 90%, но меню из двух ({выбор Jev, предыдущий инструмент}) и top-2 Jev выглядели хорошо: проверить меню из 2 вариантов. Только результат, у которого нижняя граница 95% интервала не ниже 90%, — ясное «да». Не так: ни одна политика туда не доходит. Лучшая политика из 2 вариантов, P3, покрывает 91% (85–94%) шагов проверки, то есть правильный инструмент находится внутри меню; нижняя граница — 85%, на пять пунктов ниже линии. И это покрытие, потолок для сквозной точности (см. «Потолок»), а не сама точность. Тот же экземпляр, те же оговорки, что в предыдущих раундах; ничего здесь не меняет продукт (это AT-239). Воспроизведите через
pnpm --filter @brabo/scripts jev:menu.
Политики (записаны до запуска, в scripts/jev/menu.ts)
Политика превращает ответ Jev в ограниченное меню. Решения владельца
действуют везде: Jev только ограничивает меню, а когда он отвечает
responder_sem_ferramenta, остаётся весь каталог (AT-236); потолок
2 000 мс (во втором раунде его не превысил ни один из 328 запросов). Ничего
не переклассифицировалось после просмотра результата; menu.spec.ts
фиксирует, что возвращает каждая политика.
| политика | меню |
|---|---|
| P0 | весь каталог (якорь: 100% покрытия, ничего не ограничивает) |
| P1 | два самых вероятных инструмента Jev |
| P2 | {выбор Jev, предыдущий инструмент того же исполнения} |
| P3 | P2, но если Jev говорит responder_sem_ferramenta (или предыдущего инструмента нет), меню — весь каталог |
| P4 | {выбор, предыдущий инструмент, второй вариант Jev} (до 3; только для справки) |
Правила, общие для всех: упавший ответ (таймаут, ошибка, выбор вне вариантов) оставляет весь каталог; предыдущий инструмент, которого нет в каталоге агента, игнорируется; меню, которое вышло бы пустым, — весь каталог.
Покрытие = доля шагов с инструментом, на которых инструмент, который агент
действительно вызвал, находится внутри меню, — строго (то же имя) и по
эквивалентности (классы E1–E3 второго раунда, так что меню с read_file
покрывает terminal, который лишь читает). Доля ограничения = доля
шагов, на которых меню меньше каталога агента: политика, которая почти
никогда не ограничивает, тривиально «100%» и бесполезна, поэтому покрытие
даётся также среди ограниченных шагов. Вариант escopo (победитель второго
раунда), половина проверки, 160 шагов с инструментом (плюс 1 без).
Числа (проверка)
| политика | покрытие, эквивалентность (95% ДИ) | строго | ограничивает | покрытие среди ограниченных | открытые инструменты, до → после | токены определений на вызов, до → после |
|---|---|---|---|---|---|---|
| P0 | 160/160 = 100% (98–100%) | 100% | 0/160 = 0% | — | 7.4 → 7.4 | 746 → 746 |
| P1 | 144/160 = 90% (84–94%) | 134/160 = 84% (77–89%) | 158/160 = 99% | 142/158 = 90% (84–94%) | 7.4 → 2.0 | 746 → 150 |
| P2 | 143/160 = 89% (84–93%) | 128/160 = 80% (73–85%) | 157/160 = 98% | 140/157 = 89% (83–93%) | 7.4 → 1.3 | 746 → 102 |
| P3 | 145/160 = 91% (85–94%) | 132/160 = 83% (76–88%) | 147/160 = 92% (87–95%) | 132/147 = 90% (84–94%) | 7.4 → 1.6 | 746 → 138 |
| P4 | 149/160 = 93% (88–96%) | 142/160 = 89% (83–93%) | 158/160 = 99% | 147/158 = 93% (88–96%) | 7.4 → 2.2 | 746 → 166 |
Какая лучшая. Важна P3: наибольшее покрытие среди политик из 2 вариантов, она соблюдает правило владельца и ограничивает 92% шагов. Её нижняя граница — 85,1%; у P1 — 84,4%, у P2 — 83,6%, у P4 — 88,1%. P4 ближе всех к линии, но меню до 3 — не тот вопрос, который был задан. Все политики остаются без «ясного да»; P1 и P3 попадают в 90% по точечной оценке, те же «90% (84–94%)», что второй раунд уже имел для top-2, — предыдущий инструмент ничего не добавляет к покрытию сверх второй догадки самого Jev, он лишь делает меню меньше (1,6 инструмента против 2,0).
Две вещи, которые таблица скрывает.
- P3 предлагает лишь один инструмент в 98 из 160 шагов. Когда выбор Jev является предыдущим инструментом, в меню одно имя (P2: 108 из 160). Эти меню покрывают 89 из 98 (91%). Остальные 49 ограниченных шагов имеют меню из двух и покрывают 43 (88%). «Меню из 2» большую часть времени — это меню из 1 без выбора, оставленного агенту: он вынужден.
- Интервал оптимистичен. Уилсон считает 160 шагов независимыми, а они
таковыми не являются: проверка — это 10 исполнений (задачи четырёх dev
agents, gates
qa-estrategia,appsecиinfra-workflows, а также Anamnese, Infra и Dev Lead), и последовательные шаги одного исполнения делят запрос, файлы и результаты. При так малом числе кластеров честный интервал шире напечатанных. Это не меняет ответа («нет» не улучшается), но и «да» не было бы заработано 160 шагами.
Настройка и новые шаги
| политика | настройка (161 шаг) покрытие, эквивалентность | ограничивает | новые шаги (36) покрытие |
|---|---|---|---|
| P1 | 144/161 = 89% (84–93%) | 156/161 = 97% | 36/36 |
| P2 | 143/161 = 89% (83–93%) | 127/161 = 79% | 36/36 (ограничивает 0) |
| P3 | 147/161 = 91% (86–95%) | 115/161 = 71% | 36/36 (ограничивает 0) |
| P4 | 148/161 = 92% (87–95%) | 156/161 = 97% | 36/36 |
Настройка говорит то же, что и проверка (P3 91%, нижняя граница 86%), как и
ожидалось, поскольку ни одна из политик не настраивалась. Она несёт 32 шага
Anamnese, для которых Jev говорит responder_sem_ferramenta, и P3 поэтому
оставляет весь каталог: 71% ограничения вместо 92%. 36 новых шагов (база
dev выросла с 333 до 369 строк расхода после снимка) — все от Anamnese,
агента, приостановленного в продукте: Jev отвечает «никакого инструмента», и
P2/P3 открывают весь каталог, поэтому «36/36» тривиально верно и ничего не
говорит о меню. Все шаги вместе и без Anamnese (287 с инструментом; половина
настройки уже выбрала вариант, так что это не чистая проверка): P1 89%
(84–92%), P2 88% (84–91%), P3 90% (86–93%), P4 92% (88–94%). Больше шагов
сужают интервал, но нижняя граница остаётся на 86%; при истинном покрытии
около 91% нужно около трёх тысяч шагов, чтобы нижняя граница перешла 90%, а
помог бы лучший маршрутизатор, а не большая выборка.
Чувствительность, а не выбор: с любым из двенадцати вариантов state P2
покрывает 88–91%, а P3 — 89–92% шагов проверки, поэтому результат не зависит
от escopo (P1 зависит: 82% с исходным входом, 93% с texto или fluxo). Ни
у одного варианта нижняя граница тоже не 90% — самые высокие у P1 с texto
(88%) и P3 с enxuto (87%), — а выбрать один после того, как это увидено, —
ровно то, что запрещает протокол.
Где покрытие проваливается
P3, проверка, 15 шагов вне меню: write_file использован, когда меню было
terminal (4), read_file, когда terminal (2), report_done, когда
terminal (2), write_file, когда {search_workspace, terminal} (2), и
пять единичных случаев среди gates и разговорных агентов
(emit_plano_de_teste предложен вместо rag_search или search_workspace, и
наоборот). Это расстояние темпа второго раунда, увиденное с другой стороны:
Jev называет следующий этап, агент делает ещё шаг в текущем — а агент, чьё
меню — terminal, не может записать файл инструментом, сделанным для этого.
По агентам (P3, эквивалентность): dev-board-engine 23/24 = 96% (80–99%),
dev-scoring 35/38 = 92% (79–97%), dev-persistence 31/34 = 91% (77–97%),
qa-estrategia 14/16 = 88% (64–97%), dev-game-session 32/38 = 84% (70–93%);
у остальных меньше четырёх шагов.
Потолок: чего не говорит покрытие
Покрытие — это потолок сквозной точности, а не точность. Меню содержит правильный инструмент; модели агента ещё нужно выбрать его среди вариантов. Метка («инструмент, который вызвала чат-модель») — выбор, сделанный агентом из всего каталога, поэтому полное меню — 100% по построению, а ограниченное может только терять. Что можно сказать без продукта, для P3 на проверке, разделив 160 шагов:
- 99 (62%, 54–69%): каждый инструмент меню верен, поэтому любой выбор агента верен — пол.
- 46 «спорных»: ровно один из вариантов верен, и агент решает сам — 28 только предыдущий инструмент, 18 только выбор Jev. Агент, всегда берущий предыдущий инструмент, заканчивает на 127/160 = 79%; всегда берущий выбор Jev — на 117/160 = 73% (снова базовые линии второго раунда); бросающий монетку — на 76%. Агент, выбирающий лучше обоих, а на этом и строится вся ставка, попадает между 79% и потолком 91%.
- Остальные 15 вне меню: потеряны, что бы ни делал агент.
Итак, сквозной диапазон — от 62% до 91%, и где он окажется, зависит от того, насколько хорошо модель агента читает ситуацию, которую повтор не видит. Ответить на это может только живой тест (AT-239).
Экономия на определениях инструментов
Каталог содержал описание каждого инструмента (catalogo.json), а теперь
ещё и definicoes: размер в байтах полного spec/0 каждого инструмента (имя,
описание и схема параметров), сериализованного engine (catalogo.exs,
запущен заново; остальная часть файла не изменилась). Токены — это байты,
делённые на 4, приближение, которое использует весь повтор. На проверке P3
уходит с 746 до 138 токенов определений на вызов — 608 токенов, на 81%
меньше: восемь инструментов dev agent весят 3 068 байт, а меню из двух —
около 500. P1 экономит 596, P2 — 644 (средние по шагам половины; половина
настройки экономит меньше, потому что Anamnese сохраняет весь каталог).
Сколько это стоит — другой вопрос. Сам вызов Jev читает ~1 870 токенов (US$ 0,0000769) на модели гораздо дешевле модели агента; экономия на определениях — 608 токенов модели агента на вызов. Точка безубыточности — входная цена US$ 0,126 за миллион токенов (0,0000769 / 608): выше неё экономия окупает вызов Jev, без учёта кеша. Кеш — подвох: провайдеры кешируют префикс запроса, определения инструментов идут первыми, а меню, меняющееся от шага к шагу, меняет префикс — экономия может быть возвращена промахами кеша. Это живое измерение, и причина, по которой тест ниже записывает долю кешированных токенов.
Живой тест (не выполнен): схема
- Вопрос. С P3 перед агентом ухудшается ли работа агента, и насколько она дешевеет? Не «есть ли инструмент в меню» (это этот раздел).
- Этап 1, тень (дёшево, до того как пользователь увидит меню). Для каждого шага зарезервированной выборки спросить саму модель агента дважды об одном state, с полным каталогом и с меню P3, и сравнить инструмент, который она выбирает, по правилу эквивалентности. Метрика: согласие между двумя, с нижней границей 95% интервала. Чтобы показать истинное согласие 94% с нижней границей 90% (мощность 80%): около 390 шагов, взятых из не менее чем 40 разных исполнений и более чем одного проекта (в этой выборке 21 исполнение в одном проекте), интервал по кластеру (исполнению), а не по шагу.
- Этап 2, живой A/B, только если этап 1 пройден. Ветви: контроль (полный каталог) и P3, разыгрываемые по исполнению, никогда по шагу (меню одного шага формирует следующий). Основная метрика: исполнение доходит до gate без переделки, допуск не меньшей эффективности 5 пунктов; при 80% завершения, 5% одностороннем и мощности 80% — около 790 исполнений на ветвь (допуску 10 пунктов нужно около 200). При объёме этого проекта (7 исполнений dev во всей выборке) это требует многих проектов или недель. Вторичные: шаги на исполнение, входные токены включая долю кешированных токенов, стоимость исполнения, задержка, добавленная Jev (p50/p95, потолок 2 000 мс), и доля шагов, на которых Jev оставляет весь каталог.
- Стражи. Флаг отключения на проект; откат на весь каталог при любом сбое Jev или выше потолка задержки (уже в каждой политике); способ для агента запросить полный каталог, когда в меню нет нужного (меню из одного вынуждает инструмент, 61% шагов здесь); промежуточный просмотр каждые 100 исполнений с остановкой, если основная метрика падает больше допуска; Anamnese вне выборки (приостановлена); меню и выбор записываются на каждом шаге, чтобы анализ был запросом, а не воспоминанием.
Чего этот раздел всё ещё не видит
- Выбор агента внутри меню: причина, по которой это потолок.
- Зависимость между шагами: 10 кластеров в проверке; интервалы — по шагам.
- Кеш промпта и задержка в цикле агента: не измерены (вызов Jev измерен, p50 317 мс; его эффект в цикле — нет).
- Метка: по-прежнему инструмент, который вызвала чат-модель, а ограниченное меню меняет то, что она делает дальше.
Стоимость этого теста
Один вызов, для 36 новых шагов: US$ 0,001275 по usage.cost (потолок
задачи US$ 0,30). Политики работают офлайн по ответам, уже лежащим на диске.
Накопленный расход каталога вывода вырос с US$ 0,4136 до US$ 0,4149.
Живое сравнение (AT-239, 2026-10-01): ограничивать, включено для Haiku и выключено для DeepSeek
AT-239 (EP-029, HS-063) задаёт вопрос, который оставлял открытым каждый предыдущий раздел: с включённым маршрутизатором становятся ли ходы короче, быстрее или дешевле, и выходит ли работа по-прежнему? Те же задачи, на той же модели OpenRouter, с маршрутизацией ADR 0179, включённой и выключенной, на двух чат-моделях разной скорости.
Ситуация на 2026-10-01: измерено с третьей попытки (ниже). Первая попытка: не измерено. У сессии, построившей инструмент (ветка
test/jev-com-e-sem, отdevнаd070b000d7), был платный ключ и потолок US$ 5,00, но её исходящий прокси отказал вCONNECTкopenrouter.ai:443по политике организации, а также кrepo.hex.pmиbuilds.hex.pm, поэтому даже engine нельзя было скомпилировать. Ни один вызов чата и ни один вызов Jev не покинул машину: потрачено US$ 0,00. Далее — инструмент, протокол и правило решения, всё записанное до любого исполнения, числа, которые удалось измерить без сети, а затем живая таблица третьей попытки.Вторая попытка, в тот же день: всё ещё не измерено, по другой причине. Владелец открыл
openrouter.aiна выходе сессии (GETответил 200), и обе модели протокола значились с поддержкой инструментов (deepseek/deepseek-v4.1-flashпо US$ 0,03/0,50 за миллион входа/выхода,anthropic/claude-haiku-4.5по US$ 1,00/5,00). Первый вызов чата вернулHTTP 403 Key limit exceeded (total limit): у тестового ключа лимит US$ 10,00, US$ 10,05 уже использовано и никакого сброса (GET /api/v1/key). Smoke одной задачи, вне протокола и в собственной папке, потратил US$ 0,00; ни одно исполнение протокола не было записано. Его запуск требует, чтобы владелец поднял лимит ключа (или выдал другой ключ) — около US$ 1,28 по оценке ниже, меньше по текущей цене DeepSeek. Smoke также нашёл две вещи, которые фальшивая сеть никогда бы не нашла, обе исправлены или задокументированы в инструменте:
- Сбой учётной записи или сети записывался как измеренное исполнение. Оба исполнения smoke закончились
erroна шаге 0 сHTTP 403, были записаны в вывод как «задача не вышла», и возобновление никогда бы их не повторило: с мёртвым ключом все 40 исполнений попали бы в таблицу.falhaDeInfra(resumo.ts) теперь классифицирует ошибку чата HTTP 401/402/403/407/429 или транспорта как несостоявшееся измерение;vivo.tsне записывает такое исполнение, останавливает исполнение с причиной, а возобновление его повторяет. Ошибка провайдера в теле 200, 5xx и падение Jev остаются исходами ветви, как в engine.fetchв Node игнорируетHTTPS_PROXY. За прокси запускайте сNODE_USE_ENV_PROXY=1(Node ≥ 22.21); без этого вызовы шли напрямую, и выход отвечал 403 с телом, которое не было JSON.Третья попытка, в тот же день: измерено. Владелец поднял лимит ключа до US$ 20,00 (US$ 9,95 оставалось перед исполнением) и разрешил потолок US$ 4,50. Протокол ниже прошёл ровно так, как написан, — две модели, пять задач, два раунда, 40 исполнений, значения по умолчанию
jev:vivo, — на коммите0eab0cb0e9, без единого сбоя учётной записи или сети и без прерванных исполнений. Потрачено: US$ 0,5195 по суммеusage.costв выводе (US$ 0,0424 DeepSeek, US$ 0,4771 Haiku);usageключа сдвинулся на те же US$ 0,5195 (US$ 10,0545 → US$ 10,5739, прочитано примерно через десять минут после исполнения — чтение сразу после него ещё показывало US$ 0,4597, списание запаздывает относительно ответов). Таблица и вердикт — в «Живой таблице» и «Рекомендации» ниже; протокол, правило и оценку не трогали после прихода чисел.
Что запускает инструмент и что является переносом
pnpm --filter @brabo/scripts jev:vivo (файлы в scripts/jev/vivo/) запускает
цикл dev agent на пяти маленьких задачах, один раз с включённым
маршрутизатором и один раз с выключенным, и вызывает OpenRouter напрямую —
api, engine и база не участвуют, как в платной проверке языка AT-167.
| файл | роль | продукт или перенос |
|---|---|---|
roteador.ts | recortarEstado, montarPedidoAoJev, lerRespostaDoJev, menuP3, потолок state | код продукта, реэкспортированный из apps/api/src/domain/llm/tool-router.ts |
laco.ts | цикл: потолок итераций, восстановление вызовов инструментов, написанных текстом, остановка на принятом report_done/report_blocked, единственная повторная попытка с полным каталогом, когда ограниченное меню получило ответ без инструмента | перенос ToolLoop, EngineApiClient.llm_turn/5 и DecidirFerramentaDoPassoUseCase |
recuperacao.ts | какие шаги прошли через tool_call_recovery.ex | перенос; его spec прогоняет случаи tool_call_recovery_test.exs и фиксирует строки-якоря .ex |
rede.ts | вызов чата (сообщения на проводе, как их пишет api) и вызов Jev (потолок 2 000 мс, те же именованные падения) | перенос toWireMessage/toWireTool и JevToolRouter; строки-якоря зафиксированы |
ferramentas-dev.exs → ferramentas-dev.json | полный spec/0 девяти инструментов Engine.Dev.Tools.registry/0 (имя, описание, схема параметров) | прочитан из исходников engine чистым elixir, без скомпилированного engine; spec падает, когда реестр или описание дрейфует |
tarefas.ts | пять задач над проектом Node без зависимостей, у каждой проверка, запускаемая после цикла (исходные тесты восстановлены, скрытые тесты добавлены): «задача вышла» | собственный инструмента |
executor.ts | инструменты в временной песочнице, возвращающие тексты engine там, где они зашиты в код | собственный инструмента |
resumo.ts, vivo.ts | таблица, правило решения, оценка стоимости, CLI | собственный инструмента |
Вне инструмента, одинаково в обеих ветвях и заявлено: конвейер одобрения
(terminal и write_file выполняются напрямую, как в автоматическом
режиме), сжатие контекста, языковая ориентация и профиль автора, добавляемые
в конце каждого вызова, RAG (пустой индекс), контракты модулей (ни одного не
объявлено) и остальная часть настоящего системного промпта (файлы
инструкций, бизнес-правила, состояние задачи). Системный промпт — это
идентичность dev из engine плюс две строки контекста проекта; первое
пользовательское сообщение — собственный kickoff engine
(scripts/jev/kickoff.ts). Стоимость каждого вызова — usage.cost ответа,
число, которое RN-665 (ADR 0188)
кладёт в metering; token_usage продукта не участвует.
Протокол (записан до любого исполнения)
- Модели:
deepseek/deepseek-v4.1-flash(медленнее, рассуждающая) иanthropic/claude-haiku-4.5(быстрее), две, до которых проверка AT-167 уже дотягивалась с определениями инструментов на этом ключе. - Задачи:
T1-media(реализовать функцию),T2-moeda(исправить баг форматирования),T3-limite(причина в другом файле:config/),T4-slug(создать модуль и его тесты; скрытые тесты его проверяют),T5-renomear(переименовать в двух файлах; вsrc/не должно остаться следа старого имени). Начальное состояние каждой задачи проваливает проверку, а эталонное решение её проходит (spec). - Выборка: 2 раунда → 10 исполнений на ветвь на модель, всего 40. Порядок ветвей чередуется по задаче и раунду, чтобы ни одна ветвь не получала всегда самую медленную минуту провайдера. Потолок итераций 30 (потолок dev в продукте — 60; здесь он ограничивает расход).
- На исполнение: шаги; шаги, чьи вызовы пришли текстом и прошли через
восстановление; шаги, на которых маршрутизатор был опрошен, применил меньшее
меню, оставил весь каталог без падения (нет предыдущего инструмента, или Jev
сказал
responder_sem_ferramenta) или упал (с причиной и источником RN-059); повторные попытки с полным каталогом; вызовы вне меню шага; настенная задержка всего исполнения (Jev + чат + инструменты) и части LLM; стоимость чата и Jev; как закончилось; вышла ли задача. - Правило решения (
compararвresumo.ts), по модели, включено против выключено: качество — доля «задача вышла» у включённого не более чем на 10 пунктов ниже выключенного; стоимость — средняя стоимость на вышедшую задачу (чат + Jev) у включённого не выше выключенного; задержка — медианное исполнение включённого не выше 1,2× выключенного. Оставить включённым по умолчанию, если все три выполняются на каждой модели; выключить, если качество проваливается на каждой модели или ни одна модель не проходит все три; ограничить моделями, которые проходят, — в противном случае. Меньше 10 исполнений на ветвь — этоamostra_insuficiente, но никогда не вердикт.
Оценка стоимости (без сети)
--estimar с ценами, которые можно было измерить по прежним записям: вход
DeepSeek по US$ 0,30 за миллион и Haiku по US$ 1,00 (оба выведены из парной
входной стоимости, сообщённой в
Измерении эвристики языка);
выход US$ 1,20 для DeepSeek — предположение (нигде не записанное), а
US$ 5,00 для Haiku — прейскурантная цена. Предполагаются 12 шагов на
исполнение, 400 токенов истории, добавляемых на шаг, 150 выходных токенов, без
кеша:
| модель | на исполнение | обе ветви, 2 раунда |
|---|---|---|
deepseek/deepseek-v4.1-flash | ~US$ 0,015 | ~US$ 0,29 |
anthropic/claude-haiku-4.5 | ~US$ 0,049 | ~US$ 0,99 |
| итого | ~US$ 1,28 |
Исполнение останавливается перед любым вызовом, как только накопленный
usage.cost папки вывода достигает --teto-usd (по умолчанию 4.50), и не
начинает исполнение, если осталось меньше --reserva-usd (0.10).
Что удалось измерить офлайн (2026-10-01)
Каталог dev вырос со времени повтора: listar_contratos_de_modulos (RN-684) —
девятый инструмент, и catalogo.json (восемь) для него устарел — 746 → 138
токенов повтора уже не описывают текущий каталог. На проводе (форма
toWireTool, ferramentas-dev.json):
| символов | ≈ токенов (÷ 4) | |
|---|---|---|
| весь каталог dev, 9 инструментов | 3 816 | 954 |
| меню из 2 инструментов, медиана по всем парам (мин 501, макс 1 464) | 850 | 213 |
| экономия на вызов там, где P3 ограничивает меню | ≈ 741 |
Jev стоит US$ 0,0000727 за шаг (AT-237), поэтому экономия на определениях окупает его на чат-модели, вход которой стоит больше US$ 0,098 за миллион токенов (0,0000727 ÷ 741) — обе модели протокола, по прейскуранту. Подвох — тот же, что записан в «Экономии на определениях инструментов»: определения инструментов — начало кешированного префикса, меню, меняющееся от шага к шагу, ломает кеш, а DeepSeek отдал большую часть входа из кеша в AT-167. Переживёт ли экономия это и сколько дополнительный последовательный вызов стоит в задержке внутри цикла (p50 306 мс на шаг, измерено вне его), — ровно то, на что ответила бы живая таблица.
Живая таблица (2026-10-01, коммит 0eab0cb0e9)
Сгенерирована jev:vivo --relatorio по 40 исполнениям прогона протокола (все
датированы 2026-10-01, все на 0eab0cb0e9). В скобках — 95% интервал доли
«задача вышла».
| модель | Jev | исполнений | задача вышла | шаги (медиана · среднее) | шаги через tool_call_recovery | исполнение p50 · p95 | LLM (чат + Jev) p50 | стоимость/исполнение (чат + Jev) | из них Jev | стоимость на вышедшую задачу | Jev: применён · без ограничения · падение | повторы с полным каталогом | вне меню |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
deepseek/deepseek-v4.1-flash | вкл | 10 | 10/10 = 100% (72%–100%) | 5 · 6.5 | 0/65 | 4.8 s · 16.3 s | 4.4 s | US$ 0.0029 | US$ 0.0004 | US$ 0.0029 | 55/65 · 10 · 0 | 0 | 3 |
deepseek/deepseek-v4.1-flash | выкл | 10 | 10/10 = 100% (72%–100%) | 6 · 6.1 | 0/61 | 3.3 s · 25.4 s | 2.9 s | US$ 0.0013 | — | US$ 0.0013 | — | — | — |
anthropic/claude-haiku-4.5 | вкл | 10 | 10/10 = 100% (72%–100%) | 6 · 6.4 | 0/64 | 12.7 s · 20.2 s | 12.2 s | US$ 0.0194 | US$ 0.0004 | US$ 0.0194 | 54/64 · 10 · 0 | 0 | 4 |
anthropic/claude-haiku-4.5 | выкл | 10 | 9/10 = 90% (60%–98%) | 7 · 7.1 | 0/71 | 13.3 s · 21.6 s | 12.9 s | US$ 0.0283 | — | US$ 0.0314 | — | — | — |
Каждое исполнение закончилось принятым report_done; единственная задача,
которая не вышла (Haiku, выкл, T4-slug раунд 0), сообщила о завершении после
12 шагов с падающими скрытыми тестами. Ни одному шагу ни одной ветви не
понадобился tool_call_recovery, Jev ни разу не упал (ни таймаута, ни
HTTP-ошибки, ни недопустимого ответа на 129 вызовах), и ни одно ограниченное
меню не получило ответа без инструмента. 10 шагов «без ограничения» на ветвь —
это первый шаг каждого исполнения, где нет предыдущего инструмента.
Правило (comparar), по модели, включено против выключено:
| модель | качество: Δ «вышла» | стоимость на вышедшую задачу, вкл ÷ выкл | медианное исполнение, вкл ÷ выкл | проходит |
|---|---|---|---|---|
deepseek/deepseek-v4.1-flash | 0 пунктов (в пределах 10) | ×2.21 (выше 1) | ×1.45 (выше 1.2) | нет |
anthropic/claude-haiku-4.5 | 10 пунктов в пользу включённого (в пределах 10) | ×0.62 | ×0.95 | да |
Вердикт правила: ограничить — Haiku проходит все три условия, DeepSeek проваливает стоимость и задержку. Качество не провалилось ни на одной модели.
Почему две модели расходятся, по пошаговым записям того же вывода (токены промпта и кешированные токены, о которых сообщил провайдер):
| модель | Jev | токены промпта (все шаги) | из них из кеша | стоимость чата | стоимость Jev |
|---|---|---|---|---|---|
deepseek/deepseek-v4.1-flash | выкл | 131 983 | 111 104 (84%) | US$ 0.0132 | — |
deepseek/deepseek-v4.1-flash | вкл | 97 070 | 43 520 (45%) | US$ 0.0248 | US$ 0.0044 |
anthropic/claude-haiku-4.5 | выкл | 228 886 | 0 | US$ 0.2827 | — |
anthropic/claude-haiku-4.5 | вкл | 137 059 | 0 | US$ 0.1901 | US$ 0.0043 |
- Оговорка о кеше, записанная до исполнения, — то, что случилось с DeepSeek. Меню урезает промпт (−26% токенов), но меняется от шага к шагу, поэтому кешированный префикс ломается: доля из кеша упала с 84% до 45%, а счёт за чат вырос ×1,9 при меньшем числе отправленных токенов. Сверх того Jev (US$ 0,0004 на исполнение) — треть стоимости целого исполнения с выключенным маршрутизатором.
- На Haiku кеша не было ни в одной ветви (инструмент не отправляет
cache_control, и форма провода продукта, которую он переносит, тоже), поэтому −40% токенов промпта пошли прямо в счёт: −31% на исполнение, а Jev — 2% от него. - Задержка следует скорости чата, как и ожидала AT-239. Вызов Jev последовательный (p50 232 мс / p95 358 мс на вызов в исполнениях DeepSeek, 217 / 319 мс в исполнениях Haiku): в исполнении Haiku на 13 с он поглощается на шаг меньшим числом шагов; в исполнении DeepSeek на 3 с он добавляет 1,5 с к медиане.
- Качество: разница в одно исполнение на Haiku, интервалы пересекаются (60%–98% против 72%–100%) — нет свидетельств, что маршрутизатор помогает качеству, и нет — что вредит, на этих пяти маленьких задачах.
Рекомендация (измерено — решает владелец)
Ограничить, по правилу, записанному до исполнения: оставлять маршрутизатор включённым там, где у чат-модели нет действующего кеша промпта и она достаточно медленна, чтобы дополнительный последовательный вызов был мал (класс Haiku, US$ 1,00/5,00 за миллион), и выключенным там, где провайдер отдаёт большую часть входа из кеша, а чат быстр (класс DeepSeek). Числа, которые это оправдывают, — строки правила выше: Haiku ×0,62 по стоимости и ×0,95 по задержке без потери качества; DeepSeek ×2,21 по стоимости и ×1,45 по задержке без выигрыша в качестве.
Чего это не покрывает и чего потребовало бы ограничение:
- Измерены только dev agents (
ToolLoop). Прежнее прочтение — никаких свидетельств для семи разговорных агентов — остаётся в силе; ничто здесь на них не распространяется. - Разделение — по модели, а переключатель продукта — по workspace
(
PUT workspaces/:workspaceId/tool-router). Ограничения по модели (или по «действующему кешу») не существует; его создание — новое решение и новое изменение, здесь не сделанное. До тех пор измеренный выбор по workspace таков: включено, если его агенты работают на модели класса Haiku без кеша, выключено, если на модели класса DeepSeek с кешем. - Выборка и задачи малы (10 исполнений на ветвь, пять задач от 4 до 16 шагов). Отношения стоимости и задержки достаточно широки, чтобы их читать; разница качества в одно исполнение — нет.
- Эффект кеша зависит от кеша провайдера, а не только от скорости: workspace на Haiku с включённым кешем промпта, вероятно, сдвинулся бы к линии DeepSeek. Не измерено.
Предварительная рекомендация, которая была здесь до исполнения (ограничить: включено для dev agents, выключено для разговорных агентов), заменена вердиктом правила выше, как и говорил тот абзац.
Как воспроизвести
# the catalog, when a tool or an identity changed (inside the dev engine container)
docker cp scripts/jev/catalogo.exs brabo-dev-engine-1:/tmp/catalogo.exs
docker exec brabo-dev-engine-1 mix run --no-start --no-compile --no-deps-check /tmp/catalogo.exs \
> scripts/jev/catalogo.json
# count the steps and estimate the cost, without calling the network
pnpm --filter @brabo/scripts jev:replay -- --container brabo-dev-postgres-1 --estimar
# the run (resumes: a step already answered in the output is not asked again)
pnpm --filter @brabo/scripts jev:replay -- --container brabo-dev-postgres-1 \
--arquivo-de-chave ~/.config/brabo/openrouter-test.env --teto-usd 2
# the report again, and the billed cost per generation
pnpm --filter @brabo/scripts jev:replay -- --so-relatorio
pnpm --filter @brabo/scripts jev:replay -- --conferir-custo --arquivo-de-chave ~/.config/brabo/openrouter-test.env
Вывод ($XDG_CACHE_HOME/brabo/replay-jev/respostas.jsonl, иначе
~/.cache/…, права 600) несёт id сессий и остаётся вне checkout — скрипт
отвергает назначение внутри него. Печатаемый отчёт содержит лишь счётчики и
id агентов. Ключ читается из окружения (OPENROUTER_TEST_KEY) или из
указанного файла и никогда не печатается и не записывается.
У второго раунда собственная команда, которая читает замороженный снимок базы
(--dados-cache, вне checkout) и пишет файл на вариант:
# the snapshot, once (the dev database keeps moving; every later run reads this file)
pnpm --filter @brabo/scripts jev:analise -- --container brabo-dev-postgres-1 \
--dados-cache ~/.cache/brabo/replay-jev/dados.json --variante original --estimar
# one variant, tuning half first; validation only when the choice is made
pnpm --filter @brabo/scripts jev:analise -- --dados-cache ~/.cache/brabo/replay-jev/dados.json \
--variante escopo --metade tuning --arquivo-de-chave ~/.config/brabo/openrouter-test.env
pnpm --filter @brabo/scripts jev:analise -- --dados-cache ~/.cache/brabo/replay-jev/dados.json \
--variante escopo --metade validacao --arquivo-de-chave ~/.config/brabo/openrouter-test.env
# the tables of this section, and the wrong steps for reading by hand
pnpm --filter @brabo/scripts jev:analise -- --dados-cache ~/.cache/brabo/replay-jev/dados.json --relatorio --final escopo
pnpm --filter @brabo/scripts jev:analise -- --dados-cache ~/.cache/brabo/replay-jev/dados.json \
--variante escopo --metade validacao --dump-erros ~/.cache/brabo/replay-jev/v2/erros.txt
Снимок этого раунда снят с базы в том виде, в каком она была
2026-09-29 14:27 UTC, плюс события proposed_action.created и исходы
proposed_actions тех же сессий; у нового снимка больше шагов и другое
разделение. Потолок касается накопленного расхода каталога вывода
(--teto-usd, по умолчанию US$ 1,00), а не одного вызова.
Тест меню работает офлайн по этим ответам; сеть вызывают только 36 шагов, родившихся после снимка:
pnpm --filter @brabo/scripts jev:analise -- --dados-cache ~/.cache/brabo/replay-jev/dados-d.json \
--variante escopo --metade ambas --arquivo-de-chave ~/.config/brabo/openrouter-test.env
# --dados-base is the snapshot that fixed the split; what it did not know is "new"
pnpm --filter @brabo/scripts jev:menu -- --dados-cache ~/.cache/brabo/replay-jev/dados-d.json \
--dados-base ~/.cache/brabo/replay-jev/dados-c.json --sensibilidade
Живое сравнение AT-239 (нужен доступный openrouter.ai; папка вывода,
$XDG_CACHE_HOME/brabo/jev-vivo/ по умолчанию, остаётся вне checkout, и
скрипт отвергает папку внутри него):
# the dev tool definitions, when a dev tool changed (plain elixir, no compiled engine)
elixir scripts/jev/vivo/ferramentas-dev.exs > scripts/jev/vivo/ferramentas-dev.json
# the estimate, without the network
pnpm --filter @brabo/scripts jev:vivo -- --estimar \
--preco deepseek/deepseek-v4.1-flash=0.3/1.2 --preco anthropic/claude-haiku-4.5=1/5
# the run (resumes; stops before any call at the ceiling, and on an account or
# network failure without recording it), then the table and the verdict;
# behind a proxy, prefix NODE_USE_ENV_PROXY=1 (Node's fetch ignores HTTPS_PROXY)
pnpm --filter @brabo/scripts jev:vivo -- --rodadas 2 --teto-usd 4.5 \
--arquivo-de-chave ~/.config/brabo/openrouter-test.env
pnpm --filter @brabo/scripts jev:vivo -- --relatorio