Open14

LLMを使ったプロダクトの技術スタック

kuboshokubosho

LLMプロバイダーの比較

各社の料金表やAI Model & API Providers Analysis | Artificial Analysisを見た結果、賢さ・スピード・コストの面でバランスが良いのは次の3つと判断した。

  • GPT-5 mini
  • Gemini 2.5 Flash
  • Grok 4 Fast

有料プラン向けにはより上位のモデルを提供していいかも。

各LLMを使い「振る舞いの制御のしやすさ」「日本語対応の品質」「SDKの充実度」「レート制限」を見ていく。

なおストリーミングAPIは各社が対応している。リンクは下記の通り。

kuboshokubosho

UIフレームワーク・メタフレームワーク

慣れていること・ライブラリなどが多いこと・Web上に情報が多くAIにコンテキストを与えやすいことを踏まえて、UIフレームワークはReactを使い、メタフレームワークはNext.jsを使う。Next.jsではApp Routerベースを使う。

Next.jsのドキュメントにAI SDK by Vercelを使ったストリーミング形式レスポンスをハンドリングする方法の例が掲載されている。

UIフレームワークのその他選択肢

  • Vue:Composition API、リアクティブシステム、学習コスト低い
  • Svelte:コンパイラベース、ランタイムなし、高速、バンドルサイズ小
  • Solid.js:細かいリアクティビティ、パフォーマンス重視

メタフレームワークのその他選択肢

  • Next.js Pages Router:従来のファイルベースルーティング、App Routerより成熟
  • Remix(React Router):Web標準重視、ネストルーティング、データローディング最適化
  • SvelteKit:Svelteベース、ファイルベースルーティング
kuboshokubosho

コンポーネント設計

ReactのServer ComponentsとClient Componentsを組み合わせる。Server ComponentsとClient Componentsの使い分けは次の通りとする。

  • Server Components:ページの初期表示に使うデータの取得・SEO用に静的コンテンツが必要な部分
  • Client Components:UIのインタラクション・状態管理・ブラウザ搭載APIを使う部分

例として、リアルタイムチャット用の投稿UIでは状態管理やインタラクションが必要なためClient Componentで実装して、メッセージ履歴はServer Componentで実装するといった具合。

kuboshokubosho

状態管理

状態を一ヶ所に集められて見通しが良くなるという点でZustandを選択する。

状態管理のその他選択肢

  • Jotai
  • Valito
kuboshokubosho

認証戦略

現状はGoogleアカウントによるOAuth認可・認証のみを提供する。実装にはAuth.jsを使う。

ID/パスワードによる認証はパスワードの管理が発生して実装が煩雑になるので提供しない。

将来的にはMagic Linkの提供も考えられるが、予定は未定。

kuboshokubosho

セキュリティ

下記項目を設定する。

  • HTTPSの強制:本番環境でHTTP → HTTPSリダイレクトやStrict-Transport-Securityヘッダーの設定
  • JWT(JSON Web Token)を使ったセッショントークンの実装:15分 ~ 1時間といった短い時間でリフレッシュトークンを使い更新
  • CSRFトークン:フォーム送信時にトークン検証
  • SameSite CookieSameSite=Lax または Strict 設定
  • Content Security Policy (CSP)
  • 入力に対するサニタイゼーション

Webサービス公開前のチェックリストも参考にする。

kuboshokubosho

リアルタイム対話UI

リアルタイム通信

リアルタイム通信の実装にServer-Sent Events(SSE)を使って実装する。

  • HTTP 上の一方向通信(サーバー → クライアント)
  • 自動再接続機能
  • ブラウザネイティブサポート(EventSource API)
  • 効率的なリクエスト・レスポンス

Server-Sent Events(SSE)の参考記事

リアルタイム通信のその他選択肢

  • WebSocket:
    • 双方向通信(サーバー ↔ クライアント)
    • 低レイテンシ、リアルタイム性高い
    • 実装が複雑、接続管理が必要
    • LLMストリーミングには過剰(一方向通信で十分)
  • ポーリング:
    • 定期的にHTTPリクエストで状態確認
    • 実装が簡単
    • 非効率、レイテンシが高い、サーバー負荷大
  • GraphQL Subscriptions:
    • GraphQLベースのリアルタイム通信
    • WebSocket使用
    • LLMストリーミングで使うには複雑

UI/UXパターン

ストリーミング中の表示方法案

  • タイピングアニメーション:文字が一文字ずつ表示される効果
  • チャンク表示:単語や文節単位で表示
  • プログレスインジケーター:ストリーミング中であることを視覚的に表示
  • カーソル点滅:生成中の視覚的フィードバック

楽観的UI更新

  • ユーザーが入力したメッセージを即座に表示(APIレスポンスを待たない)
  • LLMの応答中もUIをブロックしない
  • エラー時にロールバック処理

エラー時のリトライUI

  • タイムアウト時の「再試行」ボタン
  • 接続断時の自動リトライ + ユーザー通知
  • エラーメッセージの明確な表示
kuboshokubosho

学習テーマ抽出方法

会話の文脈から適切な学習テーマを抽出でき、柔軟性が高く多様性があり、精度が高くなるため、LLMによる会話要約を使う。

追加のAPI呼び出しは必要なので、その分コストはかかる。しかし精度が高い学習テーマの自動抽出は使い続けるという点で大事になるため、コストをかける。

学習テーマ抽出方法のその他選択肢

  • キーワード抽出(TF-IDF、TextRank等):
    • 統計的手法、文脈を理解しない
    • コストが低い(ローカル処理)
    • 精度が低い、抽象的な内容に弱い
    • 「React」「useEffect」などのキーワード抽出には有効
  • 固定カテゴリ分類:
    • 事前定義されたカテゴリに分類
    • 柔軟性がない、学習内容の多様性に対応不可
    • シンプル、予測可能
  • 手動入力のみ:
    • ユーザーがすべてのテーマを入力
    • ユーザー体験が悪い
    • 追加コストなし
