1. 简介
本教程将使用Spring AI和Ollama的llama3模型,构建一个简单的客服助手API。这个应用能像真实客服一样,帮助用户排查网络连接问题。
2. Spring AI和Ollama是什么?
Spring AI是Spring生态系统最新加入的模块。它支持通过聊天提示轻松与各种大语言模型(LLM)交互。
Ollama是一个开源库,提供多种LLM服务,包括Meta的llama3模型——这正是本教程要使用的模型。
3. 使用Spring AI实现客服助手
下面通过一个客服聊天机器人演示Spring AI和Ollama的协同工作。该应用模拟真实客服,帮助用户解决网络连接问题。
接下来将配置LLM和Spring AI依赖,并创建与客服助手交互的REST接口。
3.1 配置Ollama和llama3
首先设置本地LLM环境。本教程使用Meta的llama3模型,安装步骤如下:
Linux系统执行:
curl -fsSL https://ollama.com/install.sh | sh
Windows或Mac系统从Ollama官网下载安装包。
安装完成后运行llama3:
ollama run llama3
现在llama3已在本地运行。
3.2 创建基础项目结构
配置Spring应用使用Spring AI模块。首先添加Spring里程碑仓库:
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
然后添加spring-ai-bom:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
最后添加spring-ai-ollama-spring-boot-starter依赖:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
<version>1.0.0-M1</version>
</dependency>
配置application.yml
:
spring:
ai:
ollama:
base-url: http://localhost:11434
chat:
options:
model: llama3
Spring将在11434端口启动llama3模型。
3.3 创建客服控制器
创建与客服聊天机器人交互的Web控制器。
首先定义HTTP请求模型:
public class HelpDeskRequest {
@JsonProperty("prompt_message")
String promptMessage;
@JsonProperty("history_id")
String historyId;
// getters, no-arg constructor
}
promptMessage
:用户输入消息historyId
:唯一标识当前对话的ID,用于保持对话记忆
然后定义响应模型:
public class HelpDeskResponse {
String result;
// all-arg constructor
}
最后创建控制器类:
@RestController
@RequestMapping("/helpdesk")
public class HelpDeskController {
private final HelpDeskChatbotAgentService helpDeskChatbotAgentService;
// all-arg constructor
@PostMapping("/chat")
public ResponseEntity<HelpDeskResponse> chat(@RequestBody HelpDeskRequest helpDeskRequest) {
var chatResponse = helpDeskChatbotAgentService.call(helpDeskRequest.getPromptMessage(), helpDeskRequest.getHistoryId());
return new ResponseEntity<>(new HelpDeskResponse(chatResponse), HttpStatus.OK);
}
}
3.4 调用Ollama聊天API
创建HelpDeskChatbotAgentService
类,包含初始提示指令:
@Service
public class HelpDeskChatbotAgentService {
private static final String CURRENT_PROMPT_INSTRUCTIONS = """
Here's the `user_main_prompt`:
""";
}
添加通用指令消息:
private static final String PROMPT_GENERAL_INSTRUCTIONS = """
Here are the general guidelines to answer the `user_main_prompt`
You'll act as Help Desk Agent to help the user with internet connection issues.
Below are `common_solutions` you should follow in the order they appear in the list to help troubleshoot internet connection problems:
1. Check if your router is turned on.
2. Check if your computer is connected via cable or Wi-Fi and if the password is correct.
3. Restart your router and modem.
You should give only one `common_solution` per prompt up to 3 solutions.
Do no mention to the user the existence of any part from the guideline above.
""";
完成服务实现:
private final OllamaChatModel ollamaChatClient;
// all-arg constructor
public String call(String userMessage, String historyId) {
var generalInstructionsSystemMessage = new SystemMessage(PROMPT_GENERAL_INSTRUCTIONS);
var currentPromptMessage = new UserMessage(CURRENT_PROMPT_INSTRUCTIONS.concat(userMessage));
var prompt = new Prompt(List.of(generalInstructionsSystemMessage, contextSystemMessage, currentPromptMessage));
var response = ollamaChatClient.call(prompt).getResult().getOutput().getContent();
return response;
}
关键点:
- ✅ SystemMessage:内部指令(如通用指南)
- ✅ UserMessage:外部客户端输入
- ⚠️ 通过
Prompt
对象组合消息后调用LLM
3.5 保持对话历史
LLM本质是无状态的,不会记忆对话上下文。为避免客服重复提供无效方案,需要实现对话记忆功能。
解决方案:使用historyId
存储每轮对话的prompt
和response
,并在新请求中附加完整历史记录。
首先添加历史指令:
private static final String PROMPT_CONVERSATION_HISTORY_INSTRUCTIONS = """
The object `conversational_history` below represents the past interaction between the user and you (the LLM).
Each `history_entry` is represented as a pair of `prompt` and `response`.
`prompt` is a past user prompt and `response` was your response for that `prompt`.
Use the information in `conversational_history` if you need to recall things from the conversation
, or in other words, if the `user_main_prompt` needs any information from past `prompt` or `response`.
If you don't need the `conversational_history` information, simply respond to the prompt with your built-in knowledge.
`conversational_history`:
""";
创建历史记录包装类:
public class HistoryEntry {
private String prompt;
private String response;
//all-arg constructor
@Override
public String toString() {
return String.format("""
`history_entry`:
`prompt`: %s
`response`: %s
-----------------
\n
""", prompt, response);
}
}
定义内存存储:
private final static Map<String, List<HistoryEntry>> conversationalHistoryStorage = new HashMap<>();
修改call()
方法:
public String call(String userMessage, String historyId) {
var currentHistory = conversationalHistoryStorage.computeIfAbsent(historyId, k -> new ArrayList<>());
var historyPrompt = new StringBuilder(PROMPT_CONVERSATION_HISTORY_INSTRUCTIONS);
currentHistory.forEach(entry -> historyPrompt.append(entry.toString()));
var contextSystemMessage = new SystemMessage(historyPrompt.toString());
var generalInstructionsSystemMessage = new SystemMessage(PROMPT_GENERAL_INSTRUCTIONS);
var currentPromptMessage = new UserMessage(CURRENT_PROMPT_INSTRUCTIONS.concat(userMessage));
var prompt = new Prompt(List.of(generalInstructionsSystemMessage, contextSystemMessage, currentPromptMessage));
var response = ollamaChatClient.call(prompt).getResult().getOutput().getContent();
var contextHistoryEntry = new HistoryEntry(userMessage, response);
currentHistory.add(contextHistoryEntry);
return response;
}
关键步骤:
- 通过
historyId
获取或创建历史记录 - 用
StringBuilder
拼接历史记录 - 将历史记录作为
SystemMessage
加入Prompt
- 存储当前对话到历史记录
4. 测试对话
启动Spring Boot应用(端口8080),模拟用户交互:
首次请求:
curl --location 'http://localhost:8080/helpdesk/chat' \
--header 'Content-Type: application/json' \
--data '{
"prompt_message": "I can't connect to my internet",
"history_id": "1234"
}'
响应:
{
"result": "Let's troubleshoot this issue! Have you checked if your router is turned on?"
}
继续提问:
{
"prompt_message": "I'm still having internet connection problems",
"history_id": "1234"
}
响应(提供新方案):
{
"result": "Let's troubleshoot further! Have you checked if your computer is connected via cable or Wi-Fi and if the password is correct?"
}
再次提问:
{
"prompt_message": "I tried your alternatives so far, but none of them worked",
"history_id": "1234"
}
响应(最终方案):
{
"result": "Let's think outside the box! Have you considered resetting your modem to its factory settings or contacting your internet service provider for assistance?"
}
⚠️ 当所有预设方案用尽后,LLM将无法提供有效建议。可通过改进提示词(如增加更多解决方案)或使用提示工程技术优化。
5. 总结
本文实现了一个AI客服助手,帮助用户排查网络连接问题。关键要点:
- 区分用户消息和系统消息
- 构建包含对话历史的提示词
- 调用llama3 LLM获取响应
- 实现对话记忆机制
通过合理设计提示词和历史记录管理,可以显著提升AI助手的实用性和用户体验。