From 0c1276b730f30e38a71a3119894c0ef75d7b6b20 Mon Sep 17 00:00:00 2001 From: Hare Date: Wed, 22 Apr 2026 13:21:15 +0900 Subject: [PATCH] =?UTF-8?q?Memory=E3=82=B7=E3=82=B9=E3=83=86=E3=83=A0?= =?UTF-8?q?=E3=81=AE=E6=95=B4=E7=90=86=E3=83=BBPrompt=E3=82=AB=E3=82=BF?= =?UTF-8?q?=E3=83=AD=E3=82=B0=E3=83=81=E3=82=B1=E3=83=83=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO.md | 1 + docs/plan/memory-prompts.md | 84 ++++++++++++++++++++ docs/plan/memory.md | 95 +++++++++++++++++------ docs/ref/memory-systems.md | 123 +++++++++++++++++++++++++++-- tickets/pod-prompt-catalog.md | 140 ++++++++++++++++++++++++++++++++++ tickets/test-design.md | 7 +- 6 files changed, 417 insertions(+), 33 deletions(-) create mode 100644 docs/plan/memory-prompts.md create mode 100644 tickets/pod-prompt-catalog.md diff --git a/TODO.md b/TODO.md index 3378fd54..6f41409d 100644 --- a/TODO.md +++ b/TODO.md @@ -6,6 +6,7 @@ - [ ] LLM プロバイダ/モデルカタログ → [tickets/llm-provider-catalog.md](tickets/llm-provider-catalog.md) - [ ] Pod オーケストレーション - [ ] 動的 Scope 変更 → [tickets/dynamic-scope.md](tickets/dynamic-scope.md) + - [ ] Pod 内部プロンプトのカタログ化 → [tickets/pod-prompt-catalog.md](tickets/pod-prompt-catalog.md) - [ ] ネイティブ GUI クライアント MVP → [tickets/native-gui-mvp.md](tickets/native-gui-mvp.md) - [ ] TUI 拡充 - [ ] フルスクリーン化によるオーバーホール → [tickets/tui-fullscreen-overhaul.md](tickets/tui-fullscreen-overhaul.md) diff --git a/docs/plan/memory-prompts.md b/docs/plan/memory-prompts.md new file mode 100644 index 00000000..13e82a21 --- /dev/null +++ b/docs/plan/memory-prompts.md @@ -0,0 +1,84 @@ +# メモリ機構の prompt 要件 + +## Context + +`docs/plan/memory.md` で決めた memory の挙動を、実装時に prompt 要件へ落とすための補助ページ。ここでは「個別タスクをこなすベーシックな system / developer 指示」の上に追加で必要になる、memory 固有の指示だけをまとめる。 + +前提は **tool-based + agentic**。専用 schema で挙動を閉じ込めるのではなく、基本は sub-Worker に CRUD tool を渡し、prompt と post-write 検証で振る舞いを制約する。例外は Workflow で、`docs/plan/workflow.md` の通り自動書き込みはしない。 + +## 決定事項 + +### 共通原則 + +memory 関連 prompt は種別を問わず、最低限以下を共有する: + +- **source を推論しない**。`session_id` や entry range は wrapper / 呼び出し側が機械付与し、LLM が捏造しない +- **rewrite は許可**するが、情報損失は最小化する。既存の主張・根拠・参照を保ったまま整理・圧縮する +- **単純 append を優先しない**。既存 record に統合できるなら update を優先する +- **session 固有の進行状態を書かない**。長期参照価値のある内容だけを memory に残す +- **既存 docs と重複保存しない**。`AGENTS.md`、`docs/plan/*`、固定運用文書に既にある内容を再保存しない +- **空出力を許容**する。保存価値が無ければ「何も追加しない」を正当な結果として扱う + +### Phase 1: 活動抽出 prompt + +Phase 1 は「派生物を作る」段階ではなく、「起きたことを抽出する」段階として縛る: + +- 対象は `decisions`、`discussions`、`attempts`、`requests` の候補に限る +- Knowledge 化、summary rewrite、slug 命名、`auto_invoke` 判断は行わない +- 一回限りの雑談、浅い質問、長期参照価値の薄い進行ログは返さなくてよい +- 出力は schema 準拠の構造化データのみ。自由文の補足説明で schema 外情報を足さない +- 対象が無ければ空配列を返す + +### Phase 2: 統合 prompt + +Phase 2 は既存 `memory/*` と staging を見て、追加・更新・統合を agentic に判断する: + +- 入力には staging の活動ログ、既存 `memory/*`、Knowledge 化候補レポートを含める +- 新規作成より update を優先し、既存 slug に自然に統合できる場合は新規 file を増やさない +- Decisions / Requests は staging の `source` をそのまま使い、LLM が `sources` を組み立てない +- summary は必要なときだけ rewrite し、常に 1-5k tokens 目安に圧縮する +- 削除は直接行わず、Decision の置き換えは `status: replaced` と `replaced_by` で表現する +- 人間編集との不整合が見える rewrite は避け、衝突しそうなら保守的に統合する + +### Phase 2: Knowledge 書き込み prompt + +Knowledge の新規作成 / 更新では、Phase 2 全体の原則に加えて以下を明示する: + +- 採択ラインは「このプロジェクト / ユーザーに対して再度参照価値のある事実・ルール・ノウハウ」に限る +- 一回限りの判断や議論は Decisions に留め、繰り返し参照される抽象化だけを Knowledge に上げる +- 新規作成は Knowledge 化候補レポートに載った source から派生する場合に限る +- 既存 Knowledge の slug / description 一覧を見て、適合先があるなら必ず update を優先する +- 新規 slug は「既存に適合先が無い」と説明できるときだけ作る +- `last_sources` は入力で与えられた source を使い、推論で補完しない +- `description` は「何の知識か / いつ使うか」が短く分かる文にする +- `auto_invoke` ON/OFF は頻度・常駐コストの判断材料を踏まえて慎重に扱い、初期値は OFF とする +- `#` 参照を書く場合は、実在 record への参照だけを使う +- `AGENTS.md` や `docs/` に既に固定化されたルールの写しは作らない +- 保存価値が無ければ Knowledge を何も追加しない + +### 監査 LLM prompt + +監査 LLM は「よく書けているか」ではなく、「壊していないか」を見る: + +- 入力には write 前後 diff、対象 record の直前内容、今回の source / staging 抜粋、採択基準を渡す +- rewrite / 圧縮で主張、根拠、参照が失われていないかを確認する +- 新規追加が source や活動ログに裏打ちされているかを確認する +- session 固有の進行状態や一時的事情が混入していないかを確認する +- `description` が本文のスコープと一致しているかを確認する +- `auto_invoke` の設定が本文の重要度や再利用性と不整合でないかを確認する +- 問題がある場合は `pass | fail` に加えて違反カテゴリと具体箇所を返し、Hook が差し戻せる形にする + +### GC prompt + +GC は Phase 2 より攻撃的に整理してよいが、可逆性と説明可能性を保つ: + +- 入力には GC 対象 record 群に加えて、Linter Warn、使用頻度メトリクス、`replaced` chain、sources 過多情報を含める +- 明示 invoke 保護閾値を超える record は drop / 大幅圧縮の対象外とする +- `similar-slug`、`sources-overflow`、`replaced` 滞留、stale record を優先的に処理する +- merge / split / trim / drop の理由を diff から読める形で残す +- 直接削除してよいが、git で可逆である前提に甘えすぎず、誤判定しやすいものは merge / trim を優先する + +## 関連 + +- `docs/plan/memory.md`: memory 全体方針 +- `docs/plan/workflow.md`: workflow を自動書き込みの例外にしている理由 diff --git a/docs/plan/memory.md b/docs/plan/memory.md index 98f83693..734ba220 100644 --- a/docs/plan/memory.md +++ b/docs/plan/memory.md @@ -6,19 +6,20 @@ INSOMNIA がユーザーのプロジェクトに対して提供するメモリ リサーチは `docs/ref/memory-systems.md`。前提として、**レポジトリがファイルシステム上にある**ケースで設計する(越境・バックエンド抽象は Scope 外)。 +prompt 要件の整理は `docs/plan/memory-prompts.md` に切り出した。 + Workflow(`/` で呼び出される制約付き作業フロー)は別 plan に切り出した。`docs/plan/workflow.md` 参照。 ## 決定事項 -### 記録対象の 5 種 +### 記録対象の 4 種 -| 種別 | パス | 備考 | -| ---------------- | ------------------------------ | ----------------------------------------------------------------------------------------------- | -| Always-on サマリ | `memory/summary.md` | 1-5k tokens 目安 | -| Lessons | `memory/lessons/.md` | | -| Decisions | `memory/decisions/.md` | `status: open \| resolved \| replaced` で未決議論も保持、置き換え時は `replaced_by: ` | -| Requests | `memory/requests/.md` | ユーザー submit の構造化要約 | -| Knowledge | `memory/knowledge/.md` | `#slug` で注入。ノウハウ / 用語 / 運用方針 / ルール / 事実など型を設けず Markdown 自由記述 | +| 種別 | パス | 備考 | +| ---------------- | ---------------------------- | ------------------------------------------------------------------------------------------- | +| Always-on サマリ | `memory/summary.md` | 1-5k tokens 目安 | +| Decisions | `memory/decisions/.md` | `status: open \| resolved \| replaced` で未決議論も保持、置き換え時は `replaced_by: ` | +| Requests | `memory/requests/.md` | ユーザー submit の構造化要約 | +| Knowledge | `memory/knowledge/.md` | `#slug` で注入。ノウハウ / 用語 / 運用方針 / ルール / 事実など型を設けず Markdown 自由記述 | - `` は kebab-case(内容を要約した短い識別子)。**ファイル名そのものが ID**、frontmatter に別途 `id` field は持たない - **1 件 1 ファイル**。append-only な複数エントリログファイルは作らない @@ -37,18 +38,27 @@ agentskills.io の `SKILL.md` 形式は採用しない。Knowledge は `#` | `auto_invoke` | description が LLM context に載り、LLM が自発的に呼べる | **OFF** | | `user_invocable` | ユーザーが `#` で明示的に呼べる | **ON** | -`auto_invoke` の ON 化は人間の判断、または consolidation が頻繁に `user_invoke` されているものを検出して offer する。自律的に新規エンティティを生成はしない。Workflow も同じフラグ仕様(`workflow.md` 参照)。 +Knowledge は Phase 2 が自律的に新規作成 / 更新 / フラグ切替を行う前提。毎回の人間承認 gate は設けない(実効性が低い)。保護は 3 段で担保: + +- **採択 gate**: Knowledge 新規作成は使用頻度メトリクスの Knowledge 化候補レポート(後述)に載った source から派生する場合に限る。閾値未満のうちは decisions / requests に留める +- **Linter + 監査 LLM**: 構造違反と意味破壊を watch(詳細は後述) +- **OS ファイル権限**: 人間が書き換えさせたくない record は `-r--` にしてロック。Phase 2 / GC の write は OS レベルで弾かれる + +Workflow も同じフラグ仕様(`workflow.md` 参照)。per-record 保護フラグを提供する拡張は将来検討、初期は OS 権限で足りる。 ### 書き込み経路と Linter -人間も consolidation sub-Worker も**同じ CRUD tool(file read / write / edit)**で `memory/*` を触る。書き込み時の制約は Linter で検証し、違反時は post-write Hook が turn を戻して sub-Worker に自己修正させる(N 回失敗で abort)。 +人間も consolidation sub-Worker も**同じ CRUD tool(file read / write / edit)**で `memory/*` を触る。書き込み時の制約は 2 層で検証し、違反時は post-write Hook が turn を戻して sub-Worker に自己修正させる(N 回失敗で abort): + +1. **Linter(静的)**: frontmatter / slug / 参照整合などの機械的ルール +2. **監査 LLM(意味的)**: rewrite が元の情報を壊していないかを別 prompt で check。特に Knowledge の意味損壊を watch する主経路 Linter ルールは 2 系統: **静的 error**(post-write Hook で turn 戻し、sub-Worker が自己修正): - frontmatter 必須 field - - Lessons / Decisions / Requests: `created_at`, `updated_at`, `sources` + - 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 のみ、人間編集は除外) @@ -75,7 +85,7 @@ Workflow 保護は専用 tool schema のトリックではなく Linter ルー - **Trigger**: activity tokens の累積閾値。tool call カウントは不採用(ツールカスタマイズ非依存・大小重みづけのため) - **実行主体**: 既存 compact と同じ Worker spawn 機構を再利用。Pod は立てない -- **入力**: 前回 Phase 1 以降の session log 範囲 +- **入力**: 前回 Phase 1 以降の session log 範囲。処理済み境界の pointer は session 側に保持(寿命を session と揃える) - **出力**: JSON schema で**活動ログ**の候補配列を返す。Knowledge 等の派生物は Phase 2 が活動ログから導出するので、Phase 1 では純粋な「起きたこと」に絞る - `decisions`: 判断したこと(選択肢 + 選んだ + 根拠) - `discussions`: 議論したこと(トピック + 論点) @@ -89,17 +99,26 @@ Workflow 保護は専用 tool schema のトリックではなく Linter ルー #### Phase 2: 永続化への統合 - **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 全件(活動ログ + `source`)+ 既存 `memory/*`(summary / lessons / decisions / requests / knowledge) +- **実行主体**: Phase 1 を終えた pod が consolidation Worker を spawn。並走防止は staging 配下の進行状況ファイル(後述)で担保 +- **入力**: 起動時スナップショットで確定した consumed ID list 分の staging エントリ(活動ログ + `source`)+ 既存 `memory/*`(summary / decisions / requests / knowledge)+ **Knowledge 化候補レポート**(後述の使用頻度メトリクスから機械集計、閾値超過の source 一覧) - **処理**: sub-Worker に**汎用 CRUD tool(file read / write / edit)+ post-write Linter Hook** を渡し、agentic に以下を自律判断: - - 新規 lessons / decisions / requests を 1 件 1 ファイルで追加。`sources` は staging の `source` をコピー(LLM 推論ではない) - - 活動ログから派生する Knowledge(用語定義 / 運用方針 / ルール / 事実 / ノウハウ)を新規作成 or 既存 patch。`last_sources` を更新 + - 新規 decisions / requests を 1 件 1 ファイルで追加。`sources` は staging の `source` をコピー(LLM 推論ではない) + - 活動ログから派生する Knowledge(用語定義 / 運用方針 / ルール / 事実 / ノウハウ)を新規作成 or 既存 patch。**新規作成は候補レポート掲載の source から派生する場合に限る**。`last_sources` を更新 - summary を必要に応じて rewrite - **書き込み先**: `memory/*` 配下。Workflow 禁止は Linter で担保(`workflow.md` 参照) -- **完了処理**: staging cleanup + lock release。Phase 2 完了時に staging に新着があれば次を発火(Coalesce) +- **完了処理**: consumed ID list の staging のみ cleanup(実行中に Phase 1 が追加した分は残す)。Phase 2 完了時に staging に新着があれば次を発火(Coalesce) - **モデル**: `memory.consolidation_model`。reasoning 系 +##### 並走防止 + +- 場所: staging 配下に 1 ファイル(名前・形式は未定) +- 中身: 動作中の Pod 識別子 + **consumed ID list**(この Phase 2 run が起動時スナップショットで確定した staging エントリ ID の列) +- 占有ルール: そのファイルが存在し、示された Pod が動作している間、そのプロセスが排他占有 +- 実行中に Phase 1 が staging に追加したエントリは触らず、次回 Phase 2(Coalesce)に委ねる +- cleanup は consumed ID list のエントリのみ削除、追加分は残す +- クラッシュ時は consumed ID list から処理途中を特定できる。重複作成は同一 slug update に自然収束 +- 占有の実現方法(pid 存在確認 / flock / 他)は未定 + #### Phase 2 agent への原則 `memory/` 配下は人間も git 経由で編集する。Phase 2 prompt で以下を明示: @@ -111,9 +130,8 @@ Workflow 保護は専用 tool schema のトリックではなく Linter ルー #### Offer 経路 -consolidation は自律生成しない。以下は Client に `Event::Notification` で提案し、人間承認で反映: +Memory record の書き込みは Phase 2 が自律判断し、Offer は設けない(Knowledge 含む)。人間承認経路が必要なのは以下: -- Knowledge の `auto_invoke` ON 化 - Workflow 関連の offer(新規作成 / 改善 / `auto_invoke` ON 化)は `workflow.md` 参照 #### Compact との関係 @@ -122,7 +140,7 @@ consolidation は自律生成しない。以下は Client に `Event::Notificati ### GC(定期再評価) -Phase 2 とは別経路で memory を再評価し、drop / merge / split / `replaced` chain の整理を行う。Shann³ llm-wiki の lint 相当。Phase 2 は rewrite 許可で情報統合寄りの働きをするが、それでも残る以下の課題の出口として機能する: +Phase 2 とは別経路で memory を再評価する定期ジョブ。Phase 2 は rewrite 許可で情報統合寄りの働きをするが、それでも残る以下の課題の出口として機能する: - 重要度の低い record が累積する - 類似 slug が乱立する(Linter Warn で検出したものをまとめて処理) @@ -130,14 +148,45 @@ Phase 2 とは別経路で memory を再評価し、drop / merge / split / `repl - sources 累積 - 長期的に陳腐化した記録の drop -**詳細仕様は後で詰める**(scope 内、未決定)。方針として LLM 駆動の定期ジョブで、Linter Warn 群 + 人間 offer 承認を併用する前提。 +他プロジェクトの GC 設計の横断比較は `docs/ref/memory-systems.md` §8。 + +#### 権限と操作粒度 + +GC Agent は **drop / merge / split を自律実行**(削除まで含む)。人間 offer はかけず、結果は git diff で検証する建て付け。operation 粒度は以下の両方: + +- **ファイル単位**: 丸ごと drop、複数ファイルの merge、1 ファイルの分割(split) +- **ファイル内の部分削除**: 本文の一部節・箇条を削除 or 圧縮。frontmatter の `sources` 古いエントリの trim も含む + +Phase 2 と同じ CRUD tool + Linter Hook を使うので、operation 粒度は自然にサポートされる(専用 API は用意しない)。 + +#### 使用頻度メトリクス + +時間単位は実時間を使わない(LLM スループット向上で陳腐化の意味が変わるため)、累積 input token で正規化する。 + +**観測経路**: `memory/*` への読み取りは専用の memory 検索ツール(既存 built-in の grep / read とは別に用意)経由に揃える。invoke 計測はツール内でフックし、`#` / `/` / 明示検索呼び出しを同一経路に集約する。 + +**カウント対象**: + +- **明示 invoke**: 検索ツール経由の読み取り / `#` / `/` を n回/Mtoken でスコア化 +- **auto_invoke 注入**: 注入は context 常駐コストで、「載っているだけ」か「使われた」かを統計上区別不能。明示 invoke の分子には含めず、**コスト側(注入した record に対する消費 input tokens)として別途記録**する。使われ率 ratio や ON/OFF 判断の材料として後段で使う +- ファイル token 数 + +**記録先**: staging とは独立。invoke event を UUID + Stats 形式で workspace 側に記録し、session データが失われても統計が残るようにする。具体 schema・フォーマットは未定。 + +**累積方式**(後集計アプローチ): 上記 invoke 記録に対して最大 10 回前の invoke から現在までの時系列窓でフィルタして集計する。 + +**Knowledge 化候補レポート**: Phase 2 が入力に受け取る、Knowledge 新規作成 gate 用の機械集計。対象は `memory/*` 配下の record(Phase 1 成果物である decisions / requests / 既存 knowledge)で、明示 invoke 頻度が閾値超過のものを列挙する。spike 除外のため、同一 session 内の連続参照は 1 count に丸め、複数 session での再参照を要件とする。閾値の具体値は運用で調整、設定ファイルで tune。 + +#### 判断ルール + +- 保護閾値: **明示 invoke** の `frequency >= 1.0 invokes/Mtoken` の record は drop / 大幅圧縮の対象外(初期値 1.0、workspace 設定でカスタマイズ可)。auto_invoke 注入による常駐は計数対象外(別指標として後段で参照) ### ファイル形式 - 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 時は追記累積) + - 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` diff --git a/docs/ref/memory-systems.md b/docs/ref/memory-systems.md index fab68476..1208dc94 100644 --- a/docs/ref/memory-systems.md +++ b/docs/ref/memory-systems.md @@ -33,6 +33,7 @@ Codex CLI に 2026-03 頃から追加された "Memories" 機能と、2026-04-15 - `memory_summary.md` を中心に「summaries / durable entries / recent inputs / supporting evidence」の Markdown を束ねる構成。 - Chronicle 派生メモリは `$CODEX_HOME/memories_extensions/chronicle/` に分離。 - スクリーンキャプチャ中間物は `$TMPDIR/chronicle/screen_recording/` に置かれ、running 中 6h で自動削除。サーバー側には保存しない(法的義務時を除く)。 +- **Extension resource retention は決定論的 7 日 hard-coded**(`EXTENSION_RESOURCE_RETENTION_DAYS = 7`, `memories/extensions.rs:11`)。filename embedded timestamp (`%Y-%m-%dT%H-%M-%S`) が cutoff より古い `.md` リソースを Phase 2 直前に物理削除し、削除リストを `removed_extension_resources` として consolidation prompt に渡す(agent はそれを見て派生メモリを抹消)。 ### 設定 @@ -93,7 +94,7 @@ Shann³ の構成は Karpathy が 2026-04 に公開した `llm-wiki` gist をそ - **query**: wiki を検索し citation 付きで答える。意義ある合成は `syntheses/` として保存。 - **lint**: 矛盾・stale claim・孤立ページ・参照抜け・データ欠落の定期点検。 -`SamurAIGPT/llm-wiki-agent` がこのパターンを具体化しており、ディレクトリ構造が示唆的: +`SamurAIGPT/llm-wiki-agent` がこのパターンを具体化しており、ディレクトリ構造が示唆的(なお `/wiki-lint` のプロダクション実装としては OpenClaw `memory-wiki` extension があり、§4 / §9 で触れる): ``` wiki/ @@ -168,6 +169,7 @@ Self-improving agent を名乗るフレーム。メモリ周りは **3 層 + ク - `skill_manage` tool: OpenAI function-calling schema。frontmatter YAML 検証、security scan、atomic write (temp + `os.replace()`) - `memory` tool: target = `memory | user`、action = `add | replace | remove`。fcntl/msvcrt で file lock、entry-based rewrite +- **char limit 超過時は `add` を hard reject**(`memory_tool.py:248-259`)。エラーメッセージに `Memory at {current}/{limit} chars. Adding this entry would exceed the limit. Replace or remove existing entries first.` と明記し、LLM 側に「自分で削減してから再追加」を強制する。GC は完全に LLM agentic(決定論的 eviction は無い)、人間 offer や scoring も無い、非常にシンプルな設計 - FTS5: `messages_fts` virtual table + INSERT/DELETE/UPDATE trigger で auto-sync、CJK は LIKE フォールバック ### 実装観点 @@ -197,13 +199,14 @@ Self-improving agent を名乗るフレーム。メモリ周りは **3 層 + ク - 4 区画: **Message Buffer**(直近)、**Core Memory**(in-context の編集可能 block: user prefs / persona)、**Recall Memory**(全履歴の検索)、**Archival Memory**(ベクトル or グラフ DB に外部化された宣言的知識)。 - OS メタファ: context window = RAM、外部ストア = Disk。エージェントが function call で階層間を移動させる。 - memory block = `{label, description, value, char_limit}`。 -- **sleep-time agent** が idle 中に非同期で block を書き換える。 -- context 限界近くで要約による eviction → 再帰要約で古いメッセージを累進圧縮。 +- **sleep-time agent** が idle 中に非同期で block を書き換える。主要 agent と **別 process で並列**に走り、`core_memory_replace` / `core_memory_append` / archival tool 群を呼んで **直接 memory block を書き換える**。主 agent のターン処理をブロックしないため、「mid-session で memory 品質を上げる」のが特徴。 +- context 限界近くで要約による eviction → 再帰要約で古いメッセージを累進圧縮。**eviction 判断は context window fill の決定論的しきい値**で発火するが、要約の内容生成は LLM。evicted message は recursive summary として memory block に残り、古いほど重みが相対的に下がる。 ### Cloudflare Agent Memory (2026-04 Agents Week) - 分類: **Fact / Event / Instruction / Task**(Task のみベクトル索引除外)。 - 基盤: Durable Objects(プロファイル毎の SQLite、FTS・supersession chain・tx write を担う)、Vectorize(セマンティック)、Workers AI(Llama 4 Scout: 構造化、Nemotron 3: 自然文合成)。 +- **Supersession chain は決定論的**(公式 blog 再確認): Fact / Instruction は classification pipeline で **normalized topic key** が振られ、同 key の新 memory が来ると旧 memory を **supersede(物理削除せず forward pointer で版チェーン化)**。LLM 判断は入らない。superseded 側の vector は並行して削除され、新 vector が upsert される。SHA-256 content-addressed ID で `INSERT OR IGNORE` による冪等 dedupe も併用。 - API: ```javascript const profile = await env.MEMORY.getProfile("my-project"); @@ -262,6 +265,17 @@ Agent Workspace(`~/.openclaw/workspace/`)構成: **insomnia にとって重要**: consolidation を LLM 依存から切り離せる見本。narrative は subagent が生成するが、promotion の判断は純機械(scoring)。insomnia の plan では Scope 外(Phase 2 は当面 agent 委任)だが、成熟したカテゴリから決定論的 promotion に差し替える upgrade path の参考になる。 +**GC 観点の追加詳細**(`extensions/memory-core/src/short-term-promotion.ts:1518-1652` 実装より): + +- `applyShortTermPromotions()` は **append 専用**。gate 通過候補の snippet を `MEMORY.md` 末尾に ` ` marker 付きで追記するだけで、既存 `MEMORY.md` ブロックの書き換えや削除は一切行わない +- **重複回避**: marker set を先読みし、既に書かれた key はスキップ +- **汚染検出**: `isContaminatedDreamingSnippet()` で dreaming narrative prompt が snippet に混入している候補を promotion 前に弾く(再帰汚染防止) +- **日次ノートの decay は物理削除ではなく search score 減衰のみ**(`memory/temporal-decay.ts`)。`halfLifeDays = 30` で `exp(-ln2/HL * age)` を score に乗じる。対象は `memory/YYYY-MM-DD.md` 形式のファイル限定で、`MEMORY.md` や topic ファイルは evergreen 扱い(減衰しない) +- **自己参照汚染の退避**: `dreaming-repair.ts` は dreaming narrative が session corpus や `DREAMS.md` に逆流した場合を検出し、該当ファイルを `.openclaw-repair/dreaming//` に **rename で退避**(削除ではない)。lint と同じく audit-first の GC スタイル +- `MEMORY.md` 側の GC(重複 block の統合、stale record の drop 等)は **この extension には存在しない**。書き込みは promotion の append のみで、ユーザーが手で消すか `memory-wiki` lint のレポート経由で扱う + +OpenClaw は「**削除は人間、script は append と退避まで**」という強い原則が貫かれている。 + 設計上の示唆: - Workspace = git リポジトリ 1 本で完結、配置もフラット。insomnia の pod workspace 概念にそのまま借用できる。 @@ -448,9 +462,108 @@ insomnia で意思決定すべきポイントはこの対応表: --- -## 8. 未調査 / 次に掘るべき項目 +## 8. GC 機構の横断比較 + +`docs/plan/memory.md` §GC は「Phase 2 とは別経路で memory を再評価し、drop / merge / split / `replaced` chain 整理を行う」ことを決めた段階で、判断主体と処理種別の仕様をこれから詰める。本節は他プロジェクトの GC 設計を共通の 6 軸で並べて、insomnia で採るべき型の材料とする。 + +### 8.1 比較表 + +対象の行は「その system が実装している GC 機構」単位で、同じプロジェクトが複数機構を持つ場合は複数行にした。 + +| # | プロジェクト / 機構 | 対象 | トリガー | 判断主体 | 処理種別 | 人間介入点 | 履歴保持 | +|---|---------------------|------|----------|----------|----------|-----------|---------| +| 1 | Codex Phase 2 consolidation | `MEMORY.md` block / `memory_summary.md` / `skills/*` | session idle + age | **LLM agentic**(sub-agent) | drop / merge / split / rewrite、removed thread id に紐づく block を **部分削除**(block 丸ごとは消さない、thread-local 記述のみ除去) | 無し(`/memories` で thread 単位 opt-out のみ) | git 任意、memory_root は単なる Markdown | +| 2 | Codex extension resource retention | `memories_extensions//resources/*.md` | Phase 2 実行直前に cron 相当 | **決定論** | **物理削除** (filename timestamp cutoff) | 無し | 完全消去、Phase 2 prompt に removed list を通知 | +| 3 | Codex stage1 pruning | `stage1_outputs` SQLite row | Phase 2 後 / 容量超過 | **決定論** | `selected_for_phase2 = 0` の古い row を cutoff + `LIMIT` で DELETE | 無し | SQL 完全削除 | +| 4 | Hermes `memory` tool | `MEMORY.md` / `USER.md` のエントリ | **char limit (2,200 / 1,375) 超過時の add 拒否** | **LLM agentic**(エラー受けて自分で `replace` / `remove` を呼ぶ) | drop / rewrite | 無し(all-agent) | ディスク消去(file lock で tx 化) | +| 5 | Hermes background review | entry / skill | turn / iter カウンタ(10 デフォルト) | **LLM agentic**(fork agent、max_iterations = 8) | add / update / delete をレビュー判断、`Nothing to save.` で no-op | 無し | same as 4 | +| 6 | OpenClaw `applyShortTermPromotions` | promotion candidate → `MEMORY.md` | Deep dreaming phase | **決定論**(6 重み合算 + 3 ゲート、LLM ゼロ) | **append のみ**(`` marker、既存 block は触らない) | 無し | 追記のみ、削除系統は別 | +| 7 | OpenClaw temporal decay | `memory/YYYY-MM-DD.md` | search 呼び出し毎 | **決定論** | **物理削除せず search score 減衰**(half-life 30d) | 無し | ファイル残置、検索順位だけ沈む | +| 8 | OpenClaw dreaming-repair | `DREAMS.md` / session-corpus / session-ingestion | 手動 + audit CLI | **決定論** | **archive 退避**(`.openclaw-repair/dreaming//` に rename)、完全削除しない | 退避後は手動判断 | archive ディレクトリに rename で保持 | +| 9 | memory-wiki `/wiki-lint`(OpenClaw extension、SamurAIGPT/llm-wiki-agent と Karpathy llm-wiki の production 実装) | wiki page / claim | `/wiki-lint` 手動 or cron 想定 | **決定論**(issue 検出ロジック) + その後の **human/LLM 任意判断** | **削除ゼロ**、`reports/lint.md` に issue を書き出すのみ。修正は別コマンド / 人間が行う | 全出力点が人間承認 | 原ファイル無変更、report は上書き | +| 10 | Cloudflare Agent Memory supersession | Fact / Instruction row(Durable Object SQLite) | 新 memory ingest 時 | **決定論**(normalized topic key が一致した瞬間) | **supersede**(旧 row を forward pointer で版チェーン化、物理削除せず)、vector は非同期 delete + new upsert | 無し(全自動) | SQLite に旧版残置、version chain で参照可能 | +| 11 | Letta sleep-time agent | in-context memory block | 非同期 idle process | **LLM agentic** | `core_memory_replace` / `core_memory_append` / archival 移動で block 書き換え | 無し | 書き換え後の block が保存、旧 block は失われる | +| 12 | Letta recursive summarization | message buffer | **context window fill の決定論閾値** | 発火は決定論 / 要約内容は LLM | evict + 再帰要約(古いほど重み減) | 無し | 要約に畳み込まれる(原メッセージは消失) | +| 13 | LinkedIn CMA(公開情報レベル) | Episodic / Semantic / Procedural 各層 | 未公開 | 未公開 | **summarization による compaction**(階層別の drop 仕様は非公開、"cache invalidation is one of the hardest problems" と明示) | **高 stake 用途は human validation** を挟む | 非公開 | + +### 8.2 軸別の知見 + +**対象(何が GC されるか)の粒度差**: + +- 最小粒度の Hermes(entry)と Cloudflare(row)は、LLM がエントリ単位で `remove` / `replace` する設計に寄る。 +- 中粒度の Codex(`MEMORY.md` block)は、block 内を thread id で部分削除しながら必要なら split / rewrite する agentic 寄り。 +- 最大粒度の OpenClaw / memory-wiki は **ファイル単位でのみ削除 / 退避** し、file 中身は LLM / 人間に委ねる。`docs/plan/memory.md` の「1 件 1 ファイル」方針とは **OpenClaw の粒度が最も近い**。 + +**トリガー(いつ GC するか)の 4 パターン**: + +1. **容量超過 hard reject**(Hermes): 追加要求を失敗で返して LLM に自律対処を強制。**決定論的 trigger + agentic 処理**で、設計最小コスト。 +2. **session idle + age**(Codex Phase 2): 人間の活動終了を待って非同期、最もユーザー体感を壊しにくい。 +3. **cron / scheduled sweep**(OpenClaw dreaming default `0 3 * * *`, Codex extension retention): 定期的・予測可能。人間 review との組み合わせがしやすい。 +4. **ingest 時の即時**(Cloudflare supersession): 書き込みの tx 内で完結、後続 GC 走査が要らない。topic key 設計が前提。 + +insomnia の plan は (2) Phase 2 で rewrite 許可を置きつつ、GC は (3) 方向で別経路という構造。これは Codex / OpenClaw の両方と整合する。 + +**判断主体の 3 系統**: + +- **決定論のみ**: Codex retention / stage1 pruning / OpenClaw temporal decay / Cloudflare supersession / lint 検出。条件がはっきりしているもの(age / key 一致 / 構造的 issue)は決定論が強い。 +- **決定論 scoring → 閾値 gate → 機械適用**: OpenClaw Deep promotion。LLM の揺れを除き、コストも LLM コールゼロ。ただし対象が append 側のみで、削除には使われていない。 +- **LLM agentic**: Codex Phase 2 / Hermes review / Letta sleep-time。判断の柔軟性(block 内部分削除、context 依存の merge)を LLM に委ねる。 + +`docs/plan/memory.md` は Phase 2 が LLM agentic、GC も暫定的に **LLM agentic + Linter Warn 併用**としている。完全に一致する事例は **Codex Phase 2 の consolidation prompt**(836 行)で、「removed thread id を `MEMORY.md` から部分削除し、blockに他の thread が残っている場合は split / rewrite して保持」という手続きを自然言語で指示している。**insomnia は Linter 側に警告カテゴリ(類似 slug / `replaced` 滞留 / sources 過多 / stale)を先に定義し、GC 実行の agent プロンプトはそれを入力にする**構造が素直。 + +**処理種別の選択肢**: + +- `drop / merge / split / rewrite` の組み合わせは Codex Phase 2 が最も自由度高く、Hermes もそれに近い(entry 粒度)。 +- `replaced` chain の整理は **Cloudflare だけが自動で版チェーン維持**、他は LLM 任せ。insomnia は decision record に `replaced_by` を入れているので、Cloudflare 方式の forward pointer 概念を **人間可読な `replaced_by:` frontmatter** で既に踏襲している。GC 時に chain をどこまで短く畳むか(長大な `a → b → c → d` を `a → d` に圧縮するか)は未決定論点で、Cloudflare は圧縮せず chain を保持する設計。 +- **`split` は Codex だけが明示**。block 内に複数 thread id が混ざった場合に thread id 単位で分ける。insomnia の「1 件 1 ファイル」方針では split = ファイル分割となり、主題の粒度判断は GC agent に委ねる必要がある。 + +**人間介入点の 3 段**: + +- 完全無介入: Codex / Cloudflare / Letta / Hermes +- audit-first(issue を surface し、人間が決断): memory-wiki lint / OpenClaw dreaming-repair +- high-stake 限定 gate: LinkedIn CMA + +insomnia の plan は「人間 offer 承認を併用」なので **audit-first に寄る**のが自然。lint 相当の Warn を Linter で出し、LLM Phase 2 / GC がそれを消費する前に人間が承認 / 拒否できる UI を提供する構造。memory-wiki lint は `reports/lint.md` というシンプルな Markdown 出力なので、そのまま `memory/reports/gc-lint.md` 相当を tick off する実装が参考になる。 + +**履歴保持の 3 モデル**: + +1. **物理削除**: Codex retention / stage1 / Hermes / Letta(要約で畳む分は実質消失) +2. **archive 退避(rename)**: OpenClaw dreaming-repair +3. **forward pointer / tombstone**: Cloudflare supersession + +insomnia は **git 管理下に memory を置く**前提なので、物理削除を選んでも git log で復元できるのが強み。`replaced_by:` frontmatter が forward pointer の役割を果たしているので、Cloudflare 型と git を足した「**現物は物理削除、frontmatter pointer で chain を参照、git で history**」が最も設計コストに合う。archive 退避は git を前提にすると冗長。 + +### 8.3 insomnia の GC 仕様を詰めるときの示唆 + +1. **GC trigger は 2 系統に割る**。(a) 決定論: Linter Warn 群 + age / count / size 閾値の sweep、(b) LLM 判定: Phase 2 とは別 prompt で Linter の issue リストを入力に渡す。両方が `memory/reports/gc-*.md` 相当を書き、それを次回の GC run が読む、というフィードバックループが OpenClaw lint / Codex Phase 2 input selection の両方と整合する。 +2. **Linter に「GC 候補検出」カテゴリを足す**。memory-wiki の lint issue code が参考になる: `stale-page`(90d 超)/ `stale-claim` / `low-confidence` / `orphan` / `duplicate-id` / `broken-wikilink` / `contradiction-present` / `open-question`。insomnia 固有の追加候補: `similar-slug`(類似 slug 乱立、既に plan に記載)/ `replaced-chain-long`(`replaced_by` が 3 段以上)/ `sources-overflow`(1 record の sources が閾値超)/ `knowledge-invoke-frequency-low`(`user_invoke` が一定期間ゼロ)。 +3. **処理は rewrite 優先、削除は `status: replaced` 経由**(既に plan 方針と一致)。forward pointer は Cloudflare 流、ただし chain 圧縮ルール(例: 「chain が n 段超えたら中間を drop、端のみ残す」)を決めるかは別論点。Cloudflare は圧縮しない、insomnia は git があるので圧縮してよい。 +4. **char limit は採用しない方が筋が良い**。Hermes の hard limit + LLM self-rewrite は設計最小だが、insomnia は 1 record 1 file なのでファイル内 size 制約は薄く、file 数による grep コストの方が支配的になる。file 数閾値 → GC trigger の方が insomnia の形に合う。 +5. **決定論 scoring を後から差し込む余地を残す**。OpenClaw Deep phase のような「頻度 / 関連度 / 多様性 / 時間減衰 / 整合性 / 概念」の 6 重み + 閾値は、agent LLM の出力が運用で評価可能になった段階で部分的に差し替える upgrade path として最適。初期は Phase 2 LLM + Linter Warn で十分。 +6. **削除は git commit 単位で可逆**という前提を明示する。プロジェクトメモリは git 管理下なので、GC が誤って drop してもユーザーは revert できる。これは Codex が持っていない利点で、GC agent の判断を多少攻めても安全マージンがある。 + +一次ソース: +- Codex Phase 2 consolidation: `github.com/openai/codex/codex-rs/core/src/memories/phase2.rs`, `core/templates/memories/consolidation.md` +- Codex retention / stage1 pruning: `github.com/openai/codex/codex-rs/core/src/memories/extensions.rs:11-139`, `codex-rs/state/src/runtime/memories.rs:290-331, 333-464` +- Hermes char limit reject: `github.com/NousResearch/hermes-agent/tools/memory_tool.py:211-266` +- Hermes review spawn: `github.com/NousResearch/hermes-agent/run_agent.py:2727-2830` +- OpenClaw promotion apply: `github.com/openclaw/openclaw/extensions/memory-core/src/short-term-promotion.ts:1518-1652` +- OpenClaw temporal decay: `github.com/openclaw/openclaw/extensions/memory-core/src/memory/temporal-decay.ts` +- OpenClaw dreaming-repair: `github.com/openclaw/openclaw/extensions/memory-core/src/dreaming-repair.ts:70-165` +- memory-wiki lint: `github.com/openclaw/openclaw/extensions/memory-wiki/src/lint.ts`, `claim-health.ts:6-7`(`WIKI_AGING_DAYS = 30`, `WIKI_STALE_DAYS = 90`) +- Cloudflare supersession: https://blog.cloudflare.com/introducing-agent-memory/ +- Letta sleep-time: https://docs.letta.com/guides/agents/memory/, https://www.letta.com/blog/sleep-time-compute, arxiv 2504.13171 +- Letta recursive summary: https://www.letta.com/blog/agent-memory +- Karpathy llm-wiki lint: https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f +- SamurAIGPT/llm-wiki-agent `/wiki-lint`: https://github.com/SamurAIGPT/llm-wiki-agent +- LinkedIn CMA: https://www.infoq.com/news/2026/04/linkedin-cognitive-memory-agent/ + +--- + +## 9. 未調査 / 次に掘るべき項目 - Letta `memory block` の Rust 実装例・永続化形式。 -- Cloudflare Agent Memory の supersession chain の具体アルゴリズム(記事は概略のみ)。 - `agentskills.io` の CRDT 的バージョニング方針(標準は version metadata を任意 key にしている。実運用でどう衝突解決するかは未整備)。 - Hermes の Honcho 連携の実体(外部サービス API 越しの dialectic user modeling、repo には prompt と API 呼び出しのみ)。 +- LinkedIn CMA の層別 GC 仕様(公開情報では compaction-by-summarization までしか開示されていない)。 +- Cloudflare supersession chain の圧縮ポリシー(chain 長の上限、vector GC と row GC の tx 境界)。 diff --git a/tickets/pod-prompt-catalog.md b/tickets/pod-prompt-catalog.md new file mode 100644 index 00000000..9f08c4f6 --- /dev/null +++ b/tickets/pod-prompt-catalog.md @@ -0,0 +1,140 @@ +# Pod 内部プロンプトのカタログ化 + +## 背景 + +Pod は Worker を拡張する機構を持つ: コンテキスト圧縮 (Compact)、非同期通知 (Notify)、中断と再開 (Interrupt)、system prompt の trailing section、AGENTS.md 取込時の注記。これらの機構がランタイムで Worker に注入する/Worker 向けに構成するプロンプトは、現状各モジュール内の `const &str` / `format!` に分散している。 + +| 場所 | 役割 | +|---|---| +| `crates/pod/src/pod.rs:50` `SUMMARY_SYSTEM_PROMPT` | Compact worker の system prompt | +| `crates/pod/src/notification_buffer.rs:73` `format_notification` | `Method::Notify` を Worker history に system_message として注入するラッパー | +| `crates/pod/src/interrupt_and_run.rs:18-20` | 中断時の synthetic tool_result と system_message | +| `crates/pod/src/system_prompt.rs:179-203` `append_trailing_section` | `## Working boundaries` / `## Project instructions (AGENTS.md)` ヘッダとレイアウト | +| `crates/pod/src/agents_md.rs:19` `TRUNCATION_NOTICE` | AGENTS.md が 64KB 超過したときの末尾注記 | + +`resources/prompts/default.md` でユーザー向け instruction テンプレートは一元化された一方、Pod が Worker を拡張する側のプロンプトは並行した一元管理を持たない。結果として: + +- 全体を俯瞰しづらく、新しい injection 点を足すときに既存との一貫性が取れない +- 多言語化や文体カスタマイズの導線がない +- builtin から差分だけ書き換える軽量な手段がない + +tool description は tool 宣言と併置が自然(tool 追加 = 1 箇所追加)なので本チケット対象外。Pod の Worker 拡張機構は異質なものが「Pod のトーン」として束になって振る舞う性質上、中央で扱う価値がある。 + +## 要件 + +### 1. 中央モジュール: `PodPrompt` enum + +Pod が Worker に注入する全プロンプトを列挙する enum を 1 つ置く。variant の集合が「存在する injection 点」の master。新しい注入点を増やすときは variant 追加が必要 = コード上で勝手に散らかせない。 + +Pod 内部の各モジュールは `PodPrompt::CompactSystem.render(&ctx)` のように 1 本の API で引く。直接 `include_str!` / `const &str` / `format!` で prompt 文字列を書かない。 + +### 2. 翻訳パック形式: `prompts.toml` + +全 variant を key-value で持つ TOML。値は minijinja テンプレート文字列(builtin/ランタイム問わず一律 minijinja render)。 + +```toml +[prompt] +interrupt_system_note = "[The previous turn was interrupted by the user. The user's next request follows.]" + +notify_wrapper = """[Notification] +{{ message }} + +This is a notification, not a blocking request. ...""" + +# 長文は外部ファイルに link +compact_system = "{% include '$insomnia/internal/compact_system.md' %}" +``` + +変数展開は各 variant ごとに定義された context を render 時に渡す(例: `notify_wrapper` は `message`、`compact_system` は tool 名リストなど)。context の具体形は実装段階で variant ごとに確定する。 + +### 3. Builtin pack の網羅性をビルドエラーで強制 + +`resources/prompts/internal.toml` が `PodPrompt` 全 variant を網羅していないとビルド失敗。enum に variant を足したが builtin pack に key が無い、あるいは逆、はコンパイルが通らない。`build.rs` もしくは proc-macro で検査する(どちらを取るかは実装時判断)。 + +### 4. 4 段の置換マージ + +key 単位で overlay。下層から順に apply、後勝ち: + +``` +builtin resources/prompts/internal.toml (必須・網羅) + ↓ +user $XDG_CONFIG_HOME/insomnia/prompts.toml (任意・auto) + ↓ +workspace /.insomnia/prompts.toml (任意・auto) + ↓ +manifest pack manifest.pod.prompt_pack で指名 (任意) +``` + +- 欠落 key: 下層から継承 +- ランタイム層 (user/workspace/manifest pack) の unknown key: `tracing::warn!` して無視 +- builtin 層の不整合はビルドエラー(前項) + +### 5. 値 render は minijinja 統一、include は既存 prefix resolver 流用 + +全値を minijinja で parse / render。`{% include "$prefix/..." %}` によって外部ファイルを link 可能。resolver は既存 `crates/pod/src/prompt_loader.rs` を流用し、`$insomnia` / `$user` / `$workspace` すべてをどの層の pack からも参照できる。 + +### 6. manifest 露出 + +```toml +[pod] +prompt_pack = "$user/packs/japanese.toml" +``` + +任意フィールド。指定されていれば 4 段目 overlay として適用。auto-discovery (user/workspace) とは独立で共存する。子 Pod を spawn する際、親が子の役割に応じた pack を明示する用途を想定。 + +## 設計判断 + +### prompt_pack は prefix 名前空間に載せない + +既存 `$insomnia/` / `$user/` / `$workspace/` は**名前空間**で、同じ key で複数の source を指し合うことはない。一方 pack は**レイヤー**で、同じ key を置き換える。この 2 つを同じ軸に混ぜるとユーザーが「どの書き方で上書きされるか」を予測できなくなる。 + +pack ファイルは**固定パスの auto-discovery** と **manifest による明示指名**の 2 経路のみ。各値内部で `{% include "$prefix/..." %}` を使うのは別軸なので prefix 体系の利点はそのまま享受できる。 + +### 長文ファイル分離用の独立フィールドを作らない + +値が minijinja である以上、長文を別ファイルにしたければ `"{% include '$insomnia/internal/foo.md' %}"` と書けば済む。「TOML 値の文字列」と「ファイル参照」の 2 値型を用意する必要はない。1 種類で統一する。 + +### ランタイム層の unknown key は warn + +pack ファイルを書いた時点と Pod バージョンがずれたとき、hard error にすると古い pack で Pod が起動しなくなる。前方互換のため warn で無視する。builtin 層は build-time に同梱するので不整合はビルドで捕まえられる = error で問題ない。 + +### tool description は本チケット対象外 + +tool 追加は tool ごとの 1 箇所の自然な単位で、description は tool の属性として宣言と併置が読みやすい。Pod 内部 injection は「異質なものが一体としてトーンを決める」共通軸なので中央化の価値が別にある。この差を混ぜない。 + +### auto-discovery と manifest 指定を両立させる + +auto-discovery は「ユーザー or プロジェクトの永続設定 (翻訳、文体)」、manifest 指定は「**その Pod の役割**による差し替え」と目的が別。どちらか一方では片方のユースケースが潰れるので両立する。key 単位の merge は共通なので実装コスト差は小さい。 + +## Scope 外 + +- tool description の resources 化(併置方針を維持) +- 具体的な翻訳 pack の作成(本チケットは導線のみ) +- pack 編集 GUI / TUI +- user/workspace 以外の auto-discovery パス追加(別 XDG 層、bundle pack 等) + +## 依存 + +- `crates/pod/src/prompt_loader.rs` (`$prefix` resolver を minijinja include から流用) +- `crates/pod/src/system_prompt.rs` (minijinja 使用パターン) +- `crates/manifest`: `pod.prompt_pack: Option` 追加のみで破壊的変更なし + +## 影響範囲 + +- `crates/pod/src/prompts.rs` (新設): `PodPrompt` enum、render API、pack loader、4 段 merge +- `resources/prompts/internal.toml` (新設): builtin pack +- `resources/prompts/internal/*.md` (新設): 長文外出し +- `crates/pod/` 各モジュール: 既存ハードコードを `PodPrompt::...render(&ctx)` 呼び出しに置換 +- `crates/pod/build.rs` もしくは proc-macro (新設): enum ⇔ builtin pack 網羅検査 +- `crates/manifest/src/config.rs`: `prompt_pack` フィールド追加 + +## 実装順序 + +1. `PodPrompt` enum と render API を定義。builtin map (in-memory、`include_str!`) から引くだけの最小実装。variant の render 網羅を単体テストで確認 +2. `resources/prompts/internal.toml` に全 key を書き、既存ハードコードをそのまま文字列として移植。各モジュールの呼び出しを `PodPrompt::...render` に置換 (挙動は既存と完全同一) +3. builtin 網羅を build-time 検査に格上げ (enum ⇔ pack の双方向で欠落/余剰を検出) +4. user / workspace の auto-load と 3 段マージ。ランタイム unknown key の warn +5. `manifest.pod.prompt_pack` を追加、4 段目 overlay として load +6. 長文 variant (compact_system など) を `{% include "$insomnia/internal/..." %}` 形式に分離。`$user` / `$workspace` から include で override できることをテスト + +各ステップ終了時点でビルド通過・既存テスト合格を維持する。 diff --git a/tickets/test-design.md b/tickets/test-design.md index 6ce3e0ef..15195c6e 100644 --- a/tickets/test-design.md +++ b/tickets/test-design.md @@ -7,8 +7,5 @@ ## 検討事項 -- `llm-worker`: LlmClient の mock 実装によるターンループ・ツール実行のテスト -- `llm-worker-persistence`: FsStore / FsBlobStore のファイルシステムテスト(tempdir) -- `pod`: PodController / SocketServer の結合テスト -- `protocol`: シリアライズ/デシリアライズの往復テスト -- `manifest`: パースのバリエーション(既存テストの拡充) +低層部分のテストを信頼し、上層までモックデータを引っ張ってきてテストする必要は無いのか? +実際の認証を使ったE2Eはどの様に結果を出すか?/