欢迎光临
我们一直在努力

Spring AI Alibaba 框架实战指南

面向Java开发者的智能体(Agent)AI开发框架

目录

  • 1. Spring AI Alibaba概述
  • 2. 核心架构与设计理念
  • 3. 快速开始
  • 4. 核心组件详解
  • 5. 高级特性
  • 6. 企业级应用实践
  • 7. 最佳实践与注意事项

1. Spring AI Alibaba概述

1.1 什么是Spring AI Alibaba?

Spring AI Alibaba 是阿里云基于 Spring AI 构建的开源AI框架,是阿里云通义系列模型及服务在Java AI应用开发领域的最佳实践。它为Java开发者提供了一套完整的AI应用开发解决方案,让传统Java开发者能够快速上手AI应用开发。

#mermaid-svg-lR2yhmY45CVUBzUR {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-lR2yhmY45CVUBzUR .error-icon{fill:#552222;}#mermaid-svg-lR2yhmY45CVUBzUR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lR2yhmY45CVUBzUR .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-lR2yhmY45CVUBzUR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lR2yhmY45CVUBzUR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lR2yhmY45CVUBzUR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lR2yhmY45CVUBzUR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lR2yhmY45CVUBzUR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lR2yhmY45CVUBzUR .marker.cross{stroke:#333333;}#mermaid-svg-lR2yhmY45CVUBzUR svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lR2yhmY45CVUBzUR .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-lR2yhmY45CVUBzUR .cluster-label text{fill:#333;}#mermaid-svg-lR2yhmY45CVUBzUR .cluster-label span{color:#333;}#mermaid-svg-lR2yhmY45CVUBzUR .label text,#mermaid-svg-lR2yhmY45CVUBzUR span{fill:#333;color:#333;}#mermaid-svg-lR2yhmY45CVUBzUR .node rect,#mermaid-svg-lR2yhmY45CVUBzUR .node circle,#mermaid-svg-lR2yhmY45CVUBzUR .node ellipse,#mermaid-svg-lR2yhmY45CVUBzUR .node polygon,#mermaid-svg-lR2yhmY45CVUBzUR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lR2yhmY45CVUBzUR .node .label{text-align:center;}#mermaid-svg-lR2yhmY45CVUBzUR .node.clickable{cursor:pointer;}#mermaid-svg-lR2yhmY45CVUBzUR .arrowheadPath{fill:#333333;}#mermaid-svg-lR2yhmY45CVUBzUR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lR2yhmY45CVUBzUR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lR2yhmY45CVUBzUR .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-lR2yhmY45CVUBzUR .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-lR2yhmY45CVUBzUR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lR2yhmY45CVUBzUR .cluster text{fill:#333;}#mermaid-svg-lR2yhmY45CVUBzUR .cluster span{color:#333;}#mermaid-svg-lR2yhmY45CVUBzUR 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-lR2yhmY45CVUBzUR :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

Spring AI Alibaba生态

Spring AI核心

Spring AI Alibaba

阿里云通义系列

企业级集成

标准化API

多模型支持

通义千问Chat

通义万相Image

语音合成Audio

向量检索Embedding

Nacos注册中心

Redis会话存储

Elasticsearch向量库

1.2 核心特点

1. 简化AI开发复杂度

#mermaid-svg-h0KM3oCP8m9HDr9a {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-h0KM3oCP8m9HDr9a .error-icon{fill:#552222;}#mermaid-svg-h0KM3oCP8m9HDr9a .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-h0KM3oCP8m9HDr9a .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-h0KM3oCP8m9HDr9a .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-h0KM3oCP8m9HDr9a .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-h0KM3oCP8m9HDr9a .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-h0KM3oCP8m9HDr9a .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-h0KM3oCP8m9HDr9a .marker{fill:#333333;stroke:#333333;}#mermaid-svg-h0KM3oCP8m9HDr9a .marker.cross{stroke:#333333;}#mermaid-svg-h0KM3oCP8m9HDr9a svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-h0KM3oCP8m9HDr9a .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-h0KM3oCP8m9HDr9a .cluster-label text{fill:#333;}#mermaid-svg-h0KM3oCP8m9HDr9a .cluster-label span{color:#333;}#mermaid-svg-h0KM3oCP8m9HDr9a .label text,#mermaid-svg-h0KM3oCP8m9HDr9a span{fill:#333;color:#333;}#mermaid-svg-h0KM3oCP8m9HDr9a .node rect,#mermaid-svg-h0KM3oCP8m9HDr9a .node circle,#mermaid-svg-h0KM3oCP8m9HDr9a .node ellipse,#mermaid-svg-h0KM3oCP8m9HDr9a .node polygon,#mermaid-svg-h0KM3oCP8m9HDr9a .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-h0KM3oCP8m9HDr9a .node .label{text-align:center;}#mermaid-svg-h0KM3oCP8m9HDr9a .node.clickable{cursor:pointer;}#mermaid-svg-h0KM3oCP8m9HDr9a .arrowheadPath{fill:#333333;}#mermaid-svg-h0KM3oCP8m9HDr9a .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-h0KM3oCP8m9HDr9a .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-h0KM3oCP8m9HDr9a .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-h0KM3oCP8m9HDr9a .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-h0KM3oCP8m9HDr9a .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-h0KM3oCP8m9HDr9a .cluster text{fill:#333;}#mermaid-svg-h0KM3oCP8m9HDr9a .cluster span{color:#333;}#mermaid-svg-h0KM3oCP8m9HDr9a 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-h0KM3oCP8m9HDr9a :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

传统AI开发

直接调用API

处理响应

管理上下文

实现工具调用

Spring AI Alibaba

高层抽象ChatClient

自动处理细节

开箱即用

传统方式:

// 需要手动处理大量细节
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://dashscope.aliyuncs.com/api/v1/services/…"))
.header("Authorization", "Bearer " + apiKey)
.POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
.build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// 解析JSON、处理错误、管理会话…

Spring AI Alibaba方式:

// 简洁优雅的API
@Resource
private ChatClient chatClient;

String response = chatClient.prompt()
.user("你好,请介绍一下Spring AI Alibaba")
.call()
.content();

2. 多模型无缝切换

#mermaid-svg-SXSpLZn72sx0hJc7 {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SXSpLZn72sx0hJc7 .error-icon{fill:#552222;}#mermaid-svg-SXSpLZn72sx0hJc7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SXSpLZn72sx0hJc7 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-SXSpLZn72sx0hJc7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SXSpLZn72sx0hJc7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SXSpLZn72sx0hJc7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SXSpLZn72sx0hJc7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SXSpLZn72sx0hJc7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SXSpLZn72sx0hJc7 .marker.cross{stroke:#333333;}#mermaid-svg-SXSpLZn72sx0hJc7 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SXSpLZn72sx0hJc7 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-SXSpLZn72sx0hJc7 .cluster-label text{fill:#333;}#mermaid-svg-SXSpLZn72sx0hJc7 .cluster-label span{color:#333;}#mermaid-svg-SXSpLZn72sx0hJc7 .label text,#mermaid-svg-SXSpLZn72sx0hJc7 span{fill:#333;color:#333;}#mermaid-svg-SXSpLZn72sx0hJc7 .node rect,#mermaid-svg-SXSpLZn72sx0hJc7 .node circle,#mermaid-svg-SXSpLZn72sx0hJc7 .node ellipse,#mermaid-svg-SXSpLZn72sx0hJc7 .node polygon,#mermaid-svg-SXSpLZn72sx0hJc7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SXSpLZn72sx0hJc7 .node .label{text-align:center;}#mermaid-svg-SXSpLZn72sx0hJc7 .node.clickable{cursor:pointer;}#mermaid-svg-SXSpLZn72sx0hJc7 .arrowheadPath{fill:#333333;}#mermaid-svg-SXSpLZn72sx0hJc7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SXSpLZn72sx0hJc7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SXSpLZn72sx0hJc7 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-SXSpLZn72sx0hJc7 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-SXSpLZn72sx0hJc7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SXSpLZn72sx0hJc7 .cluster text{fill:#333;}#mermaid-svg-SXSpLZn72sx0hJc7 .cluster span{color:#333;}#mermaid-svg-SXSpLZn72sx0hJc7 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-SXSpLZn72sx0hJc7 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

配置切换

配置切换

配置切换

配置切换

应用代码层

ChatClient统一接口

模型提供商

通义千问

OpenAI GPT

本地Ollama

其他模型

相同的应用代码

3. 丰富的AI能力支持
能力类型支持的模型应用场景API接口
对话聊天 通义千问、GPT-4、Claude等 智能客服、问答系统 ChatClient
图像生成 通义万相、DALL-E等 文生图、图像编辑 ImageClient
语音合成 通义语音合成 文字转语音 AudioClient
文本向量化 通义Embedding RAG、语义搜索 EmbeddingClient
多模态 通义Vision 图文理解、OCR ChatClient

1.3 版本信息

  • 当前版本:1.0 GA(2025年正式发布)
  • 依赖要求:
    • JDK 17+(基于Spring Boot 3.x)
    • Spring Boot 3.2+
    • Maven 3.6+ 或 Gradle 7.5+

1.4 与其他框架对比

#mermaid-svg-UsUX8hZFnV5GOIHj {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-UsUX8hZFnV5GOIHj .error-icon{fill:#552222;}#mermaid-svg-UsUX8hZFnV5GOIHj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UsUX8hZFnV5GOIHj .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-UsUX8hZFnV5GOIHj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UsUX8hZFnV5GOIHj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UsUX8hZFnV5GOIHj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UsUX8hZFnV5GOIHj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UsUX8hZFnV5GOIHj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UsUX8hZFnV5GOIHj .marker.cross{stroke:#333333;}#mermaid-svg-UsUX8hZFnV5GOIHj svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UsUX8hZFnV5GOIHj .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-UsUX8hZFnV5GOIHj .cluster-label text{fill:#333;}#mermaid-svg-UsUX8hZFnV5GOIHj .cluster-label span{color:#333;}#mermaid-svg-UsUX8hZFnV5GOIHj .label text,#mermaid-svg-UsUX8hZFnV5GOIHj span{fill:#333;color:#333;}#mermaid-svg-UsUX8hZFnV5GOIHj .node rect,#mermaid-svg-UsUX8hZFnV5GOIHj .node circle,#mermaid-svg-UsUX8hZFnV5GOIHj .node ellipse,#mermaid-svg-UsUX8hZFnV5GOIHj .node polygon,#mermaid-svg-UsUX8hZFnV5GOIHj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UsUX8hZFnV5GOIHj .node .label{text-align:center;}#mermaid-svg-UsUX8hZFnV5GOIHj .node.clickable{cursor:pointer;}#mermaid-svg-UsUX8hZFnV5GOIHj .arrowheadPath{fill:#333333;}#mermaid-svg-UsUX8hZFnV5GOIHj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UsUX8hZFnV5GOIHj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UsUX8hZFnV5GOIHj .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-UsUX8hZFnV5GOIHj .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-UsUX8hZFnV5GOIHj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UsUX8hZFnV5GOIHj .cluster text{fill:#333;}#mermaid-svg-UsUX8hZFnV5GOIHj .cluster span{color:#333;}#mermaid-svg-UsUX8hZFnV5GOIHj 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-UsUX8hZFnV5GOIHj :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

框架对比

Spring AI Alibaba

LangChain4j

原生API调用

Spring生态集成

阿里云深度优化

企业级特性

功能最全面

社区活跃

20+模型支持

灵活性最高

学习成本高

开发效率低

选择建议:

场景推荐框架理由
使用阿里云服务 Spring AI Alibaba 深度优化,文档完善
Spring Boot项目 Spring AI Alibaba 无缝集成,开箱即用
需要多模型切换 LangChain4j 支持模型最多
复杂Agent应用 LangChain4j Agent功能更强
企业级应用 Spring AI Alibaba 企业特性丰富

2. 核心架构与设计理念

2.1 三层架构设计

Spring AI Alibaba采用分层架构,从底层到高层提供渐进式的抽象:

#mermaid-svg-fNjJHclyHQRHAC4h {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-fNjJHclyHQRHAC4h .error-icon{fill:#552222;}#mermaid-svg-fNjJHclyHQRHAC4h .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fNjJHclyHQRHAC4h .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-fNjJHclyHQRHAC4h .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fNjJHclyHQRHAC4h .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fNjJHclyHQRHAC4h .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fNjJHclyHQRHAC4h .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fNjJHclyHQRHAC4h .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fNjJHclyHQRHAC4h .marker.cross{stroke:#333333;}#mermaid-svg-fNjJHclyHQRHAC4h svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fNjJHclyHQRHAC4h .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-fNjJHclyHQRHAC4h .cluster-label text{fill:#333;}#mermaid-svg-fNjJHclyHQRHAC4h .cluster-label span{color:#333;}#mermaid-svg-fNjJHclyHQRHAC4h .label text,#mermaid-svg-fNjJHclyHQRHAC4h span{fill:#333;color:#333;}#mermaid-svg-fNjJHclyHQRHAC4h .node rect,#mermaid-svg-fNjJHclyHQRHAC4h .node circle,#mermaid-svg-fNjJHclyHQRHAC4h .node ellipse,#mermaid-svg-fNjJHclyHQRHAC4h .node polygon,#mermaid-svg-fNjJHclyHQRHAC4h .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fNjJHclyHQRHAC4h .node .label{text-align:center;}#mermaid-svg-fNjJHclyHQRHAC4h .node.clickable{cursor:pointer;}#mermaid-svg-fNjJHclyHQRHAC4h .arrowheadPath{fill:#333333;}#mermaid-svg-fNjJHclyHQRHAC4h .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fNjJHclyHQRHAC4h .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fNjJHclyHQRHAC4h .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-fNjJHclyHQRHAC4h .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-fNjJHclyHQRHAC4h .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fNjJHclyHQRHAC4h .cluster text{fill:#333;}#mermaid-svg-fNjJHclyHQRHAC4h .cluster span{color:#333;}#mermaid-svg-fNjJHclyHQRHAC4h 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-fNjJHclyHQRHAC4h :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

模型服务层

增强LLM层 – Augmented LLM

Agent框架层

应用层

通义千问

OpenAI

本地Ollama

ChatClient/ChatModel

Tool工具调用

MCP协议

Message消息

Vector Store向量存储

ReactAgent

Graph工作流

智能体编排

聊天机器人

RAG问答系统

多智能体应用

工作流应用

层级说明

1. Augmented LLM(增强LLM层)

  • 提供基础抽象:ChatModel、ImageModel、AudioModel等
  • 统一的消息格式和工具调用
  • 向量存储和检索能力
  • MCP(Model Context Protocol)支持

2. Agent Framework(智能体框架层)

  • ReactAgent:基于ReAct模式的智能体
  • Graph:工作流和多智能体编排
  • 状态管理和上下文记忆

3. Application Layer(应用层)

  • 具体的AI应用实现
  • 业务逻辑集成

2.2 核心设计理念

2.2.1 统一的API抽象

#mermaid-svg-0ArVxUtsWmOsMiay {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-0ArVxUtsWmOsMiay .error-icon{fill:#552222;}#mermaid-svg-0ArVxUtsWmOsMiay .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0ArVxUtsWmOsMiay .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-0ArVxUtsWmOsMiay .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0ArVxUtsWmOsMiay .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0ArVxUtsWmOsMiay .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0ArVxUtsWmOsMiay .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0ArVxUtsWmOsMiay .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0ArVxUtsWmOsMiay .marker.cross{stroke:#333333;}#mermaid-svg-0ArVxUtsWmOsMiay svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0ArVxUtsWmOsMiay .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-0ArVxUtsWmOsMiay .cluster-label text{fill:#333;}#mermaid-svg-0ArVxUtsWmOsMiay .cluster-label span{color:#333;}#mermaid-svg-0ArVxUtsWmOsMiay .label text,#mermaid-svg-0ArVxUtsWmOsMiay span{fill:#333;color:#333;}#mermaid-svg-0ArVxUtsWmOsMiay .node rect,#mermaid-svg-0ArVxUtsWmOsMiay .node circle,#mermaid-svg-0ArVxUtsWmOsMiay .node ellipse,#mermaid-svg-0ArVxUtsWmOsMiay .node polygon,#mermaid-svg-0ArVxUtsWmOsMiay .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0ArVxUtsWmOsMiay .node .label{text-align:center;}#mermaid-svg-0ArVxUtsWmOsMiay .node.clickable{cursor:pointer;}#mermaid-svg-0ArVxUtsWmOsMiay .arrowheadPath{fill:#333333;}#mermaid-svg-0ArVxUtsWmOsMiay .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0ArVxUtsWmOsMiay .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0ArVxUtsWmOsMiay .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-0ArVxUtsWmOsMiay .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-0ArVxUtsWmOsMiay .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0ArVxUtsWmOsMiay .cluster text{fill:#333;}#mermaid-svg-0ArVxUtsWmOsMiay .cluster span{color:#333;}#mermaid-svg-0ArVxUtsWmOsMiay 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-0ArVxUtsWmOsMiay :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

开发者代码

统一接口

ChatClient

ImageClient

AudioClient

通义千问

OpenAI

其他模型

通义万相

DALL-E

通义语音

其他TTS

好处:

  • ✅ 应用代码与具体模型解耦
  • ✅ 可通过配置切换模型
  • ✅ 便于测试和开发
2.2.2 声明式编程

// 声明式配置
@Configuration
public class AiConfig {

@Bean
public ChatClient chatClient(ChatClient.Builder builder) {
return builder
.defaultSystem("你是一个专业的Java技术顾问")
.defaultOptions(ChatOptions.builder()
.temperature(0.7)
.build())
.build();
}
}

// 使用时只需注入
@Service
public class AiService {

@Resource
private ChatClient chatClient;

public String chat(String userMessage) {
return chatClient.prompt()
.user(userMessage)
.call()
.content();
}
}

2.2.3 响应式编程支持

#mermaid-svg-DInA7HjnUtXJojAg {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-DInA7HjnUtXJojAg .error-icon{fill:#552222;}#mermaid-svg-DInA7HjnUtXJojAg .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DInA7HjnUtXJojAg .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-DInA7HjnUtXJojAg .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DInA7HjnUtXJojAg .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DInA7HjnUtXJojAg .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DInA7HjnUtXJojAg .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DInA7HjnUtXJojAg .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DInA7HjnUtXJojAg .marker.cross{stroke:#333333;}#mermaid-svg-DInA7HjnUtXJojAg svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DInA7HjnUtXJojAg .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-DInA7HjnUtXJojAg text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-DInA7HjnUtXJojAg .actor-line{stroke:grey;}#mermaid-svg-DInA7HjnUtXJojAg .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-DInA7HjnUtXJojAg .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-DInA7HjnUtXJojAg #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-DInA7HjnUtXJojAg .sequenceNumber{fill:white;}#mermaid-svg-DInA7HjnUtXJojAg #sequencenumber{fill:#333;}#mermaid-svg-DInA7HjnUtXJojAg #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-DInA7HjnUtXJojAg .messageText{fill:#333;stroke:#333;}#mermaid-svg-DInA7HjnUtXJojAg .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-DInA7HjnUtXJojAg .labelText,#mermaid-svg-DInA7HjnUtXJojAg .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-DInA7HjnUtXJojAg .loopText,#mermaid-svg-DInA7HjnUtXJojAg .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-DInA7HjnUtXJojAg .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-DInA7HjnUtXJojAg .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-DInA7HjnUtXJojAg .noteText,#mermaid-svg-DInA7HjnUtXJojAg .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-DInA7HjnUtXJojAg .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-DInA7HjnUtXJojAg .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-DInA7HjnUtXJojAg .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-DInA7HjnUtXJojAg .actorPopupMenu{position:absolute;}#mermaid-svg-DInA7HjnUtXJojAg .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-DInA7HjnUtXJojAg .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-DInA7HjnUtXJojAg .actor-man circle,#mermaid-svg-DInA7HjnUtXJojAg line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-DInA7HjnUtXJojAg :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

客户端

Spring Boot应用

大模型

发送问题(SSE请求)

流式调用

Token片段

推送Token

实时显示

loop

[流式响应]

完成

关闭连接

客户端

Spring Boot应用

大模型

同步调用:

String response = chatClient.prompt()
.user("介绍Spring AI Alibaba")
.call()
.content();

流式调用:

Flux<String> responseFlux = chatClient.prompt()
.user("介绍Spring AI Alibaba")
.stream()
.content();

// 在Controller中返回SSE
@GetMapping(value = "/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.<String>builder()
.data(chunk)
.build());
}


3. 快速开始

3.1 环境准备

3.1.1 系统要求

✓ JDK 17 或更高版本
✓ Maven 3.6+ 或 Gradle 7.5+
✓ IDE:IntelliJ IDEA / Eclipse / VS Code
✓ 阿里云账号(获取API Key)

3.1.2 获取API Key

#mermaid-svg-NqfNvveyNOBQFS2x {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-NqfNvveyNOBQFS2x .error-icon{fill:#552222;}#mermaid-svg-NqfNvveyNOBQFS2x .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NqfNvveyNOBQFS2x .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-NqfNvveyNOBQFS2x .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NqfNvveyNOBQFS2x .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NqfNvveyNOBQFS2x .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NqfNvveyNOBQFS2x .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NqfNvveyNOBQFS2x .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NqfNvveyNOBQFS2x .marker.cross{stroke:#333333;}#mermaid-svg-NqfNvveyNOBQFS2x svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NqfNvveyNOBQFS2x .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-NqfNvveyNOBQFS2x .cluster-label text{fill:#333;}#mermaid-svg-NqfNvveyNOBQFS2x .cluster-label span{color:#333;}#mermaid-svg-NqfNvveyNOBQFS2x .label text,#mermaid-svg-NqfNvveyNOBQFS2x span{fill:#333;color:#333;}#mermaid-svg-NqfNvveyNOBQFS2x .node rect,#mermaid-svg-NqfNvveyNOBQFS2x .node circle,#mermaid-svg-NqfNvveyNOBQFS2x .node ellipse,#mermaid-svg-NqfNvveyNOBQFS2x .node polygon,#mermaid-svg-NqfNvveyNOBQFS2x .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NqfNvveyNOBQFS2x .node .label{text-align:center;}#mermaid-svg-NqfNvveyNOBQFS2x .node.clickable{cursor:pointer;}#mermaid-svg-NqfNvveyNOBQFS2x .arrowheadPath{fill:#333333;}#mermaid-svg-NqfNvveyNOBQFS2x .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NqfNvveyNOBQFS2x .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NqfNvveyNOBQFS2x .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-NqfNvveyNOBQFS2x .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-NqfNvveyNOBQFS2x .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NqfNvveyNOBQFS2x .cluster text{fill:#333;}#mermaid-svg-NqfNvveyNOBQFS2x .cluster span{color:#333;}#mermaid-svg-NqfNvveyNOBQFS2x 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-NqfNvveyNOBQFS2x :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

登录阿里云

进入百炼平台

开通模型服务

获取API Key

配置到应用

