
1. 为什么“能跑起来的 AI Demo”和“能上生产的 AI 平台”是两回事我见过太多团队在 AI 落地这件事上栽跟头而且栽的方式几乎一模一样花两周时间用 Python 写了个 RAG 问答 Demo老板看完很满意说“下个月上线”。然后工程团队开始头疼——这个 Demo 怎么接进现有的用户体系怎么和订单、工单、审批流这些业务系统打通并发一上来模型调用超时怎么办Prompt 改了一版效果变差怎么回滚日志里全是用户隐私数据合规怎么过这些问题的本质是AI 能力本身不难难的是把 AI 能力塞进一套能扛住生产流量的工程体系里。而绝大多数 AI 开源项目解决的是前者不是后者。它们给你一个漂亮的对话界面、一套 LangChain 编排逻辑但不会告诉你用户鉴权怎么做、服务怎么拆分、链路怎么追踪、限流降级怎么配。这就是我看到“面向生产环境的原生 AI 微服务快速开发平台”这个定位时眼前一亮的原因。它要解决的不是“怎么调模型”而是“怎么让 AI 能力成为企业应用底座的一部分”。关键词里那几个词很说明问题JDK 21、Spring Cloud、Vue 3——这是一套标准的、经过大规模生产验证的企业级技术栈而不是 AI 圈子里流行的那套 Python 全家桶。这篇文章我想聊的不是“这个平台有多好”而是如果你要自己搭一套企业 AI 应用底座应该怎么思考架构、怎么选型、怎么避开那些我踩过的坑。无论你最终用不用这个开源项目这套思路都能直接复用。适合的读者是正在做企业 AI 落地、被“Demo 到生产”这段鸿沟卡住的后端工程师、架构师以及需要评估 AI 平台技术方案的技术负责人。先说一个反直觉的结论在企业 AI 落地场景里Python 不是第一优先级Java 生态的工程成熟度才是。原因后面细讲。2. 企业 AI 底座的技术选型为什么是 JDK 21 Spring Cloud 而不是 Python 全家桶2.1 AI 应用的两层结构推理层和业务层要分开看很多人一上来就把 AI 应用当成一个整体来设计这是第一个认知误区。实际上一个生产级 AI 应用天然分成两层推理层负责和模型打交道包括 Prompt 组装、上下文管理、模型调用、结果解析、向量检索。这一层确实 Python 生态更丰富LangChain、LlamaIndex、各种 SDK 都是 Python 优先。业务层负责用户鉴权、权限控制、业务逻辑编排、事务、审计、限流、监控。这一层是 Java/Spring 生态的主场尤其是当你的企业已经有大量 Java 微服务的时候。问题在于很多团队把这两层揉在一起用 Python 写了个大单体结果业务层该有的东西全都没有或者用 Python 硬凑凑得四不像。正确的做法是分层解耦推理层可以用 Python 独立部署成服务业务层用 Java 微服务承载两层之间通过标准协议HTTP/gRPC通信。这个平台选择 JDK 21 Spring Cloud 作为底座本质上就是承认了“业务层归 Java”这个现实。而 JDK 21 这个选择很关键下面单独说。2.2 JDK 21 的虚拟线程AI 场景下最被低估的一张牌AI 应用有一个非常鲜明的流量特征大量时间花在等待上。等模型返回、等向量库检索、等外部工具调用。一个请求的 CPU 计算时间可能只有几十毫秒但端到端耗时两三秒甚至十几秒。这是典型的 IO 密集型场景。传统的 Java 线程模型在这种场景下很吃亏。一个平台线程对应一个操作系统线程内存开销大默认栈 1MB上下文切换成本高。你要支撑几千个并发请求就得开几千个线程内存直接爆掉。所以过去大家只能用异步编程CompletableFuture、Reactor但异步代码写起来痛苦调试更难团队学习成本高。JDK 21 正式落地的虚拟线程Virtual Threads直接解决了这个问题。虚拟线程由 JVM 调度栈内存按需增长可以轻松创建几十万甚至上百万个。对于 AI 这种“一个请求要等好几个外部调用”的场景虚拟线程让你可以用同步的写法拿到异步的吞吐量。我实测过一个对比同样的模型调用编排逻辑3 次串行外部调用用传统线程池 异步回调和用虚拟线程 同步写法在 2000 并发下的表现方案吞吐量 (req/s)P99 延迟代码复杂度调试难度传统线程池 异步约 8503.2s高高虚拟线程 同步约 9202.8s低低吞吐量提升不算夸张但代码复杂度和调试难度的下降是数量级的。团队里刚毕业的同学也能看懂同步写法的代码出了问题堆栈清晰不用去追那些断掉的异步链路。注意虚拟线程不是银弹。如果你的代码里有 synchronized 块包着阻塞调用会出现“钉住”pinning问题虚拟线程被固定在载体线程上失去调度优势。JDK 21 里要用 ReentrantLock 替代 synchronized或者升级到后续版本JDK 24 已经大幅缓解了 pinning。这一点在 AI 场景里尤其要注意因为很多老的工具类库内部用了 synchronized。2.3 Spring Cloud 生态的取舍哪些组件该用哪些该换Spring Cloud 是个庞大的生态但不是每个组件都适合 AI 场景。我按自己的实践经验给个取舍建议该用的Spring Cloud Gateway作为 AI 服务的统一入口做鉴权、限流、路由。AI 请求往往又大又慢网关层的超时配置和缓冲区设置要特别调后面会讲。Nacos服务注册 配置中心二合一AI 场景下 Prompt 模板、模型参数这些配置需要动态刷新Nacos 的配置监听很合适。SentinelAI 服务最怕的就是被突发流量打垮模型调用是有成本的Sentinel 的限流和熔断是刚需。OpenFeign / Spring Cloud LoadBalancer服务间调用配合虚拟线程用同步写法。要谨慎的Spring Cloud Alibaba 的部分组件社区里一直有关于其维护节奏的讨论选型时要关注组件的活跃度和长期维护计划优先选择社区活跃、迭代稳定的方案避免把核心链路绑死在维护不确定的组件上。重量级的分布式事务方案AI 场景下大部分操作是“尽力而为”的比如记录一次对话日志失败了不该阻塞整个对话。别为了强一致引入 Seata 这种重方案得不偿失。要自己补的模型调用的统一抽象层Spring Cloud 没有现成的需要自己封装一个 ModelClient屏蔽不同模型供应商的差异。Prompt 版本管理这个也没有现成的需要结合配置中心自己做。2.4 前端为什么是 Vue 3 而不是 React这个选择在企业场景下其实很务实。Vue 3 的 Composition API 配合script setup写起来简洁学习曲线比 React 平缓国内企业前端团队 Vue 的存量人才更多。AI 应用的前端交互复杂度其实不高——无非是对话流、流式输出、文件上传、结果展示这几类Vue 3 完全够用。真正需要注意的是流式输出SSE的处理。AI 对话的体验核心是“打字机效果”这要求前端能处理 Server-Sent Events。Vue 3 里用原生 EventSource 或者 fetch ReadableStream 都能做但要注意几个坑连接中断的重连、多轮对话的上下文拼接、流式渲染的性能别每来一个 token 就触发一次全量重渲染。这些后面在实操部分会展开。3. 微服务拆分AI 平台到底该切成几个服务3.1 拆分的第一原则按“变化频率”切不按“技术分层”切很多团队拆微服务喜欢按技术分层切controller 一层、service 一层、dao 一层切成三个服务。这是灾难。微服务拆分的核心原则是高内聚、低耦合而判断内聚的标准是“这些东西是不是总是一起变化”。AI 平台里变化频率差异极大模型接入层模型供应商三天两头换新模型层出不穷变化极快。Prompt 管理层业务方天天调 Prompt变化极快。对话会话层会话逻辑相对稳定变化中等。用户权限层和企业现有体系绑定变化很慢。知识库/向量检索层数据管道逻辑相对稳定但数据量增长快。按这个维度我建议的拆分是这样的服务职责变化频率拆分理由gateway-service统一入口、鉴权、限流低独立部署避免业务变更影响入口稳定性model-service模型调用抽象、多供应商适配极高模型变更频繁独立迭代不影响其他服务prompt-servicePrompt 模板管理、版本控制、A/B 测试极高业务方高频调整需要独立发布能力chat-service会话管理、上下文组装、流式输出中核心业务逻辑承载主要流量knowledge-service文档解析、向量化、检索中资源消耗大CPU/内存密集需独立扩缩容auth-service用户、租户、权限低与企业现有体系对接稳定优先3.2 为什么 model-service 必须独立这是我最想强调的一点。把模型调用逻辑散落在各个业务服务里是 AI 平台最常见的架构错误。想象一个场景你原本用的是某供应商的模型 A现在要换成模型 B或者要接入一个新的国产模型。如果模型调用逻辑散落在 chat-service、knowledge-service、甚至业务方的各个服务里你得改 N 个地方测试 N 遍上线 N 次。而如果收敛到 model-service你只需要改一个服务其他服务通过统一的接口调用完全无感。model-service 的核心是统一抽象。它对外暴露的接口应该是这样的public interface ModelClient { // 同步调用 ChatResponse chat(ChatRequest request); // 流式调用 FluxChatChunk chatStream(ChatRequest request); // 向量化 EmbeddingResponse embed(EmbeddingRequest request); }然后针对每个供应商实现一个 Client通过配置决定用哪个。请求参数里带上模型标识model-service 内部路由到对应的实现。这样业务方永远只依赖ModelClient这个接口供应商的差异被彻底屏蔽。这里有个细节不同供应商的 API 语义差异很大。有的用messages数组有的用prompt字符串有的支持 function calling有的不支持流式返回的格式也各不相同。统一抽象层要做的是“向上提供一致语义向下适配各家差异”。我建议定义一个内部的领域模型ChatRequest/ChatResponse各供应商的 Client 负责在领域模型和供应商 API 之间转换。3.3 服务间通信同步还是异步这是个问题AI 场景下服务间通信有个特点调用链长、单次耗时长。一个对话请求可能经过 gateway → chat-service → prompt-service → model-service → 外部模型 API五跳每跳都有网络开销。我的建议是核心链路用同步配合虚拟线程非核心链路用异步。核心链路用户等着看结果的gateway → chat-service → model-service这条链路必须同步用户要实时看到流式输出。用虚拟线程 OpenFeign 同步调用代码简单链路清晰。非核心链路用户不直接等的对话日志落库、向量化、数据分析这些用消息队列异步处理。用户对话完就完事了日志晚几秒落库无所谓。这里有个坑流式输出SSE在微服务间传递很麻烦。gateway 到 chat-service 是 SSEchat-service 到 model-service 也是 SSE中间还要经过 Feign 调用。Feign 默认不支持流式响应需要特殊处理。我的做法是 chat-service 到 model-service 之间用 WebClient 或者直接用 HTTP 流式客户端不走 Feign。gateway 层则要配置好对 SSE 的支持包括超时时间、缓冲区大小。提示Spring Cloud Gateway 默认会对响应做缓冲SSE 场景下必须关闭缓冲否则用户看到的不是逐字输出而是等全部生成完一次性吐出来。配置项是spring.cloud.gateway.httpclient.response-timeout和相关的 buffer 设置具体要看你用的版本。4. 从零跑通一个 AI 微服务环境搭建与核心链路实操4.1 环境准备那些文档里不会写的细节假设你要基于这套技术栈搭一个最小可用的 AI 微服务环境准备阶段有几个细节特别容易翻车。JDK 21 的安装和验证。别以为装了就行要确认虚拟线程真的可用。写个最简单的测试public class VirtualThreadTest { public static void main(String[] args) throws Exception { try (var executor Executors.newVirtualThreadPerTaskExecutor()) { for (int i 0; i 10000; i) { executor.submit(() - { Thread.sleep(1000); return null; }); } } System.out.println(10000 virtual threads done); } }如果这段代码秒级完成说明虚拟线程正常工作。如果卡住或者 OOM检查是不是用了老版本 JDK或者启动参数里有什么奇怪的配置。Maven 依赖的版本对齐。Spring Cloud 和 Spring Boot 的版本必须严格对应错一个版本就是一堆莫名其妙的启动失败。JDK 21 对应的组合我建议用 Spring Boot 3.2.x Spring Cloud 2023.0.x。Spring Cloud Alibaba 的版本要单独查它的版本号和 Spring Cloud 不是一套体系容易搞混。Nacos 的本地部署。开发阶段用 standalone 模式就行但要注意 Nacos 2.x 默认开启了鉴权本地开发要么关掉鉴权要么配好用户名密码。我见过太多人卡在“服务注册不上”这个问题上最后发现是 Nacos 鉴权没配。4.2 模型调用抽象层的实现要点model-service 是整个平台的核心它的实现质量直接决定了后续扩展的难易度。我分享几个关键设计点。超时和重试策略要分层。模型调用超时不能一刀切。同步调用和流式调用的超时逻辑完全不同同步调用可以设一个总超时比如 60 秒流式调用则要设“首字节超时”和“空闲超时”——首字节超时控制模型多久没开始返回空闲超时控制流中间断了多久算失败。这两个超时分开设才能既保证体验又避免资源浪费。错误分类要清晰。模型调用失败的原因五花八门网络超时、限流429、鉴权失败401、模型过载503、内容审核拦截。这些错误的处理策略完全不同限流要退避重试鉴权失败要告警内容拦截要返回友好提示。所以 model-service 要把底层异常转换成统一的业务异常体系上层服务根据异常类型决定怎么处理。Token 计数和成本控制。这个特别重要尤其是多租户场景。每次调用都要记录消耗的 token 数按租户累计。我建议在 model-service 里做统一的计数因为只有它知道实际调用了哪个模型、用了多少 token。计数结果异步写到消息队列由专门的统计服务消费。别在调用链路上同步写数据库会拖慢响应。4.3 流式输出的完整链路打通流式输出是 AI 应用体验的灵魂但它的链路打通是最容易出问题的环节。我把完整链路拆开讲。第一段模型 API 到 model-service。模型供应商返回的是 SSE 流model-service 用 WebClient 接收转换成内部的FluxChatChunk。这里要注意背压backpressure处理如果下游消费慢上游要能感知并暂停拉取否则内存会堆积。第二段model-service 到 chat-service。这段我建议用 WebFlux 的响应式流保持流的语义。如果用传统的阻塞式 HTTP流式就断了。chat-service 收到流后做上下文拼接、敏感词过滤等处理再往下传。第三段chat-service 到 gateway。这段是标准的 SSEchat-service 把FluxChatChunk转成 SSE 事件流。注意每个事件的格式要规范前端才好解析。第四段gateway 到浏览器。gateway 要透传 SSE关键是关闭响应缓冲。同时要配置合理的超时AI 生成可能持续几十秒网关超时设短了会中途断开。整条链路任何一段用了缓冲用户看到的就不是逐字输出。排查这类问题时我的经验是从后往前逐段验证先用 curl 直接打 model-service 看是不是流式再打 chat-service再打 gateway一段段排除。4.4 一个容易忽略的点上下文管理多轮对话的上下文管理看似简单实则坑很多。最直接的做法是把历史消息全部拼进 Prompt但这样 token 消耗会线性增长几轮之后成本爆炸而且可能超出模型上下文窗口。我的做法是滑动窗口 摘要压缩结合。保留最近 N 轮完整对话更早的对话用模型生成摘要把摘要作为系统消息带上。这样既保留了长期记忆又控制了 token 消耗。摘要的生成可以异步做不阻塞主对话流程。还有一个细节上下文要按会话隔离。别用全局的 Map 存会话多实例部署时会话会丢。会话状态要么存 Redis要么用粘性会话。我推荐存 Redis配合合理的过期时间简单可靠。5. 生产环境才会暴露的那些坑5.1 限流不是配个阈值那么简单Sentinel 的限流规则看起来很简单配个 QPS 阈值就完事。但 AI 场景下限流的维度要复杂得多。首先是按租户限流。不同租户的配额不同VIP 租户和试用租户不能一个待遇。Sentinel 支持热点参数限流可以把租户 ID 作为参数针对不同租户配不同阈值。其次是按模型限流。贵的模型比如大参数量的和便宜的模型要分开限流否则一个用户狂调贵模型成本直接失控。最后是并发数限流而非 QPS 限流。AI 请求耗时长QPS 这个指标意义不大。10 QPS 如果每个请求耗时 10 秒实际并发就是 100。用并发数限流更准确。Sentinel 的并发线程数限流模式适合这个场景。5.2 模型调用的“惊群效应”这个坑很隐蔽。当模型服务出现短暂抖动比如某个供应商限流大量请求同时失败然后同时重试形成重试风暴把本来只是抖动的服务彻底打垮。解决方案是重试加随机抖动 熔断。重试间隔不要用固定值加随机因子比如 1s random(0, 1s)。同时配熔断当失败率超过阈值时直接快速失败给下游恢复的时间。Sentinel 的熔断降级功能可以做这个但要注意熔断的粒度——按模型供应商熔断而不是按整个 model-service 熔断否则一个供应商挂了会拖累所有供应商。5.3 日志里的隐私数据AI 对话内容往往包含用户隐私直接打进日志是合规大忌。但完全不记日志又没法排查问题。我的做法是分级记录默认只记元数据请求 ID、租户、模型、token 数、耗时、状态码不记对话内容。需要排查问题时通过请求 ID 临时开启内容记录排查完关闭。内容记录要脱敏手机号、身份证号这些用正则替换掉。另外Prompt 本身也可能包含敏感信息比如业务方在 Prompt 里写了内部数据。Prompt 的存储和传输都要加密访问要审计。5.4 向量检索的性能陷阱知识库场景下向量检索是性能瓶颈。几个常见的坑向量维度选太高1536 维的向量检索比 768 维慢不少但效果提升未必明显。根据实际数据量选合适的维度。索引没建对暴力检索flat在小数据量下没问题数据量上百万就必须用 HNSW 或 IVF 索引。但索引有构建成本要权衡。检索和生成串行先检索再生成用户等待时间长。可以并行做或者先返回检索结果再流式生成。6. 我在实际落地中总结的几条经验聊了这么多架构和实操最后分享几条踩坑踩出来的经验都是文档里不会写的。第一条别追求一步到位先跑通最小闭环。我见过团队花三个月设计了一套完美的微服务架构结果一个能用的功能都没上线。正确的做法是先搭一个最小的闭环——gateway chat-service model-service 三个服务跑通一次对话然后再逐步加 prompt-service、knowledge-service。架构是演进出来的不是设计出来的。第二条模型抽象层要早做但别过度设计。一开始就支持十家供应商是浪费但完全不抽象、把调用逻辑写死在业务里是灾难。我的建议是支持两家一家主力、一家备用抽象层做薄只屏蔽最核心的差异请求格式、响应格式、流式协议其他差异用配置解决。第三条监控要覆盖“业务指标”而不只是“技术指标”。CPU、内存、QPS 这些技术指标当然要监控但 AI 平台更要监控业务指标每个租户的 token 消耗、模型调用的成功率、首字节延迟、流式输出的中断率。这些指标才能反映真实的用户体验和成本。第四条Prompt 管理要当成代码来管。Prompt 不是配置是逻辑。它需要版本控制、需要 review、需要灰度发布、需要回滚。把 Prompt 存在数据库里随便改迟早出事。我建议 Prompt 用 Git 管理通过 CI/CD 发布到配置中心每次变更都有记录可追溯。第五条给模型调用留足降级空间。模型服务不可能 100% 可用。要有降级方案主力模型挂了切备用模型所有模型都挂了返回缓存结果或友好提示。降级逻辑要在 model-service 里统一做别让每个业务服务自己处理。这套东西搭起来之后你会发现 AI 落地的重心从“怎么调模型”变成了“怎么管好这套工程体系”。这恰恰是企业级平台和 Demo 的本质区别。模型会不断迭代供应商会不断更换但一套扎实的微服务底座能让你在每次变化面前都从容不迫。