目录

一、介绍

二、使用ChatClient

1、pom.xml

2、application.properties

3、创建pojo包并创建Student.java类

4、ChatController.java

5、测试


一、介绍

在前面“Chat Models”小节内容中,我们在与各个模型进行对话时使用的是对应模型的ChatModel对象,例如:DeepSeekChatModel、OllamaChatModel。然后通过chatModel.call(...)、chatModel.stream(...)直接调用模型,这其中我们需要手动构造Prompt、处理流响应(例如将返回封装成对象、从模型响应中获取回复内容)、拼装调用链(RAG需要自己构建流程)等操作。

以上使用ChatModel与模型进行对话方式中,如果在项目中使用大模型涉及到记忆上下文、Prompt模版化、RAG开发、返回内容映射为实体等操作时,单纯的ChatModel代码量很多,维护成本高,为了简化这个流程,Spring AI 中提供了ChatClient对象,该对象可以看做一个更高级的“客户端 API”,建立在ChatModel之上,可以用链式的方式快速搭配 Prompt、系统设定、变量替换、上下文记忆等,并支持文本/JSON/实体对象等多种形式的输出。

ChatModel和Chat Client对象对比如下:

方面ChatModelChatClient
Prompt构建需手动new Prompt(...)链式.prompt().user(...)使用
流式处理手动处理流对象.stream().content()简洁获取流内容
上下文/记忆手动拼接历史上下文支持内置ChatMemory和Advisor
RAG架构需自行编写检索、检索结果推进流程有内置Advisor支持RAG简化
输出映射.chatResponse()后自己解析JSON映射到实体.entity(...)可直接映射实体类

特别注意:ChatClient 目前只支持 Chat(对话)模型,不包括 Embedding、Image、Audio 等多模态模型要使用 Embedding、Image 或 Audio 等模型,需要直接使用 Spring AI 提供的对应 API,比如 EmbeddingModel、ImageModel、AudioModel。

二、使用ChatClient

下面以使用DeepSeek为例来演示Spring AI中如何使用ChatClient。涉及ChatClient创建、设置提示词、流式回复、回复映射到对象操作。

1、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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.5.15-SNAPSHOT</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>SpringAIChatClient</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>SpringAIChatClient</name>
	<description>SpringAIChatClient</description>
	<url/>
	<licenses>
		<license/>
	</licenses>
	<developers>
		<developer/>
	</developers>
	<scm>
		<connection/>
		<developerConnection/>
		<tag/>
		<url/>
	</scm>
	<properties>
		<java.version>17</java.version>
	</properties>

	<!-- 导入 Spring AI BOM,用于统一管理 Spring AI 依赖的版本,
引用每个 Spring AI 模块时不用再写 <version>,只要依赖什么模块 Mavens 自动使用 BOM 推荐的版本 -->
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.ai</groupId>
				<artifactId>spring-ai-bom</artifactId>
				<version>1.0.0-SNAPSHOT</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

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

	<!-- 声明仓库, 用于获取 Spring AI 以及相关预发布版本-->
	<repositories>
		<repository>
			<id>spring-snapshots</id>
			<name>Spring Snapshots</name>
			<url>https://repo.spring.io/snapshot</url>
			<releases>
				<enabled>false</enabled>
			</releases>
		</repository>
		<repository>
			<name>Central Portal Snapshots</name>
			<id>central-portal-snapshots</id>
			<url>https://central.sonatype.com/repository/maven-snapshots/</url>
			<releases>
				<enabled>false</enabled>
			</releases>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</repository>
	</repositories>

</project>

2、application.properties

spring.application.name=SpringAIChatClient

server.port=8080

#配置 Deepseek的基础URL、密钥和使用模型
spring.ai.deepseek.base-url=https://api.deepseek.com
spring.ai.deepseek.api-key=${deepseek_api_key}
spring.ai.deepseek.chat.options.model=deepseek-chat

3、创建pojo包并创建Student.java类

package com.example.springaichatclient.pojo;

public class Student {
    private Long id;
    private String name;
    private Integer age;

    public Student() {}

    public Student(Long id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    // Getter 和 Setter
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }

    @Override
    public String toString() {
        return "Student{id=" + id + ", name='" + name + "', age=" + age + "}";
    }
}

4、ChatController.java

package com.example.springaichatclient.controller;

import com.example.springaichatclient.pojo.Student;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

