Docs · App Dev Guide

大模型应用开发

从 Prompt 工程到 AI Agent,一站式覆盖大模型应用开发全链路 —— 让你能在 1 周内跑出生产级 MVP。

Function Call · Anthropic 实战

Anthropic Claude 的 Tool Use 与 OpenAI 协议相似但有几处关键差异 —— 理解差异能少踩 80% 的坑

与 OpenAI 的差异速览

维度OpenAIAnthropic
工具字段名tools[].functiontools[] 直接(无嵌套)
参数 schemaparametersinput_schema
调用 blocktool_calls[]content[].type == "tool_use"
结果 blockrole: toolrole: user + content[type:tool_result]
严格模式strict: true默认就比较严格
并行调用默认开启disable_parallel_tool_use: false 启用

完整 5 步代码

Step 1:定义工具(注意是 input_schema)

from anthropic import Anthropic
      import json
      
      client = Anthropic()  # 自动读 ANTHROPIC_API_KEY
      
      tools = [{
          "name": "get_weather",  # 注意:没有嵌套在 function 里
          "description": "查询指定城市的当前天气情况",
          "input_schema": {  # 注意:叫 input_schema,不是 parameters
              "type": "object",
              "properties": {
                  "city": {
                      "type": "string",
                      "description": "城市名称"
                  },
                  "unit": {
                      "type": "string",
                      "enum": ["celsius", "fahrenheit"],
                      "default": "celsius"
                  }
              },
              "required": ["city"]
          }
      }]

Step 2:业务函数(同 OpenAI)

def get_weather(city: str, unit: str = "celsius") -> dict:
          fake_data = {"北京": {"temp": 32, "condition": "晴"}}
          return fake_data.get(city, {"temp": 20, "condition": "未知"})

Step 3:第一轮 —— 模型决策

messages = [{"role": "user", "content": "北京今天热吗?"}]
      
      resp = client.messages.create(
          model="claude-sonnet-4-5",
          max_tokens=1024,
          tools=tools,
          messages=messages
      )
      
      # resp.stop_reason 可能值:
      # "end_turn"   → 正常结束
      # "tool_use"   → 要调用工具
      # "max_tokens" → 截断
      # "stop_sequence" → 遇到停止符
      
      assert resp.stop_reason == "tool_use"
      
      # 找到 tool_use block(content 可能混合文本和工具调用)
      tool_uses = [b for b in resp.content if b.type == "tool_use"]
      tu = tool_uses[0]
      print(tu.name)   # "get_weather"
      print(tu.input)  # {"city": "北京"}(已经是 dict,不用 json.loads)
      print(tu.id)     # "toolu_xxx" 唯一 ID

Step 4:执行工具

result = get_weather(**tu.input)
      # {"temp": 32, "condition": "晴"}

Step 5:第二轮 —— 返回结果

messages.extend([
          {"role": "assistant", "content": resp.content},  # 模型的 content(含 tool_use)
          {
              "role": "user",  # 注意:tool_result 用 user 角色
              "content": [{
                  "type": "tool_result",
                  "tool_use_id": tu.id,
                  "content": json.dumps(result, ensure_ascii=False)
              }]
          }
      ])
      
      resp2 = client.messages.create(
          model="claude-sonnet-4-5",
          max_tokens=1024,
          tools=tools,
          messages=messages
      )
      print(resp2.content[0].text)
      # "北京今天 32°C 晴天,挺热的,注意防晒。"

通用调度循环(Anthropic 版)

def chat_with_tools_anthropic(user_message: str, tools: list, 
                                    tool_handlers: dict, max_iterations: int = 5):
          messages = [{"role": "user", "content": user_message}]
      
          for _ in range(max_iterations):
              resp = client.messages.create(
                  model="claude-sonnet-4-5",
                  max_tokens=2048,
                  tools=tools,
                  messages=messages
              )
              messages.append({"role": "assistant", "content": resp.content})
      
              # 没有工具调用 → 结束
              if resp.stop_reason != "tool_use":
                  # 提取最终文本
                  text_blocks = [b.text for b in resp.content if b.type == "text"]
                  return "\n".join(text_blocks)
      
              # 执行所有工具调用
              tool_results = []
              for block in resp.content:
                  if block.type == "tool_use":
                      fn = tool_handlers[block.name]
                      try:
                          result = fn(**block.input)
                          tool_results.append({
                              "type": "tool_result",
                              "tool_use_id": block.id,
                              "content": json.dumps(result, ensure_ascii=False)
                          })
                      except Exception as e:
                          tool_results.append({
                              "type": "tool_result",
                              "tool_use_id": block.id,
                              "content": f"Error: {str(e)}",
                              "is_error": True  # 告诉模型这次调用失败
                          })
      
              messages.append({"role": "user", "content": tool_results})
      
          raise RuntimeError(f"超过 {max_iterations} 轮仍未结束")

tool_choice 选项

Anthropic 也支持强制工具调用:

resp = client.messages.create(
          model="claude-sonnet-4-5",
          tools=tools,
          tool_choice={"type": "auto"},      # 默认
          # tool_choice={"type": "any"},      # 必须调一个工具(任意)
          # tool_choice={"type": "tool", "name": "get_weather"},  # 指定工具
          messages=messages
      )

并行工具调用控制

Claude 4.5 默认会并行调用工具。可以禁用:

resp = client.messages.create(
          model="claude-sonnet-4-5",
          tools=tools,
          tool_choice={
              "type": "auto",
              "disable_parallel_tool_use": True  # 强制串行
          },
          messages=messages
      )

何时需要禁用:

  • 工具之间有依赖关系(必须先查 A 再查 B)
  • 后端 API 有并发限制
  • 调试时想要确定性顺序

与 Computer Use(电脑操作能力)结合

Claude 4.5 独有的能力:直接操作电脑屏幕 —— 截图、点击、打字。

# Claude 可以"看见"屏幕并控制电脑
      resp = client.messages.create(
          model="claude-sonnet-4-5",
          max_tokens=1024,
          tools=[{
              "type": "computer_20250124",  # 内置 computer use 工具
              "name": "computer",
              "display_width_px": 1920,
              "display_height_px": 1080
          }],
          messages=[{"role": "user", "content": "帮我在淘宝搜索 iPhone 16 Pro"}]
      )
      # 模型会返回 tool_use: {action: "screenshot"} 或 {action: "left_click", coordinate: [x,y]}

这是 Anthropic 的独特优势 —— OpenAI 还在追赶。

Extended Thinking + Tool Use

Claude 4.5 的推理模式可以和工具调用结合 —— 模型先深度思考再决定调哪个工具。

resp = client.messages.create(
          model="claude-sonnet-4-5",
          max_tokens=4096,
          thinking={"type": "enabled", "budget_tokens": 5000},
          tools=tools,
          messages=[{"role": "user", "content": "..."}]
      )
      # resp.content 可能包含:
      # - thinking block(推理过程)
      # - tool_use block(调用决策)
      # - text block(说明)

适合复杂决策场景:在多个工具之间精挑细选、参数推算需要计算等。

Prompt Caching + Tools

工具定义是个长 JSON,每次都传浪费 token。开启 cache:

resp = client.messages.create(
          model="claude-sonnet-4-5",
          tools=tools,
          system=[{
              "type": "text",
              "text": SYSTEM_PROMPT,
              "cache_control": {"type": "ephemeral"}  # 缓存系统 prompt
          }],
          messages=messages,
          extra_headers={"anthropic-beta": "tools-2024-04-04"}
      )
      # 工具定义 + 系统 prompt 都被缓存,第 2 次起便宜 90%

Anthropic vs OpenAI 总结

Anthropic 更强的地方

  • 推理质量更高(Extended Thinking)
  • Computer Use 能直接操作电脑
  • Prompt Caching 折扣更大
  • 工具调用的 reasoning 更稳定

OpenAI 更强的地方

  • strict: true 100% schema 保证
  • 国内模型几乎都兼容它的 API(迁移成本低)
  • 生态更成熟(LangChain 默认就是 OpenAI 风格)

实战建议:客服 / 高频调用用 DeepSeek(OpenAI 兼容),复杂决策 / Agent 用 Claude。

下一节:并行调用与错误处理 —— 生产环境的硬核话题。