Skip to content

Latest commit

 

History

History
948 lines (725 loc) · 42.6 KB

File metadata and controls

948 lines (725 loc) · 42.6 KB

プライミングレイヤー実装計画書

作成日: 2026-02-14 実装日: 2026-02-14 関連: [[20260212_AI_Agent記憶アーキテクチャ調査]] セクション10、[[dynamic-prompt-injection]] ステータス: ✅ Phase 1 実装完了 (2026-02-14) 実装レポート: [[priming-layer-phase1]]


目次

  1. 課題と目的
  2. 設計思想
  3. アーキテクチャ概要
  4. RAGデータベース設計
  5. プライミングパイプライン
  6. 記憶タイプ別の注入戦略
  7. behavior_rules.md の改訂
  8. 実行モードとの整合
  9. インデキシングと同期
  10. 記憶固定化アーキテクチャ
  11. トークン予算管理
  12. 段階的実装計画
  13. 評価基準

1. 課題と目的

現状の問題

  1. 強制的記憶検索: behavior_rules.md の「記憶を検索せずに判断するのは禁止」により、エージェントが毎回必ず search_memory / read_memory_file ツールを呼び出す
  2. レスポンス遅延: ツール呼び出し1往復分(数秒〜十数秒)が毎回発生
  3. 不自然な宣言: 「記憶を確認します」という人間らしくない発話が発生
  4. プロンプト指示の限界: 「報告不要」と記述しても、「禁止→必ず検索」の強制力が優先される

根本原因

脳科学の記憶想起モデルにおける自動想起(プライミング) が未実装。現在のシステムは意図的想起(Controlled Retrieval)のみを持ち、すべての記憶アクセスがエージェントの明示的ツール呼び出しに依存している。

目的

  • 自動想起メカニズムを追加し、関連記憶がエージェント起動前にコンテキストに存在する状態を作る
  • 意図的想起(ツール呼び出し)は「不足時の補助」に位置づけを変更する
  • 結果としてレスポンス速度の改善、自然な会話体験を実現する

2. 設計思想

脳科学との対応

本設計は人間の記憶想起メカニズムに基づく(詳細: 調査ドキュメント セクション10)。

脳のプロセス AnimaWorks実装 担当
自動想起(海馬パターン補完) プライミングレイヤー フレームワーク
拡散活性化(意味ネットワーク伝播) RAGハイブリッド検索 フレームワーク
エピソードバッファ(多モーダル統合) コンテキスト組立 フレームワーク
意図的想起(前頭前皮質の戦略的検索) search_memory ツール エージェント
即時符号化(海馬の自動符号化) 会話終了フック → episodes/ 自動記録 フレームワーク
日次固定化(NREM睡眠時の固定化) 深夜cron → episodes/ → knowledge/ 変換 フレームワーク
週次統合(長期的新皮質統合) 週次cron → knowledge/ 統合 + episodes/ 圧縮 フレームワーク
意図的記銘(前頭前皮質の精緻化符号化) write_memory_file で knowledge/ / procedures/ に直接書き込み エージェント

図書館型モデルとの整合

プライミングは図書館型を否定しない。

  • 図書館の入口掲示板: プライミングレイヤー。入館時に「今日のあなたに関連する情報」が目に入る
  • 図書館の書架を歩く: 意図的想起。自分で棚を探しに行く必要がある時だけ行う
  • 司書の不在: LLMに記憶管理の全責任を負わせない。フレームワークが「良い司書」として機能する

非目標(スコープ外)

  • ベクトルDBの完全な知識グラフ化(Phase 3以降の検討事項)
  • 記憶の自動削除・忘却メカニズム(別途設計)
  • マルチモーダル記憶(画像・音声)

3. アーキテクチャ概要

全体フロー

メッセージ受信
    │
    ▼
┌─────────────────────────────────────────────┐
│  ① コンテキスト抽出                          │
│  送信者名、キーワード、意図分類を抽出         │
│  (LLM不使用。ルールベース + 形態素解析)     │
└─────────────┬───────────────────────────────┘
              │
              ▼
┌─────────────────────────────────────────────┐
│  ② プライミング検索(自動想起)               │
│                                               │
│  A. 送信者プロファイル → 完全一致ルックアップ │
│  B. 直近エピソード → 日付フィルタ             │
│  C. 関連知識 → RAGハイブリッド検索            │
│  D. スキルマッチ → キーワードマッチ           │
│                                               │
│  ※ 並列実行。全体で200ms以内を目標           │
└─────────────┬───────────────────────────────┘
              │
              ▼
