
Function Calling 参数填充策略必填和选填的默认值设计一、模型输出了一个空字符串地址用户说帮我查一下最近的订单Agent 调用了query_order函数。模型返回的order_id是空字符串start_date是null只有limit字段被准确填充为 10。后端查了全表、扫了 30 万行超时报错用户等了 8 秒后关掉了页面。这不是模型不聪明而是函数描述给得不够明确。模型不知道order_id为空的含义是查全部还是这次不需要这个参数。参数填充的本质问题如何让模型理解参数的真正语义在缺失时做出正确的默认行为二、参数填充的三层策略Function Calling 的参数填充可以分成三层来处理Schema 层通过 JSON Schema 的required、default、description字段在函数定义阶段声明参数约束。提示层在 System Prompt 中注入参数填充规则告诉模型在什么情况下用什么默认值。代码层在函数执行前做二次校验和默认值注入作为安全兜底。flowchart TD A[用户输入] -- B[LLM 分析意图] B -- C{参数提取} C -- D[Schema 层过滤] D -- E{必填参数完整?} E --|是| F[提示层补充默认值] E --|否| G[追问用户补充] F -- H[代码层二次校验] G -- A H -- I{校验通过?} I --|是| J[执行函数调用] I --|否| K[使用代码层默认值] K -- J J -- L[返回结果]这个三层策略的核心思想是纵深防御不能只依赖模型来填参数。Schema 定义边界Prompt 指导行为代码做最后的兜底。三、生产级参数填充实现import json from typing import Any, Optional from dataclasses import dataclass, field from datetime import datetime, timedelta from enum import Enum # Schema 层函数定义 class ParamRequired(Enum): REQUIRED required OPTIONAL optional CONDITIONAL conditional # 条件必填 dataclass class ParamDef: 参数定义 name: str param_type: str # JSON Schema type required: ParamRequired default: Any None # 默认值 description: str # 给模型看的描述需要精确 enum: list field(default_factorylist) # 枚举值 condition: str # 条件必填的触发条件 dataclass class FunctionDef: 函数定义 name: str description: str # 给模型看的函数描述 params: list[ParamDef] # 参数列表 # 以查询订单为例 ORDER_QUERY_FUNC FunctionDef( namequery_order, description查询用户订单。如果不指定order_id则返回最近N条订单, params[ ParamDef( nameorder_id, param_typestring, requiredParamRequired.OPTIONAL, defaultNone, description订单号。不提供时查询最近的全部订单, ), ParamDef( namestart_date, param_typestring, requiredParamRequired.OPTIONAL, defaultNone, description查询起始日期格式YYYY-MM-DD。不提供时默认最近7天, ), ParamDef( nameend_date, param_typestring, requiredParamRequired.OPTIONAL, defaultNone, description查询结束日期格式YYYY-MM-DD。不提供时默认今天, ), ParamDef( namelimit, param_typeinteger, requiredParamRequired.OPTIONAL, default10, description返回条数上限默认10最大100, ), ParamDef( namestatus, param_typestring, requiredParamRequired.OPTIONAL, defaultall, enum[pending, paid, shipped, completed, cancelled, all], description订单状态过滤。不提供时返回全部状态, ), ], ) # 代码层参数填充与校验 class ParamFiller: 参数填充器三层策略的代码实现 # 内置默认值规则根据参数名推断合理的默认值 BUILTIN_DEFAULTS { limit: 10, offset: 0, page_size: 20, timeout: 30, } # 时间相关参数的动态默认值 TIME_DEFAULTS { start_date: lambda: (datetime.now() - timedelta(days7)).strftime(%Y-%m-%d), end_date: lambda: datetime.now().strftime(%Y-%m-%d), start_time: lambda: (datetime.now() - timedelta(hours1)).isoformat(), end_time: lambda: datetime.now().isoformat(), } def __init__(self, func_def: FunctionDef): self.func_def func_def self._build_param_map() def _build_param_map(self): 构建参数名到参数定义的映射 self.param_map {p.name: p for p in self.func_def.params} def fill_and_validate( self, llm_params: dict[str, Any] ) - tuple[dict[str, Any], list[str]]: 填充默认值并校验参数。 返回: (filled_params, errors): 填充后的参数和错误列表 如果 errors 非空不应执行函数调用 filled {} errors [] for param in self.func_def.params: value llm_params.get(param.name, None) # 步骤 1必填参数校验 if param.required ParamRequired.REQUIRED: if value is None or value : errors.append(f必填参数 {param.name} 缺失) continue # 步骤 2条件必填校验 if param.required ParamRequired.CONDITIONAL: if not self._check_condition(param.condition, llm_params): if value is None or value : errors.append(f条件必填参数 {param.name} 在当前条件下缺失) continue # 步骤 3填充默认值 if value is None or value : value self._get_default(param) # 步骤 4类型转换与校验 try: value self._coerce_type(param, value) except (ValueError, TypeError) as e: errors.append(f参数 {param.name} 类型转换失败: {e}) continue # 步骤 5枚举值校验 if param.enum and value not in param.enum: # 不报错修正为包含值或默认值 value param.default or param.enum[0] # 步骤 6业务范围校验 if param.name limit: value max(1, min(value, 100)) # 钳制到 [1, 100] filled[param.name] value return filled, errors def _get_default(self, param: ParamDef) - Any: 获取参数的默认值优先级param.default TIME_DEFAULTS BUILTIN_DEFAULTS if param.default is not None: return param.default # 动态时间默认值 if param.name in self.TIME_DEFAULTS: return self.TIME_DEFAULTS[param.name]() # 内置类型默认值 if param.name in self.BUILTIN_DEFAULTS: return self.BUILTIN_DEFAULTS[param.name] # 根据类型返回零值 type_defaults { string: , integer: 0, number: 0.0, boolean: False, array: [], } return type_defaults.get(param.param_type, None) def _coerce_type(self, param: ParamDef, value: Any) - Any: 类型转换确保参数值符合声明的类型 if value is None: return self._get_default(param) type_converters { string: str, integer: int, number: float, boolean: bool, array: lambda v: v if isinstance(v, list) else [v], } converter type_converters.get(param.param_type) if converter and not isinstance(value, eval(param.param_type)): return converter(value) return value def _check_condition(self, condition: str, params: dict) - bool: 检查条件必填的触发条件简化版 if not condition: return True # 实际项目中应实现完整的条件表达式解析 return True # 使用示例 def execute_function_call(func_name: str, raw_params: dict) - dict: 执行 Function Calling 的统一入口 # 找到函数定义 func_def find_function_def(func_name) if not func_def: return {error: f未定义的函数: {func_name}} # 参数填充与校验 filler ParamFiller(func_def) params, errors filler.fill_and_validate(raw_params) if errors: return { error: 参数校验失败, details: errors, suggestion: 请提供缺失的参数, } # 执行实际的业务逻辑 return dispatch_call(func_name, params) def find_function_def(name: str) - Optional[FunctionDef]: # 实际项目中从注册表查找 if name query_order: return ORDER_QUERY_FUNC return None def dispatch_call(name: str, params: dict) - dict: # 实际调用业务函数 return {status: ok, func: name, params: params}四、参数填充的边界与权衡默认值过大会引发性能问题。limit默认 100 听起来合理但如果用户不传任何过滤条件全表扫描 100 行再逐条处理接口就会变慢。建议根据业务场景给 limit 设置合理的上限。模型过度填充的问题。有时候模型会给可选参数填入一个看起来合理但实际上错误的默认值。比如用户没有提到日期模型却填了昨天——这是模型根据对话上下文推测的。解决方案是在 Prompt 中明确如果用户没有显式提到某参数不要自行推断让它保持缺失。条件必填的复杂度。条件必填Conditional Required在 JSON Schema 中表达困难。比如如果查询类型是按订单号则 order_id 必填。这时需要在代码层实现额外的校验逻辑Schema 层只做基本约束。参数描述决定了模型填充质量。描述越精确模型填充越准确。查询起始日期不如查询起始日期格式YYYY-MM-DD。不提供时默认最近7天清晰。为模型写函数描述要像给新同事写接口文档一样细致。五、总结Function Calling 的参数填充不能只靠模型的聪明需要 Schema 定义边界、Prompt 引导行为、代码做最终兜底。核心策略必填参数严格校验、可选参数必须有合理的默认值、所有参数在代码层做二次校验和类型转换。参数的默认值直接影响了函数的执行效率和用户体验设计时就要考虑最坏情况——模型填错了会怎样代码能否兜住