拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

企业Agent上生产:MCP适配层架构设计与踩坑实录

企业Agent上生产:MCP适配层架构设计与踩坑实录

项目刚上生产那几天,我每天打开监控面板的心情都很复杂。模型调用那一侧的成功率曲线跑得很漂亮,99.7%,几乎无懈可击。但业务那边过来问的问题,十个里有八个和模型推理一点关系都没有——为什么 OA 里的审批单状态查不到?为什么 ERP 传过去的物料编码老是报格式错误?为什么 Agent 用着用着就被踢下线,还得重新登录一次?

这个项目就是我们团队做的“FDE MCP Blade”,简单说,是一个把企业 OA、ERP 等存量系统接入 AI Agent 的 MCP 适配层。今天想把这些踩过的坑、调过的参、重构过的架构,完整记录下来。这篇文章适合那些正准备把 Agent 推上生产、或者正在做企业级落地的开发者和架构师。如果你以为难点在模型调优,那看完这篇可能会改变想法。

1. 项目背景与核心问题:Agent 上生产,真正的卡点是接入

1.1 项目要解决的实际问题

企业内部系统其实没有想象中那么现代化。我们接的 OA 系统承载着审批流、公文流转、行政申请;ERP 系统则管着进销存、财务凭证、生产计划和物料主数据。业务方提出的诉求非常朴素——希望让 Agent 能直接查审批进度、查库存、创建采购申请,甚至主动提醒业务人员哪些单据卡在某个节点超过三天了。

听起来不复杂,但真正开工才发现问题藏在深处:OA 的审批单状态字段在不同模块里居然叫法不一致,ERP 的物料编码在采购模块和库存模块的校验规则竟有细微差别,更别提有些老系统的接口文档还停留在好几年前。Agent 调用这些系统的每一项能力,背后都是一整套业务逻辑和数据口径的对齐工作。

1.2 模型不是瓶颈,接口才是

我当时在团队内部反复强调一个观点:大模型的能力已经溢出,真正制约 Agent 落地的是它和外部世界之间的通路。你让模型写一首诗、总结一份文档、生成一段代码,这些能力在商用模型里已经足够稳定。但你要让它查一下“某张采购订单当前处在哪个审批节点”,它需要先知道调哪个接口、传什么参数、怎么解析结果、遇到错误怎么处理。

模型侧的准备工作反而是最顺利的。Function Calling 也好,Tool Use 也好,只要给模型清晰的工具定义和参数说明,它基本能正确选择调用。真正的困难在于:

  • 接口荒:很多老系统根本没有开放 API,或者只提供了几个残缺不全的接口,连最基本的字段说明都对不上。
  • 数据结构不标准:同一个业务对象在不同系统里名字不同、层级不同、状态枚举不同,Agent 拿到数据后经常“看不懂”。
  • 权限体系复杂:Agent 以什么身份操作业务系统?能不能越权?每一步操作怎么留审计日志?这些在传统系统里都是硬约束。
  • 网络与部署环境隔离:生产环境中 OA、ERP 常常位于不同网段,Agent 服务部署在容器平台,中间还隔着一层网络安全策略。

后来的经验总结成一句话:Agent 上生产,本质上是“模型能力 × 系统集成能力 × 稳定性”三者的乘积,而系统集成恰恰是变量最大的那部分。

2. 为什么选 MCP:给 Agent 装一个标准插座

2.1 MCP 是什么:从“一对一对接”到“统一协议”

MCP(Model Context Protocol)的出现正好解决了我在项目早期最头疼的碎片化问题。可以把它类比成 Agent 界的 USB-C 接口:以前每个硬件厂商都有自己的充电口,出门得带一堆线;USB-C 统一之后,一根线走天下。MCP 做的就是这个事——它定义了一套标准协议,让 Agent 能够通过统一的方式发现、调用外部系统提供的工具和数据资源。

MCP Server 在协议层提供三类核心能力:

  • Tools:可执行的业务函数,Agent 根据用户意图选择调用,比如“查询审批单”“创建采购订单”。
  • Resources:可读取的数据资源,用来向模型注入上下文,比如把一份业务报表的内容直接塞给模型做分析。
  • Prompts:预设的提示模板,用来规范人和模型协作的固定套路,比如“审批异常排查流程”的引导话术。

