SpringAlibaba Ai基础入门

AI基础概念学习
1.1 模型调用快速入门

注册链接:创建API Key

我们的调用模型的时候需要使用API Key进行身份验证。所以需要参加一个API Key。创建完后避免暴露。

创建好后,复制对应的key

1.1.2 接口调用

使用ApiFox调用模型

我们先使用接口的调用下模型测试下key是否正常可用。

同时也是也是让大家原始的模型调用。后面我们在使用Spring AI Alibaba的时候也更容易理解。

每个模型都有自己的API文档。我们去看下智谱AI的API文档。(可以先在官网上测试接口)
智普Api文档

xml 复制代码
curl -X POST "https://open.bigmodel.cn/api/paas/v4/chat/completions" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
    "model": "glm-5",
    "messages": [
        {
            "role": "system",
            "content": "你是一个有用的AI助手。"
        },
        {
            "role": "user",
            "content": "你好,请介绍一下自己。"
        }
    ],
    "temperature": 1.0,
    "stream": true
}'

把curl复制到apifox,然后把自己的API Key替换上去

然后点击发送即可

由于上面请求的stream是true。所以结果是流式输出。

1.2 相关概念

  • LLM - LLM是大型语言模型(Large Language Model)的缩写。是一种通过海量文本数据训练,能够理解和生成人类语言的人工智能系统

  • token - AI大模型调用中,模型处理文本的基本单位。各家算法切字逻辑,但是大致一个token约等

    于一个汉字左右。 具体可以看各家厂商提供的切词可视化工具

    (OpenAI:https://platform.openai.com/tokenizer 文心一言:

    https://console.bce.baidu.com/support/#/tokenizer 阿里千问:

    "stream": true

    }'

    https://dashscope.console.aliyun.com/tokenizer)

  • 模型参数 - 调用模式时可以设置的一些参数,每个模型支持的参数会有一些参数,都是大部分参数

    都是相同的。常见参数:

  • Temperature (温度):控制生成文本的随机性。值越低(如0.2),输出越确定、保守和专注;值越高(如0.9),输出越具有创造性和多样性,但也可能更不连贯

  • Top-p (核采样):与Temperature类似,用于控制采样的多样性。它从累积概率超过阈值p的最可能候选词中随机选择。较低的值(如0.5)限制选择范围,输出更可预测;较高的值(如0.9)则扩大选择范围

  • Max Tokens (最大生成长度):限制模型单次响应所能生成的最大token数量。设置过短可能导致回答被截断。

  • 流式响应 - 类似于我们使用Deepseek时AI给我们一个字一个字的回答的效果。 目前流式响应主要是使用SSE技术实现的。

  • message - 目前主要是4种消息角色

    • system(系统消息,用于设定AI的行为和角色)
    • user(用户消息,来自用户的输入)
    • assistant(助手消息,来自AI的回复)
    • tool(工具调用消息)
  • prompt - Prompt(提示词) 是您传递给AI模型的指令或问题。简单来说,Prompt是你用来告诉AI"做什么"和"怎么做"的话。

2.SpringAIAlibaba概述

它本质上是一个基于 Spring AI 抽象层的"增强插件集",其核心价值在于提供了统一、便捷的接口,让你可以灵活地选择和切换底层模型。并不是使用了SpringAIAlibaba就必须和阿里千问模型等深度绑定。我们去学习它最主要的一个核心原因是 Graph:用于编排多智能体与工作(Multi-Agent/Workflow)

官方文档:https://java2ai.com/docs/1.0.0.2/overview/?
spm=4347728f.6476bf87.0.0.a3c1556b0YIohP

3.快速入门
3.1 目标

使用Spring AI Alibaba 实现模型调用。

调用/zhipuai/simple 接口 传入一个问题 就能调用实现AI模型的调用,把模型的答案返回

3.2 准备工作

3.2.1 把API Key写入环境变量

为了避免把我们的API Key明文写入代码或者配置文件中。我们可以把API Key存储到系统的环境变量中。然后工程中去引用对应的环境变量。

系统 写入方式
windows 通过系统属性 GUI 操作,添加环境变量 ZHIPU_KEY 值为 你的API Key

3.2.2 创建父工程

为了便于我们管理依赖。我们先创建个父工程。

版本说明:

JDK:17 (官方最低要求17)

SpringAI :1.0.0

SpringBoot : 3.4.0

Spring AI Alibaba : 1.0.0.4

