メインコンテンツまでスキップ

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.mdgit 操作の規範規範 (Policy)
rules/safety-gate.md破壊的操作の規範規範 (Policy)
skills/commit/SKILL.mdcommit 手順手順 (Instruction) なのに規範文言が混入

書き手の頭の中では「観点が違うから別ファイル」という整理がありますが、各観点は規範であって手順ではない、という分解ができていません。観点 (= what about) で分けるのではなく、関心 (= what kind of) で分けることを徹底すれば、同じ規範が 4 箇所に散ることは構造的に起きにくくなります。

Faceted Prompting はこの「関心で分ける」軸を 5 つに固定したものです。

2.2. 5 つの関心

新ハーネスでは、プロンプトに含まれる情報を次の 5 種類に分類します。

facetwhat kind of置き場
Persona誰 (= 振る舞いの主体)agents/*.md body + CLAUDE.md 冒頭「あなたは critical な plan 検証者」「main agent は ... のように振る舞う」
Policyhow (= 規範、こうあってほしい)rules/*.md「主張は証跡で裏付ける」「commit は明示トリガー語が必要」
Instructionwhat to do (= 手順)skills/*/SKILL.md「① architect spawn → ② plan-reviewer → ③ 人間承認 ...」
Knowledgewhat is (= 記述的知識)knowledge/*.md (共通) / repos/<repo>/ (固有)API 仕様 / 用語集 / 設計判断の履歴
Output Contractwhat comes out (= 成果物テンプレート)output-contracts/*.mdspec / 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.shgit 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 行に収まる形になります。

1 ファイル 1 関心と DRY の関係

「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 上に独自移植したかを対応表付きで掘り下げます。