AI应用开发平台实战:Agent编排、MCP、SKILL与RAG全链路解析
1. 为什么我会去折腾一个AI应用开发平台先说结论我搭这套东西不是为了追新概念而是被现实逼出来的。过去一年我手上同时跑着四五个AI相关的项目有做内部知识问答的有做自动化流程的还有给业务部门做智能助手的。每个项目单拎出来都不复杂但凑在一起就出问题了——模型供应商换了三家的APIAgent逻辑散落在各个脚本里RAG的检索链路每个项目都重新写一遍工具调用更是各搞各的。改一个公共逻辑得在五个地方同步改完还得逐个回归测试。这种状态下我迫切需要一个统一的底座把Agent编排、多供应商接入、扩展能力MCP、SKILL、RAG和工程化支撑这几件事收拢到一处。XXL-AI这个平台就是在这个背景下进入我视野的。它定位很明确一个AI应用开发平台核心能力覆盖Agent编排、多供应商适配、以MCP加SKILL加RAG为核心的扩展体系以及一套工程化底座。说白了它想解决的就是我上面遇到的那堆问题——让AI应用的开发从每个项目重新造轮子变成在统一底座上组装能力。这篇文章适合谁看如果你正在做AI应用开发尤其是需要把大模型能力落地到具体业务场景里的开发者、技术负责人或者你已经被多供应商切换、Agent流程编排、知识库检索这些事折腾过那这篇内容应该能给你一些直接能抄的参考。我会把整个平台的思路拆解、核心细节、实操过程和我踩过的坑都摊开讲尽量做到你看完就能动手复现的程度。2. 平台整体设计与思路拆解2.1 为什么是编排扩展底座这三层结构我研究一个平台习惯先看它的分层逻辑因为分层直接决定了这个平台能长多大、能撑多久。XXL-AI的结构我理解下来是三层最上面是Agent编排层中间是扩展能力层MCP、SKILL、RAG最下面是工程化底座。这个分层不是随便切的背后有很实际的考量。Agent编排层负责的是决策和调度。一个AI应用的核心逻辑无非是接收输入、判断意图、调用能力、组织输出。这些事如果散在业务代码里就会变成一堆if-else和硬编码的调用链。把它抽象成编排层意味着你可以用配置或者可视化方式定义Agent的行为而不是每次都写代码。我试过用纯代码写Agent流程一个稍微复杂的多轮对话加工具调用代码量轻松上千行而且逻辑一改就牵一发动全身。编排层把这个复杂度收走了。扩展能力层是这套平台最有意思的地方。MCP、SKILL、RAG这三个词现在很热但很多人分不清它们的边界。我的理解是MCP解决的是Agent怎么和外部工具、数据源通信的协议问题SKILL解决的是具体某个能力怎么封装和复用的问题RAG解决的是怎么让模型用上私有知识的问题。三者是互补的不是替代关系。平台把这三个都纳入扩展体系说明它想覆盖的是从工具调用到知识增强的完整链路。工程化底座是最容易被忽视但最关键的一层。多供应商适配、配置管理、日志追踪、限流熔断、部署运维这些事不做扎实上面两层再花哨也落不了地。我见过太多Demo很惊艳但一上生产就崩的AI项目问题几乎都出在底座上。2.2 多供应商适配不做绑定才有退路多供应商这件事我是吃过亏的。早些年做项目图省事直接绑死一家模型供应商结果对方接口一调整、价格一变动整个项目就得跟着改。后来学乖了所有模型调用都走一层抽象供应商可替换。XXL-AI的多供应商设计我理解核心是两点一是统一的调用接口不管底层是哪家模型上层Agent编排看到的都是同一套API二是能力声明机制每个供应商支持什么能力比如是否支持函数调用、是否支持流式输出、上下文窗口多大在接入时就声明清楚编排层根据这些声明做适配。这样做的好处很直接。我可以在开发阶段用便宜的小模型快速迭代上线时切到能力更强的大模型某个供应商出故障时能快速切到备用供应商不同业务场景用不同模型成本和质量之间做平衡。这些操作在统一接口下都是改配置的事不用动业务代码。具体到实现我一般会定义一个模型供应商的抽象接口包含对话、补全、函数调用、流式输出这几个核心方法然后每个供应商写一个适配器实现这个接口。适配器里处理各家API的差异比如参数命名、返回结构、错误码。上层只依赖抽象接口不关心具体实现。这个模式不新鲜但真正在AI应用里做扎实的不多。2.3 MCP、SKILL、RAG三者的协作关系这三个概念经常被混着讲我按自己的理解把它们的关系理一理。MCP是一种协议全称是Model Context Protocol它定义的是模型或者Agent和外部能力之间的通信标准。你可以把它理解成AI世界的USB接口——只要对方实现了MCP你的Agent就能用统一的方式去调用它不用为每个工具单独写适配。这个协议的价值在于标准化让工具生态能互通。SKILL是能力的封装单元。一个SKILL就是一件具体的事比如查天气发邮件执行一段代码调用某个内部系统。SKILL可以基于MCP协议暴露出去也可以内部直接调用。它的核心是复用——写一次多个Agent都能用。RAG是知识增强的手段。当模型自身知识不够用或者需要用到私有数据时RAG通过检索外部知识库把相关内容拼进上下文让模型基于这些内容回答。RAG的关键在检索质量检索不准后面全白搭。三者的协作关系我这样理解Agent编排层决定要做什么MCP决定怎么和外部通信SKILL提供具体能做什么RAG补充需要知道什么。一个完整的智能问答Agent可能是这样的流程用户提问Agent判断需要查内部文档通过RAG检索知识库拿到相关片段同时通过MCP调用一个SKILL去查实时数据最后综合两部分信息生成回答。这个链路里三者各司其职。2.4 工程化底座到底要解决什么问题工程化底座这个词听起来虚但拆开看都是实打实的问题。第一是配置管理。AI应用的配置项特别多模型参数、提示词模板、工具配置、检索参数、限流阈值。这些配置如果散在代码里改一次就得重新部署。底座要提供统一的配置中心支持热更新。第二是日志和追踪。AI应用的调用链路长一次请求可能经过意图识别、检索、工具调用、生成多个环节。出问题时没有完整的链路追踪排查起来就是盲人摸象。底座要记录每个环节的输入输出、耗时、状态。第三是限流和熔断。模型调用是有成本和延迟的不加限制一个异常流量就能把配额打满或者把响应时间拖垮。底座要在入口做限流在调用外部服务时做熔断。第四是部署和运维。AI应用的部署和传统应用不太一样涉及模型服务的健康检查、扩缩容、版本管理。底座要提供标准化的部署方案。这四件事任何一件没做好平台都撑不起生产环境。我在实际项目里光是为了把链路追踪做清楚就花了不少功夫因为AI调用的中间状态太多不像传统接口那样输入输出清晰。3. 核心细节解析与实操要点3.1 Agent编排的核心状态机还是工作流Agent编排的实现方式主流有两种状态机和工作流。我两种都用过说说区别和选择。状态机适合对话类Agent。它的核心是状态和转移——当前处于什么状态收到什么输入转移到什么状态。多轮对话天然适合状态机因为对话本身就是有状态的。比如一个订票Agent状态可能是等待出发地等待目的地等待日期确认订单每个状态下接收用户输入判断是否满足转移条件。工作流适合任务类Agent。它的核心是节点和连线——一个任务拆成多个步骤步骤之间有依赖关系按顺序或者条件执行。比如一个数据处理Agent节点可能是读取数据清洗分析生成报告节点之间是顺序执行。XXL-AI的编排能力我理解是两者都支持或者用一套统一的抽象来覆盖。实际用下来我的建议是对话场景用状态机思路任务场景用工作流思路不要强行用一种模式套所有场景。我见过有人用工作流硬做多轮对话结果状态管理写得极其别扭。编排里有个关键细节是上下文管理。Agent在多轮交互中需要记住历史信息。但上下文窗口是有限的不能无限堆积。常见的做法是滑动窗口加摘要——保留最近N轮完整对话更早的对话压缩成摘要。这个策略要可配置因为不同场景对历史信息的依赖程度不一样。3.2 MCP接入的实操细节MCP接入这块我踩过的坑最多重点讲。首先是MCP Server的发现和注册。一个平台要接入MCP得先知道有哪些MCP Server可用。常见做法是维护一个注册表记录每个Server的地址、能力、认证方式。注册可以是静态配置也可以是动态发现。静态配置简单可控动态发现灵活但复杂。我一般先用静态配置等生态稳定了再考虑动态。然后是连接管理。MCP Server可能是本地进程也可能是远程服务。本地进程要考虑启动、健康检查、重启远程服务要考虑网络、认证、超时。我遇到过MCP Server启动慢导致首次调用超时的情况后来加了预热机制——平台启动时先ping一遍所有注册的Server确认可用。认证是个容易忽略的点。很多MCP Server需要token或者密钥这些凭证不能硬编码在配置里要走密钥管理。我一般用环境变量或者专门的密钥服务配置里只放引用。调用时的参数校验也很重要。MCP协议定义了工具的输入schema平台在调用前应该校验参数是否符合schema不符合就提前报错而不是等Server返回错误。这样能省一次网络往返也能给出更清晰的错误信息。还有一个实际问题是错误处理。MCP Server可能返回各种错误参数错误、权限不足、内部异常、超时。平台要能区分这些错误并决定是重试、降级还是直接失败。我的经验是参数错误和权限错误不重试直接返回内部异常和超时可以重试一到两次重试还失败就降级。3.3 SKILL的设计与复用SKILL的设计核心是接口定义和实现分离。一个SKILL的接口定义包括名称、描述、输入参数、输出结构、错误码。描述很重要因为Agent要靠描述来判断什么时候该用这个SKILL。描述写得含糊Agent就可能用错或者不用。我一般要求描述里包含这个SKILL做什么什么场景下用输入输出是什么。实现部分一个SKILL可以是一个函数、一个HTTP调用、一段脚本。平台要提供多种实现方式因为不同SKILL的复杂度不一样。简单的比如获取当前时间一个函数就够了复杂的比如调用内部审批系统可能要封装一整套HTTP交互。SKILL的复用关键在于版本管理和依赖管理。一个SKILL改了依赖它的Agent要能感知到。我一般给SKILL加版本号Agent引用时指定版本这样SKILL升级不会影响已有Agent除非主动升级。还有一个实践是SKILL的组合。有些复杂能力是多个简单SKILL的组合平台应该支持把多个SKILL编排成一个复合SKILL。这样上层Agent看到的还是一个SKILL但内部是多个SKILL协作。这个能力在做复杂业务时特别有用。3.4 RAG的检索质量怎么保证RAG这块我最大的体会是检索质量决定一切。模型再强检索出来的内容不相关回答就是错的。检索质量的第一关是文档处理。原始文档格式五花八门PDF、Word、HTML、Markdown都有。处理时要做好解析、清洗、分块。分块特别关键块太大检索出来的内容冗余浪费上下文块太小信息不完整模型拼不出完整答案。我一般按语义分块而不是按固定字数保证每块是一个完整的语义单元。第二关是向量化。选什么embedding模型直接影响检索效果。我的经验是中文场景要用针对中文优化的模型通用模型在中文上效果会打折扣。另外向量维度不是越高越好高维度检索慢、存储大要权衡。第三关是检索策略。纯向量检索在语义相似上表现好但在关键词精确匹配上弱。实际项目里我一般用混合检索——向量检索加关键词检索两路结果融合。融合算法可以用RRFReciprocal Rank Fusion简单有效。第四关是重排。检索出来的Top-K用一个重排模型重新排序把最相关的排前面。重排模型比embedding模型更重但效果好很多。我一般检索Top-50重排后取Top-5给模型。第五关是上下文组织。检索出来的片段怎么拼进提示词也有讲究。我一般按相关性排序加上来源标注让模型知道每段内容的出处。如果片段之间有冲突要让模型知道而不是简单拼接。3.5 工程化底座的关键配置工程化底座的配置我挑几个最关键的讲。限流配置要分维度。按用户限流防止单个用户刷爆按供应商限流防止超过供应商配额按SKILL限流防止某个工具被过度调用。限流算法我一般用令牌桶平滑且能应对突发。超时配置要分层。模型调用超时、工具调用超时、整个请求超时各设各的。模型调用超时一般设长一点因为生成本身耗时工具调用超时设短一点因为工具应该快速返回。整个请求超时要大于各环节超时之和否则会出现环节还没超时但整体已经超时的情况。重试配置要区分错误类型。网络错误、超时错误可以重试参数错误、权限错误不重试。重试要有退避策略避免雪崩。我一般用指数退避第一次等1秒第二次等2秒第三次等4秒。日志配置要分级。DEBUG级别记录所有输入输出用于排查INFO级别记录关键节点用于监控ERROR级别记录异常用于告警。生产环境一般开INFO排查问题时临时开DEBUG。4. 实操过程与核心环节实现4.1 环境准备与依赖安装动手之前先把环境理清楚。我这次实操用的是一台Linux服务器配置不算高4核8G够跑通流程。操作系统是Ubuntu 22.04Python版本3.10。为什么强调Python版本因为很多AI相关的库对版本有要求3.10是个比较稳的选择太新太旧都容易踩坑。依赖安装我分三块平台本身的依赖、模型调用的依赖、RAG相关的依赖。平台依赖主要是Web框架和配置管理我用的是FastAPI加Pydantic轻量且类型友好。模型调用依赖看供应商OpenAI的用openai库国内的用各家SDK统一封装在适配器里。RAG依赖主要是向量库和embedding向量库我用的是Milvus的轻量版embedding用的是一个中文优化的开源模型。安装命令我列一下方便你抄# 创建虚拟环境 python3.10 -m venv xxlai-env source xxlai-env/bin/activate # 平台核心依赖 pip install fastapi uvicorn pydantic pydantic-settings # 模型调用依赖 pip install openai httpx # RAG依赖 pip install pymilvus sentence-transformers jieba rank-bm25 # 工具依赖 pip install requests beautifulsoup4装完之后我建议先跑一个最小验证确认各库能正常导入别等到写了一半才发现某个库装不上。4.2 多供应商适配层的实现适配层是整个平台的地基我先把这块搭起来。定义一个抽象基类规定所有供应商适配器要实现的方法from abc import ABC, abstractmethod from typing import List, Dict, Any, AsyncIterator class ModelProvider(ABC): abstractmethod async def chat(self, messages: List[Dict], **kwargs) - Dict: 非流式对话 pass abstractmethod async def chat_stream(self, messages: List[Dict], **kwargs) - AsyncIterator[str]: 流式对话 pass abstractmethod async def function_call(self, messages: List[Dict], tools: List[Dict], **kwargs) - Dict: 函数调用 pass property abstractmethod def capabilities(self) - Dict[str, Any]: 声明能力上下文窗口、是否支持流式、是否支持函数调用等 pass然后针对每个供应商写适配器。以OpenAI风格的接口为例class OpenAIProvider(ModelProvider): def __init__(self, api_key: str, base_url: str, model: str): self.client AsyncOpenAI(api_keyapi_key, base_urlbase_url) self.model model async def chat(self, messages, **kwargs): resp await self.client.chat.completions.create( modelself.model, messagesmessages, **kwargs ) return { content: resp.choices[0].message.content, usage: resp.usage.model_dump(), finish_reason: resp.choices[0].finish_reason } property def capabilities(self): return { context_window: 128000, stream: True, function_call: True }这里有个细节不同供应商的返回结构不一样适配器要统一成平台内部的结构。这样上层编排层拿到的永远是同一套字段不用关心底层是谁。供应商的配置我放在一个YAML文件里支持热加载providers: - name: openai-gpt4 type: openai api_key: ${OPENAI_API_KEY} base_url: https://api.openai.com/v1 model: gpt-4 priority: 1 - name: backup-model type: openai api_key: ${BACKUP_API_KEY} base_url: https://api.backup.com/v1 model: backup-model priority: 2priority字段用于故障转移主供应商不可用时自动切到备用。4.3 Agent编排的落地实现编排层我用的是状态机加工作流的混合模式。核心是一个Agent类内部维护状态外部通过统一的run方法驱动。class Agent: def __init__(self, name, state_machine, skills, ragNone): self.name name self.state_machine state_machine self.skills skills self.rag rag self.context [] async def run(self, user_input: str) - str: # 1. 更新上下文 self.context.append({role: user, content: user_input}) # 2. 判断当前状态 current_state self.state_machine.current_state # 3. 如果需要检索走RAG if current_state.get(use_rag): retrieved await self.rag.retrieve(user_input) self.context.append({role: system, content: retrieved}) # 4. 判断是否需要调用SKILL skill_decision await self._decide_skill(user_input) if skill_decision: skill_result await self.skills[skill_decision].execute(user_input) self.context.append({role: system, content: skill_result}) # 5. 生成回复 response await self._generate() # 6. 状态转移 self.state_machine.transition(user_input, response) self.context.append({role: assistant, content: response}) return response状态机的定义我用配置描述states: - name: greeting transitions: - condition: user_asks_question target: answering - name: answering use_rag: true transitions: - condition: user_satisfied target: closing - condition: user_asks_followup target: answering - name: closing transitions: []这个设计的好处是改流程不用改代码改配置就行。我实际用下来业务方调整对话流程时我只需要改YAML不用重新部署。4.4 MCP Server的接入与调用MCP接入我分两步注册和调用。注册时平台维护一个MCP Server列表每个Server记录地址、能力、认证信息。启动时平台会尝试连接每个Server拉取它的工具列表。class MCPRegistry: def __init__(self): self.servers {} async def register(self, name: str, endpoint: str, auth: dict): client MCPClient(endpoint, auth) tools await client.list_tools() self.servers[name] { client: client, tools: tools, status: healthy } async def call(self, server_name: str, tool_name: str, params: dict): server self.servers.get(server_name) if not server or server[status] ! healthy: raise MCPUnavailableError(server_name) # 参数校验 tool_schema next( (t for t in server[tools] if t[name] tool_name), None ) if not tool_schema: raise ToolNotFoundError(tool_name) validate_params(params, tool_schema[input_schema]) # 调用 try: result await server[client].call_tool(tool_name, params) return result except Exception as e: server[status] unhealthy raise MCPCallError(str(e))这里有个我踩过的坑MCP Server的调用是异步的但有些Server响应很慢。如果不设超时一个慢调用会拖住整个Agent。我后来给每个MCP调用加了独立的超时超时后走降级逻辑。4.5 RAG链路的完整实现RAG链路我拆成四步文档处理、向量化、检索、重排。文档处理def process_document(file_path: str) - List[Dict]: # 解析 text parse_file(file_path) # 清洗 text clean_text(text) # 语义分块 chunks semantic_chunk(text, max_size500, overlap50) return [{content: c, source: file_path} for c in chunks]语义分块我用的是基于标点和段落的分块保证每块是完整语义单元。max_size是最大字符数overlap是块之间的重叠防止信息被切断。向量化from sentence_transformers import SentenceTransformer model SentenceTransformer(BAAI/bge-small-zh-v1.5) def embed(chunks: List[Dict]) - List[Dict]: texts [c[content] for c in chunks] vectors model.encode(texts, normalize_embeddingsTrue) for chunk, vec in zip(chunks, vectors): chunk[vector] vec.tolist() return chunks检索我用混合检索async def retrieve(query: str, top_k: int 5) - List[Dict]: # 向量检索 query_vec model.encode(query, normalize_embeddingsTrue) vector_results milvus.search(query_vec, top_k50) # 关键词检索 keyword_results bm25_search(query, top_k50) # RRF融合 fused rrf_fusion(vector_results, keyword_results) # 重排 reranked rerank(query, fused[:20]) return reranked[:top_k]RRF融合的公式很简单每个文档的得分是它在各检索结果中排名的倒数和。这个算法不需要调参效果稳定。重排我用的是一个小型的cross-encoder模型比embedding模型慢但准。实际项目里如果对延迟敏感可以跳过重排只用融合结果。4.6 工程化底座的配置落地限流我用的是令牌桶基于Redis实现支持分布式class RateLimiter: def __init__(self, redis_client, key: str, rate: int, capacity: int): self.redis redis_client self.key key self.rate rate self.capacity capacity async def acquire(self, tokens: int 1) - bool: now time.time() pipe self.redis.pipeline() pipe.hgetall(self.key) result await pipe.execute() if not result[0]: state {tokens: self.capacity, last: now} else: state { tokens: float(result[0][tokens]), last: float(result[0][last]) } # 补充令牌 elapsed now - state[last] state[tokens] min( self.capacity, state[tokens] elapsed * self.rate ) state[last] now if state[tokens] tokens: state[tokens] - tokens await self.redis.hset(self.key, mappingstate) return True else: await self.redis.hset(self.key, mappingstate) return False链路追踪我用的是OpenTelemetry每个环节打一个span记录输入输出和耗时。这样出问题时能一眼看出是哪个环节慢或者错。from opentelemetry import trace tracer trace.get_tracer(__name__) async def traced_call(name: str, func, *args, **kwargs): with tracer.start_as_current_span(name) as span: span.set_attribute(input, str(args)[:500]) try: result await func(*args, **kwargs) span.set_attribute(output, str(result)[:500]) span.set_attribute(status, success) return result except Exception as e: span.set_attribute(status, error) span.set_attribute(error, str(e)) raise5. 常见问题与排查技巧实录5.1 模型调用超时和限流问题这是最常见的问题。表现是请求响应慢或者直接报429错误。排查思路先看是单个供应商的问题还是全局问题。如果只有某个供应商慢那是供应商的问题切备用如果所有供应商都慢那是平台的问题看是不是并发太高。我遇到过一次所有请求都超时排查发现是限流配置写错了rate设成了每秒1次但实际QPS是10。改配置后恢复。所以限流参数一定要根据实际流量压测后设定不能拍脑袋。另一个坑是重试放大流量。一个请求失败后重试3次如果失败率高实际流量会放大4倍把供应商配额打满。我的做法是重试加退避并且限制总重试次数。5.2 RAG检索不准的排查检索不准表现是模型回答跑偏或者答非所问。排查分几步先看检索出来的内容本身对不对。如果检索内容就不相关那是检索环节的问题如果检索内容相关但模型没用上那是提示词或者上下文组织的问题。检索环节的问题常见原因有三个分块不合理、embedding模型不匹配、检索策略单一。分块问题我一般通过调整块大小和重叠解决embedding问题换模型检索策略问题加混合检索和重排。我踩过的一个坑是文档里有大量表格按文本分块后表格结构全乱了检索出来的内容没法用。后来针对表格做了特殊处理把表格转成结构化文本再分块。5.3 MCP调用失败的排查MCP调用失败表现是工具调用报错或者超时。排查顺序先确认MCP Server是否存活再确认认证是否有效最后确认参数是否符合schema。我遇到最多的是认证问题。MCP Server的token过期了但平台没感知一直用旧token调用全部失败。后来加了token刷新机制并且定期健康检查。另一个坑是参数schema不匹配。MCP Server声明的schema和实际期望的不一致平台按schema校验通过了但Server还是报错。这种情况只能看Server的日志或者联系Server提供方确认。5.4 常见问题速查表问题现象可能原因排查方法解决方案请求响应慢供应商限流或网络问题看链路追踪定位慢的环节切备用供应商或加限流429错误超过供应商配额看配额使用情况降级到小模型或加限流检索不准分块或embedding问题看检索结果相关性调整分块换embedding模型MCP调用失败认证过期或参数错误看MCP Server日志刷新token校验参数上下文超限历史信息堆积看上下文长度加滑动窗口和摘要状态机卡死转移条件不满足看当前状态和输入加兜底转移或人工干预5.5 几个独家避坑技巧第一个技巧所有外部调用都要有超时和降级。不管是模型、MCP还是RAG只要涉及外部依赖就要假设它会失败。我一般给每个外部调用设一个超时超时后走降级逻辑返回一个默认值或者提示而不是让整个请求挂掉。第二个技巧配置要能热更新。AI应用的参数调整很频繁如果每次都要重启效率太低。我用的是配置中心加监听机制配置一变平台自动加载。第三个技巧日志要能按请求ID串联。一次请求经过多个环节每个环节的日志都要带上同一个请求ID这样排查时能一键拉出完整链路。这个习惯帮我省了大量排查时间。第四个技巧压测要覆盖异常场景。正常流程的压测谁都会做但异常场景的压测容易被忽略。我一般会模拟供应商超时、MCP失败、RAG返回空等场景确认平台的降级逻辑正常工作。6. 我对这套平台的一些实际体会搭完这套东西跑了一段时间有几个体会比较深。第一编排层的抽象程度要适中。抽象太浅业务代码还是散抽象太深灵活性又不够。我的经验是把决策和调度抽象出来把具体实现留给SKILL这个粒度比较合适。第二扩展能力要标准化。MCP、SKILL、RAG各自有各自的规范平台要做的是把这些规范统一到一套接口下让上层用起来无感。标准化做得好扩展就简单做得不好每加一个能力都要改上层。第三工程化底座是长期投入。限流、追踪、配置这些事短期看不出价值但项目一上规模没有这些就是灾难。我建议一开始就把底座搭好别等出问题再补。第四多供应商不是越多越好。接太多供应商维护成本高而且每个供应商的行为差异都要处理。我的做法是接两到三家一家主用一家备用够用就行。最后分享一个小技巧平台上线前我会做一个混沌测试随机让某些外部依赖失败看平台能不能正常降级。这个测试能提前暴露很多问题比等到生产环境出故障再排查强得多。