Java转AI应用开发速成(3)—— 第一个 SpringAI 聊天应用

准备工作:

创建api-key,以deepSeek为例:DeepSeek开放平台

一、创建项目

Spring Boot 3.3+,JDK 21,添加依赖:

xml 复制代码
<!-- Spring AI  -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
        </dependency>

application.yml

yaml 复制代码
spring:
  application:
    name: java-tech-assistant

  ai:
    openai:
      api-key: ${DEEPSEEK_API_KEY:换成自己的key}
      base-url: https://api.deepseek.com
      chat:
        options:
          model: deepseek-chat
          temperature: 0.7

server:
  port: 8080
  servlet:
#    否则流式输出乱码
    encoding:
      charset: UTF-8
      enabled: true
      force: true

二、最简单的聊天接口

java 复制代码
@RestController
public class ChatController {

    private final ChatClient chatClient;

    public ChatController(ChatClient.Builder builder) {
        this.chatClient = builder
                .defaultSystem("你是一个专业的Java技术助手,回答简洁准确。")
                .build();
    }

    /**
     * 聊天接口 - 完整输出
     * GET /chat?message=你好
     */
    @GetMapping("/chat")
    public String chat(@RequestParam String message) {
        return chatClient.prompt()
                .user(message)
                .call().content();
    }
}

接口测试 测试:

AI 返回一连串 Java 八股文------你的第一个 AI 应用就跑通了。


三、加上流式输出(打字机效果)

上面那个是一口气吐完整段回复,没有 ChatGPT 那种逐字输出的效果。加流式输出:

java 复制代码
 /**
     * 流式聊天接口 - 逐字输出
     * GET /chat/stream?message=你好
     */
    @GetMapping(value = "/chat/stream", produces = "text/event-stream;charset=UTF-8")
    public Flux<String> streamChat(@RequestParam String message) {
        return chatClient.prompt()
                /**
                 * 创建一个 Prompt 对象,并设置用户输入的 message
                 */
                .user(message)
                .stream()
                .content();

    }

四、加上历史对话记忆

聊到第三句 AI 忘记了第一句?加上记忆:

java 复制代码
	public ChatController(ChatClient.Builder builder, ChatMemory chatMemory) {
        this.chatClient = builder
                .defaultSystem("你是一个专业的Java技术助手,回答简洁准确。")
                .defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory))
                .build();
    }

    @GetMapping(value = "/chat/stream", produces = "text/event-stream;charset=UTF-8")
    public Flux<String> streamChat(
            @RequestParam String message,
            @RequestParam(defaultValue = "default") String sessionId) {
        return chatClient.prompt()
                .user(message)
                // 历史对话 
                .advisors(a -> a.param("chat_memory_conversation_id", sessionId))
                .stream()
                .content();
    }

核心机制是 Spring AI 的 MessageChatMemoryAdvisor:

数据流:

  • → Controller
  • → MessageChatMemoryAdvisor.aroundCall/aroundStream
    1. 从 InMemoryChatMemory 查 sessionId 对应的历史消息
    2. 将历史消息注入到 Prompt 的 messages 列表中
    3. 调用 LLM
    4. LLM 回复后,将本轮 User 消息 + Assistant 回复存入 InMemoryChatMemory
  • → 返回结果

一句话总结:

Advisor 拦截请求 → 从 Map<sessionId, 历史消息> 取出前文拼进 prompt → LLM 回复后把本轮问答追加回去。纯内存,默认保留最近 100 条。

五、加上页面测试

六. ChatClient API 速查表

1. 构建输入 --- .prompt()

方法 作用
.prompt() 创建请求入口,返回 PromptRequestSpec
.prompt(String text) 快捷方式,等价于 .prompt().user(text)

2. 组装消息 --- PromptRequestSpec

方法 说明
.user(String text) 用户消息
.system(String text) 系统角色设定
.messages(Message... msgs) 自定义消息列表
.messages(Consumer) 编程式追加消息
.options(ChatOptions ops) 模型参数(temperature、topP 等)
.advisors(Advisor... ads) 请求拦截器链(RAG、日志、对话记忆)
.advisors(Consumer) 编程式组装 advisor 链
.toolContext(Map) 传上下文给 advisor
.tools(Object... beans) 注册 Function Calling 工具

3. 发送 & 返回形式 --- PromptRequestSpec

方法 返回类型 用途
.call() ChatClientCallResponseSpec 同步、一次性返回完整结果
.stream() ChatClientStreamResponseSpec 流式、逐 token 输出
.entity(Class<T>) UserInputAdvisor 链 结构化提取(提前终止,不调 LLM)

4. 取结果

call() 的结果:

方法 返回
.chatResponse() ChatResponse(含 metadata、token 用量)
.content() String(纯文本,最常用)
.entity(Class<T>) 反序列化到实体
.entities(ParameterizedTypeRef<T>) 反序列化到泛型集合

stream() 的结果:

方法 返回
.chatResponse() Flux
.content() Flux(逐字推送文本)
.entity(Class<T>) Flux

付源码地址: https://gitee.com/menglinjie/hello-ai

相关推荐
ServBay13 小时前
打通 AI 编程本地运维边界,利用 MCP 协议简化环境与服务管理
后端·ai编程·mcp
程序员cxuan13 小时前
DeepSeek 杀入多模态,识图功能正式上线!
人工智能·后端·程序员
IT_陈寒16 小时前
SpringBoot这个自动配置坑我跳了三次
前端·人工智能·后端
用户3952409988016 小时前
排坑日记:ASP.NET Core 中 "Required field is not provided" 验证错误全记录
后端
用户35218024547516 小时前
当 Prompt 学会"热更新":Spring Boot × Nacos3 AI 实战
java·spring boot·ai编程
JaguarJack17 小时前
Openai Codex 重大更新 已支持接入任意开源大模型
ai·openai·codex
用户83562907805117 小时前
使用 Python 自动化 PowerPoint 形状布局与格式设置
后端·python
Oneslide18 小时前
sudo免密权限配置不生效
后端
站大爷IP18 小时前
为什么Python不用var或let声明变量?
后端