初めに
最近、コーディングエージェントの pi(@earendil-works/pi-coding-agent)をよく使っています。
使い続けていて、あるとき疑問に思うようになりました。
「このエージェントのシステムプロンプト、一体どこに何が入っているんだろう。」
そこで pi のソースコードを掘り下げて、プロンプトの組み立て部分まで分解してみました。
結果は意外でした。
pi のシステムプロンプトは、長い固定テキストではないんです。
固定部分は 20 行ちょっとの小さなテンプレートで、残りは各ツールやプロジェクトファイルがそれぞれ「寄与」して、起動ごとに動的に組み立てられています。
この記事では、次の 3 つを整理します。
1. pi のシステムプロンプトの組み立て機構(コードベースで)
2. システムプロンプトの原文(英語)と日本語訳
3. 自分のエージェントのプロンプトを書くときに使える設計パターン
対象読者は、AI エージェントの開発をしていて、自前エージェントのシステムプロンプトを書きたい(あるいは見直したい)技術者です。
単なる概要ではなく、組み立てコード、プロンプト原文、実装上で効いてくる判断ポイントまで書きます。
結論
先に結論を書くと、pi のプロンプト設計は「小さいテンプレート + ツール単位の寄与」です。
システムプロンプト =
1. 固定テンプレート(persona + ツール欄 + ガイドライン欄 + docs ポインタ)
+ 2. 各ツールの 1 行紹介 (promptSnippet)
+ 3. 各ツールの利用ルール (promptGuidelines)
+ 4. 追記テキスト (APPEND_SYSTEM.md)
+ 5. プロジェクト指示 (AGENTS.md / CLAUDE.md)
+ 6. スキル・インデックス (名前 + 説明 + パスのみ)
+ 7. 作業ディレクトリ
設計上の大事な点は 4 つあります。
- 固定テンプレートは小さくする。強調すべき詳細はツール側へ寄せる
- ツール紹介・ルールはツール定義本体に持つ(単一の真実の情報源)
- プロンプトは有効なツールに合わせて条件分岐する
- ナレッジ(スキル)は全文ではなくインデックスだけ載せて、必要時に読む
先に組み立てコードを見てから、原文と日本語訳をゆっくり読みます。
1. 出所: 1 つの関数 buildSystemPrompt がプロンプトを組む
pi のシステムプロンプトは、pi-coding-agent 内の 1 関数、buildSystemPrompt が生成しています。
GitHub: earendil-works/pi(packages/coding-agent)
npm: @earendil-works/pi-coding-agent(この記事執筆時は 0.84.2)
関数: src/core/system-prompt.ts の buildSystemPrompt
オプションの形は次です。
export interface BuildSystemPromptOptions {
/** Custom system prompt (replaces default). */
customPrompt?: string;
/** Tools to include in prompt. Default: [read, bash, edit, write] */
selectedTools?: string[];
/** Optional one-line tool snippets keyed by tool name. */
toolSnippets?: Record<string, string>;
/** Additional guideline bullets appended to the default system prompt guidelines. */
promptGuidelines?: string[];
/** Text to append to system prompt. */
appendSystemPrompt?: string;
/** Working directory. */
cwd: string;
/** Pre-loaded context files. */
contextFiles?: Array<{ path: string; content: string }>;
/** Pre-loaded skills. */
skills?: Skill[];
}
一言で言うと、呼び出し側が「どのツール・どのコンテキスト・どのスキルを使うか」を決めて渡すと、関数側が最終プロンプトを組み立てる、という形です。
セッション側(agent-session.ts)がこれをどう呼び出しているかというと、ツールレジストリから各ツールの snippet と guidelines を収集し、リソースローダから SYSTEM.md・APPEND_SYSTEM.md・AGENTS 系ファイル・スキルを読み込んで、まとめて buildSystemPrompt に渡しています。
2. アセンブリのパイプライン
組み立て順は固定です。
buildSystemPrompt(options)
│
├─ customPrompt がある?
│ ├─ ある → それをベースにする(既定テンプレートを丸ごと置き換え)
│ └─ なし → 既定テンプレート(persona + tools + guidelines + docs)
│
├─ + appendSystemPrompt (追記テキスト)
├─ + <project_context> (プロジェクト指示ファイル)
├─ + <available_skills> (スキル・インデックス、read ツール有効時のみ)
└─ + Current working directory
2 つ、押さえておきたい点があります。
1 つ目は、customPrompt で既定テンプレートを置き換えても、後半の付加セクション(コンテキスト・スキル・cwd)はそのまま追加されることです。つまり「テンプレートを差し替える」と「プロジェクトコンテキストの挙動を変える」は別のレイヤになっています。
2 つ目は、スキル・インデックスは read ツールが有効なときだけ載ることです。モデルに全文を読める手段(read)がないなら、インデックスを載せても意味がない、という判断がコードにそのまま書かれています。
3. 1 層目: 固定テンプレート(原文 + 日本語訳)
既定テンプレートは、この組み立てロジック全体の中で唯一ハードコードされたテキストです。以下がその全文です。
3.1 原文(英語)
You are an expert coding assistant operating inside pi, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.
Available tools:
${toolsList}
In addition to the tools above, you may have access to other custom tools depending on the project.
Guidelines:
${guidelines}
Pi documentation (read only when the user asks about pi itself, its SDK, extensions, themes, skills, or TUI):
- Main documentation: ${readmePath}
- Additional docs: ${docsPath}
- Examples: ${examplesPath} (extensions, custom tools, SDK)
- When reading pi docs or examples, resolve docs/... under Additional docs and examples/... under Examples, not the current working directory
- When asked about: extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), pi packages (docs/packages.md), environment variables (docs/environment-variables.md)
- When working on pi topics, read the docs and examples, and follow .md cross-references before implementing
- Always read pi .md files completely and follow links to related docs (e.g., tui.md for TUI API details)
${toolsList} と ${guidelines} の 2 つの穴(スロット)に、次の層が埋め込まれます。
3.2 日本語訳
あなたは pi(コーディングエージェントのハネス)内で動作するエキスパートのコーディングアシスタントです。ファイルの読み取り、コマンドの実行、コードの編集、新規ファイルの作成を通じて、ユーザーを支援します。
利用可能なツール:
${toolsList}
上記のツールに加え、プロジェクトによって、その他のカスタムツールにアクセスできる場合があります。
ガイドライン:
${guidelines}
pi ドキュメント(ユーザーが pi 自体、その SDK、エクステンション、テーマ、スキル、TUI について質問した場合にのみ読むこと):
- メインドキュメント: ${readmePath}
- 追加ドキュメント: ${docsPath}
- サンプル: ${examplesPath}(エクステンション、カスタムツール、SDK)
- pi のドキュメントやサンプルを読む場合、docs/... は「追加ドキュメント」配下、examples/... は「サンプル」配下を解決すること。現在の作業ディレクトリを参照しないこと
- 質問の受け答え先: extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), pi packages (docs/packages.md), environment variables (docs/environment-variables.md)
- pi 関連の作業をする場合、実装前にドキュメントとサンプルを読み、.md 間の相互参照を追うこと
- pi の .md ファイルは必ず全文を読み、関連ドキュメントへのリンクも追うこと(例: TUI の API 詳細は tui.md)
3.3 意外なほど少ないテンプレート
このテンプレートを眺めると、かなり引き締まっていることに気づきます。
テンプレートに入っているもの:
- 1 文の persona(誰であるか)
- ツール欄・ガイドライン欄の 2 つのスロット
- docs ポインタ(しかも「pi 自体の質問に答える時だけ読め」と限定済み)
テンプレートに入っていないもの:
- ツールの使い方 → ツール側が寄与(4, 5 章)
- プロジェクトのルール → AGENTS.md 系(6.3 章)
- ナレッジ・ノウハウ → スキル(6.4 章)
- 出力フォーマットやトーン → 一切ない
テンプレートは「persona + スロット + docs ポインタ」だけを担当し、詳細はすべて動的レイヤに押し出しています。
固定テキストを小さく保つことで、プロンプトの修正・レビューコストを下げている、というのが pi の設計思想です。
4. 2 層目: ツール一覧は各ツールの promptSnippet
Available tools 欄はテンプレートに書かれていません。ツール側が 1 行紹介(promptSnippet)を出し、それが組み込まれます。
const tools = selectedTools || ["read", "bash", "edit", "write"];
const visibleTools = tools.filter((name) => !!toolSnippets?.[name]);
const toolsList =
visibleTools.length > 0
? visibleTools.map((name) => `- ${name}: ${toolSnippets![name]}`).join("\n")
: "(none)";
組み込まれる実際のテキストはこうなっています。
| ツール | promptSnippet(原文) | 日本語 |
|---|---|---|
| read | Read file contents | ファイル内容を読み取る |
| bash | Execute bash commands (ls, grep, find, etc.) | bash コマンドを実行する(ls, grep, find など) |
| edit | Make precise file edits with exact text replacement, including multiple disjoint edits in one call | 正確なテキスト置換による精密なファイル編集を行う(1 回の呼び出しで複数の離れた箇所の編集を含む) |
| write | Create or overwrite files | ファイルを作成または上書きする |
ここで 1 つ、大事な条件があります。
promptSnippet があるツールだけがプロンプトに表示される、というルールです。説明のないツールは素通りで、リストに出ません。
これは「単一の真実の情報源(single source of truth)」の設計です。
ツール定義 1 つの中に:
- 呼び出し仕様 (JSON Schema 相当)
- 自己紹介 (promptSnippet)
- 利用ルール (promptGuidelines)
が同居するので、ツール実装とプロンプトが別ファイルで
別々に管理されてズレる、ということが構造的に起きにくい。
ツールを増やしても減らしても、プロンプト側を別途編集する必要はありません。
5. 3 層目: ガイドラインは各ツールの promptGuidelines
Guidelines 欄も同じ仕組みです。ツールごとに利用ルールを複数寄与できます。
| ツール | promptGuidelines(原文) | 日本語要約 |
|---|---|---|
| read | Use read to examine files instead of cat or sed. | ファイル閲覧は cat や sed の代わりに read を使う |
| bash | You can inspect PI_* environment variables for current model and session details. | 現在モデル・セッション詳細は PI_* 環境変数を見ればよい |
| edit | Use edit for precise changes (edits[].oldText must match exactly) | 精密な変更は edit を使う(oldText は完全一致必須) |
| edit | When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls | 1 ファイル内の複数箇所を変更するなら edit 呼び出し 1 回に edits[] を複数エントリにして渡す |
| edit | Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit. | 各 oldText は「元のファイル」に対して一致判定される(前エントリ適用後ではない)。重複・入れ子のエディットは禁止し、近くの編集は 1 つにまとめる |
| edit | Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions. | oldText はファイル内で一意を維持する範囲で最小に。大きな不変領域をパディングしない |
| write | Use write only for new files or complete rewrites. | write は新規ファイルか全面書き直しの時のみ使う |
この他に、組み立て関数側が 2 種類のガイドラインを追加します。
// bash が有効で、grep / find / ls が無効な場合のみ
if (hasBash && !hasGrep && !hasFind && !hasLs) {
addGuideline("Use bash for file operations like ls, rg, find");
}
// 常に追加
addGuideline("Be concise in your responses");
addGuideline("Show file paths clearly when working with files");
edit の 4 本のルールを改めて見ると、ここが「ツールごとのガイドライン」の正しい粒度だと思います。
- 複数箇所の変更 → 1 回呼び出しで済ませる(往復回数の節約)
- oldText は「元のファイル」に対して一致(逐次適用との誤解を防ぐ)
- 重複・入れ子エディットを禁止(曖昧な出力を防ぐ)
- oldText は最小だが一意(巨大コンテキストをプロンプトに載せない)
これらは LLM への一般的な言い訳ではなく、そのツールの API 契約をモデルが誤用しないためのルールです。ツールとペアで書くから、ズレにくい。
6. 4 層目: 付加セクション(ファイルから読む)
テンプレートの後には、4 つの付加セクションが順に追加されます。
+ appendSystemPrompt ← APPEND_SYSTEM.md
+ <project_context> ← AGENTS.override.md / AGENTS.md / CLAUDE.md
+ <available_skills> ← 発見された SKILL.md 群のインデックス
+ Current working directory
これらはすべて「ファイルに書いておけば、プロンプトコードを触らずに入力できる」形です。
6.1 追記テキスト: APPEND_SYSTEM.md
テンプレートの直後に追加されるテキストです。プロジェクト側(.pi/APPEND_SYSTEM.md)とグローバル側(agent ディレクトリの APPEND_SYSTEM.md)の両方から発見されます。
使い方:
.pi/APPEND_SYSTEM.md → プロジェクト固有の追記(信頼されたプロジェクトのみ)
~/.pi/agent/APPEND_SYSTEM.md → グローバルな追記
効かせること:
- 会社のルール・環境説明の注入
- テンプレート本体を壊さず上流に追記する
パッケージを fork せずに挙動を変えられる、軽めの拡張ポイントです。
6.2 テンプレート差し替え: SYSTEM.md
customPrompt には、.pi/SYSTEM.md(プロジェクト)または ~/.pi/agent/SYSTEM.md(グローバル)の内容が設定されます。
存在するなら:
- 既定テンプレートを「丸ごと置き換え」る
- ただし 6.3 〜 6.5 の付加セクションは引き続き追加される
「プロンプトの全部を自前化したい」ケース用の逃げ口です。ただしスロット埋め込み(ツール一覧・ガイドラインの自動組み立て)は失われるので、必要な場合は自分側で tools・guidelines を組み込む設計になります。
6.3 プロジェクト指示: AGENTS.md / CLAUDE.md
リソースローダが探すファイル名は次の候補です。
AGENTS.override.md / AGENTS.md / AGENTS.MD / CLAUDE.md / CLAUDE.MD
内容は <project_context> ブロックとして注入されます。
<project_context>
Project-specific instructions and guidelines:
<project_instructions path="AGENTS.md">
(ファイル内容)
</project_instructions>
</project_context>
ファイルごとに path 付きタグでラップしているのがポイントで、どの指示がどのファイル由来かをモデルに明示できます。複数ファイルを積んでも混線しにくいです。
6.4 スキル・インデックス: 進行的な開示(progressive disclosure)
スキル(SKILL.md)は、名前・説明・パスのインデックスだけがプロンプトに入ります。
The following skills provide specialized instructions for specific tasks.
Use the read tool to load a skill's file when the task matches its description.
When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.
<available_skills>
<skill>
<name>pi-subagents</name>
<description>Delegate work to builtin or custom subagents ...</description>
<location>/root/.pi/agent/.../SKILL.md</location>
</skill>
</available_skills>
日本語にすると、「以下のスキルが特定のタスク用の専用手順を提供する。タスクが説明に合えば read ツールでファイルを読みなさい。相対パスはスキルディレクトリ基準で解決すること」という案内で、中身は <available_skills> に名前・説明・場所だけです。
全文は、モデルが該当タスクに当たった時点で read して読みに行きます。この「進行的な開示」には 3 つの利点があります。
- プロンプトが小さく保てる(インストールしたスキル数が大きくても)
- 全文は必要な時だけコンテキストに載る(不要なトークン消費が減る)
- スキルの追加が「ファイルの配置」だけで完結する(プロンプト変更ゼロ)
スキルを増やしたいときは SKILL.md を 1 つ置くだけ、というのが運用上とても楽です。
6.5 作業ディレクトリ
最後に 1 行だけ、Current working directory: ${cwd} が付きます。地味ですが、相対パスの解決や bash の前提に直結するので、ここが抜けているとツール呼び出しの精度が落ちます。
7. 再構成した実際のプロンプト(例)
ここまでを全部つなげると、標準 4 ツール(read / bash / edit / write)のセッションで生成されるプロンプトは、だいたい次のようになります。
You are an expert coding assistant operating inside pi, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.
Available tools:
- read: Read file contents
- bash: Execute bash commands (ls, grep, find, etc.)
- edit: Make precise file edits with exact text replacement, including multiple disjoint edits in one call
- write: Create or overwrite files
In addition to the tools above, you may have access to other custom tools depending on the project.
Guidelines:
- Use bash for file operations like ls, rg, find
- Use read to examine files instead of cat or sed.
- You can inspect PI_* environment variables for current model and session details.
- Use edit for precise changes (edits[].oldText must match exactly)
- When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls
- Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.
- Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.
- Use write only for new files or complete rewrites.
- Be concise in your responses
- Show file paths clearly when working with files
Pi documentation (read only when the user asks about pi itself, its SDK, extensions, themes, skills, or TUI):
- Main documentation: <パッケージ>/README.md
- Additional docs: <パッケージ>/docs
- Examples: <パッケージ>/examples (extensions, custom tools, SDK)
- When reading pi docs or examples, resolve docs/... under Additional docs and examples/... under Examples, not the current working directory
- When asked about: extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), pi packages (docs/packages.md), environment variables (docs/environment-variables.md)
- When working on pi topics, read the docs and examples, and follow .md cross-references before implementing
- Always read pi .md files completely and follow links to related docs (e.g., tui.md for TUI API details)
(+ APPEND_SYSTEM.md の内容)
(+ <project_context>(AGENTS.md 等がある場合))
(+ <available_skills>(スキルがある場合))
Current working directory: /path/to/project
全部で思ったより短いです。
pi のプロンプトの「賢さ」は長さではなく、この組み立て機構にあります。
8. 自分のエージェントに取り込める設計
pi の実装から、自分のエージェントプロンプトを書くときにそのまま取り込めるパターンをまとめます。
8.1 テンプレートは小さく保つ
固定テキストには persona・スロット・ポインタしか置かない。ツール単位・条件付きになりうるルールはテンプレートに入れない。
固定: 誰であるか / どのスロットがあるか / docs の場所
動的: ツール一覧 / ツールルール / プロジェクトコンテキスト / スキル
8.2 ツールに自己紹介を持たせる
ツール紹介と利用ルールを別ファイルのプロンプトに書かない。ツール定義側に promptSnippet(1 行紹介)と promptGuidelines(利用ルール)を載せる。
利点:
- 単一の真実の情報源になる(ツール定義がそのままプロンプト)
- ツールの増減でプロンプトが自動更新される
- ルールが API 契約の隣にあるので、実装変更時に古いままになりにくい
注意:
- snippet のないツールはプロンプトに出ない(ここを ON/OFF スイッチにしてよい)
8.3 プロンプトは条件分岐させる
有効になっていないツールについて喋らない。pi は「bash だけ有効で grep/find/ls が無効」のときだけファイル探索の bash ルールを入れています。
アンチパターン:
10 ツール分の使い方をプロンプトに書いておき、
実際には 2 つしか有効にしていない
8.4 ナレッジはインデックスだけ載せる
ノウハウ・手順書は全文をプロンプトに載せない。name + description + path のインデックスだけにして、モデルがマッチしたタスクで全文を読みにいく形にする。
プロンプト内: <available_skills>(名前 / 説明 / 場所)
読み込み: タスクがマッチした時だけ read で全文
pi のスキル機構はこのパターンをファイル配置だけで実装した、かなり軽量な設計です。
8.5 プロジェクト規約はファイル化して path 付きで注入
「このリポジトリではこういう手順でデプロイする」「このタブーがある」は、AGENTS.md 系のファイルに書いて、path 付きタグでプロンプトに注入する。
- 複数ファイルを積んでも出所が分かる
- プロンプトコードを触らずに規約を変更できる
- 人が読んで編集できる形(Markdown)をそのまま使える
8.6 チェックリスト
自分のエージェントのプロンプトを書くときのチェックリストです。
- [ ] 固定テンプレートは数十行以内に収まっているか
- [ ] 各ツールの紹介とルールはツール定義側に載っているか
- [ ] プロンプトは有効なツール構成に合わせて条件分岐しているか
- [ ] 大きなナレッジはインデックスのみで、全文は必要時読み込みか
- [ ] プロジェクト規約はファイル化して path 付きで注入されているか
- [ ] ツールの増減でプロンプトを手で直す必要はないか
- [ ] 作業ディレクトリ等の環境情報が含まれているか
まとめ
pi のシステムプロンプトは、結局は小さなテンプレートでした。
本物の設計は、組み立て機構のほうです。
| 層 | 内容 | 出所 |
|---|---|---|
| テンプレート | persona + スロット + docs ポインタ | buildSystemPrompt(固定) |
| ツール一覧 | ツール 1 行紹介 | 各ツールの promptSnippet |
| ガイドライン | ツールの利用ルール | 各ツールの promptGuidelines + 条件分岐 |
| 追記 | 環境・ルール追加 | APPEND_SYSTEM.md |
| コンテキスト | プロジェクト指示 | AGENTS.override.md / AGENTS.md / CLAUDE.md |
| スキル | ナレッジ・インデックス(名前 / 説明 / パス) | formatSkillsForPrompt |
| 環境 | 作業ディレクトリ | cwd |
個人的に、ここで一番大事な教訓はこれだと思います。
プロンプトは、手書きで固定される大きなテキストではなく、ツール・コンテキスト・ナレッジから組み立てられる構造であるべきです。
こうすると、プロンプトは小さく保てます。
そして、時間とともに腐りにくくなります。
是非お試しください!