Function Calling 的参数错误通常不是模型能力不足,而是参数契约没有被严格描述。大模型在生成工具调用参数时,本质上是在输出一段需要被 JSON 解析器接受的文本,如果开发者只给了一个简单的描述,比如“返回城市和日期”,模型可能会返回字段名不一致、类型错误、多余字段或缺少必填项。这样的参数一进入业务函数就会抛异常,甚至造成静默数据错误。解决思路是把自然语言约束升级为 JSON Schema,并且在服务端做二次校验,让错误能够被及时发现和纠正。

为什么 Function Calling 参数容易出错?
Function Calling 并不是传统编程中的函数调用。模型不会直接执行函数,而是根据你在 tools 或 functions 中给出的说明,生成一个 JSON 格式的 arguments 字符串。这个字符串随后由框架解析并交给你的业务函数。这个过程的本质是文本生成,而不是类型安全的参数传递。模型在生成 token 时会受到训练数据、上下文和随机采样影响,因此即使提示词清晰,也可能出现字段缺失、类型不匹配、枚举值越界等问题。
更麻烦的是,很多开发者在定义工具时只写了简单的 description,没有给出严格的参数结构。例如获取天气的函数只说明“查询某地天气”,模型可能返回 { "city": "北京", "date": "明天" },而你的函数签名却需要 latitude、longitude 和 unit。当这些字段对不上时,运行时就会报 KeyError、TypeError 或 API 参数错误。要降低这种概率,就需要把接口契约显式化,让模型知道每个字段的约束,而不是依赖它猜测。
用 JSON Schema 严格定义参数:核心字段与实践
JSON Schema 是描述 JSON 数据结构的标准。在 Function Calling 中,它可以作为参数模板,告诉模型目标 JSON 应该有哪些属性、每个属性是什么类型、哪些必填、是否允许额外字段。几个关键字段包括 type、properties、required、additionalProperties、items、enum 和 description。
以查询天气为例,假设业务函数要求传入 location 对象,包含 latitude 和 longitude 两个数值,以及一个可选的 unit 字符串,只能是 celsius 或 fahrenheit。严格的 Schema 可以这样定义:
{
"type": "object",
"properties": {
"location": {
"type": "object",
"description": "目标位置的经纬度坐标",
"properties": {
"latitude": {
"type": "number",
"description": "纬度,范围 -90 到 90"
},
"longitude": {
"type": "number",
"description": "经度,范围 -180 到 180"
}
},
"required": ["latitude", "longitude"],
"additionalProperties": false
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位,默认 celsius"
}
},
"required": ["location"],
"additionalProperties": false
}
这里 additionalProperties: false 的作用很大。它明确禁止模型在对象里添加未定义字段。很多真实场景中,模型会自作主张添加 city、country 等字段,虽然看起来无害,但下游严格反序列化时会报错。通过 required 声明必填字段,则可以让模型意识到哪些字段没有生成就属于失败结果。
此外,enum 对枚举值非常有效。与其在描述中写“单位只能是摄氏度或华氏度”,不如直接用 enum 约束。模型在面对明确枚举时会优先选择其中之一,而不是生成 celcius 这种拼写错误。限制数值字段类型为 number 而不是 string,也能减少 "23.5" 和 23.5 混用的问题。
服务端二次校验与错误回传重试
JSON Schema 定义只是第一层防线,它让模型输出更规范,但不能保证百分百正确。服务端在接收到 arguments 后,仍然必须进行真正的 JSON Schema 校验。校验的目的不是怀疑模型,而是找到那些会直接导致业务逻辑崩溃的坏参数,并给出可读的错误信息。
以 Python 为例,可以使用 jsonschema 库的 validate 方法。先定义相同的 schema,然后调用 json.loads 解析模型输出的 arguments,最后执行校验。如果出现问题,会抛出 ValidationError。
import json
from jsonschema import validate, ValidationError
schema = {
"type": "object",
"properties": {
"location": {
"type": "object",
"properties": {
"latitude": {"type": "number"},
"longitude": {"type": "number"}
},
"required": ["latitude", "longitude"],
"additionalProperties": False
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"],
"additionalProperties": False
}
def safe_call_weather(arguments: str):
try:
params = json.loads(arguments)
validate(instance=params, schema=schema)
return call_real_weather_api(params)
except json.JSONDecodeError as e:
return {"error": "arguments is not valid json", "detail": str(e)}
except ValidationError as e:
return {"error": "schema validation failed", "detail": e.message}
上面的 validate 会根据 schema 检查类型、必填字段、枚举值和额外字段。捕获到 ValidationError 后,不应该只记录日志就结束,而应该把错误信息反馈给模型,让它重新生成。比如可以在下一次请求中追加一条 tool 消息,告诉模型:上次生成的参数违反了哪些约束,请根据错误信息重新生成。这种错误回传重试机制能显著提升 Function Calling 的成功率。
需要注意的是,重试不能无限进行。通常设置 2 到 3 次即可。如果模型连续失败,说明 schema 本身可能过于复杂,或者业务描述与函数签名不一致。此时应该简化嵌套结构、补充示例值,而不是继续提高重试上限。
def run_with_retry(user_query: str, max_retries: int = 3):
messages = [{"role": "user", "content": user_query}]
for attempt in range(max_retries):
response = model.chat(messages=messages, tools=tools)
tool_call = response.tool_calls[0]
try:
params = json.loads(tool_call.arguments)
validate(instance=params, schema=schema)
return call_real_weather_api(params)
except (json.JSONDecodeError, ValidationError) as e:
messages.append({
"role": "assistant",
"content": None,
"tool_calls": [tool_call]
})
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": f"参数错误:{e}。请根据 schema 重新生成。"
})
raise RuntimeError("Function calling failed after retries")
常见 Schema 设计与校验误区
很多参数错误不是模型不遵守约束,而是 Schema 本身设计不合理。例如把参数嵌套层级设计得过深。模型对深层嵌套对象的生成稳定性会下降,尤其是同时要求多个层级必填时。建议尽量将参数控制在两到三层以内,必要时拆成多个工具调用。另一个常见误区是只写 type 不写 required,模型会把所有字段都当作可选,导致关键参数缺失。
还有一个隐蔽问题是 additionalProperties 没有关闭。在某些框架中,模型看到历史示例后可能添加额外字段,例如把 latitude 写成 lat 的同时保留 lat。如果不禁止额外字段,这种偏差可能直接进入下游。开启 additionalProperties: false 后,误差会被暴露出来,反而更容易被修正。
对于枚举值,建议同时提供说明和示例。一个技巧是在 description 中写出目标 JSON 的完整示例,例如“示例:{"location":{"latitude":39.9042,"longitude":116.4074},"unit":"celsius"}”。模型看到示例后会明显更倾向于输出合法结构。当然,示例必须在语义上正确,否则会起到反效果。
Function Calling 的可靠性不是单纯靠模型能力提升就能解决的,更关键的是把约束前移到参数定义阶段,把校验落实到服务端执行阶段。JSON Schema 提供了一套通用且可验证的描述方式,配合错误回传重试,可以显著减少参数错误带来的业务故障。对于正在集成工具调用的项目来说,优先完善 Schema 往往比反复调整提示词更有效。
Function CallingJSON Schema参数校验修改时间:2026-08-22 16:40:13