yoi/tickets/memory-file-format.md

8.0 KiB
Raw Blame History

メモリ機構: ファイル形式 + Linter 土台

背景

docs/plan/memory.md で決めたメモリ機構の永続化レイヤの土台。memory/*knowledge/* の record を保存・編集する際の静的スキーマと、書き込み時の Linter を成立させる。Phase 1/2、検索ツール、常駐注入、GC はすべてこの層に乗る。

Workflowdocs/plan/workflow.md)も同じ frontmatter / Linter 経路で扱うため、memory/workflow/<slug>.md の frontmatter 検証と書き込み制限も本チケットに含める。実行経路(/<slug> dispatchは別。

設計方針

memory クレートに集約

memory 関連は新規 crates/memory/ に全部閉じ込める。tools クレートや pod 層に memory 由来のコードを漏らさない。

  • schemafrontmatter 型、slug 文法、Linter ルール、Tool 実装、ワークスペース解決を全部 memory クレート内に置く
  • tools::write_tool / edit_tool には一切手を入れない
  • LLM への違反伝達は memory tool が ToolError::InvalidArgument を返すことで自然に成立。Interceptor 拡張・retry message 注入・違反カウンタは持たない
  • 「N 回失敗で abort」は worker 層の max iteration に委ね、memory 固有のカウンタは設けない

memory 専用 Tool汎用 CRUD ではない)

memory クレートが read_tool / write_tool / edit_tool の 3 種を提供する。これらは <workspace>/memory/<workspace>/knowledge/ 配下のみを対象とし、write/edit は fs 書き込み前 に Linter を通して違反は ToolError::InvalidArgument で返す。

docs/plan/memory.md の「同じ汎用 CRUD」記述は本チケットで memory 専用 Tool 方式に書き換える(汎用 CRUD は memory ディレクトリには触らせない)。

Pod 側の責務(最小限)

memory を有効化する Pod は、generic tool に渡す Scope から memory/knowledge/ を deny に落とす。これにより同じ workspace 内で、generic write/edit は memory 配下を触れず、memory tool だけが触れる構造になる。Pod 側でやることはこの Scope deny と memory tool の登録だけ。

「sub-Worker / 人間」の二系統

  • sub-Worker: tool 層を経由するため Linter を必ず通る
  • 人間: エディタ / git commit は tool 層を経由しないので Linter を通らない。memory::Linter を import して走らせる CLI / pre-commit hook を後で用意できる構造にしておく(本チケットでは実装しない)

要件

ディレクトリと record 種別

  • memory/summary.md — Always-on サマリ1 ファイル固定)
  • memory/decisions/<slug>.md — Decisions
  • memory/requests/<slug>.md — Requests
  • memory/workflow/<slug>.md — Workflowfrontmatter 検証のみ対象)
  • memory/_staging/<id>.json — Phase 1 中間パス予約のみ。Linter 対象外)
  • knowledge/<slug>.md — Knowledgememory/ の兄弟)

slug 文法: ^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$agent-skills 準拠、1-64 chars、先頭末尾 - 不可、-- 連続不可。ファイル名がそのまま識別子で、frontmatter に id / name は持たない。

frontmatter スキーマ

  • 共通: created_at, updated_atRFC3339
  • Decisions: sources, status: open | resolved | replaced、置き換え時 replaced_by: <slug>
  • Requests: sources
  • Knowledge: kind, description, model_invokation, user_invocable, last_sources
  • Summary: updated_atoptional: last_rewritten_from_range
  • Workflow: description, auto_invoke, user_invocable, requires

sources / last_sources の要素形式は { session_id: String, range: [u64, u64] }range は session-store の entry index ペア。

Linter ルール

静的 errormemory tool が ToolError::InvalidArgument で返す。複数違反は集約して 1 回で返す):

  • frontmatter 必須 field 欠落・型違反
  • memory/workflow/ への書き込み禁止sub-Worker のみ。memory tool が拒否、人間編集は memory tool を通らないので素通り)
  • 同 slug での新規作成禁止(既存があれば update を要求)
  • replaced_by: <slug> / requires: [..] が実在 record を指す
  • replaced_by の循環は error
  • Decisions status の enum 違反
  • slug 文法違反
  • model_invokation: true な Knowledge の description 1024 chars 上限
  • 種別ごとの char 硬上限(初期既定値、設定 key は別 PR:
    • summary.md: 20000 chars
    • decisions / requests / knowledge 本文: 各 8000 chars

膨張抑制 Warnerror にせず、warn として返す。memory tool は受け取って summary に追記する程度):

  • 低重要度 × char の天秤(暫定: 1500 chars 超のレコードに対して sources 1 件のみなら warn
  • sources 配列長の累積(暫定: 10 件超で warn
  • 類似 slug 乱立(暫定: Levenshtein 距離 2 以下の slug が 3 件以上で warn

具体閾値の調整は別 PR設定 key 化)。

#<slug> 本文中検出はスコープ外

本文中の #<slug> 参照の検出 / 補完は submit-segment 系チケットの責務。本チケットの Linter は frontmatter 由来の参照(replaced_by / requires)のみを検証する。

参照整合チェックの実行方式

write/edit 毎に <workspace>/memory/<workspace>/knowledge/ を毎回 walk して slug 集合を構築(キャッシュなし)。ファイル数は少ない想定。

適用経路

  • memory tool の write/edit 内で pre-write 検証
  • 人間編集 / pre-commit hook 経路は本チケットでは作らない。memory::Linter を pure 関数として export しておけば後で CLI 化できる

範囲外

  • 検索ツール、常駐注入、Phase 1/2、GC の実装
  • 意味破壊rewrite で主張が落ちる等)の検出 — 監査 LLM 層は将来検討
  • staging JSON の schema — Phase 1 チケット
  • Workflow の /<slug> 実行経路
  • 本文中 #<slug> 参照の検出 — submit-segment 系
  • 設定 key閾値 tune— 別 PR
  • 人間編集向け CLI / pre-commit hook — 別チケット
  • Interceptor / Hook 系統への拡張 — 不要tool error で完結)

完了条件

  • crates/memory/ が新設され、workspace に登録されている
  • memory tool 3 種read / write / editが登録できる
  • write/edit に違反 content を渡すと、複数違反を集約した ToolError::InvalidArgument が返り、fs に書き込まれない
  • 正常 content は通常通り書き込まれる
  • memory/workflow/<slug>.md への write/edit は error で止まる
  • 同 slug で新規作成しようとすると error になるexisting → edit に倒すサイン)
  • replaced_by / requires の参照切れと循環が error として検出される
  • Pod が memory を有効化すると、generic tool の Scope から memory/knowledge が deny される
  • 既存ビルド・テストを壊さない

実装順序

  1. crates/memory/ 新設、workspace 登録、依存追加
  2. schema/, slug.rs, error.rspure 関数 + 型)
  3. linter/frontmatter / size / 参照存在 / 循環 / workflow 拒否)
  4. tool/read / write / edit、pre-write で linter 通す)
  5. Pod 側の Scope deny 配線
  6. 単体テスト

各ステップ終了時点でビルド通過を維持する。

参照

  • docs/plan/memory.md §ファイル形式 / §書き込み経路と Linter / §Knowledge の採択基準(本チケットで該当箇所を memory 専用 tool 方式に更新)
  • docs/plan/workflow.md §格納先とファイル形式 / §生成・更新ポリシー
  • crates/tools/src/{write,edit,read}.rs — Tool 実装の参考(依存はしない)
  • crates/llm-worker/src/tool.rsTool trait / ToolError / ToolOutput

Review