ai-lab.org
MindCell

MindCell ができるまで — ブラウザだけで LLM を動かすチャット設計

@mlc-ai/web-llm を WebGPU 上で動かして、 ローカル推論の AI チャットをブラウザ完結で実装した Webサービスの設計記録。 ストリーミング応答、モデルロード進捗、対応外環境の扱いまで解説します。

判断: 改善

MindCell ができるまで — ブラウザだけで LLM を動かすチャット設計

MindCell は、AI チャットを 会話をサーバーに送らずに 動かす Webサービスです。 WebGPU + @mlc-ai/web-llm でローカル LLM をブラウザ内推論し、 メッセージはあなたのデバイスから一歩も出ません。 メディア処理 7本に続く、 ラボ 8本目。 今回はテキスト生成という別軸での 「ブラウザ完結」 実証です。

なぜこの形にしたか

AI チャットに入力する内容は、 個人的だったり機密だったりすることが多い。 仕事のメール、健康相談、家族のこと、契約書のドラフト。 これらが外部 API を経由する設計は、本来は正しくない。

WebGPU が Chrome 113+ で安定し、 @mlc-ai/web-llm が量子化済みモデル (Qwen 2.5 0.5B など) を高水準 API で扱える今、 ローカル LLM を Web サービスとして出す素地が整っています。 「LLM もブラウザだけで動く」 という事実を、 そのまま製品にしたのが MindCell。

visual direction

ニューラル・パスウェイ × インディゴ + シアン。

  • 深い真夜中のインディゴ (#07091a) を背景に、 インディゴ (#6366f1) と電気シアン (#22d3ee) のグラデーションをアクセント
  • 「思考の回路が光る」 メタファー
  • 既存7本 (mint / amber / violet / cobalt / lime / gold / rose) と完全に別の色相 + 異なる温度感

実装の見どころ

1. 高水準 API でロードを1行化

@mlc-ai/web-llmCreateMLCEngine は、 モデルID と進捗コールバックを渡すだけでエンジンを返してくれる。 量子化済みモデルが Hugging Face CDN から並列ダウンロードされ、 IndexedDB に自動キャッシュ。 2回目以降はオフラインで動きます。

const engine = await CreateMLCEngine("Qwen2.5-0.5B-Instruct-q4f16_1-MLC", {
  initProgressCallback: (p) => onProgress({ text: p.text, progress: p.progress }),
})

2. ストリーミング応答

OpenAI 互換の API で stream: true を指定すると、 AsyncIterable でトークンが届きます。 これを React の setMessages に渡すと、 タイプライター風の応答 UI が成立。

const stream = await engine.chat.completions.create({
  messages: [...],
  stream: true,
})
for await (const chunk of stream) {
  const delta = chunk.choices[0]?.delta?.content
  if (delta) yield delta
}

UI 側は generator を for await で受けて、 ステート更新時に最後のメッセージのみ書き換える設計。 余計な再レンダリングを避けています。

3. WebGPU 非対応の検出と honest fallback

WebGPU は Chrome / Edge / Opera のデスクトップ最新版が最も安定。 Safari と Firefox は実験的または未対応。 起動時に navigator.gpu の有無を見て、 非対応なら専用パネルで対処法を案内します。

{webGpuOk === false ? (
  <UnsupportedPanel>
    Chrome / Edge / Opera で再アクセスしてください
  </UnsupportedPanel>
) : null}

ここで「動くフリ」 をしないことが重要。 text-pluck のロゴ事件で得た教訓を踏襲しています。

4. モデル切替

Qwen 0.5B (~400MB) / Llama 1B (~900MB) / Phi 3.5 mini (~2.2GB) をプルダウンで切替。 切替時は既存エンジンを unload() してから新規ロード、 という Promise キャッシュ管理を runner 側に閉じ込めています。

5. System prompt 編集

折りたたみ式の editor で system prompt を即時変更可能。 ユーザーが「丁寧な翻訳者として振る舞って」 のような指示を入れて、 そのまま会話を始められる。 系列の text-pluck や bg-snap の preset プリセットと同じ流儀で、 「設定が見える時だけ触れる」 UI に統一しています。

苦労したところ

  • WebGPU 普及度の現実: モバイル Safari は iOS 18+ でフラグ必要、Firefox は Nightly のみ。 「対応していないブラウザでは何もできない」 のを正直に出す UX が必須でした。
  • モデル初回ロードの巨大さ: ~400MB は Wi-Fi 前提です。 モバイル回線では離脱可能性が高い。 FAQ で明示し、 progress バーで透明化することで対処。
  • 量子化モデル ID の命名: WebLLM 公式が使う Qwen2.5-0.5B-Instruct-q4f16_1-MLC のような長いID をUI に出さないよう、 ラベルと容量の組合せで見せる構造にしました。

今後の拡張

  • 会話履歴の保存 / エクスポート: localStorage + JSON ダウンロード
  • 添付ファイル対応: text-pluck (OCR) や voice-scribe (文字起こし) の出力を直接食わせる導線
  • 複数モデル並列: Phi で詳細応答、 Qwen で素早い相槌、のような役割分担
  • WebGPU 不在時の WASM fallback: 速度は出ないが動かす選択肢
  • マルチターン要約: 長い会話を別モデルで要約してコンテキスト圧縮

このサービスから言える事

ラボの 8本柱:

  • voice-scribe / clip-cast / bg-snap / text-pluck / pdf-anvil / pixel-lift / pic-flip / mind-cell

メディア処理 7本に続いて、 今度はテキスト生成系もブラウザ完結に来ました。 つまり「AI 処理を全部ブラウザで」 という主張が、 認識系 + 変換系 + 生成系の3カテゴリで実証された状態。 ここまで来ると、 「サーバーに送らないと使えない」 という SaaS の前提自体が、 そろそろ更新時期に入っている、 と言えそうです。

Next Action

読んだあとに、そのまま使って確かめる。

この開発ログは MindCell をどう育てているかの記録です。読んだらそのままサービス本体へ戻って、価値を確かめられるようにしています。

Keep Reading, Keep Trying

読むだけで終わらせない主力サービス

改善ログで方向を理解したあとに、そのまま使って価値を確かめやすい主力を並べています。

hack-sim主力

HackSim

/hack-sim

1分で始められるSNS向けハッキング体験シミュレーター

· エンタメ· 公開中· 48 pv
prompt-stock主力

PromptStock

/prompt-stock

使うだけで終わらせず、改善して育てるためのプロンプト実用品ストック

· 改善· 改善して伸ばす· 20 pv

More Logs

次に読みたい改善ログ

記事一覧へ