ARTICLE DETAIL

资讯详情

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

AI应用开发平台深度复盘:Agent编排、MCP扩展与工程化落地

AI应用开发平台深度复盘:Agent编排、MCP扩展与工程化落地 这个平台名“XXL-AI”我很早之前就想写一篇复盘了。起因很直接随着Agent应用越来越多靠一个个独立脚本去调大模型接口的方式已经行不通了——模型参数散落在代码里业务方要接入一个新供应商就得改半天Agent流程稍微复杂一点就开始失控。于是我就动手做了XXL-AI这个AI应用开发平台核心围绕四件事Agent编排、多供应商接入、以MCP、SKILL、RAG三条线为主的扩展机制以及一套真正能支撑生产环境的工程化底座。这篇文章会把这四块的选型思路、落地细节和实际踩过的坑全部摊开讲适合正在做Agent应用、或者打算把散装脚本整理成平台的开发者参考。1. 为什么我会动手做XXL-AI1.1 从散装脚本到平台的必然过程最开始我们团队做AI应用和大多数团队一样是“脚本驱动”的。每个功能写一个Python文件里面直接调用OpenAI或者Anthropic的接口prompt写在常量里工具函数散落在utils目录下。这种模式在只有一两个场景时问题不大但等需求多起来痛点就会集中爆发第一是供应商耦合。业务代码里到处是openai.chat.completions.create这种调用想换一家模型供应商就不是改一个配置项的问题而是要把所有调用点翻一遍。第二是Agent流程不可控。用一个while循环去反复调模型模型说要用工具就调工具返回结果再扔回去看起来跑得通但流程一旦超过三五步就完全无法追踪出了问题只能靠print日志去猜。第三是能力复用极差。今天写了一个搜索工具明天另一个项目又要写一遍代码复制来复制去行为还不一样。我当时的判断是这个阶段必须有一个平台层把“能力”沉淀下来把“流程”显式地编排出来。XXL-AI就是这么来的。它的核心定位不是做一个类似ChatGPT的成品应用而是给内部多个业务线提供一个统一的、可扩展的AI应用开发底座。1.2 平台的核心定位与模块划分在动手之前我先把XXL-AI拆成了四个核心模块这也是这篇文章的主线Agent编排负责把“单次模型调用”升级为“可设计、可观测的智能体流程”。多供应商接入解决的是模型来源问题让上层业务只面对一套统一接口。扩展机制是平台最灵活的部分——MCP解决“工具从哪来”SKILL解决“专业能力怎么沉淀”RAG解决“知识怎么进到模型里”。工程化底座则是一切能上生产的前提包含配置管理、可观测性、测试评估、限流审计这些“不性感但绝对不能少”的能力。这个划分方式我后来复盘觉得是合理的。它把平台的能力边界切得很清楚你不会在Agent编排里去定义RAG的分块策略也不会在供应商层去做技能管理。每层只做一件事接口边界清晰了后面的演进才会顺畅。2. Agent编排让流程可控而不是全靠模型自由发挥2.1 编排模型的选型状态机、DAG还是图Agent编排最核心的问题是选择一种抽象方式来表达“流程”。我见过不少人直接用一个数组把步骤串起来或者用LangGraph这样的现成框架但实际做平台时你会发现抽象方式直接决定了后面所有功能的复杂度。我对比过三种主流方案状态机、DAG有向无环图、循环图。状态机适合步骤固定、迁移关系明确的流程比如一个客服工单待分配→处理中→已完成每一步做什么都是确定的。DAG适合流水线类任务比如先检索再生成再校验数据一层层往下流。循环图则是对Agent原生循环的建模——模型观察当前状态决定调用什么工具看到结果再继续直到任务完成。XXL-AI最终选择了“DAG为骨架、节点内部允许循环”的混合方案。为什么这么选纯粹的DAG无法表达“模型调用工具直到成功为止”这种内聚循环纯粹的自由循环又让流程失去确定性难以追踪和审计。折中下来我们把一个Agent任务描述成一个DAG每个节点是“一步原子操作”节点内部可以是一个模型调工具的小循环。这样整个流程图是确定的、可观测的同时每个节点的内部又保留了大模型的灵活性。2.2 核心循环的落地细节DAG里的每个节点本质上是这样一个循环把任务目标、可用工具列表、历史上下文一起给模型模型返回一个决策——是直接给出最终答案还是请求调用某个工具。如果是后者平台执行工具把结果附加回上下文继续下一轮直到模型给出最终答案或者达到最大轮数上限。这里有一个细节值得展开工具调用的“轮数上限”怎么定。我一开始设的是10轮觉得够多了结果在真实场景里遇到过Agent为了填一个表单反复调用工具的尴尬——一轮查一个字段查完还校验一遍10轮根本不够。后来我改成按任务动态计算先估一下这个节点可能有几步工具操作再乘以一个冗余系数。比如检索类任务预计3步工具操作就给它8轮而那种多步决策类任务预计10步以上就放开到25轮。轮数不是越大越好因为上下文会不断膨胀超过窗口之后模型的表现会明显变差。上下文的裁剪同样重要。每轮工具结果都塞进上下文对话稍微长一点就可能爆窗口。我们的做法是三级处理对每个工具结果做摘要只保留关键信息进上下文对早期轮次的消息做压缩用一个“历史摘要”代替完整内容如果上下文仍在膨胀就开启强制裁剪策略把最老的工具调用记录丢弃同时保留它的最终结论。这三级的优先级是“越靠近当前轮次的信息越优先保留”实测下来效果尚可至少没有出现因为裁剪导致模型忘了自己在干嘛的情况。2.3 多Agent协作的实战示例单Agent能解决问题但复杂任务往往需要多个Agent协同。我拿一个实际的“资料调研”场景举例这个流程我们在XXL-AI里已经跑过很多次。流程分三步。第一步由“调研Agent”负责它拿到主题后会自主决定检索哪些来源、看哪些页面产出一份带引用的资料笔记。第二步交给“写作Agent”它读取调研笔记按照目标受众和风格要求生成初稿。第三步是“审核Agent”角色是挑剔的编辑针对初稿的事实错误、逻辑漏洞、信息缺失逐条列出问题如果审核结果不合格整个流程会跳回第二步重写重写次数上限是两次。这个流程看似简单但实现时有两个关键点。第一个是上下文传递调研Agent产出的资料笔记要结构化地传下去而不是把整个对话历史丢给写作Agent。因为调研过程可能有十几轮工具调用对话历史很长直接传下去浪费token且噪声大。所以我们定义了一个“任务产出协议”每个Agent结束时必须把结果提炼成结构化数据下一个Agent只消费这个产出。第二个是审核Agent的“否决权”审核不仅可以列意见还能直接打回。这需要在编排层支持条件跳转DAG里我加了一个特殊的“验证节点”它返回FALSE时流程会跳转到之前的某个节点重新执行。3. 多供应商接入一套接口任意切换3.1 统一供应商抽象层的设计供应商接入层是整个XXL-AI里看起来最简单、实际上最容易翻车的一部分。表面上的需求很直接把OpenAI、Anthropic、Google这些厂商的接口包一层提供统一的调用入口。但一旦深入你就会发现各家的差异比想象中大得多。起初团队里有人提议直接用OpenAI的接口格式作为标准各家不都声称“兼容OpenAI格式”吗这个思路对了一大半但不够。OpenAI格式兼容得很好的是那些“兼容型”服务商而Anthropic的Messages API、Google的GenerateContent API形式和语义都有明显差异。比如一个简单的工具调用OpenAI用tools参数Anthropic用tools加tool_choice的控制逻辑Google又有自己的一套function_declarations。强行把OpenAI格式作为统一标准的结果就是适配Anthropic时要把一大坨语义映射代码塞进适配器里后期维护非常吃力。我们的最终设计是定义了三层原子能力chat对应纯对话补全、chat_with_tools对应带工具调用的对话、embed对应向量化。每层接口只保留最核心的参数然后魔改掉各种供应商的特色参数——比如温度、top_p这些就算了但像Anthropic的thinking参数、Google的candidate_count这类特色功能不走通用接口需要时用“供应商直通参数”透传。这里有一个经验不要试图把所有供应商的特性都抽象进统一层抽象层只保留“所有供应商都支持且语义一致的能力”特色能力一律透传否则抽象层会变成一个大杂烩。3.2 供应商适配与故障转移策略供应商适配层不仅要解决接口不一致还要处理行为不一致。模型返回工具调用的格式、对系统提示词的敏感度、对特殊字符的容错各家都有微妙的差别。这类问题很难在文档里发现只能靠接入了之后跑业务case去对比。我整理过一个“供应商行为差异清单”里面记录了我们踩过的具体差异比如某家模型对JSON Schema的严格模式支持不佳工具参数偶发返回非法JSON某家的内容审核更激进会直接把包含敏感词的工具参数截断还有某家的并发限制策略比较特别突发流量下会先返回限流错误而不是排队。故障转移策略是接入层另一个重头。我们采用了下沉路由策略也就是调用方无需关心具体用哪家模型接入层根据预设策略决定。策略分数值模型正常情况下按优先级走首选供应商不可用超时、5xx、限流时自动切换到备用供应商同时有失败率统计如果某家模型最近5分钟的错误率超过阈值直接把流量切走避免持续超时拖垮业务。切换代价不是零不同模型的能力有参差业务方可能要跟着微调提示词所以我们在切换时保留一份详细的切换日志方便后续复盘。成本控制也放在这一层。各家计价方式不同有的按token有的按请求数有的带阶梯计价。我们在接入层统一记录每个请求的token消耗和估算费用按月按业务线聚合。这个数据对运营决策帮助很大——比如我们发现某个内部工具场景用中型模型完全够用但默认配置却走了旗舰模型仅靠成本报表就省下了近一半的模型费用。3.3 统一访问入口与鉴权设计供应商接入层对外提供的访问入口需要先经过统一的鉴权与配额校验。内部业务线要调用模型必须先申请API Key配额按项目维度隔离。这个设计初看不必要但实际运行中非常关键——没有配额隔离的时候一个业务线的流量抖动会拖慢另一个业务线的响应。配额控制我们用的是令牌桶算法按“每分钟请求数”和“每分钟token数”双维度限制。之所以用双维度是因为有的业务场景大量短请求、有的场景单次超长生成只看请求数管不住token消耗只看token数管不住并发洪峰。双维度同时限制两个指标都超了才限流比单维度更精准。限流时返回标准错误码和Retry-After时间业务方据此做退避重试而不是无脑重试把故障放大。4. 三大扩展机制MCP、SKILL、RAG4.1 MCP给Agent接入工具的“USB-C”扩展机制里MCPModel Context Protocol是最值得细说的。你可以把MCP理解成工具接入的“USB-C接口”——以前每个工具都要为Agent专门写一套接入代码现在只要工具实现一个MCP服务端任何支持MCP的客户端都能直接用。这个标准化对Agent应用的生态意义是巨大的它把“工具长尾”变成了一个可积累的资源池。XXL-AI里对MCP的定位是“外部工具总线”。实现MCP服务端并不复杂核心是暴露一个JSON-RPC 2.0接口定义好三个能力区域工具、资源、提示词。工具是执行动作比如查询数据库、调用外部API资源是提供上下文比如读取一个文件的内容提示词是可复用的提示模板。我们的Agent循环在需要执行动作时会通过MCP客户端发现已连接服务端上注册的工具然后把工具列表和参数Schema一起塞给模型做决策。实际接入中最容易踩坑的是鉴权与安全。MCP服务端可能部署在本地进程、内网服务器或者公网暴露的工具能力各异。我们的隔离策略是所有MCP服务的启动和调用都限定在特定的运行环境里平台侧做“工具白名单”只有经过审批的工具才允许注册给Agent使用。这个审批流程不能省否则内网数据库接口一旦被注册成工具Agent又恰好被越权提示词诱导风险会非常大。历史上出现过由于工具权限过宽Agent意外执行了高危写操作的事故后来我们给所有MCP工具都打上了能力标签写操作类工具默认禁用必须单独申请开通。4.2 SKILL把专家经验固化成技能卡如果说MCP解决的是“工具从哪来”SKILL解决的就是“专业能力怎么沉淀”。一个SKILL我把它定义成一个自包含的技能包里面包含三个部分触发条件说明这个技能在什么场景下适用执行流程一个有序的步骤列表告诉Agent先做什么后做什么约束与风格规定了输出格式、必须遵守的边界、以及语气风格要求。举一个内部的知识库问答SKILL的例子。这个技能专门处理“用户问内部规章制度类问题”的场景它的执行流程会先判断问题是否涉及具体的制度条款是的话引导用户到指定模块如果问题比较模糊要反问澄清而不是直接猜测。约束部分写得比较死——必须引用来源不知道的不能编涉及敏感数据要中断回答。这些规范性要求如果没有SKILL的约束模型自由发挥时很容易跑偏。有人会问SKILL和普通prompt模板有什么区别区别在于SKILL是结构化的、可调用的程序化单元不只是几句prompt。一个SKILL可以在执行流程里调用MCP工具也可以去RAG知识库检索还可以在执行过程中触发子Agent。它是“技能行为”的封装是把一个专家的工作方法固化下来。我们在实际写SKILL时会把提示词、规则、工具调用序列打包进一个配置包并且给每个技能维护一个稳定的编码和版本号方便下游流程引用。关于“去AI味”这件事SKILL不仅能管能力还能管风格。我们在给内容生成类SKILL写“约束与风格”部分时会明确写入一些反AI味的规则不要用“在当今社会”“总的来说”这类套话避免排比句堆砌输出要有具体的场景细节而不是空泛描述。这些规则写在SKILL里比在prompt里临时加要稳定得多因为SKILL是经过反复测试后才固化的而用户临时对话里的风格要求很容易被模型忽略。4.3 RAG知识库到底怎么做得能用聊到RAG这个领域看起来入门门槛不高实际做生产级知识库时坑特别多。XXL-AI的RAG模块经历过好几个版本迭代我挑几个最关键的工程问题来讲。第一个是数据接入与解析。很多团队把RAG想成“文档丢进去就能回答”但现实中文档格式五花八门PDF有扫描版、文字版、双层版Word里有表格、页眉页脚、文本框PPT的内容是分散在形状里的。我们的解析管线针对不同格式走不同处理分支PDF先用版面分析区分正文、表格、图片表格单独走表格解析模型转成结构化数据网页内容则先做正文抽取去掉导航和广告再入库。这里有一个容易忽视的点图片要不要入库答案是分场景。在纯文本检索的RAG里图片无法直接参与检索但如果知识库里有大量截图、图表类内容就需要OCR提取文字信息一并入库否则这些内容对检索来说就是“不存在”的。第二个是分块策略。切分粒度直接决定了检索效果。切得太粗一个chunk里包含多个主题语义模糊召回靠前但噪声大切得太细语义不完整召回不准确。我们实测下来按“语义边界”而非固定窗口切分效果最好先用段落或标题做结构切分再在每个结构块里按句群语义完整度合并chunk长度控制在300到500个token之间。每个chunk还要带上来源信息、层级路径、更新时间等元数据这些元数据在后续做过滤和溯源时非常重要。第三个是检索与重排。召回不是只做一次向量检索就完事。我们的检索流程是“多路召回重排”一路用向量相似度召回最相关的chunk一路用关键词匹配BM25类算法召回精确匹配的chunk两路结果混合后送进重排序模型把相关性差的结果挤下去再截取TopK个chunk交给模型生成。多路召回解决的是向量检索对精确名词召回不强的问题——比如一个专有名词“XXL-AI”分词后向量容易跑到语义相近但名词不同的chunk上而关键词匹配能精准锁定。重排模型则负责把体验细节调优实测在同样的知识库里加了重排后回答的引文准确率明显上升。第四个是RAG的瓶颈与改进。很多人说RAG有瓶颈我理解你们说的“瓶颈”主要是两类一是“检索质量瓶颈”知识库里信息本身矛盾或过时再好的检索也会给模型错误材料二是“混用瓶颈”RAG、知识图谱、结构化知识库各有优势但大部分应用只用了向量库一种形态。我们现在的做法是把三类知识源分开管理强实体关系类知识比如人员组织架构、产品关联关系放知识图谱结构化事实类数据比如系统配置、价格表放结构化的表结构非结构化文档知识才走向量RAG。检索时按问题类型路由到不同知识源或者做多源融合才能覆盖更多场景。5. 工程化底座AI应用不能只有“能跑”5.1 可观测性每个Agent的每次调用都有迹可循AI应用的排障难度比传统应用高一个量级。传统应用出错有明确堆栈AI应用的错误则经常是“结果不对”而不是“系统崩了”这时候没有一套好的可观测性系统排查问题就像在迷宫里找路。XXL-AI的可观测性核心是“追踪树”。每一次Agent任务都会生成一个全局TraceID从这个ID能展开一棵完整的调用树哪个节点调了模型、prompt是什么、模型返回了什么、是否调用了工具、工具返回了什么、轮与轮之间上下文怎么变的。每一层调用都记录耗时、token数、费用估算。这个追踪树对排查“模型明明应该调用工具却没有调用”这类问题帮助极大——你可以逐轮回放看到模型在某个轮次的决策依据。日志系统也做了结构化设计。每条日志都有固定的字段模型任务ID、节点ID、轮次、事件类型、模型供应商、模型名、输入摘要、输出摘要。摘要不等同于全文因为token量太大存储成本高我们只保存关键轮次的完整输入输出其余以摘要形式落库。需要全文时可以通过TraceID触发定向采集把对应轮次的完整数据重新拉出来。5.2 测试与评估没有评测就谈不上迭代AI应用有个容易犯的毛病prompt或模型一换行为就变了但没人及时发现。解决这个问题必须靠“评测集”。我强烈建议所有AI应用从第一天就开始积累评测集而不是等功能上线后再补。我们的评测集按场景分文件夹组织每个用例包含输入、期望行为、检查点。检查点可以用程序化断言比如“输出必须包含指定关键词”“必须引用给定来源”也可以用大模型当裁判让一个评分模型根据规则打分。程序化断言和模型评分结合使用前者保证硬性约束不破后者评估软性质量。每次修改prompt、切换模型、调整RAG参数都必须跑一遍回归评测集对比打分结果。这个流程一开始会被团队嫌慢但坚持跑下来就能避免大量“上线后才发现效果变差”的返工。我还给评测过程加了“差异快照”评测失败时自动生成一组对比信息列出前一个版本的行为和当前版本的行为方便定位是什么改动导致的行为漂移。5.3 配置管理、限流与安全审计工程化底座还需要覆盖配置管理、限流、审计这些“不性感但绝对必要”的能力。配置管理上平台的所有prompt模板、SKILL配置、路由策略都做成了可版本化的配置而不是硬编码在代码里。这样业务方调整参数时不需要发版同时配置的变更记录留痕出问题能随时回滚。安全审计是一条不可妥协的底线。所有Agent的敏感操作都要审计包括是谁发起的任务、执行了哪些工具、工具返回了什么、模型产出了什么、有没有触发敏感词规则。我们的审计日志独立于业务日志存储定期做合规检查。同时密钥管理用了专门的密钥服务模型API Key不会以明文出现在任何配置文件和代码仓库里运行时通过密钥服务动态注入。限流与配额在底座层做统一控制。每个业务线、每个API Key都有自己的配额限制既限制调用频率也限制token量级。一旦触发限流返回标准错误码并给出建议的退避时间避免业务方无序重试。这一层还要兼顾“关键业务的优先保障”我们给核心链路打了高优先级标签在资源紧张时优先保障低优先级任务排队等待。6. 实操中踩过的坑与排查技巧6.1 常见问题速查表整理一份我们实际运行中频繁遇到的问题速查表方便对照排查。现象可能原因排查路径与解决方案Agent反复调用同一个工具不前进工具结果没有真正改变环境状态模型觉得没完成查看追踪树中工具返回内容确认是否包含模型可识别的“成功标志”必要时在工具返回里增加明确的完成状态字段模型该调用工具却不调用工具描述写得不清晰或prompt被上下文噪声淹没精简工具描述把最关键的工具行为放在描述前两行检查上下文裁剪是否把工具权限描述裁掉了长对话后模型记忆混乱上下文被裁剪或压缩过度早期关键信息丢失确认裁剪策略是否保留了任务目标摘要必要时把任务目标固定在上下文的不可裁剪区域供应商返回限流错误并发请求超过供应商配额检查配额的令牌桶设置增加退避重试在路由层临时切到备用供应商知识库检索返回无关内容分块策略不合理或未做重排检查chunk是否跨主题调整语义分块确认是否启用了重排模型生成结果引用内容不存在RAG检索结果被截断或生成时未严格遵守“只基于上下文”规则检查TopK截断是否丢失关键材料在prompt中强化“必须忠实引用禁止推断”的规则并加程序化断言校验引用来源6.2 几个让我印象深刻的故障案例第一个是“工具无限循环”事故。某个Agent在查询订单状态时工具返回了“订单处理中”的状态模型不认识这个状态以为工具调用失败开始换个参数反复查询一轮接一轮最后把供应商的配额全打爆了。后来修复方案有两个在工具返回里增加明确的“操作已完成”语义让模型能识别成功结果同时在编排层加了“相同工具相同参数连续调用超过三次就强制中断”的保护规则。第二个是“上下文在不知不觉中爆掉”的问题。有时候追踪树看起来一切正常每轮的token消耗也不算大但任务进行到中段模型响应就变得极慢甚至开始出现重复输出。查下来发现是某个外部工具返回了一个超大JSON单个工具结果就有几万个token直接挤占了整个上下文窗口。从那以后我们给所有工具结果都加了长度上限截断和字段白名单超出范围的字段自动丢弃不进入上下文。第三个是RAG“检索结果互相打架”的问题。同一个问题在知识库里存在两个版本的答案一份来自新制度文档一份来自旧版手册向量检索把两篇都召回了模型困惑地生成了一个自相矛盾的回答。解决方式是在分块时给每个chunk标注版本信息和生效时间检索时通过元数据过滤掉已失效的版本同时在prompt里明确“如果材料相互矛盾优先采用更新版本的材料”。6.3 关于MCP连接的稳定性的几个细节MCP服务端的连接稳定性是我们在使用MCP时遇到最多问题的领域。如果是本地进程型服务启动失败、崩溃退出、端口占用都是常见问题如果是通过网络连接的MCP服务断线重连、超时、协议版本不兼容也会频繁出现。我们的处理方式是给MCP接入统一做了一层“连接治理”启动时做健康检查确认工具列表可发现调用时设置超时时间防止服务端没响应时Agent一直等待断线时自动重连重连次数有限制超过限制就把该服务标记为不可用并通知平台管理员。另一个容易被忽视的坑是MCP服务端的“协议版本”兼容性。MCP协议还在演进期不同版本的SDK之间可能存在兼容问题。我们的经验是锁定SDK版本升级前必须在测试环境完整回归一遍工具调用链路而不是直接在生产环境换版本。7. 一点个人体会做完XXL-AI这个平台我最大的体会是AI应用开发走到一定规模决定效率上限的往往不是模型本身而是模型外围的工程能力。Agent编排决定了流程的上限和可控性多供应商接入决定了你是否有足够的弹性来应对成本和质量波动MCP、SKILL、RAG这三级扩展机制决定了能力边界能延伸多远工程化底座则决定了这一切能不能稳定、安全、可审计地跑在生产环境。如果让我给正在做类似平台的团队提一个建议那就是顺序很重要先打好工程化底座再去做花哨的编排和扩展机制。没有可观测性和测试集Agent编排做得再漂亮出问题的时候也无从下手。我们的前几个版本就是因为过度关注编排灵活性而忽视了工程化导致后期排障成本极高后来回头补底座反而走了不少弯路。最后分享一个平台运维阶段才知道的小技巧给所有模型调用、RAG检索、工具执行都打上统一的成本标签后你会拥有一个非常清晰的成本地图。基于这个地图去淘汰那些“大材小用”的场景、合并重复的工具调用、压缩不必要的上下文长度省下来的钱往往比你想象中多得多。这也是XXL-AI迭代到现在仍然坚持的一件事——平台不仅要让AI应用跑起来还要让它跑得明明白白。
返回列表