做 Agent 有一段时间了,我发现很多刚入门的同学会把注意力全放在模型选型、Prompt 调优和大模型推理上,反而忽略了一个真正影响落地效果的东西——AI Skills。如果你的 Agent 只会聊天,那它撑死算个聊天机器人;Skills 才是让 Agent “会干活”的肌肉和工具库。这篇文章不聊虚的,我把在腾讯云上从零搭起一套可用的 Agent + AI Skills 全链路写出来,包括概念理解、云端环境准备、Skill 编写、服务编排、上线部署,以及我在真实项目中踩过的坑和排查思路。如果你正准备把 Agent 推到业务场景里,这篇应该能帮你省掉不少冤枉时间。
先说清楚一条主线:Agent 是大脑,负责理解用户意图、拆分任务、决定下一步做什么;AI Skills 是手脚,负责把某一个具体动作变成可执行、可复用、可被 Agent 调用的能力单元。你可以在腾讯云上用一台轻量服务器外加云上网关、对象存储等基础服务,把这两层完整地串起来。我会尽量按“先设计、再搭建、后编码、终排查”的顺序来讲,少说抽象概念,多给可以直接照做的方案。
1. 先搞懂 Agent 与 AI Skills 的分工
1.1 最容易被忽略的区别
很多刚接触 Agent 开发的人一上来就搜“agent 框架”“agent 架构”,然后发现资料一大堆,反而更乱。我建议先忘掉框架,把 Agent 和 Skills 的边界想清楚。
Agent 的核心职责是规划与决策。它拿到用户的一句话之后,要判断应该调用哪个工具、要填充哪些参数、调用失败后要不要换一种方式重试,以及在多步任务里如何串联出最终结果。Agent 并不直接关心“天气接口的 URL 是什么”或“数据库密码存在哪里”,这些细节应该下沉到 Skill 层。
Skill 的核心职责是执行。它是一段封装好的能力代码,有自己的名称、描述、入参出参格式,甚至可以有独立的依赖环境。你可以把它理解为“给 Agent 用的函数”:Agent 负责传参数,Skill 负责老老实实地把活儿干完,然后把结构化结果交回来。
实操中最容易犯的错误就是把业务逻辑全塞进 Agent 的 System Prompt。短期看好像也能跑通,但一旦你开始加第二个、第三个功能,Prompt 会越来越臃肿,模型容易“精神分裂”,要么调错工具,要么漏参数。正确的做法是把每项能力做成独立 Skill,让 Agent 在需要时才加载。这就是我强调 Skill 与 Agent 分离的最直接原因。
1.2 Skills 在腾讯云上的具体形态
在腾讯云这套体系里,AI Skills 并不是一个玄乎的概念,它其实就是你部署在云上的“可执行能力单元”。我常用的落地形态有三类:
第一类是纯代码型 Skill。比如一个“URL 内容抓取并提取正文”的 Skill,内部用 Python 写爬虫逻辑,通过云函数或容器跑起来,向外暴露一个 HTTP 接口。Agent 收到“帮我看一下这个页面讲了什么”的请求时,会调用这个 Skill,传入目标 URL,拿到返回的正文摘要。
第二类是 Prompt 模板型 Skill。这种 Skill 不写复杂代码,而是封装一套带参数的大模型调用模板。比如“生成周报”这个 Skill,输入是“本周工作事项列表”,内部会组装专用的 Prompt 调一次大模型,输出一份结构化周报。它适合复用性高的文本生成场景。
第三类是外部 API 代理型 Skill。你需要在腾讯云上申请二级域名、配置安全组和网关,把第三方接口包装成自己可控的 Skill 入口。好处是上游接口变了,你只改 Skill 这一层,不需要动 Agent 主逻辑。
以上三种形态在腾讯云上都有对应的承载方式:轻量应用服务器或者云服务器跑容器、云函数跑轻量逻辑、API 网关做统一路由。我在下文会展开讲具体怎么搭。
2. 腾讯云环境搭建与基础配置
2.1 服务器选型和网络规划
不要一上来就买最高配的机器,先想清楚你的 Agent 主服务和 Skills 到底要跑什么。如果 Skill 大多是小体量的 HTTP 接口,没有 GPU 推理需求,那 2 核 4G 的轻量服务器足够跑通整套原型。如果你打算用本地模型做部分推理,或者 Skill 里涉及视频深度估计、图像批量处理这类重负载任务,再考虑带 GPU 的实例。
网络规划很多人会忽略。我建议把服务按“公网入口层”和“内部业务层”分开理解:对外暴露的服务只留必要的端口,内部服务之间尽量走腾讯云的内网 IP。这样既安全,又不容易出现端口冲突,还能省一点公网流量费。
我最初做第一版 Agent 时,把所有 Skill 和应用都塞在同一台服务器上,端口从 8000 一路开到 9000,表面上跑得挺欢。等后面要加新 Skill,发现端口规划已经乱成一团,排查问题非常痛苦。后来我强制自己在腾讯云控制台建了安全组,把服务按“网关层”“Agent 核心层”“Skills 业务层”三个维度分开,清清爽爽。
2.2 安全组端口放行的正确姿势
热搜里有人搜“腾讯云如何开放所有端口”,我必须提醒一句:千万别在生产环境把安全组入方向设成 /0 加全部端口,这是把服务器裸奔在公网上。真正应该做的是最小化放行。
实操中我一般这样配:
- 22 端口只允许你自己的办公网 IP 访问,用来 SSH 登录。
- 80/443 端口对所有公网开放,用于对外提供 Agent 服务和 Skill 调用入口。
- 其他业务端口,比如 8080、5000 等,不直接暴露公网,而是由 Nginx 反向代理转发,或者只允许腾讯云内网 IP 访问。
如果你确实有临时调试需要,最多在安全组里加一条“来源 IP 填你当前网络出口 IP、端口填临时调试端口”的规则,用完就删。这也是回答“腾讯云如何开放所有端口”这类问题的最好方式:不是不会开,是真的不建议开。
配好安全组之后,建议顺手在服务器内部把防火墙也启动起来,例如在 Ubuntu 上用 ufw 只允许必要端口。双重校验比单靠云平台规则更稳,因为我遇到过某次控制台规则被误删、安全组实际没生效的情况,最终是服务器本机防火墙兜住了。
2.3 域名解析与二级域名配置
Agent 服务上线后,你总不能一直用 IP 加端口调接口,既不好记也不好管理。建议准备一个域名,然后用二级域名区分不同 Skill。
搜索“腾讯云怎么申请二级域名”的同学,本质是想要一条清晰的操作路径。我在腾讯云上的操作顺序是:
- 在腾讯云 DNS 解析控制台添加一个 A 记录,主机记录填你想要的二级域名前缀,比如 skill.agent.example.com,记录值填服务器公网 IP。
- 等解析生效后,在服务器 Nginx 里为这个二级域名配置一个 server 块,反向代理到本机某个 Skill 服务端口。
- 如果服务基于 HTTPS,记得在腾讯云申请 SSL 证书并配置到 Nginx,这样 Agent 云端调用时走 HTTPS 更稳。
一个二级域名对应一个 Skill 服务,是我目前最顺的管理方式。我做过一个项目,一共六个 Skill,域名分别为 fetch.skill.example.com、summary.skill.example.com、search.skill.example.com 等,维护起来非常直观。
2.4 运行环境与常用服务安装
环境初始化这部分,我通常会在腾讯云 Ubuntu 服务器上完成以下几步:
- 把系统源切换成腾讯云内网镜像源,速度会快很多。
- 安装 Docker,后续 Skill 全部用容器跑,方便隔离依赖和版本回滚。
- 安装 Nginx,作为统一入口做反向代理。
- 搭建一个简单的日志目录结构,所有服务的日志都集中放,便于后续排查。
Skill 用 Docker 容器承载,是我反复验证后觉得最省心的方案。每个 Skill 一个镜像,镜像里装好自己需要的 Python 包或 Node 模块,互不干扰。你不需要给每个 Skill 单独配一套虚拟环境,也不怕某个依赖升级把其他服务搞挂。需要更新时,重新构建镜像、滚动重启容器即可。
如果 Skill 只是简单的几十行逻辑,用云函数会更轻。它不需要你维护服务器,上传代码就能跑。我通常用这条规则来区分:逻辑简单、调用量有波峰波谷的 Skill 放云函数;有状态、依赖重、需要常驻内存的 Skill 放 Docker 容器。
3. Skill 的编写与接入细节
3.1 Skill 的结构定义
Skill 不是随手写一个函数就算完,你要给它一个 Agent 能读懂的结构。我习惯每个 Skill 包含三部分:元信息描述、输入输出 Schema、执行逻辑。
元信息描述是给 Agent 看的。你要用自然语言写清楚这个 Skill 是干什么的、什么场景下调用、有什么限制。这个看起来不起眼,实际上直接决定 Agent 在复杂场景下能否准确选中这个 Skill。描述写得太宽泛,Agent 就容易误调用;写得太窄,Agent 该用的时候又想不到。
输入输出 Schema 我建议用 JSON Schema 格式。比如“网页正文提取”这个 Skill,入参是 url 字符串和超时时间整数,出参是标题、正文、作者、发布时间等字段。把 Schema 给到 Agent,Agent 才能学会如何从用户句子中抽取参数并完成调用。
执行逻辑就是 Skill 的真正实现。我通常用 FastAPI 写一个简单服务,暴露 POST 接口,接收符合 Schema 的 JSON 请求,处理完后返回同样符合 Schema 的 JSON 响应。Skill 内部的代码不需要关心 Agent 怎么思考,它只需要保证:给定合法入参,返回合法出参;入参不合法时,返回清晰的错误信息。
有一个经验值得分享:错误信息一定要写得结构化。不要只返回一个 “error: failed”,最好返回 error_code 加 error_message。Agent 拿到错误信息后,能根据 error_code 判断是重试还是换一个 Skill,还是直接告诉用户“这个操作我现在做不了”。写得模糊会导致 Agent 反复重试同一错误,白白浪费 token。
3.2 多 Skill 统一管理和编排
Skill 数量多了之后,你就要考虑管理问题。我见过有人把几十个 Skill 的源码堆在同一个目录里,起名 skill_1.py、skill_2.py,后期根本没法维护。更合理的做法是每个 Skill 一个独立目录,目录里包含源码文件、依赖清单、README 和测试用例。
这个环节经常会有人问“skill 和 agent 的区别是什么”“skill 和 workflow 的区别是什么”。我理解的区别是这样的:Skill 是单点能力,它只负责一件事;Agent 是协调者,它决定在什么条件下按什么顺序调用 Skill;Workflow 则是把多个 Skill 和判断节点按固定流程串起来,用于那些规则明确、不需要模型动态规划的场景。三者可以配合使用,不能混为一谈。
在编排层面,我推荐做一层“Skill 网关”,让 Agent 不直接感知每个 Skill 的物理地址,而是通过网关做统一路由。网关里维护一张 Skill 注册表,包含 Skill 名称、描述、入口地址、当前版本、健康状态。Agent 发起调用时,网关负责找到正确的 Skill 并把请求转发过去。未来如果你要升级某个 Skill,只需要在注册表中切换版本,不需要改 Agent 代码。
刚开始搭建时,如果你想降低成本,甚至可以先把“Skill 网关”做成一个简单的 Python 字典映射,先不引复杂的服务发现组件。等 Skill 数量上了两位数,再考虑上真正的注册中心。
3.3 Agent 与 Skill 的调用约定
Agent 和 Skill 之间要有一份双方都遵守的约定,我的实践是全部用 JSON over HTTP,不搞私有协议。原因很简单:HTTP 协议通用,调试方便,Agent 框架和 Skill 代码可以解耦,将来 Skill 换语言、换部署方式都不会影响主线。
一次完整的调用会是这样一个流程:
- 用户输入“帮我抓取下这个网页并总结要点”。
- Agent 主服务把这个请求交给大模型做意图识别和参数抽取,得到“调用网页提取 Skill,参数 url=xxx”。
- Agent 向 Skill 网关发起请求,网关转发给网页提取 Skill。
- Skill 执行完,返回 title、content 等结构化字段。
- Agent 拿到结构化的正文内容后,再调大模型做摘要生成。
- 最终把摘要返回给用户。
流程不复杂,但有一个隐藏的坑:大模型输出的参数不一定严格合法。用户说“看一下这个网址”,模型可能把多段无关文本都塞进 url 参数里,导致 Skill 执行失败。所以我都会在 Agent 和 Skill 之间加一个参数校验层,不满足 Schema 就做一次轻量修正。有的 Agent 框架自带 function calling 的参数约束,你可以直接利用;如果用自研方案,就得自己写校验。
调用超时和重试策略也要提前想好。Skill 执行时间如果超过 Agent 的等待阈值,Agent 端就可能报 “the agent execution provider did not respond in time”。这类问题我在第 5 部分会重点讲排查思路。
4. 一条完整的上线链路与性能调优
4.1 从零到一上线一个“文档问答”项目
我用一个实际做过的“知识库文档问答”项目来串一遍完整流程。这个项目的目标是让用户用自然语言提问,Agent 从其 Skill 管理的多个 PDF 文档里找到答案并回答。
第一步,在服务器上部署文档解析 Skill。这个 Skill 接收 PDF 文件路径或上传文件,解析出纯文本然后切片。我用 Docker 装了 PDF 解析库,暴露一个 HTTP 接口。返回内容是 JSON 数组,每段包含文本和页码信息。
第二步,部署向量化和检索 Skill。文档被切分成段落后,需要向量化入库。向量库我直接用了云上的数据库产品,没有自己在容器里硬扛。检索 Skill 的作用是接收用户问题向量,在向量库里做相似度检索,返回最相关的前几段文本。
第三步,把以上 Skill 挂到 Agent 主服务上。Agent 判断用户提问是否需要查知识库,如果需要,先调检索 Skill,拿到候选文档片段,再把这些片段作为上下文连同原始问题一起送给大模型做生成回答。这样大模型不需要记住几千页 PDF 的内容,只需要针对检索到的那几段文字做推理,准确率和成本都可控。
第四步,通过 API 网关暴露给前端。前端聊天窗口的请求先进网关,网关转发到 Agent 主服务,Agent 主服务再按需调用 Skill。整个过程前后端分离,后续要接入小程序或公众号也方便。
4.2 记忆与上下文管理
Agent 开发里“记忆”是个高频话题。做知识库问答时你会发现,如果用户连续追问“刚才那段说的具体是哪个版本”“再详细讲一下第三条”,Agent 必须能引用多轮对话里的信息。
我的方案是把记忆分成短期和长期两层。短期记忆指当前会话窗口内的上下文,直接存在 Agent 主服务的内存里,用一个 session_id 关联,超过一定轮数就截断或压缩。长期记忆指跨会话的用户偏好、历史关注点,存到腾讯云的数据库服务里,当新会话开始时按用户 ID 拉取相关摘要注入到上下文中。
特别提醒:不要把所有历史消息一字不差地塞进每次请求。模型上下文窗口虽然越来越大,但塞得越多,响应越慢、费用越高、注意力越容易被稀释。我的习惯是主动做摘要压缩:每一轮对话结束后,把关键信息浓缩成几句话,存成记忆摘要,下一轮只带摘要和最近两轮完整对话。
搜索“agent 记忆”相关内容的同学,应该也是想解决这个问题。其实现阶段不需要追求完美记忆,先做到“关键信息不丢、无关信息不带”就够了。等用户量上来,再针对记忆的存储结构、检索召回做优化。
4.3 响应速度与成本控制
Agent 多轮调用大模型的延迟,很多时候不来自模型本身,而来自链路中的额外等待。我在项目里实测发现,一个简单问答如果完全不做优化,从用户发消息到收到回复可能要 5 到 8 秒,其中大模型生成只占 2 到 3 秒,剩下全耗在 Skill 循环调用、上下文重复传递、网络多次往返上。
第一刀砍在 Skill 调用次数。Agent 每次动大模型做“下一步决策”都有成本,如果能把固定动作做成 Workflow,就绝不每次走模型随机规划。比如文档解析后必然要切片,切片后必然要向量化,这三个步骤即使让模型来规划也是固定顺序,不如用 Workflow 串联,省掉中间多次模型决策调用。
第二刀砍在冗余上下文。不少 Agent 框架会把工具描述、历史记忆、系统提示全部塞进每次请求。你可以在请求前做一次 token 估算,把和当前问题无关的历史消息剔除,把可以压缩的描述精简。实测能把单次请求 token 数降 30% 到 50%。
第三刀是缓存。对相同或高度相似的问题,可以直接把答案缓存起来。我在项目里设置了一个相似度阈值,用户新问题和历史问题的向量相似度超过 0.95 就返回缓存答案。这类命中的响应几乎无延迟,还能明显降低模型调用费用。像 Litellm Proxy 这类工具也可以实现统一的模型路由和缓存策略,如果你已经在用类似的网关组件,可以直接在上面配置缓存规则,不必自己再重复造轮子。
5. 常见问题与排查技巧实录
5.1 日志里那些吓人的报错
Agent 项目跑起来之后,你大概率会遇到五花八门的报错。我整理了一份高频问题速查表,都是我在腾讯云上实测遇到的,按错误关键词分类:
| 报错特征 | 常见原因 | 处理方式 |
|---|---|---|
| agent execution terminated due to error | 某个 Skill 内部抛了未捕获异常 | 看完整堆栈,定位是参数错误还是依赖缺失;Skill 侧统一加异常兜底,返回结构化错误 |
| the agent execution provider did not respond in time | Skill 接口响应超过 Agent 等待上限 | 排查 Skill 是否有阻塞操作,调大超时阈值或给 Skill 做异步化 |
| timeout / upstream timed out | Nginx 转发时后端处理太久 | Nginx 的 proxy_read_timeout 调大,同时优化 Skill 自身性能 |
| connection refused | 服务没起来或端口监听错误 | 确认容器状态、监听地址是否为 ,别只监听 127.0.0.1 |
| 502 Bad Gateway | 后端服务异常退出或端口不对 | 查看 Nginx error log,确认容器或进程是否还活着 |
| 参数校验失败 | Agent 抽取参数不准 | 在网关层加参数兜底修正,让 Skill 自身也能处理缺失值 |
| JSON 解析错误 | 大模型返回了不规范 JSON | 开启更严格的 function calling,或加一层 JSON 修复逻辑 |
我遇到最典型的一次 “agent execution terminated due to error”,查了半天发现是一个 Skill 里用了相对路径读写临时文件,而容器工作目录和预期不一致,导致文件找不到。这种问题在本地能跑、上容器就挂,往往是路径和依赖环境差异造成的。
5.2 排查问题的一线流程
排查 Agent 链路问题,最忌讳漫无目的地翻日志。我有一套固定的排查顺序,按这个顺序走,绝大多数问题十分钟内能定位到根因。
第一步,确认故障发生在哪一层。看用户原始请求、Agent 决策记录、Skill 调用记录、最终响应。如果 Skill 调用记录里根本没有这条请求,说明 Agent 压根没选择这个 Skill,问题出在意图识别或 Skill 描述上。如果有调用记录但返回错误,问题在 Skill 本身或网关层。
第二步,手动复现 Skill 调用。直接用 Postman 或 curl 构造一个符合 Schema 的请求,打到 Skill 地址上,看它是否正常返回。这一步能把“Agent 外部问题”和“Skill 内部问题”彻底分开。如果手动调用正常,说明问题出在 Agent 传给 Skill 的参数不对。如果手动调用也报错,就去翻 Skill 的日志和依赖。
第三步,检查网络与安全组。腾讯云服务器上经常出现“在服务器本机 curl 通,但公网访问不通”的情况,先看安全组是否放行了对应端口,再看本机防火墙,然后用另一台机器做公网连通性测试。
第四步,看日志中的耗时分布。从入口日志里提取每个环节的耗时,看是卡在 Agent 思考阶段、Skill 执行阶段还是网络转发阶段。哪一段耗时长就优化哪一段,不要盲目给整条链路加超时时间。
这套流程配合结构化的日志格式会非常高效。建议从一开始就给请求分配 request_id,让所有日志带上这个 ID,排查时可以像拉火车一样把整条链路的记录串起来。
5.3 部署与迭代中的几条守则
最后分享几条我个人在腾讯云上做 Agent 和 Skill 迭代时一直坚持的守则,都是用真金白银换来的教训。
第一,Skill 版本必须显式标记。每次修改 Skill 逻辑,都要改版本号。因为 Agent 的决策依赖 Skill 描述,如果描述变了但版本号没变,出了问题你根本不知道线上跑的是哪套逻辑。我目前的习惯是在 Skill 网关注册表里维护 version 字段,并在每次发布时强制比对。
第二,任何 Skill 都必须有独立的健康检查接口。这个接口不执行复杂逻辑,只返回存活状态。Agent 调度前可以先查 Skill 健康状态,虽然多一次请求,但能避免把用户请求转发给一个已经挂了半天的服务。用容器跑的时候,把健康检查配到 Docker 的 HEALTHCHECK 里,系统会自动重启异常容器。
第三,所有对外 Skill 接口都要做鉴权。Agent 调用 Skill 时,网关要校验调用方身份。最简单的方式是网关生成一个 token,Agent 请求时带上,网关校验通过才转发。如果不做这道校验,你的 Skill 一旦暴露在公网上,就可能被陌生人刷接口,不仅产生费用还有数据风险。
第四,每个 Skill 发布前必须准备测试用例。我的测试用例其实很简单,就是几条真实的输入输出对。每次改完代码,先把测试用例跑一遍,再上到线上环境。Agent 项目牵一发而动全身,一个 Skill 的小改动可能导致其他 Skill 被误调,测试用例至少能兜住你预期中的那部分行为。
我写这套东西的时候,手边正好有一个正在迭代的 Agent 项目。回看这一路的踩坑经历,最大的感受是:Agent 开发的复杂度不在于单个技术点多难,而在于它把模型、接口、网络、数据、编排这么多环节绑在了一起。任何一个环节不够工程化,最后都会以玄学报错的形式还给你的。把 Skills 当成工程来做,把边界和契约定清楚,Agent 的养成之路会顺畅很多。