资讯动态

Agent工具落地实战:从函数调用到多工具编排的完整链路

发布时间:2026/10/24 1:05:49 来源:尧图企业网站定制
做 Agent 的人应该都有过同一种挫败感模型回答得头头是道可一旦让它去查一下明天的天气把这份表格整理好发我这种具体指令它就立刻露馅。我在内部代号 Hermes 的 Agent 项目里摸爬滚打了一段时间之后越来越确信一件事——一个 Agent 到底是不是真的有用不取决于它的 Prompt 写得有多花哨而取决于它的tools 能不能扎实落地。这个系列就是围绕 Hermes agent 的 tools 落地应用来写的今天先讲清楚从设计到上线的完整链路重点拆解那些文档里不会写、但实际一定会踩的坑。如果你正准备给 Agent 接工具或者已经在接但总出幺蛾子这篇文章应该能帮你省下不少排查时间。1. 先搞清楚Hermes 的 tools 到底在解决什么问题1.1 会聊天和会干活之间隔着一层函数调用很多人第一次接触 Agent 时会误以为模型本身具备执行能力。实际上绝大多数通用大模型只是一个文本生成器。你问它今天北京天气适合跑步吗它能洋洋洒洒写出一段天气分析和跑步建议但这些内容都是凭训练数据推测的它并不知道此刻北京的实时温度。Hermes 要做的事就是在这条想法和行动之间搭一座桥。这座桥就是工具调用function calling / tool use。大模型不再只输出纯文本而是会在需要的时候输出一个结构化的调用请求包括工具名和参数我们这边的执行层收到请求后真正去调用天气接口、数据库、文件系统或者通知服务执行完再把结果作为一条消息丢回给模型模型基于真实结果继续回答或执行下一步。这个机制听起来朴素但它是所有 Agent 应用的地基。我见过不少团队花大量精力调 Prompt结果工具链路设计得一塌糊涂用户问一句话模型要么不调工具、要么调错工具、要么参数传得离谱。所以说tools 落地的第一步不是写工具本身而是先把模型怎么调、执行层怎么接、结果怎么回这条链路想清楚。1.2 工具落地的三条主线接入、编排、治理在 Hermes 项目里我倾向于把 tools 落地拆成三条主线后面所有讨论都沿着这三条线展开接入把一个普通函数变成模型可调用的接口核心是工具注册表和 Schema 设计解决的是模型能不能正确选中并调用它。编排多个工具组合起来完成复杂的任务比如先查日历再安排会议再发提醒解决的是模型怎么在不同工具之间做出决策。治理权限、超时、错误回传、日志和灰度解决的是工具被调用之后会不会出安全事故出了问题能不能查得到。这不是学院派的划分而是我在实际开发中总结出来的工作顺序。前两条决定 Agent 的上限第三条决定它能不能真正上线。很多时候团队卡住不是模型不够聪明而是三条线里有一条没打通。2. 工具注册与接口契约让大模型一眼看懂每个 tool2.1 一套统一的 Tool 接入协议Hermes 最开始犯过的一个错是每个开发者按自己的习惯暴露工具有人写一个 Flask 接口有人直接丢一个 Python 函数还有人把参数塞在请求头里。结果就是模型调用五花八门执行层根本没法统一解析。后来我们收敛成一套协议每个工具必须覆盖四部分名称、描述、参数 Schema、执行函数。拿一个最普通的天气查询工具举例它的注册信息长这样{ type: function, function: { name: get_weather, description: 查询指定城市的实时天气返回温度、湿度、风力和天气状况。当用户询问天气、是否适合户外活动、是否该带伞时使用。, parameters: { type: object, properties: { city: { type: string, description: 城市名称使用中文全称例如北京、上海。 }, date: { type: string, description: 查询日期格式为 YYYY-MM-DD默认今天。, enum: [今天, 明天, 昨天] } }, required: [city] } } }这段 JSON 是给模型看的使用说明书。执行函数则是给系统看的真正的实现它必须接收和 Schema 严格一致的参数并返回能被模型理解的结果文本。这里有个很关键的原则工具描述是写给模型看的不是写给开发看 API 文档。很多初学者习惯写调用云平台的 weather API 获取实时天气数据这样写模型很难建立触发关联。更好的做法是在 description 里写清楚什么时候该用它最好直接列举几类典型问法。测试下来这种描述方式能把工具选择的准确率提升不少。2.2 Schema 描述怎么写模型才不选错工具选错是 tools 落地最常见的翻车场景。用户说帮我算一下这个月报销总额模型不去调用计算器或表格工具反而调了数据库查询工具最后返回一堆原始数据。这类问题一半源自 Schema 写得不够明确。我总结了几个实战中验证有效的写法参数描述要带单位和边界。比如查询温度温度范围 -40 到 50 摄氏度就能避免模型填出离谱数值查询时间请使用北京时间格式 YYYY-MM-DD HH:mm能避开时区歧义。enum 和格式约束能省则省。能用枚举约束的不要交给模型自由发挥比如日期查询只有今天、明天、昨天三个选项就写死成 enum。模型在开放文本框里犯错的概率比在固定选项里高得多。布尔值参数是重灾区。有个工具的参数叫send_notification模型经常在该传true时传是或者把布尔值写进字符串。后来我们规定布尔参数必须在描述里写明true 表示发送false 表示不发送执行层再做一次防御性转换。2.3 参数类型与默认值陷阱还有一类坑发生在参数默认值上。最初我们在 Schema 里给date字段设了默认值今天但某些模型在用户没提到日期时并不会主动读取默认值而是自己编一个日期填进去——最常见的就是把下周三理解成明天然后填错。所以我的建议是Schema 里的默认值只服务于执行层兜底不能指望模型一定会遵守。不必要带默认值的参数一律设为 required让模型必须向用户确认。如果某个参数允许为空描述里要明确写当用户未提供该信息时传空字符串不要猜测。这一步看着琐碎但它直接决定工具返回的数据质量。参数错了后面所有流程都是错上加错。3. 从模型说要调到真的调起来中间这层执行链路3.1 Agent 主循环的三段式流程当模型返回一个tool_call之后真正的执行链路才开始。Hermes 的主循环可以简化为三段组装请求把历史消息、用户最新输入、所有已注册工具的定义一起发给模型。解析决策模型返回普通文本或者返回工具调用请求工具名 参数 JSON。执行回填校验参数、执行工具、把结果以tool角色的消息追加进消息列表再发给模型继续生成。这是一个标准的 while 循环伪代码如下messages [{role: system, content: SYSTEM_PROMPT}] messages.append({role: user, content: user_input}) for _ in range(MAX_ITERATIONS): response llm.chat(messagesmessages, toolsall_tool_definitions) if response.tool_calls is None: break for call in response.tool_calls: validated_args validate_with_schema(call.arguments) result execute_tool(call.function_name, validated_args) messages.append({ role: tool, tool_call_id: call.id, content: json.dumps(result, ensure_asciiFalse) }) final_answer response.content这里有两个容易被忽略的细节。第一MAX_ITERATIONS必须设置否则模型可能陷入调用工具→获得结果→再调用工具的死循环token 成本直接失控。第二每一轮工具结果都要和对应的tool_call_id严格绑定否则多工具并行时消息顺序一乱模型就懵了。3.2 解析与校验宁可拒绝不可瞎传模型输出的参数是 JSON 字符串但这一步远没有想象中顺利。实测中你会遇到花括号不闭合、字段名被模型改写、字符串里混入多余逗号、日期格式从YYYY-MM-DD变成2024年7月1日。所以我强烈建议所有工具在执行前必须过一遍 JSON Schema 校验而不是直接json.loads之后丢给函数。Hermes 现在采用两段式校验结构校验用解析器尝试解析失败则把错误信息回传给模型让它重新生成参数。不要自己去猜模型想表达什么猜错的代价往往更大。语义校验即使 JSON 合法还要检查字段是否在合理范围内比如日期是否在过去、邮箱是否符合基本格式。语义校验属于业务层逻辑通常写在执行函数内部。这一点我的态度是该拒绝时就拒绝。宁可让模型多来回一轮也不能传一个错参数把线上数据改了。3.3 权限、超时与错误回传的前置设计工具执行是 Agent 最危险的地方因为一旦放权模型就可能帮你发邮件、改订单、删文件。Hermes 的做法是在执行层做四件事超时控制每个工具设置独立超时时间网络请求类工具不超过 15 秒本地计算类工具不超过 5 秒。超时后返回工具执行超时给模型让它决定是重试还是告知用户。权限分级只读工具查询、检索默认放行写操作工具发消息、改数据必须经过二次确认高危操作删除、转账、删除数据库表默认直接禁止。错误标准化工具抛出的异常统一封装成 JSON包含error_code和error_message回传给模型。模型看到错误后可以自行修正参数重试或者坦诚地告诉用户暂时办不到。审计日志每次调用都记录调用方会话 ID、工具名、参数、执行结果和耗时。没有日志线上出了问题你连复现都无从下手。这一节看起来不性感但恰恰是这套执行链路决定了 Hermes 的 tools 能不能从演示环境走到生产环境。我在后面第 6 节还会展开讲几个真实踩坑案例。4. 第一批落地的 12 个工具按场景而不是按功能堆砌4.1 工具选型的思路先问用户高频要什么很多团队给 Agent 接工具时习惯按能力堆能接微信就接微信能连数据库就连数据库结果工具列表二十多个模型选择准确率直线下降。Hermes 第一批只做了 12 个原则很简单——只做用户真实高频请求里出现次数最多的那些场景。表里的前六个属于读写类负责从各种数据源拿信息工具名干什么用典型问法权限级别get_weather查询实时天气明天适合爬山吗只读get_current_time查询当前时间、日期、星期帮我定个后天上午十点的闹钟思路只读calculator数学计算、单位换算1340 的 15% 是多少只读query_database只读模式下查询业务数据库上个月销售总额是多少只读search_documents在内部知识库检索文档报销流程是什么只读fetch_webpage抓取指定网页并提取正文把这篇新闻摘要出来只读后六个属于动作类负责真正改变外部世界工具名干什么用典型问法权限级别send_email发送邮件给张三发一封会议纪要邮件确认后执行create_calendar_event创建日历日程帮我约周三下午的会议室确认后执行send_webhook触发公司内部自动化流程跑一遍每日数据巡检流程确认后执行write_file在指定沙箱目录写入文件把总结保存为 Markdown沙箱受限lookup_contact查询内部通讯录信息找一下王经理的电话只读translate_text中英互译把这段话翻译成英文发我只读注意这里有几个刻意为之的设计。send_email和create_calendar_event都设置成确认后执行因为写操作的错误代价远高于读操作write_file只允许写进预先划定的沙箱目录绝不开放全盘写权限。工具数量控制在 12 个也是为了让模型的工具选择面更聚焦实测工具定义总字符数控制在 8000 token 以内时模型选择准确率明显更高。4.2 每个工具上线前要过的四道检查工具写得差不多之后不要急着挂到正式环境。Hermes 现在每个工具上线前都要过四道检查每道检查我都踩过不止一次功能可用性检查直接调用执行函数确认输入输出符合预期。这一步能拦住接口参数写反权限配置错误这类低级问题。描述有效性检查把工具定义交给一个没有看过代码的同事让他假装自己是用户随机提几个问题看他觉得哪种问题应该触发这个工具。如果同事猜不到模型大概率也猜不到。参数边界检查用空参数、缺失必填项、极端值分别测一遍确认 Schema 校验能拦住非法输入且错误信息足够模型理解。安全兜底检查检查执行函数是否在权限范围内、是否记录日志、是否有超时保护。安全兜底不过工具直接打回。这套检查看起来不过是测试流程但它是功能上线和安全落地之间最重要的一道闸门。5. 多工具协作一个复合任务是靠什么拆开的5.1 用会议安排的例子拆解工具编排单工具调用只能解决单点问题Agent 真正体现价值的地方在于多工具协作。我以帮我安排下周三下午和客户的需求评审顺便提醒我提前一天准备材料为例演示 Hermes 是怎么拆解任务的。理想情况下模型会按如下顺序调用调用get_current_time确认今天是星期几推算出下周三的具体日期。调用query_database或专门的日历查询查看下周三下午是否有空档。调用create_calendar_event在选定的时间段创建客户需求评审日程。调用send_webhook或send_email在下周二上午设置一条提醒。每一步的产物都是下一步的输入。模型的思维链在这里变成工具调用链时间计算 → 可用性检查 → 写操作 → 通知操作。但现实往往没有这么顺利。你会发现模型在第一步就直接创建日程了完全跳过空闲检查或者在第三步才想起来还没确认客户是否有空。这就是为什么执行层需要把工具结果组织成带状态的上下文比如create_calendar_event的成功结果里带着事件 ID模型就知道下一步可以继续如果返回该时间段已有冲突日程模型就需要换一个时间重新调用。5.2 模型中途换参数的应对策略多工具协作里最常见的故障是参数执行到一半就不对了。比如模型第一次调用query_database查了销售数据获得结果后说让我把结果以表格形式展示于是又调用write_file但文件名参数却编了一个完全不存在的路径。这种问题没法靠 Prompt 完全消除Hermes 的应对策略是错误回传 有限重试把文件路径参数改成枚举只允许模型从几个预设目录里选堵住瞎编路径的入口。执行失败时把标准错误信息回传让模型修正参数后重试但重试次数上限为 2。2 次之后如果还在失败直接终止工具链把当前状态告诉用户并给出人工处理的建议。有些团队会让模型无限重试这既烧 token也让用户体验变差。我的经验是工具调用连续失败 2 次还查不出原因多半不是参数问题而是工具本身有 bug这时候应该让用户介入而不是让模型继续空转。5.3 什么时候该让 Agent停下来问人多工具协作还有一个反常识的设计原则不是所有信息都该靠模型猜。Hermes 里专门设了一个ask_user工具模型在以下情况必须调用它向用户确认缺少必要参数比如安排会议没提参会人时先去问这个会议需要邀请哪些人。存在歧义比如用户说把那个文件发我模型不知道那个指哪个文件问清楚再动作。将要执行高风险操作比如发送邮件、删除数据必须让用户输入一句明确确认语而不是只靠模型判断。这个设计在早期是被团队反对的觉得向用户提问会让 Agent 显得笨。但实际跑下来懂得在关键时刻停下来问人的 Agent完成任务的成功率反而更高因为它避免了基于错误假设一路滑向错误结论。多工具编排不是越快越好而是每一步都走得对才叫好。6. 落地一段时间之后的真实复盘那些坑我现在还在躲6.1 最容易被忽视的幻觉参数问题工具落地之后最大的坑不是工具不会调而是参数幻觉。有次用户让 Hermes 给某客户发一封邮件模型在参数里填了一个编造的邮箱地址执行层傻乎乎地发出去结果退信。从那以后我们把send_email的收件人参数改成枚举校验——必须先从lookup_contact查到真实邮箱然后才能选。这一步等于强制模型先查证再执行。类似的还有日期和时间。用户说这周五时模型经常不查当前日期就直接填一个固定日期而那个日期可能是上周五。我们的做法是所有涉及相对时间的问题模型必须先用get_current_time获取当前时间再计算。虽然每次多一次工具调用但换来的是日期不再翻车。6.2 工具评价别只看调没调对还要看不该调时没调很多团队给工具调用打的指标是调用成功率但我觉得更重要的两个数是工具选择召回率和精确率。什么意思我们希望用户问北京天气时模型调用了get_weather这是召回命中但同时也要统计用户问今天中午吃什么时模型有没有错误地调用查询工具——如果调了说明它过度使用工具精确率被拉低。Hermes 的做法是维护一份约 200 条真实问题的评测集每条标注应该调用哪个工具或不应该调用任何工具。每次升级模型或修改工具描述后跑一遍评测集对比工具选择的变化。这套小型评测集帮我们拦住了很多感觉上变聪明了实际选择更乱的回归问题成本不高效果却很明显。6.3 日志、灰度与兜底的日常最后说三点日常心得。日志不能只记调用了什么工具要把完整的输入、输出、异常、耗时都记下来。有一次用户反馈日程建重复了最后查日志发现是模型在同一轮里连续调用了两次create_calendar_event参数一模一样。这种问题只有靠日志才能定位。灰度也很重要新工具上线前几周只对内部白名单用户开放观察工具调用频率和错误率稳定之后再全量放开。兜底则是给每个工具配一个失败文案让模型能友好地告诉用户这一步没成功你可以手动操作或者换个方式重试。现在回头看Hermes 的 tools 从能用、到好用、到敢真正交到用户手里最值钱的经验就一句工具链路的每一条细节都是靠真实调用喂出来的而不是靠设计稿推演出来的。你给模型定义的每一个参数、写的每一句描述、设的每一个超时时间都会在某个深夜以你意想不到的方式回来找你。所以别怕工具少先把两三个工具扎扎实实地服务好高频场景再慢慢加这条路比一口气接二十个工具要稳得多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