メモリシステムの設計

This commit is contained in:
Keisuke Hirata 2026-04-21 19:23:07 +09:00
parent 47da4a03cb
commit ca5a3d1152
4 changed files with 178 additions and 35 deletions

View File

@ -1,4 +1,5 @@
- [ ] テスト設計 → [tickets/test-design.md](tickets/test-design.md)
- [ ] Agent Skills サポート → [tickets/agent-skills.md](tickets/agent-skills.md)
- [ ] ツール設計
- [ ] Bash ツール (Permission 層と統合) → [tickets/bash-tool.md](tickets/bash-tool.md)
- [ ] パーミッション: パターンベースのツール実行制御 → [tickets/permission-extension-point.md](tickets/permission-extension-point.md)

View File

@ -6,39 +6,66 @@ INSOMNIA がユーザーのプロジェクトに対して提供するメモリ
リサーチは `docs/ref/memory-systems.md`。前提として、**レポジトリがファイルシステム上にある**ケースで設計する(越境・バックエンド抽象は Scope 外)。
Workflow`/workflow-name` で呼び出される制約付き作業フロー)は別 plan に切り出した。`docs/plan/workflow.md` 参照。
Workflow`/<slug>` で呼び出される制約付き作業フロー)は別 plan に切り出した。`docs/plan/workflow.md` 参照。
## 決定事項
### 記録対象の 5 種
| 種別 | パス | 備考 |
| ---------------- | ---------------------------- | -------------------------------------------------------------------------------------------- |
| Always-on サマリ | `memory/summary.md` | 1-5k tokens 目安 |
| Lessons | `memory/lessons/<id>.md` | |
| Decisions | `memory/decisions/<id>.md` | `status: open \| resolved \| superseded` で未決議論も保持、supersede は `superseded_by` 参照 |
| Requests | `memory/requests/<id>.md` | ユーザー submit の構造化要約 |
| Knowledge | `memory/knowledge/<name>.md` | `#name` で注入。ノウハウ / 用語 / 運用方針 / ルール / 事実など型を設けず Markdown 自由記述 |
| 種別 | パス | 備考 |
| ---------------- | ------------------------------ | ----------------------------------------------------------------------------------------------- |
| Always-on サマリ | `memory/summary.md` | 1-5k tokens 目安 |
| Lessons | `memory/lessons/<slug>.md` | |
| Decisions | `memory/decisions/<slug>.md` | `status: open \| resolved \| replaced` で未決議論も保持、置き換え時は `replaced_by: <slug>` |
| Requests | `memory/requests/<slug>.md` | ユーザー submit の構造化要約 |
| Knowledge | `memory/knowledge/<slug>.md` | `#slug` で注入。ノウハウ / 用語 / 運用方針 / ルール / 事実など型を設けず Markdown 自由記述 |
- `<id>` は UUIDv7時系列順に並ぶ、`<name>` は slug小文字英数とハイフン
- `<slug>` は kebab-case内容を要約した短い識別子。**ファイル名そのものが ID**、frontmatter に別途 `id` field は持たない
- **1 件 1 ファイル**。append-only な複数エントリログファイルは作らない
- Phase 1 の中間ストアとして `memory/_staging/<id>.json` を使うPhase 2 完了で cleanup、後述
- **同一 slug の衝突は新規作成禁止**。既存があれば updateLinter で検証、sub-Worker は read→edit に切り替え)
- 同主題の content 進化 = 上書き update + git log で履歴追跡
- 別主題が古い主題を置き換える場合のみ、別 slug で新規作成し古い方に `status: replaced` + `replaced_by: <新 slug>` を記録
- Phase 1 の中間ストアとして `memory/_staging/<id>.json` を使う短命、P2 完了で cleanup。ここは衝突回避と順序のため UUIDv7 可)
- Raw session log は既存 `llm-worker-persistence` で保持する。memory 対象外、参照経路のみ
### Knowledge の呼び出し制御
agentskills.io の `SKILL.md` 形式は採用しない。Knowledge は `#knowledge-name` でユーザー / LLM から注入参照する。名前空間は**フラット**、name は slug。
agentskills.io の `SKILL.md` 形式は採用しない。Knowledge は `#<slug>` でユーザー / LLM から注入参照する。名前空間は**フラット**、slug は kebab-case小文字英数とハイフン
| フラグ | 意味 | デフォルト |
| ---------------- | ------------------------------------------------------- | ---------- |
| `auto_invoke` | description が LLM context に載り、LLM が自発的に呼べる | **OFF** |
| `user_invocable` | ユーザーが `#` で明示的に呼べる | **ON** |
| `user_invocable` | ユーザーが `#<slug>` で明示的に呼べる | **ON** |
`auto_invoke` の ON 化は人間の判断、または consolidation が頻繁に `user_invoke` されているものを検出して offer する。自律的に新規エンティティを生成はしない。Workflow も同じフラグ仕様(`workflow.md` 参照)。
### 書き込み先の制約
### 書き込み経路と Linter
`memory/workflow/` への自動書き込みは、consolidation Worker の structured output schema に `workflow` カテゴリを含めないことで担保する(後述、および `workflow.md`。OS ファイル権限や Scope 上の特別な保護機構は設けず、`memory/` 配下を write allow する以上の細工はしない。衝突は git で解決する前提。
人間も consolidation sub-Worker も**同じ CRUD toolfile read / write / edit**で `memory/*` を触る。書き込み時の制約は Linter で検証し、違反時は post-write Hook が turn を戻して sub-Worker に自己修正させるN 回失敗で abort
Linter ルールは 2 系統:
**静的 error**post-write Hook で turn 戻し、sub-Worker が自己修正):
- frontmatter 必須 field
- Lessons / Decisions / Requests: `created_at`, `updated_at`, `sources`
- Knowledge: `description`, `auto_invoke`, `user_invocable`, `last_sources`, `created_at`, `updated_at`
- Summary: `updated_at`optional: `last_rewritten_from_range`
- `memory/workflow/` への書き込み禁止sub-Worker context のみ、人間編集は除外)
- 同 slug での新規作成禁止(既存があれば update に切り替えるサイン)
- `#<slug>` 参照が実在ファイルを指す
- `replaced_by: <slug>` が実在 record を指す
- Decisions の `status` は enum `open | resolved | replaced`
- `auto_invoke: true` の record は description 文字数上限agentskills 準拠 1024 chars
- 種別ごとの char 硬上限(具体値は運用で調整、設定ファイルで tune
**膨張抑制 Warn**error ではなく改善ヒント、sub-Worker は task 余力があれば対応):
- 重要度 × char の天秤: 低重要度で char 使用過多な record に「圧縮余地あり」Warn
- sources 累積: 配列が閾値超過で「直近 N 件に絞り過去は git log に委ねる候補」Warn
- 類似 slug 乱立: 近似 slug の集合に「merge 候補」Warn
Workflow 保護は専用 tool schema のトリックではなく Linter ルールで担保するため、人間が規則を読んで理解できる。OS ファイル権限や Scope 上の特別な保護機構は設けず、`memory/` 配下を write allow する以上の細工はしない。衝突は git で解決する前提。
### 自動化consolidationメカニズム
@ -56,6 +83,7 @@ agentskills.io の `SKILL.md` 形式は採用しない。Knowledge は `#knowled
- `requests`: ユーザー submit の構造化要約(意図 / 対象 / 要約)
- **抽出対象がなければ空配列を返してよい**Hermes の "Nothing to save." と同系。頻繁発火を許容する前提)
- **書き込み先**: `memory/_staging/<id>.json`
- LLM 出力(活動ログ JSONは pod 側ラッパーが `source: { session_id, range: [start_entry, end_entry] }` を**機械付与**して wrap。LLM には source を推論させない
- **モデル**: `memory.extract_model`。軽量だが文脈理解できる中堅クラスHaiku / 4o-mini / Flash 相当)を想定
#### Phase 2: 永続化への統合
@ -63,13 +91,12 @@ agentskills.io の `SKILL.md` 形式は採用しない。Knowledge は `#knowled
- **Trigger**: staging の累積ファイル数 or bytes が閾値超過、または compact 発火時(必ず flush
- **実行主体**: Phase 1 を終えた pod が `memory/_staging/.lock` の advisory file lock を `LOCK_EX | LOCK_NB` で取得試行。取れたら consolidation Worker を spawn。取れなければ skip他 pod が処理中)
- **Lock**: POSIX `flock(2)` / `fcntl` ベース。プロセスが生きている限り保持、落ちれば OS が自動解放。stale 検出・heartbeat 不要。reasoning 処理が数分〜十数分かかる前提
- **入力**: staging 全件(活動ログ)+ 既存 `memory/*`summary / lessons / decisions / requests / knowledge
- **処理**: sub-Worker に memory read/write tool を渡し、agentic に以下を自律判断:
- 新規 lessons / decisions / requests を 1 件 1 ファイルで追加
- 活動ログから派生する Knowledge用語定義 / 運用方針 / ルール / 事実 / ノウハウ)を新規作成 or 既存 patch
- **入力**: staging 全件(活動ログ + `source`+ 既存 `memory/*`summary / lessons / decisions / requests / knowledge
- **処理**: sub-Worker に**汎用 CRUD toolfile read / write / edit+ post-write Linter Hook** を渡し、agentic に以下を自律判断:
- 新規 lessons / decisions / requests を 1 件 1 ファイルで追加。`sources` は staging の `source` をコピーLLM 推論ではない)
- 活動ログから派生する Knowledge用語定義 / 運用方針 / ルール / 事実 / ノウハウ)を新規作成 or 既存 patch。`last_sources` を更新
- summary を必要に応じて rewrite
- **書き込み先**: summary / lessons / decisions / requests / knowledge の 5 種
- **Workflow への書き込み禁止**: Phase 2 の write tool schema に workflow を含めないことで構造的に担保(`workflow.md` 参照)
- **書き込み先**: `memory/*` 配下。Workflow 禁止は Linter で担保(`workflow.md` 参照)
- **完了処理**: staging cleanup + lock release。Phase 2 完了時に staging に新着があれば次を発火Coalesce
- **モデル**: `memory.consolidation_model`。reasoning 系
@ -77,8 +104,10 @@ agentskills.io の `SKILL.md` 形式は採用しない。Knowledge は `#knowled
`memory/` 配下は人間も git 経由で編集する。Phase 2 prompt で以下を明示:
- 既存内容を尊重し、**差分追加を優先**する。全文 rewrite は `summary.md` のみ、かつ人間編集と整合する範囲で
- 削除は supersede 記録(`status: superseded` + `superseded_by: <id>`)で表現、直接削除しない
- **rewrite は許可**。既存内容と新規情報を統合・再構成して情報密度を上げることを優先。単純 append追記で増やすだけは避ける
- rewrite 時は**情報損失を最小化**する: 既存の主張・根拠・sources を保持。表現を整理・短縮しても、含まれている要素は落とさない
- 削除は置き換え記録(`status: replaced` + `replaced_by: <slug>`)で表現、直接削除しない
- 人間編集は git diff で顕在化する前提。整合しない rewrite は避け、衝突時は git で解決
#### Offer 経路
@ -91,13 +120,29 @@ consolidation は自律生成しない。以下は Client に `Event::Notificati
基本分離memory は独立トリガー、compact は `input_tokens` 既存閾値のまま)。ただし **compact 発火時は Phase 2 を必ず同時 flush**compact で失われる raw を漏らさないため)。
### GC定期再評価
Phase 2 とは別経路で memory を再評価し、drop / merge / split / `replaced` chain の整理を行う。Shann³ llm-wiki の lint 相当。Phase 2 は rewrite 許可で情報統合寄りの働きをするが、それでも残る以下の課題の出口として機能する:
- 重要度の低い record が累積する
- 類似 slug が乱立するLinter Warn で検出したものをまとめて処理)
- `replaced` が溜まり続けて grep / 注入時のノイズになる
- sources 累積
- 長期的に陳腐化した記録の drop
**詳細仕様は後で詰める**scope 内、未決定)。方針として LLM 駆動の定期ジョブで、Linter Warn 群 + 人間 offer 承認を併用する前提。
### ファイル形式
- frontmatter + Markdown 本文
- Knowledge の frontmatter: `name`, `description`, `auto_invoke`, `user_invocable`
- Lessons / Decisions / Requests の frontmatter: `id`, `created_at`, および種別固有フィールド
- Decisions は `status: open | resolved | superseded`、supersede 時は `superseded_by: <id>` を併記
- Phase 1 staging は `memory/_staging/<id>.json`JSON、1 件 1 ファイル、Phase 2 完了で削除)
- frontmatter + Markdown 本文。全 record 共通: `created_at`, `updated_at`
- ファイル名slugがそのまま識別子、frontmatter に `id` / `name` field は持たない
- source トレーサビリティsession log への逆引き、粒度は `session_id` + entry range:
- Lessons / Decisions / Requests: `sources: [{session_id, range: [start, end]}, ...]` 永続化update 時は追記累積)
- Knowledge: `last_sources: [{session_id, range}, ...]`(最新更新時のみ、過去履歴は git log で追う)
- Summary: optional `last_rewritten_from_range`(なしでも可)
- Knowledge 固有: `description`, `auto_invoke`, `user_invocable`
- Decisions 固有: `status: open | resolved | replaced`、置き換え時は `replaced_by: <slug>`
- Phase 1 staging: `memory/_staging/<id>.json`JSON、1 件 1 ファイル、Phase 2 完了で削除。短命なので UUIDv7 可。pod 側ラッパーが `source` を機械付与して LLM 出力と wrap
- Workflow の frontmatter は `workflow.md` 参照
## Scope 外
@ -112,7 +157,6 @@ consolidation は自律生成しない。以下は Client に `Event::Notificati
- Phase 2 を担う常駐 daemon 化 — オンデマンド + lock 方式で始める。必要性が出たら upgrade path として daemon 化
- Deterministic promotionOpenClaw 型 scoring + ゲート)— 初期は Phase 2 agent の LLM 判断に委ねる。運用実績で出力を評価してから、成熟カテゴリから scoring 導入
- Shallow request の自動除外判定 — 初期は Phase 1 prompt で「些細な質問は返さなくてよい」と指示する程度。精緻な filter は後
- Retention / GC — Phase 2 agent に直接削除禁止supersede のみ)を課す一方、明示クリーンアップ経路は未定義。別途 LLM 駆動 GC を用意する前提で、初期は無制限蓄積 + summary rewrite で凌ぐ
### 別 plan / ticket で扱う

View File

@ -2,15 +2,15 @@
## Context
Workflow は制約付きの強制的な作業フロー。`/workflow-name` で明示的に呼び出し、依存 Knowledge を context に inject してから実行する。Knowledge`#knowledge-name`)は `docs/plan/memory.md` 側で定義。
Workflow は制約付きの強制的な作業フロー。`/<slug>` で明示的に呼び出し、依存 Knowledge を context に inject してから実行する。Knowledge`#<slug>`)は `docs/plan/memory.md` 側で定義。
## 決定事項
### 呼び出しと依存
- 呼び出し: `/workflow-name`
- 名前空間はフラット、name は slug小文字英数とハイフン
- frontmatter `requires: [knowledge-name, ...]` で依存 Knowledge を name 参照
- 呼び出し: `/<slug>`
- 名前空間はフラット、slug は kebab-case(小文字英数とハイフン)
- frontmatter `requires: [knowledge-slug, ...]` で依存 Knowledge を slug 参照
- 実行時は依存 Knowledge 本文を context に inject してから Workflow 本文を実行
### 呼び出し制御フラグ
@ -18,15 +18,15 @@ Workflow は制約付きの強制的な作業フロー。`/workflow-name` で明
| フラグ | 意味 | デフォルト |
| ---------------- | ------------------------------------------------------- | ---------- |
| `auto_invoke` | description が LLM context に載り、LLM が自発的に呼べる | **OFF** |
| `user_invocable` | ユーザーが `/` で明示的に呼べる | **ON** |
| `user_invocable` | ユーザーが `/<slug>` で明示的に呼べる | **ON** |
`auto_invoke` の ON 化は人間の判断、または consolidation からの offer 経由のみ。同じ制御は Knowledge 側(`memory.md`)でも採用。
### 格納先とファイル形式
- `memory/workflow/<name>.md`
- `memory/workflow/<slug>.md`(ファイル名 = slug がそのまま識別子、`name` field は持たない)
- frontmatter + Markdown 本文
- frontmatter フィールド: `name`, `description`, `auto_invoke`, `user_invocable`, `requires`
- frontmatter フィールド: `description`, `auto_invoke`, `user_invocable`, `requires`
### 生成・更新ポリシー

