Commit 10a94c3ef20aeb6767f3f5327a2552bdfa28c6da

Authored by cherishsince
1 parent c811f3a4

【优化】处理百度 system 角色定制失效问题。

yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/YiYanChatClient.java
@@ -2,6 +2,8 @@ package cn.iocoder.yudao.framework.ai.chatyiyan; @@ -2,6 +2,8 @@ package cn.iocoder.yudao.framework.ai.chatyiyan;
2 2
3 import cn.hutool.core.bean.BeanUtil; 3 import cn.hutool.core.bean.BeanUtil;
4 import cn.iocoder.yudao.framework.ai.chat.*; 4 import cn.iocoder.yudao.framework.ai.chat.*;
  5 +import cn.iocoder.yudao.framework.ai.chat.messages.Message;
  6 +import cn.iocoder.yudao.framework.ai.chat.messages.MessageType;
5 import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions; 7 import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions;
6 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; 8 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
7 import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanApi; 9 import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanApi;
@@ -9,6 +11,7 @@ import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletion; @@ -9,6 +11,7 @@ import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletion;
9 import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletionRequest; 11 import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletionRequest;
10 import cn.iocoder.yudao.framework.ai.chatyiyan.exception.YiYanApiException; 12 import cn.iocoder.yudao.framework.ai.chatyiyan.exception.YiYanApiException;
11 import lombok.extern.slf4j.Slf4j; 13 import lombok.extern.slf4j.Slf4j;
  14 +import org.jetbrains.annotations.NotNull;
12 import org.springframework.http.ResponseEntity; 15 import org.springframework.http.ResponseEntity;
13 import org.springframework.retry.RetryCallback; 16 import org.springframework.retry.RetryCallback;
14 import org.springframework.retry.RetryContext; 17 import org.springframework.retry.RetryContext;
@@ -18,10 +21,11 @@ import reactor.core.publisher.Flux; @@ -18,10 +21,11 @@ import reactor.core.publisher.Flux;
18 21
19 import java.time.Duration; 22 import java.time.Duration;
20 import java.util.List; 23 import java.util.List;
  24 +import java.util.stream.Collectors;
