From 1828d1953ab15beb501720e2a066f7ce7d8f8069 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Tue, 12 Mar 2024 21:44:07 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=81=E7=A7=BB=E6=96=87=E5=BF=83=E4=B8=80?= =?UTF-8?q?=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/ai/chatyiyan/YiYanApi.java | 97 ++++++++++ .../ai/chatyiyan/YiYanChatClient.java | 137 ++++++++++++++ .../ai/chatyiyan/YiYanChatModel.java | 35 ++++ .../ai/chatyiyan/api/YiYanAuthRes.java | 48 +++++ .../ai/chatyiyan/api/YiYanChatCompletion.java | 91 +++++++++ .../api/YiYanChatCompletionMessage.java | 8 + .../api/YiYanChatCompletionRequest.java | 176 ++++++++++++++++++ .../exception/YiYanApiException.java | 16 ++ 8 files changed, 608 insertions(+) create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/YiYanApi.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/YiYanChatClient.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/YiYanChatModel.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/api/YiYanAuthRes.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/api/YiYanChatCompletion.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/api/YiYanChatCompletionMessage.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/api/YiYanChatCompletionRequest.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/exception/YiYanApiException.java diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/YiYanApi.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/YiYanApi.java new file mode 100644 index 000000000..d22e8ffc9 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/YiYanApi.java @@ -0,0 +1,97 @@ +package cn.iocoder.yudao.framework.ai.chatyiyan; + +import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanAuthRes; +import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletion; +import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletionRequest; +import cn.iocoder.yudao.framework.ai.chatyiyan.exception.YiYanApiException; +import lombok.Data; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.ResponseEntity; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * 文心一言 + *

