ブラウザで使う
ワーカーを使う
エンコーダーが動かす wasm はスレッドに順番を渡しません。メインスレッドでは BANKING77 の訓練がページを 171 s 止めました。ワーカーでは、訓練中のページの最長の停止は 18 ms でした。そのためブラウザでは createWorkerDecider() を使ってください。
import { createWorkerDecider, loadHead } from "decidekit/browser";
const d = await createWorkerDecider();
const r = await d.decide(input.value, ["question", "bug_report", "praise"]);createDecider() と同じ decide()、decideMany()、train() を持ち、ワーカーを止める terminate() が加わります。各呼び出しにメッセージのやり取りで約 1 ms かかります。違いの一覧は API リファレンスにあります。
デサイダーは 1 つ作って再利用してください。createWorkerDecider() は呼ぶたびに新しいワーカーを起動してモデルを読み込み直します(2 回目以降はキャッシュからですが、それでも手元の実行で 3.8 s かかりました)。
ダウンロードの進捗を表示する
最初の訪問でモデルをダウンロードします。onProgress は届いたファイルごとに報告します。
const bar = document.querySelector("progress")!;
const label = document.querySelector("#status")!;
const d = await createWorkerDecider({
onProgress: ({ file, loaded, total }) => {
bar.value = total ? loaded / total : 0;
label.textContent = `downloading ${file}`;
},
});
label.textContent = "ready";ファイルごとに呼ばれるので、バーはファイルごとに 1 回満ちます。キャッシュからの読み込みでは呼ばれないので、Promise が解決するまでは単純な「読み込み中」を表示してください。
訓練中の進捗を表示するには、train() に onEpoch を渡します。訓練の進行に合わせてエポックと取り分けでの精度を報告します(呼ばれるタイミングは API リファレンスを参照)。
クロスオリジン分離
次の 2 つのヘッダーを付けてページを配信してください。
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: credentiallessこれでページはクロスオリジン分離され、onnxruntime の wasm が複数のスレッドを使えます。ヘッドレス Chrome で BANKING77 を使った結果です(16 スレッド、2026-11-01)。
| 判定 1 回の中央値 | 判定 1 回の p95 | |
|---|---|---|
| クロスオリジン分離あり、bge-small(デフォルト) | 7.7 ms | 13.8 ms |
| クロスオリジン分離あり、all-MiniLM-L6-v2 | 4.2 ms | 7.7 ms |
| 分離なし、all-MiniLM-L6-v2(2026-10-31 の実行) | 14.3 ms |
分離なしは all-MiniLM-L6-v2 でしか計測していません。その実行では、分離で p95 が 14.3 ms から 7.8 ms になりました。ワーカーでの BANKING77 の訓練は bge-small で 228 s かかり、ページが 18 ms を超えて止まることはありませんでした。
credentialless を使うと、ページはクロスオリジンの画像やスクリプトを資格情報なしで読み込み続けられます。各サーバーの対応は要りません。頼っているサードパーティの埋め込み(サインインのポップアップ、決済フレーム)が same-origin でも動くかを、先に確かめてください。ヘッダーがなくても decidekit はシングルスレッドで動きます。
Vite、webpack、Next.js のヘッダー設定はバンドラーにあります。
初回の読み込みサイズ
| ファイル | サイズ |
|---|---|
| モデル、q8 の Decide Small(デフォルト) | 34 MB |
| モデル、q8 の all-MiniLM-L6-v2(リリース前のデフォルト) | 23.7 MB |
| onnxruntime の wasm | 23.6 MB(gzip で 5.8 MB) |
| 下のエイリアスを使った onnxruntime の wasm | 12.9 MB(gzip で 3.3 MB) |
| ヘッド、77 ラベル | 156 KB |
| ヘッド、150 ラベル | 303 KB |
エイリアスを使うと、最初の訪問でダウンロードするのは 34 MB のモデルと 12.9 MB の wasm(gzip で 3.3 MB)です。以後はすべてキャッシュされます。all-MiniLM-L6-v2 では最初の訪問は約 36 MB でした。
エイリアスは onnxruntime の WebGPU ビルドを、decidekit がデフォルトで使う素の wasm ビルドに差し替えます。
// vite.config.js
export default {
resolve: { alias: { "onnxruntime-web/webgpu": "onnxruntime-web/wasm" } },
};device: "webgpu" を渡すなら外してください。webpack と Next.js 版はバンドラーにあります。
キャッシュ
モデルファイルは最初のダウンロードのあとブラウザにキャッシュされ、以後の訪問ではローカルから読み込みます。キャッシュはページのオリジンに属し、ブラウザが消すこともあるので、2 回目のダウンロードにも備えてください。ヘッドファイルは普通の静的ファイルです。他のアセットと同じように、ファイル名のコンテンツハッシュやキャッシュヘッダーを付けてください。
モデルファイルを自分で配信する
デフォルトでは、ファイルは最初の利用時に Hugging Face からダウンロードされ、以後はキャッシュされます。自分のホストから配信するには modelHost を設定します(0.1.1 以降)。
const d = await createWorkerDecider({ modelHost: "https://cdn.example.com/models" });ファイルは <modelHost>/<model>/ から読み込みます。トークナイザーと設定のファイルは onnx フォルダと同じ場所に置きます。
https://cdn.example.com/models/debuffjp/decide-small/config.json
https://cdn.example.com/models/debuffjp/decide-small/tokenizer.json
https://cdn.example.com/models/debuffjp/decide-small/tokenizer_config.json
https://cdn.example.com/models/debuffjp/decide-small/onnx/model_quantized.onnxファイルは debuffjp/decide-small または debuffjp/decide-multilingual からコピーしてください。リビジョンの固定はここには適用されません。配信されるのはコピーしたファイルそのものです。
modelHost はページまたはプロセス全体に適用されます。transformers.js のホストを設定するので、同じスレッドの以後のデサイダーは、渡さなくてもこの設定を使います。ワーカーのデサイダーでは、そのワーカーの中で設定します。ホストのオリジンが別なら、ページからの CORS リクエストを許可する必要があります。
バンドラー
decidekit はワーカーを new Worker(new URL("./worker.js", import.meta.url), { type: "module" }) で起動します。Vite、webpack 5、esbuild ベースのツールが認識するパターンです。パック済みのパッケージを Vite 7.1、webpack 5.112、Next.js 16.4(Turbopack)でテストしました。それぞれの設定はバンドラーにあります。
別のワーカー URL が必要なら、Worker を自分で作ってください。バンドラーが配信する decidekit の dist/worker.js を指し、2 番目の引数として渡します:createWorkerDecider(options, worker)。
エンコーダーをページのバンドルから外す
ページのコードでは decidekit ではなく decidekit/browser からインポートしてください。decidekit は transformers.js とエンコーダーを引き込みます。ブラウザ用のエントリーにあるのはワーカーのクライアントとヘッドの関数だけです。ヘッドを読み込むだけのページは、decidekit からの 482 KB に対して decidekit/browser からは 3.6 KB でした。
// good: the encoder stays in the worker's chunk
import { createWorkerDecider, loadHead } from "decidekit/browser";
// pulls the encoder into the page bundle
import { loadHead } from "decidekit";型はどちらのエントリーからでも同じように使えます。
サーバーレンダリング
decidekit/browser は Web Worker を起動するので、ブラウザでのみインポートしてください。Next.js ではクライアントコンポーネントの useEffect の中で動的 import() を使います。コードはバンドラーにあります。