
1. 动手前先把概念理顺Agent 和 AI Skills 到底分工干什么上个月我开始动手做一个跑在云端的小项目目标很简单把我平时那些重复性杂活——归档下载文件夹、查资料出摘要、按Git提交记录生成周报、临时跑点脚本——全部丢给一个 Agent 去做。项目前后折腾了三个星期最终在一台腾讯云服务器上稳定跑起来了。回头看最容易被卡住的点根本不是算力或模型而是最初那两天我不停在想Agent 到底是什么AI Skills 又是什么为什么网上都在说 Skills 是让 Agent 变强的关键。这篇我完整复盘一遍包括概念拆解、项目目录结构、AI Skills 编写套路以及腾讯云上的部署和守护方式。如果你也正准备搭一个能持续替自己干活的 Agent这篇文章基本能帮你绕开我踩过的那些坑。先说结论Agent 项目做得好不好模型只占一半剩下的一半全在“怎么给 Agent 装技能”和“怎么让技能稳定地跑在云上”。1.1 用包工头的思路拆解 Agent 的运行逻辑我习惯用一个包工头的例子来解释 Agent。你可以把大模型想象成一个刚从名校毕业、脑子转得很快的新人但他入职第一天什么工具都不会用。你让他“把文件夹里的合同按年份整理好”他能给你写出一份详细到步骤的操作说明书但他自己不会动一下鼠标因为他还没有接入真实世界的工具。Agent 就是给这个新人配上了手脚和大脑回路。它内部其实是一个循环接收任务拆解计划调用工具观察返回结果再决定下一步。这个循环跑起来以后它才真正从“会聊天”变成“能干活”。实操层面你不用重复造这个循环。Python 生态里有一堆现成的 Agent 框架从轻量的自写 while 循环到 LangGraph、CrewAI 这类完整框架都有。我自己项目里用的是很朴素的一版模型收到任务后我让它在上下文里维护一个待办列表每次只决定“下一步调用哪个技能”技能返回结果后再更新待办直到列表清空。如果你的任务不是特别复杂完全没有必要一上来就上重型框架先跑通一个最小闭环再逐步叠功能。1.2 为什么说 Skill 决定了 Agent 的天花板同一个大模型有人用它做出来的 Agent 只会说“你可以这么操作”有人做出来的 Agent 能自己把操作做完。差别在哪差在是不是给它准备了足够的 AI Skills。AI Skills 可以理解成一张张“技能卡”。每张卡告诉模型我叫什么名字、我适合处理哪类任务、我应该接收什么参数。模型在跑任务时会根据用户的需求去挑选技能卡选中之后执行对应的代码再把结果拿回来继续规划。也就是说Skills 负责的是 Agent 的“手和脚”模型则负责当“大脑”。所以“全能 Agent”这件事本质上不是你找到了一个全知全能的模型而是你亲手给 Agent 配齐了一套覆盖常见需求的技能库。模型负责判断什么时候用什么技能技能负责把事办成。有人问 skill 和 agent 到底有什么区别我一般这么答一个 Agent 可以有几十个技能它通过记忆和规划把这些技能编排起来而一个 Skill 只是单一能力的封装它不能自己决定何时出场。把这两层混在一起后面设计目录和写描述的时候一定会乱。2. 腾讯云上跑 Agent先做架构选型和目录规划2.1 我为什么把 Agent 放到云服务器而不是只留在本地项目最开始我是在本地电脑上开发调试的效果不错但很快发现两个问题。第一个问题是我不能关电脑Agent 晚上要定时跑整理任务电脑一睡眠整个流程就断掉。第二个问题是有些任务需要固定出口环境比如定时去访问某个接口拉数据放在家里的动态网络下维护起来很麻烦。所以我最后把 Agent 放到了一台腾讯云轻量应用服务器上。选它的原因很实际配置 2核4G 对这类任务来说足够跑价格也不算贵系统镜像直接装 Ubuntu 22.04不折腾安全组和域名解析都在同一个控制台里省得在多个平台之间来回跳。下面我列一下两种方式的取舍你可以根据自己的场景判断本地部署的优点数据不出自己电脑调试方便不需要额外花钱适合前期验证思路。本地部署的缺点断网断电就罢工不方便挂定时任务想通过手机随时查看状态很麻烦。云服务器部署的优点7x24小时常驻可以挂着自动任务和消息推送外网访问统一后续加域名、加监控都很顺。云服务器部署的缺点需要一定的 Linux 基础密钥管理要上心安全组规则要自己把关。如果你想长期跑一个真正能替你干活的 Agent我个人建议直接上云。本地部署适合作为“训练场”但“正式上岗”还是放到云上更省心。2.2 一个低耦合、好扩展的 Agent 项目骨架项目目录我迭代了好几版现在长这样agent-project/ ├── skills/ │ ├── file_organizer/ │ │ ├── skill.yaml │ │ └── run.py │ ├── web_info_collector/ │ │ ├── skill.yaml │ │ └── run.py │ ├── code_runner/ │ │ ├── skill.yaml │ │ └── run.py │ └── weekly_reporter/ │ ├── skill.yaml │ └── run.py ├── agent/ │ ├── core.py # agent 主循环 │ ├── tools.py # 技能加载器读取所有 skill.yaml │ └── memory.py # 长期记忆模块 ├── server/ │ ├── api.py # FastAPI 入口 │ └── config.py ├── gateway/ │ ├── litellm_proxy.py │ └── .env ├── scripts/ │ └── install.sh # 初始化脚本 └── .env这个结构的核心思路是“一个技能一个目录”。以后我想给 Agent 加新能力只需要在 skills 下新建一个子目录写好 skill.yaml 和 run.py启动时由 tools.py 自动扫描加载。不需要改 Agent 主循环也不需要动其他技能。我见过很多人做 Agent半年后代码变成一个几千行的单文件里面堆满了各种 if 分支。那不是 Agent那是一团乱麻。保持低耦合最关键的一点就是Agent 本身只负责编排具体的脏活累活全部丢给技能目录里的独立模块。2.3 模型网关与 Skill 路由用 LiteLLM Proxy 统一出口项目里还有一个容易被忽略但非常重要的组件模型网关。我用的方案是 LiteLLM Proxy它把不同模型的 API 统一成了 OpenAI 兼容格式。这么做的直接好处是代码里所有调用模型的地方只需面向一个 base_url换模型时改一行环境变量就够了。之前我在本地直接调各家模型 SDK后来发现 Agent 的每个子任务可能适合不同模型复杂推理用强模型简单分类用便宜的小模型。如果对接方式不统一Agent 框架里要写一堆分支维护成本很高。接上 LiteLLM Proxy 之后我只需要在配置里维护多个 model 条目然后通过 model 名区分# 在云服务器上后台启动一个 LitellM proxy 实例 litellm --model deepseek/deepseek-chat --port 4000然后在 Agent 代码里设置openai_base_url http://127.0.0.1:4000这样 Agent 的 core 层只认一个 OpenAI 兼容接口。后续如果某个模型效果更好、价格更合适我只在网关配置里换 model 名称业务代码完全不用动。如果你只做单模型 Agent这一段可以跳过但只要是打算长期演进、要接多个模型的项目LiteLLM 这套统一出口的思路值得提前布局。3. AI Skills 编写从 0 到 1 的最佳实践3.1 每个 Skill 必须同时包含描述文件和执行代码网上很多人问“ai skills 怎么写”我总结下来最稳妥的格式是一个 YAML 描述文件加上一个 Python 执行脚本。YAML 文件负责让模型“看懂”这个技能这是 AI Skills 和普通 Python 函数最大的区别。普通函数只需要人能看懂Skill 还需要模型能看懂。下面拿我最常用的文件整理类 Skill 来举例这是我目录里的skills/file_organizer/skill.yamlname: file_organizer description: 将指定目录中的文件按扩展名或日期分类移动到对应子文件夹。当用户提到整理、清理、归档、分类某个目录例如下载文件夹、桌面、文档目录时优先调用本技能。不要用本技能查看磁盘占用或删除文件。 input_schema: type: object properties: target_dir: type: string description: 要整理的目录绝对路径例如 /home/user/downloads mode: type: string enum: [extension, date] default: extension description: extension 表示按文件扩展名归档date 表示按最后修改日期归档 required: - target_dir对应的run.py只需要是一个可以被外部调用的函数返回值统一为 JSON 结构import json import shutil from pathlib import Path def run(target_dir: str, mode: str extension): try: base Path(target_dir).resolve() if not base.is_dir(): return {status: error, data: None, error: f{target_dir} is not a directory} moved 0 for f in base.iterdir(): if f.is_file(): if mode extension: folder_name f.suffix.lstrip(.) or no_ext else: folder_name f.stat().st_mtime_ns # date 模式这里可以继续细化 dest base / str(folder_name) dest.mkdir(exist_okTrue) shutil.move(str(f), str(dest / f.name)) moved 1 return {status: ok, data: {moved: moved, target_dir: str(base)}, error: None} except Exception as e: return {status: error, data: None, error: str(e)}3.2 Skill 描述写得好不好直接决定模型调得准不准我花了很多时间打磨 description 部分因为模型判断“什么时候调用哪个 Skill”靠的就是这段文字。如果描述写得太抽象模型完全可能在你需要整理文件的时候输出一段建议而不是真的去调用如果描述写得太宽泛模型又会在不该用的时候胡乱触发。我的经验是把 description 写成一个触发条件清单当用户提到某些关键词时调用当场景不匹配时不要调用。比如上面的 description 里我就明确写了“不要用本技能查看磁盘占用或删除文件”这句话可以挡住很多误调用。三个注意点动词开头名称像file_organizer这种“动词_对象”结构最容易被模型理解。覆盖同义说法用户可能说“清理下载”“整理桌面”“把文档分类”这些都应在描述里有所体现或能通过相似语义召回。别超过合理长度描述不是论文200 字以内把触发条件、功能边界说清楚就够了太长反而干扰模型判断。3.3 给“全能 Agent”配齐第一波 Skills我从最简单的“全能”开始理解。一个真正能干活的 Agent 通常需要几个基础能力组合能处理文件、能获取信息、能执行代码、能汇总结果。所以我首批只做了四个 Skills它们的定位各不相同。我用一张表来对比一下这四个技能的边界Skill 名称解决的问题典型触发场景file_organizer本地文件分类归档把下载目录里文件按类型整理好web_info_collector访问公开页面并提取关键信息帮我查一下某产品公开文档的最新更新说明code_runner在临时沙箱中执行 Python 代码算一下这批数据的平均值画一张图weekly_reporter根据 Git 日志和任务记录生成周报根据本周提交记录生成一份周报做完这批之后Agent 在我这边的可用性明显提升了一个台阶。它不是只会聊天了而是开始像一个“助理”你说“把那个目录收拾一下”它会真的去移动文件你说“生成周报”它会把提交记录取回来整理成结构化文本。这种能实际产生结果的感觉和单纯对话完全不一样。3.4 测试与迭代让 Agent 从“偶尔会”变成“稳定会”Skill 写完之后最大的坑是你以为写完了实际上模型根本不按预想的方式调用。我第一次写好 file_organizer 后给 Agent 下了个指令“把桌面上的文档按类型收拾一下”。结果它一个技能都没调用直接给我输出了一段操作步骤告诉我应该怎么打开终端、怎么建文件夹。后来通过打印 Agent 的中间决策日志我发现问题是 description 里只写了“下载文件夹”没有覆盖“桌面”这种常见说法。模型不是不知道有这个 Skill而是它判断当前场景和 Skill 描述里提到的“下载文件夹”不匹配所以干脆放弃调用。我把 description 改成“目录可以是任一路径包括下载文件夹、桌面、文档目录等”再测试就通了。这里的关键是每一轮测试都要让 Agent 输出它选择 Skill 的思考过程或者至少打印出模型最终选中的工具名这样才能定位是“没识别到技能”还是“识别错了技能”。我自己整理过一个简单的召回测试表每改一次 description 就重新跑一遍确保旧场景没被新改动破坏。4. 把 Agent 发布到腾讯云部署、配置与守护4.1 服务器初始化与代码上传在腾讯云控制台买好轻量服务器后我选了 Ubuntu 22.04 系统镜像。登录服务器后第一件事不是急着传代码而是先建好目录和虚拟环境sudo mkdir -p /opt/agent-project sudo chown ubuntu:ubuntu /opt/agent-project cd /opt/agent-project python3 -m venv venv source venv/bin/activate pip install fastapi uvicorn litellm openai pyyaml然后在本地机器上用 rsync 把项目同步上去。rsync 比 scp 好用在于可以断点续传第二次同步时只传变动文件调试时代码更新非常方便。我习惯把 .env 和缓存目录排掉避免环境变量和临时文件被打包上云rsync -av --exclude .env --exclude __pycache__ \ ./agent-project/ ubuntu你的公网IP:/opt/agent-project/这里要提醒一句.env 里是你的 API Key绝不能进 git也不能直接传到我这种公网可以访问的默认位置。第一版项目我图省事把密钥放在代码目录里后来赶紧换掉了这也是 Agent 安全里最基础的一条红线。4.2 域名解析、安全组与反向代理Agent 需要一个对外访问入口。虽然直接用 http://服务器IP:3000 也能调但后续要加 HTTPS、要挂机器人回调还是有个域名方便。我自己是在域名解析控制台添加了一条 A 记录主机记录填api记录类型选 A记录值填腾讯云服务器的公网 IP。这样api.example.com就指向了我的服务器。解析生效后服务不能直接裸奔在 3000 端口上最好再套一层反向代理。我用的 Caddy配置非常简单api.example.com { reverse_proxy 127.0.0.1:3000 }Caddy 会自动申请并续期 HTTPS 证书省掉自己折腾证书的时间。然后我需要去腾讯云控制台的安全组里放行端口。很多人一上来就把所有端口都开放了这是很危险的习惯。我的安全组规则只有一个原则只放行真正需要的端口通常就是 22SSH尽量限制来源 IP、80 和 443。业务端口 3000 完全不用暴露到公网因为外部请求会先到 Caddy再由 Caddy 转发到内网端口天然多了一层隔离。4.3 用 systemd 让 Agent 变成一个常驻服务我在本地调试时直接python agent/core.py就能跑但在服务器上不能这么干因为 SSH 断开后进程会收到挂断信号Agent 就停了。所以需要用 systemd 把 Agent 注册成常驻服务。我在/etc/systemd/system/agent.service里写了一个服务文件[Unit] DescriptionAll-in-one Agent Service Afternetwork.target [Service] Userubuntu WorkingDirectory/opt/agent-project EnvironmentFile/opt/agent-project/.env ExecStart/opt/agent-project/venv/bin/python -m agent.core Restartalways RestartSec10 [Install] WantedBymulti-user.target然后执行sudo systemctl daemon-reload sudo systemctl enable --now agent sudo systemctl status agent这里最有价值的配置是Restartalways。Agent 进程偶尔会因为模型接口超时或者磁盘满这种意外情况退出如果没有这个配置服务就静默挂了等到我发现时可能已经过去一整天。加上之后systemd 会在 10 秒后自动拉起来配合日志查看命令journalctl -u agent -f可以迅速定位崩溃原因。4.4 长期记忆把 Agent 变成有“工龄”的员工最后一块拼图是长期记忆。第一版 Agent 每次对话结束就失忆导致它记住的用户偏好和之前任务的结论全部丢失。我在项目里加了两个简单记忆层短期记忆存在内存字典里保存当前任务上下文的运行状态长期记忆落到本地 SQLite按日期、任务类型、用户偏好三个维度存。每次任务完成后Agent 会把重要的结论和用户偏好提取出来写入 SQLite下一次任务启动时先查一遍是否有可复用的历史信息。这一层看起来不起眼但实际体验差异非常大。有了记忆之后Agent 会记得我比较喜欢把报告按 Markdown 格式输出会记得我上次整理文件时跳过了某个目录不用每次重新交代。所谓“养成”很大程度就是靠这个记忆系统完成的。5. 实操中常见的坑与排查技巧5.1 从注册到上线的账号与环境问题坦白说最让人沮丧的不一定是代码 bug而是你刚想开始试试结果在账号环节就被卡住。我自己就遇到过新用户注册时提示风控的情况当时手机号验证码一直收不到换浏览器也一样。后来发现多数时候是平台的风控策略临时收紧并不是你的账号有问题。我的建议是稍微等一段时间再重试确认短信确实被手机正常接收检查一下有没有被当成垃圾短信自动拦截。如果反复不行再检查填写的手机号是否和其他账号有关联。千万别走一些非正规捷径去解决这种问题那只会给自己埋坑。5.2 二级域名解析不生效有朋友问过我在腾讯云上怎么申请二级域名这里要区分两件事域名本身需要在域名服务商处购买然后在解析控制台添加解析记录指向服务器不属于“申请”而是“解析”。添加 A 记录后通常几分钟内生效但如果你本机 DNS 缓存太旧可能一直解析不到新地址。最简单的排查命令dig api.example.com如果解析结果还是旧 IP可以等几分钟再dig一次。不要在生产服务器上反复重装 DNS 配置绝大多数情况只是 TTL 还没过。另外要注意解析记录指向的 IP 必须和当前服务器公网 IP 一致很多人改过服务器后忘了同步解析记录导致访问到旧地址。5.3 Agent 不按预想调用 Skill 时怎么办这类问题在调试中占了一半时间。我会启用 Agent 的 debug 日志把模型每一步的工具调用结果打印出来。观察之后通常能归成三类原因第一类描述里没覆盖用户说法。解决办法就是扩充同义表达。第二类多个 Skill 描述太相似。比如“文件整理”和“磁盘清理”在模型眼里边界模糊它就可能乱选。解决办法是在两个技能描述里都加一句“什么时候不要用我”。第三类参数必填项太多模型不会填。如果一个 Skill 需要五个必填参数模型一犹豫就可能放弃调用。我的原则是只保留必要参数其他全部给默认值。还有一类更隐蔽的问题输入 Schema 里参数类型写得太严格模型给的参数偶尔会不符合类型。所以执行函数内部一定要做容错处理比如字符串传成数字就强制转一次不要因为一个小参数类型导致整个技能崩溃。5.4 Agent 的安全与数据隔离小清单Agent 在云端跑起来之后安全问题是不能不提的。我给自己定了几条规矩不要用 root 账号跑 Agent单独建一个低权限用户。所有密钥都放 .env 文件并且执行chmod 600 .env只允许当前用户读取。code_runner 这类技能要在隔离的临时目录里运行禁止它写系统关键路径。如果 Agent 会抓取公开网页内容要注意内容里可能夹杂恶意指令。模型读了网页后网页里的文本有可能试图影响它执行别的操作。我采取的缓解方案是把网页内容只当成参考数据不让它进入“用户指令”所在的层级。最后一条现在已经成了我上线所有 Agent 前的默认检查项。很多人只关心功能跑不跑得通忽略了给 Agent 设置权限边界。既然是“全能 Agent”权限越大越要克制这一点上我交过学费不希望你再交一次。5.5 让 Agent 越用越“全能”的小技巧最后分享一个我很受用的习惯每完成一个需求我都问自己能不能把它沉淀成新的 AI Skills。一开始我整目录里只有四五个技能一个月后已经扩展到十几个包括按模板生成会议纪要、检测磁盘占用告警、定时抓取几个公开网页的关键指标等。每新增一个技能我会顺手记录一条调用日志存到 SQLite 里。周五下午我会花十分钟看这周的调用数据哪些技能被频繁触发哪些技能从来没人提过。高频技能说明需求真实值得继续打磨无人调用的技能要么描述不准确导致模型不会用要么这个需求本身就不成立可以直接下线。这就是我理解的“养成记”——不是让模型变聪明而是让围绕它的技能集和服务架构在一轮轮迭代中变得越来越贴合你自己的需求。