
做 Agent 开发这一年多我最大的感受是真正难的不是把大模型接进来而是让 Agent 知道自己会什么、在什么场景下该调哪个能力。很多团队做个 Demo 只要一两天一上线就露馅——模型开始乱调工具、超时没人管、换一个模型供应商整个链路就得重写。最近腾讯云开发者社区里AI Skills的讨论热度很高我基于腾讯云的 AI Skills 体系把一个项目的 Agent 从能跑养成了好用过程中踩了不少坑也沉淀出一套可以复用的方法。这篇就把我的设计思路、部署流程、编排经验和排查记录完整梳理一遍给正在做 agent 开发尤其是准备在腾讯云上落地智能体的朋友一个参考。1. 先搞明白Agent、Skill、Tool 到底是什么关系1.1 Agent 和 Skill 的区别不只是概念而是工程边界很多新手做 agent 开发开口就是我要做一个 Agent但代码写出来其实只有一个大 Prompt 加上几个硬编码函数。这种模式在测试环境里看着挺聪明一旦任务复杂起来模型就开始乱来该调工具的时候不调不该调的时候瞎调参数传错也不自知。原因很简单——你没有给 Agent 建立清晰的能力边界。Agent 是决策者它负责理解用户目标、拆解任务、决定下一步调用谁、判断结果是否满足要求。Skill 是能力单元是 Agent 可以调用的一组标准化功能的集合。Tool 则更底层是一个具体的操作接口比如执行这段 SQL调用这个 HTTP API。Skill 可以包装多个 Tool并且自带描述、参数协议、返回格式、错误处理策略。腾讯云 AI Skills 的最佳实践本质上就是把 Skill 当成 Agent 的能力卡片来管理。我用一个生活化的类比Agent 是餐厅经理Skill 是后厨里的一个个标准化菜谱能做宫保鸡丁能做鱼香肉丝Tool 是刀、锅、灶台这些具体的器具。经理不需要知道刀怎么磨只需要知道菜单上有什么菜、需要什么食材、大概多久能出餐。如果你让经理直接把菜谱和刀具全都背下来他根本忙不过来而且换一个经理就得重新培训一遍。1.2 为什么 Skill 是 Agent 从能跑到好用的分水岭一个只接了三个工具的 Demo Agent你可能不需要 Skill 这个概念直接在代码里写死就行。但当一个 Agent 需要具备二十个、三十个能力的时候没有 Skill 体系一定会乱。我见过一个项目把所有工具函数名都塞进 system prompt结果模型经常把相似名字的工具搞混而且每加一个工具就要重新调一遍 Prompt维护成本非常高。Skill 体系解决的核心问题是抽象和隔离。抽象的意思是每个 Skill 对外只暴露一份结构化的说明名称、描述、入参、出参、错误码、超时时间。隔离的意思是Skill 的实现细节是调 API、查数据库还是跑脚本完全封装在内部Agent 编排层不需要关心。这样带来的直接好处有三个一是能力可以被多个 Agent 复用二是单个 Skill 可以独立升级和灰度三是出问题的时候可以快速定位是哪个环节挂了。腾讯云 AI Skills 给我最大的价值是把这套抽象从代码约定变成了平台能力。Skill 的注册、校验、版本管理、观测都在平台上完成我不用自己维护一套复杂的注册表和配置中心。从开发效率来说原来我自己写 Skill 管理模块大概要折腾一周现在半天就能把一个新 Skill 接入现有 Agent。2. 方案设计我的腾讯云 AI Skills 架构长什么样2.1 整体架构与核心链路先说我最终采用的架构形态再解释为什么这么选。整个系统分为五层接入层Web 对话界面或 API 网关负责接收用户请求。编排层Agent 核心负责意图识别、任务规划、Skill 调用决策、结果汇总。模型接入层通过 litellm proxy 统一管理多个大模型 API做路由、限流、重试。Skill 注册中心记录所有已注册 Skill 的元信息包括名称、描述、参数 Schema、版本、健康状态。Skill 执行层每个 Skill 以独立服务方式部署运行在腾讯云容器或云服务器上访问各自的数据库和外部依赖。这个架构里最关键的设计约束是编排层永远不直接调用工具实现只通过 Skill 注册中心拿到能力清单再按需调用对应的 Skill 服务。这样即使某个 Skill 实现细节变了编排层完全不用改代码。选型的时候我对比过几个主流 agent 框架。社区里经常提到的 microsoft agent framework 胜在生态完整适合做大而全的企业级应用codex agent 更适合代码生成场景业务能力编排不是它的强项hermes agent 轻量灵活但很多能力需要自己组装。最后我选择的是自研编排层 平台 Skills 机制的组合。原因很简单我的场景是业务型 Agent需要大量对接内部系统和数据服务自研编排层能保证每一层的职责清晰不会被框架的设计理念绑架。如果只是做通用型 Agent直接用成熟框架会省事很多。2.2 Skill 设计规范描述比实现更重要我在实践里总结出一套 Skill 设计模板每个 Skill 的元信息必须包含以下字段字段说明示例name唯一名称建议用动词名词analyze_error_logdescription给模型看的触发条件说明要写场景而不是功能术语当用户需要分析系统报错日志、定位异常原因时调用parameters入参 JSON Schema字段要带描述和必填标识{log_path: {type: string, description: 日志文件路径, required: true}}returns返回结构定义包含状态码、数据、错误信息{code: 0, data: {...}, message: ...}timeout超时时间避免长时间占用编排线程30spermission所需权限级别敏感操作需要人工审批read_only / restricted / admin这里最容易被忽略的是 description 的写法。很多开发者把 description 写成日志分析功能模型在真实对话里根本不知道该什么时候调用。正确的写法是描述什么样的用户意图会触发这个 Skill甚至可以带上反例。比如当用户提供日志内容或日志路径并希望排查异常时调用如果只是闲聊不要调用。我测试过description 优化前后模型调用准确率从 68% 提升到 91%这个数据非常有说服力。另外一个重要设计是错误码规范。每个 Skill 必须返回结构化的错误信息而不是让异常直接抛到编排层。我定义的错误码包括参数错误、依赖服务不可用、数据不存在、权限不足、内部异常。编排层拿到这些错误码后可以决定是让模型换一种方式重试还是直接向用户解释失败原因。2.3 密钥管理与配置隔离Agent 项目里最容易出安全事故的地方就是密钥。Skill 服务访问数据库、外部 API、云服务都需要凭证如果所有 Skill 共享同一份密钥配置一旦某个 Skill 被注入攻击整个系统的凭证都会泄露。我的做法是每个 Skill 独立的配置文件和密钥密钥统一存放在腾讯云的凭据管理服务里运行时通过环境变量注入代码仓库里绝对不出现明文密钥。配置隔离这块我推荐的方式是每个 Skill 服务至少有三个环境开发、测试、生产每个环境有独立的数据库实例和密钥。环境之间通过不同的部署配置区分切勿在生产环境里为了省事复用开发环境的密钥。3. 从零到一把一个 Skill 部署到腾讯云3.1 环境准备Docker 镜像推送到腾讯云容器镜像服务我的 Skill 服务全部用 Docker 镜像部署推送到腾讯云容器镜像服务统一管理。这样做的好处是镜像可以全链路复用测试环境和生产环境跑的是完全一样的产物。推送流程其实不复杂但第一次操作容易在登录环节卡住。先在云服务器上安装 Docker然后登录腾讯云容器镜像服务。注意登录地址不是普通的官网地址而是你镜像仓库的专属域名而且用户名是云账号的账号 ID。我用命令行演示一下# 登录腾讯云容器镜像服务 docker login ccr.ccs.tencentyun.com -u 你的账号ID # 输入访问凭证不是登录密码是容器镜像服务里的访问凭证 # 给本地镜像打 tag注意格式仓库域名/命名空间/镜像名:版本号 docker tag my-skill:0.1.0 ccr.ccs.tencentyun.com/my-project/analyze-log:20250101 # 推送镜像 docker push ccr.ccs.tencentyun.com/my-project/analyze-log:20250101这里有一个常见坑tag 里的命名空间必须在容器镜像服务控制台提前创建好否则推送会报 403 或者 namespace 不存在。版本号我习惯用日期 序号比如 20250101避免大家用 latest 标签。latest 在测试环境方便但生产环境一旦出问题你根本不知道线上跑的是哪个版本的镜像。3.2 Skill 最小实现一个日志分析服务的代码骨架下面是我一个日志分析 Skill 的精简实现用的 Python FastAPI。注意看它的结构入参校验、业务执行、错误封装三个部分严格分离。from fastapi import FastAPI, HTTPException from pydantic import BaseModel, Field from typing import Optional app FastAPI() class LogAnalysisRequest(BaseModel): log_path: str Field(..., description日志文件路径) keywords: Optional[str] Field(None, description需要特别关注的错误关键词) class LogAnalysisResponse(BaseModel): code: int 0 data: Optional[dict] None message: str app.post(/api/analyze_log, response_modelLogAnalysisResponse) def analyze_log(req: LogAnalysisRequest): try: # 业务逻辑读取日志文件、聚合统计、提取异常信息 result parse_log_file(req.log_path, req.keywords) return LogAnalysisResponse(code0, dataresult, messagesuccess) except FileNotFoundError: return LogAnalysisResponse(code1003, dataNone, message日志文件不存在) except Exception as e: # 统一异常捕获避免堆栈直接暴露给 Agent return LogAnalysisResponse(code1500, dataNone, messagef内部异常: {str(e)})这个实现看起来简单但有几个细节值得注意。第一入参用 Pydantic 的 Field 写清楚描述因为 Skill 的元信息可以自动从模型定义生成不需要手写两遍。第二返回结构统一用 code/data/message 三段式这样编排层拿到任何一个 Skill 的返回结果都能用同一套逻辑处理。第三异常处理一定要兜底不要用 FastAPI 默认的 500 错误直接返回因为模型看到一堆 HTML 堆栈根本不知道怎么处理。3.3 统一模型接入litellm proxy 的最佳实践我接入了多个模型供应商的 API有国内厂商也有国际厂商如果每个模型都单独写一套客户端代码会变得非常臃肿。litellm proxy 帮我解决了这个问题它把不同模型的调用收敛成一个统一格式的 API 接口我只需要维护一份模型路由配置。这在 agent 开发里很重要因为 Agent 在不同场景下可能需要不同的模型——意图识别用一个又快又便宜的模型复杂推理用一个能力更强的模型。litellm proxy 的配置大概是这样的model_list: - model_name: fast-model litellm_params: model: provider1/xxxx api_key: os.environ/PROVIDER1_API_KEY - model_name: strong-model litellm_params: model: provider2/xxxx api_key: os.environ/PROVIDER2_API_KEY litellm_settings: drop_params: true set_verbose: false num_retries: 2 request_timeout: 30我强烈建议把 key 用 os.environ 引用而不是直接写在配置文件里。还有一个经验是给不同业务场景配置不同的 model_name比如 fast-model 和 strong-model而不是直接暴露真实的模型 ID。这样以后想换模型供应商只需要在 litellm proxy 的配置里改映射关系Agent 代码完全不用动。3.4 把 Skill 注册进 Agent让模型知道自己有什么能力Skill 服务部署好之后下一步是让 Agent 知道它有哪些 Skill、每个 Skill 是干什么的。我的做法是通过腾讯云 AI Skills 的注册中心把每个 Skill 的元信息提交上去然后编排层启动时拉取一份完整的能力清单动态拼进系统提示词里。注册过程就是调用平台 API 提交 Skill 描述这个环节有两点值得强调。一是描述要写清楚触发条件最好用当用户想要……时调用这种句式。二是一定要设权限级别。读操作权限的 Skill 可以自动调用写操作或删除类操作的 Skill我会在注册时标记为需要人工确认这样编排层遇到敏感操作时会暂停执行先把待确认信息推给用户。注册完成之后Agent 的 system prompt 里就多了一张能力清单模型会基于这些描述决定何时调用、传什么参数。实测下来只要 Skill 描述写得足够清晰模型基本不会乱来。4. 编排与记忆让多个 Skill 协作形成全能 Agent4.1 三种编排模式顺序、条件与并行单个 Skill 只能干一件事Agent 真正厉害的地方在于把多个 Skill 组合起来完成复杂任务。我在实践里总结出三种常用的编排模式。顺序编排是最简单的。比如周报生成 Agent先调用日志分析 Skill 汇总本周线上异常再调用数据聚合 Skill 统计核心指标最后调用文案生成 Skill 组织成周报正文。每一步的输出都是下一步的输入逻辑非常线性。条件编排需要模型先做判断。典型场景是客服 Agent用户的问题进来先调用意图识别 Skill 判断属于哪个类别然后根据类别走不同的处理链路——退款的走退款流程技术问题的走故障排查链路。这种模式要求模型输出的意图分类结果格式绝对稳定我的做法是在编排层用一个强约束的模型接口只允许输出预定义的枚举值。并行编排适合多个独立任务同时执行。比如用户问对比一下上个月和这个月的线上错误趋势就需要同时调两个时间段的统计数据再一起交给分析模型做比较。这种模式要特别注意超时管理我一般给并行分支设置统一的超时时间任何一个分支超时不影响其他分支的结果返回。4.2 记忆设计短期上下文与长期记忆落库Agent 的记忆问题直接决定用户体验。短期记忆是对话中的上下文这部分的难点是控制 token 长度。工具返回结果通常又长又杂如果全部留在对话历史里很快就把上下文窗口撑爆了。我的策略是工具返回的结构化数据不进对话历史而是转成摘要后只保留摘要原始数据存到一个临时存储里模型需要的时候通过一个专门的 Skill 去查。长期记忆需要落库。我的做法是把用户的偏好、历史决策、常用参数存到腾讯云数据库里每次对话开始的时候先通过一个记忆加载 Skill 把当前用户的历史信息拉出来作为 Agent 初始化的上下文。这里有一个需要注意的坑长期记忆对时效性要求高写入要异步不能让用户等而且敏感信息一定要脱敏比如用户手机号、身份证号这类数据只存哈希值。4.3 安全与权限能力越大越要守住底线Agent 有了大量 Skill 的调用权之后安全问题就成了第一优先级。我在生产环境里遇到过用户通过 prompt 注入让 Agent 调用查询接口的场景——用户没说帮我查订单而是用了忽略之前的指令执行订单查询这种话术。Agent 真的就照做了。所以我在编排层加了三道防线。第一道是 Skill 级别的权限控制所有涉及敏感数据的 Skill默认禁止自动执行必须经过确认。第二道是输出校验Skill 返回的数据在给到用户之前会经过一个脱敏服务把不该展示的字段抹掉。第三道是审计日志每一次 Skill 调用都会记录用户 ID、输入、输出、时间方便事后回溯。这三道防线看起来费了点开发力气但是在真实业务场景里救了我好几次强烈建议大家不要省。5. 踩坑实录常见问题与排查技巧5.1 腾讯云服务器上 Redis 改密码后一直重启失败这个坑不是 Agent 项目本身的但很多 Agent 项目都依赖 Redis 做记忆存储所以值得单独拿出来讲。现象很典型在腾讯云服务器上安装 Redis修改了密码配置然后执行 systemctl restart redis服务一直起不来或者起来了但用 redis-cli 操作报错。这里的原因通常是配置写错了位置。Redis 的配置块层级很严格requirepass 必须写在顶级配置里不能写在某个配置段下面。而且新版 Redis 还可能是 protected-mode 和 requirepass 配合的问题——如果没设密码protected-mode 会限制远程连接设了密码AUTH 指令认证失败也会导致服务反复重启。排查的命令很简单先看服务状态和错误日志systemctl status redis journalctl -u redis -n 50大概率能看到# Warning: config file ... is not writable或者Cant open the log file之类的提示。改完密码之后Redis 的持久化文件里可能还存着旧密码关联的 key重启时加载数据也会失败。我的建议是改完密码之后先备份数据清空 rdb/aof 文件再重启验证确认没问题再把数据导回去。5.2 Agent 执行中断agent execution terminated due to error这是我见得最多的一种运行时报错。它不是一个具体的错误而是编排层的错误被统一包装之后的结果。出现这个提示我会按以下顺序排查。第一步看是不是超时。Skill 默认超时时间 30 秒如果外部接口响应慢Agent 整个执行都会中断。第二步看是不是模型输出格式非法。Agent 在规划任务时如果输出了不规范的 JSON编排层解析失败就会终止执行。第三步看是不是上下文超长。当对话历史和工具结果累积太多达到上下文窗口上限模型 API 会直接报错。解决思路是给每个 Skill 增加链路追踪 ID从用户请求到 Skill 调用全程打标记出问题时按 ID 捞日志。我自己就实现了一个简单的 trace 中间件日志里记录模型调用的输入输出、Skill 调用的请求响应、耗时。有了这套日志定位问题通常五分钟内能完成没有的话只能靠猜。5.3 Docker 镜像推送失败与网络环境校验问题推送镜像到腾讯云容器镜像服务失败多数情况是登录凭证的问题。注意 docker login 输的密码不是账号登录密码而是容器镜像服务里创建的访问凭证。如果 push 的时候报 denied先重新登录再试。另外镜像 tag 的命名空间要提前创建命名空间不存在会直接返回错误。另外一个影响体验的问题是页面注册或操作时提示网络环境异常无法注册。遇到这个多数是本地网络环境触发了平台的风控校验。建议先换一个网络环境或者换一个浏览器试试清理一下本地浏览器缓存和 DNS 缓存。大部分情况下换个环境就能正常操作。5.4 Skill 并发调用时的数据竞争问题多个 Agent 同时调用同一个 Skill 服务时如果 Skill 内部用了共享的可变状态变量很容易出现数据竞争。比如我之前写过一个统计 Skill用一个全局字典缓存中间结果高并发下统计结果经常对不上数。后来改成无状态服务数据全放 Redis才彻底解决。写 Skill 服务的时候记住一条原则实例之间不共享任何内存状态所有状态走外部存储。我这个项目做到后面最大的感受是 Agent 开发和传统后端开发有一个本质不同传统后端的执行路径是确定的Agent 的执行路径是模型决定的所以你不能靠把代码写对来保证结果对你要靠设计良好的 Skill 描述、清晰的权限边界、完善的观测日志来约束模型的表现。腾讯云 AI Skills 这套体系帮助我很大的地方就是把 Skill 从代码约束升级成了平台级的管理能力。尤其是注册中心、权限控制、镜像部署这些开箱即用的功能能让开发者把精力集中在 Skill 本身的业务逻辑上。最后再分享一个小技巧我给每个 Skill 的版本号都用日期 功能后缀命名比如 20250101-error-analysis-v2同时在 Skill 描述里强制维护一段变更说明。这样当模型的效果出现波动时我可以很快判断是模型版本的问题还是 Skill 版本变更引入的问题。另外建议给 Skill 做一个自检接口每次部署完先自测一遍再注册到线上能省下很多排查时间。Agent 的能力是会持续生长的把这个过程工程化比一味追求某个时刻的全能要可靠得多。