kuboshokubosho

データベース

NeonSupabaseといった個人開発で使いやすいDBのSaaSがPostgreSQLを使っているため、PostgreSQLを使う。

ORMはTypeScriptとの統合が強くAPIもSQL-likeとなっているため学習コストが低めのDrizzle ORMを使う。

DBaaSはVercelとの統合が高いことと、使い始めるための設定が楽という観点からNeonを使う。

データベースのその他選択肢

  • MySQL:
    • シンプル
    • 学習コストが低い
    • PostgreSQLに比べて機能が少ない
  • SQLite:
    • ファイルベース、軽量、サーバー不要
    • シンプルなアプリに最適
    • マルチユーザー対応が弱い、本番環境には不向き
  • MongoDB:
    • NoSQL、スキーマレス
    • 柔軟なデータモデル
    • リレーショナルデータには不向き(ユーザー、セッション、メッセージの関係性)

ORM/クエリビルダーのその他選択肢

  • Prisma:
    • TypeScriptファースト、型安全性が高い
    • スキーマファイル(schema.prisma)からコード生成
    • マイグレーション管理が簡単
    • Prisma StudioでGUIデータ管理
    • エコシステム充実、Next.jsとの統合良好
    • スキーマファイルを別途作成する必要がある
  • TypeORM:
    • Entityクラスベース、デコレーター使用
    • マイグレーション管理充実
    • Prismaより学習コスト高い
  • Kysely:
    • SQLクエリビルダー、型安全
    • 生SQLに近い、柔軟性高い
    • ORM機能なし(リレーション自動解決なし)

DBaaSのその他選択肢

  • Supabase:
    • オープンソース、PostgreSQL ベース
    • RLS (Row Level Security) で細かいアクセス制御
    • Auth、Storage、Functions も統合
  • AWS RDS:
    • エンタープライズ向け、高可用性
    • 料金が高い、設定が複雑
    • MVPには過剰
  • その他(PlanetScale、Railway など):
    • PlanetScale:MySQL ベース、スケーラビリティ重視
    • Railway:簡単デプロイ、PostgreSQL/MySQL 対応

データベース関連のリンク

kuboshokubosho

API設計

API設計にOpenAPI Specificationを使う。バリデーションライブラリはエコシステムの充実度も踏まえてZodを使う。

Zodの特長

  • TypeScriptファースト、型推論サポート
  • スキーマからTypeScript型を自動生成
  • tRPC、Next.jsと統合良好
  • エラーメッセージのカスタマイズ可能
  • v4からZod Miniというtree-shaking対応のvariant libraryが出た
  • jsr.ioを使って @zod/zod という形式でもパッケージを使える
  • MCPサーバーも提供されている

API設計のその他選択肢

  • GraphQL:
    • クエリ言語、必要なデータのみ取得(Over-fetching回避)
    • 単一エンドポイント、柔軟なデータフェッチ
    • 学習コスト高い、キャッシュが複雑
    • N+1問題の対策が必要(DataLoaderなど)
  • tRPC:
    • TypeScriptファースト、End-to-End型安全
    • コード生成不要、型推論で自動補完
    • Next.js / Reactとの統合が簡単
    • RESTやGraphQLのような標準規格ではない
  • gRPC:
    • Protocol Buffers管理、高速、マイクロサービス向け
    • Webフロントエンドとの統合は複雑になる

API設計関連のリンク

バリデーションライブラリのその他選択肢

  • Valibot:
    • Zodより軽量、高速、新しい
  • Yup:
    • あまり活発に更新されていない
    • React Hook Formと統合しやすい
    • Zodより古い、エコシステム大きい
  • Joi:
    • あまり活発に更新されていない
    • 複雑なバリデーションルール対応
  • ajv:
    • あまり活発に更新されていない

バリデーションライブラリ関連のリンク

kuboshokubosho

テスト戦略

テストはThe Testing Trophy and Testing ClassificationsWrite tests. Not too many. Mostly integration.に沿った書き方をする。

  • Unit Testは全てのケースを含めず、70%くらいのカバレッジを目指す
  • Integration Testを厚めに書く
  • E2E Testは信頼性が高いテストだが、コストは高いためUnit TestやIntegration Testで代替できるテストは書かない

テスト戦略関連のリンク

またテストフレームワークはすべてのテストを通してVitest・Vitest Browser Modeを使う。

テストフレームワークのその他選択肢

  • Jest:
    • 最も広く使われている、エコシステム大きい
    • スナップショットテスト、モック機能充実
    • 設定が複雑になりがち
  • Playwright:
    • 複数ブラウザ対応(Chromium、Firefox、WebKit)
    • 並列実行、高速
    • コードジェネレーター、デバッグツール充実
    • APIテスト機能もある
  • Cypress:
    • 開発者体験が良い、リアルタイムリロード
    • Chromiumベースのブラウザのみ(Firefox、WebKitは実験的)
    • 並列実行は有料プラン

テストフレームワーク関連のリンク

kuboshokubosho

CI/CD

GitHubを使うのでGitHub Actionsが環境の統合や料金面で有利。

ホスティング

プレビュー環境や本番環境の構築に時間をかけたくないため、Vercelを使う。今後サービスが流行ったらAWS・GCP・Azureといったクラウドサービスに移行する。

モニタリング・ログ収集

エラートラッキングではSentryを使い、アクセス解析にはGoogle Analytics 4を使う。