1. 从报销单到智能体:一个日常场景的AI化思考
最近在折腾AI Agent相关的项目,发现一个挺有意思的现象:很多文章都在讲“意图识别”有多重要,仿佛只要模型能准确理解用户说“我要报销”,后面的一切就水到渠成了。但现实是,识别出“报销”这个意图,恰恰只是万里长征的第一步。接下来,系统到底该做什么?是直接调用一个报销接口,还是先让用户上传发票?如果发票不清晰怎么办?流程走到一半,用户突然问“我上个月的差旅补贴标准是多少”,系统是该回答这个问题,还是继续原来的报销流程?
这些问题,让我想起了公司里那套让人又爱又恨的OA报销系统。爱的是,它好歹把流程规范了;恨的是,它死板到令人发指,一点变通都没有。而我们现在要构建的AI Agent,理想状态应该是既拥有OA系统的严谨流程,又具备一个资深财务同事的灵活应变能力。这中间的差距,就是Router(路由)、Workflow(工作流)和Agent(智能体)这三个概念需要紧密配合去填补的鸿沟。
所以,我打算抛开那些晦涩的理论,就用“员工报销”这个最普通的场景作为沙盘,来拆解一下这三者到底扮演什么角色,以及它们是如何协同工作,把一个简单的用户意图,变成一套复杂、可靠且智能的自动化动作的。你会发现,意图识别是“听清话”,而Router、Workflow和Agent的共同任务,是“办成事”。
2. Router:智能体的“前台接待”与“任务分发中心”
你可以把Router想象成公司前台那位最机灵的接待员。员工走到前台说“我要报销”(意图识别结果),这位接待员不会立刻开始处理报销单,她得先做几件事:确认你的身份(权限校验)、判断你报销的类型(差旅?办公用品?)、评估事情的紧急程度,然后把你引导到最合适的处理窗口或负责人那里。
在AI Agent的架构里,Router就是这个核心的决策与分发模块。它的输入是明确的用户意图(以及可能的上下文),输出是一个具体的“下一步动作”指令。这个指令不是直接给用户的,而是告诉系统:“现在,请启动X流程”或者“现在,请调用Y工具”。
2.1 Router的核心工作:基于意图的决策
继续用报销的例子。用户输入“帮我报销一下上周去上海的差旅费”。经过意图识别模块,我们得到了一个结构化的信息:{“意图”: “报销”, “实体”: {“类型”: “差旅”, “时间”: “上周”, “地点”: “上海”}}。
现在,Router拿到这个信息,它内部的逻辑(可能是一套规则,也可能是一个小型的决策模型)开始运转:
- 权限与策略检查:这个用户有差旅报销权限吗?公司对“上海”的差旅是否有特殊政策(如高铁需二等座)?这些是前置校验。
- 流程分支判断:差旅报销通常比市内交通报销复杂,需要关联审批单、上传多种票据(车票、住宿、餐饮)。Router据此判断,应该启动“复杂差旅报销流程”,而不是简单的“交通补贴报销流程”。
- 资源路由:决定由哪个“专家”来处理。是调用“票据识别Agent”先处理发票,还是先询问用户是否有提前提交过出差申请,以关联审批流?
Router的决策结果,可能是一个具体的Workflow模板ID,也可能是一个Agent的调用指令。它的核心价值在于“分流”和“派活”,确保用户的需求被精准地导向最适合的处理流水线。
2.2 实现Router的常见模式
在实际开发中,Router的实现并非一成不变,根据复杂度和智能程度,主要有几种模式:
- 规则引擎模式:最简单直接。预定义一系列
if-then规则。例如:IF 意图=“报销” AND 实体.类型=“差旅” THEN 启动 Workflow_ComplexTravel。这种方式稳定、可控,但缺乏灵活性,难以处理规则外或模糊的情况。 - LLM即Router模式:这是当前的热门做法。将用户请求和可用的Workflow/Agent描述(作为工具列表)一起提交给大语言模型(LLM),让LLM根据理解决定调用哪个工具。这非常灵活,能处理开放域问题。例如,用户说“我上周在上海打车花了200,怎么报?”,LLM可以理解这属于“差旅报销”下的“市内交通”子项,从而路由到正确的流程。但它的挑战在于延迟、成本和对提示词(Prompt)工程的高度依赖。
- 分层路由模式:结合上述两者。第一层用规则或分类模型做粗粒度路由(如区分“报销”、“查询”、“咨询”),第二层再用LLM或细粒度规则进行精准分发。这种模式在复杂业务系统中很常见,兼顾了效率与灵活性。
实操心得:在项目初期,从规则引擎开始往往是最稳妥的。先用硬编码规则把核心流程跑通,验证整个架构的可行性。当规则膨胀到难以维护,或者频繁出现“其他”类别时,再考虑引入LLM来增强路由的智能性。千万不要一开始就追求全LLM路由,那样调试和稳定性控制会非常痛苦。
3. Workflow:定义“怎么办事”的自动化蓝图
如果Router决定了“要启动复杂差旅报销流程”,那么Workflow就是这个流程的详细剧本和自动化执行引擎。它定义了一连串的步骤(Step)、步骤之间的顺序与依赖关系(顺序、并行、分支判断)、每个步骤具体做什么(调用一个API、运行一段代码、询问用户、等待审批),以及如何传递数据。
一个设计良好的Workflow,能将复杂的业务逻辑可视化、模块化,并且具备极强的可复用性。它让Agent的“思考”过程变得可追溯、可调试。
3.1 拆解一个报销Workflow
让我们勾勒一个简化的“差旅报销Workflow”:
- 步骤1:收集基本信息。自动填充报销人、部门,并询问或确认出差起止时间、事由。
- 步骤2:票据上传与识别。引导用户上传所有票据(车票、住宿发票、餐饮发票等)。这里会并行调用两个子流程:
- 子流程A:OCR识别Agent。提取票据上的关键信息:金额、开票日期、发票代码、销售方等。
- 子流程B:票据合规性预检Agent。检查发票真伪(连接税务接口)、检查发票类型是否合规(如餐费发票是否超过标准)。
- 步骤3:信息确认与补全。将识别出的信息以结构化表单形式展示给用户确认。如果OCR识别失败或置信度低,则提示用户手动录入。
- 步骤4:计算与汇总。根据公司差旅政策,自动计算各项补贴(住宿标准、伙食补贴),汇总报销总金额。
- 步骤5:审批流触发。根据金额和公司规定,自动生成审批单,并路由给相应的主管领导。Workflow在此处暂停,进入等待状态。
- 步骤6:结果处理。收到审批结果(同意/驳回)后:
- 若同意,自动触发财务系统付款流程,并通知用户。
- 若驳回,将驳回意见反馈给用户,Workflow可以允许用户修改后重新提交(跳回步骤1或3)。
这个Workflow中,每一个步骤都可以是一个相对独立的功能单元。Workflow引擎负责按剧本推进,管理状态,处理异常(如某个步骤失败),并决定下一步走向。
3.2 Workflow与普通代码脚本的区别
你可能会问,我用Python写一个脚本也能实现上述步骤,为什么要用Workflow?关键在于可维护性、可视化与动态性。
- 可视化与可解释性:像Dify、LangChain等框架提供的Workflow编辑器,可以用拖拽节点的方式绘制流程。这对于产品经理、业务专家参与设计至关重要,也极大降低了调试成本。你能一眼看出流程卡在哪了。
- 状态持久化与恢复:一个报销流程可能持续好几天(等待审批)。Workflow引擎能持久化当前执行状态,服务器重启后也能从断点恢复。自己用数据库实现状态机非常繁琐。
- 灵活编排与复用:
票据识别Agent和合规预检Agent可以被多个不同的Workflow复用(比如业务招待费报销也会用到)。在Workflow中,它们就像乐高积木,可以灵活组合。 - 异常处理与回退:Workflow引擎通常内置了重试、超时、错误处理等机制。当OCR服务调用失败时,可以配置自动重试3次,若仍失败则转入“人工处理”分支。
踩坑记录:早期我们曾用代码硬编码流程,当业务规则变动(如审批层级调整)时,需要开发人员修改代码、测试、发布。而将规则抽象到Workflow配置后,业务人员可以在界面上直接调整审批节点,实时生效。这深刻说明了Workflow的核心价值:将易变的业务逻辑从稳定的执行引擎中分离出来。
4. Agent:执行具体任务的“专家”与“协调员”
现在,我们来聚焦Workflow中的各个步骤。当Workflow执行到“步骤2:票据识别”时,它发出指令:“调用OCR识别Agent,处理这张发票图片”。那么,Agent在这里登场了。
Agent,在此语境下,不是一个宏观的大概念,而是一个具备特定能力、能围绕一个目标执行一系列动作(包括使用工具、调用API、进行推理)的实体。它是Workflow这个“大剧本”中,具体演好某一场戏的“演员”或“专家”。
4.1 Agent的构成:思维、工具与记忆
一个功能完整的Agent通常包含几个核心部分:
- 规划与推理(Think):这是Agent的“大脑”,通常由LLM驱动。它接收目标(“识别这张发票上的所有关键信息”)和上下文(图片、历史记录),然后规划出步骤。例如,它可能会想:“这是一张增值税发票。我应该先定位表格区域,然后分别提取购买方、销售方、金额、税额、开票日期等信息。”
- 工具使用(Act):Agent自己不会OCR识别,但它可以调用工具。它的大脑(LLM)决定调用哪个工具(如
ocr_extract_text工具),并生成正确的调用参数(图片二进制流)。工具执行后,将结果(识别出的文本)返回给Agent。 - 记忆(Memory):Agent可能有短期记忆(本次对话的上下文)和长期记忆(用户偏好、历史票据格式)。例如,在连续处理同一个用户的发票时,Agent可以记住“这个用户的发票通常拍摄光线较暗”,从而在调用OCR工具时附加“增强对比度”的参数。
在我们的报销场景中,可能存在多种Agent:
- 票据识别Agent:专精于解析各类发票、车票的版式,调用OCR服务并做后处理。
- 合规检查Agent:熟知公司财务制度,能判断发票类型是否合规、金额是否超标。
- 审批路由Agent:根据报销金额、部门、项目类型,动态决定审批流路径。
- 用户交互Agent:负责以自然语言与用户沟通,收集缺失信息,澄清模糊点。
4.2 Agent与Workflow的边界
这是最容易混淆的地方。两者的关系可以概括为:Workflow是骨架和神经,定义了流程的宏观步骤与流向;Agent是肌肉和器官,在微观节点上完成具体的、智能的任务。
- Workflow负责“流程逻辑”:先A后B,如果C成立则做D,否则做E。它关注步骤的顺序、并行、选择。
- Agent负责“任务智能”:在“做D”这个节点上,如何利用LLM和工具,智能地完成D。它关注的是在一个目标下的推理与执行。
例如,Workflow规定“在信息确认步骤,如果用户对识别结果有异议,则转入人工客服”。这是一个逻辑判断。而具体执行“信息确认”这个步骤的Agent,则需要智能地生成确认话术、理解用户的修改意见、并结构化地更新数据。
核心体会:不要试图构建一个“全能Agent”去处理整个报销流程。那会变得极其复杂且脆弱。正确的做法是遵循“单一职责原则”,设计多个小而专的Agent,每个只做好一件事。然后,用一个坚实的Workflow把它们像珍珠一样串起来。Router是引线针,Workflow是串线,而Agent是一颗颗珍珠。
5. 三者协同:一次完整的报销Agent之旅
现在,让我们把Router、Workflow和Agent串起来,看一个动态的、完整的交互过程。假设用户向一个集成了这些能力的智能助手发起请求。
场景:员工小陈在聊天窗口输入:“我刚出差回来,报销一下杭州的会议费用,这是发票。”
第1步:意图识别(略,假设已完成)系统识别出:{意图:“报销”, 实体:{“类型”:“差旅/会议”, “地点”:“杭州”}, “附件”:[发票图片]}。
第2步:Router决策Router收到上述结构化信息。其内部逻辑判断:
- 有附件(发票),且意图是报销 -> 属于“主动触发报销流程”。
- 实体类型包含“会议” -> 可能涉及会议费报销,流程与普通差旅略有不同(例如可能需要会议通知作为附件)。
- 决策结果:启动
Workflow_ConferenceTravelReimbursement(会议差旅报销工作流),并将用户输入的附件作为初始参数传入。
第3步:Workflow引擎启动Workflow引擎加载ConferenceTravelReimbursement模板,创建了一个新的流程实例,状态为“进行中”。它开始执行第一个节点。
第4步:Workflow与Agent的循环交互
- 节点1(执行Agent:票据识别Agent):Workflow将发票图片交给“票据识别Agent”。该Agent调用OCR工具,识别出这是一张“会议费”发票,金额4800元,并提取了销售方(某酒店)等信息。它将结构化结果返回给Workflow。
- 节点2(执行Agent:合规检查Agent):Workflow将识别结果和用户部门信息交给“合规检查Agent”。该Agent查阅内部规则库(或通过LLM推理),发现“单次会议费超过3000元需附会议通知及预算审批单”。它生成一个检查结论:“缺少必要附件,需补全”。
- 节点3(人工任务/用户交互):Workflow根据上一个节点的结论,进入一个“用户交互”节点。它通过前端界面或聊天框,向用户小陈发送消息:“检测到您的会议费报销金额为4800元,根据规定,需要您补充上传本次会议的正式通知和预算审批单作为附件,请上传。”
- 节点4(等待与判断):Workflow进入等待状态。直到用户上传了新的文件。
- 节点5(执行Agent:文档审核Agent):用户上传了新文件。Workflow触发“文档审核Agent”,判断上传的文件是否为有效的会议通知和审批单(可通过文本分析或格式判断)。审核通过。
- 节点6(信息汇总与确认):Workflow调用一个“表单生成Agent”,将所有信息(发票信息、补充附件信息、报销人信息)汇总成一个清晰的预览表单,发送给用户最终确认。
- 节点7(审批路由):用户确认后,Workflow调用“审批路由Agent”。该Agent根据金额(4800元)、部门、项目编号,计算出需要经过“部门经理”和“财务部”两级审批。Workflow随即向这两个审批人发送通知。
- 节点8(等待审批):流程暂停,等待审批结果。
- 节点9(后续处理):审批全部通过后,Workflow触发连接财务系统的接口,完成付款,并通知用户小陈报销已完成。
在整个过程中,Router只工作了一次(最开始),它的任务是把请求送到正确的Workflow入口。Workflow是总指挥,它严格按预设的剧本(可配置)推进,管理全局状态和数据流。多个Agent是特种兵,在Workflow的调度下,分别在OCR识别、规则审查、交互沟通、审批决策等环节发挥其专项智能。
6. 架构选型与实战中的关键抉择
理解了概念和流程,当我们要亲手搭建这样一个系统时,会面临一系列技术和架构上的选择。这些选择没有绝对的对错,只有是否适合当前的场景、团队和资源。
6.1 中心化编排 vs. 去中心化自治
这是架构哲学上的一个根本分歧。
- 中心化编排(Orchestration):这正是我们上面描述的模式。一个强大的中心化Workflow引擎(如Camunda、Airflow,或Dify、LangGraph的Workflow模块)充当大脑,它显式地定义每一步,严格调度和监控每一个Agent(作为被调用的服务)。优点是流程清晰、可控、易调试、状态一致性好。缺点是中心引擎可能成为性能和单点故障的瓶颈,而且流程设计需要 upfront,灵活性稍差。
- 去中心化自治(Choreography):没有中央指挥。每个Agent都相对独立,它们通过发布/订阅消息(如使用消息队列RabbitMQ、Kafka)或事件驱动来进行协作。例如,“票据识别Agent”完成后,会向一个“事件总线”发布一个“票据识别完成事件”,并携带数据。“合规检查Agent”订阅了这个事件,就会自动被触发执行。优点是系统解耦、扩展性强、单个Agent故障不影响全局。缺点是整体流程难以监控和追溯,出现异常时调试就像破案,数据一致性保障更复杂。
选型建议:对于报销这类强流程、强事务、对顺序和状态一致性要求高的业务,强烈建议从中心化编排开始。它的可控性在业务初期至关重要。当系统极度复杂,且不同模块由不同团队维护,对弹性扩展要求极高时,可以再考虑将部分非核心链路改为事件驱动的自治模式。
6.2 工具层:如何为Agent赋能
Agent的强大与否,很大程度上取决于它能使用的“工具”(Tools)库是否丰富和可靠。工具本质上是将外部能力(数据、功能)封装成Agent可以理解和调用的接口。
- 内部API封装:这是最常见的工具来源。将公司的财务系统查询接口、OCR服务接口、审批系统推送接口等,统统封装成工具。例如
query_budget(project_id)、submit_approval_form(data)。 - 代码执行:允许Agent在安全沙箱中执行一段代码(如Python)来处理数据。例如,写一个工具函数来重新计算复杂的差旅补贴。
- 网络搜索:为Agent接入搜索引擎工具,使其能获取实时信息。例如,用户问“去上海的差旅补助标准最近有调整吗?”,Agent可以调用搜索工具来获取最新政策。
- 自定义工具开发:对于特定需求,需要专门开发。例如,开发一个“发票连号检测工具”,通过分析多张发票的号码,判断是否存在风险。
工具设计的黄金法则:工具函数应该保持单一职责、接口清晰、健壮性强。输入输出尽量使用结构化数据(JSON Schema)。因为LLM在理解和使用工具时,依赖于你提供的工具描述。一个描述清晰、功能纯粹的工具,会被Agent更准确、更可靠地调用。
6.3 记忆与上下文管理:让Agent“记得事”
在长时间的、多步骤的交互中(比如报销流程可能断断续续持续几天),记忆能力至关重要。记忆分为几个层次:
- 对话记忆(Conversation Memory):记住当前会话中已发生的历史。例如,用户之前说过“发票在附件里”,Agent在后续步骤中就不应再重复询问。实现上,通常是将整个对话历史作为上下文,在每次调用LLM时传入。需要注意上下文长度限制,必要时需做摘要压缩。
- 工作流状态记忆(Workflow State):这是由Workflow引擎负责的。它持久化存储整个流程的当前状态、各步骤的输入输出数据。这是保证流程断点续传的基础。
- 长期记忆(Long-term Memory):存储在向量数据库或其他数据库中的,跨越本次会话的知识。例如,用户小陈的历史报销偏好(他经常忘记附审批单)、公司的历史报销规则版本等。当Agent需要时,可以通过检索增强生成(RAG)技术,从长期记忆中查询相关信息。
在报销场景中,Workflow状态记忆是骨架,对话记忆是血肉,长期记忆是经验。三者结合,才能让智能体表现出连贯性和个性化。
7. 避坑指南:从理想设计到稳定上线
理论很美好,但现实很骨感。下面分享几个从零构建这类系统时,几乎一定会遇到的“坑”,以及我们的应对思路。
7.1 Router的“意图漂移”与“路由黑洞”
问题:用户表达的不确定性,可能导致Router做出错误决策。比如用户说“处理一下我的报销”,他的意图可能是“提交新报销”,也可能是“查询报销进度”。如果Router错误地将其路由到“新建报销Workflow”,用户会感到困惑。更糟糕的是,如果用户的请求完全不在你预设的意图范围内,Router可能将其丢进一个默认的、无意义的流程,形成“黑洞”。
解决方案:
- 设置明确的确认环节:对于Router置信度不高的决策,不要直接启动一个长流程。可以让一个轻量级的“确认Agent”先与用户做一轮简短确认。例如:“您是想提交新的报销申请,还是查询已有申请的进度?”
- 设计“优雅降级”路径:当Router完全无法处理时,应有一个兜底策略。例如,路由到一个“人工客服转接”流程,或者一个通用的“问答Agent”,让它尝试直接回答用户的问题,而不是强行进入某个业务闭环。
- 持续收集反馈数据:记录所有Router的决策日志和后续的用户交互满意度。用这些数据持续优化你的路由规则或微调Router所用的LLM。
7.2 Workflow的“状态爆炸”与“异常处理泥潭”
问题:一个复杂的报销Workflow可能包含几十个节点,每个节点有多种输出(成功、失败、超时)。组合起来,流程的状态空间会爆炸式增长。如果每个异常分支都需要手动设计处理逻辑,Workflow会变得极其臃肿和难以维护。
解决方案:
- 分层设计Workflow:不要设计一个巨无霸Workflow。将通用的、可复用的子流程(如“票据识别与验真”、“审批流触发”)抽象成独立的子Workflow。主Workflow像调用函数一样调用它们。这能大幅降低单个Workflow的复杂度。
- 实现全局异常处理器:在Workflow引擎层面,配置全局的异常处理策略。例如,任何节点调用外部服务超时,都统一重试3次;任何未捕获的异常,都统一跳转到一个“人工处理”节点,并发送告警。避免在每个节点都写重复的异常处理逻辑。
- 使用 Saga 模式处理分布式事务:如果Workflow涉及多个需要保证一致性的外部系统操作(如扣减预算、生成财务凭证),考虑使用Saga模式。它将一个长事务拆分为一系列本地事务,每个事务都有对应的补偿操作。如果流程失败,会按顺序执行补偿操作进行回滚,避免数据不一致。
7.3 Agent的“幻觉调用”与“工具可靠性”
问题:LLM驱动的Agent在决定调用工具时,可能会产生“幻觉”,即调用一个不存在的工具,或生成完全不合法的参数格式。此外,工具本身(如一个第三方OCR API)可能不稳定,偶尔会失败或返回错误数据。
解决方案:
- 严格的工具描述与参数校验:为每个工具提供极其清晰、格式规范的描述和参数JSON Schema。在Agent调用工具前,增加一个“参数校验”层,确保参数类型、格式、范围符合要求,拦截明显错误的调用。
- 工具调用的重试与熔断机制:将工具调用封装在具有重试、超时和熔断逻辑的客户端内。例如,一个OCR工具调用失败,自动重试2次;如果短时间内失败率过高,则熔断一段时间,避免雪崩效应,并快速失败,让Workflow进入异常处理分支。
- 引入“验证Agent”:对于关键操作,可以采用“执行-验证”双Agent模式。例如,“票据识别Agent”提取信息后,由一个轻量的“数据校验Agent”快速检查提取出的金额、日期等字段是否在合理范围内(如金额是否为数字、日期是否非未来),及时发现明显错误。
构建一个能真正处理复杂任务的AI Agent系统,是一个系统工程。它要求我们对业务逻辑有深度的抽象(Workflow),对智能决策有合理的规划(Router),对原子能力有扎实的封装(Agent & Tools)。从报销这样一个看似简单的场景切入,我们恰恰能看清这些组件如何各司其职又紧密协作。记住,不要指望用一个“超级AI”解决所有问题,而是用工程化的思维,构建一个由多个“专业AI”和“自动化流程”组成的、可靠协作的系统。这条路没有捷径,但每一步都踩在实处。