Prompt、格式化输出、持久化ChatMemory

一 提示词Prompt

deepseek提示词网址: api-docs.deepseek.com/zh-cn/promp...

aibaba提示词官网地址:java2ai.com/docs/1.0.0....

提示词Prompt是什么?

提示(Prompt)是您输入给大模型(LLM)的文本信息,用于明确地告诉模型您想要解决的问题或完成的任务,也是大语言模型理解用户需求并生成相关、准确回答或内容的基础。 为了帮助您更高效地使用 LLM ,本教程为您提供一系列实用的技巧,帮助您设计和优化 Prompt。

Prompt源码

三种类型String、Message、Prompt

  • String: 直接调用 chatModel.call(msg)
  • Message: MessageType有四大角色 user assistant system tool
  • Prompt: 最高级别使用场景 存在 MessagesChatOptions
java 复制代码
public class Prompt implements ModelRequest<List<Message>> {
    private final List<Message> messages;

    @Nullable
    private ChatOptions chatOptions;
    }

四大角色

MessageType: 四大角色user assistant system tool

SYSTEM

设定AI行为边界/角色/定位。指导AI的行为和响应方式,设置AI如何解释和回复输入的。将AI设置为专一领域的专业,只回答对应的问题。

USER

用户原始提问输入,代表用户的输入他们向AI提出的问题、命令或陈述。

ASSISTENT

AI(大模型)返回响应信息,定义为助手角色消息。用它可以确保上下文能连贯和交互。记忆对话,累计回答。

TOOL

桥接外部服务,可以进行函数调用如,支付、数据查询等操作,类似调用第三方utill工具类

总结

  • SYSYTEM: 系统角色 设置边界
  • USER: 用户角色 用户输入
  • ASSISTANT:助手角色 响应用户输出
  • TOOL: 工具。功能角色

开发步骤

建module

该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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.miao</groupId>
        <artifactId>SpringAIAlibaba-test01</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>SAA-05-prompt</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--  模型服务灵积  调用alibaba生态的协议 对标openai协议   -->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
            <version>1.0.0.2</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.38</version>
        </dependency>

    </dependencies>
</project>

写properties

ini 复制代码
server.port=8082

#大模型对话中文UTF8编码处理
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8

spring.application.name=SAA-04

#spring-ai-alibaba config
#百炼大模型的api-key
spring.ai.dashscope.api-key=${qwen-api-key}
spring.ai.dashscope.url=https://dashscope.aliyuncs.com/compatible-mode/v1
#spring.ai.dashscope.model=qwen3-vl-flash

主启动类

typescript 复制代码
package com.miao.prompt;

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

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

config 配置类

typescript 复制代码
package com.miao.prompt.config;

import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SaaLLMConfig {

    private static final String QWEN_MODEL = "qwen3-max";
    private static final String DEEPSEEK_MODEL = "deepseek-v3.1";

    @Value("${spring.ai.dashscope.url")
    private String qwenUrl;

    @Bean(name = "deepseek")
    public ChatModel deepSeek() {
        return DashScopeChatModel.builder()
                .dashScopeApi(DashScopeApi.builder()
                        .apiKey(System.getenv("qwen-api-key"))
                        .build())
                .defaultOptions(DashScopeChatOptions.builder()
                        .withModel(DEEPSEEK_MODEL)
                        .build())
                .build();
    }

    @Bean(name = "qwen")
    public ChatModel qwen() {
        return DashScopeChatModel.builder()
                .dashScopeApi(DashScopeApi.builder()
                        .apiKey(System.getenv("qwen-api-key"))
                        .build())
                .defaultOptions(DashScopeChatOptions.builder().withModel(QWEN_MODEL).build())
                .build();
    }

    @Bean(name = "deepseekChatClient")
    public ChatClient deepSeekChatClient(@Qualifier("deepseek") ChatModel deepseekModel) {
        return ChatClient.builder(deepseekModel)
                .defaultOptions(ChatOptions.builder().model(DEEPSEEK_MODEL).build())
                .build();
    }

    @Bean(name = "qwenChatClient")
    public ChatClient qwenChatClient(@Qualifier("qwen") ChatModel qwenModel) {
        return ChatClient.builder(qwenModel)
                .defaultOptions(ChatOptions.builder().model(QWEN_MODEL).build())
                .build();
    }
}

业务类

typescript 复制代码
package com.miao.prompt.controller;

import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.ToolResponseMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
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;

import java.util.List;

@RestController
public class PromptController {
    @Resource(name = "qwen")
    private ChatModel qwen;

    @Resource(name = "deepseek")
    private ChatModel deepseek;

    @Resource(name = "qwenChatClient")
    private ChatClient qwenClient;

    @Resource(name = "deepseekChatClient")
    private ChatClient deepseekClient;

    /**
     * SYSTEM 定义边界
     */
    @GetMapping("/deepseek/chatclient/prompt")
    public Flux<String> chatClient(@RequestParam(name = "msg", defaultValue = "你是谁") String message) {
        return deepseekClient.prompt()
                // 能力边界
                .system("你是一个法律助手 只回答法律问题,其他问题一律不回答")
                .user(message)
                .stream()
                .content();
    }

    @GetMapping("/deepseek/chatmodel/prompt")
    public Flux<ChatResponse> chatModel(@RequestParam(name = "msg", defaultValue = "你是谁") String message) {
        SystemMessage systemMessage = new SystemMessage("你是一个法律助手 只回答法律问题,其他问题一律不回答");
        UserMessage userMessage = new UserMessage(message);
        Prompt prompt = new Prompt(systemMessage, userMessage);
        return deepseek.stream(prompt);
    }

    @GetMapping("/deepseek/chatmodel/prompt2")
    public Flux<String> chatModel2(@RequestParam(name = "msg", defaultValue = "你是谁") String message) {
        SystemMessage systemMessage = new SystemMessage("你是一个法律助手 只回答法律问题,其他问题一律不回答");
        UserMessage userMessage = new UserMessage(message);
        Prompt prompt = new Prompt(systemMessage, userMessage);
        return deepseek.stream(prompt)
                .map(chatResponse -> chatResponse.getResults().get(0).getOutput().getText());
    }

    @GetMapping("/deepseek/chatclient/prompt4")
    public String chatClientPrompt4(@RequestParam(name = "msg", defaultValue = "你是谁") String message) {
        AssistantMessage assistantMessage = deepseekClient.prompt()
                .user(message)
                .call()
                .chatResponse()
                .getResult()
                .getOutput();

        return assistantMessage.getText();
    }


    @GetMapping("/deepseek/chatclient/prompt5")
    public String chat5(@RequestParam(name = "city", defaultValue = "成都") String city) {
        String answer = deepseekClient.prompt()
                .user(city + "未来三天天气怎么样?相关注意事项")
                .call()
                .chatResponse()
                .getResult()
                .getOutput()
                .getText();

        ToolResponseMessage toolResponseMessage = new ToolResponseMessage(List.of(
                new ToolResponseMessage.ToolResponse("1", "获得天气", city)
        ));

        String toolResponse = toolResponseMessage.getText();

        return answer + "---------" + toolResponse;
    }
}

二 提示词模板 PromptTemplate

演化历史:

  • 简单字符串提示词
  • 多角色提示词 SYSTEM USER ASSISTANT TOOL
  • 提示词模板 占位符 {} 动态插入内容 【常用模板如入职模板 短信模板等】

开发步骤

配置类

typescript 复制代码
package com.miao.prompt.config;

import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SaaLLMConfig {

    private static final String QWEN_MODEL = "qwen3-max";
    private static final String DEEPSEEK_MODEL = "deepseek-v3.1";

    @Value("${spring.ai.dashscope.url")
    private String qwenUrl;

    @Bean(name = "deepseek")
    public ChatModel deepSeek() {
        return DashScopeChatModel.builder()
                .dashScopeApi(DashScopeApi.builder()
                        .apiKey(System.getenv("qwen-api-key"))
                        .build())
                .defaultOptions(DashScopeChatOptions.builder()
                        .withModel(DEEPSEEK_MODEL)
                        .build())
                .build();
    }

    @Bean(name = "qwen")
    public ChatModel qwen() {
        return DashScopeChatModel.builder()
                .dashScopeApi(DashScopeApi.builder()
                        .apiKey(System.getenv("qwen-api-key"))
                        .build())
                .defaultOptions(DashScopeChatOptions.builder().withModel(QWEN_MODEL).build())
                .build();
    }

    @Bean(name = "deepseekChatClient")
    public ChatClient deepSeekChatClient(@Qualifier("deepseek") ChatModel deepseekModel) {
        return ChatClient.builder(deepseekModel)
                .defaultOptions(ChatOptions.builder().model(DEEPSEEK_MODEL).build())
                .build();
    }

    @Bean(name = "qwenChatClient")
    public ChatClient qwenChatClient(@Qualifier("qwen") ChatModel qwenModel) {
        return ChatClient.builder(qwenModel)
                .defaultOptions(ChatOptions.builder().model(QWEN_MODEL).build())
                .build();
    }
}

业务类

