メインコンテンツへスキップ

Claude Fable 5.1 APIチュートリアル:Pythonで長時間稼働の開発者エージェントを構築

Anthropicの最新フラッグシップモデルを使い、変更計画前にFlaskリポジトリを読むPythonエージェントを構築します。進捗更新、読み取り専用ファイルツール、コスト管理も追加。
更新 2026年9月3日  · 15 分 読む

AIで探索

ChatGPTClaudePerplexity

APIコールで新しいモデルを試すと、最初の応答だけでは情報が足りません。最初のFable 5.1実行は有効な構造と汎用的な計画を返しましたが、その後の会話の拡張で何が起きるのかを知りたくなりました。アプリは履歴を正しく保持できるか、プロジェクト外に出ずにファイルを点検できるか、進捗を報告できるか、そしてコストの内訳はどこから来たのかを示せるでしょうか?

当社のClaude Fable 5.1の概要では、ローンチ、ベンチマーク、広範なモデル比較を扱っています。ここでは小さなPython呼び出しから始めて、その周りにエージェントループを構築します。最終的なエージェントは機能要望を受け取り、Flaskプロジェクトを読み、実際に点検したファイルに結び付いた計画を返します。

以下を扱います:

  • Claude Fable 5.1 APIを呼び出し、コンテンツブロックを安全に読む
  • 推論effortの設定と会話途中での変更(ベータ)
  • システム指示を1ターンに限定する(ベータ)
  • Pydanticで構造化された計画を返す
  • プロジェクトルート境界付きのリポジトリ読み取り専用ツールを追加
  • マルチターンのツールループを実行
  • ツール呼び出し間でのエージェントの進捗更新を読む(ベータ)
  • 追記専用の履歴でthinkingブロックを有効に保つ
  • 繰り返しのコンテキストをキャッシュし、公開レートでリクエストコストを見積もる
  • 拒否応答の扱いとFastAPI経由でのエージェント公開

ベータ機能は日付付きヘッダーを使います。出荷前に必ずAnthropicのドキュメントで確認してください。

エージェントループでClaude Fable 5.1を動かすコストは?

エージェントは毎ターン同じsystemプロンプト、ツール定義、リポジトリのコンテキストを再送するため、請求額を左右するのは入力レートではなくキャッシュ読み取りレートです。

Fable 5.1の料金は入力100万トークンあたり$10、出力100万トークンあたり$50で、Fable 5から変更ありません。キャッシュ読み取りは100万トークンあたり$0.25($1から引き下げ)、5分のキャッシュ書き込みは同$12.50です。完全な料金表とAnthropic自身の節約見積もりはClaude Fable 5.1ガイドにあります。

キャッシュされたプレフィックスの読み取りは安価です。書き込みは読み取りの50倍かかるため、プレフィックスが複数回読み戻されるときにだけループの価値が出ます。後半のコスト内訳では、実行例でどのような比率になり、どのカテゴリが支配的だったかを示します。

トークン上限は予算ではなくモデルに由来します。Fable 5.1はコンテキストウィンドウ100万トークン、応答あたり最大128Kの出力トークンを提供し、max_tokensは思考と回答テキストを合わせたハード上限です。高いeffortでは両方の余地が必要なため、以下のエージェントループでは切りの良い値ではなく16,000を設定しています。

データ保持、Priority Tier、透かし

コードを書く前にいくつかのアクセス条件が重要です。そのうち2つはリクエストを即座に止めます。

  • Fable 5.1は30日データ保持が必須で、Anthropicの承認がない限りゼロ保持では利用できません。不適合ワークスペースからのリクエストは、他の手掛かりなしで400のinvalid_request_errorを返します。

  • このモデルはPriority Tierをサポートしません。Fable 5は対応しているため、移行時に見落とされがちです。

  • Fable 5.1のテキスト出力にはAnthropicのテキスト透かしが入ります。トークン増加はなく、リクエストの変更も不要です。

API経由でClaude Fable 5.1を使い、リポジトリ対応の開発者エージェントを構築

ワークフローは2段階です。

  1. 境界付きの点検ループで許可されたプロジェクトファイルを読む。
  2. 構造化出力を使う最終リクエストで、そのコンテキストを計画に変換する。

サンプルプロジェクトはブックマークの保存と検索用の小さなFlask JSON APIで、アプリファクトリ、3つのブループリント、configモジュール、モデル、pytestスイートを含みます。継続するタスクとしてレート制限を用います。なぜなら、必要なファイルとテストを特定する前に、エージェントはアプリのセットアップ、ルート、設定、テストを点検する必要があるからです。完全なコードとサンプルプロジェクトはGitHubリポジトリで公開しています。

