Function Calling 工具描述优化:怎么写工具说明让模型更准确
一、"模型总是选错函数"的背后是描述写得像猜谜
Agent 有query_order和query_order_detail两个函数。用户说"帮我查一下订单",模型有 60% 的概率选错函数。原因很简单:两个函数的 description 分别是"查询订单"和"查询订单详情"——从人类角度看区别够明显,但对模型来说,这两个描述太像了。
工具描述的写法直接影响 Function Calling 的准确率。模型是根据描述来判断"什么时候该调用哪个函数"的。描述写得好,准确率可以从 60% 提升到 90% 以上。
二、工具描述的黄金结构
一个高质量的工具描述应该包含五个要素:
- 功能动词:一句话说清楚这个函数做什么
- 使用场景:什么情况下该用这个函数(帮助模型做选择)
- 输入说明:需要什么参数才能正确调用
- 输出说明:调用后会返回什么
- 反面示例:什么情况下不该用这个函数
flowchart LR A[工具描述] --> B[功能: 一句话总结] A --> C[场景: 何时使用/何时不用] A --> D[输入: 参数要求] A --> E[输出: 返回什么] A --> F[示例: 正确调用示范] B --> G[模型快速判断函数用途] C --> H[避免选错函数] D --> I[准确填充参数] E --> J[模型知道如何使用结果] F --> K[减少调用格式错误]三、Python 实现:描述质量评估与自动优化
from dataclasses import dataclass from typing import Optional import re @dataclass class ToolDescription: """工具描述结构体""" name: str function_summary: str # 功能概述(1句话) use_cases: list[str] # 使用场景 not_use_cases: list[str] # 不应使用的情况 input_requirements: str # 输入参数说明 output_description: str # 输出说明 examples: list[str] # 调用示例 # ========== 坏 vs 好的描述对比 ========== bad_descriptions = { "query_order": { "description": "查询订单", # ❌ 太简短,无区分度 "parameters": { "order_id": {"type": "string", "description": "订单号"}, }, }, "query_order_detail": { "description": "查询订单详情", # ❌ 和上一个几乎一样 "parameters": { "order_id": {"type": "string", "description": "订单号"}, }, }, } good_descriptions = { "query_order": { "description": ( "查询用户的订单列表,支持按时间和状态筛选。" "当用户想查看「最近订单」「所有订单」或按条件筛选订单时使用。" "注意:如果用户要查看单个订单的详细信息(如物流、商品明细)," "请使用 query_order_detail。" "返回订单列表(ID、金额、状态、时间),不包含商品细节。" ), "parameters": { "status": { "type": "string", "enum": ["pending", "paid", "shipped", "completed"], "description": ( "订单状态筛选,可选。" "不提供时返回所有状态的订单。" "注意:'已完成'应使用'completed'而非'done'" ), }, "limit": { "type": "integer", "description": ( "返回条数,默认10,最大50。" "超过50会自动截断为50" ), "default": 10, }, }, }, "query_order_detail": { "description": ( "查询单个订单的完整详情,包括商品明细、物流状态、支付详情。" "仅在用户指定了具体订单号或明确说「查看订单详情」时使用。" "不要用于批量查询订单列表——批量查询请使用 query_order。" "返回完整的订单信息(商品、物流、支付、发票等)。" ), "parameters": { "order_id": { "type": "string", "description": ( "订单号,格式为 ORD-年月日-流水号。" "例如: ORD-20260715-0001。" "如果用户提供了不完整的订单号,请先向用户确认" ), }, }, }, } # ========== 描述质量评分器 ========== class DescriptionScorer: """工具描述质量评分""" @staticmethod def score(description: str, parameters: dict) -> dict: score = 0 details = [] # 1. 长度检查:要有足够信息量 if len(description) > 30: score += 20 details.append("描述长度充足 (+20)") elif len(description) > 10: score += 10 details.append("描述长度一般 (+10)") else: details.append("描述过短 (-0)") # 2. 区分度关键词:是否包含"注意/不要/请使用" if any(kw in description for kw in ["不要", "请使用", "注意"]): score += 15 details.append("包含区分度关键词 (+15)") else: details.append("缺少区分度关键词 (-0)") # 3. 场景说明:"当用户...时使用" if "当用户" in description or "用于" in description: score += 15 details.append("包含使用场景 (+15)") # 4. 输出说明:"返回..." if "返回" in description: score += 15 details.append("包含输出说明 (+15)") # 5. 参数描述质量 param_score = 0 for name, param in parameters.items(): param_desc = param.get("description", "") if len(param_desc) > 15: param_score += 5 if "例" in param_desc or "例如" in param_desc: param_score += 5 score += min(param_score, 25) details.append(f"参数描述质量 (+{min(param_score, 25)})") # 6. 检查是否有枚举约束 for param in parameters.values(): if "enum" in param: score += 5 details.append("参数使用 enum 约束 (+5)") break return {"score": min(score, 100), "details": details} # ========== 描述优化提示模板 ========== OPTIMIZATION_PROMPT = """ 你是一名 API 文档专家。优化以下 Function Calling 的工具描述。 当前描述: {current_description} 优化规则: 1. 功能概述:用一句话说清楚做什么 2. 使用场景:什么时候该用这个函数 3. 排除场景:什么情况下不该用(避免和相似函数混淆) 4. 输入说明:需要什么参数 5. 输出说明:返回什么 优化后的描述(只需返回描述文本): """四、描述优化的边界与经验法则
不要让描述太长。150-300 字是最佳范围。太短模型无法区分函数,太长模型会"忘记"前面的约束。超过 300 字的描述,模型对后半部分的注意力权重会显著下降。
参数描述比函数描述更重要。模型看到函数描述后有 30% 的可能选对函数,但参数描述的质量直接决定能否正确填充。参数描述应该包含:类型说明 + 格式示例 + 默认行为 + 边界说明。
反面示例比正面示例更有效。在描述中说"不要用于..."比"用于..."更能帮助模型区分相似函数。这利用的是模型对否定信息的注意力偏差。
描述需要迭代优化。写完描述后端到端测试 100 个真实查询,统计错误调用率。针对错误率高的函数,分析是什么原因导致模型选错或填错,针对性修改描述。
五、总结
Function Calling 工具描述的核心法则是"让模型在不确定时能做出正确判断"。五要素结构(功能/场景/输入/输出/反例)是底线。参数描述要带格式示例,函数描述要含排除场景。描述的质量不是"写出来就觉得好",而是"测 100 条查询,错误率低于 5% 才算通过"。