┌─────────────────────────────────────────────┐
│  ③ トークン予算管理                           │
│  検索結果をトークン上限内に収める              │
│  優先度: 送信者 > 直近EP > 関連知識 > スキル  │
└─────────────┬───────────────────────────────┘
              │
              ▼
┌─────────────────────────────────────────────┐
│  ④ コンテキスト組立(エピソードバッファ)     │
│  プライミング結果をシステムプロンプトに注入    │
│  「あなたが自然に思い出した記憶」として提示   │
└─────────────┬───────────────────────────────┘
              │
              ▼
┌─────────────────────────────────────────────┐
│  ⑤ エージェント実行                           │
│  記憶が既にコンテキスト内にある状態で思考     │
│  不足時のみ search_memory で追加検索          │
└─────────────────────────────────────────────┘

配置場所

core/
├── memory/
│   ├── manager.py          # 既存: MemoryManager
│   ├── conversation.py     # 既存: ConversationMemory
│   ├── shortterm.py        # 既存: ShortTermMemory
│   ├── priming.py          # 新規: PrimingEngine(自動想起)
│   └── rag/                # 新規: RAGサブシステム
│       ├── indexer.py      #   インデキシング(記憶→ベクトル化)
│       ├── store.py        #   ベクトルストア抽象化
│       └── retriever.py    #   ハイブリッド検索
├── prompt/
│   ├── builder.py          # 変更: プライミング結果の注入
│   └── context.py          # 既存: コンテキストウィンドウ追跡

4. RAGデータベース設計

4.1 方式選定

検索方式: ハイブリッド検索(BM25 + ベクトル + 時間減衰)

単一方式では不十分。調査ドキュメントの知見に基づき、3信号のハイブリッドを採用する。

信号 方式 重み 強み 弱み
Keyword BM25 (ripgrep) 0.3 固有名詞・正確なマッチ 意味的類似を捉えない
Semantic ベクトル類似度 0.5 意味的関連性 固有名詞が弱い
Recency 指数減衰関数 0.2 最新情報の優先 古い重要情報の埋没

スコア統合はRRF(Reciprocal Rank Fusion)を採用。最もシンプルで堅牢。

RRF_score(d) = Σ 1 / (k + rank_i(d))
# k = 60(標準値)
# rank_i = 各検索方式でのドキュメント順位

なぜハイブリッドか

  • 「山田」で検索 → BM25が確実にヒット(ベクトル検索だけでは不安定)
  • 「急ぎの依頼への対応方針」で検索 → ベクトル検索が意味的に関連する記憶を発見
  • 「最近のインシデント」で検索 → 時間減衰が直近を優先
  • ハイブリッド検索は単一方式比で検索失敗率を最大49%削減(Anthropic 2025, Apple ML Research 2024)

4.2 ベクトルストア選定

推奨: ChromaDB(ローカル組み込み型)

候補 方式 利点 欠点 判定
ChromaDB 組み込みSQLite + HNSW サーバー不要、Python native、メタデータフィルタ内蔵 大規模時のスケーラビリティ 採用
pgvector PostgreSQL拡張 SQL統合、フルテキスト検索も可能 別途DBサーバーが必要 Phase 3検討
FAISS Meta製ライブラリ 高速、大規模対応 メタデータフィルタなし、永続化が別途必要 不採用
Qdrant Rust製ベクトルDB 高性能、フィルタ充実 サーバー型、オーバースペック 不採用

ChromaDB選定理由:

  • AnimaWorksの単体サーバーアーキテクチャに適合(外部DB不要)
  • pip install chromadb のみで導入完了
  • SQLiteバックエンド → ~/.animaworks/ 内に配置可能
  • コレクション単位でAnima別に分離可能
  • メタデータフィルタ(日付、記憶タイプ、送信者等)が組み込み

4.3 エンベディングモデル選定

推奨: ローカルエンベディング(multilingual-e5-small)

候補 次元 日本語 レイテンシ コスト 判定
multilingual-e5-small 384 良好 ~10ms/doc (CPU) 無料(ローカル) Phase 1採用
multilingual-e5-large 1024 優秀 ~50ms/doc (CPU) 無料(ローカル) Phase 2検討
OpenAI text-embedding-3-small 1536 優秀 ~100ms/doc (API) $0.02/1M tokens 不採用(API依存)
BGE-M3 1024 優秀 ~30ms/doc (GPU) 無料(ローカル) GPU環境限定

ローカル優先の理由:

  • AnimaWorksの設計原則: 外部API依存を最小化
  • 記憶のインデキシングは頻繁に発生(エピソード書き込みのたびに)
  • レイテンシ要件(プライミング全体200ms以内)にAPI往復が不適合
  • Ollama経由でもエンベディングモデルは実行可能

