Skip to content

メモリシステム

N.E.K.O. のメモリは、単一のベクトルデータベースではなく、キャラクターごとに分離された処理パイプラインです。次の会話に使う有限のワーキングコンテキスト、時系列の会話原文、抽出済みの事実、より高次のリフレクション、永続的なペルソナ知識をそれぞれ管理します。各レイヤーでは、書き込み経路、保持規則、プロンプトへの注入方法が異なります。

このページでは、現在のランタイム契約を説明します。しきい値、モデルティア、メンテナンス間隔は config/memory_settings.py で定義されるチューニング項目であり、ここで説明するアーキテクチャを変えずに調整できます。

概念モデル

レイヤー目的主な表現新規ダイアログのコンテキストに含まれるか
ワーキングコンテキスト次の LLM セッション向けに準備されたメモリテキストGET /new_dialog/{lanlan_name} が動的生成これは生成結果であり、独立したデータベースではない
直近メモリ直近の生ターンと、インプレースの要約メモからなる有限ウィンドウrecent.jsonはい
事実会話から抽出され、対象・重要度・イベント時刻を持つ原子的な観察facts.jsonいいえ。リフレクション合成と明示的リコールに使用
リフレクション複数の事実から合成された高次の解釈reflections.jsonpending とアクティブな confirmed がレンダリング候補になる
ペルソナメモリマスター、キャラクター、両者の関係についての永続知識persona.jsonはい。ただしレンダリング予算と抑制規則の対象

生の時系列ストア time_indexed.db は、事実抽出、時間範囲スキャン、会話間隔の計算を支えるジャーナルです。別のプロンプトレイヤーではなく、過去の生ターン全体が各 LLM プロンプトへ自動的にコピーされるわけではありません。

すべてのストアはキャラクター(lanlan_name)単位で分離されます。デフォルトでは設定済みメモリルート配下に置かれますが、キャラクターごとの直近履歴と SQLite のパスは設定で上書きできます。

書き込み経路:会話から長期メモリへ

1. ターンを永続化する

メインサーバーは、完了した会話データをポート 48912 の内部メモリサーバーへ送ります。同期コネクターはセッションのライフサイクルに応じて、POST /cache/{lanlan_name}POST /process/{lanlan_name}POST /renew/{lanlan_name}、または POST /settle/{lanlan_name} を使用します。

フォアグラウンドの永続化経路では、主に次の2つを書き込みます。

  • そのターンをキャラクターの直近履歴へ追加する。
  • 元のメッセージを、タイムスタンプと会話 ID とともに time_indexed.dbtime_indexed_original テーブルへ追加する。

さらに、再実行可能なターン終了処理を outbox.ndjson に登録します。負荷の高い抽出やレビューはバックグラウンドでスケジュールされるため、バックグラウンド LLM の失敗が進行中の会話をブロックする必要はありません。

2. 直近メモリをインプレースで圧縮する

memory/recent.py::CompressedRecentHistoryManager は直近コンテキストのサイズを制限します。履歴が設定されたしきい値を超えると、古いエントリを要約し、recent.json の先頭にある SystemMessage メモへ置き換えます。最新ターンは原文のまま残ります。入力が大きすぎる場合は予算付きの map-reduce 経路を使い、最後のハードキャップによって要約器が継続的に失敗してもプロンプトが無制限に増えないようにします。

要約モデルは設定済みの summary モデルティアから選ばれ、結果は直近メモリ内に残ります。

旧圧縮テーブル

SQLite にはスキーマ互換性と移行のため time_indexed_compressed テーブルが残っている場合がありますが、現在のランタイムは新しい要約を書き込みません。旧来の長期抽象化経路は、事実とリフレクションに置き換えられています。新規コードで retrieve_summary_by_timeframe() に依存したり、この圧縮テーブルをアクティブなストアとして扱ったりしないでください。

3. 事実を抽出・重複排除する

事実抽出は、会話ウィンドウをユーザーの好み、出来事、関係上の観察などの原子的レコードへ変換します。監査可能性を保つため、重要度の低い事実も保存し、各コンシューマーが必要な重要度を判断します。

新しい事実は永続化前に重複排除されます。完全一致ハッシュとテキスト検索によるチェックは常に利用できます。ローカル Embedding が利用できる場合は、バックグラウンド worker が言い換え候補を見つけ、後続の LLM 判定キューへ送ることもできます。リフレクションに取り込まれた事実には absorbed が付き、古い absorbed 事実は削除されず、facts.json から facts_archive.json へ移されます。

事実抽出のスケジュールは、後述する強力メモリスイッチによって変わります。どちらのモードでも、永続化済みの事実はリフレクション合成と明示的リコールに利用できます。

4. リフレクションを合成する

memory/reflection/ は、十分な数の未吸収事実を定期的に高次のリフレクションへまとめます。リフレクション ID はソース事実 ID から導出されるため、同じバッチの再試行は冪等です。合成に成功すると pending リフレクションを書き込み、ソース事実を absorbed にします。

リフレクションは、独自のエビデンス、エンティティ、ライフサイクル状態、任意のイベント時刻オントロジーを持ちます。暫定状態のまま残る、confirmed になる、ペルソナへ昇格またはマージされる、否定される、最終的にアーカイブされる、といった遷移があります。リフレクション合成はメモリサーバーのバックグラウンドサービスとして動作し、メモリブラウザやプロアクティブチャットのフロントエンドが開いているかどうかには依存しません。

5. 安定した知識をペルソナへ昇格する

ペルソナエントリはエンティティごとにグループ化されます。組み込みエンティティは masternekorelationship で、ストレージ形式は追加エンティティにも対応します。キャラクターカード由来のエントリは protected として扱われ、エビデンス減衰やアーカイブスイープでは削除されません。

強力メモリが有効な場合、昇格はエビデンス駆動です。昇格しきい値に達した confirmed リフレクションは、correction ティアのマージ判定を通ります。結果は、新規ペルソナエントリ、既存知識へのマージ、拒否、または矛盾解決キューへの追加のいずれかです。強力メモリが無効な場合は、設定可能な経過時間ベースのフォールバックが、マージ LLM を使わずにリフレクションを確認・昇格します。

Recall サブシステムと自動コンテキスト

新規ダイアログの自動コンテキスト

GET /new_dialog/{lanlan_name} は、新しい LLM セッションで使うメモリ部分を構築します。進行中の settle 処理が終わるのを待ってから、次の内容をレンダリングします。

  1. protected およびエビデンス順に選ばれたペルソナエントリ。ペルソナ用 token 予算の対象。
  2. pending とアクティブな confirmed リフレクション。リフレクション用 token 予算の対象。
  3. 直近履歴の要約メモと直近の生ターン。
  4. ダイアログプロンプトで使う現在時刻と、長い会話間隔に関するヒント。

レンダラーは暫定リフレクションと confirmed リフレクションを分け、時間的に古くなった confirmed リフレクションを過去メモリのセクションへ移し、最近繰り返し言及されたペルソナ項目には一時的に自発言及しないよう指示します。

この経路は、すべての事実や生の会話履歴に対するセマンティック検索を実行しません。事実ストア全体をプロンプトへ投入することもありません。

ユーザー向けハイブリッド Recall

memory/hybrid_recall.py はユーザー向けの検索バックエンドです。メイン会話経路は main_logic/core/tool_calling.py で組み込み recall_memory ツールを登録し、POST /query_memory/{lanlan_name} を呼び出します。QQ 自動返信プラグインも受信メッセージを query として同じ endpoint を直接呼び、レンダリング済みの最大 5 件を返信コンテキストへ注入します。

会話モデルは自然言語の querytime 表現、またはその両方を指定できます。

ツール引数検索動作
query のみBM25 はアクティブな事実、アクティブなリフレクション、アーカイブ済み事実を検索し、任意のコサイン検索はアクティブな事実とリフレクションを検索する。両方の順位を Reciprocal Rank Fusion(RRF)で統合
time のみ解析されたイベント時間ウィンドウに最も近い事実、リフレクション、アーカイブ済み事実を返す
querytimeイベント時間ウィンドウでハードフィルターした後、その範囲内でセマンティック検索

ペルソナが除外されるのは、このユーザー向け検索プールだけです。ペルソナは通常のダイアログコンテキストへすでにレンダリングされます。共有ハードフィルターは、負の evidence score、suppressed、壊れたテキスト、終端状態のリフレクションを除外します。query 経路は既定で BM25 とコサインから各最大 4 件を取り、RRF で重複を除いて最大 8 件を返します。追加の LLM 再ランキングは行いません。

HTTP endpoint は構造化された行を返しますが、メイン会話ハンドラーはそれらをローカライズ済み Markdown bullet に変換してから tool output としてモデルへ渡します。メモリ本文は翻訳せず、tier/entity と、利用可能ならイベント日付および相対時間を付けます。メモリサーバー要求が失敗した場合は空結果メッセージを返し、会話を継続します。

バックグラウンド保守の関連性 Recall

memory/recall.py::MemoryRecallReranker は別のバックグラウンドサブシステムであり、recall_memory ツールへの回答には使われません。

