【12章】オリジナルアプリ制作の振り返り
公開: 2025年12月07日
「Gadget Concierge」は、ユーザーの回答に基づいて最適なガジェットを推薦するWebアプリケーションです。AI、RAG、楽天市場APIを組み合わせて構築しました。本記事では、開発を通じて得た知見と課題を共有します!
技術スタック
フロントエンド: Next.js 14 (App Router), React, TypeScript, Tailwind CSS
バックエンド: Next.js API Routes, Server Actions
データベース: PostgreSQL (Supabase) + Prisma ORM
認証: Supabase Auth (メール/パスワード + Google OAuth)
AI: Google Gemini 2.0 Flash, Google Embeddings API
AIエージェント: Mastra
外部API: 楽天市場商品検索API
ベクター検索: pgvector
レート制限: Upstash Ratelimit + Vercel KV
主な機能
1. カテゴリ別質問システム: カテゴリごとの動的質問生成
2. AI推薦エンジン: ユーザー回答を分析し、最適な3製品を推薦
3. RAG検索: ベクター埋め込みによるセマンティック検索
4. 楽天市場連携: 商品情報の自動同期とアフィリエイト対応
5. ユーザー履歴管理: 過去の診断結果と推薦履歴の保存
課題
テキスト検索では「コスパ重視」「初心者向け」などの意図を捉えにくい
製品数が増えると精度が低下
解決策
Google Embeddings APIとpgvectorを組み合わせて実装。
typescript// 製品の埋め込み生成const { embedding } = await embed({ model: google.textEmbeddingModel("text-embedding-004"), value: productContent,
});
// pgvectorによる類似度検索
const vectorResults = await prisma.$queryRaw` SELECT p.id, p.name, 1 - (p.content_embedding <=> ${JSON.stringify(embedding)}::vector) as similarity FROM products p WHERE 1 - (p.content_embedding <=> ${JSON.stringify(embedding)}::vector) > 0.7 ORDER BY similarity DESC LIMIT 20
`;学び
埋め込み生成のコストと速度のバランスが重要
フォールバック(テキスト検索)を用意することで可用性を確保
類似度閾値(0.7)は実データで調整が必要
課題
構造化出力の一貫性
エラーハンドリングとフォールバック
解決策
Mastraの構造化出力機能を活用。
typescriptconst agent = mastra.getAgent("gadgetRecommendationAgent"); const aiResponse = await agent.generate( [{ role: "user", content: aiPrompt }], {structuredOutput: { schema: recommendationSchema, errorStrategy: "fallback", fallbackValue: { recommendations: [] },},
});学び
スキーマ定義(Zod)で型安全性とバリデーションを両立
errorStrategy: "fallback"でエラー時の挙動を制御
プロンプト設計が出力品質に大きく影響
課題
レート制限(1秒1リクエスト)
大量商品の同期
エラーハンドリング
解決策
並列処理対応のレート制限管理とCronジョブを実装。
typescript// レート制限管理(並列処理対応)
let lastRakutenRequestTime = 0;let rateLimitLock: Promise<void> = Promise.resolve();async function waitForRateLimit(): Promise<void> { await rateLimitLock; let releaseLock: () => void; rateLimitLock = new Promise((resolve) => { releaseLock = resolve;
});const timeSinceLastRequest = Date.now() - lastRakutenRequestTime; if (timeSinceLastRequest < 1000) { await new Promise((resolve) => setTimeout(resolve, 1000 - timeSinceLastRequest));
} lastRakutenRequestTime = Date.now(); releaseLock();
}学び
レート制限はロック機構で並列リクエストを制御
リトライと指数バックオフで安定性を向上
バッチ処理はCronで定期実行
実装した対策
1. タイミング攻撃対策: ハッシュ化比較
typescriptconst tokenHash = crypto.createHash("sha256").update(token).digest();const secretHash = crypto.createHash("sha256").update(cronSecret).digest();if (!crypto.timingSafeEqual(tokenHash, secretHash)) { await delayOnFailure(); return { isValid: false };
}
2. レート制限: Vercel KV + Upstash Ratelimit
typescriptexport const cronRatelimit = new Ratelimit({ redis: kv, limiter: Ratelimit.slidingWindow(10, "60 s"), prefix: "ratelimit:cron",
});学び
サーバーレス環境では共有ストレージ(KV)が必要
タイミング攻撃対策はハッシュ化で実現
セキュリティは段階的に強化
階層カテゴリの実装
prismamodel Category { id String @id @default(cuid()) name String parentId String? parentCategory Category? @relation("CategorySubCategory", fields: [parentId], references: [id]) subCategories Category[] @relation("CategorySubCategory")
}// ベクターカラムの追加
prismamodel Product { content_embedding Unsupported("vector")? content_for_embedding String? embeddings_generated_at DateTime?
}学び
階層構造は自己参照リレーションで表現
pgvector拡張でベクター検索を実現
インデックス戦略が検索性能に影響
1. AI応答の一貫性
問題: 構造化出力が時々不正な形式で返る
解決:
Zodスキーマで厳密にバリデーション
errorStrategy: "fallback"でエラー時の挙動を制御
プロンプトに「製品名は完全一致」を明記
2. 埋め込み生成のコスト
問題: 全製品の埋め込み生成に時間とコストがかかる
解決:
バッチ処理で段階的に生成
新規製品のみ生成
非同期処理でユーザー体験を維持
3. レート制限の管理
問題: 楽天APIのレート制限を守りつつ効率的に同期
解決:
ロック機構で並列リクエストを制御
リトライロジックの実装
Cronジョブで定期同期
良かった選択
1. Next.js 14 App Router: Server Actionsでシンプルな実装
2. Prisma: 型安全性とマイグレーション管理
3. Google Gemini 2.0 Flash: 日本語理解と構造化出力
4. Supabase: 認証とデータベースを統合管理
改善の余地があった選択
1. Mastra: 学習コストがやや高い(代替案も検討)
2. Vercel KV: コスト面で要検討(Redis代替も検討)
3. 楽天API: レート制限が厳しい(代替データソースも検討)
本プロジェクトでは、AI、RAG、外部API連携を組み合わせた推薦システムを構築しました。特にRAGの実装とセキュリティ対策に注力し、実用的なシステムに仕上げました。
今後、継続的な最適化が必要です。特にRAGの精度向上とコスト最適化が重要!!
このプロジェクトを通じて、モダンなWebアプリケーション開発、AI統合、セキュリティ対策について多くの知見を得られました。同じようなプロジェクトに取り組む方の参考になれば幸いです。
技術スタックまとめ:
Next.js 14 + TypeScript
Supabase + Prisma
Google Gemini 2.0 Flash + Embeddings API
Mastra
pgvector
楽天市場API
主要な成果物:
パーソナライズド推薦システム
RAG検索機能
セキュアなCronジョブ実装
スケーラブルなデータベース設計
