做 Agent 这些年我发现一个特别有意思的现象Demo 里的 Agent 个个聪明伶俐一旦丢进真实的业务环境马上就变成“人工智障”。问题不在模型不够强而在绝大多数人根本没给 Agent 配一套像样的工程工作流。Skills 就是用来填这个坑的——它不是让模型变得更聪明而是让模型能稳定地、可控地、可复用地把一件事做出来。这篇文章我想把我的实操经验拆开讲讲聊聊怎么给 Agent 设计一套基于 Skills 的工程工作流包括技能定义、调度策略、上下文管理、测试流程和灰度上线适合正在做 Agent 产品落地的开发者参考。1. 给 Agent 配工作流到底在解决什么问题1.1 没有工作流时 Agent 的糟糕表现我见过太多团队把 Agent 做成一个“巨型提示词 若干个 Python 函数”的缝合怪。前面一两轮对话还行越往后越拉胯。模型开始编造函数名参数传错类型该查库存的时候去调天气接口明明上一步算好的价格下一步又自己重新算了一遍。这些问题不是模型能力不足而是流程没有边界、没有约束、没有回退机制。你想一下真实团队里是怎么干活的一个项目负责人拆解任务把“查资料”交给调研岗把“跑数据”交给分析岗把“写报告”交给文档岗每个岗位有明确输入输出做完还要评审。Agent 本质上也是一个团队Skills 就是给这个团队里的每个“员工”写的岗位说明书和作业指导书。没有这些模型就只能凭记忆和直觉乱来表现自然不可控。1.2 Skills 工作流的核心组成所谓给 AI Agent 配一套工程工作流我的理解是四个层级的组合技能层Skills把模型需要执行的原子能力打包成可被调用、可被验证的单元。比如“查询客户订单”、“计算含税价格”、“生成周报模板”。编排层Orchestration定义技能的调用顺序、分支条件、回退策略。比如先查订单再算价格库存不足就走缺货登记流程。上下文层Context管理哪些状态需要跨技能共享哪些只属于当前一次调用。避免上下文无限膨胀也避免技能之间互相污染数据。治理层Governance技能的版本管理、调用审计、灰度开关、失败告警。没有这一层工作流跑起来就像没有仪表盘的飞机起飞容易降落看运气。这四个层级缺一个都不行。很多人只做了第一层把一堆函数扔给模型祈祷它能选对。这就是为什么 Demo 看着没问题一上线就崩。2. Skills 的类型划分与设计原则2.1 原子技能、知识包、组合工作流我在实际项目里习惯把 Skills 分成三类每类的设计思路和管理方式完全不同。第一类是原子技能。这类技能对应一次不能再拆分的操作通常是“输入参数 返回结果”的纯函数。比如“查询快递物流状态”、“获取当前天气”、“计算两个日期相差天数”。这类技能最关键的设计要求是参数标准化和返回结构稳定。模型只负责传参和解读结果技能内部不跟模型对话不依赖上下文里其他技能的数据。第二类是知识包。这类技能解决的是“模型不知道但你希望它知道”的问题本质上是把外部知识以可检索、可注入的方式提供给模型。比如商品的促销规则、公司内部的数据口径定义、历史故障的处理记录。我一般会把知识包做成“关键词索引 分块摘要”的结构模型调用时先查索引再拉取命中片段注入上下文。跟直接把这些知识全部塞进系统提示词相比知识包的好处是节省 token、信息新鲜度可控、不会互相覆盖。第三类是组合工作流。这类技能由多个原子技能串联而成内部有自己的执行顺序和状态传递逻辑。比如“订单履约”内部可能包括订单校验、库存查询、价格计算、支付回调四个子步骤。组合工作流适合用固定流程的方式定义不建议把编排逻辑完全交给模型即兴发挥否则每一步的产物格式都可能不稳定后续步骤没法衔接。2.2 设计 Skills 时的三条硬性原则第一一个技能只做一件事。技能名称和功能必须高度对应“处理订单”这种模糊名字坚决不要。模糊的技能边界会导致模型在选择时产生困惑也会让后续评测和维护变得无从下手。我在团队里定的标准是技能描述的第一句话必须能回答“这个技能在什么场景下会被调用”如果一句话说不清就说明这个技能的粒度拆得有问题。第二输入输出必须有明确的 Schema。原始工具函数时代大家习惯用 JSON 格式自由传参。但在真实场景里模型调用时容易出现字段名张冠李戴、值类型不一致、缺少必填项的情况。现在我会给每个技能声明严格入参校验规则该整数的整数、该枚举的枚举、该必填的必填。不满足 Schema 的调用请求直接拒绝拉回让模型重新生成参数而不是把错误参数静默传给下游。第三技能内部不能依赖模型推理。技能一旦被触发内部的执行逻辑应该是确定性的。比如“生成订单编号”就应该用时间戳加序列号的算法直接生成不能让模型去编一个编号“计算折扣”应该查规则表而不是问模型“你觉得打几折合适”。模型在做创造性任务时有优势但在做确定性计算时反而不可靠把这两类任务彻底分离系统整体的稳定性会大幅提升。3. 搭建一套可落地的 Skills 工作流3.1 定义接口约定从 Function Calling 到统一协议我最早做这套体系的时候直接用的是底层的函数调用格式给模型列举每个函数的名字、描述、参数模型自己决定什么时候调用哪个函数。这套方式简单直观但很快就碰到问题了。不同业务的技能散落在多个服务里有的技能是内部接口有的是第三方平台能力还有的是查数据库的 SQL 封装如果每个技能都按各自的方言暴露出去模型光记调用格式就得分心。后来我把所有技能统一收敛到一套中间协议上协议的每个请求都包含四段信息技能标识全局唯一的技能编码比如ops.ticket.create。入参数据符合技能 Schema 的结构化参数。调用上下文当前会话的会话 ID、用户 ID、租户标识方便做权限校验和日志追踪。期望返回声明本次调用要拿到的数据字段集合避免一次调用返回大量用不到的冗余字段。这套协议的好处是模型侧看到的技能列表高度统一不管底层是大模型函数调用、还是某个内部 RPC甚至是一个爬虫脚本暴露出来的形态都是一样的。工程师可以各自维护自己的服务只要保证协议层的契约不打破就行。3.2 路由与调度模型决策 规则强制兜底技能数量一多编排就成了难点。我现在的做法是“模型优先 规则兜底”的双层调度策略。模型根据用户意图和对话历史选出它认为最合适的技能和参数顺序这是第一层。但模型的决策不能完全信任第二层是一套用代码写死的路由规则会在模型决策基础上做合理性校验。举个例子用户要求“把今天的销售数据汇总并发给管理者”。模型可能会倾向于先调用“查询销售明细”再调用“生成汇总报告”最后调用“发送邮件”。这套流程模型自己规划没问题。但如果模型在第一步直接调用了“发送邮件”而邮件里什么都没有就是明显的编排错误。规则层会把这种“下游技能没得到必要输入”的情况拦截掉返回一个错误码要求模型重新规划。还有一些技能是硬性串行依赖的比如先创建订单再发起支付先锁定库存再生成出库单。这些依赖关系不适合让模型自由发挥我会用有向无环图的方式定义好。模型可以决定分支和条件但依赖关系是代码写死的不可逾越。这样既保留了模型的灵活性又避免了即兴编排导致的低级错误。3.3 上下文管理别让记忆无限膨胀Skills 工作流跑起来的另一大坑是上下文失控。很多 Agent 会把前面所有轮的对话内容一直携带下去几轮之后上下文里塞满了历史消息、中间技能输出、用户重复过的表述。模型的注意力被稀释调用技能时容易参考到过期或者不相关的信息而且 token 消耗直线上升。我的处理思路是把上下文拆成三层会话层上下文包含用户身份、意图标签、当前业务场景这类信息全程保留。工作流层上下文包含当前技能链路的中间结果比如“订单号已经生成”、“库存校验已通过”。这类信息只需要保留到当前工作流结束。临时上下文单次技能调用的输入输出调用结束立即清理。每一层有独立的生命周期和写入权限。工作流层的数据在步骤间传递但不会无限累积临时上下文用完即弃避免模型把“上一次调用返回的废话”当成有效输入。做了这套拆分之后长会话场景下的准确率和 token 成本都有了明显改善。3.4 一段完整的实操示例订单履约技能我拿一个虚拟的业务场景走一遍完整流程用户在对话里说“我要买两件某款衬衫帮我下单”。Agent 收到后的实际执行链路如下。第一步意图识别与技能匹配。模型判断用户有“下单”意图路由规则确认当前用户已完成登录、收货地址已填写于是候选技能锁定为order.create、inventory.check、promotion.calc三个。第二步规则层校验依赖顺序。系统确定必须先调用inventory.check参数是商品编码和订购数量。这个技能返回库存数量及仓库分布。如果库存不足工作流在这里终止并回复用户缺货如果库存充足继续进入下一步。第三步调用promotion.calc入参是商品编码、数量、用户等级。返回的是优惠明细、优惠后单价、是否满足满减条件。这一步是确定性计算技能内部查规则表不靠模型推断。第四步模型根据前面两步的结果生成订单信息调用order.create创建订单。创建成功后拿到订单号再触发payment.request发起支付。注意payment.request和order.create必须串行而且order.create必须返回成功码这个依赖关系在规则层固定。在这个过程中模型全程没有直接操作数据库没有自己计算价格也没有凭记忆填写订单号。每个环节的产物都有 Schema 校验校验失败会回退到重试策略。这就是一套工程工作流跟“让模型自由发挥”的本质区别。3.5 实操中的小工具和参数配置跟团队协作时我会把一些技能的路由规则放到可配置的地方比如推荐存到配置中心而不是写死在网关代码里。带几个实际参数供参考。配置项推荐值说明技能超时时间5 秒超过视为调用失败走重试或回退重试次数2 次幂等技能可重试非幂等只记录上下文最大长度6000 tokens超出后裁剪临时上下文路由规则优先级规则 模型模型建议仅供参考不能覆盖硬约束日志采样率100%开发 / 10%生产全链路日志用于复现问题这些参数不是拍脑袋定的。超时时间设得太短真实接口容易假失败设得太长用户等待体验会很差。上下文 6000 的上限是我在几个项目里测试过的一个平衡点具体数值还得根据你实际用的模型和业务复杂度来调整。4. 测试、评测与灰度上线的工程技巧4.1 给 Skills 建立离线回归测试集Skills 工作流上线以后最怕的不是写不出新功能而是改了一个技能的参数格式结果把另一个跟它八竿子打不着的流程弄崩了。这类问题靠人工回归根本防不住必须把测试固化下来。我在项目里维护了一套离线回归测试集每条测试用例就是一个“用户请求 期望技能调用序列 期望输出结构”的三元组。比如用户请求“查一下我用这张券买三本书要多少钱”期望调用序列coupon.validate→book.price→order.discount期望输出结构优惠前总额、优惠金额、应付金额、券后单价每次技能有改动就批量跑一遍回归集。大模型有个特点同样的输入不同版本下的输出可能有细微差异。所以断言不能只比较“完全相等”还要做容错处理比如允许技能顺序有合理变化、允许参数名有别名映射但核心调用链路和关键输出字段必须严格匹配。从我的经验来看离线回归测试集的规模按你实际技能数量的一到两倍来建比较合理。技能少的时候可以手写技能多了就必须用“线上日志回放 自动补充”的方式持续维护不然这个测试集会很快过期失去保护作用。4.2 版本管理与灰度策略技能的升级不能走“改完直接上线”的路子尤其是那些被多个上层流程复用的技能。我现在采用的方案是给每个技能做版本化发布版本命名skill_namemajor.minor.patch主版本变更代表接口不兼容次版本代表新增可选参数补丁版本代表内部逻辑优化。灰度规则新版本先开放给 10% 的流量运行稳定后逐步扩大到 30%、70%、100%。灰度期间模型侧同时暴露新旧两个版本路由规则里加一个按用户 ID 取模的分配器。回滚机制灰度阶段发现异常直接把流量分配器切回旧版本不需要重新部署服务。这套做法的核心思路是技能的调用方不只是前端用户还有 Agent 模型本身。模型对技能行为的预期是基于历史调用经验形成的突然换参数格式或者改返回值模型来不及适应。灰度就是给模型一个缓冲期。很多团队忽略这一点技能一改全量上线然后发现模型开始乱传参原因就是新旧响应格式不一致导致的。4.3 可观测性日志、追踪与告警给 Agent 做排查可比排查询普通接口麻烦多了。普通接口出了问题看请求参数、看报错堆栈基本就能定位。Agent 工作流出问题你面对的是一个多轮对话 多次技能调用的序列某一个步骤的错误可能会在下下个环节才暴露。所以日志埋点一定要按“链路追踪”的思路来做。从我这边项目里总结出来的最小化埋点方案每一次用户请求分配一个全局唯一的request_id。每一次技能调用记录技能编码、版本号、入参摘要、出参摘要、耗时、成功/失败。模型每一步的思考摘要和最终回复也挂到同一个request_id下。所有日志统一进一个可检索的日志平台后台上能按request_id一层层展开。告警规则也要针对性设置。比较有价值的几类监控指标包括技能调用失败率超过 3%、同一用户连续三轮触发同一个技能重试、请求超过 30 秒仍未结束、日调用量环比异常波动超过 20%。这些指标能从不同维度告诉你到底是技能本身出了问题还是编排逻辑不合理或者模型在当前场景下产生了不可控行为。没有这套监控你在论坛里问一万遍“Agent 到底卡在哪了”都没用还是得回到日志里找答案。5. 常见坑与排查技巧实录5.1 技能冲突导致重复调用我遇到过的第一个高频坑是技能定义重叠。比如既有“创建订单”又有“快速下单”两个技能功能高度相似模型会随机挑一个或者在某些上下文里先调用 A 再调用 B产生重复订单。排查办法是定时做技能语义相似度对比。发现两个技能描述的向量相似度高就合并或明确边界。最好在技能设计阶段就用统一前缀比如order.*和cart.*让模型一眼能看出所属域。5.2 上下文里的幻觉参数模型在调用技能时偶尔会编造一些根本不存在的参数值。典型场景是用户没有提供商品颜色但模型在调用product.search时自作主张填了蓝色返回了错误的结果最后用户投诉说“我要的是黑色为什么给我蓝色”。排查这类问题要在规则层加一道“参数来源校验”凡是用户没提供、上下文里也没有的信息必须把参数标记为“待询问”不允许模型凭经验填充。操作时可以在技能的入参 Schema 里启用requiredIf条件声明或者让校验层对比技能入参和上下文抽取出的实体列表发现未知实体就驳回。5.3 并发调用导致状态不一致组合工作流里如果两个技能并发执行而它们共享了同一个状态位很容易出现问题。比如“锁定库存”和“生成预订单”同时跑可能预订单生成了库存却没有锁住后面真实下单时才发现超卖。这种问题的根因是技能之间缺乏状态隔离和事务边界。建议的做法是组合工作流内部尽量串行或者把共享状态抽到一个独立的资源管理器里每个技能访问状态时走统一加锁接口。宁可在这一步做同步也不要拿一致性去赌并发性能。5.4 长尾请求把工作流带偏有些用户的表达极其口语化还喜欢跳步骤。比如“我不确定要不要买先帮我看看有没有货哦对了如果有的话顺便告诉我能不能用优惠券”。模型一个不小心就会直接跳到查优惠券结果连商品编码都没获取到。我的解决经验是在对话早期模型识别出“主要意图”后先把缺失的必填参数列表列出来缺少哪个参数就主动追问而不是尝试用一个带缺省值的参数调用技能蒙混过关。追问环节本身也是一个技能叫request.missing_param被强制插在主流程和意图识别之间。这套机制治好了很多模型即兴发挥的毛病。6. 一点个人体会做 Skills 工作流这半年多我最大的感受是真正让 Agent 变可靠的不是模型而是模型身边的这条“流水线”。好的工作流能把模型的不稳定性兜住把业务逻辑从模型的随机性里剥离出来让每一步都有验证、有回退、有记录。给 AI Agent 配一套工程工作流本质上不是在写代码而是在建立一套约束和信任体系——约束模型的自由度换取系统的稳定性通过持续的测试和监控逐步建立起对这套体系的信任。踩过的坑是很好的老师希望我这次踩过的坑能帮你少走几步弯路。
阅读完成 · 觉得有帮助?