异步调用模式:Serverless 流水线中调用 Agent(避免空闲计算成本)¶
📊 Level ⭐⭐⭐ | 11.4KB
Background:本文基于 AWS ML Blog 对 Amazon Bedrock AgentCore 异步调用模式的系统分析建立。核心问题是通用且可迁移的——Agent 在回答前会"思考"一段时间,而阻塞式调用会让调用方在等待期间持续付费。文章给出三种异步模式(task-token 回调 / 直接服务集成 / durable function)及其与阻塞反模式的成本对比。
核心问题:Agent 的"思考时间"让阻塞调用浪费计算¶
Agent 与普通流水线步骤不同:它在回答前会思考一段时间(取决于 prompt、模型、文档),延迟很少是即时的。最常见的实现是一个计算服务(如 Lambda)调用 Agent 并等待响应——但函数在等待时什么都没做,却仍在运行并被按秒计费。
关键洞察在于成本落在调用方而非 Agent 侧:Agent 运行时(AgentCore runtime)按消耗计费,Agent 空闲时(等待 LLM 生成、等待 tool/MCP 调用返回)只收内存费不收 CPU 费;而发出同步调用的 Lambda/容器/EC2 实例则全程阻塞,持有并支付完整计算配额直到 Agent 响应。因此浪费不在 Agent 侧,而在"开着连接空等的调用方"。
一个 Agent 可以通过 return-of-control action 选择如何返回:收到 Step Functions task token 就唤醒该执行;收到 durable-function callback ID 就唤醒 durable function;两者都没有就同步内联返回。这意味着换编排模式无需改 Agent。
三种异步模式 vs 阻塞反模式¶
| 阻塞(反模式) | Pattern 1: Task-token | Pattern 2: 直接集成 | Pattern 3: Durable function | |
|---|---|---|---|---|
| 编排器 | Step Functions | Step Functions | Step Functions | Lambda(代码) |
| 路径中 Lambda | 有,全程存活计费 | 有,但提前返回 | 无 | durable function(挂起) |
| 等待期空闲 Lambda 计算 | 付全部等待 | 无 | 无 | 无 |
| 调用点自定义代码 | 有 | 有 | 有限(状态转换) | 有 |
| 调用方与 Agent 解耦 | 否 | 是 | 否 | 是 |
| 相对复杂度 | 最低 | 较高(token 与回调 IAM) | 最低 | 中等(checkpoint/replay) |
Pattern 1: Task-token 回调(带 dispatcher 函数)¶
Step Functions 用 waitForTaskToken 集成调用 Lambda,传入 task token 后暂停执行。函数用 token 启动 Agent 后几秒内返回;执行保持暂停(不计费),直到 Agent 调 SendTaskSuccess 用该 token 唤醒。需设 TimeoutSeconds + HeartbeatSeconds 作为安全网,静默 Agent 会以 States.Timeout 干净失败而非永久暂停。
Pattern 2: 直接服务集成¶
不需要调用点自定义代码时,可完全移除 Lambda。Step Functions 通过 AWS SDK 服务集成直接调 AgentCore,Agent 响应直接流入下一状态,Validate 分支变成单个 Task state。无 Lambda 在路径中,也就没有空闲 Lambda 计算可付;Standard workflow 按状态转换计费而非按等待时长。
Pattern 3: Lambda durable function¶
想用代码而非状态机表达编排时,durable function 提供相同成本行为:流水线阶段变成 context.step,并行变成 context.parallel,等待 Agent 变成 context.waitForCallback。等待期间函数挂起不计费,Agent 用 SendDurableExecutionCallbackSuccess 唤醒。单函数持有整条流水线,但只按挂起之间的短执行突发计费。
成本测量:state 活跃 vs 函数计费¶
文章强调重点不是具体数字,而是两个值的关系:task-token 模式下 Validate state 活跃时长 vs dispatcher 函数实际计费时长。单次运行示例:state 活跃 19.6s,但函数只计费 4.8s——中间 ~14.8s 是等待时间,期间没有 Lambda 函数在运行。总流水线时长不是有用的对比指标(被 Agent 自身推理时间主导,每次运行都不同),有意义的差异是"等待期间你为多少计算付费"。
最佳实践¶
- 防范永不回答的 Agent:给每个
waitForTaskTokenstate 设TimeoutSeconds,避免执行无限挂起;Agent 发心跳时也设HeartbeatSeconds加速死 Agent 检测,错误路由到失败或人工审查路径。 - 重试用稳定 session ID:把
sessionId设为从执行上下文派生的值(如 Step Functions 执行名),让重试恢复同一 Agent 会话而非从头开始。 - 开启 X-Ray:精确显示 Agent 思考用了多久 vs 调用方等待了多久,确认模式确实在间隙释放了计算。
- dispatcher 函数按速度配,不按 Agent 负载配:只序列化请求并调用端点,256MB 内存 + 30s 超时通常足够,重活都在 Agent 侧。
选择模式¶
- 阻塞反模式:适合原型、短 Agent(调用方成本 = 完整 Agent 处理时间)。
- Pattern 1 task-token:需要自定义 pre/post 处理逻辑时(调用方成本 = 仅几秒 dispatch)。
- Pattern 2 直接集成:纯编排、无自定义代码(调用方成本 = 零,无 Lambda)。
- Pattern 3 durable function:希望在一个函数里表达复杂异步工作流时。
核心结论:把 Agent 放进流水线是简单部分,经济地调用它才是原型与生产设计的区别。一个阻塞在 Agent 上的 Lambda 实现简单但悄悄昂贵——大部分计费时间都在空等。
深度分析¶
成本归属的不对称性:账单落在调用方一侧¶
AgentCore runtime 是消耗型计费:Agent 等待 LLM 生成、等待 tool/MCP 返回时只收内存费、不收 CPU 费;而发出同步调用的 Lambda/容器/EC2 全程持有并支付完整计算配额。这种计费不对称决定了"浪费"永远落在空等的一侧——调用方的计费时长约等于 Agent 的整个处理时间,Agent 侧却对思考时间几乎无感。AI 成本优化框架中"先定位谁在付费、再谈优化"的思路在此直接适用。
换编排不用改 Agent:return-of-control 作为解耦契约¶
三种异步模式能共享同一个 Agent,靠的是 Agent 内部的 return-of-control action:收到 task token 就唤醒 Step Functions 执行,收到 durable-function callback ID 就唤醒 durable function,两者都没有就同步内联返回。这是个干净的契约设计——调用协议由 Agent 运行时探测而非部署时绑定,编排层可独立演进(阻塞原型 → task-token → 直接集成)而 Agent 零改动、零重部署。
度量陷阱:总时长不是指标,billed duration 才是¶
总流水线时长被 Agent 自身推理时间主导、每次运行都不同,用它比较模式毫无意义;有意义的只有两个值的关系——task-token 模式下 Validate state 活跃 19.6s,dispatcher 函数只计费 4.8s,中间 ~14.8s 等待期没有任何 Lambda 在运行。验证很具体:Step Functions 事件历史里 TaskSubmitted 与 TaskSucceeded 的间隔就是释放掉的等待期,X-Ray 把"Agent 思考 vs 调用方等待"直接可视化。
复杂度与成本的真实权衡¶
三种模式都消除空闲计算,但代价分布不同:Pattern 2(直接集成)最简单、调用方成本为零,但业务逻辑只能写进 ASL intrinsic functions;Pattern 1/3 保留自定义逻辑,代价分别是 token/callback IAM 配置和 checkpoint/replay 心智模型。模式选择的实质不是"哪个省成本"(三个都省),而是业务逻辑住在哪一层——Lambda、状态机定义、还是单个 durable function 的顺序代码。
实践启示¶
- 改造前先算一笔"空等账":用 X-Ray 或 CloudWatch REPORT 读出阻塞 Lambda 的 billed duration,对照 Agent 实际处理时长——差值就是切换异步模式能省下的部分,也是证明改造值得做的最直接证据。
- 把
sessionId派生自执行上下文(如 Step Functions 执行名)而非随机生成:重试时恢复同一 Agent 会话,避免重复付费重新推理。 - 每个
waitForTaskTokenstate 必须配TimeoutSeconds(Agent 发心跳则加HeartbeatSeconds):静默 Agent 应以States.Timeout干净失败并路由到失败/人工审查路径,而非永久挂起。 - dispatcher 按速度配,不按 Agent 负载配:它只序列化并发起调用,256MB + 30s 超时通常足够——过度配置是把阻塞模式的容量思维带进了异步模式。
- 优先考虑 Pattern 2 直接集成:只要不需要调用点自定义代码,删掉 dispatcher 让 Step Functions 直接调 AgentCore,维护面最小、调用方成本恰好为零;确有 pre/post 逻辑才引入 Lambda。
- 总时长不是验收指标:用"state 活跃时长 vs 函数计费时长"的比值验收;文章的 19.6s/4.8s 只是单次运行的关系示例,须在自己的工作负载上复测。
相关实体¶
- AgentCore Managed Harness — AgentCore 平台(Harness Engineering)本身
- AgentCore Web Search — AgentCore 的 Web 搜索 grounding 能力
- Serverless A2A Gateway — Agent 间通信网关
- Bedrock Serverless Async Inference — 同族的异步推理模式(SQS+Lambda)
→ 原文存档