ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

从零搭建全能Agent:AI Skills实战指南

从零搭建全能Agent:AI Skills实战指南 1. 项目概述从零搭建一个真正能跑的智能体先说清楚这篇文章要聊什么。标题里的“全能 Agent 养成记”说的是我在腾讯云上一路折腾把一个只会回答“你好”的聊天机器人逐步培养成能查数据、能调接口、能按流程干活的智能体Agent。而“AI Skills”是这套体系里最核心的抽象——把大模型的能力装进一个个可复用、可组合的“技能包”里让 Agent 不再是一堆 prompt 的堆砌而是一个有工具箱、有操作手册、能拆解任务的执行者。我写这篇东西的初衷是因为我翻了大量国产和国外的 Agent 框架文档发现一个普遍问题教程大多停在“跑通 demo”这一步真正到了“我要把它部署到云服务器上接上真实业务让它天天稳定干活”这个阶段各种坑就冒出来了。比如 Skills 文件到底怎么写才能被框架正确加载工具调用的参数怎么定义才不会被大模型误解腾讯云上的服务怎么配 HTTPS、怎么开端口才安全这些问题在官方文档里往往一句话带过但真正动手时每个都能卡你好几天。这篇文章适合三类人一是想从零上手 Agent 开发、但对“怎么设计 Skills”没什么概念的新手二是已经跑通过本地 demo、想迁到腾讯云上长期运行的个人开发者三是团队里负责 Agent 基础设施、需要把多个模型和多个技能统一管理起来的工程师。我会把我实际踩过的坑、验证过的配置、以及每一步为什么这么做的逻辑都讲清楚。注意这篇文章里所有示例都遵循一个原则——先讲“为什么”再讲“怎么做”。因为 Skills 这套东西网上抄一份模板很容易但真要按自己的业务改起来不理解其内部加载机制和参数约定改一行配置可能就崩一个功能。2. 整体设计思路拆解为什么是“Agent AI Skills”这套组合2.1 Agent 不等于聊天机器人差异在于“使用工具的能力”很多人第一次接触 Agent会把“能对话”当成“是 Agent”这是一个挺大的误解。聊天机器人Chatbot的核心是“生成”——你问一句它根据训练数据和上下文生成一段回答。而 Agent 的核心是“执行”——它能理解你的目标把一个复杂任务拆成多步每一步调用合适的工具拿到结果后再继续下一步直到任务完成。举一个生活化的例子。你让普通聊天机器人“帮我把最近一周的服务器日志里所有 ERROR 级别的报错整理成报告”它大概率会给你一段文字告诉你“你应该怎么看日志”甚至直接编造几条不存在的报错。而一个合格的 Agent 会这样做先调用日志查询工具拉取数据再调用过滤工具筛选 ERROR再调用统计工具聚合频率最后调用报告生成工具输出结构化文档。每一步都有真实的数据支撑不是凭空生成。这种差异的根源就在于 Agent 是否具备“工具调用”Function Calling / Tool Use的能力以及是否有一套机制让大模型知道“什么时候该用什么工具”。AI Skills 正是为了解决后者——它把工具的使用说明、参数定义、适用场景、调用示例打包成一个标准化的文件让 Agent 在运行时会话中动态加载这些“技能说明书”从而知道在什么情境下调用什么工具。2.2 我在方案选型时的三个考量因素动手之前我其实对比过好几条技术路线用 LangChain 这类重量级编排框架、用开源的 Agent 运行时、还是基于模型厂商的 Function Calling 能力裸写。最终我选择了“轻量 Skill 体系 腾讯云基础设施”的组合核心原因有三个。第一个考量是可控性。LangChain 这类框架抽象层级太厚出了问题排查链路很长。很多时候一个 prompt 的小改动会因为你没注意到某个内部 Chain 的默认行为而产生意想不到的结果。而自己维护一套轻量的 Skill 加载和调用机制虽然初期代码量多一些但每一层逻辑都在自己掌控里线上出了问题能几分钟定位到根因。第二个考量是模型无关性。这一点很关键。AI Skills 的最佳实践应该做到“一处编写多处运行”——同样的一个技能包接通义千问能用接 DeepSeek 能用接 Claude 也能用。这就要求 Skills 的定义不能绑定任何特定厂商的私有格式而是用一套通用的、基于自然语言加结构化参数的描述方式。第三个考量是部署环境的稳定性。腾讯云在国内的访问速度和备案流程相对友好而且它的云服务器、对象存储 COS、API 网关这些组件天然搭配。你不需要自己去折腾“怎么让国外 VPS 在国内访问稳定”这类问题云厂商已经把这些基础设施问题解决好了。对于一个小团队或个人开发者来说把精力省下来专注在 Agent 逻辑本身性价比最高。2.3 Skills 与 Agent 的分工逻辑技能库与调度器的关系我在实际设计中把整个系统明确分成三层模型层、调度层、技能层。模型层负责自然语言理解和生成调度层负责解析用户意图、规划任务步骤技能层则以 Skills 为单位提供可执行的原子能力。这三层的关系和“一个刚入职的新员工 他的岗位说明书 公司的工具库”很像。调度层是那个新员工它理解你的指令技能层是公司的各种工具和操作手册每个手册Skill告诉你一个工具怎么用模型层则是新员工的大脑负责综合判断。Skills 和 Agent 最核心的分工原则是Agent 管脑子Skills 管手脚。任何一个需要真实世界交互的动作——查数据库、发 HTTP 请求、读写文件、调第三方 API——都应该封装成一个 Skill而不是直接写死在 Agent 的 prompt 里。这样做的好处非常明显Skills 可以独立测试、独立复用、独立更新。你改了一个 Skill 的内部实现只要保持接口不变Agent 的其它部分完全不需要动。对比维度Chatbot聊天机器人Agent智能体Skills技能包核心能力生成文本拆解任务、调用工具、执行流程封装原子操作能力输入输出文本进文本出目标进结果出参数进结构化结果出可变性改 prompt 即可需要改流程逻辑改内部实现保持接口不变可测试性弱中强3. 环境准备与基础搭配腾讯云上的 Agent 运行底座3.1 服务器选型与初始化配置要把 Agent 长期稳定地跑在云端第一步就是选一台合适的云服务器。我实际使用的是腾讯云的轻量应用服务器2核4G 的配置对于跑一个中小规模的 Agent 服务来说绰绰有余。选 2核4G 而不是更小的 1核2G是因为 Agent 运行时的内存占用比想象中要高——加载模型推理客户端、维护会话上下文、跑 Skills 的进程调度加起来轻松吃掉 1.5G 内存。如果选 1核2G系统本身占掉一半剩下那点内存稍微遇到并发请求就容易 OOM。初始化配置里最重要的一件事是修改 SSH 默认端口并配置密钥登录。云服务器默认暴露 22 端口每天会被各种扫描脚本探测攻击无数次。我的做法是先在控制台安全组里把 22 端口的来源 IP 限制成只有我自己的固定 IP 能访问然后把 SSH 服务迁移到 22022 这类高位端口最后关闭密码登录、只保留密钥认证。这三步做完服务器的安全等级会提高一个数量级。注意腾讯云轻量服务器的“防火墙”和“安全组”是两个不同的概念两者都要配置。轻量应用服务器用的是防火墙规则而云服务器 CVM 用的是安全组。我最初只配了安全组发现端口还是不通查了半天才发现轻量服务器的防火墙也要放行。3.2 域名与 HTTPS为什么 Agent 必须走加密通道Agent 服务如果只做本地调试HTTP 就够了。但只要你想通过 API 网关对外提供服务或者想在微信小程序、网页前端里调用这个 Agent那 HTTPS 就是硬性要求。原因很简单Agent 在运行过程中会传输大量敏感数据——用户对话内容、业务查询参数、可能还有内部系统返回的数据。这些信息如果通过明文 HTTP 传输在公网上等于裸奔任何一个中间节点都能截获。我在腾讯云上的做法是申请一个域名在 DNSPod 里做好解析然后用腾讯云的 SSL 证书服务申请免费证书单域名证书够用最后在 Nginx 里配置证书和反向代理。代理的核心作用是不让用户的请求直接打到 Agent 服务端口而是先经过 Nginx 这一层。这样我可以统一做 TLS 终止、请求大小限制、访问频率控制而底层 Agent 服务只需要监听内网地址即可。如果你和我一样用的是轻量应用服务器这里有一个小经验直接在服务器上装一个宝塔面板或类似的面板工具来管理 Nginx 和证书续期比自己手写配置省心很多。但注意面板本身会暴露一些管理端口用完后一定要在防火墙里限制来源 IP。3.3 代码结构设计把 Agent 的骨架搭得足够清晰在写任何业务逻辑之前我先把整个项目的目录结构定下来了。这一步看似简单但直接决定了后面维护的幸福感。我的目录结构是分层的agent-project/ ├── agent/ # Agent 调度核心 │ ├── core.py # 会话管理、任务规划循环 │ ├── llm_client.py # 模型调用封装 │ └── memory.py # 记忆管理 ├── skills/ # 技能包目录 │ ├── web_search/ # 网页搜索技能 │ │ ├── SKILL.md # 技能说明书 │ │ └── tool.py # 技能实现 │ ├── db_query/ # 数据库查询技能 │ │ ├── SKILL.md │ │ └── tool.py │ └── report_gen/ # 报告生成技能 │ ├── SKILL.md │ └── tool.py ├── config/ # 配置文件 │ ├── models.yaml # 模型配置 │ └── skills.yaml # 技能注册表 ├── server/ # 对外服务层 │ ├── api.py # FastAPI 接口 │ └── middleware.py # 鉴权、限流中间件 └── tests/ # 单元测试这个结构的核心思路是“一个技能一个文件夹”每个技能文件夹里至少包含一个SKILL.md给大模型看的说明书和一个tool.py实际执行的代码。SKILL.md是写给调度模型看的tool.py是真实干活的。这样的拆分保证了即使调度模型被替换成另一个厂商的模型只要SKILL.md写得足够清晰新模型也能正确理解并调用这些技能。4. AI Skills 编写实战从 SKILL.md 到可调用的工具函数4.1 拆解 SKILL.md 的标准格式与写作手法在 AI Skills 的最佳实践里SKILL.md是整个体系的重中之重。这个文件不是给人看的注释而是给大模型“读”的操作手册。它的质量直接决定了 Agent 能不能在正确的时候调用正确的工具、并填对参数。我在反复试错之后总结出一份比较稳妥的模板化写法。一个合格的SKILL.md至少包含七个部分技能名称、一句话描述、适用场景、参数定义、调用示例、输出说明、注意事项。其中“适用场景”和“参数定义”最容易写砸。很多新手写“适用场景”只会说“这个技能用于查询数据库”这种描述对大模型帮助为零。正确的写法是给出“正面触发词 反面触发词”比如# 技能数据库查询 ## 描述 查询业务数据库并返回结构化结果。 ## 适用场景 - 当用户询问订单数量、用户增长、收入汇总等数据指标时必须调用本技能。 - 当用户提供 SQL 语句并要求执行时必须调用本技能。 - 当用户只是闲聊、不涉及具体数据请求时不要调用本技能。为什么要写得这么细因为大模型在决定是否调用一个工具时本质是在做“语义匹配”。你描述得越具体匹配的准确率越高。我实测过描述模糊的技能误调用率能到 20% 以上把触发条件写清楚后这个比例能降到 2% 以内。参数定义部分我强烈建议使用 JSON Schema 格式而不是简单的自然语言描述。原因是大模型对结构化参数的解析准确率远高于对自然语言的解析。举个对比// 推荐的写法结构化参数定义 { name: query_database, description: 执行 SQL 查询并返回结果。仅用于 SELECT 查询禁止执行 INSERT/UPDATE/DELETE 语句。, parameters: { type: object, properties: { sql: { type: string, description: 完整的 SQL 查询语句 }, limit: { type: integer, description: 返回结果的最大行数默认 100, default: 100 } }, required: [sql] } }这里有一个设计上的关键点我在参数描述里明确写了“禁止执行 INSERT/UPDATE/DELETE 语句”这不是给开发者的提示而是给大模型的提示。因为大模型在自由发挥时可能生成任何 SQL如果不提前约束它可能在用户说“帮我把那个订单删掉”时真的生成 DELETE 语句。通过参数级别的约束相当于给模型划定了安全边界。4.2 工具函数的实现要点入参校验与错误处理SKILL.md是大模型的“说明书”但真正干活的是背后的工具函数。工具函数的实现有几个容易忽略但极其重要的点。第一是入参校验不能只靠大模型。大模型生成的参数值偶尔会有格式问题比如把数字写成字符串、把数组写成逗号分隔的文本。工具函数内部必须做一次严格的类型检查和范围校验不合法就直接返回错误信息而不是带着错误参数继续执行导致系统崩溃。我的习惯是每个工具函数开头先用 Pydantic 或类似的库做入参模型校验校验通过才进入业务逻辑。第二是错误信息要能“喂回”给大模型。当工具执行失败时返回的错误信息不是一个{error: 500}这样的状态码就完事了而应该是一段大模型“看得懂”的自然语言描述。比如def query_database(sql: str, limit: int 100): try: result db_session.execute(sql, limitlimit) return {success: True, data: result} except SQLSyntaxError as e: # 返回给大模型的错误信息要包含可修复的线索 return { success: False, error: SQL 语法错误请检查 SQL 语句中的关键字和表名, detail: str(e) }为什么错误信息要这样设计因为 Agent 的调度模型具备自我纠错能力——它看到“SQL 语法错误”且知道“需要检查关键字和表名”就可能自动改写 SQL 后重新调用一次工具。这种机制叫“自愈循环”是 Agent 比传统脚本聪明的地方。如果你只返回一个干巴巴的false大模型根本不知道哪里错了更不知道该怎么改这个工具调用链就断了。第三是超时控制必须加。云服务器上跑的 Agent所有工具调用都要设置合理的超时时间默认建议 10 到 30 秒。我用的是concurrent.futures加asyncio.wait_for双重保险确保任何一个工具卡死了都不会拖垮整个 Agent 进程。4.3 从 Skills 到技能注册表让 Agent 知道你有哪些工具有了几个写好的 Skill下一步就是让 Agent 的调度核心知道“我现在有哪些技能可用”。这就需要一个技能注册表。我的设计是在config/skills.yaml里声明所有技能的名称、路径、启用状态skills: - name: web_search path: skills/web_search enabled: true - name: db_query path: skills/db_query enabled: true - name: report_gen path: skills/report_gen enabled: false - name: time_util path: skills/time_util enabled: trueAgent 启动时会扫描并加载所有enabled: true的技能把它们的SKILL.md内容拼接到系统提示词System Prompt中同时把工具函数注册到模型调用接口的工具列表里。这里有一个性能优化的小技巧不要一次性把所有技能都注册给模型而是做一层“意图预过滤”。比如用户问“现在几点”你不需要把数据库查询技能的定义也塞给模型——那会白白消耗 token还可能干扰模型判断。我用了一个非常轻量的方案为每个技能维护几个“关键词标签”先用关键词快速匹配缩小候选技能范围再让大模型在候选集里做最终决策。这一步把 token 消耗降低了约 40%而且因为判定范围缩小模型选错工具的概率也明显下降。5. Agent 核心链路实现会话管理、记忆与工具调用循环5.1 用 LiteLLM Proxy 统一接入多模型Agent 的开发过程中有一个痛点不同厂商的模型 API 格式不统一调用方式也不一样。如果代码里直接写死某个厂商的 SDK后面换模型成本极高。我最终的方案是用 LiteLLM Proxy 做统一网关——它会暴露一个兼容 OpenAI 格式的接口背后可以代理到 DeepSeek、通义千问、智谱、Claude 等多个模型源。这个方案的好处是Agent 核心代码只需要认识一种 API 格式OpenAI 格式模型切换全在 LiteLLM Proxy 的配置文件里完成。我在config/models.yaml里维护了主模型和备用模型的列表model_providers: main_model: provider: litellm_proxy model: deepseek-chat temperature: 0.2 fallback_model: provider: litellm_proxy model: qwen-plus temperature: 0.2这里有一个小经验分享Agent 的调度模型和生成模型可以分开。调度模型负责判断“下一步调用什么工具”它的 temperature 建议调低0.1 到 0.2保证决策稳定而生成模型负责回答用户问题temperature 可以稍高一些0.7 左右让回答更自然。LiteLLM Proxy 支持在同一个接口请求里按需指定不同的模型名实现这个策略非常方便。5.2 记忆管理短期会话与长期记忆的取舍一个可用的 Agent 不能是“金鱼记忆”每次对话都从零开始。我实现的记忆系统分两层短期会话记忆和长期事实记忆。短期记忆就是当前会话的上下文窗口我直接把它存成消息列表每次调用模型时按 token 上限裁剪保证最旧的消息被优先丢弃。长期记忆则用 SQLite 数据库存储用户的关键偏好和事实信息比如“用户所在部门是技术部”“用户偏好简明扼要的回答风格”。这里有个取舍问题值得展开说。长期记忆不是越多越好——每次把大量历史事实塞进提示词会挤占有限的上下文窗口而且可能让模型混淆“长期事实”和“当前任务”的权重。我的经验是默认只注入当前会话相关的记忆只有在用户明确触发记忆查询时才加载全部长期记忆。比如用户说“我之前让你帮我查过的那份报告的数据你还有吗”Agent 检测到“之前”这个时间线索才去检索长期记忆。注意记忆功能的隐私边界一定要想清楚。你在自己项目里存哪些数据无所谓但如果你未来要把 Agent 做成 SaaS 服务涉及用户数据留存就必须做脱敏和自主可控的删除机制。现在很多大模型厂商对工具调用链路里的敏感信息也加了审计这个趋势只会越来越严。5.3 工具调用循环让 Agent 能“动手”而不是只能“动嘴”Agent 调度核心的核心是一个“规划-执行-观察”的循环。我用伪代码来解释这个循环的骨架async def run_agent(user_message: str): messages [{role: system, content: build_system_prompt()}] messages.append({role: user, content: user_message}) for step in range(MAX_STEPS): # 设置最大步数防止死循环 response await llm_client.chat(messages) if response.tool_calls: # 模型要求调用工具 for tool_call in response.tool_calls: tool_result await execute_tool(tool_call) messages.append(tool_result) # 把工具执行结果返回给模型让模型继续判断 continue else: # 模型认为任务已完成返回最终答案 return response.content return 任务步骤过多已自动终止。这个循环里最关键的是MAX_STEPS参数。它决定了 Agent 最多能连续调用多少次工具。我一开始没设这个上限结果有一次测试中模型陷入了死循环——它反复调用同一个工具每次都拿到相同的结果就是不结束。设了MAX_STEPS 10之后这类问题被直接兜住。另一个实操心得是工具执行结果返回给模型时一定要带上截断和摘要。数据库查询可能返回上千行数据如果全部塞进上下文直接就把 token 窗口撑爆了。我的做法是查询结果超过 50 行时先做聚合统计再返回给模型同时把完整结果写到本地临时文件并附上文件路径让模型知道“详细数据在哪个文件里可以进一步处理”。5.4 API 服务层与鉴权设计Agent 运行核心做好之后对外需要暴露一个 API 服务。我用 FastAPI 搭建了这一层暴露了三个关键端点/chat普通对话、/agent/run结构化任务执行、/skills/list查看当前已加载的技能。API 层的鉴权我推荐最简单的方案静态 API Token 加来源 IP 白名单。Token 放在请求头Authorization: Bearer token里服务端用环境变量存储和校验。如果你需要给多个用户提供不同的访问权限可以升级成 JWT 方案但对一个个人项目或小团队内部工具来说静态 Token 加 IP 白名单已经足够安全也更易维护。还有一点API 网关前面一定要加限流。公网接口暴露后被脚本刷是必然的。我用 FastAPI 的中间件实现了一个简单的滑动窗口限流器同一 IP 每分钟最多 30 个请求超出直接返回 429。这个数字是我压测过的合理阈值——正常用户很难超过这个频率而刷接口的脚本会被拦住。6. 腾讯云部署与对外服务把 Agent 从本机搬到云端6.1 代码部署与进程守护确保服务挂了能自动拉起本地开发跑通之后真正的考验是把 Agent 部署到腾讯云服务器上并保持长期在线。我这里的做法是用 Git 做代码管理服务器上用git pull拉取最新代码然后用systemd来做进程守护。为什么不用 Docker不是 Docker 不好而是对于单机部署的轻量 Agent直接 systemd 管理 Python 进程更直观、排查问题更直接。如果你后续要横向扩展多个 Agent 实例再上 Docker Compose 或 K8s 也不迟。systemd配置的核心是写一个 service 文件我贴一下关键配置[Unit] DescriptionAgent Service Afternetwork.target [Service] Userubuntu WorkingDirectory/home/ubuntu/agent-project EnvironmentFile/home/ubuntu/agent-project/.env ExecStart/home/ubuntu/agent-project/venv/bin/python -m uvicorn server.api:app --host 127.0.0.1 --port 8000 Restartalways RestartSec5 [Install] WantedBymulti-user.target这里的Restartalways是重点它保证进程无论因为什么原因退出systemd 都会在 5 秒后自动拉起。我遇到过一次内存泄漏导致的服务崩溃如果没有这个配置Agent 就会在半夜静默挂掉直到第二天用户反馈才被发现。有了自动拉起即使偶发故障服务中断时间也控制在几秒内。环境变量单独放在.env文件里不上传到 Git 仓库。这样做的目的是把敏感信息API Key、数据库密码、Token 密钥和代码逻辑分离就算代码仓库不小心公开了核心密钥也不会泄露。6.2 Nginx 反向代理与端口开放的正确姿势服务监听127.0.0.1:8000这意味着外部网络无法直接访问这个端口。接下来用 Nginx 做反向代理把公网请求转发到这个内网端口。这样配置有几个好处一是 HTTPS 证书统一在 Nginx 层处理Python 应用完全不需要操心证书逻辑二是 Nginx 可以统一设置 HTTP 头、请求大小限制和限流规则三是底层服务不直接暴露公网端口减少了攻击面。Nginx 配置的关键片段server { listen 443 ssl; server_name api.yourdomain.com; ssl_certificate /etc/ssl/certs/yourdomain.pem; ssl_certificate_key /etc/ssl/private/yourdomain.key; client_max_body_size 10m; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } server { listen 80; server_name api.yourdomain.com; return 301 https://$host$request_uri; }注意两个细节。一是client_max_body_size 10m这个限制非常重要它防止用户通过上传超大请求体来拖垮你的服务。二是 80 端口全部 301 跳到 HTTPS强制加密访问。腾讯云控制台的防火墙规则里只要放行443和80两个端口就够了底层 Agent 服务的8000端口绝对不要对外开放。很多人为了图方便直接把 8000 端口也加了放行规则这是完全没必要的风险敞口。只要 Nginx 能连到 8000外部用户就能正常使用为什么还要多暴露一个端口呢6.3 上线后的监控与日志出现问题第一时间知道服务上线只是开始真正的考验是后续的稳定性。我部署完 Agent 后做的第一件事就是配置最基本的监控和日志体系。监控方面我没有用复杂的 Prometheus 加 Grafana而是用了一个更轻量的方案写一个健康检查脚本每分钟用 crontab 去请求一次GET /health端点连续失败三次就通过企业微信机器人或邮件推送告警。日志方面我用 Python 的logging库把所有运行日志统一输出到/var/log/agent/目录按天分割。日志里必须包含以下关键信息每次用户请求的 ID、调用了哪些 Skills、每个 Skills 的耗时和返回状态、模型 API 的调用延迟和 token 消耗、以及任何异常堆栈。这些日志是你排查线上问题时的第一手材料没有它们出了问题只能靠猜。有个经验教训值得分享上线初期我在日志里没有打印 token 消耗量结果月底收到云厂商账单才发现模型调用费用比预期高了好几倍。后来我在日志里加了每次请求的 token 计数并在 API 响应里增加usage字段返回给调用方才把成本控制住了。Agent 的成本大头往往不在服务器而在模型 API 调用这个一定要从一开始就做好计量和监控。7. 常见问题排查与避坑技巧7.1 模型不调用工具怎么办检查提示词与参数格式最常见的问题就是Agent 收到了用户消息但它就是直接回答完全不调用已经注册好的工具。这个问题我遇到了不下五次原因通常有三种。第一种是SKILL.md的描述没有写清楚触发条件。模型无法理解“什么时候应该用这个工具”自然就不会调用。修复方法就是我把适用场景拆成“正面触发词 反面触发词”的做法。第二种是工具参数定义有误模型无法生成合法的参数。比如参数定义里required字段写了一个模型无法从用户消息中推断出来的字段比如“internal_id”模型不知道怎么填干脆就不调用这个工具了。修复方法是把参数名改成模型更容易理解的语义化名称或者在参数描述里写明“如果用户没有提供此参数请先向用户询问”。第三种是系统提示词把模型的发挥空间压得太死。我当时在 system prompt 里写了一句“请你直接回答用户的问题”结果模型就真的“直接回答”不敢调用工具了。后来我把这句改成了“当用户的问题需要外部数据或工具支持时请调用工具获取信息后再回答”问题立刻解决了。7.2 工具调用循环卡死或超时加步数限制与单工具超时第二个高频问题是 Agent 在工具调用循环里“出不来”。表现是用户提了一个简单问题Agent 却连续调用了十几个工具既不结束也不向用户解释它在干什么。原因可能是任务规划出了偏差也可能是某个工具反复返回相同错误、模型在自愈循环里打转。解决思路有两个层次。第一个层次是硬性的设置MAX_STEPS上限我一般设为 8 到 10超过直接终止循环并返回“任务步骤过多请简化问题或联系管理员”。第二个层次是软性的如果同一个工具在连续三次调用中返回相同类型的结果我可以让调度器不再把该工具的执行结果继续喂给模型而是返回一段干预信息比如“此工具已连续多次返回相同结果请考虑是否使用其他技能”。单工具超时也要单独设计。我用asyncio.wait_for给每个工具调用设了 20 秒软超时超时后返回“工具执行超时”的错误给模型。这样即使某个第三方 API 响应很慢也不会拖垮整个 Agent 会话。7.3 云服务器上的时区与中文编码问题这是一个非常土但非常真实的问题。我把 Agent 部署到服务器上后发现日志里的时间戳全是 UTC中文日志全部乱码。排查半天发现是 Python 进程没设置好时区和编码。解决方法是在.env环境文件里加上TZAsia/Shanghai LANGzh_CN.UTF-8 LC_ALLzh_CN.UTF-8 PYTHONIOENCODINGutf-8同时启动命令前加export TZAsia/Shanghai。这一步花了我半小时但问题解决后所有时间戳和日志都正常了。如果你在本地开发一切正常、部署到云服务器就出现中文乱码优先检查环境变量。7.4 腾讯云上传与部署时的权限问题在腾讯云上通过git pull拉代码时有一个容易被忽视的坑如果你用root用户创建的目录ubuntu用户运行服务时可能没有写权限。而 Agent 运行过程中可能需要写缓存文件、临时文件或日志文件没有写权限就直接报PermissionError。我的建议是在项目初始化阶段就明确目录所有者sudo chown -R ubuntu:ubuntu /home/ubuntu/agent-project如果在部署过程中遇到“无法写入文件”的问题优先检查的是目录权限而不是代码逻辑。7.5 模型 API 费用翻车用量控制与预算告警最后提醒一个所有 Agent 开发者都会经历的血泪教训模型 API 费用失控。Agent 和普通聊天机器人不一样它在工具调用循环里会发起多轮模型请求每轮都要消耗 token。一个看似简单的任务可能背后已经调了五六次模型接口。如果每个请求都传了一堆长文档、长工具描述费用会非常可观。我的建议是第一给 Agent 的每个会话设置总 token 预算比如 10 万 token超过直接终止任务。第二在模型调用层做结果缓存对于相同或高度相似的问题优先返回缓存结果。第三定期拉取模型 API 的用量账单按天分析哪些时间段、哪些技能消耗了大量 token然后针对性地优化。问题现象根本原因解决办法模型不调用工具SKILL.md 描述不清 / 参数定义不合理拆解触发词优化参数描述工具循环卡死没设步数上限 / 工具返回固定错误设置 MAX_STEPS加入工具结果去重服务莫名挂掉内存泄漏 / 进程异常退出systemd 配置 Restartalways请求超时第三方 API 响应过慢工具调用加 20 秒超时日志中文乱码服务器时区和编码未配置设置 TZ 和 LC_ALL 环境变量API 费用超预期多轮工具调用消耗大量 token设置会话 token 预算加缓存8. 我的实操心得与后续扩展方向最后聊一点实在的体会。整套 Agent AI Skills 的项目做下来我最深的感触是这个领域真正难的不是写代码而是设计“边界”。你要给模型画清楚哪些事它能做、哪些事它不能做、每个工具的参数怎么填、错误了怎么反馈、连续失败怎么止损。把这些边界设计清楚了Agent 才是一个稳定可控的生产工具否则它就是一台随时可能跑飞的发动机。如果你是从零开始我给你一个最低成本的上手路径先不急着上云在本地用 FastAPI 加 LiteLLM 加两个 Skill一个查天气、一个算时间跑通整个流程理解工具调用循环。然后把服务部署到腾讯云配上 HTTPS 和 systemd 守护。最后再根据实际业务扩展更多的 Skills。这条路径每一步的反馈环都足够短不会让你在某个环节卡太久。关于后续扩展我目前正在做两个方向。一是把 Skills 做成“热插拔”——不重启服务就能动态启用或禁用某个技能这样运营同学也能参与技能管理。二是给 Agent 加上更细粒度的权限控制让不同的 API Token 只能调用不同的技能集为将来多租户场景做准备。这两个方向都还比较简单后续有阶段性成果我再单独写文章分享。
返回列表