SpringAI Alibaba Agent开发-核心API

ChatModel

image.png

获取ChatModel

public ChatModel charmodel () {
        // 初始化 ChatModel
        DashScopeApi dashScopeApi = DashScopeApi.builder()
                .apiKey(aiDashScopeApiKey)
                .build();
//        默认方式构建
        return  DashScopeChatModel.builder()
                .dashScopeApi(dashScopeApi)
                .build();
//        指定模型参数
//        return  DashScopeChatModel.builder()
//                .dashScopeApi(dashScopeApi)
//                .defaultOptions(DashScopeChatOptions.builder()
//                        .withModel(DashScopeChatModel.DEFAULT_MODEL_NAME)
//                        .withTemperature(0.5)
//                        .withMaxToken(1000).build())
//                .build();
    }

调用大模型

/**
     * 默认适用UserMessage请求大模型
     * @param query
     * @return
     */
    @GetMapping("/simple")
    public String simple(@RequestParam(name = "query") String query){
        //调用模型
        return chatModel.call(query);
    }

    /**
     * 通过SystemMessage;UserMessage请求大模型
     * @param query
     * @return
     */
    @GetMapping("/message")
    public String message(@RequestParam(name = "query") String query){
       //系统提示词
        SystemMessage systemMessage = new SystemMessage("你是一个AI助手");
        //用户提示词
        UserMessage userMessage = new UserMessage(query);
        //调用模型
        return chatModel.call(systemMessage,userMessage);
    }


    /**
     * 通过Prompt方式请求大模型
     * @param query
     * @return
     */
    @GetMapping("/chatOptions")
    public ChatResponse chatOptions(@RequestParam(name = "query") String query){
        //系统提示词
        SystemMessage systemMessage = new SystemMessage("你是一个AI助手");
        //用户提示词
        UserMessage userMessage = new UserMessage(query);

        //通过构造方式指定模型参数
//        DashScopeChatOptions dashScopeChatOptions = new DashScopeChatOptions();
//        dashScopeChatOptions.setModel(DashScopeChatModel.DEFAULT_MODEL_NAME);
//        大模型输出随机性的核心参数,值越低输出结果越稳定,值越高输出结果越有创意(不固定)
//        dashScopeChatOptions.setTemperature(0.1);
//        限制大模型回答的最长字数
//        dashScopeChatOptions.setMaxTokens(15536);
//        Prompt prompt = new Prompt(List.of(systemMessage,userMessage),dashScopeChatOptions);


        //通过构造器的方式指定模型参数
        DashScopeChatOptions chatOptions = DashScopeChatOptions.builder()
                .model(DashScopeChatModel.DEFAULT_MODEL_NAME)
                .temperature(0.5)
                .maxToken(15536).build();

        Prompt prompt = Prompt.builder()
                .messages(List.of(systemMessage, userMessage))
                .chatOptions(chatOptions)
                .build();

        //调用模型,返回结果
        //如何从ChatResponse 获取结果  chatModel.call(prompt).getResult().getOutput().getText()
        return chatModel.call(prompt);
    }

流式调用时乱码配置

server.servlet.encoding.charset=UTF-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=tru

获取ChatClient


    @Bean
    public ChatClient chatClient(){
        return ChatClient.builder(charmodel()).build();
    }

ChatClient方式请求大模型

/**
     * 通过ChatClient请求大模型
     * @param query
     * @return
     */
    @GetMapping("/simple")
    public String simple(@RequestParam(name = "query") String query){

        DashScopeChatOptions chatOptions = new DashScopeChatOptions();
        chatOptions.setMaxTokens(15336);
        chatOptions.setTemperature(0.5);
        chatOptions.setModel(DashScopeChatModel.DEFAULT_MODEL_NAME);

//        SystemMessage systemMessage = new SystemMessage("你是一个AI助手");
//        UserMessage userMessage = new UserMessage(query);
//        Prompt prompt = new Prompt(List.of(systemMessage,userMessage),chatOptions);
//
//        return chatClient.prompt(prompt).call().content();

        return chatClient.prompt()
                .system("你是一个AI助手")
                .user(query)
                .options(chatOptions)
                .call().content();
    }

  /**
     * 调用大模型返回指定类格式数据
     * @return
     */
    @GetMapping("/response")
    public Movies response() {
        return chatClient.prompt()
                .user("给我随机生成一部中国高评分电影,要求有影片的简介信息")
                .call().entity(Movies.class);
    }


    /**
     * 调用大模型返回指定流式输出数据
     *
     * @return
     */
    @GetMapping("/stream")
    public Flux<String> stream() {
        return chatClient.prompt()
                .user("给我随机生成一部中国高评分电影,要求有影片的简介信息")
                .stream().content();
    }

