Memory System (日本語)
埋め込みプロバイダーの選択(v3.8.16+)
Section titled “埋め込みプロバイダーの選択(v3.8.16+)”OmniRoute のメモリエンジンは、4 つの埋め込みソース(src/lib/memory/embedding/)をサポートしています。それぞれ、レイテンシ、コスト、モデル品質、セットアップの複雑さにおけるトレードオフが異なります。
埋め込みソース
Section titled “埋め込みソース”| プロバイダー | ソース | レイテンシ | コスト | 品質 | セットアップ |
|---|---|---|---|---|---|
transformers |
ローカル ONNX モデル(Xenova/all-MiniLM-L6-v2) | 約50~150ms(CPU) | 無料 | 良好 | npm install のみ |
static |
事前計算済みベクトル(キャッシュ済み) | 1ms未満 | 無料 | 該当なし(キャッシュヒットに依存) | なし |
remote |
OpenAI / Cohere / Voyage API | 約100~300ms | $0.02~0.10/100万トークン | 非常に優れている | API キー |
auto |
実行時に利用可能な最適なソースを選択 | 選択されたソースと同じ | 無料 | 選択されたソースと同じ | なし |
| (cache) | 任意のソース上のインメモリ LRU レイヤー | 1ms未満(ヒット)、全レイテンシ(ミス) | 無料 | 基盤となるソースと同じ | 常時有効(選択可能なソースではない) |
デシジョンツリー
Section titled “デシジョンツリー” デプロイ環境はどれですか? │ ┌───────────┼───────────┬──────────────┐ │ │ │ │ 開発/テスト 小規模本番 大規模本番 エッジ/オフライン │ │ │ │ ▼ ▼ ▼ ▼ transformers transformers remote (Qdrant) transformers (無料、API不要) (最高品質) (インターネット不要) │ │ │ │ └────────┬──┴───────────┴──────────────┘ │ ▼ 必ず上に `cache` レイヤーを追加 (LruCache が任意のプロバイダーをラップ)データベースと API の設定
Section titled “データベースと API の設定”メモリ埋め込みオプションは、環境変数ではなく Settings API/UI を介して設定します。Settings における関連設定のデータベースキー(src/lib/memory/settings.ts の normalizeMemorySettings)は次のとおりです。
memoryEmbeddingSource:"transformers"(ローカル)、"remote"(API ベース、例:OpenAI)、"static"(外部ストア)、または"auto"memoryEmbeddingProviderModel: リモート/静的ソースのモデル識別子(例:"text-embedding-3-small")memoryTransformersEnabled:true|falsememoryStaticEnabled:true|falsememoryVectorStore:"sqlite-vec"、"qdrant"、または"auto"
ローカルモデル(transformers)
Section titled “ローカルモデル(transformers)”内部で transformers.js を使用してローカルモデルを実行します。
# コード内で読み取られる環境変数(src/lib/memory/embedding/index.ts):MEMORY_TRANSFORMERS_MODEL=Xenova/all-MiniLM-L6-v2 # HF モデルリポジトリMEMORY_STATIC_MODEL=minishlab/potion-base-8M # HF 静的 Potion モデルMEMORY_STATIC_CACHE_DIR=<DATA_DIR>/embeddings # キャッシュディレクトリLRU 埋め込みキャッシュ
Section titled “LRU 埋め込みキャッシュ”キャッシュはデフォルトで常に有効であり、環境変数を介して設定します。
MEMORY_EMBEDDING_CACHE_MAX=1000 # キャッシュされる項目の最大数MEMORY_EMBEDDING_CACHE_TTL_MS=300000 # TTL(5分)パフォーマンス数値
Section titled “パフォーマンス数値”一般的な4コアx86サーバーでのベンチマーク(各テキスト約100トークン):
| プロバイダー | p50 | p95 | p99 | 100万埋め込みあたりのコスト |
|---|---|---|---|---|
transformers (CPU) |
80ms | 180ms | 350ms | 無料 |
remote (OpenAI) |
120ms | 220ms | 400ms | 約$0.02 (ada-002) / $0.13 (3-large) |
static (Qdrant) |
15ms | 30ms | 60ms | Qdrantのホスティングによる |
cache (ヒット) |
<1ms | <1ms | 2ms | 無料 |
ファクト抽出パターン (v3.8.16+)
Section titled “ファクト抽出パターン (v3.8.16+)”extraction.ts モジュール (src/lib/memory/extraction.ts) は、正規表現によるパターンマッチングを使用して、会話メッセージから構造化されたファクトを抽出します。これらのパターンを理解することで、ユースケースに合わせて抽出品質を調整できます。
デフォルトのパターンカテゴリ
Section titled “デフォルトのパターンカテゴリ”| カテゴリ | パターン例 | 抽出対象 |
|---|---|---|
| PREFERENCE_PATTERNS | "I prefer <X>"、"I like <X>"、"I hate <X>" |
ユーザーの好み |
| DECISION_PATTERNS | "I'll use <X>"、"I decided to <X>"、"I went with <X>" |
ユーザーの決定(エピソード記憶) |
| PATTERN_PATTERNS | "I usually <X>"、"I always <X>"、"I never <X>" |
持続的な行動パターン |
パターン例(簡略版)
Section titled “パターン例(簡略版)”// src/lib/memory/extraction.ts よりconst PREFERENCE_PATTERNS = [ /\bI\s+(?:really\s+)?prefer\s+([^.,\n]+)/gi, /\bI\s+(?:really\s+)?like\s+([^.,\n]+)/gi, /\bI\s+(?:hate|dislike|avoid)\s+([^.,\n]+)/gi,];const DECISION_PATTERNS = [ /\bI'?(?:ll|will)\s+use\s+([^.,\n]+)/gi, /\bI\s+(?:have\s+)?decided\s+(?:to\s+)?([^.,\n]+)/gi,];const PATTERN_PATTERNS = [/\bI\s+usually\s+([^.,\n]+)/gi, /\bI\s+always\s+([^.,\n]+)/gi];抽出される内容
Section titled “抽出される内容”ユーザーが次のように発言した場合:
「TypeScript が好みです。このプロジェクトでは Postgres を使います。私は常にプッシュ前にコミットします。Python は好きではありません。」 抽出によって4件のメモリが生成されます:
キー カテゴリ タイプ 内容 preference:typescriptpreference factual “TypeScript” decision:postgres_for_this_projectdecision episodic “Postgres for this project” pattern:commit_before_pushingpattern factual “commit before pushing” preference:pythonpreference factual “Python”
過剰な抽出を防ぐため、次の制限が適用されます:
| 最小コンテンツ長 | 3文字 | | 最大コンテンツ長 | 500文字 |
抽出を無効にする場合
Section titled “抽出を無効にする場合”メモリが有効な場合、抽出は常に自動的に実行されます。抽出専用の切り替え設定はありません。無効にするには、PUT /api/settings/memory を介してメモリ全体を無効化してください (enabled: false)。次のような場合は、無効化を検討してください:
- メッセージ量が多く、抽出コストを無視できない場合
- 会話の大半が一時的なもの(チャット、デバッグ)で、長期的な価値がない場合
- カスタムプラグインですでにコンテキストを取得している場合
ハイブリッドRRFのチューニング (v3.8.16+)
Section titled “ハイブリッドRRFのチューニング (v3.8.16+)”Reciprocal Rank Fusion (RRF) アルゴリズムは、FTS5(キーワード)とベクトル(セマンティック)の結果を組み合わせます。k パラメータは、順位の低い結果にどの程度の重みを与えるかを制御します。
各候補メモリのRRFスコアは次のとおりです:
RRF(d) = Σ 1 / (k + rank_i(d))ここで:
kは定数(デフォルトは60)rank_i(d)は、i番目の検索システム(FTS、ベクトル)におけるドキュメントdの順位- 合計はすべての検索システムについて計算される
k が結果に与える影響
Section titled “k が結果に与える影響”k の値 |
効果 | 最適な用途 |
|---|---|---|
k=0 |
純粋な順位融合(平滑化なし) | 理論上のベースライン |
k=10-30 |
上位の結果を大幅に重視し、下位の結果はほとんど寄与しない | 上位3件が通常正しい場合 |
k=60(デフォルト) |
バランス型 — 上位10件の結果がすべて有意に寄与する | 汎用的な検索 |
k=100+ |
より平坦 — 複数のシステムに現れる場合、下位の結果でも優勢になり得る | 適合率より再現率が重要な場合 |
実践での k のチューニング
Section titled “実践での k のチューニング”# デフォルトMEMORY_RRF_K=60
# 積極的に適合率を重視(小規模なメモリ、少数のドキュメント)MEMORY_RRF_K=20
# 最大限に再現率を重視(大規模なメモリ、多様なクエリ)MEMORY_RRF_K=120k=20 の例:
- FTS順位1位 → 寄与度
1/21 = 0.048 - FTS順位10位 → 寄与度
1/30 = 0.033 - ベクトル順位1位 → 寄与度
0.048 - 合計の最大値:
0.096
k=60 の例:
- FTS順位1位 → 寄与度
1/61 = 0.016 - FTS順位10位 → 寄与度
1/70 = 0.014 - ベクトル順位1位 → 寄与度
0.016 - 合計の最大値:
0.033
k が大きいほど、1位と10位の相対的な差は小さくなるため、アルゴリズムは上位順位の確信度よりも、検索システム間の一致を重視するようになります。
k を変更する場合
Section titled “k を変更する場合”| 症状 | 試すこと |
|---|---|
| 最上位の結果が常に選ばれるが、誤っている | kを下げる(例: 20)— 上位順位の確信度をより重視する |
| 正解が上位5件にはあるが、1位ではない | kを上げる(例: 100)— より平坦なスコアリングで一致を評価する |
| 再現率は高いが適合率が低い | kを下げる — 順位付けをより明確にする |
| 再現率が低い(関連ドキュメントを逃す) | kを上げる — 下位のドキュメントにも機会を与える |
RRFの重み付け
Section titled “RRFの重み付け”Reciprocal Rank Fusionでは、セマンティックベクトル順位と全文検索順位に同じ重みを使用します:
RRF(d) = 1/(k + rank_vector) + 1/(k + rank_fts)個別の重みを調整するための環境変数はありません(MEMORY_RRF_VECTOR_WEIGHT/MEMORY_RRF_FTS_WEIGHT は存在しません)。
要約戦略 (v3.8.16+)
Section titled “要約戦略 (v3.8.16+)”summarization.ts モジュール (src/lib/memory/summarization.ts) は、想起能力を維持しながらアクティブなセットを小さく保つため、古いメモリを圧縮します。
要約がトリガーされるタイミング
Section titled “要約がトリガーされるタイミング”| トリガー | しきい値 (デフォルト) |
|---|---|
| API 経由の手動トリガー | 該当なし |
要約される内容
Section titled “要約される内容”summarization.ts からは、次の 2 つのエントリポイントがエクスポートされています。
summarizeMemories(apiKeyId, sessionId?, maxTokens = 4000)— セッションの メモリを、トークン予算内に収まる単一の要約テキストへ圧縮します。summarizeMemoriesOlderThan(apiKeyId, days, dryRun)— API で使用される 経過日数ベースの圧縮です。daysより古いすべてのメモリを選択し、それらから 1 つの圧縮された要約メモリを作成し、dryRunがfalseの場合は 元のメモリを削除します。何も変更せずに候補セットと合計トークン数を プレビューするには、dryRun: trueを渡します。
タグ/キーによるクラスタリング処理や、メモリごとの「中核か要約可能か」のスコアリングはありません。 選択は経過日数のカットオフのみに基づき、要約テキストは候補ごとに タイプを接頭辞として付けた圧縮済みの行で構成されます。
要約のトリガー方法
Section titled “要約のトリガー方法”要約は手動 / オプトインです。autoSummarize 設定はデフォルトで false のため、
自動的に圧縮されることはありません。API 経由でトリガーしてください。
curl -X POST http://localhost:20128/api/memory/summarize \ -H "Authorization: Bearer $OMNIROUTE_KEY"無効のままにするには、autoSummarize をデフォルト値 (false) のままにしてください。
要約品質を高めるためのヒント
Section titled “要約品質を高めるためのヒント”- 最初に
dryRunでプレビューする —summarizeMemoriesOlderThan(..., true)は、 候補リストと合計トークン数を返すため、元のメモリを削除する前に 何が統合されるかを確認できます。 - メモリのコーパスが大きい場合は、トラフィックの少ない時間帯に要約を実行する — LLM 呼び出しが最も時間のかかる部分です
# Cron 形式: 毎日午前 3 時に要約0 3 * * * curl -X POST http://localhost:20128/api/memory/summarize \ -H "Authorization: Bearer $OMNIROUTE_KEY"MemoryBackend プロバイダーパターン
Section titled “MemoryBackend プロバイダーパターン”信頼できる情報源:
src/lib/memory/backend.ts,src/lib/memory/genericBackend.ts,src/lib/memory/manager.tsテスト:src/lib/memory/__tests__/generic-backend.test.ts
MemoryBackend プロバイダーパターンは、既存のメモリエンジン上にプラグイン可能なバックエンド抽象化レイヤーを導入します。単一のストレージ実装に依存する代わりに、メモリシステムは設定可能なプライマリ/フォールバックルーティングを備えた複数のバックエンド (SQLite、Obsidian、Notion、カスタム HTTP バックエンド) をサポートするようになりました。
アーキテクチャ
Section titled “アーキテクチャ”┌──────────────────────────────────────────────────────────┐│ API ルート ││ (src/app/api/memory/route.ts) │└──────────────────────┬───────────────────────────────────┘ │┌──────────────────────▼───────────────────────────────────┐│ MemoryManager ││ シングルトンオーケストレーター (manager.ts) ││ ││ プライマリ ──► バックエンド A (例: SQLite) ││ フォールバック ─► バックエンド B (例: Obsidian) ││ バックエンド C (例: GenericBackend ││ 経由の Notion) │└──────────────────────┬───────────────────────────────────┘ │ ┌──────────────┼──────────────┐ ▼ ▼ ▼┌────────────┐ ┌────────────┐ ┌──────────────────┐│ SQLite │ │ Obsidian │ │ GenericMemory ││ バックエンド│ │ バックエンド│ │ バックエンド ││ │ │ │ │ (HTTP) │└────────────┘ └────────────┘ └──────────────────┘コアインターフェース (backend.ts)
Section titled “コアインターフェース (backend.ts)”すべてのバックエンドは、MemoryBackend インターフェースを実装する必要があります。
interface MemoryBackend { readonly id: string; readonly displayName: string;
// CRUD create(input: CreateMemoryInput): Promise<Memory>; get(id: string): Promise<Memory | null>; update(id: string, updates: Partial<...>): Promise<boolean>; delete(id: string): Promise<boolean>; list(filter: MemoryFilter): Promise<{ data: Memory[]; total: number; byType: Record<string, number> }>;
// 検索 search(config: SearchConfig): Promise<Memory[]>;
// ヘルスチェック health(): Promise<HealthCheckResult>;
// ライフサイクル (任意) initialize?(): Promise<void>; shutdown?(): Promise<void>;}MemoryManager (manager.ts)
Section titled “MemoryManager (manager.ts)”次の処理を行うシングルトンオーケストレーターです。
register(backend)を介してバックエンドを登録 — 起動時にindex.tsから呼び出されますconfigure(primary, fallbacks)を介してプライマリ + フォールバックを設定- CRUD/検索をプライマリへルーティングし、失敗時にはフォールバックチェーンを使用
- すべてのバックエンドを定期的にヘルスチェック
フォールバックの動作:
| 操作 | プライマリ | フォールバック |
|---|---|---|
create |
✅ プライマリのみ | ❌ |
get |
✅ 最初にプライマリを試行 | ✅ null の場合にフォールバック |
update |
✅ プライマリのみ | ✅ 非同期で同期処理 |
delete |
✅ プライマリのみ | ✅ 非同期で同期処理 |
list |
✅ プライマリのみ | ❌ |
search |
✅ 最初にプライマリを試行 | ✅ エラー時にフォールバック |
GenericMemoryBackend (genericBackend.ts)
Section titled “GenericMemoryBackend (genericBackend.ts)”任意の REST API を MemoryBackend に適合させる汎用 HTTP コネクターです。次の用途に役立ちます。
- Notion — Notion API 経由で接続
- Obsidian — Obsidian Local REST API 経由で接続
- カスタムバックエンド — RESTful なメモリ API を公開する任意のサービス
設定:
interface GenericBackendConfig { baseUrl: string; // バックエンド API のベース URL apiKey?: string; // 認証用 Bearer トークン headers?: Record<string, string>; // カスタム HTTP ヘッダー timeout?: number; // リクエストタイムアウト(デフォルト: 30000ms) backendType?: string; // ログ記録用
// エンドポイントのオーバーライド(デフォルトでは REST の規約を使用) endpoints?: { search?: string; // デフォルト: "/memories/search" create?: string; // デフォルト: "/memories" list?: string; // デフォルト: "/memories" get?: string; // デフォルト: "/memories/{id}" update?: string; // デフォルト: "/memories/{id}" delete?: string; // デフォルト: "/memories/{id}" health?: string; // デフォルト: "/health" };
// クエリパラメーター名のマッピング queryParams?: { query?/apiKeyId?/limit?/offset?/strategy?/maxTokens?/type?/sessionId?/orderBy?/orderDir?/options? };
// パスパラメーター名のマッピング pathParams?: { id?/memoryId? };}既知のバックエンドは KNOWN_BACKENDS に事前設定されています:
createKnownBackend("obsidian"); // → localhost:27123 を参照する GenericMemoryBackendcreateKnownBackend("notion"); // → api.notion.com/v1 を参照する GenericMemoryBackend組み込みバックエンド
Section titled “組み込みバックエンド”SQLiteBackend (sqliteBackend.ts)
Section titled “SQLiteBackend (sqliteBackend.ts)”デフォルトのプライマリバックエンドです。src/lib/memory/store.ts を使用して、既存の SQLite ベースのメモリストアをラップします。起動時に自動的に登録されます。
import { sqliteBackend } from "./sqliteBackend";memoryManager.register(sqliteBackend);ObsidianBackend (obsidianBackend.ts)
Section titled “ObsidianBackend (obsidianBackend.ts)”既存の Obsidian 統合(src/lib/memory/obsidianBackend.ts)をラップします。Obsidian Local REST API を介して Obsidian Vault に接続します。
メモリバックエンドの設定はアプリの設定テーブルに保存され、src/lib/memory/settings.ts を介して管理されます:
| 設定 | 環境変数/設定キー | デフォルト | 説明 |
|---|---|---|---|
| プライマリバックエンド | memoryPrimaryBackend |
"sqlite" |
プライマリバックエンドの ID |
| フォールバックバックエンド | memoryFallbackBackends |
[] |
順序付けされたフォールバックバックエンドの ID |
| バックエンド設定 | memoryBackendConfigs |
{} |
バックエンドごとの設定オーバーライド |
設定は normalizeMemorySettings() によって正規化され、getMemorySettings() でキャッシュされます。
初期化フロー
Section titled “初期化フロー”アプリのブートストラップ → index.ts のインポート(副作用): SQLiteBackend を登録 → アプリのライフサイクルから initMemoryBackends() を呼び出し: 1. 設定を読み込む(getMemorySettings) 2. プライマリとフォールバックを設定 3. すべてのバックエンドを初期化(ヘルスチェック) 4. リクエストを処理する準備が完了新しいバックエンドの追加
Section titled “新しいバックエンドの追加”src/lib/memory/<name>Backend.tsでMemoryBackendを実装src/lib/memory/index.tsから エクスポート- 起動時に
memoryManager.register(yourBackend)で 登録 - 設定を介して 構成:
memoryPrimaryBackendをバックエンド ID に設定 src/lib/memory/__tests__/generic-backend.test.tsを参考に テスト
例: Brain バックエンド
Section titled “例: Brain バックエンド”import { createGenericMemoryBackend } from "./genericBackend";
const brainBackend = createGenericMemoryBackend("brain", "BK-Brain", { baseUrl: process.env.BRAIN_API_URL || "http://localhost:9099", apiKey: process.env.BRAIN_API_KEY, endpoints: { search: "/api/memory/search", create: "/api/memory", health: "/api/health", },});
memoryManager.register(brainBackend);ユニットテスト
Section titled “ユニットテスト”npx vitest run src/lib/memory/__tests__/generic-backend.test.ts --reporter=verbose期待される出力: 以下を網羅する 35 件のテストがすべて成功
- コンストラクター(2)
- ヘルスチェック(4)— 成功、500 エラー、ネットワークエラー、レイテンシー
- 初期化(2)— 成功、失敗
- 作成(2)— デフォルトエンドポイント、カスタムエンドポイント
- 取得(4)— 成功、404 → null、404 以外では例外をスロー、カスタムパスパラメーター
- 更新(2)— 成功、404 → false
- 削除(2)— 成功、404 → false
- 一覧(2)— クエリパラメーター、カスタムパラメーター名
- 検索(3)— クエリパラメーター、カスタムエンドポイント、オプションのシリアル化
- 認証ヘッダー(2)— Bearer トークン、カスタムヘッダー
- ファクトリー(1)
npm run typecheck:core期待される結果: エラー 0 件。
HagiCode
HagiCode は構造化ワークフロー、マルチエージェント実行、Hero Dungeon ビューを備えたエージェント型コーディングワークスペースです。
よりスマートで速く、楽しいエージェント型ワークフローで、使いやすいソフトウェアを形にします。

- Smart構造化ワークフローは意図をアイデアから変更のリリースまで実行可能な道筋にします。
- Efficientマルチエージェントのワークフローで調査、実装、レビューを並行して進めます。
- FunHero Dungeon により長時間のコーディングを視覚的で協力的な体験にします。