AI应用开发平台复盘:Agent编排、MCP与RAG的工程化落地 大概从2025年开始大家聊AI应用开发的画风明显变了。以前是你Prompt写得怎么样哪个模型更聪明现在更多是你的Agent怎么编排MCP Server接了多少SKILL包怎么设计RAG的检索准确率怎么拉上去。从单个对话到多Agent协作AI应用正在从聊天玩具走向业务系统。XXL-AI这个项目就是在这种背景下启动的——一个AI应用开发平台核心只有四件事Agent编排、多供应商接入、基于MCP SKILL RAG的扩展机制以及一套工程化底座。这篇文章是我作为主要开发者的完整复盘不写产品宣传话术重点讲清楚每一层为什么这么设计、具体怎么落地以及上线之后才暴露出来的坑。如果你正在做AI平台选型或者打算自研一套Agent底座这篇应该有参考价值。坦白说市面上类似的AI开发平台已经不少但大多数要么偏重可视化拖拽、要么偏重大模型API聚合真正把编排、工具扩展、知识检索、工程治理放在一起做扎实的很少。XXL-AI的目标不是再做一个多模型聊天套壳而是把AI应用开发里重复性最高的那部分——调度、接线、喂知识、管上线——沉淀成一套可复用的基础设施。1. 为什么说XXL-AI不是套壳平台——立项前先划三条边界1.1 平台类产品的通病界面很强编排很弱现在很多标榜AI应用开发平台的产品打开一看本质是两个东西一个模型API管理后台加一个拖拽对话界面。这种平台对问一句答一句的场景确实够用但真实的业务很少是单轮对话。我举一个很典型的例子运营同学想要把本季度所有渠道的用户反馈整理成重点问题清单按优先级发给对应负责人并在群里同步一份摘要。这个需求牵扯到读取数据源、多次LLM调用做分类和摘要、按条件路由到不同负责人、最后调用IM机器人发消息。如果平台只有对话界面这个需求就只能靠写脚本脚本里全是胶水代码换个模型就要跟着改。XXL-AI从立项第一天就把编排引擎当作核心中的核心界面反而是次要的。我们认为一个AI应用平台真正兜住的是流程怎么走、数据怎么传、失败了怎么办而不是按钮长什么样。所以平台的交互设计全部围绕编排图、节点、变量、断点来展开可视化只是辅助不是卖点。1.2 三条边界不做模型、不做成品、不做封闭生态划边界这件事比写代码重要得多。XXL-AI第一条边界是不做模型。平台不训练、不微调模型也不打包自己的专属大模型我们只做接入层和调度层。理由很简单模型的迭代速度太快一个平台如果把底座绑在某个模型上半年就会过时。第二条边界是不做面向终端用户的AI成品。XXL-AI不做聊天机器人App也不做一键生成公文这种垂直应用而是提供构建这些应用的组件和管线。这样说可能不够性感但只有想清楚我不做什么才能避免在功能上无限扩张最后变成什么都做、什么都做不好。第三条边界是不做封闭生态。所有外部工具、数据源、技能库都要通过开放协议接入特别是MCP这类已经形成事实标准的协议。这个决定让我们放弃了短期控制力换来了长期生态红利——社区里已有的MCP服务端、SKILL资产平台可以直接复用不需要用户从零开始迁入。1.3 一条主线贯穿所有设计这三条边界定下来之后内部沟通就变得特别高效。因为我们有一条判断标准任何功能只要能把它放进编排、接线、喂知识、管上线这条主线里就值得做放不进去的再炫也不做。后来整个平台抽象成一句话Agent编排决定做什么MCP解决能调用什么SKILL封装特定任务怎么做RAG提供知识从哪里来工程化底座保证跑得稳、看得清、敢上线。这篇文章后面的几个章节其实就是按这条主线逐层拆解。2. Agent编排引擎流程图只是表象状态机才是内核2.1 为什么用图执行引擎而不是链式调用我们最早的原型用的是链式调用就是最经典的先调用A再调用B最后调用C的直线写法。这种写法在Demo阶段完全没有问题但一旦业务分支多起来代码里全是循环、条件、异常处理的胶水。链式的本质是串行顺序而真实业务流程是图——分支、并行、聚合、循环甚至人类介入。所以XXL-AI的编排引擎从原型验证后就直接重构为图执行引擎把一次完整任务建模成有向图节点是操作单元边是数据流转。图执行带来的直接好处有三个第一天然支持并行比如同时检索三个数据源这类需求可以在图上表达为三个分支同时执行最后汇总第二天然支持条件路由节点的输出可以决定走哪条边第三可观测和断点调试容易做因为每个节点执行完输入输出都能记录。如果说链式调用像做菜时只能按菜谱一步步来图执行就像后厨多线并行炖汤的同时切菜、蒸饭到点拼成一桌。2.2 节点类型LLM、工具、条件、子AgentXXL-AI的编排引擎只保留了四类基础节点没有做一堆花哨的业务节点。LLM节点负责调用某个模型完成生成、分类、抽取等任务。每个LLM节点要明确prompt模板、模型参数、输出解析方式。我们要求输出必须带schema宁可让模型输出严格JSON也不要自由格式文本否则下游没法稳定消费。工具节点绑定MCP服务端或平台内置工具负责执行一次外部调用比如查订单、发消息、跑SQL。工具节点的关键是超时和错误处理一个工具挂了不能拖垮整条流程。条件节点根据上游节点的输出做路由判断把流程导到不同分支。条件节点只做一件事就是让图活起来。子Agent节点递归调用另一个编排图。这是实现多Agent协作的关键。每个Agent负责自己熟悉的那一段由编排图控制它们之间的通信与数据传递。这里有一个经验节点类型越少越好。每次往引擎里加一种特殊节点引擎的调试器、测试器、权限模型都要跟着改。用四类基础节点可以表达绝大多数业务剩下的交给SKILL去封装。2.3 一个多Agent协作的具体示例智能工单分诊纸上谈兵没意思我拿一个真实跑通的场景来拆解一个企业内部的智能工单处理流程。用户提交工单后编排图的第一步是意图识别Agent它判断工单属于账号问题订单问题还是其他并抽取工单里的关键信息。第二步根据意图路由账号问题走到知识库检索Agent从RAG里找答案订单问题则先调订单系统工具节点查询订单状态再让生成Agent组织回复。如果检索置信度不足条件节点会把工单转给人工队列同时在IM群里通知。这个例子里有三个Agent节点、两个工具节点、一个条件节点构成了一个非常典型的多Agent编排。我的体会是多Agent协作的关键不是让Agent之间互相发消息而是由编排引擎统一调度Agent本质上只做单点能力通信和数据流转全部收归图来管。这样出问题时才能像查水电图一样一眼看出是哪段流程出了问题。强调一句多Agent不是越多越好。单个模型用function calling就能完成的任务硬拆成三四个Agent只会增加延迟、token成本和调试难度。什么时候才真的需要多Agent任务里有明显不同的专业知识边界、需要独立维护和复用时才值得拆。3. 多供应商接入模型API之间的差异比大多数人想象的大3.1 表面差异地址、Key和限流多供应商听起来很简单不就是配几个API Key嘛。实际上一旦要做得稳问题就来了。不同供应商的接口地址格式不一样有的走OpenAI兼容接口有的完全自创key管理方式不同有的还要单独的project idquota和限流语义不同有的按TPM限流有的按RPM限流有的按每分钟字符数限流。XXL-AI在底层做了一个供应商适配层把每个供应商的API翻译成统一的内部协议。开发者写编排时不需要关心这个环节用的是哪个供应商的接口只需要在配置里声明模型名称和参数。平台内部用一个ProviderDescriptor来描述每个供应商的能力包括支持的模型列表、是否支持流式输出、是否支持工具调用、上下文长度、价格模型等。配置的大致形态是这样的provider: name: deepseek base_url: https://api.deepseek.com/v1 api_key_env: DEEPSEEK_API_KEY models: - model: deepseek-chat context_length: 65536 supports_tools: true supports_stream: true换供应商对上层业务来说只是把provider.name从一个值改成另一个值。这个抽象的价值在AI模型快速迭代的当下特别明显今天某模型大火明天某模型降价业务代码不需要跟着改成本全在配置层。3.2 深层差异参数语义、工具调用格式、推理模型的行为差异比接口地址更麻烦的是参数语义差异。同样叫temperature有的模型是0到1有的模型是0到2有的推理模型干脆要求固定为1你传0.7它会忽略甚至报错。max_tokens有的限制输出上限有的算总token有的模型没有这个参数而是用max_completion_tokens。如果你在业务代码里天天硬编码这些字段每接一个新供应商就是一次重构事故。差异项传统聊天模型推理模型输出字段直接给content可能包含独立思考字段temperature常用0.1~0.7多数要求固定为1输出预算max_tokens即答案长度需要预留思考token工具调用function calling的格式差异更大。有的模型会在stream事件里给出逐步的工具参数增量有的只在结束事件给完整JSON有的模型则根本不支持工具调用。如果不做抽象Agent编排里要对每个供应商写一套工具解析逻辑维护成本直接失控。我们最终把工具调用统一成平台内部的ToolCall对象不管供应商底层怎么表达引擎只认这个对象。能力不支持的模型在编排时就会被ProviderDescriptor判定为不可用或者自动换成支持工具调用的替代模型。另外推理模型越来越流行之后这类模型的行为和传统聊天模型差异很大它们会把思考过程暴露在流式输出里输出token预算也要预留更多。抽象层如果不兼容推理模型的思考字段很多功能会表现异常。这块我们也是边踩坑边补上的。3.3 路由、容灾与成本观测多供应商不只是为了比价更是为了业务稳定。我们实现了一个简单的路由策略每个任务可以声明首选模型和备选模型首选模型连续报错或触发限流时自动降级到备选更复杂的场景还可以按成本路由——在满足任务要求的供应商里选当前价格最低的一个。成本观测方面平台每一次调用都会记录输入token数、输出token数、单价和总费用最后汇总到任务级别的账单里。这样运营同学能直接看到上个月Agent工单流程花了多少钱哪个模型最费钱而不只是甩给技术一句怎么又超预算了。4. MCP扩展Agent的USB-C接口4.1 MCP是什么解决什么问题MCP的全称是Model Context Protocol是一套开放协议用来统一AI应用与外部工具、数据源之间的连接方式。很多人第一次见到这个词会疑惑它到底是软件协议还是硬件协议这里明确一下——MCP是软件层的协议和HTTP、JSON-RPC这类协议是同一个概念层级。它做的其实是老生常谈的接口标准化但在AI应用场景里它解决了很具体的痛点以前每个Agent要接一个工具就得写一套定制代码工具方也要为每个Agent框架单独适配两边全是私有的对接方式。用USB-C来类比最合适充电器、显示器、硬盘盒不需要知道笔记本内部怎么实现供电协议只要双方都遵守USB-C标准就能通。MCP在AI应用里的角色就是这样——Agent应用是宿主工具是外设MCP就是那个统一接口。MCP定义了两个角色MCP Client是发起方通常是Agent应用或编排引擎MCP Server是工具提供方。Server通过JSON-RPC暴露三类能力工具Tools、资源Resources、提示模板PromptsAgent可以在运行时自动发现这些能力再按照模型需求调用。这件事之所以值得押注是因为生态起来了。从效率工具、数据库、浏览器到游戏引擎甚至底层调试工具都开始有人提供MCP Server实现。XXL-AI选择原生支持MCP而不是发明自己的一套工具接入协议就是因为发明的协议没人接入等于没做。4.2 服务端与客户端的接入实践先看一个最简单的MCP Server长什么样。在XXL-AI里我们用Python写过一个内部订单查询工具的Server核心代码大致是这样from mcp.server import Server from mcp.types import Tool async def handle_query_order(request): order_no request.arguments[order_no] data await query_db(order_no) return {status: success, data: data} server Server(order-service) server.add_tool( Tool( namequery_order, description根据订单号查询订单状态, input_schema{ type: object, properties: { order_no: {type: string} }, required: [order_no] } ), handle_query_order, ) if __name__ __main__: server.run()然后是宿主侧怎么接入。XXL-AI使用配置文件声明MCP连接支持stdio和streamable HTTP两种传输方式。stdio适合本机工具HTTP适合远程服务。配置形如mcp_servers: - name: order_server transport: stdio command: python args: [/srv/mcp/order_server.py] env: ORDER_DB_DSN: ${ORDER_DB_DSN}引擎启动时会启动Server进程发现工具列表把工具的名字、描述、参数schema注册到模型可用的工具集合里。大模型根据用户意图和工具描述来决策要不要调用、传什么参数。写工具描述这件事被很多人忽略但它比写工具实现还重要——描述写得模糊模型就容易瞎选工具或传错参数。我们内部有一条铁律每个工具的描述必须明确说明适用场景、参数格式、返回值示例。4.3 接入真实工具时踩过的坑接入MCP不是配一下就行下面这几个坑是我们真实踩过的。第一是授权问题。MCP协议本身不管鉴权工具自己的API Key、OAuth流程都要宿主来处理。类似用Codex接Figma的MCP时授权失败这类问题本质上都是OAuth回调和token刷新没有在宿主侧做好。我们的做法是把授权状态也纳入平台统一管理第三方工具的凭证和供应商API Key一样存在密钥管理模块里运行时动态注入到环境变量而不是写死在MCP配置里。第二是长任务超时。有些工具调用要跑很久比如批量生成报表模型等不起。如果同步调用MCP Server超时流程就失败。我们后来在工具节点上增加了异步任务模式给可以异步执行的工具返回一个task_id然后引擎定时轮询任务状态完成后继续后续节点。这个模式在真实业务里非常常用。第三是上下文爆炸。MCP工具返回大结果比如一个查询返回5000行数据时如果直接把全部文本塞进模型上下文token立刻爆掉费用也高得离谱。我们的兜底方案是大返回值落地到对象存储只把摘要和引用地址给模型。模型要详细数据时再通过一个专门的读取结果工具去取。这个设计一开始没做第一次跑真实查询就被用了10万token教训很直接。5. SKILL体系把会做某件事沉淀成可复用的技能包5.1 SKILL包的结构不止是PromptSKILL这个词在AI应用圈子里越来越常出现但很多人把它理解成一个写得很好的Prompt这就窄了。XXL-AI里SKILL的定义是针对一类特定任务的完整解决方案包它包含触发条件、执行步骤、配套工具、输出校验规则和失败处理策略。我打个比方Prompt像是要放两勺盐SKILL更像是整本菜谱——不仅告诉你用多少盐还告诉你怎么腌、什么时候下锅、出锅怎么判断熟没熟。一个典型的SKILL包目录结构长这样skill-acs/ ├── SKILL.md # 技能描述、适用场景、触发条件 ├── steps/ # 执行步骤定义可引用LLM节点、工具节点 ├── prompts/ # 各步骤使用的Prompt模板 ├── schemas/ # 输入输出的JSON Schema ├── tests/ # 验证用例与通过标准 └── version.json # 版本号与兼容范围SKILL的好处是把组织里某个很会做某件事的经验从人身上剥离出来变成团队资产。比如有人特别擅长写产品需求文档把他写文档的检查清单、语气要求、结构偏好沉淀成一个SKILL其他人调用时就能有七八成的效果。那些skill编码skill插件的说法本质上就是在讨论这类技能资产的管理和分发。5.2 SKILL、MCP、RAG怎么分工这三者的分工我们内部用一个客服场景说得很清楚。组件回答的问题客服场景例子MCP能调用什么查订单、查物流、发券接口RAG知识从哪来售后政策、话术规范、产品手册SKILL这件事怎么做安抚情绪客户并判断是否补偿MCP是手RAG是大脑的藏书SKILL是肌肉记忆。要加一个新数据源改RAG要接一个新系统改MCP要调整服务策略改SKILL。三者互不干扰各自可以版本化和灰度。这个分工在实际开发中大大降低了心智负担。5.3 技能包怎么管版本、评审和灰度SKILL本质上是会出Bug的代码所以我们用代码的方式管理它。每个SKILL都纳入Git仓库有版本号变更走评审流程。我们吃过亏一开始允许大家用自由格式写SKILL结果不同人写的风格差异极大质量完全不稳定。后来强制结构化——角色设定、固定步骤、输出schema、边界说明一个都不能少质量立刻提上来了。另一个教训是一个SKILL别贪大。某次我们试图把一个全流程客户服务技能写进一个大SKILL里结果步骤超过二十步模型执行时经常在中间走偏又很难Debug。后来拆成情绪安抚订单查询补偿判定三个小SKILL用编排图串起来效果稳定很多。在XXL-AI里面SKILL负责单一能力复杂的多步流程交给编排引擎这才是两者的正确关系。6. RAG不是vector search而是一条完整的知识管线6.1 通用RAG链路拆解RAG检索增强生成是现在做知识型AI应用绕不开的东西。但很多人以为RAG 向量搜索这可能是最大的认知误区。把文档丢进向量库然后用户问一句搜一下这只能叫向量搜索Demo离生产可用的RAG还很远。生产级RAG是两条完整的管线。第一条是入库管线Ingestion采集文档、格式解析、清洗去页眉页脚去噪声、结构切分、向量化、写库。第二条是检索管线Retrieval用户问题理解与改写、混合检索、重排序、结果合成为上下文最后交给模型生成。一条链路里的任何一个环节出问题最终效果都会打折。我们见过太多项目向量库做得很好但文档解析把表格全部搞乱了导致检索到一堆乱码。6.2 切分、向量化、混合检索的实操参数先讲切分。固定长度切分最简单但效果通常一般。更好的做法是按文档结构切分优先以标题、章节为边界段落太长了再按句子和语义切。我们常用的参数是块大小控制在512到1024 token之间块与块之间保留50到150 token的重叠避免把一句话从中间切断。当然这些参数要按实际语料调没有万能值。再讲检索。纯向量检索对同义词和专有名词不敏感所以生产里一般做混合检索向量检索负责语义相似BM25关键词检索负责精确命中然后用RRFReciprocal Rank Fusion把两路结果合并。我们自己线上的经验混合检索比纯向量的命中率能提升不少尤其是在问某个接口叫什么名字这类精确问题时关键词检索几乎是刚需。顺便提一句本地部署场景。现在有不少人搜ollama 简易本地RAG知识库零基础教程说明轻量本地方案的需求很大。如果你想快速验证完全可以用ollama跑一个embedding模型加本地向量数据库数据量在几千文档以内完全够用。XXL-AI的RAG组件本身就支持这种轻量部署不强制上分布式向量库这点对中小团队很友好。6.3 知识库能存图片吗和RAG的边界网上经常有人问RAG知识库能存储图片吗这个问题背后其实是对RAG边界的困惑。严格来说传统文本RAG存不了图片图片也不会被检索到。如果文档里的图片承载了关键信息比如产品图、界面截图、流程图就需要单独处理。我们目前的实用方案是先抽取、后检索入库阶段对图片做OCR和图像描述把提取出来的文字和说明存成文本块跟原图建立关联。用户问登录页面在哪时检索命中的是图片的描述文本再由模型返回图片引用。这套方案成本低、部署简单效果对绝大多数业务够用。将来如果多模态embedding成熟可以再升级为图文联合检索但在当前阶段先把文本检索做稳更实际。再补充一个关于RAG效果瓶颈的观察当你的RAG回答效果不好时先检查入库管线再检查检索最后才怀疑模型。我们见过太多团队一上来就换大模型结果问题根本在文档切分——正文和页眉被切进同一个块检索返回的全是噪声。这种问题换什么模型都救不回来。7. 工程化底座可观测、权限控制与灰度发布7.1 每次Agent运行都能回放AI应用的Debug和传统软件不太一样。传统代码出问题错误往往是确定的可以复现AI应用出问题可能是模型这次抽风了、工具这次超时了、上下文这次被截断了是多个因素叠加的结果。所以工程化底座的第一件事就是全链路Trace。XXL-AI里每次任务执行都会生成一条完整记录走过了哪些编排节点、每个节点的输入输出、调了哪个模型哪个工具、token消耗多少、耗时多少、如果报错报在哪一步。这套Trace还支持回放把一个历史任务的入参重新跑一遍对比输出。虽然大模型的输出不可能保证完全相同但对比可以发现明显的回归比如以前能答对的问题现在答错了多半是新Prompt或者新SKILL引起的。我们内部还写了一些断言测试把应该走工具A而不是工具B输出必须包含订单号这类规则固化成自动化用例每次发布前跑一遍。7.2 多租户下的Key安全与模型授权工程化底座里最容易被忽视但也最容易出事的是权限模型。企业用AI平台不可能把主API Key直接暴露给每个员工必须在平台侧统一管理供应商密钥按用户、项目、团队维度做配额和鉴权。更细的粒度是某些模型只有特定角色能用比如付费的高阶模型只开放给核心人员某些MCP工具需要额外授权甚至要求人工二次确认后才能调用比如发送对外邮件这种高风险动作。我们的实践是密钥全部走平台的密钥管理服务运行时注入到对应容器或进程环境变量团队成员拿到的是平台生成的短时凭证不是供应商原始key。所有调用都有审计日志谁在什么时间、用哪个模型、调了哪个工具事后都能追溯。这套东西不性感但出了事它才是真正的救命稻草。7.3 Prompt和编排也是代码上线要走灰度很多团队把Prompt、SKILL、编排图当配置看待随手改随手发一出问题又回滚不了。这是AI应用线上事故的重要来源。我们把Prompt和SKILL做得跟代码同等对待版本化管理、PR评审、灰度发布。灰度方式很直接——新Prompt先让5%的流量跑通过质量阈值再扩到50%、100%。影子模式也很有用新版本在后台跑但输出不生效只和线上版本做对比攒够样本后人工评估。关于去AI味这个大家都在聊的话题其实和灰度发布也能结合起来我们允许在SKILL里定义风格校验规则比如禁止使用首先其次最后禁止使用总的来说。这些规则可以作为输出质量检查的一项在灰度期间自动打分风格分低了就不放量。这套机制比在Prompt里写一百遍不要像AI要可靠得多因为它把质量意图变成了可量化的校验逻辑。最后分享一点个人体会。做AI应用平台最大的挑战不是某一个算法而是把Agent编排、多供应商、MCP、SKILL、RAG、工程治理这几层组合在一起时还能保持一致的开发体验。每一层单独拿出来都不算颠覆真正难的是按软件工程的标准把它们做扎实让开发者不再重复制造轮子。我在XXL-AI这个项目里学到最有用的一件事是架构的边界感比功能数量更重要——克制地划清每一层该管什么、不该管什么之后所有新需求都会自动落到正确的位置。如果你正在调研或自建类似底座希望这篇复盘能帮你少踩几个坑。