機能要望がClaude Fable 5.1エージェント、パス許可リスト、サンプルプロジェクトを経由して、構造化された計画として返るフロー図

リクエストは1つの境界を通ってファイルに到達します。画像:著者作成。

エージェントが使えるツールは3つのみです:list_project_filesread_project_fileget_project_metadata。Claudeがファイルシステムへ直接アクセスすることはありません。パスを要求し、そのパスを許可するかどうかはコード側で判断します。

PythonでClaude Fable 5.1 APIをセットアップ

Pythonの独立した環境から始め、APIキーはサーバー側に保持してください。

前提条件

Python 3.10以降と、claude-fable-5-1にアクセスできるAnthropic APIキーが必要です。

APIキーを作成するには、Claude Consoleにサインインし、API keysページを開き、Create keyをクリックしてキーをコピーします。目的を思い出しやすい名前を付け、有効期限を設定し、安全に保管することを推奨します。

SDKをインストールしAPIキーを追加

仮想環境を作成し、パッケージをインストールします:

python -m venv .venv
source .venv/bin/activate          # macOS or Linux
.venv\Scripts\Activate.ps1         # Windows PowerShell
pip install anthropic==1.3.0 pydantic fastapi uvicorn python-dotenv

ベータ機能は頻繁に変わるため、SDKのバージョンは固定してください。進捗更新には少なくとも1.1.0が必要で、例では1.3.0を使用しています。

キーは .envファイルに入れ、最初のコミット前に.env.gitignoreに追加してください。キーは管理下のサーバーに置き、ブラウザや公開リポジトリに置かないでください。露出すると、入力・出力・キャッシュ操作にわたる不正利用と課金の恐れがあります。

ANTHROPIC_API_KEY=sk-ant-your-key-here

これでクライアントは自動的にキーを検出します。

Pythonで最初のClaude Fable 5.1 APIコールを行う

APIリクエストは、上に何かを積む前に最小のものを送って確認しましょう。

最初のAPIリクエストを送る

クライアントを初期化し、ユーザーメッセージを1つ送り、応答メタデータを出力します:

from anthropic import Anthropic
from dotenv import load_dotenv

load_dotenv()

client = Anthropic()
MODEL = "claude-fable-5-1"

response = client.messages.create(
    model=MODEL,
    max_tokens=512,
    messages=[{"role": "user", "content": "Reply in one sentence to confirm the API connection is working."}],
)

text = next((b.text for b in response.content if b.type == "text"), None)
print(text if text is not None else f"No text returned ({response.stop_reason})")
print(f"Model: {response.model}")
print(f"Stop reason: {response.stop_reason}")
print(f"Input tokens: {response.usage.input_tokens}")
print(f"Output tokens: {response.usage.output_tokens}")
print(f"Request ID: {response._request_id}")

Claude Fable 5.1 APIの応答。モデルID、停止理由、トークン数、リクエストIDが表示されたターミナル

初回の呼び出しはテキストとメタデータを返します。画像:著者作成。

next(...)は最初のテキストブロックを選びます。適応的thinkingは常に有効で無効化できないため、応答はthinkingブロックから始まることがあります。thinking: {"type": "disabled"}を送ると無効化ではなく400が返ります。thinkingブロックが先頭に来ると、response.content[0].textは例外になります。

解決策は固定位置を仮定せず、ブロックタイプでフィルタすることです。さらにresponse._request_idもログしましょう。Anthropicサポートはこれを使ってリクエストを追跡します。

以下は計画とeffortの例で使ったリクエストです。エージェントが複数のファイルを点検することを要求します。

feature_request = (
    "Add rate limiting to the public API endpoints so one client cannot exhaust "
    "the search endpoint or brute force the token endpoint."
)

effortレベルやトークン数を比較する間、そのテキストは変更しないでください。結果が異なるプロンプトではなくAPI設定の差を示すようにできます。

output_configで推論effortを設定

推論effortはoutput_configで設定します。lowmediumhighxhighmaxを受け付け、APIのデフォルトはhighです。

response = client.messages.create(
    model=MODEL,
    max_tokens=8192,
    output_config={"effort": "high"},
    messages=[{"role": "user", "content": feature_request}],
)

effortはトークン使用量、ツール動作、レイテンシーに影響することがあります。同じ機能要望を各effortレベルで3回ずつ実行し、平均を表にまとめました。

Effort

Seconds

Thinking tokens

Total output tokens

Cost

low

7.7

111

173

$0.0093

medium

8.1

129

186

