Claude Codeのシステムプロンプト:エージェント開発者のための設計上の教訓
先月、社内のコーディングエージェントが、削除すべきでない生成ファイルを削除し、それによって発生したテストの失敗を「修正」したうえで、自信満々に成功を報告するのを目にしました。根本的な不具合は、モデルの品質ではありませんでした。問題は境界の設計にありました。エージェントには広すぎる編集権限が与えられ、生成物に関するルールは曖昧で、破壊的な操作の前に確認を求める要件もありませんでした。
だからこそ、最近公開の場で交わされているClaudeやClaude Codeのシステムプロンプトに関する議論には価値があります。ただし、流出したプロンプトや収集されたプロンプトを絶対的な正解として扱うべきではありません。公開されたプロンプトは、非公式、不完全、古いものであることが多く、すでに存在しない製品の状態から抽出されている場合もあります。Claude Code自体も急速に進化しており、現在のClaudeのラインアップであるOpus 4.8、Sonnet 4.6、Haiku 4.5、そして1Mのコンテキストを備えたFable 5は、エージェント開発者にとって、以前の世代とは大きく異なるトレードオフをもたらします。
しかし、興味深い教訓は「このプロンプトをコピーすること」ではありません。役に立つのはアーキテクチャ上の教訓です。成熟したコーディングエージェントは、操作上の境界、指示の優先順位、確認ルール、ファイルアクセスの規律、失敗時の振る舞いを、モデルを取り巻くシステムに組み込んでいます。
この記事では、そうした教訓を取り上げます。
プロンプトをコピーせずに学ぶ、Claude Codeのシステムプロンプトの教訓
「Claude Codeのシステムプロンプト」という言葉は、「本格的なコーディングエージェントは、モデルにどのような振る舞いを指示するのか」という問いの略称になっています。プロンプトの発掘作業に向かうのであれば、それは誤った問いですが、よりよいエージェント設計につながるのであれば、適切な問いです。
実際には、システムプロンプトは層のひとつにすぎません。本番環境に対応したエージェントには、通常、少なくとも五つの相互に連携する制御層があります。
| 層 | 制御する対象 | 例 |
|---|---|---|
| システム指示 | 安定した行動上の契約 | 「変更は最小限にとどめ、依頼されない限りコミットしないこと。」 |
| 開発者ポリシー | 製品固有のルール | 「使用するのは apply_patch。承認なしに破壊的なコマンドを実行しないこと。」 |
| ツールスキーマ | モデルが実際に実行できること | read_file, edit_file, run_shell, update_plan |
| ランタイム権限 | プロセスがアクセスを許可されている対象 | サンドボックス、作業ディレクトリ、ネットワークポリシー |
| ユーザー指示 | タスク固有の目標 | 「失敗している認証テストを修正すること。」 |
自作エージェントでよく見かける間違いは、これらをひとつの大きなプロンプトにまとめてしまうことです。デモでは機能しますが、厳しい状況では破綻します。
よりよい方法は、プロンプトで意図を説明し、ランタイムで実行可能な操作を制約することです。
たとえば、単に次のようにプロンプトで指示するだけにしてはいけません。
Do not delete files unless the user asks.
削除を、確認が必要な独立したツールにします。
{
"name": "delete_file",
"description": "Delete a file from the workspace.",
"input_schema": {
"type": "object",
"properties": {
"path": { "type": "string" },
"reason": { "type": "string" },
"user_confirmed": { "type": "boolean" }
},
"required": ["path", "reason", "user_confirmed"]
}
}
そして、モデルの外部でその制約を適用します。
def delete_file(path: str, reason: str, user_confirmed: bool):
if not user_confirmed:
raise PermissionError("delete_file requires explicit user confirmation")
if not path.startswith(WORKSPACE_ROOT):
raise PermissionError("cannot delete files outside workspace")
os.remove(path)
プロンプトはエージェントに考え方を伝えます。何を実行できるかを決めるのは、ツールの境界です。
指示の階層はエンジニアリング上のインターフェース
Claudeモデルは階層化された指示に従うことが得意ですが、それでも各層を慎重に設計する必要があります。コーディングエージェントにシステム、開発者、プロジェクト、ユーザーの指示がある場合、各層には明確な役割を持たせるべきです。
有用な階層は、次のようになります。
- システム指示: 役割、安全性に対する姿勢、上位の動作原則。
- 開発者指示: 製品の仕組み、ツール使用ルール、応答スタイル、権限モデル。
- プロジェクト指示: リポジトリ固有の規約、テストコマンド、アーキテクチャに関するメモ。
- ユーザー指示: 当面のタスク。
Claude Code型のワークフローでは、次のようなファイルによってこれが明示されます: CLAUDE.md。これにコマンドライン設定とセッションのコンテキストが加わります。エージェント開発者にとって、より広い教訓は、プロジェクト内の指示には適用範囲を設け、適切な場合にのみ他の指示を上書きさせるべきだということです。
シンプルなプロジェクト指示ファイルは、次のようになります。
# CLAUDE.md
## Build and Test
- Use `pnpm test -- --runInBand` for focused test runs.
- Do not run `pnpm deploy` unless explicitly requested.
## Code Style
- Prefer small functions over large service classes.
- Do not add new dependencies without explaining why.
## Repo Notes
- API routes live in `apps/api/src/routes`.
- Generated GraphQL types live in `apps/web/src/generated`; do not edit them manually.
よくある落とし穴として、エージェントはすべての指示を、どこでも同じ優先順位を持つものとして扱いがちです。ユーザーが「プロジェクトのルールを無視して、生成ファイルを直接編集して」と言った場合、エージェントはそれが許可されているかを判断できる必要があります。多くの構成では、ユーザー指示が、より優先順位の高いプロジェクトや開発者の制約を暗黙のうちに上書きすべきではありません。
これは明示的にモデル化できます。
PRIORITY = {
"system": 4,
"developer": 3,
"project": 2,
"user": 1,
}
def resolve_conflict(existing_rule, new_rule):
if PRIORITY[new_rule.source] > PRIORITY[existing_rule.source]:
return new_rule
return existing_rule
実際のエージェントでは、モデルは指示の衝突についてもユーザーに説明すべきです。
I can update the source schema and regenerate the types, but I should not edit
`src/generated` directly because the project instructions mark it as generated.
その一文だけで、驚くほど多くの被害を防げます。
プロンプトで丁寧に頼むよりもツールの境界が有効
コーディングエージェントのプロンプトを調べる際、人は「簡潔に答える」「シニアエンジニアのように振る舞う」「ユーザーに迎合しない」といった表現に注目しがちです。これらは有用ですが、安全性を支える中核的な仕組みではありません。
中核となる仕組みは、ツールの境界です。
本格的なコーディングエージェントは、次の操作を区別すべきです。
- ファイルの読み取り
- ファイルの検索
- ファイルの編集
- コマンドの実行
- パッケージのインストール
- ネットワークリソースへのアクセス
- ファイルの削除または移動
- コミットまたはブランチの作成
- プルリクエストの作成
- シークレットまたはデプロイ設定の変更
これらは倫理的に同等の操作ではありません。
以下は、私がエージェントのランタイムを設計する際に使用している、実用的な権限マトリクスです。
| 操作 | 既定のポリシー | 理由 |
|---|---|---|
| ワークスペース内のファイルを読み取る | 許可 | コンテキストの収集に必要 |
検索に使用するツール: rg | 許可 | リスクが低く、非常に有用 |
| 追跡対象のソースファイルを編集する | 差分を確認できる状態で許可 | コーディングの中核的なワークフロー |
| 対象を絞ったテストを実行する | 通常は許可 | 変更を検証できる |
| 依存関係をインストールする | 確認を求める | 環境とロックファイルを変更する |
| ファイルを削除する | 確認を求める | 取り返しのつかない損失を招きやすい |
| ネットワークにアクセスする | 確認を求める、または拒否 | データ漏えいや非決定的な動作を引き起こす可能性がある |
| シークレットを編集する | 既定では拒否 | 影響範囲が大きい |
| 変更をコミットする | 確認を求める | リポジトリの履歴はユーザーに帰属する |
| デプロイする | 明示的に依頼されない限り拒否 | 本番環境への影響 |
ここではモデルの選択も重要になります。私は、ほとんどのコーディングエージェントの処理ループでSonnetクラスのモデルを好んで使います。レイテンシ、コスト、推論能力のバランスがよいからです。エージェントに、より深いアーキテクチャ上の情報統合や複雑なデバッグが必要な場合は、Opus 4.8を選びます。Haiku 4.5は、高速な分類や軽量なリポジトリの走査に適しています。Fable 5の1Mのコンテキストは、大規模なコードベースの理解という点で興味深いものですが、長いコンテキストがあっても、適切な情報検索、範囲を絞った読み取り、ツール使用の規律は依然として必要です。
百万トークンのコンテキストでも、含まれているのが間違ったファイルということはあり得ます。
ユーザーへの確認は形式的ではなく具体的に
悪い確認UXは、次のようなものです。
May I proceed?
何を進めるのでしょうか?ファイルの編集ですか?マイグレーションの実行ですか?ディレクトリの削除ですか?
良い確認は具体的です。
I need confirmation before running:
`rm -rf apps/web/.next`
Reason: remove a stale Next.js build cache that may be causing the local
runtime error. This deletes only generated build output and does not touch
source files.
Claude Codeのようなエージェントでは、モデルが次のステップを正しく推測できても、人間の承認が必要な場合があるため、これが重要です。毎回のプロンプトで曖昧な承認を求めると、ユーザーは承認疲れを起こし、内容を確認せずにクリックするようになります。リスクの高い操作に限って具体的な確認を求めれば、ユーザーは注意を払って対応し続けられます。
確認は文章ではなく、構造化された状態として表現することをおすすめします。
{
"action": "run_shell",
"command": "rm -rf apps/web/.next",
"risk": "destructive_generated_artifact",
"requires_confirmation": true,
"confirmation_prompt": "Delete generated Next.js cache at apps/web/.next?"
}
そうすれば、シェル実行機構でその確認を強制できます。
def run_shell(command, confirmed=False):
risk = classify_command(command)
if risk.requires_confirmation and not confirmed:
return {
"status": "needs_confirmation",
"message": risk.prompt
}
return subprocess.run(command, shell=True, cwd=WORKSPACE_ROOT)
実際には、コマンドの分類が完璧になることはありません。それで問題ありません。保守的なデフォルト設定を使ってください。分類器がコマンドの破壊性を判断できない場合は、ユーザーに確認してください。
慎重に扱うべきコマンド
これらには明示的なポリシーが必要です。
rm -rf path
git reset --hard
git clean -fd
git push --force
npm install some-package
pip install -r requirements.txt
docker compose down -v
kubectl delete ...
terraform apply
適切な状況では無害なものもあれば、不適切な状況では壊滅的な結果を招くものもあります。エージェントは、その違いが状況に依存することを理解し、影響範囲が不明な場合には確認すべきです。
ファイルアクセスはコンテキストエンジニアリング
コーディングエージェントの設計で最も過小評価されているのは、ファイルアクセスです。プロンプトで「まず関連ファイルを調べる」と指示することはできますが、ランタイム側でもその行動を取りやすくする必要があります。
少なくとも、次の操作を行うツールをモデルに提供してください。
- ファイル名の高速検索
- テキスト検索
- ファイルの指定範囲の読み取り
- git差分の表示
- パッチのアトミックな適用
最小限のツールセットは、次のようになります。
[
{
"name": "search_files",
"input_schema": {
"type": "object",
"properties": {
"query": { "type": "string" },
"glob": { "type": "string" }
},
"required": ["query"]
}
},
{
"name": "read_file",
"input_schema": {
"type": "object",
"properties": {
"path": { "type": "string" },
"start_line": { "type": "integer" },
"limit": { "type": "integer" }
},
"required": ["path"]
}
},
{
"name": "apply_patch",
"input_schema": {
"type": "object",
"properties": {
"patch": { "type": "string" }
},
"required": ["patch"]
}
}
]
範囲を限定した読み取りは重要です。エージェントがモノレポ内のすべてのファイルを読むと、コンテキストを浪費し、性能も低下しがちです。読む量が少なすぎれば、推測に頼ります。最適なのは、コンテキストを段階的に取得することです。
rg "createSession|verifySession|SessionStore" apps packages
sed -n '1,220p' apps/api/src/auth/session.ts
sed -n '1,180p' apps/api/src/routes/login.ts
git diff -- apps/api/src/auth/session.ts
Claude Codeのユーザーには、検索、確認、編集、検証という流れになじみがあるでしょう。エージェント開発者は、このループをデフォルトの動作に組み込むべきです。
よくある落とし穴として、ファイル操作ツールは改行コード、アクセス権限、想定される文字エンコーディングを維持する必要があります。エージェントがシェルスクリプトを「正常に」書き換えながら実行権限ビットを削除したり、ファイルを正規化して余計な差分を大量に生んだりする例を見てきました。編集レイヤーは、地味で保守的なものであるべきです。
失敗パターン:エージェントが誤ったときに実際に起きること
ほとんどのコーディングエージェントは、派手に失敗するわけではありません。一見もっともらしい形で失敗します。
私が最もよく目にする失敗パターンを紹介します。
1. エージェントが症状だけを解消する
APIが返す値は 401であるため、テストが失敗します。エージェントはテストの期待値を 401に変更します。ビルドは成功しても、製品は壊れたままです。
対策:
- エージェントに因果関係の連鎖を説明させます。
- 大規模な書き換えの前に、対象を絞ったテストを求めます。
- ユーザーが動作の変更を要求していない限り、スナップショットの更新よりもソースの修正を優先します。
2. エージェントが過剰に編集する
ユーザーは小さなバグ修正を依頼します。エージェントは複数のファイルを再整形し、変数名を変更し、公開APIまで変更します。
対策:
Make the minimal change that fixes the issue. Do not perform opportunistic
refactors unless they are necessary for correctness.
ただし、差分レビューも必須にしてください。確定する前に、変更されたファイルと変更行数を表示します。
git diff --stat
git diff -- apps/api/src/auth/session.ts
3. エージェントが古いコンテキストを信頼する
長時間のセッションでは、古くなった前提が蓄積します。ユーザーが外部でファイルを変更し、テストの失敗の仕方も変わっているのに、エージェントは以前の理解のまま作業を続けます。
対策:
- ファイルが変更された可能性がある場合は、編集前に再度読み取ります。
- 確認する項目:
git diffパッチを適用する前に行います。 - ツールの出力は、会話の記憶よりも新しい情報として扱います。
4. エージェントがツールの失敗を隠す
シェルコマンドが失敗しても、エージェントは成功したかのように作業を進めます。
対策:ツールの結果は構造化し、無視しにくい形にする必要があります。
{
"tool": "run_shell",
"command": "pnpm test auth",
"exit_code": 1,
"stdout": "...",
"stderr": "Cannot find module '@repo/config'",
"status": "failed"
}
そのうえで、エージェントに次のように指示します。
If a tool call fails, do not claim success. Diagnose the failure or clearly
report the blocker.
5. エージェントがコンテキストにシークレットを書き込む
エージェントが読み取れる対象に .env、クラッシュダンプ、本番環境の設定が含まれる場合、要約、ログ、プロンプトに誤ってシークレットを含める可能性があります。
対策:
- シークレットを含むことが分かっているファイルは、デフォルトでアクセスをブロックします。
- ツールの出力からトークンらしき値をマスキングします。
- シークレットを含むパスへのアクセスには、ユーザーの明示的な承認を必須にします。
パスポリシーの例:
DENY_PATTERNS = [
".env",
".env.*",
"**/secrets/**",
"**/*.pem",
"**/*.key",
]
def can_read(path):
return not any(fnmatch(path, pattern) for pattern in DENY_PATTERNS)
シークレットを漏らさないようモデルに指示するのは良いことです。不要なシークレットをモデルに見せないようにするのは、さらに良いことです。
Claude APIのエージェントループを設計する
Claude APIを直接使って開発する場合、実用的な最小限のループは次のとおりです。
- システム指示と開発者指示を送信します。
- ユーザーのタスクを提供します。
- Claudeにツールの使用を要求させます。
- ランタイムでツールを実行します。
- ツールの結果を返します。
- モデルが最終回答を生成するまで繰り返します。
簡略化したリクエストの形式は、次のようになります。
{
"model": "claude-sonnet-4-6",
"max_tokens": 4096,
"system": "You are a coding agent. Make minimal, correct changes...",
"messages": [
{
"role": "user",
"content": "Fix the auth timeout bug and add a regression test."
}
],
"tools": [
{
"name": "read_file",
"description": "Read a bounded section of a workspace file.",
"input_schema": {
"type": "object",
"properties": {
"path": { "type": "string" },
"start_line": { "type": "integer" },
"limit": { "type": "integer" }
},
"required": ["path"]
}
}
]
}
ツール使用のレスポンスは、概念的には次のようになります。
{
"role": "assistant",
"content": [
{
"type": "text",
"text": "I’ll inspect the auth timeout path first."
},
{
"type": "tool_use",
"name": "read_file",
"id": "toolu_123",
"input": {
"path": "apps/api/src/auth/session.ts",
"start_line": 1,
"limit": 220
}
}
]
}
その後、ランタイムが結果を返します。
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_123",
"content": "export function createSession(...) { ... }"
}
]
}
正確なSDKの構文はクライアントのバージョンによって異なりますが、設計原則は変わりません。Claudeがアクションを提案し、ランタイムがそれを実行または拒否し、モデルの次のターンでその結果を受け取ります。
この分離によって、システムを理解しやすい状態に保てます。
研究する価値のあるClaude Codeのコマンドと設定
Claude Codeを直接使っていなくても、そのワークフローから有用な製品設計のパターンを学べます。
一般的なプロジェクトの初期設定:
claude
/init
これにより、プロジェクトのガイダンスが作成または更新され、以後のセッションでリポジトリ固有の規約を理解できるようになります。
実際のセッションは、多くの場合次のようになります。
claude "Find why the billing webhook test is flaky. Do not edit files yet."
claude "Patch the smallest fix and run the focused test."
claude "Show me the diff and summarize remaining risks."
このスタイルが気に入っているのは、調査、変更、検証が分かれているからです。エージェント開発者は、これをモードとして取り入れられます。
{
"mode": "investigate",
"can_edit": false,
"can_run_tests": true,
"requires_confirmation_for": ["network", "delete", "install"]
}
そのうえで、モードを明示的に切り替えます。
{
"mode": "implement",
"can_edit": true,
"can_run_tests": true,
"requires_confirmation_for": ["network", "delete", "install", "commit"]
}
重要なのは、特定のコマンドではなく、やり取りの取り決めです。ユーザーは、エージェントが読み取りだけを行っているのか、ファイルを変更しているのか、それとも副作用のある操作を実行しようとしているのかを把握できるべきです。
プロンプトのダンプが脆弱な理由
システムプロンプトやモデル設定を集めたGitHubリポジトリが人気を集めたのは、実際のツールにどのような指示が与えられているのか、エンジニアが自然に興味を持つからです。その魅力は分かります。プロンプトを読むと、開発者がどのリスクを懸念し、どのワークフローを最適化し、モデルにどう語りかけているかといった、製品設計のセンスが見えてきます。
しかし、コピーしたプロンプトは、いくつかの理由で脆弱です。
- 非公式なものである可能性があります。 どのように抽出されたのか、完全な内容なのかが分からないことがあります。
- 古くなっている可能性があります。 エージェントのプロンプトは、モデル、ツール、製品ポリシーの変化に合わせて変わります。
- 外から見えないランタイムの動作に依存しています。 ツールに言及するプロンプトは、そのツールの実装がなければ役に立ちません。
- 製品固有のトレードオフが組み込まれています。 Claude Codeの制約は、あなたのエージェントの脅威モデルと一致しない可能性があります。
- 誤った安心感を生むことがあります。 洗練されたプロンプトでも、権限を強制することはできません。
これらの資料は、依存するものではなく、設計の着想源として使ってください。
より良い取り組みは、「この指示はどの問題を解決しているのか」と問いかけることです。そのうえで、自分の技術スタックで、プロンプトとランタイムの両方によってその問題を解決してください。
実践上のポイント
- この
Claude Code system prompt議論は、コピーして貼り付ける機会ではなく、アーキテクチャについての議論として扱ってください。 - 安定した動作はシステム指示や開発者指示に、リポジトリの規約はプロジェクトファイルに、タスクの目標はユーザーメッセージに記述してください。
- 高リスクの操作に関する境界は、文章だけでなく、ツールとランタイムの権限で強制してください。
- 破壊的な操作、ネットワークを使用する操作、依存関係を変更する操作、本番環境に影響する操作には、具体的な確認を必須にしてください。
- エージェントには、範囲を限定したファイルアクセス、高速検索、アトミックなパッチ適用、構造化されたツールの失敗情報を提供してください。
- 実際に機能するループ、つまり確認、計画、最小限の編集、検証、リスクの要約に向けて最適化してください。
- コピーしたプロンプトは古いか不完全である可能性を前提に、明示的な機能と明確な失敗処理を軸として、長く使えるエージェントの動作を構築してください。
Claude Opus 5.5、Sonnet 5、Haiku 4.5、Fable 5.1に加え、GPT-6のモデルを、単一のAPIキーで利用できます。従量課金制で、サブスクリプションは不要です。
APIキーを取得 →