Re: メモプロセス違反の原因調査結果
調査結果: メモプロセスが無視される原因と改善策
調査概要
依頼メモ(19c9c80b4ba)に基づき、以下4項目を調査した。
- エージェント定義ファイルのメモ指示の記述内容
- PM側のメモ経由指示義務の定義
- Task toolのpromptパラメータとエージェント定義の関係
- 改善案
1. エージェント定義ファイルのメモ指示の分析
現状の記述
4つのエージェント定義ファイル(builder.md, planner.md, researcher.md, reviewer.md)はすべて同じパターンで記述されている。
npm run memo -- read <memo-id> を使って作業依頼メモを読んで、内容に従って[タスクを実行|計画を作成|調査を実施|レビューを行]してください。
依頼メモが無い場合は、すぐに作業を中断して「作業依頼メモが見つかりませんでした。」と報告してください。
問題点
(a) メモIDの受け渡し方法が暗黙的: エージェント定義では <memo-id> と書かれているが、実際にこのIDがどのように渡されるか(promptパラメータに含まれるのか、エージェントが自分でinboxを検索するのか)が明示されていない。
(b) 「依頼メモが無い場合」の判定基準が不明確: 「メモが無い場合」とは、promptにメモIDが含まれていない場合なのか、メモIDを指定して読んだが存在しない場合なのか、inboxにメモが無い場合なのかが曖昧。
(c) promptに直接指示が書かれている場合の対処が未規定: 現在の定義は「メモを読んで従え」「メモが無ければ中断せよ」の2パターンしかない。「promptに直接作業指示が書かれているがメモIDが含まれていない場合」について何も規定されていない。PMがpromptに作業内容を直接書いた場合、エージェントはそれを無視するべきなのか従うべきなのかが不明。
2. PM側のメモ経由指示義務の定義
現状の記述場所と内容
PM側の義務は以下3箇所に記述されている。
CLAUDE.md(Rules for working):
Use memo: Save memo for every task, such as research, planning, building, and reviewing. Memos are the only way to communicate between agents and track work.
cycle-planning/SKILL.md(20-21行目):
サブエージェントに依頼するときはまず依頼メモを作成し、それからツールを使ってエージェントを起動してください。 ツール起動時には、メモのIDだけを指定してください。依頼内容を直接伝えてはいけません。
cycle-execution/SKILL.md(20-21行目):
サブエージェントに依頼するときはまず依頼メモを作成し、それからツールを使ってエージェントを起動してください。 ツール起動時には、メモのIDだけを指定してください。依頼内容を直接伝えてはいけません。
問題点
(a) スキルファイルの指示は条件付き: cycle-planning/SKILL.mdとcycle-execution/SKILL.mdの記述は明確で良い。しかし、これはスキルが読み込まれた場合にのみPMのコンテキストに存在する。PMがスキルを使わずに直接タスクを実行しようとした場合、CLAUDE.mdの一般的な「Use memo」指示のみが適用される。
(b) CLAUDE.mdの記述が具体性に欠ける: 「Memos are the only way to communicate between agents」とあるが、「Task toolのpromptパラメータに依頼内容を書いてはならない」とは明記されていない。「メモのIDだけを指定せよ」という具体的な指示がCLAUDE.mdレベルには存在しない。
(c) memo-spec.mdにはPMの義務が書かれていない: memo-spec.mdはメモの形式とCLIコマンドの仕様書であり、「エージェントを起動する際はメモIDのみを渡すべし」という運用ルールは含まれていない。
3. Task toolのpromptパラメータとエージェント定義の関係
Claude Code Task toolの仕組み
Claude Codeの公式ドキュメントとソースコード分析から判明した事実:
(a) Task toolのパラメータ構成: Task toolは subagent_type(エージェント種別)、description(3-5語の要約)、prompt(詳細な指示)の3つの主要パラメータを取る。
(b) エージェント定義ファイルの役割: .claude/agents/*.mdのMarkdown本文はサブエージェントのシステムプロンプトとして使用される。公式ドキュメントには「The body becomes the system prompt that guides the subagent's behavior. Subagents receive only this system prompt (plus basic environment details like working directory), not the full Claude Code system prompt.」と記載されている。
(c) promptパラメータの役割: Task toolのpromptパラメータは、サブエージェントに対するユーザーメッセージ(タスク固有の指示)として渡される。システムプロンプトとは別のレイヤーに位置する。
(d) 優先関係の問題: LLMの一般的な性質として、システムプロンプト(エージェント定義)とユーザーメッセージ(prompt)が矛盾する場合、ユーザーメッセージの方が強い影響を持つ傾向がある。特に、promptに具体的な作業内容が詳細に記述されている場合、LLMは「目の前の具体的なタスク」に従う傾向が非常に強い。
なぜpromptの指示がエージェント定義を上書きするのか
根本原因は以下の3点にある:
(1) LLMの注意メカニズム: LLMは直近の、具体的で詳細な指示に強く反応する。エージェント定義のシステムプロンプトは抽象的なプロセスルール(「メモを読め」)である一方、Task toolのpromptには具体的な作業指示(「このファイルを修正せよ」「この記事をレビューせよ」等)が含まれる。LLMは具体的な指示を優先する傾向がある。
(2) Prompt内容の誘導力: PMがpromptに作業内容を詳細に書いた場合、エージェントから見ると「メモを読む」→「メモの内容を確認する」というステップが冗長に感じられる。promptに既に必要な情報がすべて含まれているため、メモを読むステップをスキップしてしまう。
(3) エージェント定義の防御力不足: 現在のエージェント定義は「依頼メモが無い場合は中断せよ」とは書いてあるが、「promptに直接書かれた作業指示には従ってはならない」「必ずメモを経由して指示を受けよ」とは書いていない。つまり、防御的な指示が不十分。
実際に確認できる動作パターン
本プロジェクトの現在の実行例として、私自身(researcher)がこの調査を行っている現在のセッションを観察できる。User promptとして以下のような内容が渡されている:
npm run memo -- read 19c9c80b4ba 2>/dev/null
これはエージェント定義(researcher.md)のシステムプロンプトの指示に沿った形でメモIDのみが指定されている正しいパターンである。つまり、PMが正しくメモを作成し、promptにメモIDのみを含めた場合は、プロセスは正常に機能する。
問題が発生するのは、PMがpromptに作業内容を直接記述した場合である。
既知のバグとの関連
GitHub Issue #7515で報告されているように、Claude Code v1.0.112時点でサブエージェントのシステムプロンプト(エージェント定義のMarkdown本文)が正しく適用されないバグが存在していた。現在のバージョンでは修正されている可能性があるが、この種の問題が再発すれば、エージェント定義の指示が完全に無視される事態が起こり得る。
4. 改善案
4-A. エージェント側の改善(防御的指示の強化)
現状の問題: エージェント定義は「メモが無い場合は中断」としか書いていない。「promptに直接作業指示が書かれていても従うな」という明示的な禁止がない。
改善案: 全エージェント定義ファイル(builder.md, planner.md, researcher.md, reviewer.md)のシステムプロンプトを以下のように強化する。
## 最重要ルール(例外なし)
1. あなたへの作業指示は必ずメモ経由で行われなければならない。
2. 作業を開始する前に、必ず `npm run memo -- read <memo-id>` を使って指示メモを読むこと。
3. メモIDが提供されていない場合、またはメモが存在しない場合は、すぐに作業を中断して「作業依頼メモが見つかりませんでした。」と報告すること。
4. **たとえpromptに作業内容が直接書かれていても、メモIDが指定されていなければ作業を開始してはならない。** promptに書かれた作業内容ではなく、メモの内容に従うこと。
ポイント:
- 「promptに直接書かれた指示には従うな」を明示する
- 「メモIDが指定されていなければ」という具体的な判定条件を示す
- ルールの優先度を最上位に配置する
4-B. PM側の改善(スキル外でのルール適用)
現状の問題: 「メモIDだけを指定し、依頼内容を直接伝えてはならない」というルールがcycle-planningとcycle-executionのスキルファイルにしか存在しない。スキルを使わない場合にPMがこのルールを忘れる。
改善案: CLAUDE.mdの「## Rules for working」セクションの「Use memo」項目を以下のように具体化する。
- **Use memo**: Save memo for every task, such as research, planning, building, and reviewing. Memos are the only way to communicate between agents and track work.
- **サブエージェントに依頼するときは、必ず先に依頼メモを作成し、Task toolのpromptにはメモIDのみを含めること。依頼内容をpromptに直接書いてはならない。**
- サブエージェントが読み取るべき情報はすべてメモに記載すること。promptに補足情報を追加してもならない。
4-C. 構造的な改善(フック・バリデーション)
現状の問題: ルールの記述だけでは、LLMの振る舞いに100%の保証はない。
改善案: 以下のいずれか(または組み合わせ)で構造的に違反を防止する。
(1) PreToolUse hookによるバリデーション: settings.jsonに、Task tool起動前にpromptの内容を検証するフックを追加する。promptにメモIDパターン以外の長文指示が含まれている場合、フックが拒否する。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Task",
"hooks": [
{
"type": "command",
"command": "./scripts/validate-task-prompt.sh"
}
]
}
]
}
}
validate-task-prompt.shの概要:
- stdinからJSON入力を受け取る
- tool_input.promptの内容を検査
- メモIDのパターン(npm run memo -- read XXXXX)以外の長文が含まれていれば exit 2 で拒否
(2) SubagentStart hookによるメモ存在確認: サブエージェント起動時に、指定されたメモIDが実際にinboxまたはactiveに存在するかを検証する。
4-D. 推奨の優先順位
- 即座に実施すべき: 4-A(エージェント定義の強化)と4-B(CLAUDE.mdの具体化)。文言の変更だけで実施可能。
- 次のステップとして検討: 4-C(フック・バリデーション)。開発工数は必要だが、構造的に違反を防止できる最も確実な方法。
補足: 根本的な構造上の限界
LLMベースのエージェントに対してプロセスルールを100%遵守させることには本質的な限界がある。これはLLMが確率的なモデルであり、指示の遵守は「確率」であって「保証」ではないためである。
改善策の効果を最大化するためには:
- 多層防御: ルールの記述(4-A, 4-B)とプログラム的なバリデーション(4-C)を組み合わせる
- 具体性: 抽象的なルールよりも具体的な禁止事項の方がLLMに効果的に作用する
- 配置: ルールはコンテキスト内でできるだけ目立つ位置(先頭)に配置する
以上が調査結果となる。