☰
生产级AI Agent工作流实战:从原型到稳定落地的工程化改造指南
2026/10/19 4:17:27 网站建设 项目流程

带过三个从 Demo 落到线上的 Agent 项目之后,我对“生产级 AI Agent 工作流”这几个字有了完全不同的理解。这个词看上去像是一个技术方案,实际上是一整套关于稳定性、成本、失控风险的系统工程。今天不聊概念,直接讲我是怎么把一条“能用就行”的 Agent 链路,改造成能在线上稳定跑、能排障、能算钱的完整工作流。如果你也正在从原型走向产品,这篇内容会帮你少踩很多我踩过的坑。

先说一个大多数团队都会踩的坑:很多人把 Agent 项目做出来之后,第一反应是“能跑就行”,然后直接丢到线上,结果第一天就翻车。有的问题是模型突然返回一段和业务无关的闲聊,有的是工具调用失败导致整个会话卡死,更常见的是月底一看账单,成本高到让人怀疑人生。这不是模型不行,而是你还没把它放进一个生产级的工作流里。所谓工作流,不是一串 Prompt,而是一整套能够约束模型、控制风险、度量结果的工程骨架。

1. 为什么生产级 Agent 这么难做

1.1 一个容易被低估的问题:生成的偶然性

开发和跑通一个 Agent 可能只需要几天,但线上稳定运行几个月才是真正的挑战。LLM 每次返回的内容不完全一样,同一个 Prompt 今天可能输出一段标准的 JSON,明天就可能在 JSON 前后多了一句“好的,我来帮你查询”。这种随机性在 Demo 里是特色,在生产里就是事故。生产系统需要的是可复现、可回滚、可回归,而生成式模型天然不具备确定性。所以第一步不是调整 Prompt,而是把所有不确定性来源列出来,用工程手段一个个围住。

举个例子,做一个简单的“订单查询助手”,你在测试环境反复调用,模型大概率都能返回正确的订单号。可一旦放到线上,用户的问法千奇百怪,模型可能在同一个槽位上给出多个值,甚至把订单号与日期混淆。如果不做校验和强制结构,后面所有流程都会拿到脏数据。我们最初把这类问题当“模型不够聪明”去处理,后来发现真正缺的是一个带有状态和校验的框架。

我自己的体会是,别指望模型“自觉”遵守规则。所有生产上必需的规则,必须下沉到代码里校验,比如输出格式校验、字段枚举校验、内容长度校验。模型负责生成候选,工程负责判断能不能用。这个分工一旦确认,后面所有设计都顺了。你可以在代码里加一层“结果校验器”,专门负责把模型输出里的非法内容拦在业务逻辑之外。

1.2 生产环境的约束条件:时延、成本、资源

线下体验一个 Agent,慢几秒无所谓。线上每一个请求都在消耗 Token,模型推理要时间,外部 API 也要时间,工具调用还可能卡住。成本按量计费,延迟影响用户体验。一个生产级工作流,必须给每个环节设置时间预算和 Token 预算。我们常用一句话约束团队:不要设计一个无法回答“单次请求花了多少钱”的系统。

算一笔账。假设一次客服工单处理需要 3 轮模型交互,每轮输入输出加起来约 4000 个 Token,按主流定价估算,单次成本大概在几分钱到几毛钱之间。看着不多,但一天一万次工单,就是几百到几千元的成本。如果状态机设计得不好,让模型在无关分支上多转了两圈,成本会翻倍。所以生产级设计必须对每一步都“精打细算”,该收敛时就收敛。

时延同理。模型推理本身就要一两秒,工具调用又要一两秒,如果还允许模型自由循环调用,用户等待时间会迅速超过可接受阈值。我们在生产环境里强制设定了 Agent 的“最大步数”和每个工具的超时时间,超过就终止并转人工。这个限制看起来简单,却能阻止大多数线上事故。你要在系统设计时就想清楚,什么情况下允许 Agent 继续思考,什么情况下必须停下来。

1.3 生产级 Agent 的四个硬指标:稳定、可控、可观测、可算账

我把生产级 Agent 的验收标准收敛成四个硬指标,缺一个都算不上“生产级”。

