news 2026/5/4 6:38:17

#007 Agent 的执行层:工具调用(Function Calling)与 API 集成

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
#007 Agent 的执行层:工具调用(Function Calling)与 API 集成

从一次凌晨三点的事故说起

凌晨三点,线上告警:Agent 连续三次调用天气 API 返回了“晴”,但用户反馈窗外正在下暴雨。我盯着日志看了十分钟,发现 Agent 调用的参数里 latitude=39.9042, longitude=116.4074——这是北京天安门的坐标。用户明明在深圳。

问题出在哪?Agent 把用户说的“深圳”解析成了“北京”的经纬度。更致命的是,我写的工具函数没有校验参数范围,直接把这个坐标塞给了气象局 API。那天晚上我学到一件事:工具调用不是“把参数传过去就行”,而是 Agent 与外部世界之间的防火墙

工具调用的本质:给 Agent 装一双手

很多人把 Function Calling 理解成“让大模型调用函数”,这个说法没错,但太浅了。真正的工程视角是:工具调用是 Agent 的“执行层”,负责把自然语言意图翻译成结构化的系统指令

大模型本身不执行任何操作,它只生成 JSON。比如用户说“帮我查一下北京明天的天气”,模型输出的是:

{"function":"get_weather","parameters":{"city":"北京","date":"2026-01-15"}}

然后你的代码拿到这个 JSON,去调用真实的 API。这个“拿到 JSON → 校验 → 执行 → 返回结果”的过程,就是执行层的全部工作。

工具定义:别让模型猜你的参数

写工具定义(Tool Definition)是最容易踩坑的地方。我见过最离谱的写法是:

# 别这样写!参数描述太模糊tools=[{"type":"function","function":{"name":"send_email","description":"发送邮件","parameters":{"type":"object","properties":{"to":{"type":"string"},"subject":{"type":"string"},"body":{"type":"string"}}}}}]

模型看到这个定义,会把“发送邮件”理解成任何形式的邮件发送。用户说“给张三发个问候邮件”,模型可能把“张三”填进to字段,但系统里根本没有叫“张三”的用户。

正确的做法是把参数约束写进描述里

# 这里踩过坑:参数描述要包含业务规则tools=[{"type":"function","function":{"name":"send_email","description":"发送内部邮件,收件人必须是公司邮箱后缀","parameters":{"type":"object","properties":{"to":{"type":"string","description":"收件人邮箱,必须是 @company.com 结尾"},"subject":{"type":"string","description":"邮件主题,不超过100字符"},"body":{"type":"string","description":"邮件正文,支持Markdown格式"}},"required":["to","subject","body"]}}}]

经验:描述越具体,模型越不容易犯错。把业务规则、边界条件、格式要求全部写进去。别指望模型“理解”你的业务,它只理解你写出来的文字。

参数校验:执行层的最后一道防线

模型生成的参数不一定合法。我见过模型把temperature传成"high"(字符串),把user_id传成负数。所以执行层必须做两件事:

  1. 类型校验:确保参数类型正确
  2. 业务校验:确保参数值在合理范围内
defvalidate_weather_params(params):# 这里踩过坑:模型可能传非数字的经纬度try:lat=float(params.get("latitude",0))lon=float(params.get("longitude",0))except(TypeError,ValueError):returnFalse,"经纬度必须是数字"# 别这样写:直接信任模型传的值ifnot(-90<=lat<=90):returnFalse,f"纬度{lat}超出范围 [-90, 90]"ifnot(-180<=lon<=180):returnFalse,f"经度{lon}超出范围 [-180, 180]"returnTrue,{"latitude":lat,"longitude":lon}

经验:永远不要信任模型输出的参数。把它当成用户输入来校验。我见过一个生产事故,模型把amount传成了-100,导致财务系统扣了负数的钱——相当于给用户充值了。

错误处理:别让 Agent 卡死

工具调用一定会出错。API 超时、参数非法、权限不足……这些错误怎么处理,决定了 Agent 的鲁棒性。

我见过两种极端:

  • 直接抛异常:Agent 对话中断,用户看到一堆 traceback
  • 吞掉错误:Agent 说“操作成功”,但实际没执行

正确的做法是把错误信息结构化地返回给模型,让模型决定下一步:

defcall_api_with_retry(func,params,max_retries=2):forattemptinrange(max_retries):try:result=func(**params)return{"status":"success","data":result}exceptTimeoutError:ifattempt==max_retries-1:return{"status":"error","error_type":"timeout","message":"API 请求超时,请稍后重试"}continueexceptPermissionErrorase:return{"status":"error","error_type":"permission","message":f"权限不足:{str(e)}"}exceptExceptionase:return{"status":"error","error_type":"unknown","message":f"未知错误:{str(e)}"}

经验:错误信息要包含“错误类型”和“可读描述”。模型看到error_type: timeout会知道“哦,需要重试”;看到error_type: permission会知道“需要换一个方式”。

上下文管理:工具调用的“记忆”

Agent 调用工具后,结果需要放回对话上下文。但这里有个坑:工具返回的数据可能很大

比如调用“查询用户订单”API,返回了 1000 条订单记录。如果你把这些数据全部塞回上下文,token 会爆炸,模型也会迷失在数据里。

我的做法是对工具返回结果做摘要

defsummarize_tool_result(result,max_length=500):# 这里踩过坑:直接返回完整数据导致 token 超限ifisinstance(result,list)andlen(result)>10:summary=result[:5]# 只保留前5条summary.append(f"... 还有{len(result)-5}条记录未显示")returnsummaryifisinstance(result,str)andlen(result)>max_length:returnresult[:max_length]+"..."returnresult

经验:工具返回给模型的数据,应该是“模型需要知道的信息”,而不是“API 返回的全部信息”。模型不需要看 1000 条订单,它只需要知道“用户有 1000 条订单,最近一条是昨天”。

并发与限流:别把下游打崩

Agent 调用工具时,可能同时触发多个 API 调用。比如用户说“查一下北京、上海、深圳的天气”,模型可能一次性生成三个工具调用请求。

如果你的代码是串行执行的,用户要等 3 秒(每个 API 1 秒)。但如果并发执行,1 秒就能返回。但并发有风险:下游 API 可能有频率限制

importasynciofromaiolimiterimportAsyncLimiter# 这里踩过坑:并发太高被 API 封 IPlimiter=AsyncLimiter(max_rate=10,time_period=1)# 每秒最多10次asyncdefcall_with_rate_limit(func,params):asyncwithlimiter:returnawaitfunc(**params)asyncdefbatch_call_tools(tool_calls):tasks=[]forcallintool_calls:task=call_with_rate_limit(call.func,call.params)tasks.append(task)results=awaitasyncio.gather(*tasks,return_exceptions=True)returnresults

经验:给每个外部 API 单独配置限流器。不同 API 的限流策略不同,别用一个全局限流器。我见过一个案例:调用天气 API 和调用数据库 API 共用一个限流器,导致数据库查询被天气 API 拖慢。

安全:工具调用的“门禁”

工具调用是 Agent 与外部系统交互的通道,也是最容易被攻击的地方。我见过最严重的安全事故是:Agent 调用了delete_user工具,参数是user_id=1,结果删除了管理员账号。

安全措施至少包括:

  1. 参数白名单:只允许模型使用指定的参数值
  2. 操作权限:区分“只读”和“读写”工具
  3. 人工确认:高危操作需要用户二次确认
# 别这样写:直接执行模型传过来的 SQLdefexecute_sql(query):# 这里踩过坑:模型可能传 DROP TABLEallowed_operations=["SELECT","INSERT","UPDATE"]operation=query.split()[0].upper()ifoperationnotinallowed_operations:return{"status":"error","message":f"不允许执行{operation}操作"}# 执行查询...