コンシューマー候補プールと query順位付けとフォールバック
memory/facts.py の Stage-2 evidence signal 検出confirmed/promoted リフレクションと non-protected ペルソナ。query は新しく抽出された事実共有ハードフィルター後、予算の 3 倍までベクトル粗順位を作り、必要な場合だけ LLM で再順位付けする。Stage-2 prompt に入る observation の上限は 30。ベクトルが利用できない場合は、フィルター後の項目を evidence score 順にし、再順位付け LLM は呼ばない。
memory/reflection/synthesis.py のリフレクション合成未吸収の各事実が、吸収済み事実を個別に queryquery ごとにコサイン top 3 を取り、round-robin で union/dedup して全体を 20 件に制限する。ready かつ有効な embedding がない場合は、無関係な高スコア事実で代替せず related-context を空にする。

この違いは重要です。ペルソナはユーザー向けハイブリッド Recall からだけ除外され、non-protected ペルソナは Stage-2 保守 Recall に明示的に含まれます。

memory/embeddings.py の共有 Embedding サービスは任意のローカルコンポーネントで、CPU ONNX execution provider を使用します。フォールバックは呼び出し元ごとに異なります。ユーザー向け Recall は BM25 のみ、Stage-2 保守 Recall は evidence score 順、リフレクションの履歴アンカー Recall は空の related-context になります。

NEKO_DISABLE_BUILTIN_TOOLS=1 は診断用にメイン会話セッションから組み込み tool schema を外すだけです。POST /query_memory、QQ 自動返信 Recall、バックグラウンド保守 reranker は無効になりません。

エビデンスとリフレクションのライフサイクル

エビデンスは、独立した reinforcement(強化)値と disputation(反証)値として保存されます。それぞれ別の時計を使って読み取り時に減衰し、差し引きスコアによって pending、confirmed、昇格可能、アーカイブ候補のいずれかが決まります。しきい値と半減期は config/memory_settings.py に集約されています。

エビデンスには複数の入力経路があります。

  • 新しく抽出された事実が、既存のリフレクションまたはペルソナエントリを強化・否定する。
  • ユーザーが、キャラクターから提示されたリフレクションを確認、否定、または無視する。
  • 否定キーワードに一致した場合、候補メモリを確認してから反証シグナルを適用する。
  • 定期的な反証スキャンが、新しいユーザーメッセージをアクティブな confirmed リフレクションと照合する。

ユーザーからの直接フィードバックと、事実からメモリへの関係を推論したシグナルでは重みが異なります。そのため、単一の LLM 抽出をただちに確定情報として扱わず、蓄積されたエビデンスに基づいて昇格とアーカイブを判断します。

ライフサイクルの概略は次のとおりです。

text
事実
  -> pending リフレクション
  -> confirmed リフレクション
  -> 昇格またはマージ済みのペルソナ知識

否定的エビデンス
  -> denied / スコアがゼロ未満のエントリ
  -> アーカイブ候補
  -> 設定期間の経過後にシャードアーカイブ

言及抑制はエビデンスとは別の仕組みです。同じメモリをキャラクターが自発的に繰り返すのを一時的に防ぐだけで、基礎となるエントリは削除しません。

強力メモリモード

core_config.jsonpowerful_memory_enabled はメモリサーバーが都度読み直し、メインサーバーの GET および POST /api/memory/powerful_memory_config から操作できます。設定がない場合は有効がデフォルトです。

機能有効無効
事実抽出十分なユーザーターンまたはアイドル条件の後に Stage 1 をバッチ実行し、Stage 2 が新しい事実をエビデンスシグナルへ対応付けるターンごとの Stage 1 フォールバックで基本的な事実蓄積は継続し、Stage 2 は停止
AI 自己開示の事実ユーザーが関与したウィンドウから定期的に AI-aware 抽出停止
リフレクション合成実行実行
提示済みリフレクションへのフィードバック確認実行実行
定期的な反証と否定キーワードの対象確認実行停止
昇格エビデンススコアとマージ判定を使用設定された経過時間で確認・昇格し、マージ LLM は不使用
ペルソナ矛盾解決、ベクトル補助の事実重複排除、ペルソナ/リフレクションの refinement作業と依存関係がある場合に実行再有効化までキューに残るか停止
直近履歴の圧縮とレビュー実行。レビューは独立した recent_memory_auto_review スイッチの対象実行。レビューには独立スイッチあり
明示的リコール、アーカイブスイープ、移行、schema 再判定このスイッチとは独立して利用可能このスイッチとは独立して利用可能

有効から無効へ切り替える際は、新しい設定を保存する前に現在 confirmed のリフレクションの経過時間アンカーをリセットします。これにより、古いエントリが時間ベースのフォールバックで即座に一括昇格されることを防ぎます。

バックグラウンドメンテナンス