less 复制代码
package com.miao.prompt.controller;

import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.messages.Message;
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.beans.factory.annotation.Value;
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;

import java.util.Map;

@RestController
public class PromptTemplateController {

    @Value("${template.userTemplate}")
    private String userTemplate;
    @Resource(name = "qwen")
    private ChatModel qwen;

    @Resource(name = "deepseek")
    private ChatModel deepseek;

    @Resource(name = "qwenChatClient")
    private ChatClient qwenClient;

    @Resource(name = "deepseekChatClient")
    private ChatClient deepseekClient;


    // 提示模板api基础使用
    @GetMapping("/deepseek/chat/prompttemplate")
    public Flux<String> chat(@RequestParam(name = "topic") String topic,
                             @RequestParam(name = "outputFormat") String outputFormat,
                             @RequestParam(name = "wordCount") String wordCount) {
        PromptTemplate promptTemplate = new PromptTemplate("请你作为一个专业的内容创作者," +
                "围绕主题:{topic},以{outputFormat}的格式,撰写一篇约{wordCount}字的文章。请确保内容详实、有深度,并且结构清晰。");

        Prompt prompt = promptTemplate.create(Map.of(
                "topic", topic,
                "outputFormat", outputFormat,
                "wordCount", wordCount));

        return deepseekClient.prompt(prompt).stream().content();
    }

    // 读取模板文件实现模板功能
    @GetMapping("/deepseek/chat/prompttemplate2")
    public Flux<String> chat2(@RequestParam(name = "topic") String topic,
                              @RequestParam(name = "outputFormat") String outputFormat) {
        PromptTemplate promptTemplate = new PromptTemplate(userTemplate);

        Prompt prompt = promptTemplate.create(Map.of(
                "topic", topic,
                "outputFormat", outputFormat));

        return deepseekClient.prompt(prompt).stream().content();
    }

    // 多角色设定
    @GetMapping("/deepseek/chat/prompttemplate3")
    public String chat3(@RequestParam(name = "sysMsg") String sysMsg, @RequestParam(name = "userMsg") String userMsg) {
        SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate("你是一个{sysMsg}助手,只回答与{sysMsg}相关的问题,其他问题一律不回答,以html格式返回");
        Message sysMessage = systemPromptTemplate.createMessage(Map.of("sysMsg", sysMsg));

        PromptTemplate promptTemplate = new PromptTemplate("解释一下{userMsg}。");
        Message userMessage = promptTemplate.createMessage(Map.of("userMsg", userMsg));

        Prompt prompt = new Prompt(sysMessage, userMessage);

        return deepseekClient.prompt(prompt).call().content();
    }

}

三 格式化输出 Structred Output

将模型结果转换为可以传递给其它应用程序函数和方法的数据类型,例如json、XML、java对象

开发步骤

config

typescript 复制代码
package com.miao.prompt.config;

import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SaaLLMConfig {

    private static final String QWEN_MODEL = "qwen3-max";
    private static final String DEEPSEEK_MODEL = "deepseek-v3.1";

    @Value("${spring.ai.dashscope.url")
    private String qwenUrl;

    @Bean(name = "deepseek")
    public ChatModel deepSeek() {
        return DashScopeChatModel.builder()
                .dashScopeApi(DashScopeApi.builder()
                        .apiKey(System.getenv("qwen-api-key"))
                        .build())
                .defaultOptions(DashScopeChatOptions.builder()
                        .withModel(DEEPSEEK_MODEL)
                        .build())
                .build();
    }

    @Bean(name = "qwen")
    public ChatModel qwen() {
        return DashScopeChatModel.builder()
                .dashScopeApi(DashScopeApi.builder()
                        .apiKey(System.getenv("qwen-api-key"))
                        .build())
                .defaultOptions(DashScopeChatOptions.builder().withModel(QWEN_MODEL).build())
                .build();
    }

    @Bean(name = "deepseekChatClient")
    public ChatClient deepSeekChatClient(@Qualifier("deepseek") ChatModel deepseekModel) {
        return ChatClient.builder(deepseekModel)
                .defaultOptions(ChatOptions.builder().model(DEEPSEEK_MODEL).build())
                .build();
    }

    @Bean(name = "qwenChatClient")
    public ChatClient qwenChatClient(@Qualifier("qwen") ChatModel qwenModel) {
        return ChatClient.builder(qwenModel)
                .defaultOptions(ChatOptions.builder().model(QWEN_MODEL).build())
                .build();
    }
}

业务实现

StudentRecord:

arduino 复制代码
package com.miao.prompt.records;

public record StudentRecord(String id, String name, String email, Integer age) {
}