步骤:

  • 访问:https://dashscope.console.aliyun.com/
  • 开通百炼服务
  • 创建API Key
  • 复制保存(仅显示一次)
  • 3.2 创建Spring Boot项目

    3.2.1 使用Spring Initializr

    # 方式1:使用Spring Initializr网站
    # https://start.spring.io/

    # 方式2:使用命令行
    curl https://start.spring.io/starter.zip \\
    -d dependencies=web \\
    -d javaVersion=17 \\
    -d bootVersion=3.2.0 \\
    -d groupId=com.example \\
    -d artifactId=ai-demo \\
    -o ai-demo.zip

    unzip ai-demo.zip
    cd ai-demo

    3.2.2 添加依赖

    Maven(pom.xml):

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
    https://maven.apache.org/xsd/maven-4.0.0.xsd"
    >

    <modelVersion>4.0.0</modelVersion>

    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.0</version>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>ai-demo</artifactId>
    <version>1.0.0</version>

    <properties>
    <java.version>17</java.version>
    <spring-ai-alibaba.version>1.0.0-M2</spring-ai-alibaba.version>
    </properties>

    <dependencies>
    <!– Spring Boot Web –>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!– Spring AI Alibaba –>
    <dependency>
    <groupId>com.alibaba.cloud.ai</groupId>
    <artifactId>spring-ai-alibaba-starter</artifactId>
    <version>${spring-ai-alibaba.version}</version>
    </dependency>

    <!– Lombok(可选) –>
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
    </dependency>

    <!– 测试依赖 –>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
    </dependencies>

    <build>
    <plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
    </plugins>
    </build>
    </project>

    Gradle(build.gradle):

    plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.0'
    id 'io.spring.dependency-management' version '1.1.0'
    }

    group = 'com.example'
    version = '1.0.0'
    sourceCompatibility = '17'

    repositories {
    mavenCentral()
    }

    ext {
    springAiAlibabaVersion = '1.0.0-M2'
    }

    dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation "com.alibaba.cloud.ai:spring-ai-alibaba-starter:${springAiAlibabaVersion}"

    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    }

    tasks.named('test') {
    useJUnitPlatform()
    }

    3.3 配置应用

    3.3.1 application.yml配置

    spring:
    application:
    name: ai–demo

    ai:
    dashscope:
    # API Key配置(推荐使用环境变量)
    api-key: ${DASHSCOPE_API_KEY}

    # Chat模型配置
    chat:
    enabled: true
    options:
    model: qwen–max # 模型名称
    temperature: 0.7 # 温度参数(0-1)
    top-p: 0.9 # 采样参数
    max-tokens: 2000 # 最大Token数

    # Image模型配置
    image:
    enabled: true
    options:
    model: wanx–v1

    # Embedding配置
    embedding:
    enabled: true
    options:
    model: text–embedding–v2

    server:
    port: 8080

    # 日志配置
    logging:
    level:
    com.alibaba.cloud.ai: DEBUG

    3.3.2 环境变量配置

    # 方式1:在IDE中配置环境变量
    # DASHSCOPE_API_KEY=your-api-key-here

    # 方式2:使用.env文件(需要spring-dotenv依赖)
    # .env文件内容:
    DASHSCOPE_API_KEY=sk-xxxxxxxxxxxxx

    # 方式3:在启动脚本中设置
    export DASHSCOPE_API_KEY=sk-xxxxxxxxxxxxx
    java -jar ai-demo.jar

    3.4 第一个AI应用

    3.4.1 创建Controller

    package com.example.aidemo.controller;

    import org.springframework.ai.chat.client.ChatClient;
    import org.springframework.web.bind.annotation.*;
    import reactor.core.publisher.Flux;
    import org.springframework.http.MediaType;

    @RestController
    @RequestMapping("/api/chat")
    public class ChatController {

    private final ChatClient chatClient;

    // 构造器注入
    public ChatController(ChatClient.Builder chatClientBuilder) {
    this.chatClient = chatClientBuilder.build();
    }

    /**
    * 简单对话 – 同步
    */

    @GetMapping("/simple")
    public String simpleChat(@RequestParam String message) {
    return chatClient.prompt()
    .user(message)
    .call()
    .content();
    }

    /**
    * 流式对话 – SSE
    */

    @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> streamChat(@RequestParam String message) {
    return chatClient.prompt()
    .user(message)
    .stream()
    .content();
    }

    /**
    * 带系统提示词的对话
    */

    @PostMapping("/chat")
    public String chat(@RequestBody ChatRequest request) {
    return chatClient.prompt()
    .system("你是一个专业的Java技术顾问,擅长Spring框架")
    .user(request.getMessage())
    .call()
    .content();
    }
    }

    // DTO类
    record ChatRequest(String message) {}

    3.4.2 启动应用

    package com.example.aidemo;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;

    @SpringBootApplication
    public class AiDemoApplication {

    public static void main(String[] args) {
    SpringApplication.run(AiDemoApplication.class, args);
    }
    }

    3.4.3 测试API

    # 1. 简单对话
    curl "http://localhost:8080/api/chat/simple?message=你好"

    # 响应:
    你好!很高兴为您服务。请问有什么我可以帮助您的吗?

    # 2. 流式对话
    curl "http://localhost:8080/api/chat/stream?message=介绍Spring%20AI%20Alibaba"

    # 响应(流式):
    Spring AI Alibaba 是...
    阿里云基于...
    主要特性包括...

    # 3. POST请求
    curl -X POST http://localhost:8080/api/chat/chat \\
    -H "Content-Type: application/json" \\
    -d '{"message":"如何使用Spring Boot开发微服务?"}'

    3.5 项目结构

    ai-demo/
    ├── src/
    │ ├── main/
    │ │ ├── java/com/example/aidemo/
    │ │ │ ├── AiDemoApplication.java # 启动类
    │ │ │ ├── controller/
    │ │ │ │ └── ChatController.java # 控制器
    │ │ │ ├── service/
    │ │ │ │ └── AiService.java # 服务层
    │ │ │ ├── config/
    │ │ │ │ └── AiConfig.java # 配置类
    │ │ │ └── dto/
    │ │ │ └── ChatRequest.java # DTO
    │ │ └── resources/
    │ │ ├── application.yml # 配置文件
    │ │ └── logback-spring.xml # 日志配置
    │ └── test/
    │ └── java/com/example/aidemo/
    │ └── AiDemoApplicationTests.java
    ├── pom.xml # Maven配置
    └── README.md


    4. 核心组件详解

    4.1 ChatClient – 对话客户端

    4.1.1 ChatClient架构

    在这里插入图片描述

    4.1.2 基础使用

    1. 简单对话

    @Service
    public class SimpleAiService {

    @Resource
    private ChatClient chatClient;

    /**
    * 最简单的使用方式
    */

    public String chat(String userMessage) {
    return chatClient.prompt()
    .user(userMessage)
    .call()
    .content();
    }
    }

    2. 带系统提示词

    /**
    * 设置系统角色和行为
    */

    public String chatWithSystem(String userMessage) {
    return chatClient.prompt()
    .system("""
    你是一个资深的Java架构师,擅长:
    1. Spring生态系统
    2. 微服务架构设计
    3. 性能优化
    请用专业但易懂的方式回答问题。
    """
    )
    .user(userMessage)
    .call()
    .content();
    }

    3. 自定义模型参数

    /**
    * 动态调整模型参数
    */

    public String chatWithOptions(String userMessage, double temperature) {
    return chatClient.prompt()
    .user(userMessage)
    .options(DashScopeChatOptions.builder()
    .temperature(temperature) // 温度:控制随机性
    .topP(0.9) // Top-P采样
    .maxTokens(1000) // 最大Token数
    .build())
    .call()
    .content();
    }

    参数说明:

    参数范围说明推荐值
    temperature 0.0 – 1.0 控制随机性,越高越发散 创意任务:0.8-1.0事实任务:0.1-0.3
    top-p 0.0 – 1.0 核采样参数 0.9(默认)
    max-tokens 1 – 6000 限制响应长度 根据需求设置
    4.1.3 获取完整响应

    /**
    * 获取包含元数据的完整响应
    */

    public void chatWithFullResponse(String userMessage) {
    ChatResponse response = chatClient.prompt()
    .user(userMessage)
    .call()
    .chatResponse();

    // 获取内容
    String content = response.getResult().getOutput().getContent();

    // 获取元数据
    ChatResponseMetadata metadata = response.getMetadata();
    System.out.println("模型: " + metadata.getModel());
    System.out.println("使用Token: " + metadata.getUsage().getTotalTokens());
    System.out.println("完成原因: " + response.getResult().getMetadata().getFinishReason());

    // 输出示例:
    // 模型: qwen-max
    // 使用Token: 156
    // 完成原因: STOP
    }

    4.1.4 流式响应详解

    流式响应是AI应用中非常重要的特性,它能够让用户实时看到生成的内容,而不是等待完整响应后才显示,极大提升了用户体验。

    流式响应的优势

    #mermaid-svg-z55v4etRKWtYjDxm {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-z55v4etRKWtYjDxm .error-icon{fill:#552222;}#mermaid-svg-z55v4etRKWtYjDxm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-z55v4etRKWtYjDxm .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-z55v4etRKWtYjDxm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-z55v4etRKWtYjDxm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-z55v4etRKWtYjDxm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-z55v4etRKWtYjDxm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-z55v4etRKWtYjDxm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-z55v4etRKWtYjDxm .marker.cross{stroke:#333333;}#mermaid-svg-z55v4etRKWtYjDxm svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-z55v4etRKWtYjDxm .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-z55v4etRKWtYjDxm .cluster-label text{fill:#333;}#mermaid-svg-z55v4etRKWtYjDxm .cluster-label span{color:#333;}#mermaid-svg-z55v4etRKWtYjDxm .label text,#mermaid-svg-z55v4etRKWtYjDxm span{fill:#333;color:#333;}#mermaid-svg-z55v4etRKWtYjDxm .node rect,#mermaid-svg-z55v4etRKWtYjDxm .node circle,#mermaid-svg-z55v4etRKWtYjDxm .node ellipse,#mermaid-svg-z55v4etRKWtYjDxm .node polygon,#mermaid-svg-z55v4etRKWtYjDxm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-z55v4etRKWtYjDxm .node .label{text-align:center;}#mermaid-svg-z55v4etRKWtYjDxm .node.clickable{cursor:pointer;}#mermaid-svg-z55v4etRKWtYjDxm .arrowheadPath{fill:#333333;}#mermaid-svg-z55v4etRKWtYjDxm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-z55v4etRKWtYjDxm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-z55v4etRKWtYjDxm .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-z55v4etRKWtYjDxm .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-z55v4etRKWtYjDxm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-z55v4etRKWtYjDxm .cluster text{fill:#333;}#mermaid-svg-z55v4etRKWtYjDxm .cluster span{color:#333;}#mermaid-svg-z55v4etRKWtYjDxm 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-z55v4etRKWtYjDxm :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    流式响应优势

    用户体验

    系统性能

    错误处理

    实时反馈

    降低等待焦虑

    类似打字效果

    降低内存占用

    减少首字节延迟

    支持长文本生成

    早期发现问题

    可中断处理

    渐进式错误恢复

    Flux响应式编程基础

    Spring AI Alibaba基于Spring WebFlux和Project Reactor实现流式响应,核心是Flux类型。

    Flux vs Mono vs 传统返回值:

    在这里插入图片描述

    基础示例:

    @Service
    public class StreamingService {

    @Resource
    private ChatClient chatClient;

    /**
    * 方式1:返回Flux<String> – 纯文本流
    */

    public Flux<String> streamChat(String userMessage) {
    return chatClient.prompt()
    .user(userMessage)
    .stream()
    .content(); // 返回内容流
    }

    /**
    * 方式2:返回Flux<ChatResponse> – 完整响应流
    */

    public Flux<ChatResponse> streamChatResponse(String userMessage) {
    return chatClient.prompt()
    .user(userMessage)
    .stream()
    .chatResponse(); // 返回完整响应流(包含元数据)
    }
    }

    SSE(Server-Sent Events)实现

    SSE是HTML5标准,用于服务器向浏览器推送实时数据。

    SSE vs WebSocket:

    特性SSEWebSocket
    通信方向 单向(服务器→客户端) 双向
    协议 HTTP 独立协议(升级自HTTP)
    实现复杂度 简单 复杂
    自动重连 ✅ 支持 ❌ 需手动实现
    事件ID ✅ 支持 ❌ 不支持
    适用场景 AI流式输出、实时通知 聊天、游戏

    完整的SSE实现(推荐方式):

    @RestController
    @RequestMapping("/api/chat")
    @Slf4j
    public class StreamChatController {

    @Resource
    private ChatClient chatClient;

    /**
    * 方式1:简单SSE – 只返回文本
    */

    @GetMapping(value = "/stream/simple", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> streamSimple(@RequestParam String message) {
    log.info("开始流式响应: {}", message);

    return chatClient.prompt()
    .user(message)
    .stream()
    .content()
    .doOnNext(chunk -> log.debug("生成chunk: {}", chunk))
    .doOnComplete(() -> log.info("流式响应完成"))
    .doOnError(e -> log.error("流式响应错误", e));
    }

    /**
    * 方式2:标准SSE – 使用ServerSentEvent包装
    * 推荐:支持事件ID、事件类型、重连时间等
    */

    @GetMapping(value = "/stream/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> streamSSE(@RequestParam String message) {
    AtomicLong counter = new AtomicLong(0);

    return chatClient.prompt()
    .user(message)
    .stream()
    .content()
    .map(chunk -> ServerSentEvent.<String>builder()
    .id(String.valueOf(counter.incrementAndGet())) // 事件ID
    .event("message") // 事件类型
    .data(chunk) // 数据
    .comment("AI生成内容") // 注释
    .build())
    .concatWith(Flux.just(
    ServerSentEvent.<String>builder()
    .event("done") // 完成事件
    .data("[DONE]")
    .build()
    ));
    }

    /**
    * 方式3:带元数据的SSE – 返回JSON格式
    */

    @GetMapping(value = "/stream/json", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<StreamResponse>> streamJSON(@RequestParam String message) {
    AtomicLong tokenCount = new AtomicLong(0);
    AtomicLong chunkIndex = new AtomicLong(0);

    return chatClient.prompt()
    .user(message)
    .stream()
    .chatResponse() // 获取完整响应
    .map(response -> {
    String content = response.getResult().getOutput().getContent();
    long tokens = response.getMetadata().getUsage() != null
    ? response.getMetadata().getUsage().getTotalTokens()
    : 0;

    tokenCount.addAndGet(tokens);

    StreamResponse streamResp = StreamResponse.builder()
    .index(chunkIndex.incrementAndGet())
    .content(content)
    .totalTokens(tokenCount.get())
    .model(response.getMetadata().getModel())
    .finishReason(response.getResult().getMetadata().getFinishReason())
    .build();

    return ServerSentEvent.<StreamResponse>builder()
    .id(String.valueOf(chunkIndex.get()))
    .event("stream")
    .data(streamResp)
    .build();
    });
    }

    /**
    * 方式4:多会话流式 – 支持上下文
    */

    @PostMapping(value = "/stream/conversation", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> streamConversation(
    @RequestBody ConversationRequest request) {

    // 构建消息历史
    List<Message> messages = buildMessages(request);

    return chatClient.prompt()
    .messages(messages)
    .stream()
    .content()
    .map(chunk -> ServerSentEvent.<String>builder()
    .data(chunk)
    .build());
    }

    /**
    * 方式5:流式响应 + 数据库保存
    */

    @GetMapping(value = "/stream/persist", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> streamWithPersist(
    @RequestParam String message,
    @RequestParam String sessionId) {

    StringBuilder fullResponse = new StringBuilder();

    return chatClient.prompt()
    .user(message)
    .stream()
    .content()
    .doOnNext(fullResponse::append) // 收集完整响应
    .map(chunk -> ServerSentEvent.<String>builder()
    .data(chunk)
    .build())
    .doOnComplete(() -> {
    // 流式完成后保存到数据库
    saveConversation(sessionId, message, fullResponse.toString());
    });
    }

    // 辅助方法
    private List<Message> buildMessages(ConversationRequest request) {
    List<Message> messages = new ArrayList<>();
    // 添加历史消息
    request.getHistory().forEach(msg ->
    messages.add(msg.getRole().equals("user")
    ? new UserMessage(msg.getContent())
    : new AssistantMessage(msg.getContent()))
    );
    // 添加当前消息
    messages.add(new UserMessage(request.getMessage()));
    return messages;
    }

    private void saveConversation(String sessionId, String question, String answer) {
    log.info("保存会话: session={}, Q={}, A={}", sessionId, question, answer);
    // 实际保存逻辑
    }
    }

    // DTO类
    @Data
    @Builder
    class StreamResponse {
    private Long index;
    private String content;
    private Long totalTokens;
    private String model;
    private String finishReason;
    }

    @Data
    class ConversationRequest {
    private String message;
    private List<HistoryMessage> history;
    }

    @Data
    class HistoryMessage {
    private String role;
    private String content;
    }

    前端集成示例

    1. 原生JavaScript(EventSource):

    // 简单文本流
    const eventSource = new EventSource('/api/chat/stream/simple?message=介绍Spring AI');

    eventSource.onmessage = (event) => {
    document.getElementById('response').innerText += event.data;
    };

    eventSource.onerror = (error) => {
    console.error('SSE错误:', error);
    eventSource.close();
    };

    // 关闭连接
    // eventSource.close();

    2. 标准SSE(支持事件类型):

    const eventSource = new EventSource('/api/chat/stream/sse?message=你好');

    // 监听message事件
    eventSource.addEventListener('message', (event) => {
    const data = event.data;
    document.getElementById('response').innerText += data;
    });

    // 监听done事件
    eventSource.addEventListener('done', (event) => {
    console.log('生成完成');
    eventSource.close();
    document.getElementById('status').innerText = '完成';
    });

    // 错误处理
    eventSource.onerror = (error) => {
    console.error('连接错误:', error);
    eventSource.close();
    };

    3. 使用Fetch API(更灵活):

    async function streamChat(message) {
    const response = await fetch(`/api/chat/stream/simple?message=${encodeURIComponent(message)}`);
    const reader = response.body.getReader();
    const decoder = new TextDecoder();

    while (true) {
    const { done, value } = await reader.read();

    if (done) {
    console.log('流式响应完成');
    break;
    }

    const chunk = decoder.decode(value, { stream: true });
    document.getElementById('response').innerText += chunk;
    }
    }

    // 使用
    streamChat('介绍Spring AI Alibaba');

    4. React集成示例:

    import { useState, useEffect } from 'react';

    function StreamChat() {
    const [message, setMessage] = useState('');
    const [response, setResponse] = useState('');
    const [loading, setLoading] = useState(false);

    const handleStream = () => {
    setLoading(true);
    setResponse('');

    const eventSource = new EventSource(
    `/api/chat/stream/sse?message=${encodeURIComponent(message)}`
    );

    eventSource.addEventListener('message', (event) => {
    setResponse(prev => prev + event.data);
    });

    eventSource.addEventListener('done', () => {
    eventSource.close();
    setLoading(false);
    });

    eventSource.onerror = () => {
    eventSource.close();
    setLoading(false);
    };
    };

    return (
    <div>
    <input
    value={message}
    onChange={e => setMessage(e.target.value)}
    placeholder="输入问题"
    />
    <button onClick={handleStream} disabled={loading}>
    {loading ? '生成中…' : '发送'}
    </button>
    <div className="response">
    {response}
    </div>
    </div>
    );
    }

    流式响应高级特性

    1. 背压处理(Backpressure)

    /**
    * 控制流速,避免客户端处理不过来
    */

    @GetMapping(value = "/stream/backpressure", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> streamWithBackpressure(@RequestParam String message) {
    return chatClient.prompt()
    .user(message)
    .stream()
    .content()
    .delayElements(Duration.ofMillis(50)) // 延迟50ms,控制速度
    .onBackpressureBuffer(100) // 缓冲100个元素
    .map(chunk -> ServerSentEvent.<String>builder()
    .data(chunk)
    .build());
    }

    2. 超时处理

    /**
    * 设置超时时间
    */

    @GetMapping(value = "/stream/timeout", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> streamWithTimeout(@RequestParam String message) {
    return chatClient.prompt()
    .user(message)
    .stream()
    .content()
    .timeout(Duration.ofSeconds(30)) // 30秒超时
    .onErrorResume(TimeoutException.class, e ->
    Flux.just("[错误: 请求超时]")
    )
    .map(chunk -> ServerSentEvent.<String>builder()
    .data(chunk)
    .build());
    }

    3. 错误处理和重试

    /**
    * 完善的错误处理
    */

    @GetMapping(value = "/stream/resilient", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> streamResilient(@RequestParam String message) {
    return chatClient.prompt()
    .user(message)
    .stream()
    .content()
    .retryWhen(Retry.backoff(3, Duration.ofSeconds(1)) // 重试3次
    .filter(throwable -> throwable instanceof IOException))
    .onErrorResume(throwable -> {
    log.error("流式响应错误", throwable);
    return Flux.just("[错误: " + throwable.getMessage() + "]");
    })
    .map(chunk -> ServerSentEvent.<String>builder()
    .data(chunk)
    .build());
    }

    4. 多路复用(组合多个流)

    /**
    * 同时调用多个模型,合并结果
    */

    @GetMapping(value = "/stream/multiplex", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<MultiModelResponse>> streamMultiModel(@RequestParam String message) {
    // 模型1:快速模型
    Flux<String> stream1 = chatClient.prompt()
    .user(message)
    .options(DashScopeChatOptions.builder().model("qwen-turbo").build())
    .stream()
    .content()
    .map(chunk -> new MultiModelResponse("qwen-turbo", chunk));

    // 模型2:高质量模型
    Flux<String> stream2 = chatClient.prompt()
    .user(message)
    .options(DashScopeChatOptions.builder().model("qwen-max").build())
    .stream()
    .content()
    .map(chunk -> new MultiModelResponse("qwen-max", chunk));

    // 合并两个流
    return Flux.merge(stream1, stream2)
    .map(resp -> ServerSentEvent.<MultiModelResponse>builder()
    .data(resp)
    .build());
    }

    @Data
    @AllArgsConstructor
    class MultiModelResponse {
    private String model;
    private String content;
    }

    实现方式对比总结
    方式优点缺点适用场景
    Flux<String> 简单,性能好 无元数据,无事件控制 纯文本流式输出
    Flux<ServerSentEvent<String>> 标准SSE,支持事件ID 略复杂 生产环境推荐
    Flux<ChatResponse> 包含完整元数据 数据量大 需要Token统计等场景
    WebSocket 双向通信 实现复杂 实时交互应用

    最佳实践推荐:

    #mermaid-svg-Xb7e7wO8o8Cp9f8L {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .error-icon{fill:#552222;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .marker.cross{stroke:#333333;}#mermaid-svg-Xb7e7wO8o8Cp9f8L svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .cluster-label text{fill:#333;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .cluster-label span{color:#333;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .label text,#mermaid-svg-Xb7e7wO8o8Cp9f8L span{fill:#333;color:#333;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .node rect,#mermaid-svg-Xb7e7wO8o8Cp9f8L .node circle,#mermaid-svg-Xb7e7wO8o8Cp9f8L .node ellipse,#mermaid-svg-Xb7e7wO8o8Cp9f8L .node polygon,#mermaid-svg-Xb7e7wO8o8Cp9f8L .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .node .label{text-align:center;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .node.clickable{cursor:pointer;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .arrowheadPath{fill:#333333;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .cluster text{fill:#333;}#mermaid-svg-Xb7e7wO8o8Cp9f8L .cluster span{color:#333;}#mermaid-svg-Xb7e7wO8o8Cp9f8L 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-Xb7e7wO8o8Cp9f8L :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    是

    否

    是

    否

    是

    否

    选择流式方式

    需要双向通信?

    使用WebSocket

    需要元数据?

    Flux>

    简单演示?

    Flux

    生产环境推荐

    原型开发

    复杂交互场景

    性能对比:

    /**
    * 性能测试对比
    */

    @RestController
    @RequestMapping("/api/benchmark")
    public class StreamBenchmarkController {

    @Resource
    private ChatClient chatClient;

    /**
    * 同步调用(基准)
    */

    @GetMapping("/sync")
    public String syncCall(@RequestParam String message) {
    long start = System.currentTimeMillis();
    String result = chatClient.prompt()
    .user(message)
    .call()
    .content();
    long duration = System.currentTimeMillis() – start;
    log.info("同步调用耗时: {}ms", duration);
    return result;
    }

    /**
    * 流式调用
    */

    @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<BenchmarkData>> streamCall(@RequestParam String message) {
    long start = System.currentTimeMillis();
    AtomicLong firstChunkTime = new AtomicLong(0);
    AtomicInteger chunkCount = new AtomicInteger(0);

    return chatClient.prompt()
    .user(message)
    .stream()
    .content()
    .doOnNext(chunk -> {
    if (firstChunkTime.get() == 0) {
    firstChunkTime.set(System.currentTimeMillis() – start);
    log.info("首个chunk延迟: {}ms", firstChunkTime.get());
    }
    chunkCount.incrementAndGet();
    })
    .doOnComplete(() -> {
    long total = System.currentTimeMillis() – start;
    log.info("流式调用总耗时: {}ms, chunk数: {}", total, chunkCount.get());
    })
    .map(chunk -> ServerSentEvent.<BenchmarkData>builder()
    .data(new BenchmarkData(
    chunk,
    firstChunkTime.get(),
    System.currentTimeMillis() – start
    ))
    .build());
    }
    }

    @Data
    @AllArgsConstructor
    class BenchmarkData {
    private String content;
    private Long firstChunkLatency;
    private Long currentLatency;
    }

    典型性能指标:

    同步调用:
    – 首字节延迟(TTFB): 2000-5000ms
    – 总耗时: 3000-10000ms
    – 用户体验: 需等待完成后才看到内容

    流式调用:
    – 首字节延迟(TTFB): 200-500ms ⚡
    – 总耗时: 3000-10000ms(相同)
    – 用户体验: 立即开始显示内容 ✨
    – 优势: 降低感知延迟80%+

    4.1.5 多轮对话

    @Service
    public class ConversationService {

    @Resource
    private ChatClient chatClient;

    /**
    * 多轮对话实现
    */

    public String multiTurnChat(String userMessage, List<Message> history) {
    // 构建消息列表
    List<Message> messages = new ArrayList<>(history);
    messages.add(new UserMessage(userMessage));

    return chatClient.prompt()
    .messages(messages)
    .call()
    .content();
    }
    }

    // 使用示例
    List<Message> history = new ArrayList<>();
    history.add(new UserMessage("我想学习Spring AI"));
    history.add(new AssistantMessage("很好!Spring AI是…"));

    String response = conversationService.multiTurnChat(
    "那我应该从哪里开始?",
    history
    );

    4.1.6 ChatClient配置

    @Configuration
    public class ChatClientConfig {

    /**
    * 自定义ChatClient Bean
    */

    @Bean
    public ChatClient chatClient(ChatClient.Builder builder) {
    return builder
    // 默认系统提示词
    .defaultSystem("""
    你是一个专业的AI助手。
    回答要准确、简洁、专业。
    """
    )
    // 默认选项
    .defaultOptions(DashScopeChatOptions.builder()
    .model("qwen-max")
    .temperature(0.7)
    .build())
    // 默认函数(后面会讲)
    .defaultFunctions("weatherFunction")
    .build();
    }

    /**
    * 创建专门用途的ChatClient
    */

    @Bean
    @Qualifier("codingAssistant")
    public ChatClient codingAssistantClient(ChatClient.Builder builder) {
    return builder
    .defaultSystem("你是一个Java代码助手,专注于Spring Boot开发")
    .defaultOptions(DashScopeChatOptions.builder()
    .temperature(0.3) // 代码生成用较低温度
    .build())
    .build();
    }
    }

    4.2 ImageClient – 图像生成

    4.2.1 基础使用

    @Service
    public class ImageService {

    @Resource
    private ImageClient imageClient;

    /**
    * 文生图
    */

    public String generateImage(String prompt) {
    ImageResponse response = imageClient.call(
    new ImagePrompt(prompt,
    DashScopeImageOptions.builder()
    .model("wanx-v1") // 通义万相模型
    .n(1) // 生成1张图片
    .width(1024) // 宽度
    .height(1024) // 高度
    .build())
    );

    // 获取图片URL
    return response.getResult().getOutput().getUrl();
    }

    /**
    * 生成多张图片
    */

    public List<String> generateMultipleImages(String prompt, int count) {
    ImageResponse response = imageClient.call(
    new ImagePrompt(prompt,
    DashScopeImageOptions.builder()
    .model("wanx-v1")
    .n(count) // 生成多张
    .build())
    );

    return response.getResults().stream()
    .map(result -> result.getOutput().getUrl())
    .collect(Collectors.toList());
    }
    }

    4.2.2 Controller实现

    @RestController
    @RequestMapping("/api/image")
    public class ImageController {

    @Resource
    private ImageService imageService;

    @PostMapping("/generate")
    public Result<ImageResult> generateImage(@RequestBody ImageRequest request) {
    String imageUrl = imageService.generateImage(request.getPrompt());

    return Result.success(ImageResult.builder()
    .url(imageUrl)
    .prompt(request.getPrompt())
    .build());
    }
    }

    // DTO
    record ImageRequest(String prompt) {}

    @Builder
    record ImageResult(String url, String prompt) {}

    4.3 AudioClient – 语音合成

    4.3.1 文字转语音

    @Service
    public class AudioService {

    @Resource
    private AudioTranscriptionClient transcriptionClient;

    @Resource
    private AudioSpeechClient speechClient;

    /**
    * 文字转语音
    */

    public byte[] textToSpeech(String text) {
    SpeechPrompt prompt = new SpeechPrompt(text,
    DashScopeAudioSpeechOptions.builder()
    .model("sambert-zhichu-v1") // 语音模型
    .voice("zhixiaoxia") // 音色
    .build());

    SpeechResponse response = speechClient.call(prompt);
    return response.getResult().getOutput();
    }

    /**
    * 语音转文字
    */

    public String speechToText(byte[] audioData) {
    AudioTranscriptionPrompt prompt = new AudioTranscriptionPrompt(audioData);
    AudioTranscriptionResponse response = transcriptionClient.call(prompt);
    return response.getResult().getOutput();
    }
    }

    4.4 EmbeddingClient – 向量化

    4.4.1 文本向量化

    @Service
    public class EmbeddingService {

    @Resource
    private EmbeddingClient embeddingClient;

    /**
    * 单个文本向量化
    */

    public List<Double> embed(String text) {
    EmbeddingResponse response = embeddingClient.embedForResponse(
    Collections.singletonList(text)
    );

    return response.getResult().getOutput();
    }

    /**
    * 批量文本向量化
    */

    public List<List<Double>> embedBatch(List<String> texts) {
    EmbeddingResponse response = embeddingClient.embedForResponse(texts);

    return response.getResults().stream()
    .map(result -> result.getOutput())
    .collect(Collectors.toList());
    }

    /**
    * 计算文本相似度
    */

    public double calculateSimilarity(String text1, String text2) {
    List<Double> embedding1 = embed(text1);
    List<Double> embedding2 = embed(text2);

    return cosineSimilarity(embedding1, embedding2);
    }

    /**
    * 余弦相似度计算
    */

    private double cosineSimilarity(List<Double> v1, List<Double> v2) {
    double dotProduct = 0.0;
    double norm1 = 0.0;
    double norm2 = 0.0;

    for (int i = 0; i < v1.size(); i++) {
    dotProduct += v1.get(i) * v2.get(i);
    norm1 += Math.pow(v1.get(i), 2);
    norm2 += Math.pow(v2.get(i), 2);
    }

    return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
    }
    }


    5. 高级特性

    5.1 RAG(检索增强生成)

    5.1.1 RAG架构

    #mermaid-svg-L5si6q8cEnLQfQU0 {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-L5si6q8cEnLQfQU0 .error-icon{fill:#552222;}#mermaid-svg-L5si6q8cEnLQfQU0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-L5si6q8cEnLQfQU0 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-L5si6q8cEnLQfQU0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-L5si6q8cEnLQfQU0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-L5si6q8cEnLQfQU0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-L5si6q8cEnLQfQU0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-L5si6q8cEnLQfQU0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-L5si6q8cEnLQfQU0 .marker.cross{stroke:#333333;}#mermaid-svg-L5si6q8cEnLQfQU0 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-L5si6q8cEnLQfQU0 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-L5si6q8cEnLQfQU0 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-L5si6q8cEnLQfQU0 .actor-line{stroke:grey;}#mermaid-svg-L5si6q8cEnLQfQU0 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-L5si6q8cEnLQfQU0 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-L5si6q8cEnLQfQU0 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-L5si6q8cEnLQfQU0 .sequenceNumber{fill:white;}#mermaid-svg-L5si6q8cEnLQfQU0 #sequencenumber{fill:#333;}#mermaid-svg-L5si6q8cEnLQfQU0 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-L5si6q8cEnLQfQU0 .messageText{fill:#333;stroke:#333;}#mermaid-svg-L5si6q8cEnLQfQU0 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-L5si6q8cEnLQfQU0 .labelText,#mermaid-svg-L5si6q8cEnLQfQU0 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-L5si6q8cEnLQfQU0 .loopText,#mermaid-svg-L5si6q8cEnLQfQU0 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-L5si6q8cEnLQfQU0 .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-L5si6q8cEnLQfQU0 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-L5si6q8cEnLQfQU0 .noteText,#mermaid-svg-L5si6q8cEnLQfQU0 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-L5si6q8cEnLQfQU0 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-L5si6q8cEnLQfQU0 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-L5si6q8cEnLQfQU0 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-L5si6q8cEnLQfQU0 .actorPopupMenu{position:absolute;}#mermaid-svg-L5si6q8cEnLQfQU0 .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-L5si6q8cEnLQfQU0 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-L5si6q8cEnLQfQU0 .actor-man circle,#mermaid-svg-L5si6q8cEnLQfQU0 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-L5si6q8cEnLQfQU0 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    用户

    应用服务

    向量数据库

    大模型

    提问

    向量化问题

    检索相似文档

    Top-K文档

    构建增强Prompt

    调用模型(问题+文档)

    生成答案

    返回答案+来源

    用户

    应用服务

    向量数据库

    大模型

    5.1.2 数据准备

    1. 文档加载和切分

    @Service
    public class DocumentService {

    /**
    * 加载PDF文档
    */

    public List<Document> loadPdfDocuments(String filePath) {
    // 使用PDF文档加载器
    PdfDocumentReader reader = new PdfDocumentReader(
    new FileSystemResource(filePath)
    );

    return reader.get();
    }

    /**
    * 文档分块
    */

    public List<Document> splitDocuments(List<Document> documents) {
    TokenTextSplitter splitter = new TokenTextSplitter(
    500, // chunk size
    50, // overlap
    5, // min chunk size
    10000 // max chunk size
    );

    return splitter.apply(documents);
    }
    }

    2. 向量存储

    @Configuration
    public class VectorStoreConfig {

    /**
    * 配置Elasticsearch向量存储
    */

    @Bean
    public VectorStore vectorStore(
    EmbeddingClient embeddingClient,
    RestClient restClient) {

    return new ElasticsearchVectorStore(
    restClient,
    embeddingClient
    );
    }

    /**
    * 配置Redis向量存储
    */

    @Bean
    public VectorStore redisVectorStore(
    EmbeddingClient embeddingClient,
    RedisVectorStoreConfig config) {

    return new RedisVectorStore(config, embeddingClient);
    }
    }

    3. 文档导入

    @Service
    public class DocumentImportService {

    @Resource
    private DocumentService documentService;

    @Resource
    private VectorStore vectorStore;

    /**
    * 导入文档到向量库
    */

    public void importDocuments(String filePath) {
    // 1. 加载文档
    List<Document> documents = documentService.loadPdfDocuments(filePath);

    // 2. 分块
    List<Document> chunks = documentService.splitDocuments(documents);

    // 3. 存入向量库
    vectorStore.add(chunks);

    log.info("导入{}个文档片段到向量库", chunks.size());
    }
    }

    5.1.3 RAG查询

    @Service
    public class RagService {

    @Resource
    private ChatClient chatClient;

    @Resource
    private VectorStore vectorStore;

    /**
    * RAG问答
    */

    public String ragQuery(String question) {
    // 1. 向量检索相关文档
    List<Document> relevantDocs = vectorStore.similaritySearch(
    SearchRequest.query(question)
    .withTopK(5) // 检索Top 5
    .withSimilarityThreshold(0.7) // 相似度阈值
    );

    // 2. 构建上下文
    String context = relevantDocs.stream()
    .map(Document::getContent)
    .collect(Collectors.joining("\\n\\n—\\n\\n"));

    // 3. 使用QuestionAnswerAdvisor
    return chatClient.prompt()
    .user(question)
    .advisors(new QuestionAnswerAdvisor(vectorStore))
    .call()
    .content();
    }

    /**
    * RAG问答(手动构建Prompt)
    */

    public RagResponse ragQueryManual(String question) {
    // 检索文档
    List<Document> docs = vectorStore.similaritySearch(
    SearchRequest.query(question).withTopK(5)
    );

    // 手动构建Prompt
    String prompt = String.format("""
    # 任务
    基于以下文档内容回答用户问题。

    # 文档内容
    %s

    # 用户问题
    %s

    # 要求
    1. 仅基于提供的文档内容回答
    2. 如果文档中没有相关信息,明确说明
    3. 给出文档引用
    """,
    buildContext(docs),
    question
    );

    String answer = chatClient.prompt()
    .user(prompt)
    .call()
    .content();

    return RagResponse.builder()
    .answer(answer)
    .sources(extractSources(docs))
    .build();
    }

    private String buildContext(List<Document> docs) {
    return docs.stream()
    .map(doc -> String.format("【来源:%s】\\n%s",
    doc.getMetadata().get("source"),
    doc.getContent()))
    .collect(Collectors.joining("\\n\\n—\\n\\n"));
    }

    private List<DocumentSource> extractSources(List<Document> docs) {
    return docs.stream()
    .map(doc -> DocumentSource.builder()
    .source(doc.getMetadata().get("source"))
    .snippet(doc.getContent().substring(0, Math.min(200, doc.getContent().length())))
    .build())
    .collect(Collectors.toList());
    }
    }

    @Builder
    record RagResponse(String answer, List<DocumentSource> sources) {}

    @Builder
    record DocumentSource(String source, String snippet) {}

    5.1.4 使用QuestionAnswerAdvisor

    @Configuration
    public class RagConfig {

    @Bean
    public QuestionAnswerAdvisor questionAnswerAdvisor(VectorStore vectorStore) {
    return new QuestionAnswerAdvisor(vectorStore,
    SearchRequest.defaults()
    .withTopK(5)
    .withSimilarityThreshold(0.7)
    );
    }
    }

    @Service
    public class SimpleRagService {

    @Resource
    private ChatClient chatClient;

    @Resource
    private QuestionAnswerAdvisor questionAnswerAdvisor;

    public String query(String question) {
    return chatClient.prompt()
    .user(question)
    .advisors(questionAnswerAdvisor)
    .call()
    .content();
    }
    }

    5.2 Function Calling(工具调用)

    5.2.1 什么是Function Calling?

    Function Calling允许大模型主动调用外部函数/API来完成任务。

    #mermaid-svg-u6WV4r4oWIwEoSqL {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-u6WV4r4oWIwEoSqL .error-icon{fill:#552222;}#mermaid-svg-u6WV4r4oWIwEoSqL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-u6WV4r4oWIwEoSqL .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-u6WV4r4oWIwEoSqL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-u6WV4r4oWIwEoSqL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-u6WV4r4oWIwEoSqL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-u6WV4r4oWIwEoSqL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-u6WV4r4oWIwEoSqL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-u6WV4r4oWIwEoSqL .marker.cross{stroke:#333333;}#mermaid-svg-u6WV4r4oWIwEoSqL svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-u6WV4r4oWIwEoSqL .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-u6WV4r4oWIwEoSqL text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-u6WV4r4oWIwEoSqL .actor-line{stroke:grey;}#mermaid-svg-u6WV4r4oWIwEoSqL .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-u6WV4r4oWIwEoSqL .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-u6WV4r4oWIwEoSqL #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-u6WV4r4oWIwEoSqL .sequenceNumber{fill:white;}#mermaid-svg-u6WV4r4oWIwEoSqL #sequencenumber{fill:#333;}#mermaid-svg-u6WV4r4oWIwEoSqL #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-u6WV4r4oWIwEoSqL .messageText{fill:#333;stroke:#333;}#mermaid-svg-u6WV4r4oWIwEoSqL .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-u6WV4r4oWIwEoSqL .labelText,#mermaid-svg-u6WV4r4oWIwEoSqL .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-u6WV4r4oWIwEoSqL .loopText,#mermaid-svg-u6WV4r4oWIwEoSqL .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-u6WV4r4oWIwEoSqL .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-u6WV4r4oWIwEoSqL .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-u6WV4r4oWIwEoSqL .noteText,#mermaid-svg-u6WV4r4oWIwEoSqL .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-u6WV4r4oWIwEoSqL .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-u6WV4r4oWIwEoSqL .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-u6WV4r4oWIwEoSqL .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-u6WV4r4oWIwEoSqL .actorPopupMenu{position:absolute;}#mermaid-svg-u6WV4r4oWIwEoSqL .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-u6WV4r4oWIwEoSqL .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-u6WV4r4oWIwEoSqL .actor-man circle,#mermaid-svg-u6WV4r4oWIwEoSqL line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-u6WV4r4oWIwEoSqL :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    用户

    大模型

    函数/工具

    外部系统

    "查询北京明天天气"

    分析需要调用天气API

    返回函数调用请求

    getWeather(city="北京", date="明天")

    调用天气API

    返回天气数据

    函数执行结果

    整合结果

    "北京明天晴,15-25℃"

    用户

    大模型

    函数/工具

    外部系统

    5.2.2 定义函数

    方式1:使用@Bean注册函数

    @Configuration
    public class FunctionConfig {

    /**
    * 天气查询函数
    */

    @Bean
    @Description("查询指定城市的天气信息")
    public Function<WeatherRequest, WeatherResponse> weatherFunction() {
    return request -> {
    // 模拟调用天气API
    return WeatherResponse.builder()
    .city(request.city())
    .temperature(25)
    .condition("晴天")
    .humidity(60)
    .build();
    };
    }

    /**
    * 订单查询函数
    */

    @Bean
    @Description("根据订单ID查询订单详情")
    public Function<OrderRequest, OrderResponse> orderQueryFunction() {
    return request -> {
    // 实际业务逻辑
    Order order = orderService.getById(request.orderId());
    return OrderResponse.from(order);
    };
    }
    }

    // 请求和响应DTO
    record WeatherRequest(
    @JsonProperty(required = true, description = "城市名称") String city,
    @JsonProperty(required = false, description = "日期,默认今天") String date
    ) {}

    @Builder
    record WeatherResponse(String city, int temperature, String condition, int humidity) {}

    record OrderRequest(@JsonProperty(required = true, description = "订单ID") String orderId) {}
    record OrderResponse(String orderId, String status, BigDecimal amount) {
    static OrderResponse from(Order order) {
    return new OrderResponse(
    order.getId().toString(),
    order.getStatus().name(),
    order.getAmount()
    );
    }
    }

    5.2.3 使用Function Calling

    @Service
    public class FunctionCallingService {

    @Resource
    private ChatClient chatClient;

    /**
    * 使用函数调用
    */

    public String chatWithFunctions(String userMessage) {
    return chatClient.prompt()
    .user(userMessage)
    .functions("weatherFunction", "orderQueryFunction") // 注册可用函数
    .call()
    .content();
    }
    }

    // 使用示例
    String response = service.chatWithFunctions("帮我查一下北京的天气");
    // 大模型会自动调用weatherFunction,然后返回:
    // "北京当前天气晴天,温度25℃,湿度60%"

    String response2 = service.chatWithFunctions("查询订单12345的状态");
    // 大模型会自动调用orderQueryFunction

    方式2:动态注册函数

    public String chatWithDynamicFunction(String userMessage) {
    // 动态创建函数
    FunctionCallback weatherCallback = FunctionCallback.builder()
    .function("getCurrentWeather", (WeatherRequest request) -> {
    // 函数实现
    return getWeatherData(request.city());
    })
    .description("获取当前天气")
    .inputType(WeatherRequest.class)
    .build();

    return chatClient.prompt()
    .user(userMessage)
    .function(weatherCallback)
    .call()
    .content();
    }

    5.2.4 复杂工具调用示例

    /**
    * 数据库查询工具
    */

    @Component
    public class DatabaseTools {

    @Resource
    private UserRepository userRepository;

    @Resource
    private OrderRepository orderRepository;

    @Bean
    @Description("查询用户信息")
    public Function<UserQueryRequest, UserInfo> userQueryFunction() {
    return request -> {
    User user = userRepository.findById(request.userId())
    .orElseThrow(() -> new BusinessException("用户不存在"));

    return UserInfo.builder()
    .userId(user.getId())
    .username(user.getUsername())
    .email(user.getEmail())
    .registerDate(user.getCreateTime())
    .build();
    };
    }

    @Bean
    @Description("查询用户的订单列表")
    public Function<UserOrderRequest, List<OrderInfo>> userOrdersFunction() {
    return request -> {
    List<Order> orders = orderRepository.findByUserId(request.userId());

    return orders.stream()
    .map(order -> OrderInfo.builder()
    .orderId(order.getId().toString())
    .amount(order.getAmount())
    .status(order.getStatus().name())
    .createTime(order.getCreateTime())
    .build())
    .collect(Collectors.toList());
    };
    }
    }

    // 使用示例
    String response = chatClient.prompt()
    .user("查询用户1001的信息和最近的订单")
    .functions("userQueryFunction", "userOrdersFunction")
    .call()
    .content();

    // 大模型会:
    // 1. 先调用userQueryFunction获取用户信息
    // 2. 再调用userOrdersFunction获取订单列表
    // 3. 整合结果返回给用户

    5.3 Graph – 工作流和多智能体

    5.3.1 Graph框架概述

    Spring AI Alibaba Graph是一个低级工作流和多智能体编排框架,灵感来自LangGraph。

    #mermaid-svg-xRBLmtc6Msi7hHVw {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-xRBLmtc6Msi7hHVw .error-icon{fill:#552222;}#mermaid-svg-xRBLmtc6Msi7hHVw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xRBLmtc6Msi7hHVw .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-xRBLmtc6Msi7hHVw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xRBLmtc6Msi7hHVw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xRBLmtc6Msi7hHVw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xRBLmtc6Msi7hHVw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xRBLmtc6Msi7hHVw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xRBLmtc6Msi7hHVw .marker.cross{stroke:#333333;}#mermaid-svg-xRBLmtc6Msi7hHVw svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xRBLmtc6Msi7hHVw .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-xRBLmtc6Msi7hHVw .cluster-label text{fill:#333;}#mermaid-svg-xRBLmtc6Msi7hHVw .cluster-label span{color:#333;}#mermaid-svg-xRBLmtc6Msi7hHVw .label text,#mermaid-svg-xRBLmtc6Msi7hHVw span{fill:#333;color:#333;}#mermaid-svg-xRBLmtc6Msi7hHVw .node rect,#mermaid-svg-xRBLmtc6Msi7hHVw .node circle,#mermaid-svg-xRBLmtc6Msi7hHVw .node ellipse,#mermaid-svg-xRBLmtc6Msi7hHVw .node polygon,#mermaid-svg-xRBLmtc6Msi7hHVw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xRBLmtc6Msi7hHVw .node .label{text-align:center;}#mermaid-svg-xRBLmtc6Msi7hHVw .node.clickable{cursor:pointer;}#mermaid-svg-xRBLmtc6Msi7hHVw .arrowheadPath{fill:#333333;}#mermaid-svg-xRBLmtc6Msi7hHVw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xRBLmtc6Msi7hHVw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xRBLmtc6Msi7hHVw .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-xRBLmtc6Msi7hHVw .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-xRBLmtc6Msi7hHVw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xRBLmtc6Msi7hHVw .cluster text{fill:#333;}#mermaid-svg-xRBLmtc6Msi7hHVw .cluster span{color:#333;}#mermaid-svg-xRBLmtc6Msi7hHVw 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-xRBLmtc6Msi7hHVw :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    路径A

    路径B

    路径C

    开始

    节点1: 分析需求

    决策节点

    节点2A: 调用API

    节点2B: 查询数据库

    节点2C: 人工审核

    节点3: 汇总结果

    结束

    5.3.2 基础Graph示例

    /**
    * 简单工作流示例
    */

    @Service
    public class SimpleGraphService {

    @Resource
    private ChatClient chatClient;

    public String executeWorkflow(String userInput) {
    // 1. 创建Graph
    Graph<WorkflowState> graph = new Graph<>();

    // 2. 添加节点
    graph.addNode("analyze", this::analyzeNode);
    graph.addNode("process", this::processNode);
    graph.addNode("summarize", this::summarizeNode);

    // 3. 添加边(定义流程)
    graph.addEdge(START, "analyze");
    graph.addEdge("analyze", "process");
    graph.addEdge("process", "summarize");
    graph.addEdge("summarize", END);

    // 4. 编译并执行
    CompiledGraph<WorkflowState> compiled = graph.compile();

    WorkflowState initialState = WorkflowState.builder()
    .userInput(userInput)
    .build();

    WorkflowState finalState = compiled.invoke(initialState);

    return finalState.getResult();
    }

    /**
    * 分析节点
    */

    private WorkflowState analyzeNode(WorkflowState state) {
    String analysis = chatClient.prompt()
    .system("你是分析专家,分析用户需求")
    .user(state.getUserInput())
    .call()
    .content();

    return state.toBuilder()
    .analysis(analysis)
    .build();
    }

    /**
    * 处理节点
    */

    private WorkflowState processNode(WorkflowState state) {
    String processed = chatClient.prompt()
    .system("根据分析结果进行处理")
    .user("分析结果:" + state.getAnalysis())
    .call()
    .content();

    return state.toBuilder()
    .processed(processed)
    .build();
    }

    /**
    * 汇总节点
    */

    private WorkflowState summarizeNode(WorkflowState state) {
    String summary = chatClient.prompt()
    .system("汇总所有信息,生成最终结果")
    .user("处理结果:" + state.getProcessed())
    .call()
    .content();

    return state.toBuilder()
    .result(summary)
    .build();
    }
    }

    /**
    * 工作流状态
    */

    @Data
    @Builder(toBuilder = true)
    class WorkflowState {
    private String userInput;
    private String analysis;
    private String processed;
    private String result;
    }

    5.3.3 条件路由

    /**
    * 带条件判断的工作流
    */

    public String executeConditionalWorkflow(String userInput) {
    Graph<WorkflowState> graph = new Graph<>();

    // 添加节点
    graph.addNode("classify", this::classifyNode);
    graph.addNode("handleQuestion", this::handleQuestionNode);
    graph.addNode("handleCommand", this::handleCommandNode);
    graph.addNode("handleOther", this::handleOtherNode);

    // 添加边
    graph.addEdge(START, "classify");

    // 条件路由
    graph.addConditionalEdge("classify", this::routeByType);

    graph.addEdge("handleQuestion", END);
    graph.addEdge("handleCommand", END);
    graph.addEdge("handleOther", END);

    // 执行
    CompiledGraph<WorkflowState> compiled = graph.compile();
    WorkflowState finalState = compiled.invoke(
    WorkflowState.builder().userInput(userInput).build()
    );

    return finalState.getResult();
    }

    /**
    * 分类节点
    */

    private WorkflowState classifyNode(WorkflowState state) {
    String type = chatClient.prompt()
    .system("""
    分析用户输入的类型:
    – question: 问题
    – command: 命令
    – other: 其他
    只返回类型名称
    """
    )
    .user(state.getUserInput())
    .call()
    .content()
    .toLowerCase();

    return state.toBuilder()
    .type(type)
    .build();
    }

    /**
    * 路由函数
    */

    private String routeByType(WorkflowState state) {
    return switch (state.getType()) {
    case "question" -> "handleQuestion";
    case "command" -> "handleCommand";
    default -> "handleOther";
    };
    }

    5.3.4 多智能体协作

    /**
    * 多智能体协作示例
    */

    @Service
    public class MultiAgentService {

    @Resource
    private ChatClient chatClient;

    /**
    * 代码审查多智能体流程
    */

    public CodeReviewResult reviewCode(String code) {
    Graph<CodeReviewState> graph = new Graph<>();

    // 定义多个智能体节点
    graph.addNode("securityReviewer", this::securityReviewNode);
    graph.addNode("performanceReviewer", this::performanceReviewNode);
    graph.addNode("styleReviewer", this::styleReviewNode);
    graph.addNode("coordinator", this::coordinatorNode);

    // 并行执行审查
    graph.addEdge(START, "securityReviewer");
    graph.addEdge(START, "performanceReviewer");
    graph.addEdge(START, "styleReviewer");

    // 汇总到协调者
    graph.addEdge("securityReviewer", "coordinator");
    graph.addEdge("performanceReviewer", "coordinator");
    graph.addEdge("styleReviewer", "coordinator");

    graph.addEdge("coordinator", END);

    // 执行
    CompiledGraph<CodeReviewState> compiled = graph.compile();
    CodeReviewState finalState = compiled.invoke(
    CodeReviewState.builder().code(code).build()
    );

    return finalState.getResult();
    }

    private CodeReviewState securityReviewNode(CodeReviewState state) {
    String review = chatClient.prompt()
    .system("你是安全审查专家,检查代码安全问题")
    .user("审查代码:\\n" + state.getCode())
    .call()
    .content();

    return state.toBuilder()
    .securityReview(review)
    .build();
    }

    // 其他审查节点类似…

    private CodeReviewState coordinatorNode(CodeReviewState state) {
    String summary = chatClient.prompt()
    .system("你是协调者,汇总所有审查意见")
    .user(String.format("""
    安全审查:%s
    性能审查:%s
    风格审查:%s
    请汇总生成最终报告
    """
    ,
    state.getSecurityReview(),
    state.getPerformanceReview(),
    state.getStyleReview()))
    .call()
    .content();

    return state.toBuilder()
    .result(CodeReviewResult.builder()
    .summary(summary)
    .securityIssues(extractIssues(state.getSecurityReview()))
    .performanceIssues(extractIssues(state.getPerformanceReview()))
    .styleIssues(extractIssues(state.getStyleReview()))
    .build())
    .build();
    }
    }

    5.4 MCP(Model Context Protocol)集成

    5.4.1 什么是MCP?

    MCP是一个标准化协议,让AI模型能够安全地访问外部数据源和工具。

    #mermaid-svg-xIOKMOQWkFg4Much {font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-xIOKMOQWkFg4Much .error-icon{fill:#552222;}#mermaid-svg-xIOKMOQWkFg4Much .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xIOKMOQWkFg4Much .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-xIOKMOQWkFg4Much .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xIOKMOQWkFg4Much .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xIOKMOQWkFg4Much .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xIOKMOQWkFg4Much .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xIOKMOQWkFg4Much .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xIOKMOQWkFg4Much .marker.cross{stroke:#333333;}#mermaid-svg-xIOKMOQWkFg4Much svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xIOKMOQWkFg4Much .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-xIOKMOQWkFg4Much .cluster-label text{fill:#333;}#mermaid-svg-xIOKMOQWkFg4Much .cluster-label span{color:#333;}#mermaid-svg-xIOKMOQWkFg4Much .label text,#mermaid-svg-xIOKMOQWkFg4Much span{fill:#333;color:#333;}#mermaid-svg-xIOKMOQWkFg4Much .node rect,#mermaid-svg-xIOKMOQWkFg4Much .node circle,#mermaid-svg-xIOKMOQWkFg4Much .node ellipse,#mermaid-svg-xIOKMOQWkFg4Much .node polygon,#mermaid-svg-xIOKMOQWkFg4Much .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xIOKMOQWkFg4Much .node .label{text-align:center;}#mermaid-svg-xIOKMOQWkFg4Much .node.clickable{cursor:pointer;}#mermaid-svg-xIOKMOQWkFg4Much .arrowheadPath{fill:#333333;}#mermaid-svg-xIOKMOQWkFg4Much .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xIOKMOQWkFg4Much .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xIOKMOQWkFg4Much .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-xIOKMOQWkFg4Much .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-xIOKMOQWkFg4Much .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xIOKMOQWkFg4Much .cluster text{fill:#333;}#mermaid-svg-xIOKMOQWkFg4Much .cluster span{color:#333;}#mermaid-svg-xIOKMOQWkFg4Much 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-xIOKMOQWkFg4Much :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    AI应用

    MCP Client

    MCP Server 1
    文件系统

    MCP Server 2
    数据库

    MCP Server 3
    Web API

    读取本地文件

    查询数据

    调用API

    5.4.2 使用Nacos MCP Registry

    @Configuration
    public class McpConfig {

    /**
    * 配置Nacos MCP注册中心
    */

    @Bean
    public NacosMcpRegistry nacosMcpRegistry(NacosProperties nacosProperties) {
    return new NacosMcpRegistry(
    nacosProperties.getServerAddr(),
    nacosProperties.getNamespace()
    );
    }

    /**
    * MCP服务自动发现
    */

    @Bean
    public McpServiceDiscovery mcpServiceDiscovery(NacosMcpRegistry registry) {
    return new McpServiceDiscovery(registry);
    }
    }

    # application.yml
    spring:
    ai:
    alibaba:
    mcp:
    enabled: true
    registry:
    type: nacos
    address: localhost:8848
    namespace: mcp–services


    6. 企业级应用实践

    6.1 会话记忆管理

    6.1.1 内存存储(开发测试)

    @Configuration
    public class ChatMemoryConfig {

    /**
    * 内存Chat Memory
    */

    @Bean
    public ChatMemory inMemoryChatMemory() {
    return new InMemoryChatMemory();
    }
    }

    6.1.2 Redis存储(生产推荐)

    @Configuration
    public class RedisChatMemoryConfig {

    @Bean
    public ChatMemory redisChatMemory(
    RedisTemplate<String, Object> redisTemplate) {

    return new RedisChatMemory(redisTemplate,
    ChatMemoryConfig.builder()
    .maxMessages(20) // 最多保留20条消息
    .timeToLive(Duration.ofHours(24)) // 24小时过期
    .build());
    }
    }

    @Service
    public class ConversationService {

    @Resource
    private ChatClient chatClient;

    @Resource
    private ChatMemory chatMemory;

    public String chat(String sessionId, String userMessage) {
    // 1. 加载历史
    List<Message> history = chatMemory.get(sessionId);

    // 2. 添加新消息
    history.add(new UserMessage(userMessage));

    // 3. 调用模型
    String response = chatClient.prompt()
    .messages(history)
    .call()
    .content();

    // 4. 保存响应
    history.add(new AssistantMessage(response));
    chatMemory.save(sessionId, history);

    return response;
    }
    }

    6.1.3 数据库存储(持久化)

    @Configuration
    public class JdbcChatMemoryConfig {

    @Bean
    public ChatMemory jdbcChatMemory(DataSource dataSource) {
    return new JdbcChatMemory(dataSource);
    }
    }

    // 数据库表结构
    CREATE TABLE chat_memory (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    session_id VARCHAR(64) NOT NULL,
    role VARCHAR(20) NOT NULL,
    content TEXT NOT NULL,
    metadata JSON,
    create_time DATETIME NOT NULL,
    INDEX idx_session_id (session_id),
    INDEX idx_create_time (create_time)
    );

    6.2 监控与可观测性

    6.2.1 集成Micrometer

    @Configuration
    public class ObservabilityConfig {

    @Bean
    public ObservationRegistry observationRegistry() {
    return ObservationRegistry.create();
    }

    @Bean
    public ChatClientObservationConvention customConvention() {
    return new ChatClientObservationConvention() {
    @Override
    public KeyValues getLowCardinalityKeyValues(ChatClientObservationContext context) {
    return KeyValues.of(
    "ai.model", context.getModel(),
    "ai.provider", "dashscope"
    );
    }
    };
    }
    }

    # application.yml
    management:
    endpoints:
    web:
    exposure:
    include: health,info,metrics,prometheus
    metrics:
    tags:
    application: ${spring.application.name}
    export:
    prometheus:
    enabled: true

    6.2.2 自定义指标

    @Aspect
    @Component
    public class AiMetricsAspect {

    private final MeterRegistry meterRegistry;

    public AiMetricsAspect(MeterRegistry meterRegistry) {
    this.meterRegistry = meterRegistry;
    }

    @Around("@annotation(com.example.annotation.AiMetrics)")
    public Object recordMetrics(ProceedingJoinPoint pjp) throws Throwable {
    Timer.Sample sample = Timer.start(meterRegistry);

    String methodName = pjp.getSignature().getName();
    try {
    Object result = pjp.proceed();

    // 记录成功
    sample.stop(Timer.builder("ai.call.duration")
    .tag("method", methodName)
    .tag("status", "success")
    .register(meterRegistry));

    // 记录Token使用(如果可获取)
    if (result instanceof ChatResponse response) {
    meterRegistry.counter("ai.tokens.used",
    "type", "total"
    ).increment(response.getMetadata().getUsage().getTotalTokens());
    }

    return result;

    } catch (Exception e) {
    sample.stop(Timer.builder("ai.call.duration")
    .tag("method", methodName)
    .tag("status", "error")
    .tag("error.type", e.getClass().getSimpleName())
    .register(meterRegistry));
    throw e;
    }
    }
    }

    6.3 安全与限流

    6.3.1 API Key管理

    @Configuration
    public class SecurityConfig {

    /**
    * 从环境变量或密钥管理系统获取API Key
    */

    @Bean
    public DashScopeApiKeyProvider apiKeyProvider() {
    return new DashScopeApiKeyProvider() {
    @Override
    public String getApiKey() {
    // 1. 优先从环境变量
    String apiKey = System.getenv("DASHSCOPE_API_KEY");

    // 2. 或从密钥管理服务获取
    if (apiKey == null) {
    apiKey = secretManager.getSecret("ai/dashscope/api-key");
    }

    return apiKey;
    }
    };
    }
    }

    6.3.2 限流配置

    @Configuration
    public class RateLimitConfig {

    /**
    * 基于Redis的限流
    */

    @Bean
    public RateLimiter aiRateLimiter(RedisTemplate<String, Object> redisTemplate) {
    return new RedisRateLimiter(
    10, // 每秒10个请求
    100, // 突发上限100
    redisTemplate
    );
    }
    }

    @Aspect
    @Component
    public class RateLimitAspect {

    @Resource
    private RateLimiter aiRateLimiter;

    @Before("@annotation(com.example.annotation.RateLimited)")
    public void checkRateLimit() {
    if (!aiRateLimiter.tryAcquire()) {
    throw new RateLimitException("请求过于频繁,请稍后重试");
    }
    }
    }

    6.4 异常处理与降级

    @ControllerAdvice
    public class GlobalExceptionHandler {

    @ExceptionHandler(AiException.class)
    public Result<?> handleAiException(AiException e) {
    log.error("AI调用异常", e);

    return Result.error()
    .code("AI_ERROR")
    .message("AI服务暂时不可用,请稍后重试")
    .build();
    }

    @ExceptionHandler(RateLimitException.class)
    public Result<?> handleRateLimit(RateLimitException e) {
    return Result.error()
    .code("RATE_LIMIT")
    .message(e.getMessage())
    .build();
    }
    }

    @Service
    public class ResilientAiService {

    @Resource
    private ChatClient chatClient;

    @Resource
    private CacheManager cacheManager;

    /**
    * 带降级的AI调用
    */

    public String chatWithFallback(String userMessage) {
    try {
    return chatClient.prompt()
    .user(userMessage)
    .call()
    .content();

    } catch (AiException e) {
    log.warn("AI调用失败,使用缓存降级", e);

    // 降级1:返回缓存结果
    String cached = cacheManager.get(userMessage);
    if (cached != null) {
    return cached + "\\n(来自缓存)";
    }

    // 降级2:返回默认响应
    return "抱歉,AI服务暂时不可用,请稍后重试。";
    }
    }
    }


    7. 最佳实践与注意事项

    7.1 Prompt优化

    7.1.1 System Prompt最佳实践

    /**
    * ✅ 好的System Prompt
    */

    String goodSystemPrompt = """
    # 角色
    你是一个专业的Java技术顾问,擅长Spring生态系统。

    # 能力
    – 解答Java和Spring相关技术问题
    – 提供代码示例和最佳实践
    – 进行架构设计建议

    # 约束
    – 回答要准确、简洁、专业
    – 提供的代码必须可运行
    – 引用官方文档时给出链接
    – 如果不确定,明确说明

    # 输出格式
    – 使用Markdown格式
    – 代码使用代码块包裹
    – 重要信息使用粗体标注
    """;

    /**
    * ❌ 不好的System Prompt
    */

    String badSystemPrompt = "你是一个AI助手,帮助用户解决问题。";

    7.1.2 温度参数选择
    任务类型推荐温度说明
    代码生成 0.1 – 0.3 需要精确性,避免创造性
    技术问答 0.3 – 0.5 平衡准确性和灵活性
    文案创作 0.7 – 0.9 需要创造性和多样性
    头脑风暴 0.9 – 1.0 最大化创新性

    7.2 性能优化

    7.2.1 缓存策略

    @Service
    public class CachedAiService {

    @Resource
    private ChatClient chatClient;

    /**
    * 使用Spring Cache缓存结果
    */

    @Cacheable(value = "ai-responses",
    key = "#userMessage",
    unless = "#result == null")
    public String chatWithCache(String userMessage) {
    return chatClient.prompt()
    .user(userMessage)
    .call()
    .content();
    }

    /**
    * 手动缓存管理
    */

    public String chatWithManualCache(String userMessage) {
    // 1. 检查缓存
    String cached = redis.get("ai:" + hash(userMessage));
    if (cached != null) {
    return cached;
    }

    // 2. 调用AI
    String response = chatClient.prompt()
    .user(userMessage)
    .call()
    .content();

    // 3. 存入缓存
    redis.setex("ai:" + hash(userMessage), 3600, response);

    return response;
    }

    private String hash(String input) {
    return DigestUtils.md5DigestAsHex(input.getBytes());
    }
    }

    7.2.2 异步处理

    @Service
    public class AsyncAiService {

    @Resource
    private ChatClient chatClient;

    @Async("aiTaskExecutor")
    public CompletableFuture<String> chatAsync(String userMessage) {
    String response = chatClient.prompt()
    .user(userMessage)
    .call()
    .content();

    return CompletableFuture.completedFuture(response);
    }
    }

    @Configuration
    public class AsyncConfig {

    @Bean("aiTaskExecutor")
    public Executor aiTaskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(5);
    executor.setMaxPoolSize(10);
    executor.setQueueCapacity(100);
    executor.setThreadNamePrefix("ai-task-");
    executor.initialize();
    return executor;
    }
    }

    7.3 成本控制

    7.3.1 Token使用优化

    /**
    * Token使用优化策略
    */

    @Service
    public class TokenOptimizedService {

    /**
    * 1. 精简Prompt
    */

    public String chatOptimized(String userMessage) {
    // ❌ 不要包含冗余信息
    String badPrompt = """
    请你仔细阅读以下问题,然后认真思考,给出详细的、
    完整的、准确的回答,确保回答质量高…
    问题:%s
    """
    .formatted(userMessage);

    // ✅ 精简到重点
    String goodPrompt = userMessage;

    return chatClient.prompt()
    .user(goodPrompt)
    .call()
    .content();
    }

    /**
    * 2. 使用max-tokens限制
    */

    public String chatWithLimit(String userMessage) {
    return chatClient.prompt()
    .user(userMessage)
    .options(DashScopeChatOptions.builder()
    .maxTokens(500) // 限制响应长度
    .build())
    .call()
    .content();
    }

    /**
    * 3. 选择合适的模型
    */

    public String chatWithRightModel(String userMessage, boolean isComplex) {
    String model = isComplex ? "qwen-max" : "qwen-turbo";

    return chatClient.prompt()
    .user(userMessage)
    .options(DashScopeChatOptions.builder()
    .model(model)
    .build())
    .call()
    .content();
    }
    }

    7.3.2 成本监控

    @Component
    public class CostMonitor {

    private final MeterRegistry meterRegistry;
    private final AtomicLong totalTokens = new AtomicLong(0);

    public CostMonitor(MeterRegistry meterRegistry) {
    this.meterRegistry = meterRegistry;

    // 注册指标
    meterRegistry.gauge("ai.cost.total.tokens", totalTokens);
    }

    public void recordUsage(ChatResponse response) {
    long tokens = response.getMetadata().getUsage().getTotalTokens();
    totalTokens.addAndGet(tokens);

    // 计算成本(假设每1000 tokens = 0.01元)
    double cost = tokens / 1000.0 * 0.01;

    meterRegistry.counter("ai.cost.amount",
    "model", response.getMetadata().getModel()
    ).increment(cost);
    }
    }

    7.4 测试策略

    7.4.1 单元测试

    @SpringBootTest
    class AiServiceTest {

    @MockBean
    private ChatClient chatClient;

    @Resource
    private AiService aiService;

    @Test
    void testChat() {
    // Mock ChatClient
    ChatResponse mockResponse = mock(ChatResponse.class);
    when(chatClient.prompt()).thenReturn(mock(PromptSpec.class));
    // … 设置mock行为

    // 测试
    String result = aiService.chat("测试消息");

    assertNotNull(result);
    verify(chatClient).prompt();
    }
    }

    7.4.2 集成测试

    @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
    class ChatControllerIntegrationTest {

    @Resource
    private TestRestTemplate restTemplate;

    @Test
    void testChatEndpoint() {
    ResponseEntity<String> response = restTemplate.getForEntity(
    "/api/chat/simple?message=你好",
    String.class
    );

    assertEquals(HttpStatus.OK, response.getStatusCode());
    assertNotNull(response.getBody());
    }
    }


    附录

    A. 常见问题FAQ

    Q1: Spring AI Alibaba vs LangChain4j,如何选择?

    A:

    • 使用阿里云服务 → Spring AI Alibaba
    • Spring Boot项目 → Spring AI Alibaba(无缝集成)
    • 需要复杂Agent → LangChain4j(功能更全面)
    • 需要多模型切换 → LangChain4j(支持20+模型)

    Q2: 如何降低API调用成本?

    A:

  • 使用缓存减少重复调用
  • 选择合适的模型(简单任务用qwen-turbo)
  • 限制max-tokens
  • 精简Prompt
  • 批量处理请求
  • Q3: 如何处理大模型的幻觉问题?

    A:

  • 使用RAG提供准确的上下文
  • 降低temperature参数
  • 明确要求模型基于提供的信息回答
  • 使用Function Calling获取实时数据
  • 人工审核关键输出
  • B. 参考资源

    官方文档:

    • Spring AI Alibaba官网:https://java2ai.com
    • Spring Cloud Alibaba文档:https://sca.aliyun.com
    • 阿里云百炼平台:https://dashscope.console.aliyun.com

    开源项目:

    • GitHub仓库:https://github.com/alibaba/spring-ai-alibaba
    • 示例项目:https://github.com/springaialibaba/spring-ai-alibaba-examples

    社区资源:

    • 钉钉社区群
    • GitHub Discussions
    • CSDN技术博客

    Sources:

    • Spring AI Alibaba 1.0 正式发布
    • Spring AI Alibaba Overview
    • Chat Client Guide
    • Spring AI Alibaba GitHub
    • Spring AI Alibaba Graph Preview
    赞(0)
    未经允许不得转载:171主机测评 » Spring AI Alibaba 框架实战指南
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址