
1. 从一堆零散服务到统一底座QuickBlue 到底想解决什么问题第一次听到“QuickBlue”这个名字很多人会以为是某个新出的前端 UI 库或者监控面板。实际上它要处理的是企业里越来越普遍的一种困境AI 能力已经能跑起来了但跑得七零八落。模型调用散落在各个业务代码里提示词硬编码在 Java 类中向量检索、会话管理、限流降级各写一套最后没人说得清线上到底有几个版本在同时服务。QuickBlue 的定位就是把这些零散能力收拢到一个统一的“AI 应用底座”上。底座这个词很关键它不是业务系统本身而是业务系统下面那层承重结构。就像盖楼时先打地基、立柱子之后每层楼怎么隔间、怎么装修可以自由发挥但承重体系是统一的。QuickBlue 想做的就是让企业在接入大模型、构建 AI 应用时不必每次都从零搭一套调用框架、鉴权体系、流量治理和可观测链路。从热搜词能看出大家关注的点集中在微服务、JDK 21、Spring Cloud这几块。这说明 QuickBlue 并不是一个孤立的 AI 工具而是把 AI 能力嵌入到已有的微服务治理体系里。企业已经有了一套基于 Spring Cloud 的服务集群现在要往里面加 AI 能力最怕的就是另起炉灶。QuickBlue 的思路是复用微服务那套注册发现、配置中心、网关路由、熔断限流的成熟机制把 AI 调用当成一种特殊的服务能力来治理。那为什么非要一个“底座”而不是每个团队自己封装一个 SDK 就完事我踩过的坑很典型三个业务团队各自封装了模型调用A 团队用 OkHttp 直接发请求B 团队用 WebClient 做流式C 团队在 Spring AI 上又包了一层。结果就是日志格式不统一超时时间各不相同出了故障排查要翻三套代码。更麻烦的是当公司要统一换一个模型供应商时三个团队要改三遍测试三遍上线三遍。底座的价值就在这里——把变化点收敛到一层上层业务只面向稳定接口编程。QuickBlue 适合谁来参考如果你是后端架构师正在规划公司级 AI 能力接入方案那它的分层思路值得细看。如果你是 Spring Cloud 微服务的维护者被要求“把 AI 能力接进来但别搞乱现有体系”那它的集成方式能直接抄作业。如果你只是刚接触 AI 应用开发想理解一个生产级底座长什么样那从它的模块划分入手比直接看模型 API 文档更有全局感。2. 拆开 QuickBlue 的骨架分层设计与技术选型逻辑2.1 为什么是“底座”而不是“框架”或“平台”框架通常指一套开发范式比如 Spring Boot 让你按它的方式写代码平台往往带控制台、带运维界面偏向管理侧。底座介于两者之间它既提供开发时依赖的公共能力又承担运行时的统一治理职责但不强制你改变业务代码的组织方式。QuickBlue 选择“底座”这个定位我理解是为了降低接入成本——业务团队不需要把现有代码推倒重来只需要把原来直连模型的那几行替换成对底座客户端的调用。这个选择背后有个很现实的考量企业里跑着的 AI 相关代码很多是业务同学在赶需求时快速写出来的能跑就行。你让他为了接入底座去重构整个模块他肯定抵触。但如果底座提供的是一个和原来调用方式几乎一样的客户端只是底层换成了统一通道那推广阻力就小得多。QuickBlue 在接口设计上明显照顾了这种“无感迁移”的需求。2.2 JDK 21 带来的底层能力变化热搜词里 JDK 21 出现得很频繁这不是偶然。JDK 21 是 LTS 版本虚拟线程正式转正这对 AI 应用底座来说是个大利好。AI 调用有个特点大量时间花在等待模型响应上尤其是流式输出场景一个请求可能持续几十秒。传统平台线程模型下每个请求占一个线程并发一高线程池就爆。虚拟线程让“一个请求一个线程”的简单模型重新变得可行不用再为了省线程去写复杂的响应式代码。QuickBlue 如果基于 JDK 21 构建最直接的收益在流式对话和批量推理这两个场景。流式对话需要长时间持有连接虚拟线程的挂起成本极低可以轻松支撑数万并发会话。批量推理时底座可以给每个子任务分配一个虚拟线程代码写起来是同步阻塞的风格但实际执行效率接近异步。我实测过类似方案同样的硬件下虚拟线程版本比传统线程池版本在 IO 密集型任务上吞吐能高出三到五倍而且代码可读性好了不止一个档次。注意虚拟线程虽好但不要用它跑 CPU 密集型任务。AI 底座里做向量计算、文本预处理这类活还是应该交给固定大小的平台线程池否则虚拟线程的调度开销反而拖慢整体。2.3 Spring Cloud 生态的取舍与适配Spring Cloud Alibaba 停更的消息在社区里传了很久这让很多企业在选型时犹豫。QuickBlue 如果深度绑定 Spring Cloud就必须面对这个现实用原生 Spring Cloud 还是继续用 Alibaba 套件从热搜词看Sentinel、Nacos 这些 Alibaba 组件依然在被大量使用说明存量系统迁移成本很高。QuickBlue 比较务实的做法是抽象出服务发现、配置管理、流量治理这几个接口底层实现可以插拔。今天用 Nacos 做注册中心明天想换 Consul改配置就行不用动业务代码。Sentinel 做限流熔断如果将来要换 Resilience4j也是换实现类的事。这种“面向接口治理”的思路比直接依赖某个具体组件要稳得多。我在实际项目里吃过亏早期图省事直接调 Nacos API后来要换注册中心时发现几十个地方要改教训很深刻。2.4 微服务拆分粒度对 AI 底座的影响微服务拆分是个老话题但 AI 场景下有了新变化。传统业务微服务按领域拆分订单服务、用户服务、库存服务边界相对清晰。AI 能力怎么拆一种做法是拆出一个“AI 网关服务”所有模型调用都走它另一种是每个业务服务自己内嵌 AI 客户端底座只提供 SDK。QuickBlue 看起来倾向于前者但做了更细的层次划分。底座本身可能包含模型路由服务、提示词管理服务、会话状态服务、向量检索服务等。这些服务各自独立部署通过内部 RPC 或消息队列协作。这样拆的好处是模型路由可以独立扩缩容提示词管理可以独立发版不会因为改一个提示词模板就重启整个 AI 网关。但代价是调用链路变长一次对话可能经过三四个内部服务延迟和故障点都增加了。所以底座内部的服务间通信必须用高性能的 RPC不能再用 HTTP 绕一圈。3. 核心模块逐个拆从模型接入到流量治理的实操细节3.1 模型接入层统一协议与多供应商适配模型接入层是底座最靠近外部的一层也是最容易变的一层。今天接这家模型明天接那家接口协议、鉴权方式、流式格式都不一样。QuickBlue 在这一层的核心任务是定义一套内部统一协议把外部差异屏蔽掉。具体怎么做首先定义一个ModelRequest和ModelResponse包含消息列表、温度、最大 token 数这些通用参数。然后为每个模型供应商写一个适配器把内部请求翻译成供应商要求的格式再把供应商的响应翻译回内部格式。流式场景下适配器还要把不同格式的 SSE 事件统一成内部的事件流。这里有个细节很容易被忽略不同模型对“系统提示词”的支持程度不一样。有的模型有专门的 system 角色有的只能把系统提示词拼在用户消息前面。适配器要处理这种差异让上层业务不用关心。我见过一个项目业务代码里到处判断“如果是某模型就拼字符串否则用 system 角色”这种逻辑散落各处后来加新模型时改得痛不欲生。QuickBlue 把这种判断收在适配器里是正确做法。实操心得适配器里一定要做超时和重试的差异化配置。不同模型的响应时间差异很大用一个全局超时值要么误杀快模型要么拖死慢模型。建议按模型维度配置超时并且重试只对幂等的非流式请求开启流式请求重试会导致重复输出。3.2 提示词管理从硬编码到可配置提示词硬编码在 Java 类里是 AI 应用早期最常见的反模式。改一个标点符号都要走发版流程产品经理改一版文案开发就要跟着上一次线。QuickBlue 把提示词管理独立出来支持在配置中心或数据库里维护模板业务代码通过模板 ID 引用。模板引擎的选择也有讲究。简单的占位符替换用String.format或MessageFormat就够了但如果提示词里有条件分支、循环、变量默认值就需要更强大的模板引擎。我倾向于用轻量的模板语法比如{{variable}}这种不要引入太重的模板引擎否则学习成本和渲染开销都上去了。提示词版本管理是另一个关键点。同一个模板 ID 下可能有多个版本线上跑的是 v3测试环境在验证 v4。底座要支持按环境、按租户、按灰度比例来路由到不同版本。这样产品经理改提示词时可以先在小流量上验证效果确认没问题再全量。没有这套机制每次改提示词都是一次全量赌博。3.3 会话与上下文管理状态放哪里多轮对话需要保存上下文这个状态放哪里是个架构决策。放内存最简单但服务重启就丢多实例部署时还会串会话。放 Redis 是常见做法但要注意序列化方式和过期策略。会话数据可能很大尤其是长对话全部塞进一个 Redis value 里读写放大很严重。QuickBlue 如果做得好应该支持会话的分层存储最近几轮对话放 Redis更早的历史归档到数据库或对象存储。读取时先查 Redis命中不了再回源。这样既保证了热数据的低延迟又控制了内存占用。另外会话的过期时间要可配置不同业务场景对上下文保留时长的要求不一样客服机器人可能只需要保留最近半小时个人助理可能需要保留几个月。还有一个容易被忽视的问题会话并发写。同一个会话如果同时有两个请求在写后写的可能覆盖先写的。底座需要提供乐观锁或分布式锁机制保证会话更新的原子性。我见过线上出现对话内容错乱排查半天发现是两个请求并发更新会话导致的。3.4 流量治理限流、熔断与降级在 AI 场景的特殊性AI 调用的流量治理和传统接口有很大不同。传统接口的耗时通常在几十到几百毫秒AI 调用动辄几秒到几十秒。这意味着同样的并发数下AI 服务占用的连接和线程资源要多得多。限流策略不能简单照搬 QPS 限流还要考虑并发连接数限流。熔断策略也要调整。传统接口失败率超过阈值就熔断但 AI 调用失败的原因很复杂可能是模型服务过载可能是网络抖动也可能是提示词触发了内容安全策略。不同原因应该有不同的处理方式。模型过载可以重试或降级到备用模型内容安全拦截则不应该重试直接返回用户提示。降级方案在 AI 场景下尤其重要。当主模型不可用时是降级到更小的模型还是返回缓存结果还是直接告诉用户“服务繁忙”这取决于业务容忍度。底座应该提供降级策略的配置能力让业务方自己决定。我建议至少配置两级降级一级是切换到备用模型二级是返回兜底话术。不要小看兜底话术它能让用户在系统故障时依然得到有意义的反馈而不是一个冰冷的错误码。3.5 可观测性日志、指标与链路追踪AI 应用的可观测性比传统应用更难做因为多了模型这个黑盒。一次调用出了问题可能是业务代码的 bug可能是提示词写得不好可能是模型本身抽风也可能是网络问题。没有完善的观测数据排查就是盲人摸象。QuickBlue 需要在三个层面埋点请求层面记录完整的输入输出注意脱敏、模型层面记录 token 消耗和响应延迟、系统层面记录资源使用情况。链路追踪要把一次用户请求经过的所有内部服务串联起来包括模型路由、提示词渲染、会话读写、向量检索等环节。日志脱敏是个必须重视的问题。AI 对话内容可能包含用户隐私直接打到日志里风险很大。底座应该提供脱敏规则配置比如自动识别手机号、身份证号并替换。同时日志的存储周期也要控制不能无限期保留。4. 把 QuickBlue 跑起来从零搭建的实操路径4.1 环境准备与依赖版本锁定假设我们要在一台开发机上把 QuickBlue 的最小可用版本跑起来。首先明确基础环境JDK 21 是必须的因为要用虚拟线程。Maven 或 Gradle 选一个顺手的我习惯用 Maven依赖管理直观。注册中心和配置中心开发环境可以用 Nacos 的单机模式一条 Docker 命令就能起。依赖版本锁定是第一步。Spring Boot 3.2 以上才完整支持 JDK 21 的虚拟线程Spring Cloud 版本要对应 2023.0.x 系列。这里有个坑Spring Cloud Alibaba 的版本和 Spring Cloud 版本有严格的对应关系选错了启动就报兼容性错误。我建议直接查官方版本对应表不要凭感觉选。properties java.version21/java.version spring-boot.version3.2.5/spring-boot.version spring-cloud.version2023.0.1/spring-cloud.version spring-cloud-alibaba.version2023.0.1.0/spring-cloud-alibaba.version /properties数据库方面会话和提示词模板需要持久化MySQL 8.0 是稳妥选择。Redis 用于缓存和会话热数据版本 7.x 即可。向量检索如果要用可以选 Milvus 或 PgVector开发阶段用 PgVector 更省事不用额外维护一个向量数据库。4.2 服务拆分与模块初始化QuickBlue 底座本身建议拆成这几个模块quickblue-gateway负责统一入口和鉴权quickblue-model-router负责模型适配和路由quickblue-prompt负责提示词管理quickblue-session负责会话状态quickblue-common放公共依赖和工具类。初始化时用 Spring Initializr 生成骨架每个模块选上需要的依赖。网关模块选 Spring Cloud Gateway模型路由模块选 WebFlux 或 MVC 加虚拟线程会话模块选 Spring Data Redis 和 MyBatis-Plus。注意不要所有模块都引入全量依赖按需引入否则启动慢、内存占用高。模块间的调用关系要提前定好。网关只调用模型路由模型路由调用提示词和会话会话调用 Redis 和数据库。不要让网关直接调会话否则链路混乱。调用方式统一用 OpenFeign 或 Dubbo我倾向于 Feign和 Spring Cloud 集成更顺滑。4.3 模型适配器的编写与注册写一个模型适配器核心是实现统一接口。假设我们定义ModelAdapter接口包含chat和streamChat两个方法。然后为每个模型供应商写实现类比如OpenAIAdapter、QwenAdapter、DeepSeekAdapter。适配器里要做的事情包括把内部ModelRequest转成供应商格式、设置鉴权头、处理流式响应、把供应商的错误码转成内部错误码。流式响应的处理最麻烦不同供应商的 SSE 格式不一样有的用data:前缀有的用 JSON Lines。适配器要统一成内部事件流上层业务只关心onMessage、onComplete、onError三个回调。适配器写完后要注册到路由表里。可以用 Spring 的Component自动扫描也可以手动注册到ModelRouter里。我建议用配置驱动的方式在配置文件里指定模型名称和适配器类的映射关系这样加新模型不用改代码加个配置就行。quickblue: models: - name: gpt-4o adapter: openai endpoint: https://api.example.com/v1 timeout: 60s - name: qwen-max adapter: qwen endpoint: https://dashscope.example.com/v1 timeout: 30s4.4 提示词模板的存储与渲染提示词模板存在数据库里表结构至少包含模板 ID、版本号、内容、变量定义、创建时间、状态。变量定义用 JSON 存描述每个变量名、类型、是否必填、默认值。渲染时先查模板再用变量值填充。渲染引擎我推荐用 Handlebars 或 Mustache语法简单功能够用。不要用 FreeMarker 或 Velocity太重了而且语法复杂容易写出难以维护的模板。渲染时要做好变量校验必填变量缺失直接报错不要渲染出半成品提示词发给模型。模板的灰度发布通过版本号加路由规则实现。请求里带上租户 ID 或用户 ID底座根据配置决定用哪个版本。灰度比例可以按百分比也可以按白名单。我建议灰度初期用白名单找几个内部用户先试稳定后再逐步放量。4.5 会话状态的读写与过期策略会话状态用 Redis Hash 存储key 是会话 IDfield 是轮次序号value 是消息内容。这样存储的好处是可以按轮次读取不用一次性加载全部历史。读取最近 N 轮时用HSCAN或按序号范围查效率比读整个 String 高。过期策略分两层Redis 层面设置 TTL比如 2 小时数据库层面设置归档策略比如 7 天后清理。TTL 到期后热数据消失但归档数据还在需要时可以回源。归档可以用定时任务批量写入不要每次写会话都同步写数据库那样数据库压力太大。并发写的问题用 Redis 的WATCH或 Redisson 的分布式锁解决。我倾向于用 RedissonAPI 更友好而且有看门狗机制自动续期不用担心锁过期。但要注意锁的粒度按会话 ID 加锁不要全局一把锁。4.6 限流熔断规则的配置与验证限流用 Sentinel 或 Resilience4j 都行。Sentinel 的规则可以动态推送到 Nacos改规则不用重启。配置限流规则时除了 QPS还要加并发线程数限流。AI 调用的并发线程数建议设小一点比如单实例 50因为每个线程占用的资源多。熔断规则要区分异常类型。模型超时算慢调用触发熔断模型返回 4xx 错误算业务异常不触发熔断模型返回 5xx 算系统异常触发熔断。Sentinel 支持按异常类型配置但需要自定义BlockExceptionHandler来区分。验证限流熔断是否生效可以用 JMeter 或 wrk 压测。压测时观察日志里有没有触发限流熔断后有没有走降级逻辑。我建议在测试环境专门做一轮故障注入手动把模型服务停掉看底座是否能正确降级降级后的响应时间是否在可接受范围内。5. 踩坑实录那些文档里不会写的经验5.1 虚拟线程与 ThreadLocal 的冲突JDK 21 的虚拟线程对 ThreadLocal 的支持有变化。传统平台线程里ThreadLocal 是每个线程一份用起来很顺手。虚拟线程数量巨大如果每个虚拟线程都持有一份 ThreadLocal 副本内存会爆。所以虚拟线程默认不支持 ThreadLocal需要用ScopedValue替代。QuickBlue 里如果用了 ThreadLocal 存用户上下文、租户信息迁移到虚拟线程时会出问题。我踩过的坑是在虚拟线程里取 ThreadLocal 值取到的是 null 或者别的请求的值。解决办法是把这些上下文改成方法参数传递或者用ScopedValue。改造量不小但必须做否则线上会出现串数据的严重 bug。注意如果暂时改不动可以在虚拟线程的入口处显式拷贝 ThreadLocal 值到局部变量但这是权宜之计长期还是要用 ScopedValue。5.2 流式响应的背压处理流式输出时模型吐 token 的速度可能快于客户端消费的速度。如果没有背压机制数据会在内存里堆积最终 OOM。Spring WebFlux 的Flux天然支持背压但如果你用的是 MVC 加SseEmitter就要自己处理。我的做法是在SseEmitter的onCompletion和onTimeout回调里做清理同时限制每个连接的缓冲区大小。当缓冲区满时暂停从模型读取等客户端消费后再继续。这需要和模型适配器配合适配器要支持暂停和恢复读取。实现起来有点复杂但流式场景下这是必须的。5.3 模型返回内容的安全过滤模型返回的内容可能包含不当信息底座需要在返回给用户前做过滤。过滤可以在两个地方做模型适配器里做一次网关里再做一次。适配器里做是为了尽早拦截网关里做是为了兜底。过滤规则要可配置不同业务场景的严格程度不一样。内部工具可以宽松一些面向公众的产品要严格。过滤命中后是直接替换敏感词还是返回兜底话术也要可配置。我建议至少支持替换和拦截两种模式。5.4 常见问题速查表问题现象可能原因排查方向解决办法启动报类冲突Spring Cloud 与 Alibaba 版本不匹配检查版本对应表按官方对应关系调整版本流式输出中断网关超时时间太短查看网关超时配置调大网关和负载均衡的超时会话串数据ThreadLocal 在虚拟线程中失效检查上下文传递方式改用 ScopedValue 或方法参数限流不生效Sentinel 规则未推送成功查看 Nacos 配置和 Sentinel 日志检查规则格式和推送通道模型调用超时超时设置过短或网络问题查看模型响应时间和网络延迟按模型维度调整超时加网络监控提示词渲染报错变量缺失或类型不匹配查看渲染日志和模板定义补全变量加渲染前校验Redis 内存增长快会话数据未设 TTL检查 Redis key 的过期时间设置合理 TTL加归档任务熔断后不恢复熔断时间窗口设置过长查看熔断器配置调整时间窗口和半开探测策略5.5 性能调优的几个关键参数虚拟线程的调度器默认用 ForkJoinPool并行度默认是 CPU 核数。IO 密集型场景下这个并行度可能不够可以适当调大。但不要调得太大否则上下文切换开销会抵消收益。我一般设成 CPU 核数的 2 到 4 倍具体看压测结果。Redis 连接池的大小也要调。会话读写频繁连接池太小会成为瓶颈。建议最大连接数设成 50 到 100具体看并发量。但要注意 Redis 服务端的连接数限制别把 Redis 打挂了。模型调用的 HTTP 客户端连接池同样重要。如果用 OkHttp默认连接池是 5 个肯定不够。调到 50 到 100并且开启连接复用。如果用 WebClient底层是 Reactor Netty连接池配置在HttpClient里。6. 底座之上还能长什么扩展方向与个人体会QuickBlue 作为底座本身不产生业务价值价值在于让上层业务跑得更快更稳。底座稳定之后可以在上面长出的东西很多。比如统一的 AI 效果评估模块自动收集用户反馈计算模型回答的满意度为提示词优化提供数据支撑。再比如成本核算模块按租户、按业务线统计 token 消耗让 AI 成本可分摊、可控制。还有一个方向是智能路由。现在模型路由大多是静态配置指定哪个业务用哪个模型。将来可以根据请求内容自动选择模型简单问题走小模型复杂问题走大模型中文问题走中文优化模型代码问题走代码模型。这需要底座积累足够的调用数据训练一个路由决策模型。听起来有点绕但逻辑上说得通。我在实际搭建类似底座的过程中最大的体会是不要一开始就追求大而全。先把模型接入和会话管理这两个最核心的模块做扎实让业务能跑起来。限流熔断、提示词管理、可观测性这些可以逐步加。我见过一个团队底座还没跑通就设计了十几个模块结果三个月没上线业务方失去耐心项目被砍。底座的价值在于被使用不被使用的底座设计得再漂亮也是零。另一个体会是底座的接口要尽量稳定内部实现可以频繁改。业务方依赖的是接口接口一变他们就要跟着改怨声载道。所以设计接口时要多想几步留好扩展点。比如模型调用的返回结果除了内容本身还要预留 token 消耗、模型标识、延迟等字段将来要用的时候不用改接口。最后分享一个小技巧底座上线初期一定要加一个“旁路模式”。业务请求正常走原有逻辑同时异步复制一份到新底座对比两边的输出和延迟。确认底座稳定后再逐步切流量。这样即使底座出问题业务也不受影响。旁路模式跑一两周心里就有底了。