メモリサーバーの起動フックは pending outbox レコードを走査して replay task を生成し、その後に未適用イベントの照合と 1 回限りの schema/アーカイブ移行を開始します。現在のランタイムは、後続処理の前に生成済み outbox task の完了を待ちません。そのため outbox handler は reconciler、移行、最初の時差付き loop と重なる可能性があります。厳密な復旧順序や、outbox の副作用が reconciler より先に見えることを前提にしないでください。長期実行ジョブには次が含まれます。

  • 事実とエビデンスシグナルのバッチ抽出。
  • confirmed リフレクションに対する反証確認。
  • スコアベースまたは時間ベースの自動昇格。
  • アイドル時の直近履歴圧縮、直近履歴レビュー、事実重複排除、矛盾解決。
  • リフレクション合成。
  • ペルソナとリフレクションの refinement。
  • ゼロ未満エビデンスの日数追跡とアーカイブスイープ。
  • 旧事実/リフレクションのイベント時刻 schema の低速移行。
  • 任意の Embedding ウォームアップとバックフィル。

正確な間隔とバッチサイズは運用上のチューニングであり、公開 API の保証ではありません。

ストレージと復旧

デフォルトのキャラクター別ディレクトリには、次の主要な永続データがあります。一部のファイルやディレクトリは、その機能で初めて永続化対象が発生したときに遅延作成されます。

memory/<character>/ 配下のパス役割
recent.json直近ターンと現在の要約メモ
recent_meta.json直近要約の鮮度メタデータ
time_indexed.dbtime_indexed_original の生の時系列会話行。旧圧縮テーブルが存在する場合もある
facts.jsonfacts_archive.jsonアクティブな事実と古い absorbed 事実
reflections.jsonsurfaced.jsonアクティブなリフレクションと、提示/ユーザーフィードバックの状態
reflection_archive/シャード化されたリフレクションアーカイブ
persona.jsonpersona_corrections.jsonペルソナビューとキュー済みの矛盾判定
persona_archive/シャード化されたペルソナエントリアーカイブ
cursors.json定期スキャンの永続進捗位置
outbox.ndjson再実行可能なバックグラウンド処理の pending/done レコード
events.ndjsonevents_applied.json順序付き状態遷移ジャーナルと照合用センチネル

JSON ビューファイルは引き続き編集可能な状態であり、完全なイベントソーシングではありません。イベント対応の状態遷移では、ランタイムはビューを更新する前にイベントを追記します。起動時の reconciler は、イベント書き込み後にビュー書き込みが完了しなかった遷移を再適用できます。Outbox ハンドラーは少なくとも1回の配信セマンティクスを使うため、冪等でなければなりません。

JSON ビュー全体の置換にはアトミック書き込みを使い、キャラクター単位のロックが競合する変更を直列化します。バックグラウンド LLM の失敗には有限回の再試行、バックオフ、進捗マーカーがあり、メンテナンスタスクは通常のチャットを妨げずに縮退または再試行します。

プライバシーと障害時の動作

  • メモリデータはデフォルトでローカル保存されますが、メモリの処理までローカルとは限りません。要約、抽出、リフレクション、昇格、レビュー、修正は設定済みモデルプロバイダーを使用します。これらの処理を実行すると、関連する会話またはメモリテキストがそのプロバイダーへ送られます。
  • 明示的リコールの結果はツール出力としてアクティブな会話モデルへ返されるため、選択中のチャットプロバイダーはそのメモリ断片を受け取ります。
  • メイン会話 tool handler の通常 INFO Recall ログには、モード、ヒット件数、処理時間などのメタデータだけが入り、生の query や Recall 本文は入りません。診断用 DEBUG ログには、生の query と完全な tool 引数が含まれる場合があります。
  • ベクトル推論はローカルかつ任意です。ベクトルが利用できなくても、事実、リフレクション、ペルソナ、BM25 リコール、時間リコールは無効になりません。
  • 任意アーカイブの破損や利用不能はアクティブストアのみへ縮退します。リコールエラーはヒットなしを返します。要約失敗時も未圧縮の直近履歴は利用可能で、後で再試行されるかハードキャップで制限されます。
  • 保存場所の選択、移行、復旧中はメモリサーバーが制限モードになり、ストレージを安全に使用できるまでメモリ操作へ 409 を返すことがあります。

レビュー UI とインターフェース

http://localhost:48911/memory_browser では、直近の会話メモリを閲覧・編集し、直近メモリレビューと強力メモリを設定できます。現在のブラウザ API が読み書きするのは recent.json であり、facts.jsonreflections.jsonpersona.json の汎用エディターではありません。

ユーザー向けのメインサーバールートはメモリ REST APIを参照してください。プロセス間インターフェースはメモリサーバー APIを参照してください。/cache/process/settle/new_dialog/query_memory などの内部メモリサーバーパスは N.E.K.O. プロセス間の実装インターフェースであり、外部互換性を保証する公開エンドポイントではありません。

ローカル保存と Provider 処理のユーザー向け境界は、会話・メモリのデータフローローカル・オフラインの境界を参照してください。