pom.xml

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>
<groupId>com.sangeng</groupId>
<artifactId>sangeng-springai-examples</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.version>1.0-SNAPSHOT</project.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-ai.version>1.0.0</spring-ai.version>
<spring-ai-alibaba.version>1.0.0.4</spring-ai-alibaba.version>
<!-- Spring Boot -->
<spring-boot.version>3.4.0</spring-boot.version>
<!-- maven plugin -->
<maven-deploy-plugin.version>3.1.1</maven-deploy-plugin.version>
<flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-bom</artifactId>
<version>${spring-ai-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>${maven-deploy-plugin.version}</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<release>17</release>
<compilerArgs>
<compilerArg>-parameters</compilerArg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>${flatten-maven-plugin.version}</version>
<inherited>true</inherited>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
<configuration>
<updatePomFile>true</updatePomFile>
<flattenMode>ossrh</flattenMode>
<pomElements>
<distributionManagement>remove</distributionManagement>
<dependencyManagement>remove</dependencyManagement>
<repositories>remove</repositories>
<scm>keep</scm>
<url>keep</url>
<organization>resolve</organization>
</pomElements>
</configuration>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

3.2.3 创建子工程

01_springai-alibaba-quick-start

3.2.3.1 添加依赖

xml 复制代码
<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-zhipuai</artifactId>
  </dependency>
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <scope>provided</scope>
</dependency>
</dependencies>

由于我们要调用的是智谱AI,所以需要引入spring-ai-starter-model-zhipuai。如果用的是别的AI模型引入

对于的启动器即可

  • deepseek - spring-ai-starter-model-deepseek
  • 千问 - spring-ai-alibaba-starter-dashscope
  • OpenAI - spring-ai-starter-model-openai

3.2.3.2 修改配置

application.yaml

yml 复制代码
server:
  port: 8080
  servlet:
    encoding:
      charset: utf-8
      enabled: true
      force: true

spring:
  ai:
    zhipuai:
      api-key: ${zhipu}  # 配置 API Key
      base-url: "https://open.bigmodel.cn/api/paas"   # 配置 模型地址
      chat:
        options:
          model: glm-4.5

  http:
    client:
      read-timeout: 1000000

3.2.3.3 创建SpringBoot启动类

java 复制代码
package com.sangeng;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ZhipuAIApplication {
 public static void main(String[] args) {
   SpringApplication.run(ZhipuAIApplication.class, args);
}
}

3.3 代码编写

java 复制代码
@RestController
@RequestMapping("/zhipuai")
public class ZhipuChatController {
private final ChatModel chatModel;
// 通过构造器注入 ChatModel
public ZhipuChatController(ChatModel chatModel) {
this.chatModel = chatModel;
}
@GetMapping("/simple")
public String simpleChat(@RequestParam(name = "query") String query) {
// 调用ChatModel 的call方法传入问题完成模型调用
return chatModel.call(query);
 }
}

3.4 测试

http://localhost:8080/zhipuai/simple?query=你是谁

3.5 原理初探

我们可以在org.springframework.ai.zhipuai.api.ZhiPuAiApi#chatCompletionEntity 打上断点进行调试

4.核心API
4.1 Message

我们来看请求中的这部分内容。messages是一个数组。

其中的每个对象都有role和content两个属性。 这个对象在SpringAI中就被抽象成Message。

我们先来看下智谱的文档中对messages的介绍:

再来看下SpringAI中的设计的Message的继承体系:

从这个继承关系中我们可以看到AbstractMessage相对于把我们接口中看到的role字段抽象成messageType属性。content字段抽象成textContent属性。

MessageType定义的枚举源码如下:

java 复制代码
public enum MessageType {
  USER("user"),
  ASSISTANT("assistant"),
  SYSTEM("system"),
  TOOL("tool");
}

也是和我们目前role可以使用的4种值对应。

这个时候我们回过头去看call方法的支持的参数类型:

你就能理解为什么它可以支持传Message的可变参的含义了。 相对于就是传入了一个Message列表。这个列表本质上会转化为请求中的messages。

所以如果想使改造入门案例的代码和下面的curl调用相同的效果应该怎么实现呢?

json 复制代码
curl -X POST "https://open.bigmodel.cn/api/paas/v4/chat/completions" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "glm-4.5",
"messages": [
{
"role": "system",
"content": "你是一个有用的AI助手。"
},
{
"role": "user",
"content": "你好,请介绍一下自己。"
}
]
}'
java 复制代码
@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);
}

