コンテンツにスキップ

メモリ & Sleep

記憶エンジン

Riverse は RAG システムではありません。生の会話チャンクを保存して類似テキストを検索するのではなく、会話のたびにオフライン整理パイプライン(Sleep)を実行し、自己修正し続ける個人プロファイルを構築します。

抽出されるもの

すべての会話は、それぞれ独自のライフサイクルを持つ 3 種類のデータ構造に処理されます:

タイプ 捕捉する内容 ライフサイクル
プロファイル事実 永続的な属性:仕事・都市・好み・健康パターンなど 信頼度ベース、時間減衰、更新時に上書き
人間関係 言及された人:名前・関係・詳細・言及回数 言及のたびに更新、状態を追跡
イベント 期限付き出来事:転職・引っ越し・手術など decay_days で自動期限切れ

事実のライフサイクル

各プロファイル事実は明確なライフサイクルを経ます:

観察(会話から抽出された生の陳述)
suspected(推測) →  confirmed(確認) →  established(確立)
    ↓                    ↓
  rejected(否定)    closed(クローズ:新しい事実に置き換え)
  • suspected — 抽出済みだが複数会話での検証未完了
  • confirmed — 2 回以上のセッションで一貫して登場、LLM が交差検証済み
  • established — 長期存在・証拠充分で、単発の会話では覆りにくい
  • rejected — 誤りとしてマーク(手動または LLM 仲裁)
  • closed — 事実が変化。古いレコードは end_time 付きで保持、新しい事実が引き継ぐ

時間的な減衰

各事実は decay_days 値を持ちます — その種の情報の推定有効期間。期限に近い事実はコンテキスト注入時の重みが下がり、期限切れ後はアクティブプロファイルから除去されますが、タイムラインには永続的に保持されます。

矛盾の仲裁

Sleep が矛盾する事実(例:「東京在住」vs「大阪に引っ越した」)を検出すると:

  1. 両方を争議ペアとしてフラグ
  2. 完全なコンテキスト(軌跡・タイムライン・最近の会話)付きで LLM 仲裁を実行
  3. 新しい事実を受け入れて古いものをクローズするか、新しい主張を否定

証拠チェーン

各事実はそれを生んだ証拠 — 特定の会話からの観察リスト — を保持します:

  • すべての事実はそれを生んだ会話まで遡れる
  • 矛盾仲裁時に LLM は完全なコンテキストを持つ
  • 信頼度スコアは支持する観察の数と質を反映

知識グラフ

Sleep は事実間の型付きエッジを抽出し、個人知識グラフを構築します:

  • causes — 「ストレス」が「睡眠障害」を引き起こす
  • related_to — 「転職」が「引っ越し」に関連
  • supports / contradicts — 事実間の証拠関係

コンテキスト注入時、関連トピックが出たときに因果関係のある事実を浮上させるためにグラフが使われます。

Sleep — オフライン記憶統合

Sleep は Riverse が会話を消化し、プロフィールを更新するプロセスです。自動・手動の両方で実行可能:

トリガー 方法
Telegram /new を送信 — セッションをリセットし、バックグラウンドで Sleep を実行
CLI 終了時に自動実行(quit または Ctrl+C)
REST API POST /sleep
cron(推奨) 毎晩の定時ジョブで一日の会話を統合

cron の例

毎日 0 時に Sleep を実行:

# crontab -e
0 0 * * * cd /path/to/JKRiver && /path/to/python -c "from agent.sleep import run; run()"

14 ステップパイプライン

パイプライン全体が単一のデータベーストランザクション内でアトミックに実行されます。いずれかのステップが失敗するとすべてロールバックされ、同じ会話が次回の実行時に再処理されます(at-least-once セマンティクス)。

フェーズ 1: 抽出

ステップ 1 — 初期データ読込 既存のユーザープロファイル(置き換え済み事実を除く)と最新のライフ軌跡サマリーを読み込みます。後続のすべてのステップにコンテキストを提供します。

ステップ 2 — セッション抽出 未処理の各会話セッションに対して、LLM が以下を抽出します:

  • 観察 — 事実の陳述、矛盾、好み、行動シグナル。各観察はタイプとサブジェクトでタグ付けされます。第三者(ユーザーが言及した人物)に関する観察は別途保存されます。
  • タグ — セッションレベルのトピックタグ
  • 人間関係 — 言及された人物、ユーザーとの関係、詳細。既存の関係はマージされます(言及回数インクリメント、詳細更新)。
  • イベント — 期限付きの出来事(転職、引っ越し、手術など)、重要度と減衰日数付き

ツール/外部委託のインテントのみのセッションはスキップされます — 個人情報を含まないためです。

フェーズ 2: 分析

ステップ 3 — 行動分析 LLM がすべての観察を現在のプロファイルと軌跡と照合し、行動パターンを推論します。例えば、深夜の複数のメッセージはユーザーが夜型であることを示唆するかもしれません。推論された事実は source_type=inferred で保存されます。証拠数が 3 以上に達すると、将来の会話で能動的に検証するための clarify 戦略が生成されます。

ステップ 4 — 分類と統合 コアステップ。LLM が各観察を既存のプロファイルに対して分類します:

  • support — 既存の事実を強化 → 証拠を追加、言及回数をインクリメント
  • contradict — 既存の事実と矛盾 → supersedes リンク付きで新事実を作成し、争議ペアを形成
  • evidence_against — 代替値を主張せずに既存の事実を弱める
  • new — 一致する事実なし → 新しいプロファイル事実を作成

未分類の statement/contradiction タイプの観察は自動的に new に割り当てられます。統合後、新規作成または矛盾した事実に対して変更戦略(probe タイプ)が生成されます。

ステップ 5 — 交差検証 推測事実の検証を行います:

  • ルールベースの高速パス: source_type=stated かつ mention_count ≥ 2 の事実は自動確認
  • LLM 検証: 残りの推測事実(言及回数降順、最大 80 件)をタイムライン履歴、関連する会話サマリー(直近 3 ヶ月)、軌跡コンテキストを用いて交差検証

検証に合格した事実は suspected から confirmed に昇格します。

ステップ 6 — 争議解決 争議中の事実ペア(新事実が旧事実を supersedes しているが両方ともクローズされていない)が完全なコンテキスト付きで LLM 仲裁に送られます。LLM は accept_new(新事実を受け入れ)または reject_new(新事実を却下)を決定します。敗訴側はクローズされ(end_time が設定)、その知識グラフエッジがクリーンアップされます。

フェーズ 3: 維持

ステップ 7 — エッジ抽出 今回の実行で影響を受けたすべての事実に対して、LLM が型付きエッジ(因果・時系列・階層)を抽出し、個人知識グラフを構築します。

ステップ 8 — 期限切れ処理 expires_at を過ぎた事実がクローズされます。各期限切れ事実に対して verify 戦略が生成され、AI が次回の関連会話で自然に確認できるようにします。

ステップ 9 — 成熟度減衰 長期間存在し証拠が充分な事実は、より長い寿命を獲得します。減衰ティア:

事実の経過日数 証拠数 新しい decay_days
≥ 730 日 ≥ 10 730(2 年)
≥ 365 日 ≥ 6 365(1 年)
≥ 90 日 ≥ 3 180(6 ヶ月)

軌跡でキーアンカーとして識別された事実(例:母語、長期キャリア)は閾値が 40% 引き下げられ、より早く成熟します。

フェーズ 4: 出力

ステップ 10 — ユーザーモデル LLM が会話からコミュニケーションスタイルの次元を分析します(例:直接的 vs 間接的、フォーマル vs カジュアル、ユーモアのスタイル)。結果は user_model テーブルに保存され、応答のトーン調整に使用されます。

ステップ 11 — 軌跡 重大な変化が検出され(事実の確認、争議の解決、重要カテゴリの矛盾)、前回の更新から少なくとも 2 セッション経過している場合、ライフ軌跡が再生成されます。ライフフェーズ、方向性、安定性、キーアンカー、不安定な領域、直近のモメンタムを含みます。

ステップ 12 — 統合 抽出と統合の過程で生じた重複を整理します。

ステップ 13 — スナップショット 完全な記憶スナップショットを事前コンパイルします:プロファイル事実 + ユーザーモデル + アクティブイベント + 人間関係 + 知識グラフエッジ。次回の会話ではスナップショットが直接提供され、複数テーブルからのコンテキスト再構築コストを回避します。

ステップ 14 — 完了 処理済みの全会話をマークします。これは最後のステップです — パイプラインがこの前にクラッシュした場合、トランザクションがロールバックされ、同じ会話が再処理されます。

トランザクション後処理

アトミックトランザクション完了後、2 つの非クリティカルなタスクがトランザクション外で実行されます:

  • ベクトル埋め込み — すべてのメモリを埋め込み、セマンティック検索をサポート
  • メモリクラスタリング — KMeans クラスタリングとテーマラベル生成

知識ネットワーク

Sleep は知識ネットワークも構築します — 関連するプロファイル事実を型付きエッジ(causesrelated_tocontradictssupports など)で接続します。AI が孤立した項目ではなく、事実間の構造的関係を把握できるようになります。

事実がクローズまたは置き換えられると、関連するエッジは自動的にクリーンアップされます。

メモリクラスタリング

埋め込みを有効にすると、Sleep はオプションでメモリベクトルを KMeans クラスタリングし、各クラスタにテーマラベルを生成できます。AI があなたについて何を知っているかの俯瞰図を提供します。

embedding:
  clustering:
    enabled: true
    show_themes: true

セマンティック検索

埋め込み(BGE-M3)を有効にすると、キーワードではなく意味で関連する記憶を検索できます。

embedding:
  enabled: true
  model: "bge-m3"
  api_base: "http://localhost:11434"

