上个月我把一套基于大模型开发的内部系统,从"能聊天"硬生生改造成了"能办事",背后就是靠一个名为 Agent-Reach 的触达中间层。这不算什么惊天动地的框架,但它解决了团队里最头疼的问题:Agent 回答得头头是道,最后却落不了地,连个工单都开不了。这篇文章就围绕 Agent-Reach 的架构设计、连接器实现、权限控制和稳定性排查展开,把踩过的坑和对应的取舍都写清楚,希望能给正在做同类项目的朋友一点可参考的实操经验。
1. 从"会聊天"到"能办事":Agent-Reach要解决的真实痛点
先说背景。我们团队之前的 Agent 系统,本质上是一个"会说话的文档库":用户问问题,Agent 检索知识库,然后生成一段看起来很有道理的回答。但问题在于,回答之后没有下一步动作。比如用户问"帮我查一下 A 项目的资源配额",Agent 能告诉你配额是多少条数据,却不能去监控系统里把数据拉出来;用户说"给新同事开通数据库只读权限",Agent 只能输出一段 SQL 建议,还得靠人手动执行。
这种"嘴上会说,手上不动"的状态,在上线两周后就被业务部门吐槽得不行。真正要落地,Agent 必须有能力触达外部系统:调用内部 API、读写工单平台、查询监控系统、操作权限中心,甚至是拉起一条流水线构建。这里的关键词是"触达",也就是标题里的 Reach——Agent 要能伸手够到那些真实的生产系统。
1.1 我们当时评估过的几种路线
在做 Agent-Reach 之前,团队内部讨论过好几个方案,这里简单列一下,方便后面理解选型逻辑:
- 在 Prompt 里直接塞工具描述,靠模型自己生成调用参数,然后由业务代码硬编码执行。这条路对简单场景有效,但工具一多,Prompt 会爆炸,参数结构一复杂,模型就开始乱编。
- 用现成的 Function Calling 能力,配合一个工具注册表。实现快,但缺乏统一的鉴权、重试、审计、限流机制,很多生产诉求得自己补。
- 单独做一个触达中间层,把工具调用模型化、配置化、可观测化,也就是最终 Agent-Reach 走的方向。
我最终选第三条路线,不是因为前两条不能用,而是因为我们面对的接入方不止一个 Agent 实例,后续还要接对话机器人、定时任务、告警自动化这些触发入口。如果每个入口都各自写一套工具调用逻辑,维护成本会成倍上升。Agent-Reach 的角色,就是把这些入口对工具系统的访问都收敛到一个统一平面上。
1.2 Agent-Reach 的定位和边界
Agent-Reach 不是一个 Agent 框架本身,不负责编排对话、维护上下文,也不参与大模型的推理过程。它的职责非常聚焦:提供一个标准化的触达层,让任何 Agent 应用都能通过同一套协议去发现工具、发起调用、接收结果。
打个比方,Agent-Reach 像一个"外勤调度中心"。Agent 是业务员,它接到客户需求后不亲自去办手续,而是把需求转成一张标准工单交给调度中心,调度中心负责找到对应的办事窗口、检查资格、递送材料、拿回回执。业务员不需要关心窗口内部怎么运作,只需要知道工单写对了,结果就能回来。
边界清楚了,后面的设计才不会跑偏。我在项目初期踩过的一个大坑,就是想把对话状态管理和触达层耦合在一起,结果每加一个工具就要改对话逻辑,非常痛苦。把边界划清楚之后,Agent-Reach 只关心"触达动作"本身,其余一概不管,整个系统的复杂度立刻降下来了。
2. Agent-Reach核心架构拆解:触达层的分层设计与数据流
Agent-Reach 的整体结构,如果从职责上切分,可以分成四层:协议层、注册层、执行层、治理层。下面逐个展开,这部分是理解整个项目的基础。
2.1 协议层:统一工具描述格式
所有接入 Agent-Reach 的工具,都必须遵守同一份描述规范。我们基于 JSON Schema 做了二次封装,每个工具描述包含以下核心字段:
name:工具唯一名称,全局不可重复。description:自然语言描述,说明工具用途、适用场景、典型输入输出。parameters:参数定义,包含类型、是否必填、默认值、取值范围、示例。returns:返回格式定义,描述结果结构。timeout:建议超时时间,单位毫秒。permission:所需权限等级,和治理层联动。
之所以强调统一描述,是因为大模型侧需要根据工具描述来生成调用参数,如果每个工具的描述格式五花八门,模型就很容易混淆参数含义。实测下来,描述写得越规范,模型生成参数的准确率越高。我们在一个查询类工具上做过对比,格式统一之后,参数完整率从 78% 提升到了 93%。
下面是一个简化版的工具描述示例,实际项目中每个工具需要补充更多细节注释:
{ "name": "query_cluster_cpu", "description": "查询指定集群的CPU使用率,支持按时间段聚合,适用于容量评估和异常排查场景。", "parameters": { "type": "object", "properties": { "cluster_name": { "type": "string", "description": "集群名称,必填,例如 production-east", "examples": ["production-east"] }, "window_minutes": { "type": "integer", "description": "查询时间窗口,单位分钟,默认30,最大1440", "default": 30 } }, "required": ["cluster_name"] }, "returns": { "type": "object", "properties": { "cluster_name": { "type": "string" }, "avg_cpu_usage": { "type": "number" }, "peak_cpu_usage": { "type": "number" }, "sample_points": { "type": "integer" } } }, "timeout": 5000, "permission": "read_only", "endpoint": "http://internal-metrics.internal/v1/cluster/cpu" }2.2 注册层:工具生命周期管理
注册层维护一份工具清单,Agent-Reach 里的所有工具都通过注册接口动态接入,而不是写死在代码里。每个工具注册时,需要提供上面说的描述文件、实际的调用地址、对应的认证方式、是否启用、版本号等信息。Agent 应用通过服务发现接口拉取工具清单,然后决定哪些工具适合当前对话任务。
注册层还承担一个容易被忽视的职责:对工具做版本管理。生产系统里的工具,返回格式难免会调整。比如某个权限接口原来返回role字段,后来改成了role_list,如果 Agent 还按老字段解析,就会拿到空值。Agent-Reach 的做法是给每个工具维护多个版本,注册时标记默认版本,调用侧可以通过显式版本号请求历史版本,避免格式变更导致线上故障。
其实这种设计也有成本。维护多版本意味着接口的后端逻辑要兼容多种返回结构,甚至需要写适配器做字段映射。如果在早期阶段工具数量少,这个成本可以接受。但工具数量上了 50 以后,多版本就成了负担。后来我们把规则改成了:工具默认只保留当前版本和历史版本的兼容层,超过三个版本必须走正式下线流程,防止注册表无限膨胀。
2.3 执行层:调度、转发、重试
执行层是 Agent-Reach 真正干活的地方。它接收 Agent 侧发来的调用请求,校验参数,定位到具体的工具实现,然后向目标系统发起实际请求。这里的核心挑战不是"转发一次请求",而是"在各种异常情况下仍然可靠地完成任务"。
执行层内置了三种重试策略:
- 快速失败:适用于校验错误、参数不合法等场景,直接返回错误信息,不重试。
- 线性重试:适用于目标系统临时过载,等待固定间隔后重试,次数有限。
- 指数退避重试:适用于依赖系统不稳定、网络抖动等场景,间隔时间逐步拉长,同时加入随机抖动,防止重试风暴。
具体重试逻辑在代码里是这样组织的:
import time import random def execute_with_retry(call_func, retry_policy): if retry_policy["strategy"] == "fast_fail": return call_func() max_retries = retry_policy.get("max_retries", 3) base_interval = retry_policy.get("base_interval", 1) for attempt in range(max_retries + 1): try: return call_func() except TransientError as e: if attempt >= max_retries: raise if retry_policy["strategy"] == "exponential": interval = base_interval * (2 ** attempt) + random.uniform(0, 0.5) else: interval = base_interval time.sleep(interval) raise RuntimeError("unreachable")这里的一个经验是:重试策略不能写死在工具代码里,必须由工具描述中的retry_policy字段来配置。因为不同工具的失败模式完全不同:查询类工具重试没有副作用,但创建类工具如果重试可能导致重复创建,比如重复提交工单,这种工具就必须要配合幂等键使用,甚至彻底禁用自动重试。Agent-Reach 在注册时强制检查:如果一个工具被标记为non_idempotent,则默认禁止自动重试,只能人工介入或者走补偿流程。
2.4 治理层:限流、熔断、审计
治理层是 Agent-Reach 和简易工具调用脚本最大的区别。脚本只能转发请求,无法回答"这个工具被谁调用了""调用导致下游系统压力如何""某个对话是否做了越权操作"这些问题。Agent-Reach 的治理层提供了四个核心能力。
第一是限流。每个接入的 Agent 应用有一个身份标识,治理层会按身份和应用维度做并发和吞吐限制。不同工具配额不同,比如查询类工具限流可以宽松一些,写操作类工具则严格得多。限流逻辑用令牌桶实现,参数可配置,支持运行时调整。
第二是熔断。当一个目标系统的错误率连续超过阈值,治理层会打开熔断开关,后续请求直接快速失败,而不是继续把流量打向已经脆弱的系统。熔断半开状态会放少量探测请求,确认下游恢复后才完全关闭。这个机制在我们后续对接一个不稳定的监控系统时非常有用,没有它,Agent 一次并发调 10 个工具,就能把那个系统打到误报警。
第三是审计。所有调用行为都会记录审计日志,包括发起方、目标工具、参数摘要、返回状态、耗时、是否命中缓存等。审计日志不能只记成功请求,失败请求更要记录,因为很多权限问题和配置问题都是通过失败的审计日志追踪到的。
第四是权限校验。每次调用前,Agent-Reach 都会校验发起方是否具备该工具的操作权限,权限判断不是简单二分法,而是按read_only、execution、admin等多个等级动态匹配。权限校验失败时返回统一错误码,Agent 侧可以根据错误码向用户说明原因,而不是抛出一段晦涩的异常堆栈。
3. 连接器体系:二十余种工具适配的生产实践
架构层面解决了"怎么组织"的问题,实际落地时还要面对"怎么接"的问题。Agent-Reach 的接入对象五花八门,有内部自研服务的 HTTP API,有老系统的 SOAP 接口,有数据库查询通道,也有消息队列触发入口。这部分我讲讲连接器(Connector)体系的设计。
3.1 连接器的统一抽象
连接器是 Agent-Reach 执行层和具体系统之间的适配模块。每个连接器负责一种通信风格,比如 HTTPConnector、SQLConnector、MQConnector。Agent 侧的调用方不需要感知连接器差异,它们只需要知道工具名、参数、返回结果。所有协议差异都被封装在连接器内部,这是分层设计带来的直接好处。
以 HTTPConnector 为例,它解决了几个共性问题:认证信息管理、请求头封装、响应体解析、超时处理、错误码映射。内部系统的认证方式五花八门,有 token 认证、有 basic auth,还有复杂的签名机制。Agent-Reach 没有让每个工具自己实现认证,而是在连接器层统一处理,工具描述里只用auth_method字段声明走哪种认证即可。这样做的直观受益是:新增一个工具时,如果复用已有认证模式,几乎不用写业务代码,只需要写一份配置。
# 工具注册配置节选 name: query_incident_list description: 查询指定时间段的线上工单列表 parameters: - name: start_time type: string required: true - name: end_time type: string required: true - name: status type: string required: false default: "all" timeout: 8000 permission: read_only connector: type: http auth_method: service_token endpoint: http://incident-api.internal/v1/incidents method: GET后面接入新工时,我们大量采用配置化的方式,一个工具对应一个 YAML 片段。这种方式在工具数量还少的时候可能看不出多大优势,但当名单扩到 20 个以上,每少写一段代码,就少了一个需要长期维护的测试用例。
3.2 返回结果的标准包装
连接器从目标系统拿到原始响应后,并不会直接返回给 Agent 侧,而是先包装成统一的结果结构。包装结构包括status、data、error三个核心字段,其中status表示整个触达调用的状态,data是真正的数据负载,error包含错误码、错误信息和可读性提示。
这个包装看似多此一举,实际上价值很大。因为底层系统的错误格式千奇百怪:有的系统出错时返回 200 + error code,有的返回 500 + HTML 页面,还有的直接超时不返回。如果没有统一包装,Agent 侧解析返回结果时将被迫理解每个系统的错误风格,这对大模型来说非常困难,也容易在排障时产生误解。
包装后的标准错误结构长这样:
{ "status": "failed", "data": null, "error": { "code": "UPSTREAM_TIMEOUT", "message": "上游系统响应超时", "retryable": true, "suggestion": "请稍后重试,或联系系统管理员排查服务状态" } }让错误信息包含retryable字段,也是一个很值得分享的经验。Agent 侧看到retryable: true时,可以选择提示用户稍后重试,或者主动触发补偿流程;看到retryable: false时,就应该停止操作并寻找替代方案,避免无效重试浪费资源。这个字段最初不在设计里,是在一次线上质量问题复盘后加上的,当时一个工单系统返回了持久性错误,Agent 却傻傻重试了六次,把问题放大了。
3.3 幂等策略:防止Agent重复"闯祸"
Agent 场景下,重复调用比人工操作更容易发生。原因有两个:一是大模型在生成参数时可能因为上下文切换而重复发起同一个调用意图;二是 Agent-Reach 自身的重试机制会放大重复风险。尤其是创建类操作,比如创建虚拟机、提交审批单、发送通知,一旦重复执行,后果很难收拾。
Agent-Reach 对幂等问题的处理分成三层:
- 在工具描述层显式标注
idempotent: true/false,对于非幂等工具,执行层默认不做自动重试。 - 在参数层支持
idempotency_key,Agent 侧在发起调用时生成唯一键,目标系统侧根据该键做去重。 - 在治理层提供"重复调用检测"能力,短时间窗口内如果同一 Agent 对同一非幂等工具发起多次调用,会触发告警并且要求二次确认。
这三层机制配合下来,写操作类事故的频率已经降到很低。我在内部分享时经常强调一个原则:宁可让一个查询请求多等几秒,也不能让一个创建请求被执行两次。幂等不是一个锦上添花的特性,而是 Agent 触达生产系统的底线。
4. 权限边界与审计:让Agent伸手但绝不出格
Agent 一旦具备触达能力,安全问题就从一个理论话题变成了日常话题。如果一个低权限用户的对话能促使 Agent 执行高权限管理操作,那这个系统就是在制造一个新的攻击面。Agent-Reach 在权限和审计方面花的时间,不比功能开发少,这里挑选几个关键设计展开。
4.1 最小权限原则在Agent调用链中的落地
我们把权限判断的维度从"用户"扩展到了"用户 + Agent身份 + 会话上下文"三元组。用户是 U,Agent 是 A,会话上下文是 C,一个工具调用只有当(U, A, C)这一组合被判定为允许时才会放行。
举例来说,运维部门的架构师 U1 通过常规对话 Agent A1 发起"删除故障演练环境中的临时容器"请求,权限系统会检查 U1 是否拥有容器操作权限、A1 的权限范围里是否包含container:delete、当前会话上下文是否标记为"运维演练"。只有三项全部通过,执行才会被允许。这种三级校验确实增加了一些链路耗时,实测单次校验大约增加 20 到 60 毫秒,对于绝大多数业务场景完全可接受,但安全性提升是数量级上的。
权限配置本身也采用声明式管理模式。每个工具的permission字段定义基础等级,每个 Agent 应用在接入时声明自己的权限矩阵,运行时由 Agent-Reach 的权限引擎统一裁决。为了避免权限配置在运行中被意外修改,所有权限变更都要求走审批流程,并且记录变更日志。
4.2 高风险操作的实时确认机制
就算权限校验都通过了,某些操作仍然不应该由 Agent 静默执行。Agent-Reach 里设计了一个"高风险操作确认"机制:当工具被标记为requires_confirmation: true时,Agent 侧的调用请求并不会立刻发往目标系统,而是先返回一个待确认状态,等待用户在对话界面明确点击"确认执行"。
这个机制听起来很简单,真正做好却需要处理一个时序问题:用户确认发生在几秒钟之后,而 Agent 大模型的对话上下文可能已经滚动更新了。我们刚开始做的时候,确认操作直接丢给 Agent 重新理解,结果模型经常把确认信息理解成另外的意图,导致操作匹配错误。后来改为:确认时不经过大模型,而是由 Agent-Reach 直接把原始请求参数原样发送给目标系统,相当于一次"参数快照重放"。这个改动看似是在绕开模型,实际上避免了最不可控的不确定性来源。
哪些操作应该标记为高风险?我们的经验标准是:凡是对生产环境产生持久影响的、涉及数据删除的、涉及权限变更的、涉及外部通知的操作,一律要求确认。宁可多一步确认,也不能让 Agent 在无人知晓的情况下把事办了。实际上用户对多一步确认的容忍度远高于对我们出安全事故的容忍度,这个取舍很早就在团队内达成了共识。
4.3 审计日志怎么设计才算真正有用
很多系统的审计日志只是流水账,记录了"谁在什么时候调用了什么接口",但真正遇到问题时很难回溯。Agent-Reach 的审计设计从"问题回溯"的角度倒推,每个审计记录必须满足三个要求:能回答"发生了什么"、能回答"为什么发生"、能帮助后续"避免再次发生"。
第一个要求靠基础字段实现:发起方、工具名、参数摘要、结果状态、耗时。第二个要求需要记录触发链路:是哪个用户指令引发的调用?上下文里包含了哪些工具候选?大模型选择这个工具时参考了哪些描述块?这些信息能从 Agent 侧的日志同步过来,在审计记录中形成链路ID 关联。第三个要求则依赖统计分析:周期性扫描审计日志,找出"高失败率工具""高频调用但结果为空"等模式,反哺到工具描述优化和权限策略调整中。
审计日志本身也需要保护。Agent-Reach 将审计日志写入独立的存储,与业务数据库物理隔离,防止因业务故障导致日志丢失。同时,日志查询权限严格限制,审计数据默认保留 180 天。有一次线上争议调查就是靠审计日志还原了整个调用链,才确定问题出在工具描述里的参数示例过时,导致模型生成了旧格式参数。这个结论如果只看应用日志,根本发现不了。
5. 稳定性建设:真实环境中的故障排查与对策
Agent-Reach 上线之后,真正的考验才开始。对话场景下,调用频率高、并发压力大、下游系统水位不一,稳定性问题远比传统的企业内系统复杂。这一节我详细讲几个真实处理过的故障案例,希望能帮读者少走弯路。
5.1 一次"幽灵超时"的完整排查链路
上线第二周,监控告警突然报告一批调用超时,涉及的工具五花八门,但共同点是都经过了同一个连接器节点。排查第一步,我看了 Agent-Reach 自己的日志,发现超时点全部集中在"等待连接器响应"阶段,但连接器日志显示请求已经正常返回了。这说明问题不是出在下游,而是出在我们自己的处理管道里。
顺着链路继续查,发现连接器下游还有一层异步事件循环,负责把原始响应包装成标准结果。恰恰是这层事件循环的并行度配置太低,大量响应堆积在队列里排队等待包装,导致超时。表面上看是网络问题,实际是线程耗尽和队列堆积造成的等待问题。修复办法很朴素:把事件循环的并发度从 8 调整到 32,同时给队列加上有界容量,超过容量直接快速失败,而不是无限堆积。这次排查给我一个很深的认识:在高并发触达场景里,任何中间处理环节的队列都是潜在的地雷,必须设置有界队列和背压策略。
修复之后,同类超时告警再也没有出现。为了保险起见,我又给执行层加了"熔断降级"机制:当队列堆积超过阈值时,新请求直接返回一个明确的忙信号,Agent 侧收到忙信号后会主动延后重试。适度的退化比无限排队更符合实际需求。
5.2 模型"幻觉参数"导致的连环请求事故
另一个印象深刻的故障,是大模型在生成参数时产生幻觉,把工具描述里根本不存在的参数值捏造出来了。一次测试中,Agent 调一个"查询发布记录"的工具,居然生成了branch=release_2025_weird这种完全没出现过的值,目标系统当然查不到,返回空列表,Agent 并没有停下来,而是自动调整了另一个参数继续查,最后引发了一连串无效请求,把一个内部服务打到限流。
这个问题的根源不在 Agent-Reach 的执行层,而在协议层和模型侧之间。Agent-Reach 能做的,是把参数校验做得更严格。我们在执行层加了一个"参数合法性预校验",除了类型和必填项检查,还会对比工具描述中的枚举值和示例值。如果模型生成了不在范围内的参数,直接返回参数错误,并附带提示信息,要求模型重新生成。这个校验不是为了防止所有幻觉,而是防止幻觉参数被传递到下游系统。
不过预校验并不能解决全部问题。更长效的手段是主动增强工具描述,在描述中明确标注"可接受的参数来源"和"常见误用提醒"。比如原描述只写"branch 是发布分支名",增强后写"branch 必须是当前仓库已存在的分支,参考用户消息中的明确值,不要自行创造"。实测下来,这类描述优化能明显降低参数幻觉概率。后来新增工具时,我们把"描述中的参数约束说明"列为必填项,否则不予注册。
5.3 断点续接:Agent触达中断后的体验补偿
触达调用必然有失败时刻,用户看到的不能只是一句"系统错误"。Agent-Reach 里设计了断点续接机制:当某个工具调用失败时,把失败原因、可重试性、建议的替代路径都写入一个"续接记录",用户在对话里可以直接点击"重试上次操作"或"换一个方式尝试"。
这个功能一开始没做。最早失败时,用户的体验是重新描述一遍需求,让 Agent 重新理解和执行。听起来不复杂,但用户重新描述时常常漏掉关键信息,比如忘记指定时间范围,Agent 拿到的参数就不完整,结果还是不成功。断点续接直接把上次的参数快照和失败原因呈现给用户,让用户确认或修改后再提交,大幅降低了重复沟通的认知负担。
实现上,续接记录和审计日志共用同一个链路 ID,只是多了一段"待恢复的操作快照"。快照中保存目标工具名、完整参数、过期时间、允许恢复的次数上限。过期时间和次数上限是必须的,否则老旧的快照可能被无意中重放,造成过期操作被执行。我建议过期时间默认不超过 30 分钟,特别敏感的操作不提供续接能力,只能重新发起。
6. 从0到1搭建Agent触达层时,我最实际的几个建议
Agent-Reach 从设计到上线经历了不少波折,有架构层面的决策,也有细节层面的教训。最后分享几条个人感觉最有价值的经验,不是理论,都是从实际运营里总结出来的。
6.1 工具数量要克制,质量要挑剔
团队在中期一度陷入"工具越多越好"的误区,两周内接入了 40 多个工具,覆盖各种边缘场景。结果是什么?工具清单暴增之后,大模型在工具选择上的准确率明显下降,经常选中功能相近但字段完全不同的工具。后来我们做了工具瘦身,合并了重复能力,下架了一批低使用频率的工具,把工具数量控制在 20 个左右,工具选择的准确率才重新回到 90% 以上。
一个理性的做法是:每新增一个工具,必须回答三个问题——有没有已有工具能覆盖这个能力?调用频率预期是多少?缺少这个工具会不会显著影响用户体验?如果新工具和已有工具能力重叠,就应该优先扩展已有工具的字段,而不是另起炉灶。这样既保证覆盖率,又控制选择空间,模型侧压力也会小很多。
6.2 工具描述要当成产品文档来写
很多技术出身的人写工具描述时,只图自己看得懂,结果大模型和用户都看不懂。好的工具描述应该做到:让一个完全不了解团队内部系统的模型,也能从描述中准确理解工具的能力边界和参数含义。
我在实践中总结了一些描述优化原则:第一,描述开头用一句话说明工具能解决什么问题,避免抽象用语;第二,参数说明必须包含示例值,且示例值要贴近真实业务,不要写"请在这里填参数"这种废话;第三,明确写出不适合使用此工具的情况,帮助模型排除错误选择。这些规则看起来是写作技巧,实际效果却是实打实的协议质量提升,直接影响了触达调用的成功率。
6.3 先跑通一条最小闭环,再横向扩展
从零搭建时,最容易犯的错误是想把所有能力都做好再上线。Agent-Reach 第一批只接入了三个工具:一个查询类工具、一个搜索类工具、一个创建类工具。这三个工具覆盖了只读、执行、写操作三种典型模式,足以验证整个链路的安全性和稳定性。
这个小闭环跑了一周,确认权限校验、审计、重试、确认机制都正常后,才开始批量接入其余工具。事实证明,这个渐进式策略非常有效,因为第一批的稳定运行为后续接入提供了模板和信心,也让团队在解决问题时不用面对工具数量爆炸的并发复杂度。如果时间倒流,我会更坚决地把"最小闭环先行"当成项目启动阶段的铁律。
Agent-Reach 的整个设计思路,其实就围绕一句话:Agent 要能可靠地触达外部世界,不能只是聪明地说话。统一协议、分层架构、连接器适配、权限控制、稳定性治理,每一层都在为这个目标服务。现在这套系统接管的日常触达调用量已经不少,像开权限、查监控、拉日志、发工单这类操作,业务方已经习惯了直接跟 Agent 说需求,由触达层在后台完成实际操作。回看整个过程,当初最耗时的工作不是写代码,而是把"谁可以做什么""失败了怎么办""如何证明做过什么"这些问题理清楚。这些问题想通了,系统的骨架自然就立起来了。