98
tickets/agent-skills.md Normal file
View File

@ -0,0 +1,98 @@
# Agent Skills サポート
## 背景
[agentskills.io](https://agentskills.io/) で公開されているオープン標準 "Agent Skills" は、エージェントに手続き的知識・ドメイン能力をフォルダ単位で供給する形式。Anthropic 発で Claude Code / Cursor / OpenCode / Gemini CLI / Goose / OpenHands など主要な coding agent 群が採用しつつあり、ユーザーが既に書いた skill を insomnia に持ち込める/他ツールと共有できる価値が明確になっている。
`docs/ref/memory-systems.md` でも Hermes Agent の "Skill Library" が agentskills.io 互換として言及済みで、将来のセルフ生成メモリ経路 (procedural memory) の受け皿としてもこのフォーマットに合わせておく利がある。
### Skill の骨格 (仕様要旨)
```
skill-name/
├── SKILL.md # 必須: YAML frontmatter + Markdown 本体
├── scripts/ # 任意: 実行コード
├── references/ # 任意: 詳細ドキュメント
└── assets/ # 任意: テンプレート等の静的資産
```
SKILL.md frontmatter:
| フィールド | 必須 | 制約 |
|---|---|---|
| `name` | ○ | 1-64 chars, `[a-z0-9-]+`, 先頭末尾 `-` 不可, `--` 連続不可, **ディレクトリ名と一致** |
| `description` | ○ | 1-1024 chars, 非空 |
| `license` | | 自由文 |
| `compatibility` | | 1-500 chars |
| `metadata` | | 任意の key-value map |
| `allowed-tools` | | experimental (空白区切りのツールリスト) |
仕様の肝は **progressive disclosure**:
1. メタデータ (`name` + `description`) — セッション開始時に**全 skill 分**をプロンプトに載せる (~100 tokens/skill)
2. 指示本体 (SKILL.md body) — skill が必要になった時点で agent 側が読みに行く
3. リソース (`scripts/` / `references/` / `assets/`) — さらに on-demand
## 既存機構との関係
insomnia のシステムプロンプトは現状、`instruction` (テンプレ本体) + scope summary + AGENTS.md (trailing section) の 3 要素構成。Skill はこの並びに**任意個のオプショナル能力資産**として追加する位置取り。instruction / AGENTS.md を置き換えない。
配置は既存の prefix addressing と同じ軸:
- `$XDG_CONFIG_HOME/insomnia/skills/<name>/SKILL.md`
- `<project>/.insomnia/skills/<name>/SKILL.md`
- `$insomnia/skills/` (ビルトイン) は不要になるまで作らない
## 方針
### MVP スコープ
1. **Skill ディスカバリと検証**
- 上記 2 経路のサブディレクトリを走査して `SKILL.md` を読む
- frontmatter を仕様通り検証。name 不一致・必須欠落・制約違反は hard error (Pod 起動失敗)
- 未知フィールドは `tracing::warn!` して無視 (pod-factory と同方針)
- 重複 name は workspace 層優先で user 層を上書き + ユーザー向け notification 発行
2. **システムプロンプトへの注入 (progressive disclosure 準拠)**
- `SystemPromptContext``skills: Vec<SkillSummary>` を追加 (`name`, `description`, 絶対パス)
- ビルトイン `$insomnia/default.md` に skill 列挙ブロックを追加 (空なら一切出力しない)
- ユーザーテンプレートからも `{{ skills }}` で参照可能
- **本体は注入しない**。description の末尾に絶対パスを併記し、agent が Read ツールで取れるようにする
3. **本体への agent アクセス**
- skill ディレクトリを `Scope``permission = read` で自動 allow
- `scripts/` の実行は将来 Bash ツール + Permission 層で成立するまで Read 経由での閲覧に留める
### 範囲外
- `allowed-tools` フィールドの実効化 — `permission-extension-point.md` が Permission 層を整備するまで受信して無視
- skill 専用の実行機構 — `scripts/` は Bash / Read で自然に扱う
- skill の自動生成 (Hermes 風 Skill Library) — memory 系チケットで別途。本チケットで作る ingest 経路を再利用する側
- ビルトイン skill (`$insomnia/skills/`) — 必要になるまで追加しない
- skill 間依存の解決 — 独立単位、相互参照は本体の Markdown リンクで
## 完了条件
- `$user/skills/``$workspace/skills/` の両方から skill をロードし、frontmatter 違反は Pod 起動エラーになる
- ビルトインの `$insomnia/default.md` をレンダした結果、存在する skill の name + description + 絶対パスがシステムプロンプトに列挙される
- skill が 0 件のとき、既存の出力 (AGENTS.md / scope summary) が全く変わらない
- skill ディレクトリが scope の readable に含まれ、agent が Read ツールで `SKILL.md` 本体・`scripts/`・`references/` にアクセスできる
- 単体テストで frontmatter 検証の正常 / 異常系、progressive disclosure レンダリング、user/workspace 重複解決が verify される
## 実装順序
1. `manifest` または新設 `skill` クレートに `Skill` 構造体と `SkillDirectoryLoader` を置く。SKILL.md パースと検証のみでテスト完結
2. `pod::SystemPromptContext``skills` を生やし、`ensure_system_prompt_materialized` で loader を呼ぶ
3. `resources/prompts/default.md` に skills ブロックを追加、`Pod::from_manifest` で skill ディレクトリを scope readable に union
4. 重複 name 解決と notification 発行を乗せる
各ステップ終了時点でビルド通過・既存テスト合格を維持する。skill 0 件時の後方互換は Phase 2 で確認する。
## 参照
- 仕様本体: https://agentskills.io/specification
- 採用状況: https://agentskills.io/home
- Anthropic 公式サンプル: https://github.com/anthropics/skills
- 検証 CLI: https://github.com/agentskills/agentskills/tree/main/skills-ref (将来 `pod skills validate` 相当を作るなら参考)
- 類似機構: `crates/pod/src/prompt_loader.rs` (prefix addressing), `crates/pod/src/agents_md.rs` (cwd-relative ingest), `crates/pod/src/system_prompt.rs` (render pipeline)
- 後続接続先: `docs/ref/memory-systems.md` の Skill Library (自動生成 skill の保存先)