
1. 为什么“能跑起来的 AI Demo”和“能上生产的 AI 平台”是两回事我见过太多团队在 AI 落地这件事上栽跟头。演示阶段一切顺利本地起个 Python 脚本调一下大模型接口前端套个对话框领导看完点头项目立项。然后真正要接入业务系统的时候问题全冒出来了——模型调用散落在各个业务代码里没人管、会话状态存在内存里一重启就丢、多个 AI 能力之间互相调用没有统一协议、权限和审计完全空白、并发一上来接口就开始超时。最后这个“AI 项目”变成了一个谁都不敢碰的黑盒。这个开源项目的定位就是冲着这个断层去的面向生产环境的原生 AI 微服务快速开发平台。注意三个关键词——“生产环境”“原生 AI”“微服务”。它不是又一个 AI 套壳工具也不是把 AI 能力硬塞进传统 CRUD 框架里而是从架构层面就把 AI 当成一等公民来设计。技术栈上它选了 JDK 21 Spring Cloud Vue 3 这套组合这个选择本身就透露了很多信息后面我会展开讲。这篇文章适合谁看如果你正在做企业级 AI 应用的架构选型或者你手上有一个 AI 功能要接入现有的微服务体系又或者你单纯想搞清楚“AI 微服务”到底该怎么拆、怎么管、怎么保证稳定那这篇内容应该能帮你少走几个月弯路。我会从架构设计、技术选型理由、AI 能力的服务化封装、生产环境的稳定性保障、以及实际落地时的踩坑经验几个维度把这个平台的设计思路和实操要点讲透。需要先说明一点项目正文和关键词给的信息比较有限所以下面涉及具体实现的部分我会基于“一个合格的后端架构师在做这类平台时最可能采用的方案”来补充并明确标注哪些是通用实践、哪些是需要你根据自己项目调整的地方。这样你拿到的不只是一篇介绍而是一套可以对照落地的思路。2. 拆解这个平台的核心架构分层与 AI 原生设计2.1 什么叫“AI 原生”和“AI 外挂”的本质区别先把概念理清楚。“AI 外挂”指的是你有一套成熟的业务微服务然后单独搞一个 AI 服务业务系统通过 HTTP 调它AI 服务内部再调大模型。这种模式的问题是AI 能力是孤岛它不知道业务上下文业务系统也不理解 AI 的调用特征比如长耗时、流式返回、Token 计费。“AI 原生”的意思是AI 相关的关注点被下沉到平台层统一解决。具体体现在几个地方统一的模型网关层所有对大模型的调用都经过这一层负责路由、限流、重试、降级、计费统计、Prompt 模板管理。业务代码里不应该出现任何模型厂商的 SDK 调用。会话与上下文管理作为基础设施多轮对话的状态、上下文窗口的裁剪策略、历史消息的存储这些是平台能力不是每个业务自己实现。AI 能力的服务注册与发现一个“文档摘要”能力、一个“意图识别”能力都应该像普通微服务一样注册到注册中心能被其他服务发现和编排。流式响应的一等支持大模型输出天然是流式的平台的网关、服务间通信、前端对接都要原生支持 SSE 或 WebSocket而不是等模型全部生成完再返回。这个平台把这四件事都做进了底座所以叫“应用底座”而不是“开发框架”。框架是你按它的规矩写代码底座是你在这上面搭业务脏活累活它替你干了。2.2 分层结构从接入到模型的全链路一个生产级的 AI 微服务平台我理解应该分成这么几层从下往上说基础设施层注册中心Nacos 或 Consul、配置中心、网关Spring Cloud Gateway、链路追踪、日志聚合。这部分和传统微服务没区别但要注意 AI 场景下日志量会大很多尤其是 Prompt 和响应内容的记录需要单独设计脱敏和采样策略。模型接入层这是 AI 原生的核心。它要屏蔽不同模型提供方的差异——有的走 HTTP、有的走 SDK、有的支持 Function Calling、有的支持多模态。这一层对外暴露统一的接口契约内部做协议适配。同时它还要管住成本Token 用量统计、按租户/按应用的配额、超限降级到小模型或直接拒绝。AI 能力服务层把原子化的 AI 能力封装成独立微服务。比如 RAG 检索服务、向量化服务、Agent 编排服务、Prompt 管理服务。每个服务职责单一可以独立扩缩容。这里有个关键设计——能力服务不直接持有模型连接而是通过模型接入层调用这样模型切换、灰度、A/B 测试都在一层完成。业务编排层面向具体业务场景的组合。比如“智能客服”这个场景可能需要先调意图识别再调知识库检索再调大模型生成最后调敏感词过滤。这一层用工作流引擎或者编排 DSL 来描述而不是硬编码。接入层对外的 API 网关和 BFFBackend for Frontend。前端 Vue 3 应用通过 BFF 拿数据BFF 负责聚合多个微服务的返回、处理流式转发、做前端需要的裁剪。这个分层的好处是每一层可以独立演进。模型厂商换了只动模型接入层业务场景变了只动编排层前端改版只动 BFF。2.3 为什么是 JDK 21 而不是 JDK 17 或 8技术选型里 JDK 21 这个点值得单独说。JDK 21 是 LTS 版本相比 JDK 17 最大的生产价值在于**虚拟线程Virtual Threads**正式转正。AI 应用的调用特征是什么大量时间在等 IO——等模型返回、等向量库查询、等外部工具调用。传统平台线程模型下一个请求占一个线程线程池就那么大并发一高就排队。虚拟线程让“一个请求一个线程”的编程模型可以支撑极高的并发因为阻塞时底层载体线程会被释放。实测数据上在 IO 密集型的 AI 网关场景虚拟线程相比传统线程池在相同硬件下吞吐量能提升数倍而且代码不用改成响应式那套复杂的链式写法。对于团队里大部分是写同步代码的工程师来说这个收益非常实在。当然有坑虚拟线程不适合 CPU 密集型任务也不要在虚拟线程里用 synchronized 做长时间持锁会 pin 住载体线程。平台里如果用了本地缓存或者某些老库需要检查这些点。JDK 21 还带来了分代 ZGC对 AI 场景下大对象多、内存压力大的情况停顿时间控制得更好。2.4 Spring Cloud 在这个平台里承担什么角色Spring Cloud 这套东西在 AI 平台里不是过时了而是角色变了。它不再负责业务逻辑而是负责服务治理的骨架服务注册发现AI 能力服务注册上来编排层才能发现它们。配置中心Prompt 模板、模型参数、限流阈值这些都应该动态可配改完不用重启。网关统一入口做鉴权、限流、路由、流式转发。熔断降级模型服务不稳定时快速失败而不是拖垮整个链路。分布式事务AI 场景下事务需求相对少但涉及计费、配额扣减时还是需要。选型上Nacos 做注册和配置是当前国内团队最顺手的选择社区活跃、文档全、和 Spring Cloud Alibaba 集成成熟。Sentinel 做流控降级配合 Redis 集群做集群限流的数据源这个组合在高并发场景下经过验证。至于 Spring Cloud Alibaba 某些组件停更的传闻实际影响的是特定组件的特定功能核心的 Nacos、Sentinel 依然在维护选型时关注具体组件版本即可不必因噎废食。3. AI 能力怎么拆成微服务粒度、边界与通信3.1 拆分粒度拆太细是灾难拆太粗是单体微服务拆分最怕两种极端。拆太细一个 AI 请求要跨七八个服务网络开销和故障点成倍增加拆太粗又退化成单体失去了独立扩缩容的意义。我的经验是AI 能力服务按资源特征和扩缩容需求来拆而不是按业务功能拆。举几个例子向量化服务CPU/GPU 密集需要独立扩缩容单独拆。向量检索服务内存密集向量库连接是稀缺资源单独拆。大模型调用网关IO 密集需要统一管控单独拆。Prompt 管理服务读多写少可以和其他轻量服务合并。Agent 编排服务CPU 中等但逻辑复杂单独拆便于迭代。判断标准很简单如果两个能力的资源画像差异大或者扩缩容节奏不同就拆开否则合并。不要为了微服务而微服务。3.2 服务间通信同步、异步、流式三套机制AI 场景下的通信比传统 CRUD 复杂因为存在流式返回。平台需要同时支持三种模式同步请求-响应适合意图识别、分类、短文本处理这类快速返回的能力。用 OpenFeign 或 Dubbo 都行Feign 更简单Dubbo 性能更好。AI 场景下我倾向 Feign因为调用链清晰、和 Spring Cloud 生态无缝。异步消息适合文档批量向量化、长任务处理。用 RocketMQ 或 Kafka把任务丢进队列消费者慢慢处理处理完回调或写状态。这样前端不用干等用户体验好服务端也不会被长任务拖垮。流式传输大模型生成必须用流式。服务间通信用 SSE 或者 gRPC streaming网关到前端用 SSE 或 WebSocket。这里有个细节——流式链路上任何一环做了缓冲用户就会感觉到卡顿。网关要配置不缓冲Nginx 要关掉 proxy_buffering服务端要 flush。这些配置漏一个流式就变成了“等半天然后一次性出来”。3.3 统一接口契约让 AI 能力可编排的前提如果每个 AI 服务的接口长得都不一样编排层就没法通用化。平台需要定义一套统一的 AI 能力接口契约我建议至少包含这些字段字段说明是否必填capabilityId能力唯一标识是inputs输入参数结构化是context会话上下文含历史消息否options模型选择、温度、最大 Token 等否stream是否流式返回否callbackUrl异步回调地址否traceId链路追踪 ID是输出侧统一包含结果内容、Token 用量、耗时、模型标识、是否被截断、错误码。有了这套契约编排层才能像搭积木一样组合能力监控系统才能统一采集指标计费系统才能统一扣减。3.4 一个具体的拆分案例智能问答场景假设要做企业知识库智能问答按这个平台的设计链路是这样的前端发起问题BFF 接收生成 traceId。BFF 调编排服务编排服务按预定义工作流执行。第一步调意图识别服务判断是知识问答还是闲聊还是转人工。如果是知识问答调向量化服务把问题转向量。调向量检索服务从知识库召回相关片段。调 Prompt 管理服务拿到问答模板填充召回内容。调模型网关流式生成回答。流式结果经过敏感词过滤服务也是流式处理。通过 BFF 的 SSE 通道推给前端。每一步都是独立服务可以独立扩容。向量检索慢了就加检索服务实例模型网关压力大就加网关实例。这种灵活性是单体架构给不了的。4. 生产环境的稳定性AI 平台最容易翻车的地方4.1 模型调用的超时、重试与降级策略模型调用是整条链路里最不可控的一环。第三方模型服务可能抖动、可能限流、可能返回慢。如果不在平台层统一处理每个业务自己写重试逻辑结果就是重试风暴把模型服务彻底打挂。平台层的策略应该是超时分级连接超时设短比如 3 秒读超时按场景设流式场景要长比如 60 秒非流式 30 秒。超时时间要可配置不同模型不同。重试要克制只对幂等且明确可重试的错误重试如 429、503重试次数不超过 2 次且必须带退避指数退避 抖动。流式请求一旦开始返回就不能重试。降级有预案主模型不可用时降级到备用模型备用也不可用时返回缓存结果或友好提示而不是让请求一直挂着。熔断保护用 Sentinel 对模型调用做熔断错误率超过阈值直接快速失败给模型服务恢复的时间。这里有个容易忽略的点重试和熔断的阈值要联动。如果熔断已经打开了重试就没意义应该直接走降级。平台里要把这两个逻辑串起来。4.2 流式响应的稳定性断线、续传与背压流式响应在生产环境会遇到几个典型问题连接中断用户网络抖动SSE 连接断了。平台需要支持断点续传——记录已生成的 Token 位置重连后从断点继续。这要求会话状态持久化不能只存在内存。背压模型生成速度快于前端消费速度数据在网关堆积。需要做背压控制前端消费慢时通知上游减速或者丢弃非关键内容。多实例下的会话粘性如果会话状态在服务实例内存里用户重连到另一个实例就丢了上下文。解决方案是把会话状态外置到 Redis任何实例都能接管。流式链路的监控普通请求看响应时间流式请求要看首 Token 延迟TTFT和 Token 生成速率TPOT。这两个指标直接决定用户体验必须单独监控和告警。4.3 限流与配额防止一个租户拖垮所有人多租户场景下限流必须做到租户级。一个租户疯狂调用不能影响其他租户。平台需要网关层限流按租户、按接口、按 IP 多维度限流。模型层配额按租户分配 Token 配额用完降级或拒绝。并发控制限制单租户的同时在线请求数。Sentinel 配合 Redis 集群做集群限流是成熟方案。Redis 存计数器和令牌桶状态Sentinel 做规则判断。要注意 Redis 本身的高可用限流组件挂了不能影响主链路要有本地兜底限流。4.4 可观测性AI 链路的追踪比普通微服务难在哪普通微服务的链路追踪Span 里记录的是方法调用和耗时。AI 链路的 Span 里还要记录用了哪个模型、消耗多少 Token、Prompt 模板版本、召回文档 ID、是否命中缓存。这些信息对排查问题和成本分析至关重要。难点在于内容脱敏。Prompt 和响应里可能包含用户隐私或商业敏感信息不能明文记日志。平台需要做脱敏处理或者只记录元数据不记录内容需要内容时通过采样和授权机制获取。另外AI 链路的耗时分布和普通服务完全不同。一个请求 90% 时间花在模型生成上如果只看总耗时优化方向会跑偏。必须把模型调用、检索、后处理各阶段拆开监控。5. 前后端协作与 Vue 3 在 AI 场景下的特殊处理5.1 前端对接流式接口的正确姿势Vue 3 对接 SSE 或 WebSocket很多人第一反应是用 EventSource。但 EventSource 有个硬伤——只支持 GET 请求不能带自定义 Header。而 AI 接口通常需要传 Authorization、租户 ID 等 Header。实际项目里更常用的是fetchReadableStream手动解析 SSE或者用microsoft/fetch-event-source这类库。核心逻辑是发起 fetch 请求拿到 response.body 的 reader循环读取 chunk按 SSE 格式解析出 data 行更新到响应式状态。Vue 3 的响应式系统在这里很顺手——把流式内容绑定到一个 ref每收到一个 chunk 就更新界面自动刷新。但要注意更新频率如果每个 Token 都触发一次渲染高频输出时会有性能问题。实践中会做节流比如每 50ms 或每积累一定字符数才更新一次视图。5.2 会话状态管理Pinia 还是后端多轮对话的状态放哪放前端 Pinia 里刷新页面就丢放后端每次请求都要传上下文网络开销大。我的建议是混合方案后端存完整会话历史持久化到数据库或 Redis前端只存当前会话的最近几轮用于快速渲染。新请求时前端把会话 ID 传给后端后端自己取历史前端不用每次传全量上下文。这样既保证了刷新不丢又减少了传输量。Vue 3 的 Pinia 管理前端会话列表和当前会话后端通过会话 ID 关联。5.3 大文本渲染的性能优化AI 生成的回答可能很长几千字甚至上万字。如果直接 v-html 渲染长文本会导致页面卡顿。优化手段包括虚拟滚动只渲染可视区域的内容长对话列表必备。Markdown 增量渲染流式输出时不要每来一个字符就重新解析整个 Markdown而是增量解析。代码块高亮延迟流式过程中代码块可能不完整等流结束再高亮避免反复重排。图片懒加载如果回答里有多模态内容图片要懒加载。这些细节不做功能是能跑但用户体验会很差尤其是长回答场景。6. 落地实操从零搭建一个 AI 能力服务的完整步骤6.1 环境准备与依赖版本锁定先把基础环境列清楚版本不一致是新手最容易踩的坑组件推荐版本说明JDK21 LTS虚拟线程、分代 ZGCSpring Boot3.2.x支持 JDK 21Spring Cloud2023.0.x对应 Boot 3.2Spring Cloud Alibaba2023.0.x.xNacos、SentinelNacos2.3.x注册配置中心Redis7.x会话、限流、缓存MySQL8.x业务数据Node.js20 LTS前端构建Vue3.4.x前端框架版本锁定用 Maven 的 dependencyManagement 或者 Gradle 的 platform避免传递依赖冲突。Spring Cloud 和 Boot 的版本对应关系一定要查官方兼容表差一个小版本都可能启动报错。6.2 搭建模型网关服务的核心代码结构模型网关是平台的心脏代码结构建议这样组织model-gateway/ ├── adapter/ # 各模型厂商适配器 │ ├── OpenAiAdapter │ ├── QwenAdapter │ └── ... ├── router/ # 模型路由策略 ├── limiter/ # 限流配额 ├── metrics/ # Token 统计与监控 ├── fallback/ # 降级策略 └── controller/ # 对外统一接口适配器层用策略模式每个厂商实现统一接口。路由层根据租户配置、模型健康度、成本策略选择具体模型。这样新增一个模型厂商只需要加一个适配器不改其他代码。关键接口定义大致长这样public interface ModelAdapter { ModelResponse invoke(ModelRequest request); FluxModelChunk invokeStream(ModelRequest request); boolean healthCheck(); String getModelId(); }流式返回用 Reactor 的 Flux配合 Spring WebFlux 或者 Spring MVC 的 StreamingResponseBody。如果用虚拟线程同步写法也能支撑高并发看团队熟悉度选。6.3 会话服务的存储设计会话数据分两部分会话元数据会话 ID、用户、创建时间、最后活跃时间和消息列表角色、内容、时间戳、Token 数。存储选型上元数据放 MySQL消息列表放 RedisList 或 Stream 结构冷数据定期归档到 MySQL 或对象存储。Redis 里按会话 ID 做 key设置过期时间活跃会话续期。要注意上下文窗口裁剪。模型有最大 Token 限制历史消息不能无限传。策略有几种保留最近 N 轮、按 Token 数从旧到新裁剪、用摘要压缩早期对话。平台应该把裁剪策略做成可配置不同场景用不同策略。6.4 前端工程的目录组织与流式 Hook 封装Vue 3 项目里把流式请求封装成 composable 是很好的实践// composables/useStreamChat.js import { ref } from vue export function useStreamChat() { const content ref() const loading ref(false) const error ref(null) async function send(payload) { loading.value true content.value try { const response await fetch(/api/chat/stream, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(payload) }) const reader response.body.getReader() const decoder new TextDecoder() let buffer while (true) { const { done, value } await reader.read() if (done) break buffer decoder.decode(value, { stream: true }) const lines buffer.split(\n\n) buffer lines.pop() for (const line of lines) { if (line.startsWith(data: )) { const data line.slice(6) if (data [DONE]) return content.value JSON.parse(data).delta } } } } catch (e) { error.value e } finally { loading.value false } } return { content, loading, error, send } }这个 Hook 处理了缓冲、分片、结束标记业务组件里直接const { content, send } useStreamChat()就能用。注意 buffer 的处理——网络分片不一定按 SSE 消息边界切必须自己缓冲拼接。7. 踩过的坑与经验教训7.1 虚拟线程不是银弹这些场景反而更慢虚拟线程在 IO 密集场景确实强但有几个坑我实际踩过synchronized 导致的 pinning虚拟线程在 synchronized 块里阻塞时会占住载体线程失去虚拟线程的优势。如果代码里有老库用 synchronized 做锁或者自己写了 synchronized 的缓存需要换成 ReentrantLock。JDK 21 里可以用-Djdk.tracePinnedThreadsfull来检测 pinning。ThreadLocal 的滥用虚拟线程数量可能非常多每个都带 ThreadLocal 副本会吃内存。AI 场景下如果用了 ThreadLocal 存上下文要评估内存影响或者改用 ScopedValueJDK 21 预览特性。CPU 密集任务向量计算、文本处理这类 CPU 密集操作放虚拟线程里没收益反而增加调度开销。这类任务应该用固定大小的平台线程池。7.2 流式接口在网关被缓冲一个配置漏了全白干这个坑我印象最深。本地测试流式好好的一上生产就变成“等 30 秒然后一次性出来”。排查了半天发现是 Nginx 的proxy_buffering默认开着把 SSE 数据缓冲了。解决要改三处Nginxproxy_buffering off;和proxy_cache off;Spring Cloud Gateway确认没有配置响应体缓存应用层每次 write 后 flush任何一环缓冲流式体验就没了。上线前一定要在真实网关链路上测流式不能只测本地。7.3 Token 计费的坑流式请求的用量怎么算非流式请求模型返回里直接带 usage 字段好算。流式请求很多模型在最后一个 chunk 才返回 usage如果连接提前断了这次调用的 Token 就统计不到。平台需要做兜底估算——按输入文本长度和输出字符数估算 Token和实际值对账。另外要注意缓存命中的计费差异。很多模型对命中缓存的 Prompt 部分收费更低平台要能识别并分别统计否则成本核算会偏高。7.4 多模型切换时的 Prompt 兼容性不同模型对 Prompt 格式的敏感度不同。同一个 Prompt 在 A 模型上效果好换到 B 模型可能完全跑偏。平台做模型路由时不能简单地把同一个 Prompt 发给不同模型需要按模型维护 Prompt 模板变体。实践中Prompt 管理服务要支持模板继承——基础模板定义通用部分各模型变体覆盖差异部分。切换模型时自动选择对应变体而不是硬套。8. 这套底座适合什么样的团队以及后续怎么扩展8.1 团队规模与阶段匹配这套平台不是所有团队都需要。我的判断标准10 人以下、单一 AI 场景别上微服务一个单体应用加个模型调用封装就够了微服务的运维成本会压垮你。10-50 人、多个 AI 场景这套底座的价值开始显现统一模型网关和会话管理能省大量重复工作。50 人以上、AI 是核心业务必须上而且要在此基础上做更细的治理比如按业务线隔离、精细化成本核算。技术选型上如果团队 Java 背景强JDK 21 Spring Cloud 是顺理成章。如果团队 Python 背景强可以考虑 Python 服务通过标准协议融入这套微体系注册到同一个 Nacos走同一套网关。混合技术栈在 AI 场景很常见关键是协议统一。8.2 可以继续扩展的方向这套底座搭好之后往上可以长很多东西Agent 编排引擎把工作流从硬编码升级为可视化编排支持条件分支、循环、人工介入。RAG 增强接入更多检索策略混合检索、重排序支持知识库版本管理和灰度。评测体系建一套自动化评测每次 Prompt 或模型变更都跑回归防止效果退化。成本看板按租户、按应用、按模型维度展示 Token 消耗和费用让成本可见可控。多模态支持图片、音频、视频的输入输出统一到能力契约里。我个人在实际搭建这类平台时的体会是最难的不是技术是克制。一开始总想什么能力都做进去结果平台越来越重业务接入成本越来越高。后来想明白了底座只做三件事统一模型接入、统一会话管理、统一治理策略。其他都交给业务自己组合。底座越薄活得越久。最后分享一个实用技巧平台上线初期一定要做一个影子模式。新模型、新 Prompt 先在影子链路跑不影响真实用户对比效果和成本确认没问题再切流。这个机制能帮你避免很多线上事故尤其是在模型频繁迭代的阶段。