Skip to content

fix(pipeline): bound embed batches by count times longest input, not by total #246

Description

@smileygames

purpose

Workers AI の batch embed が拒否する量は、入力の合計ではなく batch 件数 × batch 内の最長入力 である可能性が高い。#236 / #241 / #244 は 3 回とも合計を縛っており、縛る対象そのものが違っていた。境界の形を変える。

observation

0411f101#244 の UTF-8 バイト版)が 8 時間前から 100% traffic で稼働中。それでも 2026-08-15 09:30 JST の cron で同じ失敗が出る。

Failed to batch-embed diffs for Liplus-Project/neuron-graph-rag@1fb0f6b397c1ff333d698fe58a862b9583b463fe
  chunk offset 0: 3030: Max context reached 60678 tokens but model supports only 60000

18 file が chunk offset 0 の 1 batch。そして 60,678 という報告値が #242 の前から一度も変わっていない。推定トークン軸・文字数軸・バイト数軸の 3 つを通しても同一である以上、batch の組み方が変わっていないことになる。

報告値を件数で割ると全て整数になる

commit batch 件数 報告値
1fb0f6b397c1ff333d698fe58a862b9583b463fe 18 60,678 3,371
bdf8b62bfc8cc88944d342ab0cefc1f544edfa5c 17 64,413 3,789
92eb94dcb88c5e7494bc3b69377249e08601d99f 20 85,920 4,296
92eb94dcb88c5e7494bc3b69377249e08601d99f 16 68,736 4,296

検算: 18 × 3,371 = 60,678 / 17 × 3,789 = 64,413 / 20 × 4,296 = 85,920 / 16 × 4,296 = 68,736

決定的なのは最後の 2 行。同一 commit を異なる件数で投げて、1 件あたりの商が 4,296 で一致している。 報告値が実トークンの合計であれば、4 件減らして商が変わらないためには除外された 4 件が全て同一トークン数である必要があり、偶然としては苦しい。

推論

エンドポイントは batch 内の入力を最長のものに padding し、消費コンテキストを 件数 × padding 後の長さ として数えていると考えられる。

charged = batch_size × max_over_inputs(tokens(input))

これが正しければ、これまでの 3 回が効かなかった理由が全て説明される。合計をどれだけ正確に縛っても、課金式に合計は現れないため。

確からしさ: 4 観測からの推論であり、確定ではない。確認方法は下記。

確認方法

同一内容の入力を件数だけ変えて投げ、報告値が件数に比例するかを見る。比例すれば padding モデルが確定する。小さな入力 1 件と大きな入力 1 件を混ぜた batch が、大きい方の 2 倍で課金されるかを見るのが最短。

正しい境界の形

batch_size × max_over_inputs(utf8ByteLength(input) + TOKEN_OVERHEAD_PER_INPUT) <= WORKERS_AI_BATCH_CONTEXT_LIMIT

トークン数の上界としてバイト数を使う点(#244)は維持する。破れていたのは上界そのものではなく、合計を縛っていたことである。

1fb0f6b で検算すると、最長入力が約 3,371 トークン相当なので 1 batch は 17 件まで。18 件で超えていた。

設計上の帰結(合計軸には無かった性質)

  • 最大の入力が batch 全体の単価を決める。 小さい入力を並べても安くならない。大きい入力が 1 件混ざれば、全件がその長さで課金される
  • したがってサイズの近いものをまとめるほど効率が上がる。極端に大きい 1 件は単独 batch にするのが最適
  • ただし現在の planEmbeddingBatches は連続範囲 {start, end} を返し、呼び出し側が files 配列を同じ境界で切ることで位置対応を保っている。サイズ順にまとめるには入力の並べ替えが必要で、この interface では表現できない。範囲ではなく index 集合を返す形への変更が要るかどうかを判断すること
  • 並べ替えを行わない素朴な実装でも上記の境界式は満たせる。効率と実装量の判断は実装側に委ねる

constraints

target files

  • src/pipeline/embedding.ts — 境界式、必要なら planner の返り値形式
  • src/pipeline/embed-diff.ts — 並べ替えを行う場合の呼び出し側
  • src/pipeline/embedding.test.ts / src/pipeline/embed-diff.test.ts
  • src/poller.tsDIFF_SUBREQUESTS_PER_FILE の前提が動く場合
  • docs/0-requirements.md / docs/0-requirements.ja.md

acceptance

経緯

#236(推定トークン合計)、#241#242(文字数合計)、#244(バイト数合計)に続く 4 度目の修正。前 3 回は「上界の取り方が実測より楽観」という同じ形の失敗だったが、本 issue はそれとは別で、縛る量の選択そのものが誤っていたという診断である。合計を縛る限り、上界をいくら厳しくしても課金式に届かない。

#236 の受け入れ条件は一度も満たされていない。

Metadata

Metadata

Assignees

Labels

bug動いていない、壊れているready本文が実装開始できる形まで収束している状態。ただし更新は継続可能

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions