ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

XXL-AI平台拆解:Agent编排、多供应商与MCP+SKILL+RAG工程化实践

2026/10/7 6:27:15 拓冰建站 浏览量
XXL-AI平台拆解:Agent编排、多供应商与MCP+SKILL+RAG工程化实践 1. 从标题拆解一个AI应用开发平台到底该长什么样第一次看到“XXL-AI”这个命名的时候我脑子里第一反应是又是一个套壳的聊天界面但把标题后半段读完——Agent编排、多供应商、「MCP SKILL RAG」扩展、工程化底座——这四个词摆在一起基本就能判断出这不是玩具项目而是一套想认真解决“AI应用怎么从Demo走到生产”的底层框架。我做过几个把大模型接进业务系统的项目最深的体会是模型能力本身不是瓶颈真正让人头疼的是编排、扩展和工程化这三件事。你让一个Agent去查数据库、调接口、读文档、再根据结果决定下一步这套流程如果没有一个统一的编排层代码会迅速变成一团意大利面。而多供应商这件事更现实——今天用这家明天那家降价了或者限流了你总不能在业务代码里到处改API调用吧。所以这篇博文我想干的事是把XXL-AI这个标题背后的东西彻底拆开讲清楚。它是什么、解决什么问题、核心的四个模块各自承担什么职责、怎么落地、踩过哪些坑。适合正在做AI应用开发、被Agent编排和多模型接入折磨过的工程师也适合想搭一套自己AI中台的团队参考。哪怕你只是想搞明白MCP、SKILL、RAG这几个热词到底怎么串起来看完应该也能有个清晰的图景。我尽量不写成产品说明书而是按一个实际搭过类似系统的人的视角把设计取舍、参数选择、实操细节和踩坑经验都摊开讲。有些地方我会给出具体的配置和代码片段有些地方会讲清楚“为什么这么设计而不是那么设计”。你完全可以拿着这套思路去复现一个自己的版本。2. 整体架构设计与核心思路拆解2.1 为什么要把Agent编排放在最核心的位置Agent编排这个词这两年快被说烂了但真正落地的时候很多人对它的理解还停留在“让模型自己决定调用哪个工具”。这其实只是最浅的一层。真正的编排要解决的是多个Agent之间怎么协作、状态怎么传递、失败怎么重试、超时怎么处理、人类怎么介入。我见过太多项目一开始就是一个while循环模型输出一个动作代码执行把结果塞回去再循环。跑通一个demo没问题但一旦要处理复杂任务比如“先检索知识库再根据检索结果决定调用哪个外部接口接口返回后还要做一轮校验校验不过要回退到上一步换个策略”这个while循环就会爆炸。XXL-AI把Agent编排作为核心我理解它的设计意图是提供一个声明式的编排层。你可以用配置或者DSL来描述一个Agent的工作流而不是用一堆if-else硬编码。这样做的好处是流程可视、可改、可复用。比如一个“客服工单处理”的Agent它的流程可能是意图识别 - 知识库检索 - 判断是否需要人工 - 生成回复 - 记录日志。这套流程用编排层描述出来换一个业务场景改几个节点就能复用。从工程角度看编排层还需要处理几个关键问题。第一是上下文管理多轮对话和多次工具调用的历史怎么裁剪、怎么压缩直接决定了token成本和响应质量。第二是并发控制有些步骤可以并行比如同时查三个数据源编排层要能表达这种并行关系。第三是错误恢复某个工具调用失败了是重试、跳过还是走降级分支这些策略应该在编排层统一配置而不是散落在业务代码里。2.2 多供应商抽象层的价值与设计难点多供应商这件事表面上看就是封装几个不同的API客户端做个统一接口。但实际做起来坑比想象的多。首先是协议差异。不同供应商的API在消息格式、工具调用Function Calling的返回结构、流式输出的分片方式上都不一样。有的把工具调用放在单独的字段里有的混在content里有的流式返回是SSE有的是WebSocket。抽象层要做的第一件事就是把这些差异抹平对上暴露统一的接口。其次是能力差异。不是所有模型都支持工具调用不是所有模型都支持长上下文不是所有模型都支持结构化输出。抽象层需要维护一个能力矩阵编排层在决定用哪个模型的时候要能根据任务需求筛选。比如一个需要调用外部工具的任务就不能路由到不支持Function Calling的模型上。第三是成本与限流。不同供应商的价格差异很大同一个供应商不同模型的价差也很大。一个成熟的多供应商层应该支持按任务复杂度路由到不同价位的模型同时要处理各家的速率限制。我自己的做法是给每个供应商维护一个令牌桶请求进来先过桶超了就排队或者降级到备用供应商。XXL-AI把多供应商作为基础能力意味着上层应用不需要关心底层用的是哪家。这对业务连续性很重要——某家服务抖动了切到另一家业务代码一行不用改。这也是我一直建议团队在做AI应用时优先考虑的事情不要把模型调用写死在业务逻辑里。2.3 MCP、SKILL、RAG三者如何协同扩展能力边界这三个词放在一起其实是三种不同维度的扩展方式理解它们的区别和联系很关键。MCPModel Context Protocol解决的是“模型怎么和外部世界通信”的问题。它定义了一套标准协议让模型可以发现和调用外部工具、读取外部资源。你可以把它理解成AI应用的USB接口——只要符合这个协议工具就能被即插即用。MCP的价值在于标准化以前每接一个工具就要写一套适配代码现在只要工具实现了MCP Server任何支持MCP的客户端都能直接用。SKILL解决的是“特定任务怎么做”的问题。它更像是一种封装好的能力单元把某个领域的知识、流程、工具组合打包成一个可复用的技能。比如一个“合同审查”的SKILL内部可能包含了法律知识库检索、条款比对、风险标注等一系列操作对外只暴露一个简单的调用接口。SKILL和MCP的关系是SKILL可以基于MCP来调用外部工具但SKILL本身更偏向业务逻辑的封装。RAG解决的是“模型怎么获取私有知识”的问题。它通过检索增强生成的方式把外部知识库的内容注入到模型的上下文中。RAG的核心挑战在于检索质量和上下文组织——检索不准模型就会胡编上下文塞太多token成本飙升还影响效果。这三者在XXL-AI里的协同关系我的理解是这样的MCP提供工具接入的标准通道SKILL提供业务能力的封装方式RAG提供知识注入的机制。一个完整的Agent任务可能是先用RAG检索相关知识然后根据知识决定调用哪个SKILLSKILL内部再通过MCP调用具体的外部工具。这套组合拳打下来Agent的能力边界就被大大扩展了。2.4 工程化底座为什么是决定成败的关键前面三个都是能力层面的东西但真正决定一个AI应用能不能上生产的是工程化底座。我见过太多项目demo惊艳一上生产就各种问题响应慢、成本失控、输出不稳定、出了问题没法排查。工程化底座要解决的核心问题包括可观测性每次调用的输入输出、耗时、token消耗都要有记录、可测试性Agent的行为怎么回归测试、可配置性prompt、模型参数、路由策略能不能不改代码就调整、可扩展性新增一个工具或模型要改多少地方。XXL-AI把工程化底座作为四大支柱之一说明设计者是有生产经验的。一个没有工程化底座的AI平台本质上就是个高级demo。而有了底座你才能做A/B测试、才能做灰度发布、才能做成本优化、才能做故障排查。3. 核心模块细节解析与实操要点3.1 Agent编排引擎的节点设计与状态管理编排引擎的核心是节点Node和边Edge的设计。节点代表一个操作单元边代表流转关系。常见的节点类型包括LLM调用节点、工具调用节点、条件判断节点、循环节点、人工介入节点、子流程节点。我在设计类似系统时最关注的是状态管理。每个节点执行完会产生输出这个输出要能被后续节点访问。状态的结构设计直接影响编排的灵活性。我的做法是维护一个全局的Context对象所有节点的输出都挂在这个对象上用命名空间区分。比如context.llm_output、context.tool_result、context.rag_docs。但这里有个坑状态对象会随着流程推进越来越大如果每个节点都把完整历史塞进prompttoken会爆炸。所以需要一个状态裁剪策略。我的经验是给每个节点配置它需要读取的状态字段只把这些字段注入prompt而不是把整个Context都塞进去。另一个关键点是条件判断。编排引擎要支持基于状态的条件分支比如“如果检索到的文档数量为0走兜底回复分支”。这个条件表达式最好用简单的DSL描述而不是让用户写代码。比如{{rag_docs.length}} 0这种模板语法既灵活又安全。# 一个编排流程的示例配置 nodes: - id: intent type: llm model: gpt-4o-mini prompt: 识别用户意图输出分类{{user_input}} output: intent_result - id: route type: condition conditions: - when: {{intent_result}} 查询 next: rag_search - when: {{intent_result}} 操作 next: tool_call - default: next: fallback - id: rag_search type: rag knowledge_base: product_docs query: {{user_input}} top_k: 5 output: rag_docs next: generate - id: generate type: llm model: gpt-4o prompt: | 基于以下资料回答用户问题 资料{{rag_docs}} 问题{{user_input}} output: final_answer这种声明式配置的好处是流程一目了然改起来也方便。但要注意复杂的循环和嵌套子流程用YAML表达会比较别扭这时候可能需要支持代码节点作为逃生舱。3.2 多供应商接入的统一抽象与路由策略多供应商接入的抽象层我建议至少定义三个核心接口chat对话、embedding向量化、tool_call工具调用。每个供应商实现这三个接口上层只依赖接口。路由策略是抽象层的灵魂。最简单的路由是静态配置比如“所有请求走A供应商”。但更实用的是动态路由根据任务类型、成本预算、当前负载来决定。我常用的策略组合是按能力路由需要工具调用的任务只路由到支持Function Calling的模型。按成本路由简单分类任务走便宜的小模型复杂生成任务走大模型。按负载路由主供应商限流时自动切到备用供应商。按地域路由如果业务有地域要求选择对应区域的供应商。实现上我一般用一个路由表配置加上一个健康检查机制。每个供应商维护一个健康状态连续失败N次就标记为不健康暂时从路由池里摘除过一段时间再探测恢复。# 路由策略的简化实现 class ModelRouter: def __init__(self, providers, health_checker): self.providers providers self.health health_checker def route(self, task): candidates [ p for p in self.providers if p.supports(task.required_capabilities) and self.health.is_healthy(p.name) ] if not candidates: raise NoAvailableProvider() # 按成本排序选最便宜的 return min(candidates, keylambda p: p.cost_per_1k_tokens)这里有个经验不要过度追求智能路由。我见过一些团队花大力气做基于强化学习的路由结果还不如简单的规则策略稳定。路由策略要可解释、可调试出问题的时候你能快速定位是哪个环节选错了供应商。3.3 MCP协议接入的实操步骤与注意事项MCP的接入核心是理解它的通信模型。MCP Server对外暴露两类能力Tools可调用的函数和Resources可读取的数据。客户端通过标准协议发现这些能力然后按需调用。接入一个MCP Server的步骤大致是确认Server的传输方式。MCP支持stdio和SSE两种传输。stdio适合本地进程SSE适合远程服务。选择哪种取决于你的部署形态。配置Server连接信息。在XXL-AI的配置里注册MCP Server的地址和认证信息。发现能力。客户端连接后调用list_tools和list_resources获取可用能力列表。映射到编排节点。把MCP Tool映射成编排引擎里的工具调用节点这样Agent就能在流程中调用它。处理认证和权限。MCP Server可能需要API Key或者OAuth这些要在配置里管理好不要硬编码。注意MCP Server的工具描述description质量直接影响模型能否正确调用。描述要清晰说明工具的功能、参数含义、返回值格式。我见过因为描述写得太模糊模型反复调用错误工具的情况。还有一个坑是超时和错误处理。MCP调用本质上是网络调用可能超时、可能返回错误。编排层要能捕获这些异常并决定是重试、降级还是中断流程。我的做法是给每个MCP工具配置独立的超时时间和重试策略不要用全局默认值。3.4 SKILL封装的设计模式与复用技巧SKILL的封装我倾向于用“配置代码”的混合模式。简单的SKILL比如“格式化输出”用配置就能描述复杂的SKILL比如“多轮合同审查”需要写代码来实现内部逻辑。一个SKILL的定义应该包含输入schema需要什么参数、输出schema返回什么结果、内部流程怎么处理、依赖声明需要哪些模型、工具、知识库。SKILL复用的关键在于参数化。比如一个“文档摘要”SKILL不应该把摘要长度、语言、风格写死而应该作为参数暴露出来。这样同一个SKILL可以用于不同场景。# SKILL定义示例 name: contract_review description: 审查合同条款标注风险点 inputs: - name: contract_text type: string required: true - name: risk_level type: enum values: [low, medium, high] default: medium outputs: - name: risks type: array items: clause: string risk_type: string suggestion: string steps: - type: rag knowledge_base: legal_clauses query: {{contract_text}} - type: llm model: gpt-4o prompt: | 根据法律条款库审查以下合同标注风险 合同{{contract_text}} 条款库{{rag_result}} 风险等级{{risk_level}}SKILL的版本管理也很重要。业务在变SKILL的逻辑也要跟着变。我建议给每个SKILL打版本号编排流程引用SKILL时指定版本这样升级SKILL不会意外影响正在运行的流程。3.5 RAG检索增强的链路优化与瓶颈突破RAG这条链路从文档入库到最终生成中间有很多可以优化的点。我把它拆成四个阶段入库、检索、重排、生成。入库阶段核心是分块策略。块太大检索不精准块太小上下文不完整。我的经验是对于技术文档按标题层级分块效果最好对于对话记录按轮次分块对于长文章用滑动窗口加重叠。重叠比例一般设10%-20%保证跨块的信息不丢失。检索阶段向量检索是基础但纯向量检索有个问题对精确匹配不敏感。比如用户搜一个产品型号“XR-2000”向量检索可能返回一堆语义相似但型号不同的文档。所以我的做法是混合检索向量检索加关键词检索BM25两路结果合并后去重。重排阶段用一个小模型对检索结果做精排。这一步能显著提升top结果的准确率。我实测下来加一个重排模型答案准确率能提升15%-20%。重排模型不需要太大几亿参数的交叉编码器就够用。生成阶段关键是上下文组织。不要把检索到的所有内容都塞进prompt要按相关性排序取top N并且加上明确的引用标记让模型知道哪些内容来自哪里。提示RAG的瓶颈往往不在检索算法而在文档质量。如果原始文档本身结构混乱、信息重复再好的检索也救不回来。所以入库前的文档清洗和结构化值得花大力气。关于RAG能不能存图片这个问题答案是能但要看怎么存。一种方式是把图片转成文字描述再入库另一种是用多模态向量模型直接对图片编码。前者实现简单但丢失视觉信息后者效果好但成本高。我的建议是如果图片包含关键信息比如图表、流程图用多模态方案如果只是装饰性图片直接忽略。4. 完整实操流程与核心环节实现4.1 环境准备与基础依赖搭建假设我们要从零搭一套类似XXL-AI的平台第一步是环境准备。我用的技术栈是Python作为主语言FastAPI做API层PostgreSQL存元数据Redis做缓存和队列向量库用Milvus或者Qdrant。基础依赖包括LLM客户端库openai、anthropic等、向量库客户端、MCP SDK、编排引擎可以用LangGraph或者自己写。我的建议是编排引擎不要一开始就上重型框架先用简单的状态机实现等流程复杂了再考虑引入。# 基础环境 python -m venv xxl-ai-env source xxl-ai-env/bin/activate pip install fastapi uvicorn sqlalchemy redis pymilvus openai anthropic mcp数据库表的设计核心是几张表agentsAgent定义、flows编排流程、skills技能定义、providers供应商配置、call_logs调用日志。call_logs这张表特别重要每次LLM调用、工具调用都要记录包括输入、输出、耗时、token数、供应商、模型。这是后续做成本分析和问题排查的基础。4.2 第一个Agent编排流程的落地我们来落地一个具体的流程一个“技术文档问答助手”。用户提问Agent先检索文档如果检索到相关内容就基于文档回答如果没检索到就调用一个外部搜索工具最后生成回答。第一步定义Agent。Agent的核心是系统提示词和可用工具列表。agent_config { name: doc_qa_assistant, system_prompt: 你是一个技术文档助手基于检索到的文档回答用户问题。如果文档中没有相关信息调用搜索工具。, tools: [rag_search, web_search], model: gpt-4o, temperature: 0.3 }第二步定义编排流程。用前面提到的YAML配置方式。nodes: - id: rag type: rag knowledge_base: tech_docs query: {{user_input}} top_k: 5 output: docs - id: check type: condition conditions: - when: {{docs.length}} 0 next: answer - default: next: search - id: search type: mcp_tool server: web_search_server tool: search params: query: {{user_input}} output: search_results next: answer - id: answer type: llm model: gpt-4o prompt: | 基于以下信息回答用户问题。如果信息不足明确说明。 文档{{docs}} 搜索结果{{search_results}} 问题{{user_input}} output: final_answer第三步注册MCP Server。假设我们有一个搜索工具的MCP Server配置它的连接信息。mcp_config { name: web_search_server, transport: sse, url: http://localhost:8080/mcp, auth: {type: api_key, key: your-key} }第四步跑起来测试。用几个典型问题验证流程一个文档里有的问题、一个文档里没有的问题、一个模糊的问题。观察每一步的输出确认检索、条件判断、工具调用、生成都符合预期。4.3 多供应商切换的配置与验证多供应商的配置我建议用一个统一的配置文件管理支持热更新。providers: - name: openai type: openai api_key: ${OPENAI_API_KEY} models: - name: gpt-4o capabilities: [chat, tool_call, vision] cost_per_1k_input: 0.005 cost_per_1k_output: 0.015 - name: gpt-4o-mini capabilities: [chat, tool_call] cost_per_1k_input: 0.00015 cost_per_1k_output: 0.0006 - name: anthropic type: anthropic api_key: ${ANTHROPIC_API_KEY} models: - name: claude-3-5-sonnet capabilities: [chat, tool_call, vision] cost_per_1k_input: 0.003 cost_per_1k_output: 0.015验证多供应商切换我一般做三个测试正常切换手动指定供应商、故障切换模拟主供应商超时、成本路由简单任务是否走了便宜模型。故障切换的测试特别重要我见过配置了备用供应商但实际切换不生效的情况原因是健康检查逻辑有bug。4.4 RAG知识库从入库到检索的完整链路RAG链路的落地我按阶段拆解。入库阶段写一个文档处理管道def ingest_document(file_path, kb_name): # 1. 解析文档 text parse_document(file_path) # 2. 清洗 text clean_text(text) # 3. 分块 chunks split_by_heading(text, max_size500, overlap50) # 4. 向量化 vectors embed(chunks) # 5. 存入向量库 vector_db.insert(kb_name, chunks, vectors) # 6. 同时存入关键词索引 keyword_index.insert(kb_name, chunks)检索阶段实现混合检索def hybrid_search(query, kb_name, top_k5): # 向量检索 vector_results vector_db.search(kb_name, embed(query), top_ktop_k*2) # 关键词检索 keyword_results keyword_index.search(kb_name, query, top_ktop_k*2) # 合并去重 merged merge_and_dedup(vector_results, keyword_results) # 重排 reranked rerank(query, merged, top_ktop_k) return reranked重排模型我推荐用bge-reranker或者cohere的rerank接口。实测下来加了重排之后top1的准确率提升很明显。4.5 工程化底座的日志、监控与成本控制工程化底座的第一件事是全链路日志。每次请求生成一个trace_id所有相关的LLM调用、工具调用、检索操作都带上这个trace_id。这样排查问题时能完整还原一次请求的全过程。class CallLogger: def log_llm_call(self, trace_id, provider, model, input_tokens, output_tokens, latency, prompt, response): self.db.insert(call_logs, { trace_id: trace_id, type: llm, provider: provider, model: model, input_tokens: input_tokens, output_tokens: output_tokens, latency_ms: latency, cost: self.calc_cost(provider, model, input_tokens, output_tokens), prompt: prompt[:1000], # 截断存储 response: response[:1000] })成本控制方面我设置了三道防线单次请求token上限、单用户日token配额、全局日成本告警。超过阈值就触发告警或者降级。这个在实际运营中非常必要我见过因为一个死循环导致一天烧掉几千块的情况。监控指标要关注请求量、平均延迟、P95延迟、错误率、token消耗趋势、各供应商的调用占比。这些指标用Prometheus加Grafana就能搭起来。5. 常见问题与排查技巧实录5.1 Agent编排中的典型故障与排查思路问题一Agent陷入死循环。表现是同一个工具被反复调用流程不推进。排查思路先看日志里工具调用的输入是否相同如果相同说明模型没有根据工具返回结果调整策略。解决方法是在prompt里明确要求“如果工具返回结果与上次相同不要重复调用”或者在编排层加一个最大循环次数限制。问题二条件判断不生效。表现是流程总是走default分支。排查思路检查条件表达式的变量名是否和上游节点的输出名一致检查值的类型是否匹配字符串比较和数字比较容易搞混。我一般会在编排引擎里加一个调试模式把每个节点的输入输出都打印出来。问题三上下文超长。表现是请求报token超限错误。排查思路检查每个节点注入prompt的上下文大小特别是RAG检索结果和对话历史。解决方法是加一个上下文裁剪策略按优先级保留最重要的内容。5.2 多供应商接入的兼容性坑坑一工具调用格式不统一。OpenAI的工具调用返回的是结构化的tool_calls数组而有些供应商返回的是文本里嵌JSON。抽象层要做格式转换把不同格式统一成标准结构。坑二流式输出的结束标志不同。有的用[DONE]有的用特定的finish_reason。抽象层要统一处理对上暴露一致的流式接口。坑三错误码和重试策略不同。429限流、500服务器错误、超时不同供应商的返回可能不一样。我的做法是定义一套统一的错误类型每个供应商的客户端负责把原生错误映射过来。问题类型常见表现排查方向解决方案工具调用失败模型不调用或调用错误工具检查工具描述和参数schema优化description简化参数流式中断输出到一半停止检查网络和超时配置增加超时加重试限流429错误检查调用频率加令牌桶做队列成本超支账单异常检查token消耗日志加配额路由到便宜模型5.3 RAG检索质量差的优化实录RAG效果不好我一般按这个顺序排查文档质量 - 分块策略 - 检索算法 - 重排 - prompt组织。文档质量是最容易被忽视的。如果原始文档是扫描件OCR出来的错字多、格式乱检索效果肯定差。我的做法是入库前做一轮人工抽检看看分块后的内容是否语义完整。分块策略的调整我一般会做A/B测试。同一批问题用不同的分块参数跑一遍看检索命中率。实测下来对于技术文档按标题分块加200字重叠效果比固定长度分块好很多。检索算法方面纯向量检索对专有名词不敏感加BM25混合检索能明显改善。重排模型的选择我试过几个开源方案bge-reranker-v2在中文场景下表现不错延迟也可接受。注意RAG的评估不能只看检索命中率还要看最终答案的准确率。有时候检索到了正确文档但模型没有正确使用答案还是错的。所以要端到端评估。5.4 工程化落地的性能与稳定性问题性能问题最常见的是串行调用太多。一个流程里如果有5个LLM调用每个2秒串行就是10秒。优化方法是识别可以并行的步骤比如多个独立的检索可以并行多个不相关的判断可以并行。编排引擎要支持并行节点。稳定性问题最常见的是单点故障。向量库挂了、某个供应商挂了、数据库连接池满了。解决方法是每个依赖都要有降级方案。向量库挂了降级到关键词检索供应商挂了切备用数据库慢了加缓存。可观测性不足是排查困难的根源。我的经验是日志要记录得足够细但也要有结构。用JSON格式记日志方便后续用ELK或者Loki查询。关键字段包括trace_id、node_id、event_type、duration、status。6. 一些实操后的个人体会搭这套东西的过程中我最大的体会是AI应用开发的难点不在AI在应用开发。模型能力已经足够强了真正花时间的是编排、抽象、工程化这些“传统”软件工程的事情。另一个体会是不要过度设计。我一开始想做一个万能的路由策略结果复杂度爆炸还不如简单的规则好用。MCP、SKILL、RAG这些扩展机制也是按需引入不要一上来就全上。最后分享一个小技巧在编排流程里加一个“调试节点”可以把当前上下文输出到日志或者返回给调用方。排查问题时把这个节点插到可疑位置比看日志猜要快得多。这个节点在生产环境可以关掉只在调试时开启。这套思路和实现我后续还打算在几个方向继续打磨一是编排流程的可视化编辑让非技术人员也能改流程二是RAG的自动化评估用LLM来打分检索质量三是成本优化的自动路由根据实时价格和负载动态调整。这些等有新的实践了再分享。