共通Stateの再利用

September 19, 2026 · View on GitHub

公式互換APIの同一リクエスト内で、各質問に共通する入力prefixを一度だけ計算します。各質問の完全なチャット入力を先にトークン化し、実際に一致するトークン列を共有するため、文字列連結によるトークン境界の変化はありません。State以外の共通システム指示も対象です。

LFM2.5は再帰状態を持つため、cache_prompt=trueだけに依存せず、llama-serverのslot保存・復元でattentionと再帰状態の両方を引き継ぎます。質問ごとに独立したslotを借り、prefixを復元してから質問固有の後半だけ評価します。候補logprobの再取得時にも必ず復元します。各質問のcache_nを検査し、再利用されなければエラーにします。

画像を含むリクエストはテキストtoken列の保存・復元を行わず、各質問でdecoderの画像入力を評価します。--state-cache shared指定時も同様で、診断ヘッダーはoff-imagesです。画像エンコードキャッシュを有効にした実験ビルドではencoderの出力だけを別途再利用できます。画像推論も同じslot貸し出しを使うため、テキストリクエストの復元処理とslotを取り合いません。以下の速度測定は旧テキストモデルでの結果です。

起動

既存の推論サーバーとAPIサーバーをそれぞれCtrl-Cで止め、別ターミナルで再起動します。

./run_server.sh
./run_api_server.sh
python3 systemone_client.py

既定は--state-cache auto --cache-min-tokens 256です。2問以上の共通prefixが256トークン以上なら共有し、短ければ保存・復元のコストを避けます。256は今回の測定による目安で、GPU・質問数に応じた自動学習はしません。短いStateでも試すには./run_api_server.sh --state-cache shared、無効化するには--state-cache offを指定します。

推論サーバーは.cache/slots--slot-save-pathとして使います。変更する場合は、推論側のSLOT_CACHE_DIR=/absolute/pathとAPI側の--slot-cache-dir /absolute/pathを合わせてください。両プロセスから同じファイルが見える必要があります。保存機能のない既存バックエンドには--state-cache offを使うか、起動し直してください。

slot管理のため、推論サーバーはこのAPI専用にしてください。別クライアントから直接推論させる構成は対応しません。同一URL・保存ディレクトリのAPI重複起動はロックで防ぎますが、別URL表記・別ディレクトリや外部クライアントまでは検出できません。

snapshotはリクエストごとに一意のファイルに保存し、全質問終了後にエラー時も削除します。リクエストをまたぐ永続キャッシュではありません。強制終了時は残る可能性があります。保存先には入力由来のモデル状態が含まれるため、他ユーザーと共有しないディレクトリを使ってください。

確認

curl -sS -D - http://127.0.0.1:8080/v1/systemone \
  -H 'Authorization: Bearer local-dev' \
  -H 'Content-Type: application/json' \
  --data-binary @examples/systemone.json

公式JSON形式は変更せず、HTTPヘッダーに診断値を返します。

ヘッダー末尾(X-Jev-Local-に続く)内容
State-Cacheshared / off-short / off
Prefix-Tokens共有prefixの長さ
Cached-Tokens各質問・再試行で再利用したトークン数の合計
Processed-Tokens準備を含め実際に評価した入力トークン数

既定の12問デモはprefixが32トークンなのでoff-shortになります。長いStateまたはshared指定で再利用を確認できます。準備ではバックエンドの都合で無視する1トークンを生成します。そのトークンは共有状態には入りませんが、usage.output_tokensには含めます。usage.input_tokensはバックエンドの論理入力長の合計であり、計算削減率そのものを表しません。

実測

Radeon AI PRO R9700、ROCm、LFM2.5-1.2B-Instruct Q8_0、4 slots、同じ12問。各条件5回の中央値、モデルロードを除外し、各リクエストのtokenize・prefix準備・保存復元を含みます。中・長は同じ顧客情報の履歴を追加した合成入力です。

共通prefix無効強制共有既定auto
32 tokens114.9 ms162.5 ms125.0 ms
365 tokens241.8 ms183.6 ms181.0 ms
1025 tokens552.7 ms231.7 ms230.7 ms

長い例はautoで約2.40倍、中程度は約1.34倍高速です。短い例ではautoも共有判定用tokenizeのコストが残ります。長い例の実評価トークン数は13,105から1,830へ減りました。すべての入力・負荷での性能保証ではありません。

計算の区切りが変わるため、非共有時と確率・確信度が完全一致する保証はありません。 実測でもNoulが0.508から0.479へ動いた例があり、0.5付近の判断は反転し得ます。プロンプト・候補・確率の計算式は変えていません。

再測定は、他のAPIが接続していない専用バックエンドに対して行います。

python3 -m tools.benchmark_state_cache --backend-url http://127.0.0.1:8097 --rounds 5

応答・診断・全測定値はresults/state_cache_benchmark.jsonへ保存します。従来のjev_local.py直接実行には今回の共有機能は適用していません。