4.2 Prompt

Prompt 提示词 是引导 AI 模型生成特定输出的输入格式,Prompt 的设计和措辞会显著影响模型的响应。

在SpringAI中,是把模型参数和消息列表的组合抽象为Prompt。

所以Prompt中主要是有以下两个属性

这个时候我们再看下call方法支持的参数类型中就有Prompt类型的就更容易理解了。

接下去我们聊下ChatOptions。

我们前面讲过不同厂商的模型支持的模型参数不同,当然也有些参数上普遍都支持的。所以SpringAI在设计的时候把共有的模型参数都设计到了ChatOptions中。而每个模型厂商可以创建自己的ChatOptions实现定义厂商支持的模型参数。所以我们看下当前项目中ChatOptions的实现有哪些。就发现了ZhiPuAiChatOptions。

ZhiPuAiChatOptions对象的创建

java 复制代码
ZhiPuAiChatOptions zhiPuAiChatOptions = new ZhiPuAiChatOptions();
zhiPuAiChatOptions.setModel("glm-4.5");
zhiPuAiChatOptions.setTemperature(0.0);
zhiPuAiChatOptions.setMaxTokens(15536);

另外一种创建CharOptions的方式

java 复制代码
ZhiPuAiChatOptions chatOptions = ZhiPuAiChatOptions.builder()
.model("glm-4.5")
.maxTokens(15536)
.temperature(0.0)
.build();

所以如果想使改造入门案例的代码和下面的curl调用相同的效果应该怎么实现呢?

json 复制代码
curl -X POST "https://open.bigmodel.cn/api/paas/v4/chat/completions" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"model": "glm-4.5",
"messages": [
{
"role": "system",
"content": "你是一个有用的AI助手。"
},
{
"role": "user",
"content": "你好,请介绍一下自己。"
}
],
"temperature": 0.0,
"maxTokens":15536
}
java 复制代码
@GetMapping("/chatOptions")
public ChatResponse chatOptions(@RequestParam(name = "query") String query)
{
   SystemMessage systemMessage = new SystemMessage("你是一个有  用的AI助手。");
  UserMessage userMessage = new UserMessage(query);
  ZhiPuAiChatOptions zhiPuAiChatOptions = new ZhiPuAiChatOptions();
  zhiPuAiChatOptions.setModel("glm-4.5");
  zhiPuAiChatOptions.setTemperature(0.0);
  zhiPuAiChatOptions.setMaxTokens(15536);
  // 调用模型
  return chatModel.call(new
  Prompt(List.of(systemMessage,userMessage),zhiPuAiChatOptions));
}

4.3 ChatModel

从图中可以看出ChatModel API 让应用开发者可以非常方便的与 AI 模型进行文本交互,它抽象了应用与模型交互的过程,包括使用 Prompt作为输入,使用ChatResponse作为输出等。

4.3.1 对象获取

引入模型的启动器后直接从容器中注入即可

java 复制代码
@RestController
@RequestMapping("/zhipuai")
public class ZhipuChatController {
  private final ChatModel chatModel;
  // 通过构造器注入 ChatModel
  public ZhipuChatController(ChatModel chatModel) {
  this.chatModel = chatModel;
}
}

4.3.2 模型调用

同步响应

java 复制代码
@GetMapping("/simple")
public String simpleChat(@RequestParam(name = "query") String query) {
return chatModel.call(query);
}

流式响应

java 复制代码
@GetMapping("/stream/chat")
public Flux<String> stream(@RequestParam(name = "query")  String query) {
return chatModel.stream(query);
}

乱码问题解决:

java 复制代码
server:
 servlet:
  encoding:
   charset: UTF-8
   enabled: true
   force: true

4.4 ChatClient
4.4.1 概述

虽然我们可以使用ChatModel去调用模型。但是它的使用方法还是有点繁琐的。

SpringAI为我们提供了ChatClient来让我们的使用更加简便。

基础功能:

  • 定制和组装模型的输入(Prompt)
  • 格式化解析模型的输出(Structured Output)
  • 调整模型交互参数(ChatOptions)
  • ...

并且它还提供了更多的高级功能:

  • 聊天记忆(Chat Memory)
  • 工具/函数调用(Function Calling)
  • RAG
  • ...

4.4.2 创建 ChatClient

直接从容器中注入ChatClient.Builder

java 复制代码
@RestController
public class ChatController {
  private final ChatClient chatClient;
  public ChatController(ChatClient.Builder builder) {
    this.chatClient = builder.build();
}
}

