fix(pipeline): bound embed batches by character count, not estimated tokens [pipeline, poller, docs, tests] - #242
Conversation
…tokens [pipeline, poller, docs, tests] batch の切れ目を `estimateEmbeddingTokens` による推定から文字数へ移す。 #237 の推定器は ASCII を 3 文字/token として読むが、bge-m3 が diff patch を 割る密度は約 1.4 文字/token である。`+` / `-` 接頭、インデント、記号、短い 識別子がいずれも細かく分割されるため、推定は約 2.1x 楽観に振れていた。 #240 merge 後の 16:31 JST cron でも、18 file / 17 file の 2 commit が 「30000 token 予算に収まる」と判定されて単一 batch のまま送られ、実測 60678 / 64413 token で天井 60000 を超えて chunk 全体が失敗している。 #178 の不変条件により、vector が載らなかった commit で diff watermark が 留まるので、同じ commit が毎 cron 同じ形で落ち続けていた。 置き換えの根拠は上界であって校正ではない。BPE / SentencePiece のいずれでも 1 token は入力の 1 文字以上に対応するので、各 input に「文字数 + special token 2 個」を課金すれば、batch が天井の内側に収まることが無条件に成立する。 payload の言語にも記号密度にも依存しないため、次に想定外の payload が来ても 同じ破れ方をしない。special token を batch 固定の引当てではなく input ごとに 課金するのは、batch の input 件数に上限が無く、極小 input が多数並ぶ場合に 固定引当てでは足りなくなるためである。 - `MAX_EMBEDDING_BATCH_TOKENS` / `estimateEmbeddingTokens` / `ASCII_CHARS_PER_TOKEN` を退役。`MAX_EMBEDDING_BATCH_CHARS` (= `WORKERS_AI_BATCH_CONTEXT_LIMIT`)と `SPECIAL_TOKENS_PER_INPUT` を導入 - `WORKERS_AI_BATCH_CONTEXT_LIMIT` は天井の記録として維持し、文字数予算の 根拠として参照する。天井の下に margin は取らない — 構成上成立する上界に margin が買えるものは残っていない - truncate が 1 input を 8000 文字に抑えるので 1 batch は最低 7 file を保持 する。#240 の `DIFF_SUBREQUESTS_PER_FILE = 3` が立っていた床(3 file)より 良い側なので、file 予算は変更していない。derivation の comment のみ更新 - 実測 payload 相当(18 file、単一 call では天井超え、かつ退役した推定では 30000 token 予算に収まる)の回帰 test を追加。fixture が再現領域から 外れないよう両側を assert している - `MAX_EMBEDDING_BATCH_CHARS` / `MAX_EMBEDDING_INPUT_CHARS` の比が最小 file 数であることを test で押さえた - 入力の順序保持、input 配列と file 配列を同じ境界で切る契約、単独で予算を 超える input を単独 batch として送る扱いは変更していない #241
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
github-rag-mcp | c34887a | Aug 14 2026, 10:40 AM |
…on divides [tests] 回帰 test の第 2 assertion は、退役した推定器の値そのものではなく `callCharge`(文字数 + special token)を 3 で割っている。両者は special token の分だけずれ、割られる側が大きい = 失敗しやすい側に寄るため安全側だが、 comment がその差を書いていなかった。fixture が再現領域に留まっていることを 将来 assert し直す人が、どちらの数値を見ているのか判断できるようにする。 comment のみ。挙動の変更なし。 #241
… comment [pipeline] `embed-diff.ts` の comment が「同じ 2 commit が 3 cron cycle 続けて天井を 超えた」と書いていたが、#241 が観測として挙げているのは 14:31(#237 merge 前)と 16:31(#240 merge 後)の 2 点であり、3 回という回数は裏づけが無い。 回数は本来の論点でもない。効いているのは「#237 の推定 token 予算が入る前と 後で実測値が同一だった = その予算はこの 2 commit を一度も縛っていなかった」 という点なので、そちらを書く形に直す。 comment のみ。挙動の変更なし。 #241
smileygames
left a comment
There was a problem hiding this comment.
AI self-review (auto mode, parent)
境界の健全性
中心となる主張を追った。
tokens(input) <= input.length + SPECIAL_TOKENS_PER_INPUT
BPE / SentencePiece のいずれでも 1 token は入力の 1 文字以上を占めるため、content token は文字数を超えない。special token は入力あたり <s> / </s> の 2 個。したがって batch 合計は
Σ tokens(input_i) <= Σ (len_i + 2) = charged total <= 60,000
無条件に成立する。校正なし、payload の言語・記号密度に非依存。issue の狙いどおりの形になっている。
UTF-16 code unit で数えている点も安全側。surrogate pair は 2 文字と数えられるが tokenizer は 1 token にすることがあるため、過大に課金される方向。
special token を入力ごとに課金した判断
issue は「60,000 −(special token 分の小さな定数)」と書いたが、実装は batch 一律の予約ではなく入力ごとに 2 を課金している。この変更を支持する。
batch の入力数はどこにも上限が無い。一律予約にすると、微小な入力が N 個並んだとき N×2 の special token が固定予約を超え、issue の rationale が主張する「無条件の保証」が成立しなくなる。入力ごとの課金なら不等式が入力単位で閉じるので、batch 合計も構成上収まる。1 文字入力 60 個を予算 60 に対して分割するテストが、まさにこのケースを押さえている。
起票側の記述が甘く、実装側が正した形。
予算が天井と等値である点
MAX_EMBEDDING_BATCH_CHARS = WORKERS_AI_BATCH_CONTEXT_LIMIT(60,000)で、天井の下にマージンを置いていない。根拠は拒否メッセージが厳密に超過した値のみを挙げていること(60678 tokens but model supports only 60000)。エラー文字列からの推論であり、60,000 が最初の拒否値である可能性は文字列上は排除しきれない。
それでも受け入れる。課金が実測を支配しているため、charge が 60,000 に達しても実 token 数はそれを大きく下回る。等号が binding になるのは全 token が厳密に 1 文字を占める場合のみで、diff patch の実測 1.4 文字/token ではおよそ 43,000 token 相当にしかならない。マージンは明示されていないが、支配関係の中に含まれている。
#240 との整合
MAX_EMBEDDING_BATCH_CHARS / MAX_EMBEDDING_INPUT_CHARS = 60,000 / 8,002 = 7.49 より、1 batch は 7 入力以上を保持する。#240 の DIFF_SUBREQUESTS_PER_FILE = 3 は「1 batch が 3 file 以上」を前提に amortised cost を見積もっていたので、良い側へ振れる。file 予算は変更されておらず、定数もそのまま。整合している。
回帰テスト
fixture が失敗領域に留まることを二重にassert している点を評価する。
- 1 call としては天井を超える
- 退役した推定器なら 30,000 予算を通していた(約 85,200 文字 → 比率 3 で約 28,400 token)
片方だけなら、後日 patch サイズを触ったときに fixture が静かに失敗領域から出て、テストが何も検証しなくなる。両方あることでそれが防がれている。issue の受け入れ条件にはここまで書いていなかった。
受け入れ条件
| criterion | 結果 |
|---|---|
| 1 batch の合計文字数が上限を超えない | pass — 構成上保証、テストで assert |
1fb0f6b / bdf8b62 が embed に成功する |
post-merge 観測 |
forward watermark が 1fb0f6b を越える |
post-merge 観測 |
| 既存テスト + 実測相当の回帰テスト | pass — 316 tests green、18 file / 約 85,200 文字の fixture |
| #240 の file 予算が変更されていない | pass — 定数不変、コメントのみ更新 |
scope 逸脱
commit 2-4 はコメントのみの精度修正(未検証の「three cron cycles」記述、assertion と一致しない数値、#240 を校正軸に置いていた issue 参照)。挙動変更なし。自己レビューによる修正であり、受け入れる。
next step
auto mode のため human check なし。self-review pass をもって AI が直接 merge する。
merge 後、次の DIFFS_CRON(毎時 30 分)で観測する。判定は具体的で、1fb0f6b(18 file)と bdf8b62(17 file)が embed に成功し、forward watermark が 2026-08-11T15:33:02Z から前進すること。これは #236 の最後の受け入れ条件でもある。
Closes #241
何を変えたか
embed batch の切れ目を、推定 token 数から文字数に置き換えた。
MAX_EMBEDDING_BATCH_TOKENS = 30000MAX_EMBEDDING_BATCH_CHARS = 60000estimateEmbeddingTokens(text)(ASCII 3 文字/token, 非 ASCII 1 文字/token)text.length + SPECIAL_TOKENS_PER_INPUTなぜ
ASCII_CHARS_PER_TOKEN = 3は英語散文の比率であって、diff patch の比率ではない。+/-接頭、インデント、記号、短い識別子はいずれも SentencePiece で細かく分割されるため、実測は約 1.4 文字/token であり、推定は約 2.1x 楽観に振れていた。その結果、#240 merge 後の 16:31 JST cron でも次の 2 commit が単一 batch のまま天井を超えている。
1fb0f6bbdf8b62#178 の不変条件により、vector が載らなかった commit で diff watermark が留まる。つまり毎 cron 同じ commit が同じ形で落ち続け、
neuron-graph-ragの forward watermark はここで停止したままだった。推定器を校正し直しても、次に想定外の payload が来たときに同じ形で破れる。破れ方が「chunk 全損 → watermark 恒久停止」である以上、コストが精度に見合わない。
上界であって校正ではない
BPE / SentencePiece のいずれでも、1 token は入力の 1 文字以上に対応する。したがって各 input について
が常に成立し、この課金の合計が 60,000 以内なら batch の token 数も 60,000 以内である。payload の言語にも記号密度にも依存しない。tokenizer が Worker 内で使えないという制約とも整合する(そもそも推定しない)。
SPECIAL_TOKENS_PER_INPUT(<s>…</s>の 2 個)を batch 固定の引当てではなく input ごとに課金している。batch の input 件数には上限が無く、極小 input が多数並ぶ場合に固定引当てでは足りなくなるため。issue 本文が想定していた「特殊 token 分の小さな定数」を input 軸に置き直した形で、こうしないと「無条件に成立」が成立しない。天井の下に margin は取っていない。構成上成立する上界に、margin が買えるものは残っていない。逆に margin は無料ではなく、batch が 1 つ増えるごとに subrequest を 2 つ消費する。
天井ちょうどを許容している根拠は、拒否応答が名指しする値が常に天井より上であること(
Max context reached 60678 tokens but model supports only 60000)。60,000 は supported 側である。acceptance に対する確認
planEmbeddingBatchesの契約 test と、embed-diff側の全 call に対する asserttsc --noEmitcleanDIFF_SUBREQUESTS_PER_FILE = 3は据え置き。derivation の根拠だけ更新した(1 batch の最低保持 file 数が 3 から 7 に上がり、amortised cost は 2.67 → 2.29 と良い側に振れるため、3 は引き続き安全側)MAX_EMBEDDING_INPUT_CHARSと batch 上限の関係を test で押さえる — 比が最小 file 数(>= 7)であることを算術と planner 経由の両方で assert変更していないもの
prepareDiffEmbeddingInputが commit message を全 file 入力の先頭に複製する構造(fix(pipeline): batch-embed diffs by token budget, not input count #236 で指摘済み、本 PR の scope 外)影響範囲
pipeline, poller (comment only), docs, tests