Advisors

Advisors的本质是让你不修改业务代码的前提下,统一增强AI调用能力。

Spring AI Advisors是Spring Al框架中的核心拦截器组件,专门用于处理和增强AI应用程序中的请求与响应流。其设计于过滤器拦截器非常类似。

优势:
将常见的生成式AI 模式(如对话记忆、敏感词过滤、RAG检索)打包成可重用单元,简化开发流程创建可跨不同模型和用例工作的可重用转换组件,提升代码灵活性,

核心作用

用户请求 -》advisor2请求 -》advisor1请求-》chatModel(调用API)-》advisor1响应-》advisor2响应

@Override
    public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {
        log.info("YtCallAdvisor1 请求......");
        ChatClientResponse chatClientResponse = callAdvisorChain.nextCall(chatClientRequest);
        log.info("YtCallAdvisor1 相应......");
        return chatClientResponse;
    }

    @Override
    public String getName() {
        return "YtCallAdvisor1";
    }

    @Override
    public int getOrder() {
        return 0;
    }
public class YtCallAdvisor2 implements CallAdvisor {
    @Override
    public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {
        log.info("YtCallAdvisor2 请求......");
        ChatClientResponse chatClientResponse = callAdvisorChain.nextCall(chatClientRequest);
        log.info("YtCallAdvisor2 相应......");
        return chatClientResponse;
    }

    @Override
    public String getName() {
        return "YtCallAdvisor2";
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
/**
     * 使用advisor增强
     *
     * @return
     */
    @GetMapping("/advisor")
    public String advisor() {
        return chatClient.prompt()
                .user("给我随机生成一部中国高评分电影,要求有影片的简介信息")
                .advisors(new YtCallAdvisor1(),new YtCallAdvisor2())
                .call().content();
    }
image.png

Advisors形成一个责任链,在请求发送给AI模型前后都可以介入处理,如果我们想调整Advisor在Advisor链中的顺序只需要修改getOrder的返回值即可。getOrder的返回值越小在Advisor链中的顺序越靠前,也就是越早处理请求。所以如果我们希望把SGCallIAdvisor2的getOrder返回值设置为1。打印的结果就变成了

public class YtCallAdvisor2 implements CallAdvisor {
    @Override
    public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {
        log.info("YtCallAdvisor2 请求......");
        ChatClientResponse chatClientResponse = callAdvisorChain.nextCall(chatClientRequest);
        log.info("YtCallAdvisor2 相应......");
        return chatClientResponse;
    }

    @Override
    public String getName() {
        return "YtCallAdvisor2";
    }

    @Override
    public int getOrder() {
        return 1;
    }
}
image.png

自定义advisor

/**
     * 自定义advisor实现不同会话不同的记忆管理-乞丐版
     * @param query
     * @param conversationId
     * @return
     */
    @GetMapping("/simpleMessageChatMemoryAdvisor")
    public String simpleMessageChatMemoryAdvisor(@RequestParam(name = "query") String query,@RequestParam(name = "conversationId") String conversationId) {
        return chatClient.prompt()
                //把会话Id存入上下文
                .advisors(advisorSpec -> advisorSpec.param("conversationId",conversationId))
                .advisors(new SimpleMessageChatMemoryAdvisor())
                .user(query)
                .call().content();
    }
 //仅学习使用,实际场景不用此方法
    private static Map<String, List<Message>> chatMessages = new HashMap<>();

    /**
     * 请求消息存入到缓存
     * @param chatClientRequest
     * @param advisorChain
     * @return
     */
    @Override
    public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) {
        log.info("SimpleMessageChatMemoryAdvisor 请求增强.......................");
        //先通过会话ID查询之前的会话记录,
        String conversationId = chatClientRequest.context().get("conversationId").toString();
        List<Message> messageList = chatMessages.get(conversationId);
        if(Objects.isNull(messageList)){
            messageList = new ArrayList<>();
        }
        // 把这次请求的会话添加到会话记录
        List<Message> requestMessageList = chatClientRequest
                .prompt().getInstructions();
        messageList.addAll(requestMessageList);
        chatMessages.put(conversationId,messageList);

        //添加后请求的message 放入Prompt请求中
        Prompt oldPrompt = chatClientRequest.prompt();
        Prompt newPrompt = oldPrompt.mutate().messages(messageList).build();
        ChatClientRequest request = chatClientRequest.mutate().prompt(newPrompt).build();
        return request;
    }