自己创建ChatClient.Builder来构造ChatClient

java 复制代码
@RestController
@RequestMapping("/chatclient")
public class ZhipuChatClientController {
  private final ChatClient chatClient;
 public ZhipuChatClientController(ChatModel chatModel) {
    this.chatClient = ChatClient.builder(chatModel)
     .build();
}
}

4.4.2 调用模型并处理响应
4.4.2.1 返回字符串

java 复制代码
@RestController
@RequestMapping("/chatclient")
public class ZhipuChatClientController {
private final ChatClient chatClient;
public ZhipuChatClientController(ChatModel chatModel) {
this.chatClient = ChatClient.builder(chatModel)
.build();
}
@GetMapping("/simple")
public String simpleChat(@RequestParam(name = "query") String query) {
ZhiPuAiChatOptions chatOptions = ZhiPuAiChatOptions.builder()
.maxTokens(15536)
.temperature(0.0)
.model("glm-4.5")
.build();
return chatClient.prompt()
.system("你是一个有用的AI助手。")
.user("你好,请介绍一下自己。")
.options(chatOptions)
.call().content();
}
}

4.4.2.2 返回 ChatResponse

java 复制代码
ZhiPuAiChatOptions chatOptions = ZhiPuAiChatOptions.builder()
  .maxTokens(15536)
  .temperature(0.0)
  .model("glm-4.5")
  .build();
ChatResponse chatResponse = chatClient.prompt()
  .system("你是一个有用的AI助手。")
  .user("你好,请介绍一下自己。")
  .options(chatOptions)
  .call()
  .chatResponse();

4.4.2.3 响应数据转化成实体

我们在实际开发过程中会更希望模型返回的数据是结构话的数据,这样我们才更好对数据去处理。目前我们最熟悉的结构化数据就是json格式。并且我们希望能把json直接转化成bean对象更方便我们使用。

例如,我们需要让AI随机生成一本书,要有书名和作者。并且我们希望结果能直接转化成一个Book对象供我们使用。那么我们只需要先定义好Book类

java 复制代码
@Data
public class Book {
  private String name;
  private String author;
}

然后在使用chatClient调用完call方法后调用entity方法传入要转化类的字节码对象即可。

java 复制代码
@GetMapping("/response")
public Book response() {
 return chatClient.prompt()
  .user("给我随机生成一本书,要求书名和作者都是中文")
  .call().entity(Book.class);
}

结果测试

但是我们也要理解他的原理。

前面我们讲过模型调用的本质就是调用模型的接口。所以想知道它是怎么实现的只要看下他在给模型发

送请求的时候究竟发送了什么就知道了。

我们可以在ZhiPuAiApi的chatCompletionEntity方法中打上断点。

我们可以看到本质上他其实就是使用RestClient发送请求。

从这里我们可以看到请求中包含了一个UserMessage,这个UserMessage的实际内容上:这个内容就是在我们的设置的message的基础上又加了一段提示词。这个提示词主要就是告诉模型,要求回复的格式是JSON格式。并且还告诉了模型我们的Json要求有哪些字段,字段的类型是什么。

json 复制代码
给我随机生成一本书,要求书名和作者都是中文
Your response should be in JSON format.
Do not include any explanations, only provide a RFC8259 compliant JSON response
following this format without deviation.
Do not include markdown code blocks in your response.
Remove the ```json markdown from the output.
Here is the JSON Schema instance your output must adhere to:
```{
"$schema" : "https://json-schema.org/draft/2020-12/schema",
"type" : "object",
"properties" : {
"author" : {
"type" : "string"
},
"name" : {
"type" : "string"
}
},
"additionalProperties" : false
}

4.4.2.4 响应数据流式返回

使用stream方法调用模型

java 复制代码
@GetMapping(value = "/stream")
public Flux<String> stream() {
 return chatClient.prompt()
  .user("给我随机生成一本书,要求书名和作者都是中文")
  .stream().content();
}

4.4.3 Advisors

4.4.3.1 概述

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

优势:

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

Advisor增强流程如下:

继承体系

如果我们需要对同步调用进行增强可以使用CallAdvisor。如果是对流式调用(响应式调用)进行增强可以使用StreamAdvisor。
4.4.3.2 快速入门

添加两个自定义的Advisor进行增强

我们只需要实现CallAdvisor接口重写其中的方法即可。

adviseCall方法中的chatClientRequest 是封装了AI请求的对象,我们可以在Advisor方法中对其进行增强。

