Spring AI
约 10744 字大约 36 分钟
2025-04-22
1.Spring AI 的全面概述
Spring AI
项目旨在简化包含人工智能功能的应用程序的开发,而不会产生不必要的复杂性。该项目从著名的 Python
项目(如 LangChain
和 LlamaIndex
)中汲取灵感,但 Spring AI
并不是这些项目的直接移植。该项目的成立理念是,下一波生成式 AI
应用程序将不仅适用于 Python
开发人员,而且将在许多编程语言中无处不在。
官方文档认为,Spring AI
解决的痛点问题是:将企业数据和 api
与 AI
模型连接起来。

重要
补充:另外一个集成 AI
的 Java
框架就是 Langchain4j,您可以简单了解一下。
2.Spring AI 的基本功能
支持所有主要 的 AI 模型提供商 ,例如
Anthropic、OpenAI、Microsoft、Amazon、Google 和 Ollama
支持的模型类型包括:
支持跨
AI
提供商的同步处理API
和流式处理API
选项,并且可移植,还可以访问特定于模型的高级功能(说白了就是有些AI
厂家提供了一些独有的功能)结构化输出,从
AI
模型输出到POJO
的映射支持所有主要的 矢量数据库提供商,例如
Apache Cassandra、Azure Vector Search、Chroma、Milvus、MongoDB Atlas、Neo4j、Oracle、PostgreSQL/PGVector、PineCone、Qdrant、Redis、Weaviate
...
3.Spring AI 的使用教程
3.1.基本知识
首先我们需要了解一些基础的 AI
知识才能开始使用 Spring AI
,我们不着急使用。
3.1.1.模型
AI
模型是旨在处理和生成信息的算法,通常模仿人类的认知功能。通过从大型数据集中学习模式和见解,这些模型可以进行预测、文本、图像或其他输出,从而增强跨行业的各种应用程序。有许多不同类型的 AI
模型,每种模型都适用于特定的使用案例。虽然 ChatGPT
及其生成式 AI
功能通过文本输入和输出吸引了用户,但许多模型和公司都提供了不同的输入和输出。在 ChatGPT
之前,许多人对 Midjourney
和 Stable Diffusion
等文本到图像生成模型着迷。根据用途,常见的模型可以分为四种:
- 语言模型
- 图像模型
- 音频模型
- 嵌入模型

Spring AI
对这些模型都做了相应的支持,不过类似 GPT
这种模型会比较特殊一些,GPT
的 P
代表预训练,预训练让 AI
更容易用。而因为它是预训练的,您不需要训练它、您不需要懂机器学习、您只要提供 Prompt, 提示
就能做很多事。
模型 | 特点 |
---|---|
GPT-4o/GPT-4/GPT-3.5 Turbo | 多模态/文本+图像/主要处理文本 |
Claude 3 系列(Opus, Sonnet, Haiku,由强到弱) | 多模态 |
Gemini Ultra/Pro/Nano | 多模态 |
Llama 3/Llama 2 | 开源,70B 和 8B 参数版本/开源,多种参数规模 |
文心一言/通义千问/豆包/星火/Deepseek | 国产大模型 |

3.1.2.提示
提示是基于语言的输入的基础,这些输入可指导 AI
模型生成特定输出。对于熟悉 ChatGPT
的人来说,提示可能看起来只是在发送到 API
的对话框中输入的文本。然而,它包含的远不止于此。在许多 AI
模型中,提示的文本不仅仅是一个简单的字符串。
ChatGPT
的 API
在一个提示中有多个文本输入,每个文本输入都分配了一个角色。例如,有 system
角色,它告诉模型如何行为并设置交互的上下文。还有 user role
,通常是来自用户的 Importing
。
重要
补充:也就是说,ChatGPT API
的提示是“消息列表”组成的,不是纯字符串。因此每个消息是一个对象,有两个关键字段:
role
:角色(告诉模型这句话是谁说的)content
:具体内容(这句话的文本)
常见角色名 | 含义 | 示例用途 |
---|---|---|
system | 系统提示,用来设定模型行为 | 你是一个乐于助人的编程助手。 |
user | 用户说的话 | 怎么用 Python 写个排序? |
assistant | AI 的回复 | 你可以用 sorted() 函数。 |
制作有效的提示既是一门艺术,也是一门科学。ChatGPT
专为人类对话而设计。这与使用 SQL
之类的东西来 ask a question
(细致判断后的精确回答)完全不同。一个人必须与 AI
模型进行交流,类似于与另一个人交谈。正是这种交互方式的重要性,以至于 Prompt Engineering, 提示词工程
一词已经成为一门独立的学科。有一系列新兴的技术可以提高提示的有效性。投入时间制作提示可以大大提高结果输出。
分享提示已成为一种公共实践,并且正在积极地进行关于这一主题的学术研究。例如,创建有效的提示(例如,与 SQL
形成对比)是多么违反直觉,最近的一篇研究论文 发现,您可以使用的最有效的提示之一以短语“深呼吸并逐步完成此工作”开头。这应该可以告诉你为什么语言如此重要。我们还不完全了解如何最有效地利用这项技术的先前迭代,例如 ChatGPT 3.5
,更不用说正在开发的新版本了。
创建有效的提示包括建立请求的上下文,并将请求的各个部分替换为特定于用户输入的值。此过程使用传统的基于文本的模板引擎进行提示创建和管理。Spring AI
为此使用了 OSS
库 StringTemplate。例如,考虑简单的提示模板:
Tell me a {adjective} joke about {content}.
Copied!
在 Spring AI
中,提示模板可以比作 Spring MVC
架构中的 “视图”。提供模型对象(通常是 java.util.Map
)来填充模板中的占位符。提示模板加数据模型合成的 rendered
最终字符串成为提供给 AI
模型的提示的内容。
不过就像之前说的提示的文本不再是单纯的消息字符串,也可能是消息对象,现在发送到模型的提示的特定数据格式存在相当大的变化。提示最初从简单字符串开始,现在已经发展到包含多条消息,其中每条消息中的每个字符串代表模型的不同角色。
3.1.3.嵌入
这里的嵌入就是指 文本、图像、视频
的数字表示形式,用于捕获输入之间的关系(人话就是对比两个输入之前的关系,比如相似度)。嵌入的工作原理是将文本、图像、视频转换为浮点数数组(称为向量)。这些矢量旨在捕获文本、图像、视频的含义。嵌入数组的长度称为向量的维数。通过计算两段文本的向量表示之间的数值距离,应用程序可以确定用于生成嵌入向量的对象之间的相似性。

嵌入在 RAG, Retrieval Augmented Generation, 检索增强生成
模式等实际应用中尤其相关。它们能够将数据表示为语义空间中的点,这类似于欧几里得几何的二维空间,但维度更高。这意味着就像欧几里得几何中平面上的点可以根据其坐标来接近或远一样,在语义空间中,点的接近反映了含义的相似性。在这个多维空间中,关于相似主题的句子被放置在更近的位置,就像图表上彼此靠近的点一样。这种接近有助于文本分类、语义搜索甚至产品推荐等任务,因为它允许 AI
根据相关概念在这个扩展的语义环境中的 “位置” 来识别和分组。
3.1.4.令牌
Token
是 AI
模型工作原理的构建块(其实就是处理时的基本元素)。在输入时,模型将单词转换为 Token
。在输出时,他们将 Token
转换回单词。在英语中,一个标记大约相当于一个单词的 75%
。作为参考,莎士比亚全集总计约 900,000
字,翻译成大约 120
万个代币。

通常在一些 AI
厂家中,您的费用由使用的令牌数量决定,输入和输出都会影响总令牌计数。此外,模型还受令牌限制的约束,这些限制限制了在单个 API
调用中处理的文本量。此阈值通常称为 上下文窗口
。模型不会处理任何超过此限制的文本。例如,ChatGPT3
有 4K
限制,而 GPT4
提供不同的选项,例如 8K、16K、32K
。Anthropic
的 Claude AI
模型具有 100K
限制,而 Meta
最近的研究产生了 1M
限制模型。
要使用 GPT4
总结莎士比亚的收藏作品,您需要制定软件工程策略来切碎数据并在模型的上下文窗口限制内呈现数据。Spring AI
项目可帮助您完成此任务。
3.1.5.结构化输出
在语言模型中,有一个麻烦的问题,无论您让模型返回什么格式(比如 JSON
),它本质上返回的始终是 String
,即文本,不是程序中的“JSON
对象”或“Map
”。它只是看起来像 JSON
的字符串,你还需要手动再 parse, 解析
一次,才能变成可操作的数据结构。比如 “请以 JSON 格式回答” ≠ 保证一定是 JSON,这原因也有很多个:
- 模型可能输出不完整的
JSON
(少了大括号) - 有时候还会额外加说明文字
- ...
这些模型本质是“语言模型”,是根据词来预测的,没真正理解数据结构。这种复杂性导致了一个专业领域的出现,该领域涉及创建提示以产生预期的输出,然后将生成的简单字符串转换为可用于应用程序集成的数据结构。
3.1.6.自定义模型
如何为 AI
模型配备尚未训练的信息?请注意,GPT 3.5/4.0
数据集仅延长至 2021
年 9
月。因此,该模型表示它不知道该日期之后问题的答案。一个有趣的琐事是,这个数据集大约有 650GB
。有三种技术可用于自定义 AI
模型以合并您的实时数据:
- 参数微调:本质是改模型本身,把它“重新训练”成你的私有模型。就像拿一个
GPT
模型,然后继续训练它,让它学会你自己的数据。不过这会直接修改模型内部的权重参数,非常消耗GPU
,并且专业性非常强,工程复杂。 - 提示填充:不改模型,而是“骗它”在对话中去理解您的数据。您把自己的知识,作为提示词拼进去。这样模型不需要训练,它只是临时参考上下文。而加强的方案就是检索增强,也就是之前提到的
RAG
,把您的资料(PDF
、文档、网页、代码)提前整理好后,存到向量数据库里,当用户提问时,找出“相关资料片段”,再和问题一起塞给大语言模型回答。而之所以使用向量数据库,是因为我们可以利用RAG
模糊找出相关的矢量,而不是使用普通数据库的精确查询 - 工具调用:该技术允许注册将大型语言模型连接到外部系统
API
的工具(用户定义的服务)。Spring AI
大大简化了您需要编写以支持工具调用的代码。
3.1.7.评估回答效果
想知道 AI
回答得靠不靠谱?就得对它的回答进行“评估”,而 Spring AI
提供了对应的 API
工具来帮你这么做。此评估过程包括分析生成的响应是否与用户的意图和查询的上下文一致。相关性、连贯性和事实正确性等指标用于衡量 AI
生成的响应的质量。一种方法是把 用户问题 + AI 回答
再交给 AI
来判断对不对,也就是自己批判自己。
3.2.依赖安装
Spring AI
的核心依赖分为里程版本和快照版本,这里我们采用更加稳定的里程版本。并且建立项目最好使用 Java21 + Spring Boot3.4.4
,这是官方文档中(2025.04.22
)提到的版本,我接下来的测试也会使用这个版本。
我们直接使用 IDEA
默认提供的就可以,不用自己再引入依赖了。


