工具调用的瓶颈在"参数"而不在"调用"
很多Agent接入工具后效果差,问题不在函数本身,而是LLM生成的参数不对:日期格式错了、枚举值不在范围内、必填字段缺失、单位没换算。Function Calling的参数抽取,本质是把"自然语言意图"转成"严格类型的JSON",需要Prompt约束、Schema定义、代码校验三层配合。
一、先定义清晰的工具Schema
工具描述越含糊,参数抽取越不准。用JSON Schema把每个字段的约束写死:
TOOLS = [{
"type": "function",
"function": {
"name": "book_meeting",
"description": "预定会议室,必须提供开始时间与时长",
"parameters": {
"type": "object",
"properties": {
"room_id": {
"type": "string",
"enum": ["A101", "A102", "B201"], # 枚举约束
"description": "会议室编号,只能是枚举值之一"
},
"start_time": {
"type": "string",
"format": "date-time", # ISO 8601格式约束
"description": "会议开始时间,ISO格式如2026-08-24T14:00:00"
},
"duration_minutes": {
"type": "integer",
"minimum": 15,
"maximum": 240
}
},
"required": ["room_id", "start_time", "duration_minutes"]
}
}
}]
二、Prompt层:给LLM"翻译规则"
光有Schema不够,实测还要在System Prompt里补充隐含规则,比如时间表达换算:
当用户说"明天下午3点"时,start_time必须换算成具体的ISO 8601时间戳,
基准日期为今天。如果用户没指定会议室,不要猜测,把room_id留空并在
参数中附加missing_fields说明。
三、代码层:参数校验与自动修复
LLM输出再可靠也要兜底。用Pydantic校验+修复策略:
from pydantic import BaseModel, ValidationError
from datetime import datetime, timedelta
class BookMeetingParams(BaseModel):
room_id: str
start_time: datetime
duration_minutes: int
def parse_and_fix(raw_args: dict) -> dict:
# 策略1:直接校验
try:
return BookMeetingParams(**raw_args).model_dump()
except ValidationError:
pass
# 策略2:常见问题自动修复
fixed = dict(raw_args)
if isinstance(fixed.get("start_time"), str):
try:
# "明天下午3点"等自然语言交给LLM二次修正
fixed["start_time"] = normalize_time(fixed["start_time"])
except Exception:
return {"error": "时间解析失败,请明确具体时间"}
if fixed.get("room_id") not in ("A101", "A102", "B201"):
return {"error": f"会议室不存在: {fixed.get('room_id')}"}
return fixed
四、参数抽取质量评估清单
| 检查项 | 通过标准 | 测试方法 |
|---|---|---|
| 枚举值合法 | 100%落在枚举内 | 构造50条边界case |
| 时间格式 | 全部为ISO 8601 | 正则+datetime解析 |
| 必填字段 | 无缺失 | Schema校验 |
| 隐含换算 | 相对时间正确转绝对时间 | 跨时区case |
| 拒绝率 | 信息不足时明确拒绝而非瞎猜 | 缺省case |
五、高频翻车场景与对策
2. 日期歧义:"周五"到底指这周还是下周?Prompt里给基准日期,代码里做二次确认
3. 幻觉参数:用户没提的参数,LLM会脑补。对策:Prompt强调"未提及的字段不要填",代码层校验未授权字段一律拒绝
总结:参数抽取做得好不好,直接决定工具链路的稳定性。把Schema写严、把Prompt规则写清、把校验兜底写实,三步下来工具调用的成功率能稳定在95%以上。