docs(tickets): セッション内 Task ツールを本体と注意機構に分割
This commit is contained in:
parent
0c10150b02
commit
28fe1dae1c
4
TODO.md
4
TODO.md
|
|
@ -15,6 +15,8 @@
|
|||
- Manifest: Tool Output / File Upload 上限の分離とデフォルト緩和 → [tickets/manifest-output-upload-limits.md](tickets/manifest-output-upload-limits.md)
|
||||
- メモリ機構
|
||||
- 使用頻度メトリクス + Knowledge 化候補レポート → [tickets/memory-usage-metrics.md](tickets/memory-usage-metrics.md)
|
||||
- セッション内 TODO ツール(注意機構付き) → [tickets/session-todo.md](tickets/session-todo.md)
|
||||
- セッション内 Task ツール
|
||||
- ツール本体(TaskCreate / TaskUpdate / TaskList / TaskGet + 永続化 / compact 跨ぎ) → [tickets/session-todo.md](tickets/session-todo.md)
|
||||
- 注意機構(無アクティビティで `<system-reminder>` ナッジ) → [tickets/session-todo-reminder.md](tickets/session-todo-reminder.md)
|
||||
- ワークスペースのメモリーをLintするヘッドレスCLI
|
||||
- system-reminder 注入機構の汎用化(2件目の利用者が出た時に検討。タグ形式と「履歴を汚さない」原則は session-todo で先行確立)
|
||||
|
|
|
|||
60
tickets/session-todo-reminder.md
Normal file
60
tickets/session-todo-reminder.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# セッション内 Task ツールの注意機構
|
||||
|
||||
## 背景
|
||||
|
||||
`tickets/session-todo.md` で導入する Task ツール群があっても、LLM はそれを使わずに作業を続け得る。ツールを呼ばないまま会話が長引くと、
|
||||
|
||||
- 開始した作業の `inprogress` がずっと放置されたままになる
|
||||
- 「やったつもり」になって `completed` への更新を忘れる
|
||||
- そもそも TaskStore の存在を忘れて、構造化を諦めて自由記述に回帰する
|
||||
|
||||
OpenCode の todo は専用の注意機構を持たない(汎用 reminder 経由)。一方 Claude Code は `task_reminder` を「N リクエスト無アクティビティで初めて発火するナッジ型」として実装しており、毎リクエスト押し戻しはしない(`/home/hare/.local/share/claude/versions/2.x` の `du_` / `cu_` 関数、閾値 `Z_8 = { TURNS_SINCE_WRITE: 10, TURNS_BETWEEN_REMINDERS: 10 }`)。
|
||||
|
||||
Insomnia でも同方針を採り、active Task が残っているのに `TaskCreate` / `TaskUpdate` が一定リクエスト呼ばれていない場合に限り、`<system-reminder>` で揮発的に思い出させる。「やったつもり」抑止と、トークン浪費・LLM の自律性侵害のバランスを取るため、毎リクエスト押し戻しはしない。
|
||||
|
||||
## 前提
|
||||
|
||||
- `tickets/session-todo.md` の TaskStore と `TaskCreate` / `TaskUpdate` / `TaskList` / `TaskGet` ツールが利用可能であること
|
||||
- `pre_llm_request` 相当のフックを Pod が持つこと(無ければ本ticket で導入)
|
||||
|
||||
## 方針
|
||||
|
||||
- **`pre_llm_request` Interceptor として実装**。直近の user message に `<system-reminder>` ブロックを揮発的に append するだけ。履歴・ログには載せない
|
||||
- **system-reminder 注入の汎用化はやらない**。利用者が Task 1機構しかない段階で抽象を立てない(CLAUDE.md「概念の追加は不在が問題になってから」)。ただし「タグ形式は `<system-reminder>...</system-reminder>` で揃える」「履歴は汚さない」の2点は本実装で確立し、将来の追加機構が同じ規約に乗れるようにする
|
||||
- **発火はナッジ型**。N リクエスト無アクティビティで初めて発火し、cooldown も持つ
|
||||
|
||||
## 要件
|
||||
|
||||
### Interceptor
|
||||
|
||||
- `pre_llm_request` で `Vec<Item>` を受け取り、以下の AND を満たした場合のみ発動
|
||||
- active Task(`pending` または `inprogress`)が1件以上存在する
|
||||
- 直近 N リクエスト (暫定 N=8) `TaskCreate` / `TaskUpdate` のいずれも呼ばれていない
|
||||
- 前回 reminder 注入から M リクエスト (暫定 M=8) 以上経過
|
||||
- ここで言う「リクエスト」は **LLM への1回の推論呼び出し (= assistant 応答1回)** の単位で数える。ユーザー発火単位ではない。1ユーザー発火内で tool ループが回れば、tool_result を受けて発火する次のリクエストもそれぞれ1としてカウントする
|
||||
- カウント対象はメインスレッドの assistant 応答に限る。サブエージェント / sidechain の assistant 応答は除外する
|
||||
- カウンタは Pod 側の session-lifetime 状態として保持する(`requests_since_last_task_management` / `requests_since_last_reminder`)。resume 時は履歴の逆走査で再計算するか 0 リセットで再開する。どちらでも「初回ナッジが最大 N リクエスト遅れる」だけで挙動として致命ではない
|
||||
- 発動時、直近の user message の content(または content[最終 text part])の末尾に `<system-reminder>` ブロックを append し、現在の active Task リストを `taskid` / `status` / `subject` を含む簡潔な形式で列挙する。`description` は長大化を避けるため省略してよい
|
||||
- 履歴 (`Worker` の保持する `Vec<Item>`) は変更しない。リクエスト送信時の Vec のみ加工する
|
||||
- active Task が空の場合は何も差し込まない(忘却防止が目的なので、思い出させる対象が無いなら不要)
|
||||
|
||||
## 完了条件
|
||||
|
||||
- 直近 N リクエスト連続で `TaskCreate` / `TaskUpdate` が呼ばれず、かつ active Task が残っている場合に限り、`pre_llm_request` で `<system-reminder>` が直近 user message に append される
|
||||
- `TaskCreate` / `TaskUpdate` のいずれかが呼ばれるとカウンタがリセットされ、再び N リクエスト経過するまでは reminder が出ない
|
||||
- reminder が一度出たあとは、cooldown M リクエストが経過するまで再注入されない
|
||||
- active Task が0件の場合は reminder が出ない
|
||||
- system-reminder の注入は揮発的で、`get_history` / セッションログには現れない
|
||||
- 単体テストで Interceptor の発火条件(リクエスト回数閾値、active 0件、cooldown、サブエージェント除外)がカバーされる
|
||||
|
||||
## 範囲外
|
||||
|
||||
- inprogress 滞留検出 / 多重 inprogress 検出など、状態異常ベースの追加トリガ(必要になれば別チケットで追加)
|
||||
- system-reminder 注入機構の汎用化(`TODO.md` に立項済み、別途検討)
|
||||
- `TaskCreate` / `TaskUpdate` の戻り値に active Task 全件を埋め込む強化(必要に応じて Tool ticket 側で対応)
|
||||
|
||||
## 参照
|
||||
|
||||
- 設計指針: `CLAUDE.md`(最小の構造化 / 概念の追加は不在が問題になってから)
|
||||
- 前提: `tickets/session-todo.md`(Tool 群と TaskStore)
|
||||
- 参考実装: Claude Code の `task_reminder`(`du_` / `cu_` 関数、閾値 `Z_8 = { TURNS_SINCE_WRITE: 10, TURNS_BETWEEN_REMINDERS: 10 }`)
|
||||
|
|
@ -1,74 +1,105 @@
|
|||
# セッション内 TODO ツール
|
||||
# セッション内 Task ツール
|
||||
|
||||
## 背景
|
||||
|
||||
長めのタスクを LLM に進めさせる際、Claude Code / OpenCode が備える「セッション内 TODO リスト」相当の機構が無いため、エージェントが自分の作業計画を構造化された形で保持・更新できない。Reasoning や text 出力の中で擬似的に TODO を書くことはできるが、
|
||||
長めのタスクを LLM に進めさせる際、Claude Code / OpenCode が備える「セッション内 TODO / Task リスト」相当の機構が無いため、エージェントが自分の作業計画を構造化された形で保持・更新できない。Reasoning や text 出力の中で擬似的に TODO を書くことはできるが、
|
||||
|
||||
- ターンを跨いだとき直近の TODO 状態が context から押し出される
|
||||
- compact を跨ぐと完全に消える
|
||||
- ツール結果ではないため、状態の上書き・部分更新の規約が決まらず、意図と乖離した「やったつもり」を引き起こす
|
||||
- ツール結果ではないため、状態の作成・更新・削除の規約が決まらず、意図と乖離した「やったつもり」を起こしやすい
|
||||
- 安定した識別子が無いため、後から特定 entry を更新できない
|
||||
|
||||
この用途のために、セッション内に正規化された TODO リストを保持し、ターンごとに LLM へ最新状態を再提示(注意機構)し、compact を跨いで保存される専用ツールを導入する。
|
||||
この用途のために、セッション内に正規化された Task リストを保持し、compact を跨いで保存される専用ツールを導入する。
|
||||
|
||||
「LLM がツールを使わなくなった場合のサボり防止」を目的とした注意機構(`<system-reminder>` の自動注入)は本チケットの範囲外。`tickets/session-todo-reminder.md` で別途扱う。
|
||||
|
||||
## 方針
|
||||
|
||||
- **保存先は `tools` 層の session-lifetime 状態**。`Tracker` と同じ生存スコープで `Pod` が所有。`Arc<Mutex<Vec<TodoItem>>>` ベースの `TodoStore` を tool に注入する
|
||||
- **永続化は専用レーンを持たない**。`tool_call.arguments` がセッションログに既に乗っているため、resume 時には履歴 replay の中で最後の `todo_write` 引数を `TodoStore` に再適用すれば状態が復元される
|
||||
- **注意機構は `pre_llm_request` Interceptor**。直近の user message に `<system-reminder>` ブロックを揮発的に append するだけ。履歴・ログには載せない
|
||||
- **system-reminder 注入の汎用化はやらない**。利用者が TODO 1個しかない段階で抽象を立てない(CLAUDE.md「概念の追加は不在が問題になってから」)。ただし「タグ形式は `<system-reminder>...</system-reminder>` で揃える」「履歴は汚さない」の2点は本実装で確立し、将来の追加機構が同じ規約に乗れるようにする
|
||||
- **保存先は `tools` 層の session-lifetime 状態**。`Tracker` と同じ生存スコープで `Pod` が所有。`Arc<Mutex<TaskStore>>` ベースの store を tool に注入する
|
||||
- **Task は差分操作**。全置換ではなく `TaskCreate` / `TaskUpdate` によって TaskStore を逐次更新する
|
||||
- **永続化は専用レーンを持たない**。`tool_call.arguments` がセッションログに既に乗っているため、resume 時には履歴 replay の中で `TaskCreate` / `TaskUpdate` を順に再適用すれば状態が復元される
|
||||
|
||||
## Task データモデル
|
||||
|
||||
各 Task entry は以下の4フィールドを持つ。
|
||||
|
||||
- `taskid`: store が自動採番する整数 ID。`TaskCreate` ごとに単調増加する
|
||||
- `status`: `pending | inprogress | completed | deleted`
|
||||
- `subject`: 1行程度の短い件名
|
||||
- `description`: 詳細説明
|
||||
|
||||
状態遷移の通常ルートは `pending -> inprogress -> completed` とする。完了ではなく一覧から取り下げたい場合は、物理削除ではなく `TaskUpdate` で `status = deleted` を設定する。
|
||||
|
||||
## 要件
|
||||
|
||||
### `todo_write` ツール
|
||||
### `TaskCreate` ツール
|
||||
|
||||
- 入力は TODO リスト全体(全置換)。差分更新は受けない
|
||||
- 各エントリは `id` / `content` / `status (pending | in_progress | completed)` の 3 フィールド
|
||||
- `id` は LLM 側が一貫して採番できる文字列。同 id があれば置換、なければ新規。順序は配列順を信頼
|
||||
- 戻り値は更新後のスナップショットを summary に含める(次ターンで再確認可能)
|
||||
- 読み出し専用ツール(`todo_read`)は作らない。注意機構と tool result snapshot で代替
|
||||
- 入力は `subject` / `description` のみ
|
||||
- TaskStore の末尾に新規 entry を push する
|
||||
- `taskid` は TaskStore が自動で割り当てる。LLM は指定できない
|
||||
- 初期 `status` は `pending`
|
||||
- 戻り値は作成された Task と、更新後の TaskStore スナップショット概要を summary に含める
|
||||
|
||||
### `TaskList` ツール
|
||||
|
||||
- 入力なし
|
||||
- 現在の TaskStore スナップショットを返す
|
||||
- `taskid` / `status` / `subject` / `description` を含める
|
||||
- `completed` / `deleted` も含め、store に残っている Task をすべて列挙する
|
||||
|
||||
### `TaskGet` ツール
|
||||
|
||||
- 入力は `taskid`
|
||||
- 指定された Task を1件返す
|
||||
- 存在しない `taskid` はエラーにする
|
||||
|
||||
### `TaskUpdate` ツール
|
||||
|
||||
- 入力は `taskid` と、更新する `status` / `subject` / `description` のいずれか
|
||||
- 少なくとも1フィールドは更新指定が必要
|
||||
- `taskid` は変更できない
|
||||
- `status` は `pending | inprogress | completed | deleted` のいずれか
|
||||
- 存在しない `taskid` はエラーにする
|
||||
- 削除は `status = deleted` への更新として表現し、entry は store から取り除かない
|
||||
- 戻り値は更新された Task と、更新後の TaskStore スナップショット概要を summary に含める
|
||||
|
||||
### Resume 時の復元
|
||||
|
||||
- `Pod::resume` の履歴 replay 中に `todo_write` の `tool_call.arguments` を観測したら、`TodoStore` を引数値で上書き
|
||||
- `Pod::resume` / restore の履歴 replay 中に `TaskCreate` / `TaskUpdate` の `tool_call.arguments` を観測したら、TaskStore に順に再適用する
|
||||
- `TaskCreate` は replay 時も自動採番を行う。ログ上の call 順序が保持されるため、通常の replay では元の `taskid` が再現される
|
||||
- `TaskUpdate` は replay 済みの TaskStore に対して適用する
|
||||
- 専用 LogEntry / Persistence 型は追加しない(`Tracker` と同じ方針)
|
||||
- `tool_call.arguments` のフォーマットが `todo_write` の引数 schema と乖離した場合(旧バージョンのログ)は、その call を無視してよい
|
||||
- `tool_call.arguments` のフォーマットが現在の schema と乖離した場合(旧バージョンのログ)は、その call を無視してよい
|
||||
|
||||
### Compact 跨ぎ
|
||||
|
||||
- compact 起動時、Pod は現在の `TodoStore` スナップショットを compact worker context に渡す
|
||||
- compact worker は summary を書く際、未完了 TODO を summary 文に取り込める情報源として参照する(強制ではない)
|
||||
- compact 後の新セッション開始時、Pod は **`mark_read_required` と同じ system message 注入レーン**に「未完了 TODO スナップショット」を 1 メッセージとして注入する
|
||||
- 新セッションは空の `TodoStore` で始まる。次に LLM が `todo_write` を呼び出した時点で再構築される(system message に書かれたスナップショットがその拠り所)
|
||||
- compact worker に TODO 編集権限は与えない(消去・縮約はしない)
|
||||
|
||||
### 注意機構(Interceptor)
|
||||
|
||||
- `pre_llm_request` で `Vec<Item>` を受け取り、未完了 TODO(`pending` または `in_progress`)が 1 件でも存在する場合に発動
|
||||
- 直近の user message の content(または content[最終 text part])の末尾に `<system-reminder>` ブロックを append
|
||||
- ブロック内には現在の TODO リストを、status を含む簡潔な形式で列挙
|
||||
- 履歴 (`Worker` の保持する `Vec<Item>`) は変更しない。リクエスト送信時の Vec のみ加工
|
||||
- TODO が空の場合は何も差し込まない
|
||||
- compact 起動時、Pod は現在の TaskStore **全スナップショット**を compact worker context に渡す
|
||||
- compact worker は summary を書く際、TaskStore を参照できる。未完了 Task を summary 文に取り込める情報源として参照する(強制ではない)
|
||||
- compact worker に Task 編集権限は与えない(作成・更新・削除はしない)
|
||||
- compact 後の新セッションでも TaskStore は空にせず、compact 前の TaskStore **全件**を維持する。`completed` / `deleted` も含めて保持する
|
||||
- compact 後の新セッション開始時、Pod は **`mark_read_required` と同じ system message 注入レーン**に TaskStore 全スナップショットを1メッセージとして注入する
|
||||
- compact 完了後、Pod は新セッションの継続前に必ず `TaskList` を発火させ、LLM が compact 後の TaskStore 状態を tool result として再確認できるようにする
|
||||
|
||||
## 完了条件
|
||||
|
||||
- `todo_write` ツールが builtin tool として登録され、Pod で利用できる
|
||||
- LLM が `todo_write` を呼ぶと TodoStore が更新され、その後の `pre_llm_request` で system-reminder として LLM に再提示される
|
||||
- セッションを resume すると、最後の `todo_write` の状態から再開される
|
||||
- compact を跨いでも、未完了 TODO が新セッション冒頭の system message として残る
|
||||
- system-reminder の注入は揮発的で、`get_history` / セッションログには現れない
|
||||
- 単体テストで `todo_write` の更新挙動 / replay 復元 / Interceptor の差し込みがカバーされる
|
||||
- `TaskCreate` / `TaskList` / `TaskGet` / `TaskUpdate` が builtin tool として登録され、Pod で利用できる
|
||||
- LLM が `TaskCreate` を呼ぶと TaskStore に pending Task が末尾追加され、自動採番された `taskid` が返る
|
||||
- LLM が `TaskUpdate` を呼ぶと既存 Task の `status` / `subject` / `description` が更新される
|
||||
- `TaskList` / `TaskGet` で TaskStore の状態を確認できる
|
||||
- セッションを resume すると、履歴中の `TaskCreate` / `TaskUpdate` replay により TaskStore が復元される
|
||||
- compact を跨いでも、TaskStore 全件が維持され、新セッション冒頭の system message と `TaskList` 発火により再提示される
|
||||
- 単体テストで TaskCreate / TaskUpdate / TaskList / TaskGet の挙動、replay 復元がカバーされる
|
||||
|
||||
## 範囲外
|
||||
|
||||
- 差分更新 API(add / remove / patch)。全置換のみで十分
|
||||
- TODO 階層・優先度・タグ
|
||||
- TUI / GUI での TODO 状態の可視化(ツール呼び出しのイベントは既に流れているので、クライアント側で表示するかは別軸)
|
||||
- system-reminder 注入機構の汎用化(`TODO.md` に立項済み、別途検討)
|
||||
- TODO の永続化を専用 LogEntry に分離する設計(現方針は tool_call replay で復元、追加レーン不要)
|
||||
- 複数 Pod 間で TODO を共有する仕組み
|
||||
- 注意機構(`<system-reminder>` の自動注入による「サボり防止」)→ `tickets/session-todo-reminder.md`
|
||||
- Task の階層・優先度・タグ
|
||||
- TUI / GUI での Task 状態の可視化(ツール呼び出しのイベントは既に流れているので、クライアント側で表示するかは別軸)
|
||||
- Task の永続化を専用 LogEntry に分離する設計(現方針は tool_call replay と compact 時 snapshot 維持で復元、追加レーン不要)
|
||||
- 複数 Pod 間で Task を共有する仕組み
|
||||
|
||||
## 参照
|
||||
|
||||
- 設計指針: `CLAUDE.md`(最小の構造化 / 概念の追加は不在が問題になってから)
|
||||
- 参考実装: Claude Code の TodoWrite、OpenCode の todo tool
|
||||
- 関連: `crates/tools/src/tracker.rs`(session-lifetime 状態の前例)、`crates/pod/src/compact/worker.rs`(auto-injection レーン)
|
||||
- 後続: `tickets/session-todo-reminder.md`(注意機構)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user