$0.0099

high

7.9

136

199

$0.0106

xhigh

20.0

151

1,764

$0.0888

思考トークンは総出力トークンに含まれるため、2列を合算しないでください。これらの実行では、lowmediumhighはレイテンシーとコストが近い水準に収まりました。

xhighは所要時間が2.5倍、出力トークンは約9倍、コストは8倍になりました。

要点:まずはhighから始め、定型ステップではmediumに落とし、明確な改善がテストで示せる場合のみ上げてください。 lowでは、モデルが取得ツールを呼ばずに記憶で答えることがあります。新しい情報が必要なターンでは、そう明示するか、レベルを上げてください。

systemプロンプトでエージェントの範囲を制約

systemプロンプトはエージェントの振る舞いを定義します。

SYSTEM_PROMPT = """You are a senior engineer who turns feature requests into implementation plans for an existing codebase.

Stay inside the requested feature. Do not propose unrelated refactors, dependency upgrades, or style changes.

If a file or dependency you need does not exist, say so plainly instead of inventing it.

Write in plain sentences and do not use em dashes.

Finish with concrete guidance: what changes, where, in what order, what could break, and which tests to add."""

Anthropicのプロンプトガイダンスでは、タスクが膨らむ、または早すぎる終了の可能性が指摘されています。プロンプトで範囲内に留まることと、具体的な指針で締めくくることを指示します。出力形式は後でスキーマで管理します。

Pydanticで構造化された計画を返す

Pydanticで計画を定義し、アプリケーションが検証して他のコードへ渡せるようにします。

from pydantic import BaseModel, Field

class FeaturePlan(BaseModel):
    summary: str = Field(description="One or two sentences on what will be built.")
    implementation_steps: list[str]
    files_to_modify: list[str]
    risks: list[str]
    tests: list[str]

response = client.messages.parse(
    model=MODEL,
    max_tokens=8192,
    system=SYSTEM_PROMPT,
    messages=[{"role": "user", "content": feature_request}],
    output_format=FeaturePlan,
)

if response.stop_reason == "refusal":
    category = (
        response.stop_details.category
        if response.stop_details and response.stop_details.category
        else "unspecified"
    )
    print(f"Declined: {category}")
elif response.parsed_output is None:
    print(f"No plan. Stop reason: {response.stop_reason}")
else:
    print(response.parsed_output.summary)

messages.parse()はPydanticモデルをJSONスキーマに変換して送信し、返信を検証し、parsed_outputに型付きオブジェクトを返します。構造化出力は一般提供で、ベータヘッダーは不要です。まずstop_reasonを確認してください。後述の拒否はスキーマをスキップし、パース可能なものが残りません。

冒頭の汎用的な結果にも良い点が1つありました:見えていないファイルを名指ししなかったことです。スキーマは構造を検証しますが、事実の根拠までは検証しません。

Claude Fable 5.1とFable 5の違い:API移行の変更点

ツールの強制実行制限、thinkingブロック互換性、追記専用の履歴を先に考慮してください。

  • Fable 5.1はツールの強制選択を拒否します。下のツールループ節でエラーと、代わりに使うauto設定を示します。

  • thinkingブロックの互換性は一方向です。Fable 5.1は以前のClaudeモデルのブロックを読みますが、以前のモデルはFable 5.1のブロックを読めません。

ルーターやフォールバックで会話が古いモデルへ移ると、APIは対象モデルが見る前に非互換なブロックを削除します。残りの履歴は維持されますが、古いモデルはそれらのブロックなしで計画しなければなりません。

以前のターンを編集すると、その後に来るthinkingブロックが無効になります。これは履歴のトリミングやクライアント側要約を壊す可能性があります。

移行ガイドで変更点の全体像を確認してください。

読み取り専用のリポジトリツールを追加

モデルにリポジトリのコンテキストを読み取り専用ツールで与えます。

読み取り専用ツールを定義

ツール層は2部構成です。アクセスルールを強制するPython関数群と、Claudeが呼び出せるスキーマです。

パスをプロジェクトルートに制限

読み取り専用は安全と同義ではありません。モデルは../../.envconfig.pyと同じくらい容易に要求できます。したがって、ガードはプロンプトではなくコードに置くべきです。

def _resolve(self, relative_path: str) -> Path:
    relative = Path(relative_path)
    if relative.is_absolute() or relative.drive:
        raise ToolError(f"path is outside the project root: {relative_path}")

    cursor = self.root
    for part in relative.parts:
        cursor /= part
        if cursor.is_symlink():
            raise ToolError(f"symlinks are not followed: {relative_path}")

    candidate = (self.root / relative).resolve()

    # After resolving "..", the path still has to sit under the allowed root.
    if candidate != self.root and self.root not in candidate.parents:
        raise ToolError(f"path is outside the project root: {relative_path}")
    if candidate.name in DENY_NAMES:
        raise ToolError(f"reading {candidate.name} is not allowed")

    return candidate

