Skip to content

fix(pipeline): bound embed batches by character count, not estimated tokens #241

Description

@smileygames

purpose

estimateEmbeddingTokens の推定が実測の半分以下になっており、#237 で入れた token 予算が機能していない。推定をやめ、文字数で上限を引く形に置き換える。

observation(2026-08-14 16:31 JST cron、#240 merge 後)

MAX_EMBEDDING_BATCH_TOKENS = 30000#237)が効いているはずの状況で、次の 2 commit が chunk offset 0 の単一 batch のまま天井を超えている。

commit file 数 batch 推定 実測
1fb0f6b397c1ff333d698fe58a862b9583b463fe 18 offset 0 のみ(分割されず) <= 30,000 60,678
bdf8b62bfc8cc88944d342ab0cefc1f544edfa5c 17 offset 0 のみ(分割されず) <= 30,000 64,413
3030: Max context reached 60678 tokens but model supports only 60000
3030: Max context reached 64413 tokens but model supports only 60000

過小評価の倍率は約 2.02x と約 2.15x。#237 が採った 2x margin では足りていない

この 2 値は #237 merge 前(14:31 の cron)の実測値と同一である。同じ commit が同じ実測 token 数を出し続けており、推定が 30,000 以下と判断して 1 batch に載せている。つまり #237 の token 予算はこの 2 commit に対して一度も効いていない。

44 file の 92eb94d#240 の file 予算(18/phase)により deferred となり、この cron では試行されていない。

原因

src/pipeline/embedding.tsASCII_CHARS_PER_TOKEN = 3

bge-m3 は XLM-RoBERTa SentencePiece 語彙を使い、英語散文では約 4 文字/token、句読点の多いソースでは約 3 文字/token という前提で 3 を採った。しかし diff patch の実測は約 1.4 文字/token である。+ / - 接頭、インデント、記号、短い識別子が並ぶため、SentencePiece が極端に短い token を大量に生成する。

prepareDiffEmbeddingInput が commit message を全 file 入力の先頭に複製する構造も分母を押し上げる(#236 で指摘済み、未修正)。

推定器の校正で追いかける限り、次に想定外の payload が来たときに同じ形で破れる。破れ方は「chunk 全体 failed → #178 不変条件により watermark 恒久停止」であり、コストが高い。

方針:推定をやめ、文字数で縛る

token は入力の 1 文字以上を消費する。 BPE / SentencePiece のいずれでも、1 token が 0 文字に対応することはない。したがって

1 batch の合計文字数 <= 60,000 - (special token 分の小さな定数)
  ==>  合計 token 数 <= 60,000  (無条件に成立)

校正が不要になる。payload の性質・言語・記号密度に依存しない。tokenizer が Worker 内で利用できないという制約とも整合する(そもそも推定しない)。

副作用の確認:

constraints

  • 推定関数(estimateEmbeddingTokens)を残さない。残せば校正の議論が別の場所に残る
  • WORKERS_AI_BATCH_CONTEXT_LIMIT = 60000 は天井の記録として維持する。文字数上限の根拠として参照する
  • 単独で上限を超える入力(8000 文字は 60,000 未満なので現状では発生しないが)は fix(pipeline): batch-embed diffs by token budget, not file count [pipeline, docs, tests] #237 と同じく 1 件の batch として送る。欠落させない、無限ループさせない
  • 入力の順序保持と、input 配列 / file 配列を同じ境界で切る契約は変更しない
  • MAX_EMBEDDING_INPUT_CHARS と新しい batch 上限の関係(batch 上限 / 入力上限 = 最小 file 数)を test で押さえる
  • 要件記述(docs/0-requirements.md / docs/0-requirements.ja.md)を同一 PR で更新する

target files

  • src/pipeline/embedding.tsestimateEmbeddingTokens / ASCII_CHARS_PER_TOKEN を退役、planEmbeddingBatches を文字数基準へ
  • src/pipeline/embedding.test.ts — 推定器のテストを退役、文字数上限の契約テストへ
  • src/pipeline/embed-diff.test.ts — 分割ケースの期待値更新
  • docs/0-requirements.md / docs/0-requirements.ja.md — Embedding Pipeline 節

acceptance

経緯

#236 で token 軸を、#238 で subrequest 軸を扱った。本 issue は #236 の修正が推定器の精度不足により機能していなかったことを扱う。#236 の受け入れ条件「watermark が boundary commit を越える」は本 issue の解決まで開いたままである。

Metadata

Metadata

Assignees

Labels

bug動いていない、壊れているready本文が実装開始できる形まで収束している状態。ただし更新は継続可能review-pending実装フェーズ完了。orchestration (brake eval / review / merge / close) 待ち

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions