让 Agent 真的能干活:把操作封成 webhook,而不是给它更长的提示词

2026-09-15 AgentMCP工作流工程实践

很多人第一次把 Agent 接进业务,都会撞上同一堵墙:它能把方案讲得头头是道,但真要它去发一封邮件、建一张工单、改一条数据库记录,就开始原地打转——要么编造一个「已经完成」,要么把参数拼错三次。

问题很少出在模型身上。出在你只给了它嘴,没给它手。

先分清两种「工具」

给 Agent 接能力,本质上有两条路:

  • 让它直接碰你的系统:把数据库账号、API 密钥塞进上下文,让它自己写 SQL、自己拼请求。灵活,但出事就是大事。
  • 你先把操作封好,只给它一个入口:每个动作是一个独立的 HTTP 端点,输入有 schema,输出有结构,权限你在服务端控制。

第一种适合玩具,第二种才能上线。差别在于:第二种的每个动作都是可命名、可鉴权、可限流、可回滚的,而第一种出问题时你连日志都看不懂。

动作层长什么样

一个能被 Agent 稳定调用的动作,至少要有这四样东西:

1. 一个动词化的名字 create_refund_tickethandle_request 好。模型是靠名字和描述来选工具的,含糊的命名会直接导致选错。

2. 一份输入输出 schema 别让模型自由发挥。写清楚字段、类型、必填、枚举值。返回也一样——永远返回结构化结果,哪怕出错也返回 {ok: false, reason: "..."},而不是抛一个 500。

3. 一次只做一件事 我吃过亏:一个「处理客户请求」的端点内部会判断意图、发邮件、改状态,Agent 一次误调用就群发了。拆成 send_emailupdate_ticket_statuscreate_task 三个之后,误操作的爆炸半径小了一个量级。

4. 写在服务端的权限与限额 Webhook 只认一个专用 token,能做什么在服务端写死。Agent 就算被提示注入骗了,攻击者也只能在限额内调这几个端点。

写操作要有两步

只读动作(查订单、搜文档、读报表)可以直接让 Agent 调。写动作(发钱、发信、改数据)我一律加一层确认:

  1. Agent 调用带 dry_run: true 的动作,服务端返回「我准备做什么」的预览,不落库;
  2. 人(或一条规则)确认后,Agent 再带同一个 request_id 调一次真正执行。

request_id 在这里是关键:服务端用它做幂等,重复调用同一 id 只会执行一次。这条规则救过我两次——Agent 超时重试,如果没有幂等,客户会收到两封一样的邮件。

一个能直接抄的结构

我现在的默认做法是:n8n(或任何工作流引擎)负责把内部系统拼成动作,每个动作暴露成一个 webhook,Agent 侧只维护一份工具清单:

意图层   模型决定「该做什么」
   ↓  传入结构化参数
动作层   webhook:校验 → 幂等检查 → 执行 → 返回结构化结果

审计层   落一条日志:谁触发的、参数是什么、改了什么、能不能回滚

三层分开的好处是,任何一层都能单独换。模型换成更强的,动作层不动;动作层从 n8n 换成自己写的服务,模型侧也不用改。

什么时候别这么干

  • 动作只有一两个、而且不会变:直接写个脚本让 Agent 调就够了,上工作流引擎是给自己找事。
  • 延迟敏感:多一跳 HTTP 就多几十毫秒,实时对话里能感觉到。
  • 你需要事务性:HTTP 调用之间没有事务,做不到「要么全成功要么全回滚」。这种场景老实写成一个服务端函数,让 Agent 一次调完。

最后一句

Agent 的能力上限,是被你提供的动作层的质量锁死的。与其花时间把提示词从 300 字改到 800 字,不如把三个动作的接口写清楚——前者的收益会迅速饱和,后者的收益是线性的。