絶対パスとシンボリックリンク要素を拒否し、解決後にプロジェクトルート配下に留まることを確認します。../.envを要求すると「path is outside the project root.」が返ります。ツールエラーが返れば、エージェントは許可ファイルで処理を継続できます。

厳密なツールスキーマを定義

リーダークラスはPythonが開けるものを制御します。加えて、Claudeが要求できる3つのアクションを記述するJSONスキーマが必要です。

EMPTY_SCHEMA = {
    "type": "object",
    "properties": {},
    "additionalProperties": False,
}

TOOLS = [
    {
        "name": "list_project_files",
        "description": "List readable text files in the project.",
        "input_schema": EMPTY_SCHEMA,
        "strict": True,
    },
    {
        "name": "read_project_file",
        "description": "Read one text file relative to the project root.",
        "input_schema": {
            "type": "object",
            "properties": {"path": {"type": "string"}},
            "required": ["path"],
            "additionalProperties": False,
        },
        "strict": True,
    },
    {
        "name": "get_project_metadata",
        "description": "Read project metadata and dependency manifests.",
        "input_schema": EMPTY_SCHEMA,
        "strict": True,
    },
]

strictはモデルがツールを選んだ際に引数を検査します。Fable 5.1では重要な点として、ツール呼び出しを強制はしません。

マルチターンのツールループを実行

ベースのループから始めます:ツールを送信し、stop_reasonを確認し、要求された処理を実行して結果を追加し、繰り返します。

MAX_AGENT_TURNS = 8
reader = ProjectReader("sample_project")
messages = [{"role": "user", "content": feature_request}]

for turn in range(1, MAX_AGENT_TURNS + 1):
    response = client.messages.create(
        model=MODEL,
        max_tokens=16000,
        system=SYSTEM_PROMPT,
        tools=TOOLS,
        messages=messages,
    )

    if response.stop_reason == "refusal":
        return declined(response.stop_details.category)
    if response.stop_reason == "max_tokens":
        return cutoff()
    if response.stop_reason != "tool_use":
        messages.append({"role": "assistant", "content": response.content})
        break

    messages.append({"role": "assistant", "content": response.content})
    results = []
    for block in response.content:
        if block.type != "tool_use":
            continue
        output, is_error = reader.run(block.name, block.input)
        results.append({
            "type": "tool_result",
            "tool_use_id": block.id,
            "content": output,
            "is_error": is_error,
        })

    messages.append({"role": "user", "content": results})
else:
    return turn_limit()

MAX_AGENT_TURNSはモデルの要求回数を制限しますが、支出は制限しません。必要に応じて別途コスト上限を設けてください。ループはrefusalmax_tokenstool_useを直接処理し、それ以外の停止理由は点検段階の終了を意味します。is_errorは拒否されたパスがあったことをモデルに伝え、別のアクションの選択を促します。

強制ツール選択が400を返す理由

Fable 5ではtool_choice: {"type": "any"}で初回のツール呼び出しを強制できました。Fable 5.1では、実行前に以下のエラーを返します。

tool_choice: type "tool" and "any" are not supported for this model.

強制呼び出しは常時オンのthinkingをスキップしてしまいます。tool_choiceautoのままにし、上で定義した厳密なスキーマを使い、特定のステップで必要なときはプロンプト内でツール名を明示してください。

Fable 5.1は1ターンにつき1回のツール呼び出しを発行することがあり、Fable 5のように複数をバッチしない場合があります。これにより往復が増えます。プロンプトにこの1行を追加してください:「独立したファイルは1ターン内でまとめて要求すること」。サンプル実行では独立ファイル9件をバッチ化しましたが、数は変動します。

Claude Fable 5.1の応答と進捗更新をストリーミング

テキストストリーミングは生成中に応答コンテンツを吐き出し、進捗更新はツール呼び出し間の間合いをカバーします。

テキスト応答をストリーミング

フルプロジェクトでは、context_system()SYSTEM_PROMPTとプロジェクト要約を結合してからストリームを開始します。

with client.messages.stream(
    model=MODEL,
    max_tokens=8192,
    system=context_system(),
    messages=[{"role": "user", "content": feature_request}],
) as stream:
    for chunk in stream.text_stream:
        print(chunk, end="", flush=True)
    final = stream.get_final_message()

