ARTICLE DETAIL

资讯详情

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

AI Agent触达层设计:让Agent真正接上业务系统的中间层实践

AI Agent触达层设计:让Agent真正接上业务系统的中间层实践 做AI Agent项目我最头疼的不是模型选型也不是编排图怎么画而是让Agent真正“碰得到”业务系统。“碰不到”前面所有推理和规划全是空中楼阁。Agent-Reach是我们内部给外部工具做统一触达的中间层项目一句话讲清楚它的作用把所有外部工具接入收口到一个统一层让Agent只关心下一步该做什么不用管该调谁、鉴权怎么做、超时怎么办。它能解决的最直接问题就是Agent说完“好的我帮你查工单”之后系统真的把工单查回来而不是满屏报错。这套东西最适合正在搭Agent应用、准备接RAG工具、做内部自动化流程的朋友参考。模型侧的Function Calling越来越成熟但真正落到工程上“最后一公里”的触达往往比模型还难伺候。今天我把Agent-Reach的设计思路、核心实现和几次真实踩坑过程一次性聊透你听完之后至少能少走几个月的弯路。1. 为什么Agent会做事之前先得有一层“触达底座”1.1 大模型缺的不是脑子是手脚先想清楚一个原点大模型本质上是一个“预测下一个Token”的系统它输出的是文本不是动作。当用户说“帮我看看工单INC001234到哪一步了”模型经过推理后最多生成一行合法的函数调用参数剩下的“真的去查ITSM系统”这件事没有任何一个Token能替你完成。这个缺口就是Agent触达层存在的意义。模型负责决策和表达意图触达层负责把这些意图变成对外部系统的真实调用再把结果变成模型看得懂的结构化信息。没有这层Agent就像脑子很灵但手脚被绑住的人想得到做不到。很多刚开始做Agent的团队会直接把工具调用写成几个Python函数扔给模型。模型说想查工单代码里就写个query_incident()。这种方式在小Demo里跑得通一旦系统变多问题立刻暴露。工具本身散落在各个业务方协议五花八门鉴权机制各不相同模型一遇到复杂场景就开始乱选工具、乱传参数。1.2 现实世界的接口又杂又乱不是一个“Function”能搞定的我拿真实情况举例。一家公司里ITSM是REST接口CMDB是GraphQL数据库直接是MySQL消息走Kafka老系统还留着XML-RPC。每个系统的返回格式也不一样有的返回result_code有的返回status有的返回data。你在写Agent时不可能让模型理解这么多差异也没必要让它理解。更麻烦的是鉴权。工单系统用OAuth2客户系统走mTLS数据库用账号密码内部服务可能还要过一层单点登录。这些凭据如果散落在Agent代码里不出两个星期就会出现Token泄露、权限交叉、过期时间不同步的问题。Agent治理变成信任重建工程。限流和超时也同样让人头疼。外部系统的稳定性通常没有想象中好某个接口平均响应200ms偶尔会飙到5秒。如果不做统一处理Agent会因为等待而白白消耗模型上下文如果重试策略写得不稳妥写操作就会重复执行给业务方制造脏数据。1.3 把触达独立出来是Agent工程化的必然结果经历了几个项目之后我的选择很明确把“触达”从Agent业务代码里独立出来做成一个单独的层级也就是Agent-Reach。这个层级承担四类职责协议适配、鉴权统一、稳定性治理、可观测性采集。Agent侧只知道自己有什么技能不知道也不关心这些技能背后的系统是怎么接的。维度直接在Agent代码里散点接入通过Agent-Reach统一触达新接一个系统改动Agent代码重新发布在网关加一个Connector注册技能鉴权变更到处改凭据风险高凭据集中在连接器侧刷新逻辑统一错误处理每处单独try/catch策略不一致网关层统一错误分类重试和降级自动处理可观测性靠人工抽查日志Trace贯穿调用次数、成本、故障可统计模型幻觉兜底工具描述随代码版本零散维护技能描述集中管理边界清晰这样拆完以后Agent层的代码会变得非常干净基本只保留业务策略和编排逻辑。真正和外部世界打交道的复杂度全部沉淀在触达层里。2. Agent-Reach 核心设计把外部工具接入做成一件事2.1 分层逻辑模型、编排、触达、外部系统各管一段Agent-Reach在架构里的位置介于Agent编排层和外部系统之间。整体分层大概是这样最上层是模型层负责文本理解与生成再往下是Agent编排层负责拆解任务、调技能、维护上下文中间是Agent-Reach触达层负责技能注册、参数校验、协议转换、稳定性处理最底层是外部系统包括各种API、数据库、消息队列。每一层只管自己的事。编排层不碰HTTP细节触达层不关心业务上这句话该不该这样说外部系统更不知道模型是什么。这个边界一旦立住后面做权限治理、成本核算都会容易很多。从实现形态上看Agent-Reach不用做成一个大而全的重框架做“网关加连接器SDK”的样子最合适。网关负责技能注册、路由、鉴权、限流、审计连接器SDK帮开发者快速把新系统接进来避免每个系统都从零实现一遍超时、重试、Token刷新这些通用逻辑。2.2 统一技能描述给大模型一份看得懂的“技能说明书”Agent-Reach把每个外部能力抽象成一个“技能”每个技能用一份结构化的描述定义。这份描述不仅给网关做参数校验更关键是给大模型看。模型能不能正确使用工具很大程度上取决于这份说明书写得明不明白。我这里用JSON Schema来描述技能结构包含技能名、适用场景、参数、返回字段和稳定性策略。下面是一个查询工单技能的简化示例{ name: query_incident, description: 按工单号或上报人查询IT服务台工单仅用于查看问题处理进度。不要用该技能创建、修改工单。, parameters: { type: object, properties: { incident_no: { type: string, description: 工单号例如 INC001234 }, requester: { type: string, description: 上报人工号 } }, oneOf: [ { required: [incident_no] }, { required: [requester] } ] }, return_fields: [incident_no, status, assignee, updated_at], retry: { max_attempts: 2, on_status: [502, 503, 504] }, timeout: { connect_ms: 1500, read_ms: 8000 } }我后来发现description里有一类信息特别值钱边界说明。如果只写“查询工单”模型很容易在用户说“帮我建个单子”时也去调它。写清楚“仅用于查看不要创建或修改”之后模型误用的概率明显下降。另外oneOf这种约束其实是在告诉模型“参数只能二选一不能都传也不能都空”。这比单纯在description里用文字描述更有效因为Schema校验是硬约束模型生成的不合法参数会在网关层被拦下来不会污染外部系统。2.3 连接器每个外部系统一个稳固的“外设驱动”连接器是Agent-Reach里真正和各系统打交道的地方。它相当于电脑的外设驱动每个外部系统对应一个Connector里面封装了三部分内容协议转换、鉴权逻辑、错误翻译。协议转换指的是把网关下发的统一技能标准请求改成目标系统认识的格式。ITSM希望收到JSON over RPCGraphQL端要走GraphQL语法SQL端要拼查询语句。这些差异对上层完全透明。鉴权逻辑集中在连接器内部凭据不落到Agent侧。这样每次生成模型调用时上下文里不会泄漏敏感信息。连接器统一实现describe()、execute()、health_check()三个接口。网关靠health_check()知道这个连接器是否可用靠describe()拿到技能说明注册到模型可调用列表里靠execute()真正执行任务。错误翻译是我特别坚持的一点。外部系统返回的错误往往是一大段机器码或错误堆栈直接抛给模型毫无意义。连接器要做的是把错误翻译成模型能看懂的简洁描述比如“ITSM系统返回工单号不存在请确认编号是否正确”。模型拿到这种反馈才能在下一次调用中自我修正。2.4 执行引擎里的稳定性动作重试、超时、限流、降级一次配齐一个技能调用从入站到出站Agent-Reach内部有一套完整的生命周期接收请求、解析技能标识、鉴权、参数校验、路由到Connector、发起外部调用、校验响应、格式化返回。这里面最容易被忽略的是稳定性动作我逐个讲。超时要分两层连接超时和读取超时。连接超时一般在1.5秒左右读取超时要根据业务接口的合理耗时设置通常会给到平均耗时的3到5倍。单独一个超时往往不够因为外部系统最常见的故障不是不响应而是响应特别慢把Agent的上下文窗口拖爆。重试最关键的是区分操作类型。查询操作可以放心重试因为它是幂等的多查一次没有副作用。创建、更新这类写操作重试前必须有幂等标识通常是业务主键或唯一请求号。否则一次网络抖动工单就可能建重复了。Agent-Reach在封装技能的时候就会声明这个技能是read_only还是read_write执行引擎据此决定重试策略。限流我放在网关层做但维度不只看总请求量还要看“用户维度”和“连接器维度”。同一用户短时间内把查询技能刷爆要拦所有流量把一个下游系统打满要排队。降级也很朴素下游故障时网关不再继续放量而是直接在返回里标记“当前该系统响应异常建议稍后再试”。模型拿到这个提示后会自己调整话术有效避免把一次故障扩散成反复空转。3. 实操记录5步让Agent真正查到内部工单3.1 场景设定让工作助手Agent具备“查工单、提工单”能力我拿实际做过的一个需求当例子。内部要做一个工作助手Agent它需要能回答“我的工单到哪了”也要能根据用户描述提交一个新的IT工单。技术前提是Agent侧已经联网到某个基础大模型并支持函数调用底层系统是公司ITSM平台提供REST和GraphQL两种接口。按Agent-Reach的思路我们不碰Agent主流程代码而是做一次“技能接入”。整个过程分成5步每一步都有明确产物。整体花的时间不长但每一步都有需要留意的地方。3.2 第一步定义工单技能描述把边界和入参写死先定义两个技能。查询技能用前面那段JSON Schema就可以提交工单则对应一个写操作。写操作的描述我会额外强调“提交即生效请二次确认后再调用”避免模型在对话中途猜测用户意图并直接创建工单。参数设计上提出工单我们要求用户描述、紧急程度、影响范围三个字段其中紧急程度用枚举值限制不让模型自由发挥。实践里模型的枚举遵循度通常会优于自由文本但依然需要网关侧校验兜底。有人会问为什么不在Agent侧直接用自然语言让模型提取参数而是先定义严格的参数结构。我的观点是参数结构是人和系统之间的契约。你让模型自由提取五个会话可能有五种字段名变体后面做审计和数据分析会非常痛苦。严格结构虽然看起来土但稳定可靠。3.3 第二步写一个ITSM连接器把GraphQL差异消化掉ITSM平台主接口是GraphQL但Agent-Reach对外提供给Agent的调用协议是统一的JSON。如果不加连接器模型每次都要理解GraphQL语法这并不现实。连接器的作用就是把这层差异消化掉。简易的Python Connector长这样class ItsmConnector(BaseConnector): retryable_status {502, 503, 504} def execute(self, action: str, payload: dict): # payload 在进入这里之前已经通过 schema 校验 if action query_incident: gql query($no: String) { incident(no: $no) { no status assignee updatedAt } } variables {no: payload.get(incident_no) or payload.get(requester)} elif action create_incident: gql mutation($input: IncidentInput!) { createIncident(input: $input) { no } } variables {input: payload} resp self.session.post( f{self.base_url}/graphql, json{query: gql, variables: variables}, timeout(self.timeout_connect, self.timeout_read) ) if resp.status_code in self.retryable_status: raise RetryableError(resp.text[:200]) if resp.status_code 400: raise BusinessError(resp.text[:200]) return normalize_incident(resp.json())这里有个细节RetryableError和BusinessError不是随手写的它们是执行引擎识别重试和终止的契约。RetryableError表示这次失败还可以重试BusinessError表示业务上不对重试也没有意义。如果所有异常都一视同仁就会出现业务性错误反复重试浪费调用次数也让模型越修越乱。连接器里我还会维护一个session对象复用HTTP连接池同时挂上Token自动刷新逻辑。Token过期时不能简单抛异常而是应该刷新Token后重放一次请求。这种做法避开了最典型的“令牌过期连环失败”。3.4 第三步在网关注册技能按Agent身份做权限隔离新技能写好之后去Agent-Reach网关注册。注册信息里要写清楚技能名、绑定的连接器、支持的操作类型、以及允许使用该技能的Agent身份。权限隔离我做得比较细。同一个ITSM连接器可以注册出不同权限面的技能比如只读技能query_incident给普通助手使用可写技能create_incident只给有审批流程的应用使用。这样即使模型在某个场景里出现幻觉权限边界也能拦住它。注册完技能后网关会同步一份技能清单到Agent侧Agent侧再去拼装模型可调用函数的上下文。整个过程不需要改Agent主服务代码这也是独立触达层的优势。后续再加技能基本就是配置加连接器的事。3.5 第四步联调验证重点看错误返回模型能不能听懂联调阶段我通常会准备三类用例正常用例、参数错误用例、系统降级用例。正常用例拿一条真实工单号验证返回结果参数错误用例故意用空参数或不存在编号调用看错误信息是否足够模型自动纠偏系统降级用例则更像压力测试确认下游不可用时返回给模型的信息不是一堆堆栈。这里有个自己摸索出来的经验把Agent当成一个“带上下文的新人”来设计错误提示。它每收到一次错误就会把错误接进上下文然后尝试下一次工具调用。如果你返回“HTTP 500 Internal Server Error”模型只会反复重试然后放弃如果你返回“查询服务暂时繁忙建议30秒后重试”模型会自然把这句话融入对用户的回答。验证通过后一次技能接入就算完成。这个流程走顺了以后我接一个新系统的时间基本能压缩到半天以内其中大部分时间都花在理解对端接口语义上而不是写技术代码。4. 踩坑实录Agent触达层的典型故障与排查方法4.1 模型不按技能描述规范传参怎么办实际使用中我遇到最多的一个问题就是模型生成参数时并不是每次都老老实实按Schema走。它偶尔会把incident_no写成id或者在两个互斥字段里同时传值。我一开始以为是大模型厂家的函数调用能力不行后来发现根子在于我的技能描述和Schema设计得太粗糙。解决办法分三层。第一层是打磨描述把“什么时候用参数怎么配不要做什么”写清楚。第二层是在Schema里使用强约束比如oneOf、enum、minLength让不合法参数在网关层被拦截。第三层是网关返回“参数校验失败”时必须带上合法参数的示例。模型看到示例之后下一次调用大概率会纠正过来。有一个小坑要提醒JSON Schema的校验信息不能太长。给模型错误反馈时只给最关键的一条比如“必须提供工单号或上报人工号之一”不要把它生成的那一大段原始校验报错转发回去模型会被无关信息带偏。4.2 偶发超时被误判成“工具坏了”外部系统偶发抖动是常态但模型很容易把一次超时理解成“这件事干不成”然后直接放弃或者更糟编造一个结果。这个锅不能让模型背是触达层给到的反馈不充分。我在连接器里把所有失败都细分连接超时、读取超时、5xx、429、业务错误、鉴权失败。前四类走重试或降级业务错误直接返回给模型鉴权失败则刷新Token再重放。同时网关层会把这类抖动当作健康度指标记录如果某个连接器的失败率超过阈值下一步就不再路由新的请求并返回一个清晰的降级提示。这样一来模型侧感知到的“失败”数量会大幅下降。不是靠欺骗它而是把稳定性问题消化在更低层级这是触达层的本职。4.3 Token过期导致连环失败这个坑出现得很隐蔽。连接器里如果每个请求都临时去取Token外部系统忽然切换认证策略时所有请求会同时失败。而且第一次失败是因为Token过期等你去取新Token时可能又因为接口调用过于密集被限流整条链路雪崩。我的做法是给连接器加一个异步的凭据刷新机制它独立于请求链路。新Token提前刷新并缓存请求真正发出前先检查当前凭据剩余有效期不足两分钟就等待刷新完成。这样外部系统切换认证策略时首次失败至多影响到一两个请求之后就自动恢复。排查时建议在网关日志里把“鉴权失败”单独标记出来不要和普通5xx混在一起。否则你会看到请求失败率莫名其妙升高但不知道有多少是因为凭据问题。4.4 可观测性不能只记录一次调用Agent和普通API调用不一样的地方在于一次用户问题会引发多次工具调用而且调用之间有上下文依赖。只记录某一次调用成不成功根本回答不了“这个用户问题为什么最后答偏了”这种问题。我在Agent-Reach里做了三层串联模型侧生成一个conversation_idAgent编排层生成trace_id网关调用外部系统后记录下游的request_id。这三个ID拼进同一条日志和埋点链路里。排查问题时不管从模型日志还是网关日志进入都能把链路串起来。这套可观测体系也顺便解决了成本分析。每次技能调用都打上技能名和Agent身份标签到了月底统计调用量和Token消耗能很清楚地看到哪些技能是高频刚需哪些技能在反复失败空耗成本。网关返回码常见原因处理建议VALIDATION_FAILED模型传参结构不合规检查技能Schema的约束和示例返还模型精炼错误提示UPSTREAM_TIMEOUT外部系统响应过慢降低重试次数直接在返回中提示系统繁忙RETRYABLE_5XX下游网关或服务抖动走退避重试超过阈值自动降级TOKEN_STALE凭据过期或未刷新检查连接器凭据刷新逻辑确认是否提前异步刷新RATE_LIMITED触达层限流生效按用户维度和连接器维度检查是否打满配额UPSTREAM_NOT_FOUND数据不存在或路由错误返回业务错误让模型调整话术不要触发重试5. 后续扩展当触达层接的东西多了以后5.1 多Agent协作时的触达仲裁Agent-Reach跑稳之后下一步自然要考虑多Agent场景。多个Agent同时在线可能都会去操作同一个业务系统。比如招聘Agent和员工助手Agent同时更新候选人状态如果没有锁和队列状态互相覆盖是必然会发生的。当前的思路是在触达层增加“协作域”的概念。对支持并发读的技能放行对需要独占的写技能加操作锁同一个用户或同一笔业务对象的请求按优先级排队。仲裁逻辑放在触达层比放在Agent编排层更合适因为只有它看得到跨Agent的真实流量。5.2 安全强化与用量成本扩展接的系统越多安全控制就要越细。网关前置校验可以加更多规则敏感参数识别、脱敏策略、外部系统地址白名单、调用频率弹性阈值。做权限定位时也不再只是看Agent身份还可以看用户上下文做到“人和Agent双重鉴权”。成本侧同样需要扩展。技能调用次数、Token消耗、下游系统间平均耗时这些数据按技能维度统计出来后不仅能盘点资源消耗还能反向推动技能设计的优化。比如某个技能总是让模型反复调用三遍才能成功说明它的参数结构可能有问题该动手改掉。最后分享一点我个人的实操体会。Agent工程化最容易低估的正是这层“够不着”的触达问题。模型能力是快变量外部系统是慢变量把两者之间的触达层做扎实后面加技能、加场景都会顺很多。如果你是刚开始搭Agent我建议别急着把上百个工具一次性接进来想办法先把三五个核心技能通过统一的触达层跑通把重试、鉴权、可观测这些底座立住再逐步扩大范围。这条路我一步一步蹚过能帮你省下不少时间。
返回列表