背景
EgoPulse のプロジェクト目標は「自分自身の文脈を理解するAIエージェント」だが、現在はセッションを跨いだ記憶がない。毎回ゼロから会話を始める状態。
Microclaw のメモリシステムを参考に、5テーブル全て を導入する。Reflector/Injection のログテーブルは「ちゃんと動いているか」の確認に不可欠であり、最初の実装・デバッグフェーズで最も必要になる。
仕組みの概要
会話する → Reflector(LLM呼び出しで記憶を抽出) → memories に保存
↓
次の会話開始時 ← Injection(記憶をシステムプロンプトに埋め込み)
Reflector(抽出)
- 会話が一定量溜まったら、別LLM呼び出しで「この会話から覚えておくべきことを抽出」
- 構造化パースして memories テーブルに保存
memory_reflector_state で「どこまで抽出したか」を進捗管理(差分抽出)
memory_reflector_runs で実行結果(成功/失敗/抽出数/エラー詳細)を記録
Injection(注入)
- セッション開始時に最新の memories を上位N件取得
- トークン上限内に調整して system prompt に append
memory_injection_logs で注入結果(候補数/採用数/除外数/推定トークン)を記録
Supersede(置換)
- 新しい記憶が古い記憶を更新・訂正した場合、古い記憶をアーカイブし置換関係を記録
memory_supersede_edges で置換グラフを管理
スキーマ(5テーブル)
1. memories — 構造化知識ストレージ
CREATE TABLE IF NOT EXISTS memories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
chat_id INTEGER, -- NULL = グローバル記憶
content TEXT NOT NULL,
category TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
confidence REAL NOT NULL DEFAULT 0.70,
source TEXT NOT NULL DEFAULT 'reflector',
last_seen_at TEXT NOT NULL,
is_archived INTEGER NOT NULL DEFAULT 0,
archived_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_memories_chat ON memories(chat_id);
CREATE INDEX IF NOT EXISTS idx_memories_active_updated
ON memories(is_archived, updated_at);
CREATE INDEX IF NOT EXISTS idx_memories_confidence ON memories(confidence);
| カラム |
説明 |
| chat_id |
チャット固有の記憶。NULL = グローバル記憶 |
| content |
記憶の本文 |
| category |
カテゴリ分類(preference, fact, instruction 等) |
| confidence |
信頼度スコア(0.0〜1.0)。高いほど優先的に注入 |
| source |
生成元(reflector, legacy 等) |
| last_seen_at |
最後に参照された日時。鮮度管理に使用 |
| is_archived |
ソフトデリートフラグ。置換や陳腐化でアーカイブ |
2. memory_reflector_state — 抽出進捗管理
CREATE TABLE IF NOT EXISTS memory_reflector_state (
chat_id INTEGER PRIMARY KEY,
last_reflected_ts TEXT NOT NULL,
updated_at TEXT NOT NULL
);
| カラム |
説明 |
| last_reflected_ts |
前回抽出完了時点のメッセージタイムスタンプ。この後の新規メッセージのみ差分抽出 |
3. memory_reflector_runs — Reflector 実行ログ
CREATE TABLE IF NOT EXISTS memory_reflector_runs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
chat_id INTEGER NOT NULL,
started_at TEXT NOT NULL,
finished_at TEXT NOT NULL,
extracted_count INTEGER NOT NULL DEFAULT 0,
inserted_count INTEGER NOT NULL DEFAULT 0,
updated_count INTEGER NOT NULL DEFAULT 0,
skipped_count INTEGER NOT NULL DEFAULT 0,
dedup_method TEXT NOT NULL,
parse_ok INTEGER NOT NULL DEFAULT 1,
error_text TEXT
);
CREATE INDEX IF NOT EXISTS idx_memory_reflector_runs_chat_started
ON memory_reflector_runs(chat_id, started_at);
| カラム |
説明 |
| extracted_count |
LLMが抽出した記憶候補数 |
| inserted_count |
新規挿入数 |
| updated_count |
既存記憶の更新数 |
| skipped_count |
スキップ数(重複等) |
| dedup_method |
重複排除手法(semantic, jaccard, none 等) |
| parse_ok |
LLM出力のパース成功フラグ。失敗時は error_text に詳細 |
なぜ最初から必要か: Reflector はバックグラウンドで動く非同期ジョブ。「抽出が動いてるか」「何件取れてるか」「パースエラーが出てないか」を確認する唯一の手段。
4. memory_injection_logs — Injection 実行ログ
CREATE TABLE IF NOT EXISTS memory_injection_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
chat_id INTEGER NOT NULL,
created_at TEXT NOT NULL,
retrieval_method TEXT NOT NULL,
candidate_count INTEGER NOT NULL DEFAULT 0,
selected_count INTEGER NOT NULL DEFAULT 0,
omitted_count INTEGER NOT NULL DEFAULT 0,
tokens_est INTEGER NOT NULL DEFAULT 0
);
CREATE INDEX IF NOT EXISTS idx_memory_injection_logs_chat_created
ON memory_injection_logs(chat_id, created_at);
| カラム |
説明 |
| retrieval_method |
検索手法(keyword, semantic, provider 等) |
| candidate_count |
候補として抽出された記憶数 |
| selected_count |
実際にコンテキストに注入された数 |
| omitted_count |
トークン制限等で除外された数 |
| tokens_est |
推定消費トークン数 |
なぜ最初から必要か: 「記憶は注入されてるか」「トークンを食いすぎてないか」「除外されすぎてないか」の確認に必須。
5. memory_supersede_edges — 記憶の置換関係グラフ
CREATE TABLE IF NOT EXISTS memory_supersede_edges (
id INTEGER PRIMARY KEY AUTOINCREMENT,
from_memory_id INTEGER NOT NULL,
to_memory_id INTEGER NOT NULL,
reason TEXT,
created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_memory_supersede_from
ON memory_supersede_edges(from_memory_id, created_at);
CREATE INDEX IF NOT EXISTS idx_memory_supersede_to
ON memory_supersede_edges(to_memory_id, created_at);
| カラム |
説明 |
| from_memory_id |
置換元(新しい記憶)の memories.id |
| to_memory_id |
置換先(古い記憶、アーカイブ対象)の memories.id |
| reason |
置換理由(LLMが出力) |
なぜ最初から必要か: 記憶が上書きされた際に「なぜ古い記憶が消えたか」を追跡する唯一の手段。デバッグで「この記憶どうして消えた?」を調査する時に必要。
実装ステップ
- スキーマ追加: マイグレーションで5テーブルを CREATE
- storage.rs に CRUD 追加: 全テーブルの操作メソッド
- Reflector 実装: 会話履歴 → LLM抽出 → 構造化パース → DB保存 → 実行ログ記録
- Injection 実装: セッション開始時に記憶を検索 → system prompt に組み込み → 注入ログ記録
- Supersede 実装: 重複検出時の古い記憶アーカイブ + 置換エッジ記録
- トリガー設計: Reflector をいつ走らせるか(会話終了時?定期実行?)
- テスト: AAA パターンで単体テスト(スキーマ操作 + Reflector/Injection/Supersede 各ロジック)
参考
- Microclaw メモリ設計:
docs/30.egopulse/microclaw-db-memory.md
- Microclaw 実装:
/root/workspace/microclaw/crates/microclaw-storage/src/db.rs
- Microclaw Reflector ロジック:
/root/workspace/microclaw/src/scheduler.rs (lines 516-673)
- Microclaw Injection ロジック:
/root/workspace/microclaw/src/memory_service.rs (line 401)
- EgoPulse 現状スキーマ:
docs/30.egopulse/egopulse-db-schema.md
背景
EgoPulse のプロジェクト目標は「自分自身の文脈を理解するAIエージェント」だが、現在はセッションを跨いだ記憶がない。毎回ゼロから会話を始める状態。
Microclaw のメモリシステムを参考に、5テーブル全て を導入する。Reflector/Injection のログテーブルは「ちゃんと動いているか」の確認に不可欠であり、最初の実装・デバッグフェーズで最も必要になる。
仕組みの概要
Reflector(抽出)
memory_reflector_stateで「どこまで抽出したか」を進捗管理(差分抽出)memory_reflector_runsで実行結果(成功/失敗/抽出数/エラー詳細)を記録Injection(注入)
memory_injection_logsで注入結果(候補数/採用数/除外数/推定トークン)を記録Supersede(置換)
memory_supersede_edgesで置換グラフを管理スキーマ(5テーブル)
1. memories — 構造化知識ストレージ
preference,fact,instruction等)reflector,legacy等)2. memory_reflector_state — 抽出進捗管理
3. memory_reflector_runs — Reflector 実行ログ
semantic,jaccard,none等)なぜ最初から必要か: Reflector はバックグラウンドで動く非同期ジョブ。「抽出が動いてるか」「何件取れてるか」「パースエラーが出てないか」を確認する唯一の手段。
4. memory_injection_logs — Injection 実行ログ
keyword,semantic,provider等)なぜ最初から必要か: 「記憶は注入されてるか」「トークンを食いすぎてないか」「除外されすぎてないか」の確認に必須。
5. memory_supersede_edges — 記憶の置換関係グラフ
なぜ最初から必要か: 記憶が上書きされた際に「なぜ古い記憶が消えたか」を追跡する唯一の手段。デバッグで「この記憶どうして消えた?」を調査する時に必要。
実装ステップ
参考
docs/30.egopulse/microclaw-db-memory.md/root/workspace/microclaw/crates/microclaw-storage/src/db.rs/root/workspace/microclaw/src/scheduler.rs(lines 516-673)/root/workspace/microclaw/src/memory_service.rs(line 401)docs/30.egopulse/egopulse-db-schema.md