+ * author: fansili + * time: 2024/3/8 21:47 + */ +@Data +public class YiYanApi { + + private static final String DEFAULT_BASE_URL = "https://aip.baidubce.com"; + + private static final String AUTH_2_TOKEN_URI = "/oauth/2.0/token"; + + public static final String DEFAULT_CHAT_MODEL = "ERNIE 4.0"; + + // 获取access_token流程 https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Ilkkrb0i5 + private String appKey; + private String secretKey; + private String token; + // token刷新时间(秒) + private int refreshTokenSecondTime; + // 发送请求 webClient + private final WebClient webClient; + // 使用的模型 + private YiYanChatModel useChatModel; + + public YiYanApi(String appKey, String secretKey, YiYanChatModel useChatModel, int refreshTokenSecondTime) { + this.appKey = appKey; + this.secretKey = secretKey; + this.useChatModel = useChatModel; + this.refreshTokenSecondTime = refreshTokenSecondTime; + + this.webClient = WebClient.builder() + .baseUrl(DEFAULT_BASE_URL) + .build(); + + token = getToken(); + } + + private String getToken() { + // 文档地址: https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Ilkkrb0i5 + ResponseEntity response = this.webClient.post() + .uri(uriBuilder -> uriBuilder.path(AUTH_2_TOKEN_URI) + .queryParam("grant_type", "client_credentials") + .queryParam("client_id", appKey) + .queryParam("client_secret", secretKey) + .build() + ) + .retrieve() + .toEntity(YiYanAuthRes.class) + .block(); + // 检查请求状态 + if (HttpStatusCode.valueOf(200) != response.getStatusCode()) { + throw new YiYanApiException("一言认证失败! api:https://aip.baidubce.com/oauth/2.0/token 请检查 client_id、client_secret 是否正确!"); + } + YiYanAuthRes body = response.getBody(); + return body.getAccess_token(); + } + + public ResponseEntity chatCompletionEntity(YiYanChatCompletionRequest request) { + // TODO: 2024/3/10 小范 这里错误信息返回的结构不一样 +// {"error_code":17,"error_msg":"Open api daily request limit reached"} + return this.webClient.post() + .uri(uriBuilder + -> uriBuilder.path(useChatModel.getUri()) + .queryParam("access_token", token) + .build()) + .body(Mono.just(request), YiYanChatCompletionRequest.class) + .retrieve() + .toEntity(YiYanChatCompletion.class) + .block(); + } + + public Flux chatCompletionStream(YiYanChatCompletionRequest request) { + return this.webClient.post() + .uri(uriBuilder + -> uriBuilder.path(useChatModel.getUri()) + .queryParam("access_token", token) + .build()) + .body(Mono.just(request), YiYanChatCompletionRequest.class) + .retrieve() + .bodyToFlux(YiYanChatCompletion.class); + } +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/YiYanChatClient.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/YiYanChatClient.java new file mode 100644 index 000000000..fb7f155b4 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/YiYanChatClient.java @@ -0,0 +1,137 @@ +package cn.iocoder.yudao.framework.ai.chatyiyan; + +import cn.iocoder.yudao.framework.ai.chat.ChatClient; +import cn.iocoder.yudao.framework.ai.chat.ChatResponse; +import cn.iocoder.yudao.framework.ai.chat.Generation; +import cn.iocoder.yudao.framework.ai.chat.StreamingChatClient; +import cn.iocoder.yudao.framework.ai.chat.messages.Message; +import cn.iocoder.yudao.framework.ai.chat.prompt.Prompt; +import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletion; +import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletionMessage; +import cn.iocoder.yudao.framework.ai.chatyiyan.api.YiYanChatCompletionRequest; +import cn.iocoder.yudao.framework.ai.chatyiyan.exception.YiYanApiException; +import cn.iocoder.yudao.framework.ai.model.function.AbstractFunctionCallSupport; +import cn.iocoder.yudao.framework.ai.model.function.FunctionCallbackContext; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.http.ResponseEntity; +import org.springframework.retry.RetryCallback; +import org.springframework.retry.RetryContext; +import org.springframework.retry.RetryListener; +import org.springframework.retry.support.RetryTemplate; +import reactor.core.publisher.Flux; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +/** + * 文心一言 + * + * author: fansili + * time: 2024/3/8 19:11 + */ +@Slf4j +public class YiYanChatClient + extends AbstractFunctionCallSupport> + implements ChatClient, StreamingChatClient { + + private YiYanApi yiYanApi; + + public YiYanChatClient(YiYanApi yiYanApi) { + super(new FunctionCallbackContext()); + this.yiYanApi = yiYanApi; + } + + public final RetryTemplate retryTemplate = RetryTemplate.builder() + // 最大重试次数 10 + .maxAttempts(10) + .retryOn(YiYanApiException.class) + // 最大重试5次,第一次间隔3000ms,第二次3000ms * 2,第三次3000ms * 3,以此类推,最大间隔3 * 60000ms + .exponentialBackoff(Duration.ofMillis(3000), 2, Duration.ofMillis(3 * 60000)) + .withListener(new RetryListener() { + @Override + public void onError(RetryContext context, + RetryCallback callback, Throwable throwable) { + log.warn("重试异常:" + context.getRetryCount(), throwable); + }; + }) + .build(); + + @Override + public String call(String message) { + return ChatClient.super.call(message); + } + + @Override + public ChatResponse call(Prompt prompt) { + return this.retryTemplate.execute(ctx -> { + // ctx 会有重试的信息 + // 创建 request 请求,stream模式需要供应商支持 + YiYanChatCompletionRequest request = this.createRequest(prompt, false); + // 调用 callWithFunctionSupport 发送请求 + ResponseEntity response = this.callWithFunctionSupport(request); + // 获取结果封装 ChatResponse + YiYanChatCompletion chatCompletion = response.getBody(); + return new ChatResponse(List.of(new Generation(chatCompletion.getResult()))); + }); + } + + private YiYanChatCompletionRequest createRequest(Prompt prompt, boolean stream) { + List messages = new ArrayList<>(); + List instructions = prompt.getInstructions(); + for (Message instruction : instructions) { + YiYanChatCompletionRequest.Message message = new YiYanChatCompletionRequest.Message(); + message.setContent(instruction.getContent()); + message.setRole(instruction.getMessageType().getValue()); + messages.add(message); + } + YiYanChatCompletionRequest request = new YiYanChatCompletionRequest(messages); + request.setStream(stream); + return request; + } + + @Override + public Flux stream(Prompt prompt) { + // ctx 会有重试的信息 + // 创建 request 请求,stream模式需要供应商支持 + YiYanChatCompletionRequest request = this.createRequest(prompt, true); + // 调用 callWithFunctionSupport 发送请求 + Flux response = this.yiYanApi.chatCompletionStream(request); +// response.subscribe(new Consumer() { +// @Override +// public void accept(YiYanChatCompletion chatCompletion) { +// // {"id":"as-p0nfjuuasg","object":"chat.completion","created":1710033402,"sentence_id":0,"is_end":false,"is_truncated":false,"result":"编程语","need_clear_history":false,"finish_reason":"normal","usage":{"prompt_tokens":5,"completion_tokens":0,"total_tokens":5}} +// System.err.println(chatCompletion); +// } +// }); + return response.map(res -> { + return new ChatResponse(List.of(new Generation(res.getResult()))); + }); + } + + @Override + protected YiYanChatCompletionRequest doCreateToolResponseRequest(YiYanChatCompletionRequest previousRequest, YiYanChatCompletionMessage responseMessage, List conversationHistory) { + return null; + } + + @Override + protected List doGetUserMessages(YiYanChatCompletionRequest request) { + return null; + } + + @Override + protected YiYanChatCompletionMessage doGetToolResponseMessage(ResponseEntity response) { + return null; + } + + @Override + protected ResponseEntity doChatCompletion(YiYanChatCompletionRequest request) { + return yiYanApi.chatCompletionEntity(request); + } + + @Override + protected boolean isToolFunctionCall(ResponseEntity response) { + return false; + } +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/YiYanChatModel.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/YiYanChatModel.java new file mode 100644 index 000000000..77675430e --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/YiYanChatModel.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.framework.ai.chatyiyan; + +import lombok.Getter; + +/** + * 一言模型 + * + * 可参考百度文档:https://cloud.baidu.com/doc/WENXINWORKSHOP/s/clntwmv7t + * + * author: fansili + * time: 2024/3/9 12:01 + */ +@Getter +public enum YiYanChatModel { + + ERNIE4_0("ERNIE 4.0", "/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro"), + ERNIE4_3_5_8K("ERNIE-3.5-8K", "/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions"), + ERNIE4_3_5_8K_0205("ERNIE-3.5-8K-0205", "/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-3.5-8k-0205"), + + ERNIE4_3_5_8K_1222("ERNIE-3.5-8K-1222", "/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-3.5-8k-1222"), + ERNIE4_BOT_8K("ERNIE-Bot-8K", "/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie_bot_8k"), + ERNIE4_3_5_4K_0205("ERNIE-3.5-4K-0205", "/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/ernie-3.5-4k-0205"), + + ; + + YiYanChatModel(String value, String uri) { + this.value = value; + this.uri = uri; + } + + private String value; + + private String uri; + +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/api/YiYanAuthRes.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/api/YiYanAuthRes.java new file mode 100644 index 000000000..e828858c2 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/api/YiYanAuthRes.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.framework.ai.chatyiyan.api; + +import lombok.Data; + +/** + * 一言 获取access_token + * + * author: fansili + * time: 2024/3/10 08:51 + */ +@Data +public class YiYanAuthRes { + + /** + * 访问凭证 + */ + private String access_token; + /** + * 有效期,Access Token的有效期。 + * 说明:单位是秒,有效期30天 + */ + private int expires_in; + /** + * 错误码,说明:响应失败时返回该字段,成功时不返回 + */ + private String error; + /** + * 错误描述信息,帮助理解和解决发生的错误 + * 说明:响应失败时返回该字段,成功时不返回 + */ + private String error_description; + /** + * 暂时未使用,可忽略 + */ + private String session_key; + /** + * 暂时未使用,可忽略 + */ + private String refresh_token; + /** + * 暂时未使用,可忽略 + */ + private String scope; + /** + * 暂时未使用,可忽略 + */ + private String session_secret; +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/api/YiYanChatCompletion.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/api/YiYanChatCompletion.java new file mode 100644 index 000000000..b995da82a --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/api/YiYanChatCompletion.java @@ -0,0 +1,91 @@ +package cn.iocoder.yudao.framework.ai.chatyiyan.api; + +import lombok.Data; + +/** + * 聊天返回 + * 百度链接: https://cloud.baidu.com/doc/WENXINWORKSHOP/s/clntwmv7t + * + * author: fansili + * time: 2024/3/9 10:34 + */ +@Data +public class YiYanChatCompletion { + + /** + * 本轮对话的id + */ + private String id; + /** + * 回包类型,chat.completion:多轮对话返回 + */ + private String object; + /** + * 时间戳 + */ + private int created; + /** + * 表示当前子句的序号。只有在流式接口模式下会返回该字段 + */ + private int sentence_id; + /** + * 表示当前子句是否是最后一句。只有在流式接口模式下会返回该字段 + */ + private boolean is_end; + /** + * 当前生成的结果是否被截断 + */ + private boolean is_truncated; + /** + * 输出内容标识,说明: + * · normal:输出内容完全由大模型生成,未触发截断、替换 + * · stop:输出结果命中入参stop中指定的字段后被截断 + * · length:达到了最大的token数,根据EB返回结果is_truncated来截断 + * · content_filter:输出内容被截断、兜底、替换为**等 + */ + private String finish_reason; + /** + * 搜索数据,当请求参数enable_citation为true并且触发搜索时,会返回该字段 + */ + private String search_info; + /** + * 对话返回结果 + */ + private String result; + /** + * 表示用户输入是否存在安全,是否关闭当前会话,清理历史会话信息 + * true:是,表示用户输入存在安全风险,建议关闭当前会话,清理历史会话信息 + * false:否,表示用户输入无安全风险 + */ + private boolean need_clear_history; + /** + * 说明: + * · 0:正常返回 + * · 其他:非正常 + */ + private int flag; + /** + * 当need_clear_history为true时,此字段会告知第几轮对话有敏感信息,如果是当前问题,ban_round=-1 + */ + private int ban_round; + /** + * token统计信息 + */ + private Usage usage; + + @Data + public static class Usage { + /** + * 问题tokens数 + */ + private int prompt_tokens; + /** + * 回答tokens数 + */ + private int completion_tokens; + /** + * tokens总数 + */ + private int total_tokens; + } +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/api/YiYanChatCompletionMessage.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/api/YiYanChatCompletionMessage.java new file mode 100644 index 000000000..ce6259c76 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/api/YiYanChatCompletionMessage.java @@ -0,0 +1,8 @@ +package cn.iocoder.yudao.framework.ai.chatyiyan.api; + +/** + * author: fansili + * time: 2024/3/9 10:37 + */ +public class YiYanChatCompletionMessage { +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/api/YiYanChatCompletionRequest.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/api/YiYanChatCompletionRequest.java new file mode 100644 index 000000000..8c80d55f7 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/api/YiYanChatCompletionRequest.java @@ -0,0 +1,176 @@ +package cn.iocoder.yudao.framework.ai.chatyiyan.api; + +import lombok.Data; + +import java.util.List; + +/** + * 一言 Completion req + * + * 百度千帆文档:https://cloud.baidu.com/doc/WENXINWORKSHOP/s/jlil56u11 + * + * author: fansili + * time: 2024/3/9 10:34 + */ +@Data +public class YiYanChatCompletionRequest { + + public YiYanChatCompletionRequest(List messages) { + this.messages = messages; + } + + /** + * 聊天上下文信息。 + * 必填:是 + */ + private List messages; + /** + * 一个可触发函数的描述列表,说明: + * (1)支持的function数量无限制 + * (2)长度限制,最后一个message的content长度(即此轮对话的问题)、functions和system字段总内容不能超过20480 个字符,且不能超过5120 tokens + * 必填:否 + */ + private List functions; + /** + * 说明: + * (1)较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定 + * (2)默认0.8,范围 (0, 1.0],不能为0 + * 必填:否 + */ + private String temperature; + /** + * 说明: + * (1)影响输出文本的多样性,取值越大,生成文本的多样性越强 + * (2)默认0.8,取值范围 [0, 1.0] + * 必填:否 + */ + private String top_p; + /** + * 通过对已生成的token增加惩罚,减少重复生成的现象。说明: + * (1)值越大表示惩罚越大 + * (2)默认1.0,取值范围:[1.0, 2.0] + * + * 必填:否 + */ + private String penalty_score; + /** + * 是否以流式接口的形式返回数据,默认false + * 必填:否 + */ + private Boolean stream; + /** + * 模型人设,主要用于人设设定,例如,你是xxx公司制作的AI助手,说明: + * (1)长度限制,最后一个message的content长度(即此轮对话的问题)、functions和system字段总内容不能超过20480 个字符,且不能超过5120 tokens + * (2)如果同时使用system和functions,可能暂无法保证使用效果,持续进行优化 + * 必填:否 + */ + private String system; + /** + * 生成停止标识,当模型生成结果以stop中某个元素结尾时,停止文本生成。说明: + * (1)每个元素长度不超过20字符 + * (2)最多4个元素 + * 必填:否 + */ + private String stop; + /** + * 是否强制关闭实时搜索功能,默认false,表示不关闭 + * 必填:否 + */ + private Boolean disable_search; + /** + * 是否开启上角标返回,说明: + * (1)开启后,有概率触发搜索溯源信息search_info,search_info内容见响应参数介绍 + * (2)默认false,不开启 + * 必填:否 + */ + private Boolean enable_citation; + /** + * 指定模型最大输出token数,范围[2, 2048] + * 必填:否 + */ + private Integer max_output_tokens; + /** + * 指定响应内容的格式,说明: + * (1)可选值: + * · json_object:以json格式返回,可能出现不满足效果情况 + * · text:以文本格式返回 + * (2)如果不填写参数response_format值,默认为text + * 必填:否 + */ + private String response_format; + /** + * 表示最终用户的唯一标识符 + * 必填:否 + */ + private String user_id; + /** + * 在函数调用场景下,提示大模型选择指定的函数(非强制),说明:指定的函数名必须在functions中存在 + * 必填:否 + */ + private String tool_choice; + + + @Data + public static class Message { + private String role; + + private String content; + } + + @Data + public static class ToolChoice { + /** + * 指定工具类型,function + * 必填: 是 + */ + private String type; + /** + * 指定要使用的函数 + * 必填: 是 + */ + private Function function; + /** + * 指定要使用的函数名 + * 必填: 是 + */ + private String name; + } + + @Data + public static class Function { + /** + * 函数名 + * 必填: 是 + */ + private String name; + /** + * 函数描述 + * 必填: 是 + */ + private String description; + /** + * 函数请求参数,说明: + * (1)JSON Schema 格式,参考JSON Schema描述 + * (2)如果函数没有请求参数,parameters值格式如下: + * {"type": "object","properties": {}} + * 必填: 是 + */ + private String parameters; + /** + * 函数响应参数,JSON Schema 格式,参考JSON Schema描述 + * 必填: 否 + */ + private String responses; + /** + * function调用的一些历史示例,说明: + * (1)可以提供正例(正常触发)和反例(无需触发)的example + * ·正例:从历史请求数据中获取 + * ·反例: + * 当role = user,不会触发请求的query + * 当role = assistant,有固定的格式。function_call的name为空,arguments是空对象:"{}",thought可以填固定的:"我不需要调用任何工具" + * (2)兼容之前的 List(example) 格式 + */ + private String examples; + } + +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/exception/YiYanApiException.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/exception/YiYanApiException.java new file mode 100644 index 000000000..dbb8ed93a --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/chatyiyan/exception/YiYanApiException.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.framework.ai.chatyiyan.exception; + +/** + * 一言 api 调用异常 + */ +public class YiYanApiException extends RuntimeException { + + public YiYanApiException(String message) { + super(message); + } + + public YiYanApiException(String message, Throwable cause) { + super(message, cause); + } + +} \ No newline at end of file