Skip to Content
ДизайнTool Use SummaryСводка по дизайну сводок использования инструментов

Сводка по дизайну сводок использования инструментов

Обозначения быстрой модели для параллельных пакетов инструментов — мотивация, конкурентный анализ с Claude Code, архитектура и обоснование подхода только-добавление через Static, что привело к текущему рендеру в полном режиме.

Пользовательская документация: Сводки использования инструментов.

1. Краткое описание

После завершения каждого пакета инструментов Qwen Code запускает короткий вызов быстрой модели, который возвращает метку в стиле темы коммита git, описывающую пакет. Метка отображается в виде приглушенной строки ● <label> в полном режиме и заменяет общий заголовок Tool × N в компактном режиме. Генерация выполняется асинхронно и параллельно с потоком API следующего раунда, поэтому задержка около 1 с скрывается за стримингом основной модели.

ИзмерениеClaude CodeQwen Code
Точка срабатыванияquery.ts — после финализации пакета инструментовuseGeminiStream.tshandleCompletedTools — та же точка жизненного цикла
Модель генерацииHaiku через queryHaikuНастроенная fastModel через GeminiClient.generateContent
Поведение суб-агентов!toolUseContext.agentId — только основная сессияНеявно — суб-агенты работают через agents/runtime/, а не useGeminiStream
ПланированиеАсинхронный запуск, ожидание перед началом стриминга следующего раундаАсинхронный запуск, добавление в историю при разрешении
Формат выводаToolUseSummaryMessage, передаваемый в поток SDKHistoryItemToolUseSummary, добавляемый в историю UI + фабрика экспортируется для будущего использования в SDK
ВключениеПеременная окружения CLAUDE_CODE_EMIT_TOOL_USE_SUMMARIES, по умолчанию выклНастройка experimental.emitToolUseSummaries (по умолчанию вкл) + переменная окружения
Основной потребительМобильные клиенты / SDKКомпактный режим CLI + полный режим, будущий SDK
ПромптGit-коммит-тема, прошедшее время, самое характерное существительное (дословный перевод)Идентичный системный промпт
Обрезка входных данных300 символов на поле инструмента через truncateJsonИдентичная
Префикс намеренияПервые 200 символов последнего сообщения ассистентаИдентичный
Кэширование промптовenablePromptCaching: true на вызове HaikuПока не реализовано (доступен маршрут через forked-agent; отмечено как будущая оптимизация)
Постобработка меткиСырой текст моделиcleanSummary (удаляет markdown, кавычки, префиксы ошибок; ограничение 100 символов, защита от ReDoS)
Сохранение сессииТолько поток; каждая сессия генерирует зановоТолько история UI; ChatRecordingService не сохраняет записи tool_use_summary

2. Анализ реализации Claude Code

2.1 Поток

Claude Code выполняет цикл инструментов в query.ts. После выполнения пакета инструментов и нормализации результатов функция-генератор разветвляет вызов Haiku, сохраняет ожидающий промис в nextPendingToolUseSummary и продолжает выполнение вызова API следующего раунда. Задержка Haiku (~1 с) перекрывается стримингом основной модели (5–30 с), поэтому пользователь не замечает дополнительной задержки. Непосредственно перед отправкой контента следующего раунда генератор ожидает ожидающую сводку и передает сообщение tool_use_summary в поток.

tool_batch_complete → fork queryHaiku (асинхронный запуск) next_turn_stream_starts ← Promise сводки разрешается во время стриминга → await pendingToolUseSummary → yield ToolUseSummaryMessage продолжение со следующим раундом

2.2 Ключевые исходные файлы

КомпонентФайлКлючевая логика
Генераторservices/toolUseSummary/toolUseSummaryGenerator.ts:45-97generateToolUseSummary({ tools, signal, isNonInteractiveSession, lastAssistantText })
Триггерquery.ts:1411-1482Проверка через emitToolUseSummaries + не суб-агент; разветвление Haiku; сохранение promise
Ожидание+отправкаquery.ts:1055-1060Ожидание pendingToolUseSummary на границе следующего раунда, передача сообщения
Фабрика сообщенийutils/messages.ts:5105-5116createToolUseSummaryMessage(summary, precedingToolUseIds)
Флаг функцииquery/config.ts:23,36-38emitToolUseSummaries: isEnvTruthy(CLAUDE_CODE_EMIT_TOOL_USE_SUMMARIES)

2.3 Дизайнерские решения

  1. Всегда генерировать, когда флаг включен, независимо от компактного/детального состояния. Сводка — это артефакт уровня потока; UI решает, отображать ли ее.
  2. Отправлять как тип сообщения первого класса. tool_use_summary находится на одном уровне с user, assistant, tool_result в потоке SDK с полем precedingToolUseIds для связывания с пакетом.
  3. Суб-агенты исключены. !toolUseContext.agentId — вывод суб-агентов агрегируется выше; отдельные пакеты суб-агентов создавали бы зашумленные метки, которые никогда не появляются в основном UI.
  4. По умолчанию выключено. Флаг только через окружение гарантирует нулевую стоимость, пока потребитель SDK не решит использовать функцию. Сам терминал CC не отображает сообщение.
  5. Обрезка входных данных до 300 символов на поле. Покрывает основной риск стоимости — один большой результат инструмента, раздувающий промпт, при этом сохраняя достаточно сигнала для метки.

3. Реализация Qwen Code

3.1 Поток

Qwen Code подключается к той же точке жизненного цикла (useGeminiStream.handleCompletedTools), но отображает результат по обе стороны ui.compactMode, чтобы функция была полезна пользователям CLI без какой-либо инфраструктуры SDK.

tool_batch_complete (handleCompletedTools) config.getEmitToolUseSummaries()? fork generateToolUseSummary (асинхронный запуск) submitQuery() для следующего раунда (стриминг начинается) ← Promise сводки разрешается во время стриминга → addItem({type:'tool_use_summary', summary, precedingToolUseIds}) HistoryItemDisplay рендерит: compactMode=false → ● <label> отдельная строка compactMode=true → скрыт; MainContent смотрит и вставляет в заголовок CompactToolGroupDisplay

3.2 Ключевые исходные файлы

КомпонентФайлКлючевая логика
Сервисpackages/core/src/services/toolUseSummary.tsgenerateToolUseSummary, truncateJson, cleanSummary, фабрика сообщений
Флаг конфигурацииpackages/core/src/config/config.ts:getEmitToolUseSummariesПеременная окружения → настройки → по умолчанию (true)
Триггерpackages/cli/src/ui/hooks/useGeminiStream.ts:handleCompletedToolsЗапускает вызов быстрой модели, addItem при разрешении
Рендер полного режимаpackages/cli/src/ui/components/HistoryItemDisplay.tsxРендерит строку ● <label> при !compactMode
Поиск в компактномpackages/cli/src/ui/components/MainContent.tsxsummaryByCallId map → compactLabel prop для каждой tool_group
Заголовок компактногоpackages/cli/src/ui/components/messages/CompactToolGroupDisplay.tsxЗаменяет стандартный Tool × N на <Summary> · N tools при наличии метки
Обработка слиянияpackages/cli/src/ui/utils/mergeCompactToolGroups.tsОбрабатывает tool_use_summary как скрытый в компактном для смежности
Тип UIpackages/cli/src/ui/types.ts:HistoryItemToolUseSummary{ type: 'tool_use_summary', summary, precedingToolUseIds }

3.3 Ограничение <Static>: только добавление

Центральное архитектурное решение в этом PR: почему метка в полном режиме — это отдельный элемент истории, а не украшение для самой tool_group.

Qwen Code рендерит транскрипт через Ink <Static>. Static работает по принципу “только добавление”: как только элемент зафиксирован в буфере терминала, Ink не будет перерисовывать эту область, пока не будет вызван refreshStatic() для очистки и полного перерендера транскрипта. Это модель производительности, от которой зависит CLI — статические элементы не перерисовываются при каждом нажатии клавиши.

Теперь учтем время вызова быстрой модели:

T0 Пакет инструментов завершен, tool_group добавлена в историю T0+ε tool_group рендерится через <Static> и фиксируется в буфере T0+1с Вызов быстрой модели разрешается с меткой

В момент T0+1с мы не можем ретроспективно добавить метку к уже зафиксированной tool_group. Есть два варианта:

  1. Обновить свойства tool_group + вызвать refreshStatic(). Работает, но вызывает полную перерисовку транскрипта при каждом пакете — одна из самых дорогих операций UI в приложении. Видимая вспышка. Неприемлемо для декоративной метки.
  2. Отобразить сводку как новый элемент истории, добавленный после tool_group. Static обрабатывает это естественно — новые элементы добавляются чисто, без перерисовки.

Этот PR выбирает вариант 2 в полном режиме. Запись tool_use_summary — это реальный элемент истории, отображаемый HistoryItemDisplay как одна приглушенная строка ● <label>. Без refreshStatic.

Компактный режим отличается из-за mergeCompactToolGroups. Когда последовательные tool_groups объединяются, MainContent уже вызывает refreshStatic() — это существующий путь выполнения, и он перерисовывает объединенную группу с меткой, найденной в истории. Таким образом, компактный режим получает метку как замену заголовка. Чтобы избежать отображения метки дважды (один раз как заголовок компактного режима, второй раз как завершающая строка ● <label>), HistoryItemDisplay скрывает отдельную строку, когда compactMode равен true.

Полный режим Компактный режим (с объединением) ─────────── ────────────────────────────── [tool_group] [объединенная tool_group — заголовок заменен по lookup] ● <label> (строка ● <label> скрыта)

