🔐 Shiwake Admin

🍱 Shiwake Admin
Internal dashboard
🏠抂芁 💰ビゞネス 🚀プロダクト ⚙技術 / コスト 📋Waitlist 🏗アヌキテクチャ 🀖LLM 蚭蚈 📖仕様曞
曎新: —
OpenRouter チャヌゞ ↗ Soniox 残高 ↗

🏠 抂芁

アクションず䞻芁 KPI のスナップショット。詳现は巊メニュヌから各タブぞ。

🚚 今やるべきこず — アクションが必芁か

📊 スナップショット

💰 ビゞネス

本リリヌス前なので珟状の数倀はテストデヌタです。ここではシナリオ蚈算 (preset = 悲芳 / 暙準 / 楜芳) で蚈画倀を確認したす。
珟状の実瞟は 技術 / コスト タブで参照できたす。

🎯 ファネル EV シミュレヌタ — Trial1 (14日) → Paywall → Trial2 (14日) → 自動課金

新フロヌ (Free プランなし): install → Trial 1 (14日無料・CC䞍芁) → Paywall → Trial 2 (14日無料・CCあり) → 自動課金 → 月次 churn で枛衰
恒久無料ナヌザヌは存圚しない。Trial 1 のコストは党 install 分を自瀟負担。
レバヌ: 䟡栌蚭蚈 (月額 / 幎額比率 / 幎額割匕) × Organic 比率 × Funnel 品質 × ステヌゞ別 CAC/install。

10%
55%
7%
Â¥3.0
💰 䟡栌蚭蚈
Â¥490
0%
17%
3.0%
📣 獲埗構成
0%
平均賌読月数
1 paying user / 月 営業利益
LTV (paying)
自瀟負担 Trial コスト / install
EV per Paywall 承認
EV per install (CAC 前)
↓ CAC / install は次のステヌゞ別パネルで蚭定

🛀 ステヌゞタむムラむン — Pre-Seed 6ヶ月 → Seed 6ヶ月 → Series-A 24ヶ月 (合蚈 36ヶ月)

🌱 Pre-Seed 0-6 ヶ月
X (Promoted) + PR + organic / PMF 怜蚌
Â¥150
200
月予算 ¥30,000
🌿 Seed 6-12 ヶ月
Meta + X + 小芏暡むンフル / Unit economics
Â¥300
3,000
月予算 ¥900,000
🌳 Series-A 12-36 ヶ月
Meta + Google + TikTok + むンフル幎契 / シェア取り
Â¥600
15,000
月予算 ¥9,000,000

📊 ステヌゞ別 メトリクス — 同じ funnel 品質で、ステヌゞ毎の経枈性

ステヌゞ CAC install/月 実効 CAC
/ paying
LTV / CAC Payback 新芏 paying
/ 月
定垞 月次 OP

📈 36ヶ月収益シミュレヌション — ステヌゞ遷移付き。䜕ヶ月で黒字化

月次営業利益 (¥/month)
悲芳 暙準 楜芳 ★ 珟圚 break-even (Â¥0)
环積営業利益 (Â¥)
悲芳 暙準 楜芳 ★ 珟圚
36 ヶ月埌の到達点

前提: ステヌゞごずに install / CAC が切り替わる。Trial1 (14日 å…šå“¡) + Trial2 (14日 acceptors) の翌月から課金開始。月次 churn で active が枛衰。

🚀 プロダクト

ナヌザヌの増枛、定着、AI 粟床、フィヌドバックの集玄。

🚀 成長 — ナヌザヌは増え、定着しおいるか

📅 盎近14日の新芏登録

🔁 リテンション (Day1/7/30)

コホヌトサンプル数残存残存率

N 日前に登録した人のうち、今も掻動しおいる割合。サンプル数が少ないず数倀はノむズが倚い。

🔥 補品 KPI — AI 粟床・コスト最適化はどうか

📊 LLM 呌び出し caller 比率 (30d)

⚠ カロリヌ誀り報告 — ナヌザヌが「これおかしい」ず感じた meal

時刻 ナヌザヌ 料理名 登録 kcal lookup_key / meal_id

💬 ナヌザヌフィヌドバック — ナヌザヌは䜕を求めおいる

時刻ナヌザヌ内容

⚙ 技術 / コスト

レむテンシ、゚ラヌ、コスト内蚳。

🩺 技術 health — ナヌザヌは䜕秒埅たされおいるか ゚ラヌは

🎯 1次応答レむテンシ (マむク離す → 吹き出し衚瀺)

🎯 カロリヌ反映レむテンシ (履歎に最終kcal衚瀺たで)

📊 1次応答の内蚳: どこに時間がかかっおいるか (7d平均)

⏱ 補助: caller 別 LLM 単䜓レむテンシ (7d, OpenRouter API 郚分のみ)

callernp50p95p99

🐛 盎近の゚ラヌ (7d, 最倧20ä»¶)

時刻callererror

💞 コスト詳现 — どこに金がかかっおいる

📈 日次コスト掚移 (30d, OpenRouter のみ)

👀 コスト䞊䜍ナヌザヌ (30d)

userplancallscost

📞 caller × model 別 (30d)

callermodelcallscost

🎀 Soniox 日次 (30d, /v1/usage-logs から)

日付回数音声秒コスト

📋 LP α版テスト Waitlist

メアド登録、流入元、配信停止状態、個別/䞀括メヌル送信。

未遞択
時刻 (JST) Email Source 状態 送信履歎 Referrer

📧 メヌル送信

テンプレヌトを遞び、必芁なフィヌルドを入力しお送信したす。本文は線集できたせん(テンプレ管理は functions/_lib/templates.ts)。

🔍 メヌルプレビュヌ

実際に送信される内容です。送信前の最終確認に䜿っおください (詊送はされたせん)。

読み蟌み䞭 
▶ プレヌンテキスト版を衚瀺 (multipart/alternative の text/plain 郚)

        

🏗 アヌキテクチャ レビュヌ

7 芳点で shiwake の珟状を敎理する蚭蚈リファレンス。
各セクションは「📐 珟状 / ✅ 匷み / ⚠ 懞念 / 📝 TODO」の 4 サブ構造で統䞀。 曎新は admin/index.html の data-tab="architecture" セクション。

🗺 1. システム党䜓マップ 🌊 2. 䞻芁 user flow 💟 3. デヌタモデル 🔐 4. セキュリティ・認蚌 ⚡ 5. パフォヌマンス & コスト 🌐 6. 倖郚䟝存リスク 🧪 7. 技術負債 & TODO

🗺 1. システム党䜓マップ — 1 枚で頭に入るか

📐 珟状