4.4 データスキーマ

ChromaDBコレクション構成

Anima単位でコレクションを分離する。

~/.animaworks/vectordb/
└── chroma.sqlite3        # ChromaDB永続化ファイル

コレクション:
├── {anima_name}_knowledge    # 意味記憶(knowledge/)
├── {anima_name}_episodes     # エピソード記憶(episodes/)
├── {anima_name}_procedures   # 手続き記憶(procedures/)
├── {anima_name}_skills       # スキル記憶(skills/)
└── shared_users               # 共有ユーザー記憶(shared/users/)

ドキュメント構造

各記憶ファイルをチャンク分割してベクトル化する。

Document:
  id:        "{anima}/{memory_type}/{filename}#{chunk_index}"
  content:   "チャンクされたテキスト内容"
  embedding: [0.12, -0.34, ...]  # 384次元
  metadata:
    anima:       "sakura"
    memory_type:  "knowledge" | "episodes" | "procedures" | "skills" | "shared_users"
    source_file:  "knowledge/chatwork-response-policy.md"
    created_at:   "2026-02-10T09:00:00+09:00"
    updated_at:   "2026-02-14T15:30:00+09:00"
    importance:   "normal" | "important"   # [IMPORTANT]タグの有無
    tags:         ["chatwork", "対応方針"]  # ファイル内のタグ
    chunk_index:  0
    total_chunks: 3

チャンキング戦略

