Spring with AI (3): 定制对话——Prompt模板引入

本文代码:https://github.com/JunTeamCom/ai-demo/tree/release-3.0

Spring with AI系列,只关注上层AI的应用程序(基于JAVA搭建),不关注底层的LLM原理、搭建等技术。

通过简单的自定义Prompt模板,即可定制一个AI,专注某一领域的知识回答。

1 创建模板

先在pom.xml引入验证Starter:

xml 复制代码
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

我们定义一个关于"世界各国地理历史知识"的AI,模板也简单明了:

实体定义:

java 复制代码
package com.junteam.ai.demo.model;

import jakarta.validation.constraints.NotBlank;

public record ChatQuestion(
        @NotBlank(message = "标题不能为空") String title,
        @NotBlank(message = "问题不能为空") String question) {
}

模板文件resources/promptTemplates/questionPromptTemplate.st定义:

复制代码
你是一个有用的助手,负责回答有关"代码编程题"的问题。
如果你对这个编程语言一无所知或不知道答案,请回答"我不知道"。

只给出实现代码。

编程语言是 {title}。

问题是:
{question}

2 实现逻辑

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);
    }
}

3 运行效果

测试用例:

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];
    }
}

4 更多补充

4.1 基于模板扩展内容

再引入RAG之前,可以用简单的模板填充、来实现一些扩展内容。

比如再加一个langRules/java.txt,内容形如:

复制代码
- java中尽量使用基本数据、而非封装类型。
- java中尽量使用静态方法实现代码。

这样可以再模板可以修改为:

复制代码
你是一个有用的助手,负责回答有关"代码编程题"的问题。
如果你对这个编程语言一无所知或不知道答案,请回答"我不知道"。
如果可能,使用规则:{rules}。

只给出实现代码。

编程语言是 {title}。

问题是:
{question}

4.2 大模型选项

4.2.1 大模型类型

在配置讲解中已经提到,不再赘述

4.2.2 大模型温度

temperature参数是生成策略中的核心参数,直接影响输出的随机性与创造性。

  • 低Temperature(0.3-0.7):适用于客服机器人等需要精准回答的场景,减少错误信息
  • 中Temperature(0.7-1.2):适合创意写作,平衡逻辑性与多样性
  • 高Temperature(1.2-2.0):用于头脑风暴工具,激发非常规创意
java 复制代码
ChatOptions chatOptions = ChatOptions.builder()
  .temperature(0.7)
  .build();

String answerText = chatClient.prompt()
  .user(question.question())
  .options(chatOptions)
  .call()
  .content();

4.2.3 其他选项

  • topP拣选答案的比例,比如.topP(0.8)从排名前80%的结果中拣选
  • topK排除答案的比例,比如.topP(0.2)排名后20%的结果排除

4.3 格式化

例如前面所提的,在Prompt中,指定输出格式为json、或者只保留java代码。

这样可以快速实现热门歌单等json接口

另外可以把输出格式就设置为流式,这样客户端或网页前端,可以使用SSE协议接收结果、逐个Token显示。

java 复制代码
return chatClient.prompt()
  .system(systemSpec -> systemSpec
    .text(promptTemplate)
    .param("title", question.title())
    .param("rules", langRules))
    .user(question.question())
  .stream() // 流式
  .content();

4.4 响应元数据

LLM返回的内容,例如OpenAI,包含了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
}

这样可以再代码中获取和记录:

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使用量
return responseEntity.entity();
相关推荐
智算菩萨3 小时前
ChatGPT 5.4在英语学习中的应用:经典专四英语散文《Spring Thaw》赏析
人工智能·学习·ai·chatgpt·机器翻译
最初的↘那颗心4 小时前
Spring AI 生产避坑指南与 RAG 内存向量库实战
大模型·向量数据库·rag·spring ai·生产实践
视觉&物联智能4 小时前
【杂谈】-人工智能蓬勃演进背后的隐性支撑体系
人工智能·ai·aigc·算力·agi·deepseek
最初的↘那颗心5 小时前
Spring AI Alibaba 多模态全家桶:图片理解、图片生成与语音合成实战
spring boot·大模型·多模态·通义千问·spring ai
云安全联盟大中华区5 小时前
[特殊字符] | OpenClaw威胁模型:MAESTRO框架分析
大数据·人工智能·深度学习·安全·ai
CoderJia程序员甲5 小时前
GitHub 热榜项目 - 日榜(2026-03-16)
人工智能·ai·大模型·github·ai教程
qq_452396236 小时前
【Python × AI】Prompt Engineering 深度工程化:打造大模型的“确定性”控制链路
人工智能·python·ai·prompt
秃头摸鱼侠6 小时前
OpenClaw 团队级落地手册:规范、权限、安全、CI/CD 一体化实践
数据库·安全·ci/cd·ai
风静如云6 小时前
Ubuntu(25.04):安装Openclaw
ai