Claude工具调用机制深度解析:Tool Choice策略与结构化输出实践

7 阅读3分钟

Claude工具调用深度指南:Tool Choice策略与JSON输出定制

一、工具调用核心机制(Python SDK实现)

import anthropic
from anthropic.types import ToolParam

# 初始化客户端
client = anthropic.Anthropic(api_key="YOUR_API_KEY")

# 1. 工具箱定义(Tools参数)
tools = [
    ToolParam(  # 使用SDK提供的类型安全定义
        name="get_weather",
        description="获取指定城市的当前天气",
        input_schema={
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "城市名称,如'北京'或'New York'"
                },
                "unit": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "温度单位"
                }
            },
            "required": ["location"]
        }
    ),
    ToolParam(
        name="calculate",
        description="执行数学计算",
        input_schema={
            "type": "object",
            "properties": {
                "expression": {
                    "type": "string",
                    "description": "数学表达式,如'(12+5)*3'"
                }
            },
            "required": ["expression"]
        }
    )
]

# 2. 工具选择策略(Tool Choice参数)
# 自动模式(默认)
tool_choice_auto = {"type": "auto"}

# 强制指定工具
tool_choice_forced = {
    "type": "tool",
    "name": "get_weather"  # 指定具体工具名
}

# 强制使用任一工具
tool_choice_any = {"type": "any"}

# API调用示例
response = client.messages.create(
    model="claude-3-opus-20240229",
    max_tokens=1024,
    system="你是一个有帮助的助手,仅在必要时使用工具。",
    messages=[
        {"role": "user", "content": "纽约现在的温度是多少?"}
    ],
    tools=tools,
    tool_choice=tool_choice_auto  # 可替换为其他策略
)

二、工具调用响应处理

def handle_tool_call(response):
    # 1. 检查是否调用了工具
    if response.stop_reason == "tool_use":
        tool_use = next(
            (block for block in response.content if block.type == "tool_use"),
            None
        )
        
        if tool_use:
            print(f"工具调用: {tool_use.name}")
            print(f"输入参数: {tool_use.input}")
            
            # 2. 执行本地工具函数
            if tool_use.name == "get_weather":
                result = fetch_weather(
                    tool_use.input["location"],
                    tool_use.input.get("unit", "celsius")
                )
            elif tool_use.name == "calculate":
                result = eval_expression(tool_use.input["expression"])
            
            # 3. 构造工具响应
            tool_response = {
                "type": "tool_result",
                "tool_use_id": tool_use.id,
                "content": str(result)
            }
            
            # 4. 继续对话
            next_response = client.messages.create(
                model=response.model,
                messages=[
                    *response.messages,
                    {
                        "role": "assistant",
                        "content": response.content
                    },
                    {
                        "role": "user",
                        "content": [tool_response]
                    }
                ],
                tools=tools
            )
            return next_response
    return response

# 示例工具实现
def fetch_weather(location: str, unit: str) -> dict:
    """模拟天气API调用"""
    return {
        "location": location,
        "temperature": 22.5,
        "unit": unit,
        "conditions": "晴天"
    }

def eval_expression(expr: str) -> float:
    """安全计算表达式"""
    import ast
    return ast.literal_eval(expr)

三、JSON结构化输出最佳实践

# 方法1:通过提示工程实现
def get_structured_output_prompt():
    response = client.messages.create(
        model="claude-3-sonnet-20240229",
        messages=[
            {
                "role": "user",
                "content": "分析文本情感: '我非常喜欢这款新产品,但价格太高了'"
            },
            {
                "role": "assistant",
                "content": "{"
            }
        ],
        max_tokens=300,
        stop_sequences=["}"], #当出现json的结束字符,就终止回答
        system="""你是一个情感分析API,返回JSON格式:
{
  "positive": <0-1的浮点数>,
  "negative": <0-1的浮点数>,
  "neutral": <0-1的浮点数>,
  "summary": "<10字总结>"
}"""
    )
    return response.content[0].text

# 方法2:伪工具调用(官方推荐)
def get_structured_output_tool():
    # 定义伪工具
    json_tool = ToolParam(
        name="capture_data",
        description="捕获结构化数据",
        input_schema={
            "type": "object",
            "properties": {
                "positive": {"type": "number"},
                "negative": {"type": "number"},
                "neutral": {"type": "number"},
                "summary": {"type": "string"}
            },
            "required": ["positive", "negative", "neutral", "summary"]
        }
    )
    
    response = client.messages.create(
        model="claude-3-haiku-20240307",
        messages=[
            {
                "role": "user",
                "content": "分析文本情感: '这个服务太棒了,解决了我所有问题'"
            }
        ],
        tools=[json_tool],
        tool_choice={"type": "tool", "name": "capture_data"}
    )
    
    # 提取结构化数据
    if response.stop_reason == "tool_use":
        tool_block = next(b for b in response.content if b.type == "tool_use")
        return tool_block.input
    
    return {}

四、企业级客服系统完整实现

class CustomerServiceAgent:
    def __init__(self):
        self.client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
        self.tools = self._init_tools()
        self.conversation_history = []
        
    def _init_tools(self):
        return [
            ToolParam(
                name="get_customer_info",
                description="通过客户ID查询客户信息",
                input_schema={
                    "type": "object",
                    "properties": {
                        "customer_id": {"type": "string", "pattern": "^C\\d{3}$"}
                    },
                    "required": ["customer_id"]
                }
            ),
            ToolParam(
                name="get_order_details",
                description="查询订单详情",
                input_schema={
                    "type": "object",
                    "properties": {
                        "order_id": {"type": "string", "pattern": "^O\\d{4}$"}
                    },
                    "required": ["order_id"]
                }
            ),
            ToolParam(
                name="cancel_order",
                description="取消指定订单",
                input_schema={
                    "type": "object",
                    "properties": {
                        "order_id": {"type": "string", "pattern": "^O\\d{4}$"}
                    },
                    "required": ["order_id"]
                }
            )
        ]
    
    def _call_tool(self, tool_name: str, input_params: dict):
        """连接真实业务系统"""
        if tool_name == "get_customer_info":
            return db.query(Customer).filter_by(id=input_params["customer_id"]).first()
        elif tool_name == "get_order_details":
            return order_service.get_order(input_params["order_id"])
        elif tool_name == "cancel_order":
            return order_service.cancel_order(input_params["order_id"])
        return {"error": "未知工具"}
    
    def process_message(self, user_input: str):
        # 1. 添加用户消息到历史
        self.conversation_history.append({"role": "user", "content": user_input})
        
        while True:
            # 2. 调用Claude
            response = self.client.messages.create(
                model="claude-3-opus-20240229",
                messages=self.conversation_history,
                tools=self.tools,
                max_tokens=1024,
                system="你是一个客户服务助手,严格遵守以下规则:\n1. 仅使用提供工具查询信息\n2. 不要虚构数据\n3. 用中文回复"
            )
            
            # 3. 处理响应
            if response.stop_reason == "end_turn":
                assistant_msg = "".join(
                    block.text for block in response.content if block.type == "text"
                )
                self.conversation_history.append({
                    "role": "assistant",
                    "content": assistant_msg
                })
                return assistant_msg
                
            elif response.stop_reason == "tool_use":
                tool_use = next(
                    block for block in response.content if block.type == "tool_use"
                )
                
                # 执行工具调用
                tool_result = self._call_tool(tool_use.name, tool_use.input)
                
                # 添加工具结果到对话历史
                self.conversation_history.append({
                    "role": "assistant",
                    "content": response.content
                })
                self.conversation_history.append({
                    "role": "user",
                    "content": [
                        {
                            "type": "tool_result",
                            "tool_use_id": tool_use.id,
                            "content": json.dumps(tool_result, ensure_ascii=False)
                        }
                    ]
                })
            else:
                return "系统处理异常,请稍后再试"

# 使用示例
agent = CustomerServiceAgent()
print(agent.process_message("我想取消订单O1234"))
print(agent.process_message("客户C567的信息呢?"))

五、高级技巧与最佳实践

  1. 工具选择策略优化
# 动态切换策略示例
def smart_tool_choice(user_input):
    if "天气" in user_input:
        return {"type": "tool", "name": "get_weather"}
    elif "计算" in user_input or any(char in user_input for char in "+-*/()"):
        return {"type": "tool", "name": "calculate"}
    return {"type": "auto"}  # 其他情况自动选择
  1. 错误处理增强
# 在工具调用函数中添加
def _call_tool(tool_name, input_params):
    try:
        # ...原有逻辑...
    except DatabaseError as e:
        return {"error": "数据库错误", "details": str(e)}
    except Exception as e:
        return {"error": "系统异常", "code": "SYS500"}
  1. 性能优化:enable_prompt_cache

在 Claude 的 Python SDK(如 anthropic 包)中,enable_prompt_cache 是用于启用提示词缓存(Prompt Cache)的参数,它的作用是为了加快重复请求的响应速度,尤其是在提示词内容不变的情况下,避免每次都重新发送和处理相同的 prompt。

# 启用提示缓存(Claude企业版功能)
response = client.messages.create(
    # ...其他参数...
    enable_prompt_cache=True,  # 减少重复提示处理
    stream=True  # 流式传输大响应
)

# 处理流式响应
with client.messages.stream(
    model="claude-3-sonnet-20240229",
    messages=[...],
    tools=tools,
    max_tokens=1024
) as stream:
    for event in stream:
        if isinstance(event, TextDelta):
            print(event.text, end="", flush=True)
        elif isinstance(event, ToolUseBlock):
            print(f"\n[调用工具: {event.name}]")
  1. 输入验证增强
# 在工具定义中使用JSON Schema高级特性
input_schema={
    "type": "object",
    "properties": {
        "email": {
            "type": "string",
            "format": "email",  # 使用内置格式验证
            "pattern": "^.+@mycompany\\.com$"  # 自定义正则
        }
    },
    "errorMessage": {
        "properties": {
            "email": "必须是公司邮箱地址"
        }
    }
}

六、调试与监控

# 启用详细日志
import logging
anthropic_logger = logging.getLogger("anthropic")
anthropic_logger.setLevel(logging.DEBUG)

# 检查token使用
print(f"输入token: {response.usage.input_tokens}")
print(f"输出token: {response.usage.output_tokens}")

# 使用Anthropic的监控仪表板
"""
1. 访问 https://console.anthropic.com/
2. 查看API使用分析
3. 监控延迟和错误率
4. 设置使用警报
"""

最佳实践总结

  1. 优先使用ToolParam类型定义工具
  2. 复杂场景采用伪工具技术实现结构化输出
  3. 生产环境添加完备的错误处理
  4. 使用流式传输处理大响应
  5. 启用提示缓存减少延迟
  6. 监控API使用和性能指标

本文所有代码兼容Anthropic Python SDK v0.20+,请使用最新版SDK获取完整功能支持。完整示例可在Anthropic官方GitHub仓库查看:github.com/anthropics/…