资讯动态

AI Agent 技能管理实战:从混乱到可视化治理

发布时间:2026/10/1 4:48:04 来源:尧图企业网站定制
前两天有个朋友找我聊天说他的 Agent 项目技能越加越多今天接个天气 API明天写个数据库查询函数后天又塞进去一个内部工单系统脚本。结果不到一个月整个项目就像塞满杂物的储物间新 Agent 不知道该调哪个技能老 Agent 改个参数要翻半天代码技能之间的依赖关系更是没人说得清。我听完笑了笑这不就是我几个月前踩过的坑吗。后来我干脆花了两周时间做了一个给 AI Agent 用的可视化技能管理器把所有技能统一收纳、统一注册、统一编排还能在界面上实时看到每个技能的运行状态和调用链路。这篇文章就把这个项目的完整思路、核心设计、实操过程和一些踩坑记录分享出来。这个管理器解决的是 AI Agent 开发里一个很实际的问题当你的 Agent 不再只有三五个技能而是有几十个甚至上百个技能的时候靠代码里 hardcode 调用关系、靠文档维护技能清单已经完全行不通了。它把技能变成一种可注册、可发现、可编排、可监控的标准化资源让 Agent 通过统一入口去调用技能也让开发者通过可视化界面去管理技能。适合正在做 Agent 项目、被技能管理搞得焦头烂额的开发者参考尤其是不满足于只写 Demo、想把手里的 Agent 真正做成一个可维护系统的朋友。1. 先聊聊为什么需要技能管理器1.1 Agent 项目里最常见的技能混乱现场我见过太多 Agent 项目早期功能少的时候怎么都行技能一多就立刻失控。这里的“失控”不是说代码跑不起来而是维护成本开始指数级上升。具体表现大概是这个样子技能调用方式五花八门——有的是直接 import 一个函数有的是调一个 HTTP 接口有的是读取某个配置文件再拼参数技能之间出现隐式依赖——你的订单查询技能内部偷偷调用了用户信息服务但这个依赖关系只存在于代码里没有任何地方记录技能状态不可见——某个技能因为第三方 API 挂了已经失效半天但检查的人不翻日志根本发现不了。我自己踩过一个特别典型的坑。当时项目里有十几个技能为了省事直接在 Agent 的 system prompt 里把所有技能说明写上然后靠 LLM 自己去选。结果技能一多prompt 越来越长LLM 的注意力被稀释经常选错技能。更离谱的是有一次我改了其中一个技能的入参格式忘了通知另一个依赖它的技能线上直接报错。那之后我就明白了一个道理Agent 的技能管理不该靠人的记忆也不该靠 prompt 的堆砌而是需要一套系统化的管理机制。1.2 一个技能管理器到底解决了什么我做这个可视化技能管理器核心就解决三件事。第一统一注册与统一调用。所有技能不管底层是 Python 函数、HTTP 服务还是数据库脚本都按照同一种协议注册进来对外暴露同一种调用接口。Agent 不需要关心技能内部是怎么实现的只需要说“我要调用技能 X参数是 Y”。第二可视化编排与状态感知。在管理界面里你能看到所有技能的列表、分类、依赖关系、健康状态和实时调用情况。哪个技能正在被高频调用、哪个技能最近一直报错、哪个技能依赖了哪个技能一眼就看清。第三可控的技能生命周期。技能的注册、启停、版本升级、下架都在管理端操作不需要改代码重新部署。对于要把 Agent 产品化的团队来说这一点尤其重要——你不可能每次调整技能都让研发去发版。1.3 它跟“工具调用”和“插件系统”不是一回事很多人会问Agent 框架本身不就有 tool calling 机制吗LangChain 里有 toolsOpenAI 有 function calling为什么要另做一个管理器我区分得很清楚工具调用是 Agent 内部的通信机制它解决的是“模型如何决定调用哪个工具”的问题而技能管理器是 Agent 外部的资源管理系统它解决的是“整个系统里有哪些技能、它们是否健康、谁能调用它们”的问题。换句话说工具调用是运行时协议技能管理器是治理平台。插件系统也不一样。IDE 里的插件系统面向代码编辑器扩展Agent 的技能管理器面向运行时的能力供给。它更像是整个 Agent 世界的“中台”。把技能从 Agent 应用里抽离出来沉淀到一层独立的设施里任何 Agent 都能连上来按需取用能力。想明白这个定位后面所有设计决策都顺了。2. 整体架构与设计思路2.1 四个核心组件构成的分层架构技能管理器的整体架构我从一开始就没打算做得太复杂但该有的层一定要有。整个系统分成四块技能注册中心、技能执行引擎、可视化控制台、存储与基础设施。技能注册中心负责接收技能注册请求校验技能描述文件把技能的元数据存起来。它是整个系统的“户口本”所有技能的信息都汇总在这里。技能执行引擎负责真正运行技能。它从注册中心拿到技能定义解析参数执行对应的处理逻辑把结果返回给调用方。可视化控制台是面向人的界面你在这里浏览技能、管理版本、看监控图表、发起测试调用。存储层用了 PostgreSQL 存技能元数据和审计日志Redis 用来做缓存和临时状态存储比如技能的心跳状态、当前并发数这些高频读写的数据。这里有个设计决策值得一提我没有把技能的执行逻辑直接嵌在管理器进程里而是抽象成统一接口。本地技能通过 Python 函数注册进来远程技能通过 HTTP/gRPC 接进来管理器只负责调度和转发。这样做的最大好处是管理器本身不跟具体业务耦合新技能接入不会污染核心进程出问题也更容易隔离。2.2 技能描述协议先定义好再说别的整个项目里我最看重的一块设计是技能的描述协议。它可以理解成每个技能的“身份证”Agent 靠它做选择管理器靠它做校验执行引擎靠它做调用。我给每个技能定义了一套统一的描述结构核心技术点是 JSON Schema。一个技能描述文件长这样name: order_query display_name: 订单查询 description: 根据订单号查询订单状态与物流信息 version: 1.2.0 type: http author: team-orders tags: [order, logistics, query] parameters: type: object required: - order_id properties: order_id: type: string description: 订单编号 with_logistics: type: boolean default: true description: 是否同时返回物流信息 endpoint: url: http://order-service.internal:8080/api/order/{order_id} method: GET timeout_ms: 3000 auth: type: service_token scopes: [order:read]这里每一项都有意义。description 字段极其重要因为 Agent 决定是否调用这个技能主要就是靠它。Agent 的模型层会根据 description 来匹配用户意图。parameters 用 JSON Schema 描述可以通过 FastAPI 的模型自动生成校验逻辑也可以直接把这个 schema 喂给 LLM作为工具参数定义。endpoint 和 timeout 决定了调用路径和兜底策略。version 字段给后续的版本管理留了空间。2.3 为什么我坚持做可视化而不是纯配置文件有人可能会说用 Git 管理 YAML 配置文件不也挺好为什么非要搞个可视化界面我举一个实际场景来说明。有一次业务同事跑过来跟我说这个技能测试环境是通的生产环境一直报错能不能帮我看看如果是纯配置文件他需要去看代码、找日志、猜环境差异。但在可视化界面里我打开技能详情页直接看到同一技能在不同环境的健康状态对比再点开最近调用记录错误信息一目了然。这就是可视化的价值它不是简单地把配置搬上网页而是把状态和关系呈现出来降低排查和理解成本。还有个原因可视化面板对“非技术角色”友好。很多 Agent 项目里业务人员也需要参与技能配置比如调整技能描述、修改参数说明或者临时把一个技能设为不可用。让业务人员改 YAML 再走 Git 提交流程门槛太高了但给他一个可以点选、可以填写的界面这件事就变得很顺畅。这也是我从真实项目中得到的体会——Agent 技能的管理迟早会从开发者的职责边界扩展到业务运营的范畴。2.4 技能管理器不是终点是技能中台的起点在构思这个项目的过程中我越来越清楚地意识到这个管理器本质上是在往“技能中台”的方向走。所谓技能中台就是让技能成为一种组织级的共享资源多个 Agent 共享一套技能池而不是每个 Agent 项目各搞一套重复造轮子。比如你的公司里可能有客服 Agent、数据分析 Agent、内部运维 Agent它们都需要查询订单或者读取用户信息。如果没有技能中台每个 Agent 都要各自接一遍订单服务、用户服务有了技能中台能力只沉淀一次三个 Agent 共用。后续技能的认证、限流、审计、计费也都能在这一层统一解决。所以我做这个管理器的时候刻意没把它的接口设计成“单机工具”而是从第一天起就按照可水平扩展的服务来设计就是为了以后往中台方向走的时候不用推倒重来。3. 核心模块拆解与实操细节3.1 技能注册中心技能入库这件事没那么简单技能注册是管理器的入口也是很多人低估了难度的一环。我最早做的版本注册就是往数据库插一条记录后来发现远远不够。一个完整的技能注册流程应该是提交技能描述文件做格式校验和 Schema 解析检查参数定义是否合法、endpoint 格式是否正确、是否存在同名列和版本冲突然后测试连通性。尤其是连通性测试这一步帮我挡住了无数个错误——很多本地开发环境正常的技能注册到生成环境就调不通因为内网地址不一样、凭据缺失或者依赖服务没上线。所以现在的注册流程里技能注册之后会先处于“待验证”状态系统自动发一个 ping 请求通了才标记为“可用”不通就标记为“异常”。技能分类方面我目前分了基础技能、业务技能和编排技能三种。基础技能是原子能力比如发送邮件、查询天气业务技能是面向具体业务场景的比如“售后工单创建”编排技能比较特殊它本身不实现具体功能而是把多个技能按规则串起来供 Agent 一次性调用。后面接上编排引擎之后编排技能会变成越来越重要的类型。3.2 执行引擎调度逻辑里藏着稳定性的命门执行引擎负责把技能的调用请求真正跑起来。它看起来只是一个转发器但稳定性全藏在这里。我做的第一批压测就发现几十个 Agent 同时调用技能时如果不加控制依赖的远程服务很容易被打垮。所以执行引擎里必须做并发治理。我在每个技能上挂了独立的信号量控制最大并发数超过并发上限的请求直接返回“技能繁忙”让 Agent 稍后重试而不是无限排队。同时接口层做了基于令牌桶的限流防止单个 Agent 把整个引擎的资源吃光。超时处理也是一门学问。外部技能接口普遍要求 3 到 5 秒响应但如果某个技能长时间没响应执行引擎不能干等着。我采用了“快速失败 有限重试”的策略第一遍超时立刻返回错误如果技能的容错级别允许就做一次指数退避重试300ms、600ms、1.2s最多三次重试之间故意加大间隔给下游服务恢复的时间。这里有个关键点对于写操作类技能默认禁止自动重试以免重复扣款、重复建单。把这一点也想清楚了才算把执行引擎做明白。3.3 可视化控制台怎么看比看什么更重要可视化控制台是这个项目里最出彩的部分也是交互设计上反复推翻次数最多的模块。技能列表页是最基础的卡片式布局每个卡片显示技能名称、类型、版本、健康状态、最近调用趋势。加上筛选器和搜索框支持按标签、按状态、按负责人过滤。对于几十个技能的场景这个列表页已经完全够用。我做得比较得意的是技能拓扑图。它可以展示技能之间的依赖关系比如“订单查询”依赖“用户信息校验”“用户信息校验”依赖“内部账号服务”。依赖拓扑不是靠手动画的而是从技能注册数据和运行时调用链里提取的。这里我给每个调用请求生成了一个全局唯一的 trace_id从入口到技能执行再到外部服务调用全程携带这个 ID执行引擎把这些 span 信息落库控制台再根据 span 里记录的 parent-child 关系还原出拓扑。状态实时刷新这块用了 WebSocket 推送。所有状态变化——技能上线、技能异常、调用失败——都会通过 WebSocket 推到前端不用手动刷新页面。这里吃过的亏是WebSocket 连接断开了不知道前端不重连结果控制台显示的状态停在十分钟前。后来加了心跳检测和断线重连机制才算稳定。这种运维细节不实际跑一段时间真的想不到。3.4 权限与审计技能越强大越要管住技能管理器有个容易被忽视但特别重要的模块权限与审计。技能本质上是一种能力能力越强被滥用的风险越大。一个能执行订单查询的技能如果任何 Agent 都能无限制调用总有一天会出事。我只设计了简单的两层技能级权限和调用级权限。技能级权限控制谁能查看、编辑、上下架某个技能调用级权限控制哪些 Agent 或服务可以实际调用技能。敏感技能被我单独标记成“受限技能”新 Agent 要调用这类技能必须通过管理端手动审批。这个审批流程看起来增加了操作步骤但实际用下来它有效地防止了很多“手滑”导致的线上事故。审计日志更是不能省。每一次调用的发起方、发起时间、参数摘要、执行结果、错误信息全部记下来。日志不仅是排查问题的依据也是未来做成本分析、Agent 行为分析的数据基础。我这里用了一张独立的审计表按天分表存储查询时按 trace_id 索引体量上来之后可以再考虑引入专业的日志系统。很多 Agent 项目不到线上出事故意识不到审计日志的价值但我建议从第一天就埋好。4. 实操从零搭建一个可视化技能管理器4.1 技术选型与项目结构这个项目我采用的是 FastAPI 做后端主框架原因它是 Python 生态里和 Agent 开发配合最顺的框架——类型标注天然支持 Pydantic 校验、异步性能好、自带 OpenAPI 文档。前端选择了 Vue 3 Element Plus做中后台管理界面省事。集成方面Agent 侧用的是 LangChain 和 LangGraph技能管理器对外提供工具发现接口让 LangChain 能动态拉取技能列表并转成 tool 对象。项目目录结构大概是这样的agent-skill-manager/ ├── backend/ │ ├── app/ │ │ ├── main.py # FastAPI 入口 │ │ ├── models/ # 数据库模型 │ │ ├── schemas/ # Pydantic 校验模型 │ │ ├── registry/ # 技能注册模块 │ │ ├── engine/ # 执行引擎模块 │ │ ├── audit/ # 审计日志模块 │ │ └── ws.py # WebSocket 状态推送 │ └── skills/ # 内置技能实现 ├── frontend/ │ ├── src/ │ │ ├── views/SkillList.vue │ │ ├── views/SkillDetail.vue │ │ ├── views/Topology.vue │ │ └── api/ # 后端接口封装 └── deploy/ ├── docker-compose.yml # 本地开发环境 └── nginx.conf # 前端静态资源代理4.2 技能注册中心的代码实现要点技能注册接口是整个系统的心脏。关键代码逻辑分三步解析技能描述文件、校验完整性、落地数据库。from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel, ValidationError from typing import Dict, Any router APIRouter(prefix/api/v1/skills) class SkillRegisterRequest(BaseModel): manifest: Dict[str, Any] router.post(/register) async def register_skill(req: SkillRegisterRequest): manifest req.manifest # 必填字段校验 required_fields [name, version, description, parameters] for field in required_fields: if field not in manifest: raise HTTPException(400, fmissing required field: {field}) # 技能名 版本唯一约束 existing await SkillRecord.find_one({ name: manifest[name], version: manifest[version], }) if existing: raise HTTPException(409, skill version already exists) # 参数 Schema 合法性校验 try: validate_json_schema(manifest[parameters]) except SchemaError as e: raise HTTPException(400, finvalid parameters schema: {e}) # 连通性预检远程技能 if manifest.get(type) http: ok, msg await preflight_check(manifest.get(endpoint)) status up if ok else pending else: status up record await SkillRecord.create( namemanifest[name], display_namemanifest.get(display_name, manifest[name]), versionmanifest[version], descriptionmanifest[description], manifestmanifest, statusstatus, created_atdatetime.utcnow(), ) await notify_clients(skill.registered, {skill_id: str(record.id)}) return {id: str(record.id), status: status}这里有个细节技能的状态不是注册成功就是“up”而是分“up”和“pending”。pending 状态的技能要等预检通过才会对外提供调用。这避免了一个新注册的技能还没准备好就被 Agent 选中然后跑出一堆莫名其妙的错误。这个细节虽然小但对稳定性提升非常明显。技能的本地实现我这里最简单的做法是给每个基础技能建一个 Python 文件统一暴露一个 async execute 函数async def execute(context: dict, params: dict) - dict: # context 里带了 trace_id、调用方信息 # params 已经经过 schema 校验 try: result await do_real_work(params) return {ok: True, data: result} except Exception as e: logger.error(f[{context[trace_id]}] skill execute failed: {e}) return {ok: False, error: str(e), trace_id: context[trace_id]}统一用 async 定义是为了保证执行引擎可以并发调度多个技能不会因为一个技能阻塞把整个进程拖死。写操作类技能我还会在函数开头加一个修改锁防止同一技能实例被并发调用导致数据错乱。4.3 可视化面板的关键实现思路前端这块我不打算贴大段源码说几个关键处的实现思路。技能列表页是最直接的 CRUD 展示接口是 GET /api/v1/skills返回技能数组前端做卡片渲染。状态标签直接用颜色区分绿色是 up橙色是 degraded红色是 down灰色是 disabled。这里“degraded”这个状态是后来加的代表技能还在运行但最近五分钟的错误率超过了阈值。这个渐进式状态设计比单纯的非绿即红好用得多。拓扑图一开始我想用重一点的图数据库方案后来发现不划算。技能之间依赖关系其实是很稀疏的图用轻量方式完全能画。我选择了 Canvas 手绘加简单力导向布局节点就是技能边是依赖关系。数据来自调用记录聚合成的依赖矩阵。实时性要求不高的话每十秒拉一次依赖数据就够了。力量导向布局的物理模拟参数需要调一下斥力太強节点全散开看着稀引力太强又挤成一团看不清依赖关系。我调了几轮最终固定在一个合适的参数组合上细节体验差的挺远的。WebSocket 推送的代码骨架如下from fastapi import WebSocket, WebSocketDisconnect class ConnectionManager: def __init__(self): self.active_connections: list[WebSocket] [] async def connect(self, ws: WebSocket): await ws.accept() self.active_connections.append(ws) def disconnect(self, ws: WebSocket): self.active_connections.remove(ws) async def broadcast(self, event: str, payload: dict): for conn in self.active_connections.copy(): try: await conn.send_json({event: event, payload: payload}) except Exception: self.disconnect(conn) manager ConnectionManager() router.websocket(/ws/events) async def ws_events(ws: WebSocket): await manager.connect(ws) try: while True: # 心跳保活 data await ws.receive_text() if data ping: await ws.send_text(pong) except WebSocketDisconnect: manager.disconnect(ws)前端收到技能状态变化事件后只更新对应卡片的状态字段不整页刷新所以页面操作很流畅。注意心跳不是前端发而是后端每隔一段时间主动给前端发一个“heartbeat”事件前端超过 30 秒没收到任何消息就主动重连续接。这个策略实测下来比前端主动 ping 更省资源也更可靠。4.4 与 Agent 对接的完整流程管理器本身跑起来只是第一步真正让它发挥价值的是跟 Agent 对接。我这里用一套“技能发现”接口让 Agent 可以从管理器拉取可用技能列表再动态转成 LangChain 的 tool。from langchain.tools import BaseTool class ManagedSkillTool(BaseTool): name: str description: str skill_id: str manager_base_url: str def _run(self, **params): import requests resp requests.post( f{self.manager_base_url}/api/v1/invoke/{self.skill_id}, json{params: params}, timeout10, ) result resp.json() if not result[ok]: return fskill invocation failed: {result[error]} return result[data] # 从管理器拉取可用技能 skills fetch_available_skills(agent_idagent-001) tools [ ManagedSkillTool( names[name], descriptions[description], skill_ids[id], manager_base_urlMGR_URL, ) for s in skills if s[status] up ]这段代码里有两处细节值得注意。一是每次都从管理器拉取最新技能列表这样新注册的技能不用改 Agent 代码就能立即被使用。二是只把 status 为 up 的技能暴露给 Agent异常技能直接被过滤掉。这两个细节加起来相当于给 Agent 加上了一个动态的能力感知层——它能感知到系统里哪些技能现在可用哪些不可用这就避免了 Agent 拿着一个过期技能列表硬调一个已经挂掉的服务。Agent 侧与 LangGraph 配合也很顺。在 LangGraph 的 state 里加入技能调用结果字段Agent 节点根据用户输入决定调用哪个 ManagedSkillTool工具执行完成后把结果写回 state再交给下一个节点。管理器在其中只负责一件事接请求跑技能返回结果。Agent 的具体决策逻辑、多轮对话流程、状态管理全部留在 LangGraph 里分工非常清楚。5. 常见问题与排查技巧实录5.1 技能调用超时与并发问题现象Agent 调用技能时经常报“timeout”或者某个外部服务响应突然变慢技能管理器里对应技能的错误率开始爬升。排查思路先在控制台看技能详情页的调用耗时趋势。如果耗时有明显抬升说明下游服务出问题的概率更大如果管理器总耗时正常但 Agent 那边还是报超时就要检查 Agent 到管理器之间的网络链路和 Agent 自身的请求超时设置。我这里曾经踩过一个坑管理器返回结果只要 1 秒但 Agent 侧的 HTTP client 默认连接超时设成了 3 秒结果因为一次 GC 停顿加上网络波动整体耗时偶发超过 3 秒就被 Agent 判定为超时。调大 Agent 侧超时配置后问题消失。并发另一个常见情况技能正常但多个 Agent 同时调用时大量失败。这种基本都是触发了限流或者信号量上限。先看技能详情里的“请求拒绝”计数如果拒绝数很高说明并发治理在起作用需要评估是提升技能并发水位还是让 Agent 侧降低调用频率。5.2 技能注册失败与状态不对现象注册技能时提示“invalid parameters schema”或者注册成功但状态一直是 pending。最常出现在 parameters 的 JSON Schema 写得不规范。我遇到过 required 字段写成了数组套数组、properties 里的键和 required 对不上、枚举值类型和字段类型冲突等。用调试工具先把 schema 跑一遍校验就能定位。注册成功但状态一直 pending多半是预检的 endpoint 配置有问题本机能访问但 Manager 部署环境访问不了。排查时先手动请求一下 endpoint再看预检请求的日志。另外如果技能是本地函数注册则不存在预检问题状态直接是 up所以看到 pending 状态基本可以断定是远程技能且预检失败。5.3 可视化页面状态与真实状态不一致现象控制台显示技能是 up但 Agent 调用一直报错。或者某个技能明明不可用控制台还是绿的。这个问题的根源在于技能状态是通过心跳和调用记录更新。如果一个技能已经很久没有被调用状态就不会刷新页面上看到的只是“最后的已知状态”。我加了存活性探测机制对于远程技能管理器每 30 秒主动发一次健康检查请求对于本地技能用异步任务定期检查技能的上次心跳时间超过 90 秒没心跳就标记为 down。加了这套机制之后控制台的 status 和真实可用性基本能够同步。如果你没有做探活看到的状态显示滞后就一点也不奇怪。另外 WebSocket 断连也会导致页面停在旧状态前面提到的心跳检测和自动重连务必加上。这里我不止一次忘记处理前端断线重连结果整页看起来毫无问题实际上信息已经过期。加了前端收到“heartbeat”事件后重置计时器30 秒没收到任何事件就调用 reconnect 的逻辑后这个问题彻底解决。5.4 技能冲突与版本管理现象新注册的技能把旧技能的配置覆盖了或者同名技能在不同环境之间造成混淆。这里要强调一个我反复吃教训后的强制约定技能运行时的唯一标识是“name version”注册新版本不会覆盖旧版本而是并存。Agent 要么显式指定版本号要么默认调用 highest version 里的 stable 状态版本。控制台里对同名不同版本的技能做了分组展示默认折叠显示最新版展开可以看到历史版本并支持一键回滚到指定版本。“回滚”的实现也很直接——把技能的 active_version 指向前一个版本号不是真的把旧代码恢复而是把调用路由切回去所以操作很快很安全。这里我整理了一份速查表方便各位对照排查故障现象首要排查点常见根因解决方案Agent 调用技能超时管理器调用耗时趋势Agent 侧超时配置过小调大 Agent HTTP timeout技能大量执行失败请求拒绝计数触发限流或信号量上限提升并发上限或降低调用频率状态显示 pending预检日志Manager 无法访问 endpoint检查网络与凭据配置页面状态长期不更新WebSocket 连接状态断线未重连增加断线重连机制新版本技能不生效版本路由配置active_version 未切换在控制台手动切换版本技能被 Agent 反复误选技能描述清晰度description 与真实能力不符重写 description 并增加触发标签审计日志查询过慢审计表数据量未做分表或索引按天分表并增加 trace_id 索引5.5 从一次线上事故聊起的反思最后分享一个让我印象深刻的线上小事故。某个 Agent 在高峰期突然频繁调用一个报表技能而这个报表技能每次执行都要跑一次较重的 SQL 聚合。因为并发超过了我设置的阈值执行引擎开始拒绝多余请求Agent 收到“技能繁忙”后不断地换措辞重试结果形成了重试风暴。当时页面上一片红排查发现原来是 Agent 的 retry 策略把“技能繁忙”也算进了可重试错误而且重试次数上限设得贼高。这个事故让我学到了两点。第一执行引擎返回的错误码要区分清楚对于“技能繁忙”这种限流错误Agent 应该做退避而不是立即重试对于真正的执行失败才可以按照重试策略来。第二在技能管理器里增加一层“Agent 调用行为监控”如果一个 Agent 对同一技能的重试频率异常直接启动熔断暂时屏蔽该 Agent 对该技能的访问防止拖垮整个系统。熔断的阈值可以根据技能重要性配置重要技能熔断时间短一些。这套机制上线后重试风暴基本没有再发生过。个人在实际操作中的体会是做可视化技能管理器最有价值的不是界面做得多漂亮而是底层的技能协议和执行引擎设计得够不够稳。技能协议决定了系统的扩展边界执行引擎决定了系统的稳定性底线界面反而是最容易被替换掉的表象。所以如果要参考这个项目做自己的版本我强烈建议先把技能描述协议定义清楚把执行引擎的超时、限流、熔断做好最后再做可视化界面。另外再给一个小技巧技能管理器里所有技能都应该支持“测试调用”。在技能详情页放一个测试按钮填上参数就能立刻调一次并展示完整返回和耗时。这个功能看着不起眼但在日常维护里使用频率极高无论是验证技能修改还是给业务同事演示技能能力都离不开它。我建议所有做类似管理系统的人第一个要做的功能不是漂亮的图表而是这个测试调用按钮。这个项目后续还能继续扩展的方向挺多的比如技能市场用于团队内部共享和复用技能、技能自动生成让 Agent 根据自然语言描述自动生成技能配置、更细粒度的调用成本分析。框架已经预留了接口后边一步步填坑就好。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