1. 智能体协作的通信困境:REST轮询在agent时代为何撑不住
1.1 从单体AI到多agent矩阵:西南总部部署遇到的真实场景
西南总部的智能体平台并不是一开始就是多agent架构的。最早跑的是单体的任务处理网关,一个入口进来,一个模型服务出去,中间靠HTTP调用串起来,虽然谈不上高性能,但胜在简单。真正的转折点是业务侧开始要求"智能体可以自主决策、动态编排任务、跨节点协作",单体那套同步调用链彻底扛不住了——指挥官节点要同时盯着十几个调度官节点,调度官又要向下管理几十个工作节点,每个节点随时都在产生状态变更、进度回报、心跳消息。这时候最直观的感受是:消息太多了,而且是双向的、不定时的、持续涌过来的。
那段时间我一直在检查当时的通信链路,发现问题非常典型:任务下发靠轮询,状态上报靠轮询,连心跳也是轮询。所有的"实时"都是假实时,最坏情况下一个状态变更要等3秒才能被指挥层感知。对智能体决策这种场景来说,3秒的延迟意味着协调层的判断永远慢半拍,任务调度基本靠猜。
1.2 REST轮询的三个致命伤
把REST轮询拆开看,有三个问题是架构层面绕不过去的:
第一,空转消耗与响应延迟完全不可兼得。轮询间隔设短了,调度官接口每秒被请求几十次,大量请求返回的是"没变化"的空响应,CPU和网络带宽全浪费在无效载荷上;轮询间隔设长了,事件从发生到被感知的时间被无限拉长,指挥官永远得不到足够新鲜的状态。
第二,REST的请求-响应模型天然是一问一答,无法做服务端主动推送。智能体运行过程中会发生大量"主推"消息:某个调度官突然发现自己负责的队列积压了,需要向指挥官申请临时扩容;某个子任务执行超时要被强制中断,需要立刻通知上游。这些问题用轮询表达起来极其别扭,基本要靠"客户端频繁拉取+本地diff"来模拟推送,复杂度全堆在客户端。
第三,长连接场景下的资源利用率极低。每一条HTTP请求都要走一遍完整的头部解析、路由匹配、序列化包装,在高频小消息场景下,报文头部的开销甚至可能比业务数据还大。REST + JSON的组合在低频调用时很舒服,但高频、小包、乱序到达的协作场景里,这种开销是不可接受的。
1.3 为什么是gRPC流式:候选协议对比
在决定改造方案时,我认真比较过几类技术:WebSocket、消息队列(MQ)、REST + SSE、gRPC。每一类都有自己的拥护者,但落到"智能体指挥官与调度官"这个具体场景,gRPC流式的优势非常突出。
WebSocket做双向通信没问题,但它只解决传输通道,不解决消息契约。消息格式还得自己定,序列化方案还得自己选,重连逻辑、流控逻辑也全部要手写,等于把一个应用层协议的所有脏活累活全扛在自己肩上。
MQ(比如Kafka、RocketMQ)适合解耦和削峰,但它引入了一个中间跳板。指挥官下发任务给调度官,中间隔了一层Broker,端到端延迟即便在最优情况下也要多一跳;而且对"指挥官实时感知调度官状态"这种强同步需求,MQ的异步模型反而增加了心智负担。我们需要的是直接的双向通道,而不是中转站。
REST + SSE 能解决服务端到客户端的单向下行推送,但上行还是只能靠普通POST请求。真实场景中,指挥官和调度官之间是双向高频交互:指挥官下发任务,调度官回报进度,指挥官又要根据进度动态调整策略。这种"来回拉锯"型交互,只有双向流能干净表达。
gRPC流式最终胜出,核心就三点:基于HTTP/2的多路复用让多个逻辑流共享一条TCP连接;Protobuf序列化让消息体积和解析开销都压到极低;语言无关的契约定义让Commander和Dispatcher可以用不同技术栈实现。这三点正好对应智能体协作场景中"高频、多流、异构"三个关键特征。
| 方案 | 双向实时 | 消息契约 | 序列化开销 | 端到端延迟 | 运维复杂度 |
|---|---|---|---|---|---|
| REST轮询 | 否 | 手写 | 高(JSON) | 高 | 低 |
| WebSocket | 是 | 手写 | 中(可选) | 低 | 中 |
| MQ | 伪实时 | 需额外Schema | 中 | 中+一跳 | 高 |
| SSE+REST | 单向 | 手写 | 中 | 中 | 中 |
| gRPC流式 | 是 | Protobuf天然支持 | 低(Binary) | 低 | 低 |
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 指挥官与调度官的职责边界:先画清楚架构图,再动Protobuf
2.1 指挥官的"大脑"定位:任务拆解与决策分发
在动手写proto文件之前,我花了两天时间只做一件事:把指挥官和调度官的职责边界用文字写清楚。后来发现这一步省掉了后面无数次返工。
指挥官节点(Commander)是整个agent集群的决策中心。它接收外部业务请求,把一个大目标拆解成若干个可执行的子任务,决定每个子任务应该交给哪个调度官,并根据调度官回报的实时状态动态调整策略。它需要知道的信息是"全局状态"——哪个调度官当前负载高、哪个调度官手头有空闲、哪个任务卡住了需要干预。指挥官的通信特征是:下行下发指令较多,上行接收全局状态,消息频率高但单条消息很小。
设计上,指挥官是发起方,它主动建立与调度官的流式连接。这符合控制面的角色定位——谁负责决策,谁掌握连接主动权。
2.2 调度官的"四肢"定位:资源调度与结果回收
调度官节点(Dispatcher)是执行层和决策层之间的缓冲带。它一方面接收指挥官下发的任务指令,另一方面管理着自己辖区内的具体工作节点(Worker),负责资源分配、任务编排、进度上报和结果回收。
调度官是一个"承上启下"的角色,它的通信特征非常特殊:既要接收来自指挥官的指令流,又要随时上报任务状态和资源情况。它不是一个单纯的执行器,而是一个有自主判断能力的二级调度器——比如某个任务派给哪个Worker执行、资源不足时先排队还是先拒绝,这些决策权在调度官手里。
这意味着调度官和指挥官之间需要的不是"命令-响应"的单向链路,而是随时双向发起消息的信道。我把这种交互定义为"协商式协作":指挥官可以主动发任务,调度官也可以主动发告警或申请。双向流(bidi streaming)是唯一能干净表达这种关系的通信模型。
2.3 消息方向的确定:聊聊"指挥官主动建连"还是"双方互连"
设计过程中有一个争论比较大的点:
