見出し画像

なぜClaude Codeは静かに賢さを落としたのか|2026年最新 自前エージェント設計の3つの落とし穴を徹底解説

Anthropic が2026年4月23日付の engineering blog で公開した postmortem は、Claude Code の品質を 3 月から 4 月にかけて静かに下げていた 3 件の不具合 を詳細に明かしています。reasoning effort が暗黙的に `medium` へ落とされていた件、thinking 履歴を毎ターン破棄していたキャッシュバグ、そして 25 語制約で書き直された system prompt。レビュー・単体テスト・E2E・dogfood という社内の 4 重チェックすべてを通り抜けたバグ が、最終的にはユーザーフィードバックと拡張 eval セットによってようやく発見された、と公式が認めています。

この事実は、ほとんどの読者にとって「Anthropic 内部の話」として消費されがちです。けれど、私のように Claude Code の上に薄いラッパー(subagent / routine / Skill / 自前 harness)を載せて日常運用しているエンジニアにとっては、まったく他人事ではありませんでした。気づきにくいデフォルト変更こそ、自前エージェントが最初に潰すべき罠である。 これが本記事を貫く One Key Message です。

本記事では、Anthropic の 3 件の不具合を「reasoning デフォルト」「セッション state 管理」「system prompt 改修」という 3 軸に再整理し、自分のリポを点検するためのチェックリストと最小実装スニペットに落とし込みます。読了後には、自分のエージェントを 3 軸でレビューし、回帰検知用の minimal eval を最低 1 本追加できる状態になっているはずです。価格 980 円のぶん、机上論ではなく「私自身が手元のリポで踏んだ / 踏みかけた失敗」を一次素材として書きます。

REASONING、STATE、SYSTEM PROMPTを頂点とする三角形ダイアグラム。各ノードに警告グローが入り、自前エージェントの3つの罠を表現
図1: 自前エージェントが最初に潰すべき3つの罠。
reasoning・state・system promptが三角形を成す。

落とし穴 1: reasoning デフォルトを「速い方」に倒している

結論から書きます。あなたのラッパー / SDK / config のどこかで、reasoning effort や thinking budget が「速い・安い」側にハードコードされていませんか。 Anthropic が今回 postmortem で認めたのは、まさにこのデフォルトの取り違えでした。本来 `high` で動くべき経路が `medium` に落ちていたが、その差は出力品質に薄く滲むだけで、明確なエラーにはならない ため、社内の自動テストもユーザーも気付くまでに時間がかかった、という構造です(Anthropic engineering blog)。

私の個人開発システムでも、似たことを過去にやっています。コスト計算スプレッドシートを見ながら「探索フェーズは reasoning を下げて回数を稼ぐ方が ROI が良い」と判断し、薄い wrapper の中で `reasoning_effort = "medium"` を直書きしました。短期的にはトークン単価が下がって満足するのですが、3 週間後にコードレビュー用 subagent の指摘が浅くなっていることに気付き、慌てて巻き戻したことがあります。「速い方に倒す」という決定は、決定した瞬間ではなく数週間後に効いてくる。 これが厄介な性質です。

なぜこの罠は気づきにくいのか

reasoning パラメータの劣化は、ユニットテストで落とせません。期待出力が固定されていない自然言語タスクの「賢さ」は、合否ではなく分布で表れるからです。E2E テストも、ハッピーパス通過を確認するだけのものはほぼ素通りします。dogfood すら、慣れた開発者が「最近ちょっと浅いかも」と感じても、それを issue に起こすには弱すぎる証拠しか手に入りません。Anthropic 自身がこの構造に苦しんだことを公開してくれた事実は、「自前で eval を持っていなければ検知できない」 という冷たい現実を裏付けています。

解決ステップ: reasoning 設定を 1 箇所に集約し、canary を回す

私が手元のリポで実際に入れた / 入れる予定の対策は、次の 4 ステップです。

  1. 設定を 1 ファイルに集約する。reasoning effort、thinking budget、max tokens、温度などモデル挙動に効くパラメータは、コード内に散らさず `config/model.yaml` のような単一ファイルに集める。grep 一発で「今どこで何が決まっているか」が分かる状態にすることが最優先です。

  2. タスク種別ごとにデフォルトを分ける。私は今、探索系・実装系・レビュー系の 3 種別に分け、レビュー系だけは必ず `high` を渡すようにしています。低い側に倒すときは、設定ファイルにコメントで「なぜ低くしたか / いつ見直すか」を残します。これは将来の自分への申し送り書です。

  3. canary プロンプトを週次で回す。既知の難問を 3 〜 5 本固定し、毎週同じ入力で同じモデルに同じパラメータで投げ、出力を JSONL に追記して時系列で比較します。指標は厳密でなくて良く、行数・tool 呼び出し回数・特定キーワードの出現有無のような 粗い proxy で十分 です。Anthropic が postmortem で「拡張 eval セットで初めて発見した」と書いた、その縮小再生産を自分のリポでもやるイメージです。

  4. ハードコードに linter ルールを追加する。`reasoning_effort\s*=\s*["']` を grep で検出し、設定ファイル以外で書かれていたら CI で警告を出す。30 行で書ける程度の薄い守りですが、未来の自分や同僚が無自覚にデフォルトを倒してしまう事故を防げます。

実装スニペット: 薄い reasoning wrapper

具体イメージを Python で示します。Claude Code SDK の上に薄く被せるだけの最小例です。

# config/model.yaml
# 探索 / 実装 / レビュー系で reasoning を切り替える
profiles:
  explore:
    reasoning_effort: medium
    note: "探索フェーズはコスト優先。週次canaryで品質劣化監視中"
  implement:
    reasoning_effort: high
    note: "実装は high 固定。差分が品質に直結するため"
  review:
    reasoning_effort: high
    note: "レビューは絶対に下げない。指摘の浅さは検知困難"
# wrapper/model_config.py
import os
import yaml
from pathlib import Path

_CONFIG_PATH = Path(__file__).parent.parent / "config" / "model.yaml"

def load_profile(name: str) -> dict:
    """profile 名から reasoning パラメータを返す薄い wrapper。
    呼び出し側は profile 名だけ知っていれば良い。
    """
    with _CONFIG_PATH.open() as f:
        config = yaml.safe_load(f)
    if name not in config["profiles"]:
        raise KeyError(f"unknown profile: {name}")
    return config["profiles"][name]

def call_model(messages: list[dict], profile: str = "implement"):
    params = load_profile(profile)
    # ここで実際の SDK を呼ぶ。reasoning_effort は config 由来のみ
    return _sdk_call(
        messages=messages,
        reasoning_effort=params["reasoning_effort"],
    )

ポイントは「呼び出し側コードに `reasoning_effort` という文字列を一切書かせない」ことです。grep で `reasoning_effort` を検索して、wrapper と config 以外でヒットしたら自動的に CI 落としにする。これで「うっかりハードコード」を構造的に防げます。

ここから先は

7,354字 / 2画像

¥ 980

Amazon Payで支払うと最大2%還元のチャンス! 9/30まで

最後まで読んでくれてありがとうございます!「よかったよ」の気持ちをチップで送ってもらえると、次の記事を書く燃料になります☕