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