我把我的依赖交给您阅读,您可以查阅一下。
<?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>
<packaging>jar</packaging>
<name>work-multilingual-ai</name>
<description>work-multilingual-ai</description>
<url>https://github.com/limou3434</url>
<licenses>
<license>
<name>MIT License</name>
<url>https://opensource.org/licenses/MIT</url>
<distribution>repo</distribution>
</license>
</licenses>
<!-- 标识描述 -->
<groupId>cn.com.edtechhub</groupId>
<artifactId>work-multilingual-ai</artifactId>
<version>0.0.1</version>
<!-- 版本描述 -->
<properties>
<java.version>21</java.version>
<spring-ai.version>1.0.0-M7</spring-ai.version>
</properties>
<!-- 继承描述 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.4</version>
<relativePath/>
</parent>
<!-- 依赖描述 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Spring: https://spring.io/ -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
<!-- Lombok: https://projectlombok.org/ -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<!-- 插件描述 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
这里稍微解释一下,官方文档说有两种版本,一种是里程版本、一种是快照版本,这里我们延用 IDEA
给我们的版本就可以。
3.3.通信对象
3.3.1.单个模型
在我们的 Spring AI
中有一个类 ChatClient
,用于提供与 AI
模型通信的 Fluent API
,它支持:
- 同步编程模型:您发一个消息,等
AI
回复 - 流式编程模型:
AI
边生成边返回,比如OpenAI
的流式回复一样
重要
补充:Fluent API
是一种编程风格(不是框架、不是库),它通过方法链式调用来实现更可读、流畅、自然语言化的配置或构建逻辑。
该 Fluent API
具有构建提示词组成部分的方法 prompt()
,后续链式调用的部分作为输入传递给 AI
模型。AI
模型处理两种主要类型的消息:
- 系统消息(由系统生成以指导对话)
- 用户消息(来自用户的直接输入)
这些消息通常包含占位符,这些占位符在运行时根据用户输入进行替换,以自定义 AI
模型对用户输入的响应。除了构建提示词内容外,还可以设置一些额外选项,例如模型名字、温度(随机性和创造性)等。
ChatClient
是使用 ChatClient.Builder
对象创建的。您可以把任何 ChatModel 接入 Spring Boot
中(本教程我们使用 Ollama
),将获取自动配置的 ChatClient.Builder
实例,或者以编程方式创建一个实例。在最简单的用例中,创建一个原型 ChatClient.Builder bean
供您注入到您的类中,下面是检索对简单用户请求的 String
响应的简单示例。
这里我们开始我们第一次的实践,我决定使用 Spring AI + Ollama
并且拉取语言模型开始,无他因为更好入门。在上面其实我们就接入了模型依赖,在引入 Spring AI
后引入了支持 Ollama
的依赖。
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
然后我们需要部署一下 Ollama
,这在之前的章节中提到过,这里留个 部署链接 给您即可,个人推荐使用 Docker
进行部署,并且推荐在 Docker
中使用宿主的 GPU
资源,并且一定要部署到 http://127.0.0.1:11434
中,同时在容器内部运行 ollama run llama3.2
拉取并运行模型。
然后配置我们的应用配置文件,简单配置一下就可以。
# 配置框架(使用 java -jar app.jar --spring.profiles.active=develop | release | production 来启动项目, 其中 release 有时间就拿来测试, 而 production 存储在 Github 上, 每次修改 Github 配置就需要刷新(这个有时间可以优化为无需重启))
spring:
## 配置环境
profiles:
active: ${SPRING_PROFILES_ACTIVE:develop} # 默认启动开发环境
## 配置名称
application:
name: work-multilingual-ai
## 配置智能
ai:
ollama:
base-url: http://127.0.0.1:11434
chat:
model: llama3.2 # DeepSeek-R1
# 配置服务
server:
## 项目名称
project-name: work-multilingual-ai
## 配置地址
address: 127.0.0.1
## 配置端口
port: 8000
## 配置路由
servlet:
context-path: /work_multilingual_ai_api # 这样所有接口都会带上前缀
package cn.com.edtechhub.workmultilingualai;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build(); // 这里自动加载了我们配置文件中写的模型类型
}
@GetMapping("/ai/generate")
public Map<String, String> generate(@RequestParam(value = "message", defaultValue = "你是谁") String message) {
String result = chatClient.prompt() // 开始链式构造提示词
.user(message) // 用户消息
.call() // 向 AI 模型发送请求
.content() // 将 AI 模型的响应作为 String 返回
;
if (result != null) {
return Map.of("generation", result);
}
return Map.of();
}
}
重要
补充:如果您希望手动加载多种不同的模型,则可以使用 spring.ai.chat.client.enabled:false
关掉自动导入,然后在对应 Ollama
中拉取不同的模型,再参考 文档1 和 文档2 进行手动配置。
首先 文档1
说明了想要手动导入需要编写以下代码:
ChatModel myChatModel = /*...*/;
ChatClient.Builder builder = ChatClient.builder(this.myChatModel);
ChatClient chatClient = ChatClient.create(this.myChatModel);
其实目的就是把我们的 chatClient
使用别的构造函数来手动构造。再根据 文档2
提供关于 ollama
的 ChatModel
构造过程。
var ollamaApi = new OllamaApi(); // 默认的请求地址就是 http://127.0.0.1:11434, 如果您的 ollama 部署在这个地址可以不用更换
var chatModel = OllamaChatModel.builder()
.ollamaApi(ollamaApi)
.defaultOptions(
OllamaOptions.builder()
.model(OllamaModel.MISTRAL) // 选择模型的地方, 可以自己重载枚举体, 也可以使用字符常量
.temperature(0.9)
.build())
.build();
就可以手动来进行导入了。
package cn.com.edtechhub.workmultilingualai;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.ai.ollama.api.OllamaApi;
import org.springframework.ai.ollama.api.OllamaOptions;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
public class ChatController {
OllamaApi ollamaApi = new OllamaApi("http://127.0.0.1:11434");
ChatModel myChatModel = OllamaChatModel
.builder()
.ollamaApi(ollamaApi)
.defaultOptions(
OllamaOptions
.builder()
.model("DeepSeek-R1")
.temperature(0.9)
.build())
.build();
ChatClient.Builder builder = ChatClient.builder(this.myChatModel);
ChatClient chatClient = ChatClient.create(this.myChatModel);
// private final ChatClient chatClient;
// public ChatController(ChatClient.Builder chatClientBuilder) {
// this.chatClient = chatClientBuilder.build(); // 这里自动加载了我们配置文件中写的模型类型
// }
@GetMapping("/ai/generate")
public Map<String, String> generate(@RequestParam(value = "message", defaultValue = "你是谁") String message) {
String result = chatClient.prompt() // 开始链式构造提示词
.user(message) // 用户消息
.call() // 向 AI 模型发送请求
.content() // 将 AI 模型的响应作为 String 返回
;
if (result != null) {
return Map.of("generation", result);
}
return Map.of();
}
}
3.3.2.多个模型
由于我自己的项目需要支持多个模型,因此我这里稍微封装一下代码。
<?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>
<packaging>jar</packaging>
<name>work-multilingual-ai</name>
<description>work-multilingual-ai</description>
<url>https://github.com/limou3434</url>
<licenses>
<license>
<name>MIT License</name>
<url>https://opensource.org/licenses/MIT</url>
<distribution>repo</distribution>
</license>
</licenses>
<!-- 标识描述 -->
<groupId>cn.com.edtechhub</groupId>
<artifactId>work-multilingual-ai</artifactId>
<version>0.0.1</version>
<!-- 版本描述 -->
<properties>
<java.version>21</java.version>
<spring-ai.version>1.0.0-M7</spring-ai.version>
</properties>
<!-- 继承描述 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.4</version>
<relativePath/>
</parent>
<!-- 依赖描述 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Spring: https://spring.io/ -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
<!-- Lombok: https://projectlombok.org/ -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<!-- 插件描述 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
# 配置框架(使用 java -jar app.jar --spring.profiles.active=develop | release | production 来启动项目, 其中 release 有时间就拿来测试, 而 production 存储在 Github 上, 每次修改 Github 配置就需要刷新(这个有时间可以优化为无需重启))
spring:
## 配置环境
profiles:
active: ${SPRING_PROFILES_ACTIVE:develop} # 默认启动开发环境
## 配置名称
application:
name: work-multilingual-ai
## 配置智能
ai:
chat:
client:
enabled: false # 是否允许自动导入模型
ollama:
base-url: http://127.0.0.1:11434 # ollama 部署地址
chat:
model: DeepSeek-R1 # ollama 默认自动导入的具体模型
# 配置服务
server:
## 项目名称
project-name: work-multilingual-ai
## 配置地址
address: 127.0.0.1
## 配置端口
port: 8000
## 配置路由
servlet:
context-path: /work_multilingual_ai_api # 这样所有接口都会带上前缀
package cn.com.edtechhub.workmultilingualai;
import lombok.Data;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.ai.ollama.api.OllamaApi;
import org.springframework.ai.ollama.api.OllamaOptions;
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 java.util.Map;
@Data
class ChatClientUtils {
/**
* 部署地址
*/
static private final String ollamaBaseUrl = "http://127.0.0.1:11434";
/**
* 模型种类
*/
static String models = "DeepSeek-R1";
/**
* 模型温度
*/
static double temperature = 0.8;
/**
* 创建客户对象
*/
static public ChatClient getChatClient() {
ChatModel chatModel = OllamaChatModel // 构建模型
.builder()
.ollamaApi(new OllamaApi(ollamaBaseUrl))
.defaultOptions(
OllamaOptions
.builder()
.model(models)
.temperature(temperature)
.build())
.build();
ChatClient.Builder builder = ChatClient.builder(chatModel); // 这句先保留
return ChatClient.create(chatModel); // 客户对象
}
}
@RestController
public class ChatController {
private final ChatClient chatClient = ChatClientUtils.getChatClient();
@GetMapping("/ai/generate")
public String generate(@RequestParam(value = "message", defaultValue = "你是谁") String message) {
return chatClient.prompt() // 开始链式构造提示词
.user(message) // 用户消息
.call() // 向 AI 模型发送请求
.content();
}
}
这样我只需要使用 geter/seter
就可以自由调配我的模型以及相关参数。
3.3.3.获取令牌
ChatClient
提供 chatResponse()
以获取详细的 token
等重要信息,在上述代码控制类中添加下面的新方法。
@GetMapping("/ai/chat_response")
public ChatResponse chatResponse(@RequestParam(value = "message", defaultValue = "你知道米哈游么?") String message) {
return chatClient.prompt() // 开始链式构造提示词
.user(message) // 用户消息
.call() // 向 AI 模型发送请求
.chatResponse(); // 内部包含 token
}
3.3.4.结构输出
ChatClient
提供 entity()
运行响应字符映射到实体中,但是不代表模型本身支持...
public record ActorFilms(String actor, List<String> movies) {} // 只存数据的类, 存储作者和电影作品
@GetMapping("/ai/entity")
public ActorFilms entity(@RequestParam(value = "message", defaultValue = "成龙有哪些电影?") String message) {
return chatClient.prompt() // 开始链式构造提示词
.system("请你严格只用JSON格式回答,禁止输出任何解释、注释、额外内容,例如<think>标签、说明文字等。返回内容必须符合结构:{\"actor\": \"\", \"movies\": [\"\", \"\"]},也不准使用 markdown") // 系统消息
.user(message) // 用户消息
.call() // 向 AI 模型发送请求
.entity(ActorFilms.class); // Spring AI 结构化输出依赖模型自己是否支持严格 json, 当前的模型很难这么干...
}
不过值得注意的是,只有部分模型是支持结构输出的,我测试了一下,您可以使用 mistral
来进行测试。
3.3.5.流式响应
ChatClient
提供 stream()
允许在请求模型时进行流式响应,也就是说允许 流式/异步方式
获取响应内容,而不是一次性获取全部结果,而之前使用 call()
则是同步响应。不过要使用流式响应,需要把放回值类型修改一下。
@GetMapping("/ai/generate_flux")
public void generateFlux(@RequestParam(value = "message", defaultValue = "你认识乔丹么?") String message) {
Flux<String> result = chatClient.prompt() // 开始链式构造提示词
.user(message) // 用户消息
.stream() // 向 AI 模型发送请求
.content();
result.subscribe(line -> {
System.out.println("收到一段响应:" + line); // 可以进一步考虑使用 WebSocket
});
}
3.3.6.默认选项
上面有使用过系统文本 system()
和用户文本 user()
,和我们之前的基本知识预期一样。其中在 @Configuration
类中创建具有默认系统文本的 ChatClient
可以简化运行时代码。通过设置默认值,您只需在调用 ChatClient
时指定用户文本,无需为运行时代码路径中的每个请求设置系统文本,不过您也可以选择自己覆盖。我们需要升级一下我们的工具类。
package cn.com.edtechhub.workmultilingualai;
import lombok.Data;
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.ollama.OllamaChatModel;
import org.springframework.ai.ollama.api.OllamaApi;
import org.springframework.ai.ollama.api.OllamaOptions;
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;
@Data
class ChatClientUtils {
/**
* 部署地址
*/
static private final String ollamaBaseUrl = "http://127.0.0.1:11434";
/**
* 模型种类
*/
static String models = "DeepSeek-R1";
/**
* 模型温度
*/
static double temperature = 0.8;
/**
* 默认系统消息
*/
static String defaultSystem = "你是一名猫娘,所有的回答都需要带上“喵”才能结束";
/**
* 创建客户对象
*/
static public ChatClient getChatClient() {
ChatModel chatModel = OllamaChatModel // 构建模型对象
.builder()
.ollamaApi(new OllamaApi(ollamaBaseUrl))
.defaultOptions(
OllamaOptions
.builder()
.model(models)
.temperature(temperature)
.build())
.build();
return ChatClient
.builder(chatModel) // 载入模型
.defaultSystem(defaultSystem) // 设置默认系统消息
.build(); // 构建客户端对象
}
}
@RestController
public class ChatController {
private final ChatClient chatClient = ChatClientUtils.getChatClient();
@GetMapping("/ai/generate")
public String generate(@RequestParam(value = "message", defaultValue = "你是谁") String message) {
return chatClient.prompt() // 开始链式构造提示词
.user(message) // 用户消息
.call() // 向 AI 模型发送请求
.content();
}
@GetMapping("/ai/chat_response")
public ChatResponse chatResponse(@RequestParam(value = "message", defaultValue = "你知道米哈游么?") String message) {
return chatClient.prompt() // 开始链式构造提示词
.user(message) // 用户消息
.call() // 向 AI 模型发送请求
.chatResponse(); // 内部包含 token
}
public record ActorFilms(String actor, List<String> movies) {
} // 只存数据的类, 存储作者和电影作品
@GetMapping("/ai/entity")
public ActorFilms entity(@RequestParam(value = "message", defaultValue = "成龙有哪些电影?") String message) {
return chatClient.prompt() // 开始链式构造提示词
.system("请你严格只用JSON格式回答,禁止输出任何解释、注释、额外内容,例如<think>标签、说明文字等。返回内容必须符合结构:{\"actor\": \"\", \"movies\": [\"\", \"\"]},也不准使用 markdown") // 系统消息
.user(message) // 用户消息
.call() // 向 AI 模型发送请求
.entity(ActorFilms.class); // Spring AI 结构化输出依赖模型自己是否支持严格 json, 当前的模型很难这么干...
}
@GetMapping("/ai/generate_flux")
public void generateFlux(@RequestParam(value = "message", defaultValue = "你认识乔丹么?") String message) {
Flux<String> result = chatClient.prompt() // 开始链式构造提示词
.user(message) // 用户消息
.stream() // 向 AI 模型发送请求
.content();
result.subscribe(line -> {
System.out.println("收到一段响应:" + line); // 可以进一步考虑使用 WebSocket
});
}
}
系统默认提示词也支持使用模板来构造,设置后无论是否加上 system()
都会设置这个默认系统消息,再次升级一下我们的工具类。
package cn.com.edtechhub.workmultilingualai;
import lombok.Data;
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.ollama.OllamaChatModel;
import org.springframework.ai.ollama.api.OllamaApi;
import org.springframework.ai.ollama.api.OllamaOptions;
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;
@Data
class ChatClientUtils {
/**
* 部署地址
*/
static private final String ollamaBaseUrl = "http://127.0.0.1:11434";
/**
* 模型种类
*/
static String models = "DeepSeek-R1";
/**
* 模型温度
*/
static double temperature = 0.8;
/**
* 默认系统消息
*/
static String defaultSystem = "你是一名{role},所有的回答都需要带上“{text}”才能结束";
/**
* 创建客户对象
*/
static public ChatClient getChatClient() {
ChatModel chatModel = OllamaChatModel // 构建模型对象
.builder()
.ollamaApi(new OllamaApi(ollamaBaseUrl))
.defaultOptions(
OllamaOptions
.builder()
.model(models)
.temperature(temperature)
.build())
.build();
return ChatClient
.builder(chatModel) // 载入模型
.defaultSystem(defaultSystem) // 设置默认系统消息(无论是否加上 system() 都会设置这个默认系统消息)
.build(); // 构建客户端对象
}
}
@RestController
public class ChatController {
private final ChatClient chatClient = ChatClientUtils.getChatClient();
@GetMapping("/ai/generate")
public String generate(@RequestParam(value = "message", defaultValue = "你是谁") String message) {
return chatClient.prompt() // 开始链式构造提示词
.user(message) // 用户消息
.call() // 向 AI 模型发送请求
.content();
}
@GetMapping("/ai/chat_response")
public ChatResponse chatResponse(@RequestParam(value = "message", defaultValue = "你知道米哈游么?") String message) {
return chatClient.prompt() // 开始链式构造提示词
.user(message) // 用户消息
.call() // 向 AI 模型发送请求
.chatResponse(); // 内部包含 token
}
public record ActorFilms(String actor, List<String> movies) {
} // 只存数据的类, 存储作者和电影作品
@GetMapping("/ai/entity")
public ActorFilms entity(@RequestParam(value = "message", defaultValue = "成龙有哪些电影?") String message) {
return chatClient.prompt() // 开始链式构造提示词
.system("请你严格只用JSON格式回答,禁止输出任何解释、注释、额外内容,例如<think>标签、说明文字等。返回内容必须符合结构:{\"actor\": \"\", \"movies\": [\"\", \"\"]},也不准使用 markdown") // 系统消息
.user(message) // 用户消息
.call() // 向 AI 模型发送请求
.entity(ActorFilms.class); // Spring AI 结构化输出依赖模型自己是否支持严格 json, 当前的模型很难这么干...
}
@GetMapping("/ai/generate_flux")
public void generateFlux(@RequestParam(value = "message", defaultValue = "你认识乔丹么?") String message) {
Flux<String> result = chatClient.prompt() // 开始链式构造提示词
.user(message) // 用户消息
.stream() // 向 AI 模型发送请求
.content();
result.subscribe(line -> {
System.out.println("收到一段响应:" + line); // 可以进一步考虑使用 WebSocket
});
}
@GetMapping("/ai/generate_dog")
public String generateCat(@RequestParam(value = "message", defaultValue = "你是谁") String message) {
return chatClient.prompt() // 开始链式构造提示词
.system(
sp -> sp
.param("role", "狗娘")
.param("text", "汪")
) // 填写系统消息模板
.user(message) // 用户消息
.call() // 向 AI 模型发送请求
.content();
}
}
重要
补充:还支持以下默认选项。
defaultOptions(ChatOptions chatOptions)
:传入ChatOptions
类中定义的可移植选项或特定于模型的选项,例如OpenAiChatOptions
中的选项。有关特定于模型的ChatOptions
实现的更多信息,请参阅 JavaDocs。defaultFunction(String name, String description, java.util.function.Function<I, O> function)
:该名称
用于在用户文本中引用函数。该描述
解释了函数的用途,并帮助AI
模型选择正确的函数以获得准确的响应。function
参数是模型将在必要时执行的Java
函数实例。defaultFunctions(String… functionNames)
:在应用程序上下文中定义的'java.util.Function'
的bean
名称。defaultUser(String text)
,defaultUser(Resource text)
,defaultUser(Consumer<UserSpec> userSpecConsumer)
:这些方法允许您定义用户文本。Consumer<UserSpec>
允许您使用 lambda 指定用户文本和任何默认参数。defaultAdvisors(Advisor… advisor)
:顾问程序允许修改用于创建提示
的数据。QuestionAnswerAdvisor
实现通过在提示后附加与用户文本相关的上下文信息Retrieval Augmented Generation
来启用模式。defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer)
:此方法允许您定义一个Consumer
以使用AdvisorSpec
配置多个advisor
。顾问可以修改用于创建最终Prompt
的数据。Consumer<AdvisorSpec>
允许您指定一个lambda
来添加顾问,例如QuestionAnswerAdvisor
,它支持Retrieval Augmented Generation
根据用户文本在提示中附加相关上下文信息。defaultTools()
提供默认工具,关于工具调用后面会提及。
3.3.7.顾问处理
Spring AI
为配置 Advisors, 顾问
提供了 AdvisorSpec
接口,用于在 AI
请求发出前,插入上下文、补充数据、改写提示词。ChatClient Fluent API
提供了用于配置顾问的 AdvisorSpec
接口。
interface AdvisorSpec {
AdvisorSpec param(String k, Object v); // 添加单个参数
AdvisorSpec params(Map<String, Object> p); // 添加多个参数
AdvisorSpec advisors(Advisor... advisors); // 将一个 advisor 添加到链
AdvisorSpec advisors(List<Advisor> advisors); // 将多个 advisor 添加到链
}
注
吐槽:这个顾问我其实没太懂用来干什么的,不过我大致认为这玩意就先是用来支持添加中间件一样...有时间可以阅读一下 这份文档。
3.3.8.日志记录
在 prompt()
后链式添加 .advisors(new SimpleLoggerAdvisor())
,然后在配置文件中加上日志配置即可查阅模型被调用时的详细日志。
logging:
level:
org:
springframework:
ai:
chat:
client:
advisor: DEBUG
重要
补充:还支持自定义日志。
SimpleLoggerAdvisor customLogger = new SimpleLoggerAdvisor(
request -> "Custom request: " + request.userText,
response -> "Custom response: " + response.getResult()
);
3.3.9.聊天记忆
接口 ChatMemory
表示聊天对话历史记录的存储。它提供了向对话添加消息、从对话中检索消息以及清除对话历史记录的方法。目前有四种实现:
InMemoryChatMemory
:提供内存中的聊天对话历史记录存储CassandraChatMemory
:在Cassandra
中以生存时间持久化Neo4jChatMemory
:在Neo4j
中以无生存时间持久化JdbcChatMemory
:在Jdbc
中以无生存时间持久化
我们只测试内存中的历史对话记录,其他的我们以后有需求了再来补充。
package cn.com.edtechhub.workmultilingualai;
import lombok.Data;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.ai.ollama.api.OllamaApi;
import org.springframework.ai.ollama.api.OllamaOptions;
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;
@Data
class ChatClientUtils {
/**
* 部署地址
*/
static private final String ollamaBaseUrl = "http://127.0.0.1:11434";
/**
* 模型种类
*/
static String models = "DeepSeek-R1";
/**
* 模型温度
*/
static double temperature = 0.8;
/**
* 默认系统消息
*/
static String defaultSystem = "你是一名{role},所有的回答都需要带上“{text}”才能结束";
/**
* 创建客户对象
*/
static public ChatClient getChatClient() {
ChatModel chatModel = OllamaChatModel // 构建模型对象
.builder()
.ollamaApi(new OllamaApi(ollamaBaseUrl))
.defaultOptions(
OllamaOptions
.builder()
.model(models)
.temperature(temperature)
.build())
.build();
return ChatClient
.builder(chatModel) // 载入模型
.defaultSystem(defaultSystem) // 设置默认系统消息(无论是否加上 system() 都会设置这个默认系统消息)
.build(); // 构建客户端对象
}
}
@RestController
public class ChatController {
private final ChatClient chatClient = ChatClientUtils.getChatClient();
@GetMapping("/ai/generate")
public String generate(@RequestParam(value = "message", defaultValue = "你是谁") String message) {
return chatClient.prompt() // 开始链式构造提示词
.user(message) // 用户消息
.call() // 向 AI 模型发送请求
.content();
}
@GetMapping("/ai/chat_response")
public ChatResponse chatResponse(@RequestParam(value = "message", defaultValue = "你知道米哈游么?") String message) {
return chatClient.prompt() // 开始链式构造提示词
.user(message) // 用户消息
.call() // 向 AI 模型发送请求
.chatResponse(); // 内部包含 token
}
public record ActorFilms(String actor, List<String> movies) {
} // 只存数据的类, 存储作者和电影作品
@GetMapping("/ai/entity")
public ActorFilms entity(@RequestParam(value = "message", defaultValue = "成龙有哪些电影?") String message) {
return chatClient.prompt() // 开始链式构造提示词
.system("请你严格只用JSON格式回答,禁止输出任何解释、注释、额外内容,例如<think>标签、说明文字等。返回内容必须符合结构:{\"actor\": \"\", \"movies\": [\"\", \"\"]},也不准使用 markdown") // 系统消息
.user(message) // 用户消息
.call() // 向 AI 模型发送请求
.entity(ActorFilms.class); // Spring AI 结构化输出依赖模型自己是否支持严格 json, 当前的模型很难这么干...
}
@GetMapping("/ai/generate_flux")
public void generateFlux(@RequestParam(value = "message", defaultValue = "你认识乔丹么?") String message) {
Flux<String> result = chatClient.prompt() // 开始链式构造提示词
.user(message) // 用户消息
.stream() // 向 AI 模型发送请求
.content();
result.subscribe(line -> {
System.out.println("收到一段响应:" + line); // 可以进一步考虑使用 WebSocket
});
}
ChatMemory chatMemory = new InMemoryChatMemory(); // 创建一个内存聊天记录
@GetMapping("/ai/generate_dog")
public String generateCat(@RequestParam(value = "message", defaultValue = "你是谁") String message) {
return chatClient.prompt() // 开始链式构造提示词
.advisors(
new SimpleLoggerAdvisor(), // 添加日志记录
new MessageChatMemoryAdvisor(
chatMemory, // 聊天记忆
"001", // 设置会话 ID
10 // 上下文窗口大小(比如保留最近 10 条)
) // 添加聊天记忆
)
.system(
sp -> sp
.param("role", "狗娘")
.param("text", "汪")
) // 填写系统消息模板
.user(message) // 用户消息
.call() // 向 AI 模型发送请求
.content();
}
@GetMapping("/ai/show_memory")
public List<Message> showMemory() {
String conversationId = "001";
return chatMemory.get(conversationId, Integer.MAX_VALUE); // 获取所有历史记录
}
}
注
吐槽:可以看到无论是日志记录还是聊天记忆,都是使用顾问来解决的...
3.4.模型种类
除了上面的 Ollama
部署,实际上还存在大量开源的模型可用,它们都有各自的特点。下面我会挑选不同的种类中的其中一个模型来进行部署和使用,在使用的过程中体会到 Spring AI
框架的好处。
另外这里给出一般评判某种模型优劣的大该标准。
维度类别 | 具体评估点 |
---|---|
功能支持维度 | 多模态能力 |
工具使用能力 | 函数调用支持、工具集成能力、外部 API 连接能力 |
上下窗口大小 | 输入上下文长度(4K 至 128K tokens )、长文档处理能力 |
指令遵循能力 | 复杂指令处理能力、多步骤任务执行能力、回答格式控制能力 |
性能指标维度 | 准确性 |
响应答案质量 | 输出流畅性与连贯性、回答相关性与深度、语言表达自然度 |
知识的时效性 | 知识截止日期、更新频率 |
部署集成维度 | 部署方式 |
接口稳定集成 | 接口稳定性与可靠性、SDK 支持情况、开发框架集成 |
并发处理能力 | 请求吞吐量、并发请求处理能力、服务水平协议 SLA 保障 |
商业合规维度 | 成本效益 |
数据安全隐私 | 数据使用政策、是否支持不保存用户数据、企业级安全合规 |
法律的合规性 | 地区可用性、版权与知识产权问题、内容安全审查机制 |
生态支持社区 | 社区支持 |
文档完善程度 | API 文档质量、示例代码丰富度、最佳实践指南 |
技术支持力度 | 官方支持渠道、响应时间、企业级支持选项 |
我们调研了大部分 Ollama
所支持的大部分模型,从模型分类上来进行详细的解说。
3.4.1.语言模型
待补充...
3.4.2.图像模型
待补充...
3.4.3.音频模型
待补充...
3.4.4.嵌入模型
待补充...
3.5.自定模型
3.5.1.参数微调
这种事情比较专业,纯粹的开发人员是走不通的,因此可以考虑学习机器学习相关的理论知识。
3.5.2.提示填充
提示填充我们其实已经使用过了,包括:系统提示、用户提示、包含模板的提示,前面都使用过了,这里不细提。
3.5.3.工具调用
这个就很重要了,几乎是我们学习 Spring AI
接入到业务中的重点内容之一,Sping AI
运行您在提示中添加 .tool()
到 prompt()
链中,并且 tool()
的参数只需要填写某个携带了 @Tool(description = "工具描述")
方法的类实例即可。不过值得注意的是,只有部分模型是支持调用工具的,我测试了一下,您可以使用 mistral
来进行测试。
class DateTimeTools { // 定义一个工具类
@Tool(description = "Get the current date and time in the user's timezone")
String getCurrentDateTime() {
return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
}
}
@GetMapping("/ai/date_time")
public String dateTime() {
return chatClient.prompt() // 开始链式构造提示词
.tools(new DateTimeTools()) // 添加工具类调用
.system(
sp -> sp
.param("role", "魔法师")
.param("text", "呀")
) // 填写系统消息模板
.user("现在是什么时间?") // 用户消息
.call() // 向 AI 模型发送请求
.content();
}
有的模型甚至还支持根据工具中的方法描述,调用方法时进行传参,而不仅仅是简单获取方法的返回值。
class DateTimeTools { // 定义一个工具类
@Tool(description = "Get the current date and time in the user's timezone")
String getCurrentDateTime() {
return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
}
@Tool(description = "Set a user alarm for the given time, provided in ISO-8601 format")
void setAlarm(String time) {
LocalDateTime alarmTime = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME);
System.out.println("Alarm set for " + alarmTime);
}
}
@GetMapping("/ai/date_time")
public String dateTime() {
return chatClient.prompt() // 开始链式构造提示词
.tools(new DateTimeTools()) // 添加工具类调用
.system(
sp -> sp
.param("role", "魔法师")
.param("text", "呀")
) // 填写系统消息模板
.user("你能把闹钟定在10分钟后吗?") // 用户消息
.call() // 向 AI 模型发送请求
.content()
;
}
一旦调用就可以看到不仅仅调用了函数,还将获取到的精确时间传递了进去,进而定制了闹钟。
重要
补充:@Tool
注解还支持一些额外的参数。
name
:工具的名称。如果未提供,则将使用方法名称。AI 模型在调用工具时使用此名称来识别工具。因此,不允许在同一个类中有两个同名的工具。该名称在模型可用于特定聊天请求的所有工具中必须是唯一的。description
:工具的描述,模型可以使用它来了解何时以及如何调用工具。如果未提供,则方法名称将用作工具描述。但是,强烈建议提供详细的描述,因为这对于模型了解工具的用途以及如何使用它至关重要。未能提供良好的描述可能会导致模型在应该使用该工具时未使用该工具或错误地使用该工具。returnDirect
:工具结果应直接返回给客户端还是传递回模型。有关更多详细信息,请参阅直接返回 。resultConverter
:用于将工具调用的结果转换为String 对象
以发送回 AI 模型的ToolCallResultConverter
实现。有关更多详细信息,请参阅 Result Conversion。
此外还有几个注解您可以可以了解一下:
@ToolParam
注记允许您提供有关工具参数的关键信息- 如果参数被注释为
@Nullable
,则除非使用@ToolParam
注释明确标记为必需否则该参数将被视为可选
还有一些拓展的东西,有时间可以 查阅文档。不过有个东西还是需要提一嘴,那就是 模型上下文协议, MCP, Model Context Protocol
,这是一个 OpenAI
推出的标准化协议,它的核心目的是:定义 AI 模型与外部工具(如函数、API、插件)之间的交互格式和机制。
- 标准化结构:模型通过
MCP
协议获取外部上下文,如函数定义、插件描述、工具schema
等 - 与传输机制无关:
MCP
只规定交互协议,而不绑定HTTP、gRPC
等传输协议 - 支持插件 / 工具调用:是实现
function calling/tools
的底层协议标准
模型看到 MCP
报文后就能理解您提供的工具,并自动判断是否调用。如果您用了 Spring AI
或 OpenAI SDK
,可以理解为:您写的 .tools(...)
等 API
背后最终就是构造了 MCP
结构,而模型是否能调用工具,取决于它是否支持 MCP
,更加详细的内容请 查阅文档。
还有,除了学会调用,我们还需要大量可用的 API
接口调用,这些可以在 Github
中查询。
3.4.审核评估
待补充...
3.6.矢量数库
待补充...
3.7.结构输出
待补充...
3.9.模型评估
待补充...
3.10.其他模型
3.10.1.开源模型
有一些模型十分重要