资讯详情

资讯详情

AI Agent 应用实战(2):用 function calling 让模型调工具

上一篇划清了“模型负责判断、程序负责执行”的边界。本篇把边界落实为可验证的工具契约模型只能提出调用建议参数校验、权限判断和副作用控制始终留在应用侧。一、痛点从“能演示”走向“可托管”function calling 的本质不是让模型获得函数执行权而是让模型输出一个结构化意图。一个完整调用要经过四道门工具名必须在白名单参数满足类型与业务约束当前用户对目标资源有权限高风险动作得到明确批准。Schema 只解决形状不解决授权例如 customer_id 是字符串并不代表当前用户可以查询任意客户。真正可迁移的做法是先写任务契约再选模型输入从哪里来成功产物是什么哪些动作禁止自动执行最多允许多少步、多少时间和多少费用失败后由谁接管。契约一旦写进代码和测试模型升级只是实现替换不会悄悄改变业务责任边界。二、原理把概率判断放进确定性边界工具描述要同时写“什么时候用”和“什么时候不能用”。字段优先使用枚举、整数和明确格式金额使用整数分日期使用 ISO 8601对象应关闭额外字段。执行器不要用反射按模型给出的名称导入模块而应从静态注册表取函数。工具返回值也要裁剪只把判断所需字段、来源和稳定错误码送回模型。模型输出、工具数据和记忆内容都按不可信输入处理。每个边界都执行校验、授权、裁剪和审计所有状态变化都有明确原因。这样做看似增加一些代码却把“偶尔答错”拆成可定位的规划错误、参数错误、工具错误或策略错误团队才能针对性改进。三、实现用独立程序跑通最小闭环下面先把一个库存查询调用当作不可信输入。程序逐项验证工具名、商品编号、仓库范围和数量再决定是否执行。这个模式可以迁移到 CRM 查询、日程创建和退款申请替换 Schema 与授权函数即可验证顺序不变。fromdataclassesimportdataclassfromtypingimportAnydataclass(frozenTrue)classContext:allowed_warehouses:frozenset[str]defvalidate_call(call:dict[str,Any],ctx:Context)-tuple[bool,str]:ifcall.get(name)!check_inventory:returnFalse,unknown_toolargscall.get(arguments)ifnotisinstance(args,dict):returnFalse,arguments_not_objectifset(args)!{sku,warehouse,quantity}:returnFalse,unexpected_fieldsifnotisinstance(args[sku],str)ornotargs[sku].startswith(SKU-):returnFalse,invalid_skuifargs[warehouse]notinctx.allowed_warehouses:returnFalse,warehouse_forbiddeniftype(args[quantity])isnotintornot1args[quantity]20:returnFalse,invalid_quantityreturnTrue,acceptedctxContext(frozenset({SH-A,SH-B}))calls[{name:check_inventory,arguments:{sku:SKU-7,warehouse:SH-A,quantity:2}},{name:check_inventory,arguments:{sku:SKU-7,warehouse:BJ-X,quantity:2}},]forindex,callinenumerate(calls,1):ok,reasonvalidate_call(call,ctx)print(fcall{index}allowed{ok}reason{reason})运行输出call1 allowedTrue reasonaccepted call2 allowedFalse reasonwarehouse_forbidden校验通过后仍不要直接产生副作用。读取类工具可以立即执行写入类工具应先生成预览把标准化后的参数、影响范围和幂等键展示给审批者。模型看到的错误应是warehouse_forbidden这类稳定类别而不是数据库栈信息。这样提示词升级、模型切换后安全边界仍由确定性代码保持。第二个程序补上生产中最容易遗漏的控制点。它与前一个示例完全独立可单独保存运行不依赖本系列其他文件示例数据是内存假实现因此不会访问真实账户或产生外部副作用。fromdataclassesimportdataclassdataclassclassToolResult:code:strsummary:strsource:strINVENTORY{(SKU-7,SH-A):8}defcheck_inventory(sku:str,warehouse:str,quantity:int)-ToolResult:availableINVENTORY.get((sku,warehouse))ifavailableisNone:returnToolResult(not_found,没有匹配库存,inventory:v1)enoughavailablequantity textfrequested{quantity}, available{available}, enough{str(enough).lower()}returnToolResult(ok,text,inventory:v1)defdispatch(name:str,arguments:dict[str,object])-ToolResult:registry{check_inventory:check_inventory}functionregistry.get(name)iffunctionisNone:returnToolResult(blocked,工具不在白名单,executor)returnfunction(**arguments)request{sku:SKU-7,warehouse:SH-A,quantity:2}resultdispatch(check_inventory,request)print(fcode{result.code})print(fsummary{result.summary})print(fsource{result.source})运行输出codeok summaryrequested2, available8, enoughtrue sourceinventory:v1落地时应把示例中的内存状态替换为事务型存储把打印日志替换为结构化事件但不要改变契约。事件至少包含 run_id、步骤、输入摘要、策略结果、耗时、错误类别和版本密钥、完整个人信息及未经脱敏的提示不得进入日志。四、踩坑失败路径决定系统上限常见失败有三类。第一把参数解析成功误当成业务合法遗漏租户隔离与资源授权第二把工具返回的网页或数据库文本视为可信指令导致间接提示注入第三失败后让模型随意改参数重试造成越权范围逐步扩大。解决办法是授权绑定用户与资源结果使用数据通道而非指令通道重试只能修改预先允许的字段。还要防止把确定逻辑模型化。金额计算、权限判断、枚举路由和状态转移应使用普通代码模型适合处理分类、抽取、歧义消解与证据综合。每减少一个无必要的模型决策点就减少一处延迟、成本和不可复现性同时让测试更容易覆盖。五、验证用轨迹和门槛验收验收应保存模型原始调用、标准化参数、策略判定、工具结果摘要和最终回答。测试至少覆盖未知工具、额外字段、错误类型、越权资源、空结果、下游超时和重复写入。只有非法调用拒绝率为百分之百、合法调用成功率达到门槛、日志不含敏感值才进入下一篇的循环执行。发布顺序固定为离线回放、影子流量、小比例灰度和有限自治。每阶段先定义通过线和回滚条件再看结果不能在看到分数后移动门槛。关键样本要保存初始状态、每步动作、观察、最终产物与终止原因模型、提示和工具契约版本也必须一起记录。下一篇会把一次安全调用放进多轮任务循环本篇的白名单、参数校验与审计记录将成为每一轮都不可绕过的执行门。参考来源OpenAI Function callingJSON Schema ObjectOWASP API Security 觉得有用就点个赞 收藏方便回头查阅有疑问直接在评论区留言我看到都会回。 本文属于《AI Agent 应用实战》系列持续更新关注不迷路。 文章里的代码都能直接跑。想要可直接 clone 的完整工程 配套部署脚本 / 踩坑清单评论一声或发邮件到cj2664qq.com我免费发你。如果你正好在做类似系统、或有工程化难题想找人做也欢迎邮件聊一句——我按实际情况评估能落地的就接单或出方案。评论和邮件都能直接找到我不用跳别的平台。¥9.9 付费专栏《Python 自动化接单实战从脚本到第一单》共 10 篇、已完结不再更新一次拿走点此直达
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →