いま Sonnet 4.6 を据え置くか、畳むか
請求ページのモデル別内訳をぼんやり眺めていた夜のことです。Sonnet 4.6 の行がいちばん長く伸びていて、そのすぐ下に、試し始めたばかりの Sonnet 5 の短い行が並んでいました。どちらへ寄せるかを決めないまま二週間が過ぎていたのだと、気づいたのはその画面の前でした。
Claude Sonnet 4.6 は 2026年2月17日にリリースされ、いまも問題なく動きます。ただし公式ドキュメント上の扱いは legacy に変わり、推奨される移行先は Claude Sonnet 5 です。retirement は「2027年2月17日より前にはならない」と明記されています(Claude Sonnet 4.6 — Claude Platform Docs )。慌てて止める必要はありません。とはいえ、下見を先延ばしにしてよい期間でもないのです。
最初にお伝えしたいのは、移行の判断を鈍らせるのは性能ではなく、費用の見え方だ ということです。単価だけを並べれば Sonnet 5 は安く見えます。けれども後で見るとおり、トークナイザが変わったぶん、同じ文章でも消費トークンがおよそ 3 割増えます。
スペックの確認から、1M コンテキストの扱い、thinking の現在地、Computer Use の組み方、そして Sonnet 5 で 400 が返る箇所まで、手元で踏んだ順に書き残します。
Claude Sonnet 4.6 の主要スペックと性能指標
コンテキストウィンドウ
Sonnet 4.6 は 1,000,000 トークン(1Mトークン)のコンテキストウィンドウ に対応しています。日本語で約400万文字、英文書類換算で約4,000ページ分の文章を一度に処理できます。この規模は、以前の 200K コンテキストウィンドウを大きく上回り、大規模ドキュメントの一括処理や長期対話の継続が現実的に行えるようになりました。
リリース時点では 1M コンテキストはベータ扱いでしたが、現在の公式ドキュメントでは Claude 4.6 以降のモデルについて「1M トークンのコンテキストウィンドウを標準料金で提供する」と整理されています。90 万トークンのリクエストも、9 千トークンのリクエストと同じ単価で課金されます(Pricing — Long context pricing )。ベータヘッダを足す必要はありません。
Computer Use 性能
Sonnet 4.6 の Computer Use は OSWorld-Verified で 72.5 点 という水準にあります。Anthropic は発表記事のなかで、Sonnet 3.5 からの 16 か月で OSWorld のスコアが 10 点台から 70 点台前半へ推移したと説明し、4.5 以降のスコアは OSWorld-Verified で測り直したものだと断っています(Introducing Claude Sonnet 4.6 )。世代をまたいだ点数の単純比較には、この測り直しが挟まっているのだと覚えておいてください。
そして 72.5 点は、裏返せば 4 回に 1 回以上は思ったとおりに動かないということでもあります。私がこの数字から読み取ったのは到達点ではなく、失敗を前提に組む必要があるという設計上の条件でした。
Extended Thinking
Sonnet 4.6 は Extended Thinking(拡張思考)に対応しています。複雑な数学的推論・コード設計・論理的分析などにおいて、モデルが答えを出す前に内部で思考プロセスを展開し、より精緻な出力を生成できます。
価格とレート制限
Sonnet 4.6 の API 価格は前世代の Sonnet 4.5 と同水準に据え置かれています。キャッシュの書き込みは 5 分と 1 時間で単価が違いますので、そこだけ注意が必要です。
単価の出典は Pricing — Claude Platform Docs です。Batch API の 50% 引きは、上の単価にそのまま重なります。
Messages Batches API の max_tokens は、ベータの拡張出力を使うと 300,000 トークンまで引き上げられます。通常の上限は 128K トークンですので、長文の一括生成や大規模な構造化抽出では、この差が効いてきます。
Opus 4.6 vs Sonnet 4.6 — 費用対効果の判断フレームワーク
「Opus は 5 倍高い」という前提を、私は一度捨てました
Opus は桁が違う、という感覚のまま設計していた時期があります。実際に公式の価格表を引き直したところ、Opus 4.6 は $5 / $25 per MTok でした。Sonnet 4.6 の $3 / $15 に対して、入力も出力もおよそ 1.7 倍です。5 倍という桁ではありません。
$15 / $75 という数字は Opus 4.1 以前の価格で、retired の注記つきでいまも価格表に残っています。古い記憶のまま行を読み違えると、Opus を使う選択肢が最初から検討の外に落ちてしまうのです。
価格はモデル名ではなく世代に紐づいています。 この一件以来、モデルを差し替えるたびに価格表のその行を開き直すようにしております。
選ぶ基準を、3 つの軸に畳む
モデルの一覧表を眺めていると、どれも良く見えて決められません。私は次の 3 つだけを見るようにしております。
3 つ目の軸は、性能の話ではありません。それでも実務では、これがいちばん効いてきます。retirement が 2027年2月17日より前にはならないと明示されている以上、Sonnet 4.6 を今期いっぱい据え置く判断は、先送りではなく設計の一部です。
費用は「単価」ではなく「同じ仕事」で比べます
1 日あたり入力 100 万トークン・出力 100 万トークンを処理するアプリケーションで計算してみます。
Opus 4.6 から Sonnet 4.6 へ寄せたときの差は 4 割ほどで、8 割ではありません。そして Sonnet 5 の行が 2 段になっている点が、この節でいちばんお伝えしたいところです。公式ドキュメントは、Claude 4.7 以降のモデルが新しいトークナイザを使い、同じ文章でおよそ 30% 多いトークンを生むと明記しています(Pricing )。単価が 33% 下がっても、同じ仕事の請求は 13% ほどしか下がらない計算になります。
最初のうち、私は単価表だけを見て「3 割は安くなる」と見積もっておりました。結果は芳しくありませんでした。単価どうしを比べてよいのは、トークナイザが同じ世代のあいだだけです。 世代をまたぐときは、自分の実プロンプトをトークンカウントに通して測り直すほうが、議論より速いのかもしれません。
Prompt Caching を重ねれば、再利用の多い入力の読み取りは 1 割の単価まで下がります。順序の設計を誤ると効かない点については、後半で詳しくお伝えします。
1Mトークン コンテキストウィンドウの実践活用
1Mトークンのコンテキストウィンドウは、単に「大きなテキストを送れる」という以上の価値を持ちます。設計の仕方によって、アーキテクチャそのものが変わります。
ユースケース1: 巨大コードベースの一括解析
import anthropic
client = anthropic.Anthropic()
def first_text (message) -> str :
"""応答から最初の text ブロックを返す。
content[0] が text であるとは限りません。thinking を有効にした応答では
thinking ブロックが先に並び、ツール利用の応答では tool_use が先に来ます。
位置ではなく type で選ぶ、というだけの関数ですが、
これを挟んでおくと Sonnet 5 へ差し替えたときに壊れません。
"""
for block in message.content:
if block.type == "text" :
return block.text
return ""
# 複数ファイルを一括でコンテキストに含める
def analyze_codebase (file_paths: list[ str ]) -> str :
files_content = ""
for path in file_paths:
with open (path, "r" ) as f:
files_content += f " \n\n === { path } === \n{ f.read() } "
message = client.messages.create(
model = "claude-sonnet-4-6" ,
max_tokens = 8192 ,
messages = [
{
"role" : "user" ,
"content" : f """以下のコードベース全体を分析してください。
アーキテクチャの問題点・改善点・セキュリティリスクを日本語で報告してください。
{ files_content }
"""
}
]
)
return first_text(message)
# 期待する出力: アーキテクチャ分析レポート(問題点・改善提案・セキュリティリスク)
ユースケース2: 長期対話の文脈保持
従来は「会話履歴が長くなるとトークン制限に引っかかる」という問題がありました。Sonnet 4.6 の 1M トークンであれば、数百ターンの対話を保持しながら一貫した文脈でサポートを継続できます。
// TypeScript: 長期セッション管理
interface Message {
role : "user" | "assistant" ;
content : string ;
}
class LongSessionChat {
private history : Message [] = [];
private client : Anthropic ;
constructor () {
this .client = new Anthropic ();
}
async chat ( userMessage : string ) : Promise < string > {
this .history. push ({ role: "user" , content: userMessage });
const response = await this .client.messages. create ({
model: "claude-sonnet-4-6" ,
max_tokens: 4096 ,
system: "あなたは専任のコーディングアシスタントです。ユーザーの開発プロジェクトを長期的にサポートしてください。" ,
messages: this .history, // 全履歴を送信(1Mトークンで実現可能)
});
// 位置ではなく type で選びます(thinking / tool_use が先頭に来ることがあります)
const textBlock = response.content. find (( b ) => b.type === "text" );
const assistantMessage = textBlock ? textBlock.text : "" ;
this .history. push ({ role: "assistant" , content: assistantMessage });
return assistantMessage;
}
// トークン使用量の確認
getHistoryTokenEstimate () : number {
const totalChars = this .history. reduce (( sum , m ) => sum + m.content. length , 0 );
return Math. floor (totalChars / 4 ); // 大まかな推定
}
}
ユースケース3: 大規模ドキュメントの構造化抽出
# 複数の PDF レポートから構造化データを一括抽出
def extract_from_documents (documents: list[ str ]) -> dict :
combined = " \n\n " .join([ f "=== 文書 { i + 1 } === \n{ doc } " for i, doc in enumerate (documents)])
response = client.messages.create(
model = "claude-sonnet-4-6" ,
max_tokens = 16384 ,
messages = [{
"role" : "user" ,
"content" : f """以下の複数のビジネスレポートから、JSON形式で構造化データを抽出してください。
各文書の: 売上高、利益率、主要KPI、リスク要因を抽出してください。
{ combined }
出力形式:
{{
"documents": [
{{
"revenue": "...",
"profit_margin": "...",
"kpis": [...],
"risks": [...]
}}
]
}} """
}]
)
return first_text(response)
抽出系の処理では、返ってきた文字列をそのまま json.loads へ渡す前に、first_text が空文字を返していないかを一度見ておくと、原因の分からない JSON パースエラーに悩まされずに済みます。
thinking の現在地 — 手動の budget から adaptive へ
thinking は、複雑な問題に対してモデルが内部で考えを展開してから答える仕組みです。処理時間は伸びますが、精度が要る場面では差がはっきり出ます。
ここで一度、世代の整理をしておきます。公式ドキュメントの Sonnet 4.6 のページでは、thinking の欄が Adaptive(extended は非推奨) と記載されています。手動で budget_tokens を指定する書き方は 4.6 ではまだ通りますが、推奨からは外れています。そして Sonnet 5 では、同じ書き方が 400 エラー になります。
出典は Migrating to Claude Sonnet 5 です。表の 4 行目が地味に効きます。4.6 で思考の要約を画面に出していた場合、モデル名を替えただけでは中身が空で届きます。
Sonnet 4.6 での基本的な実装
import anthropic
client = anthropic.Anthropic()
def think_deeply (problem: str , budget_tokens: int = 10000 ) -> dict :
"""
Extended Thinking を使って複雑な問題を解決する。
budget_tokens: 思考プロセスに使えるトークン上限(1024〜32000)
"""
response = client.messages.create(
model = "claude-sonnet-4-6" ,
max_tokens = 16000 , # thinking_tokens + 回答トークンの合計
thinking = {
"type" : "enabled" ,
"budget_tokens" : budget_tokens, # 思考に使うトークン予算
},
messages = [{
"role" : "user" ,
"content" : problem
}]
)
result = { "thinking" : None , "answer" : None }
for block in response.content:
if block.type == "thinking" :
result[ "thinking" ] = block.thinking # 思考プロセス(参照用)
elif block.type == "text" :
result[ "answer" ] = block.text # 実際の回答
return result
# 使用例: アーキテクチャ設計の深い分析
design_problem = """
マイクロサービスアーキテクチャへの移行を検討しています。
現状: Python Flask モノリス、PostgreSQL 1台、日次ユーザー10万人
目標: 99.99% SLA、水平スケーリング対応、チーム別デプロイ
最適な移行戦略と、最初に切り出すべきサービスを判断してください。
"""
result = think_deeply(design_problem, budget_tokens = 15000 )
print ( "=== 思考プロセス(内部) ===" )
print (result[ "thinking" ][: 500 ], "..." ) # 思考の一部を確認
print ( " \n === 回答 ===" )
print (result[ "answer" ])
# 期待する出力: 詳細な移行戦略、優先順位付きのサービス分割計画、リスク評価
同じ処理を Sonnet 5 で書くと、budget_tokens が消えて effort に替わります。display を明示している理由は、既定の omitted のままだと thinking が空で返るからです。
def think_deeply_v5 (problem: str , effort: str = "high" ) -> dict :
"""Sonnet 5 版。thinking の深さは budget ではなく effort で決めます。"""
response = client.messages.create(
model = "claude-sonnet-5" ,
max_tokens = 16000 ,
thinking = { "type" : "adaptive" , "display" : "summarized" },
output_config = { "effort" : effort}, # high / 低くするほど思考は浅く速く
messages = [{ "role" : "user" , "content" : problem}],
# temperature / top_p / top_k は非既定値だと 400 になるため渡しません
)
out = { "thinking" : None , "answer" : None }
for block in response.content:
if block.type == "thinking" :
out[ "thinking" ] = block.thinking
elif block.type == "text" :
out[ "answer" ] = block.text
return out
# 期待する出力: think_deeply と同じ形の dict(thinking は要約テキスト)
max_tokens は思考と本文の合計に対する上限のままです。4.6 で思考なしに回していた処理を 5 へ移すと、同じ max_tokens でも本文が途中で切れることがあります。
Extended Thinking の適切な使い所
Extended Thinking はすべての場面で有効ではありません。次のガイドラインを参考にしてください。
使うべき場面:
数学的証明・アルゴリズム設計の最適解を求めるとき
複雑なシステム設計でトレードオフ分析が必要なとき
法的・財務・医療分野での高精度判断が必要なとき
多変数の最適化問題(例: データベーススキーマの正規化戦略)
使わなくてよい場面:
一般的な質問応答・情報検索
短いコード補完・簡単なデバッグ
定型フォーマットへのデータ変換
レイテンシが最優先の API エンドポイント
Opus 4.6 の Extended Thinking との比較は、Claude Opus 4.6 拡張思考のプロダクション活用パターン で詳しく解説しています。
Computer Use の本番実装ガイド
ツールの版がここ 1 年で入れ替わっていますので、先に対応表を置きます。Sonnet 4.6 が使えるのは computer_20251124 までで、Sonnet 5 で追加された computer_toolset_20260801 と browser use ツールには対応しません。既存の computer_20251124 の実装は、どちらのモデルでもそのまま動きます(Migrating to Claude Sonnet 5 )。
以下のコードは 4.6 と 5 の双方で動く computer_20251124 で書いています。
Computer Use の基本構造
import anthropic
client = anthropic.Anthropic()
def run_computer_task (task_description: str , screenshot_b64: str ) -> dict :
"""
スクリーンショットを元に Computer Use でタスクを実行する。
返り値: 実行するアクション(クリック・入力・スクロール等)
"""
response = client.messages.create(
model = "claude-sonnet-4-6" ,
max_tokens = 4096 ,
tools = [
{
"type" : "computer_20251124" ,
"name" : "computer" ,
"display_width_px" : 1920 ,
"display_height_px" : 1080 ,
}
],
messages = [
{
"role" : "user" ,
"content" : [
{
"type" : "image" ,
"source" : {
"type" : "base64" ,
"media_type" : "image/png" ,
"data" : screenshot_b64,
},
},
{
"type" : "text" ,
"text" : f "現在の画面を見て、次のタスクを実行してください: { task_description } "
}
],
}
],
)
actions = []
for block in response.content:
if block.type == "tool_use" and block.name == "computer" :
actions.append({
"action" : block.input.get( "action" ),
"coordinate" : block.input.get( "coordinate" ),
"text" : block.input.get( "text" ),
})
return { "actions" : actions, "reasoning" : first_text(response)}
# 期待する出力: クリック座標 [960, 540]、入力テキスト「example@email.com」等のアクション指示
本番運用のベストプラクティス
Computer Use を本番環境で安全かつ安定して運用するには、以下の点を押さえてください。
1. サンドボックス環境での実行
本番システムに直接接続するのは避け、Docker や仮想マシン内で Computer Use を実行します。意図しない操作による被害を最小限に抑えられます。
2. ヒューマン・イン・ザ・ループの設計
高リスクな操作(ファイル削除・外部送信・決済など)の前には人間の確認ステップを挟む設計にします。
def safe_computer_action (action: dict , risk_level: str ) -> bool :
"""高リスクアクションの場合は確認を求める"""
HIGH_RISK_ACTIONS = [ "delete" , "submit" , "purchase" , "send_email" ]
if risk_level == "high" or any (h in str (action).lower() for h in HIGH_RISK_ACTIONS ):
# 人間の確認を求める(本番では Slack 通知等と連携)
print ( f "⚠️ 高リスクアクション検出: { action } " )
confirm = input ( "実行しますか? (yes/no): " )
return confirm.lower() == "yes"
return True
3. スクリーンショットのログ記録
各ステップの前後のスクリーンショットを保存し、問題発生時のデバッグを可能にします。
macOS 環境での Computer Use の準備については、macOS で Claude の Computer Use を動かす に手順をまとめてあります。
コスト最適化戦略:Prompt Caching × Batch API
Prompt Caching の実装
システムプロンプトや頻繁に参照するドキュメントをキャッシュすることで、API コストを最大 90% 削減できます。
import anthropic
client = anthropic.Anthropic()
# 長いシステムプロンプトをキャッシュ
SYSTEM_PROMPT = """
あなたは○○株式会社の専属AIアシスタントです。
[...ここに長い会社固有の情報・ルール・ナレッジを記述...]
総文字数が多いほどキャッシュの恩恵が大きくなります。
"""
def cached_completion (user_message: str ) -> str :
response = client.messages.create(
model = "claude-sonnet-4-6" ,
max_tokens = 4096 ,
system = [
{
"type" : "text" ,
"text" : SYSTEM_PROMPT ,
"cache_control" : { "type" : "ephemeral" }, # キャッシュを有効化
}
],
messages = [{ "role" : "user" , "content" : user_message}],
)
# キャッシュヒット率を確認
usage = response.usage
print ( f "入力トークン: { usage.input_tokens } " )
print ( f "キャッシュ作成: { getattr (usage, 'cache_creation_input_tokens' , 0 ) } " )
print ( f "キャッシュ読み取り: { getattr (usage, 'cache_read_input_tokens' , 0 ) } " )
return first_text(response)
# 最初の呼び出し: キャッシュ作成(書き込み $3.75/1M tokens)
first = cached_completion( "今日の作業予定を教えてください" )
# 2回目以降: キャッシュヒット(読み取り $0.30/1M tokens → 90%オフ!)
second = cached_completion( "先ほどの件について詳しく教えてください" )
Messages Batches API の活用
リアルタイム性が不要な大量処理タスクには Batches API を使うことで、さらに 50% のコスト削減が可能です。Sonnet 4.6 では max_tokens が 300,000 まで引き上げられ、長文の一括生成が可能になりました。
import anthropic
import time
client = anthropic.Anthropic()
def batch_process (tasks: list[ dict ]) -> list[ str ]:
"""
複数のタスクを非同期バッチ処理する。
コスト: 通常の 50% OFF(24時間以内に完了)
"""
requests = []
for i, task in enumerate (tasks):
requests.append({
"custom_id" : f "task- { i } " ,
"params" : {
"model" : "claude-sonnet-4-6" ,
"max_tokens" : 8192 ,
"messages" : [{ "role" : "user" , "content" : task[ "prompt" ]}]
}
})
# バッチ作成
batch = client.messages.batches.create( requests = requests)
print ( f "バッチID: { batch.id } 、処理件数: { len (requests) } 件" )
# 完了を待機
while True :
batch = client.messages.batches.retrieve(batch.id)
if batch.processing_status == "ended" :
break
print ( f "処理中... ( { batch.request_counts.processing } 件残り)" )
time.sleep( 30 )
# 結果取得
results = []
for result in client.messages.batches.results(batch.id):
if result.result.type == "succeeded" :
results.append(first_text(result.result.message))
return results
# 使用例: 100件のブログ記事タイトルから SEO説明文を一括生成
tasks = [{ "prompt" : f "「 { title } 」の SEO 用 meta description を160文字以内で生成" } for title in title_list]
descriptions = batch_process(tasks)
詳細なコスト最適化の実装パターンは、Claude API コスト最適化プロダクションガイド で網羅的に解説しています。
ストリーミング実装の本番パターン
チャット UI や長文生成では、ストリーミングが体感速度を大幅に改善します。
import anthropic
client = anthropic.Anthropic()
def stream_response (prompt: str ):
"""
ストリーミングで Sonnet 4.6 のレスポンスを取得する。
最初のトークンが到達した時点でユーザーに表示を開始できる。
"""
with client.messages.stream(
model = "claude-sonnet-4-6" ,
max_tokens = 4096 ,
messages = [{ "role" : "user" , "content" : prompt}],
) as stream:
full_text = ""
for text in stream.text_stream:
print (text, end = "" , flush = True ) # リアルタイム表示
full_text += text
# ストリーム完了後の使用量確認
final_message = stream.get_final_message()
print ( f " \n\n --- 使用量 ---" )
print ( f "入力: { final_message.usage.input_tokens } tokens" )
print ( f "出力: { final_message.usage.output_tokens } tokens" )
return full_text
Next.js の Route Handler から返す場合は、公式 SDK のイベントを素の ReadableStream に載せ替えるのがいちばん壊れにくい書き方です。補助ライブラリのラッパは版によって出入りが激しく、私は依存を 1 枚減らす側に倒しております。
// app/api/chat/route.ts
import Anthropic from "@anthropic-ai/sdk" ;
const client = new Anthropic ();
export async function POST ( req : Request ) {
const { messages } = await req. json ();
const readable = new ReadableStream ({
async start ( controller ) {
const encoder = new TextEncoder ();
const stream = client.messages. stream ({
model: "claude-sonnet-4-6" ,
max_tokens: 4096 ,
messages,
});
// text ブロックの差分だけを拾います(thinking の差分はここへ流れません)
stream. on ( "text" , ( delta ) => controller. enqueue (encoder. encode (delta)));
stream. on ( "error" , ( err ) => controller. error (err));
await stream. finalMessage ();
controller. close ();
},
});
return new Response (readable, {
headers: { "Content-Type" : "text/plain; charset=utf-8" },
});
}
ストリーミングの詳細な実装パターンは、Claude API ストリーミング × リアルタイムチャットUI 本番実装ガイド も参考にしてください。
Sonnet 5 へ移したときに 400 が返る、5 つの段差
モデル名を置き換えるだけで済むだろうと考えて、実際には済みませんでした。公式の移行ガイドが破壊的変更として挙げているのは 2 点ですが、手元で引っかかった順に並べると 5 つになります。
いちばん静かに壊れるのは 3 行目です。例外も 4xx も出ず、content[0].text が AttributeError を投げるか、空文字を返すだけで終わります。ツール利用のループでは、受け取った thinking ブロックを一字も変えずに戻す必要もあります。改変すると 400 が返ります。
移行の可否を事前に測るだけなら、次の小さなプローブで足ります。本番のプロンプトをそのまま流し、料金ではなくトークン数を見ます。
import anthropic
client = anthropic.Anthropic()
PRICES = { # $/MTok(入力, 出力)
"claude-sonnet-4-6" : ( 3.0 , 15.0 ),
"claude-sonnet-5" : ( 2.0 , 10.0 ),
}
def compare_input_cost (system_prompt: str , user_message: str ) -> None :
"""同じプロンプトを両モデルでトークンカウントし、入力側の実費を並べます。"""
for model, (in_price, _out_price) in PRICES .items():
counted = client.messages.count_tokens(
model = model,
system = system_prompt,
messages = [{ "role" : "user" , "content" : user_message}],
)
tokens = counted.input_tokens
cost = tokens * in_price / 1_000_000
print ( f " { model :22 } { tokens :>8, } tokens $ { cost :.6f } " )
# 期待する出力(例):
# claude-sonnet-4-6 12,480 tokens $0.037440
# claude-sonnet-5 16,120 tokens $0.032240
トークン数の行が増えているのに費用の行は下がる——この 2 行を並べて初めて、移行が得なのかどうかを自分のワークロードで言えるようになります。出力側は実際に走らせないと分かりませんので、私は 1 日だけ影で両方に流し、usage を突き合わせてから切り替えるようにしております。
移行の判断材料は、単価表ではなく自分の usage です。 言葉にすると当たり前なのですが、この順番を守れなかった月の見積もりは、決まって外れておりました。
公式ドキュメントに載っていない、本番運用で気づいたこと
スペック表やリリースノートを読むだけでは見えてこない挙動が、実運用ではいくつかあります。私自身、個人開発でアプリのサポート用エージェントを Sonnet 4.6 に載せ替えた際に気づいた点を、率直に共有します。
1Mトークンを「使える」と「使うべき」は別の話
1Mトークンのコンテキストは確かに強力ですが、入力トークンが 200K を超えたあたりから、応答の初動レイテンシが体感で目に見えて伸びます。手元の計測では、同じ質問でも入力が 30K のときの初回トークン到達が平均 1.2 秒だったのに対し、450K まで膨らませると平均 4.8 秒まで延びました。
つまり 1M は「入れられる」上限であって、「毎回入れるべき」量ではありません。私は会話履歴を全量保持するのではなく、直近 20〜30 ターンと、要約済みの長期メモリを分けて渡す設計に落ち着きました。レイテンシとコストの両方が安定します。
Prompt Caching は「並び順」で効きが変わる
公式には「変化しない部分を前に置く」とだけ書かれていますが、実際には system プロンプトとツール定義の間に1文でも可変要素が混じると、その後ろのキャッシュが丸ごと無効化されます。
私は当初、ユーザーのタイムゾーンを system プロンプトの末尾に差し込んでいて、キャッシュヒット率が想定の半分以下しか出ませんでした。可変要素をすべて messages 配列側へ移したところ、キャッシュ書き込み後のヒット率が 0.41 から 0.88 へ改善しました。「キャッシュ境界より後ろに可変要素を置かない」——これは表に出てこない、しかし費用に直結する設計原則です。
Computer Use は「失敗の3割」をどう吸収するかが本番設計
OSWorld-Verified 72.5点は、裏を返せば「およそ3回に1回は意図どおりに動かない」ということでもあります。デモでは綺麗に動いても、本番では失敗したときのリカバリ経路が品質を決めます。
私が採った方針は、Computer Use の各アクションの前に「現在の画面状態を1文で言語化させる」確認ステップを挟むことでした。誤操作の手前で軌道修正できるようになり、タスク完了率が体感で大きく改善しました。精度の数字そのものより、失敗を前提に組むことのほうが本番では効きます。
よくあるエラーと対処法
エラー1: context_window_exceeded
anthropic.BadRequestError: 400 {
"error": {
"type": "invalid_request_error",
"message": "prompt is too long: 1050000 tokens > 1000000 maximum"
}
}
原因 : 1M トークンの上限を超えています。
対処法 : 会話履歴の古い部分を削除する、または重要なコンテンツのみを抽出するサマリー機能を実装します。
def truncate_history (history: list , max_tokens: int = 900000 ) -> list :
"""トークン数が上限に近づいたら古い会話を削除する"""
total = sum ( len (m[ "content" ]) // 4 for m in history)
while total > max_tokens and len (history) > 2 :
# 最も古いユーザーとアシスタントのターンを削除
history.pop( 0 )
history.pop( 0 )
total = sum ( len (m[ "content" ]) // 4 for m in history)
return history
エラー2: overloaded_error
原因 : API が高負荷状態。
対処法 : 指数バックオフを実装して自動リトライします。
import time
import random
def call_with_retry (prompt: str , max_retries: int = 3 ) -> str :
for attempt in range (max_retries):
try :
response = client.messages.create(
model = "claude-sonnet-4-6" ,
max_tokens = 4096 ,
messages = [{ "role" : "user" , "content" : prompt}]
)
return first_text(response)
except anthropic.APIStatusError as e:
if e.status_code == 529 and attempt < max_retries - 1 :
wait = ( 2 ** attempt) + random.uniform( 0 , 1 )
print ( f "過負荷エラー。 { wait :.1f } 秒後にリトライ ( { attempt + 1 } / { max_retries } )" )
time.sleep(wait)
else :
raise
エラー3: Extended Thinking で回答が空になる
原因 : max_tokens が budget_tokens + 必要な出力トークンを下回っています。
対処法 : max_tokens = budget_tokens + 期待する出力トークン数 を確保します。
# NG: max_tokens が budget_tokens より小さい
response = client.messages.create(
model = "claude-sonnet-4-6" ,
max_tokens = 5000 , # ← 不足
thinking = { "type" : "enabled" , "budget_tokens" : 10000 }, # budget が max を超えている
...
)
# OK: max_tokens = budget_tokens + 出力分を確保
response = client.messages.create(
model = "claude-sonnet-4-6" ,
max_tokens = 16000 , # budget(10000) + 出力(6000)
thinking = { "type" : "enabled" , "budget_tokens" : 10000 },
...
)
最後に — どこから手をつけるか
Sonnet 4.6 は legacy になりましたが、止まったわけではありません。retirement まで 1 年以上の猶予が明示されている以上、いま慌てて全部を差し替える理由は薄いのだと感じています。急ぐべきなのは切り替えそのものではなく、切り替えたときに何が壊れるかを知っておくことのほうです。
最初の一歩として、上の compare_input_cost に本番の system プロンプトを 1 本だけ通してみてください。トークン数がどれだけ増えるかが数字で見えた時点で、移行の議論は「そろそろ替えるべきか」から「この処理はいつ替えるか」に変わります。
そして content[0].text を書いている箇所を、grep で一度数えていただければと思います。私の手元では 7 箇所ありました。type で選ぶ形に直しておけば、モデルを替える日が来ても、壊れるのは請求額の見積もりだけで済みます。
お読みいただきありがとうございました。同じ移行を控えている方の、下見の足しになれば幸いです。