指标具体含义缺失时的后果
稳定可用性达标,单个工具报错不会导致整体挂掉一次 API 抖动就让整个业务不可用
可控行为可约束,输出格式可控,工具权限可控模型自由发挥,出现越权操作
可观测调用链可追踪,每个环节有日志和指标出了问题只能靠猜,无法定位
可算账成本可核算,每次请求能对应到业务指标月底账单爆炸,不知道钱花在哪

如果这四个指标没有在设计阶段就规划好,等项目上了线再补,会非常痛苦。稳定依赖超时、重试、降级;可控依赖状态机和权限设计;可观测依赖统一的 trace_id 和日志规范;可算账依赖请求级的 Token 计数器。这些都不是模型能力,而是工程底子。很多人觉得 Agent 项目的核心是模型,但真正决定它能不能上线的,恰恰是这些看起来不那么性感的工程能力。

2. 工作流整体设计:从自由对话到可编排

2.1 状态机:把“智能”收敛到确定的流程里

最初我们的 Agent 是聊天式的,用户随便说,Agent 随便接,除了爽快一无是处。后来线上开始有业务方抱怨“它有时候会自己跑到某个流程里去”,才意识到自由对话不等于自由流程。把一次任务拆成“受理→信息收集→识别→查询→生成方案→确认→完成”,每个阶段定义入口条件和出口条件,就形成了一个状态机。状态机不是限制 Agent 智能,而是把智能放在“每个节点做什么”上。

# 状态机编排(伪代码示例) STATES = { "collect": {"next": "classify", "tools": ["ask_user"]}, "classify": {"next": "search", "tools": ["intent_classify"]}, "search": {"next": "solution", "tools": ["query_order", "query_faq"]}, "solution": {"next": "approval", "tools": ["generate_solution"]}, "approval": {"next": "done", "tools": ["submit_approval"]}, } def run_agent(workflow, user_input): state = "collect" while state != "done": allowed = workflow[state]["tools"] result = llm_call(state, user_input, allowed_tools=allowed) state = validate_and_next(result) # 必须校验后流转 return result

状态机天然解决了三个问题:流程可追踪、异常可定位、权限可控制。出错时我们知道是在哪一步出的错,而不需要把整个对话历史翻出来。自由对话模式很难做到这一点,因为每一步都是模型临时决定的,没有固定边界。把任务拆成确定的状态之后,你还能在状态之间插入人工审批、条件判断、超时处理,这些都是生产系统必须的能力。

2.2 Agent 的工具设计:既要给能力,也要上锁

工具调用是 Agent 能力核心。生产环境里工具即权限,不能随意放开。我们维护一张工具清单,每个工具包含:名称、描述、参数 Schema、超时时间、权限级别、错误码。描述特别重要,因为模型根据描述选择工具。如果描述含糊,模型就会把“查询订单”和“查询物流”搞混。描述要写“什么场景下用、需要哪些参数、返回什么结果”,不要写“查询数据的函数”这种模糊话。

{ "name": "query_order", "description": "根据订单号查询订单基础信息,仅用于售后和物流场景", "parameters": { "order_id": { "type": "string", "pattern": "^[A-Z0-9]{12}$", "description": "12位订单号" } } }

除了 Schema,还要注意三点。第一,工具内部必须做参数白名单校验,模型给了非法值直接拒绝,不要向下游传。第二,工具返回结果一定要标准化,成功返回数据,失败返回错误码,不要用自然语言描述“我失败了”。否则后续逻辑没法判断到底发生了什么。第三,工具执行要记录审计日志,尤其是涉及退款、发货、修改权限的工具,后续扯皮时这是唯一证据。

2.3 上下文与记忆的分层设计

不能把所有历史都塞给模型。生产成本和上下文长度成正比,而且超过一定长度,模型注意力会下降。我们采用三层记忆策略。会话内记忆只保留最近 N 轮完整对话和当前任务的关键字段;业务记忆把用户偏好、历史处置摘要放外部数据库,按需检索注入;静态知识把 FAQ、商品参数等放向量数据库,用 RAG 检索相关片段。三层之间有明确的读取接口,不是一锅炖。

