Spring-AI与LangChain4j

Java AI 框架 · Spring AI 与 LangChain4j

风格说明 :本篇是 设计型(主)+ 操作型(辅)混合 ------覆盖 Spring AI 核心架构、模型接入、RAG 实战、Tool Calling、Advisor 护栏、LangChain4j 对比、Spring AI Alibaba、生产化工程、完整实战项目;v2.1 新增 §12 Spring AI 1.0 GA / MCP / LiteLLM 网关、§99 本章冲刺这是 Java 后端开发者转型 AI 的技术抓手------不需要转 Python,在 Spring Boot 生态内即可完成 LLM 应用开发

返回 README | ⬅️ 上一篇 13-Agent工程实践 | ➡️ 下一篇 15-低代码AI平台

前置阅读03-RAG(RAG 全流程);04-Agent(Agent 框架)。

后续展开15-低代码AI平台(Dify/Coze 实战);16-AI全栈实战(从原型到生产方法论)。

官方对照(编写依据)

组件 官方文档 本篇边界
Spring AI Reference · ChatClient · Advisors · MCP Client API 名与 starter 以 1.0+ 为准;Resilience4jChatModel 等为工程伪代码
LangChain4j docs.langchain4j.dev 版本以 BOM 1.x 为准(非 0.36)
LiteLLM docs.litellm.ai OpenAI-compatible base-url 路由
MCP modelcontextprotocol.io 协议与 Spring AI MCP starter 对齐

1. Java AI 框架全景与工程定位

1.1 一句话定义

Spring AI = Spring Boot 生态的 AI 抽象层------用 Java 开发者熟悉的编程模型(自动配置、依赖注入、AOP)封装 LLM 调用、RAG、Tool Calling 和 Agent,让 10 年 Java 经验直接迁移到 AI 应用开发。

1.2 为什么 Java 开发者不应该全转 Python

维度 全转 Python Java + Spring AI
学习成本 6-12 月(语言 + 生态 + 工程习惯) 2-4 周(API 风格与 Spring 一致)
已有资产 全部废弃(微服务、中间件、监控) 完全复用(Spring Boot / Cloud / Security)
并发模型 GIL 限制,多进程开销大 虚拟线程(Loom)/ Reactor,天然高并发
类型安全 动态类型,运行时出错 编译期检查,IDE 重构安全
企业级特性 需额外搭建(认证/授权/审计/事务) Spring Security / Actuator / Micrometer 开箱即用
团队协作 团队需重新培训 现有 Java 团队无缝上手

面试金句:"Python 是 AI 研究的最佳语言,Java 是 AI 工程化落地的最佳语言------关键不是选哪个,而是在正确的层用正确的语言。"

1.3 Java AI 框架全景

#mermaid-svg-Wxtjq9KSTxXXw9ZU{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Wxtjq9KSTxXXw9ZU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .error-icon{fill:#552222;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .marker.cross{stroke:#333333;}#mermaid-svg-Wxtjq9KSTxXXw9ZU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Wxtjq9KSTxXXw9ZU p{margin:0;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .cluster-label text{fill:#333;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .cluster-label span{color:#333;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .cluster-label span p{background-color:transparent;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .label text,#mermaid-svg-Wxtjq9KSTxXXw9ZU span{fill:#333;color:#333;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .node rect,#mermaid-svg-Wxtjq9KSTxXXw9ZU .node circle,#mermaid-svg-Wxtjq9KSTxXXw9ZU .node ellipse,#mermaid-svg-Wxtjq9KSTxXXw9ZU .node polygon,#mermaid-svg-Wxtjq9KSTxXXw9ZU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .rough-node .label text,#mermaid-svg-Wxtjq9KSTxXXw9ZU .node .label text,#mermaid-svg-Wxtjq9KSTxXXw9ZU .image-shape .label,#mermaid-svg-Wxtjq9KSTxXXw9ZU .icon-shape .label{text-anchor:middle;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .rough-node .label,#mermaid-svg-Wxtjq9KSTxXXw9ZU .node .label,#mermaid-svg-Wxtjq9KSTxXXw9ZU .image-shape .label,#mermaid-svg-Wxtjq9KSTxXXw9ZU .icon-shape .label{text-align:center;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .node.clickable{cursor:pointer;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .arrowheadPath{fill:#333333;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Wxtjq9KSTxXXw9ZU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Wxtjq9KSTxXXw9ZU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Wxtjq9KSTxXXw9ZU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .cluster text{fill:#333;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .cluster span{color:#333;}#mermaid-svg-Wxtjq9KSTxXXw9ZU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Wxtjq9KSTxXXw9ZU rect.text{fill:none;stroke-width:0;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .icon-shape,#mermaid-svg-Wxtjq9KSTxXXw9ZU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .icon-shape p,#mermaid-svg-Wxtjq9KSTxXXw9ZU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .icon-shape .label rect,#mermaid-svg-Wxtjq9KSTxXXw9ZU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Wxtjq9KSTxXXw9ZU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Wxtjq9KSTxXXw9ZU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Wxtjq9KSTxXXw9ZU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Java AI 框架全景
Spring AI
LangChain4j
Semantic Kernel Java
其他: JLama / DJL
Spring 官方 · 1.0 GA
Spring AI Alibaba 扩展
Quarkus / Micronaut / Spring 均可
Microsoft 主导 · 多语言统一 API


2. 框架选型决策树

2.1 六维对比表

维度 Spring AI 1.0+ LangChain4j 1.x(BOM 如 1.15+) Semantic Kernel Java
抽象层次 高(ChatClient Fluent API) 中(AiService 代理模式) 高(Kernel + Plugin)
Spring 集成度 ⭐⭐⭐⭐⭐ 原生 Auto-Config ⭐⭐⭐ 有 Spring Boot Starter ⭐⭐ 需手动集成
社区活跃度 ⭐⭐⭐⭐(Spring 官方背书) ⭐⭐⭐⭐⭐(社区驱动,迭代快) ⭐⭐(Java 版关注度低)
模型覆盖 20+ Provider(OpenAI/Ollama/通义/DeepSeek/Azure) 15+ Provider 10+ Provider
RAG 支持 完整 ETL 管线 + VectorStore 抽象 完整 RAG 模块 + EmbeddingStore 基础 Memory + Plugin
生产就绪度 ⭐⭐⭐⭐(1.0 GA,API 稳定) ⭐⭐⭐(API 仍在快速变化) ⭐⭐(Java 版功能滞后)

2.2 选型决策树

#mermaid-svg-TWbp40vZFQzD45c6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TWbp40vZFQzD45c6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TWbp40vZFQzD45c6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TWbp40vZFQzD45c6 .error-icon{fill:#552222;}#mermaid-svg-TWbp40vZFQzD45c6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TWbp40vZFQzD45c6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TWbp40vZFQzD45c6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TWbp40vZFQzD45c6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TWbp40vZFQzD45c6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TWbp40vZFQzD45c6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TWbp40vZFQzD45c6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TWbp40vZFQzD45c6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TWbp40vZFQzD45c6 .marker.cross{stroke:#333333;}#mermaid-svg-TWbp40vZFQzD45c6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TWbp40vZFQzD45c6 p{margin:0;}#mermaid-svg-TWbp40vZFQzD45c6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TWbp40vZFQzD45c6 .cluster-label text{fill:#333;}#mermaid-svg-TWbp40vZFQzD45c6 .cluster-label span{color:#333;}#mermaid-svg-TWbp40vZFQzD45c6 .cluster-label span p{background-color:transparent;}#mermaid-svg-TWbp40vZFQzD45c6 .label text,#mermaid-svg-TWbp40vZFQzD45c6 span{fill:#333;color:#333;}#mermaid-svg-TWbp40vZFQzD45c6 .node rect,#mermaid-svg-TWbp40vZFQzD45c6 .node circle,#mermaid-svg-TWbp40vZFQzD45c6 .node ellipse,#mermaid-svg-TWbp40vZFQzD45c6 .node polygon,#mermaid-svg-TWbp40vZFQzD45c6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TWbp40vZFQzD45c6 .rough-node .label text,#mermaid-svg-TWbp40vZFQzD45c6 .node .label text,#mermaid-svg-TWbp40vZFQzD45c6 .image-shape .label,#mermaid-svg-TWbp40vZFQzD45c6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-TWbp40vZFQzD45c6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TWbp40vZFQzD45c6 .rough-node .label,#mermaid-svg-TWbp40vZFQzD45c6 .node .label,#mermaid-svg-TWbp40vZFQzD45c6 .image-shape .label,#mermaid-svg-TWbp40vZFQzD45c6 .icon-shape .label{text-align:center;}#mermaid-svg-TWbp40vZFQzD45c6 .node.clickable{cursor:pointer;}#mermaid-svg-TWbp40vZFQzD45c6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TWbp40vZFQzD45c6 .arrowheadPath{fill:#333333;}#mermaid-svg-TWbp40vZFQzD45c6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TWbp40vZFQzD45c6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TWbp40vZFQzD45c6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TWbp40vZFQzD45c6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TWbp40vZFQzD45c6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TWbp40vZFQzD45c6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TWbp40vZFQzD45c6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TWbp40vZFQzD45c6 .cluster text{fill:#333;}#mermaid-svg-TWbp40vZFQzD45c6 .cluster span{color:#333;}#mermaid-svg-TWbp40vZFQzD45c6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TWbp40vZFQzD45c6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TWbp40vZFQzD45c6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-TWbp40vZFQzD45c6 .icon-shape,#mermaid-svg-TWbp40vZFQzD45c6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TWbp40vZFQzD45c6 .icon-shape p,#mermaid-svg-TWbp40vZFQzD45c6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TWbp40vZFQzD45c6 .icon-shape .label rect,#mermaid-svg-TWbp40vZFQzD45c6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TWbp40vZFQzD45c6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TWbp40vZFQzD45c6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TWbp40vZFQzD45c6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是: 统一路由/计费/限流
否: 单云直连
Spring Boot 为主
Quarkus / Micronaut
多语言统一
是: Security/Actuator/Cloud
否: 轻量独立
高: 生产核心链路
低: 实验/原型
Java AI 框架选型
多模型/多云统一入口?
LiteLLM Proxy

OpenAI-compatible base-url
团队技术栈?
需要 Spring 深度集成?
LangChain4j
Semantic Kernel
Spring AI
API 稳定性要求?
推荐: 企业级/金融/电商核心
推荐: 快速迭代/多框架/社区先进特性
推荐: 微软生态/多语言团队
Gateway 失败时 fallback

直连 Azure OpenAI / 通义

2.3 按场景推荐

场景 推荐 原因
存量 Spring Boot 微服务接入 AI Spring AI 零摩擦集成,复用现有基础设施
新项目快速原型 LangChain4j 社区活跃,新特性快
金融/支付核心链路 Spring AI API 稳定 + Spring Security 原生集成
Quarkus 云原生项目 LangChain4j 原生支持 Quarkus 扩展
微软 Azure 技术栈 Semantic Kernel 与 Azure OpenAI 深度集成
多模型路由 / 统一计费 / 密钥轮换 LiteLLM Proxy + Spring AI base-url 指向网关;网关挂 Azure / OpenAI / DeepSeek / 通义;见 §12.2

网关原则 :Spring AI 侧只认 OpenAI-compatible API ------生产默认走 LiteLLM;网关故障或单模型 SLA 跌破阈值时 fallback 直连 Azure OpenAI(或区域备援),避免单点拖垮客服/搜索全链路。


3. Spring AI 核心架构

3.1 分层架构

#mermaid-svg-7PnY9Vl41KImJE4W{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7PnY9Vl41KImJE4W .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7PnY9Vl41KImJE4W .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7PnY9Vl41KImJE4W .error-icon{fill:#552222;}#mermaid-svg-7PnY9Vl41KImJE4W .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7PnY9Vl41KImJE4W .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7PnY9Vl41KImJE4W .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7PnY9Vl41KImJE4W .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7PnY9Vl41KImJE4W .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7PnY9Vl41KImJE4W .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7PnY9Vl41KImJE4W .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7PnY9Vl41KImJE4W .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7PnY9Vl41KImJE4W .marker.cross{stroke:#333333;}#mermaid-svg-7PnY9Vl41KImJE4W svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7PnY9Vl41KImJE4W p{margin:0;}#mermaid-svg-7PnY9Vl41KImJE4W g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-7PnY9Vl41KImJE4W g.classGroup text .title{font-weight:bolder;}#mermaid-svg-7PnY9Vl41KImJE4W .cluster-label text{fill:#333;}#mermaid-svg-7PnY9Vl41KImJE4W .cluster-label span{color:#333;}#mermaid-svg-7PnY9Vl41KImJE4W .cluster-label span p{background-color:transparent;}#mermaid-svg-7PnY9Vl41KImJE4W .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7PnY9Vl41KImJE4W .cluster text{fill:#333;}#mermaid-svg-7PnY9Vl41KImJE4W .cluster span{color:#333;}#mermaid-svg-7PnY9Vl41KImJE4W .nodeLabel,#mermaid-svg-7PnY9Vl41KImJE4W .edgeLabel{color:#131300;}#mermaid-svg-7PnY9Vl41KImJE4W .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-7PnY9Vl41KImJE4W .label text{fill:#131300;}#mermaid-svg-7PnY9Vl41KImJE4W .labelBkg{background:#ECECFF;}#mermaid-svg-7PnY9Vl41KImJE4W .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-7PnY9Vl41KImJE4W .classTitle{font-weight:bolder;}#mermaid-svg-7PnY9Vl41KImJE4W .node rect,#mermaid-svg-7PnY9Vl41KImJE4W .node circle,#mermaid-svg-7PnY9Vl41KImJE4W .node ellipse,#mermaid-svg-7PnY9Vl41KImJE4W .node polygon,#mermaid-svg-7PnY9Vl41KImJE4W .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7PnY9Vl41KImJE4W .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-7PnY9Vl41KImJE4W g.clickable{cursor:pointer;}#mermaid-svg-7PnY9Vl41KImJE4W g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-7PnY9Vl41KImJE4W g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-7PnY9Vl41KImJE4W .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-7PnY9Vl41KImJE4W .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-7PnY9Vl41KImJE4W .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-7PnY9Vl41KImJE4W .dashed-line{stroke-dasharray:3;}#mermaid-svg-7PnY9Vl41KImJE4W .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-7PnY9Vl41KImJE4W #compositionStart,#mermaid-svg-7PnY9Vl41KImJE4W .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7PnY9Vl41KImJE4W #compositionEnd,#mermaid-svg-7PnY9Vl41KImJE4W .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7PnY9Vl41KImJE4W #dependencyStart,#mermaid-svg-7PnY9Vl41KImJE4W .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7PnY9Vl41KImJE4W #dependencyStart,#mermaid-svg-7PnY9Vl41KImJE4W .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7PnY9Vl41KImJE4W #extensionStart,#mermaid-svg-7PnY9Vl41KImJE4W .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7PnY9Vl41KImJE4W #extensionEnd,#mermaid-svg-7PnY9Vl41KImJE4W .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7PnY9Vl41KImJE4W #aggregationStart,#mermaid-svg-7PnY9Vl41KImJE4W .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7PnY9Vl41KImJE4W #aggregationEnd,#mermaid-svg-7PnY9Vl41KImJE4W .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7PnY9Vl41KImJE4W #lollipopStart,#mermaid-svg-7PnY9Vl41KImJE4W .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7PnY9Vl41KImJE4W #lollipopEnd,#mermaid-svg-7PnY9Vl41KImJE4W .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-7PnY9Vl41KImJE4W .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-7PnY9Vl41KImJE4W .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7PnY9Vl41KImJE4W .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7PnY9Vl41KImJE4W .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7PnY9Vl41KImJE4W :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 委托调用
链式拦截
RAG 检索
ChatClient
+prompt() : Prompt
+call() : ChatResponse
+stream() : Flux<ChatResponse>
+defaultAdvisors() : ChatClient
+defaultTools() : ChatClient
<<interface>>
ChatModel
+call(Prompt) : ChatResponse
+stream(Prompt) : Flux<ChatResponse>
<<interface>>
Advisor
+getName() : String
+getOrder() : int
<<interface>>
Message
+getMessageType() : MessageType
+getText() : String
<<interface>>
VectorStore
+add(List<Document>) : void
+similaritySearch(SearchRequest) : List<Document>
OpenAiChatModel
OllamaChatModel
QianWenChatModel
QuestionAnswerAdvisor
SafeGuardAdvisor
MessageChatMemoryAdvisor
SystemMessage
UserMessage
AssistantMessage
ToolResponseMessage

3.2 核心概念映射(Spring AI vs LangChain)

Spring AI LangChain (Python) 说明
ChatClient ChatOpenAI / Runnable 统一调用入口
ChatModel BaseChatModel 模型抽象层
Advisor Middleware / Callback 请求/响应拦截
VectorStore VectorStore 向量存储抽象
@Tool @tool decorator 工具声明
QuestionAnswerAdvisor RetrievalQA RAG 一行开启
MessageChatMemoryAdvisor ConversationBufferMemory 对话记忆

3.3 ChatClient Fluent API 核心用法

java 复制代码
// 最简用法:一行调用 LLM
String answer = chatClient.prompt("什么是微服务?").call().content();

// 完整用法:系统提示 + 用户输入 + Advisor + Tool
String answer = chatClient.prompt()
    .system("你是电商客服助手,只回答订单相关问题")
    .user("我的订单 {orderId} 什么时候到?")
    .param("orderId", "2024001")
    .advisors(new MessageChatMemoryAdvisor(chatMemory))  // 对话记忆
    .advisors(new QuestionAnswerAdvisor(vectorStore))      // RAG
    .tools(orderQueryTool, refundTool)                     // 工具
    .call()
    .content();

// 流式输出
Flux<String> stream = chatClient.prompt()
    .user("解释分布式事务的三种方案")
    .stream()
    .content();

3.4 Message 类型体系

类型 用途 示例
SystemMessage 设定 AI 角色和边界 "你是金融客服,不回答投资建议"
UserMessage 用户输入 "查询订单 2024001"
AssistantMessage AI 回复(含 tool_calls) "正在为您查询..."
ToolResponseMessage 工具执行结果 {"status": "已发货", "eta": "明天"}

4. Spring AI 模型接入

4.1 多 Provider 配置

yaml 复制代码
# application-openai.yml
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      chat:
        options:
          model: gpt-4o
          temperature: 0.7
          max-tokens: 4096

# application-ollama.yml
spring:
  ai:
    ollama:
      base-url: http://localhost:11434
      chat:
        options:
          model: qwen2.5:72b
          temperature: 0.3

# application-deepseek.yml
spring:
  ai:
    openai:
      base-url: https://api.deepseek.com
      api-key: ${DEEPSEEK_API_KEY}
      chat:
        options:
          model: deepseek-chat

4.2 模型路由策略

java 复制代码
@Component
public class ModelRouter {
    private final ChatModel primaryModel;   // GPT-4o(复杂任务)
    private final ChatModel cheapModel;     // DeepSeek(简单任务)

    public ChatModel route(String userMessage) {
        int complexity = estimateComplexity(userMessage);
        if (complexity <= 3) {
            return cheapModel;   // 简单问题:$0.14/M tokens
        }
        return primaryModel;     // 复杂问题:$2.50/M tokens
    }

    private int estimateComplexity(String message) {
        if (message.length() < 50 && !message.contains("为什么")) return 1;
        if (message.contains("对比") || message.contains("设计")) return 5;
        return 3;
    }
}

4.3 流式输出 SSE

java 复制代码
@GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> streamChat(@RequestParam String message) {
    return chatClient.prompt()
        .user(message)
        .stream()
        .content()
        .map(chunk -> ServerSentEvent.builder(chunk).build())
        .concatWith(Flux.just(ServerSentEvent.builder("[DONE]").build()));
}

5. Spring AI RAG 实战

5.1 RAG 请求流程

ChatModel EmbeddingModel VectorStore QuestionAnswerAdvisor ChatClient 用户 ChatModel EmbeddingModel VectorStore QuestionAnswerAdvisor ChatClient 用户 #mermaid-svg-R9k7Isf7SqCOhkX2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-R9k7Isf7SqCOhkX2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-R9k7Isf7SqCOhkX2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-R9k7Isf7SqCOhkX2 .error-icon{fill:#552222;}#mermaid-svg-R9k7Isf7SqCOhkX2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-R9k7Isf7SqCOhkX2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-R9k7Isf7SqCOhkX2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-R9k7Isf7SqCOhkX2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-R9k7Isf7SqCOhkX2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-R9k7Isf7SqCOhkX2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-R9k7Isf7SqCOhkX2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-R9k7Isf7SqCOhkX2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-R9k7Isf7SqCOhkX2 .marker.cross{stroke:#333333;}#mermaid-svg-R9k7Isf7SqCOhkX2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-R9k7Isf7SqCOhkX2 p{margin:0;}#mermaid-svg-R9k7Isf7SqCOhkX2 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-R9k7Isf7SqCOhkX2 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-R9k7Isf7SqCOhkX2 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-R9k7Isf7SqCOhkX2 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-R9k7Isf7SqCOhkX2 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-R9k7Isf7SqCOhkX2 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-R9k7Isf7SqCOhkX2 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-R9k7Isf7SqCOhkX2 .sequenceNumber{fill:white;}#mermaid-svg-R9k7Isf7SqCOhkX2 #sequencenumber{fill:#333;}#mermaid-svg-R9k7Isf7SqCOhkX2 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-R9k7Isf7SqCOhkX2 .messageText{fill:#333;stroke:none;}#mermaid-svg-R9k7Isf7SqCOhkX2 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-R9k7Isf7SqCOhkX2 .labelText,#mermaid-svg-R9k7Isf7SqCOhkX2 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-R9k7Isf7SqCOhkX2 .loopText,#mermaid-svg-R9k7Isf7SqCOhkX2 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-R9k7Isf7SqCOhkX2 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-R9k7Isf7SqCOhkX2 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-R9k7Isf7SqCOhkX2 .noteText,#mermaid-svg-R9k7Isf7SqCOhkX2 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-R9k7Isf7SqCOhkX2 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-R9k7Isf7SqCOhkX2 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-R9k7Isf7SqCOhkX2 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-R9k7Isf7SqCOhkX2 .actorPopupMenu{position:absolute;}#mermaid-svg-R9k7Isf7SqCOhkX2 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-R9k7Isf7SqCOhkX2 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-R9k7Isf7SqCOhkX2 .actor-man circle,#mermaid-svg-R9k7Isf7SqCOhkX2 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-R9k7Isf7SqCOhkX2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} "退货政策是什么?" Advisor 拦截请求 文本 → 向量 0.12, -0.34, ... similaritySearch(向量, topK=5) 5 篇相关文档 拼接 context 到 SystemMessage prompt(系统提示 + 上下文 + 用户问题) "根据我们的退货政策..." 流式返回

5.2 VectorStore 抽象层

实现 适用场景 特点
PgVectorStore 已有 PostgreSQL 的团队 零额外运维,pgvector 扩展
MilvusVectorStore 大规模向量检索(>1000 万) 分布式,支持 GPU 加速
RedisVectorStore 低延迟 + 已有 Redis 内存检索,P99 < 5ms
ChromaVectorStore 开发/测试环境 轻量级,嵌入式
WeaviateVectorStore 多模态检索 支持图片 + 文本混合

5.3 ETL 管线:文档 → 向量

java 复制代码
@Configuration
public class RagPipelineConfig {

    @Bean
    public VectorStore vectorStore(EmbeddingModel embeddingModel, JdbcTemplate jdbc) {
        return new PgVectorStore(jdbc, embeddingModel, PgVectorStore.COSINE_DISTANCE);
    }

    public void ingestDocuments(VectorStore vectorStore) {
        // 1. 读取文档
        List<Document> docs = new TikaDocumentReader("classpath:/docs/faq.pdf").get();

        // 2. 分块(按 token 切割,重叠 100 token)
        TextSplitter splitter = new TokenTextSplitter(800, 100, 5, 1000, true);
        List<Document> chunks = splitter.apply(docs);

        // 3. 元数据增强
        chunks.forEach(doc -> {
            doc.getMetadata().put("source", "faq");
            doc.getMetadata().put("category", "退货政策");
        });

        // 4. 写入向量库(自动 embedding)
        vectorStore.add(chunks);
    }
}

5.4 一行开启 RAG

java 复制代码
@Bean
public ChatClient chatClient(ChatClient.Builder builder, VectorStore vectorStore) {
    return builder
        .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore,
            SearchRequest.builder()
                .topK(5)
                .similarityThreshold(0.7)
                .filterExpression("category == '退货政策'")
                .build()))
        .build();
}

5.5 检索调优参数

参数 默认值 调优建议 影响
topK 4 知识库 <100 篇用 3,>1000 篇用 5-10 召回量 vs 噪声
similarityThreshold 0.0 生产建议 0.65-0.75 过滤低相关度结果
filterExpression 按分类/部门/时间过滤 精准命中
Chunk Size 800 tokens FAQ 用 200-400,文档用 600-1000 粒度 vs 上下文
Chunk Overlap 100 tokens 设为 chunk size 的 10-15% 连贯性

6. Spring AI Tool Calling

6.1 @Tool 注解声明

java 复制代码
@Component
public class OrderTools {

    @Tool(description = "根据订单号查询订单状态、物流信息和预计到达时间")
    public OrderInfo queryOrder(
            @ToolParam(description = "订单号,格式如 2024001") String orderId) {
        return orderService.getOrderById(orderId);
    }

    @Tool(description = "提交退款申请,需要订单号和退款原因")
    public RefundResult submitRefund(
            @ToolParam(description = "订单号") String orderId,
            @ToolParam(description = "退款原因") String reason) {

        // 幂等校验:防止 LLM 重试导致重复退款
        String idempotentKey = "refund:" + orderId + ":" + LocalDate.now();
        if (redis.hasKey(idempotentKey)) {
            return RefundResult.duplicate("该订单今日已提交退款");
        }

        RefundResult result = refundService.apply(orderId, reason);
        redis.set(idempotentKey, result.getRefundId(), Duration.ofHours(24));
        return result;
    }
}

6.2 Tool Calling + Agent 完整流程

java 复制代码
@Service
public class CustomerServiceAgent {

    private final ChatClient chatClient;

    public CustomerServiceAgent(ChatClient.Builder builder,
                                 OrderTools orderTools,
                                 VectorStore vectorStore,
                                 ChatMemory chatMemory) {
        this.chatClient = builder
            .defaultSystem("""
                你是电商客服助手。规则:
                1. 只回答订单、退货、物流相关问题
                2. 退款金额 > 500 元时,必须提示需要人工审批
                3. 不要编造订单信息,必须通过工具查询
                4. 用中文回复,语气友好专业
                """)
            .defaultAdvisors(
                new MessageChatMemoryAdvisor(chatMemory),
                new QuestionAnswerAdvisor(vectorStore))
            .defaultTools(orderTools)
            .build();
    }

    public Flux<String> chat(String sessionId, String userMessage) {
        return chatClient.prompt()
            .user(userMessage)
            .advisors(a -> a.param(CHAT_MEMORY_CONVERSATION_ID_KEY, sessionId))
            .stream()
            .content();
    }
}

6.3 MCP 协议集成

java 复制代码
// Spring AI 内置 MCP Client 支持
@Bean
public McpSyncClient mcpClient() {
    return McpClient.sync(new StdioServerTransport("npx", "-y", "@modelcontextprotocol/server-filesystem"))
        .build();
}

// 将 MCP 工具注册到 ChatClient
@Bean
public ChatClient chatClient(ChatClient.Builder builder, McpSyncClient mcpClient) {
    List<McpToolCallback> mcpTools = McpToolCallbackProvider.from(mcpClient);
    return builder.defaultTools(mcpTools.toArray(new McpToolCallback[0])).build();
}

6.4 幂等设计要点

风险 原因 防御
LLM 重试触发重复调用 temperature > 0 导致不同回复,Agent 循环重试 幂等 key = tool_name:核心参数:日期
并发请求同一订单 用户多次点击 / 多端同时操作 Redis 分布式锁 + 幂等表
降级重试 模型切换后重新执行工具 执行日志去重

7. Spring AI Advisor 与护栏

7.1 内置 Advisor 全览

Advisor 作用 使用场景
QuestionAnswerAdvisor 自动 RAG 检索 + 上下文注入 知识库问答
SafeGuardAdvisor 敏感词过滤(输入 + 输出) 合规场景
MessageChatMemoryAdvisor 最近 N 轮对话记忆 多轮对话
VectorStoreChatMemoryAdvisor 向量库存储长期记忆 跨会话记忆
SimpleLoggerAdvisor 记录请求/响应日志 调试/审计

7.2 Advisor 链执行顺序

#mermaid-svg-VDjA3nYKl4iE7iCQ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VDjA3nYKl4iE7iCQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VDjA3nYKl4iE7iCQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VDjA3nYKl4iE7iCQ .error-icon{fill:#552222;}#mermaid-svg-VDjA3nYKl4iE7iCQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VDjA3nYKl4iE7iCQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VDjA3nYKl4iE7iCQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VDjA3nYKl4iE7iCQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VDjA3nYKl4iE7iCQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VDjA3nYKl4iE7iCQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VDjA3nYKl4iE7iCQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VDjA3nYKl4iE7iCQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VDjA3nYKl4iE7iCQ .marker.cross{stroke:#333333;}#mermaid-svg-VDjA3nYKl4iE7iCQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VDjA3nYKl4iE7iCQ p{margin:0;}#mermaid-svg-VDjA3nYKl4iE7iCQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VDjA3nYKl4iE7iCQ .cluster-label text{fill:#333;}#mermaid-svg-VDjA3nYKl4iE7iCQ .cluster-label span{color:#333;}#mermaid-svg-VDjA3nYKl4iE7iCQ .cluster-label span p{background-color:transparent;}#mermaid-svg-VDjA3nYKl4iE7iCQ .label text,#mermaid-svg-VDjA3nYKl4iE7iCQ span{fill:#333;color:#333;}#mermaid-svg-VDjA3nYKl4iE7iCQ .node rect,#mermaid-svg-VDjA3nYKl4iE7iCQ .node circle,#mermaid-svg-VDjA3nYKl4iE7iCQ .node ellipse,#mermaid-svg-VDjA3nYKl4iE7iCQ .node polygon,#mermaid-svg-VDjA3nYKl4iE7iCQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VDjA3nYKl4iE7iCQ .rough-node .label text,#mermaid-svg-VDjA3nYKl4iE7iCQ .node .label text,#mermaid-svg-VDjA3nYKl4iE7iCQ .image-shape .label,#mermaid-svg-VDjA3nYKl4iE7iCQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-VDjA3nYKl4iE7iCQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VDjA3nYKl4iE7iCQ .rough-node .label,#mermaid-svg-VDjA3nYKl4iE7iCQ .node .label,#mermaid-svg-VDjA3nYKl4iE7iCQ .image-shape .label,#mermaid-svg-VDjA3nYKl4iE7iCQ .icon-shape .label{text-align:center;}#mermaid-svg-VDjA3nYKl4iE7iCQ .node.clickable{cursor:pointer;}#mermaid-svg-VDjA3nYKl4iE7iCQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VDjA3nYKl4iE7iCQ .arrowheadPath{fill:#333333;}#mermaid-svg-VDjA3nYKl4iE7iCQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VDjA3nYKl4iE7iCQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VDjA3nYKl4iE7iCQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VDjA3nYKl4iE7iCQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VDjA3nYKl4iE7iCQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VDjA3nYKl4iE7iCQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VDjA3nYKl4iE7iCQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VDjA3nYKl4iE7iCQ .cluster text{fill:#333;}#mermaid-svg-VDjA3nYKl4iE7iCQ .cluster span{color:#333;}#mermaid-svg-VDjA3nYKl4iE7iCQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VDjA3nYKl4iE7iCQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VDjA3nYKl4iE7iCQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-VDjA3nYKl4iE7iCQ .icon-shape,#mermaid-svg-VDjA3nYKl4iE7iCQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VDjA3nYKl4iE7iCQ .icon-shape p,#mermaid-svg-VDjA3nYKl4iE7iCQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VDjA3nYKl4iE7iCQ .icon-shape .label rect,#mermaid-svg-VDjA3nYKl4iE7iCQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VDjA3nYKl4iE7iCQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VDjA3nYKl4iE7iCQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VDjA3nYKl4iE7iCQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户请求
SafeGuardAdvisor

敏感词过滤

order=1
MessageChatMemoryAdvisor

加载历史对话

order=2
QuestionAnswerAdvisor

RAG 检索上下文

order=3
AuditAdvisor

审计日志

order=4
ChatModel 调用
AuditAdvisor

记录响应
SafeGuardAdvisor

输出过滤
返回用户

7.3 自定义 Advisor:金融合规审计

java 复制代码
@Component
public class FinancialComplianceAdvisor implements CallAdvisor {

    @Override
    public String getName() { return "financial-compliance"; }

    @Override
    public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; }

    @Override
    public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) {
        String userInput = request.prompt().getUserMessage().getText();

        // 1. PII 检测:身份证、银行卡、手机号
        if (PiiDetector.containsPii(userInput)) {
            String masked = PiiDetector.mask(userInput);
            auditLog.warn("PII detected and masked: {}", masked);
            request = request.mutate()
                .prompt(request.prompt().augmentUserMessage(masked))
                .build();
        }

        // 2. 投资建议检测
        if (userInput.contains("推荐股票") || userInput.contains("该买什么基金")) {
            throw new ComplianceRejectException("不允许提供投资建议");
        }

        ChatClientResponse response = chain.nextCall(request);
        String output = response.chatResponse().getResult().getOutput().getText();

        // 3. 输出合规检查
        if (ComplianceChecker.containsProhibited(output)) {
            auditLog.error("Prohibited content in AI response: {}", output);
            return ChatClientResponse.builder()
                .chatResponse(ChatResponse.builder()
                    .generations(List.of(new Generation("抱歉,该问题需要人工客服为您处理。")))
                    .build())
                .build();
        }

        // 4. 审计日志(示意;token 取自 response.chatResponse().getMetadata())
        auditLog.info("AI response audit: tokens={}",
            response.chatResponse().getMetadata().getUsage().getTotalTokens());

        return response;
    }
}

7.4 Advisor 链编排的工程影响

顺序错误 后果 正确做法
RAG 在 Memory 之前 检索不考虑对话上下文,召回不准 Memory → RAG
审计在 SafeGuard 之前 审计日志包含未过滤的敏感词 SafeGuard → 审计
Memory 窗口太大 Token 浪费(每次发送全部历史) maxMessages=10

8. LangChain4j 深度对比

8.1 AiService 代理模式

java 复制代码
// LangChain4j:接口 + 注解,编译期生成代理
interface CustomerAssistant {

    @SystemMessage("你是电商客服助手")
    @UserMessage("{{message}}")
    String chat(@MemoryId String sessionId, @V("message") String message);
}

// 构建
CustomerAssistant assistant = AiServices.builder(CustomerAssistant.class)
    .chatLanguageModel(model)
    .chatMemory(MessageWindowChatMemory.withMaxMessages(10))
    .tools(new OrderTools())
    .contentRetriever(contentRetriever)
    .build();

// 使用
String answer = assistant.chat("session-1", "查询订单 2024001");

8.2 Spring AI vs LangChain4j 终极对比

