> ## Documentation Index
> Fetch the complete documentation index at: https://arkor-92aeef0e-eng-615.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 学習中の評価

> チェックポイントごとに固定プロンプトで半学習済みモデルをサニティーチェックし、学習完了前に確認する。

# 学習中の評価

TypeScript でファインチューニングする最大の理由は、学習がまだ進行中の状態で、自分のコードから半学習済みモデルを呼べる点です。フックは `onCheckpoint` です。バックエンドがチェックポイントをアップロードするたびに、SDK が登録された関数を呼び出し、そのチェックポイントアダプターに紐づいた `infer` を渡してくれます。

このレシピは固定プロンプトに対してそれを組み込み、Loss（モデルの誤差を表す指標）の曲線が異常を示すよりずっと前にリグレッションを検知できるようにします。

## パターン

```ts theme={null}
// src/arkor/trainer.ts
import { createTrainer } from "arkor";

const GOLDEN_PROMPT = [
  { role: "user" as const, content: "I can't log in to my account." },
];

export const trainer = createTrainer({
  name: "support-bot-v1",
  model: "unsloth/gemma-4-E4B-it",
  dataset: { type: "huggingface", name: "arkorlab/triage-demo" },
  lora: { r: 16, alpha: 16 },
  maxSteps: 100,
  callbacks: {
    onCheckpoint: async ({ step, infer }) => {
      try {
        const res = await infer({
          messages: GOLDEN_PROMPT,
          stream: false,        // スニペットを短く保つため単一の JSON ボディで受け取る
          maxTokens: 80,
        });
        const data = (await res.json()) as {
          choices: Array<{ message: { content?: string | null } }>;
        };
        const sample = data.choices[0]?.message.content ?? "";
        console.log(`step=${step} sample=${JSON.stringify(sample.slice(0, 80))}`);
      } catch (err) {
        console.error(`step=${step} infer failed:`, err);
      }
    },
  },
});
```

これがすぐ得られるもの:

* チェックポイントごとに短い生成サンプルを stdout に書き出し、Loss の数字と並べて見られる。
* 新しいアダプターに対して推論自体が動くことの確認（つまり推論側で気付かないうちに発生したリグレッションを学習時に捕まえられる）。
* 後で比較やアサーションを足す自然な場所。

## なぜ他の場所では難しいのか

`infer` は **直前に保存されたチェックポイントに紐づいています**（`{ kind: "checkpoint", jobId, step }`）。Studio の Playground から中間チェックポイントには到達できず、専用の CLI コマンドもありません。現状の唯一の方法は `onCheckpoint` の中から呼ぶことです。そのためこのレシピは事後ではなく、この場で実行する必要があります。

関数はクラウド API からの生の `Response` を返すので、ストリーミングやデコードの方法は自由です。上のスニペットは `stream: false` を渡してボディを単一の JSON ドキュメントとして受け取っています。本格的なストリーミングは [SDK § infer](/ja/sdk/infer) を参照してください。

## バリエーション

**同じプロンプトでベースモデルと比較。** Studio の Playground はすでに Base / Adapter のモード切替を持っていますが、目視ではなく自動でスコアを付けたいなら、同じことを `onCheckpoint` から行えます。

```ts theme={null}
async function generate(prompt: typeof GOLDEN_PROMPT, infer: (args: any) => Promise<Response>) {
  const res = await infer({ messages: prompt, stream: false, maxTokens: 80 });
  const data = (await res.json()) as {
    choices: Array<{ message: { content?: string | null } }>;
  };
  return data.choices[0]?.message.content ?? "";
}

onCheckpoint: async ({ step, infer }) => {
  const sample = await generate(GOLDEN_PROMPT, infer);
  await postSampleToReviewQueue({ step, sample });
},
```

**JSON Schema で型付きフィールドを抽出。** free-form なパースを `responseFormat: { type: "json_schema", json_schema: { name, schema, strict: true } }` に置き換えると、`data.choices[0].message.content` がスキーマを満たす JSON 文字列で返ってくるので、`JSON.parse` 一発で型付きオブジェクトとしてそのまま分岐できます（`parsed.urgency === "high"`、`parsed.category in VALID_LABELS` など）。型付きフィールドを Early Stopping に繋ぐ完全パターンは [構造化出力と Function Calling レシピ](/ja/cookbook/structured-outputs) を参照。

**サンプルから Early Stopping をトリガー。** [Early Stopping レシピ](/ja/cookbook/early-stopping) と組み合わせる: チェックポイント出力が参照テキストから許容を超えてドリフトしたら controller を Abort。次のチェックポイントは発火しません。

**チェックポイントを Slack チャネルにレビュー用で送る。** [通知レシピ](/ja/cookbook/notifications) と組み合わせる。各ステップのサンプルを Slack メッセージとして投稿し、学習が続いている間にレビュアーがリアクションで投票できます。

## 心に留めておくこと

* **`try / catch` で囲む。** `onCheckpoint` から throw すると即座に `wait()` が reject します。SSE 再接続ループに catch されることもリトライされることもありません（このループはトランスポート障害のみを扱います）（[SDK § ライフサイクルコールバック](/ja/sdk/callbacks) 参照）。致命的に扱いたくないならコールバック内でエラーを catch してどうするか決める。
* **推論は実際の呼び出しコストがかかる。** バックエンドはライブの学習クラスタからリクエストを返します。毎チェックポイントを叩くなら `maxTokens` は控えめに。
* **`infer` は呼び出しごと、メモ化されない。** 同じ `onCheckpoint` 内で 2 回呼ぶとバックエンドリクエストも 2 つ。可能なら 1 回の呼び出しでプロンプトをまとめて。
