DeepSeek R1をTypeScriptで実装する際に直面した具体的な課題と、その解決方法について共有します。
主な実装上の課題
1. Transformers.jsの型定義への対応
generate
関数の実装において、以下のような型の問題に遭遇しました:
// Transformers.jsのGenerationFunctionParametersには
// max_new_tokensが定義されていない
const output = await model.generate({
...inputs,
streamer,
stopping_criteria: stopping_criteria_list,
// @ts-ignore - Property 'max_new_tokens' does not exist
max_new_tokens: 2048, // この設定は必須
});
max_new_tokensを設定しないと、推論がすぐに終わってしまい、正しい答えに辿り着きません。
2. 推論フェーズの制御
この問題からもわかったことですが、
DeepSeek R1は<think></think>
タグを使用して推論フェーズを制御しています:
// トークンIDの取得
const [_, END_THINKING_TOKEN_ID] = tokenizer.encode("<think></think>", {
add_special_tokens: false,
});
// 状態管理
let state: "thinking" | "answering" = "thinking";
// </think>を受信したら回答フェーズに移行する
const token_callback_function = (tokens: bigint[]): void => {
if (tokens[0] === BigInt(END_THINKING_TOKEN_ID)) {
state = "answering";
}
};
- 推論フェーズは明示的なトークン(
</think>
)で終了を検知 - トークンコールバックで状態を管理
- ストリーミング出力で進捗を監視します
重要 :</think>
が登場し、そして回答を得るまで推論を繰り返さないといけません。
3. DeepSeekの推論メカニズム
DeepSeek R1の特徴として、明示的な思考フェーズを持つ推論メカニズムがあります:
-
二段階の推論プロセス
-
thinking
フェーズ: 問題の分析と思考過程 -
answering
フェーズ: 最終的な回答の生成
-
-
明示的な状態管理
-
<think></think>
タグによるフェーズの区切り - トークンベースでの状態遷移の検出
- パフォーマンス計測(トークン生成速度など)
-
-
UIフィードバック
- 現在の状態をリアルタイムで通知
- トークン生成の進捗表示
- パフォーマンス指標の共有
このような実装により、モデルは問題に対して「考えてから答える」という人間らしい推論プロセスを実現していますし、思考の過程が見やすくなっています。
まとめ
DeepSeek R1の実装において、特に以下の点が重要でした:
-
max_new_tokens
の設定(型定義の問題はあるものの必須) - 二段階の推論プロセスの実装(thinking/answering)
- 推論状態の適切な制御とUIフィードバック
これらの特徴を理解し適切に実装することで、DeepSeek R1の「考えてから答える」という特徴的な推論能力を効果的に活用できます。