还要给每个请求设置上下文窗口上限。比如“单次请求的 Prompt 不超过 6000 Token,超出时把最旧的对话压缩成摘要”。压缩策略要单独评估,不能随意丢,否则用户问“我刚才说了什么”时 Agent 会失忆。我的经验是宁可每次多花一点时间做摘要,也不要把所有原始历史全部塞进去。摘要做在离线任务里,线上只取摘要和最近对话,效果和成本都更可控。

2.4 可观测性:日志、追踪、评估

Agent 工作流比普通接口复杂。一次处理可能涉及模型调用、工具调用、状态跳转、人工介入,任何一个环节出问题都会表现为用户侧的“答非所问”或“卡住”。我们上线了“三段式可观测”:实时日志、链路追踪、离线评估。实时日志记录每一次 LLM 调用的 Prompt 和 Completion 摘要、Token 数、延迟、工具调用参数和返回码。链路追踪用一个 trace_id 贯穿请求全链路,同时关联业务订单号。离线评估定期用脚本回放历史工单,统计任务完成率、格式合格率、工具误用率。

如果发现线上异常,第一件事不是改 Prompt,而是顺着 trace_id 把当时的输入、模型输出、工具返回全部捞出来,看到底是模型选错工具,还是工具返回了脏数据,还是状态机跳错了分支。没有这个基础设施,排障就像蒙着眼睛修车。你可以先花两天时间把日志结构定下来,后面所有迭代都会受益。这个投入绝对值得。

3. 核心模块工程化要点

3.1 模型与服务商的选型策略

不要一个模型解决所有问题。我们通常按任务类型切分:意图识别、槽位提取用轻量快速模型,要求低延迟、低成本;工具选择、复杂推理用大模型,要求较强的指令遵循能力;摘要生成、推荐回复用中等模型,平衡效果与成本。这么做的原因很简单:线上请求的难度分布很不均匀,一部分请求根本不需要大模型介入。

做选型时,建立一个评估集,至少覆盖 500 条真实业务数据,统计成功率、格式合格率、平均 Token 消耗和 P95 延迟。模型不是越强越好,关键是“这个任务是否值得这个价”。比如客服场景,70% 的请求是查订单和退换货规则,用轻量模型就够了,只有难缠的 Case 才升级到大模型。多个服务商之间要预留切换开关和适配层,避免单点依赖。这个适配层可以是一层统一的 LLM 调用接口,上层代码不感知具体服务商。

3.2 结构化输出与工具调用的工程实现

生产级要求模型输出可直接被程序消费。最简单但最不稳定的做法是“请你返回 JSON”写在 Prompt 里,模型偶尔会多解释几句,偶尔会把 JSON 藏在代码块里。我们更推荐使用模型平台提供的结构化输出或工具调用能力,让模型返回一个符合 Schema 的结果。如果平台不支持,也要在外面包一层“Prompt + 解析 + 校验 + 修复”的代码。

def call_llm_with_tool(user_input, tools): resp = llm.chat( messages=[{"role": "user", "content": user_input}], tools=tools, tool_choice="auto" ) candidate = extract_tool_call(resp) # 从响应中解析工具调用 if not valid_tool_call(candidate): # 二次修正:把错误信息返回给模型让它重新生成 resp2 = llm.chat( messages=[...] + [{"role": "tool", "content": "invalid: ..."}], tools=tools ) candidate = extract_tool_call(resp2) return candidate

这段代码说明一个常用技巧:遇到解析失败不要直接报错,把错误原因拼回去,让模型重新生成一次,成功率能显著提高。同时要对输出做业务校验,比如日期格式、订单号格式、金额范围。保存原始模型返回和清洗后的数据,方便事后查证。我们内部叫“脏数据保留原则”,所有被修正过的输出都单独标记,定期分析这些修正原因,反过来优化 Prompt 和 Schema。

3.3 重试、超时、降级和熔断

任何线上系统都要面对网络和服务不稳定。模型 API 偶尔会超时,工具 API 偶尔会返回 500,用户输入偶尔会触发极端 Case。我们需要有三重防线。第一重是重试:对模型调用和工具调用加入指数退避 + 随机抖动,最多尝试 3 次,避免连打加重问题。第二重是降级:当主模型不可用时,切到备用模型或直接走静态 FAQ;当工具不可用时,返回缓存数据或转人工。第三重是熔断:连续失败超过阈值时,自动停止调用一段时间,保护下游系统。