記憶タイプ チャンク方法 チャンクサイズ 理由
knowledge/ Markdown見出し単位 見出しセクション全体 1つの知識は1見出しに完結
episodes/ 時刻見出し単位(## HH:MM 1エピソード全体 1つのエピソードは時刻ブロック
procedures/ Markdownステップ単位 手順全体 手順は分割すると意味を失う
skills/ ファイル全体 1ファイル スキルは原子的単位
shared/users/ ファイル全体 1ファイル ユーザープロファイルは小さい

5. プライミングパイプライン

5.1 コンテキスト抽出(LLM不使用)

メッセージから検索に必要な情報を高速抽出する。LLMを使わない理由はレイテンシ。

入力: メッセージテキスト + 送信者名

抽出:
1. 送信者名       → メタデータから直接取得
2. キーワード      → 以下の優先順で抽出
   a. 固有名詞     → 正規表現(人名、サービス名、プロジェクト名)
   b. 重要単語     → ストップワード除去後の名詞・動詞
   c. 既知エンティティ → knowledge/ のファイル名との照合
3. 意図分類       → 簡易ルール(質問、依頼、報告、雑談)

将来の拡張: MeCab/Sudachi等の形態素解析ライブラリで品質向上。ただしPhase 1ではシンプルなルールベースで開始。

5.2 プライミング検索(4チャネル並列)

4つの検索チャネルを非同期並列で実行する。

チャネルA: 送信者プロファイル(完全一致ルックアップ)

入力: sender_name
検索: shared/users/{sender_name}/index.md
方式: ファイル直接読み込み(ベクトル検索不要)
出力: ユーザープロファイル全文(存在する場合)
  • ベクトル検索を使わない理由: 送信者名は完全一致で確定するため
  • 脳科学対応: 顔を見た瞬間の自動想起。意識的検索ではない

チャネルB: 直近エピソード(日付フィルタ)

入力: 現在日時
検索: episodes/ の当日 + 前日ファイル
方式: ファイル直接読み込み + 末尾N件(最新優先)
出力: 直近のエピソードエントリ(トークン予算内)
  • ベクトル検索を使わない理由: 日付でフィルタ可能な確定的検索
  • 脳科学対応: 短期〜近時記憶。「昨日何をしたか」は意識せず覚えている

チャネルC: 関連知識(RAGハイブリッド検索)★本丸

入力: 抽出されたキーワード群
検索: {anima}_knowledge コレクション + BM25
方式: ハイブリッド検索(ベクトル類似 + BM25 + 時間減衰)
出力: 上位3件の関連知識チャンク
  • ベクトル検索: キーワードをエンベディング化 → ChromaDBで類似検索
  • BM25: 同じキーワードで ripgrep を knowledge/ に実行
  • RRF統合: 両方の結果を順位ベースで統合し上位3件を選出
  • 時間減衰: updated_at からの経過に基づく指数減衰でリスコア

拡散活性化の簡易版として機能する: 直接的なキーワードマッチ(BM25)と意味的近傍(ベクトル)の両方を捕捉することで、Collins & Loftus的な連想ネットワーク上の拡散を近似する。

チャネルD: スキルマッチ(キーワードマッチ)

入力: 抽出されたキーワード + 意図分類
検索: skills/ のファイル名 + スキル説明文
方式: ファイル名マッチ + 説明文のキーワードマッチ
出力: マッチしたスキル名(内容は注入しない。名前だけ)
  • フルテキストは注入しない理由: スキルは実行時に読めばよい。名前の想起だけで十分
  • 脳科学対応: 「できること」のリストは覚えているが、詳細手順は実行時に確認

5.3 フォールバック

ChromaDBが未初期化 or 破損している場合:

  • チャネルC を BM25のみ(ripgrep)にフォールバック
  • チャネルA, B, D はファイル直接読み込みなので影響なし
  • エラーログを記録し、次回起動時にインデックス再構築を試行

6. 記憶タイプ別の注入戦略

注入テンプレート

プライミング結果はシステムプロンプト内の専用セクションとして注入する。

## あなたが思い出していること

以下は、この会話に関連してあなたが自然に想起した記憶です。

### {sender_name} について
{sender_profile_content}

### 直近の出来事
{recent_episodes_summary}

### 関連する知識
{related_knowledge_chunks}

### 使えそうなスキル
{matched_skill_names}

注入しないもの

  • エージェントの identity.md, injection.md(既存の注入パスで処理済み)
  • state/current_state.md, state/pending.md(既存の注入パスで処理済み)
  • 他のAnimaの内部記憶(カプセル化原則に違反)

7. behavior_rules.md の改訂

現行(問題あり)

### 記憶の検索(最重要)
**記憶を検索せずに判断するのは禁止。必ず書庫を引いてから判断すること。**

改訂案

### 記憶について

あなたのコンテキストには「あなたが思い出していること」セクションが含まれています。
これは、相手の顔を見た瞬間に名前や過去のやり取りを自然と思い出すのと同じです。

#### 応答の判断基準
- コンテキスト内の記憶で十分に判断できる場合: そのまま応答してよい
- コンテキスト内の記憶では不足する場合: search_memory / read_memory_file で追加検索せよ

#### 追加検索が必要な典型例
- 具体的な日時・数値を正確に答える必要がある時
- 過去の特定のやり取りの詳細を確認したい時
- 手順書(procedures/)に従って作業する時
- コンテキストに該当する記憶がない未知のトピックの時

#### 禁止事項
- 記憶の検索プロセスについてユーザーに言及すること
  (人間は「今から思い出します」とは言わない)
- 毎回機械的に記憶検索を実行すること
  (コンテキストで判断できることに追加検索は不要)

### 記憶の書き込み

#### 自動記録(あなたは何もしなくてよい)
- 会話の内容はシステムが自動的にエピソード記憶(episodes/)に記録する
- あなたが意識的にエピソード記録を書く必要はない
- 日次・週次でシステムが自動的にエピソードから教訓やパターンを抽出し、知識記憶(knowledge/)に統合する

#### 意図的な記録(あなたが判断して行う)
以下の場合のみ、write_memory_file で直接書き込むこと:
- 重要な方針・教訓を即座に記録したい時 → knowledge/ に書き込み
- 作業手順をまとめたい時 → procedures/ に書き込み
- 新しいスキルを習得した時 → skills/ に書き込み
- これは「メモを取る」行為であり、記録義務ではない

8. 実行モードとの整合

3モードでのプライミングの動作

モード プライミングレイヤー エージェントの追加検索
A1 (Agent SDK) フレームワークが注入 エージェントがRead/Grepで自律的に検索
A2 (LiteLLM) フレームワークが注入 エージェントがsearch_memoryツールで検索
B (Assisted) フレームワークが注入 フレームワークが代行(追加検索は原則不要)

Mode Bの改善: 現在Mode Bは「フレームワークが記憶を注入」する設計だが、注入される記憶の選択が粗い。プライミングレイヤーのRAG検索がMode Bの記憶注入精度を自動的に向上させる。

プライミングの呼び出し位置

メッセージ受信 or heartbeat or cronトリガー
    │
    ▼
anima.py: process_message() / run_heartbeat() / run_cron_task()
    │
    ▼
agent.py: run_cycle() → _run_priming()
    │
    ▼
priming.py: prime_memories()  ← ★ここで自動想起
    │
    ▼
builder.py: build_system_prompt()  ← プライミング結果を注入
    │
    ▼
executor: execute()  ← エージェント実行

: 初期設計では lifecycle.py から priming.py を呼ぶ想定だったが、 実装では agent.py 内の _run_priming() メソッドがプライミングを担当する。 これにより、プライミング結果を直接 build_system_prompt() に渡せるため アーキテクチャ的にシンプルになった。


9. インデキシングと同期

初回インデキシング

animaworks init または専用コマンドで、既存の全記憶ファイルをベクトル化する。

animaworks index [--anima NAME] [--full]
  • Anima指定なし → 全Animaの記憶をインデキシング
  • --full → 既存インデックスを破棄して再構築

差分インデキシング(リアルタイム同期)

記憶ファイルの書き込み時に自動的にインデックスを更新する。書き込み経路は3つ:

経路1: エージェントの意図的記銘

write_memory_file ツール実行(エージェントが明示的に呼び出し)
    │
    ▼
MemoryManager.write() 内で:
    1. ファイル書き込み(既存処理)
    2. indexer.index_file(file_path)  ← 追加
        - チャンク分割
        - エンベディング生成
        - ChromaDB upsert

経路2: 即時符号化(会話終了フック)

ConversationMemory.finalize_session()
    │
    ▼
MemoryManager.append_episode() 内で:
    1. episodes/{date}.md に追記
    2. indexer.index_file(episode_path)
        - 追記分のチャンクのみ増分インデキシング

経路3: 固定化エンジン(日次/週次cron)

ConsolidationEngine.daily_consolidate() / weekly_integrate()
    │
    ▼
MemoryManager を経由して knowledge/ に書き込み
    1. 新規/更新ファイルの書き込み
    2. indexer.index_file() で各ファイルを再インデキシング
    3. 週次統合で削除されたファイルの embeddings もクリーンアップ

ファイル監視(バックアップ経路)

Agent SDKモード(A1)ではエージェントが直接ファイルを書き込む場合がある。 この場合 MemoryManager を経由しないため、ファイル監視で補完する。

watchdog or inotify で記憶ディレクトリを監視
    │
    ▼
変更検出 → 差分インデキシングキューに追加
    │
    ▼
非同期ワーカーがバッチ処理(500ms debounce)

インデックスの永続化

~/.animaworks/
├── vectordb/
│   └── chroma.sqlite3    # ChromaDB永続化
├── index_meta.json        # インデキシング状態(最終更新日時、ファイルハッシュ)

10. 記憶固定化アーキテクチャ

設計原則: 記憶の符号化・固定化・統合はフレームワークの責務。エージェントは意図的記銘のみ。 脳科学的根拠: 調査ドキュメント セクション10.6

10.1 全体像: 3段階固定化 + 意図的記銘

脳の記憶固定化は複数の時間スケールで動作する自動プロセスである。人間は自分の海馬に「今夜はこのエピソードを新皮質に転送してくれ」と指示しない。AnimaWorksも同様に、固定化をフレームワーク側で自動実行する。フレームワークはバックグラウンドで別途LLMをワンショット呼出しし(即時符号化には安価なモデル、日次固定化には中程度のモデル)、エージェント(Animaの主LLM)は記憶インフラを管理しない。

会話中(覚醒時)                         非会話時(睡眠時)
─────────────────────                  ─────────────────────

ユーザーとの会話                        ┌─────────────────────────────┐
    │                                  │  日次固定化(深夜cron)      │
    ▼                                  │  episodes/ → LLM要約        │
会話終了検出                            │  → knowledge/ 書き込み      │
    │                                  └──────────┬──────────────────┘
    ▼                                             │
┌───────────────────┐                  ┌──────────┴──────────────────┐
│ 即時符号化         │                  │  週次統合(週次cron)        │
│ 会話要約 →        │                  │  knowledge/ 重複排除・統合   │
│ episodes/{date}.md │                  │  古い episodes/ 圧縮        │
└───────────────────┘                  └─────────────────────────────┘

                    ┌─────────────────────────────────────┐
                    │  意図的記銘(エージェント随時)         │
                    │  write_memory_file で                 │
                    │  knowledge/ や procedures/ に直接書込  │
                    │  ※ 唯一のエージェント側書き込み経路   │
                    └─────────────────────────────────────┘

10.2 即時符号化(会話終了フック)

脳科学的対応

海馬は体験をリアルタイムで自動符号化する。感覚入力が歯状回でパターン分離 → CA3で自己連合として記録。本人の意図は不要。

実装設計

会話セッション終了
    │
    ▼
ConversationMemory.finalize_session()  ← 新規メソッド
    │
    ├─ 会話ターン数が閾値以下(例: 2ターン以下) → スキップ(挨拶程度は記録不要)
    │
    ├─ 会話内容を要約LLMに送信
    │   - モデル: 安価なモデル(haiku相当 or ローカルOllama)
    │   - プロンプト: 「以下の会話から、主要なトピック・決定事項・未解決事項を抽出せよ」
    │   - 出力形式: 構造化Markdown
    │
    └─ MemoryManager.append_episode(date, summary)
        - episodes/{date}.md に追記
        - 既存エントリの末尾に時刻付きで追加
        - RAGインデックスにも自動追加(セクション9のdiff indexingフック)

会話終了の検出

トリガー 方式 優先度
明示的終了 ユーザーが「おやすみ」「終了」等を送信 最高
セッション区切り WebSocket切断 or APIセッション終了
タイムアウト 最後のメッセージから N分経過(設定可能、デフォルト30分)
heartbeat区切り heartbeat実行前に、前回heartbeat以降の会話を固定化

要約のフォーマット

## HH:MM — {自動生成タイトル}

**相手**: {sender_name}
**トピック**: {topics}
**要点**:
- {key_point_1}
- {key_point_2}

**決定事項**: {decisions_if_any}
**未解決**: {open_items_if_any}

10.3 日次固定化(NREM睡眠アナログ)

脳科学的対応

NREM睡眠中、海馬鋭波リップルが最近のエピソードを圧縮再生し、新皮質に送信。新皮質が統計的規則性を抽出して意味記憶を形成する。このプロセスは毎晩行われる。

実装設計

深夜cron(例: 02:00 JST)
    │
    ▼
ConsolidationEngine.daily_consolidate(anima)  ← 新規クラス
    │
    ├─ 直近24時間の episodes/ エントリを収集
    │   (前日朝〜当日朝のエピソード群)
    │
    ├─ エピソード数が閾値以下(例: 0件) → スキップ
    │
    ├─ LLMに固定化プロンプトを送信
    │   - モデル: 中程度のモデル(sonnet相当)
    │   - 入力: エピソード群 + 既存knowledge/の目次
    │   - プロンプト:
    │     「以下の1日のエピソードから:
    │      1. 新しい教訓・パターン・方針を抽出
    │      2. 既存知識ファイルの更新が必要な場合、差分を提示
    │      3. 新規知識ファイルが必要な場合、内容を提示」
    │
    ├─ 結果を knowledge/ に書き込み
    │   - 既存ファイルの更新: 該当ファイルにマージ
    │   - 新規知識: 新しい .md ファイルとして作成
    │   - タグ付与: [AUTO-CONSOLIDATED] + 元エピソード日付
    │
    └─ RAGインデックス更新
        - 新規/更新された knowledge/ ファイルを再インデキシング

配置場所

core/
├── memory/
│   ├── consolidation.py     # 新規: ConsolidationEngine
│   │   ├── daily_consolidate()    # 日次固定化
│   │   ├── weekly_integrate()     # 週次統合
│   │   └── _summarize_episodes()  # エピソード要約(LLM呼び出し)

APSchedulerへの登録

LifecycleManagerのcron登録に、Anima固有のcronとは別にシステムcronとして登録する。

lifecycle.py: _setup_system_crons()
    ├── daily_consolidation: CronTrigger(hour=2, minute=0)  # 毎日02:00
    └── weekly_integration:  CronTrigger(day_of_week="sun", hour=3)  # 毎週日曜03:00

10.4 週次統合(長期統合・剪定)

脳科学的対応

新皮質の統合は日〜月の長い時間スケールで進行。多数のエピソード再生を経て、文脈に依存しない抽象的知識が形成される。また、Tononi & Cirelliのシナプス恒常性仮説に基づき、低重要度のシナプスはダウンスケーリングされる。

実装設計

週次cron(例: 日曜 03:00 JST)
    │
    ▼
ConsolidationEngine.weekly_integrate(anima)
    │
    ├─ knowledge/ ファイル群を横断スキャン
    │   - 重複するトピックの検出(ベクトル類似度 > 閾値)
    │   - 矛盾する記述の検出
    │
    ├─ LLMに統合プロンプトを送信
    │   - 重複する知識ファイルの統合案を生成
    │   - 矛盾の解消案を生成
    │
    ├─ knowledge/ の統合実行
    │   - 重複ファイルのマージ
    │   - 矛盾の解消
    │
    ├─ 古い episodes/ の圧縮
    │   - 30日以上前 + [IMPORTANT] タグなし → 要約に圧縮
    │   - [IMPORTANT] タグ付き → 永続保持(多重痕跡理論)
    │   - 圧縮結果で元ファイルを置換
    │
    └─ RAGインデックスの再構築
        - 変更された全ファイルを再インデキシング

10.5 意図的記銘(エージェント側の唯一の書き込み経路)

エージェントに残る書き込み操作は write_memory_file ツールのみ。これは前頭前皮質による意識的な精緻化符号化に相当する。

用途 対象
方針の記録 knowledge/ 「山田さんはSlack連絡を好む」と学習した
手順の記録 procedures/ 「デプロイ手順をまとめた」
スキルの記録 skills/ 「新しいツールの使い方を記録」

重要: エピソード記録(episodes/)へのエージェントからの直接書き込みは非推奨とする。エピソード記録はフレームワークの即時符号化フックが担当する。エージェントが手動でエピソード記録する必要がある場合は、write_memory_file で可能だが、通常は不要。

10.6 固定化の全体フローと責務分離

                        ┌──────────────────────────────────┐
                        │        エージェント(LLM)         │
                        │                                    │
                        │  ● 会話・思考・判断               │
                        │  ● 意図的記銘(write_memory_file)│
                        │  ● 意図的想起(search_memory)    │
                        │                                    │
                        │  × 記憶の要約・圧縮              │
                        │  × 固定化の実行                   │
                        │  × インデックスの管理              │
                        └──────────────┬───────────────────┘
                                       │
                                  会話ターン
                                       │
                        ┌──────────────┴───────────────────┐
                        │     フレームワーク(Python)        │
                        │                                    │
                        │  [即時] 会話終了 → 自動符号化      │
                        │  [日次] 深夜cron → 日次固定化      │
                        │  [週次] 週次cron → 長期統合        │
                        │  [常時] プライミング → 自動想起     │
                        │  [常時] RAGインデックス → 自動同期  │
                        │                                    │
                        └──────────────────────────────────┘

11. トークン予算管理

予算配分

プライミングで注入する記憶のトークン上限を設定する。コンテキスト全体を圧迫しないことが重要。

全コンテキスト予算(例: 128Kトークン)
├── システムプロンプト(固定部分):      ~4,000 tokens
├── プライミング記憶(本設計の対象):    ~2,000 tokens ← 上限
├── 会話履歴:                           ~20,000 tokens
├── ツール定義:                          ~3,000 tokens
└── エージェント作業領域:                残り全て

予算内での優先順位

トークン上限を超える場合、以下の優先度で切り捨てる:

優先度 チャネル 最大トークン 根拠
1(最高) 送信者プロファイル 500 相手を認識するのは最優先
2 直近エピソード 600 「今日・昨日の文脈」は判断に直結
3 関連知識 700 意味的に関連する知識
4(最低) スキルマッチ 200 スキル名のリストのみ(内容は不要)

各チャネルが予算オーバーの場合:

  • 送信者プロファイル: 末尾から切り捨て(基本情報は先頭にある前提)
  • 直近エピソード: 古いエントリから除去(最新を優先)
  • 関連知識: 低スコアのチャンクから除去
  • スキルマッチ: 低関連度のスキル名から除去

動的予算調整

将来的には、メッセージの種類によって予算配分を変動させる:

  • 簡単な挨拶 → プライミング予算を縮小(500 tokens以下)
  • 複雑な業務依頼 → プライミング予算を拡大(3,000 tokens)
  • heartbeat → プライミングを最小化(送信者プロファイル不要)

12. 段階的実装計画

Phase 1: ファイルベースプライミング + 即時符号化

目的: 最小限の変更でプライミングと自動記憶記録の効果を検証する

実装内容:

プライミング(自動想起):

  • core/memory/priming.py を新規作成
  • チャネルA(送信者プロファイル直接読み込み)
  • チャネルB(直近エピソード直接読み込み)
  • チャネルD(スキル名のファイル名マッチ)
  • builder.py にプライミング結果の注入を追加
  • behavior_rules.md の改訂
  • チャネルCはBM25のみ(ripgrepでknowledge/を検索)

即時符号化(自動符号化):

  • ConversationMemory.finalize_session() メソッドを新規追加
  • 会話終了検出(タイムアウト / セッション切断 / heartbeat区切り)
  • 会話内容のLLM要約 → episodes/{date}.md への自動追記
  • エージェントのエピソード手動書き込みを非推奨化

依存ライブラリ: なし(標準ライブラリ + 既存の ripgrep 呼び出し + 既存のLLM呼び出し基盤)

検証項目:

  • エージェントがツール呼び出しなしで応答できるケースが増えるか
  • 「記憶を確認します」の宣言が消えるか
  • レスポンス時間の改善
  • 会話終了後にエピソードが自動記録されるか

Phase 2: RAGハイブリッド検索 + 日次固定化

目的: チャネルCをベクトル検索付きハイブリッドに強化し、日次の記憶固定化を導入する

実装内容:

RAG検索:

  • core/memory/rag/ サブパッケージの作成
  • ChromaDB導入 + コレクション設計
  • エンベディングモデル(multilingual-e5-small)の組み込み
  • ハイブリッド検索(BM25 + ベクトル + RRF統合)
  • 差分インデキシング(write_memory_file フック + 即時符号化フック)
  • animaworks index コマンド追加

日次固定化(NREM睡眠アナログ):

  • core/memory/consolidation.py を新規作成(ConsolidationEngine)
  • daily_consolidate(): 深夜cron → episodes/ → knowledge/ 変換
  • LifecycleManager にシステムcronとして登録(毎日02:00 JST)
  • 固定化用LLMプロンプトの設計・チューニング
  • [AUTO-CONSOLIDATED] タグによる自動生成知識の識別

依存ライブラリ:

  • chromadb — ベクトルストア
  • sentence-transformers — エンベディング生成(または onnxruntime で軽量化)

検証項目:

  • キーワード完全一致しない関連記憶がプライミングされるか
  • インデキシングのレイテンシとストレージサイズ
  • 検索精度の定性評価
  • 日次固定化で生成される knowledge/ の品質(誤った一般化がないか)
  • 固定化前後でのプライミング精度の変化

Phase 3: 週次統合 + 拡張と最適化

目的: 長期統合・剪定の導入、拡散活性化の改善、グラフ的要素の導入

実装内容:

週次統合(長期統合・剪定):

  • weekly_integrate(): 週次cron → knowledge/ 統合 + episodes/ 圧縮
  • LifecycleManager にシステムcronとして登録(毎週日曜03:00 JST)
  • knowledge/ ファイル間の重複検出(ベクトル類似度ベース)
  • 古い episodes/ の自動圧縮(30日超 + [IMPORTANT] なし → 要約化)

拡散活性化の改善:

  • 知識ファイル間のリンク関係をグラフとして抽出
  • Personalized PageRank による多段階拡散活性化(HippoRAG的)
  • エンベディングモデルのアップグレード(multilingual-e5-large, BGE-M3)
  • 動的予算調整の実装
  • ファイル監視(watchdog)による非同期インデキシング

13. 評価基準

定量指標

指標 現状(推定) Phase 1目標 Phase 2目標
初回応答までの記憶検索ツール呼び出し回数 2-4回/応答 0-1回/応答 0-1回/応答
初回応答レイテンシ 10-20秒 5-10秒 5-10秒
プライミングレイテンシ N/A <100ms <200ms
「記憶を確認します」類の発話頻度 ほぼ100% <10% <10%

定性指標

  • 会話の自然さ: 人間と話しているような即座の応答感があるか
  • 記憶の適切性: プライミングされた記憶が実際に関連しているか
  • 見逃し率: プライミングに含まれず、エージェントが追加検索で発見した記憶の頻度(低いほど良い)
  • 過剰注入率: プライミングに含まれたが使われなかった記憶の頻度(低いほど良い)

評価方法

  1. テストシナリオ: 以下のパターンでエージェントの応答を記録・比較

    • ユーザーからの簡単な挨拶
    • 過去に対応したトピックへの質問
    • 未知のトピックへの質問
    • 手順書に従う作業依頼
    • 複数の記憶を統合して判断が必要なケース
  2. A/Bテスト: プライミングあり/なしで同じシナリオを実行し、応答品質とレイテンシを比較


付録: 技術的な補足

ChromaDB のAnima間分離

ChromaDBのコレクション名にAnima名を含めることで、Anima間の記憶を物理的に分離する。これはカプセル化原則(他者の記憶は不可視)を技術的に保証する。

ただし shared_users コレクションは全Animaからアクセス可能とする。これは shared/users/ ディレクトリの既存のアクセスポリシーと一致する。

エンベディングモデルの運用

  • モデルは ~/.animaworks/models/ に配置(sentence-transformers のキャッシュディレクトリ)
  • 初回実行時に自動ダウンロード(約100MB)
  • Ollama対応: ollama pull nomic-embed-text でも代替可能(エンベディングAPIとして統一)

BM25の実装

Phase 1ではripgrep(rg)の呼び出しで代用する。これは既存の search_memory ツールと同じ方式。 Phase 2ではPython内のBM25ライブラリ(rank_bm25)への置き換えを検討。スコアの正規化がRRF統合に必要なため。