Spring AI Alibaba 快速入门指南
1. 环境准备
1.1 API Key 配置
访问 阿里云百炼平台 获取 API Key,并在 Windows 环境变量中配置:
AI_DASHSCOPE_API_KEY=your_api_key_here
Spring AI Alibaba 快速入门指南涵盖环境配置、基础对话、提示词工程、Advisor 拦截、对话记忆、结构化输出、工具调用、MCP 协议、RAG 检索增强生成、AI Agent 及可观测性等内容。通过 Maven 依赖引入核心组件,配置 DashScope API Key 或本地 Ollama 模型。演示了 ChatClient 与 ChatModel 的流式与非流式调用方式,支持多模型动态切换。深入讲解了基于 MySQL 的对话记忆存储、自定义文档分割器与向量数据库结合的 RAG 实现流程。此外还包含 Tool Calling 动态注册、MCP 服务构建以及 Graph 状态图编排工作流的实践示例,为开发者提供从基础接入到复杂应用落地的完整技术路径。
访问 阿里云百炼平台 获取 API Key,并在 Windows 环境变量中配置:
AI_DASHSCOPE_API_KEY=your_api_key_here
配置完成后重启电脑使环境变量生效,可通过命令行验证:
echo %AI_DASHSCOPE_API_KEY%
Ollama 是一个用于在本地运行大型语言模型(LLM)的工具和平台。
验证安装:
ollama --version
自定义安装路径:
OllamaSetup.exe /DIR="D:\zhengqingya\soft\soft-dev\AI\LLM\Ollama"
配置环境变量(可选):
OLLAMA_MODELS=D:\zhengqingya\soft\soft-dev\AI\LLM\Ollama\models
# 启动 Ollama 服务(默认端口:11434)
ollama serve
# 列出已下载的模型
ollama list
# 下载指定模型
ollama pull <model_name>
# 运行指定模型
ollama run <model_name>
# 停止运行中的模型
ollama stop <model_name>
# 删除已下载的模型
ollama rm <model_name>
# 查看当前运行的模型实例
ollama ps
<properties>
<!-- Spring Boot -->
<spring-boot.version>3.4.0</spring-boot.version>
<!-- Spring AI -->
<spring-ai.version>1.0.0</spring-ai.version>
<!-- Spring AI Alibaba -->
<spring-ai-alibaba.version>1.0.0.3</spring-ai-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 用于为所有组件做统一版本管理 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-bom</artifactId>
<version>${spring-ai-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 核心依赖 -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<!-- Ollama 本地模型支持(可选) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
<version>1.0.0</version>
</dependency>
server:
port: 8888
servlet:
encoding:
enabled: true
charset: utf-8
force: true
spring:
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
# Ollama 配置(可选)
ollama:
base-url: http://localhost:11434
chat:
model: deepseek-r1 # qwen3:8b | deepseek-r1
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RequiredArgsConstructor
@RestController
public class HelloWorldController {
private final ChatModel chatModel;
/**
* 简单调用
* http://localhost:888/simple/chat?msg=你是谁?
*/
@GetMapping("/simple/chat")
public String simpleChat(@RequestParam String msg) {
return chatModel.call(msg);
}
/**
* 流式调用
* http://localhost:888/stream/chat?msg=你是谁?
*/
@GetMapping("/stream/chat")
public Flux<String> streamChat(@RequestParam String msg) {
return chatModel.stream(msg);
}
}
ChatClient 是更高层次的抽象,提供了简化的聊天接口和流式处理能力。
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
public class ChatClientController {
private final ChatClient chatClient;
public ChatClientController(DashScopeChatModel dashScopeChatModel) {
chatClient = ChatClient.builder(dashScopeChatModel).build();
}
/**
* 简单调用
* http://localhost:888/chat-client/simple/chat?msg=你是谁?
*/
@GetMapping("/chat-client/simple/chat")
public String simpleChat(@RequestParam String msg) {
return chatClient.prompt().user(msg).call().content();
}
/**
* 流式调用
* http://localhost:888/chat-client/stream/chat?msg=你是谁?
*/
@GetMapping("/chat-client/stream/chat")
public Flux<String> streamChat(@RequestParam String msg) {
return chatClient.prompt().user(msg).stream().content();
}
}
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.google.common.collect.Maps;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.util.Map;
@RestController
@RequestMapping("/mutli-platform-model")
@Tag(name = "多模型动态配置")
public class MutliPlatformModelController {
private Map<String, ChatModel> STRATEGY = Maps.newHashMap();
public MutliPlatformModelController(DashScopeChatModel dashScopeChatModel, OllamaChatModel ollamaChatModel) {
STRATEGY.put("dashscope", dashScopeChatModel);
STRATEGY.put("ollama", ollamaChatModel);
}
/**
* http://localhost:888/mutli-platform-model/stream/chat?platform=dashscope&model=qwen3-max&temperature=0.8&msg=你是谁?
* http://localhost:888/mutli-platform-model/stream/chat?platform=ollama&model=deepseek-r1&temperature=0.8&msg=你是谁?
*/
@Operation(summary = "动态切换模型")
@GetMapping("/stream/chat")
public Flux<String> streamChat(@ModelAttribute MutliPlatformModelOptions options) {
ChatModel chatModel = STRATEGY.get(options.platform());
ChatClient chatClient = ChatClient.builder(chatModel)
// 模型配置参数
.defaultOptions(ChatOptions.builder()
.model(options.model())
.temperature(options.temperature())
.build()).build();
return chatClient.prompt().user(options.msg()).stream().content();
}
}
public enum MessageType {
USER("user"), // 用户发起的消息或请求
ASSISTANT("assistant"), // AI 助手生成的回复或响应
SYSTEM("system"), // 系统级别的指令或信息
TOOL("tool") // 工具调用产生的消息
}
@RestController
@RequestMapping("/prompt")
@Tag(name = "提示词")
public class PromptController {
private ChatClient chatClient;
private ChatModel chatModel;
@Value("classpath:/prompt-template/tell-joke.txt")
private org.springframework.core.io.Resource promptTemplateRes;
public PromptController(DashScopeChatModel dashScopeChatModel) {
chatClient = ChatClient.builder(dashScopeChatModel).build();
chatModel = dashScopeChatModel;
}
private static final String DEFAULT_PROMPT = """
你是一位资深 Java 架构师,专注于企业级 Java 后端开发。
请严格按照以下规则回答:
1. 只回答 Java 及相关技术栈的问题;
2. 提供准确、专业的技术解答;
3. 对于非 Java 后端相关问题,请礼貌说明超出了专业范围。
""";
/**
* http://localhost:888/prompt/chat-client?msg=今天吃什么?
*/
@GetMapping("/chat-client")
public String chatClient(@RequestParam String msg) {
return chatClient.prompt().system(DEFAULT_PROMPT).user(msg).call().content();
}
/**
* 提示词模板使用
* http://localhost:888/prompt/prompt-template?topic=无聊
*/
@GetMapping("/prompt-template")
public Flux<String> promptTemplate(@RequestParam String topic) {
// 创建提示词模板
PromptTemplate promptTemplate = new PromptTemplate("给我讲一个有关于{topic}的笑话");
// 创建 Prompt 实例
Prompt prompt = promptTemplate.create(Map.of("topic", topic));
// 流式返回结果
return chatModel.stream(prompt).map(chatResponse -> chatResponse.getResult().getOutput().getText());
}
}
Advisor 主要用于拦截 ChatClient 的对话请求和响应,在对话过程中添加通用处理逻辑。
@RestController
@RequestMapping("/advisor")
@Tag(name = "Advisor 对话拦截--日志记录")
public class AdvisorLogController {
private ChatClient chatClient;
public AdvisorLogController(DashScopeChatModel dashScopeChatModel) {
chatClient = ChatClient.builder(dashScopeChatModel).defaultAdvisors(new SimpleLoggerAdvisor()).build();
}
/**
* http://localhost:888/advisor/log?msg=你好
*/
@GetMapping("/log")
public Flux<String> log(@RequestParam String msg) {
return chatClient.prompt().user(msg).stream().content();
}
}
@RestController
@RequestMapping("/advisor")
@Tag(name = "Advisor 对话拦截--敏感词拦截")
public class AdvisorSafeGuardController {
private ChatClient chatClient;
public AdvisorSafeGuardController(DashScopeChatModel dashScopeChatModel) {
chatClient = ChatClient.builder(dashScopeChatModel)
.defaultAdvisors(
new SimpleLoggerAdvisor(),
new SafeGuardAdvisor(Lists.newArrayList("敏感词","WC"),
"由于包含敏感内容,我无法对此进行回复。我们可以重新表述或讨论其他话题吗?",
1)).build();
}
/**
* http://localhost:888/advisor/safe-guard?msg=你好
* http://localhost:888/advisor/safe-guard?msg=WC
*/
@GetMapping("/safe-guard")
public Flux<String> safeGuard(@RequestParam String msg) {
return chatClient.prompt().user(msg).stream().content();
}
}
/**
* <p> 自定义 Advisor 拦截器 -- 实现重写提示词 </p>
*/
public class RewritePromptAdvisor implements BaseAdvisor {
private static final String REWRITE_TEXT = """
让我们仔细分析这个问题:
问题:{input_msg}
分析步骤:
1. 问题理解:这个问题在问什么?
2. 关键信息:有哪些重要的细节需要注意?
3. 回答构建:基于以上分析,给出准确详细的回答:
回答要求:
- 使用通俗易懂的语言
- 结合实际情况举例说明
- 如涉及技术内容,提供具体的操作步骤
- 返回 html 格式内容
""";
@Override
public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) {
String contents = chatClientRequest.prompt().getContents();
String inputMsg = PromptTemplate.builder().template(REWRITE_TEXT).build().render(Map.of("input_msg", contents));
return chatClientRequest.mutate().prompt(Prompt.builder().content(inputMsg).build()).build();
}
@Override
public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {
return chatClientResponse;
}
@Override
public int getOrder() {
return 0;
}
}
用于维护对话上下文状态。
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-memory-jdbc</artifactId>
<version>1.0.0.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/spring-ai-alibaba-demo?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&useSSL=false
username: root
password: root
@RestController
@RequestMapping("/chat-memory")
@Tag(name = "对话记忆")
public class ChatMemoryController {
private final ChatClient chatClient;
// 注入 JdbcTemplate, ChatClient
public ChatMemoryController(JdbcTemplate jdbcTemplate, DashScopeChatModel dashScopeChatModel) {
// 构造 ChatMemoryRepository 和 ChatMemory
ChatMemoryRepository chatMemoryRepository = MysqlChatMemoryRepository.mysqlBuilder().jdbcTemplate(jdbcTemplate).build();
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10) // 消息存储条数
.build();
this.chatClient = ChatClient.builder(dashScopeChatModel)
// 增加聊天记忆能力
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
// 实现 Logger 的 Advisor
.defaultAdvisors(new SimpleLoggerAdvisor())
.build();
}
/**
* 使用自定义的 Advisor 增加聊天记忆能力
* eg:
* http://127.0.0.1:888/chat-memory/chat/123?msg=你好,我叫郑清,之后的会话中都带上我的名字
* http://127.0.0.1:888/chat-memory/chat/123?msg=我叫什么名字?
* http://127.0.0.1:888/chat-memory/chat/111?msg=我叫什么名字?
*/
@GetMapping("/chat/{id}")
public Flux<String> advisorChat(HttpServletResponse response, @PathVariable String id, @RequestParam String msg) {
response.setCharacterEncoding("UTF-8");
return this.chatClient.prompt(msg).advisors(a -> a.param(ChatMemory.CONVERSATION_ID, id)).stream().content();
}
}
将模型的输出转换为特定的数据结构。
// 定义数据结构
public record StructuredVO(String category, String question, String answer) {}
@RestController
@RequestMapping("/structured-output")
@Tag(name = "结构化输出")
public class StructuredOutputController {
private ChatClient chatClient;
public StructuredOutputController(DashScopeChatModel dashScopeChatModel) {
chatClient = ChatClient.builder(dashScopeChatModel).build();
}
/**
* http://localhost:888/structured-output/test
* {"category":"售后服务","question":"这件商品的质量不好,我可以申请退货吗?","answer":"如果商品存在质量问题,通常可以申请退货。请查看商家的退换货政策或联系客服确认具体流程。"}
*/
@GetMapping("/test")
public StructuredVO structuredOutput() {
return chatClient.prompt()
.system("对问题分类并简要总结,并给出答案")
.user("这件商品的质量不好,我可以申请退货吗?")
.call()
.entity(StructuredVO.class);
}
}
允许 AI 模型调用外部工具和函数来执行特定任务。
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;
@Service
public class TimeTools {
@Tool(description = "获取当前时间,默认时间格式:YYYY-MM-DD HH:mm:ss")
public String getCurrentTime(@ToolParam(description = "时间格式") String format) {
log.info("获取当前时间格式:{}", format);
return DateUtil.now();
}
}
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Description;
import org.springframework.stereotype.Service;
import java.util.function.Function;
@Slf4j
@Service(value = "getWeather")
public class WeatherFunction implements Function<String, String> {
@Override
@Description("获取天气")
public String apply(@JsonPropertyDescription("城市") String city) {
log.info("获取{}天气", city);
return city + "天气:晴";
}
}
@RestController
@RequestMapping("/tool-calling")
@Tag(name = "工具调用")
public class ToolCallingController {
private ChatClient chatClient;
@Autowired
private TimeTools timeTools;
public ToolCallingController(DashScopeChatModel dashScopeChatModel) {
chatClient = ChatClient.builder(dashScopeChatModel).build();
}
/**
* 法 1:方法工具
* http://localhost:888/tool-calling/time?msg=现在时间?
*/
@GetMapping("/time")
public Flux<String> time(@RequestParam String msg) {
return chatClient.prompt().user(msg).tools(timeTools).stream().content();
}
/**
* 法 2:函数工具
* http://localhost:888/tool-calling/weather?msg=成都天气?
*/
@GetMapping("/weather")
public Flux<String> weather(@RequestParam String msg) {
return chatClient.prompt().user(msg).toolNames(new String[]{"getWeather"}).stream().content();
}
}
@RestController
@RequestMapping("/tool-calling")
@Tag(name = "工具调用 - 动态注册")
public class ToolCallingDynamicController {
private ChatClient chatClient;
@Autowired
private TimeTools timeTools;
public ToolCallingDynamicController(DashScopeChatModel dashScopeChatModel, TimeTools timeTools) {
chatClient = ChatClient.builder(dashScopeChatModel).build();
}
/**
* 法一:方法工具
* http://localhost:888/tool-calling/dynamic/1?msg=现在时间?
* http://localhost:888/tool-calling/dynamic/2?msg=现在时间?
*/
@GetMapping("/dynamic/{userId}")
public Flux<String> dynamic(@PathVariable Long userId, @RequestParam String msg) {
return chatClient.prompt().user(msg)
// 动态注册工具
.toolCallbacks(getToolCallbacks(userId))
.stream().content();
}
private List<ToolCallback> getToolCallbacks(Long userId) {
List<ToolCallback> toolCallbacks = Lists.newArrayList();
if (userId != 1) {
return toolCallbacks;
}
Method method = ReflectionUtils.findMethod(TimeTools.class, "getCurrentTime", String.class);
if (method == null) {
throw new RuntimeException("Method not found");
}
String inputSchema = JsonSchemaGenerator.generateForMethodInput(method);
ToolCallback timeToolCallback = MethodToolCallback.builder()
.toolDefinition(ToolDefinition.builder()
.name("getCurrentTime")
.description("获取当前时间,默认时间格式:YYYY-MM-DD HH:mm:ss")
.inputSchema(inputSchema)
.build())
.toolMethod(method)
.toolObject(timeTools)
.build();
toolCallbacks.add(timeToolCallback);
return toolCallbacks;
}
/**
* 法二:函数工具
* http://localhost:888/tool-calling/dynamic2/1?msg=成都天气?
* http://localhost:888/tool-calling/dynamic2/2?msg=成都天气?
*/
@GetMapping("/dynamic2/{userId}")
public Flux<String> dynamic2(@PathVariable Long userId, @RequestParam String msg) {
String[] toolNames = userId == 1 ? new String[]{"getWeather"} : new String[0];
return chatClient.prompt().user(msg)
// 动态注册工具
.toolNames(toolNames)
.stream().content();
}
}
MCP 是一种模型上下文协议,主要用于 AI 模型调用外部工具和函数。
<!-- pom.xml 依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
# application.yml 配置
spring:
main:
web-application-type: none # 设置为非 Web 应用类型
banner-mode: off
ai:
mcp:
server:
stdio: true
name: my-weather-server
version: 1.0.0
// 实现 MCP 工具
@Service
public class MyWeatherService {
@Tool(description = "获取天气")
public String getWeather(@ToolParam(description = "城市") String city) {
return city + "天气:雨";
}
}
// 注册 MCP 工具
@Bean
public ToolCallbackProvider weatherTools(MyWeatherService myWeatherService) {
return MethodToolCallbackProvider.builder().toolObjects(myWeatherService).build();
}
spring:
ai:
mcp:
client:
stdio:
servers-configuration: classpath:/mcp/mcp-servers-config.json
{
"mcpServers": {
"my-weather-server": {
"command": "java",
"args": [
"-Dspring.ai.mcp.server.stdio=true",
"-Dspring.main.web-application-type=none",
"-Dlogging.pattern.console=",
"-jar",
"D:\\path\\to\\your\\mcp-server.jar"
]
}
}
}
# application.yml 配置
server:
port: 10088
spring:
main:
banner-mode: off
ai:
mcp:
server:
name: my-sse-server
version: 1.0.0
type: ASYNC
sse-endpoint: /sse
sse-message-endpoint: /mcp
capabilities:
tool: true
resource: true
prompt: true
completion: true
spring:
ai:
mcp:
client:
sse:
connections:
my-sse-server:
url: http://localhost:10088
RAG 是一种结合检索和生成的技术架构,通过引入'外接知识库'让模型在回答问题前先查阅权威资料。
spring:
ai:
dashscope:
embedding:
options:
model: text-embedding-v4
dimensions: 64
@RestController
@RequestMapping("/rag")
@Tag(name = "RAG")
public class RagController {
private EmbeddingModel embeddingModel;
public RagController(DashScopeEmbeddingModel dashScopeEmbeddingModel) {
embeddingModel = dashScopeEmbeddingModel;
}
/**
* http://localhost:888/rag/embedding?msg=你好
*/
@GetMapping("/embedding")
public Object embedding(@RequestParam String msg) {
EmbeddingResponse embeddingResponse = this.embeddingModel.embedForResponse(List.of(msg));
return Map.of("embedding", embeddingResponse);
}
}
使用 SimpleVectorStore 轻量级向量数据库:
@RestController
@RequestMapping("/rag")
@Tag(name = "RAG")
public class RagController {
private VectorStore vectorStore;
public RagController(DashScopeEmbeddingModel dashScopeEmbeddingModel) {
vectorStore = SimpleVectorStore.builder(dashScopeEmbeddingModel).build();
}
/**
* http://localhost:888/rag/vectorStore?topK=3&threshold=0.1&msg=有哪些开源的向量数据库?
*/
@GetMapping("/vectorStore")
public Object vectorStore(@RequestParam String msg, @RequestParam int topK, @RequestParam double threshold) {
// 存储向量
vectorStore.add(Lists.newArrayList(
Document.builder().text("LangChain 是一个用于开发由语言模型驱动的应用程序的框架。").build(),
Document.builder().text("Milvus 是一款开源向量数据库,专为大规模向量相似性搜索而设计。").build()
));
// 相似性搜索
return vectorStore.similaritySearch(SearchRequest.builder().query(msg).topK(topK).similarityThreshold(threshold).build());
}
}
@Value("classpath:rag/pet.txt")
private Resource textRes;
/**
* 文本读取器
* http://localhost:888/rag/elt/text
*/
@GetMapping("/text")
public Object text() {
// 1. 创建文本读取器
TextReader textReader = new TextReader(textRes);
// 2. 读取文档内容
List<Document> documents = textReader.read();
// 3. 返回结果
return documents;
}
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-markdown-document-reader</artifactId>
</dependency>
@Value("classpath:rag/iphone.md")
private Resource MdRes;
/**
* markdown 读取器
* http://localhost:888/rag/elt/markdown
*/
@GetMapping("/markdown")
public Object markdown() {
// 1. 创建 Markdown 文档读取器
MarkdownDocumentReader markdownReader = new MarkdownDocumentReader(MdRes,
MarkdownDocumentReaderConfig.builder()
.withAdditionalMetadata("filename", MdRes.getFilename())
.withHorizontalRuleCreateDocument(false)
.withIncludeCodeBlock(false)
.withIncludeBlockquote(false)
.build());
// 2. 读取 Markdown 文档内容
List<Document> documents = markdownReader.read();
// 3. 返回文档列表
return documents;
}
TokenTextSplitter 是 RAG 应用中关键的文档预处理工具。
/**
* 文本读取器
* http://localhost:888/rag/elt/split/text
*/
@GetMapping("/text")
public Object text() {
// 1. 创建文本读取器
TextReader textReader = new TextReader(textRes);
// 2. 读取文档内容
List<Document> documents = textReader.read();
// 3. 文档分割处理
List<Document> splitDocs = new TokenTextSplitter(800, 350, 5, 10000, true).split(documents);
// 4. 返回结果
return splitDocs;
}
/**
* 学校规章制度专用文档分割器 - 针对规章制度结构优化
* 支持按"第.*条"分割,保留规章制度的结构完整性
*/
public class RegulationDocumentSplitter extends TextSplitter {
// 实现细节...
public static Builder builder() {
return new Builder();
}
// ... 其他方法实现
}
@GetMapping("/keyword")
public Object keyword() {
// 1. 创建 Markdown 文档读取器
MarkdownDocumentReader markdownReader = new MarkdownDocumentReader(MdRes,
MarkdownDocumentReaderConfig.builder().withAdditionalMetadata("filename", MdRes.getFilename()).build());
// 2. 读取文档内容
List<Document> documents = markdownReader.read();
// 3. 关键字提取
KeywordMetadataEnricher enricher = new KeywordMetadataEnricher(chatModel, 3);
List<Document> enricherDocs = enricher.apply(documents);
return enricherDocs;
}
@GetMapping("/summary")
public Object summary() {
// 1. 创建 Markdown 文档读取器
MarkdownDocumentReader markdownReader = new MarkdownDocumentReader(MdRes,
MarkdownDocumentReaderConfig.builder().withAdditionalMetadata("filename", MdRes.getFilename()).build());
// 2. 读取文档内容
List<Document> documents = markdownReader.read();
// 3. 摘要生成
SummaryMetadataEnricher enricher = new SummaryMetadataEnricher(chatModel, List.of(SummaryMetadataEnricher.SummaryType.CURRENT, SummaryMetadataEnricher.SummaryType.NEXT),
"""请为以下文档内容生成摘要:
{context_str}
要求:
1. 摘要长度不超过 100 字
2. 突出关键信息
3. 保持原意不变
""", MetadataMode.ALL);
List<Document> enricherDocs = enricher.apply(documents);
return enricherDocs;
}
@GetMapping("/metadata-filter")
public Object metadataFilter() {
// 1. 创建 Markdown 文档读取器
MarkdownDocumentReader markdownReader = new MarkdownDocumentReader(MdRes,
MarkdownDocumentReaderConfig.builder().withAdditionalMetadata("filename", MdRes.getFilename()).build());
// 2. 读取文档内容
List<Document> documents = markdownReader.read();
// 3. 关键字提取
KeywordMetadataEnricher enricher = new KeywordMetadataEnricher(chatModel, 3);
List<Document> enricherDocs = enricher.apply(documents);
// 4. 存储向量
vectorStore.add(enricherDocs);
// 5. 相似性搜索
List<Document> result = vectorStore.similaritySearch(SearchRequest.builder()
.query("手机处理器")
.topK(3)
// 基于元数据的过滤条件
.filterExpression(new FilterExpressionBuilder().eq("filename", "phone.md").build())
.build());
// 6. 返回结果
return result;
}
@RestController
@RequestMapping("/rag/retrieval-augmentation")
@Tag(name = "RAG-检索增强生成")
public class RagRetrievalAugmentationAdvisorController {
@Value("classpath:rag/iphone.md")
private Resource MdRes;
private ChatModel chatModel;
private ChatClient chatClient;
private VectorStore vectorStore;
public RagRetrievalAugmentationAdvisorController(DashScopeChatModel dashScopeChatModel, DashScopeEmbeddingModel dashScopeEmbeddingModel) {
chatModel = dashScopeChatModel;
chatClient = ChatClient.builder(dashScopeChatModel).defaultAdvisors(new SimpleLoggerAdvisor()).build();
vectorStore = SimpleVectorStore.builder(dashScopeEmbeddingModel).build();
}
/**
* RetrievalAugmentationAdvisor 检索增强生成
* http://localhost:888/rag/retrieval-augmentation/chat?msg=下雨天,我不想出门,你能不能帮我了解下手机店里的 iphone18 价格?
*/
@GetMapping("/chat")
public Flux<String> chat(@RequestParam String msg) {
// 1. 创建 Markdown 文档读取器
MarkdownDocumentReader markdownReader = new MarkdownDocumentReader(MdRes,
MarkdownDocumentReaderConfig.builder().withAdditionalMetadata("filename", MdRes.getFilename()).build());
// 2. 读取文档内容
List<Document> documents = markdownReader.read();
vectorStore.add(documents);
// 3. 检索增强生成
Advisor advisor = RetrievalAugmentationAdvisor.builder()
// documentRetriever:文档检索器
.documentRetriever(VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.similarityThreshold(0.5)
.topK(3)
.build())
// queryAugmenter:查询增强器
.queryAugmenter(ContextualQueryAugmenter.builder()
.allowEmptyContext(false)
.emptyContextPromptTemplate(PromptTemplate.builder()
.template("用户查询位于知识库之外。礼貌地告知用户您无法回答。").build())
.build())
// QueryTransformer:查询转换器
.queryTransformers(
// 查询重写
RewriteQueryTransformer.builder()
.chatClientBuilder(ChatClient.builder(chatModel))
.targetSearchSystem("知识库")
.build(),
// 翻译转换器
TranslationQueryTransformer.builder()
.chatClientBuilder(ChatClient.builder(chatModel))
.targetLanguage("中文")
.build())
.build();
return chatClient.prompt().user(msg).advisors(advisor).stream().content();
}
}
@RestController
@RequestMapping("/rag/rerank")
@Tag(name = "重排序")
public class RerankController {
private RerankModel rerankModel;
private ChatClient chatClient;
private VectorStore vectorStore;
public RerankController(DashScopeChatModel dashScopeChatModel, DashScopeEmbeddingModel dashScopeEmbeddingModel, DashScopeRerankModel dashScopeRerankModel) {
chatClient = ChatClient.builder(dashScopeChatModel).defaultAdvisors(new SimpleLoggerAdvisor()).build();
vectorStore = SimpleVectorStore.builder(dashScopeEmbeddingModel).build();
rerankModel = dashScopeRerankModel;
}
/**
* http://localhost:888/rag/rerank/chat?msg=iphone20 价格
*/
@GetMapping("/chat")
public Flux<String> chat(@RequestParam String msg) {
vectorStore.add(Lists.newArrayList(
Document.builder().text("iPhone18 基础价格:5999 元,税率:13%, 运费:0 元").build(),
Document.builder().text("iPhone20 基础价格:12999 元,税率:13%, 运费:50 元").build(),
Document.builder().text("iPhone10 基础价格:1299 元,税率:13%, 运费:0 元").build()
));
// 重排序
RetrievalRerankAdvisor rerankAdvisor = new RetrievalRerankAdvisor(vectorStore, rerankModel, SearchRequest.builder().topK(10).build());
return chatClient.prompt().user(msg).advisors(rerankAdvisor).stream().content();
}
}
@RestController
@RequestMapping("/rag/fact-check")
@Tag(name = "RAG-幻觉评估")
public class RagFactCheckController {
private ChatModel chatModel;
public RagFactCheckController(DashScopeChatModel dashScopeChatModel) {
chatModel = dashScopeChatModel;
}
@GetMapping("/test")
public Object test() {
// 准备评估数据
ArrayList<Document> documents = Lists.newArrayList(
Document.builder().text("iPhone18 基础价格:5999 元,税率:13%, 运费:0 元").build(),
Document.builder().text("iPhone20 基础价格:12999 元,税率:13%, 运费:50 元").build(),
Document.builder().text("iPhone10 基础价格:1299 元,税率:13%, 运费:0 元").build()
);
// AI 回答
String aiRes = "iPhone20 6000 元";
// 执行评估
FactCheckingEvaluator evaluator = new FactCheckingEvaluator(ChatClient.builder(chatModel));
EvaluationRequest evaluationRequest = new EvaluationRequest(documents, aiRes);
EvaluationResponse evaluationResponse = evaluator.evaluate(evaluationRequest);
return evaluationResponse; // {"pass":false,"score":0.0,"feedback":"","metadata":{}}
}
}
AI Agent 是一种能够自主感知环境、做出决策并执行动作以实现特定目标的智能实体。
| 模式名称 | 核心关系 (人类 : AI) | 特点与工作方式 | 典型场景 |
|---|---|---|---|
| 嵌入模式 (Embedded) | 我做你看 | 将 AI 能力作为功能模块嵌入现有产品 | 智能客服中的自动回复、推荐系统 |
| 副驾驶模式 (Copilot) | 我们一起做 | AI 作为实时协作的伙伴,提供建议和辅助 | GitHub Copilot 代码补全 |
| 智能体模式 (Agent) | 你做我看 | AI 拥有高度自主权,可独立规划、决策、执行复杂任务 | 自动交易系统、智能助手 |
软件的可观测性是指通过系统输出(如日志、指标、跟踪等)来推断其内部状态的能力。
参考官方文档部署 Zipkin。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-tool-calling-weather</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
<version>1.5.0-M2</version>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
<version>3.4.3</version>
</dependency>
spring:
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
observations:
log-completion: true
log-prompt: true
# Chat config items
chat:
client:
observations:
log-prompt: true
log-completion: true
include-error-logging: true
# tools config items
tools:
observability:
include-content: true
http:
client:
read-timeout: 60s
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
tracing:
sampling:
probability: 1.0
zipkin:
tracing:
endpoint: http://localhost:9411/api/v2/spans
Spring AI Alibaba Graph 是一个强大且直观的框架,能让你像搭积木一样,通过编排节点和流程来构建复杂的 AI 应用。
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-graph-core</artifactId>
</dependency>
public class ExpanderNode implements NodeAction {
private final ChatClient chatClient;
public ExpanderNode(DashScopeChatModel chatModel) {
this.chatClient = ChatClient.builder(chatModel).build();
}
@Override
public Map<String, Object> apply(OverAllState state) throws Exception {
// 1. 从全局状态中获取输入
String query = state.value("query", "");
// 2. 构建提示词并调用大模型
String promptTemplate = "请为以下问题生成 3 个不同角度的变体问题:{query}";
String result = chatClient.prompt().user(u -> u.text(promptTemplate).param("query", query)).call().content();
// 3. 处理结果
List<String> queryVariants = Arrays.asList(result.split("\n"));
// 4. 将结果放入 Map
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("expander_content", queryVariants);
return resultMap;
}
}
@Configuration
public class SimpleGraphConfiguration {
@SneakyThrows
@Bean
public StateGraph simpleGraph(DashScopeChatModel chatModel) {
// 全局变量的替换策略
KeyStrategyFactory keyStrategyFactory = () -> {
HashMap<String, KeyStrategy> strategies = new HashMap<>();
strategies.put("query", new ReplaceStrategy());
strategies.put("expander_content", new ReplaceStrategy());
return strategies;
};
// 构建状态图
StateGraph stateGraph = new StateGraph("问题扩展工作流", keyStrategyFactory)
// 添加节点
.addNode("expander", AsyncNodeAction.node_async(new ExpanderNode(chatModel)))
// 添加边
.addEdge(StateGraph.START, "expander")
.addEdge("expander", StateGraph.END);
return stateGraph;
}
}
@RestController
@RequestMapping("/graph")
@Tag(name = "Graph")
public class SimpleGraphController {
private final CompiledGraph compiledGraph;
// 注入定义好的 StateGraph,并编译成 CompiledGraph
public SimpleGraphController(@Qualifier("simpleGraph") StateGraph stateGraph) throws GraphStateException {
this.compiledGraph = stateGraph.compile();
}
/**
* http://localhost:888/graph/expand?query=什么是人工智能
*/
@GetMapping("/expand")
public Map<String, Object> expandQuery(@RequestParam String query) throws GraphRunnerException {
// 设置初始状态
Map<String, Object> initialState = Map.of("query", query);
// 执行工作流
Optional<OverAllState> result = compiledGraph.invoke(initialState);
// 从最终状态中获取结果
return result.map(OverAllState::data).orElse(Map.of());
}
}

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online