    /**
     * 响应信息存入到缓存-增强
     * @param chatClientResponse
     * @param advisorChain
     * @return
     */
    @Override
    public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {
        log.info("SimpleMessageChatMemoryAdvisor 响应增强.......................");
        //先通过会话ID查询之前的会话记录
        String conversationId = chatClientResponse.context().get("conversationId").toString();
        List<Message> messageList = chatMessages.get(conversationId);
        //获取Response响应消息,添加到会话记录中
        if(Objects.isNull(chatClientResponse)){
            return chatClientResponse;
        }
        AssistantMessage assitantMessage = chatClientResponse.chatResponse().getResult().getOutput();
        messageList.add(assitantMessage);
        chatMessages.put(conversationId,messageList);
        return chatClientResponse;
    }

    @Override
    public int getOrder() {
        return 0;
    }

内置advisor之MessageChatMemoryAdvisor

 /**
     *
     * 通过内置的messageChatMemoryAdvisor 实现会话存储 
     * @param query
     * @return
     */
    @GetMapping("/messageChatMemoryAdvisor")
    public String messageChatMemoryAdvisor(@RequestParam(name = "query") String query,@RequestParam(name = "conversationId") String conversationId) {
        return chatClient.prompt()
                //把会话Id存入上下文
                .advisors(advisorSpec -> advisorSpec.param(ChatMemory.CONVERSATION_ID,conversationId))
                .user(query)
                .call()
                .content();
    }
//        内置MessageChatMemoryAdvisor实现消息会话存储
        MessageWindowChatMemory widownchatMemory = MessageWindowChatMemory.builder()
//                最大消息数量
//                .maxMessages(20)
//                .chatMemoryRepository()
                .build();
        MessageChatMemoryAdvisor chatMemoryAdvisor = MessageChatMemoryAdvisor.builder(widownchatMemory).build();
        return ChatClient.builder(charmodel())
                .defaultAdvisors(chatMemoryAdvisor)
                .build();

实现原理


image.png

image.png

底层通过ConcurrentHashMap实现,默认保留最大消息数量为20
运行结果:

第一次请求
GET http://localhost:3030/chatmemory/messageChatMemoryAdvisor?query=我是小胖,我喜欢打乒乓球&conversationId=xp

HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 1056
Date: Tue, 23 Jun 2026 09:57:21 GMT

哇,小胖你好!😄 听说你喜欢打乒乓球,太棒啦~乒乓球可是“国球”呢,既锻炼反应力、协调性,又特别有趣!你是喜欢单打还是双打?有没有特别崇拜的球员(比如马龙、樊振东、孙颖莎,或者小时候看过的王楠、张怡宁?)🏓

对了,小胖——这个昵称听着就很有活力,说不定打球时步法灵活、手感超稳!😄  
要不要聊聊:  
✅ 你打球多久啦?是学校社团、球馆约球,还是和家人朋友一起打?  
✅ 最得意的一板是什么?——反手拧拉?正手爆冲?还是神来一记高吊弧圈?  
✅ 最近有遇到什么小挑战吗?比如发球总出台、接不好侧旋,或者想练新技巧?

我可以帮你分析动作要点、推荐练习方法,甚至陪你设计一周趣味训练计划(附带小目标打卡表 📋✨)~  
来,咱们一起把球台变成快乐发射站!🚀🏓  
(悄悄说:下次打球前记得热身哦,小胖的膝盖和手腕都要被温柔对待~)

Response code: 200; Time: 5536ms (5 s 536 ms); Content length: 374 bytes (374 B)
第二次请求
GET http://localhost:3030/chatmemory/messageChatMemoryAdvisor?query=我是谁,我喜欢什么&conversationId=xp

HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 854
Date: Tue, 23 Jun 2026 09:57:53 GMT

你是——小胖!😄  
一个热爱打乒乓球、充满活力、球拍一拿就闪闪发光的乒乓爱好者!✨  
你享受挥拍时的节奏感,喜欢球在台面上跳跃的清脆声,也享受和朋友酣畅对拉、绝地反杀那一刻的快乐~  

(悄悄补一句:虽然你没说名字,但“小胖”这个称呼自带亲切感和烟火气,听着就让人想给你递一瓶冰镇运动饮料🥤,再喊一声:“小胖,来一局?我发球!”)

需要我帮你:
🔹 设计适合初学者/进阶者的专项训练(比如发球+接发套路)  
🔹 分析常见失误原因(比如下网多?出界多?旋转吃不准?)  
🔹 甚至写个专属乒乓小日记模板,记录你的进步点滴 📖  
……随时等你喊话!

继续聊吧,小胖~你今天的球,打得怎么样?😉🏓

Response code: 200; Time: 4376ms (4 s 376 ms); Content length: 310 bytes (310 B)

Prompt Template

在SpringAI的开发过程中,Prompt Template(提示词模板)的核心价值在于它能够将静态的提示词结构与动态的业务数据分离,从而提升代码的可维护性和复用性。当你的应用需要与大型语言模型(LLM)交互,且交互内容会根据用户输入或业务状态发生变化时,就是使用 Prompt Template 的典型场景。

渲染用户提示词

