AWS 开源一个借鉴 Rust 异步思想的有趣的 Agent

· 2026-11-03 17:00 · 14 阅读

原创 张汉东 2026-11-03 17:00 美国

AWS 会玩,这是借鉴了 Rust 异步的思想,把 Agent 当成"无栈协程"来玩呢。。。

Rust 的 async fn 会被编译器改写成一个状态机。挂起时只保存继续执行所需的最少数据(叫"continuation"),不用保留整个调用栈。

类比到Agent 调用工具的那一刻,对话历史本身就是它的全部 continuation。只要把历史存下来,内存里的东西就能全部丢掉,之后任何一个进程都可以把它恢复过来。

具体做法是把 Agent 的执行切成一个个有界的"切片"(slice):

读出历史 → 调一次模型 → 保存输出 → 最多发出一次工具调用 → 切片结束。

工具调用就相当于 await 点,切片永远不会阻塞等待。工具结果、用户消息等所有能唤醒 Agent 的东西,都是投进输入队列的一条消息,来了就启动下一个切片。

工具的写法也因此不一样:execute 只负责启动任务然后马上返回,任务完成后再自己把结果投回 Agent 的输入队列。所以工具跑 100 毫秒还是 3 天,代码完全一样。

这样带来几个特性:

1. 并发量极高 调度方式类似 Actor 模型:每个对话线程有自己的队列,线程之间不共享可变状态,只靠发消息通信。同一线程内的消息严格按顺序处理,不同线程之间并行。

基准测试结果是,单个 CPU 线程跑 8 万个 Agent、共 320 万次模型调用,每个空闲 Agent 大约占 100 KB 内存。换算下来,8 GB 的树莓派 5 能放下约 7.5 万个 Agent。

2. 能直接跑在 Serverless 上 "step 模式"下不保留任何常驻任务,每次消息投递就调用一次 step,处理完进程即可退出。

参考部署用的是 SQS FIFO 队列(每个线程一个 MessageGroupId)、Lambda、Aurora DSQL(存历史)、DynamoDB(去重)、EventBridge Scheduler(定时器)。

结果就是:一个等了三天 CI 的 Agent,等待期间完全不消耗计算资源。

3. 合成工具调用(Synthetic tool calls):我觉得这是最巧妙的设计。

问题在于:现在的模型 API 规定每个工具调用只能对应一个结果,没有办法表示"后来又来了一个事件"。

作者比较了几种方案:

- 在原工具调用后面追加第二个结果 → 违反规则,API 会报错

- 把事件当成用户消息塞进去 → 模型分不清这是用户说的还是工具报告的

- 把事件放进系统提示 → 丢失了时间顺序

最后的做法是伪造一次模型其实并没有发起的工具调用 receive_event__injected(...),参数里写明是哪次原始调用产生的这个事件,再接上一个包含事件内容的结果。

这样能直接复用模型在后训练阶段学到的"读工具结果"能力。这个工具名不会提供给模型调用,模型如果真去调也会被拒绝。

同一套机制还顺带解决了父子 Agent 之间的通信:spawn_thread 就像一次订阅,子 Agent 的汇报以事件的形式回到父 Agent。不需要单独再做一套消息系统。

另外还有一个处理上下文膨胀的设计:

事件默认交给一个临时子线程处理。子线程继承父线程的上下文,自己判断事件是否相关,不相关就直接关闭,父线程的历史不会变。只有标记为"associative"的事件(比如自己后台启动的测试跑出来的输出)才会直接写进父线程的历史。

我的看法,有价值的地方:

抽象很干净。"对话历史就是 continuation"这个观察很简单,但推导出了一整套架构。

思想本身并不新,可以看作是持久化执行(Temporal、Restate 这类)加上 Actor 模型(Erlang/Akka)在 Agent 场景里的应用。

真正新的是合成工具调用:它让异步事件能放进模型 API 已有的对话格式里,这一点对其他框架也有参考价值。

但是,

"7.5 万 Agent 跑在树莓派上"主要是营销亮点,基准测试跑的是模拟模型。

另外,每个切片都要从存储里读完整历史再写回去。 历史一长,存储的 I/O 和延迟就会成为新的成本,作者没有给出这方面的数据。

这算是 AWS 的研究项目,早期研究版本,核心运行时本身不绑定 AWS,参考部署之所以全用 AWS 服务,是因为作者团队自己就在 AWS。

项目地址 https://github.com/hydro-project/infinity

跳转微信打开

本文转载自微信公众号「觉学社」,查看原文。版权归原作者所有,如有侵权请联系删除。