Anthropic APIとは何か・OpenAI APIと何が同じで何が違うか

OpenAI APIの記事で、APIが基本的に何も覚えていないこと、ロールを指定して初めて振る舞いが決まることを見てきました。Anthropic API(Claudeのモデルを使うためのAPI)も同じくステートレスな部品であり、考え方の骨格はよく似ています。ただし「似ているから同じ書き方で動くだろう」と思ってOpenAI API用のコードをそのまま流用すると、実際にはエラーになる箇所がいくつかあります。ここでは、共通点を土台にしながら、地味だが詰まりやすい設計の違いを見ていきます。

「system」はメッセージの一部ではなく、独立した引数として渡す

OpenAI APIで{role: "system", content: "..."}をmessages配列に混ぜて送っていたので、Anthropic APIでも同じようにrole: "system"のメッセージを混ぜれば動くだろうと思うかもしれませんが、これはエラーになります。Anthropic APIでは、振る舞いを決める指示はmessages配列とは別の、トップレベルのsystemという引数に渡します。messages配列の先頭にsystemロールのメッセージを置くことはできず、そこに振る舞いの指示を混ぜようとするとエラーになります(一部の新しいモデルでは会話の途中からならsystemロールのメッセージを追加できますが、それでも先頭には置けません)。

import anthropic

client = anthropic.Anthropic()

# systemはmessagesの外、独立した引数として渡す
response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    system="あなたは初心者向けに丁寧語で説明するプログラミング講師です",
    messages=[
        {"role": "user", "content": "for文とwhile文の違いは?"}
        # ここに {"role": "system", ...} を混ぜるとエラーになる
    ]
)
print(response.content[0].text)
🎯 読み解きポイント:同じ「固有スキル」でも、登録する欄の位置がゲームによって違う

ハクスラゲームでたとえると、キャラクターに「丁寧語で話す」という固有の性格設定を持たせたい場面です。片方のゲームでは、この設定を通常のスキル欄に他のスキルと混ぜて登録します(OpenAI APIのmessages配列にsystemロールを混ぜる書き方)。もう片方のゲームでは、性格設定専用の独立した欄が別に用意されていて、通常のスキル欄とは分けて登録する仕組みです(Anthropic APIの独立したsystem引数)。どちらも「性格を設定する」という目的は同じでも、登録する欄の場所が違うため、片方のゲームの操作手順をもう片方にそのまま持ち込むと、間違った欄に登録しようとしてはじかれます。

OpenAI API messages: [ ... ] role: system role: user Anthropic API system(独立引数) messages: [ ... ] role: user Anthropic APIのmessagesにはuser/assistantしか入らない

会話が長くなる問題は、プロンプトキャッシュという仕組みで別に緩和する

OpenAI APIと同じくAnthropic APIも、会話を続けるにはこれまでのやり取り全部を毎回送り直す必要があるので、「会話が長くなるほど、同じ内容に何度も課金され続けて際限なく高くなるのでは」と心配になるかもしれません。Anthropic APIには、この心配を和らげるためのプロンプトキャッシュという仕組みが用意されています。systemプロンプトや長い参考資料など、毎回変わらない部分にcache_controlという印を付けておくと、2回目以降の呼び出しでその部分を再処理せず、通常の入力トークン価格の約1割ほどで読み出せます。キャッシュには対象となる最小トークン数があり、これはモデルによって512トークンから4,096トークンまで幅があります。短すぎる部分はエラーにはならず黙って通常課金のまま処理されるので、実際にキャッシュが効いているかは、応答に含まれるcache_creation_input_tokens・cache_read_input_tokensが0でないかで確認するのが確実です。

# 長い参考資料の部分にだけキャッシュの印を付ける例
response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            "text": long_reference_document,  # 数千トークンある長い参考資料
            "cache_control": {"type": "ephemeral"}  # ← ここにキャッシュの印
        }
    ],
    messages=[{"role": "user", "content": "この資料の3章を要約して"}]
)
🎯 読み解きポイント:長い前口上を毎回話すNPCに、1回だけ録音させて次からは再生させる

長い前口上を話してから本題に入るNPCがいるとします。話しかけるたびに毎回同じ前口上を最初から話させるのは時間の無駄です。プロンプトキャッシュは、このNPCに一度だけ前口上を録音させておき、次に話しかけたときは録音を再生するだけで済ませる仕組みに近いです。前口上そのもの(キャッシュした部分)は変わらないので再生で済みますが、プレイヤーが話しかける本題の部分(会話の続き)は毎回変わるので、そこだけは録音では対応できず、都度きちんと話す必要があります。

よくあるはまりポイント:モデル名をエイリアスのまま本番に固定し、いつの間にか挙動が変わる

モデル名は一度指定すれば、その後もずっと同じモデルが呼ばれ続けるはずだと思うかもしれませんが、Anthropicのモデル名には2種類あることに注意が必要です。日付入りのスナップショット名(特定時点のモデルを指す、バージョン固定の名前)と、エイリアス名(常に最新版を指す、日付なしの名前)です。開発中に動作確認したときと同じ挙動を本番でも維持したいなら、エイリアス名のまま本番のコードに書いてしまうと、Anthropic側が新しいモデルをリリースしたタイミングでエイリアスの指す先が切り替わり、コードを何も変えていないのに応答の傾向が変わって困惑する、という事故につながります。

あわせて知っておきたい用語
system引数
Anthropic APIでモデルの振る舞いを指定する、messages配列とは別のトップレベルの引数。messagesにはuser/assistantロールしか使えない
プロンプトキャッシュ
変わらない部分のプロンプトに印を付けておき、2回目以降の呼び出しで通常の約1割の価格で読み出せる仕組み。対象には最小トークン数がある
スナップショット名 / エイリアス名
モデル名の2つの形式。スナップショット名は日付入りで特定バージョンに固定、エイリアス名は日付なしで常に最新版を指す
💡 ワンポイントアドバイス:本番のモデル名はスナップショット名で固定する

開発中の動作確認はエイリアス名で手軽に進めて構いませんが、本番環境にデプロイするコードでは、日付入りのスナップショット名に固定してください。モデルを更新したいときは、まず開発環境でスナップショット名を新しいものに変えて動作を確認してから、本番のコードを意図的に書き換える、という手順を踏むと、知らないうちに挙動が変わる事故を防げます。

OpenAI APIとの共通点・相違点が掴めたら、他のAIコーディングツールとも見比べてみると理解が深まります。(シリーズ一覧・GitHub Copilot・Cursor・Ollama・Hugging Face・LangChain・Zed・Devin・Gemini CLI)