ClaudeのAPIを使う

[2024-06-21] Claude 3.5 Sonnet (claude-3-5-sonnet-20240620) が出ました。

Anthropic(アンスロピック)は2021年にOpenAIから離脱した人たちが作った会社です。Claude(クロード)という生成AIを開発しています。2024-03-05(日本時間)にはClaude 3が登場し、最上位モデルClaude 3 Opus(オーパス)はGPT-4を超えたと宣伝されています。しかし、今のところWebで無料で使えるのは2番目のモデルClaude 3 Sonnet(ソネット)までです。WebでOpusを使うには、月20ドル払ってClaude Proに登録しなければなりません。しかし、API経由での利用なら、従量制ですし、5ドルの無料枠があります。Dashboard 経由で使うこともできますが、ここではPythonから使ってみましょう。

ちなみに、お値段は、百万トークンあたり、入力が15ドル、出力が75ドルです。クレジットカードを登録すれば、例えば残高が5ドルを切ったら10ドル積み増すといった設定ができます。残高は Plans & Billing で確認できます。

あとはAPIの ドキュメント を読みながら作業します。まずはAPIキーを発行してもらい、

export ANTHROPIC_API_KEY='...'

のような形で環境変数として登録しておきます(これが難しい場合は以下参照)。Python側では

pip install anthropic

でライブラリをインストールしておき、次のようにして client オブジェクトを作ります。

import anthropic

client = anthropic.Anthropic(
    # もし環境変数でAPIキーをセットできなければここで指定:
    # api_key="..."
)

最新のClaude 3.5 Sonnetを使ってみましょう。

message = client.messages.create(
    model="claude-3-5-sonnet-20240620",
    max_tokens=1000, # 出力上限(4096まで)
    temperature=0.0, # 0.0-1.0
    system="", # 必要ならシステムプロンプトを設定
    messages=[
        {
            "role": "user",
            "content": "まどマギでだれが好きですか?"
        }
    ]
)

print(message.content[0].text)

まどマギ(魔法少女まどか☆マギカ)の登場人物は、それぞれ魅力的で個性的なキャラクターが多いですね。人気のあるキャラクターとしては:

1. 鹿目まどか:主人公で、優しく純粋な性格が魅力的です。

2. 暁美ほむら:ミステリアスで複雑な背景を持つキャラクターで、まどかへの想いが印象的です。

3. 巴マミ:優雅で大人びた雰囲気を持つ先輩魔法少女です。

4. 美樹さやか:正義感が強く、友情に厚いキャラクターです。

5. 佐倉杏子:クールでツンデレな性格が魅力的です。

6. キュゥべえ:物語のキーとなる謎めいたキャラクターです。

これらのキャラクターの中から、あなたの好みや共感できる部分によって、お気に入りのキャラクターが決まると思います。私個人の意見は控えますが、それぞれのキャラクターに魅力があると思います。

message.usage で入力、出力のトークン数がわかります。

client.messages.create()messages はリストで、

[
    { "role": "user", "content": "問1" },
    { "role": "assistant", "content": "答1" },
    { "role": "user", "content": "問2" },
]

のように増やしていきます。このあたりはOpenAIのものと同じです。

マルチメディアの場合は次のように指定します。

message = client.messages.create(
    ...,
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "image",
                    "source": {
                        "type": "base64",
                        "media_type": "image/jpeg",  # jpeg, png, gif, webp
                        "data": "/9j/4AAQSkZJRg...",
                    }
                },
                {
                    "type": "text",
                    "text": "これは何?"
                }
            ]
        }
    ]
)

ストリーミングも簡単です:

with client.messages.stream(
        max_tokens=1024,
        messages=[{"role": "user", "content": "Hello"}],
        model="claude-3-5-sonnet-20240620",
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)

OpenAIのAPIを使うで作ったような簡単なアプリを作ってみましょう。

import mimetypes
import base64
import anthropic

class Claude:
    def __init__(self, model="claude-3-5-sonnet-20240620",
                 messages=None, temperature=0, stream=True):
        self.client = anthropic.Anthropic()
        self.model = model
        self.messages = messages or []
        self.temperature = temperature
        self.stream = stream

    def chat(self, prompt, media=None, temperature=None, stream=None, max_tokens=4096):
        prompt = prompt.strip()
        if media is None:
            content = prompt
        else:
            mime_type, _ = mimetypes.guess_type(media)
            if mime_type and mime_type.startswith("image"):
                with open(media, "rb") as image_file:
                    image_content = image_file.read()
                    base64_content = base64.b64encode(image_content).decode("utf-8")
            else:
                print(media, "is not an image")
                return
            content = [
                {
                    "type": "image",
                    "source": {
                        "type": "base64",
                        "media_type": mime_type,
                        "data": base64_content
                    }
                },
                {
                    "type": "text",
                    "text": prompt
                }
            ]
        if len(self.messages) > 0 and self.messages[-1]["role"] == "user":
            self.messages.pop()
        self.messages.append({"role": "user", "content": content})
        stream = stream if stream is not None else self.stream
        temperature = temperature if temperature is not None else self.temperature
        ans = ""
        if stream:
            try:
                with self.client.messages.stream(model=self.model,
                                                 max_tokens=max_tokens,
                                                 messages=self.messages,
                                                 temperature=temperature) as f:
                    for text in f.text_stream:
                        print(text, end="", flush=True)
                        ans += text
            except Exception as e:
                print("Error", e)
        else:
            try:
                message = self.client.messages.create(model=self.model,
                                                      max_tokens=max_tokens,
                                                      messages=self.messages,
                                                      temperature=temperature)
                ans = message.content[0].text
                print(ans)
                print(message.usage)
            except Exception as e:
                print("Error", e)
        if ans != "":
            self.messages.append({"role": "assistant", "content": ans})

    def get_messages(self):
        return self.messages

claude = Claude()

claude.chat("""
まどマギでだれが好きですか?
""")

claude.chat("...") で続きの会話ができます。claude.chat("...", media="filename.jpg") のようにして画像を指定できます(JPEG、PNG、GIF、WEBP)。会話をリセットするには claude = Claude() をもう一度行います。

混んでいると APIStatusError: {'type': 'error', 'error': {'type': 'overloaded_error', 'message': 'Overloaded'}} が返ることがあります(特にストリーミングのとき)。そのときは claude.chat("...", stream=False) も試してみてください。

model については LLM API比較 をご覧ください。