
最近大半年我一直在折腾Agent开发从最开始拿Python脚本拼大模型的function calling到后来在云上搭一套相对完整的Agent服务最深的感受是真正让Agent好用的从来不只是模型本身而是你给它配的那一身“技能”。没有技能模块的Agent模型再聪明也只是个嘴强王者聊两句可以一让它干活就抓瞎。这也是我为什么特别关注腾讯云AI Skills这个方向——它把Agent“会干什么”和“怎么干”这件事拆开了让能力可以注册、复用、编排还能统一做权限和观测。这篇博文记录的是我实际在腾讯云上从一个裸机开始到部署出一个带多个可复用技能的Agent的完整过程。内容包括整体架构怎么设计、技能模块怎么定义和编排、模型怎么接入、容器镜像怎么推送到云上以及我在这个过程中踩过的坑和总结出的排查思路。不管你是刚入门Agent开发的新手还是已经被工具链折磨过一段时间的开发者这篇文章应该都能帮你少走几趟弯路。1. Agent为什么需要“技能”——先想清楚再动手1.1 单体Agent的瓶颈到底出在哪很多人的第一个Agent都是单体结构一段长长的system prompt里面写满“你可以调用这些工具”然后代码里塞了一大堆if-else把每个工具的逻辑都写在主进程里。这种写法在最开始demo的时候很爽因为逻辑直白、调起来也快。但一旦工具多起来问题就全冒出来了。举几个我自己遇到过的现象第一个是Prompt膨胀。工具描述越写越多系统提示词越来越长模型每次请求都要把这些内容重新过一遍响应延迟上去了Token成本也上去了。第二个是工具逻辑和主流程纠缠不清。比如我想给“查天气”的工具增加一个城市补全功能本来只是想改一个小模块结果要动主进程的调度逻辑一不小心把别的流程也带崩了。第三个是权限没法细化。单体结构下所有工具在模型眼里都是同一次对话里可以随便调用的函数客户数据接口和垃圾话过滤接口混在一起风险很大。后来我意识到Agent不应该是“一个大模型加一堆函数”而应该是“一个大脑配一套可插拔的工具带”。技能就是这套工具带上的一个个模块每个模块具备独立的定义、独立的执行逻辑、独立的生命周期——这样单个技能出了问题不会拽着整个Agent一起崩。1.2 AI Skills不是新概念但之前缺一套落地的规范“技能”(Skill)这个词其实不是什么新概念。最早在Semantic Kernel里有Skill在LangChain里有Tool在OpenAI的function calling里叫Function换个皮而已。但过去的问题是每个框架都有自己的定义方式迁移成本高而且大部分实现都停留在“代码层面的函数包装”没人去管技能的注册、发现、组合和权限控制。腾讯云AI Skills给我的感觉是把这件事往工程化方向推了一步。它强调的并不是“多了一样新东西”而是给Agent的能力模块提供了一套可以遵循的规范技能有统一的元信息定义有可选的权限约束能被Agent运行时动态发现和加载也支持组合编排。这种做法的价值在于当你的Agent从一个玩具变成生产系统时技能不再是散落的函数而是可以被管理、被审计、被独立升级的资产。打个比方单体Agent像是把一个工具箱里的所有工具焊死在了一台机器上每次想换扳手得把整台机器拆开而技能化的Agent像是给每个工具做了标准化接口和卡槽想换就换想装新的就装新的机器本身不用动。1.3 技能拆分到什么粒度才合理关于技能拆分我踩过的一个典型坑是拆得太碎。刚开始我把“发送HTTP请求”都做成一个技能结果模型动不动就调用这个技能去访问奇怪的地址既危险又没意义。后来我总结出一个相对合理的划分原则技能应该按照“用户可感知的能力边界”来划分而不是按底层函数来划分。我通常把技能分成三类技能类型典型例子特点工具型技能查天气、发邮件、查数据库、调API功能单一、输入输出明确、容易测试知识型技能查文档、查产品手册、检索知识库本质是检索关键是召回质量流程型技能工单流转、订单处理、审批流程多个步骤的组合需要状态管理在腾讯云上实践时我倾向于把“工具型技能”做细一点“流程型技能”单独抽出来做编排。比如“查订单状态”是一个工具型技能但“处理用户退货申请”就是一个流程型技能它内部需要调用查询接口、判断条件、生成回执等多个步骤。如果这两种混在一个技能里后面维护会很痛苦。2. 环境准备与选型云资源、模型接入与服务编排2.1 云服务器配置怎么选才不浪费钱Agent跑在云上第一件事就是选机器。我最开始用的是轻量应用服务器2C4G图的是便宜、上手快。但实际跑起来发现如果模型走云端API、Agent只做调度2C4G完全够用可一旦你动了“在本地部署模型”的念头哪怕是部署一个7B的量化模型2C4G也会立刻变得捉襟见肘。我的建议是分场景选配置。纯做Agent编排、调用云端模型API用2C4G或者4C8G的轻量服务器就够了带宽选5M以上因为请求响应是实时的带宽太低容易出现卡顿。如果想在本地跑模型建议直接上GPU机型至少16G显存起步否则光是加载模型权重就能把内存耗尽。这里要注意腾讯云服务器的安全组默认只会放行少数端口如果你在服务器上起了Agent服务记得在控制台的安全组规则里放行对应端口否则外部调用会被一直卡在连接超时。另外关于“腾讯云如何申请二级域名”这个问题实际操作中很简单在云解析控制台给已备案的域名添加一条A记录主机记录填比如“agent”记录值填服务器的公网IP解析生效后就能用agent.yourdomain.com访问服务了。如果没有域名直接用IP也行但做回调接口、或者要用HTTPS证书的时候还是有域名方便很多。2.2 模型接入云端API和本地部署怎么权衡模型接入是Agent最关键的一环。我在腾讯云上试过两种方式客观分享一下优劣。接入方式优势劣势适合场景云端API响应快、效果稳定、无需GPU、按量付费数据出网、有单次Token限制大部分生产场景本地部署数据不出内网、可私有化微调、无Token费用需要GPU资源、运维成本高数据敏感、离线环境个人经验是生产环境的Agent优先走云端API省心又稳定。本地部署模型听起来很酷但一次推理要占住显存并发一上来就很容易OOM而且模型更新迭代你也得自己跟着升级运维工作量相当可观。当然如果你的业务对数据隐私有硬性要求那本地部署就是唯一选择这个没有银弹。在接云端API时我还踩过一个坑多个服务共享一套密钥结果某个服务超量调用账单直接爆了。后来我加了一层代理网关所有模型请求都走统一入口在网关做密钥管理、配额控制和日志记录。这里我想提一下LiteLLM Proxy这类工具它就是专门做模型统一接入的把OpenAI、Anthropic、腾讯混元等各种上游模型API统一封装成一个标准接口下游Agent只需要对接这一个接口就行。配上它之后换模型、加模型都变成配置文件里加几行的事不需要动Agent代码。2.3 服务编排与Agent框架的取舍框架选型这个问题我纠结了很久。市面上的Agent框架很多各有各的抽象方式。我的观点是如果你的Agent只是简单调用两三个工具直接手写一个事件循环加function calling就够了不要为了“架构”而架构如果你的Agent技能很多、需要多轮工具调用、还需要编排和记忆管理那用框架能省下不少事。在腾讯云上实操时我倾向于自己维护一个轻量的Agent Runtime核心就三件事把用户请求发给模型、解析模型返回的工具调用请求、根据请求调度对应的技能执行。这个“模型-调度-技能”的三层结构看起来简单但特别稳。市面上那些框架为什么有时候让人觉得“玄学”就是因为它们在模型和技能之间插了太多魔法出了问题不好排查。自己维护Runtime每一个环节都看得见摸得着出问题可以顺着日志一行一行查。但这并不意味着不用框架。我在技能调度这块参考了Microsoft Agent Framework里的一些思路尤其是它的技能注册和依赖注入方式设计得很像Spring的Bean管理——定义、注册、按需取用。借鉴过来之后我的技能管理就从“一堆散落的函数”变成了“有注册表、有生命周期、有依赖关系”的模块系统。3. 核心实现从技能定义到容器化部署的完整链路3.1 技能描述与函数原型的写法技能定义是整套体系的地基。如果技能描述写得含糊模型就不知道该在什么时候调用它。我在实践中总结出的经验是技能描述要写清楚三件事——这个技能负责什么、什么情况下该调用、有什么注意限制。技能定义我推荐直接用JSON Schema格式兼容性最好。下面是一个“查询订单状态”技能的定义示例{ name: query_order_status, description: 根据订单ID查询订单的当前状态适用于用户询问订单进度、发货情况、物流信息时调用。, parameters: { type: object, properties: { order_id: { type: string, description: 用户提供的订单编号格式为10位数字加字母 } }, required: [order_id] } }这段定义会被直接传给模型作为function calling的候选函数。模型根据用户的话术把意图和这个技能描述的匹配度做判断然后决定是否调用。所以描述里的“适用于...时调用”这句话非常关键它相当于在教模型做意图路由。需要注意的是技能描述不要写得太长也不要把实现细节写进去。模型不需要知道你是用MySQL还是PostgreSQL查的数据它只关心“这个技能是干什么的、参数是什么”。描述太长会稀释模型的注意力反而影响调用准确率。3.2 技能注册、加载与权限控制定义好技能之后核心流程就是注册。我设计了一个技能注册表本质上就是一个字典加一个校验器。系统启动时扫描技能目录加载每个技能的定义和实现然后做格式校验校验通过才注册到运行时。技能注册表的核心逻辑大概是这样的class SkillRegistry: def __init__(self): self._skills {} def register(self, skill: BaseSkill): # 校验技能定义必须包含name和execute方法 if not skill.name: raise ValueError(Skill name is required) if not hasattr(skill, execute): raise TypeError(fSkill {skill.name} missing execute method) self._skills[skill.name] skill logging.info(fSkill registered: {skill.name}) def get(self, name: str): return self._skills.get(name) def list_skills(self): # 输出所有技能的元信息用于生成API给模型做候选 return {name: skill.get_schema() for name, skill in self._skills.items()}权限控制这块我的做法是给技能定义加一个required_permission字段。比如“发送邮件”技能要求调用者具备email:write权限“查询订单”技能要求order:read权限。Agent在真正执行技能前做一次权限校验不通过就往回传一个“权限不足请告知用户无法执行”的错误信息给模型让模型生成友好的答复而不是直接把堆栈抛给用户。这个设计在单体Agent里是几乎不可能优雅实现的因为所有工具都共享同一个执行上下文根本没有“这个技能该不该被调用”的判断层。技能化之后权限天然成了一等公民。3.3 记忆与上下文的工程处理Agent要“全能”记忆这一块绕不开。我在实践里把记忆分成两层短期记忆和长期记忆。短期记忆用Redis做。每次会话的上下文消息列表存在Redis里key设计成session:{session_id}设置过期时间比如2小时。这样用户关掉页面再回来历史消息还在Agent能记住之前聊了什么不会每次都是“失忆式”对话。长期记忆则用来存用户的偏好、历史结论等结构化信息。我用的是向量数据库把用户的历史问题、Agent的最终结论、甚至用户当前的偏好描述都向量化存起来。下次对话时先做相似度检索把可能相关的“记忆片段”放进上下文里让模型有据可依。这里有一个经验给记忆打标签比纯向量检索可靠很多。我在存记忆时会附带一些元信息比如时间、业务线、记忆类型偏好/事实/事件检索时先按元信息过滤再做向量召回准确率提升非常明显。纯靠向量相似度经常会把无关的记忆捞进来白白消耗Token不说还容易误导模型。3.4 容器化部署构建镜像并推送到腾讯云容器镜像服务运维部署这块我强烈建议把Agent直接容器化。用Docker打包成镜像推到腾讯云容器镜像服务(Tencent Cloud Container Registry简称TCR)再从镜像仓库拉取到云服务器运行。这种方式的好处是环境一致性本地能跑的镜像云端就一定能跑不会再出现“本地好好的上服务器就报错”这种玄学。Dockerfile我写得比较简单基于Python 3.10的slim镜像FROM python:3.10-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8080 CMD [uvicorn, main:app, --host, 0.0.0.0, --port, 8080]构建完镜像之后打标签、推送、拉取一套流程下来很顺畅。推送前先登录镜像仓库然后在服务器上拉镜像运行# 本地构建镜像 docker build -t agent-service:v1.0 . # 打上腾讯云镜像仓库的标签 docker tag agent-service:v1.0 ccr.ccs.tencentyun.com/my-project/agent-service:v1.0 # 推送镜像到容器镜像服务 docker push ccr.ccs.tencentyun.com/my-project/agent-service:v1.0 # 在服务器上拉取并运行 docker pull ccr.ccs.tencentyun.com/my-project/agent-service:v1.0 docker run -d --name agent-service -p 8080:8080 \ -e LLM_API_KEYyour_api_key \ -e REDIS_URLredis://your-redis-host:6379/0 \ ccr.ccs.tencentyun.com/my-project/agent-service:v1.0这里有个非常重要的点密钥和配置不要写死在镜像里而是通过环境变量传进去。我的习惯是把敏感信息放到环境变量或者挂载一个配置文件但配文件也要做权限收紧。之前见过有人把API密钥直接写到代码里镜像推到仓库后密钥等于裸奔后面改密钥改到怀疑人生。4. 常见问题与排查技巧实录4.1 技能调用链路的错误定位三板斧Agent开发中最让人头疼的问题就是“模型为什么不调用工具”或者“工具调用了但结果不对”。这类问题我一律建议按链路一层一层查首先是模型是否生成了tool_call其次是技能是否被正确注册最后是技能执行结果是否被正确回传给模型。检查模型是否生成tool_call最直接的办法是开启日志把每次向模型请求的完整消息和模型返回的原始响应都记录下来。我见过大量“Agent不干活”的问题最后定位下来其实是技能定义里参数格式和代码实现不一致比如模型传了date: 2025-01-15代码却按datetime对象处理一执行就类型报错。技能执行结果回传这块也要注意格式。模型看到工具调用结果后会基于它生成下一轮回复所以结果应该尽量结构化比如返回JSON而不是纯文本。如果你返回的是大段无格式文本模型容易理解偏差。我的做法是统一让技能返回一个结构体包含status、data、message三个字段这样模型很容易从data里提取信息生成回复。4.2 进程报错与被中断的Agent执行热搜词里有一条特别典型agent execution terminated due to error。这个错误我在早期开发时几乎天天见。原因几乎都是技能执行过程中抛了未捕获异常Agent运行时没有做兜底处理直接把错误往上抛导致整个执行链中止。解决思路是给技能执行包一层异常捕获任何技能崩溃都不能让Agent整体死掉。我在调度器里是这样处理的try: result skill.execute(**arguments) except Exception as e: result { status: error, message: f技能执行失败: {str(e)}, data: None } logging.exception(fSkill {skill_name} execute failed)这个兜底看起来简单价值特别大。就算某个技能挂了Agent也能用自然语言向用户解释“这个功能暂时不可用”而不是直接给用户甩一段Python堆栈。另外还有一个容易忽略的点超时控制。有些第三方接口响应很慢如果技能执行不设置超时一个卡死的接口会占用整个Agent的调度线程用户等半天没反应。我给每个技能设置了独立的超时时间默认15秒超过就终止并返回超时错误。这样单个接口再慢也不至于拖垮整个服务。4.3 安全组、端口与Redis的经典坑位服务器部署之后最容易被卡住的一环就是网络策略。我在腾讯云上第一次部署Agent服务时起了个8080端口自测本地访问一切正常换到服务器外网IP就超时。排查了半天最后发现是安全组没放行8080端口。腾讯云的安全组规则设置路径很简单控制台找到对应云服务器实例进入安全组配置添加入站规则放行TCP端口8080来源可以按需限制比如只允许自己的IP访问。如果图省事全部放行会有安全风险所以我一般只放开必要的端口并且把来源IP限制到最小范围。另外热搜里还有一条提到“修改Redis密码之后再重启就连接不上”。这个问题十有八九是配置文件里的密码没改全或者Redis的配置项写错了。注意Redis的密码配置在requirepass字段而且如果开启了一个叫masterauth的选项主从同步场景下也要同步修改。只改requirepass从节点同步时会验证失败然后整个服务看起来就像“没起来”。4.4 镜像推送、域名解析与网络环境的废话提醒镜像推送失败也是云端部署的经典问题。常见报错是unauthorized: authentication required原因基本就是没登录。执行docker login ccr.ccs.tencentyun.com --username你的腾讯云账号然后按提示输入密码或访问凭证。腾讯云容器镜像服务的凭证可以单独申请用专门的访问凭证比直接填账号密码更安全。还有一个问题有些人会遇到“腾讯云注册提示网络环境异常无法进行注册”。这种情况通常是所处网络出口IP被风控系统标记了换一个网络环境或者稍后再试一般就能解决。涉及到账号注册这类事情我建议按平台规则来不要尝试任何非常规手段去绕过风控。域名解析这块前面提到的二级域名A记录配置需要注意解析生效有TTL时间差一般几分钟到十几分钟。配完域名之后建议用dig或者nslookup命令确认一下解析是否已经生效不要配完就急着访问容易误判成服务器问题。4.5 常见问题速查表现象最可能的原因排查/解决方法模型从不调用技能技能描述不清晰或参数定义错误检查技能description是否说明“什么时候调用”检查参数schema是否与实现一致技能调用了但结果不对技能执行逻辑有bug直接调用技能接口做单元测试排除模型干扰Agent执行中途报错终止技能未捕获异常给技能执行加try/except兜底返回统一错误结构外部无法访问服务安全组未放行端口到腾讯云控制台检查安全组入站规则修改Redis密码后连不上配置没同步或主从配置不一致检查requirepass和masterauth重启RedisDocker推送镜像认证失败未登录或凭证过期重新执行docker login使用专用访问凭证上下文越来越长、费用暴涨记忆未做裁剪对历史消息做滑动窗口裁剪超长内容转存长期记忆5. 这篇实践下来我最大的几个感触整个项目做完之后最大的感触有两点。第一点Agent的能力上限不取决于模型而取决于技能的丰富度和质量。模型本身可以理解为所有人的“公共大脑”大家都在用你很难靠模型本身建立壁垒。但技能是你的私有资产你对业务的理解、你对数据接口的封装、你对用户场景的拆解最后都沉淀在技能里。你给Agent装上一百个高质量的技能它就能帮你处理一百种真实的杂事这种价值是无法被“换一个更强的模型”替代的。第二点技能化的工程思维不只是技术选型更是一种团队协作方式的转变。以前一个Agent项目大家挤在同一个代码仓库里改主逻辑现在把技能拆开每个人负责自己的技能模块接口定义好之后并行开发代码冲突少了测试也更容易写了。我在腾讯云上这套实现本质上是把“Agent开发”变成了“技能组装”开发和维护的体验都有明显改善。最后再分享一个我在生产环境下的小技巧给每个技能的执行加上耗时统计和成功率监控用日志或云监控都行。Agent上线之后你会发现有些技能调用频率高但失败率也高这些点往往是优化系统最值得投入的地方。很多时候Agent觉得“不好用”的体感背后可能是某一个工具的稳定性太差找出它、修复它整个Agent的体验会瞬间提升一大截。