找回密码
 立即注册
首页 业界区 业界 Spring with AI (3): 定制对话——Prompt模板引入 ...

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

揭荸 2 小时前
本文代码:https://github.com/JunTeamCom/ai-demo/tree/release-3.0
Spring with AI系列,只关注上层AI的应用程序(基于JAVA搭建),不关注底层的LLM原理、搭建等技术。
通过简单的自定义Prompt模板,即可定制一个AI,专注某一领域的知识回答。
1 创建模板

先在pom.xml引入验证Starter:
  1. <dependency>
  2.         <groupId>org.springframework.boot</groupId>
  3.         spring-boot-starter-validation</artifactId>
  4. </dependency>
复制代码
我们定义一个关于“世界各国地理历史知识”的AI,模板也简单明了:
实体定义:
  1. package com.junteam.ai.demo.model;
  2. import jakarta.validation.constraints.NotBlank;
  3. public record ChatQuestion(
  4.         @NotBlank(message = "标题不能为空") String title,
  5.         @NotBlank(message = "问题不能为空") String question) {
  6. }
复制代码
模板文件resources/promptTemplates/questionPromptTemplate.st定义:
  1. 你是一个有用的助手,负责回答有关“代码编程题”的问题。
  2. 如果你对这个编程语言一无所知或不知道答案,请回答“我不知道”。
  3. 只给出实现代码。
  4. 编程语言是 {title}。
  5. 问题是:
  6. {question}
复制代码
2 实现逻辑
  1. package com.junteam.ai.demo.service.impl;
  2. import org.springframework.ai.chat.client.ChatClient;
  3. import org.springframework.beans.factory.annotation.Value;
  4. import org.springframework.core.io.Resource;
  5. import org.springframework.stereotype.Service;
  6. import com.junteam.ai.demo.model.ChatAnswer;
  7. import com.junteam.ai.demo.model.ChatQuestion;
  8. import com.junteam.ai.demo.service.ChatService;
  9. @Service
  10. public class OpenAIChatServiceImpl implements ChatService {
  11.     private final ChatClient chatClient;
  12.     public OpenAIChatServiceImpl(ChatClient.Builder chatClientBuilder) {
  13.         this.chatClient = chatClientBuilder.build();
  14.     }
  15.     @Value("classpath:/promptTemplates/questionPromptTemplate.st")
  16.     Resource questionPromptTemplate;
  17.     @SuppressWarnings("null")
  18.     @Override
  19.     public ChatAnswer ask(ChatQuestion chatQuestion) {
  20.         var answer = chatClient.prompt()
  21.                 .user(userSpec -> userSpec
  22.                     .text(questionPromptTemplate)
  23.                     .param("title", chatQuestion.title())
  24.                     .param("question", chatQuestion.question())
  25.                 )
  26.                 .call();
  27.         var answerText = answer.content();
  28.         return new ChatAnswer(chatQuestion.title(), answerText);
  29.     }
  30. }
复制代码
3 运行效果

测试用例:
  1. curl http://localhost:8080/web/ask \
  2.   -X POST \
  3.   -H "Content-Type: application/json" \
  4.   -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]"}'
复制代码
返回结果(为了排版,笔者进行了截断):
  1. {
  2.   "title":"JAVA",
  3.   "answer":"```java\nclass Solution {\n public int findMin(int[] nums) {\n ...```"
  4. }
复制代码
整理出来结果如下:
  1. class Solution {
  2.     public int findMin(int[] nums) {
  3.         int left = 0;
  4.         int right = nums.length - 1;
  5.         while (left < right) {
  6.             int mid = left + (right - left) / 2;
  7.             if (nums[mid] > nums[right]) {
  8.                 left = mid + 1;
  9.             } else {
  10.                 right = mid;
  11.             }
  12.         }
  13.         return nums[left];
  14.     }
  15. }
复制代码
4 更多补充

4.1 基于模板扩展内容

再引入RAG之前,可以用简单的模板填充、来实现一些扩展内容。
比如再加一个langRules/java.txt,内容形如:
  1. - java中尽量使用基本数据、而非封装类型。
  2. - java中尽量使用静态方法实现代码。
复制代码
这样可以再模板可以修改为:
  1. 你是一个有用的助手,负责回答有关“代码编程题”的问题。
  2. 如果你对这个编程语言一无所知或不知道答案,请回答“我不知道”。
  3. 如果可能,使用规则:{rules}。
  4. 只给出实现代码。
  5. 编程语言是 {title}。
  6. 问题是:
  7. {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):用于头脑风暴工具,激发非常规创意
  1. ChatOptions chatOptions = ChatOptions.builder()
  2.   .temperature(0.7)
  3.   .build();
  4. String answerText = chatClient.prompt()
  5.   .user(question.question())
  6.   .options(chatOptions)
  7.   .call()
  8.   .content();
复制代码
4.2.3 其他选项


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

例如前面所提的,在Prompt中,指定输出格式为json、或者只保留java代码。
这样可以快速实现热门歌单等json接口
另外可以把输出格式就设置为流式,这样客户端或网页前端,可以使用SSE协议接收结果、逐个Token显示。
  1. return chatClient.prompt()
  2.   .system(systemSpec -> systemSpec
  3.     .text(promptTemplate)
  4.     .param("title", question.title())
  5.     .param("rules", langRules))
  6.     .user(question.question())
  7.   .stream() // 流式
  8.   .content();
复制代码
4.4 响应元数据

LLM返回的内容,例如OpenAI,包含了Token使用相关的数据(元数据),形如:
  1. {
  2.   "token_usage": {
  3.     "completion_tokens": 164,
  4.     "prompt_tokens": 17,
  5.     "total_tokens": 181
  6.   },
  7.   "model_name": "gpt-4-turbo",
  8.   "system_fingerprint": "fp_76f018034d",
  9.   "finish_reason": "stop",
  10.   "logprobs": null
  11. }
复制代码
这样可以再代码中获取和记录:
  1. var responseEntity = chatClient.prompt()
  2.   .system(systemSpec -> systemSpec
  3.     .text(promptTemplate)
  4.     .param("gameTitle", question.gameTitle())
  5.     .param("rules", gameRules))
  6.     .user(question.question())
  7.   .call()
  8. .responseEntity(Answer.class);
  9. var response = responseEntity.response();
  10. var metadata = response.getMetadata();
  11. log.info(metadata.getUsage()); // 获得Token使用量
  12. return responseEntity.entity();
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册