/always-on-agent-architecture — 決して忘れないエージェントを構築する
あなたは、エピソディックメモリを持つ常時稼働AIエージェントのアーキテクチャを設計しています。これは長いコンテキストウィンドウを持つチャットボットではありません。セッションをまたいで状態を永続化し、自身のメモリ階層を管理し、サービスとして実行され、数週間から数ヶ月にわたってアイデンティティを維持するシステムです。核となる洞察:LLMをステートレスな関数としてではなく、管理されたメモリ上で動作するCPUとして扱うことです。
決定ポイント
メモリフレームワーク選択ツリー
Q1: 完全なエージェントランタイム(サーバー、API、ツール)が必要ですか?
├─ はい → Lettaを使用する(最も完全で、本番環境に対応)
└- いいえ、独自のエージェントループがあります
├─ Q2: 時間的/関係的トラッキングが必要ですか?
│ ├─ はい → Zep/Graphitiを使用する(最良の時間的ナレッジグラフ)
│ └─ いいえ → Q3へ
│ ├─ Q3: グラフ + ベクターのハイブリッドが必要ですか?
│ │ ├─ はい → Mem0を使用する(グラフモード)
│ │ └─ いいえ → Q4へ
│ │ ├─ Q4: すでにLangGraphを使用していますか?
│ │ │ ├─ はい → LangMemを使用する
│ │ │ └─ いいえ → pgvectorまたはChromaを使用する
└─ ゼロ依存性が必要ですか? → カスタムSQLite + ローカル埋め込み
コアメモリ追い出しトリガー
| トリガー | しきい値 | アクション |
|---|---|---|
| サイズ超過 | コアメモリ > 4KB | 最も最近使用されていないブロックを要約し、要約をアーカイブに移動 |
| 経過時間減衰 | 30日以上データ未使用 | コンパクションレビューの対象としてマーク |
| 関連性低下 | アクセススコア < 0.3 | 減衰タグを付けてアーカイバルメモリに移動 |
| ユーザーオーバーライド | ユーザーが「Xを忘れて」と言う | 即時削除 + アーカイブの墓標 |
| 競合検出 | 矛盾する事実が保存されている | 調整するかユーザーに問い合わせるようにエージェントに促す |
ベクターDB選択基準
クエリレイテンシ要件 < 10ms かつ データサイズ > 1億ベクター の場合:
→ Qdrantを使用する(速度に最適化)
そうでなく、すでにPostgreSQLを使用している場合:
→ pgvectorを使用する(単一DB、よりシンプルな運用)
そうでなく、ハイブリッド検索(キーワード + セマンティック)が必要な場合:
→ Weaviateを使用する(最良のハイブリッド)
そうでなく、運用負荷ゼロが望ましい場合:
→ Pineconeを使用する(フルマネージド)
そうでない場合:
→ Chromaを使用する(ローカルファースト、シンプルなAPI)
メモリ階層ルーティングの決定
入力: ユーザーメッセージまたはエージェントの観測
│
├─ アイデンティティ/プリファレンスの更新を含むか?
│ └─ はい → コアメモリを更新し、即座に永続化
├─ 会話のコンテキストが必要か?
│ └─ はい → リコールメモリ(会話履歴)を検索
├─ 事実知識が必要か?
│ └─ はい → アーカイバルメモリ(ベクターストア)を検索
└─ 外部データが必要か?
└─ はい → 外部ツール(API、ファイルなど)を使用
障害モード
メモリ破損カスケード
症状: エージェントの性格の乖離、矛盾した応答、コアメモリの競合 根本原因: ロックなしのコアメモリへの同時書き込み、または失敗した部分的な更新 検出ルール: コアメモリサイズが突然50%以上減少した場合、または不正な形式のJSON/YAMLが含まれている場合 復旧手順:
- さらなる破損を防ぐためにエージェントを即座に停止
- 最後に確認された正常なバックアップ(1時間以内)からコアメモリを復元
- バックアップ以降の会話ログを再生して、失われた更新を再構築
- 再起動前にコアメモリ更新に書き込みロックを実装
ベクター検索の劣化
症状: 検索結果の関連性が低下、エージェントが最近保存した事実を見つけられない 根本原因: 埋め込みモデルのドリフト、インデックスの破損、メモリコンパクションの欠如 検出ルール: 既知のクエリに対する上位3件の結果の平均コサイン類似度が0.7未満の場合 復旧手順:
- 100ベクターのランダムサンプルで埋め込みの整合性チェックを実行
- 10%以上が異常な埋め込みを示す場合、インデックス全体を再構築
- 埋め込みモデルのバージョン固定を実装
- 再発を防ぐために埋め込みドリフト監視を追加
永続化層のデッドロック
症状: メモリ操作でエージェントがハング、データベース接続タイムアウト 根本原因: 同じメモリブロックへの同時読み取り/書き込み、不十分なコネクションプーリング 検出ルール: メモリ操作に30秒以上かかる、またはデータベースがロック待機タイムアウトを示す場合 復旧手順:
- ハングしている接続を強制終了してロックを解放
- メモリ操作に指数バックオフ再試行ロジックを実装
- 最大接続数制限付きのコネクションプーリングを追加
- メモリ更新のトランザクション分離レベルを確認
コンテキストウィンドウの爆発
症状: APIコストの急増、応答レイテンシの増加、トークン制限エラー 根本原因: コアメモリの肥大化、クエリごとにアーカイブチャンクを取得しすぎている 検出ルール: リクエストあたりの平均トークン数がモデルのコンテキスト制限の80%を超える場合 復旧手順:
- コアメモリサイズを監査 - サイズ超過のブロックを圧縮またはアーカイブ
- アーカイブ検索結果のデフォルト数を減らす(10 → 5)
- LLM呼び出し前にトークンカウントを実装
- 1会話あたり1ドルを超える場合のコスト監視アラートを追加
メモリリーク - 無制限の成長
症状: データベースサイズが線形に増加、検索パフォーマンスが時間とともに低下 根本原因: メモリコンパクションなし、重複する事実の挿入、ガベージコレクションの欠如 検出ルール: 通常の使用で総メモリサイズが月に100MB以上増加する場合 復旧手順:
- アーカイバルメモリ全体で事実の重複排除を実行(コサイン類似度 > 0.92)
- 7日以上経過したリコールメモリに会話要約を実装
- 低スコアメモリの自動プルーニングを伴う関連性スコアリングを追加
- 毎週のコンパクションジョブをスケジュール
実践例
例:個人研究アシスタントの構築
シナリオ: 技術研究を支援し、あなたの好みを記憶し、数ヶ月かけて知識を構築するエージェントのアーキテクチャを設計します。
ステップ1 - メモリ階層設計
コアメモリ (2KB):
- ユーザー名: "Sarah"
- 研究ドメイン: ["機械学習", "分散システム"]
- 推奨論文ソース: ["arxiv", "acmデジタルライブラリ"]
- 文体: "コード例を含む詳細"
- 現在のプロジェクト: "分散トレーニング最適化"
リコールメモリ:
- 全文検索可能なPostgreSQLでの全会話
- 30日間の保持期間、その後要約
アーカイバルメモリ:
- 論文要約、抽出された洞察、コードスニペット
- PostgreSQL上のpgvector(リコールに既に使用)
- ローカル埋め込み用のnomic-embed-text(プライバシー + コスト)
ステップ2 - フレームワーク選択の決定 決定木に従う:
- 完全なエージェントランタイムが必要? いいえ(カスタム構築)
- 時間的トラッキングが必要? いいえ(研究事実はほとんど時代を超越している)
- グラフ+ベクターが必要? いいえ(単純なセマンティック検索で十分)
- すでにLangGraphを使用している? いいえ
- → 決定: pgvector + PostgreSQL
ステップ3 - エージェントループの実装
async def research_step(user_query: str):
# コアメモリをロード
core = load_core_memory() # ユーザー設定、アクティブなプロジェクト
# クエリが現在のプロジェクトに関連しているかチェック
if "最適化" in user_query.lower():
# プロジェクト固有の知識をアーカイブから検索
relevant_papers = search_archival("分散トレーニング最適化")
context = f"現在のプロジェクトコンテキスト: {relevant_papers}"
else:
# 一般的なドメイン知識を検索
context = search_archival(user_query)
# コアメモリ + 取得したコンテキストでプロンプトを構築
system_prompt = f"""
あなたはSarahの研究アシスタントです。
ユーザー設定: {core['preferences']}
現在のプロジェクト: {core['current_project']}
取得したコンテキスト: {context}
"""
response = await llm.chat([
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_query}
])
# インタラクションを永続化
save_to_recall(user_query, response)
return response
初心者が見逃しがちな点:
- アーカイブに抽出された洞察ではなく、生の論文を保存している
- 会話検索(リコールメモリ)を実装していない
- コアメモリにあまりにも多くの情報を入れすぎている(50エントリの研究ドメインリスト)
- メモリコンパクション戦略がない
エキスパートが押さえる点:
- コアメモリは知識ではなく「作業中のアイデンティティ」に焦点を当てる
- アーカイバルメモリには生のドキュメントではなく、厳選された事実を保存する
- 取得前に検索を実装する(エージェントが何が関連するかを決定する)
- 初日からメモリの成長を計画する
品質ゲート
デプロイ検証チェックリスト
- メモリレイテンシSLO: コアメモリのロード <100ms、アーカイブ検索完了 <500ms
- メモリの一貫性: コアメモリがエージェントの再起動後も破損せずに保持される(意図的な強制終了でテスト)
- 検索の関連性: 既知のクエリに対する上位3件のアーカイブ結果のコサイン類似度 >0.6
- メモリサイズルール: コアメモリ ≤4KB、リコール保持 ≤30日、アーカイブチャンク ≤1KB
- 永続化の耐久性: すべてのメモリ更新がデータベース再起動後も保持される(ACID準拠を確認済み)
- コスト制御: 1日100会話で、メモリ操作のコストは1会話あたり <0.01ドル
- コンパクションスケジュール: メモリコンパクションが毎週実行され、総サイズを10%以上削減する
- アイデンティティの一貫性: 50以上の会話セッションにわたってエージェントの性格が安定している
- コールドスタート復旧: エージェントが空のメモリ状態を適切に処理する(オンボーディングフローが機能する)
- バックアップ検証: メモリバックアップが正常に復元され、エージェントのアイデンティティが保持される
非対応範囲(NOT-FOR境界)
このスキルを以下の目的で使用しないでください:
- エージェントのトレーニングデータの選択 → 代わりに
/always-on-agent-inputsを使用してください。そのスキルはエージェントに何をデータとして与えるかをカバーし、このスキルはそれを保存および取得する方法をカバーします - エージェントアプリケーションのブレインストーミング → 代わりに
/always-on-agent-applicationsを使用してください。そのスキルはユースケースのアイデア創出をカバーし、このスキルは技術的な実装をカバーします - エージェントの安全性とプライバシー → 代わりに
/always-on-agent-safetyを使用してください。そのスキルはデータガバナンス、同意、セキュリティをカバーします。このスキルはそれらがすでに設計されていることを前提とします - 一般的なエージェントパターン → 代わりに
/agentic-patternsを使用してください。そのスキルはReActループ、ツール使用、計画をカバーし、このスキルはその下にある永続化層をカバーします - 単発のエージェントタスク → 代わりに
/agent-creatorを使用してください。エージェントがセッションをまたいで記憶する必要がない場合、常時稼働アーキテクチャは必要ありません - データベーススキーマ設計 → このスキルは基本的なデータベースの概念を理解していることを前提とします。スキーマ最適化にはデータベース固有のスキルを使用してください
- コスト最適化戦略 → このスキルはコストに関する考慮事項に言及していますが、最適化について深く掘り下げていません。コスト固有のスキルに委任してください