Skip to content

fix(pipeline): bound embed batches by character count, not estimated tokens [pipeline, poller, docs, tests] - #242

Merged
smileygames merged 4 commits into
mainfrom
241-fixpipeline-bound-embed-batches-by-character-count-not-estimated-tokens
Aug 14, 2026
Merged

fix(pipeline): bound embed batches by character count, not estimated tokens [pipeline, poller, docs, tests]#242
smileygames merged 4 commits into
mainfrom
241-fixpipeline-bound-embed-batches-by-character-count-not-estimated-tokens

Conversation

@smileygames

Copy link
Copy Markdown
Member

Closes #241

何を変えたか

embed batch の切れ目を、推定 token 数から文字数に置き換えた。

before (#237) after
予算 MAX_EMBEDDING_BATCH_TOKENS = 30000 MAX_EMBEDDING_BATCH_CHARS = 60000
課金 estimateEmbeddingTokens(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 のまま天井を超えている。

commit file 数 推定 実測
1fb0f6b 18 <= 30,000 60,678
bdf8b62 17 <= 30,000 64,413

#178 の不変条件により、vector が載らなかった commit で diff watermark が留まる。つまり毎 cron 同じ commit が同じ形で落ち続け、neuron-graph-rag の forward watermark はここで停止したままだった。

推定器を校正し直しても、次に想定外の payload が来たときに同じ形で破れる。破れ方が「chunk 全損 → watermark 恒久停止」である以上、コストが精度に見合わない。

上界であって校正ではない

BPE / SentencePiece のいずれでも、1 token は入力の 1 文字以上に対応する。したがって各 input について

tokens(input) <= input.length + SPECIAL_TOKENS_PER_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 に対する確認

  • 1 batch の合計文字数が上限を超えないplanEmbeddingBatches の契約 test と、embed-diff 側の全 call に対する assert
  • 上記 2 commit が embed に成功する — 18 file / 単一 call では天井超え / 退役した推定では 30,000 token 予算に収まる、という payload の回帰 test を追加。2 batch に分割され 18 file 全てが upsert されることを確認。実 cron での確認は merge・deploy 後
  • 既存テストが通る — 316 tests pass, tsc --noEmit clean
  • fix(poller): cap diff-path files per run against the subrequest budget [poller, pipeline, docs, tests] #240 の file 予算が変更されていないDIFF_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

変更していないもの

  • 入力の順序保持、input 配列と file 配列を同じ境界で切る契約
  • 単独で予算を超える input を単独 batch として送る扱い(欠落させない / 無限ループさせない)
  • prepareDiffEmbeddingInput が commit message を全 file 入力の先頭に複製する構造(fix(pipeline): batch-embed diffs by token budget, not input count #236 で指摘済み、本 PR の scope 外)

影響範囲

pipeline, poller (comment only), docs, tests

…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
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 14, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
github-rag-mcp c34887a Aug 14 2026, 10:40 AM

@smileygames smileygames self-assigned this Aug 14, 2026
Claude Lin & Lay added 3 commits August 14, 2026 19:37
…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
…udget PR [tests]

`embedding.test.ts` の comment が「校正で失敗した状態 (#237, #240)」と 2 件を
並べていたが、#240 は diff 経路の file 予算であって校正ではない。校正だったのは
#237 だけで、その失敗が 2 度観測された(#240 merge の前と後)という関係である。

2 件を並べると、#240 も同じ軸の失敗だったと読める。軸が違うものを同列に置くと、
次にこの assert を触る人が「file 予算も校正だった」という誤った前提を引き継ぐ。

comment のみ。挙動の変更なし。

#241

@smileygames smileygames left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 入力以上を保持する。#240DIFF_SUBREQUESTS_PER_FILE = 3 は「1 batch が 3 file 以上」を前提に amortised cost を見積もっていたので、良い側へ振れる。file 予算は変更されておらず、定数もそのまま。整合している。

回帰テスト

fixture が失敗領域に留まることを二重にassert している点を評価する。

  1. 1 call としては天井を超える
  2. 退役した推定器なら 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 の最後の受け入れ条件でもある。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant