Day49-AI微服务化-将大模型能力封装为标准微服务
把大模型当作一个普通的HTTP微服务来对待是这一代Java工程师必须建立的工程思维。今天这篇我会用我们项目组的真实改造过程为例从架构分层、代码实现到模型路由一步步把AI能力封装成「别的业务系统愿意调用」的微服务。一、为什么大模型必须微服务化很多人第一次接触AI能力第一反应就是「业务代码里直接调一下ChatClient.call()不就完了」行但只在你这个项目就一个AI调用点的阶段。一旦出现下面任意一种情况你就该把它服务化了触发场景直接调用的痛点多个业务团队都要用AI每个团队都要接一遍API、申请一遍Key、重写一遍Prompt公司用了多家模型不同团队锁死在某个供应商议价权丢失要做AI用量计费用量分散在各业务系统表里月底对账对到哭出了幻觉/Prompt注入没有统一的输入净化层没有审计日志老板要看AI大屏没有统一的调用指标采集点核心原则AI能力就像数据库一样是企业级基础设施——你不会让每个业务系统直接连生产DB为什么要让它们直接调大模型二、AI微服务的四层架构把大模型封装成标准微服务我们采用四层架构。我把各层的职责划清楚关键原则业务团队只看到第②层他们不需要知道底下是GPT-4还是Qwen。这就是服务化的意义——对调用方隐藏复杂性。三、代码实现搭建一个AI能力微服务下面我用一个真实的「文档问答」能力为例把这套架构跑通。所有代码基于 Spring Boot 3.3 Spring AI 1.0 JDK 17。3.1 Maven依赖xml dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-zhipuai/artifactId /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-actuator/artifactId /dependency dependency groupIdio.micrometer/groupId artifactIdmicrometer-registry-prometheus/artifactId /dependency /dependencies3.2 业务能力服务对外的稳定接口这是给业务团队调的入口。我把所有业务字段、错误码、SLA都封装在这里下游完全不感知模型变更。/** * AI能力微服务的对外Controller * 业务团队只依赖这个接口与模型解耦 */ RestController RequestMapping(/api/v1/ai) RequiredArgsConstructor public class AiCapabilityController { private final DocumentQaService documentQaService; private final UsageRecorder usageRecorder; /** * 文档问答接口 * 业务系统只需要传文档ID 问题 用户ID */ PostMapping(/doc-qa) public ResponseEntityAiResponseDocAnswer docQa(Valid RequestBody DocQaRequest request, RequestHeader(X-Biz-Source) String bizSource) { long start System.currentTimeMillis(); try { // 1. 参数基础校验鉴权在外层网关这里只做业务校验 Document doc documentService.getById(request.getDocId()); if (doc null) { return AiResponse.fail(DOC_NOT_FOUND, 文档不存在); } // 2. 调用业务能力内部实现对调用方完全透明 DocAnswer answer documentQaService.answer( request.getDocId(), request.getQuestion(), request.getUserId() ); // 3. 记录用量这里是最关键的环节后面看 long cost System.currentTimeMillis() - start; usageRecorder.record(bizSource, doc-qa, answer.getPromptTokens(), answer.getCompletionTokens(), cost); return AiResponse.ok(answer); } catch (BusinessException e) { log.warn(业务异常 biz{} code{}, bizSource, e.getCode()); return AiResponse.fail(e.getCode(), e.getMessage()); } catch (Exception e) { log.error(AI调用异常, e); // 兜底降级返回预先准备好的标准答案或转人工 return AiResponse.fail(AI_UNAVAILABLE, AI服务暂时不可用请稍后重试); } } /** * 流式问答SSE—— 长对话场景必备 */ GetMapping(value /doc-qa/stream, produces MediaType.TEXT_EVENT_STREAM_VALUE) public FluxServerSentEventString streamDocQa(RequestParam String docId, RequestParam String question, RequestParam String userId) { return documentQaService.streamAnswer(docId, question, userId) .map(chunk - ServerSentEvent.builder(chunk).build()) // 异常也要返回给前端不能让连接挂死 .onErrorResume(e - { log.error(SSE流式异常, e); return Flux.just(ServerSentEvent.builder([ERROR]AI服务异常).build()); }); } }这段代码的几个关键设计X-Biz-Source头部强制业务方标识来源用量统计、计费、限流的依据。AiResponse统一包装所有AI接口必须返回这个结构前端对接成本最低。业务校验与AI调用分离业务校验失败绝不打模型节省Token。兜底降级AI挂掉不能影响业务——这是微服务的基本素养。3.3 模型路由层按场景选择模型与降级业务能力层下面就是模型路由层。这里是AI微服务最体现工程能力的地方。/** * 模型路由根据场景动态选择模型 * 核心思想把调哪个模型的决策从业务代码里剥离出来 */ Service Slf4j public class ModelRouter { private final ChatModel openAiModel; // GPT-4o private final ChatModel qwenModel; // 通义千问 private final ChatModel deepseekModel; // DeepSeek private final TokenBucketLimiter limiter; // 限流器 /** * 路由策略封装 */ public ChatModel route(RouteContext ctx) { // 1. 限流检查最重要的护栏 if (!limiter.tryAcquire(ctx.getBizSource())) { throw new BusinessException(RATE_LIMIT, 调用频率超限); } // 2. 按业务来源路由 return switch (ctx.getBizSource()) { case customer-service - { // 客服场景中文友好、成本敏感 → 通义 log.debug(路由到通义千问); yield qwenModel; } case code-assistant - { // 代码场景英文能力强、JSON格式稳 → DeepSeek log.debug(路由到DeepSeek); yield deepseekModel; } case image-desc - { // 多模态 → GPT-4o yield openAiModel; } default - { // 兜底用主力模型 yield qwenModel; } }; } } /** * 路由上下文把决策依据集中管理 */ Data Builder public class RouteContext { private String bizSource; // 业务来源 private String userId; // 用户ID private int priority; // 优先级 0-99最高 private boolean isVip; // 是否VIP private int estimatedTokens; // 预估Token }为什么要做路由成本优化简单问题用便宜模型复杂问题用强模型风险分散一家模型API挂掉自动切到备胎差异化SLAVIP用户走专用通道。3.4 用量统计大模型版「拦路虎」这是把AI「真正当微服务」的关键一步——没有计费的微服务老板没法给你批预算。/** * 用量记录器 * 高频调用场景必须异步不能阻塞AI调用主链路 */ Component Slf4j public class UsageRecorder { private final UsageMapper usageMapper; private final ThreadPoolExecutor asyncPool; /** * 记录一次AI调用 * 用线程池异步落库不阻塞调用链路 */ public void record(String bizSource, String capability, int promptTokens, int completionTokens, long costMs) { asyncPool.execute(() - { try { UsageRecord record UsageRecord.builder() .bizSource(bizSource) .capability(capability) .promptTokens(promptTokens) .completionTokens(completionTokens) .totalTokens(promptTokens completionTokens) .costMs(costMs) .estimatedCost(calcCost(bizSource, promptTokens, completionTokens)) .timestamp(LocalDateTime.now()) .build(); usageMapper.insert(record); } catch (Exception e) { // 用量记录失败不能影响主流程记录到日志兜底 log.error(用量记录失败 biz{} capability{}, bizSource, capability, e); } }); } /** * 成本计算 * 真实生产场景这里应该读配置中心模型价格随时会变 */ private BigDecimal calcCost(String bizSource, int pt, int ct) { // 单位元/千Token BigDecimal ptRate switch (bizSource) { case customer-service - new BigDecimal(0.0008); // 通义 case code-assistant - new BigDecimal(0.001); // DeepSeek default - new BigDecimal(0.005); // GPT-4o }; return ptRate.multiply(new BigDecimal(pt ct)) .divide(new BigDecimal(1000), 6, RoundingMode.HALF_UP); } }到这里一个完整的AI微服务就出来了。再用 Spring Cloud Gateway 给它加个网关限流一个企业级的AI能力中台就成形了。四、AI调用链追踪把日志串起来微服务架构下AI调用会跨越多个内部服务。没有TraceId出问题排查能让你薅秃头发。下面是个轻量级的TraceId方案不依赖SkyWalking这种重武器但能解决80%的问题/** * AI调用上下文贯穿整个调用链 * 用ThreadLocal传跨服务通过Header传递 */ public class AiCallContext { private static final ThreadLocalCallContext CONTEXT new ThreadLocal(); public static void set(String traceId, String userId, String bizSource) { CONTEXT.set(new CallContext(traceId, userId, bizSource, System.currentTimeMillis())); } public static CallContext get() { return CONTEXT.get(); } public static void clear() { CONTEXT.remove(); } Data AllArgsConstructor public static class CallContext { private String traceId; private String userId; private String bizSource; private long startTime; } } /** * 在请求入口设置TraceId */ Component public class AiCallContextFilter extends OncePerRequestFilter { Override protected void doFilterInternal(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) { String traceId req.getHeader(X-Trace-Id); if (StringUtils.isEmpty(traceId)) { traceId UUID.randomUUID().toString().replace(-, ); } AiCallContext.set(traceId, req.getHeader(X-User-Id), req.getHeader(X-Biz-Source)); resp.setHeader(X-Trace-Id, traceId); try { chain.doFilter(req, resp); } finally { AiCallContext.clear(); // 必须清理否则线程复用会污染 } } }日志里带上traceId出问题时grep一下一个调用从进入到AI返回的全部日志就齐了{ traceId: a3f8c91b9e2d4f5c, bizSource: customer-service, capability: doc-qa, promptTokens: 425, completionTokens: 188, costMs: 1245, level: INFO, msg: AI调用完成 }五、生产环境的几个坑最后说几个我们踩过的坑给大家提个醒1. 输入净化是AI微服务的标配别以为你接的是「自己公司的模型」就不做Prompt注入防御。我见过业务方把用户原始输入直接拼到Prompt里被攻击后模型输出违规内容被监管处罚的案例。所有用户输入必须过一道InputSanitizer至少做长度限制 敏感词过滤 角色锁定。2. 超时一定要短熔断一定要狠大模型调用动辄5~30秒你如果不在网关层设个2秒超时把请求cut掉整个调用链都会被拖死。我们生产上AI网关默认超时1.5秒熔断阈值是20%错误率就开跳。3. Token限流比QPS限流更有意义普通微服务按QPS限流AI微服务要按Token限流。因为同样是「1次调用」问你好和问翻译一篇5000字文档消耗的资源差100倍。我们用 Redis 滑动窗口 Token 计数器1分钟窗口最多消耗100万Token超了就降级到便宜模型或直接拒绝。4. 用量计费一定要做实时看板别等月底拉Excel对账。我们搞了个Grafana看板按业务方/能力/时间段三维展示用量、成功率、平均Token、花费。老板每周看一次预算批得飞快。写到这里你会发现把大模型当一个普通微服务对待本质就是抽象和分层。这两件事是软件工程40年不变的底层能力。不会因为你把数据库换成了大模型就变得更高深了。它仍然是把变化的部分隔离出来把不变的部分稳定暴露出去。下篇预告第50篇《分布式一致性Raft协议动画式讲解》—— 微服务之后必须啃的分布式一致性硬骨头从Leader选举到日志复制一次讲透。系列文章回顾90篇JAVA高级工程师深度进阶文章从实践案例、项目代码出发用事实说话版权声明本文为「老梁」原创出品90天Java后端AI系列第48篇转载请注明出处。

相关新闻

【数据结构】二叉树的存储结构(顺序/链式)

【数据结构】二叉树的存储结构(顺序/链式)

考点频率:★★★★☆(选择题常考,是理解二叉树遍历和操作的基础) 难度:⭐⭐ 建议:重点掌握顺序存储的适用范围(完全二叉树)和链式存储的节点结构 1️⃣ 存储结构概述 在上一篇文章中…

2026/10/25 17:06:31 阅读更多 →
第6章 时域递归滤波:IIR滤波器在视频降噪中的应用

第6章 时域递归滤波:IIR滤波器在视频降噪中的应用

作者:一位在ISP领域摸爬滚打十余年的算法工程师 “纸上得来终觉浅,绝知此事要躬行。”——这门课的每一行代码,都希望你亲自跑一遍。 从原理到代码,从理论到工程落地 本课程共30章,系统讲解ISP核心算法——多帧降噪、HDR合成、夜景算法的原理、实现与调优。 每章包含:原理…

2026/10/25 17:13:02 阅读更多 →
线程中断-interrupted方法

线程中断-interrupted方法

一、没有中断机制,代码会出什么问题?假设 Java 没有 interrupt(),你要怎么让一个正在运行的线程停下来?方案 1:用一个 volatile boolean 标志位class Worker implements Runnable {private volatile boolean running …

2026/10/21 19:42:31 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/25 11:19:54 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/25 22:36:10 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/24 18:34:36 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/25 10:02:42 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/25 3:12:30 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/24 21:09:59 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/25 10:02:43 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/25 13:21:54 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/25 7:47:32 阅读更多 →