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