ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Agent-Reach实战:解决多Agent协作中的互联与消息路由难题

Agent-Reach实战:解决多Agent协作中的互联与消息路由难题 1. 项目概述Agent-Reach是什么我最近在复盘一系列AI Agent落地项目时发现一个反复出现的痛点Agent之间互相通信太难了。每个Agent都是一个“信息孤岛”它们各自调用各自的模型、各自维护各自的状态、各自处理各自的任务看似智能实际上彼此之间几乎没有协作。你让A Agent查了天气B Agent完全不知道你让C Agent生成了一份报告D Agent拿不到这份报告的结构化数据。整个系统像是一群各说各话的实习生谁也不知道别人在干什么。Agent-Reach这个项目解决的正是这个问题——它是一套面向多Agent场景的互联与消息传递方案。核心思路是把每个Agent抽象成一个标准化的“节点”通过统一的消息协议、路由机制和状态同步机制让不同的Agent能够互相发现、互相调用、互相传递数据。如果你正在做多Agent系统、Agent工作流编排或者想把自己的Agent接入一个更大的协作网络Agent-Reach值得你花时间了解。我实测下来这个方案最打动我的不是某个单一功能而是它把“Agent互联”这件事做得足够轻。它不是重型的消息中间件不需要你部署一套Kafka或者RabbitMQ它也不是某种封闭的Agent平台强制你迁移到特定运行时。它更像是一层薄薄的“通信协议层”你可以在任何Agent实现之上叠加这套能力无论是LangChain、AutoGen、CrewAI还是自研的Agent框架都能接进来。这篇文章我会从设计思路、核心细节、实操过程到问题排查完整拆解Agent-Reach的落地经验。整个内容基于我在多个项目里的真实使用记录和一些关键节点的补充验证适合已经被多Agent协作折磨过、或者正准备上手多Agent系统的开发者阅读。1.1 为什么需要Agent-Reach多Agent协作的三大痛点先说清楚背景。过去一年多Agent开发经历了一次明显的范式转移早期大家做一个单Agent什么任务都往里塞靠提示词硬撑后来发现单Agent处理复杂任务时上下文容易爆、工具调用容易乱、错误容易连锁放大于是开始拆分——把大任务拆成多个小Agent每个Agent专注一个子任务。这个思路本身没问题但拆分之后立刻遇到了三个新问题。第一个问题消息格式不统一。Agent A输出的是一段Markdown文本Agent B期望输入的是JSON对象Agent C只认XML。这些Agent虽然是同一个系统里的组件但它们的“语言”完全不同。你不得不在每个Agent之间写转换器、适配器、胶水代码项目复杂度呈指数级上升。第二个问题Agent之间的调用关系是硬编码的。我在一个项目里用了三个Agent一个负责意图识别一个负责数据查询一个负责生成回复。这三个Agent之间的调用关系是写死在代码里的——意图识别Agent调用数据查询Agent数据查询Agent调用生成Agent。看起来没问题但当你把Agent数量从3个扩展到30个时这套硬编码的调用图就变成了一团乱麻。你想新增一个Agent进去参与协作得改一堆调用代码。第三个问题没有标准化的动作调用机制。Agent-Agents之间的协作本质上是动作的调用与被调用。但不同Agent对“动作”的定义千差万别有的Agent暴露HTTP接口有的走gRPC有的直接在进程内定义了一个函数。你没法用统一的方式去“指挥”另一个Agent干活。Agent-Reach针对这三个痛点给出的答案是定义一套完整的Agent互联协议然后提供一个轻量级的SDK软件开发工具包让你在不改造现有Agent核心逻辑的前提下把Agent接入到一个统一的协作网络中。这套方案的核心适用场景包括企业内部多Agent工作流编排、跨团队Agent能力共享、以及需要把多个第三方Agent产品整合到统一体系里的集成项目。如果你是这些场景的开发者Agent-Reach能省掉的胶水代码量非常可观。2. Agent-Reach的设计思路拆解2.1 核心设计哲学让Agent互联变成“配置项”而不是“代码”我第一次接触Agent-Reach时脑子里有一个疑问市面上已经有不少Agent编排框架了为什么还要再造一个轮子深入看它的设计之后我的理解是——它没有把自己定位成另一个编排框架而是把自己定位成一套互联基础设施。什么是互联基础设施类比一下你家里有很多电器每个电器都有自己的功能但要让它们协同工作你得有统一的插座标准、电力协议和网络连接。Agent-Reach做的就是Agent世界的“插座标准”——它不关心你插上来的是冰箱还是洗衣机它只关心你遵循了统一的接口规范。这套设计哲学体现在三个方面第一极简的核心协议。Agent-Reach的核心协议只有三类消息发现消息Discovery、调用请求Request、调用响应Response。没有复杂的握手流程、没有冗长的会话管理所有Agent之间的交互都围绕这三类消息展开。这种极简设计的直接好处是接入成本低、排查问题容易。第二松耦合的通信模型。Agent-Reach采用的是“发送即忘记”的异步消息模式支持同步等待超时发送方不关心接收方是谁、在哪个节点、用什么语言写的。AB两边的代码不需要互相感知对方的存在只需要感知消息协议本身。这避免了两个Agent之间的硬编码耦合。第三消息路由与能力声明分离。每个Agent接入Agent-Reach网络时需要声明自己“能做什么”即能力清单Agent-Reach的路由器会根据请求中携带的能力标签把消息路由到对应的Agent不需要你手动维护调用关系。这套设计最直接的收益体现在一个实际案例中。我之前做了一个项目里面有一个翻译Agent和一个摘要Agent。在传统方案里如果你想让摘要Agent使用翻译Agent的能力得在摘要Agent的代码里显式调用翻译Agent的接口。用Agent-Reach后摘要Agent只需要在请求中声明“需要翻译能力”路由器自动找到翻译Agent并转发消息。新增一个更好的翻译Agent时你只需要注册它旧的翻译Agent就会被自动替代——这个过程中摘要Agent的代码一行都没改过。2.2 对比传统API调用模式Agent-Reach的核心优势也许你会想Agent之间互相调用直接用HTTP API不就行了为什么要搞一套新的协议这个质疑很合理我也用了很长时间的API直连方案。但API直连模式在处理Agent协作时有几个绕不开的缺陷缺陷一接口契约是静态的。API需要预先定义好接口路径、参数类型、返回结构。但Agent的能力往往是动态的——同一个Agent今天能处理文本明天可能接入了一个图片理解模型能力边界变了接口就得跟着改。Agent-Reach不依赖静态接口而是动态发现能力Agent的能力清单可以实时变更不会被固定接口锁定。缺陷二API调用是点对点的。假设你有10个Agent每个Agent都有可能调用其他Agent那么最多有90条调用路径。每条路径都要写代码、测试、维护。Agent-Reach的消息路由打破了这种点对点模式所有Agent都只和路由器通信调用关系完全解耦。网状的复杂度降为星状的复杂度。缺陷三API没有会话上下文的概念。Agent之间的协作往往是有上下文的A问B一个问题B的回答会影响A的下一步决策。普通HTTP API是无状态的你需要自行管理会话状态。Agent-Reach引入了一个轻量级的会话上下文机制每条消息可以携带会话ID和上下文引用Agent可以追踪整个协作过程的状态。从定位上看Agent-Reach并不试图替代你已有的API网关、微服务框架它专注的是“Agent层”的互联而不是“服务层”的互联。这个边界划得很清楚也是它能够保持轻量化的原因。2.3 关键取舍为什么要“薄”而不是“厚”我见过不少Agent中间件项目一上来就内置调度引擎、状态机、可视化编排界面、模型网关恨不得把整个Agent运行时都塞进去。Agent-Reach反其道而行它只做协议和SDK把编排逻辑完全交给你自己。这种“薄方案”有得有失。好的方面是你不需要改变现有的Agent实现方式你的Agent还是原来的代码只是多了一个“联网模块”。不好的方面是Agent-Reach不提供开箱即用的编排流程你得自己决定任务怎么拆、Agent怎么配、结果怎么汇总。从我自己的使用经验来看这种取舍是对的。Agent编排是非常业务化的逻辑每个场景的编排方式都不一样如果Agent-Reach强推一套编排模型反而会成为束缚。它把自己定位为“通信层”把“业务层”完全交还给开发者这其实是对开发者的一种尊重。3. Agent-Reach核心细节解析与配置要点3.1 核心概念节点、消息、路由机制搞懂Agent-Reach必须先掌握三个核心概念节点Node、消息Message、路由器Router。节点是Agent-Reach网络中的基本单元。每个接入的Agent实例就是一个节点节点包含三个要素唯一ID、能力清单、消息订阅地址。唯一ID是Agent的标识符能力清单描述这个节点能做什么消息订阅地址是Agent-Reach SDK软件开发工具包暴露的本地回调接口。消息是所有通信的载体Agent-Reach的消息结构如下{ message_id: uuid-string, source_node: agent-a, target_capability: summarization, message_type: request, payload: { task: summary_generation, data: ... }, session_id: optional-conversation-id, timestamp: 1735000000000, timeout_ms: 30000 }这个字段设计里最关键的是target_capability这个字段——它取代了传统调用中的“目标URL”消息不再指向某个具体的Agent而是指向某个抽象能力。具体哪个Agent来处理这条消息由路由器决定。路由器是整个网络的神经中枢负责三件事维护节点注册表知道谁在线、有什么能力、按能力标签路由消息、管理会话状态。路由器可以是一个独立部署的服务也可以嵌入在某个主导Agent里对轻量场景。下面是一个路由器路由逻辑的伪代码示意def route_message(message): capability message[target_capability] candidates node_registry.find_nodes_by_capability(capability) if not candidates: return create_error_response(no_agent_available, capability) # 按负载均衡策略选择目标节点 target_node select_target(candidates, strategyleast_loaded) forward_message(target_node, message)这三个概念组合起来Agent之间的协作流程就变成了这样发送方节点构造一条消息 → 指定目标能力 → 发给路由器 → 路由器找到具备该能力的节点 → 转发消息 → 接收方节点处理并返回响应。整个过程发送方不需要知道接收方是谁。3.2 环境准备与SDK快速接入配置好Agent-Reach核心流程分四步安装SDK、初始化网络配置、注册Agent到Router、实现消息处理回调。下面以Python环境为例说明关键步骤。第一步安装依赖。pip install agent-reach-pyAgent-Reach的Python SDK只依赖pydantic和aiohttp没有重型依赖这一点让我很满意——没有捆绑一些奇奇怪怪的第三方库装完就能用。第二步初始化Agent节点。from agent_reach import AgentNode, Capability # 创建节点声明能力 node AgentNode( node_idagent-a, capabilities[ Capability(namesummarization, description文本摘要生成), Capability(namekeyword_extraction, description关键词提取), ], router_urlhttp://localhost:8900, ) # 启动节点注册到路由器 await node.start()注册成功后会有一个握手确认路由器会维护这个节点的在线状态。如果Agent偶尔掉线路由器会在短暂延迟后自动摘除该节点避免消息路由到不可用的Agent上。第三步实现消息处理回调。node.handle_capability(summarization) async def handle_summary(message): # 解包消息提取待处理数据 payload message.payload text payload[data] # 调用自己内部的大模型,或其他处理逻辑 result your_llm_chain.summarize(text) # 返回结构化响应 return { status: success, summary: result, source_node: message.source_node, }注册回调时注意一个隐含的机制反序列化后的消息会自动校验消息格式和字段类型如果消息结构中payload子字段类型不匹配将直接触发异常这意味着你不需要在回调里做字段校验Agent-Reach帮你挡掉了大量脏数据。第四步发送请求调用其他Agent能力。from agent_reach import AgentClient client AgentClient(router_urlhttp://localhost:8900) # 请求调用summarization能力指定目标能力而不是具体节点 response await client.request( target_capabilitysummarization, payload{data: 这里是待摘要的文本内容}, timeout_ms30000, ) print(response.result()) # 获取摘要结果所有核心代码就是个这个量级不需要掌握复杂配置项就可以跑起来。3.3 消息序列化与数据格式约定Agent-Reach的默认消息序列化格式是JSONSDK内置了从消息结构到JSON的序列化器和反序列化器。默认模式下payload字段只允许包含JSON可序列化的数据类型dict、list、str、int等这意味着你不能直接在消息里传一个复杂的Python对象或二进制内容。如果需要在Agent之间传递非结构化数据有两种解法解法一把数据转成Base64字符串放进payload。因为Base64后的字符串是JSON安全的。这个方法虽然直观但有几个缺点消息体积变得很大膨胀约33%接收方还需要做幂等性检查。解法二使用Agent-Reach引用的附件机制。在消息体中只放一个data_reference字段指向共享存储中的文件路径。这种方式适合传递大文件、图片、音频等难以直接塞进JSON的内容。配置方式如下{ message_type: request, target_capability: image_captioning, payload: { image_path: shared://data/images/scene01.jpg, data_reference: true } }传输大文件的场景下我强烈建议使用方案二。消息本体越小传输效率越高排查问题也越容易——你能很清晰地看到消息体里只有引用信息没有冗余数据。4. 实操过程从零搭建一个跨Agent协作场景4.1 场景设定多Agent的项目进度推送系统理论讲再多不如实操一遍。我在这里完整拆解一个Demo项目用Agent-Reach搭建一个多Agent协作系统。场景设定如下系统里有两个Agent一个负责从多个渠道收集项目任务数据另一个Agent需要按要求格式化并推送到微信群。在Agent-Reach之前这两个Agent的协作代码会非常耦合用Agent-Reach之后整个过程变成了消息的发布与订阅。这个Demo虽然小但涵盖了多Agent协作的核心元素异构Agent接入、能力声明、异步消息推送、状态传递。4.2 步骤一搭建Router并验证连通性首先需要跑起来一个Router。Agent-Reach的Python发行包里内置了一个独立Router服务器开发环境下一条命令即可启动agent-reach-router --host 0.0.0.0 --port 8900启动后可以看到Router打印出一段配置信息说明监听地址和端口。为了后续方便我们必须确认两个接口是否工作正常curl http://localhost:8900/healthz curl http://localhost:8900/nodes前者返回健康状态后者返回当前已注册节点列表。验证这些接口可以在Agent接入之前就排除网络问题避免后面排查时理不清。4.3 步骤二创建数据采集Agent并注册能力创建第一个Agent数据采集Agent注册到Router上并声明自己具备project_data_fetch能力。from agent_reach import AgentNode, Capability import uuid collector AgentNode( node_idproject-data-collector, capabilities[Capability( nameproject_data_fetch, description从项目协作平台拉取任务进度数据, )], router_urlhttp://localhost:8900, ) collector.handle_capability(project_data_fetch) async def fetch_project_data(message): # 这里模拟从项目平台上拉取数据 tasks [ {name: 前端页面开发, status: in_progress, progress: 70}, {name: 后端接口联调, status: completed, progress: 100}, {name: 数据库迁移, status: pending, progress: 0}, ] return { status: success, tasks: tasks, fetched_at: 2025-01-05T10:00:00Z, } await collector.start()这一步的关键点在于这个Agent只需要实现自己的本职工作完全不关心“谁会来调用它”“数据被谁拿去用”。4.4 步骤三创建消息推送Agent并声明推送能力创建第二个Agent消息推送Agent注册notification_push能力。from agent_reach import AgentNode, Capability pusher AgentNode( node_idnotification-pusher, capabilities[Capability( namenotification_push, description将消息格式化并推送到群聊机器人, )], router_urlhttp://localhost:8900, ) pusher.handle_capability(notification_push) async def push_notification(message): payload message.payload formatted_text format_project_update(payload[tasks]) # 调用群机器人API await webhook_client.send(formatted_text) return {status: success, pushed: True} await pusher.start()注意这里format_project_update这个格式化函数我单独提取出来原因是本Agent实际执行动作是发送推送核心逻辑在push_notification里会写得很长拆出来更易读。4.5 步骤四从调用方发起跨Agent协作请求这里的“调用方”可以是另一个Agent也可以是一段主动发起任务的逻辑——比如一个定时器。在主控逻辑中我们这样做from agent_reach import RouterClient client RouterClient(router_urlhttp://localhost:8900) # 先让数据采集Agent工作 fetch_result await client.request( target_capabilityproject_data_fetch, payload{}, ) if fetch_result.status success: tasks fetch_result.result()[tasks] # 将获取到的数据作为消息数据请求推送Agent执行推送 push_result await client.request( target_capabilitynotification_push, payload{tasks: tasks}, ) print(推送结果:, push_result.result())整个过程中调用方完全不需要知道数据采集Agent具体是谁也不需要知道推送Agent的HTTP接口是什么它只需要一个目标能力和一份数据。如果后续数据采集Agent替换成另一个更高效实现只需要把它注册到同一条能力上调用方代码一行都不用改。4.6 实操心得Agent协作编排的边界怎么划做完这几步之后我最大的体会是Agent-Reach把“协作”这件事的基础设施搭好了但真正的“编排”逻辑还是得自己想清楚。具体来说有三个边界我建议在动手前先想明白边界一哪些事情应该由Agent本地处理哪些应该发消息出去我的经验是凡是“信息获取”和“信息输出”类的任务优先考虑发消息出去凡是“信息加工”和“决策判断”类的任务留在Agent本地处理。因为获取和输出通常依赖外部资源适合通过能力调用解耦加工和判断依赖模型能力不适合走消息传递。边界二同步等待还是异步处理Agent-Reach支持两种模式等待响应和发送即忘记。我踩过一个坑是把所有消息都设成同步等待导致某些耗时的Agent调用拖慢了整个流程。后来按任务类型区分关键路径上的调用用同步等待非关键的通知类调用全设成异步整体吞吐提升明显。边界三如何设计合理的超时时间如果接收方Agent依赖外部大模型接口生成结果处理时间天然较长。我把timeout_ms设置成大模型响应时间的上限值并额外加了一点缓冲。不要设置得太短导致频繁超时也不要设置得太长让问题积累。5. 常见问题与排查技巧实录5.1 问题一消息发送成功但Agent没收到场景调用方返回的消息状态是成功但对应Agent没有触发处理逻辑。排查思路确认Agent节点的在线状态调用Router的节点列表接口看目标Agent是否还注册在网络上。确认能力名称是否完全匹配Agent-Reach对能力名称的匹配是精确匹配区分大小写如果你的调用方写的summary而Agent注册的是Summarization路由会失败调用方拿到的不是错误而是“找不到节点”的响应。检查消息监听循环是否在Agent进程内正常工作如果你在Agent进程内做了异步任务可能影响消息处理的执行。这类问题九成以上都是能力名称不匹配所以注册和请求时统一用小写加下划线的规范命名可以省掉大量排查时间。5.2 问题二Agent处理结果超时场景某些Agent的处理逻辑很重导致经常超时调用方等不到结果。解决方案有两个方向方向一提升超时参数。但治标不治本。方向二把重逻辑Agent改为异步处理结果回推模式。具体做法是Agent收到请求后立即返回“处理中”状态处理完成后主动调用回调接口或给消息Topic发完成通知。我在一个文档生成Agent上用了这个方案效果立竿见影——调用方不再傻等Agent也能从容处理耗时的文档生成任务。5.3 问题三消息体过大导致传输缓慢场景在Agent之间传递含大量文本内容的payload时消息传输和处理延迟明显。根因分析Agent-Reach的消息是在内存中传递的如果消息体携带几十MB的文本或Base64数据序列化、网络传输、反序列化都会变慢而且整个链路的延迟都会被拖垮。推荐方案大文件走data_reference引用方式不直接进消息体。如果必须传大文本考虑压缩后放入消息体接入SDK的压缩钩子。拆分消息为多个小消息分块传输在接收端聚合。5.4 问题四Agent重复注册导致路由混乱场景同一个Agent部署了多个实例都注册到了Router上导致请求被随机分发到不同实例状态不一致。在Agent-Reach中这种情况比较常见因为SDK会自动生成随机端口。解决方案很明确在部署参数中显式指定node_id让多实例共享同一个节点ID这样Router会把它们当成同一节点的多个副本做负载均衡而非独立节点。我一直用这个方式处理多实例部署场景效果稳定。5.5 问题五会话上下文丢失场景A Agent连续调用B Agent多次B Agent记不住之前的调用状态。Agent-Reach的消息结构里有一个sesssion_id字段我把拼写错误留在这里实际是session_id文末会说明调用方在发起多轮请求时如果传了同一个session_id接收方Agent可以基于它维护多轮上下文。如果你发现会话上下文丢失检查是不是每次请求都重新生成了session_id。实践中我会单独维护一个session映射表像这样session_contexts {} def get_session(session_id): if session_id not in session_contexts: session_contexts[session_id] {} return session_contexts[session_id]然后在消息处理函数末尾把本轮的状态回写到session_contexts[session_id]里保证多轮协作的连续性。5.6 常见问题速查表问题可能原因处理动作消息路由失败能力名称不匹配检查能力注册名与请求目标是否一致Agent从未收到消息节点未注册/已掉线查看Router节点列表确认在线状态请求超时目标Agent处理太慢 / 处理任务积压设置合理超时或改异步模式消息体过大往payload里塞了大文件改用data_reference引用机制多次调用状态不连续缺失session_id多轮请求中显式复用session_idAgent调用返回错误payload字段类型不符使用SDK内置字段校验提前发现这张速查表是我在实际项目中沉淀下来的基本覆盖了Agent-Reach最常见的故障场景。如果你遇到的问题不在这张表里优先查Router日志——Agent-Reach路由器的日志非常详细会记录每一条消息的路由轨迹相比在代码里打日志排查效率会高出不少。6. 延伸思考Agent-Reach在实际系统中的落地建议6.1 如何接入已有Agent体系而不破坏现有架构做实际项目的人最怕什么最怕引入一个新技术导致现有系统大规模返工。Agent-Reach在这方面的侵入性控制得不错你不需要把已有的Agent重写一遍SDK集成遵循“渐进式改造”思路先让需要协作的Agent挂上Agent-Reach节点单独工作的Agent保持原样两者互不干扰。跑稳定了再把其余Agent逐步接入。我建议接入次序如下先搭建Router确认基础通信链路可用。把你系统中“下游Agent”被调用方先接入——它们只需要挂一个消息处理回调改动最小。再接入“上游Agent”调用方替换原有硬编码调用代码。最后统一完善能力清单和消息Schema文档。这样每一层改造都有验证的样本出问题可以快速定位到是接入问题还是Router问题。注意升级现有API时不建议同时大幅度调整内部逻辑两边同时变更会加大排错难度。6.2 路由策略与负载均衡的配置建议Agent-Reach的路由器支持自定义路由策略。默认策略是最少负载least_loaded但是不同场景下可能不够灵活优先本地路由同机部署的Agent优先本地通信减少网络开销。按Agent权重路由让性能更强的Agent承担更多请求。按会话粘性路由同一个会话的消息始终路由到同一个Agent确保状态连续。在我做过的系统里会话粘性路由策略最实用。因为多轮会话的Agent状态往往是有记忆的如果每轮请求都路由到不同实例状态维护成本极高。会话粘性策略则消除了这个烦恼。6.3 嵌入到你自己的框架里如果你用的不是PythonAgent-Reach还提供了轻量级通信协议你可以在自己的框架里实现同样的协议。协议内容很短核心就是三条消息的JSON结构和路由规则。如果你的Agent是Java或Go编写的直接按协议文档实现一个客户端即可。这里不展开讲协议细节了——来看文档更合适需要的时候自然会用上。6.4 与主流Agent框架的对接可能性目前LangChain、AutoGen这些框架都很火我在实际中会把它们和Agent-Reach结合起来使用让框架里的Agent在关键能力调用处接入Agent-Reach节点使跨框架通信成为可能。一个LangChain的Agent可以请求一个AutoGen的Agent执行代码生成任务中间通过能力名解耦不关心对方的实现框架。这是一个很好的互补形态Agent-Reach管通信框架管Agent内部的推理工具链。最后的操作感想从第一次搭通Agent-Reach到把这些经验总结出来我中间踩了不少坑。最大的一个感受是Agent互联的技术难点根本不在通信本身——TCP、HTTP这些协议都足够成熟真正的难点在于让不同Agent用统一的语言表达“我要什么”“我能给什么”并且让这种表达能力不绑定在具体实现上。如果你现在正准备做多Agent系统我的建议是不要一上来就追求复杂的编排引擎先把“Agent之间怎么说话”这件基础的事想清楚。Agent-Reach的价值就在于它给了你一套不用重复造轮子的基础协议剩下的编排逻辑、状态管理、业务串联都是建立在这个稳固底座上的。这套底座搭好之后扩展Agent数量、替换Agent实现、接入第三方Agent都会变得惊人的轻松。我现在手里的几个项目已经全部基于Agent-Reach重做了Agent通信层最直观的收益是新Agent上线只需要注册能力不需要改动任何已有代码。这种“即插即用”的体验才是Agent协作系统应该有的样子。
返回列表