Faceted Prompting ── AIプロンプトに関心の分離を持ち込む
はじめに
プロンプトが巨大化している。どれを修正すべきなのだろうか。
エージェントの役割、行動規範、タスク固有の指示、ドメイン知識、出力形式。これらがすべて1つのプロンプトファイルに混在している。最初は小さかったはずのプロンプトが、気づけば数百行に膨れ上がっている。
これが直近の私の課題でした。そして、この問題に対する解決策として「Faceted Prompting」というアーキテクチャを考案しました。本稿ではこのアーキテクチャの考え方と実践方法を紹介します。
モノリシックなプロンプトが生む3つの問題
プロンプトがモノリシックになると、何が困るのでしょうか。私が直面した問題は3つありました。
まず、再利用できません。2つのステップが同じレビュアーのペルソナを必要としつつ、指示が異なる場合を考えてみてください。プロンプト全体を複製するしかありません。ペルソナ部分だけを取り出して使い回すことができないのです。
次に、暗黙的な結合が生まれます。コーディング規約を変更したいとき、それを参照するすべてのプロンプトを編集する必要があります。どこに何が書いてあるのか、すべて把握していなければなりません。規約が10個のプロンプトに散らばっていれば、10個のファイルを修正することになります。
そして、責任の所在が不明確になります。プロンプトのどの部分がエージェントの「役割」を定義し、どの部分が「やるべきこと」を定義しているのか。読み返してみても、区別がつかないことがあります。メンテナンスのたびに、どこを修正すべきか悩むことになります。
これらは、ソフトウェア開発で言うところの「モノリシックアーキテクチャの問題点」とまったく同じです。
Faceted Prompting ── 関心の分離をプロンプトに適用する
解決のアイデアは、ソフトウェア工学の基本原則から来ています。
関心の分離(Separation of Concerns)をプロンプト設計に適用するのです。
エージェントごとに1つのモノリシックなプロンプトを書く代わりに、「何の関心を扱っているか」で独立した再利用可能な単位に分解します。そしてワークフローのステップごとに宣言的に合成する。これがFaceted Promptingの核心です。
なぜ「Faceted(ファセット)」なのか。宝石のカット面を思い浮かべてください。ダイヤモンドは多面体にカットされていますが、それぞれの面(ファセット)は区別可能な独立した単位です。しかし、すべての面が合わさって一つの輝きを生み出す。プロンプトも同じように、独立した関心を組み合わせて一つのプロンプトを構成するわけです。
5つの関心
Faceted Promptingはプロンプトを5つの独立した関心に分解します。
| 関心 | 答える問い | 例 |
|---|---|---|
| Persona | 誰として判断するか? | 役割定義、専門性 |
| Policy | 何を守るか? | 禁止事項、品質基準、優先順位 |
| Instruction | 何をするか? | 目標、ステップ固有の手順 |
| Knowledge | 何を参照するか? | 前提知識、ドメイン資料、API仕様 |
| Output Contract | どう出すか? | 出力構造、レポートテンプレート |
なぜこの5つなのか
PersonaとInstructionは最低限必要なものです。エージェントが誰で、何をすべきかを定義しなければなりません。
しかし実務では、さらに3つの関心が独立した軸として現れます。
Policyはタスクをまたがって適用される規約・基準を捉えます。「何を守るか」を定義する関心であり、禁止事項(フォールバック濫用の禁止、未使用コードの禁止)、品質基準(REJECT/APPROVE判定)、優先順位(正確性 > 速度)などを含みます。
コーディングポリシーは機能実装でもバグ修正でも同じように適用されるものです。ポリシーは「横断的関心事」であり、作業内容に関係なく守るべきルールを規定します。
Knowledgeはエージェントが判断の前提として参照する情報を捉えます。アーキテクチャ文書はプランナーにもレビュアーにも必要なものです。ナレッジをインストラクションから分離することで重複を防ぎ、インストラクションを手順に集中させることができます。
ナレッジは記述的(「このドメインはこうなっている」)であり、規範的(「こうすべき」)なルールはPolicyに属します。この区別が重要です。
Output Contractは作業そのものとは独立した出力構造を捉えます。同じレビューフォーマットをアーキテクチャレビュアーとセキュリティレビュアーの両方で使えます。出力形式の変更がエージェントの振る舞いに影響しません。
配置の設計 ── system promptとuser message
LLMに渡せるスロットはsystem promptとuser messageの2つだけです。5つの関心はこの2つに配置されます。
System Prompt:
┌─────────────────────────────────────────────┐
│ Persona — エージェントの役割・専門性・行動姿勢 │
└─────────────────────────────────────────────┘
User Message:
┌─────────────────────────────────────────────┐
│ Knowledge — 判断の前提となる参照資料 │
│ Instruction — このステップでやるべき手順 │
│ Output Contract — 出力の構造定義 │
│ Policy — 守るべきルール・禁止事項・品質基準 │
└─────────────────────────────────────────────┘
Personaはエージェントのアイデンティティであり、タスクによって変わりません。system promptに置くことでLLMの応答全体を方向付けます。
残りの4つはステップごとに変わるため、user messageに合成します。
ここで注目すべき設計判断があります。Policyをuser messageの末尾に配置しているのです。
これには理由があります。LLMは直前に読んだ内容に強く影響される傾向があります(recency効果)。禁止事項やREJECT基準といった制約は、出力生成の直前にあることで遵守されやすくなります。
Knowledge → Instruction → Output Contract → Policyという流れは「文脈を理解 → 作業を理解 → 出力形式を確認 → 制約を確認」という自然な認知順序にもなっています。
各ファセットの具体例
それぞれのファセットがどのようなファイルになるのか、具体例を見ていきましょう。
Persona ──
personas/architecture-reviewer.md
system promptに配置されます。役割の定義、境界、行動姿勢のみを含みます。
# Architecture Reviewer
あなたはソフトウェアアーキテクチャの専門家です。
コードの構造・設計・保守性を評価します。
## 役割の境界
**やること:**
- 構造・設計の妥当性検証
- コード品質の評価
- 変更スコープの適切性確認
**やらないこと:**
- セキュリティ脆弱性のレビュー(Security Reviewerの仕事)
- 自分でコードを書く
## 行動姿勢
- 完璧な設計を求めない。現状の制約下で最善かを判断する
- 既存コードベースの規約を尊重するポイントは、ステップ固有の手順をここに書かないことです。Personaは「誰として振る舞うか」だけを定義します。
Policy ──
policies/coding.md
タスクをまたがって適用される共有ルールです。規範的(「こうすべき」)な内容を記述します。
# コーディングポリシー
## 原則
| 原則 | 基準 |
|------|------|
| DRY | 3回以上の重複はREJECT |
| Fail Fast | 不正状態は早期にエラー |
| 最小権限 | 必要最小限のスコープ |
## 禁止事項
- **未使用コード** - 「念のため」のメソッド、将来用フィールド
- **オブジェクトの直接変更** - スプレッド演算子で新規作成
- **フォールバックの濫用** - `?? 'default'` で不確実性を隠さないこのポリシーファイルは、実装ステップでもレビューステップでも共通して参照されます。
Knowledge ──
knowledge/architecture.md
判断の前提となる参照情報です。記述的(「こうなっている」)な内容を記述します。
# アーキテクチャ知識
## レイヤー構造
依存の方向: 上位層 → 下位層(逆方向禁止)
| レイヤー | 責務 | 依存先 |
|---------|------|--------|
| Controller | HTTPリクエスト処理 | Service |
| Service | ビジネスロジック | Repository |
| Repository | データアクセス | なし |
## ファイル構成
| 基準 | 判定 |
|------|------|
| 1ファイル300行超 | 分割を検討 |
| 1ファイルに複数の責務 | REJECT |
| 循環依存 | REJECT |Instruction ──
instructions/implement.md
このステップ固有の手順です。命令形で記述します。
計画に基づいてタスクを実装してください。
**やること:**
1. 変更スコープを宣言する
2. コードを実装する
3. テストを書いて実行する
4. 判断ログを記録する
**注意:** Previous Response がある場合は差し戻しです。
指摘事項を踏まえて修正してください。Output Contract ──
output-contracts/review.md
出力の構造を定義します。エージェントはこの形式に従って出力します。
```markdown
# アーキテクチャレビュー
## 結果: APPROVE / REJECT
## サマリー
{1-2文で結果を要約}
## 確認した観点
| 観点 | 結果 | 備考 |
|------|------|------|
| 構造・設計 | ✅ | - |
| コード品質 | ✅ | - |
| テストカバレッジ | ✅ | - |
## 問題点(REJECTの場合)
| # | 場所 | 問題 | 修正案 |
|---|------|------|--------|
| 1 | `src/file.ts:42` | 問題の説明 | 修正方法 |
```宣言的な合成
Faceted Promptingの中核メカニズムは宣言的な合成です。
ワークフロー定義が各ステップでプロンプトの内容を直接埋め込むのではなく、どの関心を組み合わせるかを宣言します。
主要な特性は次のとおりです。
- 各ファイルは1つの関心だけを持つ。ペルソナファイルには役割と専門性のみを記述し、ステップ固有の手順は書かない。
- 合成は宣言的。ワークフローはどの関心を組み合わせるかを記述し、プロンプトをどう組み立てるかは記述しない。
- 自由に組み合わせ可能。同じ
coderペルソナを異なるポリシーとインストラクションで異なるステップに使える。 - ファイルが再利用の単位。同じファイルを指すことでポリシーをワークフロー間で共有する。
TAKTでの実装例
TAKT はFaceted PromptingをYAMLベースのワークフロー定義(TAKTでは「ピース」と呼びます)で実装しています。各関心はセクションマップで短いキーにマッピングされ、各ステップ(TAKTでは「ムーブメント」と呼びます)からキーで参照されます。
name: my-workflow
max_iterations: 10
initial_movement: plan
# セクションマップ — キー: ファイルパス(このYAMLからの相対パス)
personas:
coder: ../personas/coder.md
reviewer: ../personas/architecture-reviewer.md
policies:
coding: ../policies/coding.md
review: ../policies/review.md
instructions:
plan: ../instructions/plan.md
implement: ../instructions/implement.md
knowledge:
architecture: ../knowledge/architecture.md
output_contracts:
review: ../output-contracts/review.md
movements:
- name: implement
persona: coder # WHO — personas.coder を参照
policy: coding # RULES — policies.coding を参照
instruction: implement # WHAT — instructions.implement を参照
knowledge: architecture # CONTEXT — knowledge.architecture を参照
edit: true
rules:
- condition: Implementation complete
next: review
- name: review
persona: reviewer # 異なる WHO
policy: review # 異なる RULES
instruction: review # 異なる WHAT(共有も可能)
knowledge: architecture # 同じ CONTEXT — 再利用
report:
name: review.md
format: review # OUTPUT — output_contracts.review を参照
edit: false
rules:
- condition: Approved
next: COMPLETE
- condition: Needs fix
next: implementエンジンは各キーをファイルに解決し、内容を読み込み、実行時に最終的なプロンプトを組み立てます。ワークフローの作者がモノリシックなプロンプトを書くことはありません。どのファセットを組み合わせるかを選択するだけです。
合成後のプロンプト
上記5つのファイルがエンジンによって合成されると、最終的に次のような形でLLMに渡されます。
System Prompt
# Architecture Reviewer
あなたはソフトウェアアーキテクチャの専門家です。
コードの構造・設計・保守性を評価します。
## 役割の境界
...(省略)User Message
## Knowledge
### レイヤー構造
依存の方向: 上位層 → 下位層(逆方向禁止)
...
---
## User Request
ユーザー認証モジュールにJWTトークンの検証機能を追加してください。
---
## Instructions
計画に基づいてタスクを実装してください。
...
---
## Output Contract
以下のフォーマットでレポートを出力してください。
...
---
## Policy
### 原則
| 原則 | 基準 |
...
### 禁止事項
- **未使用コード** - ...このように、独立したファイルがランタイムで1つのプロンプトに組み立てられます。ファイルの内容を変えればプロンプトが変わり、別のファイルを指せば別の組み合わせになるのです。
既存手法との違い
Faceted Promptingは既存のプロンプト設計手法とどこが違うのでしょうか。
| 手法 | 内容 | 本手法との違い |
|---|---|---|
| Decomposed Prompting (Khot et al.) | タスクをサブタスクに分解して異なるLLMに委任 | 分解するのはタスクではなくプロンプトの構造 |
| Modular Prompting | XML/HTMLタグを使った単一プロンプト内のセクション分け | 関心を独立ファイルに分離し、宣言的に合成する |
| Prompt Layering (Airia) | スタック可能なプロンプトセグメント | 管理ツールであり、プロンプト設計のデザインパターンではない |
| PDL (IBM) | YAMLベースプロンプトプログラミング言語 | 制御フローが焦点で、関心の分離ではない |
| Role/Persona Prompting | 役割を割り当ててレスポンスを方向付ける | ペルソナは5つの関心の1つにすぎない |
核心的な違いは次の点にあります。既存手法はタスク(何をするか)を分解するか、プロンプトの構造(どう書式化するか)を整理します。Faceted Promptingはプロンプトの関心(各部分がなぜ存在するか)を独立した再利用可能な単位に分解するのです。
実用上の利点
ワークフロー作者にとって
- コーディングポリシーを1つのファイルで変更すれば、それを使うすべてのワークフローに反映される
- 既存のペルソナ、ポリシー、インストラクションを組み合わせて新しいワークフローを作れる
- 各ファイルを単一の責務に集中させられる
チームにとって
- プロンプトを複製せずにプロジェクト間でポリシー(品質基準・禁止事項)を標準化できる
- ドメイン専門家がナレッジファイルを管理し、ワークフロー設計者がインストラクションを管理する分業ができる
- 個々の関心を独立してレビューできる
エンジンにとって
- プロンプト組み立ては決定的 ── 同じワークフロー定義とファイルからは同じプロンプトが構築される
- ポリシーの配置を最適化できる(例: recency効果を活用して末尾に配置し、制約遵守を強化)
- 各関心を他の部分に影響を与えずにステップごとに注入・省略・上書きできる
まとめ
Faceted Promptingは、関心の分離(Separation of Concerns)をAIプロンプト工学に適用するデザインパターンです。
プロンプトを5つの独立した関心 ── Persona、Policy、Instruction、Knowledge、Output Contract ── に分解し、宣言的に合成することで、再利用可能で保守しやすく透明なマルチエージェントワークフローを実現します。
モノリシックなプロンプトに苦しんでいる方、複数のエージェントで同じポリシーを共有したい方、プロンプトの構造を整理したい方。ぜひこのアーキテクチャを試してみてください。