ARTICLE DETAIL

建站实战干货

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

Spring AI 2.0 GA实战:Java开发者构建企业级AI应用全指南

2026/8/30 22:43:12 拓冰建站 浏览量
Spring AI 2.0 GA实战:Java开发者构建企业级AI应用全指南 2026 年再聊 Spring AI已经不是“要不要上”的问题而是“怎么用 Java 把大模型能力落到业务里”的问题。Spring AI 2.0 GA 是 Spring 官方这套 AI 框架的最新正式版本核心思路一句话让 Java 开发者不用转 Python 也能搭 RAG 知识库、设计智能体、接结构化输出并且能复用 Spring Boot 的整套工程化生态。这个版本最值得关注的几点是官方正式 GA 版本稳定性和接口设计比早期快照阶段更适合企业项目RAG、智能体、函数调用、结构化输出这些模块已经形成完整链路不再是零散 Demo模型接入层继续做抽象OpenAI、通义千问、DeepSeek、Ollama、本地 vLLM 都可以通过统一配置切换。加上 Spring Boot 原本的监控、配置、事务、异步任务体系做企业级 AI 应用会比从头写 Python 服务更顺。本文会按一条完整的实战链路走环境准备 → Spring Boot 工程搭建 → 模型服务接入 → RAG 知识库问答 → 智能体设计 → 结构化输出 → 业务封装 → 接口 API 与批量任务 → 资源占用与性能观察 → 上线排查。文章末尾会给出一份常见问题排查清单和最佳实践建议。适合正在做 Java 后端、想接大模型能力但不想推倒重来的开发者也适合团队里需要把 AI 能力嵌进现有 Spring Boot 服务的同学。1. Spring AI 2.0 GA 核心能力速览能力项说明项目类型Spring 官方推出的 Java AI 应用开发框架适用人群Java / Spring Boot 后端开发者核心能力大模型对话、RAG 知识库、智能体Agent、函数调用、结构化输出模型接入支持 OpenAI 协议、各家云厂商模型、本地 Ollama / vLLM / llama.cpp 等需要在配置层切换硬件要求远程模型 API本机不需要 GPU本地模型需要按模型版本准备 GPU 显存是否支持 CPU大模型推理本身不建议 CPU 跑CPU 只能做文档解析、向量检索等周边任务启动方式标准 Spring Boot 应用启动无独立一键包是否支持 API支持本身就是 Spring Boot 服务可暴露 REST 接口是否支持批量任务支持可结合 Spring 异步任务、消息队列和任务表设计批量流水线适合场景企业知识库问答、智能客服、文档分析、数据提取、Agent 工作流需要说明的是Spring AI 本身不是大模型它是一个“模型接入与编排层”。所以显存占用取决于你用的是云端模型还是本地模型。如果接 OpenAI、通义千问、DeepSeek本机只需要普通服务器内存和网络带宽如果接本地 Ollama 或 vLLM 部署的 7B / 14B 模型才需要考虑显卡显存这部分会在第 10 节展开。2. 适用场景与使用边界Spring AI 2.0 GA 最适合三类场景第一企业内部知识库问答。把产品文档、操作手册、售后记录接入 RAG让用户用自然语言提问模型基于检索到的内容回答而不是凭空生成。第二智能体应用。通过函数调用让模型能查数据库、调业务接口、操作工具完成“对话 → 决策 → 执行”的闭环。Spring AI 的 Agent 生态适合 Java 团队快速落地不需要额外维护一套 Python Agent 服务。第三信息抽取与结构化输出。从合同、简历、工单等非结构化文本中抽取实体直接映射成 Java 对象省掉大量正则解析和人工录入。使用边界同样要清楚。Spring AI 不是低代码 AI 平台如果你完全不想写 Java 代码Dify、Coze 这类平台会更合适Spring AI 的价值在于它是嵌在 Spring Boot 应用里的一层能力意味着鉴权、限流、监控、日志都能复用公司现有基础设施。这里也提醒一句合规边界RAG 知识库里的文档来源必须获得授权企业内部敏感数据不要直接暴露给不可控的外部模型服务涉及个人信息的输入要做脱敏智能体如果操作数据库或业务接口必须有权限校验和操作审计不能在测试环境没验证就直接上生产。3. 环境准备与前置条件在开始写代码之前先检查一遍环境。Spring AI 2.0 GA 的部署形态还是标准 Spring Boot所以环境要求并不特殊。JDK建议 JDK 17 及以上Spring Boot 3.x 配合使用更稳。构建工具Maven 3.8 或 Gradle 7.5本文按 Maven 演示。Spring Boot 版本以 Spring AI 2.0 GA 官方要求为准通常依赖 Spring Boot 3.x 系列。模型服务OpenAI / 通义千问 / DeepSeek / 豆包等云端 API或本地 Ollama / vLLM 服务。API Key如果使用云端模型先准备好 API Key如果本地模型确认模型服务地址。向量数据库RAG 场景需要可选 Chroma、PGVector、Milvus、Elasticsearch 等按团队已有基础设施选择。磁盘空间Maven 依赖 模型调用缓存预留 5GB 以上比较稳妥。网络能访问模型服务所在域名或内网地址。这些环境项里最容易出问题的是版本匹配。Spring AI 的小版本和 Spring Boot 版本往往有对应关系不要直接拿一套完全陌生的依赖硬试。更稳妥的做法是先建一个空项目把 Spring AI BOM 和 Spring Boot BOM 对齐确认能启动后再加业务代码。4. 工程搭建与模型服务接入4.1 创建 Spring Boot 工程并引入 Spring AI这里用 Maven 来建工程。Spring AI 2.0 GA 的依赖结构在不同版本略有差异但通常需要引入 BOM 和对应模块。下面是一个通用模板实际版本号需要以官方文档为准。parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version{spring-boot.version}/version relativePath/ /parent properties java.version17/java.version spring-ai.version{spring-ai.version}/spring-ai.version /properties dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version${spring-ai.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-tika-document-reader/artifactId /dependency /dependenciesspring-ai-openai模块是兼容 OpenAI 协议的客户端很多云厂商和本地服务都实现了这个协议可以用同一套配置切换。spring-ai-tika-document-reader用于读取 PDF、Word、PPT 等格式文档后面 RAG 会用到。如果使用本地 Ollama可以把spring-ai-openai换成对应模块或者直接用 OpenAI 兼容端点指向本地地址。这里要特别注意不同版本模块命名可能变化以官方文档为准。4.2 配置模型服务在application.yml中配置模型服务。如果使用云端 OpenAI 风格接口spring: ai: openai: api-key: ${AI_API_KEY} base-url: ${AI_BASE_URL:https://api.openai.com} chat: options: model: ${AI_MODEL:gpt-4o-mini} temperature: 0.3如果使用本地 Ollama类似这样spring: ai: ollama: base-url: http://127.0.0.1:11434 chat: options: model: qwen2.5:7b这里建议把 API Key、Base URL、模型名都放到环境变量里避免把密钥写进代码仓库。企业项目一般会接配置中心或环境变量本地开发可以在.env或 IDE 环境变量配置。4.3 写第一个对话接口Spring AI 2.0 里ChatClient是常用入口类似 Spring 生态里的RestClient。先注入ChatClient.BuilderService public class ChatService { private final ChatClient chatClient; public ChatService(ChatClient.Builder chatClientBuilder) { this.chatClient chatClientBuilder.build(); } public String chat(String message) { return chatClient.prompt() .user(message) .call() .content(); } }加一个 ControllerRestController RequestMapping(/api/chat) public class ChatController { private final ChatService chatService; public ChatController(ChatService chatService) { this.chatService chatService; } PostMapping(/ask) public ResultString ask(RequestBody AskRequest request) { return Result.success(chatService.chat(request.message())); } }把服务启动起来用 curl 测试一下curl -X POST http://127.0.0.1:8080/api/chat/ask \ -H Content-Type: application/json \ -d {message: 用一句话介绍 Spring AI}如果配置正确会返回模型生成的文本。这一步验证的是“模型接入链路是否通”这是后续所有功能的基础。如果这里报错先检查 API Key、Base URL、网络连通性再检查 Spring AI 版本和 Spring Boot 版本是否匹配。5. RAG 知识库问答实战RAGRetrieval-Augmented Generation是目前企业接入大模型最快见效的方式。Spring AI 里的 RAG 链路可以分成文档加载、文本切块、向量化、向量存储、检索、问答增强六个环节。5.1 RAG 的整体链路RAG 的核心思路是“先检索再生成”。用户提问时系统先从知识库中检索相关片段把片段拼进提示词再让模型基于这些片段回答。这样模型不需要记住所有业务文档答案也有出处可以显著降低幻觉。Spring AI 把这条链路抽象成了几个可替换组件DocumentReader负责加载文档TextSplitter负责切块EmbeddingModel负责向量化VectorStore负责存储和检索QuestionAnswerAdvisor负责把检索结果注入问答流程。替换任意一个组件都不会影响其他环节这是工程上很舒服的设计。5.2 文档加载与切块先写一个服务把 PDF 或文本文件加载下来再切块。下面是通用示例文件路径按实际项目调整Service public class RAGService { private final VectorStore vectorStore; private final ChatClient chatClient; public RAGService(VectorStore vectorStore, ChatClient.Builder builder) { this.vectorStore vectorStore; this.chatClient builder.build(); } public void ingestDocument(String filePath) { // 使用 Tika 读取 PDF/Word/PPT 等文档 TikaDocumentReader reader new TikaDocumentReader(filePath); ListDocument documents reader.get(); // 文本切块 TokenTextSplitter splitter new TokenTextSplitter(); ListDocument chunks splitter.apply(documents); // 向量化并写入向量库 vectorStore.add(chunks); } }这里需要注意切块策略。对于 RAG 来说切块太小会让检索片段语义不完整切块太大又可能让多个主题混在一起导致召回不精确。Spring AI 提供了多种TextSplitter实现比较常见的是按 token 切分并设置少量重叠。实践上可以先从 200 到 1000 字符区间测试根据自己文档的类型、长度和检索效果调整。这不是一个固定参数同一个知识库里不同来源文档可能需要不同的切块策略。5.3 向量化与向量库向量化需要选择EmbeddingModel也就是把文本转成向量。云端模型可以直接用 OpenAI 的 embedding 接口本地模型可以用 Ollama 里的 embedding 模型。Spring AI 里配置方式类似spring: ai: openai: embedding: options: model: text-embedding-3-small向量库可以选择 PGVector、Chroma、Milvus、Elasticsearch 等。如果团队已经有 PostgreSQLPGVector 是最省事的选择不需要多维护一套中间件。需要提前在数据库里创建好向量扩展Spring AI 会自动建表。CREATE EXTENSION IF NOT EXISTS vector;如果本地测试不想额外装数据库也可以用内存型向量库或文件型方案但生产环境建议使用有持久化能力的存储。5.4 检索问答接口构建一个带 RAG 增强的问答接口核心是QuestionAnswerAdvisorpublic String ask(String question) { return chatClient.prompt() .user(question) .advisors(new QuestionAnswerAdvisor(vectorStore)) .call() .content(); }启动后发送一个业务问题系统会先从向量库检索相关片段再让模型基于片段生成回答。判断 RAG 是否生效可以故意问一个知识库里有、但模型本身不一定知道的问题比如“我们公司的请假流程是什么”如果回答明显参考了文档内容说明链路通了。5.5 引用溯源与检索质量Spring AI 的QuestionAnswerAdvisor可以返回检索到的上下文信息企业场景需要在回答里展示“来源文档”。一种做法是在调用时使用Advisor参数把检索结果追加到响应中另一种做法是在 prompt 模板里要求模型在回答末尾注明出处通过在切块时保留Document的元数据比如文件名、页码就可以把引用信息带回来看。检索质量是 RAG 项目真正的难点。常见的坑有三个第一切块策略不合理导致关键内容被截断。第二embedding 模型与业务领域不匹配通用 embedding 对专业术语理解差。第三知识库里有大量低质量、重复、过时文档检索出来一堆噪音。解决思路是建立“知识库清洗流程”先把文档去重、去噪再考虑切块和检索优化。RAG 的效果不是上线就结束需要持续用真实问题做回归测试。6. 智能体设计6.1 Agent 的基本形态在 Spring AI 里智能体不是独立的运行框架而是“模型 工具 多轮记忆”的组合。最简形态是让模型能调用一组工具模型根据用户问题决定是否调用某个工具然后用工具返回结果继续回答。Spring AI 的函数调用Function Calling让 Java 方法可以直接暴露给模型。写一个工具方法标注Tool注解Service public class OrderTools { Tool(name queryOrderStatus, description 根据订单号查询订单状态) public String queryOrderStatus(String orderId) { // 实际场景从订单服务查询 return 订单 orderId 状态已发货预计 3 天后送达; } }然后在ChatClient中注册这个工具Service public class AgentService { private final ChatClient chatClient; public AgentService(ChatClient.Builder builder, OrderTools orderTools) { this.chatClient builder .defaultTools(orderTools) .build(); } public String chat(String message) { return chatClient.prompt() .user(message) .call() .content(); } }这样用户说“帮我查一下订单 A10086 的状态”模型会发现queryOrderStatus适合完成这个任务先把orderId抽取出来调用方法再把结果组织成自然语言回复。这就是一个最简单的 Agent。6.2 工具调用Agent 的实际价值在工具数量增加之后才会体现。工具可以包括查数据库、调订单接口、查询库存、发送通知、生成报表。每个工具都是一个普通的 Spring Bean业务逻辑复用现有代码不需要额外写 Python 服务。工具设计有几个原则工具描述要写清楚“什么时候该调用这个工具”避免模型乱用工具入参要简单尽量传一个 ID 或字符串复杂对象容易导致模型抽取参数失败工具返回结果要结构化最好返回 JSON 字符串模型更容易理解。6.3 多智能体当场景复杂时可以拆分多个智能体比如“客服助手”、“订单助手”、“售后助手”每个 Agent 拥有自己的工具集和提示词由上层路由逻辑根据用户意图分发。Spring AI 本身也支持更复杂的编排模式但多数业务的落地路径不是一上来就做复杂的多智能体网络而是先把单个 Agent 跑稳再逐渐拆分。从工程上看多智能体最重要的是“每个 Agent 职责清晰 统一入口路由”。可以先按用户问题关键词或模型意图识别来路由后面再演进为模型自主规划。这块不用追求复杂架构先把业务闭环打通最重要。7. 模型结构化输出大模型返回的是文本但企业系统要的是 Java 对象。结构化输出的核心是让模型按照我们定义的 schema 返回数据然后自动映射为实体类。在 Spring AI 中常见做法是使用BeanOutputConverter或ChatClient的实体转换能力。先定义一个实体类字段描述越清晰模型抽取越准public record ProductInfo( JsonPropertyDescription(产品名称) String name, JsonPropertyDescription(产品价格单位元) BigDecimal price, JsonPropertyDescription(产品类别) String category, JsonPropertyDescription(产品卖点描述) String description ) {}调用时把类型传给模型public ProductInfo extractProduct(String text) { return chatClient.prompt() .user(从以下文本中抽取产品信息 text) .call() .entity(ProductInfo.class); }这样模型就会按照ProductInfo的结构返回 JSON并自动反序列化成 Java 对象。这个方法在企业场景非常实用特别是在合同信息抽取、简历解析、工单信息结构化、用户评价分析这些“规则写不完”的文本处理任务里。需要注意结构化输出对模型版本有要求不同模型对工具调用和 JSON Schema 的支持程度不同。如果模型不支持严格结构化输出可能会出现字段缺失或类型不对需要做容错先捕获解析异常再让模型重新生成或者设置一个默认值兜底。8. 业务封装与项目上线准备8.1 统一返回与异常处理企业项目不能直接把模型原始响应扔给前端。需要一个统一的返回结构把业务码、消息、数据和 traceId 包起来。下面是通用写法public record ResultT(int code, String message, T data, String traceId) { public static T ResultT success(T data) { return new Result(0, success, data, UUID.randomUUID().toString()); } public static T ResultT error(int code, String message) { return new Result(code, message, null, UUID.randomUUID().toString()); } }配合全局异常处理把模型超时、解析失败、向量库不可用等异常转成统一格式前端只需要处理一种响应结构。8.2 异步任务与批处理大模型接口响应时间通常较长一般需要 1 到 10 秒批量场景可能更久。如果同步阻塞在请求里前端体验和系统吞吐都会出问题。生产环境建议把“耗时任务”和“HTTP 请求”解耦HTTP 接口先返回任务 ID后台用 SpringAsync或消息队列处理任务处理完成后写入结果表前端轮询或通过 WebSocket 获取结果。建一张任务表来管理批量 AI 请求这是企业级项目非常关键的一步。字段通常包括任务 ID、任务状态、输入参数、输出结果、错误信息、创建时间、完成时间。批量任务跑起来之后可以随时查进度、失败重试、对账而不是把数据都丢在日志里。9. 接口 API 与批量任务实战9.1 Controller 对外接口实际项目里AI 能力会以 REST API 的形式暴露给前端或其他服务。接口设计建议分两类一类是实时对话接口适合交互式场景另一类是异步任务接口适合批量处理和耗时操作。实时对话接口示例RestController RequestMapping(/api/ai) public class AiController { private final ChatService chatService; private final RAGService ragService; public AiController(ChatService chatService, RAGService ragService) { this.chatService chatService; this.ragService ragService; } PostMapping(/chat) public ResultString chat(RequestBody ChatRequest request) { return Result.success(chatService.chat(request.message())); } PostMapping(/rag/ask) public ResultString ragAsk(RequestBody ChatRequest request) { return Result.success(ragService.ask(request.message())); } }批量异步任务接口可以拆成三步提交任务、查询进度、获取结果。PostMapping(/batch/submit) public ResultString submit(RequestBody BatchRequest request) { String taskId batchTaskService.submit(request); return Result.success(taskId); } GetMapping(/batch/{taskId}/status) public ResultBatchStatus status(PathVariable String taskId) { return Result.success(batchTaskService.getStatus(taskId)); } GetMapping(/batch/{taskId}/result) public ResultBatchResult result(PathVariable String taskId) { return Result.success(batchTaskService.getResult(taskId)); }9.2 外部调用示例无论前端、Python 脚本还是定时任务调用方式都是标准 HTTP。用 curl 调用实时问答curl -X POST http://127.0.0.1:8080/api/ai/rag/ask \ -H Content-Type: application/json \ -d {message: 根据知识库回答产品退款流程是什么}用 Python 调用并轮询批量任务状态import requests import time base_url http://127.0.0.1:8080/api/ai # 1. 提交批量任务 resp requests.post(f{base_url}/batch/submit, json{ items: [ {text: 第1条待处理文本}, {text: 第2条待处理文本} ] }, timeout30) task_id resp.json()[data] print(task_id:, task_id) # 2. 轮询任务状态 while True: status requests.get(f{base_url}/batch/{task_id}/status, timeout30).json() print(status:, status) if status[data][state] in (SUCCESS, FAILED): break time.sleep(3) # 3. 获取结果 result requests.get(f{base_url}/batch/{task_id}/result, timeout30).json() print(result)批量任务的项目里至少要考虑三件事失败任务的自动重试单条失败不能拖垮整个批次任务日志要全量输出输入、输出、耗时、耗时方便出问题时回溯接口要加限流和鉴权避免内部 AI 能力被外部滥用。10. 资源占用与性能观察Spring AI 的资源占用取决于两个层面应用本身和模型服务。应用层面Spring Boot 服务本身是普通 Java 进程内存占用通常在几百 MB 到 2GB 之间取决于 JVM 参数和依赖规模。RAG 场景里向量检索、文档解析会在内存中产生对象大批量文档导入时要注意堆内存设置如果用的是 PGVector检索压力在数据库侧不需要在 Java 应用里维护大量向量数据。模型服务层面如果接的是云端 API本机没有显存压力主要看网络延迟、API 限流和 token 费用。如果接的是本地模型则要显存。判别方式很直接启动本地模型服务后用nvidia-smi看显存占用。7B 级别模型量化后通常需要 6GB 到 10GB 显存14B 级别需要更多实际以模型和推理框架为准。显存不足的表现是模型加载失败或推理时无响应而不是 Spring AI 本身的问题。在监控上建议关注三组指标模型调用延迟和 token 消耗、RAG 检索耗时、批量任务的成功率和队列长度。Spring Boot 的 Actuator 可以暴露应用监控指标模型调用的耗时和错误数可以埋点上报token 用量可以在日志或数据库中记录。这些数据是后续优化模型选择、切块策略和并发参数的基础。11. 常见问题与排查方法问题现象可能原因排查方式解决方案启动报依赖冲突Spring Boot 版本和 Spring AI BOM 版本不匹配查看 Maven 依赖树确认冲突模块统一使用官方推荐的 BOM 版本组合调用模型报 401 / 403API Key 错误或没有权限检查配置环境变量是否正确测试直接 curl 模型服务更新 API Key确认账号有模型访问权限调用模型超时网络延迟、模型服务负载高、请求体过大查看 Spring 日志中的超时时间用 curl 单独测模型端点调大超时时间缩小请求内容检查网络连通性模型返回乱码或答非所问Prompt 配置不合理或模型选型不当检查 prompt 模板和 temperature 参数优化提示词降低 temperature必要时切换模型结构化输出解析失败模型返回 JSON 格式与实体类不匹配打开日志看模型原始输出增加重试和错误兜底使用支持严格 JSON 的模型RAG 检索不到相关内容文档没导入、切块不合理、向量库为空查询向量库记录数抽查检索召回结果重新执行文档导入调整切块大小和重叠参数本地模型推理慢或无响应显存不足、模型未加载完成查看 nvidia-smi 和模型服务日志减小模型规模开启量化或增加 GPU 资源端口被占用上个服务进程没有退出或其他程序占用端口netstat -ano查端口占用换端口或清理进程批量任务卡住线程池太小、第三方 API 限流、任务无重试机制查日志看任务卡在哪一步看 API 返回的限流错误加大线程池加入退避重试机制拆分任务批次12. 最佳实践与合规建议从项目实战角度看Spring AI 2.0 GA 落地有几个值得遵守的工程原则。第一次调试先跑最小链路。先不接 RAG、不接 Agent就一个 ChatClient 对话接口确认模型链路没问题再逐步加向量库、工具和业务逻辑。这样排查问题时不会出现“到底是模型问题、向量库问题还是工具调用问题”的叠加态。RAG 项目先做知识清洗再谈效果。一个塞满过期文档和重复内容的向量库再强的 embedding 模型也救不回来。上线前要有专门的知识库治理流程规定文档格式、更新频率和责任人。AI 项目的效果必须可回归。不能只看一次演示成功要用固定测试集反复验证。模型参数、切块策略、prompt 模板调整后要跑同一组问题对比输出质量。否则很可能换个问题就崩。接口和任务要有完善的日志。模型调用耗时、token 消耗、输入输出摘要、错误信息都要记录。特别是批量任务没有日志和重试机制出问题时只能干等。合规方面再强调一遍企业文档接入 RAG 前确认版权和授权涉及个人信息的输入要脱敏智能体调用业务接口需要权限校验和审计外部模型 API 的访问范围要限流上线前对输出内容做人工抽检。AI 能力是业务系统的一部分不是可以绕过安全流程的特例。13. 总结与下一步Spring AI 2.0 GA 最值得尝试的点是把大模型能力接进 Java 服务这件事变得标准化RAG、智能体、结构化输出都有框架层支持配合 Spring Boot 的成熟体系团队可以少踩很多工程坑。如果你刚开始接触建议按这个顺序验证第一步跑通对话接口确认模型链路第二步接一个最小的 RAG 知识库导入几份测试文档验证检索问答第三步试试结构化输出定义一个实体类让模型从一段文本中抽取字段第四步再加工具调用做一个最简单的智能体。每步都跑通之后再考虑业务封装和批量任务。最容易踩的坑是版本匹配和 RAG 切块策略。版本不匹配会导致启动失败切块不合理会导致检索效果差。前者通过官方 BOM 对齐解决后者需要基于自己的文档反复测试不要拿一份教程里的参数直接上生产。后续扩展方向也很明确把向量库切到 PGVector 做持久化接入公司现有的消息队列做批量任务用 Actuator 和监控平台收集模型调用指标再往多智能体编排演进。Spring AI 2.0 GA 这套框架足够支撑一个企业级 AI 应用从 Demo 走向生产。