维度 Spring AI LangChain4j
编程模型 Fluent API(chatClient.prompt().call() 接口代理(AiServices.builder()
配置方式 application.yml 自动配置 代码式构建 + @RegisterAiService
Advisor/拦截 Advisor 链(类似 Spring Interceptor) AiServiceListener
RAG QuestionAnswerAdvisor + VectorStore ContentRetriever + EmbeddingStore
Tool Calling @Tool + ToolCallback @Tool + ToolProvider
Memory ChatMemory + Advisor 注入 ChatMemory + @MemoryId
MCP 支持 内置 McpSyncClient / McpAsyncClient 社区插件
流式输出 Flux<ChatResponse>(Reactor 原生) TokenStream(回调模式)
可观测性 Micrometer 自动指标 手动集成
Spring Security 原生集成 需手动整合
多租户 通过 Profile / Bean Scope 实现 需自行管理
API 稳定性 1.0 GA,语义化版本 0.x,API 频繁变动
国内模型 Spring AI Alibaba 扩展 社区贡献,覆盖不全
学习曲线 Spring 开发者 1 天上手 需学习新概念
适合团队 Spring Boot / Cloud 技术栈 多框架 / 求新特性

8.3 何时选 LangChain4j

  • 团队使用 Quarkus / Micronaut,非 Spring 技术栈
  • 需要最新 AI 特性(社区迭代更快)
  • 偏好接口代理模式(类似 MyBatis Mapper)
  • 项目是独立微服务,不需要 Spring 全家桶

9. Spring AI Alibaba 扩展

9.1 通义系列模型适配

yaml 复制代码
spring:
  ai:
    dashscope:
      api-key: ${DASHSCOPE_API_KEY}
      chat:
        options:
          model: qwen-max  # qwen-turbo / qwen-plus / qwen-max / qwen-long
      embedding:
        options:
          model: text-embedding-v3

9.2 DashScope 集成特性

特性 说明
模型切换 通义千问全系列(Turbo/Plus/Max/Long/VL)
Embedding text-embedding-v3(中文优化,维度 1024)
图像理解 qwen-vl-max 多模态支持
长文本 qwen-long 支持 1M token 上下文
价格优势 qwen-turbo ¥0.3/M tokens(约 GPT-4o 的 1/50)

9.3 国内大模型接入最佳实践

  1. API Key 管理:使用 Spring Cloud Config / Vault 集中管理,不硬编码
  2. 网络隔离:国内模型走内网 VPC Endpoint,不经公网
  3. 多 Provider 热切换 :配置中心推送模型变更,无需重启(@RefreshScope
  4. 合规审计:所有 Prompt/Response 留存 90 天(金融场景要求)
  5. 计费监控 :按 BU 分账,Micrometer 指标按 model 标签拆分

10. 生产化工程实践

10.1 可观测性

java 复制代码
// Micrometer 自动采集指标(Spring AI 内置)
// spring.ai.chat.client.observation.enabled=true

// 自定义指标
@Bean
public MeterBinder aiMetrics() {
    return registry -> {
        Counter.builder("ai.tokens.total")
            .tag("model", "gpt-4o")
            .tag("tenant", "biz-a")
            .register(registry);

        Gauge.builder("ai.hallucination.rate", hallucinationDetector, HallucinationDetector::getRate)
            .register(registry);

        Counter.builder("ai.tool.calls")
            .tag("tool", "orderQuery")
            .tag("status", "success")
            .register(registry);
    };
}

关键监控指标(Grafana 面板)

指标 说明 告警阈值
ai.tokens.total Token 总消耗 日 > 100 万告警
ai.latency.p99 AI 调用 P99 延迟 > 5s 告警
ai.error.rate 模型调用错误率 > 5% 熔断
ai.hallucination.rate 幻觉率 > 3% 告警
ai.tool.success.rate 工具调用成功率 < 95% 告警
ai.cost.daily 日成本(美元) > 预算 80% 告警
ai.rag.recall RAG 召回率(采样) < 70% 告警

10.2 生产部署架构

#mermaid-svg-1FBiBfzogIezmGNJ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1FBiBfzogIezmGNJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1FBiBfzogIezmGNJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1FBiBfzogIezmGNJ .error-icon{fill:#552222;}#mermaid-svg-1FBiBfzogIezmGNJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1FBiBfzogIezmGNJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1FBiBfzogIezmGNJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1FBiBfzogIezmGNJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1FBiBfzogIezmGNJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1FBiBfzogIezmGNJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1FBiBfzogIezmGNJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1FBiBfzogIezmGNJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1FBiBfzogIezmGNJ .marker.cross{stroke:#333333;}#mermaid-svg-1FBiBfzogIezmGNJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1FBiBfzogIezmGNJ p{margin:0;}#mermaid-svg-1FBiBfzogIezmGNJ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1FBiBfzogIezmGNJ .cluster-label text{fill:#333;}#mermaid-svg-1FBiBfzogIezmGNJ .cluster-label span{color:#333;}#mermaid-svg-1FBiBfzogIezmGNJ .cluster-label span p{background-color:transparent;}#mermaid-svg-1FBiBfzogIezmGNJ .label text,#mermaid-svg-1FBiBfzogIezmGNJ span{fill:#333;color:#333;}#mermaid-svg-1FBiBfzogIezmGNJ .node rect,#mermaid-svg-1FBiBfzogIezmGNJ .node circle,#mermaid-svg-1FBiBfzogIezmGNJ .node ellipse,#mermaid-svg-1FBiBfzogIezmGNJ .node polygon,#mermaid-svg-1FBiBfzogIezmGNJ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1FBiBfzogIezmGNJ .rough-node .label text,#mermaid-svg-1FBiBfzogIezmGNJ .node .label text,#mermaid-svg-1FBiBfzogIezmGNJ .image-shape .label,#mermaid-svg-1FBiBfzogIezmGNJ .icon-shape .label{text-anchor:middle;}#mermaid-svg-1FBiBfzogIezmGNJ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1FBiBfzogIezmGNJ .rough-node .label,#mermaid-svg-1FBiBfzogIezmGNJ .node .label,#mermaid-svg-1FBiBfzogIezmGNJ .image-shape .label,#mermaid-svg-1FBiBfzogIezmGNJ .icon-shape .label{text-align:center;}#mermaid-svg-1FBiBfzogIezmGNJ .node.clickable{cursor:pointer;}#mermaid-svg-1FBiBfzogIezmGNJ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1FBiBfzogIezmGNJ .arrowheadPath{fill:#333333;}#mermaid-svg-1FBiBfzogIezmGNJ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1FBiBfzogIezmGNJ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1FBiBfzogIezmGNJ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1FBiBfzogIezmGNJ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1FBiBfzogIezmGNJ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1FBiBfzogIezmGNJ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1FBiBfzogIezmGNJ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1FBiBfzogIezmGNJ .cluster text{fill:#333;}#mermaid-svg-1FBiBfzogIezmGNJ .cluster span{color:#333;}#mermaid-svg-1FBiBfzogIezmGNJ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1FBiBfzogIezmGNJ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1FBiBfzogIezmGNJ rect.text{fill:none;stroke-width:0;}#mermaid-svg-1FBiBfzogIezmGNJ .icon-shape,#mermaid-svg-1FBiBfzogIezmGNJ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1FBiBfzogIezmGNJ .icon-shape p,#mermaid-svg-1FBiBfzogIezmGNJ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1FBiBfzogIezmGNJ .icon-shape .label rect,#mermaid-svg-1FBiBfzogIezmGNJ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1FBiBfzogIezmGNJ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1FBiBfzogIezmGNJ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1FBiBfzogIezmGNJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Observability
AI 基础设施
Spring Boot 集群
Client
Web 前端
API Gateway
App 客户端
负载均衡
Service 实例 1
Service 实例 2
Service 实例 3
模型路由器
GPT-4o

复杂任务
DeepSeek

简单任务
Ollama 本地

敏感数据
PgVector

向量库
Redis

对话记忆 + 缓存
Prometheus
LangFuse

AI Trace
Grafana

10.3 Resilience4j 熔断 LLM 调用

java 复制代码
@Configuration
public class AiResilienceConfig {

    @Bean
    public CircuitBreaker aiCircuitBreaker() {
        return CircuitBreaker.of("ai-llm", CircuitBreakerConfig.custom()
            .failureRateThreshold(50)
            .waitDurationInOpenState(Duration.ofSeconds(30))
            .slidingWindowSize(10)
            .build());
    }

    @Bean
    public TimeLimiter aiTimeLimiter() {
        return TimeLimiter.of(TimeLimiterConfig.custom()
            .timeoutDuration(Duration.ofSeconds(10))
            .build());
    }
}

// 使用:AI 不可用时自动降级到规则引擎
@Service
public class ResilientAiService {
    public String chat(String message) {
        try {
            return circuitBreaker.executeSupplier(() ->
                chatClient.prompt().user(message).call().content());
        } catch (Exception e) {
            return ruleEngine.fallback(message);
        }
    }
}

10.4 多租户隔离

隔离维度 实现方式
API Key 每租户独立 key,配置中心管理,按租户计费
VectorStore 按租户分表 / 分 namespace(PgVector 用 schema 隔离)
ChatMemory Redis key 前缀 tenant:{tenantId}:session:{sessionId}
Advisor 租户级 SafeGuardAdvisor(不同行业不同敏感词库)
成本预算 每租户日限额,超额自动降级/拒绝

11. 实战项目:智能客服系统

11.1 项目架构

#mermaid-svg-CmnEeb9CUWUf2Ti6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CmnEeb9CUWUf2Ti6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .error-icon{fill:#552222;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .marker.cross{stroke:#333333;}#mermaid-svg-CmnEeb9CUWUf2Ti6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CmnEeb9CUWUf2Ti6 p{margin:0;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .cluster-label text{fill:#333;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .cluster-label span{color:#333;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .cluster-label span p{background-color:transparent;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .label text,#mermaid-svg-CmnEeb9CUWUf2Ti6 span{fill:#333;color:#333;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .node rect,#mermaid-svg-CmnEeb9CUWUf2Ti6 .node circle,#mermaid-svg-CmnEeb9CUWUf2Ti6 .node ellipse,#mermaid-svg-CmnEeb9CUWUf2Ti6 .node polygon,#mermaid-svg-CmnEeb9CUWUf2Ti6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .rough-node .label text,#mermaid-svg-CmnEeb9CUWUf2Ti6 .node .label text,#mermaid-svg-CmnEeb9CUWUf2Ti6 .image-shape .label,#mermaid-svg-CmnEeb9CUWUf2Ti6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .rough-node .label,#mermaid-svg-CmnEeb9CUWUf2Ti6 .node .label,#mermaid-svg-CmnEeb9CUWUf2Ti6 .image-shape .label,#mermaid-svg-CmnEeb9CUWUf2Ti6 .icon-shape .label{text-align:center;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .node.clickable{cursor:pointer;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .arrowheadPath{fill:#333333;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CmnEeb9CUWUf2Ti6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CmnEeb9CUWUf2Ti6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CmnEeb9CUWUf2Ti6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .cluster text{fill:#333;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .cluster span{color:#333;}#mermaid-svg-CmnEeb9CUWUf2Ti6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CmnEeb9CUWUf2Ti6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .icon-shape,#mermaid-svg-CmnEeb9CUWUf2Ti6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .icon-shape p,#mermaid-svg-CmnEeb9CUWUf2Ti6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .icon-shape .label rect,#mermaid-svg-CmnEeb9CUWUf2Ti6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CmnEeb9CUWUf2Ti6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CmnEeb9CUWUf2Ti6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CmnEeb9CUWUf2Ti6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 模型层
数据层
Spring Boot 3.3 + Spring AI 1.0
前端
SSE
客服 Widget

React/Vue
ChatController

SSE 流式
CustomerServiceAgent

核心 Agent
OrderTools + RefundTools

业务工具
Advisor 链

记忆+RAG+合规
PostgreSQL

业务数据
PgVector

知识库向量
Redis

会话记忆
GPT-4o / DeepSeek

模型路由

11.2 pom.xml 核心依赖

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-mcp-client</artifactId>
    </dependency>
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-registry-prometheus</artifactId>
    </dependency>
    <dependency>
        <groupId>io.github.resilience4j</groupId>
        <artifactId>resilience4j-spring-boot3</artifactId>
    </dependency>
</dependencies>

11.3 面试项目 STAR 模板

STAR 内容
S 电商平台日均 5000+ 客服工单,人工客服成本高,响应慢(平均 15 min)
T 我负责设计并实现基于 Spring AI 的智能客服系统
A Spring Boot + Spring AI + PgVector + Resilience4j,RAG 覆盖 FAQ 知识库,Tool Calling 对接订单/退款/物流 API,Advisor 链实现合规护栏
R AI 自动解决率 83%,平均响应时间从 15min 降到 8s,客服人力成本降低 60%,月 Token 成本 $2,800

12. 大厂面试题 5 道

Q1: 阿里 --- Spring AI vs LangChain 后端团队选型

题目:"你们团队做 Spring AI vs LangChain 选型时考虑了哪些因素?如果 Python 团队已有 LangChain 积累,Java 团队该怎么选?"

(1) 标准答案

选型核心看三个维度:团队能力迁移成本与现有系统的集成度生产化工程要求 。如果团队主力是 Java + Spring Boot,且需要与现有微服务深度集成(认证/监控/配置中心/注册中心),Spring AI 是最优选择------学习成本 1-2 天 vs 全团队转 Python 的 3-6 个月。

如果 Python 团队已有 LangChain 积累,不应该推倒重来 ,而是采用"双语共存"模式:AI 原型和模型实验用 Python + LangChain(快速迭代),生产级服务用 Java + Spring AI(稳定可靠)。通过 HTTP API / gRPC / MCP 协议 连接两端。

(2) 原理 walk

选型决策矩阵(带权重打分):

维度 权重 Spring AI LangChain (Python) 说明
团队上手成本 30% 9 3 Java 团队转 Python 3-6 月
微服务集成 25% 10 5 Spring Cloud 天然集成
AI 特性丰富度 15% 7 10 LangChain 社区更活跃
生产稳定性 20% 9 6 Spring AI 1.0 GA API 稳定
社区生态 10% 7 9 LangChain 先发优势
加权总分 8.6 5.7 Spring AI 胜出
(3) 权衡与量化数字
  • 团队 10 人全转 Python:培训成本 ~60 人月 × ¥5 万 = ¥300 万
  • Spring AI 培训:2 天 × 10 人 = 20 人天 ≈ ¥10 万
  • 差距 30 倍,且转 Python 期间生产力下降 50%
  • Spring AI 与现有 Spring Cloud 集成:0 额外代码
(4) 落地清单
  • 选型评审会:技术委员会 3 人投票,输出《AI 框架选型报告》
  • PoC 验证(1 周):Spring AI vs LangChain4j 同一场景对比
  • 标准模板项目:ai-starter-template,内置 Spring AI + PgVector + Resilience4j + Micrometer
  • 双语共存规范:Python AI 实验服务 → gRPC → Java 生产服务
(5) 追问

追问 1:"LangChain4j 和 Spring AI 有什么区别?"

答:LangChain4j 采用接口代理模式 (类似 MyBatis Mapper),Spring AI 采用 Fluent API(类似 WebClient)。选 LangChain4j 的场景:① Quarkus/Micronaut 项目 ② 需要最新社区特性 ③ 偏好声明式编程。但 LangChain4j 仍在 0.x 版本,API 不稳定,不建议核心链路使用。

追问 2:"如果未来两者合并了呢?"

答:两者定位不同,短期不会合并。关键是架构分层隔离框架依赖------AI Service Layer 抽象封装,切换框架只改实现,不改接口。


Q2: 字节 --- Java Agent 万级 QPS

题目:"Java Agent 如何支撑万级 QPS 的 AI 服务?Spring AI 的瓶颈在哪里?"

(1) 标准答案

万级 QPS 的 AI 服务,瓶颈不在 Spring AI 框架本身(~2ms/req 开销),而在 LLM API 调用的延迟和并发限制 。核心策略是分层削减实际到达 LLM 的 QPS

(2) 原理 walk
复制代码
10,000 QPS 请求入口
  ├── 语义缓存命中(~50%)→ 直接返回:5,000 QPS,P99 < 10ms
  ├── 意图分类 → 简单任务(~30%)→ 规则引擎:3,000 QPS,P99 < 50ms
  └── 复杂任务(~20%)→ LLM Agent:2,000 QPS,P99 < 5s
       ├── DeepSeek(70%):1,400 QPS → 3 个推理节点
       └── GPT-4o(30%):600 QPS → API 连接池 100 threads
(3) 权衡与量化数字
  • Spring AI 框架开销:~2ms/request(可忽略)
  • 语义缓存 Redis:P99 < 3ms,10K QPS 无压力
  • 虚拟线程(Loom):Spring Boot 3.2+ 开启 spring.threads.virtual.enabled=true
  • 单 vLLM 实例(A100 80G):qwen-72b 吞吐 ~500 tokens/s ≈ 200 QPS
(4) 落地清单
  • 开启虚拟线程:spring.threads.virtual.enabled=true
  • 语义缓存:Redis + Embedding 相似度 > 0.95 命中
  • 连接池配置:spring.ai.openai.connection.pool.size=100
  • 限流:Sentinel / RateLimiter 按 tenant 限流
(5) 追问

追问 1:"虚拟线程对 AI 调用有什么帮助?"

答:LLM 调用是 I/O 密集型,虚拟线程让每个请求资源从 ~1MB 栈降到 ~几 KB。理论并发从 200(平台线程)提升到 10000+。Spring Boot 3.2+ 一行配置开启。


Q3: 蚂蚁 --- 金融场景 Advisor 链合规审计

题目:"金融场景下 Advisor 链如何设计合规审计?敏感信息如何不出域?"

(1) 标准答案

金融合规三条硬约束:① 数据不出域 (PII 不发到外部 LLM)② 审计留痕 (每次 AI 交互可追溯)③ 禁止投资建议 (监管红线)。Spring AI Advisor 链是最自然的实现点------合规通过 Advisor 注入,业务代码零改动

(2) 原理 walk

Advisor 链 6 层设计:

复制代码
用户输入 → [1] PII 脱敏 Advisor(身份证/银行卡/手机号 → ***)
         → [2] 合规拦截 Advisor("推荐股票"直接拒绝)
         → [3] 审计写入 Advisor(请求入库)
         → [4] RAG Advisor(检索内部知识库)
         → [5] LLM 调用(只发送脱敏后的数据)
         → [6] 输出审计 Advisor(响应入库 + 合规检查)
         → 用户

数据不出域方案:

  • 方案 A:私有化模型(Ollama + qwen-72b),数据物理不出域
  • 方案 B:PII Advisor 脱敏后发外部 API,返回后还原
  • 推荐:核心数据用方案 A,非敏感可用方案 B
(3) 权衡与量化数字
  • PII 检测准确率 99.5%(正则 + NER 双校验)
  • 审计日志:每次交互 ~2KB,日均 5 万次 = 100MB/天,保留 90 天 = 9GB
  • 合规拦截率 ~3%
  • 私有化模型延迟:qwen-72b on A100,P50 = 800ms
(4) 落地清单
  • PII 检测库:内部 NLP + 正则兜底
  • 审计数据库:独立 PostgreSQL,与业务库物理隔离
  • 合规规则引擎:可配置化,新增敏感词无需发版
  • 季度合规审计:随机抽样 1000 条 AI 对话
(5) 追问

追问 1:"PII 脱敏后,AI 回复中如何还原用户信息?"

答:请求阶段将 PII 替换为占位符([PHONE_1])并缓存映射表(TTL=对话有效期)。响应阶段替换回原始值。映射表仅存内存/Redis,不落盘,对话结束即清除。


Q4: Google --- 多模型路由系统

题目:"设计一个多模型路由系统,简单任务走小模型,复杂走大模型,用 Java 怎么实现?"

(1) 标准答案

多模型路由核心是任务复杂度分类 + 成本优化 。设计 ModelRouter 组件,三级路由:规则(0ms)→ 本地分类器(~5ms)→ 中等模型兜底。

(2) 原理 walk
java 复制代码
@Component
public class SmartModelRouter {
    public ChatModel route(String message, ConversationContext ctx) {
        // Level 1: 规则路由(0ms)
        if (message.length() < 30 && isGreeting(message)) return cheapModel;
        if (ctx.hasToolCalls() || message.contains("分析")) return premiumModel;

        // Level 2: 本地分类器(~5ms,轻量 ONNX 模型)
        int complexity = localClassifier.predict(message);
        if (complexity <= 2) return cheapModel;       // DeepSeek: $0.14/M
        if (complexity >= 4) return premiumModel;     // GPT-4o: $2.50/M

        // Level 3: 边界区域用中等模型
        return midModel;                              // GPT-4o-mini: $0.15/M
    }
}
(3) 权衡与量化数字
路由策略 准确率 延迟 成本降幅
全部 GPT-4o 100% 0ms 0%
规则路由 75% ~0ms 40%
本地分类器 88% ~5ms 60%
本地分类器 + 规则兜底 90% ~3ms 65%

月费从 12,000 → 4,200(降 65%),用户满意度 4.2 → 4.1 / 5.0(无显著差异)。

(4) 落地清单
  • 分类器训练:历史对话标注 1000 条
  • A/B 实验:10% 流量全 GPT-4o 对照
  • 兜底:分类器异常 → 默认走中等模型
(5) 追问

追问 1:"分类错误的影响?"

答:简单→大模型:浪费钱但不影响体验;复杂→小模型:质量下降。分类器应偏向召回复杂任务(宁多花钱,不降质量)。


Q5: 美团 --- Ollama 本地部署延迟优化

题目:"Ollama 本地部署 + Spring AI 集成的延迟优化,P99 从 8s 降到 2s 的方案?"

(1) 标准答案

Ollama 延迟高的原因:① 模型太大 ② 量化不够 ③ KV Cache 未优化 ④ Spring AI 默认同步阻塞 ⑤ TTFT 占比高。

(2) 原理 walk
步骤 动作 延迟改善
1. 换小模型 72b → 14b(qwen2.5:14b) 8s → 4s
2. 量化 FP16 → Q4_K_M 4s → 2.5s
3. Flash Attention OLLAMA_FLASH_ATTENTION=1 2.5s → 2s
4. KV Cache num_ctx: 4096(不要设太大) -200ms
5. 流式输出 TTFT ~300ms,用户感知 <1s 感知 <1s
(3) 权衡与量化数字
  • qwen2.5:72b Q4 on RTX 4090: TTFT ~2s, ~15 tokens/s
  • qwen2.5:14b Q4 on RTX 4090: TTFT ~300ms, ~45 tokens/s
  • qwen2.5:7b Q4 on RTX 4090: TTFT ~150ms, ~80 tokens/s
  • 14b Eval 准确率 82% vs 72b 89%(多数场景足够)
(4) 落地清单
bash 复制代码
OLLAMA_FLASH_ATTENTION=1
OLLAMA_NUM_PARALLEL=4
OLLAMA_MAX_LOADED_MODELS=2

# Spring AI
spring.ai.ollama.chat.options.num-ctx=4096
spring.ai.ollama.chat.options.num-predict=512
(5) 追问

追问 1:"14b 和 72b 质量差异如何决策?"

答:500 条标注数据跑 Eval,对比准确率/幻觉率/满意度。14b ≥ 80% 准确率且幻觉率 < 3% 选 14b(延迟 3x 提升 + 成本 5x 降低)。


13. STAR-M-P 真实事故复盘

Spring AI Tool Calling 未幂等导致重复退款

S(Situation)· 业务背景与症状
  • 业务规模:电商客服 Agent,日均处理 5000 退款请求,单日退款金额 ~¥200 万
  • 业务影响:财务团队发现 3 天内有 47 笔重复退款,总计多退 ¥8.3 万
  • 时间窗口:上线第 2 周发现,持续 3 天
T(Trigger)· 监控触发点
  • 财务日对账系统告警:退款金额与实际退款单不匹配
  • 客服 Agent 日志显示同一订单在同一会话中被调用 submitRefund 2-3 次
  • 根因初判:LLM 在 Agent 循环中重复调用了退款工具
A(Approach)· 排查决策树

#mermaid-svg-Loc2OdUP0aqqW64F{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Loc2OdUP0aqqW64F .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Loc2OdUP0aqqW64F .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Loc2OdUP0aqqW64F .error-icon{fill:#552222;}#mermaid-svg-Loc2OdUP0aqqW64F .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Loc2OdUP0aqqW64F .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Loc2OdUP0aqqW64F .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Loc2OdUP0aqqW64F .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Loc2OdUP0aqqW64F .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Loc2OdUP0aqqW64F .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Loc2OdUP0aqqW64F .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Loc2OdUP0aqqW64F .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Loc2OdUP0aqqW64F .marker.cross{stroke:#333333;}#mermaid-svg-Loc2OdUP0aqqW64F svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Loc2OdUP0aqqW64F p{margin:0;}#mermaid-svg-Loc2OdUP0aqqW64F .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Loc2OdUP0aqqW64F .cluster-label text{fill:#333;}#mermaid-svg-Loc2OdUP0aqqW64F .cluster-label span{color:#333;}#mermaid-svg-Loc2OdUP0aqqW64F .cluster-label span p{background-color:transparent;}#mermaid-svg-Loc2OdUP0aqqW64F .label text,#mermaid-svg-Loc2OdUP0aqqW64F span{fill:#333;color:#333;}#mermaid-svg-Loc2OdUP0aqqW64F .node rect,#mermaid-svg-Loc2OdUP0aqqW64F .node circle,#mermaid-svg-Loc2OdUP0aqqW64F .node ellipse,#mermaid-svg-Loc2OdUP0aqqW64F .node polygon,#mermaid-svg-Loc2OdUP0aqqW64F .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Loc2OdUP0aqqW64F .rough-node .label text,#mermaid-svg-Loc2OdUP0aqqW64F .node .label text,#mermaid-svg-Loc2OdUP0aqqW64F .image-shape .label,#mermaid-svg-Loc2OdUP0aqqW64F .icon-shape .label{text-anchor:middle;}#mermaid-svg-Loc2OdUP0aqqW64F .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Loc2OdUP0aqqW64F .rough-node .label,#mermaid-svg-Loc2OdUP0aqqW64F .node .label,#mermaid-svg-Loc2OdUP0aqqW64F .image-shape .label,#mermaid-svg-Loc2OdUP0aqqW64F .icon-shape .label{text-align:center;}#mermaid-svg-Loc2OdUP0aqqW64F .node.clickable{cursor:pointer;}#mermaid-svg-Loc2OdUP0aqqW64F .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Loc2OdUP0aqqW64F .arrowheadPath{fill:#333333;}#mermaid-svg-Loc2OdUP0aqqW64F .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Loc2OdUP0aqqW64F .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Loc2OdUP0aqqW64F .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Loc2OdUP0aqqW64F .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Loc2OdUP0aqqW64F .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Loc2OdUP0aqqW64F .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Loc2OdUP0aqqW64F .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Loc2OdUP0aqqW64F .cluster text{fill:#333;}#mermaid-svg-Loc2OdUP0aqqW64F .cluster span{color:#333;}#mermaid-svg-Loc2OdUP0aqqW64F div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Loc2OdUP0aqqW64F .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Loc2OdUP0aqqW64F rect.text{fill:none;stroke-width:0;}#mermaid-svg-Loc2OdUP0aqqW64F .icon-shape,#mermaid-svg-Loc2OdUP0aqqW64F .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Loc2OdUP0aqqW64F .icon-shape p,#mermaid-svg-Loc2OdUP0aqqW64F .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Loc2OdUP0aqqW64F .icon-shape .label rect,#mermaid-svg-Loc2OdUP0aqqW64F .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Loc2OdUP0aqqW64F .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Loc2OdUP0aqqW64F .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Loc2OdUP0aqqW64F :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 单笔重复

退款金额异常
单笔重复还是多笔?
同一 session?
Agent 为什么重复调用?
LLM 未识别首次 tool_response 成功

重新发起调用
网络超时导致 Spring AI 重试

Tool 被二次执行
加幂等 key + HITL

排查时间线:

  1. 第 1 步(10 min) :查 Agent 日志,发现订单 2024-88832 在 15:03:21 和 15:03:24 被调用了两次 submitRefund
  2. 第 2 步(20 min) :分析 LLM 输出,第一次 tool_response 返回后,LLM 生成了"我再帮您确认一下退款状态",然后又调用了 submitRefund
  3. 第 3 步(15 min) :确认根因------submitRefund 无幂等保护,且 Agent 循环未限制同一工具重复调用
R(Resolution)· 解决方案

止血(当天)

  • 紧急下线退款 Agent,切回人工客服
  • 手动处理 47 笔重复退款

根治(2 周)

  1. 阶段 1(3 天) :所有写操作工具加幂等 key(tool_name:orderId:date),Redis 存储,24h 过期
  2. 阶段 2(1 周):金额 > ¥500 退款加 HITL 审批
  3. 阶段 3(2 周):Agent 框架加 Tool 调用去重------同一 session 同一工具 + 同一参数,第二次自动返回首次结果
M(Metric)· 监控指标
指标 改造前 改造后
重复退款率 0.31% 0%
退款准确率 99.2% 99.97%
HITL 触发率 0% 8%

长期监控(Grafana):

  • ai.tool.duplicate.calls:> 0 即告警
  • ai.refund.amount.daily:偏离预测 > 20% 告警
  • ai.hitl.timeout.rate:> 10% 告警
P(Postmortem)· 事后启示

架构红线(写入《AI 工具注册规范》):

  1. 所有写操作工具必须幂等
  2. 金额操作必须 HITL------阈值可配置,默认 ¥500
  3. Agent 循环必须有最大步数限制------默认 maxSteps=5
  4. 同一工具 + 同一参数不允许重复调用

14. 速记卡 + 90 秒口述

速记卡

维度 要点
定义 Spring AI = Spring Boot 的 AI 抽象层
核心 4 件套 ChatClient 调模型 + Advisor 做护栏 + VectorStore 做 RAG + @Tool 做函数调用
vs LangChain Spring AI = Fluent API + Spring 原生;LangChain4j = 接口代理 + 多框架
RAG QuestionAnswerAdvisor 一行开启;PgVector/Milvus/Redis 可切换
Tool Calling @Tool 注解声明;写操作必须幂等;金额操作必须 HITL
Advisor 链 SafeGuard → Memory → RAG → 审计;顺序影响行为
生产化 Micrometer 监控 + Resilience4j 熔断 + 虚拟线程 + 多租户
选型 Spring Boot 团队 → Spring AI;Quarkus 团队 → LangChain4j

90 秒口述脚本

起手(10 秒)

"Spring AI 是 Spring Boot 生态的 AI 抽象层------Java 开发者不需要转 Python,用熟悉的 Spring 编程模型就能做 LLM 应用。"

核心(25 秒)

"四个核心概念:ChatClient Fluent API 调模型;Advisor 链做护栏------类似 Interceptor,拦截请求响应实现合规、记忆、RAG;VectorStore 做 RAG------PgVector、Milvus、Redis 一行切换;@Tool 做函数调用------直接复用现有 Spring Bean。"

生产化(25 秒)

"生产三个关键:第一熔断降级------Resilience4j 包装 LLM 调用,不可用时 fallback 规则引擎。第二可观测------Micrometer 自动采集 Token 用量、延迟、幻觉率。第三多模型路由------简单走 DeepSeek,复杂走 GPT-4o,月成本降 65%。"

事故教训(15 秒)

"踩过坑:Tool Calling 幂等------退款工具被重复调用多退 8 万。方案:写操作幂等 key + 金额 HITL 审批 + Agent 限最大步数。"

收尾(15 秒)

"选型建议:Spring Boot 团队直接 Spring AI,1-2 天上手。不建议全转 Python------Java 的工程化优势在 AI 应用中同样重要。"


§12 Spring AI 1.0 GA 与 MCP 集成(Staff / Architect)

定位 :Spring AI 1.0 GA(2025) 把 Java 侧 LLM 应用拉到「可上生产」------统一 ChatClientAdvisor 链Micrometer ObservationVectorStore 抽象MCP Client/Server 。本节面向 全球运动零售 / 电商 Java 中台 (脱敏 Nike-like):存量 Spring Boot + SAP/OMS + Redis/pgvector + C 端 SSE。

Buy 域 14 场景(导购/客服/满减/Carts/Payment...)完整 POC + 代码 + 面试 B01--B2802-Buy领域智能体-Spring-AI全量工程.md

§12.1 Spring AI 1.0 GA 能力矩阵

能力 1.0 GA 要点 工程含义
ChatClient Fluent API:prompt().user().advisors().tools().call/stream 与 Spring MVC/WebFlux 同风格;可 per-request 覆写 advisor
Advisor 链 CallAdvisor / StreamAdvisor + getOrder();内置 RAG / Memory / SafeGuard 类似 Servlet Filter;顺序错误会污染审计或漏 RAG
Observation Micrometer + OTel:spring.ai.chat.client / model token 与现有 Grafana/Prometheus 栈对齐;按 tenant/model 打标签
VectorStore 统一 add / similaritySearch;PgVector / Redis / Milvus 可插拔 多库并存用 多 Bean + @Qualifier(§12.5)
MCP spring-ai-starter-mcp-client / spring-ai-starter-mcp-server(及 webmvc/webflux 变体) 组织级 Tool 资产;IDE + 生产 Agent 共用(深链 04 §12

§12.2 LiteLLM Proxy vs 直连 Azure OpenAI

默认路径 :Spring AI OpenAiChatModelbase-url 指向 LiteLLM Proxy(OpenAI-compatible),由网关做模型路由、配额、密钥托管、成本归因。

yaml 复制代码
# application-prod.yml --- 主路径:LiteLLM 网关
spring:
  ai:
    openai:
      api-key: ${LITELLM_API_KEY}          # 网关颁发的 team key,非裸 Azure key
      base-url: https://litellm.internal/v1
      chat:
        options:
          model: gpt-4o-mini               # 或 litellm 别名 commerce-fast / commerce-smart
          temperature: 0.2

# application-fallback.yml --- 网关 SLA 跌破 / 5xx 时激活
spring:
  ai:
    openai:
      api-key: ${AZURE_OPENAI_API_KEY}
      base-url: https://${AZURE_RESOURCE}.openai.azure.com/openai/deployments/${DEPLOYMENT}
      chat:
        options:
          model: gpt-4o
维度 LiteLLM Proxy(推荐主路径) 直连 Azure OpenAI(fallback)
密钥 应用只持 LiteLLM key;云厂商 key 在网关 每应用或每服务持 Azure key,轮换成本高
路由 按任务别名切 DeepSeek / GPT / 通义 单部署单模型,切换要改配置发版
观测 网关统一 token/成本日志 依赖各应用 Micrometer,口径易散
风险 网关成 SPOF 无统一限流,易打穿配额
java 复制代码
@Configuration
@Profile("prod")
public class LlmRoutingConfig {

    @Bean
    @Primary
    ChatModel primaryChatModel(OpenAiChatModel litellmModel) {
        return litellmModel; // base-url → LiteLLM
    }

    @Bean("azureFallbackChatModel")
    ChatModel azureFallbackChatModel(
            @Value("${spring.ai.openai.fallback.base-url}") String baseUrl,
            @Value("${spring.ai.openai.fallback.api-key}") String apiKey) {
        return OpenAiChatModel.builder()
            .openAiApi(OpenAiApi.builder().baseUrl(baseUrl).apiKey(apiKey).build())
            .build();
    }

    // 多 ChatModel + Resilience4j @CircuitBreaker fallback(Spring AI 无内置 Resilience4jChatModel)
    @Bean
    ChatClient commerceChatClient(ChatClient.Builder builder,
                                  @Qualifier("primaryChatModel") ChatModel primary) {
        return builder.defaultSystem("你是运动零售客服助手...").build();
    }
}

§12.3 MCP Server:把 @Tool 暴露为组织级 MCP Tools

java 复制代码
@Component
public class OmsMcpTools {

    private final OrderQueryService orders;

    @Tool(description = "按订单号查询履约状态,只读")
    public OrderStatusDto getOrderStatus(
            @ToolParam(description = "订单号,如 NIKE-2024-8821") String orderId) {
        return orders.getStatus(orderId);
    }

    @Tool(description = "预留库存,写操作需 idempotency_key")
    public ReservationDto reserveStock(
            @ToolParam String sku,
            @ToolParam int qty,
            @ToolParam String idempotencyKey) {
        return orders.reserve(sku, qty, idempotencyKey);
    }
}

@Configuration
public class McpServerConfig {

    @Bean
    ToolCallbackProvider omsMcpToolProvider(OmsMcpTools tools) {
        return MethodToolCallbackProvider.builder().toolObjects(tools).build();
    }

    // spring-ai-starter-mcp-server(或 -webmvc / -webflux)自动注册 SSE/stdio transport
}

要点 :MCP Server 内仍调用现有 @Service------不复制业务逻辑 ;写 Tool 强制 idempotency_key + 审计(对齐 §13 STAR 退款事故)。

§12.4 MCP Client:调用外部 SAP / OMS MCP Server

yaml 复制代码
spring:
  ai:
    mcp:
      client:
        enabled: true
        name: commerce-agent
        request-timeout: 30s
        sse:
          connections:
            sap-erp:
              url: https://mcp-sap.internal/sse
              headers:
                Authorization: Bearer ${MCP_SAP_TOKEN}
            oms-fulfillment:
              url: https://mcp-oms.internal/sse
              headers:
                Authorization: Bearer ${MCP_OMS_TOKEN}
java 复制代码
@Configuration
public class McpClientConfig {

    @Bean
    ChatClient orderAgentChatClient(
            ChatClient.Builder builder,
            List<McpSyncClient> mcpClients) {
        var callbacks = mcpClients.stream()
            .flatMap(c -> McpToolCallbackProvider.from(c).getToolCallbacks().stream())
            .toArray(ToolCallback[]::new);
        return builder
            .defaultSystem("""
                B2B 订单助手。写操作必须带 idempotency_key。
                库存/改单问题优先调 OMS MCP;财务过账调 SAP MCP。
                """)
            .defaultToolCallbacks(callbacks)
            .build();
    }
}

§12.5 多 VectorStore:Redis 热缓存 + pgvector 知识库

java 复制代码
@Configuration
public class MultiVectorStoreConfig {

    @Bean
    @Qualifier("redisHotStore")
    VectorStore redisVectorStore(RedisVectorStoreProperties props, EmbeddingModel embedding) {
        return RedisVectorStore.builder(embedding)
            .indexName("product-hot-embeddings")
            .prefix("vec:hot:")
            .initializeSchema(true)
            .build();
    }

    @Bean
    @Qualifier("pgCatalogStore")
    VectorStore pgVectorStore(JdbcTemplate jdbc, EmbeddingModel embedding) {
        return PgVectorStore.builder(jdbc, embedding)
            .dimensions(1024)
            .distanceType(PgVectorStore.PgDistanceType.COSINE_DISTANCE)
            .initializeSchema(true)
            .build();
    }

    @Bean
    VectorStoreRouter catalogRouter(
            @Qualifier("redisHotStore") VectorStore hot,
            @Qualifier("pgCatalogStore") VectorStore catalog) {
        return new VectorStoreRouter(Map.of(
            "flash_sale", hot,      // <100k SKU 促销热向量
            "catalog", catalog      // 全量商品 + SQL metadata filter
        ));
    }
}

// Advisor 侧按意图选 store
@Bean
QuestionAnswerAdvisor catalogRagAdvisor(@Qualifier("pgCatalogStore") VectorStore store) {
    return new QuestionAnswerAdvisor(store, SearchRequest.defaults().withTopK(8));
}
典型数据 QPS / 容量
L1 Redis Vector 促销 SKU、会话级热问 高 QPS,<100k 向量
L2 pgvector 全量目录 + 租户/类目 SQL 过滤 <10M,要 JOIN 权限

§12.6 C 端 StreamingChatClient(SSE)

java 复制代码
@RestController
@RequestMapping("/api/v1/chat")
public class ConsumerChatController {

    private final ChatClient chatClient;

    @PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> stream(
            @RequestHeader("X-Session-Id") String sessionId,
            @RequestBody ChatRequest req) {

        return chatClient.prompt()
            .user(req.message())
            .advisors(a -> a.param(CHAT_MEMORY_CONVERSATION_ID_KEY, sessionId))
            .stream()
            .chatResponse()
            .map(chunk -> {
                String delta = chunk.getResult().getOutput().getText();
                return ServerSentEvent.<String>builder()
                    .event("delta")
                    .data(delta != null ? delta : "")
                    .build();
            })
            .concatWith(Flux.just(ServerSentEvent.builder()
                .event("done")
                .data("[DONE]")
                .build()))
            .timeout(Duration.ofSeconds(60))
            .onErrorResume(e -> Flux.just(ServerSentEvent.builder()
                .event("error")
                .data("服务繁忙,请稍后重试")
                .build()));
    }
}

生产注意 :SSE 经 API Gateway 禁缓冲 ;首 token 目标 <800ms (网关 + 模型冷启动需预热);与 Resilience4j bulkhead 隔离 C 端与 B 端 Agent 线程池。

§12.7 LangChain4j 1.0 vs Spring AI:Nike-like Java 店选型树

#mermaid-svg-ISUCySrAgITZK9rW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ISUCySrAgITZK9rW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ISUCySrAgITZK9rW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ISUCySrAgITZK9rW .error-icon{fill:#552222;}#mermaid-svg-ISUCySrAgITZK9rW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ISUCySrAgITZK9rW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ISUCySrAgITZK9rW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ISUCySrAgITZK9rW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ISUCySrAgITZK9rW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ISUCySrAgITZK9rW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ISUCySrAgITZK9rW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ISUCySrAgITZK9rW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ISUCySrAgITZK9rW .marker.cross{stroke:#333333;}#mermaid-svg-ISUCySrAgITZK9rW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ISUCySrAgITZK9rW p{margin:0;}#mermaid-svg-ISUCySrAgITZK9rW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ISUCySrAgITZK9rW .cluster-label text{fill:#333;}#mermaid-svg-ISUCySrAgITZK9rW .cluster-label span{color:#333;}#mermaid-svg-ISUCySrAgITZK9rW .cluster-label span p{background-color:transparent;}#mermaid-svg-ISUCySrAgITZK9rW .label text,#mermaid-svg-ISUCySrAgITZK9rW span{fill:#333;color:#333;}#mermaid-svg-ISUCySrAgITZK9rW .node rect,#mermaid-svg-ISUCySrAgITZK9rW .node circle,#mermaid-svg-ISUCySrAgITZK9rW .node ellipse,#mermaid-svg-ISUCySrAgITZK9rW .node polygon,#mermaid-svg-ISUCySrAgITZK9rW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ISUCySrAgITZK9rW .rough-node .label text,#mermaid-svg-ISUCySrAgITZK9rW .node .label text,#mermaid-svg-ISUCySrAgITZK9rW .image-shape .label,#mermaid-svg-ISUCySrAgITZK9rW .icon-shape .label{text-anchor:middle;}#mermaid-svg-ISUCySrAgITZK9rW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ISUCySrAgITZK9rW .rough-node .label,#mermaid-svg-ISUCySrAgITZK9rW .node .label,#mermaid-svg-ISUCySrAgITZK9rW .image-shape .label,#mermaid-svg-ISUCySrAgITZK9rW .icon-shape .label{text-align:center;}#mermaid-svg-ISUCySrAgITZK9rW .node.clickable{cursor:pointer;}#mermaid-svg-ISUCySrAgITZK9rW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ISUCySrAgITZK9rW .arrowheadPath{fill:#333333;}#mermaid-svg-ISUCySrAgITZK9rW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ISUCySrAgITZK9rW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ISUCySrAgITZK9rW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ISUCySrAgITZK9rW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ISUCySrAgITZK9rW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ISUCySrAgITZK9rW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ISUCySrAgITZK9rW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ISUCySrAgITZK9rW .cluster text{fill:#333;}#mermaid-svg-ISUCySrAgITZK9rW .cluster span{color:#333;}#mermaid-svg-ISUCySrAgITZK9rW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ISUCySrAgITZK9rW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ISUCySrAgITZK9rW rect.text{fill:none;stroke-width:0;}#mermaid-svg-ISUCySrAgITZK9rW .icon-shape,#mermaid-svg-ISUCySrAgITZK9rW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ISUCySrAgITZK9rW .icon-shape p,#mermaid-svg-ISUCySrAgITZK9rW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ISUCySrAgITZK9rW .icon-shape .label rect,#mermaid-svg-ISUCySrAgITZK9rW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ISUCySrAgITZK9rW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ISUCySrAgITZK9rW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ISUCySrAgITZK9rW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是 存量 200+ 微服务
否 Quarkus 边缘

否 独立 CLI/实验
否 核心交易/客服
是 创新 Lab
Nike-like 全球运动零售 Java 中台
主栈是否 Spring Boot / Cloud?
需要 Actuator / Security / 多租户?
LangChain4j 1.0
Spring AI 1.0 GA
团队能否接受 API 频繁变?
ChatClient + Advisor + Observation 统一
MCP Server 暴露 OMS @Tool
LiteLLM 统一模型入口
AiServices 代理 / 跨框架

场景(脱敏) 选型 原因
全球电商客服 / 订单 Agent(核心) Spring AI Security、审计、与订单服务同 JVM 事务边界
门店 iPad 离线助手(Quarkus) LangChain4j 原生 Quarkus 扩展、镜像小
Cursor 运维查 SAP MCP Server(Spring AI 暴露) 与生产 Agent 共用 Tool 契约
Python 算法组 RAG 实验 LangChain Python + HTTP/MCP 不推倒 Java 生产链路

§12.8 生产化:Resilience4j · 限流 · 密钥

#mermaid-svg-4mnbN4sJAT1v2UON{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4mnbN4sJAT1v2UON .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4mnbN4sJAT1v2UON .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4mnbN4sJAT1v2UON .error-icon{fill:#552222;}#mermaid-svg-4mnbN4sJAT1v2UON .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4mnbN4sJAT1v2UON .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4mnbN4sJAT1v2UON .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4mnbN4sJAT1v2UON .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4mnbN4sJAT1v2UON .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4mnbN4sJAT1v2UON .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4mnbN4sJAT1v2UON .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4mnbN4sJAT1v2UON .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4mnbN4sJAT1v2UON .marker.cross{stroke:#333333;}#mermaid-svg-4mnbN4sJAT1v2UON svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4mnbN4sJAT1v2UON p{margin:0;}#mermaid-svg-4mnbN4sJAT1v2UON .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4mnbN4sJAT1v2UON .cluster-label text{fill:#333;}#mermaid-svg-4mnbN4sJAT1v2UON .cluster-label span{color:#333;}#mermaid-svg-4mnbN4sJAT1v2UON .cluster-label span p{background-color:transparent;}#mermaid-svg-4mnbN4sJAT1v2UON .label text,#mermaid-svg-4mnbN4sJAT1v2UON span{fill:#333;color:#333;}#mermaid-svg-4mnbN4sJAT1v2UON .node rect,#mermaid-svg-4mnbN4sJAT1v2UON .node circle,#mermaid-svg-4mnbN4sJAT1v2UON .node ellipse,#mermaid-svg-4mnbN4sJAT1v2UON .node polygon,#mermaid-svg-4mnbN4sJAT1v2UON .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4mnbN4sJAT1v2UON .rough-node .label text,#mermaid-svg-4mnbN4sJAT1v2UON .node .label text,#mermaid-svg-4mnbN4sJAT1v2UON .image-shape .label,#mermaid-svg-4mnbN4sJAT1v2UON .icon-shape .label{text-anchor:middle;}#mermaid-svg-4mnbN4sJAT1v2UON .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4mnbN4sJAT1v2UON .rough-node .label,#mermaid-svg-4mnbN4sJAT1v2UON .node .label,#mermaid-svg-4mnbN4sJAT1v2UON .image-shape .label,#mermaid-svg-4mnbN4sJAT1v2UON .icon-shape .label{text-align:center;}#mermaid-svg-4mnbN4sJAT1v2UON .node.clickable{cursor:pointer;}#mermaid-svg-4mnbN4sJAT1v2UON .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4mnbN4sJAT1v2UON .arrowheadPath{fill:#333333;}#mermaid-svg-4mnbN4sJAT1v2UON .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4mnbN4sJAT1v2UON .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4mnbN4sJAT1v2UON .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4mnbN4sJAT1v2UON .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4mnbN4sJAT1v2UON .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4mnbN4sJAT1v2UON .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4mnbN4sJAT1v2UON .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4mnbN4sJAT1v2UON .cluster text{fill:#333;}#mermaid-svg-4mnbN4sJAT1v2UON .cluster span{color:#333;}#mermaid-svg-4mnbN4sJAT1v2UON div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4mnbN4sJAT1v2UON .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4mnbN4sJAT1v2UON rect.text{fill:none;stroke-width:0;}#mermaid-svg-4mnbN4sJAT1v2UON .icon-shape,#mermaid-svg-4mnbN4sJAT1v2UON .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4mnbN4sJAT1v2UON .icon-shape p,#mermaid-svg-4mnbN4sJAT1v2UON .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4mnbN4sJAT1v2UON .icon-shape .label rect,#mermaid-svg-4mnbN4sJAT1v2UON .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4mnbN4sJAT1v2UON .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4mnbN4sJAT1v2UON .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4mnbN4sJAT1v2UON :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Platform
App
Edge
注入
open 5xx
API Gateway

rate limit / WAF
Spring AI ChatClient
Resilience4j

CB / Retry / Bulkhead
MCP Client
LiteLLM Proxy
Vault / K8s Secret
OTel → Grafana
Azure OpenAI fallback

能力 做法
Resilience4j CircuitBreaker 包 ChatModel;失败率 >50% 切 fallback;Bulkhead 隔离 SSE 与批处理
Rate limit 网关 per-API-key;应用内 Bucket4j per userId;LiteLLM 侧 team 配额
Secret 禁止 key 进 repo;Vault 动态凭据;LiteLLM key 与 Azure key 分 secret path;MCP token 短 TTL
审计 Observation span 带 tenant_id, model, tool_name;写 Tool 100% 落 SIEM

§12.10 Buy-Team 交易智能化统一 Demo(Carts / Pricing / Payment)

定位 :全球运动零售 buy team 核心交易链路 Spring AI 工程化样板------一个 ChatClient + 三组 @Tool ,对接存量 Carts / Pricing(到手价)/ Payment 模块化服务。深链场景叙述:08 §10.13--§10.1408 §10.5.1生产闭环(AgentOps / 轨迹 Eval / Canary / Tool 可靠性)08 §10.15
#mermaid-svg-MzQI721blEa9S8D1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MzQI721blEa9S8D1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MzQI721blEa9S8D1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MzQI721blEa9S8D1 .error-icon{fill:#552222;}#mermaid-svg-MzQI721blEa9S8D1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MzQI721blEa9S8D1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MzQI721blEa9S8D1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MzQI721blEa9S8D1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MzQI721blEa9S8D1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MzQI721blEa9S8D1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MzQI721blEa9S8D1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MzQI721blEa9S8D1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MzQI721blEa9S8D1 .marker.cross{stroke:#333333;}#mermaid-svg-MzQI721blEa9S8D1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MzQI721blEa9S8D1 p{margin:0;}#mermaid-svg-MzQI721blEa9S8D1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MzQI721blEa9S8D1 .cluster-label text{fill:#333;}#mermaid-svg-MzQI721blEa9S8D1 .cluster-label span{color:#333;}#mermaid-svg-MzQI721blEa9S8D1 .cluster-label span p{background-color:transparent;}#mermaid-svg-MzQI721blEa9S8D1 .label text,#mermaid-svg-MzQI721blEa9S8D1 span{fill:#333;color:#333;}#mermaid-svg-MzQI721blEa9S8D1 .node rect,#mermaid-svg-MzQI721blEa9S8D1 .node circle,#mermaid-svg-MzQI721blEa9S8D1 .node ellipse,#mermaid-svg-MzQI721blEa9S8D1 .node polygon,#mermaid-svg-MzQI721blEa9S8D1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MzQI721blEa9S8D1 .rough-node .label text,#mermaid-svg-MzQI721blEa9S8D1 .node .label text,#mermaid-svg-MzQI721blEa9S8D1 .image-shape .label,#mermaid-svg-MzQI721blEa9S8D1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-MzQI721blEa9S8D1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MzQI721blEa9S8D1 .rough-node .label,#mermaid-svg-MzQI721blEa9S8D1 .node .label,#mermaid-svg-MzQI721blEa9S8D1 .image-shape .label,#mermaid-svg-MzQI721blEa9S8D1 .icon-shape .label{text-align:center;}#mermaid-svg-MzQI721blEa9S8D1 .node.clickable{cursor:pointer;}#mermaid-svg-MzQI721blEa9S8D1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MzQI721blEa9S8D1 .arrowheadPath{fill:#333333;}#mermaid-svg-MzQI721blEa9S8D1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MzQI721blEa9S8D1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MzQI721blEa9S8D1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MzQI721blEa9S8D1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MzQI721blEa9S8D1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MzQI721blEa9S8D1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MzQI721blEa9S8D1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MzQI721blEa9S8D1 .cluster text{fill:#333;}#mermaid-svg-MzQI721blEa9S8D1 .cluster span{color:#333;}#mermaid-svg-MzQI721blEa9S8D1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MzQI721blEa9S8D1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MzQI721blEa9S8D1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-MzQI721blEa9S8D1 .icon-shape,#mermaid-svg-MzQI721blEa9S8D1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MzQI721blEa9S8D1 .icon-shape p,#mermaid-svg-MzQI721blEa9S8D1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MzQI721blEa9S8D1 .icon-shape .label rect,#mermaid-svg-MzQI721blEa9S8D1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MzQI721blEa9S8D1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MzQI721blEa9S8D1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MzQI721blEa9S8D1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 存量领域服务
commerce-ai-service
ChatClient
Advisor链
CommerceTools组件
CartsFacade
PricingFacade
PaymentFacade

java 复制代码
@Configuration
public class CommerceAiConfig {

    @Bean
    ChatClient commerceChatClient(
            ChatClient.Builder builder,
            CommerceTools tools,
            SafeGuardAdvisor safeGuard,
            SimpleLoggerAdvisor logger) {
        return builder
            .defaultSystem("""
                你是 buy team 交易助手。禁止口算金额、库存、券面额、支付费率。
                所有数值仅引用 Tool 返回的 JSON 字段。写操作前必须先查库存/可用渠道。
                """)
            .defaultAdvisors(safeGuard, logger)
            .defaultTools(tools)
            .build();
    }
}

@Component
public class CommerceTools {
    private final CartsFacade carts;
    private final PricingFacade pricing;
    private final PaymentFacade payment;

    // ------ Carts(§10.13)------
    @Tool List<SkuSearchDto> searchProducts(String query, String size, BigDecimal maxPrice) {
        return carts.searchProducts(query, size, maxPrice);
    }
    @Tool InventoryDto checkInventory(String skuId, String storeId) {
        return carts.checkInventory(skuId, storeId);
    }
    @Tool CartLineDto addLineItem(String cartId, String skuId, int quantity) {
        return carts.addLineItem(cartId, skuId, quantity);
    }

    // ------ Pricing(§10.5.1)------
    @Tool List<CouponDto> getEligibleCoupons(String userId, String cartId) {
        return pricing.listEligibleCoupons(userId, cartId);
    }
    @Tool GapDto gapToNextThreshold(String cartId) {
        return pricing.gapToNextThreshold(cartId);
    }
    @Tool List<AddOnSkuDto> suggestAddOnSkus(String cartId, BigDecimal gapAmount) {
        return pricing.suggestAddOnForGap(cartId, gapAmount);
    }

    // ------ Payment(§10.14)------
    @Tool List<PaymentMethodDto> listAvailablePaymentMethods(
            String userId, String orderId, String region) {
        return payment.listAvailable(userId, orderId, region);
    }
    @Tool RankedPaymentMethodsDto rankPaymentMethods(
            String userId, String orderId, List<String> candidateMethodIds) {
        return payment.rankMethods(userId, orderId, candidateMethodIds);
    }
}

按场景入口(可同 Client,不同 system 提示)

场景 HTTP 示意 主要 Tool 链
智能加车 POST /carts/{id}/ai-add search → inventory → addLineItem
选券凑满减 POST /pricing/{cartId}/ai-coupons getEligible → gap → suggestAddOn
支付推荐 POST /checkout/{orderId}/ai-pay-methods listAvailable → rank
java 复制代码
@RestController
@RequestMapping("/api/v1/commerce-ai")
public class CommerceAiController {
    private final ChatClient commerceChatClient;

    @PostMapping("/carts/{cartId}/intents")
    public AiCartsResponse cartsIntent(@PathVariable String cartId, @RequestBody String message) {
        return commerceChatClient.prompt()
            .user("场景=CARTS_ADD;cartId=" + cartId + ";" + message)
            .call()
            .entity(AiCartsResponse.class);
    }

    @PostMapping("/carts/{cartId}/coupon-advice")
    public CouponAdviceResponse pricingIntent(@PathVariable String cartId, @RequestBody String message) {
        return commerceChatClient.prompt()
            .user("场景=PRICING_COUPON;cartId=" + cartId + ";" + message)
            .call()
            .entity(CouponAdviceResponse.class);
    }

    @PostMapping("/orders/{orderId}/payment-advice")
    public PaymentAdviceResponse paymentIntent(
            @PathVariable String orderId,
            @RequestParam String userId,
            @RequestParam String region,
            @RequestBody String message) {
        return commerceChatClient.prompt()
            .user("场景=PAYMENT_RANK;orderId=" + orderId + ";userId=" + userId + ";region=" + region + ";" + message)
            .call()
            .entity(PaymentAdviceResponse.class);
    }
}

Advisor 链(推荐顺序)

  1. SafeGuardAdvisor --- 注入 / 有害内容
  2. MessageChatMemoryAdvisor(可选)--- 同会话多轮加车
  3. SimpleLoggerAdvisor + Micrometer Observation --- 审计 tool_name
  4. (Pricing 可加)输出 schema 校验 Advisor,禁止 explanation 含金额正则

与 LiteLLM :生产 base-url 见 §12.2;未部署网关时直连 Azure OpenAI,Tool 契约不变。

与 Cursor :Cursor 仅研发效能(05 §10);本模块走 用户/Checkout 请求路径,不得混用 IDE 密钥。


§12.9 面试满分答(3 道)

Q12-1 · 美团:Spring AI 生产为什么前面要挂 LiteLLM,而不是应用直连 Azure?

(1) 标准答案 :多模型(快/慢/便宜)、统一 计费归因密钥轮换 、全链路 限流 应在网关完成;Spring AI 只配置一个 OpenAI-compatible base-url。网关故障时 Resilience4j fallback 直连 Azure,避免单点。

(2) 原理 walk :LiteLLM 把 provider 差异收敛到 HTTP 层;Micrometer 仍打在 Spring AI Observation 上,网关日志做 二次对账

(3) 权衡 :多一跳 ~5--15ms;收益是 密钥不下沉到 200 个微服务

(4) 落地spring.ai.openai.base-url → LiteLLM;profile fallback 激活 Azure;模型别名 commerce-fast / commerce-smart

(5) 追问 :和 Spring AI 自带 ModelRouter?------Router 管 同进程内 模型选择;LiteLLM 管 跨云配额与 org 级策略,二者可叠加。


Q12-2 · 阿里:同一应用里 Redis Vector 和 pgvector 怎么分工?

(1) 标准答案Redis 扛促销热 SKU、高 QPS、小容量;pgvector 扛全量目录 + SQL metadata 过滤 (租户/类目/区域)。Spring AI 两个 VectorStore Bean + Router/Qualifier ,RAG Advisor 按意图选库,必要时 双路检索 RRF 合并 (见 03 §12)。

(2) 原理 walk:热数据更新频、延迟敏感 → 内存向量;冷知识要 JOIN 权限表 → PG。

(3) 量化 :热库 <100k 向量 P99 <20ms;pgvector 千万级要 HNSW + 分区

(4) 落地@Qualifier("redisHotStore") / pgCatalogStore;embedding 版本 pin 一致

(5) 追问 :能否只用一个 Milvus?------可以,但 权限 SQL + 运维面 很多团队已用 PG;Redis 做 L1 仍划算。


Q12-3 · 字节:MCP Server 和本机 @Tool 什么时候拆?

(1) 标准答案 :仅本 JVM Agent 调订单 API → @Tool + Service ;还要给 Cursor、Python 批处理、其他 BU Agent 共用 → 独立 MCP Server 暴露同一套 schema;Client 用 Spring AI MCP SSE + mTLS 连 SAP/OMS Server。

(2) 原理 walk :MCP 提供 list_tools 发现与版本治理;@Tool 是进程内最快路径。

(3) 权衡 :MCP 多一跳 + 运维一个 Server;收益是 凭证不下沉到每个 IDE/脚本

(4) 落地:写 Tool 幂等 + HITL;stdio 仅 dev,生产 SSE/HTTP。

(5) 追问 :和 04 §12 重复?------本篇偏 Spring AI 1.0 配置与 Java Bean;04 偏协议与 A2A 协作。


§99 本章冲刺

10 道 Q&A(S01--S10) + 25 项 Checklist 。全模块题见 98-面试高频题满分答与Checklist.md

§99.1 高频 Q&A(S01--S10)

S01 · Java 团队要不要全转 Python 做 AI?

结论 ------不必全转;Spring AI 让存量 Spring Boot 团队 1--2 周接入。原理 ------工程化(Security、事务、虚拟线程)Java 仍强;Python 适合研究与快速实验。落地 ------生产 Java + 实验 Python,LiteLLM/MCP 连接两端。风险------两栈治理失控 → 明确「生产只认 Java 服务」边界。

S02 · Spring AI 1.0 GA 四个必背抽象?

ChatClient (Fluent 调模型)、Advisor 链 (护栏/RAG/Memory)、VectorStore (RAG)、Observation (Micrometer token/延迟)。外加 MCP Client/Server 做组织级 Tool(§12)。

S03 · LiteLLM 和直连 Azure 怎么配?

主路径 base-url → LiteLLM + team API key;fallback profile 直连 Azure OpenAI + Resilience4j 熔断切换(§12.2)。密钥只放 Vault,不进 Git。

S04 · @Tool 写操作为什么要幂等?

:Agent 循环会 重复调用 同一 tool(LLM 未识别成功或超时重试)。键 = tool:orderId:date + Redis 24h;金额 > 阈值 HITL(§13 STAR)。

S05 · Advisor 链推荐顺序?

SafeGuard → Memory → RAG → Audit → LLM → Audit → SafeGuard。Memory 在 RAG 前才能带对话上下文检索;审计在脱敏后。

S06 · C 端 SSE 要注意什么?

stream().content() / chatResponse()Flux ;Gateway 禁缓冲timeout + 降级文案 ;与 B 端 Agent Bulkhead 分池(§12.6)。

S07 · Redis Vector vs pgvector 分工?

:Redis 热、小、高 QPS ;pgvector 全量 + SQL 过滤 + 权限 JOIN 。多 Bean @Qualifier(§12.5)。

S08 · MCP Client 连 SAP/OMS 怎么鉴权?

:SSE/HTTP + Bearer 短 TTL ;mTLS 内网;tool 级 RBAC;写操作 idempotency_key;全量审计进 SIEM(§12.4、§12.8)。

S09 · LangChain4j 1.0 vs Spring AI 怎么选(运动零售)?

Spring Boot 核心中台 → Spring AIQuarkus 边缘 / 实验 Lab → LangChain4j ;组织级 Tool → MCP Server(§12.7 决策树)。

S10 · 生产三件套?

Resilience4j (熔断/舱壁/重试)、Rate limit (网关 + 应用 + LiteLLM)、Secret(Vault + 密钥不下沉)。Observation 对接 Grafana(§12.8)。

S11 · buy team 三域共用一个 ChatClient 还是三个?

推荐一个 ChatClient + 一个 CommerceTools Bean ,按 user message 场景前缀独立 Controller 分流(§12.10)。共享 Advisor / Observation / LiteLLM 配置;避免三套密钥与监控口径。若 SLA 要硬隔离,可拆三个 ChatClient 仍共用 Tool 实现类。

S12 · Carts 加车 Tool 为什么要分三步?

:与 08 Q9 一致:库存权威、加车幂等、失败可审计。Spring AI 只编排,不合并成「伪订单 Tool」。

S13 · Pricing 凑满减如何在 Spring AI 里保证金额不幻觉?

getEligibleCoupons / gapToNextThreshold / suggestAddOnSkus 全走 PricingFacade;CouponAdviceResponse.explanation 做无数字正则校验;前端只渲染 Tool JSON(§12.10、08 §10.5.1)。

S14 · Payment rankPaymentMethods 与模块化 Payment Starter 关系?

PaymentFacade 聚合各渠道 Starter 的 healthCheck + Vault 绑卡列表;Tool 不暴露 PAN。推荐为 建议态,Checkout 用户确认后才调支付 API(08 §10.14)。

S15 · Cursor 和 §12.10 模块边界?

:Cursor = 写代码 / Skills / CI(05 §10);commerce-ai-service = 运行时用户流量。密钥、SLA、审计域隔离;面试画两张图勿合并。

S16 · buy team Agent trace 必备字段?

trace_idfeature(CARTS_AI_ADD 等)、prompt_version、每个 Tool child span 的 input/output(脱敏)、token usage。深链 08 §10.15.206 §10.3

S17 · 轨迹 Eval CI 阻断条件?

:L1 轨迹匹配率 <95% 或 L2 schema/正则失败即阻断;LLM-Judge 不评金额。golden 样例见 08 §10.15.3(→ 98 C.112--C.113)。

S18 · Tool 熔断时 commerce-ai 如何降级?

searchProducts 可空列表+转人工;checkInventory 失败 禁止 addLineItem;Payment 回退 Checkout 原生渠道表。矩阵见 08 §10.15.5(→ 98 C.114)。

S19 · Spring AI 与 LangGraph 在 buy team 的分工?

:Carts/Pricing/Payment 推荐留在 ChatClient+maxIterations;退款 Saga/多 HITL 用外部状态机经 MCP 调用(08 §10.15.6、Q20)。

S20 · Agent 灰度与 prompt 回滚?

:Langfuse 版本号 + userId 哈希流量;Payment 建议态先行;变更必重跑 Eval(08 §10.15.4、Q22)。

§99.2 考前 Checklist(36 项)

框架与 GA

  • 能口述 Spring AI = Spring Boot 的 AI 抽象层
  • ChatClient Fluent API 五步:prompt / user / advisors / tools / call|stream
  • Advisor 链顺序:SafeGuard → Memory → RAG → Audit
  • Observation:token、latency、model 标签进 Grafana
  • VectorStore 统一接口;能换 PgVector / Redis / Milvus

模型与网关

  • LiteLLM 作 OpenAI-compatible base-url 主路径
  • Azure OpenAI fallback + Resilience4j 切换条件
  • 模型别名:快/慢/便宜(commerce-fast / smart)
  • 密钥 Vault 管理;禁止 key 进 repo

RAG 与多库

  • 双 VectorStore Bean + @Qualifier
  • Redis 热向量 vs pgvector 全量 + SQL filter
  • embedding 版本 pin;与 03 三层架构对齐

Tool / MCP

  • @Tool 声明;写操作幂等 key
  • 金额阈值 HITL;Agent maxSteps 限制
  • MCP Server 暴露 @Tool;MCP Client 连 SAP/OMS SSE
  • MCP vs 进程内 @Tool 决策(多 Client 才拆 Server)

C 端与生产

  • SSE:TEXT_EVENT_STREAM + Gateway 禁缓冲
  • 首 token 延迟目标;timeout 降级文案
  • Resilience4j:CircuitBreaker / Bulkhead / Retry
  • Rate limit:网关 + Bucket4j + LiteLLM 配额
  • §14 速记卡 90 秒脚本
  • §13 STAR 退款事故 3 分钟
  • §99.1 任选 5 题录音 ≤90s

Buy-Team 交易智能化(§12.10)

  • 能白板 CommerceTools 三域 Tool 列表
  • 能口述一个 ChatClient + 场景分流 Controller
  • Carts:search → inventory → addLineItem
  • Pricing:eligible → gap → suggestAddOn;禁 explanation 写金额
  • Payment:list → rank;不返 PAN
  • S11--S15 与 08 §10.13--14 交叉复习
  • S16--S20 与 08 §10.15 交叉复习
  • 能默画 Carts trace JSON;口述 Eval L1--L3 分层
  • Tool 可靠性:熔断/超时/幂等;RAG 不替代 searchProducts

章节导航

# 章节 核心内容
1 Java AI 框架全景 定位 + 为什么不转 Python
2 框架选型决策树 Spring AI vs LangChain4j + LiteLLM 网关
3 Spring AI 核心架构 ChatClient / ChatModel / Advisor / Message
4 模型接入 多 Provider 配置 + 模型路由 + 流式输出
5 RAG 实战 VectorStore + ETL 管线 + 检索调优
6 Tool Calling @Tool + 幂等 + MCP 集成
7 Advisor 护栏 内置 Advisor + 自定义合规 + 链编排
8 LangChain4j 对比 AiService 代理 + 15 维对比表
9 Spring AI Alibaba 通义模型 + DashScope + 国内最佳实践
10 生产化工程 监控 + 熔断 + 多租户 + 部署架构
11 实战项目 智能客服系统端到端
12 面试题 5 道 阿里/字节/蚂蚁/Google/美团
13 STAR-M-P Tool Calling 重复退款
14 速记卡 90 秒口述脚本
§12 Spring AI 1.0 GA / MCP LiteLLM、多 VectorStore、SSE、生产三件套
§99 本章冲刺 S01--S10 Q&A + 25 项 Checklist

关联文件03-RAG · 04-Agent · 15-低代码平台 · 16-全栈实战 · 13-Playbook
一句话速记:Spring AI = Spring Boot 的 AI 抽象层:ChatClient 调模型 + Advisor 做护栏 + VectorStore 做 RAG + @Tool 做函数调用。Java 开发者不需要转 Python,在 Spring 生态内即可完成 LLM 应用开发。写操作工具必须幂等 + 金额必须 HITL + Agent 必须限步数。

官方文档与源码(一级依据)

AI Engineering · 正文机制应来自下方 官方文档(L1)官方源码仓库(L2)

禁止用教程站/博客充当机制依据。本章 QPS/延迟/STAR 为面试示意。

写作规范:docs/official-sources-registry.md §0

L1 · 官方文档

L2 · 官方源码

L3 · 论文 / 开放规范

相关推荐
沫儿笙1 小时前
发那科机器人氩弧焊节气装置
人工智能·机器人
Cloud_Shy6181 小时前
解读《Effective Python 3rd Edition》:从练气到老魔(第三章 Item 21 - 24)
开发语言·人工智能·笔记·python·迭代器模式
拾年2751 小时前
别调 BERT 了:我用 Prompt 做了套 NLP 系统,20 分钟搞定
前端·人工智能
野生技术架构师1 小时前
2026 Java面试宝典(春招/社招/秋招通用):没有前言,只有答案,直接开背
java·开发语言·面试
mN9B2uk171 小时前
数据库的约束简介
java·数据库·sql
AI人工智能+电脑小能手1 小时前
【大白话说Java面试题 第99题】【Mysql篇】第29题:如何选择合适的分布式主键方案?
java·数据库·分布式·mysql·面试
装不满的克莱因瓶1 小时前
学习 LLM 的函数回调及格式化输出,让 LLM 拥有更强的能力
人工智能·ai·大模型·llm·agent·智能体
极光代码工作室1 小时前
基于SpringBoot的任务管理系统
java·springboot·web开发·后端开发
涤生大数据1 小时前
从 ETL 到 Agent:AI数据工程如何搭建企业级“数据工厂“
数据仓库·人工智能·etl