基于腾讯云与AI Skills的智能Agent实战:从架构到部署
1. 项目概述从零到一搭建一个真正能用的 Agent这几年 AI 圈子的关键词从大模型本身慢慢转移到了“怎么让模型真正干实事”上。大家手里都有 GPT-4、Claude 或者国产开源模型但问几个问题可以让它自动完成一整套业务流程就抓瞎了。于是 Agent 这个概念被炒得火热——但说实话市面上的 Agent 项目鱼龙混杂很多不过是套了个壳的聊天机器人根本不具备“自主规划 调用工具 完成任务”的能力。我这次做的项目核心就是围绕腾讯云这套基础设施把 Agent 从“能聊天”升级成“能干活”。整套方案基于一个核心思想Agent 本身不直接写死业务逻辑而是通过“AI Skills”这种可插拔的原子能力模块把大模型的推理能力和外部工具、API、数据源串起来。说白了Agent 是大脑Skills 是手脚大脑负责想怎么做手脚负责真的去做。这个项目适合谁参考我觉得有三类人最受益第一类是后端开发者想给自己的系统接入 AI 能力但不想从头折腾模型部署和 Prompt 工程第二类是独立开发者想做点 AI 工具赚钱但不想被云厂商的复杂概念绕晕第三类是技术决策者需要评估 Agent 落地时到底是自研框架、用现成编排工具还是基于云平台做二次开发。我大概用了半个月时间从账号开通、环境配置、Skill 开发、Agent 编排到压测和调优把整个链路完整跑了一遍。这篇文章会把核心设计和实操细节全部摊开讲包括我踩过的坑和几次返工的经历希望能帮你少走弯路。2. Agent 与 AI Skills 的前世今生为什么“会调用工具”才是 Agent2.1 先从概念说起Skill、Agent、Workflow 到底是啥关系很多新手上来就被一堆名词搞懵了。Skill 是什么Agent 是什么Workflow 又是什么它们之间什么关系我打个比方。Agent 就像一个新入职的实习生你说“帮我整理一份本周的销售数据周报”他不会直接动手写 Excel而是需要拆解任务先查数据库、再算指标、再生成图表、最后写报告。每个环节他都不知道怎么做但他知道要找谁帮忙。Skill 就是那个“谁”。它是一段封装好的能力比如“查询数据库的 Skill”、“生成图表的 Skill”、“调用钉钉接口发消息的 Skill”。每个 Skill 包含三样东西一是给大模型看的描述说明什么时候该用我二是入参出参的定义三是一段真正的执行代码。Workflow 则是预先定义好的流水线。如果某个任务流程非常固定比如每天凌晨跑一次数据同步那就没必要让 Agent 每次重新规划直接用 Workflow 把步骤串联起来定时触发执行就行。这三者的关系可以理解成Agent 负责“临场发挥”Workflow 负责“固定套路”Skill 是它们共同使用的基本零件。2.2 为什么 AI Skills 是 Agent 落地的关键我刚开始做 Agent 的时候走过一段弯路。当时我把所有工具调用逻辑全都堆在 Prompt 里比如在系统提示词里写“如果你需要查天气就调用 weather_api参数是 city返回 JSON”。结果就是 Prompt 巨长模型经常理解偏差参数传错或漏传而且每加一个工具就得改一次 Prompt维护成本高到离谱。后来我接触到 AI Skills 的模式算是彻底解开了这个死结。Skill 把“功能的描述”和“功能的实现”绑在了一起Agent 不需要靠 Prompt 里的文字描述去猜工具怎么用而是读取 Skill 的清单文件理解它的能力和调用方式再动态决定是否调用。这样做的好处非常明显可插拔新增能力就是增加一个 Skill 目录不动 Agent 主逻辑。可复用同一个 Skill 可以在不同 Agent、不同项目里反复使用。可测试每个 Skill 是独立的可以单独调试排错效率高。语义清晰大模型读的是结构化的 YAML 描述不是一团自然语言理解准确率显著提升。所以我在这个项目里把大量的精力花在了 Skill 的设计和编写上。Skill 写得好不好直接决定了 Agent 的上限。2.3 一个 Agent 项目的典型技术栈这次我选用的整体技术栈是这样的层次选型说明大模型底座腾讯云混元大模型 / 兼容 OpenAI 协议的模型接入通过统一网关切换方便对比Agent 编排框架自研轻量编排 任务规划模块控制 Agent 思考与行动循环Skill 注册与发现腾讯云对象存储托管 Skill 清单支持动态加载与版本更新工具执行层腾讯云云函数SCF承载 Skill 逻辑无服务器弹性伸缩免运维接口网关腾讯云 API 网关统一暴露服务鉴权、限流、监控一站式搞定配置管理环境变量 配置文件分离密钥与业务配置隔离这个组合不是我拍脑袋定的而是基于成本、稳定性和运维负担的综合考虑。后面我会详细讲每一步为什么这么选。3. 动手前必须想清楚的事整体设计与架构规划3.1 先定义清楚我的 Agent 要解决什么问题我在社群和评论区遇到过不少人一上来就问“怎么做 Agent”但问他“你想让 Agent 做什么”他答不上来。这是做 Agent 项目最大的坑——没有明确的目标场景技术选型无从谈起。我这边的场景很简单做一个“智能项目助手”它能够根据自然语言指令自动完成以下事情查询腾讯云账户下各产品的账单和资源用量拉取对象存储里的文件并做简单分析比如统计日志中的错误码分布调用企业微信 Webhook 推送汇总报告对以上操作生成可读的说明文档。这个场景麻雀虽小但五脏俱全——涉及云资源查询、数据处理、外部 API 调用、内容生成基本覆盖了 Agent 应用的核心能力模型。3.2 架构选型对比自研、开源框架还是云平台确定场景之后我面临一个经典的选择题Agent 架构怎么搭方案一完全自研编排逻辑。从零写 ReAct 循环、上下文管理、工具调用的解析与执行。优点是可控性最强想怎么做就怎么做缺点是开发量大而且很多坑别人已经踩过了没必要重复造轮子。方案二基于 LangChain、Dify 等开源/半开源框架。这类框架提供了 Agent 的完整基础设施接模型、接工具都很方便。但问题也有——抽象层次太高出了问题很难排查而且框架更新快接口变动频繁维护成本不低。方案三基于云平台 Serverless 能力自研轻编排。这是我在这个项目里采用的方案。核心思想是不引入重量级框架而是用云函数 API 网关 对象存储这三大件自己写一套极简的编排逻辑。为什么选方案三我的考量是Agent 的核心逻辑其实不复杂就是“观察-思考-行动-观察”的循环用 Python 写一个几百行的循环就够了重框架的抽象反而阻碍了我对细节的控制尤其是我要调用大量腾讯云 OpenAPI与其绕框架的抽象层不如直接写 SDK 来得痛快云函数天然支持弹性伸缩不需要关心 Agent 跑起来之后的并发和资源问题这套架构不存在厂商锁定核心代码放在云函数里哪天要迁走改个运行环境就行。3.3 模块划分与目录结构整个项目的代码组织结构如下agent-project/ ├── agent/ │ ├── core.py # Agent 主循环思考-行动-观察 │ ├── planner.py # 任务规划器把复杂任务拆成子步骤 │ ├── executor.py # 动作执行器调用 Skill 并处理结果 │ └── memory.py # 记忆模块短期上下文 长期持久化 ├── skills/ │ ├── billing_query/ # 账单查询 Skill │ │ ├── SKILL.yaml # Skill 的描述文件给大模型看 │ │ └── handler.py # Skill 的具体实现 │ ├── cos_analyze/ # 对象存储日志分析 Skill │ │ ├── SKILL.yaml │ │ └── handler.py │ └── webhook_push/ # 企业微信通知 Skill │ ├── SKILL.yaml │ └── handler.py ├── config/ │ └── settings.py # 全局配置读取环境变量 ├── requirements.txt └── server.py # API 入口对接 API 网关这个结构有几个设计要点Skill 和 Agent 完全解耦。Agent 每次运行时扫描 skills 目录读取所有 SKILL.yaml把能力描述注入到系统提示词中。新增一个 Skill 只需要添加一个目录不需要改 Agent 代码。每个 Skill 是完整的服务单元。handler.py 导出一个统一接口run(params) - dictAgent 不需要关心内部实现是调 API 还是读文件。配置集中管理。所有敏感信息走环境变量团队协作时不会把密钥泄露到代码库。4. 核心细节解析AI Skills 怎么写的才算好4.1 SKILL.yaml写给大模型的“使用说明书”SKILL.yaml 是整个 Skill 体系里最重要、也是大多数人写不好的文件。它是大模型判断“何时该调用这个 Skill、怎么调用”的唯一依据。写得不清楚模型就不会用写得太长模型又会信息过载。这是我的一个实际例子name: billing_query description: 查询腾讯云账户的账单费用信息。当用户询问花了多少钱、账单 费用明细、某个产品的消费情况时使用。可以按产品、地域、 时间范围筛选。如果不确定用户想查哪个产品的费用主动询问。 version: 1.0.0 author: your_name parameters: type: object properties: product: type: string enum: [cvm, cos, scf, cdn, all] description: 要查询的云产品默认 all 表示所有产品 default: all start_date: type: string format: date description: 开始日期格式 YYYY-MM-DD默认本月1号 end_date: type: string format: date description: 结束日期格式 YYYY-MM-DD默认今天 granularity: type: string enum: [day, month, total] description: 汇总粒度默认 total default: total required: [] returns: type: object properties: total_cost: type: number description: 总费用单位元 product_breakdown: type: array description: 按产品拆分的费用列表 currency: type: string description: 币种写这个 YAML 的时候我有几个心得description 要写触发条件不要只写功能。“查询账单”这种描述太泛了模型不知道什么时候用。要像我上面那样把用户可能问的话直接写进去“花了多少钱”、“账单”、“费用明细”。这相当于给模型一个关键词触发地图。parameters 要写清楚默认值和边界。模型填参数时经常漏填所以在设计上尽量给所有参数设置默认值让 Skill 在参数不全时也能兜底运行。required 字段尽量留空用默认值兜底比强制模型填参数更可靠。枚举值降低模型出错概率。比如 product 字段如果我写成自由字符串模型可能会传“云服务器”“CVM”“轻量应用服务器”等各种变体。用 enum 限定之后模型只能从给定选项里选出错率直线下降。这里我测试过一个很典型的案例同一个查询任务description 写得模糊的 Skill模型调用成功率只有六成左右重写描述并加上 enum 约束之后成功率提到了 95% 以上。所以说Skill 的描述文件不是给程序员看的是给大模型看的花时间打磨非常值得。4.2 handler.pySkill 执行逻辑的几个硬性要求Skill 的执行逻辑写在 handler.py 里我总结了几条必须遵守的规范import json import logging from typing import Dict, Any logger logging.getLogger(__name__) def run(params: Dict[str, Any], context: Dict[str, Any]) - Dict[str, Any]: 统一入口函数所有 Skill 必须实现。 params: 大模型根据 SKILL.yaml 填入的参数 context: 运行上下文包含认证信息、请求ID等 try: # 参数解析与校验不允许在这里抛未捕获异常 product params.get(product, all) start_date params.get(start_date) end_date params.get(end_date) granularity params.get(granularity, total) # 业务逻辑... result query_billing(product, start_date, end_date, granularity) return { success: True, data: result, message: f成功查询到 {product} 的账单信息 } except Exception as e: logger.exception(billing_query skill failed) return { success: False, error: str(e), message: 账单查询失败请检查参数或稍后重试 }第一条规范统一入口签名。不管什么 Skill入口函数都叫run接收params和context返回dict。这样 Agent 的调用方代码可以写得很简洁只用一套反射逻辑就能调度所有 Skill。第二条规范异常绝不外抛。Skill 执行过程中网络超时、API 返回异常、参数缺失都是家常便饭。如果异常直接抛出去Agent 主循环就崩了。正确做法是捕获所有异常包装成固定格式的错误信息返回给 Agent让模型根据错误信息决定下一步是重试还是换个方案。第三条规范返回结构要带“给模型看的解释”。这里我特意加了message字段它不是给用户看的而是给模型看的。模型决策下一轮行动时需要知道“刚才那次调用结果意味着什么”。有个简洁的中文说明模型的理解会准确很多。这里还有一个细节值得注意Skill 返回值里不要带无关紧要的调试信息。模型一次要处理的 token 是有限的垃圾信息越多模型越容易“分心”。我见过有人把整个 HTTP 响应头都塞进返回结果模型把状态码当业务数据解析闹出不少笑话。返回值只保留对下一步决策有意义的字段其他统统过滤掉。4.3 Skill 与 Agent 的信息交互机制Agent 调用 Skill 的过程我实现得比较轻量利用 Python 的 importlib 动态扫描 skills 目录下的所有子目录读取 SKILL.yaml然后根据模型选择的 Skill 名称加载对应的 handler.py。交互链路是这样的Agent 接收用户指令连同所有 Skill 的 YAML 描述一起发给大模型大模型判断需要调用哪个 Skill输出结构化 JSON包含 skill 名称和参数Agent 解析 JSON找到对应 handler执行run(params, context)将返回值拼装成一条工具消息连同对话历史再发给大模型大模型根据工具结果决定是继续调用其他 Skill 还是给出最终回答。关于第 2 步我强烈建议让模型直接输出 JSON不要整什么 Function Calling 的特殊协议。并不是所有模型都原生支持 Function Calling直接用文本生成 JSON 反而有最好的兼容性。我用的底层模型是混元但即使以后换 Llama、Qwen这套机制完全不用改。这里踩过的一个坑是模型生成的 JSON 偶尔不合法比如引号没闭合、多了一个逗号。我后来加了一个“JSON 修复层”用正则粗略提取大括号范围再用json.loads尝试解析失败的话就用ast.literal_eval做兼容再不行就返回错误让模型重新生成。这个兜底逻辑把 JSON 解析失败的概率降到了 1% 以下。5. 实操过程腾讯云环境配置与 Skill 部署5.1 云函数SCF创建与配置细节腾讯云的 Serverless 产品叫云函数 SCFSkill 的代码我就部署在上面。创建函数的过程不细讲了控制台操作很直观重点说几个容易配置错的地方。运行时选择我用的 Python 3.9。不要用 Python 2.7很多依赖库已经不支持了。也不建议用最新版本有些第三方库还没跟上兼容。内存配置很多人图便宜选 64MB但对于 Agent 场景来说太紧张了。Skill 执行时一般会引入 requests、tencentcloud-sdk-python 这些库本身就要占几十 MB 内存。我建议至少选 256MB预算充足选 512MB 更稳。超时时间Agent 的一个完整循环可能要经历多轮模型调用和工具调用但注意 SCF 单次超时是给单次 Skill 执行设置的不是给整个 Agent 会话设置的。查询类操作通常几秒内能完成我设置 30 秒够用且避免积压。环境变量配置在云函数控制台的“环境变量”里配置腾讯云 SecretId、SecretKey还有企业微信 Webhook 地址等。千万别把这些硬编码在代码里一方面是不安全另一方面是后续换密钥还要重新部署代码麻烦。创建函数的示例命令# 使用 SCF CLI 部署函数 scf deploy \ --name agent-skill-billing \ --runtime Python3.9 \ --handler index.main \ --memory-size 256 \ --timeout 30 \ --env SECRET_IDxxx,SECRET_KEYyyy5.2 API 网关给 Agent 一个对外访问的统一入口Agent 本身需要一个 API 入口来接收前端或客户端的请求。腾讯云 API 网关在这里的作用有两个一是把 HTTP 请求转发给云函数二是提供统一的鉴权和限流能力。在 API 网关配置时我主要做了这几件事路径规划我把整个 Agent 作为单一入口POST /agent/chat接收用户消息和会话 ID。为什么不按 Skill 拆多个路径因为 Agent 是一场多轮对话状态在 Server 端维护拆太细反而增加复杂度。鉴权方式我开启了 API 网关的“密钥对鉴权”在客户端调用时通过请求头传入X-API-Key。很多初学者直接开放公网访问没有鉴权结果被人刷接口账单炸了。限流配置我设置了 QPS 限流和并发限制。单用户每秒最多 2 次请求整体 API 每秒最多 20 次。这个配置在 Agent 场景下非常必要因为一次用户请求背后可能要触发多次上游模型调用如果流量不加控制云函数费用和模型调用费用很容易失控。5.3 域名与 HTTPS 配置要点控制台里可以给 API 网关绑定自定义域名需要备案然后在域名服务商处配置 CNAME 解析到腾讯云给的默认域名。这个过程本身不复杂但有几个细节值得注意HTTPS 证书腾讯云有免费的 SSL 证书可以申请每年续一次。配置时记得选择“强制 HTTPS”不然 HTTP 明文请求会把用户对话内容暴露在网络链路上。路径转发绑定自定义域名时可以配置路径映射。我的配置是https://api.yourdomain.com/agent/*转发到对应 API 网关服务。这里注意通配符要写对否则请求会 404。跨域配置如果前端是浏览器直接调用还需要在 API 网关配置 CORS。这个我调试的时候浪费了不少时间浏览器的同源策略在 POST JSON 场景下会发预检请求处理不好就会出现“接口明明通浏览器调不通”的怪现象。后来我在网关层加了允许的来源域名和允许的请求头问题就解决了。5.4 对象存储Skill 清单与配置的热更新方案一个容易忽略但实际很刚需的能力是Skill 清单需要支持热更新。Agent 运行时不直接从本地目录读 SKILL.yaml而是先从对象存储拉取一份最新的 manifest.json再按清单加载对应的 Skill 代码。这个设计的好处是我更新一个 Skill 的描述或者修复一个 bug只需要重新上传对应的压缩包到对象存储不用去云函数控制台做代码部署。对于频繁迭代的 Agent 项目来说这个能力非常提效。具体实现不复杂云函数每次启动时或者定时 5 分钟从对象存储拉取skills/manifest.json解析里面的 Skill 名称、版本号和下载地址再决定是否需要热更新。manifest 文件的格式类似{ version: 2.3.0, skills: [ { name: billing_query, version: 1.2.0, download_url: https://xxx.cos.ap-guangzhou.myqcloud.com/skills/billing_query_1.2.0.zip, checksum: a1b2c3... } ] }为什么用对象存储而不用配置中心因为我除了清单文件还需要托管 Skill 的代码压缩包对象存储天然适合这种场景而且有 CDN 加速拉取很快。这个方案在发布新 Skill 的时候特别方便不用碰任何线上服务改一个 manifest.json 就能让所有 Agent 实例感知到新能力。我有一次加了新 Skill 后十分钟内所有线上 Agent 都能用了整个过程没有重启任何服务。6. Agent 核心逻辑的实现思考、行动与记忆6.1 Agent 主循环的实现思路接下来是重头戏Agent 主循环。这是整个项目的灵魂我直接给出核心代码的简化版本然后逐段解释。import json import time from typing import List, Dict, Any class Agent: def __init__(self, model_api, skill_registry, memory_store): self.model_api model_api self.skill_registry skill_registry self.memory_store memory_store self.max_iterations 10 def run(self, user_message: str, session_id: str) - Dict[str, Any]: messages self.memory_store.load(session_id) messages.append({role: user, content: user_message}) system_prompt self._build_system_prompt() for i in range(self.max_iterations): # 1. 思考调用大模型决定下一步动作 response self.model_api.chat( messages[{role: system, content: system_prompt}] messages ) action self._parse_action(response) # 2. 如果是最终回答循环结束 if action[type] final: messages.append({role: assistant, content: action[content]}) self.memory_store.save(session_id, messages) return {reply: action[content], iterations: i 1} # 3. 行动执行 Skill 调用 if action[type] skill_call: skill_name action[skill_name] skill_params action[params] result self.skill_registry.execute(skill_name, skill_params, {}) messages.append({ role: assistant, content: f[调用Skill {skill_name}] 参数: {json.dumps(skill_params, ensure_asciiFalse)} }) messages.append({ role: tool, content: json.dumps(result, ensure_asciiFalse) }) # 4. 记忆持久化每轮都保存防止运行中断丢失上下文 self.memory_store.save(session_id, messages) # 达到最大轮次仍未结束强制返回错误 return {reply: 抱歉我没有在规定的步骤内完成这个任务请尝试拆分成更小的请求。} def _build_system_prompt(self) - str: skills_desc self.skill_registry.get_descriptions() return f你是一个智能助手可以调用以下工具帮助用户完成任务 {skills_desc} 请根据用户需求选择调用合适的工具。如果工具不满足需求请直接作答。 你的回复必须是一个 JSON 对象格式为 - 最终回答: {{type: final, content: 你的回答}} - 调用工具: {{type: skill_call, skill_name: 工具名, params: {{...}}}} 这段代码虽然简洁但包含了 Agent 机制的几个关键设计max_iterations 限制。这是必须的不然 Agent 会陷入死循环。我设定的是 10 轮覆盖绝大多数任务。如果 10 轮还没完成说明任务本身有问题或者模型理解有偏差直接返回提示让用户换个方式表达更合理。每轮都做持久化。如果 Agent 跑了 5 轮后网络断了没有持久化的话整个会话状态就丢了。我在_parse_action之后立刻保存 messages就是为了避免中间环节崩溃导致前面的推理白费。模型的输出必须是结构化的。整个主循环的驱动力就是把模型输出解析成action。这是一个非常关键的抽象——不管是 OpenAI 风格的消息格式还是混元的返回格式最终统一转换成内部 action 结构后续扩展别的模型就很方便。6.2 任务规划把复杂问题拆成可执行的子步骤单轮“思考-行动”循环解决不了复杂任务比如“帮我分析最近一周的 CDN 流量异常并生成一份排查报告”。Agent 需要先规划再执行最后汇总。我实现了一个简单的规划器它的思路是先让模型把这个任务拆成若干子任务然后逐个执行。这里用到了一个很关键的技巧——不要让模型一次性规划完再执行而是采用“边规划边调整”的策略。class Planner: def __init__(self, model_api): self.model_api model_api def plan(self, user_message: str) - List[Dict[str, Any]]: 把用户任务拆解为子步骤 prompt f请把以下任务拆解为最多5个可执行的子步骤。 每个子步骤应包含步骤描述、是否需要调用工具、预计使用的工具。 任务{user_message} 请直接输出 JSON 数组格式如下 [{{step: 1, desc: 查询CDN流量数据, skill: cdn_query, depends_on: []}}] response self.model_api.chat([{role: user, content: prompt}]) try: steps json.loads(self._extract_json(response)) return steps except Exception: # 解析失败就退回单步执行 return [{step: 1, desc: user_message, skill: None}]这里的关键取舍是规划器只是一个“建议器”不是“强制执行器”。Agent 在实际执行每个子步骤时仍然会根据当时的中间结果动态调整。比如规划时以为要查 CDN 的 Skill执行到一半发现需要先查账号权限Agent 会停下来先解决权限校验。最初版本我把规划器设计成强约束的即 Agent 必须严格按步骤执行结果经常卡死。后来改成建议制效果反而好很多因为大模型的中间推理往往能发现纯静态规划看不到的问题。6.3 短期记忆与长期记忆别让 Agent 忘了自己说过的话Agent 的记忆机制直接决定了对话体验。我这里做了两层短期记忆 对话上下文保存在内存或 Redis。每一轮的 messages 都会完整保存直到会话结束或多轮对话超过模型上下文窗口。这块用 Redis 实现很方便天然支持过期时间设置比如 30 分钟无活跃就清理。长期记忆 用户偏好和关键事实存数据库。比如用户之前提到“我常用的实例在广州地域”这个信息值得长期记住。我在 Agent 每次回答结束后会额外让模型判断“当前对话中是否有值得长期记忆的事实”有则提取出来写入数据库。长期记忆的粒度要控制好不能什么都记。我只提取三类信息用户的固定偏好、项目中需要反复使用的上下文、业务流程中约定的规则。事实证明克制的记忆比贪婪的记忆更有效因为记忆太多会干扰模型对当前任务的注意力。6.4 并发与配额控制别让 Agent 把你账户刷爆Serverless 架构的最大优势是弹性但弹性也有副作用——如果 Agent 逻辑有 bug或者用户恶意刷接口云函数会被瞬间拉起成百上千个实例账单直接起飞。我做了三层防护第一层API 网关限流。前文提到过单用户 QPS 限制超了直接返回 429。这是最外层的防线。第二层云函数并发配额。在 SCF 控制台可以设置函数级别的并发上限。我设置的是 50也就是说整个函数同时最多 50 个实例在跑超过的请求排队或丢弃。第三层Agent 内部预算控制。我给每个会话设置了一个“步数预算”和“费用预算”。每调用一次模型或执行一个 Skill都会记账。超过预算就直接终止会话让用户重新发起请求。这里有一个很多人忽略的细节模型调用的费用往往比云函数计算费用高一个数量级。云函数跑一天可能就几块钱但模型 API 在 Agent 循环里被频繁调用一天几十上百块很正常。所以控制 Agent 的迭代次数本质上是控制模型调用次数这是成本控制的核心杠杆。7. 问题排查实录那些让我头秃的典型 Bug7.1 模型老是不按 JSON 格式输出怎么办这是 Agent 开发中最常见的头疼问题。明明系统提示词里说了必须输出 JSON模型偏要自作主张输出一段自然语言。我的排查过程分三步第一步检查提示词。把 JSON 格式要求放在提示词的最末尾离模型的输出位置更近模型遵循的概率会显著提高。第二步增加“小样本示例”。在系统提示词里给一个完整的输入输出示例模型模仿能力很强效果比单纯说“请输出 JSON”好得多。第三步实现兜底解析。前两步还失败的时候就用我前面提到的“JSON 修复层”。用正则提取最外层的花括号然后用容错解析器解读。如果还是不行就告诉模型“你上一轮输出格式不对请重新输出 JSON”通常第二次就能成功。我做了个统计加了小样本示例后格式错误率从 12% 降到了 3% 左右。所以遇到格式问题优先优化提示词而不是堆代码。7.2 Skill 调用时参数总是传错有时候模型明明调对了 Skill但参数传得离谱比如把“2024-01-01”传给了 limit 字段原本应该是整数。这种问题定位后发现根源基本都在 SKILL.yaml 的参数描述上。解决方式是提供更明确的类型和格式约束。YAML 里的type、format、enum字段要写清楚。我自己测试下来最有效的是在描述里加一个示例值start_date: type: string format: date description: 开始日期格式 YYYY-MM-DD例如 2024-01-01加上“例如 xxx”这五个字模型传参准确率提升非常明显。7.3 云函数冷启动导致首请求超时Serverless 的冷启动是个绕不开的话题。Agent 场景下每次用户发消息云函数可能刚被拉起初始化依赖库就要花 2-3 秒再加上模型调用的时间用户感知明显卡顿。我做了两个优化一是预留并发实例。在 SCF 控制台可以设置“预置并发”让 2-3 个实例常驻。这样请求进来直接复用已初始化的实例首请求延迟从 3 秒降到 200 毫秒左右。二是把重依赖放到函数初始化阶段。在main.py顶层导入所有 SDK 并做初始化不要在每次 Skill 调用时重新 import。Python 的 import 机制虽然会缓存但处理大型 SDK 时开销依然不小。7.4 排错技巧日志与追踪体系怎么搭Agent 排错比普通后端难因为链路长用户输入 → 模型推理 → 工具执行 → 再次推理 → 最终输出。任何一环出错都可能导致整体行为异常。我的做法是每个会话生成一个 trace_id从入口开始贯穿所有环节。API 网关接收请求时生成 trace_id传入云函数云函数里每次模型调用和 Skill 执行都打印带 trace_id 的结构化日志。日志格式我用的是 JSON Lines{trace_id: abc123, event: model_request, input_tokens: 1200, output_tokens: 300, latency_ms: 1500} {trace_id: abc123, event: skill_call, skill_name: billing_query, params: {product: all}, latency_ms: 800} {trace_id: abc123, event: skill_result, success: true, data_summary: total_cost12.34}排查问题时直接按 trace_id 过滤日志就能还原整个 Agent 的思考与行动过程。说实话这个日志体系是我认为整个项目里性价比最高的投入——没有它面对一个表现异常的 Agent真的是盲人摸象。7.5 常见问题速查表现象可能原因解决方案模型不调用 Skill 直接回答SKILL.yaml 描述不清晰模型不知道何时该用优化 description增加触发词和示例Skill 执行成功但 Agent 回复“无法获取信息”返回值格式不符合模型预期或字段名模糊在 message 字段中补充自然语言说明对话超过几轮后响应变慢历史消息太长超出模型上下文限制实现摘要压缩把早期消息浓缩成要点多用户并发时互相串号Session 管理不当上下文被覆盖使用 Rediskey 按 session_id 隔离查询类 Skill 偶尔报权限错误云函数角色权限配置不全在 SCF 角色中授予对应的云产品只读权限模型把日期格式传成时间戳参数描述不够明确YAML 中增加 format 和示例API 网关返回 504云函数执行超时模型调用或 Skill 执行过长检查超时设置规划 Agent 轮次上限新部署的 Skill 不生效对象存储 manifest 缓存未刷新更新 manifest.json 版本号并配置 CDN 刷新8. 性能调优与成本控制Agent 项目能不能省钱跑8.1 模型调用的降本妙招Agent 项目最大成本在模型 API 调用一个完整任务可能要调 3-8 次模型。降本的核心思路是减少不必要的模型调用。第一个技巧普通问题走小模型复杂问题走大模型。我在 Agent 主循环前加了一个“意图识别”步骤用一个便宜的小模型比如混元 Turbo判断问题的复杂度。简单问题直接让小模型回答只有需要多轮推理或工具调用时才切换到强模型。测试下来约 40% 的问题可以在小模型层结束整体模型成本下降约 30%。第二个技巧缓存重复的自然语言处理结果。用户经常会问类似的问题比如“昨天账单多少”和“查询昨天的账单费用”。我对用户输入做了归一化处理去除停用词、统一时间表达然后用语义向量提取特征在 Redis 里查询是否命中缓存。命中就直接返回上次结果不再触发模型调用。但要注意带时效性的数据查询不适合缓存需要配合时效判断。第三个技巧减少历史消息的冗余。把早期轮次的长文本消息做摘要只保留提炼后的要点发给模型。比如用户上传了一份很长的日志文件Agent 在第一轮读取后做了分析后续轮次就没有必要把完整日志继续放在上下文里把它压缩成“日志包含 XX 条错误主要分布……”即可。这些优化手段叠在一起模型 token 消耗能减少一半左右。别小看这个比例Agent 跑到生产环境一个月省下来的就是真金白银。8.2 云函数成本控制云函数的计费跟“调用次数 × 配置内存 × 运行时长”相关Agent 场景的特点是调用频繁但单次运行时间短。成本控制的核心是降低不必要的实例启动减少运行时长。我做了三个优化一是把 Skill 执行和模型调用并行化。Agent 如果需要同时查询账单和资源用量两个 Skill 之间没有依赖关系用 asyncio 并发执行总耗时从两个 Skill 的串行时间变成最长的那个。时间短了费用自然就下来了。二是合理设置内存。内存大小直接影响计费单价但也不是越小越好。内存太小会导致容器 OOM函数不停重启费用反而更高。我实测过256MB 对查询类 Skill 足够512MB 对处理大数据量的分析类 Skill 更稳。按不同类型 Skill 分配不同内存的函数比一刀切用 512MB 更省钱。三是打开函数的“请求多实例复用”。云函数在同一实例上处理完一个请求后会保留一段时间等待下一个请求。复用已初始化的实例能省掉重复的冷启动开销。8.3 监控与告警配置Agent 类应用和普通后端应用的监控侧重点不同。除了基础的 CPU、内存、错误率还要重点盯这几个指标单次会话平均迭代轮数如果超过 8 轮说明 Agent 在绕圈子大概率有逻辑问题。Skill 调用成功率低于 90% 就说明 Skill 的描述或实现有问题需要优化。平均响应延迟Agent 首字返回时间一般要控制在 3 秒内超过就需要检查是哪一环节拖慢了。模型 API 费用日趋势这个必须每天看一旦发现异常飙升立刻检查是否有死循环或恶意刷接口。腾讯云本身就提供监控告警能力在 SCF 控制台可以直接配置基于日志关键词的告警。比如设置“skill_call failed”这个关键词出现 5 次就触发告警发短信和邮件通知。9. 从开发到上线一个完整项目的落地复盘9.1 开发时间线参考阶段耗时核心内容需求确认与场景定义1 天确定 Agent 要解决的业务问题定义验收标准环境搭建与账号开通0.5 天开通云函数、API 网关、对象存储配置密钥Skill 开发3 个3 天编写 SKILL.yaml 和 handler.py本地调试通过Agent 主循环开发2 天实现思考-行动-观察循环接入模型 API集成联调2 天API 网关、云函数、对象存储全链路打通性能调优与安全加固1 天并发控制、限流配置、日志体系搭建测试与验收1 天覆盖正常流程、异常流程、并发场景总计约 10 个工作日。说实话比我想象中要快关键是没有被重框架束缚很多逻辑直接用云函数实现省去了搭建和维护基础设施的时间。9.2 几个“早知道就好了”的经验第一Skill 数量不是越多越好。我开始做了 8 个 Skill后来发现很多功能的概念边界重叠模型经常选错。砍到 3 个核心 Skill 之后准确率反而上去了。Skill 的边界要清晰每个 Skill 负责一个领域交集越小模型越容易分辨。第二Prompt 工程依然是核心竞争力。就算有再好的 Agent 框架和 Skill 体系最终驱动这一切的还是模型对自然语言的理解。我在 Agent 主循环的提示词上迭代了很多版本每次都记录下模型的表现变化最后沉淀出一套稳定的模板这个投入比写业务代码还要值得。第三提前规划好测试集。上线之前我整理了一份约 50 条常见用户问题覆盖各种边界情况比如“帮我查一下所有实例的费用”需要遍历产品枚举、“昨天和今天的差别是什么”需要连续两次调用后比较。每轮迭代都用这份测试集做回归避免改了一个功能又弄坏了另一个功能。第四安全的坑趁早填。我早期开放了 API 网关但没做鉴权被一个扫描器发现后疯狂刷接口一晚上产生了几百元费用。教训很深刻。如果你也准备上线 Agent 服务先把鉴权和限流做好再谈功能这不是可选优化而是必选项。9.3 后续可以扩展的方向这个项目做完了但 Agent 和 AI Skills 的想象空间远不止于此。我自己已经在规划几个扩展方向一是把 Skill 生态开放出来。目前的技能都是我自己写的下一步可以做成一个类似插件市场的机制让其他人贡献技能Agent 根据任务动态搜索并拉取合适的 Skill。二是加入多模态能力。比如支持用户上传图片Agent 调用图像分析的 Skill 理解图片内容再结合业务数据做综合判断。三是接入更多数据源。目前主要是腾讯云自身的资源和账单数据后续可以接入 MySQL、对象存储、企业内部的 API让 Agent 成为真正意义上的“业务大脑”。四是做多 Agent 协作。单个 Agent 负责一个领域多个 Agent 之间通过消息队列互相协作比如“数据分析 Agent”把结果交给“报告生成 Agent”再由“推送 Agent”发给相关人员。这些方向在不断试错中会逐步成型但核心的架构理念——Agent 负责认知决策、Skill 负责具体执行、云平台负责弹性底座——已经验证是稳定可靠的。最后说点真实的感受。做这个项目之前我也被各种 Agent 概念绕得云里雾里什么 Autonomous Agent、Multi-Agent、ReAct、Plan-and-Execute名词一大堆。真正自己动手实现一遍从零到一写完一个能跑通全流程的 Agent 之后才发现这些概念背后的本质其实非常朴素把大模型的推理能力和外部世界的执行能力连接起来让 AI 不仅能想还能做。腾讯云这套基础设施扮演的就是一个稳定、弹性的执行底座。云函数提供计算能力API 网关提供接入能力对象存储提供动态更新能力而我自己要做的就是把这个底座和大模型之间那层胶水写好。这个胶水层说难也难说简单也简单——难的是一开始没想清楚架构简单的是想清楚之后整个实现过程非常顺畅。希望这篇文章能帮你少走一些弯路。如果你也在折腾 Agent 和 AI Skills欢迎在评论区交流你的踩坑经历或者分享你总结出来的最佳实践。毕竟这个领域变化太快每个人踩过的坑对后来者都是宝贵的经验。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →