それぞれのコールバックがいつ発火するか
wait() 内からディスパッチされます。start() を呼んで wait() を呼ばないと、学習はバックエンドで進行していてもコールバックは 一切発火しません。arkor start は代わりに wait() を呼びます。CLI の外側で学習を自分のコードに組み込む場合は、自分でも呼ぶようにしてください。
onCompleted と onFailed は排他で、学習 1 回あたり最大 1 つしか発火しません。終端イベント到達前に wait() が throw した場合(例えば abortSignal が Abort された、再接続試行が尽きた)、どちらも発火しないことがあります。
どのコールバックも Promise を返して構いません。Arkor は次に進む前に await するので、非同期処理(DB への書き込み、Slack への投稿、infer の呼び出し)を競合なくこなせます。
onStarted({ job })
wait() が開いた SSE ストリームが training.started イベントを報告したときに 1 回発火します。これは start() の resolve のタイミングと同じではありません。start() はジョブを投入して jobId を返すだけです。
onLog({ step, loss, evalLoss, learningRate, epoch, samplesPerSecond, job })
学習が進むにつれて繰り返し発火します。各数値フィールドはバックエンドがまだそのメトリックスを出していなければ null になり得ます(例えば evalLoss は eval ステップのときだけ)。
- 自前のメトリックスパイプライン(PostHog、Datadog など)に転送。
- 早期発散の検知:
lossが増えていれば、abortSignalでwait()を Abort し、trainer.cancel()でバックエンドの GPU を止める。 - カスタム Early Stopping(指標が悪化したら学習を自動で打ち切るパターン。詳しくは Early Stopping レシピ を参照)を実装。
onCheckpoint({ step, adapter, job, infer, artifacts })
学習中にバックエンドでチェックポイントが保存されたタイミングで発火します。
adapter はチェックポイントを識別するコンパクトなオブジェクトです({ kind: "checkpoint", jobId, step })。infer は関数で、チャット形式のリクエストを受け取り、生の Response を返します。await res.text()(または res.json()、ストリーム処理)で読み取ります。
実用上もっとも価値のあるコールバックです。学習の最後まで待たずに、途中でモデルを動作確認できます。チェックポイントが既にベースモデルより悪ければ、止めるべきだとわかります。
onCompleted({ job, artifacts })
成功時に 1 回発火します。artifacts はこの学習でバックエンドが生成した成果物のリストです。用途:
- 最終アダプター ID をアプリの他の部分から見つけられる場所に保存。
- モデルを昇格させる前に最終スモークテスト。
- 「学習完了」通知の送信。
onFailed({ job, error })
バックエンドが失敗を報告した場合に 1 回発火します。error は string(バックエンドが送ってきたメッセージ)であって Error インスタンスではないことに注意:
onFailed はバックエンドからの失敗報告専用です。コールバック内で throw した例外は onFailed を経由せず、あなたのエラーで wait() を即座に reject します。throw されたコールバックは SSE のトランスポート失敗としては扱われないため、再接続ループに送られたりリトライされたりしません。再接続ループが扱うのは一過性のストリーム障害(接続断、オープン時の 404/408/429/5xx のようなリトライ可能な HTTP エラー)だけです。401/403/410/426 のような恒久的ステータスは即座に reject します(トレーナー制御の再接続 参照)。これは意図的な挙動です。失敗したイベントの Last-Event-ID は既に進んでいるため、リトライするとそのイベントをスキップし、あなたのエラーを握りつぶし、(終端の training.completed イベントの場合は)空の artifacts で wait() を解決してしまうからです。致命的でない処理が必要なら、コールバック内で catch して、外に伝播する前にどうするか(ログ、Abort、永続化)を決めてください。