Commit 10a94c3ef20aeb6767f3f5327a2552bdfa28c6da
1 parent
c811f3a4
【优化】处理百度 system 角色定制失效问题。
Showing
3 changed files
with
77 additions
and
26 deletions
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); |