print(f"\nOutput tokens: {final.usage.output_tokens}")

get_final_message()は、ストリームが枯渇した後に、使用量と停止理由を含む組み立て済みメッセージを取得します。ストリーミングのチャンクは完全なJSONを保証しないため、パースは最終メッセージを待ってから行ってください。

ツール呼び出し間の進捗を表示

テキストストリーミングはツール呼び出し中の遅延をカバーしません。Fable 5.1はツール呼び出し前に短い進捗更新を書けます。デフォルトのthinking.display"omitted"の場合、進捗専用のthinkingブロックは空ですが、通常のテキストの前置きを出すことはあります。

ベータヘッダーthinking-display-updates-2026-08-18display: "updates"を使うと、APIドキュメント上は推論を隠したまま、中身のあるthinkingブロックが読める進捗更新として定義されます。本プロジェクトの実走では、thinkingは空のままで、読めるステータスはtool_use直前の通常のtextブロックとして届きました。そのためヘルパーは両方のブロックタイプを確認し、ループはtool_useで終わるターンでのみ呼びます。

PROGRESS_BETA = "thinking-display-updates-2026-08-18"

response = client.beta.messages.create(
    model=MODEL,
    max_tokens=16000,
    betas=[PROGRESS_BETA],
    thinking={"type": "adaptive", "display": "updates"},
    system=SYSTEM_PROMPT,
    tools=TOOLS,
    messages=messages,
)

def status_lines(response) -> list[str]:
    lines = []
    for block in response.content:
        if block.type == "thinking":
            text = (block.thinking or "").strip()
        elif block.type == "text":
            text = (block.text or "").strip()
        else:
            continue
        if text:
            lines.append(text)
    return lines

進捗メッセージは、モデルが読む予定のファイルを説明します:「アプリの配線、config、拡張、公開ルートと認証ルート、そして既存のテストを読みます。レート制限が入り込むのはそこだからです。」これらを表示し、空のブロックは無視してください。

Claude Fable 5.1のエージェントループ。ターンごとのトークン使用量、進捗メッセージ、バッチ化されたファイル読取が表示されたターミナル

エージェントは進捗を報告しながらファイルを読みます。画像:著者作成。

Fable 5.1は、特に高いeffortでは、Fable 5より進捗更新が少なくなる傾向があります。インターフェースで定期的な更新が必要な場合は、冒頭の一言、進捗メッセージ、締めの要約を依頼してください。

会話途中でClaude Fable 5.1のeffortを変更

次の機能はとても便利です。ご存じのとおり、リポジトリエージェントはターンごとに同じ推論深度を必要としません。

ターン間でeffortを変更

エージェントループでは、定型的な取得ターンはeffortを下げ、最終の計画ターンでは再び上げます。

ベータヘッダーmid-conversation-output-config-2026-07-01を使うと、effortレベルだけを変更するsystemメッセージを追加できます。

EFFORT_BETA = "mid-conversation-output-config-2026-07-01"

messages.append({"role": "system", "content": [], "output_config": {"effort": "low"}})
messages.append({"role": "user", "content": "Summarize the repository evidence in five words."})

response = client.beta.messages.create(
    model=MODEL,
    max_tokens=4096,
    betas=[EFFORT_BETA],
    output_config={"effort": "high"},
    messages=messages,
)

新しいレベルは現在のターン途中ではなく、次のユーザーターンから適用され、プロンプトキャッシュは無効化しません。一方、トップレベルのoutput_config.effortをリクエスト間で変更すると、キャッシュは無効化されます。

エージェントはトップレベルをhighに保ち、定型の取得前にメッセージ単位のmedium指示を追加し、最終計画前にhighを追加します。対になるテストでは、低いeffortでの出力トークンは76から18に減りました。これは一例であり、期待値ではありません。

システム指示を1ターンに適用

最終計画中に追加のファイル読取をブロックするには、ターン限定の指示を使います。

ベータヘッダーmid-conversation-system-clear-at-2026-08-21を付けたsystemメッセージにclear_at: "next_user_message"を設定します。APIはこのテキストを現在のターンのsystem指示として扱い、次のユーザーメッセージ後にレンダリングを停止します。メッセージ自体はmessagesに残るため、前の履歴は変わらず、キャッシュも一致を保ち、クリア済みメッセージは入力トークンを消費しません。

SCOPED_SYSTEM_BETA = "mid-conversation-system-clear-at-2026-08-21"

messages.append({"role": "system", "content": [], "output_config": {"effort": "high"}})
messages.append({"role": "user", "content": "Write the implementation plan now."})
messages.append({
    "role": "system",
    "content": (
        "For this turn only: do not request more files. Base the plan on what "
        "you have already read, and name only paths you actually opened."
    ),
    "clear_at": "next_user_message",
})

response = client.beta.messages.create(
    model=MODEL,
    max_tokens=16000,
    betas=[EFFORT_BETA, SCOPED_SYSTEM_BETA],
    tool_choice={"type": "none"},
    output_config={"format": {"type": "json_schema", "schema": plan_schema()}},
    system=agent_system(),
    tools=TOOLS,
    messages=messages,
)

tool_choice={"type": "none"}は最終リクエストで新たなツール呼び出しを防ぎます。スコープ限定の指示は、エージェントが既に点検したファイルのみに計画を限定します。リマインダーを追加して次のリクエストで削除するのは避けてください。そうした編集は後続のthinkingブロックを無効にします。

Claude Fable 5.1のthinkingブロックによる400エラーを解消

The block is bound to a different conversation」エラーは、thinkingブロックより前の履歴が変わったことを意味します。Fable 5.1の各thinkingブロックは、その直前のsystemプロンプト、ツール定義、メッセージ群に厳密に結び付いています。

結果はアカウントの作成時期に依存します。

  • 2026年8月31日以降に作成されたアカウントは、ブロックが別会話にバインドされている旨の400を受け取ります。

  • それ以前に作成されたアカウントでは、不一致は記録されますが、thinking.block_binding.prefix_mismatch_behaviorを設定したリクエストでのみ作用します。

これは、ベータヘッダーthinking-binding-controls-2026-08-01thinking.block_binding.prefix_mismatch_behavior"drop_block"に設定、そしてinput_transformations配列で検出できます。編集された履歴はreason: "prefix_binding_mismatch"として現れます。統合に対して一度このチェックを行ってください。

以下の操作は不一致を誘発します:

  • 後続のターンを残したまま、前のターンを編集・並べ替え・削除する

  • リクエストごとのテキストを前のターンに差し込み、次のリクエストで削除する

  • 会話途中でトップレベルのsystemプロンプトやtools配列の内容や順序を変更する

  • 後続のリクエストで画像やドキュメントURLが返すバイト列を変える

それぞれに、バインディングを保つ代替手段があります:

  • systemの編集ではなく、会話途中のsystemメッセージで指示を追加する。

  • トップレベル配列の変更ではなく、会話途中のツール変更を使う。

  • 編集と見なされないサーバー側のコンテキスト編集やコンパクションで履歴をトリムする。

  • thinkingブロックは変更せずに戻す。

なお、cache_controlマーカーの移動や、リクエストレベルのeffort変更は安全で、thinkingブロックのバインディングを無効にしません。ただし、トップレベルのeffort変更はプロンプトキャッシュを再開するため、キャッシュ済みプレフィックスを維持したい場合はメッセージ単位のeffortを使ってください。

プロンプトキャッシュとClaude Fable 5.1 APIコスト

以下の実行では、新規入力、キャッシュ書き込み、キャッシュ読み取り、出力コストを分けて示します。

自動プロンプトキャッシュを追加

プロンプトキャッシュはターン間で繰り返されるコンテキストのコストを削減します。履歴が伸びると分岐点の位置も変わるため、ここでは自動キャッシュの方が適しています。

トップレベルのcache_controlで、各リクエストの最新のキャッシュ可能ブロックまで分岐点を移動します。

response = client.beta.messages.create(
    model=MODEL,
    cache_control={"type": "ephemeral"},
    system=system,
    tools=TOOLS,
    messages=messages,
    # Other request fields...
)

Fable 5.1では、512トークン未満のキャッシュ可能プレフィックスは、cache_controlでマークしてもキャッシュされません。APIは通常処理し、キャッシュのカウンタは両方ともゼロを返します。583トークンのプレフィックスを書き込むコストは$0.0073、次ターンでの読み取りは$0.00015でした。2ターン目でも新規部分のキャッシュ書き込みが必要だったため、ヒットしてもすべての入力コストが消えるわけではありません。

キャッシュを考慮したAPIコストを見積もる

response.usageは新規入力、キャッシュ作成、キャッシュ読み取り、出力を個別に報告します。4つのカウンタをそれぞれ課金し、新規入力と出力だけを合算しないでください。そうするとキャッシュ書き込みコストを見落とし、キャッシュヒットの価格を過大評価します。

以下は、3ターンで12ファイルを読み、最終計画を生成した1回分のコスト内訳です。

Line item

Tokens

Estimated cost

