Webhook触发Agent:别把轮询直接换成事件
很多“自动化 Agent”第一版都是轮询:
每5分钟查一次邮箱
每10分钟查一次PR
每小时看一次Slack
简单,但它有三个天然问题:
延迟
空转成本
重复处理
ChatGPT Work 8 月 25 日开始支持基于应用更新触发 Scheduled Task,目前公开支持:
新Gmail邮件
Slack频道新消息
GitHub PR活动
这种能力真正值得借鉴的,不是“有 Webhook 了”,而是 Agent 的运行模型开始从:
Cron
转向:
Event-driven
但如果只是把轮询改成 Webhook Endpoint,生产问题不会自动消失。
第一个问题:Webhook几乎都不是Exactly Once
外部平台通常保证的是:
At-least-once
同一个事件可能送两次。
原因很多:
你的服务超时
ACK丢了
平台重试
网络抖动
所以第一层必须是:
Inbox + Dedupe
一个最小Inbox
create table webhook_inbox (
provider varchar(32) not null,
delivery_id varchar(128) not null,
event_type varchar(64) not null,
payload_hash varchar(64) not null,
received_at timestamptz not null,
processed_at timestamptz,
primary key(provider, delivery_id)
);
处理:
INSERT成功
→第一次
主键冲突
→重复事件
→直接ACK
不要把“Agent有没有处理过”放进模型记忆里判断。
第二个问题:事件可能乱序
GitHub PR:
opened
synchronize
review_submitted
closed
如果 closed 先到,review_submitted 后到,不能因为后到的 Review 再把任务重新激活。
所以事件对象最好带:
resource_version
event_time
sequence
如果 Provider 没 Sequence,就自己按资源状态 Reconcile。
不要把Webhook Payload当Source of Truth
Webhook 只应该理解成:
某个资源可能发生了变化
真正执行前重新读取当前资源。
例如:
收到PR opened
↓
读取当前PR
↓
发现已经closed
↓
不启动Review
这是:
Event Notification
+
State Reconciliation
第三个问题:事件风暴
Slack 一个热门频道:
1分钟50条消息
如果每条都启动一个 Agent Run:
50个Run
非常浪费。
很多事件要做:
Debounce
例如:
同一频道
60秒窗口
合成一个任务
Debounce Key
public record EventAggregationKey(
String provider,
String resourceId,
String taskType) {
}
例如:
slack
channel-18
feedback-summary
60 秒内聚合。
GitHub PR适合Coalesce
一个开发者连续 Push 4 次:
SHA1
SHA2
SHA3
SHA4
不用跑 4 次完整 Review。
可以只保:
latest_head_sha
旧 Run 如果还没开始:
取消
已经开始:
标记STALE
第四个问题:触发不等于有权执行
OpenAI 对 webhook task 的公开说明里明确写到:
需要批准的Action
仍然会暂停等待用户Review
这是非常正确的边界。
Webhook 只说明:
条件发生了
不代表:
所有副作用自动授权
例如:
客户发邮件
→ Agent总结
可以自动。
但:
客户发邮件
→ Agent自动退款
就不是同一级别。
事件驱动Agent至少拆两步
Trigger Authorization
+
Action Authorization
Trigger Authorization:
这个事件能不能启动这个任务?
Action Authorization:
这个Run能不能执行当前副作用?
不要合成一个开关。
一个Event Policy
trigger:
provider: github
event: pull_request
action:
- opened
- synchronize
resource:
repository: org/service-a
task:
type: code-review
Action Policy:
capabilities:
- repo.read
- checks.read
forbidden:
- merge
- deploy
Webhook 触发后仍然只有只读权限。
第五个问题:无限循环触发
最经典:
Agent收到PR更新
↓
Agent写一条Comment
↓
Comment触发Webhook
↓
Agent再次运行
↓
再写Comment
形成:
Automation Loop
必须识别:
actor
source_run_id
automation_marker
一个Loop Guard
if (event.actor().isAutomation()
&& event.metadata()
.containsKey("source_run_id")) {
return IGNORE;
}
更稳的方式是每个外部写操作附带:
automation_origin
后续事件再识别。
第六个问题:共享任务不是共享Credential
ChatGPT Work 现在还支持分享 Scheduled Task:接收者可以查看、修改说明,并使用自己的连接创建独立副本。
这个设计很值得企业内部照搬。
分享:
Workflow Definition
不应该分享:
OAuth Token
Connected Account
Resource Permission
所以模板对象:
public record SharedAutomationTemplate(
String templateId,
String promptTemplate,
TriggerDefinition trigger,
String owner,
int version) {
}
实例:
public record AutomationInstance(
String instanceId,
String templateId,
String subjectId,
String connectionId,
InstanceStatus status) {
}
模板共享,Credential 不共享。
第七个问题:事件失败以后怎么补
Webhook 处理链:
Receive
→ Inbox
→ Normalize
→ Policy
→ Task
→ Agent
中间任何一步失败,都不能靠:
等Provider再发一次
应该有:
Dead Letter
+
Replay
Event State
public enum EventState {
RECEIVED,
NORMALIZED,
ACCEPTED,
DISPATCHED,
COMPLETED,
REJECTED,
FAILED
}
每一步可重放。
Replay不能重新执行副作用
如果事件对应任务已经:
发送过邮件
再 Replay 必须先查:
Side Effect Ledger
否则事件系统的 Replay 会和 Agent Retry 叠加成重复副作用。
第八个问题:同一资源的并发
两个 GitHub 事件同时到:
PR synchronize
PR review_submitted
同时启动两个 Agent,可能各自基于不同 SHA。
所以资源级别可以有:
Partition Key
例如:
github:repo:pr-182
同一 Partition 保序处理。
不同 PR:
pr-182
pr-183
pr-184
仍然可以并发。
第九个问题:事件要带快照还是只带ID
不要把完整敏感邮件正文原样复制到 Kafka、日志和 DLQ。
更好的 Event Envelope:
{
"provider": "gmail",
"event_id": "evt-912",
"resource_ref": "message/abc",
"subject_id": "user-82",
"received_at": "..."
}
真正正文需要时再通过授权 Connector 读取。
这样减少 PII 扩散。
第十个问题:任务预算
Webhook 很容易让成本从:
每天固定24次轮询
变成:
一天5000个真实事件
所以每个自动化需要:
Daily Run Limit
Cost Limit
Burst Limit
例如:
budget:
max_runs_per_hour: 20
max_runs_per_day: 100
max_cost_per_day_usd: 10
达到阈值:
暂停新Run
聚合
或通知Owner
一个完整事件架构
Provider Webhook
↓
Signature Verification
↓
Inbox/Dedupe
↓
Normalizer
↓
Partition
↓
Debounce/Coalesce
↓
Trigger Policy
↓
Task Dispatcher
↓
Agent Run
↓
Action Policy
↓
Side Effect Ledger
这比:
Webhook
→直接LLM
多很多层。
但真正稳定的自动化基本都需要这些层。
什么时候轮询反而更好
Webhook 不是永远优于 Polling。
如果数据源:
没有稳定Webhook
事件量极低
状态不敏感
每天轮询一次更简单。
例如:
每天8:00拉昨日数据
没必要为它建复杂事件总线。
Webhook 让 Agent 从“定时检查”进入“变化就执行”。
这会显著降低空转和响应延迟,但同时把分布式事件系统里的老问题全部带进来:
重复
乱序
风暴
循环
并发
重放
权限
所以最不建议的做法就是:
把Cron删掉
加一个Webhook
然后直接启动Agent
真正应该迁移的是整个执行模型:
Polling Task
→ Event Inbox
→ Policy
→ Idempotent Agent Run
只要这三层建立起来,Webhook 才是在减少自动化成本,而不是把不确定性放大。
更多企业级 AI 应用、Agent、RAG 与模型工程化内容,我会继续整理在 智元界:
https://www.zyentor.com/