ticket: compact retained split usage records

This commit is contained in:
Keisuke Hirata 2026-05-26 17:04:29 +09:00
parent 2cfc3b63c2
commit a2771180cc
2 changed files with 101 additions and 0 deletions

View File

@ -11,6 +11,7 @@
- Pod: scope 永続化 authority の整理 → [tickets/pod-scope-persistence-authority.md](tickets/pod-scope-persistence-authority.md)
- SpawnPod 初回 task delivery の受理確認 → [tickets/spawnpod-initial-run-confirmation.md](tickets/spawnpod-initial-run-confirmation.md)
- E2E テストハーネス(`tests/e2e/`、opt-in → [tickets/e2e-harness.md](tickets/e2e-harness.md)
- Compact: retained split が pruning 後 usage record に引きずられて巨大 tail を残す → [tickets/compact-retained-split-usage-records.md](tickets/compact-retained-split-usage-records.md)
- メモリ機構
- consolidation skip 表示と invalid staging の観測性 → [tickets/memory-consolidation-skip-observability.md](tickets/memory-consolidation-skip-observability.md)
- memory / knowledge tool 利用タイミングのプロンプトガイダンス → [tickets/memory-tool-guidance-prompt.md](tickets/memory-tool-guidance-prompt.md)

View File

@ -0,0 +1,100 @@
# Compact: retained split が pruning 後 usage record に引きずられて巨大 tail を残す
## 背景
2026-05-26、起動中の `insomnia` Pod で compact 後の継続 turn が実質失敗した。compact 自体は summary を生成して新 segment を作成しており、処理としては成功扱いだった。しかし post-compact `SegmentStart.history` が 1957 items / 約 3.7MB と巨大なまま残り、その後の新 segment 上の turn は assistant output / `llm_usage` を残さず `run_completed finished` になった。
関連する過去事例として `docs/report/2026-05-24-compact-retained-tail-oversize.md` がある。今回の調査では、前回疑っていた「retained split が request-time pruning / projection 後の usage record と persisted history の実サイズのズレに引きずられる」問題が、より具体的に再現した。
対象観測:
```text
session: 019e5d30-f7ad-7fb1-bdec-c592e888e290
old segment: 019e5d30-f7ad-7fb1-bdec-c5a41394e6b1
new segment: 019e62d3-5cbf-7020-b696-8d661b5e1026
```
pre-compact history は 2192 items / 約 4.5MB。compact 後の `SegmentStart.history` は 1957 items / 約 3.7MB だった。
## 原因の見立て
`llm_usage.input_total_tokens` は「その LLM request に実際に投げた prompt の占有量」としては正しいが、`split_for_retained` はそれを persisted/raw history の prefix token 数のように扱っている。
現在の retained split は概ね以下の前提で動く。
```text
current = tokens_at(history.len())
target = current - retained_tokens
先頭から tokens_at(idx) >= target になる最初の idx を cut にする
```
この探索は `tokens_at(idx)` が prefix 長に対して単調増加することを前提にしている。しかし実際の usage record は pruning / projection 後の request 実測であり、raw persisted history の累積 token 数ではないため、大きく上下する。
今回の直接原因になった周辺 record:
```text
history_len=237 input_total_tokens=93358
history_len=240 input_total_tokens=20532
history_len=242 input_total_tokens=166263 # target を超えて cut に選ばれた
history_len=245 input_total_tokens=27680
history_len=247 input_total_tokens=85797
```
compact 直前の推定は以下だった。
```text
current estimate: 173310 tokens
retained target: 8000 tokens
split target: 165310 tokens
initial cut: 242
balanced cut: 242
retained items: 1950
```
`balance_to_pair_boundary` による引き戻しは今回発生しておらず、問題は tool call/result boundary 保護ではない。最初から usage record の外れ値によって cut が早すぎる位置に決まっている。
## 要件
- Compact の retained split が request-time pruning / projection 後の `llm_usage.input_total_tokens` を raw persisted history prefix として誤用しない。
- `llm_usage.input_total_tokens` は request occupancy としては維持してよい。
- retained split 用の推定は、persisted/raw history の実サイズに近い基準で行う。
- 少なくとも usage record が非単調な場合に、古い外れ値で cut が極端に早くならない。
- Compact 成功後に post-compact context の検証を行う。
- retained item count
- retained byte size / serialized history size
- post-compact estimated tokens
- threshold / retained budget に対する比率
- post-compact history が明らかに過大な場合、単純な成功扱いにしない。
- `CompactFailed` 相当として扱う、または `just_compacted` を立てず次の safety net を有効にする。
- infinite compact loop / thrash を避けるため、失敗理由は明示する。
- compact 後の次 turn が assistant output / `llm_usage` 無しに `run_completed finished` になる経路を潰す。
- context length / request build / upstream error が発生した場合は `run_errored` または rollback として観測できる。
- 少なくとも空 finished turn を正常完了として扱わない。
- compact metrics を session log か metrics extension で追えるようにする。
- source: manual / pre_run / between_requests
- old_segment_id / new_segment_id
- old_history_len / new_history_len
- retained_from index
- retained_items / retained bytes
- estimate source / post-condition result
## 完了条件
- 非単調な usage record が存在しても、retained split が巨大 tail を残さないことを確認する test がある。
- pruning / projection 後 usage と raw persisted history size が乖離するケースの regression test がある。
- post-compact context 検証が実装され、過大 tail の成功扱いを防ぐ test がある。
- compact 後の空 turn が `run_completed finished` にならないことを確認する test がある。
- `cargo fmt --check` と関連 crate の test が通る。
## 範囲外
- ローカルトークナイザの導入。
- 既存 session log の migration。
- compact summary prompt の大幅な再設計。
- TUI 表示だけで問題を隠す対応。
## 参考
- `docs/report/2026-05-24-compact-retained-tail-oversize.md`
- `crates/pod/src/compact/token_counter.rs`
- `crates/llm-worker/src/token_counter.rs`