# ブラウザで使う

## ワーカーを使う {#use-the-worker}

エンコーダーが動かす wasm はスレッドに順番を渡しません。メインスレッドでは BANKING77 の訓練がページを 171 s 止めました。ワーカーでは、訓練中のページの最長の停止は 18 ms でした。そのためブラウザでは `createWorkerDecider()` を使ってください。

```ts
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 リファレンス](/ja/decide/docs/api#createworkerdecider-options-worker)にあります。

デサイダーは 1 つ作って再利用してください。`createWorkerDecider()` は呼ぶたびに新しいワーカーを起動してモデルを読み込み直します（2 回目以降はキャッシュからですが、それでも手元の実行で 3.8 s かかりました）。

## ダウンロードの進捗を表示する {#show-download-progress}

最初の訪問でモデルをダウンロードします。`onProgress` は届いたファイルごとに報告します。

```ts
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 リファレンス](/ja/decide/docs/api#trainoptions)を参照）。

## クロスオリジン分離 {#cross-origin-isolation}

次の 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 のヘッダー設定は[バンドラー](/ja/decide/docs/bundlers)にあります。

## 初回の読み込みサイズ {#first-load-size}

| ファイル | サイズ |
|---|---|
| モデル、q8 の Decide Small（デフォルト） | 34 MB |
| モデル、q8 の all-MiniLM-L6-v2（リリース前のデフォルト） | 23.7 MB |
| 0.2.1 以降の onnxruntime の wasm | 12.9 MB（gzip で 3.3 MB） |
| 0.2.0、または `device: "webgpu"` のときの onnxruntime の wasm | 23.6 MB（gzip で 5.8 MB） |
| ヘッド、77 ラベル | 156 KB |
| ヘッド、150 ラベル | 303 KB |

0.2.1 以降、最初の訪問でダウンロードするのは 34 MB のモデルと 12.9 MB の wasm（gzip で 3.3 MB）です。以後はすべてキャッシュされます。all-MiniLM-L6-v2 では最初の訪問は約 36 MB でした。

0.2.1 以降は decidekit が素の wasm ファイルを自分で選びます。以前のドキュメントには下のエイリアスで wasm が半分になると書いていましたが、Chrome と esbuild のビルドでは、0.2.0 はエイリアスの有無にかかわらず 23.6 MB のファイルを取得しました。エイリアスは任意になりました。onnxruntime の WebGPU 用 JS を素の wasm 用 JS に差し替えるもので、このビルドではワーカーの JS が gzip で 14.5 KB 減りました。

```js
// vite.config.js
export default {
  resolve: { alias: { "onnxruntime-web/webgpu": "onnxruntime-web/wasm" } },
};
```

`device: "webgpu"` を渡すなら外してください。webpack と Next.js 版は[バンドラー](/ja/decide/docs/bundlers)にあります。

## キャッシュ {#caching}

モデルファイルは最初のダウンロードのあとブラウザにキャッシュされ、以後の訪問ではローカルから読み込みます。キャッシュはページのオリジンに属し、ブラウザが消すこともあるので、2 回目のダウンロードにも備えてください。ヘッドファイルは普通の静的ファイルです。他のアセットと同じように、ファイル名のコンテンツハッシュやキャッシュヘッダーを付けてください。

## モデルファイルを自分で配信する {#host-the-model-files-yourself}

デフォルトでは、ファイルは最初の利用時に Hugging Face からダウンロードされ、以後はキャッシュされます。自分のホストから配信するには `modelHost` を設定します（0.1.1 以降）。

```ts
const d = await createWorkerDecider({ modelHost: "https://cdn.example.com/models" });
```

ファイルは `//` から読み込みます。トークナイザーと設定のファイルは `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](https://huggingface.co/debuffjp/decide-small) または [debuffjp/decide-multilingual](https://huggingface.co/debuffjp/decide-multilingual) からコピーしてください。リビジョンの固定はここには適用されません。配信されるのはコピーしたファイルそのものです。

`modelHost` はページまたはプロセス全体に適用されます。transformers.js のホストを設定するので、同じスレッドの以後のデサイダーは、渡さなくてもこの設定を使います。ワーカーのデサイダーでは、そのワーカーの中で設定します。ホストのオリジンが別なら、ページからの CORS リクエストを許可する必要があります。

## バンドラー {#bundlers}

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）でテストしました。それぞれの設定は[バンドラー](/ja/decide/docs/bundlers)にあります。

別のワーカー URL が必要なら、`Worker` を自分で作ってください。バンドラーが配信する decidekit の `dist/worker.js` を指し、2 番目の引数として渡します：`createWorkerDecider(options, worker)`。

## エンコーダーをページのバンドルから外す {#keep-the-encoder-out-of-the-page-bundle}

ページのコードでは `decidekit` ではなく `decidekit/browser` からインポートしてください。`decidekit` は transformers.js とエンコーダーを引き込みます。ブラウザ用のエントリーにあるのはワーカーのクライアントとヘッドの関数だけです。ヘッドを読み込むだけのページは、`decidekit` からの 482 KB に対して `decidekit/browser` からは 3.6 KB でした。

```ts
// 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";
```

型はどちらのエントリーからでも同じように使えます。

## サーバーレンダリング {#server-rendering}

`decidekit/browser` は Web Worker を起動するので、ブラウザでのみインポートしてください。Next.js ではクライアントコンポーネントの `useEffect` の中で動的 `import()` を使います。コードは[バンドラー](/ja/decide/docs/bundlers#next-js)にあります。