这三个概念是理解 MCP 的关键。你不需要把每个系统都做成独立协议对接,只需要让每个系统对应一个 MCP Server,Agent 通过 MCP Client 去连接它们。

2.2 对比传统 API 网关方案,MCP 的价值与代价

在做技术选型的时候,团队内部也讨论过要不要沿用传统路线——给每个系统单独写适配器,统一挂到 API 网关后面,再让 Agent 去调网关接口。这条路在工程上完全可行,但它有几个绕不开的弊端。

传统方案的最大问题是“胶水代码”太多。每接入一个新系统,就得在 Agent 侧重新写一套调用逻辑、错误处理、重试策略和参数映射。系统之间各自为政,Agent 的代码慢慢变成一个大杂烩。更麻烦的是,业务接口的变更需要同步修改 Agent 侧逻辑,两边经常对不上版本。

MCP 方案的思路是把“接入协议”标准化。每个系统对应一个独立的 MCP Server,Agent 只需要通过标准协议去发现和调用这些 Server 暴露出来的工具。新增一个系统,就是新增一个 Server,Agent 核心代码几乎不用动。

当然,它也不是银弹。MCP 解决的是“协议统一”,不解决“接口缺失”和“数据质量”,这两个硬骨头你照样得啃。但在一个多系统的企业环境里,MCP 至少把集成工作的边际成本降了下来,也把团队并行开发的效率提了上去。

3. FDE MCP Blade 的整体设计与架构拆解

3.1 FDE MCP Blade 的定位与命名由来

“FDE”是前端开发工程师(Front-End Developer)的缩写,代表这个项目的牵头视角;“Blade”是“刀片”的意思,用来比喻一组轻量、可插拔、可独立替换的适配器模块。整套方案的设计理念就是:每个业务系统对应一个独立的“刀片”,插在同一个 MCP 底座上,互不干扰。

这个命名的背后有一个实际考量。项目初期,团队尝试把 OA、ERP 的适配逻辑写在一个大的 MCP Server 里,结果不到两周就发现不行——OA 那边改一个字段映射,要重新发布整个服务,ERP 那边升级一次 SDK,OA 的线程池也跟着重启。后来我们干脆拆成独立进程,每个 Blade 只负责一个系统的接入,发布、扩缩容、故障隔离都各自独立。这个决定后来被证明是整个架构里最正确的一个。

3.2 分层架构:接入、协议、安全、运维四层

整体架构从下往上分为四层,每层职责单一,互不越界。

业务接入层是直接和 OA、ERP 打交道的部分。它负责处理各系统的 SDK、REST API、数据库只读视图,甚至在某些接口缺失的情况下,通过定时同步的方式把数据搬到中间库。这一层解决的是“能不能拿到数据、能不能执行操作”的问题,每个系统内部的复杂细节都封装在这里,不外泄。

MCP 协议层在上面对接层的每个能力点做了协议封装。一个查询库存的能力被定义成一个 Tool,一个审批单详情的数据视图被定义成一个 Resource。Tool 的入参、出参、错误码都按 MCP 规范声明清楚,让模型能准确理解每个工具的用途。协议层不感知具体的业务系统,它只做翻译和暴露。

安全统一层是生产环境绝对省不掉的一层。所有 Agent 的操作请求都必须经过身份认证、权限校验、数据脱敏和审计日志记录。这一层由统一的服务账号体系支撑,Agent 不以任何个人账号身份操作业务系统,而是使用独立的服务账号,权限范围严格限制在业务方授权的最小集。

运维支撑层解决的是稳定性问题。配置中心统一管理各 Blade 的连接参数,限流器保护下游系统不被高并发压垮,熔断器在 OA 接口异常时快速失败,监控面板实时展示每个 Tool 的调用量和错误率。生产环境没有这一层,出问题的时候就是两眼一抹黑。

3.3 关键设计决策:独立进程、Tool 命名规范和确认机制

有两个设计决策值得展开,因为它们直接影响生产环境的稳定性和 Agent 的行为准确性。