Share

Output

5,713

$0.2857

59.4%

Cache writes

15,426

$0.1928

40.1%

Fresh input

50

$0.0005

0.1%

Cache reads

6,549

$0.0016

0.3%

Total

27,738

$0.4806

100%

キャッシュ読取は見積もり全体の0.5%未満のごく一部でした。Fable 5の旧レートでは、実行コストは$0.4806ではなく約$0.4855になっていたはずです。各ターンでより多くのコンテキストを再利用するほど節約は大きくなります。

この実行では出力が約60%、キャッシュ書き込みが約40%を占めました。ここで用いた5分レートでは、キャッシュ書き込みトークンはキャッシュ読取りトークンの50倍のコストです。1時間のキャッシュ書き込みでは80倍になります。

Claude Fable 5.1の拒否応答とフォールバックを扱う

拒否と失敗リクエストでは、アプリケーションでの扱いが異なります。

出力のパース前に拒否を検出

出力前の拒否はHTTP 200でstop_reason: "refusal"、空のcontent、そしてstop_detailsとして届きます。カテゴリはnullのこともあります。ストリーム後半の拒否は部分的な出力に続いて届くことがあり、アプリケーションはそれを破棄すべきです。呼び出しをtry/exceptで囲んでも、どちらのケースも捕捉できません。

response = client.messages.create(model=MODEL, max_tokens=8192, messages=messages)

if response.stop_reason == "refusal":
    category = (
        response.stop_details.category
        if response.stop_details and response.stop_details.category
        else "unspecified"
    )
    return f"This request was declined ({category})."

アプリケーションの状態として扱ってください。許可されたリクエストが不明確であれば、より正確に書き直します。分類器を回避することを目的としたリトライロジックは作らないでください。

拒否はHTTP 200で届きます。画像:著者作成。

サーバー側フォールバックを構成

サーバー側フォールバックは、server-side-fallback-2026-07-01ベータヘッダー付きでfallbacks: "default"を使い、他モデルで拒否リクエストを再試行できます。Fable 5.1の許可対象はOpus 4.8とOpus 5です。

デフォルトフォールバックは、拒否カテゴリに推奨ターゲットがある場合にのみ動作します。検証したreasoning_extractionの拒否ではフォールバックは起動しませんでした。すべての拒否が自動再試行されると仮定せず、usage.iterationsを確認してください。前述のとおり、古いモデルへの移行ではFable 5.1のthinkingブロックは落とされます。

FastAPIでClaude Fable 5.1エージェントを提供

これでローカルエージェントは、同じワークフローをHTTP API経由でも提供できます。

planエンドポイントを作成

ローカルスクリプトだけで十分なら、この節はスキップしてください。WebサービスにはFastAPIAsyncAnthropicを使います。ライフスパンハンドラーでプロセス用のクライアントを1つ作り、スキーマとプロンプトは既存のエージェントモジュールからインポートします。

@asynccontextmanager
async def lifespan(_: FastAPI):
    global client
    client = AsyncAnthropic()
    try:
        yield
    finally:
        await client.close()


@app.post("/plan", response_model=PlanResponse)
async def create_plan(body: PlanRequest):
    reader = resolve_project(body.project)
    messages, totals, turns, tool_calls = await inspect(reader, body.feature_request)
    plan, final_usage = await write_plan(messages)
    totals.add(final_usage)
    return PlanResponse(plan=plan, turns=turns, tool_calls=tool_calls, usage=as_usage(totals))

呼び出し側はパスではなくプロジェクト名を送る点に注目してください。resolve_project()はそれを少数の許可ルートにマップするため、サーバーに任意の場所を読ませるリクエストはできません。このサービスはアプリの判断で拒否を422にマッピングします。Claude API自体はHTTP 200で返します。

起動はuvicorn app:app --reloadで。インタラクティブドキュメントはhttp://localhost:8000/docsにあります。

エンドポイントは見積コスト付きの計画を返します。動画:著者作成。

/plan/streamエンドポイントは、バックグラウンドタスクで点検を実行し、進捗とツールイベントをasyncio.Queueに置き、StreamingResponseで配信します。ストリームが閉じると、ジェネレーターはバックグラウンドタスクをキャンセルします。リポジトリ内の Streamlitインターフェースは同じイベントストリームをレンダリングします。

Streamlitはエージェントのライブ進捗を表示します。動画:著者作成。

Claude Fable 5.1エージェントのデプロイ前チェックリスト

