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 国内大模型接入最佳实践
- API Key 管理:使用 Spring Cloud Config / Vault 集中管理,不硬编码
- 网络隔离:国内模型走内网 VPC Endpoint,不经公网
- 多 Provider 热切换 :配置中心推送模型变更,无需重启(
@RefreshScope) - 合规审计:所有 Prompt/Response 留存 90 天(金融场景要求)
- 计费监控 :按 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 日志显示同一订单在同一会话中被调用
submitRefund2-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 步(10 min) :查 Agent 日志,发现订单
2024-88832在 15:03:21 和 15:03:24 被调用了两次submitRefund - 第 2 步(20 min) :分析 LLM 输出,第一次 tool_response 返回后,LLM 生成了"我再帮您确认一下退款状态",然后又调用了
submitRefund - 第 3 步(15 min) :确认根因------
submitRefund无幂等保护,且 Agent 循环未限制同一工具重复调用
R(Resolution)· 解决方案
止血(当天):
- 紧急下线退款 Agent,切回人工客服
- 手动处理 47 笔重复退款
根治(2 周):
- 阶段 1(3 天) :所有写操作工具加幂等 key(
tool_name:orderId:date),Redis 存储,24h 过期 - 阶段 2(1 周):金额 > ¥500 退款加 HITL 审批
- 阶段 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 工具注册规范》):
- 所有写操作工具必须幂等
- 金额操作必须 HITL------阈值可配置,默认 ¥500
- Agent 循环必须有最大步数限制------默认 maxSteps=5
- 同一工具 + 同一参数不允许重复调用
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 应用拉到「可上生产」------统一 ChatClient 、Advisor 链 、Micrometer Observation 、VectorStore 抽象 、MCP Client/Server 。本节面向 全球运动零售 / 电商 Java 中台 (脱敏 Nike-like):存量 Spring Boot + SAP/OMS + Redis/pgvector + C 端 SSE。
Buy 域 14 场景(导购/客服/满减/Carts/Payment...)完整 POC + 代码 + 面试 B01--B28 → 02-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 OpenAiChatModel 的 base-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.14、08 §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 链(推荐顺序)
SafeGuardAdvisor--- 注入 / 有害内容MessageChatMemoryAdvisor(可选)--- 同会话多轮加车SimpleLoggerAdvisor+ Micrometer Observation --- 审计tool_name- (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 AI ;Quarkus 边缘 / 实验 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_id、feature(CARTS_AI_ADD 等)、prompt_version、每个 Tool child span 的 input/output(脱敏)、token usage。深链 08 §10.15.2、06 §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 为面试示意。
L1 · 官方文档
L2 · 官方源码
L3 · 论文 / 开放规范