第一个决策是每个系统独立一个 MCP Server 进程。之前提到过的“刀片”思想落实到这里,就是 OA 一个 Deployment、ERP 一个 Deployment、后续接入的 CRM 再一个 Deployment。好处很直观:某个 Blade 出问题不会拖垮其他系统,新版 Blade 发布时可以做金丝雀验证。代价是机器资源多了,但对于企业级系统来说,这点成本完全可接受。

第二个决策是关于 Tool 命名的规范化和写操作的确认机制。Tool 命名要像函数命名一样严谨:查询类操作统一用query_或get_前缀,写操作统一用execute_或submit_前缀。这种命名不是为了好看,而是为了减少模型的误判——模型看到get_就知道这是只读操作,看到execute_就会更谨慎。

更关键的是所有写操作前加了一道“确认”屏障。Agent 在执行创建订单、提交审批这类不可逆操作之前,必须先调用一个预检工具确认参数,并在回复中提供操作摘要给用户确认。这个机制非常朴素,但有效避免了模型在上下文理解偏差时直接产生业务数据。

4. 核心开发过程:OA/ERP 接入的实操实录

4.1 打通 OA:认证、表单、审批流的三重考验

OA 系统的接入是第一个硬仗。我们面对的是一套运行多年的办公系统,对接过程中遇到了三个典型的深坑。

认证是第一个坑。OA 系统的登录态管理非常传统,基于 Cookie 和 Token 的服务端会话,还叠加了一层 CSRF 防护。Agent 进程内保持会话和在浏览器里操作是完全两回事:并发场景下 Token 刷新容易出现竞态,长时间运行后会话静默过期,Agent 还在以为自己有权限。我们的解决办法是给 Agent 申请独立的服务账号,不使用任何个人账号,避免权限边界问题。同时封装了一层会话管理器,统一处理登录、续期、重连,底层细节对 Agent 完全透明。

表单和自定义控件是第二个坑。OA 里大量使用自定义表单和自定义控件,让人头痛的是:界面展示的值和系统内部存储的值经常不一致。比如说日期控件在界面上显示“2025-03-18”,接口返回的是毫秒时间戳;下拉选择框显示的是“已通过”,接口存的是数字编码 3。如果我们直接把接口数据抛给模型,模型很可能按字面理解,给出错误判断。后来我们在适配层做了严格的字段清洗,把所有枚举值翻译成业务语言,并且明确标注每个字段的口径,Agent 拿到的数据必须是“人话”。

会签和非会签是第三个坑。审批流里存在会签、非会签、串行、并行、或签、与签等一堆业务概念。Agent 查询任务列表时,如果不区分这些状态,会给出完全错误的结论。比如一个会签节点有 5 个审批人,其中 3 人已通过,系统状态显示“审批中”,Agent 如果只简单读状态,可能以为整个流程卡住了,实际上只是等剩余 2 人。我们在 Tool 的描述里把类似场景的语义写清楚,同时在返回数据中附加一个“业务解释”字段,让模型理解当前状态的准确含义。

4.2 打通 ERP:主数据、事务、幂等的硬骨头

ERP 系统的接入难在另一个维度,它对数据的准确性、一致性要求极高,容错率极低。

主数据的层级和理解是最先遇到的障碍。ERP 里光是物料相关字段就有编码、名称、规格、计量单位、默认仓库、采购价格、销售价格等一大串,而且不同模块对同一物料的叫法经常不一样。采购模块说“物料编码”,库存模块说“料品编码”,财务模块还有一套“存货编码”。Agent 要准确查询,不能只看一个表,必须把主数据映射关系建立清楚,否则查出来的结果可能张冠李戴。

事务一致性是第二个难题。ERP 创建一个采购订单,往往不是一次接口调用就能完成的事情。从创建订单头、添加行项目、库存预留校验,再到生成财务凭证,整条链路跨了多个接口。Agent 两次调用之间如果出现网络抖动,就可能出现“订单头创建成功但行项目缺失”这种尴尬状态。我们的处理方式是把多步骤业务封装成一个“业务动作”,在 MCP Server 内部统一调度,减少 Agent 跨多次调用的风险。

幂等性是第三个关键点。生产环境里网络重试在所难免,但如果某个创建订单的接口被重试了两次,后果就是业务数据重复。我们为每个写操作引入了一个幂等键机制:Agent 发起创建类操作时携带一个唯一的 requestId,服务端根据这个 ID 判断请求是否已被处理过,如果处理过就直接返回原有结果,不再创建新数据。这样即使 Agent 因为超时重试,业务侧也不会产生脏数据。

