最近我在复盘团队自研的多智能体协同平台,发现真正决定项目生死的不是模型选型,也不是Prompt技巧,而是最容易被忽视的“Agent-Reach”——任务能不能准确触达该干活的智能体,执行结果能不能原路返回。单Agent演示时惊艳全场的项目,一接真实工单就崩,多半都是触达层没设计好。这篇文章我把Agent-Reach的触达层架构、调度策略、会话恢复机制和线上踩坑记录完整展开,适合正在做多Agent编排、智能客服、自动化运维流水线的工程师参考。
1. Agent-Reach到底解决什么问题:智能体数量上来了,结果全在单聊
1.1 从单Agent到多Agent:任务到“人手”之间的最后一公里
先聊一个我在多个项目里反复遇到的场景。团队把客服助手、工单分类器、知识库检索器、报表生成器分别做成了四个独立的Agent,每个单独拿出来效果都很好:客服助手能礼貌应答,工单分类器准确率超过90%,检索器召回率也达标。但把它们拼成一个完整系统后,用户发起一个投诉工单,整个链路就卡住了。
卡住的原因五花八门:有的任务没有路由到正确的Agent,被一个不擅长它的执行体抢走了;有的任务触达了Agent但上下文没传全,Agent开始胡编;有的Agent执行到一半挂了,上游完全不知道;有的Agent执行完了,结果却因为格式不对回传失败。这些问题的共性不是“模型不够聪明”,而是“任务触达链路不可靠”。
这就是Agent-Reach要解决的核心问题:在多Agent架构里,任务如何可靠地从触发点出发,准确到达目标执行体,并且执行结果能完整、可验证地返回。它相当于分布式系统里的消息投递、服务注册、负载均衡、重试熔断那一整套基础设施,只不过消息的载体从结构化数据变成了“一段带着目标和约束的自然语言任务”。
很多人会问:这不是消息队列能干的事吗?不完全一样。消息队列只保证字节级别的送达,但Agent-Reach还需要保证“语义级别的触达”。同一个任务描述,让擅长代码生成的Agent和擅长数据分析的Agent去执行,结果天差地别。所以触达层要理解任务的意图,匹配执行体的能力边界,还要在执行过程中持续保持上下文同步。这比单纯扔进Kafka队列里复杂得多。
1.2 “触达”和“生成”是两回事:生成得好不等于执行得到位
我习惯用一个类比来跟团队解释触达和生成的区别:LLM的生成能力像是一个专家的大脑,而触达层像是这个专家的日程管理系统。专家再聪明,如果他的日程安排混乱——会议通知没送到、送错了会议室、送到的会议材料是残缺的——那他的实际产出依然趋近于零。
具体到技术侧,生成解决的是“给定输入,计算输出”的问题,这是模型推理决定的;触达解决的是“给定任务,确保正确的执行体在正确的状态下执行它并返回可信结果”的问题,这是系统工程决定的。一个纯粹的Prompt工程方案可以把单Agent的准确率从80%提到90%,但很难解决三个Agent互相依赖时的死锁、数据方言不一致、上下文污染这类问题。
我在Agent-Reach的设计里,把触达层拆成了四个子模块:任务接入与意图解析、Agent能力注册与路由、会话保持与状态回传、结果校验与异常兜底。这四块对应了任务进入系统的第一公里、中间调度、状态传承和最后一公里。后面所有章节都是围绕这四块展开的。
2. 触达层设计:把任务描述变成可路由的路由令牌
2.1 任务意图解析与Agent能力注册表的匹配逻辑
触达层的第一个动作,是把外部进来的原始任务描述转换成内部可路由的结构化对象。我管这个对象叫“路由令牌”(Routing Token),它包含任务ID、意图标签、目标实体、约束条件、优先级、上下文字段和时间戳。意图解析可以用LLM做,也可以用小模型加规则做,取决于你的吞吐要求。
在我们平台上,每个Agent上线时必须提交一份能力注册表,字段大致如下:
{ "agent_id": "report-builder", "name": "报表生成器", "description": "生成周报、月报、经营分析报表,支持Excel和PDF输出", "input_schema": { "required": ["report_type", "date_range", "metrics"], "optional": ["format", "recipients"] }, "output_schema": { "type": "object", "properties": { "file_url": {"type": "string"}, "summary": {"type": "string"} } }, "endpoint": "http://agent-report-builder:8080/invoke", "rate_limit": 5, "timeout_seconds": 60, "tags": ["report", "analysis", "office"] }这里的关键不是注册表本身,而是意图解析结果与注册表的匹配策略。我试过纯向量相似度匹配,也试过纯规则匹配,最后发现最好用的是“规则兜底 + 向量排序”的混合策略:先用基于标签和输入模式规则的粗筛把候选Agent从几十个缩到三五个,再用意图向量和Agent描述向量的余弦相似度做精排。为什么这么做?因为纯向量匹配在长尾描述上容易飘,比如用户说“给我整一份上周的销售数字汇总”,它很可能匹配到“数据采集Agent”而不是“报表生成器”;而纯规则匹配又无法应对口语化的千变万化。
匹配完成后,触达层会生成路由令牌并写入任务队列。这一步我建议千万要把原始输入原样保留在令牌里,不要只存解析后的意图标签。因为LLM的意图解析本身可能出错,一旦后续环节发现路由错了,原始文本就是回溯定位的关键证据。
2.2 动态路由与优先级队列:谁空闲、谁擅长、谁在值班
路由策略我在Agent-Reach里分了三层:
第一层是确定性路由。某些任务类型有强约束,比如合规检查、财务审批,指定走特定Agent,不允许模糊匹配。这类任务直接按规则投递。
第二层是语义路由。就是上一节说的混合匹配,适用于客服问答、内容生成这类需要灵活度的场景。
第三层是负载与容错路由。当目标Agent的排队长度超过阈值、或者健康检查失败时,触达层会把任务重新路由到同能力的备用Agent,或者直接降级到人工队列。
优先级设计上,我采用的是“业务优先级 + 饥饿保护”双队列模型。高优先级任务(比如VIP客户投诉、线上故障告警)走快速通道,但每个队列都记录等待时间,长时间未处理的任务自动提升优先级。这个机制非常有必要——如果只有单一优先级,紧急任务会被大量普通任务淹没;如果没有饥饿保护,低优先级任务可能永远排不上。
给一个队列模板的参考配置:
priority_queues: p0: max_concurrency: 10 timeout_seconds: 30 retry_count: 2 p1: max_concurrency: 20 timeout_seconds: 120 retry_count: 1 p2: max_concurrency: 50 timeout_seconds: 300 retry_count: 0 starvation_protection: enabled: true promote_after_seconds: 600这套配置并不是拍脑袋定的,它跟实际的Agent执行时长强相关。比如报表生成Agent很少在30秒内完成,那就不该把它放进P0队列,否则每次都会超时重试,浪费算力还拖慢链路。所以路由的时候还要结合Agent的“预估执行时长”标签来选队列,这个标签来自Agent注册表,也可以根据历史执行数据动态更新。
3. 会话保持与状态回传:一次触达不丢上下文
3.1 会话快照与多轮任务的“失忆”困境
多Agent协作最让人头疼的问题,是上下文断裂。我举一个实际发生的例子:用户问“上个月华东区的退货率是多少?”,客服助手把问题路由给了数据分析Agent,数据分析Agent查完数据库返回“3.2%”,这时候用户追加一句“和上上个月比呢?”。按理说系统应该知道“上上个月”指的是华东区的退货率,但如果你没有维护会话快照,第二个问题就变成了一个孤立请求,数据分析Agent会一脸茫然。
Agent-Reach的做法是给每一条任务链维护一个可序列化的会话快照。快照里不光有历史消息,还有每个Agent执行时的工具调用现场——比如它查了哪几张表、用了什么过滤条件、中间产出了什么临时结果。这样当后续任务到达时,下一个Agent能直接读取前序Agent的上下文,而不是靠提示词里去“回忆”。
技术选型上,我们用的是事件溯源(Event Sourcing)而不是简单的Redis缓存。每轮交互都追加一条不可变的事件记录,会话的最新状态由事件流归约得出。好处有两个:一是方便复现问题,把事件流完整回放一遍,就能定位是哪一步把上下文搞坏的;二是方便回滚,如果某个Agent产出了错误结果,可以回退到之前的快照重新路由。
快照的存储开销是个现实问题。我统计过,一个50轮的长会话,序列化快照大概在200KB到800KB之间,2000个并发会话就是GB级的内存压力。所以快照必须分级存储:热会话放Redis,冷会话定期刷到对象存储。同时要设置会话生命周期,一般超过两小时未活跃的会话就归档清理,防止内存被遗忘的会话吃光。
3.2 结果校验与二次触达:打包重试还是换Agent执行
任务执行完,结果回传到触达层,这时候还不能直接返回给上游。我在Agent-Reach里强制要求走一遍结果校验器(Validator)。校验器分三层:
第一层是格式校验。检查返回结果是否符合Agent注册时的输出Schema,比如字段是否齐全、类型是否正确、文件链接是否有效。很多Agent返回的是一个“看起来像JSON的字符串”,这一层就能拦下来。
第二层是逻辑校验。针对业务规则做检查,比如报表日期范围是否与请求一致、金额是否为负数、条数是否超过预期阈值。逻辑校验规则可以用Python写死在触达层,也可以用另一个校验Agent动态判断。
第三层是置信度校验。Agent在执行结果里附带一个自评置信度,触达层根据任务类型设定阈值。低于阈值的任务自动进入“二次触达流程”。
二次触达不是简单重试,而是有策略选择的。我在实际项目里总结了三种路径:
- 同Agent重试:适合超时、临时性错误,传同一个任务ID,要求Agent从断点恢复。
- 换Agent重试:适合该Agent固有能力不足或多次失败,路由到同技能组的备用Agent。
- 降级到人工:适合反复失败的复杂任务,直接生成一份含完整上下文的工单,转人工处理。
重试次数我一般限制在2次以内。很多团队觉得重试越多成功率越高,实际上对LLM类的Agent而言,超过两次重试的成功率提升非常有限,反而会放大成本开销和排队阻塞。
4. 调度策略的实战调优:从压测数据到跪着调参
4.1 并发阈值怎么定:先算请求量再压测,别拍脑袋
调度参数是我踩坑最重的部分,也是Agent-Reach里最容易被低估的模块。很多工程师上来就拍一个并发数,比如“我们设50个并发”,结果压测一跑就崩。
正确做法是先算清楚业务量级。我给一个我们项目的推算示例:业务高峰每小时触发2400个任务,平均每个任务在Agent端执行5秒,那么单位时间内的并发需求大概是“每秒任务数 × 平均执行时长”。2400小时等于每秒0.67个任务,乘以5秒,约等于3.4个并发。这个数字看着很小,但它是“稳态需求”。
然后你必须考虑三个放大系数:重试放大、峰值波动放大、上下文复杂度放大。重试放大取1.2到1.5,波动放大取2到3倍,上下文复杂度放大取1.3到1.6。这样算下来,3.4的稳态并发,实际压测目标至少要打到15个并发。
压测的时候我建议直接用业务真实数据做混合场景测试,别只测单一Agent的吞吐。多Agent混跑会出现资源争抢和锁竞争,单测结果漂亮没用。我们当时做了三轮压测,第一轮15并发,P99延迟780ms,第二轮提到30并发,P99延迟飙升到4.2秒,同时开始出现大量超时重试——这就说明触达层本身没问题,但目标Agent的算力跟不上了。
所以并发阈值的优化方向不光是调Agent-Reach的参数,有时要回到Agent侧去优化执行时长。比如把长Prompt拆成并行子任务、缩短工具调用的等待超时,这些改动往往比盲目扩容更有效。
4.2 超时、熔断与降级:别让一个慢Agent拖垮整条流水线
Agent的响应时间天然比普通微服务更不稳定。模型推理受输入长度、上下文大小、外部工具延迟影响,同一个Agent处理不同的请求,耗时差出十倍是常态。这就让超时和熔断的设计变得格外重要。
超时设置我遵循一个原则:不要把超时设成所有Agent的统一值。要根据Agent注册表里的timeout_seconds字段动态设置。普通文本生成类Agent给30到60秒,涉及多步工具调用的数据分析Agent给120秒,涉及文件扫描或网页抓取的Agent给180秒。统一超时会造成两种恶果:设短了,复杂的正常任务老是被误杀;设长了,一个故障Agent会拖住大量队列资源。
熔断我用的是一套滑动窗口计数方案。对每个Agent维护最近5分钟内的成功率,如果连续失败超过阈值,或者P99延迟超过预期值持续一定时间,触达层就把这个Agent标记为“不健康”,不再向它路由新任务,转而路由到备用Agent或进入降级队列。
降级策略一定要提前定义,别等故障了才临时想。我们当时定义了三级降级:一级,切换到同能力备用Agent;二级,跳过非关键步骤,比如报表生成失败就直接返回数据摘要;三级,直接转人工。降级动作会记录到审计日志里,这样事后能回溯“当时为什么给用户返回了简化结果”。
5. 踩坑记录:三个让我改代码的线上事故
5.1 上下文炸弹:长会话把触达链路的内存打满
第一个事故发生在压测之后不久。我们上线了50个真实用户跑内测,刚开始很顺,大概20分钟后有一半请求开始报超时,再过几分钟整个触达层的内存使用率拉满,GC频繁,服务濒临假死。
查下来发现是上下文炸弹:有个客服会话持续了70多轮,每轮都携带完整历史消息去调用数据分析Agent,中间还有几次工具调用返回了完整的Excel预览数据,会话快照膨胀到接近3MB。当一个会话还扛得住,当几百个这样的大会话同时在内存里,触达层直接被压垮。
修复方案有两层。第一层是快照瘦身:给快照字段设置上限,工具调用的大块返回内容不直接存会话快照,而是存到对象存储里,快照里只留引用地址。第二层是消息压缩:超过一定轮数后,用LLM对早期消息做一次摘要,把摘要作为新的上下文基线。这套方案上线后,长会话的内存消耗下降了85%。
5.2 路由回环:两个智能体互相踢皮球
这个事故比较搞笑,但也最危险。我们有一个工单分类Agent和一个技术支持Agent,有一天分类Agent把一个模糊任务路由给了技术支持Agent,技术支持Agent觉得这不是技术问题,又丢回给分类Agent,分类Agent再次判给技术支持……任务在两个Agent之间循环了十几次,每次循环都消耗一次模型调用,不仅浪费算力,还把任务卡了一个多小时。
问题根源是路由逻辑里缺少“已尝试路径”的约束。任务令牌里只记录了当前路由目标,没记录已经被哪些Agent拒绝过。修复方案:在路由令牌里增加visited_agents列表,每次回传时追加当前Agent的ID和拒绝原因。路由层发现某个Agent已经在visited列表里,就不能再把任务路由给它,而是强制进入“争议仲裁流程”——把这个任务交给一个中立的路由Agent或人工来处理。
我还把重试次数跟路由回环关联起来:当同一个任务回传超过3次,触达层自动将该任务标记为“高风险”,触发降级策略。这个阈值不是随便拍的,回传3次以内可能还有救,超过3次再继续尝试只会恶化。
5.3 数据方言不一致:同一个字段两种解析方式
第三个事故不是崩服务,是静默出错,比崩溃更难发现。我们数据分析Agent输出的日期格式是“2025-08-01”,但报表生成Agent内部用的是“2025年8月1日”,触达层做结果校验时,日期字段格式校验规则跟报表Agent的解析器不一致,导致一个本该成功的结果被判定为格式错误,进入了重试。
这种问题本质上是Agent间缺乏共享数据契约。之后我们推行了严格的接口协议:所有Agent的出参和入参都必须引用统一的Schema定义库,日期、金额、百分比等常见类型有全局统一格式。同时在校验器里增加“字段语义校验”而不是只做“字段存在校验”——格式不对时先尝试一次标准化转换,转换不成功再报错,这样可以兼容历史版本。
6. 从Agent-Reach往外看:触达能力是一套通用基础设施
6.1 不只是AI:客服工单、供应链调度也能用同一套触达模型
Agent-Reach的设计思路,本质上就是一套“任务触达与执行保障”的通用模型。它不只是为LLM Agent服务,我把它重用在几个非AI场景里,效果也不差。
比如客服工单系统:工单进来后需要分诊到不同业务线,紧急程度判断、SLA计时、转派记录、超时升级——这些跟Agent路由几乎是同一个模型。再比如供应链异常调度:仓库上报一个库存异常,需要触达采购、物流、财务三个系统的处理人,每个处理人就是一个“人工Agent”,同样需要注册能力、路由、超时、重试、结果回传。
把这套模型抽象出来的好处是,上层业务可以在同一条触达链路上混用AI Agent和人工处理节点。AI Agent处理不了的,可以直接把令牌转给人工工作台,人工完成后再把结果回写到原链路。这种混合模式在我们实际业务里非常重要,因为纯AI方案不可能覆盖所有边界情况。
6.2 下一个发力点:触达审计与全链路可观测
Agent-Reach的最后一个关键设计,是全链路可观测。我强烈建议任何做多Agent平台的人都把审计日志当作一等公民来对待,而不是事后补救。我们给每一个任务生成唯一Trace ID,从意图解析、路由决策、Agent调用、结果校验到最终返回,全链路打点记录。日志里包含时间戳、路由决策的完整上下文(用了什么规则、向量匹配到了谁、置信度多少、为什么换路由)、每次调用的Token消耗和延迟。
做触达审计不只是为了排查问题。当你积累足够多的路由日志后,可以做路由策略的持续优化——比如发现某个口味的任务总是在两个Agent之间摇摆,说明注册表描述有重叠,需要拆分或合并Agent;比如发现某个Agent的失败集中在特定时段,说明它的上游依赖有周期性故障。这些洞察都来自于可观测数据的沉淀,而不是拍脑袋。
我也在探索触达层的跨平台扩展:让一个Agent-Reach实例可以触达不同基础设施上的Agent集群,通过统一的令牌协议跨网络边界传递任务。这个方向还处在早期阶段,但我觉得它会是多Agent平台走向产业化必须跨越的门槛。
个人经验是:如果想做多Agent平台,尽早把触达层当成和模型一样重要的核心组件来投入。模型能力会持续演进,但“任务可靠到达并返回”的工程能力,永远是系统价值的底座。不要等到线上连环事故再回头补课,那代价远比你想象的贵。