Commit f41e43713c9ca32bf72420f6c135b5bb7ea1e01e
1 parent
1fada268
百度 文心一言 适配chatOptions
Showing
5 changed files
with
197 additions
and
35 deletions
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/YiYanChatClient.java
| 1 | 1 | package cn.iocoder.yudao.framework.ai.chatyiyan; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.ai.chat.ChatClient; | |
| 4 | -import cn.iocoder.yudao.framework.ai.chat.ChatResponse; | |
| 5 | -import cn.iocoder.yudao.framework.ai.chat.Generation; | |
| 6 | -import cn.iocoder.yudao.framework.ai.chat.StreamingChatClient; | |
| 7 | -import cn.iocoder.yudao.framework.ai.chat.messages.Message; | |
| 3 | +import cn.hutool.core.bean.BeanUtil; | |
| 4 | +import cn.iocoder.yudao.framework.ai.chat.*; | |
| 5 | +import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions; | |
| 8 | 6 | import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; |
| 9 | 7 | import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletion; |
| 10 | 8 | import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletionRequest; |
| ... | ... | @@ -18,7 +16,6 @@ import org.springframework.retry.support.RetryTemplate; |
| 18 | 16 | import reactor.core.publisher.Flux; |
| 19 | 17 | |
| 20 | 18 | import java.time.Duration; |
| 21 | -import java.util.ArrayList; | |
| 22 | 19 | import java.util.List; |
| 23 | 20 | |
| 24 | 21 | /** |
| ... | ... | @@ -32,10 +29,17 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient { |
| 32 | 29 | |
| 33 | 30 | private YiYanApi yiYanApi; |
| 34 | 31 | |
| 32 | + private YiYanOptions yiYanOptions; | |
| 33 | + | |
| 35 | 34 | public YiYanChatClient(YiYanApi yiYanApi) { |
| 36 | 35 | this.yiYanApi = yiYanApi; |
| 37 | 36 | } |
| 38 | 37 | |
| 38 | + public YiYanChatClient(YiYanApi yiYanApi, YiYanOptions yiYanOptions) { | |
| 39 | + this.yiYanApi = yiYanApi; | |
| 40 | + this.yiYanOptions = yiYanOptions; | |
| 41 | + } | |
| 42 | + | |
| 39 | 43 | public final RetryTemplate retryTemplate = RetryTemplate.builder() |
| 40 | 44 | // 最大重试次数 10 |
| 41 | 45 | .maxAttempts(10) |
| ... | ... | @@ -70,20 +74,6 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient { |
| 70 | 74 | }); |
| 71 | 75 | } |
| 72 | 76 | |
| 73 | - private YiYanChatCompletionRequest createRequest(Prompt prompt, boolean stream) { | |
| 74 | - List<YiYanChatCompletionRequest.Message> messages = new ArrayList<>(); | |
| 75 | - List<Message> instructions = prompt.getInstructions(); | |
| 76 | - for (Message instruction : instructions) { | |
| 77 | - YiYanChatCompletionRequest.Message message = new YiYanChatCompletionRequest.Message(); | |
| 78 | - message.setContent(instruction.getContent()); | |
| 79 | - message.setRole(instruction.getMessageType().getValue()); | |
| 80 | - messages.add(message); | |
| 81 | - } | |
| 82 | - YiYanChatCompletionRequest request = new YiYanChatCompletionRequest(messages); | |
| 83 | - request.setStream(stream); | |
| 84 | - return request; | |
| 85 | - } | |
| 86 | - | |
| 87 | 77 | @Override |
| 88 | 78 | public Flux<ChatResponse> stream(Prompt prompt) { |
| 89 | 79 | // ctx 会有重试的信息 |
| ... | ... | @@ -93,4 +83,35 @@ public class YiYanChatClient implements ChatClient, StreamingChatClient { |
| 93 | 83 | Flux<YiYanChatCompletion> response = this.yiYanApi.chatCompletionStream(request); |
| 94 | 84 | return response.map(res -> new ChatResponse(List.of(new Generation(res.getResult())))); |
| 95 | 85 | } |
| 86 | + | |
| 87 | + private YiYanChatCompletionRequest createRequest(Prompt prompt, boolean stream) { | |
| 88 | + // 两个都为null 则没有配置文件 | |
| 89 | + if (yiYanOptions == null && prompt.getOptions() == null) { | |
| 90 | + throw new ChatException("ChatOptions 未配置参数!"); | |
| 91 | + } | |
| 92 | + // 优先使用 Prompt 里面的 ChatOptions | |
| 93 | + ChatOptions options = yiYanOptions; | |
| 94 | + if (prompt.getOptions() != null) { | |
| 95 | + options = (ChatOptions) prompt.getOptions(); | |
| 96 | + } | |
| 97 | + // Prompt 里面是一个 ChatOptions,用户可以随意传入,这里做一下判断 | |
| 98 | + if (!(options instanceof YiYanOptions)) { | |
| 99 | + throw new ChatException("Prompt 传入的不是 YiYanOptions!"); | |
| 100 | + } | |
| 101 | + // 转换 YiYanOptions | |
| 102 | + YiYanOptions qianWenOptions = (YiYanOptions) options; | |
| 103 | + // 创建 request | |
| 104 | + List<YiYanChatCompletionRequest.Message> messageList = prompt.getInstructions().stream().map( | |
| 105 | + msg -> new YiYanChatCompletionRequest.Message() | |
| 106 | + .setRole(msg.getMessageType().getValue()) | |
| 107 | + .setContent(msg.getContent()) | |
| 108 | + ).toList(); | |
| 109 | + YiYanChatCompletionRequest request = new YiYanChatCompletionRequest(messageList); | |
| 110 | + // 复制 qianWenOptions 属性取 request(这里 options 属性和 request 基本保持一致) | |
| 111 | + // top: 由于遵循 spring-ai规范,支持在构建client的时候传入默认的 chatOptions | |
| 112 | + BeanUtil.copyProperties(qianWenOptions, request); | |
| 113 | + // 设置 stream | |
| 114 | + request.setStream(stream); | |
| 115 | + return request; | |
| 116 | + } | |
| 96 | 117 | } | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/YiYanOptions.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.ai.chatyiyan; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.ai.chat.prompt.ChatOptions; | |
| 4 | +import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletionRequest; | |
| 5 | +import com.fasterxml.jackson.annotation.JsonProperty; | |
| 6 | +import lombok.Data; | |
| 7 | +import lombok.experimental.Accessors; | |
| 8 | + | |
| 9 | +import java.util.List; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * 百度 问心一言 | |
| 13 | + * | |
| 14 | + * 文档地址:https://cloud.baidu.com/doc/WENXINWORKSHOP/s/clntwmv7t | |
| 15 | + * | |
| 16 | + * author: fansili | |
| 17 | + * time: 2024/3/16 19:33 | |
| 18 | + */ | |
| 19 | +@Data | |
| 20 | +@Accessors(chain = true) | |
| 21 | +public class YiYanOptions implements ChatOptions { | |
| 22 | + | |
| 23 | + /** | |
| 24 | + * 一个可触发函数的描述列表,说明: | |
| 25 | + * (1)支持的function数量无限制 | |
| 26 | + * (2)长度限制,最后一个message的content长度(即此轮对话的问题)、functions和system字段总内容不能超过20480 个字符,且不能超过5120 tokens | |
| 27 | + * 必填:否 | |
| 28 | + */ | |
| 29 | + private List<YiYanChatCompletionRequest.Function> functions; | |
| 30 | + /** | |
| 31 | + * 说明: | |
| 32 | + * (1)较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定 | |
| 33 | + * (2)默认0.8,范围 (0, 1.0],不能为0 | |
| 34 | + * 必填:否 | |
| 35 | + */ | |
| 36 | + private Float temperature; | |
| 37 | + /** | |
| 38 | + * 说明: | |
| 39 | + * (1)影响输出文本的多样性,取值越大,生成文本的多样性越强 | |
| 40 | + * (2)默认0.8,取值范围 [0, 1.0] | |
| 41 | + * 必填:否 | |
| 42 | + */ | |
| 43 | + private Float top_p; | |
| 44 | + /** | |
| 45 | + * 通过对已生成的token增加惩罚,减少重复生成的现象。说明: | |
| 46 | + * (1)值越大表示惩罚越大 | |
| 47 | + * (2)默认1.0,取值范围:[1.0, 2.0] | |
| 48 | + * | |
| 49 | + * 必填:否 | |
| 50 | + */ | |
| 51 | + private Float penalty_score; | |
| 52 | + /** | |
| 53 | + * 是否以流式接口的形式返回数据,默认false | |
| 54 | + * 必填:否 | |
| 55 | + */ | |
| 56 | + private Boolean stream; | |
| 57 | + /** | |
| 58 | + * 模型人设,主要用于人设设定,例如,你是xxx公司制作的AI助手,说明: | |
| 59 | + * (1)长度限制,最后一个message的content长度(即此轮对话的问题)、functions和system字段总内容不能超过20480 个字符,且不能超过5120 tokens | |
| 60 | + * (2)如果同时使用system和functions,可能暂无法保证使用效果,持续进行优化 | |
| 61 | + * 必填:否 | |
| 62 | + */ | |
| 63 | + private String system; | |
| 64 | + /** | |
| 65 | + * 生成停止标识,当模型生成结果以stop中某个元素结尾时,停止文本生成。说明: | |
| 66 | + * (1)每个元素长度不超过20字符 | |
| 67 | + * (2)最多4个元素 | |
| 68 | + * 必填:否 | |
| 69 | + */ | |
| 70 | + private List<String> stop; | |
| 71 | + /** | |
| 72 | + * 是否强制关闭实时搜索功能,默认false,表示不关闭 | |
| 73 | + * 必填:否 | |
| 74 | + */ | |
| 75 | + private Boolean disable_search; | |
| 76 | + /** | |
| 77 | + * 是否开启上角标返回,说明: | |
| 78 | + * (1)开启后,有概率触发搜索溯源信息search_info,search_info内容见响应参数介绍 | |
| 79 | + * (2)默认false,不开启 | |
| 80 | + * 必填:否 | |
| 81 | + */ | |
| 82 | + private Boolean enable_citation; | |
| 83 | + /** | |
| 84 | + * 指定模型最大输出token数,范围[2, 2048] | |
| 85 | + * 必填:否 | |
| 86 | + */ | |
| 87 | + private Integer max_output_tokens; | |
| 88 | + /** | |
| 89 | + * 指定响应内容的格式,说明: | |
| 90 | + * (1)可选值: | |
| 91 | + * · json_object:以json格式返回,可能出现不满足效果情况 | |
| 92 | + * · text:以文本格式返回 | |
| 93 | + * (2)如果不填写参数response_format值,默认为text | |
| 94 | + * 必填:否 | |
| 95 | + */ | |
| 96 | + private String response_format; | |
| 97 | + /** | |
| 98 | + * 表示最终用户的唯一标识符 | |
| 99 | + * 必填:否 | |
| 100 | + */ | |
| 101 | + private String user_id; | |
| 102 | + /** | |
| 103 | + * 在函数调用场景下,提示大模型选择指定的函数(非强制),说明:指定的函数名必须在functions中存在 | |
| 104 | + * 必填:否 | |
| 105 | + * | |
| 106 | + * ERNIE-4.0-8K 模型没有这个字段 | |
| 107 | + */ | |
| 108 | + private String tool_choice; | |
| 109 | + | |
| 110 | + // | |
| 111 | + // 以下兼容 spring-ai ChatOptions 暂时没有其他地方用到 | |
| 112 | + | |
| 113 | + @Override | |
| 114 | + public Float getTemperature() { | |
| 115 | + return this.temperature; | |
| 116 | + } | |
| 117 | + | |
| 118 | + @Override | |
| 119 | + public void setTemperature(Float temperature) { | |
| 120 | + this.temperature = temperature; | |
| 121 | + } | |
| 122 | + | |
| 123 | + @Override | |
| 124 | + public Float getTopP() { | |
| 125 | + return top_p; | |
| 126 | + } | |
| 127 | + | |
| 128 | + @Override | |
| 129 | + public void setTopP(Float topP) { | |
| 130 | + this.top_p = topP; | |
| 131 | + } | |
| 132 | + | |
| 133 | + // 百度么有 topK | |
| 134 | + | |
| 135 | + @Override | |
| 136 | + public Integer getTopK() { | |
| 137 | + return null; | |
| 138 | + } | |
| 139 | + | |
| 140 | + @Override | |
| 141 | + public void setTopK(Integer topK) { | |
| 142 | + | |
| 143 | + } | |
| 144 | +} | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/api/YiYanChatCompletionRequest.java
| ... | ... | @@ -37,14 +37,14 @@ public class YiYanChatCompletionRequest { |
| 37 | 37 | * (2)默认0.8,范围 (0, 1.0],不能为0 |
| 38 | 38 | * 必填:否 |
| 39 | 39 | */ |
| 40 | - private String temperature; | |
| 40 | + private Float temperature; | |
| 41 | 41 | /** |
| 42 | 42 | * 说明: |
| 43 | 43 | * (1)影响输出文本的多样性,取值越大,生成文本的多样性越强 |
| 44 | 44 | * (2)默认0.8,取值范围 [0, 1.0] |
| 45 | 45 | * 必填:否 |
| 46 | 46 | */ |
| 47 | - private String top_p; | |
| 47 | + private Float top_p; | |
| 48 | 48 | /** |
| 49 | 49 | * 通过对已生成的token增加惩罚,减少重复生成的现象。说明: |
| 50 | 50 | * (1)值越大表示惩罚越大 |
| ... | ... | @@ -52,7 +52,7 @@ public class YiYanChatCompletionRequest { |
| 52 | 52 | * |
| 53 | 53 | * 必填:否 |
| 54 | 54 | */ |
| 55 | - private String penalty_score; | |
| 55 | + private Float penalty_score; | |
| 56 | 56 | /** |
| 57 | 57 | * 是否以流式接口的形式返回数据,默认false |
| 58 | 58 | * 必填:否 |
| ... | ... | @@ -71,7 +71,7 @@ public class YiYanChatCompletionRequest { |
| 71 | 71 | * (2)最多4个元素 |
| 72 | 72 | * 必填:否 |
| 73 | 73 | */ |
| 74 | - private String stop; | |
| 74 | + private List<String> stop; | |
| 75 | 75 | /** |
| 76 | 76 | * 是否强制关闭实时搜索功能,默认false,表示不关闭 |
| 77 | 77 | * 必填:否 |
| ... | ... | @@ -106,6 +106,8 @@ public class YiYanChatCompletionRequest { |
| 106 | 106 | /** |
| 107 | 107 | * 在函数调用场景下,提示大模型选择指定的函数(非强制),说明:指定的函数名必须在functions中存在 |
| 108 | 108 | * 必填:否 |
| 109 | + * | |
| 110 | + * ERNIE-4.0-8K 模型没有这个字段 | |
| 109 | 111 | */ |
| 110 | 112 | private String tool_choice; |
| 111 | 113 | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/QianWenChatClientTests.java
| ... | ... | @@ -23,9 +23,9 @@ public class QianWenChatClientTests { |
| 23 | 23 | @Before |
| 24 | 24 | public void setup() { |
| 25 | 25 | QianWenApi qianWenApi = new QianWenApi( |
| 26 | - "", | |
| 27 | - "", | |
| 28 | - "", | |
| 26 | + "LTAI5tNTVhXW4fLKUjMrr98z", | |
| 27 | + "ZJ0JQeyjzxxm5CfeTV6k1wNE9UsvZP", | |
| 28 | + "f0c1088824594f589c8f10567ccd929f_p_efm", | |
| 29 | 29 | null |
| 30 | 30 | ); |
| 31 | 31 | qianWenChatClient = new QianWenChatClient( | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/chat/YiYanChatTests.java
| ... | ... | @@ -4,12 +4,12 @@ import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; |
| 4 | 4 | import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanApi; |
| 5 | 5 | import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatClient; |
| 6 | 6 | import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanChatModel; |
| 7 | +import cn.iocoder.yudao.framework.ai.chatyiyan.YiYanOptions; | |
| 7 | 8 | import org.junit.Before; |
| 8 | 9 | import org.junit.Test; |
| 9 | 10 | import reactor.core.publisher.Flux; |
| 10 | 11 | |
| 11 | 12 | import java.util.Scanner; |
| 12 | -import java.util.function.Consumer; | |
| 13 | 13 | |
| 14 | 14 | /** |
| 15 | 15 | * chat 文心一言 |
| ... | ... | @@ -29,7 +29,7 @@ public class YiYanChatTests { |
| 29 | 29 | YiYanChatModel.ERNIE4_3_5_8K, |
| 30 | 30 | 86400 |
| 31 | 31 | ); |
| 32 | - yiYanChatClient = new YiYanChatClient(yiYanApi); | |
| 32 | + yiYanChatClient = new YiYanChatClient(yiYanApi, new YiYanOptions().setMax_output_tokens(2048)); | |
| 33 | 33 | } |
| 34 | 34 | |
| 35 | 35 | @Test |
| ... | ... | @@ -41,12 +41,7 @@ public class YiYanChatTests { |
| 41 | 41 | @Test |
| 42 | 42 | public void streamTest() { |
| 43 | 43 | Flux<ChatResponse> fluxResponse = yiYanChatClient.stream(new Prompt("用java帮我写一个快排算法?")); |
| 44 | - fluxResponse.subscribe(new Consumer<ChatResponse>() { | |
| 45 | - @Override | |
| 46 | - public void accept(ChatResponse chatResponse) { | |
| 47 | - System.err.print(chatResponse.getResult().getOutput().getContent()); | |
| 48 | - } | |
| 49 | - }); | |
| 44 | + fluxResponse.subscribe(chatResponse -> System.err.print(chatResponse.getResult().getOutput().getContent())); | |
| 50 | 45 | // 阻止退出 |
| 51 | 46 | Scanner scanner = new Scanner(System.in); |
| 52 | 47 | scanner.nextLine(); | ... | ... |