Стабилизация, итерация 5: дисциплина данных — дедуп tool-outputs, токен-бюджет реплея (один PR, 6 коммитов) #490
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Один PR, коммиты по порядку. Жёстких зависимостей от других итераций нет. Координаты — develop@363f20ab. Контекст масштаба: сейчас каждый
onStepFinishпереписывает ВСЮ assistant-строку, причём каждый tool-output хранится ДВАЖДЫ (вmetadata.partsчерезassistantParts, service:1997, И вtool_callsчерезserializeSteps, :2267) → 50-шаговый ран с outputs по 50–200 KB = 127–510 МБ записи в Postgres за один ран (+WAL/TOAST, dead tuples) и 50–100 мс блокировок event loop на поздних шагах; вся история (кап 5000 СТРОК, не токенов) реплеится провайдеру каждый ход → длинный чат терминально умирает об контекст-окно (провайдерский 400 на каждом ходе, навсегда).Коммит 1 — perf(ai-chat): формат трейса tool_calls v2 — outputs только в parts
Новый формат элементов
tool_calls:{toolName, input}для вызова;{toolName, ok: true}для успеха;{toolName, error, kind: 'thrown' | 'interrupted'}для отказа.kindобязателен: синтетический «Tool call did not complete.» при Stop иначе неотличим от реального hard-fail и загрязняет error-rate (различие структурное — errorsById vs синтетическая ветка, НЕ per-tool классификация). Soft-failure маркеры в трейс НЕ выносятся (per-tool классификатор = ручное зеркало, его ошибка персистится навсегда; soft-анализ — запросами поmetadata.parts). Back-compat: старые строки НЕ мигрируются (перезапись гигантских jsonb = тот самый WAL-чарн); маркер эрыmetadata.toolTraceVersion: 2; диагностические запросы в docs/reading-ai-logs.md переписываются dual-shape с ветвлением по маркеру + правило «агрегаты/тренды не сравнивать через границу эр» (скачок метрик на неделе перехода — артефакт). UI action-log и markdown-экспорт читают только parts — не затронуты (регресс-пин).Коммит 2 — perf(ai-chat): кэш compactToolOutput по identity шага
compactToolOutputсейчас делает JSON.stringify каждого output на КАЖДОМ flush (:1868-1885). Мемоизация по identity шага — один stringify на шаг.Коммит 3 — feat(ai-chat): токен-бюджет реплея истории + реактивная ветка
metadata.contextTokensпоследнего хода (уже персистится, :1331-1337) против порога; chars-оценка — только для дельты новых сообщений, раскройки что резать, и фолбэк для чатов без usage.min(100k, 0.7 × chatContextWindow)при заданном окне; флэт 100k при незаданном (chatContextWindow— optional-поле: бюджетер, выключенный по умолчанию, не защищает ровно те инсталляции, что ловят терминальный overflow);0= явный off-switch; warn-лог при срабатывании дефолта.chars/2.5(кириллица; chars/4 занижает вдвое) — в shared-модуле packages/; клиентскийcount-stream-tokens.ts(chars/4) переводится на тот же модуль ТЕМ ЖЕ коммитом (два расходящихся оценщика = «бейдж показывает 60%, а бюджет уже режет»).Коммит 4 — perf(ai-chat): deferred-активация тулов в metadata чата
ai-chat.service.ts:1054: активированный set сбрасывается каждый ход → loadTools-round-trip заново. Хранить в metadata чата; при загрузке пересекать с актуальным validDeferredNames (смена allowlist/ролей — иначе prepareAgentStep получит имена несуществующих тулов).Коммит 5 — perf(ai-chat): snapshotOpenPage fast-path
ai-chat.service.ts:684-717: полный экспорт+upsert каждый ход. Fast-path поupdatedAtна запись (на чтении уже есть).Коммит 6 — perf(mcp): checkNewComments — параллелизм
packages/mcp/src/client.ts:3544: O(N) последовательных REST-вызовов по страницам working set. Параллелизм с капом 5–8 (серверный batch-эндпоинт «comments updated since T по space» — опционально, отдельным заходом).Тесты (наблюдаемые свойства, не моки): объём записи через дельту
pg_current_wal_lsn()на живой БД вокруг 50-шагового прогона, assert O(Σ шагов) — НЕ «байты через мок-репо» (мок меряет ровно то, что не болит); dual-shape запросы на фикстурах обеих эр; «окно не задано → бюджет применяется»; реактивная ветка с реальным 400-шейпом провайдера; parity-тест единого оценщика клиент/сервер.