有效的沟通是任何合作多代理系统的根本要素。上一章侧重于塑造单个代理,而我们现在将讨论这些代理如何彼此交互。这种交互的中心是消息交换协议:管理代理如何发送、接收和解释信息的商定规则和格式。如果没有明确定义的协议,代理之间的对话将演变为一团混乱的误解,阻碍系统执行复杂任务的能力。本节阐述了构成代理间消息传递基础的常见协议和模式,确保其对话的清晰度、可靠性和效率。消息交换协议的主要方面有几个基本方面决定了多代理系统中消息如何交换和理解。消息格式和序列化尽管大型语言模型擅长理解自然语言,但在结构化系统内仅依赖自由格式文本进行代理间通信可能导致模糊性和处理开销。相反,使用既定的序列化格式定义清晰的消息结构非常重要。这些格式确保消息能够一致地被解释和机器读取。常见的选择包括:JSON (JavaScript 对象表示法): 因其人类可读性和与网络技术易于结合而得到广泛采用。其无模式特性提供了灵活性,尽管如果管理不当,这也可能成为错误的源头。大多数大型语言模型框架和API都可以轻松地使用和生成JSON。XML (可扩展标记语言): 另一种基于文本的格式,但通常比JSON更冗长。它通过DTD或XML Schema提供强大的模式验证能力。Protocol Buffers (Protobufs): 一种由谷歌开发的二进制序列化格式。Protobuf在大小和速度方面效率很高,并强制执行严格的模式,这有助于维护数据一致性。它们非常适合高性能内部通信,但不能直接人类可读。考虑一个简单的代理间消息结构:{ "protocol_version": "1.0", "message_id": "msg_12345abc", "sender_id": "agent_data_analyzer_001", "receiver_id": "agent_report_generator_007", "timestamp": "2024-07-15T10:30:00Z", "type": "ANALYSIS_RESULT_AVAILABLE", "payload": { "analysis_id": "analysis_xyz_987", "summary_preview": "Positive trend identified in Q2 sales data based on region X.", "confidence_score": 0.92, "data_location_ref": "/shared_storage/results/analysis_xyz_987.json" }, "metadata": { "requires_acknowledgement": true, "priority": "high" } }一个JSON消息结构示例。请注意包含发送者/接收者ID、用于路由/处理逻辑的消息类型,以及可能引用其他地方较大数据的结构化负载。地址解析和路由消息格式化后,系统需要知道其目的地。地址解析机制用于识别预期的接收方。代理标识符 (ID): 分配给每个代理的唯一ID。这是最直接的寻址方式。基于角色的地址解析: 将消息发送给执行特定角色(例如,“任何可用的‘验证代理’”)的一个或多个代理。这通常需要目录服务或编排器。服务发现: 代理可以将其能力和地址注册到中央注册中心,以便其他代理能够动态查找它们。通信可以是直接的或中介的:直接通信: 代理A建立连接并直接向代理B发送消息。对于小型系统来说更简单,但可能导致紧密耦合。代理通信: 代理通过中介进行通信,通常是消息代理(例如,RabbitMQ、Apache Kafka、Redis Streams)。代理将发送方与接收方解耦,可以提供持久性、负载均衡并支持复杂的路由模式。digraph G { rankdir=TB; bgcolor="transparent"; node [shape=box, style="filled", color="#ced4da", fillcolor="#e9ecef", fontname="Helvetica"]; edge [fontname="Helvetica", color="#495057"]; subgraph cluster_direct { label="直接通信"; style="filled"; color="#dee2e6"; node [fillcolor="#a5d8ff"]; A1 [label="代理A"]; B1 [label="代理B"]; A1 -> B1 [label="消息"]; } subgraph cluster_brokered { label="代理通信"; style="filled"; color="#dee2e6"; node [fillcolor="#96f2d7"]; Broker [label="消息代理", shape=cylinder, fillcolor="#ffec99"]; A2 [label="代理A"]; B2 [label="代理B"]; C2 [label="代理C"]; A2 -> Broker [label="发送"]; Broker -> B2 [label="交付"]; Broker -> C2 [label="交付 (如果已订阅)"]; } }图表显示了代理间直接通信与通过消息代理中介通信的区别。代理系统提供更大的解耦和灵活性。通信模式代理间通信通常遵循既定模式,这些模式定义了交互的流程和预期。请求-回复: 最常见的模式之一。同步: 代理A向代理B发送请求并阻塞(等待),直到收到回复。实现简单,但如果代理B响应慢,可能导致瓶颈。异步: 代理A发送请求并继续其处理。代理B稍后发送回复,代理A在可用时处理该回复。这需要一种机制,如关联ID,以将回复与其原始请求匹配。这在多代理系统中通常是处理非阻塞操作的首选。发布-订阅 (Pub/Sub): 代理(发布者)将消息发送到命名通道或主题,而无需知道订阅者是谁。其他代理(订阅者)对特定主题表示兴趣并接收发送到这些主题的消息。此模式非常适合解耦、广播事件(例如,“new_data_available”、“system_alert”)以及同时将信息分发给多个感兴趣的方。点对点(直接消息): 顾名思义,这是一个用于两个特定代理之间通信的专用通道。通常用于特定任务或一对代理之间持续的对话。广播/多播: 将消息发送给所有代理(广播)或特定组的代理(多播)。这对于系统范围的公告或命令很有用,但应谨慎使用,以避免网络泛滥。digraph G { rankdir=TB; bgcolor="transparent"; node [shape=box, style="filled", color="#ced4da", fillcolor="#e9ecef", fontname="Helvetica"]; edge [fontname="Helvetica", color="#495057", arrowhead="vee"]; subgraph cluster_req_reply { label="请求-回复(异步)"; style="filled"; color="#dee2e6"; node [fillcolor="#bac8ff"]; ReqA [label="请求代理"]; ResB [label="响应代理"]; ReqA -> ResB [label="1. 请求 (task_id: X)"]; ResB -> ReqA [label="2. 回复 (task_id: X)"]; } subgraph cluster_pub_sub { label="发布-订阅"; style="filled"; color="#dee2e6"; node [fillcolor="#d8f5a2"]; Pub [label="发布代理", fillcolor="#ffd8a8"]; Topic [label="主题 XYZ", shape=oval, fillcolor="#ffec99"]; Sub1 [label="订阅代理 1"]; Sub2 [label="订阅代理 2"]; Sub3 [label="订阅代理 3 (未订阅)"]; Pub -> Topic [label="1. 发布消息"]; Topic -> Sub1 [label="2a. 交付消息"]; Topic -> Sub2 [label="2b. 交付消息"]; } }异步请求-回复和发布-订阅通信模式的比较。请求-回复涉及定向交换,而发布-订阅通过主题向感兴趣的订阅者广播。标准协议与自定义协议选择或设计协议时,可以使用现有标准或根据系统特定需求定义自定义协议。通常,两者兼而有之:使用标准传输协议并在其之上分层自定义应用层协议。使用标准传输和交互协议HTTP/S (超文本传输安全协议): 网络的根本。代理可以公开RESTful API供其他代理调用。它本质上是无状态的(每个请求),广为人知,并对防火墙友好。常用于代理到服务的通信,甚至如果请求-回复模型适合,也可用于代理到代理的通信。WebSockets: 通过单个TCP连接提供全双工通信通道。它非常适合实时、交互式通信,在其中代理需要频繁交换消息,而无需为每条消息建立新的HTTP连接的开销。gRPC (Google 远程过程调用): 一个高性能、开源的RPC框架。它默认使用Protocol Buffers进行消息序列化,使用HTTP/2进行传输。它非常适合受信任环境内的内部微服务或代理之间的高效、强类型通信。MQTT (消息队列遥测传输): 一种轻量级发布-订阅协议,专为资源受限设备和低带宽、高延迟网络而设计。尽管大型语言模型代理本身通常不是“资源受限设备”,但MQTT代理对于大型多代理系统中的特定事件场景可能有用。定义自定义应用层协议即使使用HTTP或WebSockets等标准传输,您也几乎肯定会定义一个自定义的应用层协议。这包括指定:消息类型/命令: 可以通信哪些特定操作或信息类型(例如,SUBMIT_TASK、QUERY_STATUS、PROVIDE_FEEDBACK、SHARE_INSIGHT)。负载模式: 与每种消息类型关联的数据结构(通常使用JSON Schema、Protobuf定义或类似方式定义)。交互流程(编排): 复杂交互的预期消息序列(例如,谈判可能涉及OFFER、COUNTER_OFFER、ACCEPT/REJECT序列)。错误代码和响应: 发出错误信号或特定结果的标准化方式。例如,您的自定义协议可能规定,通过HTTP POST发送给“分析器”代理的REQUEST_ANALYSIS消息必须包含data_source_url,并期望收到一个带有analysis_id的JSON响应以供后续检索。大型语言模型代理的协议设计考量为涉及大型语言模型代理的系统设计通信协议,由于大型语言模型及其交互的特性,需要考虑以下具体事项:处理潜在的大型负载: 大型语言模型的输入(包含大量上下文的提示)和输出(详细解释、代码或文档)可能非常庞大。协议应能高效处理大型消息。这可能涉及支持流式传输、分块,或传递对存储在其他地方(例如,共享对象存储中)的数据的引用,而不是直接在消息中嵌入大型数据块。管理延迟和异步性: 大型语言模型的推理可能需要较长时间。协议和代理逻辑必须优雅地处理这种延迟。异步通信模式通常是防止阻塞和保持系统响应能力必不可少的。超时和明确的响应时间预期很重要。令牌限制和上下文窗口: 尽管这不是代理之间消息交换的严格协议问题,但交换的数据最终必须能被大型语言模型使用。协议设计应间接考虑消息内容如何在后续的大型语言模型提示中使用,这可能影响消息中信息的结构或摘要方式。错误处理和重试: 大型语言模型调用可能由于API问题、速率限制、内容过滤或格式错误的响应而失败。代理间协议必须清楚地定义错误消息格式和语义。代理需要可靠的错误处理逻辑,可能包括带退避策略的重试机制,或升级给人工或另一个代理。消息内容的安全: 消息可能包含用于提示或由大型语言模型生成的敏感信息。如果代理通过网络(尤其是公共网络)通信,消息负载的加密(不仅仅是传输层的TLS)可能必要。代理的认证(谁发送此消息?)和授权(此代理是否允许发送此类消息或请求此操作?)都很重要。可扩展性和版本控制: 多代理系统不断演进。新的代理类型、功能和交互模式将会出现。设计协议时应考虑可扩展性。这包括对消息模式和协议定义进行版本控制,以便旧代理可以与新代理共存或优雅地处理无法识别的消息元素。可观察性和调试: 有效的多代理系统需要良好的可观察性。协议应设计为便于记录和跟踪消息在代理间流动的过程。在消息头中包含唯一的关联ID、时间戳以及发送者/接收者信息对于调试复杂交互和理解系统行为至关重要。选择合适的协议选择最合适的代理间消息交换协议或协议组合,是一个设计决策,它在很大程度上取决于您的具体系统要求。没有唯一的“最佳”选择;相反,重要的是找到最匹配的方案。需要考虑的主要因素包括:系统规模和拓扑: 将有多少代理进行通信?它们是共存还是分布式?对于小型、共存的系统,直接消息传递或简单的内存队列可能就足够了。对于大型、分布式系统,可靠的消息代理通常不可或缺。所需的通信模式: 您的系统主要依赖请求-回复模式,还是事件分发用的发布-订阅模式更核心?选择自然支持您主要模式的协议和基础设施。性能和延迟要求: 对于高吞吐量、低延迟的交互,像gRPC这样的二进制协议可能比像JSON over HTTP这样的文本协议更受欢迎,特别是对于内部通信。交换信息的性质: 消息是小型且频繁的,还是大型且不频繁的?这会影响序列化和传输的选择。可靠性和持久性需求: 如果接收代理暂时不可用,消息是否需要持久化?消息代理提供持久队列等功能。团队熟悉度和现有基础设施: 使用团队已了解的技术可以加快开发速度。同样,与现有消息代理或API网关的集成是务实的。互操作性: 如果代理由不同团队开发或需要与外部系统交互,HTTP/REST等标准化协议或gRPC等定义明确的跨语言框架是有益的。安全要求: 交换数据的敏感性将影响协议和底层传输所支持的加密、认证和授权机制的选择。通常,混合方法是最好的。例如,代理可能使用gRPC进行集群内的高频内部通信,公开HTTP/REST API用于外部交互或控制平面,并使用Kafka或RabbitMQ等消息代理进行异步任务分发和事件通知。