[2025-02-25] Claude 3.7 Sonnet (thinking) 用のコードを追記しました。
[2025-04-27] OpenAI互換APIができていたようです。
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 をご覧ください。コード例は これ が参考になります。