adviseCall方法中的callAdvisorChain可以让我们对当前的AI请求进行放行,并且返回ChatClientResponse。我们可以增强ChatClientResponse后再返回。

java 复制代码
package com.sangeng.advisor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClientRequest;
import org.springframework.ai.chat.client.ChatClientResponse;
import org.springframework.ai.chat.client.advisor.api.CallAdvisor;
import org.springframework.ai.chat.client.advisor.api.CallAdvisorChain;
@Slf4j
public class SGCallAdvisor1 implements CallAdvisor {
/**
*
* @param chatClientRequest 请求
* @param callAdvisorChain 增强链,可以用来放行AI请求到下一个Advisor
* @return
*/
@Override
public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest,
CallAdvisorChain callAdvisorChain) {
  log.info("SGCallAdvisor1 请求");
  ChatClientResponse chatClientResponse =
    callAdvisorChain.nextCall(chatClientRequest);
    log.info("SGCallAdvisor1 响应");
    return chatClientResponse;
}
@Override
public String getName() {
 return "SGCallAdvisor1";
}
@Override
public int getOrder() {
 return 0;
}
}
java 复制代码
package com.sangeng.advisor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClientRequest;
import org.springframework.ai.chat.client.ChatClientResponse;
import org.springframework.ai.chat.client.advisor.api.CallAdvisor;
import org.springframework.ai.chat.client.advisor.api.CallAdvisorChain;
@Slf4j
public class SGCallAdvisor2 implements CallAdvisor {
/**
*
* @param chatClientRequest 请求
* @param callAdvisorChain 增强链,可以用来放行AI请求到下一个Advisor
* @return
*/
@Override
public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest,
CallAdvisorChain callAdvisorChain) {
 log.info("SGCallAdvisor2 请求");
 ChatClientResponse chatClientResponse =
 callAdvisorChain.nextCall(chatClientRequest);
 log.info("SGCallAdvisor2 响应");
return chatClientResponse;
}
@Override
public String getName() {
return "SGCallAdvisor2";
}
@Override
public int getOrder() {
return 0;
}
}

给ChatClient添加Advisor

java 复制代码
package com.sangeng.controller;
import com.sangeng.advisor.SGCallAdvisor1;
import com.sangeng.advisor.SGCallAdvisor2;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
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;

@RestController
@RequestMapping("/advisor")
public class AdvisorController {
private final ChatClient chatClient;
public AdvisorController(ChatModel chatModel) {
this.chatClient = ChatClient.builder(chatModel)
.build();
}
@GetMapping("/simple")
public String simpleChat(@RequestParam(name = "query") String query) {
ZhiPuAiChatOptions chatOptions = ZhiPuAiChatOptions.builder()
 .maxTokens(15536)
 .temperature(0.0)
 .model("glm-4.5")
 .build();
return chatClient.prompt()
 .system("你是一个有用的AI助手。")
 .user(query)
 .advisors(new SGCallAdvisor1(),new SGCallAdvisor2())
 .options(chatOptions)
 .call().content();
}
}

调用接口我们可以看到如下的打印信息


4.4.3.3 调整Advisor顺序

如果我们想调整Advisor在Advisor链中的顺序只需要修改getOrder的返回值即可。

getOrder的返回值越小在Advisor链中的顺序越靠前,也就是越早处理请求。

所以如果我们希望把SGCallAdvisor2的getOrder返回值设置为1。打印的结果就变成了

4.4.3.4 案例-自定义SimpleMessageChatMemoryAdvisor

我们通过一个简化版的MessageChatMemoryAdvisor来学习并理解如何自定义Advisor

需求:我们希望能在能够进行多轮对话,模型能知道我们之前聊过什么内容。

设计:1. 把对话记录(用户发送的消息和AI回复的消息)存储到内存中 2.每个会话有自己的会话id来判断自己的消息记录是哪些 3.给模型的请求中需要携带之前的聊天记录

定义Advisor

java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClientRequest;
import org.springframework.ai.chat.client.ChatClientResponse;
import org.springframework.ai.chat.client.advisor.api.AdvisorChain;
import org.springframework.ai.chat.client.advisor.api.BaseAdvisor;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.prompt.Prompt;

import java.util.*;

@Slf4j
public class SimpleMessageChatMemoryAdvisor implements BaseAdvisor {

