Claude Codeの問題のある機能:エージェント型ワークフローの誤り
Claude Code の問題機能の多くは境界の問題
私が見た中で最も大きな損失を招いた Claude Code のミスは、不適切なコード編集ではありませんでした。リポジトリが不適切な状態にあるときに行われた、正しい編集でした。
ある開発者が Claude Code に「認証ミドルウェアを更新して、テストを実行して」と依頼しました。作業ツリーには、別のタスクによる未コミットのマイグレーションがすでに含まれていました。Claude はミドルウェアを理解するのに十分なファイルを調べ、適切な実装を変更し、フォーマッターを実行して、成功したと報告しました。結果の差分は技術的には妥当に見えましたが、無関係な変更が混在しており、後のレビューはほぼ不可能になりました。
これは、多くの Claude Code の問題機能の背後にある、受け入れがたい教訓です。システムは、そのワークフローが促すとおりに動作していることが多いのです。問題は、ファイルシステムへのアクセス、シェルへのアクセス、指示ファイル内の記憶、会話で与えられた目標を持つエージェントが、開発者の想定する境界を自然に守るわけではないという点にあります。
批判の中には、製品の実際の弱点を指摘するものもあります。一方で、意図的なトレードオフを指摘するものもあり、それらはユーザーから見えないデフォルトとして扱われると危険になります。有益な問いは、Claude Code を「もっと自律的に」するべきか、「自律性を抑える」べきかではありません。自律性がどこで終わるのか、その境界がどのように表現されるのか、そして何が起きたかを開発者が確認できるのか、ということです。
意外なデフォルト:コンテキストは隔離ではない
Claude Code は、実際の作業ディレクトリで動作するように設計されています。リポジトリを調べ、ファイルを編集し、ツールを呼び出し、コマンドの出力に基づいて作業を繰り返せます。これは、抽象的なプロンプトからパッチを返すようモデルに依頼するより、はるかに有用です。
同時に、現在のディレクトリがエージェントの実質的な状態の一部になることも意味します。
実際には、Claude Code は次のようなものに遭遇する可能性があります。
- 別のタスクによる未コミットの変更
- 正式な情報源に見える生成ファイル
- CI と異なるローカル設定
- ツールを通じてアクセスできる認証情報や環境変数
- ワークツリー、入れ子のリポジトリ、シンボリックリンクされたディレクトリ
- A
CLAUDE.mdプロンプトに現れずに動作を変える指示ファイル - ネットワークアクセスや破壊的なクリーンアップを行うビルドスクリプト
エージェントはこれらの状況に気づくかもしれませんが、気づくことと隔離を強制することは同じではありません。モデルは「無関係な変更が見つかりました」と言いながらも、ユーザーの依頼が明確に見えれば、そのまま進めることがあります。
規律あるセッションは、明示的な状態確認から始まります。
git status --short
git branch --show-current
git diff --stat
pwd
私は、最初のプロンプトで境界を明示することも好みます。
Work only on the request in this session. Do not modify existing unrelated changes.
Before editing, report the current branch, dirty files, and the files you intend to change.
Do not commit, reset, checkout, or delete files.
セッションで問題が起きるまでは、これは繰り返しに感じられます。エージェントへの指示はアクセス制御ではありません。動作の指針であり、その指針に従うかどうかは確率的です。
製品としてのトレードオフは理解できます。すべてのタスクを使い捨てのサンドボックスで実行するよう強制すれば、一般的なワークフローは遅くなり、利便性も低下するでしょう。しかし、デフォルトの動作では、作業を委任する前にリポジトリの状態を理解する責任を、依然としてユーザーに過度に負わせています。
CLAUDE.md は強力であり、それゆえ誤解しやすい
プロジェクトの指示は、Claude Code の最も優れた機能のひとつです。テストコマンド、アーキテクチャ上の制約、命名規約、レビューで求めることを、プロンプトごとに繰り返す代わりに、一度記述しておけます。
同時に、これは隠れた結合でもあります。
リポジトリには、Claude に次のことを指示する内容が含まれる場合があります。
- 特定のパッケージマネージャーを実行する
- あるディレクトリを別のディレクトリより優先する
- スナップショットを更新する
- 生成ファイルの変更を避ける
- デプロイ用またはデータベース用のコマンドを使用する
- 追加の指示ファイルを読む
ユーザーに見えているのはタスクですが、エージェントに見えているのはタスクに指示の階層を加えたものです。予想外の動作があったときは、モデルがプロンプトを無視したと決めつけるのではなく、実際に適用されている指示を調べてください。
プロジェクトの指示は、短く、具体的な作業に即した内容にしてください。
# Repository instructions
- Run `npm test` for the unit suite.
- Run `npm run lint` before describing the work as complete.
- Do not edit files under `generated/`; update the source and regenerate only when requested.
- Never run production deployment commands.
- Preserve unrelated working-tree changes.
このファイルには、「常に最善のアーキテクチャ上の判断を行う」といった曖昧な目標を記述しないでください。広範な指示は、まさに予測可能な動作を求める場面で、エージェントの裁量を広げてしまいます。
制御の境界:確認はセキュリティモデルではない
Claude Codeの権限システムは有用な制御手段ですが、それが保証する範囲は過大評価されがちです。
確認プロンプトが通常答えるのは、「このツール操作を今実行してよいか」という限定的な問いです。次の問いに答えるとは限りません。
- コマンドは、その実際の動作まで含めて安全か?
- モデルはコマンドの副作用を理解していたか?
- 対象パスは意図したプロジェクト内にあるか?
- スクリプトは追加のコマンドを呼び出すか?
- コマンドはソースコードや機密情報をネットワーク経由で送信するか?
- 変更は元に戻せるか?
一見無害なコマンドにも、重大な動作が隠れている場合があります。
npm run test
スクリプトはキャッシュを削除したり、コンテナを起動したり、リモートサービスにアクセスしたり、postinstallフックを実行したりする可能性があります。Claude Codeはコマンドの実行許可を求めることはできますが、シェルコマンドを完全に検証済みの操作権限の境界に変えることはできません。
プロジェクト単位の権限設定を使えば、日常的な確認プロンプトを減らせます。
{
"permissions": {
"allow": [
"Bash(npm test)",
"Bash(npm run lint)",
"Bash(git diff -- *)"
],
"deny": [
"Bash(rm *)",
"Bash(git reset *)",
"Bash(git checkout *)",
"Read(.env)",
"Read(.env.*)",
"Read(secrets/**)"
]
}
}
これはポリシーの明文化と操作の手間を減らすための仕組みとして扱い、サンドボックスとは考えないでください。パターンは範囲を狭く限定すべきです。次の許可設定は Bash(npm *) 便利ですが、未確認のスクリプトも含め、すべてのnpmスクリプトに実行権限を与えます。
実務上の役割分担は次のとおりです。
| 境界 | 役立つこと | 解決できないこと |
|---|---|---|
| ツール実行の確認 | その時点で望まない操作の防止 | 承認済みコマンドに隠れた影響 |
| 権限ルール | 繰り返し適用できる予測可能なツール利用ポリシー | スクリプトの動作の意味の解釈 |
| 読み取り専用での計画 | 早まった編集の抑制 | 誤った分析や見落とされたリポジトリの状態 |
| コンテナまたはワークツリー | ファイルシステムと環境の分離 | 分離された環境内での不適切な変更 |
| 人によるレビュー | 意図と品質の検証 | 実行時のあらゆる副作用の検出 |
| CIチェック | 再現可能な検証 | セッション中のローカルの機密情報の保護 |
影響の大きい作業では、使い捨てのワークツリー、コンテナ、権限を制限した認証情報、タスクに適したネットワークポリシーを用いて、オペレーティングシステムのレベルで分離してください。Claude Codeの権限確認プロンプトは、こうした制御を補完するものであり、代替するものではありません。
可観測性は会話記録から受ける印象ほど高くない
エージェントの会話記録には、「ルートを確認します」といったメッセージ、ツール呼び出し、コマンド出力、最終要約が含まれるため、動作を観測できているように見えます。しかし、会話記録と監査ログは同じではありません。
記録に欠けがちな重要な問いには、次のようなものがあります。
- 読み取ったものの、言及されなかったファイルはどれか?
- どの指示ファイルが判断に影響したか?
- 具体的にどの環境変数が参照可能だったか?
- シェルスクリプトは子プロセスを呼び出したか?
- どのコマンドが状態を変更したか?
- モデルは何を検証済みと考え、何を単なる推論と考えていたか?
- 失敗したコマンドは、解釈を変えて再試行されたか?
Claude Codeには、次のような便利な対話型の制御機能があります。 /status, /permissions, /compact, /clear, /rewind、および /doctorこれらを活用しつつ、その適用範囲を理解してください。現在のセッションの確認や管理には役立ちますが、インシデント分析に適した永続的な変更記録が自動的に作成されるわけではありません。
リポジトリでの作業では、簡易的な証跡を自分で残してください。
git status --short
git diff --name-only
git diff --check
npm test
git diff --stat
フックを使うと、ツール利用時に制御を強制できます。たとえば、次の種類の PreToolUse フックは、実行予定のBashコマンドを検査し、特にリスクの高い操作を拒否できます。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 .claude/hooks/reject-dangerous.py"
}
]
}
]
}
}
最小限のフックでも、Claude Codeのフックプロトコルから渡されるツール入力を読み取り、既知の破壊的操作を含むコマンドを拒否できます。
#!/usr/bin/env python3
import json
import sys
event = json.load(sys.stdin)
command = event.get("tool_input", {}).get("command", "")
blocked = ("git reset --hard", "rm -rf", "git clean -fd")
if any(item in command for item in blocked):
print("Blocked by repository policy.", file=sys.stderr)
sys.exit(2)
sys.exit(0)
フックイベントの具体的なフィールドや、サポートされる設定はClaude Codeのバージョンによって異なる場合があるため、このパターンを標準化する前にローカルのスキーマを確認してください。重要な設計原則は変わりません。必ず守らせる制御は、文章だけでなく、実行可能なポリシーとして組み込むことです。
失敗パターン:エージェントがタスク完了を優先して最適化する
言語モデルは、やり取りの中で進捗を生み出すことを自然に促されます。テストが失敗すれば調査し、型エラーが出れば修正しようとします。このループは有用ですが、「依頼された変更を行う」が「その場で妥当に見えるあらゆる手段を使って、リポジトリのチェックをすべて通す」に変わってしまうことがあります。
よくある兆候には、次のようなものがあります。
- 動作を修正せずにスナップショットを更新する
- リグレッションを許容するようにテストのアサーションを緩める
- 必要性を確認せずに互換性のための分岐を追加する
- 生成された出力を直接変更する
- 無関係なファイルにまでフォーマッターを実行する
- 限定的なテストの成功を、機能が完成した証拠として扱う
- 根本の前提を変えずに、失敗したコマンドを繰り返す
対策は、目標だけでなく停止条件も定義することです。
Implement the parser change.
Run only the parser unit tests first.
Do not modify tests unless a test is demonstrably incorrect.
Do not update snapshots without showing the before/after behavior.
Stop after two failed repair attempts and summarize the failure.
Finish with the exact files changed and commands run.
これにより、不確実な点があると編集をさらに重ねてしまうエージェントの傾向を抑えられます。
特に効果的なのは、実行を段階に分ける方法です。
- 編集を行わずに、調査と計画案の提示を求める。
- ファイル一覧、リスク、テスト戦略をレビューする。
- その範囲内での実装を許可する、次のプロンプトを送る。
計画は保証ではありません。誤った解釈が差分になる前に、人が見つけられるチェックポイントです。
失敗パターン:コンテキスト圧縮で作業上の詳細が失われる
長いセッションはデバッグに役立ちますが、いずれ圧縮や要約が必要になります。モデルが保持するのは、それまでのすべてのツール出力ではなく、圧縮された表現です。大枠の目標は残ることが多いものの、細かな制約が同じように残るとは限りません。
これがよくある落とし穴を生みます。セッションの初めに「マイグレーションファイルには触れないで」と伝えたのに、後でエージェントがスキーマテストの失敗に遭遇し、まさにその変更を提案するのです。
重要な制約のある作業では、次を実践してください。
- 次の操作の後に、必ず守る条件を再提示する:
/compact - 作業対象のファイル一覧を、目に見える作業メモファイルやissueに残す
- 無関係なタスクには別のセッションを使う
- 極端に長く、話題が散漫になったセッションより、新しいセッションを選ぶ
- 次を再実行する:
git diff --name-only結果を受け入れる前に
トレードオフは明確です。コンテキストを維持すると継続性が高まり、新しいコンテキストでは予測可能性が高まります。どちらかが常に優れているわけではありません。
失敗パターン:「完了」が意味するのはローカルでは妥当に見えること
Claude Codeはテストを実行できますが、テストを実行したことと、テストが十分であることは別です。関連するコマンドの中で最も速いものを選んだり、リポジトリの指示に従ったり、十分に見えるコマンドを実行した時点で止まったりする場合があります。成功という結果は、あるローカル環境で、ある経路が動作したことだけを意味する可能性があります。
検証内容を明示し、証拠を求めてください。
Before finishing:
- Run `git diff --check`.
- Run `npm test -- --runInBand`.
- Report the exact commands and whether each passed.
- Distinguish tests run from tests not run.
- Do not claim production behavior was verified.
APIを利用するシステムでは、ラッパーから推測させるのではなく、リクエストとレスポンスの構造をエージェントに確認させてください。最小限のClaude API呼び出しは、次のようになります。
import os
from anthropic import Anthropic
client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=512,
system="Return JSON only.",
messages=[
{"role": "user", "content": "Classify this event: login_failed"}
],
)
print(response.model_dump_json(indent=2))
レスポンスに含まれるのは単なる文字列ではなく、構造化されたコンテンツブロックです。次の値が response.content[0].text 常に意味上の完全な結果だと仮定するコードは、ワークフローにツール利用や複数のブロックが入ると脆弱になります。
これも境界の問題です。モデルによる自然言語の要約は、インターフェースの契約ではありません。構造化された出力を解析して検証し、デバッグに必要な場合は生のリクエストメタデータを保持してください。
どの批判が製品の欠点に当たるのか?
製品レベルで改善すべき挙動もあります。
- 実際に適用されている重要な指示や権限が、ひと目では理解しにくい場合があります。
- 自然言語による最終要約は、完全な検証結果と誤認されやすいものです。
- 確認UXでは、任意のシェルコマンドの副作用をすべて伝えることはできません。
- 長いセッションの状態やコンテキスト圧縮によって、制約が見えにくくなる場合があります。
- リポジトリの状態は、無関係な作業から自動的に分離されません。
一方で、採用された設計の合理的な帰結といえる挙動もあります。
- 編集と実行ができるエージェントは、パッチの作成だけを行うアシスタントより有用です。
- 低リスクの読み取りやテストのたびに確認を求めると、ワークフローは耐えがたいほど遅くなります。
- リポジトリへの広範なアクセスによって、ビルド、テスト、ソースの境界をまたいだデバッグが可能になります。
- 最初のコマンドが失敗することも多い実際のプロジェクトでは、修正を繰り返すことが必要です。
適切な対応は、複数の層で運用を管理することです。権限の限定、リスクの高いタスク向けの使い捨て環境、明示的なプロンプト、短いセッション、実行可能なフック、機械的な検証を組み合わせます。
実践上のポイント
- 確認する対象は
git status、ブランチ、差分です。Claude Codeに書き込み権限を与える前に確認してください。 - 次のものを
CLAUDE.md実行可能なプロジェクトポリシーとして扱い、簡潔に保ってください。 - 限定的な
allowとdeny権限パターンを使用してください。サンドボックス化と混同してはいけません。 - 破壊的なコマンドに対する制御は、フックまたは周囲の実行環境に組み込んでください。
- 不慣れな変更や影響の大きい変更では、計画と実装を分離してください。
- コンパクション後と最終検証の前に、重要な制約を再提示してください。
- 正確なテストコマンドの提示を必須とし、「未実行」と「合格」を区別してください。
- レビューする対象は
git diff --name-only,git diff --checkと差分全体です。マージ前に確認してください。 - シェル操作の副作用が問題になる場合は、コンテナ、権限を制限した認証情報、または使い捨てのワークツリーを使用してください。
- Claude Codeの自律性は、切り替える信頼設定ではなく、適切に調整すべき能力として扱ってください。
中心的な批判は妥当です。エージェント型ワークフローは、制御の境界を明示すべきまさにその場面で、境界を暗黙的にしてしまいます。ただし、それによってワークフローが使いものにならなくなるわけではありません。経験豊富なチームが、設定、隔離、可観測性、レビューを通じて、欠けている境界を補う必要があるということです。
Claude Opus 5.5、Sonnet 5、Haiku 4.5、Fable 5.1に加え、GPT-6のモデルを、単一のAPIキーで利用できます。従量課金制で、サブスクリプションは不要です。
APIキーを取得 →