第 19 章

Tool Search Tool:BM25 + 正则双引擎支持万级工具库的动态加载

第十九章:工具设计模式:参数 Schema、描述优化与类型约束

19.1 工具设计的重要性

工具定义的质量直接决定了 Claude 使用工具的准确性。一个描述模糊的工具会导致 Claude 在不合适的场景调用它,或者传入错误格式的参数。反之,精心设计的工具 Schema 能让 Claude 在复杂场景下做出正确判断。

工具设计有三个核心目标:

  1. 语义清晰:Claude 必须能从名称和描述中准确理解工具的用途和限制
  2. 参数约束完整:通过 JSON Schema 约束,最大化减少 Claude 传入无效参数的概率
  3. 错误容忍性:即使 Claude 传入略有偏差的参数,工具也能优雅处理

19.2 工具命名的最佳实践

命名规范

工具名称应遵循以下规则:

# 好的命名:动词_名词,语义清晰
good_names = [
    "search_products",       # 搜索产品
    "create_invoice",        # 创建发票
    "get_user_profile",      # 获取用户资料
    "update_order_status",   # 更新订单状态
    "send_notification",     # 发送通知
    "calculate_shipping",    # 计算运费
]

# 不好的命名:模糊或过于宽泛
bad_names = [
    "process",       # 处理什么?
    "data",          # 数据操作?
    "helper",        # 帮什么忙?
    "do_thing",      # 做什么事?
    "api_call",      # 调哪个 API?
]

避免名称冲突

当多个工具功能相近时,名称需要体现关键差异:

# 清晰区分搜索范围
tools_with_clear_scope = [
    {
        "name": "search_internal_knowledge_base",
        "description": "在公司内部知识库中搜索文档和 FAQ。只包含已批准的内部文档。"
    },
    {
        "name": "search_web",
        "description": "通过搜索引擎搜索互联网上的公开信息。结果可能包含未经验证的内容。"
    },
    {
        "name": "search_product_catalog",
        "description": "在产品目录中搜索商品信息,包括价格、库存和规格。"
    }
]

19.3 description 字段的优化策略

description 是工具设计中最容易被忽视却最重要的部分。它不只是人类可读的文档——它是 Claude 决定是否调用该工具的主要依据。

描述的五要素

一个好的工具描述应包含:

  1. 核心功能:工具做什么
  2. 使用场景:什么时候应该使用
  3. 输出格式:返回什么样的数据
  4. 限制说明:什么情况下不应使用
  5. 与相似工具的区别:如果有多个相关工具
# 示例:数据库搜索工具的优化描述
tool_with_optimized_description = {
    "name": "search_customer_database",
    "description": """在 CRM 系统中搜索客户记录。

【适用场景】
- 需要查找特定客户的联系方式、购买历史或账户状态
- 需要按条件筛选客户群体(如地区、消费金额、注册时间)

【返回数据】
返回匹配的客户记录列表,每条记录包含:customer_id、name、email、
phone、registration_date、total_purchases、status

【重要限制】
- 最多返回 100 条记录,超出时请使用 limit 参数控制
- 不包含已删除账户,如需查询请使用 search_deleted_customers 工具
- 搜索结果实时查询,不经过缓存

【与 search_orders 的区别】
此工具搜索客户信息,search_orders 工具搜索订单信息。如需同时查询,
先用此工具获取 customer_id,再传给 search_orders。""",
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {"type": "string"},
            "limit": {"type": "integer", "default": 20}
        },
        "required": ["query"]
    }
}

使用例子增强描述

在描述中加入具体例子,帮助 Claude 更准确理解参数格式:

{
    "name": "parse_date_range",
    "description": """解析用户描述的日期范围,返回标准格式的起止日期。

【参数 date_expression 示例】
- "最近一周" → {"start": "2026-04-21", "end": "2026-04-28"}
- "上个月" → {"start": "2026-03-01", "end": "2026-03-31"}
- "Q1 2026" → {"start": "2026-01-01", "end": "2026-03-31"}
- "2026年春节" → {"start": "2026-01-29", "end": "2026-02-04"}""",
    "input_schema": {
        "type": "object",
        "properties": {
            "date_expression": {
                "type": "string",
                "description": "用户输入的日期描述,支持自然语言"
            },
            "reference_date": {
                "type": "string",
                "description": "参考日期(ISO 8601 格式),用于解析相对日期如'昨天'。默认为今天。"
            }
        },
        "required": ["date_expression"]
    }
}

19.4 参数 Schema 的高级约束模式

基础类型与格式约束

advanced_schema_examples = {
    "type": "object",
    "properties": {
        # 字符串格式约束
        "email": {
            "type": "string",
            "format": "email",
            "description": "用户邮箱地址"
        },
        "date": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
            "description": "日期,格式为 YYYY-MM-DD"
        },
        "phone": {
            "type": "string",
            "pattern": "^\\+?[1-9]\\d{6,14}$",
            "description": "手机号码(国际格式)"
        },
        
        # 数值范围约束
        "page_size": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 20,
            "description": "分页大小"
        },
        "discount_rate": {
            "type": "number",
            "minimum": 0.0,
            "maximum": 1.0,
            "description": "折扣率,0.1 表示九折"
        },
        
        # 枚举约束
        "order_status": {
            "type": "string",
            "enum": ["pending", "processing", "shipped", "delivered", "cancelled"],
            "description": "订单状态"
        },
        
        # 数组约束
        "tags": {
            "type": "array",
            "items": {"type": "string"},
            "minItems": 1,
            "maxItems": 10,
            "uniqueItems": True,
            "description": "标签列表,1-10个不重复标签"
        },
        
        # 字符串长度约束
        "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "标题,不超过200个字符"
        }
    }
}

嵌套对象 Schema

nested_schema = {
    "name": "create_shipping_order",
    "description": "创建物流订单",
    "input_schema": {
        "type": "object",
        "properties": {
            "sender": {
                "type": "object",
                "description": "发件人信息",
                "properties": {
                    "name": {"type": "string", "description": "姓名"},
                    "phone": {"type": "string", "description": "手机号"},
                    "address": {
                        "type": "object",
                        "properties": {
                            "province": {"type": "string"},
                            "city": {"type": "string"},
                            "district": {"type": "string"},
                            "street": {"type": "string"},
                            "postal_code": {"type": "string", "pattern": "^\\d{6}$"}
                        },
                        "required": ["province", "city", "street"]
                    }
                },
                "required": ["name", "phone", "address"]
            },
            "recipient": {
                "type": "object",
                "description": "收件人信息(结构与发件人相同)",
                "properties": {
                    "name": {"type": "string"},
                    "phone": {"type": "string"},
                    "address": {
                        "type": "object",
                        "properties": {
                            "province": {"type": "string"},
                            "city": {"type": "string"},
                            "district": {"type": "string"},
                            "street": {"type": "string"},
                            "postal_code": {"type": "string"}
                        },
                        "required": ["province", "city", "street"]
                    }
                },
                "required": ["name", "phone", "address"]
            },
            "package": {
                "type": "object",
                "properties": {
                    "weight_kg": {"type": "number", "minimum": 0.1, "maximum": 50},
                    "dimensions": {
                        "type": "object",
                        "properties": {
                            "length_cm": {"type": "number"},
                            "width_cm": {"type": "number"},
                            "height_cm": {"type": "number"}
                        }
                    },
                    "declared_value": {"type": "number", "minimum": 0}
                },
                "required": ["weight_kg"]
            }
        },
        "required": ["sender", "recipient", "package"]
    }
}

oneOf / anyOf 条件 Schema

对于参数格式依赖其他参数的复杂场景:

conditional_schema = {
    "name": "send_message",
    "description": "通过不同渠道发送消息",
    "input_schema": {
        "type": "object",
        "properties": {
            "channel": {
                "type": "string",
                "enum": ["email", "sms", "wechat"],
                "description": "发送渠道"
            },
            "recipient": {
                "type": "string",
                "description": "收件人:邮件填邮箱,短信填手机号,微信填 OpenID"
            },
            "content": {
                "type": "string",
                "description": "消息内容。短信不超过70字,邮件支持 HTML"
            },
            "subject": {
                "type": "string",
                "description": "邮件主题(仅 channel=email 时需要)"
            }
        },
        "required": ["channel", "recipient", "content"],
        # 条件约束:email 渠道时 subject 必填
        "if": {
            "properties": {"channel": {"const": "email"}}
        },
        "then": {
            "required": ["subject"]
        }
    }
}

19.5 参数描述的优化技巧

描述参数时的常见错误

# 错误示例:描述过于简单
bad_parameter = {
    "query": {
        "type": "string",
        "description": "查询字符串"  # 太简单,Claude 不知道格式要求
    }
}

# 正确示例:描述具体且完整
good_parameter = {
    "query": {
        "type": "string",
        "description": """搜索关键词。支持以下语法:
- 普通关键词:直接输入,如 "iPhone 14"
- 精确匹配:用引号包围,如 '"苹果手机"'
- 排除词:用减号前缀,如 "手机 -二手"
- 字段搜索:用冒号分隔,如 "品牌:苹果"
最大长度 500 字符。""",
        "maxLength": 500
    }
}

通过描述引导参数选择

# 引导 Claude 在不确定时使用默认值
smart_defaults_tool = {
    "name": "export_report",
    "description": "导出数据报告",
    "input_schema": {
        "type": "object",
        "properties": {
            "format": {
                "type": "string",
                "enum": ["pdf", "excel", "csv", "json"],
                "description": "导出格式。用户未指定时默认选择 pdf(适合查看);如果用户说要"处理数据"或"导入系统",选择 csv 或 excel。",
                "default": "pdf"
            },
            "date_range": {
                "type": "string",
                "enum": ["today", "week", "month", "quarter", "year", "custom"],
                "description": "时间范围。用户未指定时选择 month(最近30天)。",
                "default": "month"
            }
        },
        "required": ["format"]
    }
}

19.6 工具组的设计模式

模式一:CRUD 工具组

对同一资源的增删改查应保持一致的命名和参数风格:

user_crud_tools = [
    {
        "name": "get_user",
        "description": "通过 ID 获取单个用户的完整信息",
        "input_schema": {
            "type": "object",
            "properties": {
                "user_id": {"type": "string", "description": "用户 ID"}
            },
            "required": ["user_id"]
        }
    },
    {
        "name": "list_users",
        "description": "列出用户,支持分页和过滤",
        "input_schema": {
            "type": "object",
            "properties": {
                "page": {"type": "integer", "minimum": 1, "default": 1},
                "page_size": {"type": "integer", "minimum": 1, "maximum": 100, "default": 20},
                "status": {"type": "string", "enum": ["active", "inactive", "all"], "default": "active"},
                "search": {"type": "string", "description": "按姓名或邮箱搜索"}
            }
        }
    },
    {
        "name": "create_user",
        "description": "创建新用户账户",
        "input_schema": {
            "type": "object",
            "properties": {
                "name": {"type": "string", "minLength": 1, "maxLength": 100},
                "email": {"type": "string", "format": "email"},
                "role": {"type": "string", "enum": ["admin", "editor", "viewer"], "default": "viewer"},
                "send_welcome_email": {"type": "boolean", "default": True}
            },
            "required": ["name", "email"]
        }
    },
    {
        "name": "update_user",
        "description": "更新用户信息。只传入需要修改的字段。",
        "input_schema": {
            "type": "object",
            "properties": {
                "user_id": {"type": "string", "description": "要更新的用户 ID"},
                "name": {"type": "string", "minLength": 1, "maxLength": 100},
                "email": {"type": "string", "format": "email"},
                "role": {"type": "string", "enum": ["admin", "editor", "viewer"]},
                "status": {"type": "string", "enum": ["active", "inactive"]}
            },
            "required": ["user_id"]
        }
    },
    {
        "name": "delete_user",
        "description": "删除用户账户。此操作不可逆,请谨慎使用。删除前会自动转移该用户的所有资源。",
        "input_schema": {
            "type": "object",
            "properties": {
                "user_id": {"type": "string", "description": "要删除的用户 ID"},
                "transfer_to_user_id": {
                    "type": "string",
                    "description": "将被删除用户的资源转移给此用户 ID(可选,不填则转移给系统管理员)"
                }
            },
            "required": ["user_id"]
        }
    }
]

模式二:管道工具组

工具之间存在明确的依赖顺序:

pipeline_tools = [
    {
        "name": "upload_file",
        "description": "上传文件,返回文件 ID。这是处理文件的第一步。",
        "input_schema": {
            "type": "object",
            "properties": {
                "file_path": {"type": "string", "description": "本地文件路径"},
                "file_type": {"type": "string", "enum": ["csv", "excel", "json", "xml"]}
            },
            "required": ["file_path", "file_type"]
        }
    },
    {
        "name": "validate_file",
        "description": "验证已上传文件的格式和数据完整性。需要先用 upload_file 获取 file_id。",
        "input_schema": {
            "type": "object",
            "properties": {
                "file_id": {"type": "string", "description": "由 upload_file 返回的文件 ID"},
                "schema_id": {"type": "string", "description": "用于验证的 Schema ID(可选)"}
            },
            "required": ["file_id"]
        }
    },
    {
        "name": "import_file",
        "description": "将验证通过的文件导入数据库。必须先通过 validate_file 验证。",
        "input_schema": {
            "type": "object",
            "properties": {
                "file_id": {"type": "string", "description": "由 upload_file 返回的文件 ID"},
                "target_table": {"type": "string", "description": "目标数据表名"},
                "conflict_strategy": {
                    "type": "string",
                    "enum": ["skip", "overwrite", "append"],
                    "default": "skip",
                    "description": "主键冲突时的处理策略"
                }
            },
            "required": ["file_id", "target_table"]
        }
    }
]

19.7 用工具实现结构化输出

Tool Use 的一个重要用途是强制 Claude 输出结构化数据,而不是自由格式文本:

# 使用 tool_choice={"type": "tool"} 强制结构化输出
extraction_tool = {
    "name": "extract_invoice_data",
    "description": "从发票文本中提取结构化数据",
    "input_schema": {
        "type": "object",
        "properties": {
            "invoice_number": {"type": "string"},
            "invoice_date": {"type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$"},
            "vendor_name": {"type": "string"},
            "vendor_tax_id": {"type": "string"},
            "total_amount": {"type": "number"},
            "tax_amount": {"type": "number"},
            "line_items": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "description": {"type": "string"},
                        "quantity": {"type": "number"},
                        "unit_price": {"type": "number"},
                        "total": {"type": "number"}
                    },
                    "required": ["description", "quantity", "unit_price", "total"]
                }
            }
        },
        "required": ["invoice_number", "invoice_date", "vendor_name", "total_amount"]
    }
}

def extract_invoice(invoice_text: str) -> dict:
    """从发票文本提取结构化数据"""
    import anthropic
    client = anthropic.Anthropic()
    
    response = client.messages.create(
        model="claude-opus-4-5",
        max_tokens=1024,
        tools=[extraction_tool],
        tool_choice={"type": "tool", "name": "extract_invoice_data"},
        messages=[{
            "role": "user",
            "content": f"请从以下发票文本中提取数据:\n\n{invoice_text}"
        }]
    )
    
    for block in response.content:
        if block.type == "tool_use":
            return block.input
    
    raise ValueError("未能提取发票数据")

19.8 工具版本管理与向后兼容

随着系统演进,工具的 Schema 需要更新。以下是保持向后兼容的策略:

# 版本 1:只有 query 参数
tool_v1 = {
    "name": "search_products",
    "description": "搜索产品(v1)",
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {"type": "string"}
        },
        "required": ["query"]
    }
}

# 版本 2:添加新参数,保持 query 必填,新参数均有默认值
tool_v2 = {
    "name": "search_products",
    "description": "搜索产品。支持按类别过滤和价格范围筛选(v2)",
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {"type": "string"},
            "category": {
                "type": "string",
                "description": "产品类别(可选,不填则搜索全部类别)"
            },
            "min_price": {
                "type": "number",
                "description": "最低价格过滤(可选)"
            },
            "max_price": {
                "type": "number",
                "description": "最高价格过滤(可选)"
            }
        },
        "required": ["query"]  # 只有原有的必填参数
    }
}

小结

高质量的工具设计是 Tool Use 系统成功的基础。核心原则是:

下一章将探讨如何让 Claude 同时调用多个工具,实现并行处理的高效架构。

本章评分
4.6  / 5  (16 评分)

💬 留言讨论