springai Alibaba(中)

发布时间:2026/9/3 14:44:38
springai Alibaba(中) 六.提示词6.1 定义Prompt是引导Al模型生成特定输出的输入格式Prompt的设计和措辞会显著影响模型的响应。我们平常的输入是:Prompt MessageString 简单的文本字符串提问6.2 Prompt中的四大角色(Role)角色 (Role)直观比喻核心任务实际开发中的典型内容SYSTEM导演设定规则与人设。它在对话最开始运行AI 不会直接回答它但会全程遵守。“你是一个 Java 高级架构师”、“请务必用中文回答”、“禁止讨论政治”。USER甲方下达指令。代表真实用户的提问或操作请求。“帮我写一个快速排序”、“总结一下这段文档”。ASSISTANT演员提供答案。由 AI 生成的消息。存入历史记录后它能让 AI 记得自己之前说过什么。“好的这是为您生成的排序代码...”、“根据上下文我的看法是...”。TOOL外部专家返回执行结果。当 AI 需要调用外部函数如查天气、搜数据库后反馈回来的原始数据。“查询结果2026-03-24 长沙天气晴”、“数据库返回用户 ID 为 123”。6.3 案例实现和之前一样仍然是新建子模块module,改pom写yml,写启动配置类接着来看Controller:提示词之四大角色编码第一版:RestController public class PromptController { //V2 通过ChatClient实现stream实现流式输出 Resource(name deepseekChatClient) private ChatClient deepseekChatClient; Resource(name qwenChatClient) private ChatClient qwenChatClient; // http://localhost:8005/prompt/chat?question火锅介绍下 GetMapping(value /prompt/chat) public FluxString chat(RequestParam(question) String question) { return deepseekChatClient.prompt() //ai能力边界 .system(你是一个法律助手只回答法律问题其它问题回复我只能回答法律相关问题其它无可奉告) .user(question) .stream().content(); } }提示词之四大角色编码第二版:记忆对话累计回答GetMapping(/prompt/chat4) public String chat4(String question) { AssistantMessage assistantMessage deepseekChatClient.prompt() .user(question) .call() .chatResponse() .getResult() .getOutput(); return assistantMessage.getText(); }提示词之四大角色编码第三版:/** * http://localhost:8005/prompt/chat5?city北京 * 近似理解Tool后面章节讲解...... * param city * return */ GetMapping(/prompt/chat5) public String chat5(String city) { String answer deepseekChatClient.prompt() .user(city 未来3天天气情况如何?) .call() .chatResponse() .getResult() .getOutput() .getText(); ToolResponseMessage toolResponseMessage new ToolResponseMessage( List.of(new ToolResponseMessage.ToolResponse(1,获得天气,city) ) ); String toolResponse toolResponseMessage.getText(); String result answer toolResponse; return result; }七.提示词模板Prompt Template简单理解Prompt Template即为构建词写死提示词配置。所以提示词模板意为引入占位符(如{占位符变量名})以动态插入内容7.1 基本开发步骤前面的基本步骤和以往一致: 建子模块改pom,写yml,主启动写config类接下来主要对业务类的重点步骤介绍:转化流程 (三步走)New创建PromptTemplate实例。Create传入Map填充数据产生Prompt对象。Chat将Prompt喂给客户端执行。package com.alibaba.controller; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.SystemMessage; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.chat.prompt.PromptTemplate; import org.springframework.ai.chat.prompt.SystemPromptTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; import org.springframework.beans.factory.annotation.Value; import java.util.List; import java.util.Map; RestController public class PromptTemplateController { Resource(name deepseek) private ChatModel deepseekChatModel; Resource(name qwen) private ChatModel qwenChatModel; Resource(name deepseekChatClient) private ChatClient deepseekChatClient; Resource(name qwenChatClient) private ChatClient qwenChatClient; /** * 测试地址 * http://localhost:8006/prompttemplate/chat?topicjavaoutput_formathtmlwordCount200 */ GetMapping(/prompttemplate/chat) public FluxString chat(String topic, String output_format, String wordCount) { PromptTemplate promptTemplate new PromptTemplate( 讲一个关于{topic}的故事 并以{output_format}格式输出 字数在{wordCount}左右); // PromptTempate - Prompt //得到提示词模板之后需要转化为提示词 Prompt prompt promptTemplate.create(Map.of( topic, topic, output_format,output_format, wordCount,wordCount)); return deepseekChatClient.prompt(prompt).stream().content(); } }测试结果:7.2 读取配置文件构成模板在代码中写死模板并不常用因此需要像图示一样改变此时Controller新增部分:Value(classpath:/prompttemplate/atguigu-template.txt) private org.springframework.core.io.Resource userTemplate; GetMapping(/prompttemplate/chat2) public String chat2(RequestParam(topic) String topic, RequestParam(output_format) String output_format) { PromptTemplate promptTemplate new PromptTemplate(userTemplate); Prompt prompt promptTemplate.create(Map.of(topic, topic, output_format, output_format)); return deepseekChatClient.prompt(prompt).call().content(); }测试结果如下:7.3 角色设定和边界划分/** * Description: * 系统消息(SystemMessage)设定AI的行为规则和功能边界(xxx助手/什么格式返回/字数控制多少)。 * 用户消息(UserMessage)用户的提问/主题 * http://localhost:8006/prompttemplate/chat3?sysTopic法律userTopic知识产权法 * * http://localhost:8006/prompttemplate/chat3?sysTopic法律userTopic夫妻肺片 */ GetMapping(/prompttemplate/chat3) public String chat3(String sysTopic, String userTopic) { // 1.SystemPromptTemplate SystemPromptTemplate systemPromptTemplate new SystemPromptTemplate(你是{systemTopic}助手只回答{systemTopic}其它无可奉告以HTML格式的结果。); Message sysMessage systemPromptTemplate.createMessage(Map.of(systemTopic, sysTopic)); // 2.PromptTemplate PromptTemplate userPromptTemplate new PromptTemplate(解释一下{userTopic}); Message userMessage userPromptTemplate.createMessage(Map.of(userTopic, userTopic)); // 3.组合【关键】 多个 Message - Prompt Prompt prompt new Prompt(List.of(sysMessage, userMessage)); // 4.调用 LLM return deepseekChatClient.prompt(prompt).call().content(); }八.格式化输出在 Spring AI 或大模型开发语境下格式化输出 (Output Parsing / Structured Output)是指将 AI 生成的非结构化文本如一段散乱的话转化为程序可直接解析的结构化数据如 JSON、XML、HTML 或特定的 Java Bean。现在我们进行代码实战:假设我们期望将模型输出转换为Record纪录类结构体不再是传统的String.那么和以往的一样我们先新建子模块然后改pom,写yml,主启动配置类。接下来是重点步骤:1. 新建纪录类StudentRecordpackage ai.alibaba.records; /** * Description jdk14后的新特性记录类替代lombok */ public record StudentRecord(String id,String sname,String major,String email) { }2. ControllerRestController public class StructuredOutputController { Resource(name qwenChatClient) private ChatClient qwenChatClient; /** * http://localhost:8007/structuredoutput/chat?sname李四emailzzyybs126.com * param sname * return */ GetMapping(/structuredoutput/chat) public StudentRecord chat(String sname,String email) { return qwenChatClient.prompt() .user(new ConsumerChatClient.PromptUserSpec() { Override public void accept(ChatClient.PromptUserSpec promptUserSpec) { promptUserSpec.text(学号1001,我叫{sname},大学专业是计算机科学与技术,邮箱{email}) .param(sname,sname) .param(email,email); } }).call().entity(StudentRecord.class); } /** *简化 * http://localhost:8007/structuredoutput/chat2?sname孙伟emailzzyybs126.com * param sname * return */ GetMapping(/structuredoutput/chat2) public StudentRecord chat2(RequestParam(name sname) String sname, RequestParam(name email) String email) { String stringTemplate 学号1002,我叫{sname},大学专业是软件工程,邮箱{email} ; return qwenChatClient.prompt() .user(promptUserSpec - promptUserSpec.text(stringTemplate) .param(sname,sname) .param(email,email)) .call() .entity(StudentRecord.class); } }九.Chat Memory连续对话保存和持久化9.1 ChatMemory是什么对话记忆:这种记忆机制使得模型能够在对话中持续跟踪和理解用户的意图和上下文从而实现更自然和连贯的对话。而SpringAI Alibaba中的聊天记忆提供了维护AI聊天应用程序的对话上下文和历史的机制。记忆类型分为下述几个:但是因为大模型本身不存储数据需将历史对话信息一次性提供给它以实现连续对话不然服务一重启就什么都没了......所以必须要持久化。在此有两个痛点分别是持久化媒介和消息对话窗口聊天记录上限9.2 持久化开发在 Spring AI Alibaba 中我们不需要手动写List来存消息它提供了一个优雅的组件叫做ChatMemory。持久化如果你希望服务器重启后AI 依然记得以往的聊天记录你就需要持久化。Spring AI Alibaba 支持将记忆存入 Redis、JDBC 或其他数据库。以Redis为例只需要引入对应的ChatMemory实现并注入你的数据源即可。1. 添加依赖首先确保你的pom.xml中引入了 Redis 的相关依赖。由于 Spring AI Alibaba 是基于 Spring Boot 的它能完美利用 Spring Data Redis。!--spring-ai-alibaba memory-redis-- !--这是阿里巴巴专门为 ChatMemory 封装的 Starter-- !--你只要引入它Spring 容器就会自动识别并准备好 Redis 存储环境。-- dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter-memory-redis/artifactId /dependency !--redis客户端-- !--jedis-- dependency groupIdredis.clients/groupId artifactIdjedis/artifactId /dependency2. 配置 Redis 版 ChatMemory(此处可省略)在 Spring AI 中提供了一个专门的RedisChatMemory实现。你只需要把RedisTemplate传给它即可。但是引入了上述的依赖starter后甚至不需要自己去new RedisChatMemory配置会变得非常自动化。在application.yml中配置连接只需要告诉程序 Redis 在哪剩下的交给 Starter//Configuration //public class AIConfig { // Bean // public ChatMemory chatMemory(RedisConnectionFactory connectionFactory) { // 使用 Redis 来存储对话这样即使应用重启记忆依然存在 // return new RedisChatMemory(connectionFactory); // } //}# redis config spring.data.redis.hostlocalhost spring.data.redis.port6379 spring.data.redis.database0 spring.data.redis.connect-timeout3 spring.data.redis.timeout2但是如果你不想要用系统的默认配置你可以进行自定义配置:1.RedisChatMemoryRepository(存redis)ChatMemoryRepository的具体实现类它负责具体的“苦力活”——把 Java 的消息对象序列化成 JSON然后塞进 Redis 的 List 结构里。2.MessageWindowChatMemory(能存多少)它决定了 AI 应该记得“多少”。它维护一个“滑动窗口”比如只保留最近 10 条消息。它不负责存只负责计算“根据当前配置我该从仓库里拿哪几条消息给 AI”举例:3.MessageChatMemoryAdvisor它像一个“自动补全插件”。在你发消息给 AI 的一瞬间它自动去 Redis 拿历史记录在 AI 回复后它自动把回复存回 Redis。笔记要点它是执行层。它是让ChatClient具备“记忆力”的最后一道工序。eg:package ai.alibaba.config; Configuration public class RedisMemoryConfig { Value(${spring.data.redis.host}) private String host; Value(${spring.data.redis.port}) private int port; Bean public RedisChatMemoryRepository redisChatMemoryRepository() { return RedisChatMemoryRepository.builder() .host(host)//告诉它redis的host和port在哪里 .port(port) .build(); } }3. 在业务逻辑中使用修改配置类SaaLLMConfig以往我们的配置如下所示对比一下修改之后的大模型Configuration public class LLMConfig { //deepseek同理 private final String QWEN_MODEL qwen3-max; Bean(name qwen) public ChatModel qwen(){ return DashScopeChatModel.builder() .dashScopeApi(DashScopeApi.builder().apiKey(System.getenv(aliQwen-api)).build()) .defaultOptions(DashScopeChatOptions.builder().withModel(QWEN_MODEL).build()) .build(); } Bean(name qwenChatClient) public ChatClient qwenChatClient(Qualifier(qwen) ChatModel qwen, RedisChatMemoryRepository redisChatMemoryRepository) { MessageWindowChatMemory windowChatMemory MessageWindowChatMemory.builder() .chatMemoryRepository(redisChatMemoryRepository) .maxMessages(10) .build(); return ChatClient.builder(qwen) .defaultOptions(ChatOptions.builder().model(QWEN_MODEL).build()) .defaultAdvisors(MessageChatMemoryAdvisor.builder(windowChatMemory).build()) .build(); } }在实际开发中我们通常会遇到多个用户同时在线的情况。为了不让张三的记忆跑到李四那里我们需要在调用时传入一个唯一的chatId比如用户 ID 或 Session ID。package ai.alibaba.controller; RestController public class ChatMemory4RedisController { Resource(name qwenChatClient) private ChatClient qwenChatClient; GetMapping(/chatmemory/chat) public String chat(String msg, String userId) { /*return qwenChatClient.prompt(msg).advisors(new ConsumerChatClient.AdvisorSpec() { Override public void accept(ChatClient.AdvisorSpec advisorSpec) { advisorSpec.param(CONVERSATION_ID, cid); } }).call().content();*/ return qwenChatClient.prompt(msg)//“嘿通义千问Qwen这是用户刚发的消息msg。” .advisors(advisorSpec - advisorSpec.param(CONVERSATION_ID, userId))//“在发送之前请先去咨询一下我的‘记忆顾问’Advisor。” .call() .content();//“最后把综合了历史记录的回答告诉我。” } }十.文生图文生图相关参考官方文档:大模型服务平台百炼控制台 (aliyun.com)现在先来动手实例:和以往一样先建立子模块Module,改pom写yml主启动注意不用再写config类了然后是controller类:RestController RequestMapping(/ai) public class ImageController { private final ImageModel imageModel; // 自动注入通义万相模型 public ImageController(ImageModel imageModel) { this.imageModel imageModel; } GetMapping(/draw) public String draw(RequestParam String prompt) { // 1. 创建绘画请求指定提示词和参数 ImagePrompt imagePrompt new ImagePrompt(prompt, TongyiImagesOptions.builder() .withN(1) // 生成 1 张图 .withHeight(1024) // 高度 .withWidth(1024) // 宽度 .build()); // 2. 调用模型生成图片 ImageResponse response imageModel.call(imagePrompt); // 3. 返回图片的 URL 地址 return response.getResult().getOutput().getUrl(); } }1. 在 Spring AI 中ImageModel是一个通用的标准接口。2. 在聊天里我们直接传String但在绘图里需求更复杂所以需要用ImagePrompt对象进行封装。组成部分它由文字描述String prompt配置参数ImageOptions组成。十一.向量化和向量数据库什么是向量: 向量是用于表示具有大小和方向的量向量又可以描述为矢量11.2 文本向量化什么是文本向量化呢?嵌入(Embedding)的工作原理是将文本图像和视频转换为向量(Vectors)的浮点型数组。所以文本向量化就是将文本图像和视频转化为浮点型数组然后在打到一个高维的坐标系上。举个例子:总结一下嵌入模型是一种机器学习模型旨在在连续的低维向量空间中表示数据(例如文本图像或者其它形式的信息)。这些切入可以捕获数据之间的语义或者上下文的相似性使机器能够高效地执行比较聚合或分类等任务。假设你想描述不同的水果。你不用长篇大论而是用数字来描述甜度、大小和颜色等特征。例如苹果可能是[8,5,7],而香蕉是[974]。这些数字使比较或对相似的水果进行分组变得更容易。11.3 向量数据库向量存储是一种用于存储和检索高维向量数据的数据库或者存储解决方案。他特别适用于处理那些经过嵌入模型转化后的数据。注意的是它们执行相似性搜索而不是精准匹配。VectorStore用于您的数据和AI模型集成。向量数据库维度越高查询的精准度也就越高查询的效果也会越好。(比如淘宝搜索完想要的商品之后会有猜你喜欢的功能)总结:11.4 代码开发1.首先就是还是建module2.在原来的pom.xml的基础上加入---此处使用RedisStack当向量数据库!-- 添加 Redis 向量数据库依赖 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-vector-store-redis/artifactId /dependency3.写ymlserver.port8011 server.servlet.encoding.enabledtrue server.servlet.encoding.forcetrue server.servlet.encoding.charsetUTF-8 spring.application.nameSAA-11Embed2vector # SpringAIAlibaba Config spring.ai.dashscope.api-key${aliQwen-api} spring.ai.dashscope.chat.options.modelqwen-plus spring.ai.dashscope.embedding.options.modeltext-embedding-v3 # Redis Stack spring.data.redis.hostlocalhost spring.data.redis.port6380 spring.data.redis.usernamedefault spring.ai.vectorstore.redis.initialize-schematrue spring.ai.vectorstore.redis.index-namecustom-index spring.ai.vectorstore.redis.prefixcustom-prefix4.还是写主启动接下来就是重点内容:用redisStack作为向量存储RedisStack是什么:Redis Stack是Redis Labs推出的一个增强版 Redis,不是 Redis 的替代品而是在原生 Redis 基础上的功能扩展包专为构建现代实时应用而设计一句话: RedisStack原生Redis搜索图时间序列JSON概率结构可视化工具开发框架支持redisstack在docker中安装ubuntuhhhhhh:~$ docker run -d --name redis-stack-server \ -p 6380:6379 \ -p 8001:8001 \ redis/redis-stack:latest接下来就是具体业务类:要进行文本向量化向量化存储以及向量化查询组件名称核心接口角色定位嵌入模型EmbeddingModel特征提取器向量数据库VectorStore空间索引器package ai.study.controller; import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingOptions; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.embedding.EmbeddingRequest; import org.springframework.ai.embedding.EmbeddingResponse; import org.springframework.ai.vectorstore.SearchRequest; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.Arrays; import java.util.List; RestController Slf4j public class Embed2VectorController { //文本向量化 Resource private EmbeddingModel embeddingModel; //管存储 Resource private VectorStore vectorStore; /** * 文本向量化 * http://localhost:8011/text2embed?msg射雕英雄传 * * param msg * return */ GetMapping(/text2embed) public EmbeddingResponse text2Embed(RequestParam(msg) String msg) { //EmbeddingResponse embeddingResponse embeddingModel.call(new EmbeddingRequest(List.of(msg), null)); EmbeddingResponse embeddingResponse embeddingModel.call(new EmbeddingRequest(List.of(msg), DashScopeEmbeddingOptions.builder().withModel(text-embedding-v3).build()));//配置文件里的模型全局公用但是想要单粒度使用则需要单独配置可在此配置 System.out.println(Arrays.toString(embeddingResponse.getResult().getOutput())); return embeddingResponse; } /** 文本向量化后存入向量数据哭RedisStack**/ GetMapping(/embed2vector/add) public void add() { ListDocument documents List.of( new Document(i study LLM), new Document(i love java) ); vectorStore.add(documents); } /** *从向量数据库RedisStack查找进行相似度查找 *http://localhost:8011/embed2vector/get?msgLLM */ GetMapping(/embed2vector/get) public List getAll(RequestParam(name msg) String msg) { SearchRequest searchRequest SearchRequest.builder() .query(msg) .topK(2) .build(); ListDocument list vectorStore.similaritySearch(searchRequest); System.out.println(list); return list; } }此时文本向量化的结果如下所示:查询结果