
先放个我自己的经历开场去年我们团队接了一个“全能Agent”项目目标是做一个既能陪用户闲聊又能查订单、改预约、做推荐的智能助手。最初我把所有功能全写在一个服务里提示词堆了几千字模型一调用就超时回复质量忽上忽下上线后隔三差五出错。后来我彻底重构了一版把每个能力都抽象成独立的AI Skills再结合腾讯云上的容器、Redis、网关和WAF把这套系统稳稳跑起来。这篇文章就是这次从混乱到稳定、从“玩具”到“工具”的完整复盘包含我所有选型理由、代码写法、踩坑记录和线上运维经验。想让自己手头的Agent项目少走弯路的开发者可以认真看看。1. 先搞清楚Agent到底要做什么再写代码技术圈有个很普遍的误区一上来就选框架、调模型、写工具调用结果做了两周发现核心需求都没对齐。绝大多数Agent项目翻车不是因为模型不够聪明而是因为定义阶段偷了懒。我一开始也是这样拿到需求就兴奋地架构设计最后发现客户要的“全能”根本不是对话能力有多强而是能不能稳定地把事情办完。1.1 区分“对话型Agent”和“任务型Agent”一个Agent如果核心目标是聊天那重点在于语言能力、人设一致性、多轮对话管理。但一个要“办事”的Agent重点会完全不一样维度对话型Agent任务型Agent成功标准用户聊得开心、信息准确任务完成率、正确率、耗时核心模块提示词、知识库、语义理解技能编排、工具调用、状态管理失败模式答非所问、幻觉调用错误、状态丢失、超时优化重点模型选型、RAG效果接口可靠性、容错、幂等设计我们这次要做的“全能Agent”本质上是任务型Agent顺便兼顾聊天。这个定位一旦清楚后面所有设计都在围绕“稳定地把事干成”来展开。1.2 用“技能清单”替代“需求文档”很多人写需求文档喜欢写场景故事比如“用户说我想退换货Agent要引导用户完成退货流程”但这对开发没有直接指导意义。我这次的做法是直接产出技能清单每个技能就是一个AI Skill的最小颗粒度订单查询输入订单号或手机号返回订单状态与物流轨迹预约改签校验用户身份拉起改签流程确认新时间写回系统智能推荐结合用户历史行为调用推荐服务生成Top5推荐结果闲聊应答维护人设处理非任务类对话售后引导判断退换货条件输出下一步操作指引把需求拆成这个粒度之后才能判断每个技能的边界和依赖。比如“预约改签”是强状态依赖的必须设计完整的会话态和补偿机制而“闲聊应答”完全无状态可以独立降级。技能边界的划分直接决定了后续AI Skills设计的质量。1.3 为什么“会列技能”比“会写提示词”更重要这是因为大模型本身的推理能力再强也不能保证100%稳定输出结构化结果。如果把所有逻辑都塞进提示词模型一更新或上下文一长行为就飘。而把能力封装成离散的Skill大模型的任务被简化为“意图识别 参数抽取 调用技能”每个环节都可以单独测试、单独优化、单独降级。改造之后的效果非常明显原来几千字提示词里的复杂业务逻辑大部分被移到了服务端代码里模型只负责“理解用户”和“拼装参数”两件事稳定性提升了一个量级。2. 腾讯云上的底座服务器、域名、容器镜像一条龙Agent Skill设计得再好也得落在实实在在的基础设施上。这一节讲我怎么在腾讯云上把整套运行时环境搭起来包括服务器选型、二级域名申请、Docker镜像托管这些前置工作。内容偏工程实践每一步我都尽量说清楚为什么这么选。2.1 服务器选型不要盲目上GPU很多做Agent的人一上来就想买带GPU的机器觉得大模型推理必须靠GPU。实际上如果你的Agent是调用云端大模型API而不是本地部署模型那对GPU的需求几乎为零。真正吃资源的是三块服务进程的CPU、会话存储的内存、以及日志和数据的磁盘IO。我这次选的是腾讯云标准型SA2实例8核16GB起步后面压测发现瓶颈不在机器而在连接数和Redis性能所以又给Redis单独配了台2核4GB的小机器。如果你是个人项目或小团队验证4核8GB都可以起步关键是把进程和存储分开别让一个服务把资源全占了。2.2 二级域名申请与HTTPS配置Agent服务要提供给外部调用必须要有域名和HTTPS证书。这个环节看着简单但坑不少。我当时在腾讯云上申请了一个主域名下的二级域名比如agent.example.com然后去申请免费SSL证书大概十几分钟就签发了。这里我犯过一个低级错误证书申请完后忘了在安全组放行443端口结果外部一直连不上。排查了好久才意识到腾讯云的安全组规则跟云防火墙是两套体系一个管网络ACL一个管云WAF端口放行得两边都检查。建议你在配置域名后第一步就验证curl https://agent.example.com/health能通再继续。2.3 Docker镜像构建与腾讯云镜像仓库Agent服务我采用了容器化部署好处是环境一致、扩缩容方便。代码写好后我用Dockerfile把服务打包成镜像。Dockerfile里有一个细节非常关键pip install依赖时要固定版本别用最新版否则镜像今天能构建明天可能就失败。我习惯把所有依赖写进requirements.txt并逐个锁定版本号。镜像推送我走的是腾讯云容器镜像服务CCR这个服务跟docker registry是兼容的操作流程基本一样# 登录腾讯云镜像仓库 docker login ccr.ccs.tencentyun.com --username your_account # 给本地镜像打上仓库标签 docker tag agent-server:latest ccr.ccs.tencentyun.com/project/agent-server:v1.0.0 # 推送镜像 docker push ccr.ccs.tencentyun.com/project/agent-server:v1.0.0推上去之后在服务器上拉取就容易多了尤其是多台机器部署的场景没必要每台机器重复构建镜像。2.4 容器启动参数与服务编排我这里没有用K8s因为项目规模不大用K8s反而增加运维负担。我用的是docker-compose把Agent服务、Redis、Nginx放在同一个编排文件里。启动参数有几个值得注意的地方日志挂载容器内的日志目录必须挂载到宿主机否则容器一删日志就全没了时区设置容器默认UTC时间Agent的日志和任务调度会跟北京时间差8小时必须在环境变量里设置TZAsia/Shanghai优雅退出给服务设置STOPSIGNAL和graceful shutdown逻辑不然滚动更新时正在处理的请求会直接断掉这套编排跑了大半年基本没出过基础设施层面的故障。3. AI Skills核心实现技能设计、模型网关与工具链接入这一节是整个Agent项目的技术核心。AI Skills到底是什么简单说就是把Agent需要的一项具体能力封装成一个标准化的函数式接口让大模型可以通过“函数调用”的方式使用这个能力。这种做法的本质是把不确定的模型输出转化为确定的系统调用。3.1 AI Skills的标准结构我设计每个Skill都包含四个部分名称、描述、参数Schema、执行函数。大模型根据“名称”和“描述”决定是否调用这个技能根据“参数Schema”生成参数然后系统执行对应的函数。结构可以用一个JSON来描述{ name: query_order, description: 根据订单号或手机号查询订单状态和物流消息, parameters: { type: object, properties: { order_id: { type: string, description: 用户的订单号例如SO123456789 }, phone: { type: string, description: 下单时留的手机号后四位用于身份校验 } }, required: [order_id] }, handler: order_module.query_order }写技能描述时有一个很关键的经验描述要写“什么时候用”而不是“怎么用”。比如“当用户询问订单状态、物流进度、什么时候发货时调用该技能”这样模型才能准确触发。我见过不少人把描述写成“查询订单信息”太笼统模型该调的时候不调不该调的时候乱调。3.2 用litellm统一模型网关现实中的Agent通常不会只接一家模型服务。可能对话用国内某家大模型工具调用的参数抽取用另一家还需要在几家模型之间做流量切换。如果代码里直接写死各家厂商的SDK后面替换和升级会非常痛苦。我选型litellm作为统一模型网关它把OpenAI兼容的接口协议统一到一套调用方式上底层再路由到不同的大模型服务商。这样做的好处是代码只依赖一套SDK改模型配置不需要改业务代码可以做请求级别的负载均衡多个API Key轮询避免单Key限额统一处理超时、重试、Rate Limit错误不用每接一家都写一遍容错逻辑litellm的部署很简单跑一个代理服务配置里写清楚各个模型的API地址和Key就行。我在腾讯云上给它单独开了个小端口只允许内网访问Agent服务通过内网地址调用避免模型网关暴露到公网。3.3 模型回调与业务系统对接Skill执行函数最终要跟业务系统交互。我们项目里的订单模块、用户模块都是现成的内部APISkill层只需要通过HTTP或内部RPC调用这些API。对接时最重要的设计是超时与幂等。大模型调用Skill是实时阻塞的如果业务API响应超过模型端的等待阈值整个会话就会报错。我这边设置了两层超时Skill内部调用业务API的超时控制在2秒整个模型端的请求超时控制在8秒。另外对于“改签”“退款”这类写操作必须保证幂等。我在参数里加了一个request_id字段每次调用生成唯一ID业务系统按这个ID去重防止模型重试时重复执行。这里分享一个实际案例有一回模型对同一用户连续两次触发“改签”技能参数几乎一样如果没有request_id幂等用户的预约会被改两次。加了幂等校验后第二次相同的请求直接被业务系统拦截提示“改签已提交请勿重复操作”才没出大问题。3.4 技能编排单技能调用 vs 多技能协同单个Skill只能处理简单场景真实用户的问题常常要多个技能一起协作。比如“我上周买的那个快递一直没发货帮我看看还能不能退货”这句话Agent需要先调用订单查询技能确认订单状态再调用售后引导技能判断是否可以退货。我在实现技能编排时没有设计复杂的Agent规划器而是采用“链式处理 状态记录”的轻量方案每一轮对话先做意图识别判断需要哪些技能的串联每个技能的输入参数可以从对话中抽取也可以从前置技能的输出中继承会话状态统一存放在Redis中技能之间通过状态上下文交换数据这种“人工编排为主、模型自由调用为辅”的策略在工程上最稳。完全让模型自由规划技能调用在demo里很酷但在生产环境中容易失控尤其当技能数量超过20个时模型经常选错技能或漏传参数。3.5 技能降级设计线上跑久了你会发现业务API总会出故障这是必然的。关键是当某个技能不可用时Agent不能直接“死掉”。我给每个Skill都设计了降级方案如果订单查询API超时直接返回“订单系统暂时繁忙请稍后再试”如果推荐服务异常降级返回热门商品Top5不阻塞主流程如果Agent整体性能告急自动关闭闲聊技能只保留任务类技能降级的核心思路是“保核心、舍次要”。用户不会因为闲聊回复慢而投诉但会因为改签失败而投诉。4. 把记忆交给Redis会话状态、缓存与异常恢复Agent要表现得“全能”记忆能力是关键。多轮对话中用户上一句说的信息下一句可能还要用。没有记忆的Agent每次都要重新抽取语境体验非常割裂。我这次把会话状态和缓存能力全部交给Redis这个选择后来被验证非常正确。4.1 Redis在Agent项目中扮演的三个角色第一是会话存储。我把每轮对话的上下文、用户身份、技能调用记录按会话ID存成一份JSON过期时间设为30分钟。这样用户隔几分钟再回来Agent还能记得聊到哪一步。第二是技能调用缓存。像订单查询这种读多写少的操作同样的查询参数短时间内结果基本不变。我在技能层加了一层Redis缓存缓存里命中就直接返回大大减轻了业务系统的压力。这里的key格式我统一约定为{skill_name}:{param_hash}过期时间一般60秒。第三是分布式锁。改签、支付这类写操作需要用Redis的SETNX实现锁机制防止同一个用户同一时间提交多个写请求。4.2 修改Redis密码后重启不了的排查经历这个坑必须单独拿出来讲因为太典型了。有一段时间我们Redis服务报警我为了安全考虑把Redis密码改成一个强密码改了配置文件里的requirepass字段然后执行systemctl restart redis。结果服务死活起不来日志里报了一大串错误。排查过程我一步步来先看Redis日志journalctl -u redis -n 50发现提示“Bad directive or wrong number of arguments”在配置文件某一行打开配置文件检查发现配置里不仅有requirepass还有一行masterauth我之前为了从库同步配置过这次只改了requirepass没改masterauth导致主从认证信息不一致另外发现密码里含有特殊字符#没加引号Redis配置解析直接把后面的内容当注释了最终的证结是两个特殊字符没转义 主从认证参数未同步。这件事给我的教训是——改Redis密码这种事看起来是小改动但不把配置文件的每个引用点都检查一遍很容易把自己坑进去。后来我总结了一个标准操作# 1. 修改配置文件中的requirepass requirepass New_Strong_Pass_2024#Secure # 2. 如果存在主从复制必须同步修改masterauth masterauth New_Strong_Pass_2024#Secure # 3. 重启前先验证配置 redis-server /etc/redis/redis.conf --daemonize no注意配置里的密码建议用双引号包起来尤其是含有#、空格、$这类特殊字符时不然解析器会以意想不到的方式处理。4.3 AOF持久化与数据恢复Redis默认的持久化策略是RDB快照对Agent场景来说RDB有一个风险两次快照之间的数据可能丢失。比如用户刚提交了改签请求状态写入Redis还没到快照时间机器宕机了这个状态就丢了。我把Redis的持久化模式改成了appendonly yes也就是AOF模式并且设置appendfsync everysec最多丢失1秒数据。AOF文件会越来越大我配置了自动重写auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb在腾讯云上跑这套Redis后我还做了个定期备份策略每天凌晨把RDB文件复制到对象存储里备份防止物理机故障导致数据全丢。4.4 缓存穿透与雪崩的实战应对Agent服务的流量虽然不算大但Redis一旦出问题整个Agent就瘫痪了。我遇到过缓存雪崩原因是所有技能缓存同时过期瞬间大量请求打到业务API把下游系统打垮了。后来我做了两件事给缓存过期时间加随机值把“同一时刻集体过期”变成“分散过期”对于空结果也做缓存但过期时间很短比如5秒防止缓存穿透另外Redis连接池的参数需要认真调。我最初用的是默认连接池压测时发现大量连接等待后来把max_connections从默认值调到200并且设置了合理的socket_timeout和connect_timeout问题才缓解。5. 发布前必须处理的几大拦路虎WAF、超时、并发与错误恢复一个Agent项目从开发到上线技术上真正的考验不在功能实现而在稳定性。这个章节我整理了发布前遇到的几个典型问题每个都有完整的排查路径和解决方案。5.1 WAF策略对Agent请求的误伤与正确配置腾讯云的WAF默认会拦截一些常见攻击特征比如SQL注入、XSS攻击。但Agent请求里大量携带自然语言文本这些文本中很容易出现类似攻击特征的字符。比如用户说“请帮我删除DROP TABLE这条记录”如果原样传给业务APIWAF可能误判为SQL注入直接拦截。我遇到过两次线上问题用户某句话触发WAF拦截导致Agent一直无法正常响应。排查的时候一路查日志发现请求根本没到后端服务WAF日志里显示“检测到疑似SQL注入特征已拦截”。正确的做法是对Agent服务的API路径设置WAF白名单或者只开启“检测模式”不开启“拦截模式”如果必须开启拦截要针对Agent请求的Body内容做业务特征豁免更推荐的方式是把Agent内部的技能调用API设计为不直接暴露公网公网只暴露一个Agent入口WAF只检查这个入口的合规性经过调整后WAF误杀率降到了零而且安全防护能力并没有明显减弱因为Agent入口后面所有内部调用都在VPC内网完成攻击面本来就很有限。5.2 “Agent execution terminated due to error”的完整排查链路这是Agent项目里最经典的报错没有之一。“Agent execution terminated due to error”这个错误信息本身很笼统但它意味着Agent的执行链路在某一个环节断了。我总结了一套完整的排查链路按顺序来基本都能定位问题第一步看是哪个环节报错。在日志里搜execution id把一次Agent调用的完整链路日志拉出来看是模型调用环节、技能调用环节还是后处理环节报错。第二步如果是模型调用环节。检查几个常见原因模型API的Key过期、并发超过限额、上下文token超长、或者模型网关litellm到上游的超时时间太短。第三步如果是技能调用环节。重点看业务API是否正常响应技能函数的异常有没有被捕获。我当时犯过一个错误技能函数里没做异常捕获业务API一返回500异常直接抛到Agent执行器最终表现为“execution terminated”。第四步如果是后处理环节。检查输出解析比如模型返回的JSON带了多余的换行符或markdown标记解析失败导致整个链路中断。我最后给整个Agent执行链路的每个环节都加了结构化日志和唯一追踪ID。这样做的好处是下次再看到类似报错直接通过追踪ID就能定位到具体环节不用每回都盲猜。5.3 并发压测与连接数瓶颈Agent服务上线前我用压测工具模拟了100个并发用户。结果发现一个典型问题后端服务在处理高并发请求时Tomcat/uvicorn的默认线程池扛不住大量请求排队接口响应时间从200ms飙升到数秒。压测出来的瓶颈点有三个瓶颈点表现解决方案Web服务器线程池请求排队、CPU未饱和调大线程池/协程数Redis连接池连接等待超时调整max_connections和等待策略业务API依赖MySQL连接数打满为Agent服务设置独立数据库账号限制连接上限解决方案不是简单地“把连接数调大”而是要给依赖服务设置合理的超时和熔断。我给业务API调用加了熔断器连续5次失败就熔断10秒期间直接返回“服务繁忙”的降级回复不让Agent无限等待。压测完成之后我还发现了一个必须处理的细节长时间保持的连接会被腾讯云的网关断开表现为偶发的“Connection reset by peer”。后来我在服务里加了连接池的空闲回收机制问题消失。5.4 会话恢复与异常兜底即使做了这么多防护Agent线上依然不可能100%不报错。重要的是出错之后怎么办。我给Agent设计了几个兜底机制如果一次Agent调用失败外层捕获后返回“抱歉我刚才没听清能再说一遍吗”而不是把技术错误信息抛给用户如果同一用户连续3次失败直接转人工客服避免用户反复尝试无果关键流程如改签的状态会持久化到数据库即使Agent中途崩溃用户重新进入也能从断点继续6. Agent上线后的日常监控指标、日志治理与迭代节奏Agent上线不是终点而是另一个开始。这里分享我维护这个全能Agent过程中总结的监控方法、日志治理策略和迭代节奏。6.1 核心监控指标别只盯着CPU和内存服务器基础的CPU、内存监控当然要做但对Agent项目来说更关键的指标是业务层面的技能调用成功率每个Skill的成功率/失败率哪个技能掉到95%以下就要重点关注技能调用平均耗时模型响应时间 技能执行时间超过阈值要告警意图识别准确率随机抽样对话日志统计模型是否正确触发技能上下文丢失率Redis中会话失效导致前文遗忘的比例这个指标直接反映记忆能力我通过腾讯云的可观测平台把服务日志、Redis指标、业务API指标汇集到一张看板里每天扫一眼就能判断Agent整体状态。发现异常再去翻详细日志比到处查命令高效很多。6.2 日志治理结构化是关键Agent日志比普通Web服务的日志复杂得多因为一段对话包含模型输入、模型输出、技能调用、状态变更多个环节。我强烈建议把所有日志输出为JSON格式并保留一个统一的trace_id作为贯穿全链路的关联ID。我日志里最常用的几个结构{ trace_id: 7f3a1c..., session_id: sess_8888, event: skill_invoke, skill_name: query_order, params: {order_id: SO123456}, result: success, duration_ms: 325, timestamp: 2024-06-01T10:30:0008:00 }有了这个结构每次查问题我只需要做一件事输入trace_id然后把所有相关日志按时间排开链路一目了然。6.3 迭代节奏与灰度发布Agent的迭代跟传统服务很不一样因为除了代码更新还涉及技能配置、提示词和模型参数的调整。我采用的节奏是两周一个迭代每个迭代只改一个核心方向比如这轮优化意图识别下轮优化技能执行稳定性。每次发布必须走灰度先在一台机器上跑新版本用1%的线上流量验证观察技能成功率和用户反馈没问题再逐步扩大到全量。这里一定要强调Agent的上游模型行为是概率性的不能像普通服务那样只看“有没有报错”来判断版本是否正常还要看“回复质量”和“技能触发准确率”有没有波动。6.4 一个“AI Skills”持续沉淀的框架最后想分享一个长期价值很大的习惯就是我们建立了一个内部的AI Skills仓库把所有沉淀下来的技能按领域分目录管理。每新增一个技能都要填写统一的README模板包含技能的适用场景、参数说明、测试用例、降级方案。这样做的好处是三周前写的技能三周后不用重新看代码就能想起设计初衷。而且新的项目成员上手也快不需要深挖代码看README就能理解每个技能的边界和用法。这个仓库现在已经有几十个技能了Agent的业务扩展基本就变成了“往仓库里加技能”的过程。说实话我最初接手这个项目时也被“全能Agent”这个词唬住了总觉得要上什么复杂框架、什么前沿算法。走下来才发现真正让Agent变得可靠的并不是炫酷的技术而是一套踏实的工程实践清晰的需求拆解、合理的技能化抽象、稳固的容器化部署、可靠的存储与缓存、细致的监控与容错。如果你想在腾讯云上复现这套架构顺序建议是先在本地把Agent核心链路跑通再上容器和域名把Redis和WAF配好最后做压测和上线。每一步都值得多花时间把细节打牢因为Agent项目的坑几乎全藏在细节里。有个小技巧分享给你线上环境跑一段原始流量后每天抽10条对话记录人工复看一次。别小看这个动作它能帮你发现所有自动化测试都发现不了的体验问题——比如技能误触发、回复啰嗦、上下文丢失。持续记录两周你会对自家Agent的“真实水平”有一个清醒的认知这比任何监控面板都管用。