AIエージェントが賢く振る舞うためには「記憶」が不可欠です。しかし、記憶をただ蓄積するだけでは、冗長性、陳腐化、そして何よりコストの問題に直面します。agentmemoriesでは、この課題に対し「記憶のティアリング」というアプローチで解決を図っています。本記事では、AIエージェントの記憶を「仮→確定→実働」の3つのティアに分け、参照頻度と検証に基づいて昇格させ、未使用期間に応じて降格(TTL)させるための最小実装をQiita読者向けに解説します。
記憶のティアリング戦略:なぜ「仮・確定・実働」なのか
AIエージェントの記憶は、生成された直後はその信頼性が低いことがあります。例えば、LLMが生成したタスク実行計画や、ユーザーとの会話から抽出した情報などは、そのまま永続化するにはリスクが伴います。そこで、私たちは以下の3つのティアを設けています。
- 仮 (Provisional): エージェントが生成したばかりの、まだ検証されていない情報。短期間で破棄されるか、検証を経て次のティアへ昇格します。
- 確定 (Confirmed): エージェント自身または外部システムによって検証され、信頼性が高いと判断された情報。ある程度の期間保持され、エージェントの行動に影響を与え始めます。
- 実働 (Active): 頻繁に参照され、エージェントの主要な知識ベースとして機能している情報。最も長期にわたり保持され、エージェントの推論や計画立案に直接的に利用されます。
このティアリングにより、エージェントは常に最新かつ最も関連性の高い情報にアクセスしつつ、不確実な情報の永続化コストを最小限に抑えることができます。
記憶オブジェクトのデータ構造設計
記憶オブジェクトは、その内容だけでなく、ティア、最終参照日時、参照回数といったメタデータを持つ必要があります。ここでは、簡潔なJSON形式を想定します。
{
"id": "mem_001",
"content": "ユーザーは先週、新しいプロジェクトを開始した。",
"tier": "provisional",
"last_used": "2023-10-26T10:00:00Z",
"hit_count": 0,
"created_at": "2023-10-26T09:30:00Z",
"ttl_days": {
"provisional": 7,
"confirmed": 30,
"active": 365
}
}
-
id: 記憶の一意な識別子。 -
content: 実際の記憶内容。 -
tier: 現在のティア(provisional,confirmed,active)。 -
last_used: 記憶が最後に参照された日時。昇格・降格の判断基準となります。 -
hit_count: 記憶が参照された回数。昇格の判断基準となります。 -
created_at: 記憶が作成された日時。 -
ttl_days: 各ティアでのデフォルトTTL(日数)。
記憶の昇格ロジックの実装
記憶の昇格は、主にhit_countとlast_usedに基づいて行われます。例えば、「仮」の記憶が一定回数以上参照され、かつ直近で利用されている場合、「確定」へ昇格させます。さらに、「確定」の記憶が頻繁に利用されるようであれば、「実働」へ昇格させます。
import datetime
def promote_memory(memory: dict) -> dict:
current_tier = memory["tier"]
hit_count = memory["hit_count"]
last_used = datetime.datetime.fromisoformat(memory["last_used"].replace("Z", "+00:00"))
now = datetime.datetime.now(datetime.timezone.utc)
# 仮 -> 確定
if current_tier == "provisional":
# 例: 3回以上参照され、かつ過去24時間以内に利用されたら昇格
if hit_count >= 3 and (now - last_used).total_seconds() < 24 * 3600:
memory["tier"] = "confirmed"
print(f"Memory {memory['id']} promoted from provisional to confirmed.")
# 確定 -> 実働
elif current_tier == "confirmed":
# 例: 10回以上参照され、かつ過去72時間以内に利用されたら昇格
if hit_count >= 10 and (now - last_used).total_seconds() < 72 * 3600:
memory["tier"] = "active"
print(f"Memory {memory['id']} promoted from confirmed to active.")
return memory
# サンプルデータ
sample_memory = {
"id": "mem_001",
"content": "ユーザーは先週、新しいプロジェクトを開始した。",
"tier": "provisional",
"last_used": "2023-10-26T10:00:00Z",
"hit_count": 5,
"created_at": "2023-10-26T09:30:00Z",
"ttl_days": {
"provisional": 7,
"confirmed": 30,
"active": 365
}
}
# 参照回数を更新(例として)
sample_memory["hit_count"] += 1
sample_memory["last_used"] = datetime.datetime.now(datetime.timezone.utc).isoformat(timespec='seconds').replace("+00:00", "Z")
promoted_memory = promote_memory(sample_memory)
print(f"Updated memory tier: {promoted_memory['tier']}")
この昇格ロジックは、エージェントが記憶を利用するたびに実行されるか、定期的なバッチ処理で実行されることを想定しています。
記憶の降格(TTL)ロジックの実装
記憶の降格(または削除)は、各ティアに設定されたTTL(Time To Live)に基づきます。last_usedからTTL期間を超過した記憶は、ティアが降格されるか、最終的には削除されます。
import datetime
def demote_or_delete_memory(memory: dict) -> tuple[dict | None, bool]:
current_tier = memory["tier"]
last_used = datetime.datetime.fromisoformat(memory["last_used"].replace("Z", "+00:00"))
now = datetime.datetime.now(datetime.timezone.utc)
# 各ティアのTTL
ttl_map = memory["ttl_days"] # {"provisional": 7, "confirmed": 30, "active": 365}
# 最終使用からの経過日数
days_since_last_used = (now - last_used).days
if current_tier == "active":
if days_since_last_used > ttl_map.get("active", 365):
memory["tier"] = "confirmed"
print(f"Memory {memory['id']} demoted from active to confirmed due to TTL.")
elif current_tier == "confirmed":
if days_since_last_used > ttl_map.get("confirmed", 30):
memory["tier"] = "provisional"
print(f"Memory {memory['id']} demoted from confirmed to provisional due to TTL.")
elif current_tier == "provisional":
if days_since_last_used > ttl_map.get("provisional", 7):
print(f"Memory {memory['id']} deleted from provisional due to TTL.")
return None, True # 削除されたことを示す
return memory, False
# サンプルデータ(Activeから降格するケースを想定)
sample_memory_active = {
"id": "mem_002",
"content": "重要な顧客Aの担当者は山田さん。",
"tier": "active",
"last_used": (datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=400)).isoformat(timespec='seconds').replace("+00:00", "Z"), # 400日前に最終利用
"hit_count": 100,
"created_at": "2022-01-01T00:00:00Z",
"ttl_days": {
"provisional": 7,
"confirmed": 30,
"active": 365
}
}
demoted_memory, is_deleted = demote_or_delete_memory(sample_memory_active)
if not is_deleted:
print(f"Updated memory tier: {demoted_memory['tier']}")
else:
print("Memory was deleted.")
# サンプルデータ(Provisionalから削除されるケースを想定)
sample_memory_provisional = {
"id": "mem_003",
"content": "今日のランチはカレー。",
"tier": "provisional",
"last_used": (datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=10)).isoformat(timespec='seconds').replace("+00:00", "Z"), # 10日前に最終利用
"hit_count": 1,
"created_at": "2023-10-16T09:00:00Z",
"ttl_days": {
"provisional": 7,
"confirmed": 30,
"active": 365
}
}
demoted_memory_p, is_deleted_p = demote_or_delete_memory(sample_memory_provisional)
if not is_deleted_p:
print(f"Updated memory tier: {demoted_memory_p['tier']}")
else:
print("Memory was deleted.")
この降格ロジックは、定期的なバッチ処理(例えば、日次や週次)で全ての記憶オブジェクトに対して実行されるべきです。
運用上の落とし穴とチェックリスト
agentmemoriesの運用の中で、私たちはいくつかの「落とし穴」を経験してきました。
実録: 昇格閾値の調整不足による過学習
当初、昇格の閾値を低く設定しすぎていました。「仮」の記憶が少し参照されただけで「確定」に昇格してしまうため、エージェントが一時的な情報や誤った情報を過度に記憶し、その後の推論に悪影響を与える事態が発生しました。例えば、ユーザーが冗談で言った一言が「確定」記憶として残り、エージェントがその情報を真実として扱ってしまうケースです。この経験から、昇格閾値は慎重に、かつ継続的にチューニングする必要があると痛感しました。特に、「確定」や「実働」への昇格には、より厳密な検証プロセス(例: 人間による確認、複数エージェントによる検証、外部APIとの突合など)を組み込むことが重要です。
運用チェックリスト:
-
昇格閾値の定期的な見直し:
hit_countやlast_usedの期間設定は、エージェントの利用状況や記憶の性質に合わせて調整が必要です。 - TTL設定の最適化: 各ティアのTTLは、記憶の陳腐化速度やストレージコストを考慮して決定します。短すぎると重要な記憶が失われ、長すぎるとゴミが増えます。
- 手動によるティア変更機能: 緊急時や特定の重要な記憶については、人間が手動でティアを昇格・降格できる管理機能があると便利です。
- 監査ログの記録: 記憶の昇格・降格が行われた際、誰が、いつ、どのような理由で行ったかを記録することで、問題発生時の追跡が容易になります。
- バックアップ戦略: 誤って重要な記憶が削除されないよう、定期的なバックアップは必須です。特に「実働」ティアの記憶は重要度が高いです。
- 記憶の重複排除: 同じ内容の記憶が複数のティアに存在しないよう、定期的な重複排除処理を検討します。
- パフォーマンス監視: 記憶の量が増えるにつれて、検索やティアリング処理のパフォーマンスが低下する可能性があります。適切なインデックス設計やデータベース選定も重要です。
まとめ
AIエージェントの記憶を「仮→確定→実働」のティアで運用し、参照頻度と未使用期間に基づいて昇格・降格させることで、エージェントはより効率的かつ賢く振る舞うことができます。本記事で紹介した最小実装と運用上の落とし穴チェックリストが、皆さんのAIエージェント開発の一助となれば幸いです。記憶の管理はAIエージェントの性能を左右する重要な要素であり、継続的な改善が求められます。
関連リンク
設計思想の詳細はこちら(Zenn)
agentmemories 公式サイト
