在Spring框架中集成AI能力已经成为现代应用开发的重要趋势。本文将详细介绍如何利用Spring AI的Prompt模板功能,构建一个专注于编程问题解答的智能助手系统。这个系统能够根据用户提供的编程语言和具体问题,生成符合规范的代码解决方案。
该系统主要实现以下核心功能:
提示:在实际开发中,Prompt模板的设计对AI输出质量有决定性影响,需要精心设计模板内容和约束条件。
首先需要在项目的pom.xml文件中添加必要的依赖:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
这个验证Starter提供了参数校验功能,确保用户输入符合要求。对于AI交互场景,输入验证尤为重要,可以防止无效或恶意请求影响系统稳定性。
我们使用Java Record定义请求和响应模型:
java复制package com.junteam.ai.demo.model;
import jakarta.validation.constraints.NotBlank;
public record ChatQuestion(
@NotBlank(message = "标题不能为空") String title,
@NotBlank(message = "问题不能为空") String question) {
}
Record类型简洁高效,特别适合这种简单的数据传输对象。@NotBlank注解确保关键字段不为空,提供了基本的输入验证。
在resources/promptTemplates目录下创建questionPromptTemplate.st文件:
st复制你是一个有用的助手,负责回答有关"代码编程题"的问题。
如果你对这个编程语言一无所知或不知道答案,请回答"我不知道"。
只给出实现代码。
编程语言是 {title}。
问题是:
{question}
这个模板定义了AI的角色、行为规范和响应格式。关键点包括:
核心服务类实现如下:
java复制package com.junteam.ai.demo.service.impl;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import com.junteam.ai.demo.model.ChatAnswer;
import com.junteam.ai.demo.model.ChatQuestion;
import com.junteam.ai.demo.service.ChatService;
@Service
public class OpenAIChatServiceImpl implements ChatService {
private final ChatClient chatClient;
public OpenAIChatServiceImpl(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
@Value("classpath:/promptTemplates/questionPromptTemplate.st")
Resource questionPromptTemplate;
@SuppressWarnings("null")
@Override
public ChatAnswer ask(ChatQuestion chatQuestion) {
var answer = chatClient.prompt()
.user(userSpec -> userSpec
.text(questionPromptTemplate)
.param("title", chatQuestion.title())
.param("question", chatQuestion.question())
)
.call();
var answerText = answer.content();
return new ChatAnswer(chatQuestion.title(), answerText);
}
}
这段代码实现了:
可以使用curl命令测试接口:
bash复制curl http://localhost:8080/web/ask \
-X POST \
-H "Content-Type: application/json" \
-d '{"title": "java", "question": "给定一个非递减排序的整数数组 nums 和一个目标值 target,请编写一个函数,返回 target 在数组中出现的第一个位置和最后一个位置(下标从 0 开始)。\n - 如果 target 未在数组中出现,返回 [-1, -1];\n - 要求:时间复杂度不超过 O(logn),空间复杂度 O(1)。\n示例\n 1. 输入:nums = [5,7,7,8,8,10], target = 8 → 输出:[3,4]\n 2. 输入:nums = [5,7,7,8,8,10], target = 6 → 输出:[-1,-1]\n 3. 输入:nums = [], target = 0 → 输出:[-1,-1]\n 4. 输入:nums = [2,2], target = 2 → 输出:[0,1]"}'
系统应返回类似以下结构的响应:
json复制{
"title":"JAVA",
"answer":"```java\nclass Solution {\n public int findMin(int[] nums) {\n ...```"
}
实际解析后的代码示例:
java复制class Solution {
public int findMin(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] > nums[right]) {
left = mid + 1;
} else {
right = mid;
}
}
return nums[left];
}
}
可以在模板中集成编程语言规范,提升代码质量。例如创建langRules/java.txt文件:
code复制- java中尽量使用基本数据、而非封装类型。
- java中尽量使用静态方法实现代码。
然后修改模板引入规则:
st复制你是一个有用的助手,负责回答有关"代码编程题"的问题。
如果你对这个编程语言一无所知或不知道答案,请回答"我不知道"。
如果可能,使用规则:{rules}。
只给出实现代码。
编程语言是 {title}。
问题是:
{question}
Temperature参数控制输出的随机性:
java复制ChatOptions chatOptions = ChatOptions.builder()
.temperature(0.7)
.build();
String answerText = chatClient.prompt()
.user(question.question())
.options(chatOptions)
.call()
.content();
不同场景推荐值:
java复制ChatOptions.builder()
.topP(0.8) // 从排名前80%的结果中选择
.topK(0.2) // 排除排名后20%的结果
.build();
支持SSE协议实现流式响应:
java复制return chatClient.prompt()
.system(systemSpec -> systemSpec
.text(promptTemplate)
.param("title", question.title())
.param("rules", langRules))
.user(question.question())
.stream() // 启用流式
.content();
在Prompt中指定输出格式为JSON:
st复制请以JSON格式返回答案,包含以下字段:
- code: 实现代码
- complexity: 时间复杂度分析
- explanation: 简要说明
可以获取AI响应的元数据信息:
java复制var responseEntity = chatClient.prompt()
.system(systemSpec -> systemSpec
.text(promptTemplate)
.param("gameTitle", question.gameTitle())
.param("rules", gameRules))
.user(question.question())
.call()
.responseEntity(Answer.class);
var response = responseEntity.response();
var metadata = response.getMetadata();
log.info(metadata.getUsage()); // 记录Token使用量
典型元数据结构:
json复制{
"token_usage": {
"completion_tokens": 164,
"prompt_tokens": 17,
"total_tokens": 181
},
"model_name": "gpt-4-turbo",
"system_fingerprint": "fp_76f018034d",
"finish_reason": "stop",
"logprobs": null
}
在实际项目中,我们发现Prompt模板需要持续迭代优化。初期可以设置较严格的约束,随着对AI行为的了解逐步调整。同时建议建立自动化测试套件,确保Prompt修改不会导致关键功能退化。