セッションメモリ

セッションメモリは、3 つのレイヤーで単一会話内のコンテキストを管理します:

  1. スライディングサマリー — 古いターンは LLM によって要約に圧縮され、トークン制限を超えずに会話履歴を保持
  2. ベクトルリコール — 埋め込みが有効な場合、現在のメッセージとの意味的類似度で関連する以前のターンを呼び出し
  3. 最近のターン — 最新のターンはそのまま保持し、即座のコンテキストを提供

この 3 層システムにより、重要なコンテキストを失うことなく会話を無制限に続けられます。settings.yaml で設定:

session_memory:
    char_budget: 3000        # セッションコンテキストの合計文字バジェット
    keep_recent: 5           # そのまま保持する最近のターン数
    summary_ratio: 0.4       # バジェットのうち要約に割り当てる割合
    recall_max: 3            # ベクトル検索による最大リコール数
    recall_min_score: 0.45   # リコールの最小類似度

pgvector アクセラレーション

デフォルトでは、埋め込みは JSONB として保存され、コサイン類似度は Python で計算されます。大規模データセットでのパフォーマンス向上には、pgvector 拡張をインストール:

# macOS
brew install pgvector

# Debian/Ubuntu
apt install postgresql-16-pgvector

マイグレーションを実行:

psql -h localhost -U YOUR_USERNAME -d Riverse -f migrations/001_pgvector.sql

ネイティブの vector(1024) カラムと IVFFlat インデックスが作成され、高速な近似最近傍検索が可能になります。アプリケーションは pgvector を自動検出し、利用可能な場合に使用します。設定変更は不要です。

メモリの精度

Info

現在、個人プロファイル抽出に特化して訓練された LLM は存在しないため、抽出結果に誤りが含まれる場合があります。Web ダッシュボードで誤った記憶をエラーとしてマークしたり、期限切れの記憶を手動でクローズしたりできます — ただし、記憶の削除や直接編集はできません。これは意図的な設計です:河流アルゴリズムは記憶を監査記録として扱い、誤った記憶は河の泥沙のように、水流に洗い流されるべきものであり、人の手で削るべきではありません。

エラーが多い場合、根本原因はほとんどの場合 LLM の理解能力であり、システムのバグではありません。より強力なモデルへの切り替えをお勧めします — 記憶パイプラインは、LLM が自然言語をどれだけ理解できるかを評価する実用的なベンチマークにもなります。会話が蓄積されるにつれ、アルゴリズムはマルチターン検証と矛盾検出により継続的に自己修正し、プロファイルはより正確になっていきます。

アルゴリズム先行の設計思想

Riverse の記憶パイプラインは、現在の汎用 LLM が完全には発揮できない水準を見据えて設計されています。14 ステップの Sleep 統合プロセスでは、各段階で正確な構造化判断が求められ — 観察の抽出、事実の分類、交差検証、矛盾の仲裁 — 各ステップの出力が次のステップを駆動します。現在の精度のボトルネックは、各段階での LLM 出力の精度であり、アルゴリズム自体ではありません。

このカスケード効果を考えてみてください:各 LLM ステップの精度が 90%(一部のタスクではすでに楽観的)だとすると、6 ステップ直列でエンドツーエンドの精度は約 53% になります。専用に訓練されたモデルが各ステップ 99% を達成すれば、同じパイプラインで 94% に到達します — 同じコードで、質的な飛躍です。

現時点で、個人記憶の統合に特化して訓練された LLM は存在しません。理想的には、以下の能力に最適化された専用メモリ LLM の登場が望まれます:

  • 観察抽出の精度 — 「東京に住んでいる」(陳述)と「東京に住めたらいいのに」(仮定)を区別する
  • 多事実の統合推論 — 新しい観察を 30 以上の既存プロファイル事実と照合して判断する(制約付き自然言語推論タスクであり、ファインチューニングの余地が大きい)
  • キャリブレーションされた信頼度 — パイプラインが閾値ベースで意思決定できるよう、信頼性のある確率スコアを出力する(単純な二値分類ではなく)

作者はそのモデルの明確な設計を持っていますが、訓練には個人では得られない計算資源とデータが必要です。

記憶に特化したモデルを構築中の企業、またはパーソナル AI に取り組んでいる企業で、適したポジションがあればぜひご連絡ください:mailwangjk@gmail.com

それまでの間、アルゴリズムは汎用モデル上で動作し、より強力なモデルが登場するたびにコード変更なしで自動的に改善されます。パイプラインのコンテキスト入力(タイムライン、軌跡、会話要約、証拠チェーン)は、将来のモデルに最も豊かなシグナルを提供できるよう、すでに構造化されています。

これは意図的な設計選択です:先にアーキテクチャを正しく構築し、モデルがアルゴリズムに追いつくのを待つ。今日のモデル能力に合わせてアーキテクチャを簡素化するのではなく。