AIアプリの長期記憶設計:キーワード・ベクトル・グラフ・時系列検索の使い分け
AIアプリの長期記憶設計:キーワード・ベクトル・グラフ・時系列検索の使い分け
基本情報
| 項目 | 内容 |
|---|---|
| 記事種別 | 編集部による実務解説 |
| 分類 | AI Architecture |
| 情報元 | AI News Editorial |
| 公開日 | 2026/07/14 |
| 確認日 | 2026/07/14 |
| 読む目的 | AIアプリの記憶を会話ログやベクトル検索だけに任せず、保存同意、構造化データ、複数の検索方式、訂正・削除まで一つの設計として整理します。 |
先に押さえること
- 検索方式は記憶の種類と質問に合わせて使い分ける。
- 元データと派生インデックスを追跡可能にする。
- ユーザーが記憶を確認・訂正・削除できる導線を用意する。
3行要約
- 記憶、現在のコンテキスト、モデル学習、検索インデックスを分けて設計する。
- 完全一致、意味検索、関係探索、時間軸を用途に応じて組み合わせる。
- 保存同意、アクセス制御、訂正、失効、削除を後付けにしない。
実務コメント
保存量を増やす前に、何を覚えるか、誰が見られるか、いつ失効するか、どう消せるかを決めてください。検索精度だけでなく、誤取得と削除漏れを測れる構成が重要です。
概要
AIアプリの記憶は、会話履歴をすべてベクトル化すれば完成するものではありません。固有名詞はキーワード検索、意味の近さはベクトル検索、人物や案件の関係はグラフ、経緯は時系列で扱い、保存同意・訂正・削除をデータモデルへ組み込みます。
AIアプリの「記憶」とは
AIアプリにおける記憶とは、過去の入力やユーザー情報を保存し、必要な場面で検索して、モデルへ再提示する仕組みです。
LLM自体が、アプリで交わしたすべての会話を恒久的に覚え続けるとは限りません。多くのAIアプリでは、次の処理を外部システムとして設計します。
1. 会話や操作から保存候補を抽出する 2. 保存してよい情報か判定する 3. 元データと構造化データを保存する 4. 検索用インデックスを作る 5. 現在の質問に関連する情報を取得する 6. 必要な情報だけLLMへ渡す 7. 訂正、失効、削除を反映する
この意味で、AIアプリの記憶は「データベース」「検索」「権限」「保持期間」「ユーザーインターフェース」を組み合わせた機能です。
確認日: 2026年7月13日
記憶とコンテキストを分ける
AIアプリでは、次の用語を分けると設計しやすくなります。
会話ログを保存することと、モデルが学習することは別です。
また、検索インデックスは元データそのものではありません。削除や訂正を正しく行うには、元データ、要約、embedding、グラフの関係を追跡できる必要があります。
| 用語 | 意味 | 例 |
|---|---|---|
| 現在のコンテキスト | 今回の応答へ直接渡す情報 | 直近10件の会話 |
| 短期記憶 | 数分から数日の作業状態 | 現在編集中の企画、未完了タスク |
| 長期記憶 | セッションをまたいで使う情報 | 好み、継続プロジェクト、既定設定 |
| エピソード記憶 | いつ何が起きたか | 7月10日に要件を変更した |
| 意味記憶 | 安定した事実や定義 | 主言語は日本語 |
| 手続き記憶 | 作業方法や手順 | 提出前にlintを実行する |
| 検索インデックス | 記憶を探すための派生データ | 転置インデックス、embedding |
| モデル学習 | モデルの重みを変更する処理 | fine-tuning、継続学習 |
最初に決める5つの問い
記憶機能を実装する前に、次を明文化します。
最初に決める5つの問い:1. 何を覚えるか
候補:
保存しない候補:
- ユーザーが明示的に保存した情報
- 表示言語
- 通知設定
- 継続プロジェクト
- 過去の判断
- 作業上の制約
- よく使う出力形式
- 未完了タスク
- 組織内の文書
- 商品や顧客に関する業務データ
- 一時的な雑談
- 推測した属性
- センシティブな情報
- 認証情報
- 保存目的がない入力
- すでに失効した作業状態
最初に決める5つの問い:2. 何のために使うか
同じデータでも、目的によって必要な検索方式が変わります。
ユーザー設定を再現する:
完全一致、構造化検索
過去の似た相談を探す:
ベクトル検索
案件に関係する人物をたどる:
グラフ検索
直近の方針変更を確認する:
時系列検索最初に決める5つの問い:3. いつまで保持するか
「将来役立つかもしれない」だけで無期限保存しない設計が必要です。
- セッション終了まで
- 24時間
- 30日
- プロジェクト終了まで
- ユーザーが削除するまで
- 法令や契約で定めた期間
最初に決める5つの問い:4. 誰が参照できるか
記憶検索は、検索結果を生成してから権限で隠すのではなく、検索対象を決める段階でアクセス範囲を絞ります。
- 本人だけ
- 同じ組織
- 特定チーム
- 担当者
- AIエージェント
- 管理者
- 外部連携先
最初に決める5つの問い:5. どう訂正・削除するか
この問いへ答えられない状態で、長期記憶を増やさない方が安全です。
- 記憶一覧を表示できるか
- 保存理由を表示できるか
- 1件ずつ編集できるか
- プロジェクト単位で削除できるか
- 派生インデックスも消えるか
- バックアップから再出現しないか
- 削除完了を確認できるか
4種類の検索を使い分ける
AIアプリの記憶検索では、主に次の4方式を組み合わせます。
1種類ですべて解決しようとすると、誤取得または取りこぼしが増えます。
| 方式 | 得意な問い | 苦手な問い |
|---|---|---|
| キーワード検索 | 固有名詞、型番、エラーコード、完全一致 | 言い換え、抽象的な類似 |
| ベクトル検索 | 意味の近さ、類似相談、言い換え | 正確な文字列、否定、時系列 |
| グラフ検索 | 人物、案件、組織、依存関係 | 長文の意味類似 |
| 時系列検索 | 最新状態、変更履歴、期限 | 意味上の近さ、複雑な関係 |
キーワード検索
キーワード検索は、入力に含まれる語句と文書中の語句を照合します。
一般的には、転置インデックス、BM25、全文検索エンジン、データベースの全文検索機能などを使います。
キーワード検索:向いている情報
例:
これらは意味の近さより、文字列が一致することが重要です。
B0G8HBXBCM
ERR_CONNECTION_REFUSED
CLAUDE.md
customer-1842- 人名
- 会社名
- 製品名
- 型番
- URL
- ファイル名
- エラーコード
- API名
- コマンド
- 数値
- 日付
- チケット番号
- 完全一致が重要な用語
キーワード検索:利点
- 検索理由を説明しやすい
- 固有名詞に強い
- インデックスが比較的軽い
- 完全一致や除外条件を扱いやすい
- 数字や記号を探しやすい
- 更新・削除の反映が比較的明確
キーワード検索:注意点
- 表記揺れに弱い
- 同義語を拾いにくい
- 日本語では形態素解析や分かち書きが必要になる
- 一般語と専門語の重み調整が必要
- 誤字や言い換えへ弱い
キーワード検索:記憶での利用例
固有語を含む場合は、最初からベクトル検索だけに任せず、キーワード検索の結果を候補へ含めます。
質問:
「先月のOllamaのエラー記事を出して」
検索:
ollama
llama-server
binary not foundベクトル検索
ベクトル検索は、文章や画像などをembeddingへ変換し、ベクトル空間上の近さから類似情報を取得します。
Pinecone公式ドキュメントでは、dense vectorはテキストなどの意味や関係を数値表現し、semantic searchに利用すると説明されています。
ベクトル検索:向いている情報
例:
単語が完全一致しなくても、意味が近ければ候補にできます。
検索文:
「以前話した、静的サイトで広告を差し替える仕組み」
保存文:
「アフィリエイト枠を設け、イベント連動で配信内容を変更する」- 言い換え
- 類似相談
- 過去の似た事例
- 文書の意味検索
- 曖昧な自然言語質問
- 長文から関連部分を探す
- 表現が異なる同じ概念
ベクトル検索:利点
- 同義語や言い換えに強い
- 自然言語で検索しやすい
- 長い会話や文書から関連部分を探せる
- 多言語embeddingでは言語をまたぐ検索も可能
ベクトル検索:注意点
- 完全一致を保証しない
- 数字、ID、否定条件を誤解しやすい
- 類似しているが別の情報を返す
- embeddingモデル変更時に再計算が必要
- スコアの意味がモデルやデータで異なる
- 削除時にvector indexも更新する必要がある
- 誤った要約をembeddingすると検索も誤る
- 機密情報が外部embedding APIへ送信される場合がある
ベクトル検索:ベクトル検索だけで記憶を作らない
次のような質問は、意味類似だけでは危険です。
似た過去情報ではなく、正確で現在有効な1件が必要だからです。
この場合は、構造化データ、時系列、有効期間、状態を先に絞り、その後にベクトル検索を使います。
「現在の配送先住所は?」
「最後に承認された価格はいくら?」
「プロジェクトAの責任者は誰?」
「昨日変更した設定は?」ハイブリッド検索
ハイブリッド検索は、キーワード検索とベクトル検索を組み合わせます。
Pinecone公式ドキュメントでは、dense vectorによるsemantic signalと、sparse vectorによるlexical signalを組み合わせる方式が案内されています。
ハイブリッド検索:なぜ組み合わせるのか
キーワード検索だけの場合:
という語がないと、次を見落とす可能性があります。
ベクトル検索だけの場合:
のような正確なエラーコードを、意味が似た別の接続エラーと混同する可能性があります。
ハイブリッド検索では、両方を候補へ含め、スコアを統合します。
「購入率改善」
「商品ページから注文までの離脱を減らす」
ERR_CONNECTION_REFUSEDハイブリッド検索:基本構成
ユーザー質問
├── keyword search
└── vector search
↓
結果を統合
↓
metadata filter
↓
reranking
↓
LLMへ渡すハイブリッド検索:スコア統合方法
候補:
固定の重みをすべての質問へ使うより、質問タイプで変える方法があります。
ID・型番を含む:
keyword重視
抽象的な相談:
vector重視
人名と相談内容を含む:
両方
日付を含む:
time filterを先に適用- 重み付き和
- Reciprocal Rank Fusion
- keywordで絞ってvectorで並べ替える
- vectorで候補を出してkeyword一致を加点する
- rerankerで再評価する
グラフ検索
グラフ検索は、情報をnode、relationship、propertyとして保存し、関係をたどります。
Neo4j公式ドキュメントでは、graph databaseはデータをtableやdocumentではなく、node、relationship、propertyとして保存すると説明されています。
グラフ検索:向いている情報
例:
質問:
必要な情報は単一文書の意味類似ではなく、複数の関係をたどった結果です。
(User)-[:OWNS]->(Project)
(Project)-[:USES]->(Technology)
(Decision)-[:APPLIES_TO]->(Project)
(Decision)-[:SUPERSEDES]->(OldDecision)
「このプロジェクトの現在の担当者が、
以前決めた認証方式は何?」- 人と組織の関係
- 案件と担当者
- 商品とカテゴリ
- 文書間の参照
- 依存ライブラリ
- 顧客と契約
- タスクと前提条件
- 意思決定と根拠
- 会話中に登場した対象同士の関係
グラフ検索:グラフの利点
- 関係を明示できる
- 複数段階の探索に向く
- なぜ結果が関連するか説明しやすい
- 重複する人物・組織を統合しやすい
- 現在有効な関係と過去の関係を分けられる
- 権限継承や組織構造を表現できる
グラフ検索:注意点
- entity抽出を誤ると誤った関係が保存される
- 同姓同名や別名の統合が難しい
- relationshipの型を増やしすぎると管理しにくい
- 自然言語から自動生成した関係は根拠を保持する必要がある
- グラフだけでは長文の意味検索に弱い
- 削除時に関連edgeも処理する必要がある
グラフ検索:根拠を持つグラフ
AIが抽出した関係を、事実として直接保存しない方が安全です。
どの発言から抽出したか、誰が確認したか、いつまで有効かを保存します。
{
"subject": "Project A",
"relation": "USES",
"object": "PostgreSQL",
"source_record_id": "message_182",
"source_span": "DBはPostgreSQLで進める",
"confidence": 0.94,
"valid_from": "2026-07-13T10:00:00+09:00",
"valid_to": null,
"verification_status": "user_confirmed"
}時系列検索
AIアプリの記憶では、似ている情報より新しい情報が優先される場面があります。
例:
ベクトル検索では両方が同程度に関連する可能性があります。
そのままLLMへ渡すと、古い指示を採用することがあります。
7月1日:
出力形式はPDF
7月10日:
今後はMarkdown時系列検索:保存すべき時間情報
- created_at
- updated_at
- valid_from
- valid_to
- observed_at
- event_time
- deleted_at
- superseded_by
- source_time
時系列検索:作成時刻と有効時刻を分ける
この場合:
作成時刻だけでは、正しい時系列を再現できません。
2026年7月13日に登録したが、
2026年7月1日から有効な契約
{
"created_at": "2026-07-13T10:00:00+09:00",
"valid_from": "2026-07-01T00:00:00+09:00"
}時系列検索:最新1件だけ残さない
現在値だけを上書きすると、次を説明できません。
現在値と履歴を分けます。
または、各レコードへ有効期間を持たせます。
current_user_preference
preference_events- いつ変更されたか
- なぜ変更されたか
- 誰が変更したか
- 過去時点では何だったか
- 誤変更から戻せるか
構造化記憶と非構造化記憶
記憶をすべて文章で保存すると、正確な値の取得が難しくなります。
構造化記憶と非構造化記憶:構造化記憶
向いている情報:
{
"user_id": "user_123",
"preferred_language": "ja",
"output_format": "markdown",
"valid_from": "2026-07-10T00:00:00+09:00",
"source_record_id": "message_991",
"verification_status": "user_explicit"
}- 言語
- タイムゾーン
- 通知設定
- 既定フォーマット
- プロジェクト名
- 期限
- ステータス
- 担当者
- 明示的な好み
構造化記憶と非構造化記憶:非構造化記憶
向いている情報:
実務では両方を保存します。
静的サイトの広告枠について、
イベント連動で表示内容を差し替える構成を検討した。
構造化:
現在値、filter、権限、時間
非構造化:
背景、根拠、詳細
vector:
意味検索
keyword:
完全一致
graph:
関係- 議論
- 背景
- 調査メモ
- 長い要件
- アイデア
- 例外
- 判断理由
記憶レコードの推奨構造
再利用しやすい最小例です。
最低限、次を追跡します。
{
"memory_id": "mem_01JXYZ",
"subject_id": "user_123",
"scope": "user",
"memory_type": "preference",
"title": "出力形式の希望",
"content": "技術調査はMarkdownで出力する",
"structured_value": {
"output_format": "markdown"
},
"source": {
"type": "conversation_message",
"record_id": "msg_991",
"quoted_span": "技術調査はMarkdownで"
},
"consent": {
"basis": "user_explicit",
"captured_at": "2026-07-10T12:00:00+09:00"
},
"verification_status": "user_confirmed",
"confidence": 1.0,
"created_at": "2026-07-10T12:00:00+09:00",
"updated_at": "2026-07-10T12:00:00+09:00",
"valid_from": "2026-07-10T12:00:00+09:00",
"valid_to": null,
"retention_policy": "until_user_deletes",
"sensitivity": "low",
"access_scope": ["user_123"],
"index_state": {
"keyword": "indexed",
"vector": "indexed",
"graph": "not_applicable"
},
"supersedes": null,
"deleted_at": null
}- 誰の記憶か
- どの範囲で使えるか
- 元情報は何か
- ユーザーが明示したか
- AIが推測したか
- いつ有効か
- いつ削除するか
- どのインデックスへ複製したか
- 何に置き換えられたか
明示的記憶と推論記憶を分ける
ユーザーが述べたことと、AIが推測したことは別です。
明示的記憶と推論記憶を分ける:明示的記憶
保存:
ユーザー:
「今後、技術記事はMarkdownで出して」
{
"verification_status": "user_explicit",
"confidence": 1.0
}明示的記憶と推論記憶を分ける:推論記憶
保存する場合:
推論したセンシティブ属性を長期記憶へ保存しない設計が必要です。
また、推論をユーザー設定として強制適用せず、確認可能な候補として扱います。
AIの推測:
「このユーザーは短い回答を好む可能性がある」
{
"verification_status": "inferred",
"confidence": 0.62,
"requires_confirmation": true
}保存前のユーザー同意
記憶機能では、利用者が「何が保存され、何に使われるか」を理解できる必要があります。
ICOの透明性ガイダンスでは、個人情報の利用について明確で簡潔な情報を提供することが重要とされています。
実装時は、対象法域、契約、データ種別に応じた法務確認が必要です。
保存前のユーザー同意:同意画面で示す内容
- 保存する情報
- 保存目的
- 利用する機能
- 保存期間
- 参照できる範囲
- 外部サービスへの送信
- 編集方法
- 削除方法
- 同意を撤回した場合の扱い
- モデル学習へ使うか
- 自動推論を保存するか
保存前のユーザー同意:一括同意だけにしない
候補:
目的の異なる処理を1つのチェックボックスへまとめない方が、利用者が判断しやすくなります。
会話履歴:
30日保存
好み:
ユーザーが明示した場合のみ保存
業務文書:
組織管理者が設定
推論:
保存しない
モデル改善:
別の選択肢記憶の保存UI
保存のタイミングは次の3方式があります。
実用的な構成例:
表示例:
低リスクの設定:
自動保存して通知
新しい長期的好み:
保存候補を表示
個人情報・センシティブ情報:
明示確認なしに保存しない
一時的な会話:
長期保存しない
記憶として保存しますか?
内容:
技術調査はMarkdown形式を希望
利用範囲:
今後の技術調査
[保存] [今回だけ] [編集]| 方式 | 利点 | 注意 |
|---|---|---|
| 明示保存 | 意図が明確 | 利用者の操作が増える |
| 保存前確認 | 誤保存を減らす | 毎回確認すると煩雑 |
| 自動保存 | 便利 | 透明性と誤推論のリスク |
記憶の取得パイプライン
推奨する処理順です。
1. 質問を分類
2. user・tenant・projectの権限filter
3. 現在有効なレコードだけfilter
4. 構造化データを取得
5. keyword search
6. vector search
7. graph traversal
8. time decay・最新状態を評価
9. 結果を統合
10. reranking
11. 重複と矛盾を検出
12. 根拠付きでLLMへ渡す記憶の取得パイプライン:質問分類の例
分類器が誤る可能性を考え、重要な現在値は構造化データから必ず確認します。
{
"query_type": "current_preference",
"requires_exact_match": true,
"requires_semantic_search": false,
"requires_graph": false,
"requires_time_order": true
}
{
"query_type": "similar_past_discussion",
"requires_exact_match": true,
"requires_semantic_search": true,
"requires_graph": false,
"requires_time_order": true
}Metadata filterを先に使う
ベクトル検索で全ユーザーの記憶を検索してから結果を隠す設計は避けます。
先に次を絞ります。
その範囲でkeyword・vector検索を実行します。
これにより、別ユーザーや別組織のデータが候補へ入るリスクを下げます。
{
"subject_id": "user_123",
"project_id": "project_A",
"deleted_at": null,
"valid_from_lte": "2026-07-13T10:00:00+09:00",
"valid_to_gt_or_null": "2026-07-13T10:00:00+09:00",
"sensitivity_in": ["low", "medium"]
}時間減衰を無条件に使わない
新しい記憶を高く評価するtime decayは便利ですが、すべての記憶へ適用すると誤ります。
時間減衰を無条件に使わない:新しさを優先する情報
- 現在の好み
- 担当者
- 住所
- プロジェクト状態
- 締切
- 使用バージョン
- 価格
- 在庫
時間減衰を無条件に使わない:新しさだけで決めない情報
記憶typeごとに評価式を変えます。
preference:
current validityを最優先
incident:
query relevanceを最優先
task:
statusとdue dateを最優先
decision:
supersededかどうかを最優先- 法的な合意
- 設計判断の根拠
- 過去の障害
- ユーザーの長期目標
- 歴史的記録
- 監査ログ
矛盾する記憶の扱い
記憶は後から変わります。
例:
古い記憶を即削除すると履歴を失います。両方を同時に有効にすると回答が不安定になります。
推奨構造:
旧記憶:
回答時には現在有効な新記憶を使い、変更経緯を聞かれたときだけ両方を返します。
旧:
使用DBはMySQL
新:
使用DBはPostgreSQL
{
"memory_id": "mem_new",
"content": "使用DBはPostgreSQL",
"valid_from": "2026-07-13T10:00:00+09:00",
"supersedes": "mem_old"
}
{
"memory_id": "mem_old",
"content": "使用DBはMySQL",
"valid_to": "2026-07-13T10:00:00+09:00",
"superseded_by": "mem_new"
}記憶の削除性
記憶機能は、保存より削除の方が難しくなります。
1件の会話から、次の派生データが作られる可能性があります。
元メッセージだけ削除しても、派生データが残れば完全な削除になりません。
ICOのデータ最小化・保存期間に関するガイダンスでも、保持スケジュールに沿った削除や、削除できない場合のアクセス遮断・匿名化が論点として示されています。
実際の法的義務は地域、処理目的、法的根拠によって異なるため、専門家へ確認してください。
元メッセージ
要約
構造化記憶
embedding
keyword index
graph node
graph relationship
cache
analytics log
backup
evaluation dataset削除グラフを持つ
各派生データにsource_record_idを持たせます。
削除要求:
削除結果:
msg_123
├── summary_88
├── mem_77
├── vector_204
├── keyword_doc_204
├── graph_edge_31
└── cache_key_91
delete source msg_123
↓
依存レコードを列挙
↓
オンラインストアから削除
↓
検索インデックスから削除
↓
cache invalidation
↓
バックアップ保持方針を適用
↓
削除監査ログを記録
{
"request_id": "del_01",
"source_record_id": "msg_123",
"status": "completed",
"deleted": {
"primary_records": 1,
"structured_memories": 1,
"vector_records": 1,
"keyword_documents": 1,
"graph_relationships": 2,
"cache_entries": 3
},
"backup_status": "scheduled_expiry",
"completed_at": "2026-07-13T10:15:00+09:00"
}論理削除と物理削除:論理削除
利点:
注意:
{
"deleted_at": "2026-07-13T10:00:00+09:00",
"deletion_reason": "user_request"
}- 誤削除から戻せる
- 監査しやすい
- 非同期で派生削除できる
- 通常検索から確実に除外する
- LLMコンテキストへ入れない
- 一定期間後に物理削除する
- 管理者が無制限に閲覧できないようにする
論理削除と物理削除:物理削除
ストレージとインデックスから実データを消します。
削除要求の種類と法的要件に応じて、論理削除だけで十分か確認が必要です。
バックアップからの再出現を防ぐ
本番DBから削除しても、古いバックアップを復元すると記憶が戻る可能性があります。
対策:
バックアップを直接編集して1件削除できないシステムでは、復元時の再削除工程が必要です。
- バックアップ保持期間を明記する
- 削除IDのtombstone ledgerを別管理する
- 復元後に削除要求を再適用する
- バックアップへのアクセスを限定する
- 保持期間経過後に安全に破棄する
- 削除完了表示でバックアップ上の扱いを説明する
記憶の訂正
誤った記憶を消すだけでなく、訂正できる必要があります。
例:
訂正処理:
1. 元の記憶を無効化する 2. 新しい記憶を作る 3. supersedesで関連付ける 4. keyword indexを更新する 5. embeddingを再計算する 6. graph propertyを更新する 7. cacheを無効化する 8. 訂正時刻を記録する
元データの発言自体を修正するのか、解釈だけを訂正するのかも分けます。
誤:
ユーザーはWindowsを使う
正:
メインはWindows、サーバーはLinux記憶の一覧画面
利用者が確認できる画面には、次を表示します。
推論情報には明示します。
利用者が見えない記憶を増やさないことが、誤作動の発見にも役立ちます。
AIによる推測
確信度: 低
[確認] [修正] [削除]| 項目 | 表示例 |
|---|---|
| 内容 | 技術調査はMarkdown形式 |
| 種類 | 出力設定 |
| 保存日 | 2026年7月10日 |
| 情報源 | 会話から保存 |
| 確認状態 | 本人が明示 |
| 利用範囲 | 技術調査 |
| 保存期間 | 削除するまで |
| 操作 | 編集、削除、一時停止 |
センシティブ情報
AIアプリが扱うデータには、保存すべきでない情報または厳格な制御が必要な情報があります。
例:
実装上の候補:
適用法令と組織ポリシーを必ず確認してください。
自動保存しない
明示的な保存操作を要求する
別ストレージへ分離する
暗号化鍵を分ける
利用目的を限定する
保持期間を短くする
閲覧ログを残す
外部embedding APIへ送らない- 健康情報
- 政治的意見
- 宗教
- 性生活・性的指向
- 生体情報
- 犯罪歴
- 正確な位置情報
- 金融情報
- 認証情報
- 子どもの情報
- 組織の秘密情報
記憶へ保存しない認証情報
次は長期記憶へ保存しません。
これらはシークレット管理基盤で扱います。
ユーザーが会話へ貼った場合は、記憶抽出対象から除外し、ログのマスキングや削除方法を用意します。
- APIキー
- パスワード
- 秘密鍵
- セッショントークン
- OAuth refresh token
- クレジットカード番号
- リカバリーコード
- 暗号化キー
マルチテナントの分離
組織向けAIアプリでは、別tenantの記憶が混ざることが重大事故になります。
最低限:
危険な構成:
検索エンジンまたはデータベースの段階でtenantを制約します。
全tenantのvectorを検索
↓
上位結果を取得
↓
アプリ側でtenantが違う結果を捨てる- すべてのrecordへtenant_id
- vector namespaceを分ける
- keyword indexをtenantでfilterする
- graph queryへtenant条件を含める
- cache keyへtenantを含める
- batch処理でもtenantを検証する
- evaluation環境へ本番データを無断コピーしない
Embeddingモデル変更
embeddingモデルを変更すると、古いvectorと新しいvectorを同じ空間で比較できない場合があります。
保存する情報:
移行手順:
1. 新インデックスを作る 2. 元テキストから再embeddingする 3. 一部トラフィックで比較する 4. recallと誤取得を評価する 5. 新インデックスへ切り替える 6. 旧vectorを削除する
元テキストを保持していない場合、再計算できません。
一方、削除目的から元テキストを長期保存しない設計もあります。再embedding可能性とデータ最小化を比較してください。
{
"embedding_model": "model-name",
"embedding_version": "2026-06",
"dimensions": 1536,
"created_at": "2026-07-13T10:00:00+09:00"
}Chunk設計
長い会話や文書はchunkへ分けてembeddingします。
大きすぎるchunk:
小さすぎるchunk:
保存するmetadata:
取得時に前後chunkを追加する方法もあります。
{
"document_id": "doc_1",
"chunk_id": "chunk_7",
"sequence": 7,
"speaker": "user",
"start_offset": 1820,
"end_offset": 2390,
"created_at": "2026-07-13T09:20:00+09:00",
"tenant_id": "tenant_1",
"project_id": "project_A"
}- 複数話題が混ざる
- どの部分が関連したか分かりにくい
- 不要な個人情報もLLMへ渡りやすい
- 背景を失う
- 代名詞の対象が分からない
- 検索結果が断片化する
- index件数が増える
要約記憶の注意
会話全体を要約して保存すると、検索量を減らせます。
ただし、要約は事実を失ったり、AIの解釈を混ぜたりします。
推奨構成:
要約だけを唯一の保存データにしない方が、訂正と根拠確認が容易です。
raw source:
元発言
summary:
検索と概要表示用
structured facts:
確認済みの現在値
embedding:
rawまたは検証済みsummaryから生成記憶取得の評価
検索精度は、数件のデモだけでは判断できません。
評価データの例:
指標:
重要な評価:
{
"query": "現在の出力形式は?",
"expected_memory_ids": ["mem_22"],
"must_not_return": ["mem_4"],
"reason": "mem_4は旧設定"
}
似た記憶を取れるか
ではなく
現在有効な正しい記憶を取れるか- Recall@K
- Precision@K
- MRR
- nDCG
- exact ID match
- stale memory rate
- cross-tenant leakage rate
- sensitive memory exposure rate
- unsupported answer rate
- deletion propagation time
誤取得を検出する
取得結果へ次の情報を付けます。
LLMへは、本文だけでなく、現在有効か、本人確認済みか、元情報があるかを渡します。
回答生成ルール:
{
"memory_id": "mem_22",
"retrieval_sources": ["keyword", "vector"],
"keyword_rank": 2,
"vector_rank": 1,
"vector_score": 0.86,
"validity": "current",
"verification_status": "user_confirmed",
"source_record_id": "msg_91"
}
inferred:
断定しない
expired:
現在情報として使わない
conflicting:
ユーザーへ確認する
user_confirmed:
現在有効なら優先
missing source:
重要判断には使わない全会話を無条件で保存する
問題:
対策:
- 不要な個人情報が増える
- 検索ノイズが増える
- 削除範囲が大きくなる
- 保持コストが増える
- ユーザーが何を覚えられたか分からない
- 保存目的を限定する
- 一時会話と長期記憶を分ける
- 保存候補を分類する
- 保持期間を設ける
ベクトル検索だけで現在値を決める
問題:
対策:
- 古い値を返す
- 似た別案件を返す
- 数字やIDを誤る
- 否定文を混同する
- current stateを構造化する
- valid_fromとvalid_toを持つ
- keyword検索と組み合わせる
- supersedesを扱う
AIの推測を事実として保存する
問題:
対策:
- 誤った属性を固定化する
- 次の回答が推測に引っ張られる
- 利用者が修正できない
- センシティブ属性を生成する可能性がある
- explicitとinferredを分ける
- 推論は低い優先度にする
- 確認UIを用意する
- 保存禁止カテゴリを作る
削除対象を元テーブルだけにする
問題:
対策:
- vectorに残る
- graphに残る
- cacheから返る
- 要約に残る
- バックアップ復元で戻る
- lineageを管理する
- 非同期削除ジョブを作る
- 削除完了を検証する
- 復元後に削除ledgerを再適用する
関連度だけで順位を決める
問題:
順位付け候補:
関連度はその一部です。
access permission
current validity
verification status
exact match
semantic relevance
relationship distance
recency
source quality- 別tenantを返す
- 失効情報を返す
- 未確認の推論を優先する
- 古い情報を返す
最小構成
最初からvector databaseとgraph databaseを導入する必要はありません。
最小構成:フェーズ1
用途:
PostgreSQL
- memories
- memory_events
- consent_records
- deletion_requests
- full-text search- 明示的な設定
- 現在値
- 時系列
- keyword検索
- 削除
最小構成:フェーズ2
用途:
+ vector index- 類似相談
- 長文検索
- 言い換え
最小構成:フェーズ3
用途:
+ graph- 人物、案件、文書の関係
- multi-hop検索
- 依存関係
最小構成:フェーズ4
必要性を測定してから追加します。
+ reranker
+ automatic memory extraction
+ user review UI
+ evaluation pipeline推奨アーキテクチャ
削除経路:
Conversation / Documents / App events
↓
Classification & Redaction
↓
Consent / Policy check
↓
Canonical memory store
├── structured state
├── raw evidence
└── temporal history
↓
Indexing pipeline
├── keyword index
├── vector index
└── graph projection
↓
Retrieval router
├── exact lookup
├── lexical search
├── semantic search
├── graph traversal
└── temporal filter
↓
Merge / Rerank / Validate
↓
Context for the LLM
User deletion request
↓
Lineage lookup
↓
Canonical store / Keyword / Vector / Graph / Cache
↓
Completion auditどの検索を選ぶか
| 質問 | 推奨方式 |
|---|---|
| 現在の表示言語は? | 構造化lookup |
| 「ERR_1042」の過去事例 | keyword |
| 以前話した似た企画 | vector |
| 商品Aに関係する案件と担当者 | graph |
| 最後に変更した方針 | time + structured |
| 「Astro」と静的サイトの議論 | hybrid |
| 3か月前の同種障害 | time filter + hybrid |
| 現担当者が承認した最新仕様 | graph + time + structured |
| ユーザーが削除した内容 | 取得しない |
向いている読者
- AIチャットへ長期記憶を追加する開発者
- RAGを設計するエンジニア
- SaaSのパーソナライズを担当する人
- 社内AI検索を構築する人
- ベクトルDB導入を検討する人
- 顧客データを扱うプロダクト責任者
- AI機能のプライバシー設計を担当する人
- 記憶の誤取得や削除漏れに困っている人
次に行うこと
最初に、実際の質問を20件から50件集め、次の表を作ります。
その後、各記憶へ次を追加します。
最初の実装では、明示的な構造化記憶、全文検索、時系列、削除機能から始めます。意味検索が本当に必要な質問が確認できた段階でベクトル検索を追加し、複数の関係をたどる要件が増えた場合にグラフを検討してください。
source
scope
valid_from
valid_to
verification_status
retention_policy
deletion lineage| 質問 | 必要な記憶 | 正確性 | 検索方式 | 有効期間 | 同意 |
|---|---|---|---|---|---|
| 現在の出力形式は? | 明示設定 | 完全一致 | 構造化 | 現在 | 明示 |
| 以前の似た企画は? | 会話・文書 | 類似 | vector | 全期間 | 設定次第 |
| この案件の担当者は? | 人物関係 | 完全一致 | graph | 現在 | 業務規程 |
| 先月の障害は? | event | 時系列 | hybrid | 指定期間 | 業務規程 |