    // 本案例主要是为了学习和理解如何写Advisor所以这里选用最简单的实现。实际肯定不能这样存储消息
    private static Map<String, List<Message>> chatMemory = new HashMap<>();

    @Override
    public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) {
        // 通过会话id查询之前的对话记录
        String conversationId = chatClientRequest.context().get("conversationId").toString();
        List<Message> messages = chatMemory.get(conversationId);
        if (messages == null) {
            messages = new ArrayList<>();
        }
        // 把这次请求的消息添加到对话记录中
        List<Message> requestMessageList = chatClientRequest.prompt()
                .getInstructions();
        messages.addAll(requestMessageList);
        chatMemory.put(conversationId, messages);
        // 把添加后记录的List<Message> 放入请求中
        Prompt oldPrompt = chatClientRequest.prompt();
        Prompt newPrompt = oldPrompt.mutate()
                .messages(messages)
                .build();
        ChatClientRequest request = chatClientRequest.mutate()
                .prompt(newPrompt)
                .build();
        return request;
    }

    @Override
    public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {
        // 通过会话id查询之前的对话记录
        String conversationId = chatClientResponse.context().get("conversationId").toString();
        List<Message> hisMessages = chatMemory.get(conversationId);
        if (hisMessages == null) {
            hisMessages = new ArrayList<>();
        }
        // 获取response中ai的消息 添加到对话记录中
        if(Objects.isNull(chatClientResponse)){
            return chatClientResponse;
        }
        AssistantMessage assistantMessage = chatClientResponse.chatResponse()
                .getResult()
                .getOutput();
        hisMessages.add(assistantMessage);
        chatMemory.put(conversationId, hisMessages);
        return chatClientResponse;
    }

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

使用Advisor

java 复制代码
@GetMapping("/simpleChatMemory")
public String simpleChatMemory(@RequestParam(name = "msg") String
msg,@RequestParam(name = "conversationId") String conversationId) {
 return chatClient.prompt()
  .user(msg)
  .advisors(advisorSpec ->
    advisorSpec.param("conversationId",conversationId))
    .advisors(new SimpleMessageChatMemoryAdvisor())
    .call().content();
}

4.4.3.5 使用官方的Advisor实现对话记录存储-如何统一设置advisors

java 复制代码
//构造器注入
public ZhipuChatMemoryController(ChatClient.Builder builder) {
    // 创建 MessageWindowChatMemory
    MessageWindowChatMemory windowChatMemory =
            MessageWindowChatMemory.builder()
                                   .build();

   //创建 MessageChatMemoryAdvisor
  MessageChatMemoryAdvisor chatMemoryAdvisor =
  MessageChatMemoryAdvisor.builder(windowChatMemory)
   .build();
   this.chatClient = builder
    .defaultAdvisors(chatMemoryAdvisor)
    .build();
}



@GetMapping("/messageChatMemoryAdvisor")
public String messageChatMemoryAdvisor(@RequestParam(name = "query") String
query,
@RequestParam(name =
"conversationId") String conversationId) {
return chatClient.prompt()
  .user(query)
  .advisors(advisorSpec ->
      advisorSpec.param(ChatMemory.CONVERSATION_ID,conversationId))
     .call()
     .content();
}
相关推荐
悦来客栈的老板5 小时前
AI逆向|猿人学逆向反混淆练习平台第七题加密分析
人工智能
KOYUELEC光与电子努力加油5 小时前
JAE日本航空端子推出支持自走式机器人的自主充电功能浮动式连接器“DW15系列“方案与应用
服务器·人工智能·机器人·无人机
萤火阳光5 小时前
13|自定义 Skill 创作:打造专属自动化利器
人工智能
tianbaolc6 小时前
Claude Code 源码剖析 模块一 · 第六节:autoDream 自动记忆整合
人工智能·ai·架构·claude code
蓝色的杯子6 小时前
从 LLM 到 Agent Skill,龙虾的技术基础 · ② Token
人工智能
tq10866 小时前
AI时代的价值冲击——共识瓦解与转型阵痛
人工智能
Flying pigs~~6 小时前
Prompt 工程实战总结:文本分类、信息抽取、语义匹配
人工智能·自然语言处理·prompt·文本分类·大模型应用
专业发呆业余科研6 小时前
深度学习的隐形支架:对称性与不变性的架构统一论
人工智能·深度学习·神经网络·机器学习
海边的Kurisu6 小时前
Amadeus的知识库 | OpenAI的API规范是啥来头?—— 集成大模型到项目中的必备通行证
java·开发语言·人工智能