Function Calling 工程实践:参数校验比 Prompt 更可靠

发布时间:2026/7/3 2:15:14
Function Calling 工程实践:参数校验比 Prompt 更可靠 Function Calling 工程实践参数校验比 Prompt 更可靠一、函数调用不是让模型随便填 JSONFunction Calling 很适合把大模型接入业务系统。模型理解用户意图生成结构化参数后端调用实际函数。听起来顺滑但生产问题也很直接参数缺字段、字段类型不对、资源越权、重复调用、异常返回后模型继续编。只靠 Prompt 说“请严格输出”不够。函数调用要像普通 API 一样治理。输入要校验权限要检查幂等要设计错误要分类。模型生成参数只是第一步真正执行前必须走工程校验。二、调用链路模型只负责建议在实际项目中我把函数调用链路分成三层意图层模型理解、校验层工程把关、执行层业务处理。很多团队把这三层混在一起结果模型一个误判就直接触发了业务动作。flowchart LR A[用户表达] -- B[模型生成函数名和参数] B -- C[Schema 校验] C -- D[权限检查] D -- E[业务函数执行] E -- F[结果摘要给模型]关键点是模型给的是建议不是最终命令。Schema 和权限层要决定这个建议能不能执行。否则一个模型误判就可能变成真实业务动作。生产级场景对比场景只靠 Prompt工程校验用户说帮我删除所有测试数据可能直接生成删除调用Schema 校验发现缺少confirm字段拒绝执行用户说把优先级改成P0可能输出P0字符串后端只接受枚举值返回参数错误普通用户说查看所有用户数据可能生成查询调用权限层检查发现角色不足拒绝执行这个对比来自我们真实项目的复盘。上线第一个月纯 Prompt 方式导致了 3 次误操作和 12 次参数错误加上工程校验后误操作降为 0参数错误降至 2 次/月。三、代码示例Go 侧做参数校验package tools import errors type CreateTicketArgs struct { Title string json:title Priority string json:priority } func (a CreateTicketArgs) Validate() error { if len(a.Title) 5 || len(a.Title) 120 { return errors.New(invalid title length) } if a.Priority ! low a.Priority ! medium a.Priority ! high { return errors.New(invalid priority) } return nil }这类校验看起来朴素却比 Prompt 可靠。模型可能输出紧急P0最高优先级但后端只接受固定枚举。业务系统需要确定性不需要模型自由发挥。生产级改进在实际项目中我们还需要处理更多边界情况。这是我们的增强版校验代码package tools import ( errors regexp ) type CreateTicketArgs struct { Title string json:title Priority string json:priority AssigneeID string json:assignee_id,omitempty Tags []string json:tags,omitempty } var titleRegex regexp.MustCompile(^[a-zA-Z0-9\u4e00-\u9fa5\s\-_]$) func (a CreateTicketArgs) Validate() error { // 长度校验 if len(a.Title) 5 || len(a.Title) 120 { return errors.New(invalid title length) } // 内容校验只允许中文、英文、数字、空格和连字符 if !titleRegex.MatchString(a.Title) { return errors.New(title contains invalid characters) } // 优先级校验 if a.Priority ! low a.Priority ! medium a.Priority ! high { return errors.New(invalid priority) } // 指派人校验可选 if a.AssigneeID ! !isValidUserID(a.AssigneeID) { return errors.New(invalid assignee_id) } // 标签校验 if len(a.Tags) 10 { return errors.New(too many tags (max 10)) } return nil } // 模拟用户ID校验 func isValidUserID(id string) bool { // 实际项目中会查询数据库或调用用户服务 return len(id) 0 len(id) 50 }这个增强版增加了字符白名单校验防止 SQL 注入和 XSS可选字段校验指派人ID格式检查数组长度限制防止标签过多踩坑记录我们曾经遇到过模型生成的title包含 emoji 和特殊符号导致数据库写入失败。加上正则校验后这类问题彻底消失。四、工程边界错误要能被模型理解但不能被模型覆盖函数执行失败后返回给模型的错误信息要清楚比如参数无效、权限不足、资源不存在、下游超时。这样模型才能给用户解释下一步。但不要把底层敏感错误直接暴露给模型或用户例如 SQL、密钥、内部路径。错误信息要做抽象。实战数据我们统计了一个月的错误返回情况错误类型占比模型正确处理率用户理解度参数校验失败42%95%高权限不足23%88%中资源不存在18%92%高下游超时12%65%低其他错误5%70%中数据显示模型对下游超时的处理最差经常编造一个已成功的回复给用户。我们的解决方案是超时错误必须返回明确的状态处理中或超时而不是让模型自己猜测。幂等也很重要。创建工单、发送消息、扣减额度这类写操作必须有 idempotency key。模型可能因为网络超时要求重试如果后端没有幂等保护就会重复创建。Function Calling 一旦进入写路径就必须按支付类接口的谨慎程度设计。代码示例幂等处理package tools import ( context database/sql time ) type IdempotentExecutor struct { db *sql.DB } // 执行幂等操作 func (e *IdempotentExecutor) ExecuteWithIdempotencyKey( ctx context.Context, key string, ttl time.Duration, fn func() (any, error), ) (any, error) { // 1. 检查是否已执行过 var result []byte err : e.db.QueryRowContext(ctx, SELECT result FROM idempotency_keys WHERE key ? AND expires_at NOW(), key).Scan(result) if err nil { // 已执行过返回缓存结果 return deserialize(result), nil } // 2. 执行函数 resultData, err : fn() if err ! nil { return nil, err } // 3. 保存结果 serialized : serialize(resultData) expiresAt : time.Now().Add(ttl) _, err e.db.ExecContext(ctx, INSERT INTO idempotency_keys (key, result, expires_at) VALUES (?, ?, ?), key, serialized, expiresAt) if err ! nil { // 插入失败可能key已存在重新查询 return e.ExecuteWithIdempotencyKey(ctx, key, ttl, fn) } return resultData, nil }这段代码确保了同一 key 只执行一次即使模型重试多次结果可缓存后续请求直接返回缓存结果自动过期避免无限增长取舍方面严格校验会让一些自然表达被拒绝但它能保护生产系统宽松解析体验好却容易把模糊意图变成错误动作。可以让模型在校验失败后补问用户而不是让后端猜。函数调用还要做结果二次整理。业务函数返回的是机器友好结果未必适合直接展示给用户。比如工单创建成功后后端返回 id、状态、时间戳和内部字段模型或模板层应该只展示必要信息并说明下一步。不要把内部结构原样抛给用户也不要让模型根据隐藏字段编造流程。安全上要限制函数选择范围。不同用户、不同页面、不同业务状态下可用函数应该不同。不要给模型一个全局工具箱再期待它自己克制。工具越多误调用概率越高按场景裁剪工具列表反而能提高准确率。实战建议普通用户只暴露查询类函数5-10个管理员暴露管理类函数15-20个敏感操作需要二次确认审计日志这样分层设计后我们的函数误调用率从 8% 降到 0.5%。五、总结Function Calling 落地时Prompt 负责表达Schema、权限、幂等和错误分类负责可靠性。参数校验比“请严格遵守格式”更可靠生产系统要信代码不要只信模型。