一个Agent项目如果死在生产环境,大概率不是模型不够聪明,而是编排写得太糙、工具接得太散、知识库检索老是答非所问。做AI应用平台这件事,我踩过不少坑,XXL-AI就是在这个背景下搭起来的——它不是一个模型套壳,而是围绕Agent编排、多供应商模型接入、MCP+SKILL+RAG三件套扩展,以及工程化底座四个块来设计的。这篇文章会把每个块的设计思路、选型理由和踩过的坑都写透,适合正在做Agent应用、想把工具调用和RAG接进生产环境、或者准备自建AI平台的团队参考。
1. 编排层是Agent的骨架:没有它,模型再强也只是问答机器人
1.1 编排不是「把Prompt串起来」,而是定义Agent怎么思考、怎么行动
很多团队第一次做Agent,容易把编排理解成「几个Prompt按顺序调」。实际上Prompt链只是编排里最简单的一种形态,真正的Agent编排要处理的是:模型根据任务目标自主决定下一步做什么,调用工具、观察结果、再决定要不要重新思考。这个循环——感知、决策、行动、观察——才是Agent和普通问答系统的本质区别。
我在早期项目中试过用线性链把「理解需求、写代码、跑测试」串起来,第一天看着挺好,第二天就翻车:模型在“写代码”那一步产出了不符合预期的代码,线性链根本不知道要回头修正,直接进入跑测试,后面全乱套。后来才想明白,编排层要解决的根本问题是「在什么条件下允许模型继续、重试、换方案或停下来」,而不是按固定顺序把节点跑完。
具体到XXL-AI的编排引擎,我们落地了这么几个能力:
- 节点类型是可组合的。一个节点可以是LLM调用、工具调用、知识检索、代码执行、人工审批,也可以是一个子Agent。
- 节点之间支持条件边和循环边。模型输出的结构化字段(比如
next_action)决定下一跳走向。 - 每个节点都有失败策略。失败后是重试、换模型、走兜底分支,还是直接终止,由编排定义。
- 支持人工介入点。这个非常关键,很多Agent任务做到一半需要人确认,编排里必须有「暂停并等人工」的状态。
这就像老式流水线和城市立交桥的区别。流水线只允许沿着一条传送带走,立交桥有匝道、有环岛、有岔路口,车可以根据实时路况重新选择路径。Agent一旦进入生产环境,面对的输入千奇百怪,没有立交桥级别的编排,跑起来就是灾难。
1.2 有向图、循环、并行:生产环境Agent的真实执行形态
如果你去看真正在生产环境跑得住的Agent项目,它们的编排几乎都是图结构,不是链式结构。XXL-AI的编排引擎底层就是一个有向图执行器,节点是执行单元,边是流转条件。
举一个我们实际跑过的场景:一个「客服工单处理Agent」。它接到工单后,先做意图分类,然后进入不同的处理分支:
- 如果是退款类请求,先查订单信息,再判断是否满足退款条件,满足就自动创建退款单,不满足就转人工。
- 如果是技术类故障,先搜索知识库,把匹配到的方案整理给用户,同时记录工单标签。
- 如果是投诉类,直接进人工队列,不搞自动化。
这个流程在编排图上就是:一个意图分类节点,后面接三个条件分支,退款分支有「查订单→判断→动作」三个节点,技术类分支有「知识检索→生成回复」两个节点。每个节点都可以并行处理多个工单实例,互不阻塞。
这里有一个容易被忽视的设计点:循环必须有上限。Agent自以为能「再试一次」,如果不在编排层限定最大重试次数,遇到模型抽风就是死循环烧钱。我们在每个循环节点都配了max_loop_count,默认5次,超过就走兜底节点。另外一个点是循环里要保留「历史摘要」,否则Agent会把之前做过的事全忘掉,每次都从零思考。
1.3 Harness与Agent的区别,以及错误处理为什么必须在编排层做
聊Agent框架绕不开「Harness」这个词,很多人问它和Agent到底什么关系。我的理解是:Agent是决策体,负责「想」,而Harness是执行体/运行容器,负责「跑」。编排平台真正实现的是Harness这一段——它把模型决策、工具调度、状态管理、错误捕获都包起来。你问「Agent安全」「Agent怎么扛并发」,答案几乎都在Harness这一层,而不在模型本身。
错误处理是最能体现Harness价值的地方。我们遇到过「Agent execution terminated due to error」这类问题,常见的触发因素有:工具返回了非预期格式、模型输出了非法JSON、上游API超时、上下文超长被截断。单纯把错误抛给用户没有任何意义,编排层必须做三件事:
- 分类错误。是模型侧错误、工具侧错误,还是业务规则错误。
- 执行兜底策略。比如工具超时就重试一次;模型输出非法JSON就让模型基于错误信息重新生成;连续两次失败就走人工。
- 留下完整执行轨迹。每一步输入输出、选了哪条边、为什么失败,全部可回放。
如果这些逻辑散落在业务代码里,每个Agent项目都要重新实现一遍,而且质量参差不齐。放在编排引擎里统一做,整个平台的稳定性才能提上来。
2. MCP协议接入:把「工具调用」从定制开发变成标准插口
2.1 MCP不是新语言,而是一套「工具/资源/提示词」的标准化协议
最近一段时间MCP非常热,热词里甚至有人在问「MCP是软件协议,硬件协议那个概念叫什么来着」。这里先做个澄清:MCP是Model Context Protocol的缩写,中文常叫「模型上下文协议」,它本质上是一套应用层软件协议,作用是让AI应用(客户端)与外部工具/数据源(服务端)之间能按统一标准互相发现、调用和传递数据。
拿USB-C接口来类比,我实际验证下来这个类比非常好懂:以前每个设备都有自己的充电线,换一个设备就得换一根线。MCP相当于把所有外部能力统一成一个插口标准,AI应用只要实现MCP客户端,就能连接任何实现了MCP服务端的工具。协议层主要封装三类原语:
- Tools(工具):可被模型调用的函数,比如查询订单、发送邮件、执行SQL。
- Resources(资源):可被读取的数据,比如数据库里的一张表、文件系统里的一个文档。
- Prompts(提示词模板):预置的交互模板,方便复用。
MCP本身不解决模型智能问题,它解决的是「连接」问题。没有这套标准之前,每接一个新工具就要写一套HTTP调用封装、鉴权和错误转换;有了MCP,工具提供方只需要实现一次服务端,所有支持MCP的Agent平台都能直接使用。
2.2 平台侧MCP网关:注册、鉴权、执行沙箱三件套
在XXL-AI里,我们没有让每个Agent直接去连各个MCP Server,而是统一走一层MCP网关。原因有两个:一是安全,二是可观测。直接连虽然省事,但密钥管理、访问控制和调用审计全都会失控。
网关这一层要做的事,分成三块:
第一块是注册与发现。每个MCP Server在接入平台时都要提交一份元数据,包括Server名称、工具列表、每个工具的入参Schema和描述。这份描述的质量非常重要——模型决定调不调用一个工具,全靠读描述。描述含糊的(比如「执行操作」)模型根本不知道什么时候用;描述准确清晰的(比如「当用户要求查询订单物流时调用本工具,入参orderId为数字字符串」),模型的调用准确率会大幅上升。
第二块是鉴权与凭证管理。MCP Server可能有各自的API Key、Token、OAuth凭证。我们的做法是:凭证统一存储,在网关执行调用时由服务端注入,绝不把密钥塞进提示词或暴露给前端。像「接Figma的MCP怎么授权」「接设计稿平台的MCP怎么授权」这类问题,本质就是授权流程在网关里做一次,后续所有Agent共享这一份凭证授权。
第三块是执行沙箱。本地启动的MCP Server经常涉及文件读写、命令行执行这类高风险操作。如果让模型无约束地调用本地命令,后果不敢想。我们全部放进白名单机制:哪些目录可读、哪些命令可执行、哪些网络地址可达,都在沙箱里限制好。远程的MCP Server则统一设超时和并发上限,防止单个工具调用把Agent拖死。
2.3 用一组对比说透MCP的价值:普通API调用与MCP工具调用的差异
很多人会问:我直接把工具写成HTTP API不行吗,为什么非要用MCP?我用一个实际对比来说明。
| 对比维度 | 普通API接入 | MCP工具接入 |
|---|---|---|
| 工具定义 | 各自定义,文档五花八门,模型需要为每个工具写专属提示词 | 统一工具描述Schema,模型天然理解 |
| 动态发现 | 平台需要预先知道并硬编码接口 | 模型运行时可通过协议发现可用工具 |
| 鉴权与审计 | 每个接口一套,难以统一管控 | 网关统一鉴权,调用日志集中记录 |
| 上下文传递 | 每次需要手动组装上下文 | 支持标准化的资源读取和上下文注入 |
| 生态复用 | 换平台要重写 | 同一MCP Server可以在多个支持MCP的平台上直接复用 |
拿热词里提到的「Browser Use MCP 跟 Playwright MCP 有什么区别」来说:前者偏向给模型提供高抽象的浏览器操作意图(比如「打开页面、提取正文、点击按钮」),后者更偏向把Playwright底层的精细控制能力暴露给模型(比如「定位某个CSS选择器、执行JavaScript」)。两者都通过MCP协议暴露给Agent,但在工具描述的目标和使用方式上有区别。这不是谁替代谁的问题,而是你想让Agent在什么抽象层级上操控浏览器的取舍。
踩坑提醒:MCP工具接入多了以后,会出现「工具过载」——一次任务把所有工具的Schema都塞给模型,上下文窗口直接膨胀,模型反而不知道选哪个。我们在网关里增加了「工具可见性」配置,根据当前任务类型只暴露一部分工具,比如退款任务只暴露订单、支付、客服工单类工具,不相干的工具一律隐藏。这个设计对准确率和成本都是正向提升。
3. SKILL与RAG:让Agent手里有「说明书」,脑里有「知识库」
3.1 SKILL技能包:把高频操作固化成可复用、可版本化的资产
SKILL这个词在最近的热度上升很快。我的理解是:SKILL是把一类高频、可复用的操作步骤固化成「技能包」,让Agent遇到对应场景时按这套技能去执行。它并不只是提示词模板,而是「触发条件+操作流程+工具编排+校验逻辑」的完整封装。有人把一本操作手册转成SKILL(热词里的Book to Skill就是这个意思),是非常典型的用法。
举个实际例子。我们给一个售后团队做了「退款处理」SKILL,它的执行逻辑是:
- 触发条件:用户意图分类为「退款」,且情绪等级非投诉。
- 第一步:调用用户中心工具查询订单状态,校验是否在可退款时间窗内。
- 第二步:调用规则引擎判断退款原因是否符合政策。
- 第三步:满足条件则生成退款任务,不满足则跳转到转人工分支。
- 第四步:生成回复话术,带订单编号和预计到账时间。
这里的每一步都对应具体的工具调用和判断条件,不是「让模型自由发挥」。把SKILL做好之后,客户服务的一致性和体验会很稳定,不会因为模型状态波动导致同样的业务时而答应退款、时而不答应。
SKILL和MCP的关系也需要理清:MCP提供的是「工具插口」,解决能不能调用的问题;SKILL提供的是「操作流程」,解决怎么按标准做事的问题。一个SKILL可以跨多个MCP工具编排,就像一份SOP能跨多个系统操作一样。我们在平台里给每个SKILL设置了版本号、负责人、描述和测试用例,每次更新之前都要求先跑通旧的测试集,防止「修了A场景,坏了B场景」。
3.2 RAG工程落地:文档解析、切片、向量化、检索的每一步都要眼见为实
RAG(检索增强生成)是让Agent拥有「私域知识」最常用的手段。但很多人对RAG的理解停留在「把文档丢进向量数据库,然后语义搜索」——实际落地远没有这么简单。
说说我们在XXL-AI里跑通的RAG管道,一共六个环节:
- 文档解析。PDF、Word、HTML都要先转成干净的纯文本,表格要单独抽取成结构化数据,图片里的文字要过OCR。这一步最容易被低估,垃圾文本进,检索质量必崩。
- 切片。切片是RAG的命门。我们实践下来,通用文本按500-800字符切片,overlap(重叠)设50-100字符,会让检索效果比较稳。碰到代码库或表格,则要按语义边界切,不能机械地按字符数一刀切。
- 向量化。选Embedding模型要看语言适配和向量维度。中文场景可以先用中文优化过的模型,效果不够再加微调和领域适配。
- 向量存储。主流选项是向量数据库或带有向量检索能力的PostgreSQL。数据量在百万级之内,很多方案都能扛得住;真正要关心的是过滤条件能力,比如按部门、按业务线过滤后再检索。
- 检索。我们没走「只有向量召回」的路子,而是用混合检索:向量召回相似语义,关键词召回精确匹配,然后把两路结果合并。为什么?因为很多私域知识里的精确词(比如产品型号、订单编号)在语义向量空间里容易被稀释,纯向量召回会漏掉这些关键信息。
- 重排。召回topN之后,再让Rerank模型对候选片段精排,只取最相关的前K段喂给LLM。这一步对回答质量提升非常显著,强烈建议不要省。
整个环节里最容易翻车的就是「切碎了上下文」。早先我们用500字符硬切,经常把一个完整的业务规则从中间拦腰截断,结果模型"读到"了规则的上半段,回答出错误的结论。后来改成按段落和语义边界切片,并把切片后的原文片段带标题一起存进去,检索效果才稳定下来。
3.3 「知识库能存图片吗」以及RAG瓶颈到底卡在哪
热词里有人问「RAG知识库能存储图片吗」,这里展开说一下。结论是:能存,但要看你说的「存」是哪种用法。
- 如果你只是想让Agent在回答时能把图片作为附件发给用户,那把图片存到OSS/CDN,在知识片段里保留图片URL即可,回答时让模型引用这个URL。
- 如果你希望Agent能按图片内容(不是文件名)检索到图,那就需要走图像Embedding或多模态视觉模型,把图片内容抽取成向量和描述文本再入库。这个方案能做,但目前成本较高,不是每个场景都值得。
我们目前的默认做法是「文本为主、图片为辅」:文档中的图片先做OCR和场景描述,把文字内容落到文本片段里,最终检索出来的回答再附带图片URL。用户能看到图,也能从图里拿到关键信息照图操作,成本和效果处于一个比较舒服的平衡点。
至于「RAG的瓶颈」,我的体感是大部分场景的瓶颈不在生成,而在召回。具体表现是:知识库里确实有正确答案,但检索环节没把它找出来,或者找出来的片段太多太杂,模型不知道该信哪段。解决方向:
- 提升召回精度:混合检索加Rerank,指数级改善。
- 引入本体/实体关系(热词里的Ontology RAG方向):如果领域里有清晰的知识结构,先按实体和关系建索引,再让Agent沿着关系链路检索,比纯相似度搜索稳很多。
- 让Agent学会「查不到就承认」:编排层加一个校验节点,如果检索结果的相似度分数全部低于阈值,Agent必须走「知识不足,转人工」分支,绝对不能瞎编。
另外提醒一句:凡是能用工具调用和确定性规则解决的场景,优先用SKILL+工具,不要硬上RAG。比如「查询本月销售额」,本质是调BI报表接口,而不是去知识库里搜语义片段。工具能算出来的,就别让模型蒙。
4. 多供应商模型接入:真正的难点是容错、路由与成本
4.1 统一协议层之上,才谈得上「供应商随便换」
现在市面上的模型供应商很多,各家API的请求格式、系统提示词处理、Token计数、流式输出格式都不完全一致。如果业务代码直接绑定某一家的SDK,后续换模型就成了一场大手术。XXL-AI在模型接入层做的事,是把所有供应商的差异收敛成一个统一模型协议。
具体落地方式:
- 统一请求格式。业务方只需要面向平台内部定义的模型协议写代码,底层把协议转换成各家API的格式。
- 统一流式输出。无论后端是OpenAI兼容接口、Anthropic消息接口,还是本地部署的Ollama,面对业务层都输出同一种流式事件格式。
- 统一错误结构。把各家API的限流错误、鉴权错误、服务不可用错误归一成平台侧的标准错误码,这样上游重试、告警规则就不用写五六套。
有了这一层,切供应商就变成了「配置变更」而不是「代码重写」。我们遇到过供应商接口升级导致大批调用超时的情况,靠着统一协议层快速切到备用供应商,业务几乎没有感知。
4.2 面对「AI Agent怎么扛并发」:限流、退避、异步化一起来
「AI Agent怎么扛并发」是最近被问得最多的问题,因为Agent并不是单次大模型调用,一个任务往往包含几十次模型调用和工具调用,并发模型和普通接口完全不一样。
我把应对并发的手段拆成四层:
第一层,应用侧异步化。Agent任务的单次执行耗时动辄几十秒甚至分钟级,没法用同步HTTP请求一直挂着。我们的做法是:任务提交进队列,执行引擎异步调度,前端通过WebSocket或轮询获取进度。这样应用服务器的线程占用就被压下来了,而不是被慢模型调用卡死。
第二层,模型网关限流。每家模型供应商都有限流设置,而且限流的维度经常是「每分钟请求数+每分钟Token数」复合计算的。网关里要做一个令牌桶,按供应商配置好的速率放行。不要等到上游报限流错才去重试,那已经晚了。
第三层,重试退避。重试要用指数退避加随机抖动,比如第一次等1秒、第二次2秒、第三次4秒,再多就不要再重试了,直接进兜底分支。没有抖动的重试,遇到集体超时会演变成「惊群效应」,把服务端打得更惨。
第四层,预算控制。一个Agent任务跑完可能要烧掉大几万Token,并发一高,账单数字会非常吓人。我们在任务提交前会做一次预估,比如并发200个任务、每个任务平均5步模型调用、每步平均1500 Token,瞬时峰值就是200乘5乘1500,也就是150万Token左右的吞吐需求。没有预算控制,这个数字在月底对账时是会出大事的。
再补充一点:像语义缓存这类手段也很值得做。同类问题如果能让Agent直接复用之前的检索结果和模型输出,能省掉的调用量相当可观。缓存命中比任何性能优化都来得实在。
5. 工程化底座:把AI应用当成正经后端服务来交付
5.1 Trace一次Agent执行:模型调用、工具调用、检索调用必须能串起来
AI应用最难调试的地方是「不可复现」。同样的用户问题,昨天能答对,今天可能就答错,模型输出本身就是概率性的。如果全链路没有Trace,出了问题就只能靠猜。我们在XXL-AI里把可观测性做成了一等公民。
每个Agent任务从创建开始就绑定一个Trace ID,整个执行过程中的每一环都会往这个Trace里记录:
- 每一次模型调用的输入提示词、输出内容、Token用量、耗时、模型名称。
- 每一次工具调用的工具名、入参、出参、耗时、错误信息。
- 每一次知识检索的查询向量、召回候选的相似度分数、最终选中的片段。
- 每一次分支跳转的路由依据,模型输出的结构化字段内容。
这些数据日常看起来琐碎,一旦排查线上问题作用就大了。比如用户反馈「回答不对」,你把Trace拉出来,一眼就能看清:是知识库里没有正确答案、检索到了但模型没用、还是编排走错了分支。有了这个能力,AI应用就不再是黑盒。
顺带说一句告警指标,不要只盯成功率,要盯P95耗时和Token消耗波动。模型供应商的状态波动经常是缓慢爬升的,等成功率暴跌再报警往往已经晚了。
5.2 评测不是玄学:用命中率、评估集和回归测试守住质量
AI应用的质量评估不像传统软件写单元测试那么简单,但还是有章可循。热词里有人提到RAG hit rate,这里解释一下:它指的是「针对一批测试问题,检索环节能够召回正确答案片段的比例」。计算公式很简单:hit rate等于召回命中数除以总问题数。比如100个测试问题里有85个能召回正确片段,命中率就是85%。
我们在实践里建设了两类评估资产:
第一类,检索评估集。收集真实用户问题,为每个问题标注正确答案应该在哪篇文档的哪个片段。每次改切片策略、换Embedding模型、调重排逻辑,都要在这个评估集上跑一遍,看hit rate有没有下降。这是RAG优化的北极星指标。
第二类,端到端评估集。覆盖典型业务场景、边界场景和对抗场景。对抗场景包括「知识库没有答案」的问题、包含敏感词的问题、用同义词乱问的问题。端到端评估用LLM作为评判器给回答打分,同时人工抽样复核打分结果。为什么需要人工复核?因为LLM评判器本身有偏好,比如偏爱格式更长的回答,需要人工纠偏。
这套评测体系的价值,在SKILL和MCP升级时体现得最明显。没有回归测试,你永远不知道一次「看起来不错」的升级会不会让某个场景的回答质量悄悄劣化。
5.3 配置化发布的最后一公里:SKILL/MCP更新也要走灰度
传统后端的发布流程大家都很熟了,但到了AI应用这边,很多人又把规范丢掉了,Skill改一下直接上线,MCP加个工具直接部署。这种做法挺危险的。我们的做法是把「配置」和「代码」同等对待。
XXL-AI里实际上有一个管理端,功能有点像热词里提到的「后台管理平台合并MCP功能」——把MCP Server注册、SKILL配置、模型供应商参数、Prompt模板都做成可配置项,而不是散落在代码里。每个配置项都有版本号,改配置就走配置中心:
- 先在灰度环境生效,绑定少量测试任务或内部用户。
- 跑一遍端到端评估集,对比升级前后的命中率和回答质量分。
- 确认无劣化后,再逐步放大到全量流量。
- 大小版本都保留回滚能力,配置出问题一键切回旧版本。
这一步看着平淡,却能把AI应用从「能跑」推进到「能稳定运营」。很多项目死在「改了一个Prompt,线上崩了一周」,本质上不是技术难,而是没给变更关上一道闸。
最后分享一个我们内部常说的经验:刚开始搭AI应用平台,千万别贪多。先把一次Agent任务的闭环跑通,日志和评测地基打好,再逐步加MCP、加SKILL、加更多供应商。平台能力是追着业务问题长出来的,而不是一开始就铺一大堆模块,最后每个模块都在吃灰。如果能把第一条Agent任务完整地跑进生产并用Trace盯着它debug一周,你对AI应用的理解会比读十篇文章都深。