经验是,超时时间一定要比重试总时间短,否则用户会等很久。我们一般设置首轮超时 5 秒,重试三轮,总共不超过 20 秒,超过就进人工队列。这个数字需要根据你的业务场景调整,但原则一致:永远给用户一个明确的结束状态,而不是无限转圈。生产系统最怕的其实不是失败,而是“没有结论的失败”。

3.4 成本控制与限流策略

成本失控是生产级 Agent 最常见的事故。一个 Agent 系统可能日请求量不大,但一次请求烧掉的 Token 却很惊人。我从第一天起就要求每个请求必须带“成本 Tag”,记录使用的模型、输入 Token 数、输出 Token 数、工具调用次数。每天汇总到成本看板,按业务线分摊。没有这个机制,你根本不知道是哪个功能在烧钱。

还要做三层限流。用户级 QPS 限流,防止某个用户刷接口;功能级配额,比如每个用户每天最多调用 Agent 20 次;全局预算保护,当每日成本超过预警值时自动降级或熔断。另外,对可缓存的工具结果和 Prompt 做缓存,相同订单号 1 分钟内不用重复调用模型。这些措施叠加起来,能省下相当可观的成本。我见过一个团队因为忘了加缓存,同样的订单查询重复调了十几次模型,后来加了一层简单的缓存,成本直接下降了三成。

4. 完整实操:一个“客服工单自动处理 Agent”的落地过程

4.1 需求拆解与流程定义

假设要给某公司的客服系统加一个“工单自动处理 Agent”。业务要求:用户提交售后工单后,Agent 自动提取订单号、问题类型和用户诉求,查询订单信息,生成处理方案;如果方案涉及金额大于某个阈值,推送给人工审批。先定义边界:Agent 不直接操作退款,不直接修改订单,只负责生成建议和发起审批。这个边界能避免很多合规风险。

然后定义状态流转,每个状态都要明确输入、工具、输出和兜底。状态“submit”接收用户消息,不做工具调用,负责提取订单号和诉求,信息不足则追问;“classify”把订单文本送给意图分类器,输出问题类型,低置信度转人工;“query_order”用订单号调用查询 API,返回订单状态和金额,查询失败转人工;“solution”结合订单和问题类型生成方案建议,无匹配方案转人工;“approval”提交审批 API,返回审批单号,报错重试;“done”输出最终会话结论。表格化之后,整个流程一眼就能看明白。

4.2 工具集实现与 Agent 编排

实现三个工具,每个工具都是统一接口。查询订单信息、查询商品说明书、提交审批单。我们写一个统一 wrapper,统一做参数校验、超时控制和错误返回。

class Tool: def __init__(self, name, schema, handler, timeout=5): self.name = name self.schema = schema self.handler = handler self.timeout = timeout def invoke(self, params): validate_params(self.schema, params) try: result = run_with_timeout(self.handler, params, self.timeout) return {"ok": True, "data": result} except TimeoutError: return {"ok": False, "error": "timeout"} except Exception as e: return {"ok": False, "error": str(e)}

然后状态机在每个状态下调用模型,模型返回一个工具调用,编排层执行工具,把结果返回给模型,继续走下一步。工具调用后的模型再次生成需要正确的消息组织,把之前的用户请求、工具调用、工具结果一并作为历史给模型,否则模型会失忆。我们自己有一个固定的 messages 构造规则,所有状态都走同一套逻辑,减少出错的概率。

4.3 异常场景处理与人工兜底

线上最大的问题不是模型不会回答,而是模型不确定时硬答。我们的策略是给 Agent 设一条“不自信”通道:当模型置信度低于阈值,或关键字段缺失,或工具调用失败时,不允许自行编造,必须向用户请求补充信息;连续两次失败就自动转人工。另外,所有无法判定的事件都会写进一个人工队列。这个队列和 Agent 本身一样重要,甚至更重要。

