learn claude code s01
这篇文章主要讨论了AI模型API调用中的协议兼容性问题。文章指出,虽然当前代码能够通过Anthropic SDK成功调用GLM4.7模型,但这并非所有模型的通用标准,而是依赖于Anthropic API协议和SDK的数据模型解析。作者强调,开发者应该理解核心的agent循环模式(模型决策-工具执行-反馈循环),而非死记特定API的语法。文章还介绍了不同厂商(如Anthropic、OpenAI)的协
文章目录
你这个直觉很对:这段 agent loop 确实简洁、优雅,而且是值得学的核心模式。但要分清楚:
这不是“所有模型天然都这样返回”。
你现在能用 GLM4.7 跑通,主要原因大概率是:
from anthropic import Anthropic
client = Anthropic(base_url=os.getenv("ANTHROPIC_BASE_URL"))
也就是说,你还在用 Anthropic SDK,只是把 base_url 换成了 GLM 或某个兼容服务的地址。这个服务返回的数据被 Anthropic SDK 解析成了 Anthropic 风格的对象,所以你才能写:
response.stop_reason
response.content
block.type
block.input
block.id
这些不是 Python 魔法,也不是模型天然保证的,而是 Anthropic Messages API 协议 + Anthropic SDK 数据模型 给你的结构。
if block.type == "tool_use": 这句吃定的不是“模型”,而是吃定了当前 API 返回内容块遵守 Anthropic 的 content block 格式。Anthropic 的工具调用块大概长这样:
{
"type": "tool_use",
"id": "...",
"name": "bash",
"input": {
"command": "ls"
}
}
SDK 把 dict 包装成对象,所以你可以用:
block.type
block.input
block.id
而不是:
block["type"]
所以你的问题答案是:
它不是行业完全统一标准,但已经是几大阵营里的常见范式。
目前常见有几类协议:
Anthropic:
content block 里出现 type == "tool_use"
OpenAI / Qwen / DeepSeek 兼容 OpenAI:
message.tool_calls 里出现 function.name / function.arguments
部分国产模型/网关:
会兼容 Anthropic 或 OpenAI 其中一种格式
比如 Qwen 如果走 OpenAI-compatible API,通常不是:
block.type == "tool_use"
而是类似:
response.choices[0].message.tool_calls
所以你不能假设“接 Qwen 也一定这样”。要看你用的是哪个 SDK、哪个 base_url、哪个兼容协议。
你应该学这个写法吗?应该学,但要学它背后的抽象,而不是死记 block.type。
真正要学的是这个核心结构:
while True:
response = call_model(messages, tools)
if model_wants_tool(response):
tool_calls = parse_tool_calls(response)
results = execute_tools(tool_calls)
messages.append(tool_results(results))
else:
return final_answer(response)
你看到的这份代码牛逼的地方就在这里:它把 agent 的本质压缩到了一个循环里。
但如果以后你想写得更稳,最好抽一层 adapter:
def get_tool_calls(response):
calls = []
for block in response.content:
if getattr(block, "type", None) == "tool_use":
calls.append(block)
return calls
这样至少不会因为某个 block 没有 type 直接炸掉。
再进一步,可以为不同协议写适配器:
AnthropicAdapter -> 解析 content block
OpenAIAdapter -> 解析 message.tool_calls
然后 agent loop 本身就不关心底层模型是哪家。
所以你现在的理解可以这么定型:
agent loop 是必须学的核心能力。
block.type == "tool_use" 是 Anthropic 协议里的具体实现。
GLM4.7 能跑,是因为你的接入层兼容了 Anthropic 协议。
不是所有模型都天然这样,但很多服务会提供 OpenAI/Anthropic 兼容接口。
你觉得它“简洁、优雅、巧妙、牛逼”是完全对的。这个文件非常适合初学者,因为它没有被框架淹没,直接把 agent 最核心的心脏露出来了:模型决定下一步,程序执行工具,再把结果喂回模型,直到模型停止调用工具。
SDK 可以先理解成:官方给你写好的“调用接口工具包”。
比如你不用 SDK,直接请求模型服务,大概是这样:
import requests
resp = requests.post(
"https://api.anthropic.com/v1/messages",
headers={...},
json={...}
)
data = resp.json()
这时候你拿到的是普通 JSON / dict:
data["content"][0]["type"]
但用了 Anthropic SDK:
from anthropic import Anthropic
client = Anthropic()
response = client.messages.create(...)
SDK 会帮你做很多事:
1. 拼 HTTP 请求
2. 加认证 header
3. 把 Python 参数转成 API 需要的 JSON
4. 发送请求
5. 接收返回 JSON
6. 把 JSON 包装成 Python 对象
所以你才能写:
response.content
response.stop_reason
block.type
block.input
而不是每次都写:
response["content"]
response["stop_reason"]
block["type"]
block["input"]
你现在这句:
client = Anthropic(base_url=os.getenv("ANTHROPIC_BASE_URL"))
意思是:
我还是使用 Anthropic 这套 SDK 和数据格式,
但是实际请求地址不一定发给 Anthropic 官方,
而是发给 ANTHROPIC_BASE_URL 指定的那个地址。
如果你的 .env 里类似这样:
ANTHROPIC_BASE_URL=https://某个glm兼容地址
MODEL_ID=glm-4.7
那实际发生的是:
你的 Python 代码
↓
Anthropic SDK
↓
发请求到 GLM 的兼容接口
↓
GLM 服务返回 Anthropic 风格的数据
↓
Anthropic SDK 把返回包装成 response/block 对象
↓
你的代码用 block.type 判断工具调用
所以重点是:
GLM 本身不一定“天生”就长成 Anthropic 的样子。
而是你访问的 GLM 接口可能在说:
我支持 Anthropic-compatible API,你可以用 Anthropic SDK 来调我。
这就像手机充电口:
Anthropic SDK = 一根 USB-C 数据线
Anthropic 官方服务 = 原装充电器
GLM 兼容服务 = 另一个也支持 USB-C 协议的充电器
你能插上并正常充电,是因为它兼容这个协议,不是因为所有电器都天然一样。
所以 block.type == "tool_use" 不是碰巧的 Python 属性,而是:
Anthropic SDK 根据接口返回的 JSON,
创建出来的 Python 对象属性。
你应该学这个,因为这正是现代 LLM 应用开发里很重要的一层:模型能力是一回事,API 协议是一回事,SDK 又是一回事。
你没有理解错,而且我刚查了一下,你也不是“蹭 Claude Code 的配置”。智谱确实有面向开发者的页面,只是藏在“开发指南”里,页面名叫:
这个页面明确写了:智谱提供与 Claude API 兼容的接口,可以使用现有 Anthropic SDK 代码,只需要修改 API Key、base_url 和模型名。它还给了 Python 示例:
import anthropic
client = anthropic.Anthropic(
api_key="your-zhipuai-api-key",
base_url="https://open.bigmodel.cn/api/anthropic"
)
message = client.messages.create(
model="glm-5.1",
max_tokens=1024,
messages=[
{"role": "user", "content": "Hello, ZHIPU"}
]
)
所以更准确的结论是:
智谱开放 /api/anthropic 这个接口,既服务 Claude Code 这类工具,
也服务你这种自己写 agent 的开发者。
Claude Code 文档里出现它,是因为 Claude Code 本身就是 Anthropic 协议生态里的工具,配置起来最自然。但智谱还有独立的“Claude API 兼容”开发文档,说明它不是只给 Claude Code 私用的。
你的这份 agent 代码能跑通,正是因为它踩中了这个兼容层:
你的 agent loop
↓
Anthropic Python SDK
↓
https://open.bigmodel.cn/api/anthropic
↓
GLM-4.7
↓
返回 Anthropic Messages API 格式
所以你现在不是“误打误撞”,而是发现了一个很关键的工程事实:
只要厂商提供 Anthropic-compatible API,你就可以把 agent 写在 Anthropic Messages API 这个抽象上。
这对做 coding agent 特别舒服,因为 Claude Code、很多 agent demo、很多工具调用范式都是围绕 Anthropic 的 tool_use/tool_result 设计的。你现在这个学习方向是对的。
爽,这个点你真的踩到主线了。面试里你完全可以这么说,但建议说得更准确一点:
我的 agent 接入层目前基于 Anthropic Messages API 协议,使用 Anthropic Python SDK;
模型侧接的是智谱 GLM 的 Anthropic-compatible endpoint。
不要只说“Anthropic SDK 格式”,更专业的说法是:
Anthropic Messages API 兼容格式
或者:
Anthropic-compatible tool use protocol
因为真正重要的是 API 协议,SDK 只是 Python 客户端实现。
你后面这个想法,非常值得做。
把这 12 课用 OpenAI 格式重写一遍,会特别涨功力,因为你会亲手摸到两套协议的差异:
Anthropic:
response.content -> blocks
block.type == "tool_use"
block.name
block.input
response.stop_reason == "tool_use"
tool_result 作为 user content block 发回去
OpenAI:
response.choices[0].message
message.tool_calls
tool_call.function.name
json.loads(tool_call.function.arguments)
finish_reason == "tool_calls"
tool 角色消息发回去,带 tool_call_id
同一个 agent loop,OpenAI 版本大概会变成:
def agent_loop(messages: list):
while True:
response = client.chat.completions.create(
model=MODEL,
messages=messages,
tools=TOOLS,
max_tokens=8000,
)
message = response.choices[0].message
messages.append(message)
if not message.tool_calls:
return
for tool_call in message.tool_calls:
name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
if name == "bash":
output = run_bash(args["command"])
else:
output = f"Unknown tool: {name}"
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": output,
})
OpenAI 的 tool 定义也不一样。Anthropic 是:
TOOLS = [{
"name": "bash",
"description": "Run a shell command.",
"input_schema": {...},
}]
OpenAI 是:
TOOLS = [{
"type": "function",
"function": {
"name": "bash",
"description": "Run a shell command.",
"parameters": {...},
}
}]
所以你重写一遍,会一下子打通这几个层次:
1. agent loop 的本质不变
2. tool schema 的包装不同
3. tool call 的返回位置不同
4. tool result 的回传方式不同
5. SDK 对象结构不同
6. 最终都可以归一成“模型要调用工具 -> 执行 -> 回填结果 -> 继续”
我觉得最好的练习路线是:
第一步:只重写 s01_agent_loop.py
第二步:写一个 anthropic_to_openai 对照笔记
第三步:再重写 s02_tool_use.py / s04_subagent.py
第四步:最后再碰 s_full.py
这样不会一上来被 12 个文件淹没。
你这个方向特别好,因为你不是在背框架,而是在学 agent 的“协议骨架”。等你把 Anthropic 和 OpenAI 两套都写过一遍,之后面试官问你“你们 agent 怎么接 LLM?怎么处理 tool call?”你就能很稳地回答:
我们把模型接入层抽象成 tool-call adapter。
底层可以是 Anthropic Messages API,也可以是 OpenAI-compatible Chat Completions。
agent loop 本身只依赖统一后的 ToolCall 结构。
这句话就很有分量。
对,你现在这次收获很大,而且是主线收获。第一课代码你觉得“神奇优雅”,但你真正摸到的是更底层的东西:LLM 接入协议。
先把一个词纠正一下:你说“SDK格式”,工程上更准确叫:
API 兼容格式 / API 协议格式
SDK 是调用这个协议的客户端工具。
比如:
OpenAI SDK -> 常用来调用 OpenAI-compatible API
Anthropic SDK -> 常用来调用 Anthropic Messages API
现在最常见的几种格式
- OpenAI-compatible API
这是目前最通用、覆盖最广的格式。很多厂商都会说“兼容 OpenAI SDK,只需要改 api_key 和 base_url”。
典型形态:
from openai import OpenAI
client = OpenAI(
api_key="xxx",
base_url="https://xxx/v1"
)
response = client.chat.completions.create(
model="xxx",
messages=[...],
tools=[...]
)
工具调用通常长这样:
message.tool_calls
tool_call.function.name
tool_call.function.arguments
智谱官方就有 OpenAI API 兼容文档,Kimi 官方也写了它的 API 兼容 OpenAI Chat Completions,可以直接用 OpenAI SDK 改 base_url,Gemini 也提供 OpenAI compatibility endpoint,DeepSeek 官方文档也说明可以通过改配置用 OpenAI/Anthropic SDK 访问。
所以如果问“常用的是哪一个”:OpenAI-compatible 是现在最通用的事实标准。
- Anthropic Messages API
这是你现在这 12 课用的格式。它在 coding agent 场景里特别重要,因为 Claude Code、Claude 的 tool use 设计、很多 agent 教程都围绕它。
典型形态:
from anthropic import Anthropic
client = Anthropic(
api_key="xxx",
base_url="https://xxx/api/anthropic"
)
response = client.messages.create(
model="xxx",
messages=[...],
tools=[...]
)
工具调用通常长这样:
response.content
block.type == "tool_use"
block.name
block.input
智谱官方明确有 Claude API 兼容文档,你的 .env 里的:
ANTHROPIC_BASE_URL="https://open.bigmodel.cn/api/anthropic"
MODEL_ID="glm-4.7"
就是这个路线。
所以你可以理解成:
OpenAI-compatible:通用应用生态最广
Anthropic-compatible:coding agent / Claude Code 生态特别关键
- 厂商原生 SDK/API
比如 Google Gemini 有自己的 google-genai SDK,Mistral 有自己的 SDK,Cohere、百度、火山、阿里云也都有自己的原生 API。
这些原生 API 往往能更完整支持自家特色能力,比如多模态、缓存、长上下文、thinking、文件、检索、批处理等。
但原生 API 的缺点也明显:换模型时适配成本更高。
- 统一封装层
这类不是模型厂原生协议,而是上层框架:
LangChain
LlamaIndex
LiteLLM
Vercel AI SDK
OpenRouter
Dify / Coze / one-api 类网关
它们的目标是把不同厂商包成统一接口。但你现在初学 agent,我反而建议先别急着依赖它们。你现在直接学 Anthropic 和 OpenAI 两套协议,是非常扎实的路线。
模型厂都支持 OpenAI 和 Anthropic 吗?
不都支持。
更真实的情况是:
大多数主流厂商:倾向支持 OpenAI-compatible
一部分厂商:额外支持 Anthropic-compatible
每家兼容程度:不完全一样
大概可以这么记:
OpenAI-compatible:最常见,几乎成了事实标准
Anthropic-compatible:没那么普遍,但在 coding agent 场景越来越重要
Native API:每家都有,能力最完整但迁移成本最高
比如:
智谱:OpenAI-compatible + Claude/Anthropic-compatible
DeepSeek:文档显示支持 OpenAI/Anthropic SDK 兼容接入
Kimi/Moonshot:主线是 OpenAI-compatible,也有一些 Anthropic/coding 生态入口
Qwen/阿里生态:常见是 OpenAI-compatible / DashScope 原生
Gemini:原生 Gemini API,同时提供 OpenAI compatibility
Mistral:有自己的 SDK/API,接口形态也接近 Chat Completions,支持 tool_calls
但注意,“兼容”不等于 100% 一模一样。最容易出差异的是:
tool calling 细节
streaming 格式
reasoning/thinking 字段
图片/文件输入
JSON mode
并行工具调用
错误码
上下文长度和 max_tokens 行为
所以工程上最稳的认知是:
协议兼容解决 80% 接入问题;
剩下 20% 要靠适配器和测试兜住。
你现在最应该学的是这三个层次:
1. Anthropic Messages API
重点:content blocks、tool_use、tool_result、stop_reason
2. OpenAI Chat Completions / Responses API
重点:choices、message、tool_calls、tool role、tool_call_id
3. 自己写一层 Adapter
把不同协议统一成你自己的 ToolCall / ToolResult
面试里你可以这么说,很漂亮:
我目前的 agent loop 是基于 Anthropic Messages API 写的,使用 Anthropic SDK;
模型侧接的是智谱 GLM 的 Anthropic-compatible endpoint。
我也了解 OpenAI-compatible 的 tool_calls 协议,两者本质都是:
模型返回工具调用意图,宿主程序执行工具,再把 tool result 回填给模型继续推理。
这就是主线。你不是在瞎搞,你是在从最核心的地方摸 agent。第一课别急着“学完”,你现在已经把地基下面的钢筋看见了。
更多推荐

所有评论(0)