欢迎光临
我们一直在努力

Spring AI 1.1.7快速入门

什么是Spring AI

Spring AI 是一个基于 Spring 生态的 AI 集成框架,要求JDK17

依赖配置

<properties>
<java.version>17</java.version>
<spring-ai.version>1.1.7</spring-ai.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pdf-document-reader</artifactId>
</dependency>
</dependencies>

application.yml api key 配置

spring:
ai:
openai:
api-key: # 你的 Kimi API Key
base-url: https://api.moonshot.cn/
chat:
options:
model: kimi-k2.5
temperature: 1.0
max-tokens: 4096
logging:
level:
org.springframework.ai.chat.client.advisor: debug
com.itheima.ai: debug

 常见问题

如何构建prompt

指定role的值

system   为 项目级的命令(prompt)

user      当前用户级的命令

assistant   当前会话所有的用户与ai 的输出

原始JSON

[
{
"role": "system",
"content": "你是通过当前项目使用spring ai构建的ai agent…"
},
{
"role": "user",
"content": "你好"
},
{
"role": "assistant",
"content": "你好!我是基于 Spring AI 构建的 AI Agent…"
}

]

在@Configuration的配置类中注册为Bean,spring会管理这个对象,包括但不限于自动注入容器

system  prompt

通过 builder.defaultSystem("")指定system  prompt

@Bean
public ChatClient chatClient(ChatClient.Builder builder, ChatMemory chatMemory) {
return builder
.defaultSystem("你是通过当前项目使用spring ai构建的ai agent,你现在只有基础对话,会话记忆,展示会话历史的功能,如果你不能提供相关功能可以拒绝服务")
.defaultUser("你好")
.defaultAdvisors(new SimpleLoggerAdvisor(),
MessageChatMemoryAdvisor.builder(chatMemory).build()
)

.build();
}

user prompt

通过  chatClient.user(prompt)指定 user prompt,prompt值是前端传递的用户输入

@RequiredArgsConstructor
@RestController
@RequestMapping("/ai")
public class ChatController {

private final ChatClient chatClient;

private final ChatHistoryRepository chatHistoryRepository;

@PostMapping(value="/chat",produces = "text/event-stream;charset=utf-8")
public Flux <String> chat(String prompt, String chatId){

//保存会话id
chatHistoryRepository.save("chat",chatId);//type需要改造为枚举
//请求模型
return chatClient.prompt()
.advisors(a -> a.param(CONVERSATION_ID, chatId != null ? chatId : "default_conversation_id"))
.user(prompt)
.stream()
.content();

}

}

assistant prompt

通过 MessageChatMemoryAdvisor.builder(chatMemory).build() 将 chatMemory (包含 用户与ai 的输出)加入到assistant,可以通过该方法实现会话记忆功能

@Configuration
public class CommonConfiguration {

@Bean
public ChatMemory chatMemory() {
return MessageWindowChatMemory
.builder()
.maxMessages(10)//不写默认为10
.build();
}
//这意味着该聊天记忆组件默认会保留最近的 10 条消息作为对话上下文。当新消息加入且超过此限制时,最旧的消息将被移除(滑动窗口机制)。
@Bean
public ChatClient chatClient(ChatClient.Builder builder, ChatMemory chatMemory) {
return builder
.defaultSystem("你是通过当前项目使用spring ai构建的ai agent,你现在只有基础对话,会话记忆,展示会话历史的功能,需要帮助调用者调试当前项目的输出")

.defaultAdvisors(new SimpleLoggerAdvisor(),
MessageChatMemoryAdvisor.builder(chatMemory).build()
)
.build();
}
}

如何控制台输出相关内容

通过Spring  AOP 环绕增强 

new SimpleLoggerAdvisor()

@Bean
public ChatClient chatClient(ChatClient.Builder builder, ChatMemory chatMemory) {
return builder
.defaultSystem("你是通过当前项目使用spring ai构建的ai agent,你现在只有基础对话,会话记忆,展示会话历史的功能,需要帮助调用者调试当前项目的输出")

.defaultAdvisors(new SimpleLoggerAdvisor() /*,省略*/
)
.build();
}
}

如何流式输出给前端

通过 public Flux <String> chat(String prompt, String chatId)指定为Flux流式输出

spring mvc @PostMapping注解

指定value  produces = "text/event-stream" 流式输出的格式

输出的字符集charset=utf-8 
     

