ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

XXL-AI:面向生产的AI应用组装流水线架构解析

XXL-AI:面向生产的AI应用组装流水线架构解析

1. 项目概述:一个真正能落地的AI应用开发平台长什么样?

最近在几个技术社区里,反复看到“XXL-AI”这个词被高频提起,不是作为某个大厂新发布的PPT概念,而是真实出现在工程师的本地开发日志、团队内部技术选型会议纪要,甚至某家教育科技公司的交付清单里。它不叫“XXL-AI平台”,就叫“XXL-AI”——三个字母缩写背后,没有堆砌术语的宣传页,只有一份清晰的README和一套跑得通的Docker Compose启动脚本。我第一次把它拉下来跑起来,是在一个周五下午,用公司配的那台i7-11800H+32G的开发机,从clone仓库到看到Web UI上显示“Agent编排画布已就绪”,总共花了6分23秒。这不是演示,是实打实的工程化起点。

XXL-AI的核心定位非常直白:它不是另一个LLM推理服务封装器,也不是又一个带点拖拽界面的Prompt Playground。它是一个面向生产环境的AI应用组装流水线。你手头有OpenAI、Qwen、DeepSeek、Ollama本地模型,它不强制你绑定某一家;你想让一个客服Agent调用企业ERP接口查订单,再让另一个Agent根据查到的数据生成售后建议并自动发邮件,它提供的是把这两个Agent像乐高一样拼在一起的物理卡扣——不是靠写一堆if-else胶水代码,而是通过标准化的协议和可复用的组件。这里的“MCP + SKILL + RAG”不是三个并列名词的简单罗列,而是一套分层解耦的设计哲学:MCP(Model Control Protocol)是底层通信总线,SKILL是功能原子单元,RAG是数据感知层。三者嵌套,形成“协议定义交互 → 技能封装能力 → 知识增强决策”的闭环。它解决的不是“能不能调API”,而是“怎么让10个不同背景的工程师,在两周内协作上线一个含5个Agent、3类外部系统集成、支持2000并发查询的智能工单系统”。如果你正在被“模型调不通”、“Agent之间传参错乱”、“RAG召回结果总是驴唇不对马嘴”这些问题反复折磨,XXL-AI不是银弹,但它把那些散落在各处的、需要自己造轮子的工程细节,提前焊死在底座里了。

2. 整体架构设计与核心思路拆解

2.1 为什么必须是“MCP + SKILL + RAG”三层嵌套?而不是直接封装成一个大模型API?

这个问题我问过XXL-AI的主创团队(私下交流),也自己推演过三个月。答案很实在:因为AI应用的本质是状态机与数据流的混合体,不是单次函数调用。举个最典型的例子:一个“智能会议纪要助手”Agent,它的完整流程是:1)接收一段1小时的语音转文字文本;2)识别其中提到的“待办事项”和“负责人”;3)去企业通讯录里查出负责人邮箱;4)调用邮件服务发送确认邮件;5)把待办事项同步到飞书多维表格。这个过程里,步骤1和2是纯LLM任务,步骤3和4是确定性API调用,步骤5是结构化数据写入。如果强行用一个大模型API包打天下,要么模型得记住所有API文档(不现实),要么每次都要人工写Prompt描述调用逻辑(不可维护)。XXL-AI的三层设计,就是把这五步里的“不确定性计算”和“确定性操作”彻底分开。

  • MCP层(Model Control Protocol):它不是什么玄乎的新协议,本质是一套轻量级JSON-RPC 2.0规范的工程化实现。每个Agent启动时,会向MCP注册自己的能力声明(比如{"name":"email_sender","input_schema":{"to":"string","subject":"string","body":"string"},"output_schema":{"status":"string","message_id":"string"})。当编排画布上A Agent需要调用B Agent时,不是硬编码URL或写curl命令,而是通过MCP Broker统一转发请求。Broker负责超时控制、重试策略、调用链追踪。我实测过,当一个Agent因网络抖动响应超时,MCP Broker会在300ms内返回标准错误码,而不是让上游Agent卡死等待。这层存在的意义,是让“谁调用谁”这件事,从代码层面下沉到基础设施层面,彻底解耦。

  • SKILL层(Skill):这是开发者真正写业务逻辑的地方。一个SKILL就是一个独立的Python模块(也可以是Go/Java),它只做一件事:接收MCP定义的输入,执行具体操作,返回MCP定义的输出。比如上面的邮件发送SKILL,核心代码就20行:解析输入JSON、调用SMTP库、构造邮件、发送、返回结果。它不关心自己被谁调用,也不关心调用方用什么模型。SKILL的生命周期由XXL-AI的Skill Manager统一管理——启动、热更新、资源隔离(CPU/Memory限制)、健康检查。我见过最狠的实践,是把一个处理PDF解析的SKILL单独分配2GB内存和1个专用CPU核,避免它吃掉整个Agent进程的资源。SKILL不是插件,它是被调度的“服务单元”。

  • RAG层(Retrieval-Augmented Generation):这里的关键认知是:RAG不是给模型喂知识,而是给Agent装“外挂大脑”。XXL-AI的RAG模块默认不走LangChain那种通用抽象层,而是强制要求每个RAG实例绑定明确的“知识域”和“检索策略”。比如“HR政策知识库”这个RAG实例,它的向量库只存公司制度PDF,检索时强制启用“语义+关键词混合召回”,且对“年假天数”、“试用期工资”等高频词做同义词扩展。而“产品手册知识库”的RAG实例,则启用“段落级细粒度切分+标题权重提升”。这种设计牺牲了“一键接入所有文档”的便利性,但换来的是召回准确率从62%提升到89%(我们内部AB测试数据)。RAG在这里不是附加功能,是Agent决策链路中一个可配置、可监控、可替换的“数据感知节点”。

这三层不是平行关系,而是嵌套依赖:一个Agent的执行流程,必然先经过MCP路由到指定SKILL,SKILL在执行过程中,按需触发绑定的RAG实例获取上下文,再把结果喂给LLM做最终生成。这种强约束的设计,让整个系统具备了可测试性——你可以单独对一个SKILL做单元测试,可以对一个RAG实例做召回率压测,可以对MCP Broker做百万级并发压力测试。这才是工程化底座的真意:不是功能多,而是每个环节都经得起锤。

2.2 “多供应商”支持到底意味着什么?是简单换API Key吗?

网上很多文章把“支持多模型供应商”写成“只需修改config.yaml里的model_name字段”,这严重误导了实践者。XXL-AI的多供应商支持,是贯穿MCP/SKILL/RAG全栈的适配体系,核心在于协议兼容性而非参数替换。

首先看MCP层。不同厂商的API返回格式千差万别:OpenAI返回{"choices":[{"message":{"content":"xxx"}}]},Qwen返回{"output":{"text":"xxx"}},Ollama返回{"response":"xxx"}。如果让每个SKILL自己去解析,代码会变得极其脆弱。XXL-AI的做法是:在MCP Broker内部,为每个供应商预置一个“Adapter”。当你在Agent配置里指定provider: "qwen"时,Broker会自动加载Qwen Adapter,把原始响应统一转换成MCP标准格式{"result":"xxx","usage":{"prompt_tokens":123,"completion_tokens":45}}。这个Adapter是开源的,你可以自己写一个适配私有模型的Adapter,只要实现parse_response()和build_request()两个方法就行。

再看SKILL层。有些SKILL对模型能力有强依赖,比如一个“SQL生成SKILL”,需要模型理解数据库Schema。OpenAI的gpt-4-turbo和Qwen2-72B在SQL生成上的表现差异很大。XXL-AI允许你在SKILL配置里声明required_capabilities: ["sql_generation", "schema_understanding"],然后系统会根据当前可用的供应商能力矩阵,自动选择最匹配的那个。我们实测过,当同时配置了OpenAI和Qwen,一个需要复杂JOIN的SQL生成请求,会被路由到Qwen2-72B;而一个需要强逻辑推理的数学题求解请求,则被路由到gpt-4-turbo。这不是负载均衡,而是能力路由。

最后是RAG层。不同模型对向量嵌入的要求不同:text-embedding-ada-002要求向量维度1536,bge-m3要求1024,而Qwen2-vl的多模态嵌入又是另一套。XXL-AI的RAG模块在创建知识库时,会强制选择与当前主力模型匹配的Embedding Model,并在索引构建阶段完成向量化。更关键的是,它支持“跨模型RAG”——同一个知识库,可以同时为OpenAI和Qwen提供服务,因为底层存储的是标准化的向量ID,检索时按需调用对应模型的Embedding服务。我们有个客户,用同一套产品手册知识库,同时支撑着面向海外用户的OpenAI版客服Agent和面向国内用户的Qwen版客服Agent,知识库更新一次,两边自动生效。

所以,“多供应商”在XXL-AI里,不是让你在config里改个字符串,而是给你一套完整的、可组合的、可验证的能力调度框架。它解决的不是“能不能用”,而是“怎么用得稳、用得准、用得省”。

2.3 “Agent编排”为什么不用现成的LangGraph或LlamaIndex?自研画布的价值在哪?

这个问题我被问过至少二十次。答案很直接:LangGraph解决的是“怎么编排”,XXL-AI解决的是“编排完怎么活下去”。LangGraph的画布是个漂亮的可视化DSL,但它生成的DAG图,一旦部署到生产环境,你就得自己搞定:Agent状态持久化怎么做?失败后如何回滚到上一个节点?并发请求下如何保证数据一致性?监控指标怎么采集?这些都不是LangGraph的设计目标。

XXL-AI的编排画布(Canvas)表面看是个拖拽界面,底层却是一个带事务语义的状态机引擎。每个节点(Node)不只是一个函数,而是一个包含“执行逻辑”、“失败策略”、“重试配置”、“超时阈值”、“数据映射规则”的完整实体。举个真实案例:我们为客户做的“采购审批Agent”,流程是:用户提交申请 → 调用RAG查采购政策 → SKILL调用ERP校验预算 → SKILL调用OA发起审批流 → 等待OA回调 → 发送通知。这个流程里,第4步“等待OA回调”是个长时任务(可能几小时),传统编排框架遇到这种节点,要么阻塞整个流程(不可行),要么自己写异步轮询(难维护)。XXL-AI的Canvas节点类型里,专门有一个“WaitForEvent”节点,它会把当前执行上下文序列化存入Redis,释放Worker线程,当OA系统通过Webhook推送回调事件时,系统自动唤醒该上下文,继续执行后续节点。整个过程对开发者透明,你只需要在画布上拖一个“WaitForEvent”节点,配置好事件名称和超时时间即可。

更关键的是可观测性。Canvas生成的每个执行实例(Execution),都有唯一的Trace ID,全程记录:每个节点的输入/输出、耗时、调用的SKILL版本、触发的RAG查询关键词、MCP调用的供应商、LLM的token消耗。这些数据实时推送到Prometheus,你可以用Grafana看“过去一小时,所有采购审批流程中,RAG召回失败率最高的三个知识域”。这种级别的监控,不是加个日志中间件就能实现的,它需要从编排引擎底层就内置结构化埋点。

所以,XXL-AI的画布不是为了炫技,而是把AI应用开发中那些“上线后才暴露”的运维难题,在设计阶段就固化进工具链。它不阻止你用LangGraph写原型,但当你需要把原型变成每天处理5000+请求的生产系统时,Canvas提供的工程保障,才是真正的护城河。

3. 核心模块详解与实操要点

3.1 MCP协议:不止是RPC,更是Agent间的“交通规则”

MCP(Model Control Protocol)在XXL-AI里,绝不是简单的API网关。它是整个系统得以稳定运行的“交通指挥中心”。理解MCP,不能只看它的JSON-RPC格式,更要理解它定义的三套核心规则:注册发现规则、调用契约规则、故障熔断规则。

  • 注册发现规则:每个SKILL启动时,会向MCP Registry(基于etcd实现)注册自己的服务元数据,包括服务名、版本号、健康检查端点、支持的输入/输出Schema、所在主机IP和端口。Registry不是静态配置中心,而是动态服务发现中枢。当Canvas画布上配置了一个名为user_profile_fetcher的SKILL调用节点时,MCP Broker不会去读config.yaml,而是实时查询Registry,找到最新健康的user_profile_fetcher:v2.3实例列表。如果Registry里同时存在v2.2和v2.3两个版本,Broker会按权重路由(v2.3权重80%,v2.2权重20%),实现灰度发布。我们线上就用这套机制,把新版本的用户画像SKILL先导10%流量,观察一周无异常后再全量。

  • 调用契约规则:这是MCP最硬核的部分。它强制要求每个SKILL在注册时,必须提供精确的JSON Schema描述其输入和输出。比如一个invoice_parserSKILL,其输入Schema必须包含{"type":"object","properties":{"pdf_bytes":{"type":"string","format":"base64"},"file_name":{"type":"string"}}}。当Broker收到上游Agent的调用请求时,会先用这个Schema做严格校验。如果请求里漏了file_name字段,或者pdf_bytes不是base64字符串,Broker会立即返回400 Bad Request,附带详细的校验错误信息(如"field 'file_name' is required")。这杜绝了90%以上的“参数传错导致SKILL崩溃”的问题。更妙的是,Canvas画布在拖拽连接线时,会自动读取上下游SKILL的Schema,只允许连接“输出字段能完全覆盖输入字段”的节点。比如ocr_resultSKILL输出{"text":"xxx","pages":2},那么它只能连到输入Schema里包含text字段的SKILL,连不到只要求image_url的SKILL。这种设计,让编排错误在UI设计阶段就被拦截。

  • 故障熔断规则:MCP Broker内置了滑动窗口熔断器。默认配置是:每10秒统计一次对某个SKILL的调用,如果失败率超过50%且失败次数≥5次,就触发熔断,接下来30秒内所有对该SKILL的调用都会被Broker直接拒绝,返回503 Service Unavailable。熔断期间,Broker会持续探测SKILL的健康检查端点(HTTP GET /health),一旦探测成功,立即恢复流量。我们曾在线上遇到一个因数据库连接池耗尽而间歇性失败的order_checkerSKILL,MCP的熔断机制让它在故障期间自动隔离,避免了雪崩效应。熔断参数(窗口大小、失败率阈值、熔断时长)都可以在Broker配置里动态调整,无需重启。

实操中,我强烈建议新手从MCP的调试模式开始。启动XXL-AI时加上--mcp-debug=true参数,Broker会把所有进出的JSON-RPC消息打印到console,格式清晰,包含时间戳、Trace ID、请求ID。你可以亲眼看到:Canvas画布上拖拽一个节点,背后发生了几次MCP注册;点击“执行”,Broker如何解析Schema、如何路由、如何处理失败。这种透明性,是快速建立信任感的关键。不要跳过这一步,直接写业务逻辑。

3.2 SKILL开发:从“写函数”到“交付服务”的范式转变

在XXL-AI里开发一个SKILL,和写一个普通Python函数有本质区别。它不是“实现一个功能”,而是“交付一个可管理、可监控、可伸缩的服务单元”。整个流程被固化为五个强制步骤,缺一不可:

  1. 定义SKILL Manifest(清单文件):在SKILL根目录下,必须有一个skill.yaml文件。它不是可选配置,而是SKILL的“身份证”。内容示例:

    name: "email_sender" version: "1.2.0" description: "Send email via SMTP, with template rendering" input_schema: type: object properties: to: type: string format: email subject: type: string maxLength: 100 body_template: type: string enum: ["welcome", "order_confirm", "password_reset"] context_data: type: object output_schema: type: object properties: status: type: string enum: ["success", "failed"] message_id: type: string nullable: true resources: cpu_limit: "1000m" memory_limit: "512Mi" timeout_seconds: 30

    这个文件决定了SKILL如何被注册、如何被校验、如何被调度。resources部分尤其重要,它告诉Skill Manager这个SKILL最多能用多少CPU和内存,超限会被OOM Killer干掉。我们有个SKILL因为没设memory_limit,在处理大附件邮件时把整个Agent进程拖垮了,教训深刻。

  2. 实现核心逻辑(main.py):必须实现一个execute(input_data: dict) -> dict函数。input_data是MCP Broker解析并校验后的纯净字典,dict是你的返回结果。XXL-AI SDK提供了@skillexecutor装饰器,帮你自动处理异常捕获、日志记录、性能计时。示例代码:

    from xxl_ai.skill import skillexecutor import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart @skillexecutor def execute(input_data): # 1. 解析输入(已由MCP校验,可放心使用) to_email = input_data["to"] subject = input_data["subject"] template_name = input_data["body_template"] context = input_data.get("context_data", {}) # 2. 渲染模板(这里简化,实际用Jinja2) body = f"Hello {context.get('user_name', 'there')}, your order {context.get('order_id')} is confirmed." # 3. 发送邮件 msg = MIMEMultipart() msg['From'] = "no-reply@company.com" msg['To'] = to_email msg['Subject'] = subject msg.attach(MIMEText(body, 'plain')) try: server = smtplib.SMTP('smtp.company.com', 587) server.starttls() server.login("service@company.com", "app_password") server.send_message(msg) server.quit() return {"status": "success", "message_id": "msg_" + str(hash(to_email))} except Exception as e: # 3. 返回结构化错误,便于上层Agent处理 return {"status": "failed", "error": str(e)}

    关键点:绝不抛出未捕获异常。@skillexecutor会把任何异常转为标准错误响应,确保Broker不会崩溃。

  3. 编写健康检查(health.py):必须提供一个check_health() -> bool函数。它会被MCP Registry定期调用(默认30秒一次)。这个函数应该检查SKILL依赖的所有外部服务是否可用。比如email_sender的健康检查,不仅要检查自身进程,还要尝试连接SMTP服务器并发送一个测试HELO命令。如果健康检查失败,Registry会立即将该实例从服务列表中剔除。

  4. 打包为容器镜像:XXL-AI要求SKILL必须以Docker镜像形式交付。SDK提供了xxl-skill-build命令,自动读取skill.yaml,生成标准Dockerfile,构建镜像,并推送到配置的Registry(如Harbor)。镜像标签必须是<skill-name>:<version>,比如email_sender:1.2.0。这保证了SKILL的版本、依赖、环境完全一致,杜绝了“在我机器上能跑”的问题。

  5. 部署与注册:最后一步,不是手动docker run,而是通过XXL-AI的CLI工具xxl-cli skill deploy --image email_sender:1.2.0。这个命令会把镜像信息、skill.yaml元数据一起提交给Skill Manager,Manager负责拉取镜像、启动容器、执行健康检查、向MCP Registry注册。整个过程是幂等的,重复执行只会更新版本,不会产生重复实例。

这种严格的交付流程,看起来比写个Flask API麻烦十倍。但它带来的好处是:当线上出现故障时,你能精准定位到是哪个SKILL的哪个版本出了问题;当需要升级时,可以一键灰度;当团队协作时,新人拿到skill.yaml就能立刻理解这个SKILL的契约和边界。这才是工程化的代价与回报。

3.3 RAG扩展:不是“加个知识库”,而是重构Agent的认知链路

XXL-AI的RAG模块,设计初衷就不是做一个通用的“文档问答机器人”。它的目标是:让Agent在做每一个决策时,都能精准、低延迟、可追溯地获取到它此刻最需要的那一小片知识。因此,它的RAG不是单一服务,而是一套可插拔、可组合、可审计的知识增强管道。

核心组件有三个:

  • Ingestion Pipeline(摄入管道):这是RAG的“入口安检”。它不接受原始PDF/Word直接上传,而是强制要求你定义一个ingestion_config.yaml。这个配置文件规定了:文档来源(S3 bucket路径 or local folder)、文件类型过滤(只处理.pdf和.md)、切分策略(按章节?按段落?按固定token数?)、元数据提取规则(从PDF标题提取department,从文件名提取version)、Embedding Model选择(必须与后续检索模型匹配)。我们曾用这个管道,把2000份分散在不同部门共享盘里的产品手册,自动归类、切分、打标、向量化,整个过程无人值守。关键是,ingestion_config.yaml本身是版本化的,每次知识库更新,都对应一个Git commit,谁在什么时候更新了什么知识,一目了然。

  • Retrieval Engine(检索引擎):XXL-AI默认集成两种引擎:ChromaDB(轻量级,适合中小知识库)和Elasticsearch(企业级,支持复杂过滤和聚合)。选择哪个,取决于你的知识规模和查询复杂度。更重要的是,它支持“混合检索”:先用向量检索找Top-K相似片段,再用关键词检索(BM25)在这些片段里精筛,最后用规则引擎(如department == "HR" AND effective_date <= today)做过滤。这种组合,比纯向量检索的准确率高出35%(我们内部测试)。而且,每次检索请求,引擎都会返回完整的retrieval_trace,包含:原始查询、向量相似度分数、关键词匹配度、规则过滤结果、最终返回的chunk列表及其来源文档。这个trace会随Agent执行日志一起存档,方便事后审计。

  • Augmentation Context(增强上下文):这是RAG与Agent的“神经接驳点”。当一个Agent节点配置了RAG增强,Canvas画布上会出现一个“RAG Context”输入框。你在这里写的,不是Prompt,而是上下文注入指令。例如:

    # 检索HR政策知识库,聚焦“试用期”相关条款 knowledge_domain: hr_policy query: "试用期员工的社保缴纳规定" max_chunks: 3 inject_as: "system_prompt_append"

    inject_as参数决定了知识如何融入LLM上下文:system_prompt_append是追加到System Prompt末尾,user_message_prepend是插入到User Message开头,structured_json是把检索结果格式化为JSON数组传入。这种精细控制,避免了“把整篇政策文档塞给模型”导致的token浪费和幻觉。我们有个金融风控Agent,对“反洗钱”知识的检索,就用了structured_json,把召回的3条监管条款,以{"regulation_id":"AML-2023-01","text":"...","effective_date":"2023-01-01"}格式传入,模型生成的风控建议,天然就带上了条款溯源。

实操中最容易踩的坑,是忽视RAG的“冷启动”成本。向量库构建不是瞬间完成的。一个10GB的PDF集合,用bge-m3模型切分,可能需要2小时。XXL-AI提供了xxl-rag ingest --dry-run命令,可以预估时间和资源消耗。我的经验是:永远先用--dry-run,再决定是否在生产环境执行。另外,RAG知识库的更新,不是“删库重建”,而是增量更新。ingestion_pipeline支持--since-last-update参数,只处理新增或修改的文件,这对大型知识库至关重要。

4. 完整实操流程:从零开始搭建一个“智能招聘助手”Agent

4.1 环境准备与基础服务启动

在开始之前,请确保你的机器满足最低要求:Linux/macOS系统,Docker 24.0+,Docker Compose v2.20+,8GB以上可用内存。Windows用户请使用WSL2,不要用Docker Desktop的Windows容器模式,会有兼容性问题。

第一步,克隆官方仓库并进入目录:

git clone https://github.com/xxl-ai/xxl-ai.git cd xxl-ai

第二步,检查并修改.env文件。这是整个系统的心脏配置,必须仔细核对:

# XXL-AI核心服务 XXL_AI_VERSION=1.5.0 XXL_AI_DEBUG=false # MCP Broker配置 MCP_BROKER_PORT=8080 MCP_REGISTRY_URL=etcd://localhost:2379 # 数据库(PostgreSQL用于存储Agent配置、执行日志) POSTGRES_HOST=postgres POSTGRES_PORT=5432 POSTGRES_DB=xxl_ai POSTGRES_USER=xxl_user POSTGRES_PASSWORD=strong_password_here # Redis(用于状态存储、缓存、消息队列) REDIS_URL=redis://redis:6379/0 # RAG存储(默认用ChromaDB,生产环境建议换Elasticsearch) CHROMADB_PERSIST_DIR=./data/chroma # 外部模型API(示例,按需修改) OPENAI_API_KEY=sk-... QWEN_API_KEY=... OLLAMA_BASE_URL=http://ollama:11434

提示:POSTGRES_PASSWORD和REDIS_PASSWORD必须设置,且不能为空。CHROMADB_PERSIST_DIR路径要确保Docker容器有写入权限。

第三步,启动所有基础服务。XXL-AI使用Docker Compose编排,一条命令搞定:

docker compose up -d --build

这条命令会启动:PostgreSQL、Redis、Etcd(MCP Registry)、ChromaDB、Nginx(反向代理)、以及核心的XXL-AI Web Server。启动过程约需2-3分钟。你可以用docker compose logs -f web实时查看Web服务日志,直到看到INFO: Application startup complete。

第四步,验证服务健康。打开浏览器,访问http://localhost:8000,你应该能看到XXL-AI的登录页面(默认账号admin/admin)。登录后,进入“系统监控”面板,确认所有服务状态为绿色。特别注意mcp-broker和skill-manager两个服务,它们是Agent运行的基石。如果它们是红色,说明MCP Registry(Etcd)没连上,检查.env里的MCP_REGISTRY_URL是否正确。

注意:首次启动后,Web UI会自动初始化一个默认的default工作空间(Workspace)。所有后续的Agent、SKILL、RAG知识库,都归属于某个Workspace。这是多租户的基础,也是权限隔离的起点。不要删除default空间,除非你清楚后果。

4.2 创建第一个SKILL:“职位JD解析器”

现在,我们来创建一个真实的SKILL:job_description_parser。它的功能是:接收一份招聘JD文本,提取出职位名称、所需技能、学历要求、工作经验年限等结构化信息。

首先,创建SKILL目录结构:

mkdir -p skills/job_description_parser cd skills/job_description_parser

创建skill.yaml(定义契约):

name: "job_description_parser" version: "1.0.0" description: "Parse job description text into structured JSON" input_schema: type: object properties: jd_text: type: string minLength: 100 maxLength: 10000 output_schema: type: object properties: position_name: type: string maxLength: 100 required_skills: type: array items: type: string maxItems: 20 education_requirement: type: string enum: ["高中", "大专", "本科", "硕士", "博士", "不限"] years_of_experience: type: integer minimum: 0 maximum: 20 resources: cpu_limit: "500m" memory_limit: "256Mi" timeout_seconds: 60

创建main.py(核心逻辑):

from xxl_ai.skill import skillexecutor import re import json @skillexecutor def execute(input_data): jd_text = input_data["jd_text"] # 简单的正则提取(实际项目中应使用LLM,此处为演示) result = { "position_name": "", "required_skills": [], "education_requirement": "不限", "years_of_experience": 0 } # 提取职位名称(通常在第一行或包含“招聘”字样) position_match = re.search(r'招聘\s*([^\n]+?)\s*(?:岗位|职位|:)', jd_text, re.IGNORECASE | re.DOTALL) if position_match: result["position_name"] = position_match.group(1).strip() # 提取技能(常见关键词) skill_keywords = ["Python", "Java", "SQL", "React", "TensorFlow", "Docker", "Kubernetes"] for skill in skill_keywords: if re.search(rf'\b{skill}\b', jd_text, re.IGNORECASE): result["required_skills"].append(skill) # 提取学历要求 edu_patterns = [ (r'硕士', '硕士'), (r'博士', '博士'), (r'本科', '本科'), (r'大专', '大专'), (r'高中', '高中'), (r'不限', '不限') ] for pattern, edu in edu_patterns: if re.search(pattern, jd_text, re.IGNORECASE): result["education_requirement"] = edu break # 提取经验年限 exp_match = re.search(r'(\d+)\s*年', jd_text) if exp_match: result["years_of_experience"] = int(exp_match.group(1)) return result

创建health.py(健康检查):

def check_health(): # 这里可以添加更复杂的检查,比如测试正则引擎 try: import re test = re.search(r'test', 'test string') return test is not None except: return False

现在,打包并部署这个SKILL:

# 确保在skills/job_description_parser目录下 xxl-skill-build --tag job_description_parser:1.0.0 xxl-cli skill deploy --image job_description_parser:1.0.0

xxl-skill-build命令会自动生成Dockerfile,构建镜像,并推送到本地Docker Registry(由Compose中的registry服务提供)。xxl-cli skill deploy会将镜像信息和skill.yaml元数据提交给Skill Manager。稍等片刻(约10-20秒),刷新Web UI的“SKILL管理”页面,你应该能看到job_description_parser状态变为Running。

实操心得:第一次部署失败,90%的原因是skill.yaml里的resources配置过高,导致Docker容器启动时被OOM Killer杀死。建议新手从cpu_limit: "200m"和memory_limit: "128Mi"开始,逐步调高。另外,xxl-cli命令的输出日志非常详细,务必仔细阅读,它会告诉你失败的具体原因(比如“health check failed”或“port conflict”)。

4.3 构建RAG知识库:“公司技术栈文档”

接下来,我们要为Agent提供知识支撑。创建一个名为tech_stack_docs的RAG知识库,内容是我们公司的技术栈文档(假设你有一份tech_stack.md文件)。

首先,准备文档。在./data/rag_docs/目录下,放入你的tech_stack.md。内容示例:

# 公司前端技术栈 ## 主要框架 - React 18.x (TypeScript) - Vite 4.x 作为构建工具 - Tailwind CSS 3.x 作为样式方案 ## 状态管理 - Zustand 4.x (轻量级) - Redux Toolkit 1.9.x (复杂场景) # 公司后端技术栈 ## 主语言 - Java 17 (Spring Boot 3.x) - Python 3.11 (FastAPI) ## 数据库 - MySQL 8.0 (主业务) - PostgreSQL 15 (分析报表) - Redis 7.x (缓存)

然后,创建ingestion_config.yaml:

source: type: local_folder path: ./data/rag_docs/ filters: file_types: [".md"] chunking: strategy: "by_header" header_level: 2 max_chunk_size: 500 overlap: 50 metadata: extract_from_filename: false custom_fields: - key: "
返回列表