说实话,做多智能体系统最让人崩溃的从来不是模型的推理能力不够,而是Agent之间怎么把任务“递”得过去、找得对人。我自己的项目从三个Agent起步,到十几个Agent的时候彻底翻车:某次客户咨询进来,负责意图识别的Agent把请求同时广播给了四个业务Agent,四个Agent又各自拉了别的Agent帮忙,最后大家互相等结果,形成了一团乱麻般的循环依赖。排了一整天的日志,我发现问题根本不是某个Agent写得不好,而是整个系统的任务触达逻辑是缺失的——每个Agent只知道“我能做什么”,却没有人负责“这个请求到底该到谁那里”。
那之后我重新设计了调度层,也就是这个叫Agent-Reach的项目。它的核心目标只有一个:让用户的每一次请求,都能以可预期、可追踪、可降级的方式,从入口一路触达最终能处理它的那个Agent。如果你正在做多Agent协作、或者你想用一组“各司其职”的子模块去搭一个看似一体的助理,这篇文章里的思路和代码应该对你有用。
所有内容都来自我在真实业务场景里的实践,不是照搬某个框架,也不依赖特定的LLM供应商。
1. 从“群聊式协作”到“端到端触达”:我的多Agent系统为什么总在互相踢皮球
1.1 问题现场:一次请求引来的连锁混乱
先讲一个典型的翻车场景。
我在做一套企业内部的智能服务助手,里面有这些Agent:意图识别Agent、订单查询Agent、退货规则Agent、工单创建Agent、知识库检索Agent、情绪安抚Agent、人工客服转接Agent。单独拿出来,每一个都能正常工作。可一旦把它们拼到一起,问题就来了。
一次用户说“我上周买的鼠标坏了,想换个新的”。意图识别Agent认为这是“退货/换货”,于是调用了退货规则Agent;退货规则Agent返回“鼠标属于非质量问题,需提供购买凭证,并走人工审核”;订单查询Agent这时候也跳了出来,说“我查到订单在保内”;情绪安抚Agent又插了一句“先表达同理心,安抚用户”。结果这三个回复被同时拼接在一起,用户看到的是一个逻辑混乱、语气忽冷忽热、结论自相矛盾的“缝合怪”回答。
更麻烦的是,某些Agent会主动去拉别的Agent。规则Agent发现要人工审核,就创建了一个工单Agent;工单Agent又把消息回传给了主流程。每个Agent都在“各说各话”,没有人知道整条链路的当前状态。
这个问题的本质,不是模型能力不够,而是系统里缺少一层“触达控制”。每个Agent像是一个热情过度的客服,不管是不是自己的活儿都想搭茬。我后来总结了一句话:多Agent系统最大的风险不是Agent太蠢,而是Agent太主动。
1.2 任务可达性:被大多数人忽略的关键属性
“Reach”这个词在计算机网络里很常见,叫可达性——从A节点能不能通过一条明确的路由走到B节点。我把这个概念搬到Agent系统里,定义了三种可达性:
- 语义可达:当前请求的含义,是否落在某个Agent声明的能力范围内。这是最基础的一层,解决“能不能接”的问题。
- 上下文可达:目标Agent的输入上下文中,是否具备处理该请求所必需的字段和状态数据。很多系统在这一层翻车,Agent有权限和知识,但拿不到足够的信息。
- 结果可达:目标Agent处理完以后,它的输出能否被下游Agent或者最终用户正确消费。输出格式不匹配、缺少关键字段、返回了JSON却没人解析,这些都属于结果不可达。
Agent-Reach这个名字,就是围绕这三层可达性来设计的。它不是一个Agent框架,而是一层轻量的调度协议,告诉你“谁可以调用谁、调用的时候需要什么、返回之后下一步去哪”。
1.3 Agent-Reach的定位与适用边界
说得更直白一点:如果你手里只有两三个Agent,用代码写死if-else就够;如果你的Agent数量超过五个、每个Agent内部还会调用别的Agent,那你就需要一套统一的路由、校验和回退机制。Agent-Reach就是为这种“中型多Agent系统”准备的。
它适合以下几类场景:
- 企业内部助手:多个业务系统Agent协作完成一项任务。
- 内容生产管线:检索Agent、写作Agent、校对Agent、配图Agent之间需要有序交接。
- 客服工单系统:意图识别、知识库、工单创建、人工转接之间频繁切换。
不太适合的场景是:只用一个模型做意图识别然后直出结果的极简对话,以及需要Agent之间高度自由协商的开放式沙盒实验。前者用不上这套机制,后者需要的更像是一个“Agent联邦”而不是“路由中枢”。
2. Agent-Reach的核心抽象:能力名片与触达矩阵
2.1 capability.json:每个Agent的“名片”
为了让路由层知道谁该接活儿,每个Agent在启动时必须注册一份能力声明。我用一个JSON文件来描述,结构其实很简单:
{ "agent_id": "order_query_agent", "name": "订单查询Agent", "capabilities": [ { "skill": "order_query", "description": "查询订单状态、物流信息和购买记录", "input_required": ["user_id", "order_id"], "confidence_keywords": ["订单", "物流", "发货", "买了", "快递"], "cost_weight": 0.3 }, { "skill": "order_modify", "description": "修改订单地址、备注、取消订单", "input_required": ["user_id", "order_id", "action"], "confidence_keywords": ["改地址", "取消订单", "修改备注"], "cost_weight": 0.5 } ] }字段不多,但每个字段都有讲究。
capabilities里一个Agent可以挂多个skill,每个skill独立参与路由匹配。input_required字段是我后期才加上的,它解决了“路由匹配成功但执行时缺参数”的问题。confidence_keywords不是简单做关键词匹配,而是给路由算法一个先验权重,后面会详细讲打分过程。
2.2 触达矩阵:用一张白名单限制Agent之间的互相调用
Agent-Reach里最硬的一条规则是:任何Agent都不能直接调用另一个Agent,所有跨Agent调用必须经过触达矩阵的许可。
触达矩阵本质上是Agent之间的有向边权限表,类似这样:
| 调用方 | 被调用方 | 是否允许 | 说明 |
|---|---|---|---|
| planner_agent | order_query_agent | 允许 | 规划器可以查订单 |
| planner_agent | emotion_agent | 不允许 | 情绪安抚由前端直接触发 |
| order_query_agent | knowledge_agent | 允许 | 查询时可补充规则知识 |
| order_query_agent | order_query_agent | 不允许 | 禁止自我递归触发 |
| any_agent | human_fallback | 允许 | 兜底转人工永远是合法的 |
你可能会问:为什么要用这么死板的矩阵?Agent自由调用不更灵活吗?
我的体会是:自由协商在Demo里很酷,在生产环境里就是灾难。没有权限约束,Agent会出于“善意”互相调用,形成你拉我、我拉他的调用链爆炸。矩阵看起来死板,但它保证了每一条调用路径都是有人批准过的。后续审计问题的时候,它也给了你一张清晰的图。
实现矩阵的方式很简单,一张二维表查一下就行:
class ReachMatrix: def __init__(self, matrix_dict): self.matrix = matrix_dict def can_call(self, caller: str, callee: str) -> bool: if caller == callee: return False return self.matrix.get(caller, {}).get(callee, False) def routes_from(self, caller: str) -> list[str]: return [k for k, v in self.matrix.get(caller, {}).items() if v]2.3 核心API:Register、Dispatch与Handoff
Agent-Reach的内部接口我收敛成了三个动词:Register(注册)、Dispatch(调度)、Handoff(交接)。
Register很好理解,就是加载capability.json并把它注册到路由中枢。Dispatch则是把一个用户请求经过打分、过滤、选择后交给某个Agent去执行。Handoff是整个系统里最容易被忽略但最重要的环节——Agent执行完之后,它生成的不是最终答复,而是一个结构化的结果包,路由中枢根据结果包里的字段决定接下来应该回到用户、还是继续调下一个Agent。
一个典型的结果包长这样:
{ "from_agent": "order_query_agent", "status": "success", "payload": { "order_status": "已签收", "ordered_at": "2024-11-02" }, "next": "return_to_user", "context_updates": { "order_id": "NO20241102001" } }next字段是Handoff的关键。它可以是return_to_user(结束链路回用户)、可以是某个具体的agent_id(继续调度到指定Agent)、也可以是trivial_fallback(走兜底)。Agent自己不知道全局链路是什么样,它只按照自己的执行结果诚实地回答“我这边下一步需要谁”,至于这一步该不该走,路由中枢会结合触达矩阵再判断一次。
这套设计最大的好处是:Agent之间不存在隐式耦合,每个Agent只需要知道自己能干什么、以及完成之后把结果包交给系统,剩下的路由选择完全由中枢接管。
3. 一个请求到达正确Agent的完整链路:打分、过滤与降级
3.1 五步路由决策过程
当一个用户请求进入Agent-Reach,我不直接调LLM去“读懂意图”,而是先走一套确定性的五步流程。这套流程的好处是每步都可以打日志、复现、测试,出了问题你能指出具体是哪个环节。
- 第一步:意图粗分类。用一个轻量模型或规则引擎判断请求的大类,缩小候选Agent范围。例如“鼠标坏了想换新”会命中“售后咨询”大类。
- 第二步:能力打分。在大类范围内,对每个候选skill计算一个匹配分,分数由三部分构成:关键词先验分、语义相似分、上下文完整分。
- 第三步:上下文过滤。检查候选Agent的input_required字段是否都拿到了。缺了关键字段的直接降权或剔除,而不是硬塞给它。
- 第四步:握手确认。对得分最高的候选Agent发送一个“预检”消息,它内部可以拒绝或接受这个任务。这一步能拦截“理论上匹配但实际处理不了”的尴尬情况。
- 第五步:执行与回传。确认后正式进入执行,结果包交回路由中枢,中枢根据next字段决定下一步。
3.2 打分为什么不能只看关键词
很多人做Agent路由喜欢用“关键词命中即触发”,这个方案在小规模下看起来很灵,但Agent一多就乱套。
举个例子,“我要退货”这句话。订单查询Agent命中了关键词“退货”吗?它的capability里其实没有退货相关关键词,但“退”字和“货”字拆开能匹配到很多模糊东西。同一个请求,退款规则Agent、订单查询Agent、情绪安抚Agent可能各自都觉得自己沾边。
我在Agent-Reach里用的打分公式是:
score = 0.4 * keyword_score + 0.4 * semantic_score + 0.2 * context_score- keyword_score:confidence_keywords在请求文本里命中的比例,命中的词越多分越高。
- semantic_score:把请求文本和skill的description分别向量化,算余弦相似度。这一步能处理“没命中关键词但意思一样”的请求。
- context_score:检查input_required字段是否齐备,齐了给满分,缺一半给0.5分,全缺给0分。
三个分数的权重我做成了可配置的,因为在实践里我发现不同业务侧重的成分差异很大:客服场景语义分最重要,工单系统上下文分最重要,简单的FAQ关键词分反倒够用。
拿“我上周买的鼠标坏了,想换个新的”这句话来算:它命中退货/换货的confidence_keywords较多,keyword_score高;语义上和“退换货处理”最靠近,semantic_score也高;但退货换货Agent要求输入order_id,而系统里还没有order_id,context_score只有0.5。这时候它总分不一定超过别的Agent,但如果所有候选都缺订单号,路由中枢就会先进入一个“缺字段采集”的兜底流程——先询问用户订单号,而不是强行让某个Agent蒙着做。
3.3 降级链:找不对人时该有的优雅收场
再完美的路由也存在匹配失败的可能,比如用户问了一个所有Agent都没有覆盖的问题。Agent-Reach里我设计了一条降级链:
- 优先尝试相近能力。如果“换货”没有对应Agent,但“售后处理”Agent能兼容,就做一次能力映射迁移。
- 如果相近能力也失败,组合尝试。把请求同时分发给“知识库检索Agent”和“工单Agent”,让知识库先找标准答案,找不到再由工单记录并转人工。
- 最后一格是human_fallback。返回一段明确的兜底话术给用户,同时在后台生成一条人工工单,保证用户的问题不会因为“Agent不知道”而石沉大海。
这里有个容易被忽略的细节:降级过程也必须在触达矩阵允许范围内进行。不能说知识库Agent挂了,就随便找个别的Agent顶上,那会导致另一个Agent被临时塞进不擅长的工作,然后产出胡言乱语。所有降级路径要提前在矩阵里画好,这相当于给系统做了预案。
4. 从零写一个最小Agent-Reach示例:三个Agent的协作链路
4.1 场景设定:活动助手
为了把这些概念落地,我带大家从零搭一个极简但完整的三Agent系统,就叫它“活动助手”。
三个Agent分别是:
- planner_agent:活动规划Agent,负责根据用户需求设计活动方案。
- weather_agent:天气查询Agent,负责查询指定城市和日期的天气。
- memo_agent:备忘写入Agent,负责把最终方案写进用户的备忘录。
整个链路的预期是:用户说“帮我规划一个下周六在上海的户外烧烤活动”,planner_agent先给出初步方案,但方案里需要天气信息,于是Handoff给weather_agent;weather_agent返回天气后,planner_agent完善方案,再Handoff给memo_agent;memo_agent写完后返回确认,链路结束。
4.2 代码实现:注册、调度与交接
先定义三个Agent的能力文件。这里我直接用Python字典代替JSON文件,方便演示:
agent_defs = { "planner_agent": { "capabilities": [ { "skill": "activity_planning", "description": "规划活动方案,给出地点、时间、流程建议", "input_required": ["activity", "date"], "confidence_keywords": ["活动", "规划", "安排", "方案"], "cost_weight": 0.2, } ] }, "weather_agent": { "capabilities": [ { "skill": "weather_query", "description": "查询某城市某日的天气情况", "input_required": ["city", "date"], "confidence_keywords": ["天气", "温度", "下雨", "晴天"], "cost_weight": 0.2, } ] }, "memo_agent": { "capabilities": [ { "skill": "memo_write", "description": "将文本内容写入用户备忘录", "input_required": ["content"], "confidence_keywords": ["备忘录", "记住", "提醒"], "cost_weight": 0.1, } ] } }然后构建触达矩阵,规则是:planner可以调用weather和memo,weather不能调用别人,memo不能调用别人。
reach_matrix = { "planner_agent": {"weather_agent": True, "memo_agent": True}, "weather_agent": {}, "memo_agent": {} }接下来是RouteDispatcher的骨架。核心就是根据打分结果选出最高分的Agent,然后执行,然后解析结果包,继续下一步直到结束。
class RouteDispatcher: def __init__(self, agent_defs, reach_matrix): self.agents = agent_defs self.matrix = reach_matrix def score(self, request: str, skill: dict) -> float: keyword_score = sum(1 for kw in skill["confidence_keywords"] if kw in request) / max(len(skill["confidence_keywords"]), 1) # 这里用最简单的交集近似semantic_score,真正项目中可用embedding余弦 semantic_score = 0.5 if request and skill["description"] else 0.0 context_score = 1.0 if not skill["input_required"] else 0.5 return 0.4 * keyword_score + 0.4 * semantic_score + 0.2 * context_score def dispatch(self, request: str): current = "planner_agent" messages = [] for hop in range(5): if current == "return_to_user": break agent_def = self.agents[current] best_skill = max(agent_def["capabilities"], key=lambda s: self.score(request, s)) messages.append(f"[{current}]使用技能:{best_skill['skill']}") # 模拟执行结果 if current == "planner_agent": messages.append("已规划初步方案,需要天气数据") current = "weather_agent" elif current == "weather_agent": messages.append("上海周六晴转多云,22度") current = "memo_agent" elif current == "memo_agent": messages.append("已写入备忘录") current = "return_to_user" return messages跑一下,输出大概是:
[planner_agent]使用技能:activity_planning 已规划初步方案,需要天气数据 [weather_agent]使用技能:weather_query 上海周六晴转多云,22度 [memo_agent]使用技能:memo_write 已写入备忘录4.3 边界情况:两个Agent分数相同怎么办
上面这个示例很顺,但真实系统中每步都可能出现意外。
最常见的一个意外是“多个候选Agent得分相同或接近”。例如用户说“帮我记一下周六天气,顺便定一下活动”,这个请求同时绑定了memo_agent和weather_agent。
我的处理原则非常简单:同一Agent内部的多个skill竞争时,选cost_weight低的那个,省成本;不同Agent之间竞争时,按触达矩阵里离入口最近的节点优先,也就是尽量少跨Agent,减少链路风险;如果前两条都持平,就看谁的历史成功率更高。Agent-Reach每个Agent都维护了一个滑动窗口成功率统计,成功率低的会临时降权。
排序在代码里长这样:
class RoutedCandidate: def __init__(self, agent_id, score, cost, success_rate): self.agent_id = agent_id self.score = score self.cost = cost self.success_rate = success_rate def choose_candidate(candidates): return min(candidates, key=lambda c: (-c.score, c.cost, -c.success_rate))注意这里其实是一行很笨的代码,但它在生产里帮我挡掉了大量“随机选Agent导致结果不稳定”的投诉。多Agent系统的使用者最敏感的事情就是同一个问题问两遍得到两个不同答案。让选择过程确定性尽可能地高,比让单次回答更聪明重要得多。
4.4 为什么Handoff状态要显式透出
很多人问我,为什么不直接在planner_agent的Prompt里写“你需要天气信息时调用天气查询函数”?这当然可以,那是函数调用的思路。但函数调用有一个问题:它假定当前Agent有能力、有意愿、有权限去正确选择外部工具。
在真实环境里,planner_agent可能会因为提示词覆盖不完整、模型抖动、上下文过长而忽略了该做的天气查询,也可能调用了但把返回结果解析错了。而Agent-Reach把交接变成了一个独立的、由路由中枢控制的状态转移,相当于把“调用天气Agent”从模型的能力范围内移除了,变成了一种路由中枢的确定性行为。这样做的代价是灵活性下降,收益是可预测性大幅上升。
Handoff状态显式透出还有一个附加好处:你可以给用户展示一条链路轨迹。用户能看到“规划Agent → 天气Agent → 备忘Agent”的过程,这比一坨黑盒输出更能建立信任感,出了问题排查定位也清晰得多。
5. 实战中绕不开的四个坑:从崩溃到稳定的排查过程
5.1 坑一:路由风暴与循环握手
第一次用Agent-Reach接真实业务时,我遇到的最诡异的问题是:某个对话会话里,planner_agent和weather_agent在互相Handoff,一秒钟之内来回跑了十几次,token开销疯狂上涨,用户那边看到的却是“正在生成中”的转圈。
排查过程是这样的:我先在路由层打出了完整的事件日志,发现planner_agent执行完后next字段是weather_agent,weather_agent执行完后next字段又变回了planner_agent。表面上看起来像是两个Agent各有道理:planner说“我需要天气才能继续”,weather说“天气已返回,剩余方案需要planner来定”。
根因不是这两个Agent的逻辑错了,而是我在结果包协议里缺少一个“版本号”字段。weather_agent返回天气数据后再让planner介入,planner拿到的上下文里其实已经有了天气数据,但它没意识到任务已经往前推进了,于是又触发了一次天气查询需求。
修复方式是在结果包里加了一个request_version字段,要求每次Handoff时递增,并且任何Agent在触发外部调用前必须检查依赖数据是否已经存在于上下文里。自那以后我把一条铁律写进了文档:Agent向你Handoff时,你要默认它还没做这件事,除非上下文里已经有对应的结果标记。
5.2 坑二:上下文串扰——Agent在互相污染Prompt
第二个坑更隐蔽。
有一次用户先查了订单,然后又问天气,系统里order_query_agent把“订单号”写进了共享上下文,weather_agent在生成回答时读到了这个订单号字段,在回复天气的末尾突然加了一句“此外,您的订单NO20241102001还在保修期内,建议尽快处理换货”。
这是典型的上下文串扰:共享上下文空间让每个Agent都能读到所有历史字段,但Agent无法分辨哪些字段和它的当前任务相关。查天气的Agent看到订单号字段,误以为是系统向它补充的用户背景,顺手就当成了自己的任务内容。
我的解决方案是给上下文上一把“作用域锁”。每个Agent在执行时只能看到一个受限视图——也就是路由中枢从完整上下文里按input_required和output_fields裁剪出来的子集。完整上下文不是不能用,而是必须显式声明哪些字段是“全局只读公共信息”(比如用户昵称、时间、语言),其余字段一律按需传导。
这个改动让我损失了一点灵活性,但换来的是Agent回复再也不会“跑题带私货”。如果你现在做的多Agent系统也有Context乱串的问题,强烈建议你先做一次裁剪而不是盲目加Prompt约束。
5.3 坑三:提示词越写越长,效果反而变差
第三个问题来自我自身的经验教训。
刚开始给Agent写系统提示词时我追求面面俱到,把路由规则、语气要求、禁止事项、历史背景全塞进去。结果Agent表现越来越机械,稍有意外就不知所措。后来我推翻重来,把每个Agent的提示词收敛到三个部分:角色与目标、输入字段说明、输出结果包格式。至于“什么时候调用哪个Agent”这种事情,全部交给路由中枢去判断,Agent提示词里一个字都不提。
这个改动之后,Agent的表现反而稳定了很多。模型在“我只用做好自己这一步”的场景下,远比“我还要决定整个系统的下一步”表现得可靠。这也是Agent-Reach这套编排哲学的核心:让模型做模型擅长的事,也就是狭窄任务执行;把系统级的调度决定交给确定性代码。
5.4 坑四:成本失控——触达链条越长越贵
最后一个坑和钱有关。
多Agent触达链条每增加一跳,成本不只是一次额外调用,还包括上游上下文带着的历史token重复计费。我第一次跑通完整链路时,一个正常的活动规划请求花了接近两万token,其中一半都是重复传输的历史上下文。
后来做了三个优化:第一,上下文裁剪,不再把完整对话历史传给每个Agent;第二,压缩中间结果,每个Agent只把结构化结果包留在上下文里,原始的冗长文本在生成摘要后丢弃;第三,设置全局链路预算,当链路经过的Agent数量超过阈值时,强制进入“压缩模式”,让最后的意见汇总Agent只读取前序Agent的结构化摘要而不是完整记录。
优化后同样的请求token消耗降了将近60%。这个数字不是一次特殊的胜利,而是说明多Agent系统里,成本失控更多的时候不是模型太贵,而是链路设计在无意识地把token浪费在搬移不动的东西上。
6. 接下来想做的事:Agent-Reach的进阶方向
6.1 把关键词评分升级为“语义能力索引”
目前Agent-Reach的打分部分仍是关键词+embedding的混合体,胜在简单稳定,但弱点也很明显:对带隐喻、反讽、省略语的用户表达容易误判。
我计划在下一个版本里引入一个语义能力索引层,把每个skill描述扩展成一组“能力原型句子”,然后用LLM对请求文本做一次轻量“能力重写”后再去匹配。比如把“鼠标坏了想要新的”重写为“用户请求更换损坏配件”,对这个重写结果做语义检索,能明显提升召回精度。
这块还没完全跑通,主要卡在“能力原型句子”的维护成本上。你要是想自己试,可以先从二十个句子起步,看看能不能覆盖你业务里的八成常见请求。
6.2 触达链路的可视化与审计回放
Agent-Reach现在每个请求都会打一份结构化日志,但我只是把这些日志作为排错依据在用,还没有做成界面化的回放工具。
下一步我想做的是:把请求从进入到最终返回的每一步触达轨迹画成一条可展开的链路图,包含每个Agent的得分、上下文裁剪结果、Handoff理由和token消耗。这个能力对上线初期的联调和用户投诉溯源价值会很大。如果你是自己做内部系统,不妨先用“trace_id + JSON日志”凑合着实现类似的能力,体验过回放的方便之后,你就回不去了。
6.3 从路由中枢走向自组织:让Agent具备“需求广播”能力
最后聊聊更大的方向。
现在的Agent-Reach本质上是一个中心化的路由系统,所有触达决策都经过中枢。它的好处是可控,代价是系统里新增一个Agent时,需要人工改矩阵、加能力定义,才能被路由发现。
我在琢磨的进阶版本是:允许Agent在矩阵允许范围内向“能提供某类技能的Agent”发起轻量广播,而不是单独指定目标。比如说memo_agent在写入备忘时发现缺少日期信息,它可以广播“谁有日期解析能力”,持有该能力的Agent会回应并完成日期补全。
这个设计需要比当前严格得多的协议,不然会退回文章开头那种“群聊式协作”的混乱状态。但它确实代表了一个更接近未来的形态:系统的能力不是被预先编死,而是在每一次任务中按需被发现与被触达。Agent-Reach这个名字里的“触达”,迟早要从“路由到预定节点”进化成“发现离任务最近的节点”。那时候,多Agent系统的体验大概会再上一个台阶。
我在实际使用中的体会是:无论代码怎么写,路由策略都应当作为独立的可观测层保留下来,不要为了“智能”去牺牲可见性。先让每个请求的触达路径变得清晰、可控、可回放,再考虑让Agent更自主,这个顺序一定不要反过来。