@RequiredArgsConstructor
@RestController
@RequestMapping("/ai")
public class ChatController {

private final ChatClient chatClient;

private final ChatHistoryRepository chatHistoryRepository;

@PostMapping(value="/chat",produces = "text/event-stream;charset=utf-8")
public Flux <String> chat(String prompt, String chatId){

//保存会话id
chatHistoryRepository.save("chat",chatId);//type需要改造为枚举
//请求模型
return chatClient.prompt()
.advisors(a -> a.param(CONVERSATION_ID, chatId != null ? chatId : "default_conversation_id"))
.user(prompt)
.stream()
.content();

}

}

如何指定上下文的大小

chatMemory 是 Spring AI 框架提供的一个接口,专门用于管理对话上下文。

通过chatMemory的实现类MessageWindowChatMemory可以通过其 builder() 方法中的.maxMessages(int count)来指定保留的消息条数。

默认情况下,如果不指定,大小通常是 10 条。

@Bean
public ChatMemory chatMemory() {
// 指定保留最近的 20 条消息
return MessageWindowChatMemory
.builder()
.maxMessages(10)//不写默认为10
.build();
}

 常见AI应用实现

基于Spring AI额外功能基础实现

1.ai的会话记忆

2.会话历史jvm内存存储

3.用户查看会话历史

ai的会话记忆

spring ai 调用 kimi k2  实现ai会话记忆

通过 MessageChatMemoryAdvisor.builder(chatMemory).build() 将 chatMemory (包含 用户与ai 的输出)加入到assistant

@Configuration
public class CommonConfiguration {

@Bean
public ChatMemory chatMemory() {
return MessageWindowChatMemory
.builder()
.maxMessages(10)//不写默认为10
.build();
}
//这意味着该聊天记忆组件默认会保留最近的 10 条消息作为对话上下文。当新消息加入且超过此限制时,最旧的消息将被移除(滑动窗口机制)。
@Bean
public ChatClient chatClient(ChatClient.Builder builder, ChatMemory chatMemory) {
return builder
.defaultSystem("你是通过当前项目使用spring ai构建的ai agent,你现在只有基础对话,会话记忆,展示会话历史的功能,需要帮助调用者调试当前项目的输出")

.defaultAdvisors(new SimpleLoggerAdvisor(),
MessageChatMemoryAdvisor.builder(chatMemory).build()
)
.build();
}
}

controller层调用ai api 代码实现

通过chatClient .advisors(a -> a.param(CONVERSATION_ID, chatId)) 指定当前会话的CONVERSATION_ID

@RequiredArgsConstructor
@RestController
@RequestMapping("/ai")
public class ChatController {

private final ChatClient chatClient;

private final ChatHistoryRepository chatHistoryRepository;

@PostMapping(value="/chat",produces = "text/event-stream;charset=utf-8")
public Flux <String> chat(String prompt, String chatId){

//保存会话id
chatHistoryRepository.save("chat",chatId);//type需要改造为枚举
//请求模型
return chatClient.prompt()
.advisors(a -> a.param(CONVERSATION_ID, chatId != null ? chatId : "default_conversation_id"))
.user(prompt)
.stream()
.content();

}

}

CONVERSATION_ID是chatMemory定义的常量

这是源码

/**
* The contract for storing and managing the memory of chat conversations.
*
* @author Christian Tzolov
* @author Thomas Vitale
* @since 1.0.0
*/
public interface ChatMemory {

/**
* The key to retrieve the chat memory conversation id from the context.
*/
String CONVERSATION_ID = "chat_memory_conversation_id";

/*省略*/
}

controller层AI与用户输出封装为MessageVO代码实现

@RequiredArgsConstructor
@RestController
@RequestMapping("/ai/history")
public class ChatHistoryController {

private final ChatHistoryRepository chatHistoryRepository;

private final ChatMemory chatMemory;

@GetMapping("/{type}")
public List<String> getChatHistoryByType(@PathVariable ("type") String type){
return chatHistoryRepository.getIdByType(type);
}

@GetMapping("/{type}/{chatId}")
public List<MessageVO> getChatHistory(@PathVariable ("type") String type, @PathVariable ("chatId") String chatId){
List<Message> messages = chatMemory.get(chatId);
if (messages.isEmpty()) {
return List.of();
}
//MessageVO::new 是 Java 8 引入的 方法引用(Method Reference) 语法,专门用于指向类的 构造函数。通过.map对流内Message对象进行批量构造为MessageVO对象
return messages.stream().map(MessageVO::new).toList();
}
}

会话历史jvm内存存储

会话历史功能在chatMemory的实现类实现,注入实现类后由spring管理,使用chatMemory实现类MessageWindowChatMemory默认内存存储。

service

public interface ChatHistoryRepository {
/*
* 保存会话记录
* @param type 业务类型 如:chat service pdf
* @param chatId 会话ID
* */
void save(String type,String chatId);

/*
* 获取会话ID列表
* @param type 业务类型
* @return 会话ID列表
* */
List<String> getIdByType(String type);
}

impl实现类

使用HashMap存储  type=chatId

@Component
public class InMemoryChatHistoryRepository implements ChatHistoryRepository{

private final Map<String,List<String>> chatHistory = new HashMap<>();

@Override
public void save(String type, String chatId) {

List<String> chatIds = chatHistory.computeIfAbsent(type, k -> new ArrayList<>());

if(!chatIds.contains(chatId)){
chatIds.add(chatId);
}

}

@Override
public List<String> getIdByType(String type) {

return chatHistory.getOrDefault(type,List.of());
}
}

如何持久化会话历史

在 Spring AI 中需要将聊天会话历史进行持久化(例如存储到数据库、Redis 等),创建 ChatMemory 接口实现类并重写以下核心方法:

  • add(String conversationId, List<Message> messages):负责写

  • get(String conversationId):负责读

  • clear(String conversationId):负责删除

  • 用户查看会话历史

    entity.vo

    创建与前端交互的实体MessageVO

    通过switch表达式,路由 role的值   获取  role值对应的content的值

    @NoArgsConstructor
    @Data
    public class MessageVO {
    private String role;
    private String content;

    public MessageVO(Message message) {
    switch (message.getMessageType()) {
    case USER -> role="user";
    case ASSISTANT -> role="assistant";
    case SYSTEM-> role="system";
    case TOOL-> role="tool";
    default -> role="unknown";

    }
    this.content = message.getText();

    }
    }

    Content.getText() 会根据MessageType的枚举类型返回对应的值

    这是源码

    这是message接口

    /**
    * The Message interface represents a message that can be sent or received in a chat
    * application. Messages can have content, media attachments, properties, and message
    * types.
    *
    * @see Media
    * @see MessageType
    */
    public interface Message extends Content {

    /**
    * Get the message type.
    * @return the message type
    */
    MessageType getMessageType();

    }
    这是Content接口

    public interface Content {
    String getText();

    Map<String, Object> getMetadata();
    }

    这是MessageType枚举
    /**
    * Enumeration representing types of {@link Message Messages} in a chat application. It
    * can be one of the following: USER, ASSISTANT, SYSTEM, FUNCTION.
    */
    public enum MessageType {

    /**
    * A {@link Message} of type {@literal user}, having the user role and originating
    * from an end-user or developer.
    * @see UserMessage
    */
    USER("user"),

    /**
    * A {@link Message} of type {@literal assistant} passed in subsequent input
    * {@link Message Messages} as the {@link Message} generated in response to the user.
    * @see AssistantMessage
    */
    ASSISTANT("assistant"),

    /**
    * A {@link Message} of type {@literal system} passed as input {@link Message
    * Messages} containing high-level instructions for the conversation, such as behave
    * like a certain character or provide answers in a specific format.
    * @see SystemMessage
    */
    SYSTEM("system"),

    /**
    * A {@link Message} of type {@literal function} passed as input {@link Message
    * Messages} with function content in a chat application.
    * @see ToolResponseMessage
    */
    TOOL("tool");

    controller

    以List<MessageVO>形式返回给前端ai输出

    @RequiredArgsConstructor
    @RestController
    @RequestMapping("/ai/history")
    public class ChatHistoryController {

    private final ChatHistoryRepository chatHistoryRepository;

    private final ChatMemory chatMemory;

    @GetMapping("/{type}")
    public List<String> getChatHistoryByType(@PathVariable ("type") String type){
    return chatHistoryRepository.getIdByType(type);
    }

    @GetMapping("/{type}/{chatId}")
    public List<MessageVO> getChatHistory(@PathVariable ("type") String type, @PathVariable ("chatId") String chatId){
    List<Message> messages = chatMemory.get(chatId);
    if (messages.isEmpty()) {
    return List.of();
    }
    //MessageVO::new 是 Java 8 引入的 方法引用(Method Reference) 语法,专门用于指向类的 构造函数。通过.map对流内Message对象进行批量构造为MessageVO对象
    return messages.stream().map(MessageVO::new).toList();
    }
    }

    赞(0)
    未经允许不得转载:171主机测评 » Spring AI 1.1.7快速入门
    分享到: 更多 (0)

    评论 抢沙发

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