企业级LLM多Agent系统架构实战:从ERP集成到智能流程自动化
1. 项目概述:从ERP到LLM多Agent的架构演进
上一篇文章我们聊了聊从传统ERP系统出发,为什么要引入LLM多Agent系统,以及这个想法背后的核心驱动力。简单来说,就是ERP里那些僵化的流程、海量的非结构化数据和复杂的跨部门协作,单靠传统软件升级已经很难搞定了,我们需要一个更“智能”、更“自主”的帮手。今天这篇,我们就直接进入实战环节,聊聊我是如何把想法落地的,也就是这套多Agent系统的核心架构设计与实现思路。
如果你没看过上一篇也没关系,可以把它理解为我们需要构建一个“数字员工团队”。这个团队里有专门负责理解用户自然语言指令的“前台接待”(对话Agent),有精通ERP各个模块业务的“业务专家”(技能Agent),还有负责协调任务、监控流程的“项目经理”(编排与调度Agent)。我们的目标就是让这个团队能够7x24小时在线,自动处理从简单的数据查询到复杂的审批流转、异常预警等一系列任务。这不仅仅是接个ChatGPT API那么简单,它涉及到智能体(Agent)的职责定义、它们之间的通信机制、如何保证任务执行的可靠性与数据安全性,以及最终如何与现有ERP系统无缝集成。接下来,我就把自己在设计和实现这套系统时,踩过的坑、做过的权衡以及最终成型的架构,毫无保留地分享给你。
2. 核心架构设计:分层与解耦
设计一个复杂的多Agent系统,最忌讳的就是一开始就陷入某个具体技术或模型的细节里。我的经验是,必须先搭好骨架,明确系统的层次和每个部分的职责边界。我最终采用的是一种分层架构,自上而下分为:交互层、Agent核心层、能力层和基础设施层。这种分层的核心思想是“高内聚、低耦合”,让每一层只关心自己的事,这样无论是未来更换底层的LLM模型,还是增加新的业务技能,都能做到影响范围最小。
2.1 交互层:统一入口与上下文管理
交互层是系统对外的唯一窗口,所有用户的请求,无论是通过Web界面、企业内部IM(如钉钉/企微)、邮件还是API调用,都首先汇聚到这里。这一层的关键职责有三个:
- 请求路由与标准化:将不同渠道、不同格式的输入(如自然语言、结构化指令)统一转换成系统内部能理解的标准化请求格式。例如,用户在企业微信里说“帮我查一下上个月华东区的销售情况”,这个请求会被解析,并附带上用户的身份信息、上下文会话ID等元数据,打包成一个标准对象传递给下层。
- 上下文会话管理:这是实现连续、连贯对话的基础。系统需要为每个用户或每个对话线程维护一个上下文窗口,记录历史对话、已执行的任务结果、用户的偏好等。我在这里没有采用简单的“固定长度历史消息”拼接,而是设计了一个分层的上下文管理器:
- 短期记忆:存放当前对话轮次内的详细消息,用于保证LLM理解当前意图。
- 长期记忆:利用向量数据库(如Chroma、Milvus),将历史对话中的关键决策、事实结论进行摘要并存储,支持长期检索。当用户提到“上次我们说的那个订单”时,系统能快速找回相关记忆。
- 会话状态:记录当前多轮复杂任务进行到哪一步了,比如一个采购审批流程,当前是在“经理审核”还是“财务复核”状态。
- 响应组装与格式化:将Agent核心层返回的、可能包含多种元素(文本、结构化数据、建议操作按钮)的结果,根据请求渠道重新格式化成适合的样式返回给用户。比如,给API返回JSON,给企业微信返回图文消息。
实操心得:在交互层就做好用户身份鉴权和请求频率限制非常重要。别让未授权的请求直接冲击到后端的LLM和业务系统,这是一道安全防火墙。我最初图省事把这部分逻辑放在后面,结果在压力测试时遇到了不少麻烦。
2.2 Agent核心层:智能体的“大脑”与“协作网络”
这是整个系统的中枢神经,包含了各类智能体(Agent)以及让它们协同工作的机制。我将其主要分为三类Agent:
对话/路由Agent (Dialogue/Router Agent):这是第一个接触用户请求的“大脑”。它的任务不是直接回答问题,而是理解用户意图并做出决策。它基于LLM的强大理解能力,分析用户的请求到底属于哪个业务领域(销售、财务、库存?),复杂度如何(是简单查询还是需要多步骤执行的流程?)。然后,它决定是调用一个技能Agent单独完成,还是需要启动一个由多个Agent协作的“工作流”。你可以把它想象成一个经验丰富的客服主管,听到问题后立刻判断该派哪位专家处理,或者是否需要组建一个临时项目组。
技能/工具Agent (Skill/Tool Agent):这是领域的“专家”。每个技能Agent都专注于一个特定的业务领域,并且被赋予了安全调用该领域相关工具(API、数据库查询、业务逻辑函数)的能力。例如:
SalesDataAgent:专门处理销售数据查询、报表生成,它能调用ERP的销售数据API或直接查询数据仓库。InventoryCheckAgent:专门处理库存查询、安全库存预警,它能调用库存管理模块的接口。WorkflowAgent:专门驱动预定义的审批流程,它能更新流程状态、发送通知。 每个技能Agent都通过“工具调用”(Function Calling)的方式与外部能力对接。我们预先为它定义好它能用的“工具清单”(包括工具名称、描述、参数格式),当它认为自己需要使用时,会输出一个结构化的调用请求。
编排与调度层 (Orchestrator):这是隐形的“项目经理”。当路由Agent判定任务需要多个技能Agent协作时,编排器就登场了。它负责:
- 工作流定义与解析:将复杂的业务目标(如“完成一个新供应商注册”)分解为一系列顺序或并行的子任务(检查资质、创建基础信息、发起审批)。
- Agent调度:按照工作流定义,依次实例化并执行所需的技能Agent,将上一个Agent的输出作为下一个Agent的输入传递下去。
- 状态监控与异常处理:监控每个步骤的执行状态,如果某个Agent执行失败或超时,能触发重试或转入人工处理流程。 我最初尝试完全用LLM来做动态编排(即让一个超级Agent自己思考每一步该怎么做),发现它在复杂、长链条任务中容易“迷失”,成本高且不稳定。后来改为“预定义工作流模板 + LLM动态参数填充”的混合模式,大大提升了可靠性和效率。
2.3 能力层:工具、知识与数据
这一层是Agent们能够“动手做事”的保障。它不包含智能,只提供标准化的“手脚”和“资料库”。
工具集 (Toolkit):这是对ERP系统现有能力、第三方服务以及自定义函数的封装。每一个工具都是一个独立的函数或API接口,有明确的输入输出定义。例如:
get_sales_data(region, start_date, end_date),create_purchase_order(supplier_id, items_list),send_approval_notification(approver_id, content)。关键是要做好标准化和错误处理,确保Agent调用时能获得清晰的成功结果或明确的错误信息。知识库 (Knowledge Base):ERP系统有大量文档、产品手册、历史工单、会议纪要等非结构化数据。我使用RAG(检索增强生成)技术来利用这些知识。具体做法是:将这些文档切片、向量化后存入向量数据库。当用户问题涉及这些知识时(如“我们公司对于紧急采购有什么特殊规定?”),系统会先从向量库中检索出最相关的文档片段,然后连同问题和片段一起交给LLM生成精准的、有据可依的答案,避免LLM胡编乱造。
数据访问中间件:这是与核心ERP数据库或API网关通信的桥梁。为了保护生产数据安全,绝对禁止Agent直接、无限制地访问数据库。所有数据操作必须通过这一层封装好的、具有严格权限控制和审计日志的接口来进行。例如,
SalesDataAgent只能通过data_middleware.query_sales(...)方法来获取数据,这个方法内部会校验Agent的权限、记录查询日志,并可能对返回的数据进行脱敏处理。
2.4 基础设施层:稳定运行的基石
这一层关注的是非功能性需求,决定了系统是否健壮、可靠、可维护。
- LLM模型服务:提供统一的LLM API调用抽象。我们可能同时使用多个模型(例如,用GPT-4处理复杂的意图理解,用成本更低的国产大模型或微调模型处理简单的分类任务),这一层负责模型的路由、负载均衡、API密钥管理和调用计费。
- 向量数据库:用于存储和检索知识库的嵌入向量,是RAG能力的核心支撑。
- 消息队列 (Message Queue):用于Agent之间的异步通信。特别是对于耗时较长的任务(如生成一份复杂的月度报告),可以将任务放入队列,由后台Worker异步处理,避免阻塞用户的同步请求。我选用的是RabbitMQ,因为它的路由模式比较灵活,适合多种任务类型。
- 缓存:大量使用Redis对频繁访问且变化不频繁的数据进行缓存,例如用户会话上下文、常用的知识库检索结果、模型输出的中间结果等,能极大降低LLM调用次数和数据库压力,提升响应速度。
- 监控与日志:这是线上系统的“眼睛”。我们需要记录完整的审计日志:谁、在什么时候、问了什么、系统调用了哪些Agent和工具、结果是什么。同时,需要监控关键指标:各环节的响应延迟、LLM调用的Token消耗、任务成功率、错误类型分布等。这些数据对于优化系统性能和成本至关重要。
3. 关键技术实现细节与选型
架构画好了,接下来就是选用什么技术来实现它。这里没有银弹,我的选型是基于团队技术栈、社区活跃度、性能以及最重要的——可控性来决定的。
3.1 Agent开发框架选型:LangChain vs. 自研轻量框架
市面上最著名的Agent框架是LangChain,它提供了丰富的组件,能快速搭建原型。但在深入评估后,我决定基于Python自研一个轻量级框架。主要原因有三点:
- 过度抽象与“黑盒”风险:LangChain为了通用性,做了很多层抽象。当出现复杂bug或需要深度定制Agent的行为(比如精确控制工具调用的格式、修改推理逻辑)时,排查和修改成本很高,感觉像是在和一个“黑盒”搏斗。
- 性能开销:LangChain的某些链式调用会引入不必要的开销,对于需要低延迟、高并发的企业级应用,我们希望每一毫秒都花在刀刃上。
- 契合自身业务:我们的业务逻辑和工具集相对固定且明确,不需要LangChain那么庞大的、面向所有场景的生态。自研框架可以做得极其精简、高效,并且与公司内部的微服务治理、监控体系无缝集成。
我们的自研框架核心只包含几个部分:
AgentBase:所有Agent的基类,定义了think(思考)、act(行动)、observe(观察)的基本生命周期。Tool:工具基类,统一了注册、描述和调用接口。Orchestrator:一个简单的工作流引擎,支持YAML定义流程。Memory:上下文管理抽象,支持不同的存储后端(内存、Redis、数据库)。
这样做虽然前期投入大,但长期来看,系统的可控性、可调试性和性能都得到了保障。
3.2 工具调用(Function Calling)的标准化实践
让LLM稳定、准确地调用工具,是整个系统能否“落地”的关键。我们严格遵循了以下实践:
- 工具描述必须清晰、无歧义:给LLM的工具描述,就像给程序员写的API文档。不仅要说明这个工具是干什么的,还要用例子说明每个参数的含义和格式。例如,
get_employee_info工具,参数employee_id要说明是“工号,格式为‘E’后接6位数字,如E100001’”,而不是简单写个“员工ID”。 - 强制结构化输出:我们要求所有Agent在需要调用工具时,必须输出一个严格的JSON对象,包含
tool_name和arguments字段。我们在框架层做了校验,如果输出格式不符,会要求Agent重试或报错,避免解析失败导致流程中断。 - 工具调用结果的处理:工具执行后,无论是成功返回数据还是抛出异常,都需要将结果格式化成一段自然的语言描述,反馈给Agent进行下一步思考。例如,库存查询工具返回
{“product”: “A001”, “stock”: 150},我们会格式化成“查询完成:产品A001的当前库存为150件。”再喂给Agent。
3.3 工作流编排的实现:动态与静态的结合
对于复杂的业务流程,我采用了“静态模板为主,动态调整为辅”的编排策略。
- 静态工作流模板:我们将常见的复杂业务场景(如“采购申请至付款全流程”、“客户投诉处理流程”)抽象成预定义的工作流模板,用YAML或JSON定义。模板中明确了步骤顺序、每个步骤由哪个技能Agent执行、需要什么输入、输出传递给谁、失败后的处理策略(重试、转人工、终止)。
# 示例:采购申请流程模板 name: purchase_application_flow steps: - id: check_budget agent: FinanceAgent tool: check_department_budget inputs: [“{{dept_id}}”, “{{estimated_amount}}”] on_success: goto create_po on_failure: end_with_error(“预算不足”) - id: create_po agent: PurchaseAgent tool: create_purchase_order inputs: [“{{supplier_info}}”, “{{item_list}}”, “{{budget_result.ref}}”] ... - 动态参数注入与条件分支:模板是骨架,具体执行时的参数(如哪个部门、多少钱、哪个供应商)则由对话Agent在启动工作流时动态注入。同时,模板支持简单的条件分支(基于上一步的结果判断下一步走向),以应对一些常见的业务变体。
- LLM的辅助角色:对于无法完全预定义的、特别灵活的临时性任务,我们仍然保留了一个“动态编排Agent”。它接收一个高级目标,然后利用LLM进行任务分解和规划,生成一个临时的工作流计划并执行。但这部分请求我们会进行更严格的监控和成本控制。
3.4 记忆与上下文管理的工程化
让Agent有“记忆”,是实现高质量多轮对话和复杂任务处理的基础。我们的方案是分级记忆:
- 对话缓存(短期记忆):使用Redis存储最近N轮(比如10轮)的完整对话历史。Key是
session:{session_id},设置合理的TTL。这部分数据用于维持对话的连贯性。 - 摘要记忆(长期记忆):对于一次会话中产生的关键结论、用户做出的重要决策(例如“用户确认将采购订单的优先级设为高”),我们会用一个小模型(如GPT-3.5-Turbo)或规则对其进行摘要,然后将摘要文本向量化,存入向量数据库,并与该用户或会话ID关联。
- 记忆检索:当新对话开始时,系统不仅会加载近期的对话缓存,还会根据当前用户的问题,从向量数据库中检索相关的摘要记忆,作为背景信息插入到给LLM的提示词中。例如,用户问“那个高优先级的订单处理得怎么样了?”,系统能自动检索出之前关于“设置订单优先级”的记忆片段。
踩坑记录:一开始我把所有对话历史都存向量库,导致检索噪音很大,成本也高。后来改为“缓存近期完整历史 + 向量化存储关键摘要”的模式,效果和成本取得了很好的平衡。另外,记忆的更新和清理策略很重要,要避免存储过多过期或无关信息干扰当前决策。
4. 系统集成与数据安全考量
将这套多Agent系统接入现有ERP,是另一个充满挑战的环节。目标是在赋能业务的同时,绝不能引入新的风险。
4.1 与现有ERP的集成模式
我们采用了“API网关+适配器”的模式,而非直接连接数据库。
- 建立专属的AI能力网关:在ERP的API网关之上,我们新增了一个
AI-Gateway。所有Agent对ERP数据的请求,都必须通过这个网关。网关负责统一的身份认证(验证调用方是合法的Agent服务)、权限校验(这个Agent是否有权访问某个API)、流量控制、以及详细的审计日志记录。 - 开发数据适配器:ERP的原始API可能并不完全适合Agent调用(参数复杂、返回数据冗余)。我们为各个业务域开发了轻量的“适配器”微服务。这些适配器对内调用ERP标准API,对外则提供一套为Agent量身定制的、简洁明了的RESTful API或GraphQL接口。例如,将需要十多个参数的复杂报表接口,封装成
GET /ai/sales-summary?region=华东&period=last_month这样的简单接口。 - 事件驱动集成:除了主动查询,系统还需要响应ERP内部的事件。我们让ERP在关键业务状态变更时(如订单状态更新、库存低于阈值),向消息队列发送一个标准化的事件。我们的Agent系统订阅这些事件,从而可以触发后续的自动操作,比如库存预警Agent收到低库存事件后,自动发起采购建议。
4.2 安全与权限控制体系
安全是底线,我们构建了一个多层防御体系:
- Agent身份与最小权限:每个技能Agent在系统初始化时都会被分配一个唯一的服务身份(Service Account)和对应的API密钥。在
AI-Gateway上,为每个服务身份配置了最小必需的API访问权限。SalesDataAgent只能调用销售数据相关的只读接口,PurchaseAgent可以调用创建采购单的接口,但绝不能访问财务结算数据。 - 用户上下文传递与权限继承:当用户通过交互层发起请求时,其身份信息会贯穿整个调用链。例如,张三(销售员)问“我的业绩如何?”,这个请求最终由
SalesDataAgent处理,它调用数据接口时,AI-Gateway会校验“这个请求是否是在查询张三本人的数据?”,防止越权。 - 输入输出审查与过滤:
- 输入审查:在交互层和每个Agent处理前,对用户输入进行敏感词过滤和恶意指令检测,防止Prompt注入攻击。
- 输出审查:对Agent生成的、尤其是将要呈现给用户的最终文本,进行二次审查。确保不泄露内部敏感信息(如数据库错误详情、未脱敏的员工信息),不生成不当内容。我们使用了一个轻量级的规则引擎和关键词列表来做这件事。
- 完整的审计溯源:所有环节的日志必须串联。从用户原始请求、路由决策、每个Agent的思考过程、工具调用的具体参数和结果、到最终响应,都需要记录并关联到一个唯一的
trace_id。一旦出现问题,可以快速完整地回溯整个决策链条,定位是哪个环节出了错。
5. 部署、监控与持续迭代
系统开发完成后,如何让它稳定、高效地跑起来,并不断优化,是另一个重要课题。
5.1 部署架构与弹性伸缩
我们采用容器化部署(Docker + Kubernetes),将不同的组件拆分为独立的微服务:
ai-gateway-service: AI能力网关agent-orchestrator-service: 编排与路由服务skill-agent-xxx-service: 各个技能Agent服务(可以独立伸缩)knowledge-base-service: 知识库与RAG服务memory-service: 记忆管理服务
利用K8s的HPA(水平Pod自动伸缩)策略,根据CPU、内存使用率以及消息队列的堆积情况,自动扩缩容Agent实例。特别是在月底、季末等业务高峰时段,销售、财务相关的Agent服务可以自动扩容,以应对激增的查询请求。
5.2 全面的监控指标体系
我们建立了四个维度的监控看板:
- 业务健康度:
- 用户会话成功率:从请求到成功响应的比例。
- 任务完成率:对于多步骤工作流,成功走完全流程的比例。
- 用户满意度:通过简单的交互反馈(如“是否解决您的问题?”)收集。
- 性能与成本:
- 平均响应延迟(P50, P95, P99):区分简单查询和复杂流程。
- LLM API调用耗时与Token消耗:按模型、按Agent细分,这是成本控制的核心。
- 工具调用平均耗时:识别外部系统的性能瓶颈。
- 系统可靠性:
- 各服务实例的CPU、内存使用率。
- 消息队列的积压情况。
- 数据库连接池状态。
- 安全与合规:
- 敏感词触发告警次数。
- 权限校验失败次数。
- 异常输入模式检测。
所有指标都接入统一的监控告警平台(如Prometheus + Grafana + AlertManager),设置合理的阈值,一旦异常立即通知运维和开发人员。
5.3 持续迭代的飞轮:数据、评估、优化
多Agent系统不是一蹴而就的,需要建立一个持续改进的闭环。
- 数据收集:在用户同意的前提下,匿名化地收集对话日志、Agent的中间决策过程。这是宝贵的优化素材。
- 效果评估:我们建立了一个评估体系,包括:
- 自动评估:对于有明确答案的任务(如数据查询),用脚本比对Agent输出与标准答案。
- 人工评估:定期抽样一批对话,由业务专家从“准确性”、“有用性”、“流畅性”等多个维度打分。
- A/B测试:当对某个Agent的提示词(Prompt)或工作流进行优化后,可以切一部分流量进行A/B测试,用数据说话。
- 优化方向:
- 提示词工程:根据评估结果,不断迭代和优化各个Agent的System Prompt和Few-shot Examples,这是提升效果性价比最高的方式。
- 工具优化:如果某个工具频繁被调用失败或结果不佳,回头去优化这个工具本身的逻辑或接口。
- 工作流调整:分析失败的任务流程,看是否是流程设计不合理,是否需要增加校验步骤或异常处理分支。
- 知识库更新:定期将新的公司制度、产品文档更新到向量知识库中,确保Agent的知识不过时。
设计和实现这样一套系统,是一个庞大的工程,充满了细节上的挑战。从高层次的架构划分,到具体的技术选型,再到琐碎的安全策略和提示词调优,每一步都需要在功能、性能、成本和可控性之间做权衡。但当你看到它真正跑起来,开始自动处理那些曾经需要人工来回切换系统、复制粘贴的繁琐任务时,你会觉得所有的努力都是值得的。这套系统不是一个炫技的玩具,而是一个正在逐步融入企业业务流程,实实在在提升效率的“数字同事”。在下一篇文章里,我计划分享几个具体的、已经上线运行的业务场景案例,看看这些Agent们在实际工作中是如何大显身手的,以及我们又遇到了哪些意想不到的问题和解决方案。
