[2025-02-25] Claude 3.7 Sonnet (thinking) 用のコードを追記しました。
Anthropic(アンスロピック)は2021年にOpenAIから離脱した人たちが作った会社です。Claude(クロード)という生成AIを開発しています。APIを使うには コンソール で登録してAPIキーをもらいます。
お値段などは LLM API比較 をご覧ください。
Pythonから使うには、発行されたAPIキーを
export ANTHROPIC_API_KEY='...'
のような形で環境変数として登録しておくか、あるいは python-dotenv を使うのが便利です。
pip install anthropic
でライブラリをインストールしておき、次のようにして client
オブジェクトを作ります。
import anthropic client = anthropic.Anthropic( # もし環境変数でAPIキーをセットできなければここで指定: # api_key="..." )
最新のClaude 3.7 Sonnetを使ってみましょう。
message = client.messages.create( model="claude-3-7-sonnet-20250219", max_tokens=1000, # 出力上限 temperature=0.0, # 0.0-1.0 system="", # 必要ならシステムプロンプトを設定 messages=[ { "role": "user", "content": "まどマギでだれが好きですか?" } ] ) print(message.content[0].text)
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)
も試してみてください。
Claude 3.7 Sonnet からは thinking が使えますが、thinking を使うなら temperature は 1 にする必要があります。上のコードに thinking を付けたものは次のようになりそうです(とりあえず動いているようですが十分テストできていません)。
import anthropic class Claude: def __init__(self, model="claude-3-7-sonnet-20250219", messages=None): self.client = anthropic.Anthropic() self.model = model self.messages = messages or [] def get_messages(self): return self.messages def chat(self, prompt, thinking=True, max_tokens=4000, # <= 64000 (<= 128000) budget_tokens=2000, # >= 1024, < max_tokens, not used when thinking=False temperature=0): # not used when thinking=True if len(self.messages) > 0 and self.messages[-1]["role"] == "user": self.messages.pop() self.messages.append({"role": "user", "content": prompt.strip()}) content_thinking = "" content_text = "" content_signature = "" redacted_data = "" args = { "model": self.model, "max_tokens": max_tokens, "messages": self.messages } if thinking: args["thinking"] = { "type": "enabled", "budget_tokens": budget_tokens } else: args["temperature"] = temperature if max_tokens > 64000: args["extra_headers"] = { "anthropic-beta": "output-128k-2025-02-19" } try: # debugout = open("debugout", "a") ##### debug with self.client.messages.stream(**args) as stream: current_block_type = None for event in stream: # print(event, file=debugout) ##### debug if event.type == "content_block_start": current_block_type = event.content_block.type print("<" + current_block_type + ">") elif event.type == "content_block_delta": if event.delta.type == "thinking_delta": print(event.delta.thinking, end="", flush=True) content_thinking += event.delta.thinking elif event.delta.type == "text_delta": print(event.delta.text, end="", flush=True) content_text += event.delta.text elif event.delta.type == "signature_delta": # print(event.delta.signature, end="", flush=True) content_signature += event.delta.signature elif event.type == "content_block_stop": print("\n</" + current_block_type + ">") if current_block_type == "redacted_thinking": redacted_data += event.content_block.data current_block_type = None elif event.type == "message_stop": print(event.message.usage) # debugout.close() ##### debug except Exception as e: print("Error", e) content = [] if redacted_data != "": content.append( anthropic.types.redacted_thinking_block.RedactedThinkingBlock( data=redacted_data, type="redacted_thinking" ) ) if content_thinking != "": content.append( anthropic.types.thinking_block.ThinkingBlock( signature=content_signature, thinking=content_thinking, type="thinking" ) ) if content_text != "": content.append( anthropic.types.text_block.TextBlock( text=content_text, type="text" ) ) self.messages.append({ "role": "assistant", "content": content }) claude = Claude() claude.chat(""" (ここに質問を書く) """)
詳しくはドキュメントの Building with extended thinking をご覧ください。コード例は これ が参考になります。