很多人理解 Agent 协作时,只想到了“对话”。
但生产级协作远不只是对话,它本质上是通信系统。
只要系统开始在模型、工具、前端、后端、多个 agent、外部服务和人工审批之间传递信息,它面对的问题就不再是“说什么”这么简单,而是“以什么格式传、传到哪里、谁能读、谁能写、消息是否有顺序、断线后如何恢复、长任务如何继续、事件来了之后谁来响应”。这些都属于通信边界。
所以,本章的第一条结论应该是:
Agent 系统里的通信,首先是协议问题,其次才是语言问题。如果协议没有被设计清楚,再聪明的模型也只能在模糊边界里猜测系统意图;如果协议足够清楚,即便模型局部犯错,系统也至少还能靠明确的消息格式、事件语义和状态恢复机制维持基本秩序。
这也是为什么今天的 Agent 平台越来越少直接暴露“一个 prompt 进,一个答案出”的单点接口,而越来越多地暴露结构化消息对象、事件流、任务对象、流式增量、tool call item、artifact、checkpoint 和 webhook。因为真实系统已经知道:文本只是其中一种载荷,通信本身还有更重要的部分,那就是消息语义。
一旦进入系统工程,通信边界至少包括六个问题。
第一,消息格式是什么。
第二,消息里必须包含哪些字段。
第三,同步还是异步。
第四,请求响应还是事件驱动。
第五,是否支持流式。
第六,是否支持中断和恢复。
这六个问题只要有一个模糊,Agent 系统就会出现看似偶发、实则结构性的故障。例如消息里没有稳定标识,就无法关联工具调用和工具结果;缺少版本与能力声明,就无法做跨组件互通;没有恢复语义,长任务一旦断线就只能重来;没有事件分型,流式输出就会退化成无法可靠消费的文本碎片。
所以,本章真正要讨论的,不是“消息怎么发出去”,而是“系统把什么当作正式消息对象”。
现代 Agent 协议已经不再满足于一段字符串。今天的主流官方接口普遍把输入输出做成带层次的结构:message、role、content part、tool call、tool result、response item、task、artifact、event、session、conversation。也就是说,系统不再把一切都混进一段自然语言里,而是把不同类型的通信对象分开表示。
这非常关键。因为只要通信对象被分开,系统才有可能在运行时正确处理它们。文本增量是文本增量,工具参数是工具参数,状态更新是状态更新,完成信号是完成信号,错误事件是错误事件。没有这种分型,所谓“流式 Agent”其实只是把各种不同语义的内容揉成一条不断滚动的字符串。
于是,理解通信边界时,第一个要区分的就是两种基本交互模型。
第一种,是请求-响应。
客户端发一个请求,服务端返回一个结果。这是最熟悉、也最容易理解的模型。它适合短任务、单步调用、明确输入输出的场景。很多生成式 API 最初都是这种形态。
第二种,是事件驱动。
在这种模型里,系统不再只是一来一回,而是围绕会话、任务或连接持续交换事件。客户端发起一个动作,服务端不断回传不同类型的中间事件;或者外部系统通过 webhook、外部事件、sideband control、回调、队列消息把新信息推进来,系统再继续执行。到了这里,通信不再是“一次调用”,而是“一个在时间中演进的协议过程”。
流式交互正是第二种模型最直观的表现。
很多人以为流式只是“让文本边生成边显示”,但真实平台里,流式早就不是单纯的 token stream 了。它通常是一组有类型的事件流:开始事件、内容块开始、内容增量、工具参数增量、工具调用、消息完成、错误事件、会话更新、响应结束。也就是说,流式真正流出的不是文本,而是状态变化。
这件事的重要性在于:一旦系统理解流式是事件流,而不是字符串流,就能把前端 UI、后端控制、工具执行和用户中断都嵌进同一条协议轨道里。否则,“流式”就只是一种更花哨的显示方式,而不是系统能力。
因此,本章第二条结论可以写成:
流式交互的本质,不是更快显示文本,而是把系统运行过程暴露成可消费的事件流。从这个角度看,SSE、WebSocket、WebRTC、异步 HTTP、webhook、队列事件,各自解决的就不是同一个问题。
SSE 很适合服务器单向持续推送结果,尤其适合增量文本和增量状态展示。WebSocket 更适合长连接、双向事件交换,尤其是需要客户端和服务端都能随时发起动作的场景。WebRTC 则更适合低延迟音视频和强实时互动。异步 HTTP 适合长任务:先接收请求,再返回查询地址,之后由客户端轮询或事件触发继续。Webhook 则把控制权进一步外推,让外部系统在适当时机推送新事件进来。
一旦把这些放进 Agent 系统里,通信边界就会自然呈现出另外三个维度。
第一个维度,是同步与异步的权衡。
不是所有交互都应该阻塞等待结果。长流程、审批流、外部数据抓取、人工介入、代码执行、跨系统编排,很多都更适合异步模式。系统先保存状态,返回任务句柄、状态 URI 或 session ID,等外部事件到来后再恢复执行。否则,所有东西都堆在一个同步请求里,系统只会越来越脆。
第二个维度,是会话连续性。
事件驱动一旦成立,通信就不再是孤立消息,而是围绕 session、conversation、thread、task 演进。没有这些对象,系统就很难回答“这一条事件到底属于哪个任务、应该唤醒哪段执行、是否还是同一轮会话”。这也是为什么现代 Agent 协议越来越强调 session ID、thread ID、task ID、sequence number、cursor 之类的对象。它们不是实现小节,而是让通信可以跨时间连续的主键。
第三个维度,是中断与恢复。
只要系统允许流式、允许异步、允许长任务,它就必须回答:如果连接断开怎么办?如果外部确认迟迟不来怎么办?如果任务执行到一半要暂停怎么办?如果恢复时不想从头流怎么办?这些都不是附加能力,而是事件驱动系统的基本生存条件。没有恢复语义,事件驱动只会把故障暴露得更频繁。
所以,本章不能只停留在“消息从 A 到 B”。
真正的重点是:协议必须同时定义载荷、生命周期和控制语义。载荷定义“传什么”,生命周期定义“这个通信过程如何开始、推进、结束、取消、恢复”,控制语义定义“谁可以触发什么事件、谁能中断、谁能继续、谁有权限消费这些消息”。
这也正是 MCP、A2A、CloudEvents、gRPC 这些协议或标准在今天变得重要的原因。
它们的价值不只是“多一个生态名词”,而是把本来会散落在私有 SDK 和临时 JSON 里的通信规则,提升为可协商、可发现、可版本化、可验证的正式契约。MCP 解决的是模型如何标准化接入上下文与工具;A2A 解决的是 agent 如何发现彼此、分派任务、交换结果;CloudEvents 解决的是事件信封如何标准化;gRPC 和 JSON-RPC 则解决方法调用、流式调用和错误语义如何稳定表达。
这意味着 Agent 的通信边界已经开始从“框架内部约定”升级为“跨系统互操作协议”。
一旦进入这一步,消息层的设计就再也不能随便了。因为通信的对象不再只是一个模型和一个应用,而可能是多个框架、多个服务、多个厂商、多个执行器、多个安全域。消息字段怎么命名、错误如何返回、能力如何发现、版本如何协商、任务如何跟踪,这些都会影响系统是不是能长期演化。
更重要的是,通信层同时也是安全层。
消息一旦跨越 agent、浏览器、服务器、webhook、事件总线、MCP server 或 A2A endpoint,系统就必须回答:消息是谁发的、是否被篡改、有没有权限触发下游动作、是不是旧事件重放、会不会被注入伪造内容。很多看起来像“安全问题”的漏洞,最后本质上都是通信边界没有被当作正式协议边界来设计。
所以,本章第三条结论是:
通信层不是中性的搬运层,它同时定义了能力边界、恢复边界和安全边界。如果消息协议不清楚,系统就无法可靠协作;如果事件语义不清楚,系统就无法稳定恢复;如果认证授权不清楚,系统就无法安全运行;如果版本协商不清楚,系统就无法跨生态互通。对 Agent 来说,这些都不是外围工程,而是核心架构。
从工程上看,一个成熟的通信边界至少要显式回答八个问题:
- 这是一条 message、event、task 还是 artifact
- 它属于哪个 session、thread 或 conversation
- 它是 request、response、notification 还是 update
- 它是否支持增量与流式
- 它能否被取消、暂停、恢复
- 它由谁认证、谁授权、谁验签
- 它如何版本化与能力协商
- 它失败时返回什么语义,而不是只返回一段错误文本
很多团队真正的问题,不是不会发消息,而是把这些问题全都藏在实现细节里。结果就是,系统一旦扩展,通信协议就会成为最先碎掉的地方。
所以,第 24 章最终要建立的结论是:
Agent 系统里的通信边界,不是对话框边界,而是消息对象、事件语义、会话主键、恢复机制和安全验证共同组成的协议边界。只有把它这样理解,后面的 MCP、A2A、服务接口、流式系统和事件驱动编排,才不会被看成零散技术点,而会被看成同一件事的不同实现:让 Agent 之间、Agent 与工具之间、Agent 与环境之间,能够在可验证、可恢复、可演化的通信契约上协同工作。 但一旦进入多 Agent 或 Agent 与外部系统协作,这些问题就会迅速浮出水面。
很多系统的失败,并不是“Agent 做错了决策”,而是:
- 消息丢字段
- 状态版本不一致
- 同步链路太长
- 事件顺序混乱
- 流式返回和最终状态不一致
因此,协作层真正成熟的标志之一,不是对话更像人,而是消息更像系统。