496 lines
18 KiB
Markdown
496 lines
18 KiB
Markdown
# Context Usage Audit
|
||
|
||
**Дата**: 2026-04-18
|
||
**Статус**: Research
|
||
**Goal**: проверить, как в проекте сейчас считается usage контекста, сверить это с official docs и с реальными логами, и зафиксировать, что нужно менять для понятного и точного UI
|
||
|
||
## Executive Summary
|
||
|
||
Главный вывод:
|
||
|
||
- ✅ Для **Anthropic prompt-side input** текущая базовая формула `input_tokens + cache_creation_input_tokens + cache_read_input_tokens` корректна.
|
||
- ❌ Для **"процент занятого контекста"** текущий UI смешивает несколько разных сущностей:
|
||
- total prompt input
|
||
- visible/debuggable context
|
||
- full context used in the turn
|
||
- guessed context window
|
||
- ❌ Кнопка открытия context panel на team screen сейчас показывает **не процент занятого контекста**, а смесь `visible context / total tokens`, при этом подписывает это как `of input`.
|
||
- ❌ Live lead context usage в team runtime **не учитывает `output_tokens`**, хотя Anthropic docs явно пишут, что input и output components count toward the context window.
|
||
- ⚠️ Для **Codex** текущие локальные session logs часто вообще не содержат usable input-side token telemetry: в `.jsonl` виден `output_tokens`, а `input_tokens/cache_*` остаются нулями. То есть "точный процент" для Codex из текущего источника правды пока получить нельзя.
|
||
- ⚠️ Для **Anthropic context window size** нельзя опираться только на `"[1m]"` suffix. По актуальным docs/релиз-ноутам окно зависит от конкретной модели: native `1M` уже есть у новых raw model ids вроде `claude-opus-4-7`, `claude-opus-4-6`, `claude-sonnet-4-6`, тогда как часть legacy путей остаётся на `200k` или временном beta-path.
|
||
|
||
## 1. Что сейчас считается в коде
|
||
|
||
### 1.1 Live lead context в team runtime
|
||
|
||
Источник:
|
||
|
||
- `src/main/services/team/TeamProvisioningService.ts`
|
||
|
||
Текущая формула:
|
||
|
||
```ts
|
||
currentTokens = input_tokens + cache_creation_input_tokens + cache_read_input_tokens
|
||
percent = currentTokens / contextWindow
|
||
```
|
||
|
||
Это значение эмитится как `lead-context`.
|
||
|
||
Что важно:
|
||
|
||
- это **total prompt input**
|
||
- это **не full context used for the completed turn**
|
||
- `output_tokens` сейчас исключены
|
||
|
||
### 1.2 Context button на экране команды
|
||
|
||
Источник:
|
||
|
||
- `src/renderer/components/team/TeamDetailView.tsx`
|
||
|
||
Текущее поведение:
|
||
|
||
- собирается `visibleContextTokens = sumContextInjectionTokens(allContextInjections)`
|
||
- затем считается `visibleContextPercentLabel = formatPercentOfTotal(visibleContextTokens, lastAiGroupTotalTokens)`
|
||
- при этом `lastAiGroupTotalTokens` сейчас = `input + cache_read + cache_creation + output`
|
||
- но helper `formatPercentOfTotal()` возвращает строку вида `"X% of input"`
|
||
|
||
Итог:
|
||
|
||
- знаменатель уже **не input**
|
||
- числитель это вообще **visible subset**
|
||
- label говорит **of input**
|
||
- кнопка выглядит как будто это **общий context usage**
|
||
|
||
То есть тут сразу 3 semantic mismatch.
|
||
|
||
### 1.3 Session Context Panel / Token popover
|
||
|
||
Источники:
|
||
|
||
- `src/renderer/components/chat/SessionContextPanel/components/SessionContextHeader.tsx`
|
||
- `src/renderer/components/common/TokenUsageDisplay.tsx`
|
||
|
||
Сейчас в проекте одновременно существуют 3 разных процента:
|
||
|
||
1. `visible_estimated / total_input`
|
||
2. `visible_estimated / (input + output + cache)`
|
||
3. `prompt_input / context_window`
|
||
|
||
Но в UI они местами называются почти одинаково.
|
||
|
||
## 2. Что говорят official docs
|
||
|
||
### 2.1 Anthropic: что такое `input_tokens` при caching
|
||
|
||
Official docs:
|
||
|
||
- [Anthropic prompt caching](https://docs.anthropic.com/ru/docs/build-with-claude/prompt-caching)
|
||
|
||
Ключевые факты:
|
||
|
||
- `input_tokens` - это только токены **после последней cache breakpoint**
|
||
- total prompt input считается как:
|
||
|
||
```text
|
||
total_input_tokens = cache_read_input_tokens + cache_creation_input_tokens + input_tokens
|
||
```
|
||
|
||
Источник:
|
||
|
||
- docs lines 491-500, 493-500, 495:
|
||
- `input_tokens` представляет только токены после последней точки разрыва кэша
|
||
- `total_input_tokens = cache_read_input_tokens + cache_creation_input_tokens + input_tokens`
|
||
|
||
Вывод:
|
||
|
||
- текущая базовая формула runtime для **Anthropic prompt input** правильная
|
||
- жалоба пользователя на "input percent" логична, потому что **`input_tokens` alone действительно не равен общему prompt input**
|
||
|
||
### 2.2 Anthropic: что вообще считается context window
|
||
|
||
Official docs:
|
||
|
||
- [Anthropic context windows](https://docs.anthropic.com/en/docs/build-with-claude/context-windows)
|
||
|
||
Ключевые факты:
|
||
|
||
- context window refers to all text model can reference, **including the response itself**
|
||
- при tool use docs прямо говорят:
|
||
- **all input and output components count toward the context window**
|
||
|
||
Источник:
|
||
|
||
- lines 194-197
|
||
- lines 215-220
|
||
- lines 255-262
|
||
|
||
Вывод:
|
||
|
||
- если UI обещает показать именно **"сколько контекста занято"**, то `output_tokens` игнорировать нельзя
|
||
- текущий live team formula under-reports occupied context for completed turn
|
||
|
||
### 2.3 Anthropic: thinking blocks
|
||
|
||
Official docs:
|
||
|
||
- [Anthropic context windows](https://docs.anthropic.com/en/docs/build-with-claude/context-windows)
|
||
|
||
Ключевой факт:
|
||
|
||
- previous thinking blocks are automatically stripped from future context
|
||
|
||
Источник:
|
||
|
||
- lines 225-239, especially 228 and 237
|
||
|
||
Вывод:
|
||
|
||
- есть важная разница между:
|
||
- **full context used during current turn**
|
||
- **context that will carry into future prompt**
|
||
- usage fields alone не дают perfectly exact "future carried context" без доп. нормализации thinking
|
||
|
||
### 2.4 Anthropic: какие модели сейчас имеют 1M context window
|
||
|
||
Official docs:
|
||
|
||
- [Anthropic models overview](https://platform.claude.com/docs/en/about-claude/models/overview)
|
||
- [Anthropic release notes](https://platform.claude.com/docs/en/release-notes/overview)
|
||
- [Anthropic context windows](https://platform.claude.com/docs/en/build-with-claude/context-windows)
|
||
|
||
Ключевые факты на дату проверки:
|
||
|
||
- current models overview показывает:
|
||
- `claude-opus-4-7` - `1M`
|
||
- `claude-sonnet-4-6` - `1M`
|
||
- `claude-haiku-4-5` - `200k`
|
||
- release notes отдельно фиксируют:
|
||
- с `2026-03-13` `1M` GA для `Claude Opus 4.6` и `Claude Sonnet 4.6`
|
||
- `2026-03-30` объявлен retirement beta-path для `Claude Sonnet 4.5` и `Claude Sonnet 4` на `2026-04-30`
|
||
- context windows page также указывает, что native long-context matrix уже не сводится к одному beta-header сценарию
|
||
|
||
Вывод:
|
||
|
||
- inference размера окна для Anthropic надо делать по **model matrix**, а не только по `"[1m]"` suffix
|
||
- internal app-alias `"[1m]"` всё ещё полезен как явный сигнал team UX, но для raw session model ids этого уже недостаточно
|
||
|
||
## 3. Что показывают реальные локальные логи
|
||
|
||
Проверены реальные `~/.claude/projects/*.jsonl`.
|
||
|
||
### 3.1 Claude / Anthropic
|
||
|
||
Типичный реальный кейс:
|
||
|
||
```json
|
||
"usage": {
|
||
"input_tokens": 3,
|
||
"cache_creation_input_tokens": 9284,
|
||
"cache_read_input_tokens": 63347,
|
||
"output_tokens": 8
|
||
}
|
||
```
|
||
|
||
Это значит:
|
||
|
||
- `input_tokens = 3` совсем не означает "в prompt было 3 токена"
|
||
- реальный total prompt input здесь:
|
||
|
||
```text
|
||
3 + 9284 + 63347 = 72634
|
||
```
|
||
|
||
То есть UI, который визуально намекает на "input %" без явного объяснения caching breakdown, будет выглядеть багованным даже если арифметика частично правильная.
|
||
|
||
### 3.2 Codex / OpenAI path в локальных session logs
|
||
|
||
Проверены реальные Codex entries в `~/.claude/projects/-Users-belief-dev-projects-claude-claude-team/**/*.jsonl`.
|
||
|
||
Типичный кейс:
|
||
|
||
```json
|
||
"usage": {
|
||
"input_tokens": 0,
|
||
"cache_creation_input_tokens": 0,
|
||
"cache_read_input_tokens": 0,
|
||
"output_tokens": 650
|
||
}
|
||
```
|
||
|
||
Повторяется много раз на `msg_codex_*`.
|
||
|
||
Вывод:
|
||
|
||
- текущий `.jsonl` source для Codex у нас часто не даёт usable prompt-side usage
|
||
- значит из **текущих session logs** нельзя честно строить accurate Codex context percent
|
||
- сначала нужен новый telemetry source или нормализация raw usage
|
||
|
||
## 4. Codex: что говорят official OpenAI docs
|
||
|
||
### 4.1 Context windows
|
||
|
||
Official docs:
|
||
|
||
- [GPT-5-Codex model](https://developers.openai.com/api/docs/models/gpt-5-codex)
|
||
- [codex-mini-latest model](https://developers.openai.com/api/docs/models/codex-mini-latest)
|
||
|
||
Ключевые факты на дату проверки:
|
||
|
||
- `GPT-5-Codex` - `400,000 context window`
|
||
- `codex-mini-latest` - `200,000 context window`
|
||
|
||
### 4.2 Cached prompt accounting
|
||
|
||
Official docs:
|
||
|
||
- [OpenAI prompt caching](https://developers.openai.com/api/docs/guides/prompt-caching)
|
||
|
||
Ключевой факт:
|
||
|
||
- usage exposes `prompt_tokens_details.cached_tokens`
|
||
|
||
Это означает:
|
||
|
||
- на уровне OpenAI API нужная prompt-side telemetry в принципе существует
|
||
- но наш текущий local session source её, похоже, не сохраняет/не нормализует
|
||
|
||
## 5. Конкретные проблемы в текущем проекте
|
||
|
||
### 5.1 Semantic mismatch: "visible context" vs "context used"
|
||
|
||
Сейчас рядом живут две разные сущности:
|
||
|
||
- **Visible Context** - то, что мы можем debug/reduce
|
||
- **Context Used** - сколько окна реально занято
|
||
|
||
Это не одно и то же.
|
||
|
||
Visible Context:
|
||
|
||
- это subset prompt-side content
|
||
- может сравниваться с total prompt input
|
||
|
||
Context Used:
|
||
|
||
- это usage against context window
|
||
- для Anthropic completed turn это ближе к `total_input + output`
|
||
|
||
### 5.2 Неправильный label на context button
|
||
|
||
Текущая button label на team screen:
|
||
|
||
- выглядит как общий context usage
|
||
- но фактически это visible subset percent
|
||
|
||
Это и есть один из главных user-facing bugs.
|
||
|
||
### 5.3 Inconsistent denominators
|
||
|
||
Сейчас по коду используются разные denominators:
|
||
|
||
- `totalInputTokens`
|
||
- `input + output + cache`
|
||
- `contextWindow`
|
||
|
||
Без явного переименования метрик UI всегда будет путать.
|
||
|
||
### 5.4 Early-run guessed context window
|
||
|
||
В `TeamProvisioningService` размер окна сначала может быть guessed:
|
||
|
||
- `200K` для `limitContext=true`
|
||
- иначе по model-specific matrix:
|
||
- internal Anthropic `"[1m]"` alias -> `1M`
|
||
- native long-context Anthropic raw ids (`claude-opus-4-7`, `claude-opus-4-6`, `claude-sonnet-4-6`) -> `1M`
|
||
- `GPT-5.4` / `GPT-5.4 pro` -> `1.05M`
|
||
- `codex-mini-latest` -> `200K`
|
||
- остальные текущие GPT-5/Codex team models -> `400K`
|
||
|
||
Потом он обновляется из `modelUsage.contextWindow`, если это поле пришло.
|
||
|
||
Значит:
|
||
|
||
- ранний live percent может быть временно неточным
|
||
|
||
### 5.5 Shared default drift
|
||
|
||
В shared utils есть:
|
||
|
||
```ts
|
||
DEFAULT_CONTEXT_WINDOW = 200_000
|
||
```
|
||
|
||
Но team Anthropic UX по умолчанию исходит из `1M`.
|
||
|
||
Это не обязательно immediate arithmetic bug, но это source of drift для разных экранов и helper'ов.
|
||
|
||
## 6. Рекомендованная metric model
|
||
|
||
Если делать UI понятным и точным, нужно разделить **минимум 3 разные метрики**.
|
||
|
||
### 6.1 Prompt Input Used
|
||
|
||
Для Anthropic:
|
||
|
||
```text
|
||
prompt_input_used =
|
||
input_tokens +
|
||
cache_creation_input_tokens +
|
||
cache_read_input_tokens
|
||
```
|
||
|
||
Назначение:
|
||
|
||
- честный size текущего prompt
|
||
- хорошая база для Visible Context %
|
||
|
||
### 6.2 Context Window Used
|
||
|
||
Для Anthropic completed turn:
|
||
|
||
```text
|
||
context_window_used_approx =
|
||
prompt_input_used +
|
||
output_tokens
|
||
```
|
||
|
||
Почему `approx`:
|
||
|
||
- previous thinking blocks auto-strip from future turns
|
||
- exact future carried context нельзя получить из raw usage perfectly
|
||
|
||
Но если UI обещает "занятое окно прямо сейчас/на этом ходе", эта формула ближе к docs, чем текущая.
|
||
|
||
### 6.3 Visible Context Share
|
||
|
||
```text
|
||
visible_context_share = visible_context_estimated / prompt_input_used
|
||
```
|
||
|
||
Назначение:
|
||
|
||
- debug metric
|
||
- объясняет, какая часть prompt-а понятна и управляемая пользователю
|
||
|
||
Это **не** percent occupied context window.
|
||
|
||
## 7. Рекомендованный UI language
|
||
|
||
Вместо одного размыто слова `Context` лучше использовать разные подписи:
|
||
|
||
- `Context Used` - percent of context window
|
||
- `Prompt Input` - current prompt-side tokens
|
||
- `Visible Context` - debuggable subset of prompt
|
||
|
||
Тогда пользователь сразу видит:
|
||
|
||
- сколько занято всего
|
||
- сколько из этого prompt
|
||
- сколько из prompt мы реально понимаем по breakdown
|
||
|
||
## 8. Top 3 implementation options
|
||
|
||
### 1. Развести 3 разные метрики и переименовать UI честно
|
||
|
||
`🎯 10 🛡️ 9 🧠 7`
|
||
Примерно `180-260` строк изменений
|
||
|
||
Что сделать:
|
||
|
||
- team button показывает только `Context Used`
|
||
- panel header отдельно показывает:
|
||
- `Visible Context`
|
||
- `Prompt Input`
|
||
- `Context Window Used`
|
||
- `Visible Context` всегда считается только как доля prompt input
|
||
|
||
Плюсы:
|
||
|
||
- минимальный semantic debt
|
||
- почти все пользовательские жалобы закрываются сразу
|
||
- легче потом добавить Codex
|
||
|
||
Минусы:
|
||
|
||
- надо аккуратно переподписать UI в нескольких местах
|
||
|
||
### 2. Оставить один главный процент, но считать его по docs как `prompt + output`
|
||
|
||
`🎯 8 🛡️ 8 🧠 6`
|
||
Примерно `120-180` строк изменений
|
||
|
||
Что сделать:
|
||
|
||
- live team percent = `(input + cache_read + cache_creation + output) / contextWindow`
|
||
- `Visible Context` оставить только внутри sidebar/panel
|
||
|
||
Плюсы:
|
||
|
||
- очень понятная одна главная цифра
|
||
- максимально близко к official Anthropic context-window semantics
|
||
|
||
Минусы:
|
||
|
||
- future carried context всё равно не perfectly exact из-за thinking blocks
|
||
- нужен fallback wording, когда usage incomplete
|
||
|
||
### 3. Минимальный fix только label-ов и знаменателей
|
||
|
||
`🎯 6 🛡️ 6 🧠 3`
|
||
Примерно `40-90` строк изменений
|
||
|
||
Что сделать:
|
||
|
||
- перестать писать `of input`, если denominator не input
|
||
- button переименовать в `Visible`
|
||
- panel header явно разделить `Visible` и `Total`
|
||
|
||
Плюсы:
|
||
|
||
- быстро
|
||
- дешево
|
||
|
||
Минусы:
|
||
|
||
- не решает core semantic debt
|
||
- live lead percent всё ещё останется under-reported
|
||
|
||
## 9. Recommended next step
|
||
|
||
Рекомендую идти по **варианту 1**.
|
||
|
||
Почему:
|
||
|
||
- он закрывает и math, и naming, и UX confusion
|
||
- он не завязан только на Anthropic
|
||
- он даёт clean foundation для будущего Codex support
|
||
|
||
### Practical plan
|
||
|
||
1. Вынести явные type/terms для 3 метрик:
|
||
- `promptInputTokens`
|
||
- `contextWindowUsedTokens`
|
||
- `visibleContextTokens`
|
||
2. Исправить live Anthropic runtime formula и wording.
|
||
3. Перестать использовать label `of input` там, где denominator не `prompt input`.
|
||
4. Для Codex временно показывать:
|
||
- window size, если модель известна
|
||
- `context usage unavailable` или `output only`
|
||
- пока не появится raw prompt telemetry
|
||
|
||
## 10. Bottom line
|
||
|
||
Главная проблема сейчас не в одной строчке арифметики, а в том, что проект смешал:
|
||
|
||
- **prompt input**
|
||
- **visible debuggable context**
|
||
- **full context window usage**
|
||
|
||
В Anthropic path базовая input formula уже в целом нормальная, но UI поверх неё даёт неправильный смысл.
|
||
|
||
В Codex path проблема глубже:
|
||
|
||
- official API supports cached prompt accounting
|
||
- но наш текущий local session telemetry этого не доносит
|
||
- поэтому "точный % занятого контекста" для Codex пока нельзя обещать без нового data source
|