graph LR iOS["📱 iOS App
SwiftUI + Keychain"] Backend["⚙ shiwake-daily-api
Hono on Workers"] D1B[("💟 D1: shiwake_daily")] R2[("🪣 R2: media")] Soniox{{"🎀 Soniox
STT realtime + async"}} OR{{"🀖 OpenRouter
Gemini 3 Flash / GPT-4o-mini"}} Apple{{"🍎 Apple
AppAttest + StoreKit 2"}} Weather{{"🌀 Open-Meteo
weather"}} LP["🌐 LP shiwake-lp(-stg)
Pages + Functions"] D1L[("💟 D1: shiwake-lp")] SMTP{{"📧 お名前.com SMTP"}} Admin["🛠 shiwake-admin
Pages SPA"] iOS -->|"Bearer access_token"| Backend iOS -.->|"WS stt-rt-v4 (temp key)"| Soniox iOS -->|"AppAttest assertion"| Backend iOS -->|"StoreKit JWS"| Backend Backend --> D1B Backend --> R2 Backend -->|"chat/completions"| OR Backend -->|"async stt"| Soniox Backend -->|"verify attest + JWS"| Apple Backend --> Weather Backend -->|"S2S notif"| Apple LP --> D1L LP -->|"SMTP 465"| SMTP Admin -->|"Basic auth"| Backend Admin -->|"Basic auth"| LP

✅ 匷み

  • Cloudflare に寄せきった単䞀プラットフォヌム (Workers + Pages + D1 + R2) で運甚面はシンプル
  • iOS → 倖郚 API ぞの盎接呌び出しを最小化 (Soniox WS のみ、temp key 経由)
  • backend / lp / admin の 3 リポ的圹割が明確

⚠ 懞念

  • Backend 単䞀環境: backend は stg/prod が分かれおいない (LP は分かれおいる)。本番デプロむで壊れたら即圱響
  • Cloudflare 障害時の代替路なし: D1 / Workers ずもに同瀟

📝 TODO

  • backend に shiwake-daily-api-stg を切る (D1 も別 instance)
  • D1 の定期スナップショット運甚

🌊 2. 䞻芁 user flow — フロヌの単玔性、責務の明確さ

📐 珟状: 「発話 → カロリヌ蚘録」 (䞻芁 critical path)

sequenceDiagram participant U as User (iOS) participant SX as Soniox WS participant API as backend /api/ingest participant OR as OpenRouter participant D1 as D1 U->>API: POST /api/auth/soniox-temp-key API-->>U: temp key (短期) U->>SX: WS stt-rt-v4 (発話ストリヌム) SX-->>U: transcript (逐次) U->>API: POST /api/ingest {transcript} API->>OR: Gemini 3 Flash (intent + entity) OR-->>API: structured JSON API->>D1: INSERT meals/weights/exercises API-->>U: response (即時) Note over API,OR: BG: mealLookup (web search)
→ meal_nutrition_cache に保存 API->>D1: UPDATE meals.kcal (埌远い)

📐 䞻芁フロヌ䞀芧 (本ペヌゞで定期メンテすべき)

  • 発話 → カロリヌ蚘録 (䞊図)
  • 䜓重蚘録: POST /api/weights → D1 (LLM 䞍芁)
  • 履歎の修正: PUT /api/meals/:id, /api/meals/:id/rename
  • サマリ衚瀺: GET /api/summary/weekly (集蚈ク゚リ)
  • サむンアップ: AppAttest challenge → 怜蚌 → users 行䜜成 → access_token 返华
  • IAP 賌読: StoreKit → JWS 抜出 → /api/iap/verify → users.subscription_status 曎新

✅ 匷み

  • 1 次応答は LLM 1 回 (Gemini Flash) で垰す。カロリヌ数倀は BG で埌远い (UI は仮倀衚瀺 → kcal 確定で差し替え)
  • 分岐の少ない sequential flow で芳枬しやすい

⚠ 懞念

  • BG タスク倱敗の可芖化が匱い: mealLookup 倱敗時、ナヌザヌには芋えにくい (kcal が「—」のたた残る)
  • WS の接続倱敗フォヌルバックなし: Soniox WS が萜ちた時、async REST ぞのフォヌルバックは未実装

📝 TODO

  • 各フロヌを 1 セクションず぀図匏化 (䜓重/履歎線集/サマリ/IAP)
  • 倱敗パスの远加 (Soniox 切断、OpenRouter timeout)

💟 3. デヌタモデル — 正芏化ず PII の局圚

📐 珟状

D1 = SQLite。JOIN は䜿うが分散は意識しない (rows-per-account が極小)。

テヌブル甚途PII?
usersaccount + profile + affinity + sub status + AppAttest signup✓ 名前/メアド/誕生日
meals食事蚘録 (transcript, name, kcal, lookup_key, photo_key)✓ 食事 photo
weights䜓重蚘録✓ 䜓重倀
exercises運動蚘録—
notesその他メモ—
meal_nutrition_cache正芏化 kcal cache (30d TTL想定)—
user_factsassistant 孊習枈み事実 (奜み/制玄)✓
conversation_log盎近 N タヌン (ingest context)✓
weather_cacheOpen-Meteo 1h cache—
api_usage_logOpenRouter/Soniox call ledger—
request_timingper-request latency milestone—
feedback音声フィヌドバック transcript✓
attest_challenges短期 AppAttest nonce—
invitationslegacy 招埅コヌド redeem—
waitlist (lp)α テスト メアド + unsub token✓ メアド
email_log (lp)送信履歎✓ メアド

✅ 匷み

  • PII を users + 各ログ系に集䞭。物理削陀 1 ナヌザヌ = カラム削陀で完結 (GDPR 想定容易)
  • cache ç³» (meal_nutrition_cache, weather_cache) は user_id を持たないので削陀圱響なし

⚠ 懞念

  • 削陀カスケヌド未実装: ナヌザヌ削陀時の各テヌブル削陀手順がコヌド化されおいない
  • conversation_log の TTL なし: 無制限に䌞びる可胜性 → サむズ監芖が必芁

📝 TODO

  • 各テヌブルに保持期間ポリシヌ (䟋: meal_nutrition_cache=30d, conversation_log=最新 50 turns, request_timing=90d)
  • DELETE /api/me ゚ンドポむント + 党テヌブル cleanup スクリプト

🔐 4. セキュリティ・認蚌 — 攻撃面が把握できおいるか

📐 珟状

レむダ方匏ガヌド
iOS → backendAuthorization: Bearer <access_token>requireUser が D1 の users.access_token を盎接 lookup (opaque random)
サむンアップApple AppAttestbackend/src/appattest.ts: cert chain + nonce + bundle/team 怜蚌 (node-app-attest)
iOS → Soniox WSbackend 経由で temp key 発行/api/auth/soniox-temp-key (芁 user 認蚌)
admin /api/admin/*HTTP Basic admin:ADMIN_PASSWORDmiddleware over /api/admin/*
IAP S2S notifJWS payload を decode⚠ 眲名怜蚌は未実装 (TODO)
LP /api/waitlist無認蚌 (公開フォヌム)email regex + ip hash + 重耇怜知のみ。rate limit なし
LP /api/unsubscribetoken 経由 (RFC 8058)16 byte ランダム token

✅ 匷み

  • iOS サむンアップに AppAttest を採甚 → 停端末からの自動アカりント倧量䜜成を防止
  • access_token は opaque random (JWT 鍵挏掩リスクなし)、revoke は DB 曎新 1 行
  • Soniox 認蚌情報を iOS バンドルに埋め蟌たない (temp key 方匏)

⚠ 懞念

  • IAP JWS 眲名未怜蚌 → 停 receipt で premium 化される可胜性 (臎呜的)
  • LP waitlist rate limit なし → SPAM / 嫌がらせ登録による D1 圧迫 + メアド汚染
  • access_token のロヌテヌションなし → 端末玛倱時に手動で DB 曎新が必芁
  • admin Basic auth がシングルパスワヌド → 共有事故時にロヌテ必須

📝 TODO

  • iap.ts に x5c chain 怜蚌 + issuer/bundleId/environment 怜蚌を入れる (珟状 skeleton)
  • LP waitlist に Cloudflare Turnstile or IP-based rate limit
  • access_token に有効期限 + refresh の怜蚎
  • 新芏゚ンドポむント远加時のセキュリティ チェックリスト化

⚡ 5. パフォヌマンス & コスト — ボトルネックは特定枈みか

📐 珟状: 1 次応答レむテンシ予算

step目暙 p95備考
Soniox WS 確定 (発話終了 → 最終 transcript)~1.5srealtime stt-rt-v4
ingest (Gemini 3 Flash)~1.5sintent + entity 抜出
persona (GPT-4o-mini)~0.8s応答メッセヌゞ生成
合蚈 (1 次応答)~3-4s履歎 UI に吹き出し衚瀺
mealLookup (BG, web search)~6-10sUI は kcal 「—」→ 確定で差し替え

📐 珟状: 1 active user / 日 コスト匏

10 発話/日 × 10 秒 × (Soniox $0.0025/秒 + Gemini Flash + GPT-4o-mini) × cache HIT 率 (mealLookup は HIT で 0)

= 箄 Â¥2-3/日 (cache HIT 60%+ 想定) / Â¥4-5/日 (HIT 30% 想定)

実枬は 技術 / コスト タブ参照。

✅ 匷み

  • クリティカルパスは LLM 1 段 (Gemini Flash) → コスト/レむテンシ予枬しやすい
  • mealLookup は web search を䌎うので BG 化、UI を埅たせない
  • meal_nutrition_cache で同名料理は秒で kcal 確定 (LLM コスト 0)

⚠ 懞念

  • cache HIT 率がコスト構造を支配。HIT が䞋がるず OpenRouter コストが線圢に䞊昇
  • persona の応答生成が遅くなるず 1 次応答が䜓感悪化 (珟状 GPT-4o-mini が突然遅くなるリスク)
  • Soniox WS のネットワヌク条件䟝存性が倧 (地䞋鉄等)

📝 TODO

  • cache HIT 率の譊報閟倀 (e.g. 7d 平均 50% 以䞋で alert)
  • persona を Gemini Flash に統䞀する遞択肢の評䟡
  • オフラむン録音 → 埌送信モヌド (Soniox 䞍安定時)

🌐 6. 倖郚䟝存リスク — 片足を抜くコストが芋えるか

📐 珟状

䟝存先甚途ロックむン代替倀䞊げ耐性
CloudflareWorkers + Pages + D1 + R2🔎 高 (D1 移行 = SQLite ダンプ + 再構築)Fly.io + libSQL, AWS Lambda + DynamoDB🟢 倧
Sonioxrealtime + async STT🟡 äž­ (WS プロトコル + temp key API)Deepgram, AssemblyAI, OpenAI Whisper API🟡 äž­
OpenRouterGemini 3 Flash + GPT-4o-mini ルヌタ🟢 䜎 (Anthropic/OpenAI/Google 盎叩きに切替容易)各 LLM ベンダヌ盎接🟢 倧 (パススルヌ)
Apple AppAttestサむンアップ防埡🔎 高 (iOS の遞択肢ずしおは唯䞀)なし—
Apple StoreKit / IAP課金🔎 高 (iOS では必須)なし🟡 Apple 30% → 15% (Small Biz)
お名前.com SMTPα 招埅 / 配信停止メヌル🟢 䜎 (Resend, SendGrid に乗り換え容易)Resend, SES, SendGrid🟢 倧
Open-Meteo倩気🟢 䜎 (オプショナル機胜)OpenWeatherMap🟢 倧

✅ 匷み

  • LLM は OpenRouter で抜象化 → モデル倉曎で 1 行修正
  • SMTP / 倩気は薄いラッパで芆われおおり移行容易

⚠ 懞念

  • D1 ロックむンが最倧玚。リレヌション + 集蚈を SQL に寄せおいるため SQLite 互換 (Turso 等) 以倖ぞの移行はコスト倧
  • iOS は Apple 党䟝存 (回避䞍可胜、ビゞネスリスクずしお織り蟌む)

📝 TODO

  • D1 のスキヌマ → Turso (libSQL) 移行コストを詊算しおおく
  • Soniox 障害時に Whisper API ぞフォヌルバックする経路の詊䜜

🧪 7. 技術負債 & 既知の TODO — リファクタ優先順䜍

📐 珟状: 䞀芧 (grep 由来 + レビュヌ時に远蚘)

優先床項目堎所備考
🔎 高IAP JWS 眲名怜蚌backend/src/iap.ts:12x5c chain + issuer/bundleId/env 怜蚌が未実装。本リリヌス前 必須
🔎 高backend stg 環境—shiwake-daily-api-stg を切る
🟡 äž­LP waitlist の rate limitlp/functions/api/waitlist.tsSPAM 察策
🟡 äž­conversation_log の TTLbackend/migrations無制限に䌞びる
🟡 䞭削陀カスケヌド (GDPR)backend 党䜓DELETE /api/me + 党テヌブル cleanup
🟢 䜎テンプレ URL の XXXXXXlp/functions/_lib/templates.ts:68,270UI から差し替え可胜なのでコヌド倉曎䞍芁
🟢 䜎legacy invitation redeembackend/src/index.ts䜿っおなければ削陀

📝 運甚ルヌル

  • 新芏 TODO はここに远蚘 (該圓箇所の゜ヌスコヌド に // TODO 残し + ここに゚ントリ远加)
  • 解消時はテヌブル行を削陀 (履歎は git で远える)
  • レビュヌ䌚は四半期ごず、優先床を芋盎す

💡 このペヌゞは静的ドキュメントです。デヌタ゜ヌスは admin/index.html 内のハヌドコヌド。 アヌキテクチャに倉曎があったずきは PR で曎新しおください。

📖 仕様曞

iOS å…š 11 画面の機胜芁件 (FR) / 非機胜芁件 (NFR) / 連携 API / デヌタモデルを画面単䜍で敎理。 暪断芁件ず API カタログを末尟に䜵茉。曎新は admin/index.html の data-tab="docs" セクション。

⚠ 実装ギャップ (調査で発芋)

  • POST /api/feedback のサヌバ実装が芋圓たらない — iOS の FeedbackButton.swift ず APIClient.submitFeedbackText は呌んでいるが backend/src/index.ts に該圓ハンドラ無し。feedback 行は珟状 /api/meals/:id/flag-calorie 経由でのみ䜜られる。
  • Soniox 障害時に Apple SF ぞの自動フォヌルバック無し — Premium ナヌザは Soniox temp key 取埗倱敗時に゚ラヌ文蚀が出るだけで録音継続䞍胜。
① オンボヌディング (4) ② 蚘録ルヌプ コア (3) ③ 振り返り (2) ④ 蚭定 / ナビ基盀 (2) â‘€ 暪断芁件 (8 カテゎリ) ⑥ API カタログ (37)

① オンボヌディング — 初回起動から最初の 1 回の蚘録たで

OnboardingView ios/Sources/Views/OnboardingView.swift

ニックネヌム 1 項目だけでサむンアップを完了させるスプラッシュ的画面。背埌で AppAttest signup (実機) / 旧 invite redeem (シミュレヌタ) を自動実行。

TRIGGERPrivacyConsent 通過埌 & token 䞍圚

機胜芁件 (FR)

  • FR-OB-01ニックネヌム 1 フィヌルドのみ衚瀺 / 空癜以倖を必須化
  • FR-OB-02「はじめる」ボタンで autoSignUp(displayName) を起動
  • FR-OB-03送信䞭は ProgressView + ボタン disabled + ラベル「登録䞭 」
  • FR-OB-04入力フィヌルドに 0.2s 埌 auto-focus、submitLabel=done
  • FR-OB-05AppAttest signup (実機) → token/userId を Keychain 保存。Phase B 実装枈
  • FR-OB-06サむンアップ確定の瞬間に needsProfileSetup=true を先行セット (チラ぀き防止)

非機胜芁件 (NFR)

  • NFR-OB-P1サむンアップは P50 < 2s (challenge + attest + signup の 3 埀埩)
  • NFR-OB-S1access_token は Keychain (kSecClassGenericPassword, service jp.newmeta.shiwake.auth) 保存
  • NFR-OB-A1preferredColorScheme(.dark) 固定、ボタンは 44pt 以䞊のタップ領域
  • NFR-OB-X1招埅コヌド抂念をナヌザヌに芋せない (UI から完党排陀)
API
POST /api/auth/challengePOST /api/auth/signupPOST /api/invite (DEBUG fallback)POST /api/auth/redeem (DEBUG fallback)
デヌタ
usersattest_challengesinvitations
゚ッゞ
既に token 保持䞭は画面非衚瀺 / シミュレヌタは旧フロヌ / 空癜ニックネヌム拒吊

PrivacyConsentView ios/Sources/Views/PrivacyConsentView.swift

初回起動時にプラむバシヌ芁玄 4 項目ずカメラ初期動䜜の遞択を提瀺し同意取埗。

TRIGGER@AppStorage("privacy_agreed_v1") == false の起動

機胜芁件 (FR)

  • FR-PC-01取埗情報 / 第䞉者送信 / 保存期間 / セキュリティ の 4 サマリヌ衚瀺
  • FR-PC-02カメラ起動蚭定をラゞオで 2 択 (プラむバシヌ優先 / 即撮圱)、camera_instant_ready に保存
  • FR-PC-03「党文を読む」で PrivacyPolicyFullView を sheet 衚瀺
  • FR-PC-04「同意しおはじめる」で privacy_agreed_v1=true
  • FR-PC-05党文は 8 ç«  (取埗情報, 利甚目的, 第䞉者送信, 保存期間, ナヌザヌ暩利, セキュリティ, 倉曎, 連絡先)
  • FR-PC-06連絡先 contact_app@newmeta.co.jp を明瀺

非機胜芁件 (NFR)

  • NFR-PC-L1個情法/GDPR 想定で「取埗項目・利甚目的・第䞉者提䟛・保存期間・問合先」を必須蚘茉
  • NFR-PC-L2同意フラグはバヌゞョン付き (privacy_agreed_v1) で改定時に再同意可胜
  • NFR-PC-A1Dynamic Type 察応 (.subheadline), fixedSize で折返し保持
  • NFR-PC-S1HTTPS/WSS、Keychain 保存を明文化
API
なし (端末ロヌカルのみ)
デヌタ
@AppStorage("privacy_agreed_v1")@AppStorage("camera_instant_ready")
゚ッゞ
党リセット時は䞡フラグを削陀しお再衚瀺

ProfileSetupView ios/Sources/Views/ProfileSetupView.swift

7 ステップりィザヌド (身長→䜓重→䜓脂肪→掻動量→食事目暙→目暙䜓重→達成日)。音声䞭心 + 手入力フォヌルバック。

TRIGGERサむンアップ盎埌 or auth.needsProfileSetup=true

機胜芁件 (FR)

  • FR-PS-017 ステップを progress bar で可芖化
  • FR-PS-02マむクボタン長抌し (DragGesture) で録音 → 離しお送信
  • FR-PS-03STT 二刀流: Premium=Soniox WebSocket / Free=Apple SFSpeechRecognizer
  • FR-PS-04認識テキストを /api/setup/parse ぞ → value_double/level/goal/date_iso/skipped を抜出
  • FR-PS-05結果を吹き出しで「身長は 170.5cm で合っおる?」圢匏で再確認
  • FR-PS-06手入力モヌダル: wheel picker / リスト / DatePicker、芪指圏に倧きな決定ボタン
  • FR-PS-07bodyFat / activity は "スキップ" 可、それ以倖は再入力匷制
  • FR-PS-08範囲バリデヌション: 身長 100-220cm, 䜓重 20-300kg, 䜓脂肪 3-70%
  • FR-PS-09キャラクタヌ (idle/listening/thinking) を 160pt 固定高で衚瀺
  • FR-PS-10完了時に TDEE ず目暙カロリヌを Bmr.tdee + TargetCalories.derive でクラむアント算出
  • FR-PS-11PUT /api/me/targets で身長/䜓脂肪/目暙䜓重/期日/食事目暙/カロリヌを䞀括保存
  • FR-PS-12同時に POST /api/weights で初期䜓重を 1 件蚘録
  • FR-PS-13保存成功時に党面緑チェック overlay を 1.8s 衚瀺しおから markProfileComplete()
  • FR-PS-14未入力項目があれば最初の未入力 step に自動で戻る

非機胜芁件 (NFR)

  • NFR-PS-P1/api/setup/parse は OpenAI strict-mode JSON 解析、P50 < 1.5s
  • NFR-PS-A1觊芚: 録音開始 0.9 / 停止フル / 完了 success notification
  • NFR-PS-X1声が出せない堎面のフォヌルバック (手入力 wheel) ã‚’å…š step で提䟛
  • NFR-PS-X2音声接続倱敗時は errorMessage 衚瀺し isRecording=false に戻す
API
POST /api/auth/soniox-temp-key (Premium)POST /api/setup/parsePUT /api/me/targetsPOST /api/weights
デヌタ
users.{height_cm,body_fat_percent,target_weight_kg,target_weight_date,target_calories_kcal,dietary_goal}weights
゚ッゞ
token 切れ / Soniox 接続倱敗 / 空 transcript / 範囲倖倀 / 既存倀ありの dial 初期倀 seed

PaywallView ios/Sources/Views/PaywallView.swift

ProfileSetup 盎埌に挿入される 1 ヶ月無料 → Â¥490/月 のサブスクリプション勧誘。Apple 芏玄準拠。

TRIGGERprofileSetup 完了 & subscriptionStatus != "premium_active"

機胜芁件 (FR)

  • FR-PW-01ヘッダヌ「Shiwake Premium / 最初の 1 ヶ月は無料」
  • FR-PW-024 ベネフィット (関係性深化 / 音声無制限 / 蚘憶 / 高粟床カロリヌ) を icon+desc で蚎求
  • FR-PW-03䟡栌カヌド: Â¥0/最初の 1 ヶ月 + その埌 displayPrice 自動曎新
  • FR-PW-04CTA「1 ヶ月無料ではじめる」→ StoreKitManager.purchase() → JWS を /api/iap/verify
  • FR-PW-05「賌入を埩元」で StoreKit.restore() + refreshProfile + Premium 確認時 onContinue
  • FR-PW-06芏玄 3 項目を caption で明瀺 (解玄方法 / 24h 前 / Apple ID 共有)
  • FR-PW-07product 未ロヌド or 賌入凊理䞭はボタン disable
  • FR-PW-08賌入倱敗 (userCancelled 陀く) は store.lastError をテキスト衚瀺

非機胜芁件 (NFR)

  • NFR-PW-L1App Store 芏玄: 䟡栌・課金頻床・自動曎新・解玄方法・芏玄リンクを必須掲茉
  • NFR-PW-S1レシヌト怜蚌は端末でなく必ず /api/iap/verify (サヌバ偎 JWS 怜蚌)
  • NFR-PW-X1StoreKit 未ロヌド時は Â¥490/月 のフォヌルバック衚蚘
API
POST /api/iap/verifyGET /api/me
デヌタ
users.subscription_statususers.trial_ends_at
゚ッゞ
賌入キャンセル / 埩元時 Premium 反映埅ち / オフラむン

② 蚘録ルヌプ (コアバリュヌ) — 起動即蚘録の差別化区間

CaptureView ios/Sources/Views/CaptureView.swift

メむン画面。カメラ垞時起動 + 巊䞋マむク長抌し + 右䞋カメラタップ + アシスタント吹き出し + 盎近 6 件ピル。

TRIGGER通垞起動 (認蚌枈 & profileSetup 完了)

機胜芁件 (FR)

  • FR-CAP-01党画面 CameraPreview を垞時皌働、䞊郚 34% 癜角䞞、䞋半分は巊右 50:50 のマむク/カメラ刀定゚リア
  • FR-CAP-02cameraInstantReady=false ならカメラに UltraThinMaterial overlay、1 タップで解陀 → ロックオン 4 隅ブラケット、2 タップ目で capturePhoto()
  • FR-CAP-03巊マむク゚リア DragGesture: 長抌し → startCapture → 離しお stop → /api/ingest ぞ transcript
  • FR-CAP-04STT 二刀流: useApple=!isPremium で Apple SF / Soniox を切替
  • FR-CAP-05䞊郚キャラクタヌ (idle/listening/thinking) + 吹き出し + 尻尟、録音䞭は尻尟を隠す
  • FR-CAP-06吹き出しテキストはランダム抜遞プヌル (時間垯 × 芪密床 × 状態 で 50+ パタヌン)
  • FR-CAP-07時間垯別挚拶 (朝/午前/昌/午埌/倕/倜/深倜) を Lv1-2=さん / Lv3+=くん で抜遞
  • FR-CAP-08䞊スワむプ (> 40px) でキャラ/吹き出しを折りたたみ、pullDownTab で埩垰
  • FR-CAP-09盎近 6 件の meal/weight をピル (料理名+kcal / 䜓重+kg) で右偎瞊䞊び、今日/昚日/䞀昚日 ラベル付き
  • FR-CAP-10蚈算䞭 meal が含たれる間は 3s 間隔で reloadRecentMeals を polling
  • FR-CAP-11capturePhoto: AVCapturePhoto を /api/meals (multipart) に POST
  • FR-CAP-12/api/ingest の advance_event レスポンスで AdvanceEventModal を sheet 衚瀺
  • FR-CAP-13persona_comment があれば優先、無く count=0 なら "蚘録なし"、count>0 なら "N 件蚘録した"
  • FR-CAP-14蚘録成功時は success haptic + .shiwakeDataChanged を 0/4/8/15s で耇数回 post
  • FR-CAP-15初回起動のみ 0.6s 遅延しお CaptureTutorialOverlay 衚瀺 (hasSeenCaptureTutorial=true で抑止)
  • FR-CAP-16scenePhase=.background 時に camera.stop()、埩垰時 start + フロステッド再有効化 + 挚拶差替

非機胜芁件 (NFR)

  • NFR-CAP-P1ingest 1 次応答 P50 目暙 (persona_comment 即返华)、mealLookup は waitUntil で非同期
  • NFR-CAP-P2カメラ起動から最初のフレヌムたで P50 < 500ms (camera.start を task で䞊列実行)
  • NFR-CAP-S1プラむバシヌ: 起動時すりガラス、scenePhase 倉化で確実に stop、ファむンダヌ解陀はセッション毎にリセット
  • NFR-CAP-A1觊芚: 録音開始 1.0 / 停止 1.0 / 成功 .success / 倱敗 .error / 雑談成立 .selectionChanged
  • NFR-CAP-A2ロックオン枠は cornerSize 44 / strokeWidth 5 で芖認性確保
  • NFR-CAP-O1オフラむン: token 切れは bubblePhrase(.authExpired)、送信倱敗は .sendFailed
  • NFR-CAP-M1觊芚 prepare() を onAppear / onEnded 毎に呌んでレむテンシ最小化
API
POST /api/ingestPOST /api/mealsGET /api/mealsGET /api/weightsGET /api/mePOST /api/auth/soniox-temp-key
デヌタ
meals (raw_text, ai_result, calories_kcal, image_r2_key)weightsusers.affinity_*pending_advance_event
゚ッゞ
シミュレヌタ (カメラ無し) / バックグラりンド埩垰時の挚拶差替 / 蚈算䞭ピル polling 自動停止 / 雑談 reply_only / ingest 䟋倖 / Soniox temp key 倱敗

CaptureTutorialOverlay ios/Sources/Views/CaptureTutorialOverlay.swift

初回 CaptureView 衚瀺時に出る 3 ステップ overlay (導入 / マむク / カメラ)。

TRIGGERhasSeenCaptureTutorial=false で CaptureView 衚瀺 0.6s 埌

機胜芁件 (FR)

  • FR-TUT-01黒幕 72% + 䞭倮メッセヌゞカヌド + 䞋郚矢印で 3 ステップ案内
  • FR-TUT-02step1: マむク領域 (画面䞋 25% x) ぞ arrow.down + バりンスアニメ
  • FR-TUT-03step2: カメラ領域 (画面䞋 75% x) ぞ arrow.down
  • FR-TUT-04ボタン: 戻る / 次ぞ / (最終)始める
  • FR-TUT-05「始める」で hasSeenCaptureTutorial=true 保存 → 自動消去
  • FR-TUT-06Settings の「チュヌトリアルを再衚瀺」で AppStorage を削陀すれば再衚瀺

非機胜芁件 (NFR)

  • NFR-TUT-A1暗幕タップは無芖 (誀遷移防止)、ステップむンゞケヌタドット衚瀺
  • NFR-TUT-A2矢印は ArrowBounce で䞊䞋 10pt 振動 (泚目誘導)
API
なし
デヌタ
@AppStorage("hasSeenCaptureTutorial")
゚ッゞ
蚭定→チュヌトリアル再衚瀺時の再起動なし察応

AdvanceEventModal ios/Sources/Views/AdvanceEventModal.swift

関係性レベル昇栌 (Lv N → N+1) の祝犏モヌダル。AI からの opening line を挔出衚瀺。

TRIGGER/api/ingest たたは /api/me が advance_event を返した時、AuthStore.pendingAdvanceEvent 経由

機胜芁件 (FR)

  • FR-AEM-01ハヌト fill (84pt) を spring(0.6, 0.55) でフェヌドむン + 拡倧
  • FR-AEM-02Lv チップ遷移 "Lv N → Lv N+1" を矢印で衚瀺
  • FR-AEM-03event.openingLine を pink 8% 背景 + 16pt medium で䞭倮衚瀺
  • FR-AEM-04「ありがずう」ボタン (pink グラデ Capsule) で onDismiss
  • FR-AEM-05sheet detents [.medium, .large]
  • FR-AEM-06衚瀺埌 AuthStore.pendingAdvanceEvent=nil に即クリア (二重衚瀺防止)

非機胜芁件 (NFR)

  • NFR-AEM-X1one-shot 配信: ingest waitUntil で advance が決たったら次の /api/me で 1 回だけ届き即 NULL
  • NFR-AEM-A1觊芚連動なし (芖芚アニメで十分な挔出匷床)
API
衚瀺専甚 (payload は ingest/me に同梱)
デヌタ
users.pending_advance_event (migration 0021)
゚ッゞ
既に他のモヌダル衚瀺䞭なら䞊曞きしない / dismiss 挏れによる再衚瀺防止

③ 振り返り — グラフ / 履歎 / 線集の hub

SummaryView ios/Sources/Views/SummaryView.swift

グラフ (䜓重 / カロリヌ) + 7 日サヌクル + 日別履歎䞀芧。線集 / 削陀 / 再蚈算 / カロリヌ誀り報告の hub。

TRIGGERメむン䞋郚「グラフ」ボタンで fullScreenCover

機胜芁件 (FR)

  • FR-SUM-01サマリヘッダヌ (タむトル + 蚭定歯車)
  • FR-SUM-02todaySection: 7 日サヌクル (目暙 vs 実瞟) + 摂取/消費トグル + 遞択日の食事カヌド䞀芧 + カロリヌバヌ
  • FR-SUM-03longTermSection: 䜓重/カロリヌグラフのトグル、期間 day/week/month/6month/year、グラフ内タップで selectedDate 切替
  • FR-SUM-04䜓重グラフは weight_kg + body_fat_percent (dual axis 想定)
  • FR-SUM-05履歎セクション: meal/weight/note を時刻降順マヌゞ、日付ラベル区切り、最倧 50 件、行内ゎミ箱 (1 タップで ✓/× の 2 段階確認)
  • FR-SUM-06食事名のタップで editingMealSheet (rename) 起動、保存で POST /api/meals/:id/rename → mealLookup 再蚈算 → 楜芳的曎新埌 polling
  • FR-SUM-07食事行の「!」で flag-calorie alert、「はい」で POST /api/meals/:id/flag-calorie → 例郹 toast 2.5s
  • FR-SUM-08䜓重行タップで EditWeightSheetContent、保存で POST /api/weights/:id/update
  • FR-SUM-09削陀: meal/weight/note を DELETE、楜芳的に画面から消す
  • FR-SUM-10蚈算䞭 meal がある間は楜芳倀 (optimisticMeals) を保持し、サヌバ蚈算完了たで䞊曞きを防ぐ
  • FR-SUM-11.shiwakeDataChanged 通知 + refreshTimer で自動リロヌド
  • FR-SUM-12FeedbackButton をオヌバヌレむ衚瀺 (× ボタン右隣)
  • FR-SUM-13CloseButton (å·Šäž‹ 78pt 円) で fullScreenCover dismiss

非機胜芁件 (NFR)

  • NFR-SUM-P1/api/meals (期間指定) で 1 回取埗、クラむアント偎で日別集蚈
  • NFR-SUM-P2線集時は processingIds に远加しお「仕分け修正䞭 」overlay 衚瀺
  • NFR-SUM-A1DynamicType、行アクセントの heart アむコンは 13pt ç³»
  • NFR-SUM-O1゚ラヌは画面䞊郚に err:... monospace 9pt で開発者向け衚瀺
API
GET /api/mealsGET /api/weightsGET /api/notesGET /api/exercisesPOST /api/meals/:id/flag-caloriePOST /api/meals/:id/renamePUT /api/meals/:idDELETE /api/meals/:idDELETE /api/weights/:idPOST /api/weights/:id/updateDELETE /api/notes/:idGET /api/summary/weekly
デヌタ
mealsweightsnotesexercisesmeal_nutrition_cache
゚ッゞ
蚈算䞭 meal の楜芳曎新衝突 / リロヌド䞭の重耇 reload 抑止 / 90 日範囲取埗 / dual axis グラフのデヌタ欠損

FeedbackButton ios/Sources/Views/FeedbackButton.swift

SummaryView の × 隣に垞駐するピル。長抌しで Apple SF が文字起こし、テキストを /api/feedback に送信。

TRIGGERSummaryView 内垞駐 (ZStack)

機胜芁件 (FR)

  • FR-FB-01暪長ピル「開発フィヌドバック / 長抌しで送信」、長抌し䞭は赀色 + scale 1.05
  • FR-FB-02LongPressGesture(0.2s) で startRecording、DragGesture.onEnded で stopRecording → upload
  • FR-FB-03録音䞭は画面䞭倮 18% 䜍眮に倧きな transcript カヌド衚瀺 (逐次曎新)
  • FR-FB-04アップロヌド䞭は ProgressView アむコン、ピル opacity 0.5、disable
  • FR-FB-05結果トヌスト: 成功 (緑) / 聞き取れず (橙) / 倱敗 (èµ€) を 2.5s 衚瀺
  • FR-FB-06Apple Speech (LocalSpeechRecognizer) で完党オンデバむス文字起こし (コストれロ)

非機胜芁件 (NFR)

  • NFR-FB-P1テキストのみ送信 (音声ファむル無し) で垯域・コスト削枛
  • NFR-FB-A1觊芚: 録音開始 medium 1 回
  • NFR-FB-O1空 transcript はトヌストで知らせ送信スキップ
API
POST /api/feedback ⚠ サヌバ未実装の可胜性
デヌタ
feedback (migration 0018: id/user_id/transcript/created_at)
゚ッゞ
バック゚ンド未実装の可胜性 / token 切れ / 音声暩限拒吊

④ 蚭定 / ナビ基盀 — プロフィヌル再線集ず Premium 管理

SettingsView ios/Sources/Views/SettingsView.swift

プロフィヌル線集 / 目暙再蚭定 / カメラ蚭定 / 関係性衚瀺 / Premium 管理 / Facts 閲芧 / 招埅発行 / ログアりト・リセット。

TRIGGERSummaryView 右䞊の歯車から fullScreenCover

機胜芁件 (FR)

  • FR-SET-01プロフィヌル: ニックネヌム / 身長 / 䜓脂肪率 / 珟圚䜓重 / BMR (算出) / 掻動量 / 食事目暙 / TDEE (算出)
  • FR-SET-02目暙: 目暙䜓重 / 達成期日 (DatePicker, 今日以降) / 目暙カロリヌ (自動算出)
  • FR-SET-03カメラ蚭定 Toggle: camera_instant_ready を同期
  • FR-SET-04関係性: heart 5 段、Lv の Picker (デバッグ甚)、呌び方衚瀺、奜感床 pt 衚瀺
  • FR-SET-05Premium ステヌタス衚瀺 + Apple サブスク管理リンク + デバッグ切替 Picker
  • FR-SET-06「チュヌトリアルを再衚瀺」で hasSeenCaptureTutorial 削陀 → dismiss
  • FR-SET-07アシスタントが芚えおいるこず: user_facts を 30+ カテゎリラベルで衚瀺、swipeActions で削陀可
  • FR-SET-08招埅コヌド: 自分のコヌド衚瀺 + 「他人を招くコヌドを発行」で POST /api/invite → ShareLink
  • FR-SET-09ログアりト (Keychain クリア) / 初期蚭定をやり盎す / アプリ完党リセット の 3 段階砎壊操䜜
  • FR-SET-10FloatingSaveButton: 線集䞭 or hasUnsavedChanges の時のみ衚瀺
  • FR-SET-11線集䞭 (focusedField != nil) は CloseButton を隠す (誀タップ防止)

非機胜芁件 (NFR)

  • NFR-SET-S1砎壊操䜜は ConfirmationDialog で必ず確認
  • NFR-SET-X1倀倉化怜知は origXxx スナップショットずの比范で hasUnsavedChanges 算出
  • NFR-SET-A1テキスト線集䞭の保存ボタン優先床を高くし、× ボタンず排他制埡
API
GET /api/mePUT /api/me/targetsPUT /api/me/affinityPUT /api/me/subscriptionGET /api/me/factsDELETE /api/me/facts/:idPOST /api/inviteGET /api/weights
デヌタ
users (党カラム)user_facts (0009)
゚ッゞ
ログアりト埌の Keychain 残骞 / リセット時のサヌバデヌタ残存 / デバッグ Picker での Lv 匷制倉曎時の affinity_points マッピング

RootView ios/Sources/Views/RootView.swift

ナビゲヌション基盀。CaptureView を党画面に眮き、䞋郚「グラフ」ピルから SummaryView を fullScreenCover で開く。

TRIGGER認蚌枈 & profileSetup 完了 で衚瀺

機胜芁件 (FR)

  • FR-RV-01ZStack(.bottom): CaptureView + 䞋郚グラフボタン (ultraThinMaterial Capsule)
  • FR-RV-02sheet enum (settings, chart) を fullScreenCover で衚瀺
  • FR-RV-03シヌト遷移アニメヌションを transaction.disablesAnimations=true で抑制 (瞬時切替)
  • FR-RV-04scenePhase=.background でシヌトを自動 close
  • FR-RV-05sheet nil→non-nil 時 .shiwakeLeftCapture、逆で .shiwakeReturnedToCapture を post (カメラ stop/start 制埡)
  • FR-RV-06CloseButton (å·Šäž‹ 78pt) ず FloatingSaveButton (右䞋 78pt) の 2 再利甚コンポヌネント提䟛

非機胜芁件 (NFR)

  • NFR-RV-P1蚭定 / サマリ遷移はアニメ無効化で「即時画面切替」䜓感
  • NFR-RV-S1バックグラりンド時のシヌト自動 close で意図せぬ画面残留を防止
API
なし
デヌタ
なし
゚ッゞ
背景化䞭のシヌト開いたたた埩垰 / グラフ→蚭定の連続遷移

â‘€ 暪断芁件 — 画面暪断で適甚される NFR

🔐 認蚌 / セキュリティ

  • NFR-X-AUTH-01 Bearer トヌクン方匏 (Authorization: Bearer <hex32>)、auth.ts の requireUser で SELECT id FROM users WHERE access_token=?
  • NFR-X-AUTH-02 access_token は iOS Keychain (kSecClassGenericPassword, service jp.newmeta.shiwake.auth)
  • NFR-X-AUTH-03 サむンアップは AppAttest 経由 (/api/auth/challenge → DCAppAttestService → /api/auth/signup)、DEBUG ビルドのみ旧 invite redeem ぞフォヌルバック
  • NFR-X-AUTH-04 既ログむン時の invite URL は iOS 偎で早期 return (無駄な埀埩回避)
  • NFR-X-AUTH-05 IAP は Apple JWS を /api/iap/verify でサヌバ偎怜蚌 (端末偎レシヌト怜蚌は信甚しない) — 眲名 chain 怜蚌は TODO
  • NFR-X-AUTH-06 Apple Server-to-Server 通知甚に /api/iap/notification (no auth, 眲名怜蚌は TODO)
  • NFR-X-AUTH-07 AppAttest の Apple 公開鍵 + nonce 怜蚌で停端末の倧量サむンアップを防止 (Phase A 完了)
  • NFR-X-AUTH-08 テナント分離: å…š D1 ク゚リで user_id を WHERE 必須 (挏れがあれば即修正察象)

⚡ パフォヌマンス

  • NFR-X-PERF-01 /api/ingest 1 次応答 (persona_comment) は P50 < 2s、mealLookup は ctx.waitUntil で非同期
  • NFR-X-PERF-02 OpenAI prompt cache を contextBundle で掻甚、システムプロンプトを安定化
  • NFR-X-PERF-03 meal_nutrition_cache (0007) で同名料理の再蚈算を回避
  • NFR-X-PERF-04 CaptureView は蚈算䞭 meal 怜出時のみ 3s polling、完了で自動停止
  • NFR-X-PERF-05 Cloudflare Workers ゚ッゞ実行 + D1 prepared statement
  • NFR-X-PERF-06 weather_cache (0011) で倖郚 API 呌び出しを 1h ロヌカルキャッシュ

🛡 可甚性 / 障害時挙動

  • NFR-X-AVAIL-01 OpenRouter/OpenAI 障害時: persona_comment はフォヌルバック定型文、mealLookup は notes "蚈算䞭" のたた留め眮き
  • NFR-X-AVAIL-02 Soniox 障害時 (Premium): temp key 取埗倱敗 → ゚ラヌ文蚀、Apple SF ぞのフォヌルバックは未実装 (将来課題)
  • NFR-X-AVAIL-03 D1 障害時: 5xx を返し iOS は bubblePhrase(.sendFailed) で再詊行誘導
  • NFR-X-AVAIL-04 R2 障害時: image_r2_key を null で meal を䜜成 → 埌远い再アップ未実装
  • NFR-X-AVAIL-05 /health ゚ンドポむントで生存確認 ({ ok, ts })
  • NFR-X-AVAIL-06 /api/me が䞀時倱敗しおもクラむアントは前回倀を保持 (profileLoaded=false の間 splash)

📊 蚈枬 / ログ / 監査

  • NFR-X-OBS-01 api_usage_log (0012): OpenRouter/OpenAI/Soniox の token・コスト・モデル別利甚ログ
  • NFR-X-OBS-02 request_timing (0013): ゚ンドポむント毎のレむテンシ分垃
  • NFR-X-OBS-03 conversation_log (0014): user 発話 / assistant 返答の察話履歎 (persona 改善甚)
  • NFR-X-OBS-04 feedback (0018): 開発者向けフィヌドバック文 + [calorie_flag] プレフィックスでカロリヌ誀り報告
  • NFR-X-OBS-05 Admin /api/admin/stats: OpenRouter credits / 平均日次コスト / runway 日数 / フィヌドバック盎近䞀芧
  • NFR-X-OBS-06 Admin は Basic 認蚌 (app.use("/api/admin/*", basic auth))

🔒 プラむバシヌ / 法務

  • NFR-X-PRIV-01 privacy_agreed_v1 同意フラグ + 党文ポリシヌ (8 ç« , 制定 2026-05-24)
  • NFR-X-PRIV-02 同意取埗項目: ニックネヌム / 身長 / 䜓重 / 䜓脂肪 / 食事 (text/voice/photo) / 利甚時刻 / デバむス情報
  • NFR-X-PRIV-03 第䞉者送信: 音声認識 (Soniox) / AI 解析 (OpenAI/OpenRouter) / むンフラ (Cloudflare) を明瀺
  • NFR-X-PRIV-04 削陀䟝頌は contact_app@newmeta.co.jp、サヌバ偎削陀は手動オペレヌション (DELETE /api/me は TODO)
  • NFR-X-PRIV-05 カメラはバックグラりンド即 stop、起動時すりガラスを default (プラむバシヌ優先)
  • NFR-X-PRIV-06 feedback アップロヌドはテキストのみ (音声非保存)

💞 コスト / リ゜ヌス

  • NFR-X-COST-01 OpenRouter 月額予算は admin/stats の runway_days で監芖
  • NFR-X-COST-02 Free ナヌザは STT を Apple SF (コストれロ) に匷制、Premium のみ Soniox
  • NFR-X-COST-03 meal_nutrition_cache で重耇料理の LLM 呌び出しを削枛
  • NFR-X-COST-04 prompt cache 掻甚で OpenAI 入力トヌクンを 50%+ 削枛狙い
  • NFR-X-COST-05 R2 は食事写真のみ保管 (音声は保存しない)
  • NFR-X-COST-06 フィヌドバックはテキスト化埌送信 (音声ファむル送信なし)

♿ 囜際化 / アクセシビリティ

  • NFR-X-I18N-01 日本語のみ (Locale ja_JP 固定)、Asia/Tokyo TZ で日付凊理
  • NFR-X-A11Y-01 Dynamic Type を semantic font (.subheadline / .caption) で察応
  • NFR-X-A11Y-02 VoiceOver: 手入力ボタンに accessibilityLabel("手入力する")
  • NFR-X-A11Y-03 タップ領域 44pt 以䞊を意識 (CloseButton/SaveButton は 78pt 円)
  • NFR-X-A11Y-04 觊芚フィヌドバック (impact/notification/selection) で芖芚に䟝らない状態通知

🚀 配信 / リリヌス

  • NFR-X-REL-01 iOS: TestFlight → App Store Connect 経由配垃 (IAP は In-App Subscription, StoreKit2)
  • NFR-X-REL-02 バック゚ンド: Cloudflare Workers (wrangler deploy), D1 マむグレヌション wrangler d1 migrations apply
  • NFR-X-REL-03 LP: Cloudflare Pages, lp/functions/api/waitlist.ts で Edge Function
  • NFR-X-REL-04 Admin: Cloudflare Pages shiwake-admin.pages.dev、SPA + Basic auth lookup via backend
  • NFR-X-REL-05 AASA: /.well-known/apple-app-site-association で Universal Link /invite/*
  • NFR-X-REL-06 privacy_agreed_v1 のバヌゞョン番号で同意の䞖代管理

⑥ API カタログ — バック゚ンド党゚ンドポむント (37)

認蚌 / 招埅

METHODPATHAUTH甹途
POST/api/auth/challengenoneAppAttest 甹 nonce 発行 (5min TTL)
POST/api/auth/signupnoneAppAttest assertion を怜蚌 → user 䜜成 + access_token 返华
POST/api/invitenone匿名 invite code 発行 (DEBUG フォヌルバック / Settings 他者招埅)
POST/api/auth/redeemnoneinvite code を access_token ず亀換 (409=消費枈)
GET/invite/:codenoneUniversal Link 甚、アプリ未むンストヌル時のフォヌルバック
GET/.well-known/apple-app-site-associationnoneiOS Universal Link 蚭定

ナヌザヌ / プロフィヌル

METHODPATHAUTH甹途
GET/api/meBearerプロフィヌル + streak + subscription + trial + pending_advance_event 䞀括取埗
PUT/api/me/targetsBearer身長/䜓脂肪/目暙䜓重/期日/目暙カロリヌ/食事目暙/ニックネヌム 䞀括曎新
PUT/api/me/affinityBearer関係性 points 盎接セット (デバッグ)
PUT/api/me/subscriptionBearerサブスク状態手動切替 (デバッグ)
POST/api/setup/parseBearerwizard 甚音声 transcript → value/level/goal/date_iso 抜出
GET/api/me/factsBearerアシスタントが孊習した user facts 䞀芧
DEL/api/me/facts/:idBearerfact 削陀

食事 (meals / ingest)

METHODPATHAUTH甹途
POST/api/mealsBearer写真ベヌス meal 登録 (multipart: image+transcript)
GET/api/mealsBearer期間指定で meal 䞀芧取埗
DEL/api/meals/:idBearermeal 削陀
PUT/api/meals/:idBearermeal 線集 (raw_text 等)
POST/api/meals/:id/renameBearer料理名倉曎 → mealLookup 再蚈算
POST/api/meals/:id/flag-calorieBearerカロリヌ誀り報告 → feedback テヌブルに [calorie_flag] 挿入
POST/api/ingestBearerテキスト transcript で食事/䜓重/雑談を仕分け (persona_comment + advance_event 同梱)
POST/api/ingest/voiceBearer音声バむナリ → STT → ingest 統合
POST/api/auth/soniox-temp-keyBearerPremium 甹 Soniox WebSocket 䞀時キヌ発行

䜓重 / 運動 / ノヌト

METHODPATHAUTH甹途
POST/api/weightsBearer䜓重蚘録 (kg + note + body_fat_percent)
POST/api/weights/:id/updateBearer既存䜓重の䞊曞き線集
GET/api/weightsBearer期間指定取埗
DEL/api/weights/:idBearer削陀
GET/api/exercisesBearer運動蚘録 期間指定取埗
DEL/api/exercises/:idBearer運動削陀 (POST は ingest 経由で自動䜜成)
GET/api/notesBearerフリヌノヌト䞀芧
DEL/api/notes/:idBearerノヌト削陀
GET/api/summary/weeklyBearer過去 7 日の集蚈 (meal_count/total_kcal/avg_kcal + weights 配列)

フィヌドバック

METHODPATHAUTH甹途
POST/api/feedbackBearerテキストフィヌドバック送信 ⚠ サヌバ実装未確認

IAP / サブスクリプション

METHODPATHAUTH甹途
POST/api/iap/verifyBearerStoreKit JWS をサヌバ偎怜蚌 → users.subscription_status 曎新
POST/api/iap/notificationnone (眲名怜蚌)Apple Server-to-Server 通知受信

Admin / LP / ヘルス

METHODPATHAUTH甹途
GET/api/admin/statsBasicOpenRouter credits / 日次コスト / runway / フィヌドバック盎近䞀芧 (/api/admin/* 党䜓が Basic)
POST/api/waitlist (LP)none (IP hash)メアド + source + UA + referrer を waitlist テヌブルに保存
GET/api/admin/waitlist (LP)Basicwaitlist 取埗 (JSON or CSV ゚クスポヌト)
GET/noneルヌト (導線返し)
GET/healthnone{ ok: true, ts: now }

💡 このペヌゞは静的ドキュメントです。デヌタ゜ヌスは admin/index.html の data-tab="docs" セクション + admin/admin.js の docFilter* 関数。 画面远加・API 倉曎時に PR で曎新しおください。

🀖 LLM 蚭蚈曞

shiwake-daily は OpenRouter 経由で耇数の LLM を組み合わせお動いおいる。
本ペヌゞは各呌び出しの 目的・モデル・プロンプト構造・コスト + 呚蟺の ハヌネス゚ンゞニアリング の蚭蚈をたずめる。
曎新は admin/index.html の data-tab="llm" セクション。実コヌドず乖離したら盎す。

🗺 1. 党䜓像 (シヌケンス) 📋 2. 呌び出し早芋衚 🎙 3. ingest (Phase A 仕分け) 🍜 4. mealLookup (Phase B カロリヌ web 怜玢) 📷 5. analyzeMeal (写真 vision) 💬 6. persona (秘曞コメント) 💞 7. relationshipJudge (関係性昇栌) 🔧 8. ハヌネス゚ンゞニアリング 💰 9. コスト詊算 📜 10. 珟行プロンプト (本番デプロむ䞭の本文)

🗺 1. 党䜓像 (1 発話あたりの LLM 呌び出しチェヌン)

📐 音声 1 発話 → 蚘録完了たでの呌び出し順序

sequenceDiagram autonumber participant iOS as 📱 iOS participant API as ⚙ Workers participant Ing as 🎙 ingest
(Gemini 3 Flash) participant Per as 💬 persona
(gpt-4o-mini) participant ML as 🍜 mealLookup
(Gemini 3 :online) participant Judge as 💞 judge
(gpt-4o-mini) participant DB as 💟 D1 iOS->>API: POST /api/ingest (transcript) API->>DB: loadRecentContext + facts + convLog (䞊列) API->>Ing: chat.completions (tools=create_meal/weight/exercise/...) Ing-->>API: tool_calls[] (JSON args) API->>DB: INSERT meals/weights/exercises (kcal=0 暫定) API->>Per: chat.completions (秘曞発蚀生成) Per-->>API: "ラヌメンですね、お疲れさた。" API-->>iOS: 200 OK (transcript / created / persona_comment) Note over API,ML: ↓ waitUntil で裏でカロリヌ再蚈算 ↓ par API->>ML: lookupMealNutrition (1食ず぀) ML-->>API: { calories_kcal, PFC, source_note } API->>DB: UPDATE meals SET calories_kcal=... and API->>Judge: evaluateLevelAdvance (条件成立時のみ) Judge-->>API: { decision, openingLine? } API->>DB: UPDATE users SET affinity_level/cooldown end

★ primary_response = ingest + persona の盎列 2 呌び出したでで iOS に 200 を返す (~3-4 秒目暙)。
★ calorie_complete = mealLookup の web 怜玢が waitUntil 内で完了するたで (~10-15 秒、蚈算䞭 衚瀺)。

📋 2. 呌び出し早芋衚

caller モデル 甹途 呌び出し堎所 temperature 出力圢匏
ingest google/gemini-3-flash-preview 音声テキスト → tool calls (create_meal / weight / exercise 等) ingest.ts 0.1 tool_calls (parallel)
meal_lookup google/gemini-3-flash-preview:online 料理名 → 公匏倀ベヌスの kcal + PFC (web 怜玢 Exa) mealLookup.ts 0.1 json_schema strict
normalize_dish openai/gpt-4o-mini 線集時に品名を lookup_key / unit / count に分解 mealLookup.ts 0.1 json_schema strict
persona openai/gpt-4o-mini 蚘録結果に察する秘曞の 1-2 文コメント persona.ts 0.7 plain text (max 300 tok)
judge openai/gpt-4o-mini 関係性 Lv 昇栌すべきか (advance / wait) relationshipJudge.ts 0.4 json_schema strict
forced_opening openai/gpt-4o-mini 匷制ラむン到達時の「ずっず迷っおた」䞀蚀 relationshipJudge.ts 0.7 plain text (max 100 tok)
premium_unlock openai/gpt-4o-mini 課金時の「ずっず話したかった」䞀蚀 relationshipJudge.ts 0.7 plain text (max 100 tok)
photo_dish_extraction google/gemini-3-flash-preview (vision) 料理写真 → dish[] (kcal は出さない) openai.ts 0.1 json_schema strict

💡 caller は api_usage_log テヌブルの caller 列ず䞀臎。「技術 / コスト」タブで内蚳を芋れる。

🎙 3. ingest (Phase A: 仕分け)

📐 圹割

Soniox STT で曞き起こされた発話を、構造化された IngestAction[] に分解する。
parallel tool calls 必須:「朝はラヌメン食べた、䜓重 70kg だった」を 1 回の呌び出しで create_meal + create_weight に分割する。

📐 入力 / 出力

input  : transcript (string) + RecentContext (盎近 7 日の食事 / 䜓重 / 運動)
                                + userName / knownFacts / contextBundle / 䌚話履歎
output : IngestAction[] = (create_meal | create_weight | create_exercise
                          | update_meal | update_weight | update_exercise
                          | delete_*  | reply_only | record_user_fact)[]

📐 プロンプト構造

graph TB A["STATIC_PROMPT_PREFIX
(完党固定・cache prefix)
~ 4000 token"] B["nowIso + userName
(動的・小)"] C["RecentContext
(meals/weights/exercises 各10件)"] D["knownFactsBlock
(ナヌザヌ蚘憶)"] E["contextBundleBlock
(倩気・行動集蚈)"] F["conversationHistoryBlock
(盎近䌚話)"] A --> B --> C --> D --> E --> F

📐 STATIC_PROMPT_PREFIX に曞いおあるこず (抜粋)

  • 䜿える tool 䞀芧ず呌び方
  • 運動刀定ルヌル: 「スクワット 10 回」のような回数ベヌス発話も必ず create_exercise を呌ぶ
  • calories_kcal の掚定方法: MET 衚 + 䜓重 × 時間 (h)。回数ベヌスは 1 セット ~3 分換算
  • category 分類: aerobic / anaerobic / other
  • 食事刀定ルヌル: 料理名・店名単独でも create_meal (動詞䞍芁)
  • STT 誀字蚂正パタヌン: 「新ラヌメン」→「蟛ラヌメン」、「ラヌメン二号」→「ラヌメン二郎」など同音異矩語を黙っお蚂正
  • update_meal の優先順䜍: calories_kcal 明瀺 → name/quantity 倉曎で再 lookup → recorded_at だけなら据え眮き

📐 呌び出しパラメヌタ (実コヌド)

callOpenRouterChat({
  caller: "ingest",
  body: {
    model: "google/gemini-3-flash-preview",  // gpt-4o-mini より ~4× 高いが分類粟床を優先
    messages: [
      { role: "system", content: systemPrompt(...) },
      { role: "user", content: transcript },
    ],
    tools: FUNCTION_TOOLS,           // 13 個の function 定矩
    tool_choice: "auto",
    parallel_tool_calls: true,       // ★ 1 発話 → 耇数 tool 必須
    temperature: 0.1,                // 安定した分類のため䜎枩
  },
})

⚠ 既知の萜ずし穎

  • OpenAI strict mode で required に列挙したフィヌルドは null 蚱容にしないず「LLM が省略 → schema error」になる。create_meal は required で 11 個列挙する代わりに倀域を ["number", "null"] に。
  • 回数ベヌスの運動は reps + duration_minutes (~3 分) の䞡方を埋めるよう指瀺しおいる。iOS 衚瀺は reps 優先で出さないず「3 分」が出おしたう (修正枈)。
  • STT は 同音異矩語の取り違え が起きる (「皇居ラン」→「高居ラン」など)。誀字蚂正セクションをプロンプトに明蚘しおいるが、未登録パタヌンには匱い。

🍜 4. mealLookup (Phase B: カロリヌ web 怜玢)

📐 圹割

ingest が出した lookup_key + lookup_unit を入力に、1 単䜍あたりの kcal + PFC を web 怜玢で取りに行く。
ingest ず分離するこずで: ① プロンプトキャッシュが効く (ingest 偎は web 怜玢なしの軜量呌び出しでキャッシュ ヒット率を最倧化)、 ② キャッシュキヌが "生䞭ゞョッキ|1杯" のように量ず独立、量違いで分かれない。

📐 OpenRouter :online サフィックス

google/gemini-3-flash-preview:online を指定するだけで OpenRouter 偎が Exa を䜿った web 怜玢を自動付䞎。
コスト: 通垞 token 課金 + $0.005 / call フラット (PRICING table の webSearchFlatPerCall)。

📐 プロンプトのポむント (抜粋)

  • 必ず web 怜玢結果を参照しおから栄逊成分を確定 (掚枬のみは犁止)
  • 1 単䜍あたり = ナヌザヌ偎で count を掛ける前提
  • マシマシ・倧盛・特盛は count に出す (lookup_key には入れない)
  • ただし「ラヌメン二郎 小ダサむマシマシ」のような固有メニュヌ名は量増し蟌みで OK
  • 公匏倀が無ければ類䌌料理で代甚し source_note にその旚を明蚘
  • 䞍確実なら 高めに芋積もる (過小評䟡しない)
  • 参考倀: 生侭 ~145-200 / ラヌメン二郎 小 ~1300-1700 / マック ビッグマック ~525 等を 10 件皋床明瀺

📐 キャッシュ戊略

cacheKey = NFKC正芏化(lookup_key) + "|" + NFKC正芏化(lookup_unit)
         䟋: "生ビヌル 䞭ゞョッキ|1杯"
            "ラヌメン二郎 小ダサむマシマシ|1杯"

D1 テヌブル: meal_nutrition_cache (key, calories_kcal, carbs_g, protein_g, fat_g, source_note, created_at)
TTL: 30 日 (公匏メニュヌ改蚂を吞収)

📷 5. analyzeMeal (写真 vision)

📐 圹割

写真 + (任意の音声 transcript) を vision モデルに芋せお、料理名 / quantity / lookup_key / count を抜出。
kcal は 絶察に出さない (LLM のハルシネヌション抑制のため)。出した dish[] をそのたた mealLookup に流しお公匏倀を取埗する。

📐 モデル遞定

ingest ず統䞀しお google/gemini-3-flash-preview (multimodal)。Gemini の vision 粟床は gpt-4o-mini ず同等以䞊で、写真利甚は少ない想定 (月コスト誀差レベル)。

📐 出力 schema

{
  dishes: [
    { name, quantity, display_label, lookup_key, lookup_unit, count, confidence }
  ],
  notes: "写真ず発話から読み取った党䜓メモ (1-2 文)"
}

💬 6. persona (秘曞コメント生成)

📐 圹割

ingest 完了盎埌に、キャラクタヌ (珟圚は ayaka のみ) の 1-2 文の自然なリアクションを生成。
Lv1-5 で口調が倉わる (敬語のみ → タメ口 → あだ名)。文字数䞊限・絵文字ポリシヌも Lv 別。

📐 system prompt 構造 (cache 戊略)

graph TB P1["PERSONA_COMMON_RULES
(å…š Lv 共通・完党固定)
★ cache prefix"] P2["getCharacterFraming(Lv)
(Lv≀2 / Lv≥3 で 2 皮類)"] P3["getResponseStyle(Lv)
(maxChars / emoji / tone / GOOD / NG 䟋)"] P4["honorificRule
(userName + Lv 別 二人称)"] P1 --> P2 --> P3 --> P4

★ PERSONA_COMMON_RULES を system prompt の先頭に固定で眮くこずで OpenAI の自動プロンプトキャッシュにヒットさせる。
同䞀ナヌザヌ連続呌び出しなら共通郚 + Lv 郚分たで党お cache hit。

📐 user prompt に詰めるもの

  1. contextBundleBlock (今この瞬間: 倩気・行動集蚈)
  2. chatStateBlock (察話状態ガむダンス、雑談ルヌプ防止)
  3. knownFactsBlock (ナヌザヌ背景情報) + 「黙っおいる」ルヌル
  4. previousAssistantMessage (盎前の挚拶など、䌚話連続性)
  5. 蚘録した内容 (createdSummaries)
  6. Lv 別 questioningPolicy / sensitiveRule
  7. forceOpening があれば「冒頭に必ず含める」

📐 絶察ルヌル (プロンプト固定)

  • 説教・叱責・カロリヌ现かい指摘は犁止
  • BMI / PFC など専門甚語犁止
  • 䞊の Lv の口調を先取りしない (Lv1 で「えらい」NG)
  • 「先茩」ずは絶察呌ばない (ナヌザヌは幎䞋蚭定)
  • 生返事・事務応答 NG: 「特に蚘録するものはなさそうですね」NG
  • 自分から話を広げない (「ずころで」「そういえば」NG)
  • 背景情報を持ち出さない: ペットの話など、ナヌザヌが觊れない限り蚀及しない

💞 7. relationshipJudge (関係性昇栌)

📐 2 段構えの刀定

newPoints < min[next]              → 据え眮き
min[next] <= newPoints < forced    → LLM judge (advance / wait)
                                       wait なら 3 日 cooldown
newPoints >= forced[next]            → LLM 無芖で匷制昇栌

LEVEL_MIN_THRESHOLDS    = { 2: 10, 3: 30, 4: 80, 5: 200 }
LEVEL_FORCED_THRESHOLDS = { 2: 30, 3: 80, 4: 200, 5: 500 }

📐 sub-call 1: judge

caller=judge / temperature=0.4 / json_schema strict。
入力に盎近 10 件の transcript + 今この発話 + 环蚈蚘録数を枡し、{ decision, reason, opening_line } を返させる。
刀断軞: 「事務的 → 人間的」な倉化が䌚話に滲んでいるか。

📐 sub-call 2: forced_opening

匷制ラむン到達時の「ずっず迷っおた」告癜文生成。
caller=forced_opening / temperature=0.7 / max_tokens=100。本文のみ返させる。倱敗時は固定フォヌルバック文。

📐 sub-call 3: premium_unlock

無料 → Premium 課金時に「ずっず話したかった、これからは『くん』っお呌ばせお」を生成。
無料期間䞭に貯たった preservedPoints を「ずっず裏で感じおいた」ず語らせる。

📐 無料 / Premium / Trial の handling

  • 無料 / 解玄枈: effectiveLevel = min(raw, 2) で Lv2 で頭打ち
  • premium_active: raw level を玠通し (Lv5 たで)
  • trial_ends_at > now: 同じく玠通し (Premium 同等)
  • IAP launch 前は新芏ナヌザに長期 trial を付䞎 → 実質誰でも Premium 箚

🔧 8. ハヌネス゚ンゞニアリング

📐 抂芁

「プロンプトを曞く」だけでなく、LLM を本番運甚に乗せるための ガワ (harness) の蚭蚈。
shiwake では 7 ぀の局に分けお敎理しおいる。

📐 ① 統䞀゚ントリポむント callOpenRouterChat

å…š LLM 呌び出しは apiUsageLog.ts の単䞀関数を経由する。盎接 fetch を曞かない (analyzeMeal は legacy で䟋倖)。

callOpenRouterChat({
  openRouterKey, caller, body, log
}) → { raw, data }

責務:
  1. fetch (OpenRouter REST)
  2. 倱敗時の Error 投げ盎し ({caller}_http_{status})
  3. usage.prompt_tokens / completion_tokens を取り出し
  4. PRICING table で USD コスト算出
  5. api_usage_log テヌブルに INSERT (provider, caller, model, tokens, cost_usd, status, latency_ms)

📐 ② プロンプトキャッシュ最適化

  • system prompt の先頭を完党固定: STATIC_PROMPT_PREFIX (ingest) / PERSONA_COMMON_RULES (persona)
  • 固定郚 → 倉動郚 (Lv 別 / userName 別) の 順序を厳守。順序を倉えるず cache miss
  • OpenAI / Gemini はいずれも 5 分前埌の自動キャッシュ TTL を持぀ので、同䞀ナヌザヌ連続発話なら hit する
  • キャッシュ hit 埌の月コスト詊算: 1 ナヌザ × 150 リク = ~Â¥42 / 月 (ingest)

📐 ③ 2 段階呌び出し (ingest ず mealLookup の分離)

ingest 内で web 怜玢を回さない理由:

  • web 怜玢の latency (~5-10 秒) が primary_response (= 200 OK) を遅延させる
  • ingest はキャッシュ重芖で軜量化、mealLookup は web 怜玢特化で「重い・遅い」を分離
  • mealLookup の結果は meal_nutrition_cache に保存し、2 回目以降は LLM を呌ばずに即返华
  • iOS 偎は 「蚈算䞭 」衚瀺 → polling で UPDATE 反映 ずいう二盞モデル

📐 ④ 出力 schema strict

  • json_schema を䜿う呌び出しは strict: true を必ず指定 (mealLookup / normalize_dish / judge / analyzeMeal)
  • OpenAI strict mode の制玄: required 列挙したフィヌルドは null 䞍可 → null 蚱容にしたければ type: ["number", "null"] 圢匏
  • tool calling 偎 (ingest) も同じ制玄。create_meal は 11 required + null 蚱容で逃がす
  • Gemini が json_schema 出力に code fence を付ける こずがあるので、受け取り偎で ```json を剥がす sanitize 凊理を統䞀

📐 â‘€ tool calling の挙動制埡

  • tool_choice: "auto" + parallel_tool_calls: true をセット (ingest)
  • 1 発話で耇数 tool 呌び出しを蚱可するこずで「ラヌメン食べお 70kg だった」を 1 リク゚ストで凊理
  • reply_only tool を甚意するこずで「䜕も蚘録しない雑談」を明瀺的に衚珟 (返さない遞択を取らせない)
  • update_* / delete_* tool には context から拟った id を匷制し、捏造を防ぐ

📐 ⑥ ロギング・コスト蚈枬

  • api_usage_log に毎回 1 行 INSERT (provider, caller, model, tokens, cost_usd, status, latency_ms, user_id)
  • enqueueLog で fire-and-forget (waitUntil で非同期 INSERT、応答 latency に圱響させない)
  • 「技術 / コスト」タブで caller 別の集蚈を衚瀺 (どこにいくら䜿っおるか可芖化)
  • 倱敗時も status="error" + error_message で残す → ゚ラヌ率モニタリング

📐 ⑩ ゚ラヌ耐性 / フォヌルバック

  • relationshipJudge の各 sub-call は try/catch で囲み、倱敗時は固定文を返す (UX は止めない)
  • mealLookup が倱敗しおも meal レコヌドは残る (kcal=0 のたた、iOS で「蚈算䞭 」衚瀺が続く)
  • persona が空文字を返したら呌び出し偎で握り぀ぶす (空コメントを iOS に流さない)
  • ingest がリトラむを必芁ずする粒床の倱敗 (rate limit 等) は OpenRouter 偎で吞収される想定

📐 ⑧ レむテンシ蚈枬 startTiming

startTiming(endpoint, userId) → tm
  tm.mark("ingest_done")           // ingest 呌び出し完了
  tm.mark("primary_response_sent") // iOS に 200 を返した瞬間
  tm.mark("calorie_complete")      // mealLookup 完了
  tm.finalize()                    // user_perceived_latency_ms を DB に蚘録

ナヌザヌ目線のレむテンシ (発話 → 200 OK たでの時間) ず、裏でカロリヌ蚈算が終わるたでの時間を分けお蚈枬。

💰 9. コスト詊算

📐 PRICING テヌブル (apiUsageLog.ts)

モデルinput ($/1M tok)output ($/1M tok)web 怜玢
openai/gpt-4o-mini$0.15$0.60—
openai/gpt-4o$2.50$10.00—
google/gemini-3-flash-preview$0.50$3.00—
google/gemini-3-flash-preview:online$0.50$3.00+$0.005 / call

📐 想定 1 発話あたりのコスト

呌び出しtypical inputtypical output$ / 発話
ingest (Gemini 3 Flash)~3000 tok (cache hit 想定)~300 tok~$0.002
persona (gpt-4o-mini)~1500 tok~100 tok~$0.0003
mealLookup (online, 1食)~800 tok~150 tok~$0.006 (web 蟌)
judge (発火頻床 ~1%)~800 tok~80 tok~$0.0002
合蚈 (食事 1 件あり発話)~$0.008 / 発話

📐 1 ナヌザ月コスト詊算

DAU 想定: 1 日 3-5 発話 → 月 ~120 発話 → ~$1.0 / 月 / ナヌザヌ (~Â¥150)
Premium 䟡栌 Â¥980 / 月 想定なので gross margin ~85%。soniox STT (~$0.001 / 30sec 発話) を足しおも䜙裕。

💡 実瞟は「技術 / コスト」タブで api_usage_log を caller 別 / model 別に集蚈衚瀺。

📜 10. 珟行プロンプト (本番デプロむ䞭の本文)

GET /api/admin/prompts を叩いお、各モゞュヌルが export しおいる プロンプト定数をそのたた返す。
動的郚分 (Lv / userName / 环蚈 / preservedPoints 等) は admin 偎でサンプル匕数を入れおレンダリングした文字列。

💡 このペヌゞは静的ドキュメント。曎新は admin/index.html の data-tab="llm" セクション。
プロンプト本䜓やモデル倉曎時に PR で合わせお盎す。実コヌドず乖離しないよう、倉曎時はこのペヌゞの該圓箇所も曎新するこず。
Section 10 は API から本番倀を盎接取るので、コヌドず乖離しない (export を増枛したら admin.ts の /api/admin/prompts も曎新)。