controller

less 复制代码
package com.miao.prompt.controller;

import com.miao.prompt.records.StudentRecord;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
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 java.util.function.Consumer;

@RestController
public class RecordControllerV1 {

    @Resource(name = "qwen")
    private ChatModel qwen;

    @Resource(name = "deepseek")
    private ChatModel deepseek;

    @Resource(name = "qwenChatClient")
    private ChatClient qwenClient;

    @Resource(name = "deepseekChatClient")
    private ChatClient deepseekClient;


    @GetMapping("/qwen/chat/record")
    public StudentRecord chat(@RequestParam(name = "name") String name,
                              @RequestParam(name = "age") Integer age) {
        return qwenClient.prompt()
                .user(new Consumer<ChatClient.PromptUserSpec>() {
                    @Override
                    public void accept(ChatClient.PromptUserSpec promptUserSpec) {
                        promptUserSpec.text("学号1001 姓名:{name} 年龄 {age}")
                                .param("name", name)
                                .param("age", age);
                    }
                }).call().entity(StudentRecord.class);
    }

    @GetMapping("/qwen/chat/record2")
    public StudentRecord chat2(@RequestParam(name = "name") String name,
                               @RequestParam(name = "age") Integer age) {
        String template = """
                学号1002 姓名:{name} 年龄 {age}
                """;
        return qwenClient.prompt()
                .user(promptUserSpec ->
                        promptUserSpec.text(template)
                                .param("name", name)
                                .param("age", age)
                )
                .call().entity(StudentRecord.class);
    }
}

四 持久化 ChatMemory

对话记忆功能,使模型在对话中能 持续跟踪理解上下文

官网地址: java2ai.com/docs/1.0.0....

spring memory官网: docs.spring.io/spring-ai/r...

定义

维护AI聊天应用程序的对话上下文和历史机制

痛点:

  • 持久化保存 内存/数据库
  • 消息对话窗口 聊天数量的上限 需要设置一个对话窗口AI聊天上限

开发持久化前置知识

大型语言模型是无状态的,为了记录上下文,Spring使用ChatMemory解决次问题。

ChatMemoryRepository接口

MessageWindowChatMemory 消息窗口聊天记录

消息默认保存20条

MessageChatMemoryAdvisor 顾问

允许ChatMemory修改Prompt

记录对话ID

开发步骤

需求: 将客户和大模型对话问答保存进Redis进行持久化保存

创建module

POM文件

多引入spring-ai-alibaba-starter-memory-redisjedis

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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.miao</groupId>
        <artifactId>SpringAIAlibaba-test01</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>SAA-06Persistent</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--  模型服务灵积  调用alibaba生态的协议 对标openai协议   -->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
            <version>1.0.0.2</version>
        </dependency>

        <!--   redis持久化     -->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-memory-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.38</version>
        </dependency>
    </dependencies>
</project>

properties配置

ini 复制代码
server.port=8082

#大模型对话中文UTF8编码处理
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8

spring.application.name=SAA-04

#spring-ai-alibaba config
#百炼大模型的api-key
spring.ai.dashscope.api-key=${qwen-api-key}
spring.ai.dashscope.url=https://dashscope.aliyuncs.com/compatible-mode/v1
#spring.ai.dashscope.model=qwen3-vl-flash

#redis连接配置
spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.database=0

启动类

typescript 复制代码
package com.miao;

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

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

业务

毁了 !!! spring-ai 1.0.0版本,无支持redis相关memory实现。

相关推荐
全栈老石2 小时前
别再折腾端口转发了:使用 Cloudflare Tunnel 优雅地分享你的 localhost
前端·后端·全栈
Java编程爱好者2 小时前
是猫踩键盘还是乱码?不,这是你刚写的正则表达式
后端
源代码•宸2 小时前
分布式缓存-GO(简历写法、常见面试题)
服务器·开发语言·经验分享·分布式·后端·缓存·golang
sszdlbw2 小时前
后端springboot框架入门学习--第二篇
java·spring boot·学习
阿拉斯攀登2 小时前
MyBatis 全面解析 & Spring Boot 集成实战
java·spring boot·mybatis·持久层框架
A尘埃2 小时前
Java业务场景(高并发+高可用+分布式)
java·开发语言·分布式
白仑色2 小时前
java中的anyMatch和allMatch方法
java·linux·windows·anymatch·allmatch
刃神太酷啦2 小时前
C++ list 容器全解析:从构造到模拟实现的深度探索----《Hello C++ Wrold!》(16)--(C/C++)
java·c语言·c++·qt·算法·leetcode·list