これまで組み込んだ制限やチェックは、そのままサービスの一部です。デプロイ前に、ローカル実行では見えにくい運用上の要素を追加してください。

  • SDKの429および5xxに対するデフォルト2回リトライを確認し、サービスのレイテンシー予算に合わせてmax_retriesとタイムアウトを設定

  • リクエストのタイムアウトを設定し、既存のSSEタスクのキャンセルがクライアント切断時に未了処理を停止することを確認

  • 各実行で、モデルID、SDKバージョン、リクエストID、停止理由、4つのトークン区分をログ

  • キャッシュ書き込み、出力トークン、拒否、ターン上限到達の増加を検知してアラート

  • アカウントの保持設定がモデル要件に合致していることを確認

  • SDKを固定し、各リリース前にベータヘッダーを再確認

Opus 5やSonnet 5ではなくClaude Fable 5.1を使うべき場面

  • AnthropicはOpus 5を妥当なデフォルトとして推奨しています。
  • リポジトリの長大な分析、難しいデバッグ、大きなコンテキストを要するエージェント的タスクでOpus 5が及ばない場合にFable 5.1をテストしてください。
  • リポジトリ作業や日常タスクでは、品質・レイテンシー・コストの観点でSonnet 5とOpus 5を比較してください。
  • 分類、抽出、短い回答、より単純なリクエストには、Sonnet 5が適したデフォルトです。最も簡単なタスクにはHaiku 4.5でも十分な場合があります。

Fable 5.1を「新しいから」という理由だけで選ばないでください。単発のリクエストでもeffortや構造化出力、ストリーミングは使えますが、ここで用いたループや繰り返しプレフィックスのキャッシュからは恩恵を受けません。

まとめ

最初の呼び出しで得た汎用的な計画は、エージェントがリポジトリを読んではじめて有用になりました。完成版では3ターンで12ファイルを点検し、見積コストの99.5%を出力とキャッシュ書き込みが占めました。パス境界と追記専用履歴は維持しつつ、effortを下げてもリポジトリツールを省略しない範囲でコストが下がるかをテストします。

単一の応答でタスクに答えられるなら、構造化出力で止めてください。答えがリポジトリファイルに依存したり、呼び出し間の進捗報告が必要な場合は、ツールループを使いましょう。

モデル選定の詳細は、Introduction to Claude Modelsコースをおすすめします。プロンプト設計とエージェントワークフローは、Software Development with Cursorコースをご覧ください。

FAQs

Claude Fable 5.1はコードだけでなく画像も読めますか?

はい。画像入力を受け付け、グラフやPDFを読めます。リポジトリ計画には不要なため、メインの例からはビジョンを外しました。もしこのエージェントをUI変更の計画に拡張するなら、機能要望と一緒に現在のスクリーンショットを送ります。小さな視覚的差異がタスクに影響しない場合は、事前に縮小してください。

Fable 5から切り替えたら、エージェントが遅くなったのはなぜですか?

まずツール結果を確認してください。前述のバッチ化指示が既に含まれているなら、その数とサイズを両方比較します。現行のリーダーは各ファイルを40,000バイトに上限しています。まだ大きすぎる場合は、行範囲や検索引数を追加し、ツールが関連部分のみを返せるようにしてください。

なぜClaude Fable 5.1は400 invalid_request_errorを返すのですか?

まずリトライしないでください。invalid_request_errorは通常、リクエスト形状かアカウント設定の変更が必要な合図です。本プロジェクトでは、想定される原因は、強制tool_choice、不適合な保持設定、thinkingを残したままのプレフィックス編集、または対応するヘッダーなしで送られたベータフィールドです。示された原因を修正してから、再送してください。

ソースファイルと要約、どちらをキャッシュすべきですか?

私の基準では:複数ターンにわたり正確なコードが重要な場合はソースファイルをキャッシュします。後続ステップで必要なのがアーキテクチャやファイルマップだけなら要約をキャッシュします。要約はトークンコストが低い反面、最終計画に必要な1行を省く可能性があります。

Batch APIでこのエージェントを実行できますか?

それ自体では不可です。Batch APIは個別のMessagesリクエストを送るだけで、このクライアント側ツールループは動かしません。ライブ進捗が不要な自己完結型のリポジトリレビューには適します。完全なループをバッチで走らせるには、1つのバッチのツール要求を処理してから次を送るコードが必要です。

トピック

DataCampでAIを学ぼう!

Tracks

開発者向けアソシエイトAIエンジニア

26時間
APIやオープンソースライブラリを使って、ソフトウェアアプリケーションにAIを統合する方法を学びます。 AIエンジニアになるための旅を今日始めましょう!
詳細を見るRight Arrow
コースを開始
もっと見るRight Arrow