Memory Decay & Versioned Truth|記憶の減衰とバージョン管理¶
一言で(TL;DR)¶
記憶エントリに TTL(Time-To-Live)と版番号(version)を付与し、古い情報を自動的に失効させるとともに、事実が更新された際は旧版を superseded タイムスタンプ付きで保持します。テンポラルデータベースの valid_from / valid_to と同じ発想で、エージェントの記憶の鮮度と監査追跡性を両立させます。
解決する問題¶
エージェントがメモリで状態を跨いで渡す(F6)とき、記憶の中身には鮮度の異なる情報が混在します。為替レートや在庫数のように分単位で変わるデータ、ユーザの嗜好のように週〜月単位で変わるデータ、法人番号のように実質不変のデータが同列に保存されると、古くなった情報をエージェントが「最新の事実」として利用してしまいます。
TTL も版管理もない記憶ストアでは次の問題が起きます。
- 陳腐化した事実による誤判断が起こりえます。3 時間前の在庫数をもとに「在庫あり」と回答し、実際は欠品だったというケースです。
[failure_cost]が高い領域(金融・医療・EC の在庫確約)ではこの誤りが直接的な損害につながります。 - 矛盾する複数バージョンの共存も問題になります。同じ事実の旧版と新版が両方残り、検索のタイミングで異なる答えが返ります。デバッグや監査で「なぜその判断をしたか」を再現できません。
- ストレージの肥大化も見逃せません。期限切れの記憶が蓄積し、検索のノイズが増え、コンテキスト予算を圧迫します。
選定条件(When to use / When NOT)¶
- 使う条件
- 記憶する事実に寿命の差があります(価格は分単位、嗜好は月単位、ID は不変、など)。
[failure_cost]が中〜高で、古い記憶に基づく判断が金銭的・法的・安全上の損害を引き起こしうる場合です。- セッションが長時間にわたる、あるいはセッション跨ぎで記憶を再利用する設計です。
- 監査要件があります:「いつ時点でどの事実を参照したか」を後から検証する必要がある場合です。
- 使わない条件(=代替に倒す)
- セッションが短命(数分以内)で記憶の陳腐化が起きない場合 → TTL のオーバーヘッドが見合いません。セッション終了時に記憶を破棄すれば十分です。
- 記憶する情報がすべて不変(ドキュメント ID、定数マスタなど)の場合 → 版管理の必要がありません。
[failure_cost]が低い場合:古い情報で判断しても影響が軽微 → 単純な LRU 追い出しで十分です。D6 Semantic Cache のキャッシュ TTL に寄せます。
駆動変数とチューニング(程度)¶
| 目盛り | 効かなすぎ ⇔ 効きすぎ | 決め方 [駆動変数] |
目安(出発点) |
|---|---|---|---|
| TTL(失効期間) | 長すぎて陳腐化した記憶で誤判断 ⇔ 短すぎて有効な記憶を再取得するコスト増 | [failure_cost] が高いほど短めに設定。情報の変化速度に合わせる |
価格/在庫: 5〜30 分、嗜好: 7〜30 日、不変属性: 無期限 |
| 版保持数 | 少なすぎて監査証跡が不十分 ⇔ 多すぎてストレージ圧迫・検索ノイズ | [failure_cost] が高いほど多く保持(監査のため) |
概ね 3〜10 版。規制領域では法定保持期間に合わせる |
| 確信度閾値(confidence) | 低すぎて不確かな記憶を採用 ⇔ 高すぎて有用な記憶を棄却 | [failure_cost] が高いほど閾値を上げる |
0.7〜0.9。金融・医療は 0.85 以上を出発点とする |
| 競合解決ポリシー | 新版を無条件採用で誤更新が混入 ⇔ 全件手動レビューで遅延 | [failure_cost] と更新頻度のバランス |
確信度が旧版より低い場合はフラグ付きレビュー、それ以外は新版自動採用 |
相反における立ち位置(相反)¶
このパターンは特定のフォークの一方に明確に倒すものではなく、記憶階層(D1)の各層に対して TTL と版管理のポリシーを横断的に適用する補助パターンです。ただし、記憶の書き込み経路では D3 Memory Write Gate と連携し、書き込み時に valid_from と初期 TTL を付与する設計が自然になります。
構造¶
flowchart TD
Write[記憶書込要求] --> Gate[Write Gate<br>D3]
Gate -->|付与| Meta["valid_from, TTL,<br>version, confidence"]
Meta --> Store[(メモリストア)]
Read[記憶読込要求] --> Lookup[検索]
Store --> Lookup
Lookup --> Check{TTL 内か?}
Check -->|期限切れ| Decay[失効処理<br>soft-delete]
Check -->|有効| Resolve{最新版か?}
Resolve -->|旧版| Skip[スキップ<br>監査用に保持]
Resolve -->|最新版| Return[呼出元へ返却]
Update[事実更新] --> Gate
Gate -->|旧版に superseded 付与| Store
Gate -->|新版を version+1 で格納| Store
実装メモ¶
記憶エントリの最小スキーマを示します:
{
"memory_id": "mem-price-USD-JPY",
"version": 3,
"value": {"rate": 149.82, "source": "market-feed"},
"confidence": 0.95,
"valid_from": "2025-01-15T10:00:00Z",
"valid_to": null,
"ttl_seconds": 300,
"expires_at": "2025-01-15T10:05:00Z",
"superseded_by": null,
"tags": ["price", "fx"]
}
事実更新時の擬似コードは以下のとおりです:
def update_memory(store, memory_id: str, new_value, confidence: float):
now = datetime.utcnow()
current = store.get_latest(memory_id)
if current and confidence < current.confidence:
# 確信度が下がった場合はフラグを立ててレビュー待ち
store.flag_for_review(memory_id, new_value, confidence)
return
if current:
current.valid_to = now
current.superseded_by = current.version + 1
store.save(current)
new_entry = MemoryEntry(
memory_id=memory_id,
version=(current.version + 1) if current else 1,
value=new_value,
confidence=confidence,
valid_from=now,
ttl_seconds=get_ttl_for_type(memory_id),
expires_at=now + timedelta(seconds=get_ttl_for_type(memory_id)),
)
store.save(new_entry)
TTL 分類の目安を以下に示します:
| 記憶の種類 | TTL の出発点 | 根拠 |
|---|---|---|
| リアルタイム価格・在庫 | 5〜30 分 | データソースの更新頻度に合わせる |
| ユーザ嗜好・設定 | 7〜30 日 | 行動変化の時間スケール |
| マスタ属性(ID・名前) | 無期限(TTL なし) | 変更頻度が極めて低い。変更時は版更新で対応 |
| 会話中の一時的事実 | セッション終了まで | セッション外に持ち出さない |
落とし穴:
- TTL と外部データソースの更新頻度の乖離に注意が必要です。TTL をデータソースの更新間隔より短くすると、毎回取り直すことになりキャッシュ効果がなくなります。逆に長すぎると陳腐化します。データソースの公称更新間隔の 1〜2 倍を出発点とし、
[failure_cost]が高いほど短い側に寄せてください。 - soft-delete と hard-delete の使い分けも重要です。監査要件がある場合は期限切れエントリを即座に削除せず
soft-delete(valid_toを付けて検索対象外にする)にします。ストレージ圧迫を防ぐため、法定保持期間を過ぎた分は定期バッチでhard-deleteしてください。 - タイムゾーンと時刻同期にも気をつけてください。
valid_from / valid_toは UTC で統一します。ノード間の時刻ずれが TTL より大きいと、あるノードでは有効・別のノードでは失効という不整合が起きます。
効かせる力学(forces)¶
- F6(メモリで状態を跨ぐ):記憶エントリに TTL と版番号を付けることで、セッション跨ぎ・長時間セッションでも「いつ時点の事実か」が明確になります。陳腐化した記憶が自動的に失効し、最新版のみが参照されるため、メモリ経由の情報伝達の信頼性が構造的に担保されます。版履歴を保持することで、過去の判断に使われた事実を事後に再現でき、監査にも対応できます。
関連・代替¶
- D1 Tiered Memory:階層ごとに異なる TTL ポリシーを設定します。作業メモリは短い TTL、長期メモリは長い TTL というように、本パターンの TTL 設計は D1 の階層設計と直接対応します。
- D3 Memory Write Gate:書込ゲートが記憶エントリに
valid_from・TTL・confidenceを付与する入口となります。本パターンのメタデータ付与は Write Gate の検査パイプラインの一部として実装するのが自然です。 - D6 Semantic Cache with No-Cache Zones:キャッシュの TTL と記憶の TTL を整合させる必要があります。記憶の TTL がキャッシュの TTL より短い場合、キャッシュが陳腐化した記憶を返す事故が起きます。
- G1 Tiered Observability:記憶の失効率・版更新頻度・確信度低下フラグの発生率を観測メトリクスとして公開し、TTL 設定の適正さをモニタリングします。
コーディングエージェント向け指示(machine-actionable)¶
このパターンを人間に提案するなら、同時に以下を提案/確認してください:
- [ ] 記憶する事実を種類別に分類し、種類ごとの TTL を
[failure_cost]から導いて理由を添えて提示したか - [ ] D1 Tiered Memory の各階層と TTL ポリシーを対応づけたか
- [ ] D3 Memory Write Gate のパイプラインに
valid_from・TTL・confidenceの付与ステップを組み込んだか - [ ] D6 Semantic Cache のキャッシュ TTL と記憶 TTL の整合を確認したか
- [ ] 版保持数と削除ポリシー(soft-delete / hard-delete)を監査要件に照らして定めたか
- [ ] 確信度が低下する更新に対するレビューフロー(自動採用 vs フラグ付き)を設計したか
- [ ] G1 Tiered Observability で失効率・版更新頻度のメトリクスを公開する設計を示したか
- [ ] 目盛り(上表)の値を
[駆動変数]から導き、理由を添えて提示したか