Faceted Prompting: 5 関心分離
本連載は執筆時点のハーネス実環境 (設定ファイルの内容、実行記録、ファイル数) を素材にした記録です。ハーネス本体は公開後も改修が続いているため、引用したファイル内容や個数、手順は現在の実体と異なる場合があります。本連載の主眼は個々の値ではなく設計の考え方 (5 関心分離、検証ゲート、成長規律) にあります。
本章は、再構築の中心思想である Faceted Prompting を扱います。
「プロンプトを 5 つの関心に分離し、ワークフロー定義時に宣言的に合成する」という整理軸です。Faceted Prompting は nrslib/takt (AI Agent Orchestration Framework、MIT、nrslib 作) の公式概念で、正典 docs で定義されています。本連載で筆者由来なのは「Claude Code のどの機構にマッピングするか」「合成戦略をどう設計するか」の独自移植判断であり、5 関心という分解そのものは takt 由来である、という前置きをした上で進めます (詳細な takt 紹介は 次章)。
2.1. なぜ「分離」が必要だったか
前章で扱った 4 症状のうち、症状 2 (同種指示の複数 facet 散在) と症状 4 (silent drop) は、本質的に境界の曖昧さが原因でした。
「git commit は明示トリガー語が出るまで実行しない」という規範が、再構築前に 4 箇所に書かれていた例を再掲します。
- CLAUDE.md の主要規範 section
- rules/git-workflow.md
- rules/safety-gate.md
- skills/commit/SKILL.md
これらが分散していた理由を分解すると、次のようになります。
| 場所 | 書き手の言い分 | 実態 |
|---|---|---|
| CLAUDE.md | 主要規範を冒頭で宣言する責務 | 内容は規範 (Policy 相当) |
| rules/git-workflow.md | git 操作の規範 | 規範 (Policy) |
| rules/safety-gate.md | 破壊的操作の規範 | 規範 (Policy) |
| skills/commit/SKILL.md | commit 手順 | 手順 (Instruction) なのに規範文言が混入 |
書き手の頭の中では「観点が違うから別ファイル」という整理がありますが、各観点は規範であって手順ではない、という分解ができていません。観点 (= what about) で分けるのではなく、関心 (= what kind of) で分けることを徹底すれば、同じ規範が 4 箇所に散ることは構造的に起きにくくなります。
Faceted Prompting はこの「関心で分ける」軸を 5 つに固定したものです。
2.2. 5 つの関心
新ハーネスでは、プロンプトに含まれる情報を次の 5 種類に分類します。
| facet | what kind of | 置き場 | 例 |
|---|---|---|---|
| Persona | 誰 (= 振る舞いの主体) | agents/*.md body + CLAUDE.md 冒頭 | 「あなたは critical な plan 検証者」「main agent は ... のように振る舞う」 |
| Policy | how (= 規範、こうあってほしい) | rules/*.md | 「主張は証跡で裏付ける」「commit は明示トリガー語が必要」 |
| Instruction | what to do (= 手順) | skills/*/SKILL.md | 「① architect spawn → ② plan-reviewer → ③ 人間承認 ...」 |
| Knowledge | what is (= 記述的知識) | knowledge/*.md (共通) / repos/<repo>/ (固有) | API 仕様 / 用語集 / 設計判断の履歴 |
| Output Contract | what comes out (= 成果物テンプレート) | output-contracts/*.md | spec / plan / story / done / review のテンプレ |
それぞれを掘り下げます。
Persona — 誰
Persona は 振る舞いの主体を定義する facet です。
- main agent の Persona:
CLAUDE.md冒頭の「誰と話すか」section - subagent の Persona: 各
agents/<name>.mdの body (= system prompt)
Persona に書くのは「あなたは誰か」「何を主目的とするか」「どんなトーンで応答するか」だけです。具体的な規範や手順は書きません。例として、現ハーネスの agents/plan-reviewer.md の Persona 部分を引用します。
あなたは critical な plan 検証者。spec.md 原本と plan.md を照合し VERDICT を出す。
実装はしない (Write は review.md 書き込み専用、コードは変更しない)。
これは「誰」(critical な plan 検証者) と「主目的」(VERDICT を出す) と「制約」(実装しない) を 1 文ずつ書いただけで、検証観点や VERDICT の形式は別途 ## 検証観点 と ## 出力契約 section に分けています。Persona の文面は責務を起動するスイッチで、具体規範は別 section に分けるのが運用上の hygiene です。
再構築前の agents/<name>.md は、Persona の中に commit 規律や verification 規律が混入していて、subagent ごとに同じ規律が違う文言で書かれていました。これは Persona と Policy の境界が曖昧だったことが直接の原因です。
Policy — how
Policy は how (こうあってほしい) を書く facet です。
- 置き場:
rules/*.md - 配信: 多くは
paths:なし常時ロード (subagent 到達必須のため) - サイズ上限: 1 ファイル ≤30 行
Policy の代表が rules/verification.md です。冒頭の数行を引用します。
# Policy: verification (検証)
> paths なし = 常時ロード。developer / auditor (subagent) が依存するため path-scoped 禁止。
## 主張は証跡で裏付ける
- テスト・grep・コマンドの**出力を証跡として示す**。「直した」「動く」を出力なしで断定しない。
- コマンド出力は**全体を確認**してから結論する。`head` / `tail` の打ち切りで「該当なし」と誤断定しない。
- subagent / ツールの主張を**鵜呑みにしない**。重要な結論は独立に再確認する。
書かれているのは「こう判断してほしい」「こう振る舞ってほしい」という規範であって、具体手順は含まれません。「テストを走らせる」とか「grep を実行する」のような手順は、別の Instruction (= SKILL.md) で書きます。
Policy 規律として 1 ファイル 1 関心を徹底し、verification.md には verification のことだけ、fact-check.md には fact-check のことだけ、というように厳格に分けます。これにより、「同じ規範が複数 Policy ファイルに散る」現象を構造的に防ぎます。
Instruction — what to do
Instruction は 手順 (この順番でこう動く) を書く facet です。
- 置き場:
skills/<name>/SKILL.md - 配信: 起動キーワード (
/autodev等) を main agent (= orchestrator) が拾うと SKILL.md がロードされ、main agent 自身がフローを回す。subagent には SKILL.md 本文そのものを届ける経路はなく、orchestrator が必要な手順・受入基準を story.md (または spawn prompt) に合成・embed して渡す - サイズ上限: 1 ファイル ≤80 行 (autodev だけ ≤100 行を許容)
Instruction の代表が skills/autodev/SKILL.md です。冒頭部分を引用します。
---
name: autodev
description: 仕様から実装まで subagent 連鎖でオーケストレーションする。⓪'-⑤ の fail-closed 検証ゲートループ。/autodev <spec パス> で起動。
---
`/autodev <repo>/specs/<feature>.md` で起動。あなた (main agent) が orchestrator (takt エンジン役) として ⓪'-⑤ を回す。
## ⓪' 起動前 assert + resume 判定
- spec パス引数を realpath で絶対化 → `~/.claude/repos/<repo>/...` から `<repo>` 抽出 → ...
- ...
## ① architect spawn (read-only, fresh)
spawn prompt に spec.md + per-repo 知識を絶対パス注入 → architect が plan.md を生成
書かれているのは順序を持った手順であって、規範文言ではありません。「読み手はこう動く」という手順を、step ごとに番号付きで宣言します。
Instruction に規範を書かないのが鍵です。「証跡を残す」「commit 前に確認する」のような規範は Policy で書かれているはずで、Instruction の手順の中で再掲する必要はありません (再掲すると同期コストが上がる)。Instruction の役割は「Policy を順序立てて発火させる」ことに留めます。
Knowledge — what is
Knowledge は 記述的知識 (what is) を書く facet です。
- 置き場 (共通):
knowledge/*.md(bootstrap 時点では空で、必要が出てから追加する方針。後日 usage-driven で追加された) - 置き場 (固有):
~/.claude/repos/<repo>/配下 (CLAUDE.md / CONTEXT.md / INDEX.md / specs/ / handoff/) - 配信: 索引 (CLAUDE.md / INDEX.md) からの参照 + 必要時 on-demand Read
Knowledge は最も自由度の高い facet です。「事実」「仕様」「用語」「履歴」など、規範でも手順でもない記述的な情報をまとめます。
代表例として ~/.claude/repos/autodev-smoke/CONTEXT.md (用語集) の一部を引用します。
# autodev-smoke — 用語集 (Ubiquitous Language)
- **fizzbuzz(n)**: 整数 n を受け取り、3 の倍数なら `"Fizz"`、5 の倍数なら `"Buzz"`、
両方の倍数なら `"FizzBuzz"`、それ以外は n を文字列化して返す関数。
- **palindrome-check(s)**: 第 1 引数 s を受け取り、s が Unicode code point 単位で
palindrome なら exit 0、そうでなければ exit 1 で終了する CLI。空文字列は palindrome
扱い (exit 0)。raw 比較 (case-sensitive)。
書かれているのは記述的な事実 (= 用語の定義) であり、規範でも手順でもありません。
Knowledge の特徴は 本文コピー禁止が許される唯一の facet であることです。Policy / Instruction / Persona / Output Contract は、他 facet から本文を引用する場合は基本コピーで届けます (silent drop 回避のため)。Knowledge だけは「索引からの参照 + 必要時 Read」で間に合います。これは Knowledge が記述的で、合成順や読み逃しが規範違反に直結しないためです。
Output Contract — what comes out
Output Contract は 成果物テンプレートを定義する facet です。
- 置き場:
output-contracts/*.md - 配信: 各 agent body の
## 出力契約section が該当ファイルを名指しし、subagent 自身の Read で参照する (Instruction と同じく、frontmatter による preload 経路は使わない) - サイズ: 各テンプレが ≤30 行程度
Output Contract は nrslib/takt の公式 facet (Workflow YAML 内で各 step が持つ output_contracts: フィールドに相当) を本ハーネスの再構築で独立化したものです。subagent の出力フォーマットをテンプレート化して、main agent が機械的に grep / parse できる形に揃えます。
代表例として output-contracts/review.md を引用します。
VERDICT: PASS|FAIL
> Output Contract (テンプレート)。汎用検証レポート。plan-reviewer (spec ⇔ plan) と
> auditor (done.md 証跡 ⇔ spec 原本) が共用する。
## 一次信号
<plan-reviewer: spec の pass 条件カバレッジ / auditor: done.md 証跡 ⇔ spec 原本から
再導出した期待の照合結果>
## 照合詳細 (N/A 許容)
- 仕様カバレッジ / 非自明性 (空実装・no-op で通らないか):
- code ⇔ spec (auditor: `git diff <base>...<branch>` と仕様に矛盾がないか):
- doc 更新漏れ (story 明示分 = FAIL / 未列挙の不整合 = Minor warning):
- per-repo policy 違反:
## 指摘 (FAIL 時)
<差し戻し先のパスと具体的な修正指示>
先頭行が VERDICT: PASS|FAIL で固定されている点が重要です。これにより、main agent (= orchestrator) は
grep '^VERDICT:' .../review.md
だけで分岐判定でき、review.md 全文を context に読み込まなくて済みます。長文 review の context 圧迫を構造的に避ける設計です。
Output Contract は再構築前のハーネスには存在しなかった概念で、autodev フロー (= 検証ゲートループ) を成立させる土台として takt 公式 facet を採用しました。
2.3. hooks は facet でなく enforcement 層
5 つの関心とは別に、hooks/ (Computational Sensor) があります。これは facet ではなく enforcement 層 として独立に扱います。
facet が Inferential Guide (LLM が解釈する指示) であるのに対し、hooks は Computational Sensor (script が機械判定する強制器) です。Guides × Sensors の 4 象限フレームでいうと、5 facet は十字の左半分 (Inferential 側、主に Guides) のレイヤ分解で、hooks は右半分 (Computational 側、主に Sensors) の強制器になります。
hooks の役割は、Policy / Output Contract が確率的に違反される時に、deterministic に止めることです。例えば「commit は明示トリガー語が必要」という Policy (rules/git.md) は、確率的に守られないことが実証されたため、hooks/check-git-commit-gate.sh で git commit 実行時に強制的にゲートをかけています (詳細は第 5 章)。
hooks を facet にしない理由は、enforcement は規範や手順の本体ではなく、それらを強制する道具だからです。本体は 5 facet 側にあり、hooks は本体が走った後に check するだけで、本体の知識を持ちません。
2.4. 振り分けルート: どこに置くか迷ったときの判断順序
5 facet の境界を運用するうえで、最も重要なのが 新しい指示をどこに置くか の判断です。
新ハーネスでは次の判断ルートを固定しています。
このフローチャートに従うと、
- Policy に手順を書きたくなったら → それは Instruction、
skills/に置く - Instruction に規範を書きたくなったら → それは Policy、
rules/に置く - Knowledge に手順を書きたくなったら → それは Instruction、別の場所
- どこにも置けない指示 → 思想として CLAUDE.md 冒頭に書く or facet 化不要
の判断ルートが固定されます。再構築前は「同じ指示が 4 か所に散る」現象が起きていましたが、これは 5 関心の境界が曖昧だったためでした。
- 「これは規範か手順か」で迷う場合: 動詞が「〜する」(手順) か「〜であるべき」(規範) かで判定する
- 「Policy か Persona か」で迷う場合: その文を消したら誰の振る舞いが変わるかで判定する。subagent 全体が変わるなら Policy、特定 subagent だけが変わるなら Persona
- 「Knowledge か Instruction か」で迷う場合: その文が手順に組み込まれないと無意味なら Instruction、独立に参照されうるなら Knowledge
- 「Output Contract か Knowledge か」で迷う場合: それが他の facet から
grep/parseされるかで判定する。されるなら Output Contract
2.5. 1 ファイル 1 関心の運用
5 関心の境界を引いた上で、もう 1 つの規律が「1 ファイル 1 関心」です。
これは「1 ファイルの中に複数の関心 (= Policy + Instruction や、Policy + Knowledge) を混ぜない」という規律です。具体的には次のような形で運用しています。
rules/verification.mdは verification の Policy だけを書く (手順や知識は混入させない)rules/git.mdは git 操作の Policy だけ (具体的な commit コマンド例は書かない)skills/autodev/SKILL.mdは autodev の手順だけ (規範文言は別 Policy を参照する)agents/auditor.mdの body は auditor の Persona だけ (verification 規範は rules/verification.md を参照する形にし、本体を引用しない)
再構築前の rules/ には、規範と具体例と手順がしばしば混在していました。例えば rules/git-workflow.md には「commit は明示トリガー語が必要」(規範) と「commit するときは git add <file> で個別指定」(手順 + 規範) と「broken hooks の対応は ...」(手順) が混ざっていました。1 ファイル 1 関心に切り直すと、これらは
- 規範 →
rules/git.md - 手順 →
skills/<関連スキル>/SKILL.md
に分散され、それぞれが ≤30 行に収まる形になります。
「Policy と Instruction が同じ事項を書くのは DRY 違反ではないか」という疑問があり得ます。新ハーネスではこれをDRY の適用境界で整理しています。
- 関心の境界を越えた DRY (= 同じ規範を 1 ファイルにまとめる) は徹底する
- 関心の境界の中での DRY (= Instruction が Policy を再掲しない) は徹底する
- 関心の境界をまたぐ DRY (= Instruction が Policy をコピーする) は避ける
Instruction が Policy を再掲すると、同期コストが指数的に上がります。Instruction は Policy への参照 (リンク) で済ませ、Policy の本文は Policy ファイルにだけ書きます。これにより同期コストが構造的に圧縮されます。
2.6. 本章のまとめ
- Faceted Prompting は 5 関心 (Persona / Policy / Instruction / Knowledge / Output Contract) でプロンプトを分解する整理軸 (nrslib/takt の公式概念を採用、語自体は本連載の創作ではない)
- hooks は facet でなく enforcement 層として独立扱い (Computational Sensor)
- 新規指示の振り分けは 5 択 + 1 (hooks) のフローチャートで判定し、迷いを最小化する
- 1 ファイル 1 関心 を徹底し、関心境界をまたぐ DRY は避ける (Instruction は Policy を参照、本文コピーしない)
- 再構築前の規範重複問題は、関心境界の曖昧さが直接の原因でした。境界を 5 つに固定したことで、同じ規範が複数 facet に散る現象が構造的に減りました
次章では、本連載のもう 1 つの核である takt 思想の採用 を扱います。Faceted Prompting の出自である nrslib/takt 本体を紹介し、本ハーネスがどう Claude Code 上に独自移植したかを対応表付きで掘り下げます。