经验:把工具分为“安全工具”和“危险工具”。安全工具(如查询天气)可以直接执行;危险工具(如删除数据)需要用户确认。这个分类写在工具定义里,让模型知道哪些工具需要“先问用户”。

个人经验:工具调用的三个原则

写了两年 Agent 工具调用,踩了无数坑,总结三个原则:

原则一:工具是 Agent 的“手”,不是“大脑”
工具只负责执行,不负责决策。决策是模型的事。所以工具函数要简单、确定、可预测。不要在工具函数里写复杂的业务逻辑,那应该放在模型推理阶段。

原则二:每个工具都要有“失败预案”
工具调用一定会失败。网络超时、参数错误、权限不足……每个失败场景都要有对应的处理逻辑。我见过最差的代码是try: ... except: pass,这会让 Agent 在错误状态下继续运行,产生更严重的后果。

原则三:工具定义是“合同”,不是“说明书”
模型会严格按照工具定义来生成参数。所以工具定义要像法律合同一样严谨:参数类型、取值范围、业务规则、错误码……全部写清楚。模糊的描述会导致模型产生幻觉。

最后说一句:工具调用的调试是最痛苦的。因为错误可能来自模型(参数生成错误)、来自网络(API 超时)、来自业务(参数不合法)。建议在开发阶段,给每个工具调用加上详细的日志,记录“模型生成的参数 → 校验结果 → API 返回结果 → 返回给模型的结果”。这样出问题时,你能快速定位是哪个环节出了问题。

别问我为什么知道——凌晨三点的教训,一次就够了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/4 6:33:27

Android开发副驾Claw Companion:移动端调试工具的设计与实现

1. 项目概述&#xff1a;一个为Android开发者量身打造的“智能副驾”在Android应用开发的日常中&#xff0c;我们常常会陷入一种重复性的“体力劳动”&#xff1a;为了测试一个API接口&#xff0c;需要打开Postman或类似的工具&#xff0c;手动构建请求、设置Header、粘贴JSON&…

作者头像 李华
网站建设 2026/5/4 6:32:50

视频生成中的运动控制技术与优化实践

1. 运动控制在视频生成中的核心价值视频生成技术正在从静态图像合成向动态序列生成快速演进。在这个过程中&#xff0c;运动控制的质量直接决定了生成视频的连贯性、真实感和可用性。传统视频生成模型常出现物体变形、运动卡顿、时序错乱等问题&#xff0c;本质上都是运动控制机…

作者头像 李华
网站建设 2026/5/4 6:26:02

OBS Multi RTMP插件:一键实现多平台直播同步推流

OBS Multi RTMP插件&#xff1a;一键实现多平台直播同步推流 【免费下载链接】obs-multi-rtmp OBS複数サイト同時配信プラグイン 项目地址: https://gitcode.com/gh_mirrors/ob/obs-multi-rtmp 还在为每次直播只能选择一个平台而烦恼吗&#xff1f;OBS Multi RTMP插件帮…

作者头像 李华
网站建设 2026/5/4 6:21:40

Paynless Framework:一体化全栈开发框架,快速构建现代SaaS应用

1. 项目概述&#xff1a;一个为现代应用开发提速的“开箱即用”框架如果你和我一样&#xff0c;经常从零开始搭建SaaS应用或者复杂的多平台项目&#xff0c;那你一定对下面这个场景深恶痛绝&#xff1a;每次新项目启动&#xff0c;都要重新配置一遍用户认证、数据库连接、支付集…

作者头像 李华
网站建设 2026/5/4 6:19:29

Claude Code BMAD技能包:AI驱动开发流程标准化实践指南

1. 项目概述与核心价值如果你正在使用 Claude Code 进行软件开发&#xff0c;并且对如何将 AI 驱动开发流程化、标准化感到困惑&#xff0c;那么terryso/claude-bmad-skills这个项目绝对值得你花时间深入了解。它不是一个简单的代码片段集合&#xff0c;而是一套为BMAD&#xf…

作者头像 李华