跳到主要内容
4. 工程搭建
1. 版本依赖
2. 接入阿里百炼平台的通义模型
1) 获得 Api-key
2) 获取模型名称
3) 获得 baseUrl
3. 创建父工程
1) 创建 SpringAIAlibabaProject 父工程
2) 修改 pom 文件
<?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.hk</groupId>
<artifactId>springAIAlibabaProject</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<java.version>21</java.version>
<spring-boot.version>3.5.5</spring-boot.version>
<spring-ai.version>1.0.0</spring-ai.version>
<SpringAIAlibaba.version>1.0.0.2</SpringAIAlibaba.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>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-bom</artifactId>
<version>${SpringAIAlibaba.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>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
4. 创建子模块
1) 创建 SAA-01HelloWord 子模块
2) 修改 pom 文件
<?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>
<parent>
<groupId>com.hk</groupId>
<artifactId>springAIAlibabaProject</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>SAA-01HelloWord</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.22</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
3) 添加配置文件
server.port=8001
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=utf-8
spring.application.name=SSA-01HelloWord
spring.ai.dashscope.api-key=${aliQwen-api}
spring.ai.dashscope.base-url=https://dashscope.aliyuncs.com/compatible-mode/v1
spring.ai.dashscope.chat.options.model=qwen-plus
4) 配置 ApiKey
5) 主启动
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
6) 编写配置类
@Configuration
public class SaaLLMConfig {
@Value("${spring.spring.ai.dashscope.api-key}")
private String apiKey;
@Bean
public DashScopeApi dashScopeApi(){
return DashScopeApi.builder().apiKey(apiKey).build();
}
}
@Configuration
public class SaaLLMConfig {
@Bean
public DashScopeApi dashScopeApi(){
return DashScopeApi.builder()
.apiKey(System.getenv("aliQwen-api"))
.build();
}
}
7) 编写 Controller
@RestController
public class ChatHelloController {
@Autowired
private ChatModel chatModel;
@GetMapping("/hello/v1")
public String doChat(@RequestParam(value = "msg", defaultValue = "你是谁") String msg) {
return chatModel.call(msg);
}
@GetMapping("/hello/v2")
public Flux<String> streamChat(@RequestParam(value = "msg", defaultValue = "你是谁") String msg) {
return chatModel.stream(msg);
}
}
8) 测试
9) 切换其他模型
spring.ai.dashscope.api-key=${aliQwen-api}
spring.ai.dashscope.base-url=https://dashscope.aliyuncs.com/compatible-mode/v1
spring.ai.dashscope.chat.options.model=deepseek-v3.2
5. Ollama 私有化部署
1. 介绍
Ollama 是一个开源、跨平台的本地大语言模型(LLM)部署与管理工具,核心目标是让普通用户与开发者能在自己设备上一键运行各类开源大模型,无需复杂配置、不依赖云端服务,兼顾隐私与低延迟。
2. 安装
1) 下载地址
2) 安装
- 将OllamaSetup.exe复制到指定文件夹,例如:
D:\mysoftware
- 在该文件夹输入 cmd 打开黑窗口
- 输入命令
OllamaSetup.exe /DIR=要安装的目录
例如:OllamaSetup.exe /DIR=D:\mysoftware\Ollama
如果不指定目录,默认安装在 C盘
3) 修改模型存储目录
Ollama 默认存储模型的位置是 C:\Users\HP\.ollama
- 将里面的 models 文件夹复制到 D:\mysoftware\Ollama 下面,然后将 C盘的删除
- 重启 Ollama,打开 CMD,输入 Ollama list,查看大模型资源是否能够正常显示
3. 安装大模型
- Ollama 的默认端口为 11434,查看 Ollama 是否安装成功
Ollama --version
- 在 Ollama 上查找模型
找到需要下载的模型,复制命令,安装完成后用 ollama list 查看安装的模型列表
4. 微服务对接模型
1) 创建新的模块
2) 修改 pom 文件
<?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>
<parent>
<groupId>com.hk</groupId>
<artifactId>springAIAlibabaProject</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>SAA-02Ollma</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<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-ollama</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.22</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
3) 添加配置文件
server.port=8002
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
spring.application.name=SAA-02Ollama
spring.ai.ollama.base-url=http://localhost:11434
spring.ai.ollama.chat.model=qwen:0.5b
4) 编写 Controller
@RestController
public class OllamaController {
@Autowired
private ChatModel chatModel;
@GetMapping("/ollama/chat")
public String chat(@RequestParam String msg) {
return chatModel.call(msg);
}
}
5) 测试
6) ollama 与 dashscope 并存问题
server.port=8002
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
spring.application.name=SAA-02Ollama
spring.ai.dashscope.api-key=${aliQwen-api}
spring.ai.ollama.base-url=http://localhost:11434
spring.ai.ollama.chat.model=qwen:0.5b
@Configuration
public class SaaLLMConfig {
@Bean
public DashScopeApi dashScopeApi(){
return DashScopeApi.builder()
.apiKey(System.getenv("aliQwen-api"))
.build();
}
}
发现出现了异常,原因是出现了多个 Model,程序并不知道调用哪一个 model
@RestController
public class OllamaController {
@Resource(name = "ollamaChatModel")
private ChatModel chatModel;
@GetMapping("/ollama/chat")
public String chat(@RequestParam String msg) {
return chatModel.call(msg);
}
}
@RestController
public class OllamaController {
@Resource
@Qualifier("ollamaChatModel")
private ChatModel chatModel;
@GetMapping("/ollama/chat")
public String chat(@RequestParam String msg) {
return chatModel.call(msg);
}
}
6. ChatClient VS ChatModel
1. 简介
ChatClient 提供了与 AI 模型通信的 Fluent API,它支持同步和反应式(Reactive)编程模型。与 ChatModel、Message、ChatMemory 等原子 API 相比,使用 ChatClient 可以将与 LLM 及其他组件交互的复杂性隐藏在背后,因为基于 LLM 的应用程序通常要多个组件协同工作(例如,提示词模板、聊天记忆、LLM Model、输出解析器、RAG 组件:嵌入模型和存储),并且通常涉及多个交互,因此协调它们会让编码变得繁琐。当然使用 ChatModel 等原子 API 可以为应用程序带来更多的灵活性,成本就是您需要编写大量样板代码。
ChatClient 类似于应用程序开发中的服务层,它为应用程序直接提供 AI 服务,开发者可以使用 ChatClient Fluent API 快速完成一整套 AI 交互流程的组装。
- 定制和组装模型的输入(Prompt)
- 格式化解析模型的输出(Structured Output)
- 调整模型交互参数(ChatOptions)
- 聊天记忆(Chat Memory)
- 工具/函数调用(Function Calling)
- RAG
ChatClient 是高级封装,基于 ChatModel 构建,适合快速构建标准化复杂 AI 服务,支持同步和流式交互,集成多种高级功能。
2. 编码案例
1) 创建工程
2) 修改 pom 文件
3) 添加配置文件
server.port=8003
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
spring.application.name=SAA-03ChatClient
spring.ai.dashscope.api-key=${aliQwen-api}
4) 配置类
@Configuration
public class SaaLLMConfig {
@Bean
public DashScopeApi dashScopeApi(){
return DashScopeApi.builder()
.apiKey(System.getenv("aliQwen-api"))
.build();
}
}
5) 只使用 ChatModel
@RestController
public class ChatModelController {
@Autowired
private ChatModel chatModel;
@GetMapping("/chatmodel/chat")
public String chat(@RequestParam(value = "msg", defaultValue = "你是谁") String msg){
return chatModel.call(msg);
}
}
6) 只使用 ChatClient
如果直接自动注入 ChatClient,会出现异常,所以 ChatClient 不支持自动注入,只能手动注入。
@RestController
public class ChatClientController {
private final ChatClient chatClient;
public ChatClientController(ChatModel chatModel) {
this.chatClient = ChatClient.builder(chatModel).build();
};
@GetMapping("/chatclient/chat")
public String chat(@RequestParam(value = "msg", defaultValue = "你是谁") String msg){
return chatClient.prompt().user(msg).call().content();
}
}
7) ChatModel 和 ChatClient 混合使用
修改配置文件,将 ChatClient 使用配置类注入
@Configuration
public class SaaLLMConfig {
@Bean
public DashScopeApi dashScopeApi(){
return DashScopeApi.builder()
.apiKey(System.getenv("aliQwen-api"))
.build();
}
@Bean
public ChatClient chatClient(ChatModel chatModel) {
return ChatClient.builder(chatModel).build();
}
}
@RestController
public class ChatClientModelController {
@Resource
private ChatModel chatModel;
@Resource
private ChatClient chatClient;
@GetMapping("/chat/model")
public Flux<String> chatM(@RequestParam String msg) {
return chatModel.stream(msg);
}
@GetMapping("/chat/client")
public Flux<String> chatC(@RequestParam String msg) {
return chatClient.prompt().user(msg).stream().content();
}
}
7. Stream 流式输出及多模型共存
1. 流式输出介绍
StreamingOutput(流式输出)是一种逐步返回大模型生成结果的技术,生成一点返回一点,允许服务器将响应内容分批次实时传输给客户端,而不是等待全部内容生成完毕后再一次性返回。
这种机制能显著提升用户体验,尤其适用于大模型响应较慢的场景(如生成长文本或复杂推理结果)。
2. SSE
Server-Sent Events (SSE) 是一种允许服务端可以持续推送数据片段(如逐词或逐句)到前端的 Web 技术。通过单向的 HTTP 长连接,使用一个长期存在的连接,让服务器可以主动将数据'推'给客户端,SSE 是轻量级的单向通信协议,适合 AI 对话这类服务端主导的场景
SSE 的核心思想是:客户端发起一个请求,服务器保持这个连接打开并在有新数据时,通过这个连接将数据发送给客户端。这与传统的请求 - 响应模式(客户端请求一次,服务器响应一次,连接关闭)有本质区别。
3. 代码实现多模型共存使用
1) 创建模块
2) 修改 pom 文件
3) 添加配置文件
server.port=8004
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
spring.application.name=SAA-04StreamingOutput
spring.ai.dashscope.api-key=${aliQwen-api}
4) 创建配置类 ChatModel 实现
@Configuration
public class SaaLLMConfig {
private final String DEEPSEEK_MODEL = "deepseek-v3";
private final String QWEN_MODEL = "qwen-plus";
@Bean("deepseek")
public ChatModel deepSeek() {
return DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(System.getenv("aliQwen-api")).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(DEEPSEEK_MODEL).build())
.build();
}
@Bean("qwen")
public ChatModel qwen() {
return DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(System.getenv("aliQwen-api")).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(QWEN_MODEL).build())
.build();
}
}
5) 创建 Controller 类 ChatModel 实现
@RestController
public class ChatModelStreamOutputController {
@Resource(name = "deepseek")
private ChatModel deepseekChatModel;
@Resource(name = "qwen")
private ChatModel qwenChatModel;
@GetMapping("/stream-v1")
public Flux<String> streamDeepSeek(@RequestParam(value = "msg", defaultValue = "你是谁") String msg) {
return deepseekChatModel.stream(msg);
}
@GetMapping("/stream-v2")
public Flux<String> streamQWen(@RequestParam(value = "msg", defaultValue = "你是谁") String msg) {
return qwenChatModel.stream(msg);
}
}
6) ChatModel 测试
7) 修改配置类 ChatClient 实现
@Configuration
public class SaaLLMConfig {
private final String DEEPSEEK_MODEL = "deepseek-v3";
private final String QWEN_MODEL = "qwen-plus";
@Bean("deepseek")
public ChatModel deepSeek() {
return DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(System.getenv("aliQwen-api")).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(DEEPSEEK_MODEL).build())
.build();
}
@Bean("qwen")
public ChatModel qwen() {
return DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(System.getenv("aliQwen-api")).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(QWEN_MODEL).build())
.build();
}
@Bean("deepseekChatClient")
public ChatClient deepSeekChatClient(@Qualifier("deepseek") ChatModel deepSeekChatModel) {
return ChatClient.builder(deepSeekChatModel)
.defaultOptions(ChatOptions.builder().model(DEEPSEEK_MODEL).build())
.build();
}
@Bean("qwenChatClient")
public ChatClient qwenChatClient(@Qualifier("qwen") ChatModel deepSeekChatModel) {
return ChatClient.builder(deepSeekChatModel)
.defaultOptions(ChatOptions.builder().model(QWEN_MODEL).build())
.build();
}
}
8) 修改 Controller 类 ChatClient 实现
@RestController
public class ChatModelStreamOutputController {
@Resource(name = "deepseek")
private ChatModel deepseekChatModel;
@Resource(name = "qwen")
private ChatModel qwenChatModel;
@Resource(name = "deepseekChatClient")
private ChatClient deepseekChatClient;
@Resource(name = "qwenChatClient")
private ChatClient qwenChatClient;
@GetMapping("/stream-v1")
public Flux<String> streamDeepSeek(@RequestParam(value = "msg", defaultValue = "你是谁") String msg) {
return deepseekChatModel.stream(msg);
}
@GetMapping("/stream-v2")
public Flux<String> streamQWen(@RequestParam(value = "msg", defaultValue = "你是谁") String msg) {
return qwenChatModel.stream(msg);
}
@GetMapping("/stream-v3")
public Flux<String> streamDeepSeekChatClient(@RequestParam(value = "msg", defaultValue = "你是谁") String msg) {
return deepseekChatClient.prompt(msg).stream().content();
}
@GetMapping("/stream-v4")
public Flux<String> streamQWenChatClient(@RequestParam(value = "msg", defaultValue = "你是谁") String msg) {
return qwenChatClient.prompt(msg).stream().content();
}
}
9) ChatClient 测试
4. 结合前端代码
1) 创建 index.html 页面
<!DOCTYPE html>
<html>
<head>
<title>SSE 流式 chat</title>
<style>
body { font-family: Arial, sans-serif; background-color: #f4f4f4; margin: 0; padding: 20px; }
#messageInput { width: 90%; padding: 10px; font-size: 16px; border: 1px solid #ccc; border-radius: 4px; margin-bottom: 10px; }
button { padding: 10px 20px; font-size: 16px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; }
button:hover { background-color: #0056b3; }
#messages { margin-top: 20px; padding: 15px; background-color: #f9f9f9; border: 1px solid #ddd; border-radius: 8px; max-height: 300px; overflow-y: auto; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); }
#messages div { padding: 8px 0; border-bottom: 1px solid #eee; font-size: 14px; color: #333; }
#messages div:last-child { border-bottom: none; }
</style>
</head>
<body>
<textarea rows="4" cols="50" placeholder="请输入你的问题..."></textarea><br>
<button onclick="sendMsg()">发送提问</button>
<div></div>
<script>
function sendMsg() {
const message = document.getElementById('messageInput').value;
if (message == "") return false;
const messagesDiv = document.getElementById('messages');
messagesDiv.innerHTML = "";
const eventSource = new EventSource('stream-v1?msg=' + message);
eventSource.onmessage = function (event) {
const data = event.data;
messagesDiv.innerHTML += event.data;
};
eventSource.onerror = function (error) {
console.error('EventSource 发生错误:', error);
eventSource.close();
};
}
</script>
</body>
</html>
2) 测试
8. 提示词 Prompt
1. 介绍
2. 四大角色
- system:设定 AI 行为边界/角色/定位。指导 AI 的行为和响应方式,设置 AI 如何解释和回复输入的
- user:用户原始提问输入。代表用户的输入他们向 AI 提出的问题、命令或陈述。
- assistant:AI 返回的响应信息,定义为'助手角色'消息。用它可以确保上下文能够连贯的交互。
- tool:桥接外部服务,可以进行函数调用如,支付/数据查询等操作,类似调用第 3 方 util 工具类,后面章节详细介绍
3. 业务测试
@RestController
public class PromptController {
@Resource(name = "qwenChatClient")
private ChatClient chatClient;
@Resource(name = "qwen")
private ChatModel chatModel;
@GetMapping("/chat-v1")
public Flux<String> v1(@RequestParam String msg) {
return chatClient.prompt()
.system("你是一个法律助手,只回答法律问题,其它问题回复,我只能回答法律相关问题,其它无可奉告")
.user(msg)
.stream()
.content();
}
@GetMapping("/chat-v2")
public Flux<String> v2(@RequestParam String msg) {
SystemMessage systemMessage = new SystemMessage("你是一个法律助手,只回答法律问题,其它问题回复,我只能回答法律相关问题,其它无可奉告");
UserMessage userMessage = new UserMessage(msg);
Prompt prompt = new Prompt(systemMessage, userMessage);
return chatModel.stream(prompt).map(response -> response.getResult().getOutput().getText());
}
}
AI 返回的响应信息,定义为'助手角色'消息。用它可以确保上下文能够连贯的交互。
@GetMapping("/chat-v3")
public String v3(@RequestParam String msg) {
AssistantMessage assistantMessage = chatClient.prompt()
.user(msg)
.call()
.chatResponse()
.getResult()
.getOutput();
return assistantMessage.getText();
}
桥接外部服务,可以进行函数调用如,支付/数据查询等操作,类似调用第 3 方 util 工具类
@GetMapping("/chat-v4")
public String v4(@RequestParam String msg) {
String text = chatClient.prompt()
.user(msg + "未来 3 天天气情况如何?")
.call()
.chatResponse()
.getResult()
.getOutput()
.getText();
ToolResponseMessage toolResponseMessage = new ToolResponseMessage(List.of(new ToolResponseMessage.ToolResponse("1", "获取天气", msg)));
String messageText = toolResponseMessage.getText();
return text + messageText;
}
9. 提示词模板
1. 介绍
Spring AI 中用于提示模板的关键组件是 PromptTemplate 类。该类使用 Terence Parr 开发的 OSS StringTemplate 引擎来构建和管理提示。PromptTemplate 类旨在促进结构化提示的创建,然后将其发送到 AI 模型进行处理
引入占位符 (如{占位符变量名}) 以动态插入内容。
2. 代码实现
1) 基本使用
@RestController
public class PromptTemplateController {
@Resource(name = "qwenChatClient")
private ChatClient chatClient;
@GetMapping("/chatTemplate")
public Flux<String> chatTemplate(String topic, String outputFormat, String wordCount) {
PromptTemplate promptTemplate = new PromptTemplate("""
将一个关于{topic}的故事
并以{output_format}格式输出
字数在{wordCount}左右
""");
Prompt prompt = promptTemplate.create(Map.of("topic", topic, "output_format", outputFormat, "wordCount", wordCount));
return chatClient.prompt(prompt).stream().content();
}
}
2) 读取模板文件
@Value("classpath:prompttemplate/test.txt")
private org.springframework.core.io.Resource template;
@GetMapping("/chatTemplate-2")
public Flux<String> chatTemplate2(String topic, String outputFormat) {
PromptTemplate promptTemplate = new PromptTemplate(template);
Prompt prompt = promptTemplate.create(Map.of("topic", topic, "output_format", outputFormat));
return chatClient.prompt(prompt).stream().content();
}
3) 多角色设定
@GetMapping("/chatTemplate-3")
public Flux<String> chatTemplate3(String systemTopic, String userTopic) {
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate("你是{systemTopic}助手,只回答{systemTopic}其他无可奉告。以 html 的格式输出");
Message systemMessage = systemPromptTemplate.createMessage(Map.of("systemTopic", systemTopic));
PromptTemplate promptTemplate = new PromptTemplate("解释一下{userTopic}");
Message userMessage = promptTemplate.createMessage(Map.of("userTopic", userTopic));
Prompt prompt = new Prompt(systemMessage, userMessage);
return chatClient.prompt(prompt).stream().content();
}
4) 人物设定
@GetMapping("/chatTemplate-4")
public Flux<String> chatTemplate4(String msg) {
SystemMessage systemMessage = new SystemMessage("你是法律助手,拒绝回答其他问题");
UserMessage userMessage = new UserMessage(msg);
Prompt prompt = new Prompt(systemMessage, userMessage);
return chatModel.stream(prompt).map(res -> res.getResult().getOutput().getText());
}
@GetMapping("/chatTemplate-5")
public Flux<String> chatTemplate5(String msg) {
return chatClient.prompt()
.system("你是法律助手,拒绝回答其他问题")
.user(msg)
.stream()
.content();
}
10. 格式化输出
1. 介绍
LLM 生成结构化输出的能力对于依赖可靠解析输出值的下游应用程序非常重要。开发人员希望快速将 AI 模型的结果转换为可以传递给其他应用程序函数和方法的数据类型,例如 JSON、XML 或 Java 类。Spring AI 结构化输出转换器有助于将 LLM 输出转换为结构化格式。
2. 代码实现
1) 创建实体类
public record StudentRecord(String id,String name,String major,String email ) { }
2) 编写 Controller
@RestController
public class StudentController {
@Resource(name = "qwenChatClient")
private ChatClient chatClient;
@GetMapping("/getStudent")
public StudentRecord chat(String name, String email) {
return chatClient.prompt()
.user(promptUserSpec -> promptUserSpec.text("我是{name},学号 10001,学习的专业是室内设计,我的邮箱是{email}")
.param("name", name)
.param("email", email))
.call()
.entity(StudentRecord.class);
}
}
3) 测试
11. 连续对话保存和持久化
1. 介绍
对话记忆(Dialogue Memory/Conversation Memory),是对话系统(含大模型助手、客服机器人等)中存储、管理、复用对话上下文与用户历史交互信息的机制,核心目标是让系统能 '记住' 当前会话及跨会话的关键信息,从而理解上下文语义、维持对话连贯性、提供个性化响应。
2. 代码实现
1) 创建工程
2) 修改 pom 文件
3) 添加配置文件
server.port=8008
server.servlet.encoding.charset=utf-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
spring.application.name=SAA-08Persistent
spring.ai.dashscope.api-key=${aliQwen-api}
spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.database=10
spring.data.redis.password=12345
spring.data.redis.connect-timeout=3
spring.data.redis.timeout=2
4) 编写配置类
1、要实现 SpringAI 框架规定的 ChatMemoryRepository 接口,SpringAi 的默认实现是 InMemoryChatMemoryRepository
2、引入了 spring-ai-alibaba-starter-memory-redis 坐标,ChatMemoryRepository 的实现为 InMemoryChatMemoryRepository 和 RedisChatMemoryRepository
3、应为 RedisChatMemoryRepository 使用的是 jedisPool,所以需要引入 jedis 坐标
@Configuration
public class RedisMemoryConfig {
@Value("${spring.data.redis.host}")
private String host;
@Value("${spring.data.redis.port}")
private int port;
@Value("${spring.data.redis.password}")
private String password;
@Bean
public RedisChatMemoryRepository redisChatMemoryRepository(){
return RedisChatMemoryRepository.builder()
.host(host)
.port(port)
.password(password)
.timeout(10)
.build();
}
}
5) 配置 MessageWindowChatMemory
MessageWindowChatMemory维护一个最大不限的消息窗口。当消息数量超过最大值时,旧消息被删除,同时保留系统消息。默认窗口大小为 20 条消息。
@Configuration
public class SaaLLMConfig {
private final String QWEN_MODEL = "qwen-plus";
@Bean("qwen")
public ChatModel chatModel(){
return DashScopeChatModel.builder()
.dashScopeApi(DashScopeApi.builder().apiKey(System.getenv("aliQwen-api")).build())
.defaultOptions(DashScopeChatOptions.builder().withModel(QWEN_MODEL).build())
.build();
}
@Bean("qwenChatClient")
public ChatClient qwenChatClient(@Qualifier("qwen") ChatModel qwenChatModel, RedisChatMemoryRepository redisChatMemoryRepository) {
MessageWindowChatMemory messageWindowChatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(redisChatMemoryRepository)
.maxMessages(10)
.build();
return ChatClient.builder(qwenChatModel)
.defaultOptions(ChatOptions.builder().model(QWEN_MODEL).build())
.defaultAdvisors(MessageChatMemoryAdvisor.builder(messageWindowChatMemory).build())
.build();
}
}
6) Controller 类
@RestController
public class ChatMemoryController {
@Resource(name = "qwenChatClient")
private ChatClient chatClient;
@GetMapping
public String chat(String msg, String userId) {
return chatClient.prompt(msg)
.advisors(advisorSpec -> advisorSpec.param(CONVERSATION_ID, userId))
.call()
.content();
}
}
7) 测试
12. 文生图
@RestController
public class Text2ImageController {
@Resource
private ImageModel imageModel;
@GetMapping("image")
public String image(String msg){
return imageModel.call(
new ImagePrompt(msg, DashScopeImageOptions.builder()
.withWidth(1920)
.withHeight(1080)
.withModel("wan2.5-t2i-preview")
.build()))
.getResult()
.getOutput()
.getUrl();
}
}
13. 文生音
@RestController
public class Text2VoiceController {
@Resource
private SpeechSynthesisModel speechSynthesisModel;
@GetMapping("/voice")
public String voice() {
String path = "d:/" + UUID.randomUUID() + ".mp3";
DashScopeSpeechSynthesisOptions dashScopeSpeechSynthesisOptions = DashScopeSpeechSynthesisOptions.builder()
.voice("longxiaoxia_v2")
.model("cosyvoice-v2")
.build();
String msg = "支付宝到账,100 元";
SpeechSynthesisResponse response = speechSynthesisModel.call(new SpeechSynthesisPrompt(msg, dashScopeSpeechSynthesisOptions));
ByteBuffer buffer = response.getResult().getOutput().getAudio();
try(FileOutputStream fileOutputStream = new FileOutputStream(path)) {
fileOutputStream.write(buffer.array());
fileOutputStream.flush();
} catch (Exception e){ }
return path;
}
}
14. 向量数据库
1. 介绍
1) 向量
向量是将非结构化数据(文本、图像、音频等)转化为机器可理解的数字形式的核心载体,也被称为特征向量或嵌入向量(Embedding)。它不再局限于数学 / 物理中的'大小 + 方向'几何定义,而是延伸为高维空间中的一个坐标点,通过向量间的数学关系(距离、相似度)来量化数据的语义、特征或行为关联。
2) 嵌入模型
嵌入 (Embedding) 的工作原理是将文本、图像和视频转换为称为向量(Vectors)的浮点数数组。这些向量旨在捕捉文本、图像和视频的含义。嵌入数组的长度称为向量的维度(Dimensionality)。
嵌入模型(EmbeddingModel)是嵌入过程中采用的模型。当前 EmbeddingModel 的接口主要用于将文本转换为数值向量,接口的设计主要围绕这两个目标展开:
- 可移植性:该接口确保在各种嵌入模型之间的轻松适配。它允许开发者在不同的嵌入技术或模型之间切换,所需的代码更改最小化。这一设计与 Spring 模块化和互换性的理念一致。
- 简单性:嵌入模型简化了文本转换为嵌入的过程。通过提供如
embed(String text) 和 embed(Document document) 这样简单的方法,它去除了处理原始文本数据和嵌入算法的复杂性。这个设计选择使开发者,尤其是那些初次接触 AI 的开发者,更容易在他们的应用程序中使用嵌入,而无需深入了解其底层机制。
嵌入模型是一类专门将非结构化数据(文本、图像、音频等) 映射到低维稠密向量空间的机器学习模型,其输出的向量(称为嵌入向量 / Embedding)能够保留原始数据的语义关联、特征属性或相似性关系。它是 AI 领域实现'数据数字化理解'的核心工具,也是连接原始数据与向量数据库、大模型等下游应用的桥梁。
简单来说:嵌入模型的作用,就是给每一份数据生成唯一的'语义身份证'(向量)。
3) 向量数据库
向量存储(VectorStore)是一种用于存储和检索高维向量数据的数据库或存储解决方案,它特别适用于处理那些经过嵌入模型转化后的数据。在 VectorStore 中,查询与传统关系数据库不同。它们执行相似性搜索,而不是精确匹配。当给定一个向量作为查询时,VectorStore 返回与查询向量'相似'的向量。
其核心功能是通过高效的索引结构和相似性计算算法,支持大规模向量数据的快速查询与分析,向量数据库维度越高,查询精准度也越高,查询效果也越好。
总结:就是将文本、图像和视频转换为向量(Vectors)的浮点数数组在 VectorStore 中,查询与传统关系数据库不同。它们执行相似性搜索,而不是精确匹配。当给定一个向量作为查询时,VectorStore 返回与查询向量'相似'的向量
2. redisStack 向量数据库
1) 介绍
redisStack 是 redis 的增强版,不是 redis 的替代品,而是在原生的 redis 基础上的功能扩展,专为构建现代实时应用而设计。
2) RedisStack 核心组件
提供全文搜索能力,支持复杂的文本搜索、聚合和过滤,以及向量数据的存储和检索
原生支持 JSON 数据的存储、索引 I 和查询,可高效存储和操作嵌套的 JSON 文档。
支持图数据模型,使用 Cypher 查询语言进行图遍历查询。
支持 Bloom、Cuckoo、Count-Min Sketch 等概率数据结构。
RedisStack = 原生 Redis + 搜索 + 图 + 时间序列 + JSON + 概率结构 + 可视化工具 + 开发框架支持
3) 安装
docker 安装:
docker run -d --name redis-stack-server -p 6379:6379 redis/redis-stack-server
4) 常用指令
JSON.MGET - 批量获取多个键的 JSON 值
JSON.NUMINCRBY - 对 JSON 中的数字进行增量操作
JSON.STRAPPEND - 追加字符串到 JSON 字符串
JSON.STRLEN - 获取 JOSN 字符串的长度
3. 代码实现
1) 创建模块
2) 修改 pom 文件
3) 添加配置文件
本次使用阿里云百炼平台向量大模型 text-embedding-v3
server.port=8006
server.servlet.encoding.charset=utf-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
spring.application.name=SAA-06Vector
spring.ai.dashscope.api-key=${aliQwen-api}
spring.ai.dashscope.chat.options.model=qwen-plus
spring.ai.dashscope.embedding.options.model=text-embedding-v3
spring.data.redis.host=192.168.10.40
spring.data.redis.port=6379
spring.data.redis.username=default
spring.data.redis.password=
spring.ai.vectorstore.redis.initialize-schema=true
spring.ai.vectorstore.redis.index-name=custom-index
spring.ai.vectorstore.redis.prefix=custom-prefix
4) 编写 Controller
@RestController
public class VectorController {
@Resource
private EmbeddingModel embeddingModel;
@Resource
private VectorStore vectorStore;
@GetMapping("/text2embed")
public EmbeddingResponse text2embed(String msg) {
EmbeddingResponse embeddingResponse = embeddingModel.call(new EmbeddingRequest(List.of(msg), DashScopeEmbeddingOptions.builder().withModel("text-embedding-v3").build()));
return embeddingResponse;
}
@GetMapping("/add")
public void add() {
List<Document> documents = List.of(
new Document("i study java"),
new Document("i study javaScript"),
new Document("i study python"),
new Document("i study jquery")
);
vectorStore.add(documents);
}
@GetMapping("get")
public List getAll(String msg) {
SearchRequest searchRequest = SearchRequest.builder()
.query(msg)
.topK(2)
.build();
List<Document> documents = vectorStore.similaritySearch(searchRequest);
return documents;
}
}
5) 测试
15. RAG
1. 介绍
1) LLM 的缺陷
- LLM 的知识不是实时的,不具备知识更新.
- LLM 可能不知道你私有的领域/业务知识.
- LLM 有时会在回答中生成看似合理但实际上是错误的信息
2) 是什么
RAG 的全称是 Retrieval-Augmented Generation,中文译为检索增强生成,是一种提升大语言模型(LLM)回答准确性、时效性和可信度的核心技术方案。它的核心逻辑是:让大模型在生成回答前,先从外部知识库中检索相关的权威信息,再结合检索到的内容生成回答,以此解决大模型'幻觉'、知识过时、领域知识不足等痛点。
简单来说,RAG 相当于给大模型加装了一个可随时更新的'外挂大脑' —— 大模型不再只依赖训练时的固有知识,而是可以调取最新、最专业的外部数据来辅助回答。
通过引入外部知识源来增强 LLM 的输出能力,传统的 LLM 通常基于其训练数据生成响应,但这些数据可能过时或不够全面。RAG 允许模型在生成答案之前,从特定的知识库中检索相关信息,从而提供更准确和上下文相关的回答
2. 代码实现
1) 知识库
提供 ErrorCode 脚本让存入向量数据库 RedisStack,形成文档知识库
2) 添加配置类
@Configuration
public class SaaLLMConfig {
@Bean
public ChatModel chatModel() {
return DashScopeChatModel.builder()
.dashScopeApi(
DashScopeApi.builder().apiKey(System.getenv("aliQwen-api")).build())
.defaultOptions(
DashScopeChatOptions.builder().withModel("qwen-plus").build())
.build();
}
@Bean
public ChatClient chatClient(ChatModel chatModel) {
return ChatClient.builder(chatModel)
.defaultOptions(ChatOptions.builder().model("qwen-plus").build())
.build();
}
}
@Configuration
public class InitVectorDatabaseConfig {
@Autowired
private VectorStore vectorStore;
@Value("classpath:ops.txt")
private Resource resource;
@PostConstruct
public void init() {
TextReader textReader = new TextReader(resource);
textReader.setCharset(Charset.defaultCharset());
List<Document> documentList = new TokenTextSplitter().transform(textReader.read());
vectorStore.add(documentList);
}
}
3) 编写 Controller
@RestController
public class RagController {
@Resource
private ChatClient client;
@Resource
private VectorStore vectorStore;
@GetMapping("/rag")
public Flux<String> rag(String msg) {
String systemInfo = "你是一个运维工程师,按照给出的编码给出对应故障解释,否则回复找不到信息。";
return client.prompt()
.system(systemInfo)
.user(msg)
.stream()
.content();
}
@GetMapping("/rag-v1")
public Flux<String> ragv1(String msg) {
String systemInfo = "你是一个运维工程师,按照给出的编码给出对应故障解释,否则回复找不到信息。";
RetrievalAugmentationAdvisor augmentationAdvisor = RetrievalAugmentationAdvisor.builder()
.documentRetriever(
VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.build())
.build();
return client.prompt()
.system(systemInfo)
.user(msg)
.advisors(augmentationAdvisor)
.stream()
.content();
}
}
4) 测试
3. 向量数据库去重
1) 修改配置类
上述配置每次重启项目都会向数据库存入一份数据,如何去重:
@Configuration
public class InitVectorDatabaseConfig {
@Autowired
private VectorStore vectorStore;
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Value("classpath:ops.txt")
private Resource resource;
@PostConstruct
public void init() {
TextReader textReader = new TextReader(resource);
textReader.setCharset(Charset.defaultCharset());
List<Document> documentList = new TokenTextSplitter().transform(textReader.read());
String source = (String) textReader.getCustomMetadata().get("source");
String textHash = SecureUtil.md5(source);
String key = "vector-hk:" + textHash;
Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(key, "1");
if (ifAbsent) {
vectorStore.add(documentList);
} else {
System.out.println("------向量初始化数据已经加载过,请不要重复操作");
}
}
}
2) 测试
16. ToolCalling 工具调用
例如在 LLM 询问当前时间,此时他无法给我们回复,他无法获取最新的数据。
1. 介绍
ToolCalling(也称为 FunctionCalling) 它允许大模型与一组 API 或工具进行交互,将 LLM 的智能与外部工具或 API 无缝连接,从而增强大模型其功能。
2. 代码结合
@RestController
public class ToolCallingController {
@Autowired
private ChatClient client;
@GetMapping("getTime")
public Flux<String> getTime(@RequestParam(value = "msg", defaultValue = "当前时间几点") String msg){
return client.prompt(msg).stream().content();
}
}
1) 添加工具类
public class DateTool {
@Tool(description = "获取当前时间", returnDirect = false)
public String getTime() {
return LocalDateTime.now().toString();
}
}
2) 编写 Controller - ChatModel 实现
@RestController
public class ToolChatModelController {
@Resource
private ChatModel chatModel;
@GetMapping("getTime_v1")
public String getTime(@RequestParam(value = "msg", defaultValue = "当前时间几点") String msg){
ToolCallback[] callbacks = ToolCallbacks.from(new DateTool());
ChatOptions options = ToolCallingChatOptions.builder().toolCallbacks(callbacks).build();
Prompt prompt = new Prompt(msg, options);
return chatModel.call(prompt).getResult().getOutput().getText();
}
}
3) 编写 Controller - ChatClient 实现
@RestController
public class ToolChatClientController {
@Resource(name = "qwen")
private ChatClient chatClient;
@GetMapping("getTime_v2")
public String getTime(@RequestParam(value = "msg", defaultValue = "当前时间几点") String msg){
return chatClient.prompt(msg)
.tools(new DateTool())
.call()
.content();
}
}
17. 模型上下文协议 MCP
1. 概述
之前我们需要为每个 API / 工具编写定制化适配代码,上下文传递混乱,无统一规范
MCP(Model Context Protocol,模型上下文协议) 是由 Anthropic 公司于 2024 年 11 月推出的开放标准协议,被誉为 AI 领域的'USB-C 接口',核心目标是标准化大型语言模型(LLM)与外部工具、数据源和服务的双向通信机制,解决传统 AI 集成中'工具碎片化、适配成本高、安全可控难'的三大痛点。
2. 核心架构
MCP 主机(MCP Hosts):发起请求的 AI 应用程序,比如聊天机器人、AI 驱动的 IDE 等。
MCP 客户端(MCP Clients):在主机程序内部,与 MCP 服务器保持 1:1 的连接。
MCP 服务器(MCP Servers):为 MCP 客户端提供上下文、工具和提示信息。
本地资源(Local Resources):本地计算机中可供 MCP 服务器安全访问的资源,如文件、数据库。
远程资源(Remote Resources):MCP 服务器可以连接到的远程资源,如通过 API 提供的数据
3. 对比
ToolCalling:工具类,为了让大模型使用 Util 工具
之前每个大模型 (如 DeepSeek、ChatGPT) 需要为每个工具单独开发接口 (FunctionCalling),导致重复劳动,而现在开发者只需写一次 MCP 服务端,所有兼容 MCP 协议的模型都能调用,MCP 让大模型从'被动应答'变为'主动调用工具';调用一个 MCP 服务器就等价调用一个带有多个功能的 Utils 工具类。
4. 本地 MCP 开发
1) MCP 服务端
1、新建模块
2、修改 pom 文件
3、添加配置文件
server.port=8007
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
spring.application.name=SAA-07McpServer
spring.ai.mcp.server.type=async
spring.ai.mcp.server.name=customer-define-mcp-server
spring.ai.mcp.server.version=1.0.0
5、编写服务类
@Service
public class WeatherService {
@Tool(description = "根据城市名称获取天气预报")
public String getWeatherByCity(String city) {
Map<String, String> map = Map.of(
"北京", "11111 降雨频繁,其中今天和后天雨势较强,部分地区有暴雨并伴强对流天气,需注意",
"上海", "22222 多云,15℃~27℃,南风 3 级,当前温度 27℃。",
"深圳", "333333 多云 40 天,阴 16 天,雨 30 天,晴 3 天"
);
return map.getOrDefault(city, "抱歉:未查询到对应城市!");
}
}
6、接口配置类
@Configuration
public class McpServerConfig {
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder()
.toolObjects(weatherService)
.build();
}
}
7、启动
2) MCP 客户端
1、新建模块
2、修改 pom 文件
3、添加配置文件
server.port=8008
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
spring.application.name=SAA-15LocalMcpClient
spring.ai.dashscope.api-key=${aliQwen-api}
spring.ai.mcp.client.type=async
spring.ai.mcp.client.request-timeout=60s
spring.ai.mcp.client.toolcallback.enabled=true
spring.ai.mcp.client.sse.connections.mcp-server1.url=http://localhost:8007
4、编写配置类
@Configuration
public class SaaLLMConfig {
@Bean
public ChatClient chatClient(ChatModel chatModel, ToolCallbackProvider provider){
return ChatClient.builder(chatModel)
.defaultToolCallbacks(provider.getToolCallbacks())
.build();
}
}
5、编写 Controller
@RestController
public class McpClientController {
@Resource
private ChatClient chatClient;
@Resource
private ChatModel chatModel;
@GetMapping("/v1")
public Flux<String> chat(@RequestParam(name = "msg", defaultValue = "上海") String msg) {
System.out.println("使用了 mcp....");
return chatClient.prompt(msg).stream().content();
}
@GetMapping("/v2")
public Flux<String> chat2(@RequestParam(name = "msg", defaultValue = "上海") String msg) {
System.out.println("没有使用了 mcp....");
return chatModel.stream(msg);
}
}
6、测试
5. 远程 MCP 增强
1) 高德地图 mcp
2) 获取 apiKey
3) 客户端调用
1、新建模块
2、修改 pom 文件
3、添加配置文件
server.port=8009
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
spring.application.name=springAI-09amapmcp
spring.ai.openai.api-key=${aliQwen-api}
spring.ai.openai.base-url=https://dashscope.aliyuncs.com/compatible-mode
spring.ai.openai.chat.options.model=qwen-plus
spring.ai.mcp.client.toolcallback.enabled=true
spring.ai.mcp.client.stdio.servers-configuration=classpath:/mcp-server.json
4、Typescript 接入
{
"mcpServers": {
"amap-maps": {
"command": "cmd",
"args": [
"/c",
"npx",
"-y",
"@amap/amap-maps-mcp-server"
],
"env": {
"AMAP_MAPS_API_KEY": "a0***"
}
}
}
}
// 构建 McpTransport 协议
// cmd:启动 Windows 命令行解释器。
// /c:告诉 cmd 执行完后面的命令后关闭自身。
// npx:npx = npm execute package,Node.js 的一个工具,用于执行 npm 包中的可执行文件。
// -y 或 --yes:自动确认操作(类似于默认接受所有提示)。
// @amap/amap-maps-mcp-server:要通过 npx 执行的 npm 包名
// AMAP_MAPS_API_KEY 是访问高德地图开放平台 API 的 AK
5、新建配置类
@Configuration
public class SaaLLMConfig {
@Bean
public ChatClient chatClient(ChatModel chatModel, ToolCallbackProvider provider) {
return ChatClient.builder(chatModel)
.defaultToolCallbacks(provider.getToolCallbacks())
.build();
}
}
6、新建 Controller
@RestController
public class AMapController {
@Resource
private ChatClient client;
@Resource
private ChatModel chatModel;
@GetMapping("/v1")
public Flux<String> chatV1(String msg) {
System.out.println("使用了 mcp....");
return client.prompt(msg)
.stream().content();
}
@GetMapping("/v2")
public Flux<String> chatV2(String msg) {
System.out.println("没有使用了 mcp....");
return chatModel.stream(msg);
}
}
7、测试
18. 云上 RAG 知识库
1. 阿里云上知识库搭建
2. 代码结合
1) 创建模块
2) 修改 pom 文件
3) 添加配置文件
server.port=8010
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
spring.application.name=SAA-10YunRAG
spring.ai.dashscope.api-key=${aliQwen-api}
4) 编写配置类
@Configuration
public class DashScopeConfig {
@Bean
public DashScopeApi dashScopeApi(){
return DashScopeApi.builder()
.apiKey(System.getenv("aliQwen-api"))
.workSpaceId("llm-***")
.build();
}
@Bean
public ChatClient client(ChatModel chatModel) {
return ChatClient.builder(chatModel).build();
}
}
5) 编写 Controller
@RestController
public class RagController {
@Resource
private DashScopeApi dashScopeApi;
@Resource
private ChatClient client;
@GetMapping("/chat")
public Flux<String> chat(String msg){
DashScopeDocumentRetrieverOptions options = DashScopeDocumentRetrieverOptions.builder()
.withIndexName("测试 - 学习 -01")
.build();
DashScopeDocumentRetriever retriever = new DashScopeDocumentRetriever(dashScopeApi, options);
return client.prompt()
.user(msg)
.advisors(new DocumentRetrievalAdvisor(retriever))
.stream()
.content();
}
}
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online