Prisma + Postgres で daily-snap のテーブル責務を切った話。ベクトル検索は未実装だが、エントリ・チャット・画像・ai_artifacts・memory 系をどう置くか。次に足すならどこか。
正直に言うと、pgvector を使った検索まではまだ入っていない。それでも Postgres を最初に置いたのは、日記・会話・画像・AI の生成ログが全部「後から繋がる」前提だからだ。テーブルは、責務が混ざると一気に苦しくなるので、初期から分割の思想だけは固めた。
イメージとしては次のような分け方。日付単位の本文やメタは daily_entries 系。会話は chat_threads / chat_messages のように 履歴として残す方向。画像はパスとハッシュなどのメタ。AI 周りは ai_artifacts に、モデル名や遅延、参照範囲のメタを寄せる。長期記憶っぽいものは memory_* の置き場を先に用意しておく。
ここで大事なのは、「完璧なスキーマ」ではなく 拡張の方向がブレないこと。ベクトルは、embedding をどの単位で持つか(エントリ単位? 追記イベント単位?)を決めないと、あとで地獄になる。だから今は 未実装を明示して、次の一手を文章に落とす。
補足として、schema.prisma には Embedding モデル(vector カラム)も定義されている。とはいえ 製品としてのベクトル検索 UI・運用フローまでは未着手という理解でいる(この連載の方針どおり「未完」を明示)。
検索の話も同じで、平文領域と E2EE 領域(将来)で扱いが変わる。要件では E2EE 本文は検索制限が許容される、と書いてある通り、「全部を同じ検索に載せる」前提を捨てるのが安全だ。MVP では、タグや日付、カレンダー、会話のメタで拾える体験を優先する。
実装の入口は web/prisma/schema.prisma を見るのが早い。コミット履歴は CHANGELOG に日付単位でまとまっているので、「いつ何が増えたか」の裏取りにも使える。
schema.prisma のモデル対応表(ざっくり)「どのテーブルが何のためにあるか」を一文で並べる。細部は schema を正とする。
| モデル | 役割の一言 |
| --- | --- |
| User / Account / Session / VerificationToken | 認証とセッションの土台 |
| DailyEntry / EntryAppendEvent | 日付単位の本文と 追記イベント(履歴の粒) |
| Image / GooglePhotoSelection | 添付画像と Google フォト連携の選択状態 |
| Tag / EntryTag | 手動・自動のラベリング |
| GoogleCalendarEventCache / GoogleCalendarSyncState | カレンダー同期とキャッシュ |
| AppLocalCalendar / AppLocalCalendarEvent | アプリ内ローカル予定(外部同期と別系統) |
| ChatThread / ChatMessage | 会話ログ(ストリーミングの本文もここに残す想定) |
| SecurityReview | セキュリティレビュー記録 |
| AIArtifact / AuditLog | 生成物メタと監査。本文はログに流さない方針とセット |
| OAuthToken | 外部 OAuth のトークン保管 |
| MemoryLongTerm / MemoryShortTerm / AgentMemory | 長短記憶・エージェント記憶の置き場 |
| Embedding | ベクトル。スキーマ上はあるが製品としての検索 UI は未着手 |
| UsageCounter | 利用枠・レート系のカウント |
| AgentEvaluation | エージェント評価の記録 |
pgvector を「入れる」だけならマイグレーションで終わるが、検索の単位・再生成・削除・E2EE 本文の扱いまで含めるとプロダクト設計になる。だから Embedding は、テーブルより先に 索引の仕様を決めたい。
次回は、04-10 前後の **MAS(マルチエージェント)**が、DB の上でどう動くかに触れる。オーケストレータが増えた瞬間、アプリは「単一チャット」じゃなくなる。
個人開発で一番まずいのは、未実装を隠すことだと思う。だから DB の話も、いま持っているカード(会話ログ、生成物メタ、画像メタ)を先に並べて、足りないカード(embedding、インパクト推定の確定版、E2EE 本文の扱い)を明示する。こう書いておくと、未来の自分が「なぜこのテーブルがあるのか」を忘れにくい。
やることはシンプルに見えて、実装は地味に長い。embedding の更新タイミング(保存時? バッチ?)、モデル変更時の再生成、削除時の整理、検索 UI の段階的露出。MVP のままいくなら、まずは「検索できない部分」をユーザーに誤解なく説明できる状態が先だと思う。E2EE 本文が絡むと、検索の説明はさらに慎重になる。
DB は「完成品」じゃなくて、制約のストレージだ。何を永遠に保持するか、何を捨ててよいか、何を平文にするか。日記アプリはその問いが直球で来る。pgvector は次の章だ。
マイグレーションを増やすほど、ドキュメントが追いつかなくなる。だから「テーブル増やした日に、CHANGELOG に一言でもいいから理由を残す」運用が効く。自分への PR コメントみたいなものだ。
Prisma schema の一部(主要 model 名が見える範囲)、マイグレーション一覧のスクショ、または prisma studio を開いている画面。個人メモなら個人情報に注意。
daily-snap 開発ログ
前: 入口を /today に寄せたら…
次: カレンダーの色まで味方にした、振り返りチャットのオーケストレータ
索引: 04-02 · … 04-30 Docker
dairy-snap を Docker / Cloud Build / Cloud Run 気味に寄せるときに踏んだ地雷の型。standalone、prisma generate、Node heap、ダミー DATABASE_URL、PORT。04-16 前後のコミット列を軸に、個人開発でもハマる点を整理する。
天気(Open-Meteo)と Google カレンダーのキャッシュ・分類が、日記と AI の文脈にどう効くか。entry-weather・WeatherAmPmDisplay・weather-tool などコード上の出所と、設計意図・本番運用をまとめる。
Google OAuth・許可リスト・JWT セッションの方針と、middleware から proxy へ寄せた経緯。HTTPS 背後での secureCookie、Docker 本番との相性。04-17 前後のコミットを手がかりに。
daily-snap の画像アップロード。最大辺2048px、AVIF優先(非対応ならWebP)の圧縮、ストレージ抽象と本番 GCS 前提。日記アプリで画像が重い問題への殴り方。
AI チャットの SSE(ストリーミング)をローカル・本番で確認した手順と、環境差分で再発しうる罠。ai_artifacts / audit_logs を厚くする理由と、本文をサーバログに出さない方針。
dairy-snap に MAS(マルチエージェント)を入れた理由と、オーケストレータ+サブエージェント分割の効用。04-10 前後の変更と、品質・拡張性・レイテンシのトレードオフの話。