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 2  
3 3 import cn.hutool.core.bean.BeanUtil;
4 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 7 import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions;
6 8 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
7 9 import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanApi;
... ... @@ -9,6 +11,7 @@ import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletion;
9 11 import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletionRequest;
10 12 import cn.iocoder.yudao.framework.ai.chatyiyan.exception.YiYanApiException;
11 13 import lombok.extern.slf4j.Slf4j;
  14 +import org.jetbrains.annotations.NotNull;
12 15 import org.springframework.http.ResponseEntity;
13 16 import org.springframework.retry.RetryCallback;
14 17 import org.springframework.retry.RetryContext;
... ... @@ -18,10 +21,11 @@ import reactor.core.publisher.Flux;
18 21  
19 22 import java.time.Duration;
20 23 import java.util.List;
  24 +import java.util.stream.Collectors;
21 25  
22 26 /**
23 27 * 文心一言
24   - *
  28 + * <p>
25 29 * author: fansili
26 30 * time: 2024/3/8 19:11
27 31 */
... ... @@ -52,7 +56,9 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient {
52 56 public <T extends Object, E extends Throwable> void onError(RetryContext context,
53 57 RetryCallback<T, E> callback, Throwable throwable) {
54 58 log.warn("重试异常:" + context.getRetryCount(), throwable);
55   - };
  59 + }
  60 +
  61 + ;
56 62 })
57 63 .build();
58 64  
... ... @@ -92,6 +98,42 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient {
92 98 }
93 99  
94 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 137 // 两个都为null 则没有配置文件
96 138 if (yiYanOptions == null && prompt.getOptions() == null) {
97 139 throw new ChatException("ChatOptions 未配置参数!");
... ... @@ -106,19 +148,7 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient {
106 148 throw new ChatException("Prompt 传入的不是 YiYanOptions!");
107 149 }
108 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 2  
3 3 import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions;
4 4 import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletionRequest;
5   -import com.fasterxml.jackson.annotation.JsonProperty;
6 5 import lombok.Data;
7 6 import lombok.experimental.Accessors;
8 7  
... ... @@ -40,7 +39,7 @@ public class YiYanOptions implements ChatOptions {
40 39 * (2)默认0.8,取值范围 [0, 1.0]
41 40 * 必填:否
42 41 */
43   - private Float top_p;
  42 + private Float topP;
44 43 /**
45 44 * 通过对已生成的token增加惩罚,减少重复生成的现象。说明:
46 45 * (1)值越大表示惩罚越大
... ... @@ -84,7 +83,7 @@ public class YiYanOptions implements ChatOptions {
84 83 * 指定模型最大输出token数,范围[2, 2048]
85 84 * 必填:否
86 85 */
87   - private Integer max_output_tokens;
  86 + private Integer maxOutputTokens;
88 87 /**
89 88 * 指定响应内容的格式,说明:
90 89 * (1)可选值:
... ... @@ -122,12 +121,12 @@ public class YiYanOptions implements ChatOptions {
122 121  
123 122 @Override
124 123 public Float getTopP() {
125   - return top_p;
  124 + return topP;
126 125 }
127 126  
128 127 @Override
129 128 public void setTopP(Float topP) {
130   - this.top_p = topP;
  129 + this.topP = topP;
131 130 }
132 131  
133 132 // 百度么有 topK
... ... @@ -139,6 +138,5 @@ public class YiYanOptions implements ChatOptions {
139 138  
140 139 @Override
141 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 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 6 import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt;
4 7 import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatClient;
5 8 import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatModel;
... ... @@ -9,11 +12,13 @@ import org.junit.Before;
9 12 import org.junit.Test;
10 13 import reactor.core.publisher.Flux;
11 14  
  15 +import java.util.ArrayList;
  16 +import java.util.List;
12 17 import java.util.Scanner;
13 18  
14 19 /**
15 20 * chat 文心一言
16   - *
  21 + * <p>
17 22 * author: fansili
18 23 * time: 2024/3/12 20:59
19 24 */
... ... @@ -29,18 +34,36 @@ public class YiYanChatTests {
29 34 YiYanChatModel.ERNIE4_3_5_8K,
30 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 47 @Test
36 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 57 System.err.println(call.getResult());
39 58 }
40 59  
41 60 @Test
42 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 67 fluxResponse.subscribe(chatResponse -> System.err.print(chatResponse.getResult().getOutput().getContent()));
45 68 // 阻止退出
46 69 Scanner scanner = new Scanner(System.in);
... ...