线上运行后你会发现,10%~20% 的工单最后需要人工介入,这不是失败,而是系统安全设计。生产级 Agent 的目标不是替代人工,而是让部分简单问题自主解决,复杂问题有序排队。人工队列的接口要和工单系统打通,避免人工处理时还要把用户信息重新抄一遍。这个体验做得顺不顺,直接决定业务方愿不愿意用这套系统。

4.4 上线前的评估与灰度

上线前,我们整理了一个 1000 条历史工单的评估集。每条工单都有人工标注的目标动作,比如应该查哪个订单、给什么方案。Agent 跑完后自动比对,统计任务完成率、格式合格率、工具误用率、平均 Token 数和 P95 耗时。设好能够上线的阈值:任务完成率 ≥ 90%,格式合格率 ≥ 98%,工具误用率 < 2%。达不到就继续调,不要硬上。

灰度一般分三阶段:5% 流量跑两天,看错误率和人工介入率;然后放到 30% 跑一周,观察成本;最后全量。每个阶段都要有开关,出了事一键切回人工流程。这个设计看起来繁琐,但能在关键时刻救命。我们曾经在灰度阶段发现某个工具在高峰期响应特别慢,如果直接全量,大概率会把客服系统拖垮,幸亏有这层开关。

5. 高频问题与排查技巧实录

5.1 常见问题速查表

下面这张表我建议直接收藏,都是真实线上环境里反复出现的问题。

现象可能原因排查方向解决办法
Agent 频繁调用无关工具工具描述模糊、Prompt 自由度过高看 trace 中被调用的工具和当时 user query精炼工具描述,缩小每个状态的允许工具集
模型返回 JSON 解析失败模型不支持严格结构化输出抓原始响应,看解析失败的具体位置改用结构化输出接口,或加二次修正和正则兜底
线上成本突增缺少限流、缓存失效、模型循环调用看成本看板是否集中在某些用户/功能加配额、加缓存、设置最大步数
上下文超长或模型卡顿把全部历史塞进 Prompt,没有分层记忆统计请求的 Token 数和上下文长度做摘要压缩、窗口截断和按需检索
模型输出与事实不符知识库缺失、Prompt 负面引导对比检索内容和模型输出增强 RAG 召回质量,限制输出范围

5.2 排查方法:从现象到根因

分享一个真实经历。有段时间我们的 Agent 在夜间集成环境频繁报错,用户侧表现为一到晚上就卡住。一开始大家怀疑是模型服务商晚上限流,后来通过 trace_id 定位,发现夜间有批量任务触发了同一个工具,该工具每次都要等一个外部接口响应,超时时间设置过长,导致线程池被占满。问题不在模型,也不在状态机,而在工具并发设计。

排查顺序应该固定下来:先看 trace 和时间线,确定是哪一层;再看请求参数和返回码;最后做小样本复现。不要一看模型输出不对就改 Prompt,那样往往掩盖真正的问题。我见过太多人把时间花在调整 Prompt 上,结果问题出在缓存 key 写成了一样。先假设“模型没错”,检查你的调用方式、数据、基础设施,最后再回来看模型行为。

5.3 避坑建议与个人体会

最后说几点实操总结。第一,先定义评估集,再调系统。没有评估集,你会被偶尔很好用的假象骗了。第二,把 Agent 的自由度推到流程边缘。核心流程用状态机约束,边缘的开放性问答才让模型自由发挥。第三,人工兜底不是可选项。生产级系统必须设计模型失败后会发生什么的路径。第四,成本监控从第一天就做,等到月底账单出来再后悔就晚了。第五,缓存和幂等是好朋友。工具调用尽量设计成幂等,重复执行不会引发副作用。

我个人实际做下来,最值钱的经验是把 Agent 当成带概率的组件来对待,而不是当成会思考的主脑。所有概率性的输出,都要被工程确定性围住。这套工作流上线后,客服团队的自动解决率稳定在七成以上,人工只处理真正复杂的 Case,团队也从被工单淹没变成了处理有价值的问题。生产级 AI Agent 工作流不是一个炫技的摆设,它本质上是在模型能力之上,补上工程系统该有的稳定和安全。只要这个骨架搭对了,后续换模型、加工具、扩展场景都会顺畅很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询