21 25
22 /** 26 /**
23 * 文心一言 27 * 文心一言
24 - * 28 + * <p>
25 * author: fansili 29 * author: fansili
26 * time: 2024/3/8 19:11 30 * time: 2024/3/8 19:11
27 */ 31 */
@@ -52,7 +56,9 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient { @@ -52,7 +56,9 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient {
52 public <T extends Object, E extends Throwable> void onError(RetryContext context, 56 public <T extends Object, E extends Throwable> void onError(RetryContext context,
53 RetryCallback<T, E> callback, Throwable throwable) { 57 RetryCallback<T, E> callback, Throwable throwable) {
54 log.warn("重试异常:" + context.getRetryCount(), throwable); 58 log.warn("重试异常:" + context.getRetryCount(), throwable);
55 - }; 59 + }
  60 +
  61 + ;
56 }) 62 })
57 .build(); 63 .build();
58 64
@@ -92,6 +98,42 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient { @@ -92,6 +98,42 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient {
92 } 98 }
93 99
94 private YiYanChatCompletionRequest createRequest(Prompt prompt, boolean stream) { 100 private YiYanChatCompletionRequest createRequest(Prompt prompt, boolean stream) {
  101 + // 获取配置
  102 + YiYanOptions useOptions = getYiYanOptions(prompt);
  103 + // 创建 request
  104 +
  105 + // tip: 百度的 system 不在 message 里面
  106 + // tip:百度的 message 只有 user 和 assistant
  107 + // https://cloud.baidu.com/doc/WENXINWORKSHOP/s/clntwmv7t
  108 +
  109 + // 获取 user 和 assistant
  110 + List<YiYanChatCompletionRequest.Message> messageList = prompt.getInstructions().stream()
  111 + // 过滤 system
  112 + .filter(msg -> MessageType.SYSTEM != msg.getMessageType())
  113 + .map(msg -> new YiYanChatCompletionRequest.Message()
  114 + .setRole(msg.getMessageType().getValue())
  115 + .setContent(msg.getContent())
  116 + ).toList();
  117 + // 获取 system
  118 + String systemPrompt = prompt.getInstructions().stream()
  119 + .filter(msg -> MessageType.SYSTEM == msg.getMessageType())
  120 + .map(Message::getContent)
  121 + .collect(Collectors.joining());
  122 +
  123 + YiYanChatCompletionRequest request = new YiYanChatCompletionRequest(messageList);
  124 + // 复制 qianWenOptions 属性取 request(这里 options 属性和 request 基本保持一致)
  125 + // top: 由于遵循 spring-ai规范,支持在构建client的时候传入默认的 chatOptions
  126 + BeanUtil.copyProperties(useOptions, request);
  127 + request.setTop_p(useOptions.getTopP());
  128 + request.setMax_output_tokens(useOptions.getMaxOutputTokens());
  129 + request.setTemperature(useOptions.getTemperature());
  130 + request.setSystem(systemPrompt);
  131 + // 设置 stream
  132 + request.setStream(stream);
  133 + return request;
  134 + }
  135 +
  136 + private @NotNull YiYanOptions getYiYanOptions(Prompt prompt) {
95 // 两个都为null 则没有配置文件 137 // 两个都为null 则没有配置文件
96 if (yiYanOptions == null && prompt.getOptions() == null) { 138 if (yiYanOptions == null && prompt.getOptions() == null) {
97 throw new ChatException("ChatOptions 未配置参数!"); 139 throw new ChatException("ChatOptions 未配置参数!");
@@ -106,19 +148,7 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient { @@ -106,19 +148,7 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient {
106 throw new ChatException("Prompt 传入的不是 YiYanOptions!"); 148 throw new ChatException("Prompt 传入的不是 YiYanOptions!");
107 } 149 }
108 // 转换 YiYanOptions 150 // 转换 YiYanOptions
109 - YiYanOptions qianWenOptions = (YiYanOptions) options;  
110 - // 创建 request  
111 - List<YiYanChatCompletionRequest.Message> messageList = prompt.getInstructions().stream().map(  
112 - msg -> new YiYanChatCompletionRequest.Message()  
113 - .setRole(msg.getMessageType().getValue())  
114 - .setContent(msg.getContent())  
115 - ).toList();  
116 - YiYanChatCompletionRequest request = new YiYanChatCompletionRequest(messageList);  
117 - // 复制 qianWenOptions 属性取 request(这里 options 属性和 request 基本保持一致)  
118 - // top: 由于遵循 spring-ai规范,支持在构建client的时候传入默认的 chatOptions  
119 - BeanUtil.copyProperties(qianWenOptions, request);  
120 - // 设置 stream  
121 - request.setStream(stream);  
122 - return request; 151 + YiYanOptions useOptions = (YiYanOptions) options;
  152 + return useOptions;
123 } 153 }
124 } 154 }
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/YiYanOptions.java
@@ -2,7 +2,6 @@ package cn.iocoder.yudao.framework.ai.chatyiyan; @@ -2,7 +2,6 @@ package cn.iocoder.yudao.framework.ai.chatyiyan;
2 2
3 import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions; 3 import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions;
4 import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletionRequest; 4 import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletionRequest;
5 -import com.fasterxml.jackson.annotation.JsonProperty;  
6 import lombok.Data; 5 import lombok.Data;
7 import lombok.experimental.Accessors; 6 import lombok.experimental.Accessors;
8 7
@@ -40,7 +39,7 @@ public class YiYanOptions implements ChatOptions { @@ -40,7 +39,7 @@ public class YiYanOptions implements ChatOptions {
40 * (2)默认0.8,取值范围 [0, 1.0] 39 * (2)默认0.8,取值范围 [0, 1.0]
41 * 必填:否 40 * 必填:否
42 */ 41 */
43 - private Float top_p; 42 + private Float topP;
44 /** 43 /**
45 * 通过对已生成的token增加惩罚,减少重复生成的现象。说明: 44 * 通过对已生成的token增加惩罚,减少重复生成的现象。说明:
46 * (1)值越大表示惩罚越大 45 * (1)值越大表示惩罚越大
@@ -84,7 +83,7 @@ public class YiYanOptions implements ChatOptions { @@ -84,7 +83,7 @@ public class YiYanOptions implements ChatOptions {
84 * 指定模型最大输出token数,范围[2, 2048] 83 * 指定模型最大输出token数,范围[2, 2048]
85 * 必填:否 84 * 必填:否
86 */ 85 */
87 - private Integer max_output_tokens; 86 + private Integer maxOutputTokens;
88 /** 87 /**
89 * 指定响应内容的格式,说明: 88 * 指定响应内容的格式,说明:
90 * (1)可选值: 89 * (1)可选值:
@@ -122,12 +121,12 @@ public class YiYanOptions implements ChatOptions { @@ -122,12 +121,12 @@ public class YiYanOptions implements ChatOptions {
122 121
123 @Override 122 @Override
124 public Float getTopP() { 123 public Float getTopP() {
125 - return top_p; 124 + return topP;
126 } 125 }
127 126
128 @Override 127 @Override
129 public void setTopP(Float topP) { 128 public void setTopP(Float topP) {
130 - this.top_p = topP; 129 + this.topP = topP;
131 } 130 }
132 131
133 // 百度么有 topK 132 // 百度么有 topK
@@ -139,6 +138,5 @@ public class YiYanOptions implements ChatOptions { @@ -139,6 +138,5 @@ public class YiYanOptions implements ChatOptions {
139 138
140 @Override 139 @Override
141 public void setTopK(Integer topK) { 140 public void setTopK(Integer topK) {
142 -  
143 } 141 }
144 } 142 }
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/YiYanChatTests.java
1 package cn.iocoder.yudao.framework.ai.chat; 1 package cn.iocoder.yudao.framework.ai.chat;
2 2
  3 +import cn.iocoder.yudao.framework.ai.chat.messages.Message;
  4 +import cn.iocoder.yudao.framework.ai.chat.messages.SystemMessage;
  5 +import cn.iocoder.yudao.framework.ai.chat.messages.UserMessage;
3 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; 6 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
4 import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatClient; 7 import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatClient;
5 import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatModel; 8 import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatModel;
@@ -9,11 +12,13 @@ import org.junit.Before; @@ -9,11 +12,13 @@ import org.junit.Before;
9 import org.junit.Test; 12 import org.junit.Test;
10 import reactor.core.publisher.Flux; 13 import reactor.core.publisher.Flux;
11 14
  15 +import java.util.ArrayList;
  16 +import java.util.List;
12 import java.util.Scanner; 17 import java.util.Scanner;
13 18
14 /** 19 /**
15 * chat 文心一言 20 * chat 文心一言
16 - * 21 + * <p>
17 * author: fansili 22 * author: fansili
18 * time: 2024/3/12 20:59 23 * time: 2024/3/12 20:59
19 */ 24 */
@@ -29,18 +34,36 @@ public class YiYanChatTests { @@ -29,18 +34,36 @@ public class YiYanChatTests {
29 YiYanChatModel.ERNIE4_3_5_8K, 34 YiYanChatModel.ERNIE4_3_5_8K,
30 86400 35 86400
31 ); 36 );
32 - yiYanChatClient = new YiYanChatClient(yiYanApi, new YiYanOptions().setMax_output_tokens(2048)); 37 + YiYanOptions yiYanOptions = new YiYanOptions();
  38 + yiYanOptions.setMaxOutputTokens(2048);
  39 + yiYanOptions.setTopP(0.6f);
  40 + yiYanOptions.setTemperature(0.85f);
  41 + yiYanChatClient = new YiYanChatClient(
  42 + yiYanApi,
  43 + yiYanOptions
  44 + );
33 } 45 }
34 46
35 @Test 47 @Test
36 public void callTest() { 48 public void callTest() {
37 - ChatResponse call = yiYanChatClient.call(new Prompt("什么编程语言最好?")); 49 +
  50 + // tip: 百度的message 有特殊规则(最后一个message为当前请求的信息,前面的message为历史对话信息)
  51 + // tip: 地址 https://cloud.baidu.com/doc/WENXINWORKSHOP/s/jlil56u11
  52 + List<Message> messages = new ArrayList<>();
  53 + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景,所有问题都采用文言文回答。"));
  54 + messages.add(new UserMessage("长沙怎么样?"));
  55 +
  56 + ChatResponse call = yiYanChatClient.call(new Prompt(messages));
38 System.err.println(call.getResult()); 57 System.err.println(call.getResult());
39 } 58 }
40 59
41 @Test 60 @Test
42 public void streamTest() { 61 public void streamTest() {
43 - Flux<ChatResponse> fluxResponse = yiYanChatClient.stream(new Prompt("用java帮我写一个快排算法?")); 62 + List<Message> messages = new ArrayList<>();
  63 + messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景,所有问题都采用文言文回答。"));
  64 + messages.add(new UserMessage("长沙怎么样?"));
  65 +
  66 + Flux<ChatResponse> fluxResponse = yiYanChatClient.stream(new Prompt(messages));
44 fluxResponse.subscribe(chatResponse -> System.err.print(chatResponse.getResult().getOutput().getContent())); 67 fluxResponse.subscribe(chatResponse -> System.err.print(chatResponse.getResult().getOutput().getContent()));
45 // 阻止退出 68 // 阻止退出
46 Scanner scanner = new Scanner(System.in); 69 Scanner scanner = new Scanner(System.in);