1. 从"模型很聪明"到"流程真能跑":AI流程管理系统的核心命题
很多团队在2024年前后都经历过这样一个阶段:花了几周时间把大模型跑起来,Demo演示时效果惊艳,领导点头、同事鼓掌,然后……就没有然后了。模型躺在服务器里,业务部门该填的表还在填,该走的审批还在走,AI和真实业务之间隔着一道看不见的墙。
这道墙的本质,是大模型的能力边界与业务流程的执行需求之间存在结构性错位。大模型擅长理解、生成、推理,但它不擅长记住"这个审批单已经流转到第几级"、不擅长保证"金额超过5万必须走财务总监节点"、更不擅长在流程卡住时主动催办。而流程管理系统恰恰相反——它精于状态机、权限、节点流转,但对非结构化输入(一段自然语言描述、一张模糊的发票照片、一封语焉不详的邮件)几乎无能为力。
AI流程管理系统要解决的,就是把这两者的能力拼接起来:让大模型做它擅长的语义理解和决策建议,让流程引擎做它擅长的状态管理和执行约束。听起来简单,但真正落地时会发现,难点根本不在"调通API",而在于一系列工程细节——意图识别准确率不够怎么办、模型输出格式不稳定怎么兜底、流程节点如何与模型调用解耦、人工介入的时机怎么设计、成本怎么控制。
这篇文章面向的是正在或即将把大模型接入业务流程的技术负责人、全栈工程师和产品经理。我会从架构分层、模型选型、意图路由、执行引擎、人工兜底、成本优化六个维度,把"从大模型到业务执行"这条链路拆开讲清楚。不堆概念,只讲能落地的方案和踩过的坑。
2. 架构分层:为什么不能把大模型直接塞进流程引擎
2.1 直接集成的三种典型翻车方式
我见过不少团队的第一版方案是这样的:在流程引擎的某个节点里直接写一段代码调用大模型API,拿到返回结果后解析JSON,然后决定下一个节点走哪里。这种"直连"方式在Demo阶段跑得通,但上线后几乎必然出问题,而且问题往往以三种形式出现。
第一种是超时拖垮流程。大模型API的响应时间波动很大,快的时候1-2秒,慢的时候十几秒甚至超时。如果流程引擎的节点是同步等待模型返回,一个慢请求就会把整个流程实例卡住。更糟的是,如果流程引擎用的是数据库行锁来保证状态一致性,这个锁会被一直持有,其他流程实例也跟着排队。
第二种是输出格式漂移。你今天让模型返回{"action": "approve"},它老老实实返回了。明天换个输入,它可能返回{"action": "approve", "reason": "..."},或者干脆用自然语言说"我认为应该批准"。流程引擎的解析代码一旦遇到非预期格式,要么抛异常,要么静默走错分支——后者更危险。
第三种是状态不一致。模型调用成功了,但流程引擎在写状态时失败了;或者流程引擎写成功了,但模型调用其实超时了只是客户端没正确处理。这种不一致在分布式环境下会被放大,最终导致"模型以为批了、流程以为没批"的诡异状态。
2.2 推荐的四层架构
经过多个项目的迭代,我目前比较推荐的架构是四层分离:
| 层级 | 职责 | 关键技术选型 |
|---|---|---|
| 接入层 | 接收业务请求、鉴权、限流 | API Gateway + 令牌桶 |
| 智能层 | 意图识别、实体抽取、决策建议 | 大模型 + 规则引擎 |
| 编排层 | 流程定义、状态管理、节点路由 | 状态机引擎(如Temporal、Camunda) |
| 执行层 | 具体业务动作(发通知、写库、调外部系统) | 消息队列 + Worker |
这个分层的核心思想是:智能层和执行层通过编排层解耦,智能层的输出不是"直接执行指令",而是"带置信度的建议"。编排层根据建议和预设规则决定是否执行、是否需要人工确认。
举个例子。用户提交一段自然语言:"帮我申请下周三到周五的差旅,去上海,预算大概3000。"接入层收到请求后,智能层做三件事:识别意图是"差旅申请",抽取实体(时间、地点、预算),然后输出一个结构化建议:
{ "intent": "travel_request", "confidence": 0.92, "entities": { "start_date": "2025-01-15", "end_date": "2025-01-17", "destination": "上海", "budget": 3000 }, "suggested_action": "create_travel_flow" }编排层拿到这个建议后,先检查置信度是否超过阈值(比如0.85),再检查预算是否超过某个金额需要额外审批,然后才决定是直接创建流程还是转人工确认。执行层只负责在流程节点被触发时执行具体动作,完全不关心这个节点是被模型触发的还是被人手动触发的。
2.3 为什么状态机比工作流引擎更适合AI场景
传统工作流引擎(如Activiti)的设计假设是:流程路径在定义时就基本确定,运行时只是按图走。但AI流程管理系统的特点是,部分路径是运行时动态决定的——模型可能建议走A分支,也可能建议走B分支,甚至可能建议创建一个原本不存在的节点。
这种情况下,轻量级状态机(如Temporal的Workflow)比传统BPMN引擎更合适。状态机的优势在于:状态转换是显式定义的,每次转换都可以附带条件判断和副作用;而且状态机天然支持"等待外部事件"——比如等待人工确认、等待模型异步返回——不会阻塞线程。
注意:如果你的团队已经在用Camunda这类引擎,不必强行替换。可以在引擎的Service Task里调用智能层,把模型输出作为流程变量,然后用网关节点做条件路由。关键是不要让模型调用出现在网关的条件表达式里,而是提前算好。
3. 模型选型:不是越大越好,而是越"稳"越好
3.1 流程场景对模型的真实需求
很多团队选模型时的第一反应是"用最强的",但在流程管理场景里,这个思路往往是错的。流程场景对模型的需求和聊天场景完全不同:
- 输出稳定性 > 创造力:流程需要的是可预测的结构化输出,不是天马行空的回答。一个7B的微调模型如果输出格式稳定,比一个70B的通用模型更合适。
- 延迟敏感:流程节点等待模型返回的时间直接影响用户体验。P99延迟超过5秒的模型,在交互式流程里基本不可用。
- 成本可控:流程调用是高频的,一次差旅申请可能触发3-5次模型调用。如果每次调用成本0.1元,一天1000个流程就是300-500元,一个月就是上万元。
- 数据合规:很多企业的流程数据涉及内部信息,不能随便发到外部API。
3.2 三种部署模式的取舍
目前主流的部署模式有三种,各有适用场景:
模式一:公有云API
适合快速验证和低频场景。优点是开箱即用、模型能力强;缺点是数据出域、成本随调用量线性增长、延迟不可控。如果只是做POC或者流程量很小(每天几百次),这是最省事的选择。
模式二:私有化部署开源模型
适合数据敏感、调用量大的场景。目前7B-14B级别的开源模型(如Qwen2.5-7B、Llama3.1-8B)在意图识别和实体抽取任务上,经过少量微调后可以达到接近GPT-4的水平。部署工具方面,Ollama适合快速验证,vLLM适合生产环境的高并发推理。
这里有个经验数据:在意图分类任务上,一个用2000条标注数据微调过的Qwen2.5-7B,准确率通常能到92%-95%,而GPT-4零样本大概是88%-91%。微调后的7B模型不仅更准,而且延迟从2秒降到200毫秒,成本从每次0.05元降到几乎为零(只算电费)。
模式三:混合模式
这是我最推荐的方案。高频、简单的任务(意图分类、实体抽取)用本地小模型;低频、复杂的任务(长文档理解、多轮推理)用云端大模型。比如用户提交一段自然语言申请,先用本地7B模型做意图识别和实体抽取,如果置信度低于阈值,再调用云端大模型做二次判断。
3.3 微调还是提示工程
这是被问得最多的问题。我的判断标准很简单:
- 如果任务可以用明确的规则描述清楚(比如"抽取所有日期和金额"),优先用提示工程 + 输出格式约束(如JSON Schema、Function Calling)。
- 如果任务需要领域知识(比如"判断这个报销单是否符合公司差旅政策"),且你有至少500条标注数据,考虑微调。
- 如果任务需要多步推理且规则难以枚举,先用提示工程 + Few-shot,效果不够再考虑微调。
微调的技术路线目前比较成熟的是LoRA/QLoRA,用一张24G显存的卡就能微调7B模型。数据格式建议用instruction/input/output三元组,输出部分严格遵循你期望的JSON结构。训练时注意把loss计算限制在output部分,不要让模型去学input的分布。
实操心得:微调数据里一定要包含"负样本"——即那些看起来像目标意图但实际不是的样本。比如"帮我查一下上次去上海的差旅报销到哪了",这看起来像差旅申请,实际是查询。没有负样本的微调模型,会把所有相关输入都分类成目标意图,导致流程误触发。
4. 意图路由与实体抽取:让模型输出"可执行"的结构
4.1 从自然语言到流程指令的转换链路
用户输入的自然语言和流程引擎需要的结构化指令之间,隔着三层转换:
第一层是意图识别:判断用户想干什么。是申请、查询、审批、还是取消?这一步的输出是一个分类标签。
第二层是实体抽取:从文本里提取关键参数。时间、金额、人员、地点、事由。这一步的输出是一个键值对集合。
第三层是指令映射:把意图和实体映射成流程引擎能理解的指令。比如intent=travel_request+entities={...}映射成createProcess("travel_approval", params)。
这三层可以一次性让模型输出,也可以分步做。一次性输出的优点是延迟低,缺点是错误会累积;分步做的优点是每步可校验、可兜底,缺点是延迟高。我的建议是:如果模型能力足够(7B以上微调过),一次性输出;如果用的是小模型或零样本,分步做。
4.2 输出格式约束的四种手段
让模型稳定输出JSON,有四种手段,按可靠性从低到高排列:
手段一:提示词里写"请输出JSON"。可靠性最低,模型经常会在JSON前后加解释文字。
手段二:Few-shot示例。给2-3个输入输出示例,可靠性中等。适合简单任务。
手段三:JSON Schema约束。用支持结构化输出的API(如OpenAI的response_format、vLLM的guided_json),可靠性高。这是目前最推荐的方式。
手段四:后处理 + 重试。无论用哪种方式,都要加一层后处理:用正则提取JSON块,用json.loads解析,失败则重试或降级。这是最后的兜底。
import json import re def parse_model_output(text): # 尝试直接解析 try: return json.loads(text) except json.JSONDecodeError: pass # 尝试提取JSON块 match = re.search(r'\{.*\}', text, re.DOTALL) if match: try: return json.loads(match.group()) except json.JSONDecodeError: pass # 降级:返回None,触发人工处理 return None4.3 置信度阈值与路由策略
模型输出的置信度(如果是分类任务,通常是softmax概率;如果是生成任务,可以用logprob或让模型自评)是决定路由的关键。我的经验阈值是:
- 置信度 > 0.9:直接执行,无需人工确认。
- 0.7 < 置信度 ≤ 0.9:执行但标记,异步人工复核。
- 置信度 ≤ 0.7:转人工处理,模型输出作为参考。
这个阈值不是拍脑袋定的,而是根据业务容错率反推的。如果错误执行的代价很高(比如错误批准了一笔大额付款),阈值要调高到0.95甚至0.98;如果错误执行的代价只是多走一步人工确认,阈值可以降到0.6。
注意:置信度校准是个坑。很多模型的softmax概率是"过度自信"的——它说0.95,实际准确率可能只有0.8。上线前一定要用验证集做校准,画出可靠性曲线,必要时用温度缩放(Temperature Scaling)重新校准。
5. 执行引擎:流程节点如何与模型调用解耦
5.1 异步调用与状态回写
流程引擎调用模型时,最忌讳同步阻塞。正确的做法是:流程节点触发时,向消息队列发一条"模型调用请求",然后流程实例进入"等待"状态;模型调用完成后,向流程引擎发一个"回调事件",流程实例被唤醒,继续执行。
这种异步模式的好处是:流程引擎不会因为模型慢而卡住;模型调用可以重试而不影响流程状态;多个流程实例可以并发等待,不占用线程。
具体实现上,如果用Temporal,可以用Workflow.await()等待一个信号;如果用Camunda,可以用External Task模式,模型调用方作为外部Worker,完成后调用complete接口。
5.2 幂等性与重试
模型调用可能失败(超时、限流、网络抖动),必须支持重试。但重试带来一个问题:如果第一次调用其实成功了,只是响应没收到,重试会导致重复执行。
解决方案是幂等键:每次模型调用请求带一个唯一ID(比如flowInstanceId + nodeId + attempt),模型服务端记录已处理的ID,重复请求直接返回缓存结果。流程引擎侧也要保证,同一个节点的执行结果只被消费一次。
# 伪代码:带幂等键的模型调用 def call_model_with_idempotency(request_id, payload): # 检查是否已处理 cached = redis.get(f"model_result:{request_id}") if cached: return json.loads(cached) # 调用模型 result = model_client.invoke(payload) # 缓存结果,设置过期时间 redis.setex(f"model_result:{request_id}", 3600, json.dumps(result)) return result5.3 人工介入节点的设计
AI流程管理系统里,人工介入不是"异常处理",而是流程的正常组成部分。设计时要考虑三个问题:
什么时候介入:置信度低、金额超限、涉及敏感操作(如删除、付款)、模型明确表示"不确定"。
介入时看到什么:不能只给人工一个"请处理"的空白页。要把模型的原输入、模型输出、置信度、建议动作都展示出来,人工只需要做"确认/修改/拒绝"三选一。
介入后怎么回流:人工的修改结果要作为反馈数据存下来,用于后续的模型迭代。这是持续优化的关键——没有反馈闭环的AI流程系统,准确率会一直停在初始水平。
6. 成本与延迟优化:让系统跑得久、跑得省
6.1 缓存策略
流程场景里,很多模型调用是重复的。比如"差旅申请"的意图识别,用户输入千变万化,但意图标签就那几个。可以对意图识别结果做语义缓存:把用户输入向量化,在向量库里查相似度超过0.95的历史请求,直接复用其意图标签。
实体抽取的缓存更直接:如果两个请求的文本高度相似(编辑距离小于阈值),实体大概率相同。但要注意,时间、金额这类实体可能变化,缓存时要排除这些字段。
6.2 模型分级调用
不是所有请求都需要大模型。可以设计一个分级路由:
- 第一级:规则匹配。如果用户输入命中预设关键词(如"请假"、"报销"),直接走对应流程,不调模型。
- 第二级:小模型。7B模型做意图分类,置信度高则直接执行。
- 第三级:大模型。小模型置信度低时,调用云端大模型做二次判断。
实测下来,这个分级路由能把模型调用量降低60%-70%,其中规则匹配覆盖约30%,小模型覆盖约40%,只有不到30%的请求需要大模型。
6.3 批处理与流式输出
如果流程允许异步(比如夜间批量处理报销单),可以把多个请求攒成一批,一次性发给模型。批处理能显著提高GPU利用率,降低单位成本。
对于交互式流程,如果模型输出较长(比如生成审批意见),可以用流式输出,让用户先看到部分结果,降低感知延迟。但要注意,流式输出和结构化输出(JSON)有冲突——JSON必须完整才能解析。折中方案是:先流式输出自然语言部分,最后再输出一个完整的JSON块。
7. 上线后的持续迭代:反馈闭环怎么建
7.1 反馈数据的采集
系统上线后,最重要的资产不是模型,而是反馈数据。每次人工介入、每次用户修改模型建议、每次流程走错分支,都是宝贵的标注数据。
采集时要记录:原始输入、模型输出、置信度、人工修正结果、最终执行结果。这些数据积累到一定量(比如500-1000条),就可以用来微调模型,形成"使用-反馈-微调-提升"的闭环。
7.2 A/B测试与灰度发布
模型更新不能全量上线。建议用A/B测试:新模型先处理10%的流量,对比准确率、延迟、人工介入率等指标,达标后再逐步扩大。
灰度发布时要注意:流程状态不能因为模型版本切换而混乱。同一个流程实例,要么全程用旧模型,要么全程用新模型,不能中途切换。实现上可以在流程实例创建时记录模型版本,后续节点都按这个版本调用。
7.3 监控指标
上线后要盯住几个核心指标:
| 指标 | 含义 | 健康范围 |
|---|---|---|
| 意图识别准确率 | 模型分类正确的比例 | > 90% |
| 实体抽取F1 | 实体抽取的综合指标 | > 0.85 |
| 人工介入率 | 需要人工处理的流程比例 | < 20% |
| P99延迟 | 模型调用的99分位延迟 | < 3秒 |
| 单流程成本 | 每个流程的平均模型成本 | 根据业务定 |
这些指标要接入监控大盘,设置告警。特别是人工介入率,如果突然升高,往往意味着模型效果下降或输入分布发生了变化。
8. 几个真实踩过的坑
最后分享几个我在实际项目中踩过的坑,都是文档里不会写的。
坑一:模型对"否定"不敏感。用户说"我不需要报销",模型可能还是识别成"报销申请"。解决办法是在微调数据里加入否定样本,或者在提示词里明确要求"注意否定词"。
坑二:时间实体抽取的歧义。"下周三"到底是哪一天?模型可能算错。稳妥的做法是:模型只负责抽取"下周三"这个文本,具体日期转换用规则引擎做,因为规则引擎可以拿到当前日期,计算更可靠。
坑三:多轮对话的状态丢失。用户先说"申请差旅",模型问"去哪里",用户说"上海",这时候如果每次调用都是独立的,模型就不知道"上海"是回答"去哪里"。解决办法是在流程实例里维护一个对话上下文,每次调用模型时把历史对话一起传进去。
坑四:模型输出的金额单位不一致。有时候输出"3000",有时候输出"3000元",有时候输出"3千"。后处理时要做归一化,把所有金额统一成"分"或"元"的整数。
坑五:忽略模型的"幻觉"。模型可能会编造不存在的流程节点或审批人。解决办法是在执行层做白名单校验:模型建议的节点必须在流程定义里存在,否则拒绝执行并转人工。
这些坑的共同点是:模型的能力边界需要用工程手段来兜底。不要指望模型100%正确,而是设计一个即使模型出错也不会造成严重后果的系统。这才是AI流程管理系统落地的关键。