4.3 MCP Server 的 Tool 设计细节

MCP Server 实现的代码层面,我整理几个可以直接复用的经验。

Tool 的 schema 声明一定要严谨。我这里说的严谨不是格式合法,而是每个参数都要写清楚用途、取值范围和常见错误示例。模型不是人,它不会“猜”参数含义,schema 写得不明确,它就会在调用时犹豫、试错、甚至编造参数。我们把物料编码参数描述写成“ERP 系统中的物料编码,字符串类型,长度 20 位以内,例如 MTL-2025-001”,效果立竿见影,错误调用率明显下降。

超时设置必须分层。MCP Server 对外的连接超时不能太短,因为 Agent 在思考;但同时内部的业务接口超时要设得短一些,避免把下游系统拖垮。我们把 MCP 层的超时设为 60 秒,业务接口超时控制在 10 秒以内,再配合异步任务机制,长耗时操作返回任务 ID,Agent 轮询任务状态。这个折中方案兼顾了用户体验和系统保护。

认证密钥不能出现在代码里。所有 Token、密码、密钥统一放在 Secret 管理服务中,进程启动时注入环境变量。这一点我踩过坑——早期为了调试方便,把密钥写在配置文件里,结果代码库被扫描工具告警,差点出事。生产环境必须走正规的密钥管理。

4.4 生产环境部署到 K8s 的配置实践

部署到 K8s 时,我们遵循了几个基本原则。

每个 Blade 对应一个 Deployment 和一个 Service。Deployment 负责 Pod 副本管理,Service 负责 MCP Server 的地址发现。ConfigMap 存放非敏感配置,比如接口地址、超时参数、日志级别;Secret 单独存放所有敏感信息。这样做的好处是配置变更不用重新构建镜像,改 ConfigMap 后滚动重启即可。

资源限制必须设置。Requests 和 Limits 不能空着,尤其是内存。MCP Server 处理大响应时内存曲线会陡增,如果没设硬限制,出现过 Pod 被 OOM Kill,然后整个 Blade 无响应的情况。我们的实践是内存 Request 512MiB、Limit 1GiB,CPU 按实际压测数据调。

就绪探针要处理 MCP 握手。K8s 的存活探针和就绪探针我们都配了,但就绪探针的检查逻辑不是简单地返回 200,而是做一次内部依赖探测——确认 OA/ERP 的连接池已经初始化完成。否则 Agent 流量打到还没完成握手的实例上,会白白占一批超时错误。

5. 生产环境常见故障与排查实录

5.1 会话超时与登录态失效的排查

上线第一周,报得最多的错误就是“工具调用似乎执行成功,但返回结果异常”。后来发现很多情况下不是接口挂了,而是 OA 的登录态在运行了几个小时后静默过期,接口返回的不是业务数据,而是一个 HTML 登录页面。Agent 拿到这个页面,把它当正常回复内容处理,又给用户复述了一遍,看起来就像“Agent 傻了”。

这个问题的根因是:MCP Server 把会话管理做得太透明,没有把认证异常和业务异常区分开。我们后面在适配层加了错误分类逻辑:认证失效返回 401 类错误码,业务逻辑异常返回业务错误码,模型根据错误码类型决定是重试还是升级给人工干预。同时增加了会话自动续期的定时任务,把过期窗口尽可能缩小。

5.2 幂等性与重复提交问题的实战记录

有一次业务方反馈,某条采购申请被创建了两次,单据号完全一样。排查下来发现是 Agent 在第一次调用时遇到了网络超时,但服务端实际已经处理成功,而 Agent 侧收到了超时信号,就自动重试了一次。又因为没有幂等机制,服务端再次创建了一条相同的记录。

这个事件之后,我们才把幂等键机制引入所有写操作。无论 Agent 调用多少次,同一 requestId 只允许对应一条业务记录。排查问题时,建议在日志中把 requestId、业务单据号、Agent 内部的调用链 TraceID 三者串联起来,哪一个环节断了,通过日志就能一眼定位。

