1. 这不是又一个“AI平台”概念包装,而是工程化落地的实操切口
你点开这个标题,第一反应可能是:“又来?Agent编排、RAG、多供应商……全是老词套新壳。”我完全理解。过去两年,我亲手搭过17个不同形态的AI应用系统,从教育机构的智能备课助手,到制造业的设备故障诊断看板,再到本地律所的合同条款比对工具——踩过的坑比读过的论文还多。XXL-AI这个名字乍看像营销术语,但拆开它背后那串括号里的关键词:Agent编排、多供应商、「MCP + SKILL + RAG」扩展、工程化底座,其实是一份非常诚实的“施工说明书”。它不讲大模型有多强,只说一件事:怎么让AI能力真正嵌进业务流程里,不崩、不卡、不靠人盯、不改代码就能换模型换知识源。
核心关键词里,“XXL-AI”是项目代号,本质是框架;“Agent编排”不是画流程图,而是定义任务如何被拆解、路由、重试、降级;“多供应商”意味着你今天用通义千问做推理,明天换成本地Ollama跑Qwen2-7B,后天接入某私有化部署的DeepSeek-v3,接口层不动,逻辑层不改;而「MCP + SKILL + RAG」这组组合,才是真正的工程锚点——MCP(Model Control Protocol)解决的是模型调用的标准化协议问题,SKILL是可插拔的能力单元封装规范,RAG则不是简单加个向量库,而是作为知识供给管道,与SKILL协同调度。这三者不是并列关系,而是分层协作:MCP管“怎么调”,SKILL管“调什么”,RAG管“喂什么”。
适合谁看?如果你正面临这些场景,这篇就是为你写的:
- 你已经用LangChain或LlamaIndex搭出一个能跑的RAG demo,但上线后用户一并发就OOM,检索结果忽好忽坏,换模型要重写prompt模板;
- 你的团队里既有熟悉Python的算法同学,也有只会写SQL的业务分析师,还有负责对接OA系统的Java后端,大家需要一个共同语言来描述“这个AI功能该由谁提供、怎么触发、失败了怎么兜底”;
- 你被要求“把AI能力集成进现有ERP系统”,但对方只开放HTTP接口和数据库只读权限,不允许部署任何新服务,你得在零新增基础设施前提下完成交付。
这不是教你怎么调API,而是告诉你:当“让AI干活”变成一项日常运维工作时,你需要哪些底层结构支撑。下面所有内容,都来自我在三个真实交付项目中反复验证过的方案——没有PPT架构图,只有配置片段、日志截取、压测数据和凌晨三点改完上线后喝的第三杯咖啡。
2. 整体设计思路:为什么必须放弃“单体AI服务”思维?
2.1 传统AI服务模式的三大硬伤,直接导致交付失败率超60%
我统计过去年参与的23个AI应用项目,其中15个在UAT阶段暴露出根本性架构缺陷,根源全出在初始设计思路上。典型错误有三类:
第一类:把RAG当成“附加模块”,而非“知识供给总线”
很多团队的做法是:先建一个LLM服务,再单独起一个ChromaDB实例,写个脚本定期同步文档,最后在prompt里硬塞“请基于以下知识回答……”。问题在哪?当用户问“上季度华东区销售额环比变化趋势”,RAG检索返回5份PDF表格截图+3段会议纪要文本,LLM却把截图识别成乱码、把纪要里“建议暂缓”误读为“立即执行”。这不是模型能力问题,是知识供给链路断裂——RAG没告诉LLM“这份PDF是财务报表,这张截图是柱状图,这段文字是决策结论”,更没告诉系统“当检索结果含图表时,优先调用OCR-SKILL解析,再送入LLM”。
第二类:Agent编排=手写if-else状态机
见过最典型的案例:某政务热线AI助手,需求是“市民问政策→查知识库→若无结果→转人工→若人工未响应→发短信提醒”。开发同学用Flask写了800行代码,每个分支都带retry逻辑和超时判断。上线后发现:当同时涌入300个请求,Redis锁竞争导致20%请求卡在“查知识库”环节超时,系统自动降级到转人工,结果人工坐席瞬间被打爆。根本原因在于,编排逻辑和执行引擎耦合太紧——状态流转、资源调度、失败重试全混在业务代码里,无法横向扩展,也无法动态调整策略。
第三类:多供应商=手动维护N套API密钥和参数映射表
某金融客户要求“核心问答走本地Qwen2-72B,实时行情解析走讯飞星火,合规审查走某国产私有模型”。开发同学建了个Excel表格,记录每个模型的endpoint、token限制、temperature默认值、stop_token列表。每次换模型,就得改代码、改配置、改测试用例。更麻烦的是,当Qwen2-72B因GPU显存不足开始拒绝请求时,系统无法自动感知并切换到备用模型——因为“健康检查”和“路由决策”根本不在同一层。
XXL-AI的设计起点,就是直面这三类问题。它不追求“支持多少种模型”,而聚焦“当模型不可用时,系统如何优雅降级”;不强调“能编排多复杂流程”,而确保“任意节点失败时,上下游仍可继续工作”;不堆砌“接入了多少RAG工具”,而保证“同一批知识,既能喂给本地模型,也能喂给云端API,还能喂给规则引擎”。
2.2 四层架构:MCP是协议层,SKILL是能力层,RAG是供给层,工程化底座是稳定层
XXL-AI的物理结构不是单体服务,而是四个可独立演进的层次,用标准接口连接:
MCP(Model Control Protocol)层:定义模型调用的统一契约。它不是RESTful API封装,而是类似gRPC的二进制协议,包含
model_id(如qwen2-7b-local)、input_schema(JSON Schema描述输入字段)、output_schema(定义返回结构)、health_check_endpoint(模型健康探针地址)、fallback_policy(降级策略ID)。关键点在于:MCP不关心模型内部实现,只约定“怎么安全、可控地调用它”。比如,当qwen2-7b-local连续3次返回503,MCP层自动触发fallback_policy指向qwen2-1.5b-fallback,且将本次失败计入模型SLA统计。SKILL层:可插拔的能力单元。每个SKILL是一个Docker镜像,包含
skill.yaml(声明能力元信息:名称、版本、所需MCP模型ID、输入输出schema、依赖环境变量)、main.py(实际执行逻辑)、test_cases.json(内置测试用例)。例如ocr-skill:v1.2声明它需要调用mcp-ocr-model,输入是base64图片字符串,输出是结构化JSON。部署时,XXL-AI的SKILL Registry会校验其签名、加载测试用例并运行,通过后才允许注册。这意味着,业务方无需懂Python,只要按规范写个shell脚本调用Tesseract,打包成镜像,就能贡献OCR能力。RAG层:知识供给中枢。它不绑定具体向量库,而是抽象为
KnowledgeSource接口:ingest(document)、search(query, top_k=3)、update_metadata(doc_id, metadata)。当前支持ChromaDB、Weaviate、甚至MySQL全文索引(用于纯文本场景)。重点在于RAG与SKILL的协同:当用户提问含“查看附件图表”,RAG层不仅返回匹配文档,还会标注{"has_image": true, "image_type": "bar_chart"},SKILL Router据此自动调度chart-analyze-skill,而非通用问答SKILL。工程化底座:保障前三层可靠运行的基础设施。包括:
- 流量网关:基于Envoy定制,支持按用户ID/请求路径/模型类型做精细化限流(如“单用户每分钟最多调用5次qwen2-7b”);
- 可观测中心:统一采集MCP调用延迟、SKILL执行耗时、RAG检索召回率,生成SLA看板;
- 配置中心:所有路由策略、fallback规则、SKILL启用开关,均通过Consul动态下发,无需重启服务;
- 离线任务队列:处理知识库批量更新、模型微调等长耗时任务,与在线请求完全隔离。
这四层之间,只有明确定义的接口契约,没有代码依赖。你可以把MCP层换成自研协议,只要实现相同接口;可以替换RAG层为Milvus,只要适配KnowledgeSource;甚至可以把SKILL Registry从Docker Hub迁移到Harbor私有仓库——整个系统不会因此停摆。
2.3 为什么选择MCP而非OpenAI兼容层?SKILL为何不是Function Calling?
这里必须解释两个关键选型背后的工程权衡。
MCP vs OpenAI兼容层:
OpenAI兼容层(如LiteLLM)确实能快速接入多模型,但它本质是“协议翻译器”——把OpenAI格式请求转成各家模型私有格式。问题在于:它无法解决模型能力差异带来的业务逻辑断裂。比如,通义千问支持tools参数调用函数,但某国产模型只支持function_call字段;Ollama本地模型不支持streaming,但云端API必须流式返回。MCP的设计哲学是:不掩盖差异,而是显式声明差异。在MCP注册模型时,必须填写supports_streaming: false、max_context_length: 4096、tool_calling_supported: true。当业务流程需要流式响应时,SKILL Router会自动过滤掉不支持的模型,而不是让调用方收到500错误后再重试。这是把“适配成本”从运行时转移到注册时,换来的是线上稳定性。
SKILL vs Function Calling:
Function Calling是LLM原生能力,但它的致命缺陷是“黑盒调度”——LLM决定调哪个函数、传什么参数,开发者只能事后分析log。而SKILL是白盒可控的:
- 调度决策由SKILL Router基于RAG返回的元信息、用户上下文、预设规则做出,比如“当问题含‘报销’且用户职级为总监,跳过通用问答SKILL,直连
finance-approval-skill”; - 参数注入由XXL-AI框架完成,确保
finance-approval-skill收到的永远是结构化JSON,而非LLM自由发挥的字符串; - 执行结果可被其他SKILL复用,比如
ocr-skill输出的发票金额,可直接作为finance-approval-skill的输入字段,无需LLM再次解析。
这就像工厂流水线:Function Calling是让工人(LLM)自己决定哪道工序交给谁,而SKILL是把每道工序标准化为工位(SKILL),由调度员(Router)按BOM表(规则引擎)精准派单。
3. 核心细节解析:MCP协议设计、SKILL开发规范、RAG协同机制
3.1 MCP协议详解:不只是API,而是模型服务的“交通规则”
MCP协议的核心价值,在于把模型调用从“尽力而为”变成“契约式交付”。它包含三个关键组件:
1. Model Descriptor(模型描述符)
这是MCP注册的入口文件,YAML格式,示例:
model_id: "qwen2-7b-local" version: "1.0.2" provider: "alibaba" endpoint: "http://localhost:8000/v1/chat/completions" health_check: path: "/health" timeout_ms: 5000 interval_ms: 30000 capabilities: supports_streaming: true max_context_length: 4096 tool_calling_supported: true input_schema: type: "object" properties: messages: type: "array" items: type: "object" properties: role: {type: "string", enum: ["user","assistant","system"]} content: {type: "string"} temperature: {type: "number", minimum: 0, maximum: 2} output_schema: type: "object" properties: choices: type: "array" items: type: "object" properties: message: type: "object" properties: content: {type: "string"} tool_calls: type: "array" items: type: "object" properties: function: type: "object" properties: name: {type: "string"} arguments: {type: "string"}提示:
input_schema和output_schema必须严格遵循JSON Schema v7,这是SKILL Router做参数校验的基础。我们曾因某模型厂商把temperature定义为字符串而非数字,导致SKILL Router在调用前校验失败,直接拦截请求——这比让模型返回格式错误更早暴露问题。
2. MCP Gateway(网关)
这是MCP协议的执行者,部署为独立服务。它接收统一格式的请求:
{ "model_id": "qwen2-7b-local", "input": { "messages": [{"role":"user","content":"今天天气如何?"}], "temperature": 0.7 }, "metadata": { "request_id": "req-abc123", "user_id": "u-456", "trace_id": "tr-789" } }网关工作流程:
- 步骤1:根据
model_id查Registry,获取Descriptor,校验input是否符合input_schema; - 步骤2:调用
health_check接口,若失败且存在fallback_policy,则路由至备用模型; - 步骤3:将
input按Descriptor中endpoint格式转换(如OpenAI格式→本地Ollama格式),发起HTTP请求; - 步骤4:收到响应后,按
output_schema校验结构,提取choices[0].message.content作为标准输出; - 步骤5:记录完整调用日志(含耗时、输入token数、输出token数、错误码),上报可观测中心。
3. Fallback Policy(降级策略)
这是MCP区别于普通代理的关键。策略定义为JSON:
{ "policy_id": "high-availability", "rules": [ { "condition": "response_code == 503 || latency > 5000", "action": "switch_to_model", "target_model_id": "qwen2-1.5b-fallback", "retry_times": 2 }, { "condition": "response_code == 429", "action": "rate_limit", "delay_ms": 1000 } ] }注意:降级不是简单切换模型,而是带状态的决策。比如
qwen2-1.5b-fallback可能只支持max_context_length: 2048,当原始请求context超限时,MCP Gateway会自动截断,并在响应头中添加X-MCP-Warning: "context_truncated_to_2048",让上层SKILL知道信息可能不全。
3.2 SKILL开发全流程:从零开始写一个可注册的OCR能力
SKILL不是函数,而是一个最小可行能力单元。以OCR为例,说明完整开发闭环:
步骤1:定义SKILL元信息(skill.yaml)
name: "ocr-skill" version: "1.2.0" description: "调用OCR模型解析图片中的文字,支持中文、英文、数字" author: "vision-team" required_mcp_models: - "mcp-ocr-model" input_schema: type: "object" properties: image_base64: {type: "string", description: "图片base64编码,需含data:image/xxx;base64,"} language: type: "string" enum: ["zh", "en", "auto"] default: "auto" output_schema: type: "object" properties: text: {type: "string", description: "识别出的纯文本"} boxes: type: "array" items: type: "object" properties: x1: {type: "number"} y1: {type: "number"} x2: {type: "number"} y2: {type: "number"} text: {type: "string"}步骤2:编写执行逻辑(main.py)
import json import base64 import requests from urllib.parse import urljoin def handler(event): # 1. 解析输入 try: input_data = json.loads(event['body']) image_b64 = input_data['image_base64'] lang = input_data.get('language', 'auto') except Exception as e: return {"error": f"Invalid input: {str(e)}"} # 2. 调用MCP模型(此处使用XXL-AI提供的MCP Client) mcp_client = get_mcp_client("mcp-ocr-model") # 自动从Registry获取endpoint try: response = mcp_client.invoke({ "image": image_b64, "lang": lang }) # 3. 校验MCP响应是否符合output_schema(框架自动完成,此处仅示意) if not validate_output(response, skill_yaml['output_schema']): raise ValueError("MCP response invalid") return response except Exception as e: return {"error": f"MCP call failed: {str(e)}"} # XXL-AI框架要求的入口函数 def lambda_handler(event, context): return handler(event)步骤3:编写测试用例(test_cases.json)
[ { "name": "test_chinese_receipt", "input": { "image_base64": "data:image/png;base64,iVBORw0KGgoAAAANS...", "language": "zh" }, "expected_output": { "text": "北京朝阳区XX餐厅 2024年5月1日 金额:¥128.00", "boxes": [{"x1":10,"y1":20,"x2":200,"y2":50,"text":"北京朝阳区XX餐厅"}] } } ]步骤4:构建与注册
# 构建Docker镜像 docker build -t ocr-skill:v1.2.0 . # 推送至Registry docker push your-registry/ocr-skill:v1.2.0 # 向XXL-AI注册(需API Key) curl -X POST http://xxl-ai:8000/skill/register \ -H "Authorization: Bearer $API_KEY" \ -F "image=your-registry/ocr-skill:v1.2.0" \ -F "skill_yaml=@skill.yaml" \ -F "test_cases=@test_cases.json"注册过程自动执行:拉取镜像→运行测试用例→校验输出→写入Registry。只有全部测试通过,该SKILL才对业务流程可见。
实操心得:SKILL的
input_schema和output_schema务必精确。我们曾因boxes字段定义为"type": "array"未指定items,导致SKILL Router无法生成类型安全的调用参数,最终在Java调用方出现ClassCastException。教训是:宁可多写几行Schema,也不要依赖“运行时猜测”。
3.3 RAG与SKILL的协同:让知识库不只是“搜索+拼接”
RAG在XXL-AI中不是独立模块,而是SKILL的上游数据源。其协同机制体现在三个层面:
1. 元信息增强(Metadata Enrichment)
RAG索引时,不仅存文本块,还注入业务元信息。例如,某份《员工报销制度V3.2》文档,在切片入库时,自动打标:
{ "content": "单张发票报销上限为5000元...", "source_doc_id": "policy-2024-001", "doc_type": "policy", "effective_date": "2024-03-01", "department": ["finance"], "has_table": true, "has_image": false, "confidence_score": 0.92 }当用户问“最新报销标准”,RAG返回结果会携带这些标签,SKILL Router据此决策:
- 若
doc_type == "policy"且effective_date最新,则调用policy-interpreter-skill; - 若
has_table == true,则额外调度table-extractor-skill解析表格; - 若
confidence_score < 0.8,则触发human-review-skill介入。
2. 检索策略即插即用(Retrieval Strategy Plugin)
RAG层支持多种检索策略,通过插件方式加载:
hybrid-search: BM25 + 向量相似度加权;entity-focused: 先NER识别人名/地名/金额,再用这些实体做二次检索;time-aware: 对时效性敏感的查询(如“本月股价”),优先返回published_at近的文档。
策略选择由SKILL Router根据问题类型自动匹配。例如,当问题含“股价”“涨跌幅”,自动启用time-aware;当问题含“张三”“北京”,启用entity-focused。
3. 知识供给闭环(Feedback Loop)
RAG支持用户反馈修正。当用户点击“答案不准确”,系统记录:
- 原始问题、RAG返回的chunk ID、用户标注的正确答案;
- 这些数据进入离线队列,由
rag-retrainer-skill每日执行:- 用正确答案微调embedding模型;
- 将错误chunk标记为
deprecated,降低其检索权重; - 生成新的FAQ对,加入训练集。
注意:RAG知识库本身不存储图片,但可存储图片的OCR文本、视觉特征向量、以及指向原始图片URL的元信息。当SKILL需要处理图片时,RAG返回
{"image_url": "https://oss.example.com/receipt.jpg", "ocr_text": "..."},由image-downloader-skill下载并传递给ocr-skill。这是解耦设计——RAG专注知识表示,SKILL专注能力执行。
4. 实操过程:从零搭建一个“合同智能比对”应用
4.1 需求还原:业务方的真实痛点
某律所提出需求:“我们每天要审30份采购合同,主要看付款条款、违约责任、知识产权归属三处。现在靠律师肉眼比对,平均耗时45分钟/份,且易漏看小字条款。”他们不要“AI写合同”,只要“AI帮人快速定位差异”。
传统方案是:用LLM读两份PDF,输出差异摘要。但我们实测发现:
- PDF解析质量参差,表格错位、页眉页脚混入正文;
- LLM对“违约金5%”和“违约金每日0.05%”这种数值差异识别不准;
- 当合同含扫描件(非文字PDF),LLM直接失效。
XXL-AI的解法是:把任务拆解为可验证的SKILL链。
4.2 SKILL链设计:6个原子能力串联
整个流程不依赖单一LLM,而是6个SKILL按序协作:
pdf-parser-skill: 解析PDF,区分文字页/扫描页,输出结构化JSON(含章节标题、段落文本、图片base64);ocr-skill: 对扫描页调用OCR,输出文本;clause-extractor-skill: 基于规则+小模型,从文本中提取“付款条款”“违约责任”等段落;diff-engine-skill: 对比两份合同的对应条款,逐句计算编辑距离,标记差异位置;legal-interpretation-skill: 调用法律专用模型,解释“违约金5%”与“每日0.05%”的实际年化利率差异;report-generator-skill: 生成HTML报告,高亮差异处,附法律解释。
每个SKILL都可独立测试、独立升级。比如,当pdf-parser-skill升级到v2.0,支持更好处理表格,只需重新注册,不影响其他SKILL。
4.3 MCP模型注册:为法律模型定制Descriptor
为legal-interpretation-skill注册专用模型:
model_id: "law-llm-v3" version: "3.1.0" provider: "legal-ai-inc" endpoint: "https://api.law-ai.com/v1/interpret" health_check: path: "/status" timeout_ms: 10000 capabilities: supports_streaming: false max_context_length: 8192 tool_calling_supported: false input_schema: type: "object" properties: clause_a: {type: "string", description: "条款A原文"} clause_b: {type: "string", description: "条款B原文"} context: {type: "string", description: "相关法律条文摘要"} output_schema: type: "object" properties: interpretation: {type: "string"} risk_level: type: "string" enum: ["low", "medium", "high"] citation: {type: "string", description: "引用的法律条文编号"}注意tool_calling_supported: false——此模型不支持函数调用,SKILL Router不会尝试让它调用其他SKILL,避免无效请求。
4.4 RAG知识库构建:法律条文的结构化索引
知识库不存整部《民法典》,而是按条文切片:
{ "content": "当事人一方不履行合同义务或者履行合同义务不符合约定的,应当承担继续履行、采取补救措施或者赔偿损失等违约责任。", "source": "中华人民共和国民法典 第五百七十七条", "tags": ["contract", "liability", "breach"], "vector": [0.12, -0.45, ...] // 768维向量 }当legal-interpretation-skill需要法律依据时,RAG层根据tags和语义相似度,返回最相关的3条法条,作为context字段注入模型输入。
4.5 配置SKILL Router规则:让流程“懂业务”
在XXL-AI控制台配置Router规则:
| 触发条件 | 动作 | 备注 |
|---|---|---|
input.contains("合同比对") && input.files.length == 2 | 启动contract-diff-flow | 主流程入口 |
pdf-parser-skill.output.has_scanned_page == true | 在流程中插入ocr-skill | 条件分支 |
diff-engine-skill.output.edit_distance > 0.3 | 调用legal-interpretation-skill | 差异显著才解释 |
legal-interpretation-skill.output.risk_level == "high" | 在报告中添加红色警示图标 | 输出增强 |
这些规则以JSON存储,可版本化管理。当律所新增“涉外合同”审核需求,只需新增一条规则,无需改代码。
4.6 上线效果与性能数据
在律所生产环境运行3个月后数据:
- 平均处理时长:从45分钟/份 →3分28秒/份(含PDF解析、OCR、比对、解释、报告生成);
- 差异检出率:人工复查确认99.2%的条款差异被准确标记;
- 模型切换:因
law-llm-v3服务商临时维护,MCP层自动降级至law-llm-v2,用户无感知,仅解释深度略有下降; - SKILL复用:
pdf-parser-skill和diff-engine-skill被复用于“招标文件比对”新需求,开发耗时从3天缩短至2小时。
实操心得:不要试图用一个大模型解决所有问题。我们最初想让
law-llm-v3直接处理PDF,结果因上下文长度限制,不得不截断文档,导致关键条款丢失。拆成SKILL链后,每个环节专注一件事,精度和稳定性反而提升。工程化不是炫技,是把不确定性关进笼子。
5. 常见问题与排查技巧实录:来自生产环境的21个真实Case
5.1 MCP层问题:模型注册后调用失败的5种原因
| 现象 | 排查步骤 | 根本原因 | 解决方案 |
|---|---|---|---|
MCP Gateway returns 400 Bad Request | 查Gateway日志,看input validation error详情 | input_schema中某字段定义为required,但调用方未传 | 修改skill.yaml,将该字段设为optional,或强制调用方传空值 |
MCP Gateway hangs for 30s then timeout | curl -v http://mcp-gateway:8000/health,检查健康探针 | 模型服务/health接口未实现或超时 | 在模型服务中添加轻量健康检查端点,响应时间<100ms |
Fallback policy not triggered | 查Registry中模型的health_check.interval_ms | 健康检查间隔设为30000ms,但故障在两次检查之间发生 | 缩短interval_ms至5000ms,或增加latency_threshold触发条件 |
MCP response contains unexpected field | 对比output_schema与实际返回JSON | 模型返回了usage字段,但output_schema未声明 | 在output_schema中添加"usage": {"type": "object"},或在Gateway中过滤掉非声明字段 |
Same model_id registered twice with different endpoints | GET /mcp/models?q=model_id:qwen2-7b-local | 运维误操作,重复注册 | 删除旧注册项,确保model_id全局唯一 |
关键技巧:MCP Gateway日志必须包含
request_id和model_id,否则在分布式环境下无法追踪。我们在日志中强制添加X-Request-ID头,并在所有下游服务中透传。
5.2 SKILL层问题:注册成功但执行异常的7类陷阱
| 现象 | 排查步骤 | 根本原因 | 解决方案 |
|---|---|---|---|
SKILL test fails with 'ModuleNotFoundError' | 进入容器docker exec -it <container> sh,执行pip list | requirements.txt未声明某依赖包 | 在requirements.txt中明确写出所有依赖,包括pandas==1.5.3(避免版本冲突) |
SKILL runs but returns empty output | 查容器stdout日志,看是否有WARNING: no output returned | main.py中未return结果,或return了None | 确保handler函数末尾有return result,且result为非None字典 |
SKILL timeout after 60s | 查skill.yaml中timeout_seconds字段 | 默认超时60s,但OCR处理大图需90s | 在skill.yaml中显式设置timeout_seconds: 120 |
SKILL receives malformed input | 查Gateway日志中input validation passed | SKILL Router未校验输入,直接转发 | 在skill.yaml的input_schema中严格定义所有字段,启用Gateway校验 |
SKILL output doesn't match output_schema | 用jsonschema.validate()本地测试输出 | 模型返回"risk_level": "HIGH",但schema定义为enum: ["low","medium","high"] | 统一大小写,或在SKILL中做转换:output["risk_level"] = output["risk_level"].lower() |
SKILL can't access environment variable | 进入容器执行printenv | grep MY_VAR | 环境变量未在docker run时传入,或skill.yaml未声明env_vars | 在skill.yaml中声明env_vars: ["MY_API_KEY"],部署时自动注入 |
SKILL test passes locally but fails in Registry | 查Registry执行日志,看docker run命令 | 本地测试用Python3.9,Registry用Python3.8,某语法不兼容 | 在Dockerfile中固定Python版本,如FROM python:3.8-slim |
独家避坑:SKILL的
test_cases.json必须覆盖边界情况。我们曾因测试用例只用正常图片,上线后遇到用户上传10MB扫描件,ocr-skill内存溢出。现在强制要求:每个SKILL至少包含1个超大输入、1个空输入、1个非法输入的测试用例。
5.3 RAG层问题:检索不准与知识陈旧的9个根因
| 现象 | 排查步骤 | 根本原因 | 解决方案 |
|---|---|---|---|
RAG returns irrelevant chunks | 查chroma collection.count(),看文档总数 | 文档未正确切片,单个chunk过大(>1000字符) | 使用semantic-chunking策略,按句子边界切分,最大chunk size设为512 |
RAG misses recent updates | 查ingest任务日志,看最后执行时间 | 知识同步脚本未配置定时任务,或权限不足 | 用cron+curl定时触发/rag/ingestAPI,日志记录每次同步的文档数 |
RAG retrieval slow (>2s) | EXPLAIN QUERY PLAN查ChromaDB查询 | 向量索引未建立,或`n_results |