尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

多智能体编程实战:从斐波那契游戏解析对话模式与系统设计

多智能体编程实战:从斐波那契游戏解析对话模式与系统设计 1. 项目概述从对话模式看多智能体编程的实战价值最近在社区里看到不少朋友对多智能体编程感兴趣但总觉得这个概念有点“虚”停留在论文和框架介绍的层面。正好我前段时间带着团队用多智能体协作的方式复现并深度改造了一个经典的“斐波那契游戏”作为内部技术沙盘。这个项目本身不大但就像一滴水能折射太阳它把多智能体系统中那些抽象、复杂的“对话模式”给具象化了。今天我就把这个案例掰开揉碎了讲讲希望能帮你理解多智能体编程到底在解决什么问题以及那些听起来高大上的“对话模式”在实战中是怎么落地、怎么影响代码结构和系统行为的。这个“斐波那契游戏”案例本质上是一个协作计算任务。传统的实现很简单一个函数循环或递归算出数列。但在多智能体视角下我们把它拆解成多个具备特定角色的智能体比如“提议者”、“验证者”、“记录者”让它们通过彼此“对话”交换消息来协同完成计算。这听起来有点“杀鸡用牛刀”但它的价值在于为我们提供了一个极其干净、可控的“显微镜”去观察和分析智能体间各种交互模式——比如请求-响应、发布-订阅、协商、竞争——是如何被设计、实现并最终影响系统可靠性、效率和扩展性的。如果你正在考虑将单体应用重构为更灵活、更自治的智能体系统或者对分布式AI协作的底层机制感到好奇那么这次从“斐波那契”切入的探讨或许能给你带来一些不一样的、接地气的启发。2. 核心设计为何选择“斐波那契游戏”作为沙盘在决定用多智能体方式做点什么的时候我刻意避开了那些业务逻辑过于复杂的场景。因为对于学习和验证一种新的编程范式来说初始环境的“噪声”越少越好。斐波那契数列生成就是一个近乎完美的沙盘。2.1 问题域的纯粹性与可观测性斐波那契数列的定义F(0)0, F(1)1, F(n)F(n-1)F(n-2) for n1是确定性的、无状态的从递归角度看。但当我们把它任务化就产生了丰富的可分解性。例如计算F(5)可以看作是需要先知道F(4)和F(3)。这天然形成了一个任务依赖图。在多智能体系统中我们可以让一个智能体负责任务分解将F(5)拆解为获取F(4)和F(3)的子任务其他智能体负责计算或提供具体数值。这样智能体间的每一次“对话”——请求数据、返回结果、传递错误——都对应着依赖图中的一个清晰链路。整个系统的动态过程变得高度可观测、可追溯。你能够清晰地看到消息是如何流动的计算是如何一步步推进的瓶颈和错误出现在哪个环节。这种透明性对于调试和理解多智能体系统的并发、协调机制至关重要。注意选择沙盘项目的首要原则是“核心逻辑简单交互模式复杂”。斐波那契计算本身简单但通过设计我们可以让它蕴含请求/响应、扇出/扇入、错误传播、结果缓存等多种交互模式这才是我们真正要研究和练习的重点。2.2 智能体角色设计与职责边界在我们的案例中我们设计了四种核心角色这比简单的“工人-管理者”模型更精细旨在探索更丰富的对话模式任务管理智能体 (TaskMaster)这是系统的入口和协调中枢。它接收外部请求如“计算F(10)”并将其分解为子任务依赖树。它不进行计算只负责任务的规划、派发和最终结果的聚合。它的对话模式主要是“发布任务”和“收集结果”。计算工人智能体 (ComputeWorker)这是执行具体计算的单元。它从TaskMaster或其他Worker那里接收计算某个F(n)的请求。如果n很小比如0或1它直接返回基础值如果n较大它可能会向TaskMaster“咨询”或直接向其他Worker“请求”所需的F(n-1)和F(n-2)值。它的对话模式包括“请求-响应”和“发布结果”。缓存代理智能体 (CacheAgent)为了优化性能我们引入了一个专门的缓存角色。任何Worker计算出F(n)后除了返回给请求者还会“发布”给CacheAgent进行存储。其他Worker在计算前可以先向CacheAgent“查询”是否已有缓存结果。这引入了“发布-订阅”和“查询-响应”模式。验证监督智能体 (Validator)这个角色是可选的用于增加系统的鲁棒性。它订阅所有计算完成的消息对结果进行简单验证例如检查F(n)是否等于F(n-1)F(n-2)。如果发现异常它可以向TaskMaster“告警”或触发重算。这体现了“监控-告警”模式。通过这样的角色划分我们强制性地将单一的计算过程解耦成了通过消息传递连接的、各司其职的协作网络。这模拟了微服务或分布式系统中常见的协作场景。2.3 技术栈选型与框架评估多智能体编程离不开框架的支持。在这个项目中我们评估并尝试了两种主流方向专用多智能体框架我们重点使用了AutoGen和LangGraph。AutoGen 由微软推出它抽象了“代理”概念内置了群组聊天、自动回复等高级功能非常适合快速构建基于LLM的协作智能体。而LangGraph来自LangChain则更侧重于用“图”来定义智能体的工作流状态管理非常清晰。在斐波那契案例中我们用AutoGen来快速搭建一个具备对话能力的验证者智能体用LangGraph来精确地建模TaskMaster的任务分解与状态流转图。通用消息中间件自定义逻辑为了更底层地理解通信机制我们也用ZeroMQ和Redis Pub/Sub配合Python asyncio手动实现了一套轻量级智能体框架。ZeroMQ提供了灵活的Socket模式REQ/REP, PUB/SUB, DEALER/ROUTER让我们可以亲手实现上述各种对话模式。Redis则作为共享的消息总线和缓存层。选型心得对于快速原型验证和探索LLM智能体协作AutoGen和LangGraph效率极高。但如果你想深入掌握通信细节、实现定制化的消息路由或资源控制从ZeroMQ这类底层工具开始虽然更费力但理解会深刻得多。我们的建议是先用高级框架建立感性认识再用底层工具深化原理理解。3. 对话模式详解从理论到斐波那契实战多智能体系统的核心就是“对话”。下面我结合斐波那契游戏中的具体场景拆解几种最关键的对话模式。3.1 请求-响应模式计算任务的基本单元这是最同步、最直接的对话模式类似于HTTP请求。在项目中一个ComputeWorker向另一个ComputeWorker或CacheAgent请求F(n-1)的值时就使用此模式。实现要点同步性请求方发送消息后会阻塞等待响应。这要求通信链路必须可靠且响应方必须在合理时间内回复。消息信封消息中必须包含唯一的correlation_id以便请求方在收到多个响应时能正确匹配。在我们的实现中每个计算请求都会生成一个UUID作为correlation_id。超时与重试必须设置超时机制。我们使用asyncio.wait_for为每个请求设置超时如2秒。超时后根据策略决定是重试、向上游汇报失败还是寻找替代节点。# 伪代码示例ComputeWorker A 向 ComputeWorker B 发起请求-响应 import asyncio import uuid async def request_fib_value(worker_b_address, n): request_id str(uuid.uuid4()) request_msg { type: compute_request, request_id: request_id, n: n, requester: Worker_A } # 通过ZeroMQ REQ Socket发送 await socket.send_json(request_msg) try: # 等待响应设置超时 reply await asyncio.wait_for(socket.recv_json(), timeout2.0) if reply.get(response_to) request_id: return reply[value] else: raise ValueError(Correlation ID mismatch) except asyncio.TimeoutError: # 触发重试或故障处理逻辑 await handle_timeout(request_id, worker_b_address)踩坑记录初期我们没有严格管理correlation_id在高压下出现了响应错乱A的请求结果被B接收。后来我们引入了全局唯一的请求ID和每个Worker本地的待处理请求字典才彻底解决。3.2 发布-订阅模式解耦与事件驱动这是实现系统解耦的关键模式。当CacheAgent缓存了一个新值或者Validator完成了一次验证它们并不需要知道谁关心这件事只需“发布”到特定主题Topic。感兴趣的智能体如所有Worker订阅“缓存更新”TaskMaster订阅“验证告警”会自行接收。在项目中的应用缓存更新广播ComputeWorker计算出F(10)55后向主题fibonacci:computed:10发布消息。CacheAgent订阅了fibonacci:computed:*它会接收并存储。其他Worker也可以订阅实现本地缓存预热。系统状态心跳每个Worker定期向heartbeat主题发布存活状态。一个监控智能体订阅此主题实现健康检查。实现要点主题设计主题命名要有层次结构方便订阅通配符。我们采用领域:事件类型:参数的格式如fibonacci:request:10,fibonacci:computed:10。消息去重在网络不稳定或重连时可能收到重复消息。我们在消息体中加入了时间戳和唯一消息ID订阅方会做短暂去重。持久化订阅对于关键事件如最终计算结果需要确保即使订阅方临时下线重新上线后也能收到消息。这需要消息中间件如Redis Stream或RabbitMQ的支持我们项目中为简化未使用但在生产环境中是必须考虑的。这种模式极大降低了智能体间的耦合度。新增一个日志智能体或仪表盘智能体只需订阅相关主题即可无需修改现有智能体的代码。3.3 协商与竞争模式处理冲突与优化决策当多个ComputeWorker同时空闲并且TaskMaster发布了一个新的计算任务F(n)时就产生了竞争。简单的做法是TaskMaster直接指派但这可能不是最优的比如某个Worker负载已高。我们尝试了简单的协商模式。实现方案任务公告TaskMaster不直接指派而是向task:announce主题发布一个任务公告包含n和基础奖励分。投标空闲的Worker收到公告后根据自身当前负载和n的大小估算计算成本计算一个“报价”可以是期望完成时间或资源消耗分数然后向TaskMaster发送一个投标消息。决策与授予TaskMaster收集一段时间内如100毫秒的所有投标选择一个最优的如报价最低的然后单独向该Worker发送“任务授予”消息。这个过程模拟了一个简单的合同网协议。虽然对于斐波那契计算来说有点“过设计”但它清晰地展示了如何通过多轮对话实现资源的优化分配。在实际的复杂任务如负载均衡、路径规划中这种模式非常有用。注意事项协商会引入延迟投标收集期。需要根据任务粒度和系统规模权衡。对于毫秒级微任务可能不如随机指派或轮询高效。3.4 错误传播与恢复模式构建韧性系统在多智能体系统中局部故障是常态。如何让错误优雅地传播并触发恢复是关键的设计点。在我们的案例中一个ComputeWorker计算F(n)时如果它向其他Worker请求F(n-1)超时失败它不会直接对外抛出异常。而是首先尝试向CacheAgent查询是否有缓存的F(n-1)。如果缓存也没有则向TaskMaster发送一个“任务失败”消息其中包含错误上下文failed_n: n-1,reason: timeout。TaskMaster收到失败消息后有多种策略重试将计算F(n-1)的任务重新派发给另一个Worker。降级如果n较小直接指派一个可靠的Worker同步计算F(n-1)和F(n-2)。广播求助向所有Worker广播这个“困难任务”看是否有空闲Worker能接手。同时最初的Worker可以继续处理其他任务不会被一个子任务阻塞。我们设计了一个简单的错误恢复状态机由TaskMaster维护错误类型触发条件恢复策略最大重试次数子任务超时Worker请求子结果超时更换Worker重试原任务3计算异常Worker计算过程抛出异常记录异常节点指派新Worker跳过异常节点如有缓存2依赖缺失所需的前序值全部无法获得回退到最底层可计算节点重新向上推导-死锁检测多个Worker循环等待彼此的结果TaskMaster介入强制分配一个公共缓存结果或指定一个顺序1通过将错误处理本身设计为一种智能体间的对话失败通知、恢复指令系统获得了更强的容错能力。4. 系统实现与核心代码剖析有了清晰的设计接下来就是实现。这里我分享几个关键模块的实现细节和思考。4.1 智能体基类与消息循环我们为所有智能体实现了一个基类BaseAgent封装了消息收发、生命周期管理的基础功能。核心是一个异步的message_loop。import asyncio import logging from abc import ABC, abstractmethod class BaseAgent(ABC): def __init__(self, agent_id, inbox_addr, pub_addr): self.agent_id agent_id self.inbox_addr inbox_addr # 接收请求的地址 self.pub_addr pub_addr # 发布消息的地址 self.running False self._message_handlers {} self.logger logging.getLogger(fAgent.{agent_id}) async def start(self): 启动智能体的消息循环 self.running True # 初始化网络连接 (以ZeroMQ为例) context zmq.asyncio.Context() self.inbox_socket context.socket(zmq.ROUTER) # 用于接收定向消息 self.inbox_socket.bind(self.inbox_addr) self.pub_socket context.socket(zmq.PUB) # 用于发布广播 self.pub_socket.bind(self.pub_addr) self.logger.info(fAgent {self.agent_id} started on {self.inbox_addr}) await self.message_loop() async def message_loop(self): 核心消息处理循环 poller zmq.asyncio.Poller() poller.register(self.inbox_socket, zmq.POLLIN) while self.running: try: events await poller.poll(timeout100) # 100毫秒超时 if self.inbox_socket in dict(events): # 接收消息 [sender_identity, empty, message_body] sender, _, message_data await self.inbox_socket.recv_multipart() message json.loads(message_data.decode()) await self._dispatch_message(sender, message) # 此处可以添加定期执行的后台任务 await self.background_task() except Exception as e: self.logger.error(fMessage loop error: {e}, exc_infoTrue) async def _dispatch_message(self, sender, message): 根据消息类型分发给对应的处理器 msg_type message.get(type) handler self._message_handlers.get(msg_type) if handler: await handler(sender, message) else: self.logger.warning(fNo handler for message type: {msg_type}) def register_handler(self, msg_type, handler_func): 注册消息处理函数 self._message_handlers[msg_type] handler_func abstractmethod async def background_task(self): 由子类实现的背景任务如状态汇报、缓存清理等 pass async def send_to_agent(self, agent_address, message): 向特定智能体发送点对点消息 # ... 实现细节使用DEALER socket连接目标地址并发送 async def publish(self, topic, message): 向某个主题发布消息 full_topic f{topic} {json.dumps(message)} await self.pub_socket.send_string(full_topic)这个基类提供了多智能体编程中最基础的“生存”能力收发消息。每个具体的智能体如ComputeWorker继承它并注册自己关心的消息处理器。4.2 任务管理智能体的分解算法TaskMaster的核心是将一个大的F(n)计算请求分解成一个有向无环图。我们采用了记忆化递归分解并生成一个任务状态表。class TaskMaster(BaseAgent): def __init__(self, ...): super().__init__(...) self.pending_tasks {} # task_id - Task对象 self.task_dependency_graph {} # task_id - [dep_task_id, ...] self.register_handler(compute_request, self.handle_external_request) self.register_handler(task_result, self.handle_task_result) self.register_handler(task_failed, self.handle_task_failed) async def handle_external_request(self, sender, msg): 处理外部计算请求如 {type:compute_request, n: 10} n msg[n] task_id fF({n}) if task_id in self.pending_tasks: # 任务已存在可能将请求者加入结果等待列表 return # 1. 创建主任务 main_task Task(idtask_id, nn, statuspending, requestersender) self.pending_tasks[task_id] main_task # 2. 递归分解构建依赖图 dep_graph self._decompose_task(n, task_id) self.task_dependency_graph.update(dep_graph) # 3. 找出所有没有依赖的叶子任务即可以直接计算的F(0), F(1)或已缓存的任务 ready_tasks self._find_ready_tasks(dep_graph) # 4. 派发就绪任务 for ready_task_id in ready_tasks: await self._dispatch_single_task(ready_task_id) def _decompose_task(self, n, root_task_id): 递归分解任务返回依赖图 graph {} if n 1: # 基础任务没有依赖 graph[root_task_id] [] return graph # 创建两个子任务ID left_id fF({n-1}) right_id fF({n-2}) # 当前任务依赖这两个子任务 graph[root_task_id] [left_id, right_id] # 递归分解子任务避免重复分解已存在的任务 if left_id not in self.pending_tasks and left_id not in graph: graph.update(self._decompose_task(n-1, left_id)) if right_id not in self.pending_tasks and right_id not in graph: graph.update(self._decompose_task(n-2, right_id)) return graph分解算法生成的依赖图是后续调度和错误恢复的基础。TaskMaster需要维护每个任务的状态等待、执行中、完成、失败并在一个子任务完成时检查其父任务的所有依赖是否都已满足从而触发父任务的计算或结果聚合。4.3 计算工人智能体的协作逻辑ComputeWorker的逻辑相对复杂它需要处理来自多方的请求并可能主动发起子请求。class ComputeWorker(BaseAgent): async def handle_compute_request(self, sender, msg): task_id msg[task_id] n msg[n] request_id msg[request_id] self.logger.info(fReceived compute request for F({n})) # 策略1: 检查本地内存缓存避免重复计算 if n in self.local_cache: result self.local_cache[n] await self.send_result(sender, request_id, result, from_cacheTrue) return # 策略2: 如果n很小直接计算 if n 20: # 阈值可配置小于阈值直接算避免通信开销 result self._compute_directly(n) self.local_cache[n] result await self.send_result(sender, request_id, result) await self.publish(fibonacci:computed, {n: n, value: result}) # 广播结果 return # 策略3: 对于大n尝试从缓存代理获取 cached_value await self.query_cache_agent(n) if cached_value is not None: self.local_cache[n] cached_value await self.send_result(sender, request_id, cached_value, from_cacheTrue) return # 策略4: 需要计算递归请求子任务模拟分布式计算 # 这里简化直接向TaskMaster请求子任务而非联系其他Worker # 在实际更复杂的实现中Worker之间可以直接通信请求子结果 subtask_promise asyncio.create_task( self.request_subtasks_from_master(n, request_id, sender) ) # 将承诺存储起来以便后续处理 self.pending_requests[request_id] { original_sender: sender, subtask_promise: subtask_promise }这里的关键是策略分层。一个健壮的Worker不会只有一种处理方式。它优先使用最快的方式本地缓存其次是低开销方式直接计算小任务再次是协作方式查询远程缓存最后才是成本最高的方式发起分布式子计算。这种设计模式在实际业务中非常普遍例如CDN-本地缓存-源站的读取策略。4.4 性能优化缓存策略与通信压缩当计算F(40)这样的数时系统会产生大量的子任务和消息。我们引入了多级缓存和消息压缩来优化。多级缓存本地内存缓存每个ComputeWorker维护一个LRU缓存缓存最近计算过的结果。这是最快的。分布式缓存CacheAgent作为全局缓存使用Redis存储所有Worker共享。缓存策略采用TTL过期。预计算与预热系统启动后可以主动计算并缓存一些常用的中间值如F(10)到F(30)。通信压缩消息精简在设计消息协议时我们使用了简短的键名如t代表type,rid代表request_id并在传输前用msgpack替代json进行序列化体积减少了约30%。批量更新CacheAgent不是每收到一个结果就通知所有订阅者而是积累一小批如10个或100毫秒窗口后进行一次批量广播减少网络报文数量。增量传输对于非常大的计算结果本项目不涉及但其他场景会可以只传输差值或使用压缩算法。经过这些优化计算F(40)的总耗时从请求发出到收到最终结果从最初的约1200毫秒降低到了约400毫秒其中通信开销占比从70%下降到了30%以下。这告诉我们在多智能体系统中网络通信往往是瓶颈优化通信效率有时比优化计算逻辑本身收益更大。5. 调试、监控与问题排查实录开发多智能体系统最头疼的就是调试因为问题可能出现在任何一个智能体中且与时序、并发强相关。我们总结了一套实用的方法。5.1 可视化消息流我们开发了一个简单的监控智能体它订阅所有主题的消息并将消息流实时输出到控制台或Web界面用不同颜色区分消息类型和发送者。这是最直接的调试工具可以让你像看电影一样观察整个系统的对话过程。通过消息流我们早期发现了一个死锁问题两个Worker互相等待对方计算的子结果因为它们在几乎同时请求了对方的F(n-1)和F(n-2)而双方都因为对方未响应而阻塞。解决方案是引入一个全局的任务锁顺序例如总是先请求F(n-1)再请求F(n-2)或者让TaskMaster来协调子任务的分配避免循环依赖。5.2 分布式追踪我们在每条消息的元数据中都加入了一个trace_id。当一个外部请求进入系统TaskMaster生成一个根trace_id。这个ID会随着任务分解和消息传递被复制到所有相关的子任务和消息中。这样无论日志分散在哪个智能体的文件里我们都能用trace_id把它们串联起来完整还原一个请求的生命周期。我们用了OpenTelemetry的理念但实现了一个轻量版将追踪信息发布到一个专门的trace主题由追踪智能体收集和存储。5.3 典型问题与解决方案速查表下面是我们遇到的一些典型问题及解决方法供你参考问题现象可能原因排查步骤解决方案请求无响应系统“卡住”1. 消息丢失2. 接收方崩溃3. 死锁1. 检查监控消息流看请求消息是否发出。2. 检查接收方智能体日志和进程状态。3. 检查是否存在循环等待依赖。1. 实现消息确认和重发机制。2. 增加智能体心跳和看门狗自动重启。3. 设计无环的任务依赖图或引入超时和死锁检测中断。计算结果偶尔错误1. 缓存脏数据2. 消息乱序3. 竞态条件1. 检查缓存更新和读取的时序。2. 检查correlation_id匹配逻辑。3. 在关键代码段加日志检查并发执行顺序。1. 为缓存值增加版本号或计算签名。2. 使用序列号确保消息处理顺序。3. 对共享状态如本地缓存使用锁或异步队列。系统性能随规模增长急剧下降1. 消息风暴2. 单个智能体成为瓶颈3. 网络拥堵1. 监控消息总线吞吐量。2. 分析各智能体CPU/内存使用率。3. 使用网络工具分析延迟和丢包。1. 合并消息、使用批量操作。2. 对瓶颈智能体进行水平扩展多个实例。3. 优化网络配置使用更高效序列化协议。智能体启动后无法发现彼此1. 地址配置错误2. 服务发现失效3. 防火墙/网络问题1. 检查各智能体连接地址配置。2. 检查服务注册中心如Redis是否可达。3. 使用telnet或nc测试端口连通性。1. 使用统一配置中心或环境变量。2. 实现重试和退避机制的服务发现。3. 确保网络策略允许智能体间通信。5.4 压力测试与混沌工程我们使用locust编写了简单的压力测试脚本模拟并发请求计算F(30)。在测试中我们故意引入了“混沌”随机杀死Worker进程验证TaskMaster是否能重新派发任务系统最终能否返回正确结果。模拟网络延迟和丢包在消息层注入随机延迟和丢包测试系统的超时和重试机制是否健壮。消息重复发送测试智能体的消息去重和幂等性处理。这些测试暴露了我们早期版本中不少问题比如重试机制过于激进导致雪崩缓存没有考虑进程崩溃后的数据丢失等。通过反复的“破坏-观察-修复”系统的韧性得到了显著提升。6. 从斐波那契到真实世界模式迁移与扩展思考这个斐波那契游戏项目虽然小但其中演练的对话模式和设计思想可以直接迁移到更复杂的生产场景。场景一电商订单处理系统可以将订单处理拆分为多个智能体订单接收器TaskMaster、库存检查器、支付处理器、物流调度器、通知发送器。它们通过“发布-订阅”传递订单状态变更事件通过“请求-响应”进行服务调用如调用支付网关通过“协商”处理库存冲突两个订单争抢最后一件商品。错误恢复模式可以处理支付失败、库存不足等异常。场景二智能运维监控系统每个服务器或服务部署一个监控代理智能体类似ComputeWorker收集指标。一个分析中心智能体类似TaskMaster订阅所有代理的数据进行聚合分析。当发现异常如CPU飙升分析中心可以发起一个“根本原因分析”任务协调日志分析智能体、链路追踪智能体等进行协同调查这个过程就涉及复杂的多轮“协商”和“请求-响应”。扩展思考引入LLM智能体在上述任何场景中都可以引入一个基于大语言模型的“决策顾问”智能体。当系统遇到未预定义的异常或复杂决策时例如物流调度出现多个可行方案可以将上下文信息发送给LLM智能体让它生成建议或决策依据。这需要设计好与LLM交互的“提示词管理”和“结果解析”模式。动态智能体编排当前的智能体角色是静态的。更高级的模式是TaskMaster可以根据任务类型动态地组合和编排不同的智能体能力形成一个临时的工作流。这需要一套智能体能力描述和发现机制。联邦学习与隐私计算每个ComputeWorker可以看作拥有本地数据的一方。在多智能体框架下可以协调它们在不交换原始数据的情况下共同训练一个全局模型。这时的“对话”内容就变成了模型梯度或参数的加密交换对话模式需要更高的安全性和同步性保障。回过头看斐波那契游戏就像一副骨架而真实的业务场景是血肉。通过这个项目我们亲手搭建并观察了这副骨架是如何运作的。当你需要构建一个需要灵活协作、高容错、易扩展的分布式系统时多智能体编程范式以及其中丰富的对话模式就从一个抽象概念变成了你工具箱里一件实实在在的、知道如何使用的工具。
返回列表