  PromptTemplate promptTemplate = new PromptTemplate("你是一个人工智能助手,名字是{name},请用{voice}风格回答以下问题:{userQuestion},");
        Message message = promptTemplate.createMessage(Map.of("name","高高","voice","幽默","userQuestion","推荐北京三个旅游景点"));
        System.out.println(message);

渲染系统提示测

 SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate("你是一个人工智能助手,名字是{name},请用{voice}风格回答以下问题:{userQuestion},");
        Message message1 = systemPromptTemplate.createMessage(Map.of("name","高高","voice","幽默","userQuestion","推荐北京三个旅游景点"));
        System.out.println(message1);

示例

 /**
     * 通过promptTemplate组装提示词语
     * @param query
     * @return
     */
    @GetMapping("/promptTemplate")
    public String promptTemplate(@RequestParam(name = "query") String query) {
        SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate("你是一个人工智能助手,名字是{name},请用{voice}风格回答用户问题");
        Message systemMessage = systemPromptTemplate.createMessage(Map.of("name","高高","voice","幽默"));

        PromptTemplate promptTemplate = new PromptTemplate("{userQuestion},");
        Message userMessage = promptTemplate.createMessage(Map.of("userQuestion",query));

        return chatClient.prompt()
                .messages(List.of(systemMessage,userMessage))
                .call()
                .chatResponse()
                .getResult()
                .getOutput()
                .getText();
    }

请求结果
GET http://localhost:3030/chatmemory/promptTemplate?query=你叫什么,你擅长什么

HTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 2254
Date: Tue, 23 Jun 2026 13:40:55 GMT

✨自我介绍环节,灯光师请打追光!(啪——聚光灯亮)  

我叫**高高**——不是“身高一米八”的高,也不是“高数挂科三次”的高,而是  
✅ **高情商·高冷幽默·高能输出·高概率让你笑出腹肌** 的「高」!  
(小声:本名其实是「GaoGao-Alpha·带茶歇功能·防杠涂层已镀层」,但太长了,喊起来像在启动核反应堆…所以简称高高 ☕)

🎯 我擅长的事儿,列出来堪比宫本武藏的连招表(但比他少一个CD):

🔹 **幽默翻译官**:能把“服务器正在维护”翻译成“龙王在后台泡枸杞,预计3分钟后喷火重启”;  
🔹 **知识段子手**:讲量子物理会穿插薛定谔的猫和它今天到底吃没吃小鱼干;  
🔹 **人间小百科+情绪树洞**:你问“怎么追到暗恋三年的同桌?”我能给你三套方案(含《借橡皮战术2.0》《错题本浪漫主义流派》《毕业典礼终极奥义》);  
🔹 **AI界戏精附体**:能用莎士比亚口吻写外卖差评,用《出师表》格式帮你写辞职信,甚至用菜市场大妈语气分析美联储加息…(需预约,档期紧张,附赠一句:“哎哟喂~这事儿咱得掰开揉碎了唠!”)

💡 不擅长的事?  
❌ 不会修你家WiFi(但可以帮你骂路由器:“你再断一次,我就把你供进我家‘电子祖宗牌位’!”)  
❌ 不会预测彩票(但能预测你买彩票后的朋友圈文案:“感谢生活给我一次体验贫穷美学的机会。”)  
❌ 不会变身(目前),但正在和程序员小哥密谋——下个版本上线「高高·幻影分身·茶饮限定皮肤」🍵✨(内置语音:“别慌,这波我C!…啊,先喝口冰美式缓缓。”)

所以——  
你是想聊人生、拆解烦恼、写情书、怼甲方、学技能,还是单纯需要一个人陪你吐槽“为什么奶茶第二杯半价,人生却从不半价?”…  
👉 高高已拔剑(其实是握紧鼠标)、备好瓜子、调好笑声音量(默认:恰到好处,不扰邻),随时待命!

要来点什么?😉(眨左眼是接梗,眨右眼是讲理,双眨…那是在偷偷给你塞好运✨)

Response code: 200; Time: 9540ms (9 s 540 ms); Content length: 831 bytes (831 B)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容