3.4 Семантика флага

Три уровня, разрешаемые в порядке приоритета:

  1. QWEN_CODE_EMIT_TOOL_USE_SUMMARIES=0|1|true|false — переменная окружения, наивысший приоритет.
  2. experimental.emitToolUseSummaries в settings.json — по умолчанию true.
  3. Неявный пропуск — если config.getFastModel() возвращает undefined, генерация пропускается независимо от флага. Без ошибки, без видимых изменений для пользователя.

3.5 Очистка вывода

cleanSummary запускается для каждого ответа модели перед добавлением в историю:

  1. Берется только первая строка (отбрасываются преамбулы рассуждений модели).
  2. Удаляются префиксы маркеров списка (-, *, ) — модели иногда возвращают метку как элемент списка.
  3. Удаляются окружающие кавычки/бэктики через ограниченный regex {1,10} (безопасно для CodeQL; ни одна реальная метка не содержит более нескольких обертывающих кавычек).
  4. Удаляются префиксы типа Label:, Summary:, Result:, Output:, которые некоторые модели добавляют.
  5. Отклоняются формы сообщений об ошибках (API error: ..., Error: ..., I cannot ..., I can't ..., Unable to ...) — возвращается пустая строка, элемент истории не добавляется.
  6. Жесткое ограничение длины до 100 символов (мобильный UI обрезает около 30; запас покрывает фразы на CJK).

3.6 Телеметрия

Вызов генерации сводки устанавливает promptId: 'tool_use_summary_generation', поэтому его использование токенов учитывается отдельно в /stats. Это позволяет пользователям видеть точную дополнительную стоимость функции, не смешивая ее с подсказками-предложениями или использованием основной сессии.

4. Отличия от Claude Code (и причины)

ОтличиеПричина
Слой настроек в дополнение к флагу окруженияQwen Code отображает метку в CLI; пользователям нужен постоянный переключатель, а не экспорт переменной для каждой оболочки.
По умолчанию вкл вместо выклМетка сразу видна пользователю в обоих режимах отображения; пользователи, настраивающие fastModel, уже подписываются на функции быстрой модели.
Специализированная постобработка cleanSummaryQwen Code поддерживает более разнообразных провайдеров, чем CC; некоторые модели добавляют Label: или оборачивают в кавычки. Нормализация на границе обеспечивает единообразие UI.
Хранит HistoryItemToolUseSummary вместо отправки потокового сообщенияРеализация в первую очередь для CLI; маршрут через поток SDK — это будущий PR. Фабрика ToolUseSummaryMessage уже экспортирована для этой работы.
Кэширование промптов пока не реализованоБыстрая модель часто совпадает с основной для пользователей, не настроивших отдельную. Добавление общего кэша требует маршрутизации через forkedAgent.ts; отслеживается как последующая задача.
Двойной путь рендера (строчный в полном режиме + заголовок в компактном)По умолчанию Qwen Code использует ui.compactMode: false; без строчного рендера в полном режиме функция была бы невидима для большинства пользователей.

5. Известные ограничения

  • Нет сохранения сессии. tool_use_summary не записывается в JSONL-файл записи чата. При возобновлении сессии метки теряются; группы инструментов отображаются с общим заголовком как запасной вариант. Низкий приоритет: метки естественным образом генерируются заново при продолжении сессии.
  • Пока нет отправки в поток SDK. Фабрика сообщений экспортирована, но CLI пока не передает tool_use_summary в мост SDK. Следующий PR.
  • Нет кэширования промптов. Каждый пакет приводит к затратам на новые входные токены. Незначительно в абсолютных величинах (~300 токенов), но измеримо, если запускать десятки пакетов за один раунд.
  • Сводка для объединенных компактных групп берет метку первого пакета. Если пользователь запускает десять различных пакетов подряд (тесный цикл, не типично), заголовок объединенного компактного режима будет показывать только намерение первого пакета. Компромисс принят: разворачивание меток по пакетам в объединенном представлении визуально более зашумлено, чем использование первой.
  • Требуется быстрая модель. Без настроенной fastModel генерация пропускается. Использование основной модели в качестве запасного варианта намеренно запрещено, чтобы сохранить контролируемую стоимость.

6. Будущие работы

  1. Подключить ToolUseSummaryMessage к мосту SDK, чтобы существующая фабрика использовалась на стороне потребителей.
  2. Направить генерацию через forkedAgent.ts с enablePromptCaching, чтобы повторяющиеся префиксы имен инструментов попадали в кэши провайдеров.
  3. Опционально: сохранять записи tool_use_summary в ChatRecordingService и восстанавливать при возобновлении сессии.
  4. Опционально: ярлыки меток по имени инструмента (например, всегда Read <filename> для одного вызова read_file) как быстрый путь до LLM.
Last updated on