import java.util.List;
import java.util.Map;

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

    private final ChatClient client;

    public ChatController(ChatClient.Builder builder) {
        //在构造函数中创建 ChatClient
        //并设置默认 system prompt
        this.client = builder
                .defaultSystem("你是一个聊天助手,名字叫小智。")
                .build();
    }

    //使用文本响应用户问题、设置Prompt提示词
    @GetMapping("/chat")
    public Map<String, String> chatText(@RequestParam("message") String message) {
        String reply = client.prompt("如果用户让你讲故事,只能讲解神话故事,不能讲其他的。")
                .user(message)
                .call()
                .content();
        return Map.of("reply", reply);
    }

    @GetMapping("/response")
    public ChatResponse chatResponse(@RequestParam("message") String message) {
        return client.prompt()
                .user(message)
                .call()
                .chatResponse();
    }

    @GetMapping("/getOneStudent")
    public Student getOneEntity() {
        return client.prompt()
                .user("生成1个Student对象,输出单个JSON对象,字段:id(Long),name(String),age(Integer)")
                .call()
                .entity(Student.class);
    }

    @GetMapping("/getStudentList")
    public List<Student> getStudentList() {
        return client.prompt()
                .user("生成3个Student对象,JSON数组格式,字段:id(Long),name(String),age(Integer)")
                .call()
                .entity(new ParameterizedTypeReference<List<Student>>() {});
    }

    @GetMapping(path = "/chatStream")
    public Flux<String> chatStream(@RequestParam("message") String message, HttpServletResponse response) {

        // 避免返回乱码
        response.setCharacterEncoding("UTF-8");

        return client.prompt()
                .user(message)
                .stream()// 开启流式响应
                .content();
    }
}

以上代码中需要注意:defaultSystem(...)是设置全局系统提示词;prompt(...)是设置当前对话提示词;user(...)为用户输入消息内容。

5、测试

# http://localhost:8080/ai/chat?message=给我讲个故事
{
  "reply": "好的,我很高兴为您讲一个神话故事。今天我要讲的是中国上古神话《夸父追日》:\n\n很久很久以前,在北方的大荒之中,住着一个名叫夸父的巨人。他是后土神的孙子,信神的儿子。夸父身高如山,力大无穷,耳朵上挂着两条黄蛇,手里也握着两条黄蛇。\n\n那时候,太阳每天从东边升起,西边落下,夸父觉得太阳跑得太快了,大地上的时间太短暂。于是他决定要追上太阳,让它慢下来。\n\n夸父迈开巨大的步伐,开始追逐太阳。他跑啊跑啊,跨过一座座高山,越过一条条大河。太阳在天上跑,夸父在地上追。眼看就要在禺谷追上太阳了,夸父却感到口渴难忍。\n\n他俯下身来,一口气喝干了黄河的水,又喝干了渭河的水,还是觉得口渴。于是他向北跑去,想要喝大泽的水。可是还没跑到大泽,夸父就因为极度干渴而倒下了。\n\n临死前,夸父扔出了他的手杖。那手杖化作了一片桃林,结满了鲜美的桃子,为后来路过的人解渴。\n\n这个故事展现了古人征服自然的雄心壮志,也告诉我们做事要量力而行。您觉得这个神话故事怎么样?"
}


# http://localhost:8080/ai/response?message=你是谁
{
... ...
"results": [
    {
      "metadata": {
        "finishReason": "STOP",
        "contentFilters": [],
        "empty": true
      },
      "output": {
        "messageType": "ASSISTANT",
        "metadata": {
          "finishReason": "STOP",
          "index": 0,
          "id": "f68527d6-6a26-4bbb-89c0-ca849e17b900",
          "role": "ASSISTANT",
          "messageType": "ASSISTANT"
        },
        "toolCalls": [],
        "media": [],
        "prefix": null,
        "reasoningContent": null,
        "text": "你好!我是小智,一个智能聊天助手,随时为你提供帮助和解答问题。无论是日常疑问、学习辅导,还是闲聊放松,我都可以陪你聊聊!有什么我可以帮你的吗?😊"
      }
    }
  ]
}

# http://localhost:8080/ai/getOneStudent
{
  "id": 1,
  "name": "张三",
  "age": 20
}

# http://localhost:8080/ai/getStudentList
[
  {
    "id": 1,
    "name": "张三",
    "age": 20
  },
  {
    "id": 2,
    "name": "李四",
    "age": 21
  },
  {
    "id": 3,
    "name": "王五",
    "age": 22
  }
]

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