※このブログは2026年03月11日に公開された英語ブログ「Building a Voice-First AI Journal: What I Learned About AI Memory, Vector Search, and TiDB」の拙訳です。
主なポイント
- 既存のメモリフレームワークは、最も重要な詳細を知らないうちに破棄してしまうことがあります。
- 3層構造のAIメモリアーキテクチャは、単一の抽象化よりもはるかに優れた想起性能を提供します。
- TiDBのネイティブなベクトル検索により、PostgresとPineconeを組み合わせた構成に見られる2つのデータベースによるオーバーヘッドが解消されます。
- 合成タスクにおけるモデル選択は、コストの観点ではなく、信頼性の観点で行うべきです。
先日、Claudeと会話をしていました。コードや技術的な問題ではなく、仕事や人生についての愚痴をこぼしていたのです。するとClaudeから返ってきた言葉があまりにも個人的で、私の状況に即したものだったので、私は思わず手を止めて画面を見つめてしまいました。Claudeは私の娘を名前で呼び、数週間前の会話で私がストレスを感じていた事柄に触れ、全く別々のチャットの間にある点と点を結びつけました。
AIに本当に覚えてもらえていると感じるあの感覚。それ自体がプロダクトの価値なのです。
そこで私は、音声ファーストのAIジャーナルアプリSpeak2Meを作りました。友だちに話しかけるように利用でき、AIがあなたの話した内容をしっかりと記憶します。「それは大変ですね」といったありきたりな返答ではなく、あなたの人生、周囲の人々、そして行動パターンに言及した、真に個人的な文脈を持った返事を返してくれるのです。
最初のバージョンは2時間ほどで完成しました。しかし、それを実際に「使える」ものにするために、残りの1週間を費やすことになりました。なぜなら、AIの記憶について誰も教えてくれない真実があるからです。AIに正しく記憶させることは、非常に困難であるということです。
AIメモリアーキテクチャ:理想と現実
コンセプトはシンプルでした。アプリを開けば、AIがユーザーを完璧に理解しているというものです。あなたのパートナーの名前を覚え、先週話した仕事のストレスについて尋ね、赤ちゃんが夜通し眠れているかどうかを気にかけてくれる。そのような体験です。
私はすべてを繋ぎ合わせました。音声にはHume EVI、長期メモリにはMem0、データベースにはTiDB (リレーショナルデータとベクトル検索機能を統合したもの)、思考レイヤーにはClaude、デプロイにはVercelを採用しました。数人のテスターにリンクを送り、自分でも手応えを感じていました。
それから、私は実際に使ってみました。自分の年収や家族のこと、今年の目標など、個人的な詳細をAIに話しました。そして、深くパーソナライズされた体験を期待して、次のセッションを開いたのです。
しかし、AIは私が誰なのか全く理解していませんでした。コンテキストはゼロでした。プロダクトとしての存在意義は完全に破綻していました。
AIメモリアーキテクチャのレイヤーが忘れてしまうとき
私は長期メモリのためにMem0を使用していました。ご存じない方のために説明すると、Mem0はGitHubで4万以上のスターを獲得しているオープンソースのメモリフレームワークです。会話を読み込ませると重要な事実を抽出し、後でその事実を呼び出すことができるという、非常に魅力的なコンセプトを持っています。
テスト用の会話の中で、私は正確な給与の詳細 (基本給とボーナスを、1ドル単位まで含めて) 伝えました。その後、Mem0が実際に何を保存したかを確認しました。
そこに記録されていたのは、「収入について話したがっている」という曖昧な一文だけでした。実際の数値は消えていたのです。
これはMem0の設計上のバグではなく、メモリ抽出の仕組みそのものの限界です。Mem0は内部で小さな言語モデル (GPT-4o-mini) を使用して何を記憶すべきかを判断していますが、小規模モデルは要約を強引に行う傾向があります。つまり、要点だけを捉えて詳細は捨ててしまうのです。カジュアルなチャットボットのメモリであれば許容できるトレードオフかもしれませんが、人生の詳細を正確に記憶すること自体が価値提案であるプロダクトにおいて、これは致命的な欠陥となります。
さらに、家族の詳細やキャリアプラン、特定の名前や日付を使ってテストを繰り返しました。うまく抽出できるものもありましたが、内容が支離滅裂になったり、完全に無視されたりするものもありました。抽出モデルを自分でコントロールできない以上、何が保持されるかを予測する方法はなかったのです。
メモリレイヤー自体がプロダクトの価値であるなら、それを他者のブラックボックスに委ねることはできません。
ハルシネーション問題:リリーとは誰か?
Mem0の問題をデバッグしている最中、私はさらに大きな失態を演じるところでした。
コストを抑えるため、ユーザープロファイルの生成にGPT-4o-miniを使用していました。これは、すべての会話を取り込んで、ユーザーが誰で、何を重視し、人生において誰が重要かををまとめたドキュメントを生成する仕組みです。このプロファイルは、その後のすべての会話にコンテキストとして組み込まれます。
テストデータでこの生成処理を実行し、出力を確認したところ、私の娘の名前はリリー、パートナーはサラであると記されていました。
どちらの名前も正しくありません。GPT-4o-miniは、本名がまだ会話に登場していないにもかかわらず、それらしい名前を捏造したのです。「まだ言及されていません」と書く代わりに、詳細を勝手に作り上げ、あたかも事実であるかのように提示しました。
自分のパーソナルなジャーナル・コンパニオンを開いたとき、「リリーは元気ですか?」と聞かれ、実際の娘の名前がまったく違っていた状況を想像してみてください。それは単なるバグではなく、二度と回復できない信頼の崩壊です。
私はすぐに、プロフィール要約のモデルをClaude Haiku 3.5に切り替え、厳格なガードレールを追加しました。「会話の中で明示されていない名前、数字、詳細は、決して捏造、推測、推論しないこと。言及されていない場合は、「言及されていない」と明記することです。
合成タスクにおけるモデル選択は、コスト最適化の問題ではありません。信頼に関わる判断です。家族の名前を1つでも誤って生成してしまえば、そのユーザーは二度と戻ってこないでしょう。
3レイヤー構成のAIメモリアーキテクチャの構築
これらの失敗を経て、私はメモリアーキテクチャ全体を一から考え直しました。解決策には、互いに補完し合う3つのレイヤーが必要だったのです。
レイヤー1:ユーザープロフィール
毎回の会話が終わるたびに、Claude Haikuが過去のすべての会話の記録を読み込み、要約されたドキュメントを生成します。そこには、ユーザーが誰であるか、その職業、人生において大切な人々、ストレスの要因、そして目標が記されます。このドキュメントは、次回以降のすべてのセッションにおいてシステムプロンプトに注入されます。これこそが、ユーザーが最初の一言を発する前に、AIがあなたを「知っている」状態を作るための仕組みです。
レイヤー2:やりとり単位のベクトル検索
ここが最も大きな改善があったポイントです。
当初は、会話のトランスクリプト全体を1つのベクトルとしてエンベディングしていました。給与、週末の予定、家族の結婚式といった内容を含む20分の会話が、すべて1つのベクトルになっていました。つまり、それらすべてのトピックを混ぜ合わせた平均値を表す、数学的空間上の一点に過ぎませんでした。
そのため「給与」について検索するとその会話は見つかりますが、同様に情報が希釈された他の長い会話も一緒にヒットしてしまいます。重要なシグナルが埋もれてしまっていました。
この問題の解決策は、やり取りごとにチャンクを分けることでした。ユーザーの1つのメッセージとそれに対するAIのレスポンスを1つのチャンクとします。各チャンクはそれぞれ独自のエンベディングベクトルを持ちます。これにより「給与」で検索した場合、会話全体ではなく、給与について話された正確なやり取りをピンポイントで取得できるようになりました。
これは、本のタイトルで検索するのと、各ページ単位ですべてインデックスされている状態の違いに相当します。想起精度の改善は劇的でした。(さらに高精度な検索のために、TiDBは全文検索にも対応しており、キーワードマッチングとベクトル類似度を組み合わせたハイブリッド検索が可能です。現在、これを次に統合する予定です。)
エンベディングにはOpenAIのtext-embedding-3-largeモデル (3,072次元) を使用し、ベクトル検索をネイティブにサポートしているTiDBにベクトルを保存しています。AIがライブ会話中に何かを思い出す必要がある場合、コサイン距離を用いてこれらのチャンクを検索します。コストはごくわずかで、エンベディングにかかる費用はユーザー1人あたり年間10セント未満です
レイヤー3:未加工のトランスクリプト
すべての言葉を、修正を加えずそのまま保存します。これは、決してモデルによって要約されたり、圧縮されたり、歪められたりしない基準データです。もしプロフィールの要約で何かが漏れたり、ベクトル検索が予期せぬ結果を返したりしても、元データは常に存在します。
この3レイヤー構成のアプローチを検証した後、私はMem0を完全に削除しました。これはMem0が質の低いソフトウェアだからではなく、アーキテクチャが機能するようになった時点で、もはや付加価値を生まなくなっていたからです。それは単に、私とデータの間に介在する、単なる余計な依存関係に過ぎなくなっていました。
なぜPostgres + PineconeではなくTiDBを選んだのか
データベースの選択については、独立したセクションを設ける価値があります。というのも、これはRAGアプリケーションにおける最も一般的なアーキテクチャパターンの一つに関わる問題であり、なぜそのパターンが多くのユースケースにおいて間違っていると私が考えるのか、その理由を説明するものだからです。
ほとんどのRAGチュートリアルでは、同じスタックを推奨しています。リレーショナルデータにはPostgres、ベクトルデータにはPinecone。2つのデータベース、2つの請求、そしてそれらを同期するジョブが必要です。
Speak2Meにおいて、AIが記憶を呼び出す必要があるときに実際に実行されるクエリは、以下の通りです:
SELECT
e.title,
e.top_emotions,
c.chunk_text,
VEC_COSINE_DISTANCE(c.embedding, ?) AS relevance
FROM s2m_transcript_chunks c
JOIN s2m_journal_entries e ON c.entry_id = e.id
WHERE c.user_id = ?
AND e.created_at > DATE_SUB(NOW(), INTERVAL 30 DAY)
ORDER BY relevance
LIMIT 5
ベクトル検索、日付フィルタリング、ユーザーの絞り込み。完全なコンテキストを抽出するための結合。これらが1つのクエリと1回のネットワークアクセスで実行されます (TiDBがサポートするベクトル関数や演算子のリストも参照してください)。
一方で、Postgres + Pineconeの構成では、同じ操作は次のようになります:
まずベクトルを指定してPineconeを呼び出し、チャンクIDを取得し、そのIDを使ってPostgresを呼び出し、アプリケーションコード側で結果を結合します。つまり2回のラウンドトリップが発生し、障害点も2つに増え、結合ロジックはデータベースのオプティマイザではなく、JavaScript上で実行されることになるのです。
事前フィルタリングがすべてを変える
ベクトル検索は計算コストが高い — クエリベクトルを何百万もの保存済みベクトルと比較するには、膨大な計算リソースが必要になります。TiDBでは、まず標準的なインデックスを使ってuser_idや日付範囲によるフィルタリングを行います。これにより、処理は高速かつ低コストになります。その上で、大幅に絞り込まれたサブセットに対してのみベクトル比較を実行します。
一方、多くのベクトル専用データベースはその逆の手順を取ります:まずすべてのベクトルを検索し、その後で一致しないメタデータを取り除くのです。大規模な環境において、この違いは極めて重要になります。
リアルタイムAIのための強力な一貫性
会話の最中、AIはあなたがたった今話した内容から事実を抽出し、それを保存します。そして、同じセッション内で30秒後にそれを呼び出すことがあるかもしれません。Postgres + Pineconeのアーキテクチャでは、同期の遅延を管理する必要があります。つまりPostgresに書き込み、Pineconeを更新するジョブをトリガーし、次の呼び出しまでに終わることを祈る、という具合です。結果整合性に伴う頭痛の種です。
TiDBであれば、エンベディングを書き込んだ瞬間に、即座に検索可能になります。同一のトランザクション内で完結するのです。遅延も、同期ジョブも、「自分の書き込みを読み取れない」といった問題もありません。
1つのデータベース。データとその詳細を示すベクトルが同居している。より速くリリースし、より簡単にデバッグする (より詳しく知りたい場合は、アーキテクチャガイド:生成AIにおいて統合データ・アーキテクチャが重要な理由.をご覧ください)。
AIメモリアーキテクチャ:レイテンシ問題の解決
メモリアーキテクチャを改善した後でも、UXを壊す問題が残っていました。それはレイテンシです。
AIが何かを思い出す必要があるとき、以前は即座に応答を開始していました。自信満々で、具体的に。しかし、その内容はしばしば間違っていました。そして10〜20秒後、ようやくベクトル検索の結果が届くと、AIは文章の途中で自分の間違いを訂正し始めるのです。
その瞬間、プロダクトとしての約束は崩れ去ります。あなたは自分を理解してくれている相手と話しているのではなく、コンピュータが自分を検索している様子をただ眺めているだけになってしまうからです。
解決策は、メモリの取得をクエリ実行時からセッション開始時へと移動させることでした。現在は、会話が終了した時点で、Claude Haikuが約500ミリ秒という短時間で、主要な事実を同期的に抽出します。それは単なる名前や日付ではなく、まるで友人が覚えているような詳細な情報です。特定のレストラン、間近に控えた面接、何気なく語った目標などです。
次にアプリを開いたとき、ダッシュボードはバックグラウンドでプロフィール概要と、直近20件の事実を事前に取得します。あなたが口を開く頃には、AIはすべての文脈を把握しています。ツール呼び出しも、待ち時間もありません:
| セッション終了時 | セッション開始時 | メモリの呼び出し | |
|---|---|---|---|
| 改善前 | 即時 | ~2秒 | 5–10秒 (ツール呼び出し) |
| 改善後 | +500ミリ秒 | 即時 | ほとんど不要 |
古い記憶についてはまだリカバリーツールが存在します。例えば 「3ヶ月前に〜について何て言ったっけ?」のような場合です。しかし、最近の出来事については、AIはすでに知っています。これにはより多くのトークンが必要になりますが、AIが何かを瞬時に思い出し、一切の間や訂正なしで応答する初めての体験こそが、この製品の価値なのです。
地獄のようなボイス・エコー
Speak2Meは音声ファーストのアプリであり、Hume EVIを搭載しています。Hume EVIは、音声文字変換、感情検知、LLMルーティング、音声合成を単一のWebSocket接続で処理します。AIが話す際、Humeは48次元以上の音声表現を検出するため、ユーザーの声がストレスを感じているように聞こえれば、AIはそれに応じて応答を調整します。
しかし、ここで誰もドキュメント化していない問題があります。AIがスマートフォンのスピーカーから音を出すと、その音をマイクが拾ってしまい、AIが自分の言葉を文字起こしして自分自身に応答してしまうのです。無限フィードバックループです。
ネイティブのiOSアプリであれば、OSがハードウェアレベルの音響エコーキャンセラーを提供してくれます。しかし、モバイルブラウザ上で動作するウェブアプリの場合、ブラウザの実装状況に翻弄されることになります。特にモバイル版Safariの挙動は、控えめに言っても不安定です。
マイクをミュートにする方法 (これでは自然な割り込みができなくなります) なども試しましたが、最終的にブラウザに組み込まれたオーディオの制約を使用することに落ち着きました:
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true
デスクトップでは、この手法はうまく機能します。モバイルでも、音量を下げれば許容範囲内です。しかし、真の解決策はシステムレベルのエコーキャンセラーを備えたネイティブiOSアプリを構築することであり、現在その開発を進めています。
もしあなたがウェブ上でリアルタイム音声AIを構築しようとしているなら、オーディオエンジニアリングには想定よりもかなり多くの時間を予算として割いておくべきです。この領域は、実質的に未踏の地と言っても過言ではありません。
次のステップ
Speak2Meはすでに稼働中です。直近の最優先事項は暗号化です。ユーザーは極めて個人的な思考を共有しているため、ジャーナルの文字起こしデータは、保存時に暗号化されている必要があります。その次は、ハードウェアレベルでエコー問題を解決し、プッシュ通知、バックグラウンド再生、生体認証を追加するためのネイティブiOS版の開発です。
メモリシステムについては、実際の会話データが蓄積されるにつれて継続的に改善していきます。もしあなたがAIのメモリを活用したプロダクトを開発しているなら、ここで記録したアーキテクチャ上の失敗談が、あなたの時間を節約につながることを願います。AIアプリケーションに適したデータインフラを選ぶ方法や、あるいは、プライバシー重視の音声文字変換アプリやAIライフシミュレーターで私がどのように同様のパターンを適用したか興味がある方は、ぜひそれらの深掘り記事も読んでみてください。
もしSpeak2Meを試してみたいなら、実際に話しかけてみてください。本音を伝えてみて、翌日戻った際に、それを覚えているか確かめてください。
TiDB Cloud Starterで開発を始めましょう — ベクトル検索、SQLジョイン、そして強力な一貫性を備えた、MySQL互換のデータベースです。
TiDB Cloud Dedicated
TiDB Cloudのエンタープライズ版。
専用VPC上に構築された専有DBaaSでAWSとGoogle Cloudで利用可能。
TiDB Cloud Starter
TiDB Cloudのライト版。
TiDBの機能をフルマネージド環境で使用でき無料かつお客様の裁量で利用開始。