SOLE//MATRIX は、スニーカーの好みを診断し、一般的な勧めやすさと個人らしい納得感を分けて表示する判断支援プロトタイプです。
Core v1 MVP では、診断から推薦結果・説明・feedback skeleton までを Web UI と API Route で一貫して実行できます。score と Decision は TypeScript の決定論的なロジックが確定し、Gemini は説明文の生成だけを補助します。
必要環境は Node.js、pnpm、Git です。
pnpm install
pnpm web:dev開発サーバーは通常 http://localhost:3000 で起動します。
pnpm test
pnpm typecheck
pnpm web:build現時点の package.json に lint script はありません。テストは Vitest、型チェックは tsc --noEmit、production build は Next.js で行います。
8問の診断 / 対応タグ
→ PreferenceVector(0〜100の8軸)
→ local/mock候補
→ Balanced Score
→ Ryo Score
→ Decision
→ Gemini structured explanation または rule-based fallback
→ Recommendation UI
→ Feedback API skeleton / mock repositoryPreferenceVector の軸:
culture / styleFit / simplicity / street
volume / comfort / durability / priceLevelDecision:
strong_buy / consider / wait / avoid / unknownBalanced Score は、既存の recommendSneakers と Core score breakdown を薄い adapter から再利用します。Ryo Score は文化背景、クラシック/レトロ、ストリート、落ち着いた合わせやすさ、趣味としての納得感を別の純粋関数で評価します。
POST /api/core-v1/recommend
{
"diagnosisAnswers": {
"trusted-classic": "like",
"simple-daily": "neutral"
},
"preferenceTags": ["classic", "minimal"],
"budgetYen": 20000
}診断回答または対応タグが少なくとも1つ必要です。予算は任意です。候補は外部商品ではなく、app/_lib/core-v1/repository.ts のローカル仮候補を使います。
POST /api/core-v1/feedback
{
"recommendationId": "core-v1:local-classic-daily",
"sentiment": "helpful",
"comment": "理由が分かりやすかった"
}sentiment は helpful、not_helpful、unsure のいずれかです。現在は process 内の mock repository へ保存し、Supabase 未設定でも落ちません。永続化・認証・本番DB接続はまだ行いません。
Gemini に渡すのは、Core が確定した Decision、Balanced/Ryo Score、安全な候補要約、タグ、予算、rule-based explanation です。
Gemini は以下を行いません。
- score の計算・変更
- Decision の決定・変更
- 実在価格、在庫、URL、真贋の判断
- Rakuten response や個人情報の処理
出力は次の structured JSON schema を検証します。
type GeminiExplanationJson = {
summary: string;
reasons: string[];
cautions: string[];
balancedView: string;
ryoView: string;
finalTone: "positive" | "balanced" | "cautious" | "negative" | "unknown";
};APIキー未設定、通信失敗、HTTPエラー、JSON不正、schema不一致、安全でない表現のいずれでも rule-based explanation へ fallback します。Gemini の API キーは URL へ含めず、request header から送信します。
Rakuten API は Core v1 の商品候補、価格、budgetFit、URL に接続していません。現在の既知状態は blocked_forbidden で、UI には外部商品データを使っていないことを表示します。
RUN_EXTERNAL_SMOKE=1 のときだけ隔離 smoke を明示実行できます。HTTP 200 と response shape が確認できても、normalizer を実装して明示接続するまでは本線へ混ぜません。
.env.example を参照してください。本物の値は commit しません。
GEMINI_API_KEY=
RAKUTEN_APPLICATION_ID=
RAKUTEN_ACCESS_KEY=
RUN_EXTERNAL_SMOKE=
SUPABASE_URL=
SUPABASE_ANON_KEY=Gemini と Supabase は未設定でもアプリが動きます。Rakuten は設定の有無に関係なく Core v1 の推薦候補へは使いません。
app/_lib/core-v1/
diagnosis / PreferenceVector / scoring / Decision
explanation / Gemini provider / readiness
service / repository / validation
app/api/core-v1/
recommend / feedback Route Handlers
app/_components/CoreV1RecommendationPanel.tsx
Recommendation UI / readiness / feedback skeleton
src/core/
既存 recommendSneakers 公開API(Core v1からadapter経由で再利用)設計境界、provider追加、Supabase移行案は docs/core-v1-architecture.md を参照してください。
- 候補は商品カタログではなくローカルの仮候補
- 実在価格、在庫、市場価格、真贋は扱わない
- Rakuten 商品検索は本線へ未接続
- Feedback は process 内 mock のため再起動で消える
- Supabase 本番接続、認証、RLS 運用は未実装
- 検索入力型 UI は補助レーンで、Core v1 の別 Decision ロジックは持たない
既存の recommendSneakers、サンプルデータ、CLI demo、v0.2テストは維持しています。
pnpm demo
pnpm demo:gemini