Python
使用 Anthropic 和 OpenAI SDK 的 Python 示例:普通请求、流式输出、工具调用往返、图片、计算 token 数、列出模型和处理错误。
何时阅读本页
你在 Python 中通过官方 Anthropic SDK(anthropic)或 OpenAI SDK(openai)调用 EZAT Router。原始 HTTP 请求见 curl。
安装与配置
安装你要用的 SDK(或者两个都装),并把 key 放进环境变量:
pip install anthropic openai
export EZAT_API_KEY="ezat_sk_…"每个 SDK 只需要改 base URL:
| SDK | base_url | 调用的端点 |
|---|---|---|
anthropic | https://api.router.ezat.io | /v1/messages |
openai | https://api.router.ezat.io/openai/v1 | /openai/v1/chat/completions |
两个 SDK 也会从环境变量读取 base URL 和 key,对于不是你自己编写的工具很方便:ANTHROPIC_BASE_URL 和 ANTHROPIC_API_KEY,或 OPENAI_BASE_URL 和 OPENAI_API_KEY。
普通请求
import os
import anthropic
client = anthropic.Anthropic(base_url="https://api.router.ezat.io", api_key=os.environ["EZAT_API_KEY"])
message = client.messages.create(
model="anthropic/claude-sonnet-5-5",
max_tokens=64,
messages=[{"role": "user", "content": "Say hello in five words."}],
)
for block in message.content:
if block.type == "text":
print(block.text)
print(message.usage)
print("request-id:", message._request_id)usage 里是计费所依据的 token 数;见价格。
流式输出
import os
import anthropic
client = anthropic.Anthropic(base_url="https://api.router.ezat.io", api_key=os.environ["EZAT_API_KEY"])
with client.messages.stream(
model="anthropic/claude-sonnet-5-5",
max_tokens=64,
messages=[{"role": "user", "content": "Count from one to five."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
message = stream.get_final_message()
print()
print(message.usage)工具调用往返
模型请求调用工具,你的代码运行工具,再把结果连同目前为止的对话一起发回。
import os
import anthropic
client = anthropic.Anthropic(base_url="https://api.router.ezat.io", api_key=os.environ["EZAT_API_KEY"])
tools = [
{
"name": "get_weather",
"description": "Get the current weather in a city.",
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}
]
messages = [{"role": "user", "content": "What is the weather in Paris? Use the get_weather tool."}]
first = client.messages.create(model="anthropic/claude-sonnet-5-5", max_tokens=64, tools=tools, messages=messages)
call = next((block for block in first.content if block.type == "tool_use"), None)
if call is None:
raise SystemExit(f"No tool call (stop_reason: {first.stop_reason})")
print("Tool call:", call.name, call.input)
# Run the tool, then send its result back.
messages.append({"role": "assistant", "content": first.content})
messages.append(
{
"role": "user",
"content": [
{"type": "tool_result", "tool_use_id": call.id, "content": "Sunny and 18 degrees Celsius"}
],
}
)
second = client.messages.create(model="anthropic/claude-sonnet-5-5", max_tokens=64, tools=tools, messages=messages)
for block in second.content:
if block.type == "text":
print(block.text)图片
图片以 base64 内嵌发送。这里是一张 8×8 的红色 PNG;如果要用你自己的文件,可以用 base64.b64encode(open("photo.png", "rb").read()).decode()。
import os
import anthropic
client = anthropic.Anthropic(base_url="https://api.router.ezat.io", api_key=os.environ["EZAT_API_KEY"])
png = "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAEUlEQVR42mP4z8CAFTEMLQkAKP8/wc53yE8AAAAASUVORK5CYII="
message = client.messages.create(
model="anthropic/claude-sonnet-5-5",
max_tokens=64,
messages=[
{
"role": "user",
"content": [
{"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": png}},
{"type": "text", "text": "What colour is this image? Answer in one word."},
],
}
],
)
for block in message.content:
if block.type == "text":
print(block.text)计算 token 数
仅限 Anthropic SDK。计算 token 数免费,但会计入 key 的每分钟请求数。
import os
import anthropic
client = anthropic.Anthropic(base_url="https://api.router.ezat.io", api_key=os.environ["EZAT_API_KEY"])
count = client.messages.count_tokens(
model="anthropic/claude-sonnet-5-5",
messages=[{"role": "user", "content": "Say hello in five words."}],
)
print(count.input_tokens)列出模型
下面的代码列出你的 key 能调用的模型。带价格的公开模型目录见模型。
import os
import anthropic
client = anthropic.Anthropic(base_url="https://api.router.ezat.io", api_key=os.environ["EZAT_API_KEY"])
for model in client.models.list():
print(model.id, model.display_name)处理错误
每个 SDK 都会抛出自己的异常类。SDK 已经会对连接错误、429 和 5xx 重试两次,所以你捕获到的异常已经用完了重试次数(如需重试更久,请在客户端上调高 max_retries)。这个请求使用了一个不存在的模型,所以会以 404 失败。
import os
import anthropic
client = anthropic.Anthropic(base_url="https://api.router.ezat.io", api_key=os.environ["EZAT_API_KEY"])
try:
client.messages.create(
model="anthropic/no-such-model",
max_tokens=16,
messages=[{"role": "user", "content": "Hi"}],
)
except anthropic.APIConnectionError as error:
raise SystemExit(f"Could not reach the API: {error}")
except anthropic.RateLimitError as error:
print("Rate limited. Retry after", error.response.headers.get("retry-after"), "seconds")
except anthropic.APIStatusError as error:
print("status:", error.status_code, "type:", error.type)
# request-id is set once a request reaches a model; x-request-id is on every response.
print("request-id:", error.request_id)
print("x-request-id:", error.response.headers.get("x-request-id"))| Anthropic SDK | OpenAI SDK | 状态码 |
|---|---|---|
BadRequestError | BadRequestError | 400 |
AuthenticationError | AuthenticationError | 401 |
status_code 为 402 的 APIStatusError | status_code 为 402 的 APIStatusError | 402:需要充值 |
PermissionDeniedError | PermissionDeniedError | 403 |
NotFoundError | NotFoundError | 404 |
RequestTooLargeError | status_code 为 413 的 APIStatusError | 413 |
RateLimitError | RateLimitError | 429 |
InternalServerError | InternalServerError | 500 及以上 |
OverloadedError | InternalServerError | 过载:529(Anthropic 格式)、503(OpenAI 格式) |
APIConnectionError | APIConnectionError | 没有响应:网络问题或超时 |
每个状态码的含义以及是否应该重试,见错误。
给 AI agent 的说明
- 对
anthropic把base_url设为https://api.router.ezat.io,对openai设为https://api.router.ezat.io/openai/v1,并从os.environ["EZAT_API_KEY"]读取 key。 - 使用
GET https://api.router.ezat.io/v1/models返回的模型 id;不要自己编造。 - 使用 Anthropic SDK 时,
max_tokens为必填项。 - 除非需要超过两次重试,否则不要在 SDK 之上为 429 和 5xx 再加一层自己的重试循环;改为调高
max_retries。 - 记录错误中的
request-id(或x-request-id),方便用户提供给 support@ezat.io。