5.3 生产环境 K8s 常见故障:网络策略、DNS、配置漂移

K8s 环境本身也有一些高频故障,值得单独列出来。

网络策略拦截是第一个。MCP Server 和 OA/ERP 系统之间经常跨 namespace 通信,如果 NetworkPolicy 配置遗漏了某个网段的放行规则,就会出现“测试环境好好的,上了生产就连不上”的诡异问题。排查套路是:先看 DNS 解析是否正常,再查连通性,最后核对网络策略,不要上来就怀疑代码。

镜像 tag 漂移是第二个。运维同学使用的镜像 tag,会存在两个环境拉取到不同版本的可能,导致行为和预期不一致。我们的规范是生产环境一律使用完整的镜像摘要或精确的版本号,不用 latest。

配置漂移是第三个。ConfigMap 改了但 Pod 没有重新加载,是最容易忽略的问题。我们后面引入了配置热更新机制,文件变化后自动触发重载,并在监控面板上标记配置版本号。

下面整理一份问题速查表,方便大家以后排查时直接对照。

现象可能原因排查思路
Agent 报工具调用超时下游接口慢 / MCP 超时设置过短检查接口调用耗时分布,调整分层超时配置
返回结果清一色是登录页OA/ERP 会话过期查看认证状态码,增加会话自动续期和错误分类
重复创建业务单据缺少幂等机制引入 requestId 幂等键,日志串联调用链
服务在 K8s 中时而通时而不通DNS / 网络策略问题依次检查 DNS 解析、连通性、NetworkPolicy
Pod 频繁重启,内存告警Limits 未设置或超大响应调整资源限制,对流式响应做截断
Agent 调用工具参数错误schema 描述不清晰重写 Tool 参数描述,补充取值示例和约束

5.4 性能与限流:老系统被 Agent 高并发打爆

最后说一个容易被忽略但极其重要的问题:Agent 的并发行为和老系统的处理能力完全不是一个量级。Agent 在多轮对话中可能同时对同一个接口发起多次调用,而老系统设计时的预期是“一个用户几秒钟操作一次”。上线第一天,我们就发现某 ERP 的查询接口被 Agent 在 10 分钟内调了上千次,直接把数据库连接池耗尽。

解决方案是双层的。进程内限流:每个 MCP Server 根据下游系统的实际承受能力设置每分钟最大调用次数,超出部分排队或快速失败。再加上业务侧缓存:把频繁读取的只读数据(物料列表、审批状态枚举)缓存 30 秒到 5 分钟不等,大幅减少对下游系统的压力。这在业务侧完全无感知,因为数据实时性要求没那么高。上了缓存之后,下游系统的负载直接降了一个数量级,Agent 的响应速度反而更快了。

还有一个细节值得分享:写操作尽量做串行化处理。同一个业务对象的写操作如果并发执行,即使有幂等键,也可能出现状态覆盖问题。我们在 Blade 内部对写操作按业务对象加锁,让同一订单的操作串行执行,从根源上避免这这类并发冲突。

收尾:一些真实的体会

在整个项目从设计到上线的过程中,我最深的体会是“模型只是入口,系统集成才是主场”。那些真正让业务方对 Agent 建立信任的时刻,不是模型回答得有多机智,而是它第一次稳定地查询出正确库存、正确审批状态、正确订单信息。Agent 落地的本质,是让 AI 变得可靠、可控、可审计——这背后是无数接口对齐、异常处理和架构取舍的积累。

最后分享一个实用的小技巧:在 Agent 正式上线前,一定要做一轮“系统接口压测”。不是测模型,而是用脚本模拟 Agent 高频调用同一批业务接口的场景,看看老系统的连接池、数据库、中间件各自能扛多高并发。很多时候,Agent 不是被模型拖垮的,而是被自家老系统的脆弱环节绊倒的。提前把这些问题暴露出来,比上线后面对业务投诉要舒服得多。

如果后续有机会,我打算把“刀片”家族继续扩展,把 CRM、MES、报表平台都接入进来,同时完善人工确认机制和审计能力。企业级 Agent 的路还很宽,系统接入的深度决定了 Agent 能走多远。希望这篇记录能给正在做类似项目的你提供一些参照,少踩几个坑。

返回列表