Commit ffe4afaaafea38e1c4ca54f1f64025b63fd23ac7
1 parent
25a0fe90
【功能新增】AI:新增 function call 示例,完成所有模型的测试 = = 累
Showing
8 changed files
with
186 additions
and
135 deletions
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
| @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | ||
| 7 | import cn.iocoder.yudao.framework.ai.core.util.AiUtils; | 7 | import cn.iocoder.yudao.framework.ai.core.util.AiUtils; |
| 8 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 8 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 9 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 9 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 10 | +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; | ||
| 10 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 11 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 11 | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | 12 | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; |
| 12 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; | 13 | import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; |
| @@ -238,7 +239,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { | @@ -238,7 +239,8 @@ public class AiChatMessageServiceImpl implements AiChatMessageService { | ||
| 238 | // 2. 构建 ChatOptions 对象 | 239 | // 2. 构建 ChatOptions 对象 |
| 239 | AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); | 240 | AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform()); |
| 240 | ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(), | 241 | ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(), |
| 241 | - conversation.getTemperature(), conversation.getMaxTokens()); | 242 | + conversation.getTemperature(), conversation.getMaxTokens(), |
| 243 | + SetUtils.asSet("directory_list", "weather_query")); | ||
| 242 | return new Prompt(chatMessages, chatOptions); | 244 | return new Prompt(chatMessages, chatOptions); |
| 243 | } | 245 | } |
| 244 | 246 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirTool.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.service.tool; | ||
| 2 | - | ||
| 3 | -import cn.hutool.core.date.LocalDateTimeUtil; | ||
| 4 | -import cn.hutool.core.io.FileUtil; | ||
| 5 | -import cn.hutool.core.util.ArrayUtil; | ||
| 6 | -import cn.hutool.core.util.StrUtil; | ||
| 7 | -import lombok.AllArgsConstructor; | ||
| 8 | -import lombok.Data; | ||
| 9 | -import lombok.NoArgsConstructor; | ||
| 10 | -import org.springframework.ai.tool.annotation.Tool; | ||
| 11 | -import org.springframework.ai.tool.annotation.ToolParam; | ||
| 12 | -import org.springframework.stereotype.Component; | ||
| 13 | - | ||
| 14 | -import java.io.File; | ||
| 15 | -import java.nio.file.Path; | ||
| 16 | -import java.nio.file.Paths; | ||
| 17 | -import java.util.Arrays; | ||
| 18 | -import java.util.Collections; | ||
| 19 | -import java.util.List; | ||
| 20 | - | ||
| 21 | -import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN; | ||
| 22 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | ||
| 23 | - | ||
| 24 | -/** | ||
| 25 | - * 目录内容列表工具:列出指定目录的内容 | ||
| 26 | - * | ||
| 27 | - * @author 芋道源码 | ||
| 28 | - */ | ||
| 29 | -@Component | ||
| 30 | -public class ListDirTool { | ||
| 31 | - | ||
| 32 | - /** | ||
| 33 | - * 列出指定目录的内容 | ||
| 34 | - * | ||
| 35 | - * @param relativePath 要列出内容的目录路径,相对于工作区根目录 | ||
| 36 | - * @return 目录内容列表 | ||
| 37 | - */ | ||
| 38 | - @Tool(name = "listDir", description = "列出指定目录的内容") | ||
| 39 | - public Response listDir(@ToolParam(description = "要列出内容的目录路径,相对于工作区根目录") String relativePath) { | ||
| 40 | - // 校验目录存在 | ||
| 41 | - String path = StrUtil.blankToDefault(relativePath, "."); | ||
| 42 | - Path dirPath = Paths.get(path); | ||
| 43 | - if (!FileUtil.exist(dirPath.toString()) || !FileUtil.isDirectory(dirPath.toString())) { | ||
| 44 | - return new Response(Collections.emptyList()); | ||
| 45 | - } | ||
| 46 | - // 列出目录内容 | ||
| 47 | - File[] files = dirPath.toFile().listFiles(); | ||
| 48 | - if (ArrayUtil.isEmpty(files)) { | ||
| 49 | - return new Response(Collections.emptyList()); | ||
| 50 | - } | ||
| 51 | - return new Response(convertList(Arrays.asList(files), file -> new Response.File() | ||
| 52 | - .setDirectory(file.isDirectory()).setName(file.getName()) | ||
| 53 | - .setSize(file.isFile() ? FileUtil.readableFileSize(file.length()) : null) | ||
| 54 | - .setLastModified( | ||
| 55 | - LocalDateTimeUtil.format(LocalDateTimeUtil.of(file.lastModified()), NORM_DATETIME_PATTERN)))); | ||
| 56 | - } | ||
| 57 | - | ||
| 58 | - @Data | ||
| 59 | - @AllArgsConstructor | ||
| 60 | - @NoArgsConstructor | ||
| 61 | - public static class Response { | ||
| 62 | - | ||
| 63 | - /** | ||
| 64 | - * 目录内容列表 | ||
| 65 | - */ | ||
| 66 | - private List<File> files; | ||
| 67 | - | ||
| 68 | - @Data | ||
| 69 | - public static class File { | ||
| 70 | - | ||
| 71 | - /** | ||
| 72 | - * 是否为目录 | ||
| 73 | - */ | ||
| 74 | - private Boolean directory; | ||
| 75 | - | ||
| 76 | - /** | ||
| 77 | - * 名称 | ||
| 78 | - */ | ||
| 79 | - private String name; | ||
| 80 | - | ||
| 81 | - /** | ||
| 82 | - * 大小,仅对文件有效 | ||
| 83 | - */ | ||
| 84 | - private String size; | ||
| 85 | - | ||
| 86 | - /** | ||
| 87 | - * 最后修改时间 | ||
| 88 | - */ | ||
| 89 | - private String lastModified; | ||
| 90 | - | ||
| 91 | - } | ||
| 92 | - | ||
| 93 | - } | ||
| 94 | - | ||
| 95 | -} | ||
| 96 | \ No newline at end of file | 0 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/ListDirToolB.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/function/DirectoryListToolFunction.java
| 1 | -package cn.iocoder.yudao.module.ai.service.tool; | 1 | +package cn.iocoder.yudao.module.ai.service.tool.function; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.date.LocalDateTimeUtil; | 3 | import cn.hutool.core.date.LocalDateTimeUtil; |
| 4 | import cn.hutool.core.io.FileUtil; | 4 | import cn.hutool.core.io.FileUtil; |
| 5 | import cn.hutool.core.util.ArrayUtil; | 5 | import cn.hutool.core.util.ArrayUtil; |
| 6 | import cn.hutool.core.util.StrUtil; | 6 | import cn.hutool.core.util.StrUtil; |
| 7 | import com.fasterxml.jackson.annotation.JsonClassDescription; | 7 | import com.fasterxml.jackson.annotation.JsonClassDescription; |
| 8 | +import com.fasterxml.jackson.annotation.JsonProperty; | ||
| 8 | import com.fasterxml.jackson.annotation.JsonPropertyDescription; | 9 | import com.fasterxml.jackson.annotation.JsonPropertyDescription; |
| 9 | import lombok.AllArgsConstructor; | 10 | import lombok.AllArgsConstructor; |
| 10 | import lombok.Data; | 11 | import lombok.Data; |
| @@ -12,8 +13,6 @@ import lombok.NoArgsConstructor; | @@ -12,8 +13,6 @@ import lombok.NoArgsConstructor; | ||
| 12 | import org.springframework.stereotype.Component; | 13 | import org.springframework.stereotype.Component; |
| 13 | 14 | ||
| 14 | import java.io.File; | 15 | import java.io.File; |
| 15 | -import java.nio.file.Path; | ||
| 16 | -import java.nio.file.Paths; | ||
| 17 | import java.util.Arrays; | 16 | import java.util.Arrays; |
| 18 | import java.util.Collections; | 17 | import java.util.Collections; |
| 19 | import java.util.List; | 18 | import java.util.List; |
| @@ -23,21 +22,22 @@ import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN; | @@ -23,21 +22,22 @@ import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN; | ||
| 23 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | 22 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| 24 | 23 | ||
| 25 | /** | 24 | /** |
| 26 | - * 目录内容列表工具:列出指定目录的内容 | 25 | + * 工具:列出指定目录的文件列表 |
| 27 | * | 26 | * |
| 28 | * @author 芋道源码 | 27 | * @author 芋道源码 |
| 29 | */ | 28 | */ |
| 30 | -@Component("listDir") | ||
| 31 | -public class ListDirToolB implements Function<ListDirToolB.Request, ListDirToolB.Response> { | 29 | +@Component("directory_list") |
| 30 | +public class DirectoryListToolFunction implements Function<DirectoryListToolFunction.Request, DirectoryListToolFunction.Response> { | ||
| 32 | 31 | ||
| 33 | @Data | 32 | @Data |
| 34 | - @JsonClassDescription("列出指定目录的内容") | 33 | + @JsonClassDescription("列出指定目录的文件列表") |
| 35 | public static class Request { | 34 | public static class Request { |
| 36 | 35 | ||
| 37 | /** | 36 | /** |
| 38 | - * 要列出内容的目录路径 | 37 | + * 目录路径 |
| 39 | */ | 38 | */ |
| 40 | - @JsonPropertyDescription("要列出内容的目录路径,例如说:/Users/yunai") | 39 | + @JsonProperty(required = true, value = "path") |
| 40 | + @JsonPropertyDescription("目录路径,例如说:/Users/yunai") | ||
| 41 | private String path; | 41 | private String path; |
| 42 | 42 | ||
| 43 | } | 43 | } |
| @@ -48,7 +48,7 @@ public class ListDirToolB implements Function<ListDirToolB.Request, ListDirToolB | @@ -48,7 +48,7 @@ public class ListDirToolB implements Function<ListDirToolB.Request, ListDirToolB | ||
| 48 | public static class Response { | 48 | public static class Response { |
| 49 | 49 | ||
| 50 | /** | 50 | /** |
| 51 | - * 目录内容列表 | 51 | + * 文件列表 |
| 52 | */ | 52 | */ |
| 53 | private List<File> files; | 53 | private List<File> files; |
| 54 | 54 | ||
| @@ -82,13 +82,12 @@ public class ListDirToolB implements Function<ListDirToolB.Request, ListDirToolB | @@ -82,13 +82,12 @@ public class ListDirToolB implements Function<ListDirToolB.Request, ListDirToolB | ||
| 82 | @Override | 82 | @Override |
| 83 | public Response apply(Request request) { | 83 | public Response apply(Request request) { |
| 84 | // 校验目录存在 | 84 | // 校验目录存在 |
| 85 | - String path = StrUtil.blankToDefault(request.getPath(), "."); | ||
| 86 | - Path dirPath = Paths.get(path); | ||
| 87 | - if (!FileUtil.exist(dirPath.toString()) || !FileUtil.isDirectory(dirPath.toString())) { | 85 | + String path = StrUtil.blankToDefault(request.getPath(), "/"); |
| 86 | + if (!FileUtil.exist(path) || !FileUtil.isDirectory(path)) { | ||
| 88 | return new Response(Collections.emptyList()); | 87 | return new Response(Collections.emptyList()); |
| 89 | } | 88 | } |
| 90 | - // 列出目录内容 | ||
| 91 | - File[] files = dirPath.toFile().listFiles(); | 89 | + // 列出目录 |
| 90 | + File[] files = FileUtil.ls(path); | ||
| 92 | if (ArrayUtil.isEmpty(files)) { | 91 | if (ArrayUtil.isEmpty(files)) { |
| 93 | return new Response(Collections.emptyList()); | 92 | return new Response(Collections.emptyList()); |
| 94 | } | 93 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/function/WeatherQueryToolFunction.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.service.tool.function; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.date.LocalDateTimeUtil; | ||
| 4 | +import cn.hutool.core.util.RandomUtil; | ||
| 5 | +import cn.hutool.core.util.StrUtil; | ||
| 6 | +import com.fasterxml.jackson.annotation.JsonClassDescription; | ||
| 7 | +import com.fasterxml.jackson.annotation.JsonProperty; | ||
| 8 | +import com.fasterxml.jackson.annotation.JsonPropertyDescription; | ||
| 9 | +import lombok.AllArgsConstructor; | ||
| 10 | +import lombok.Data; | ||
| 11 | +import lombok.NoArgsConstructor; | ||
| 12 | +import org.springframework.stereotype.Component; | ||
| 13 | + | ||
| 14 | +import java.time.LocalDateTime; | ||
| 15 | +import java.util.function.Function; | ||
| 16 | + | ||
| 17 | +import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN; | ||
| 18 | + | ||
| 19 | +/** | ||
| 20 | + * 工具:查询指定城市的天气信息 | ||
| 21 | + * | ||
| 22 | + * @author 芋道源码 | ||
| 23 | + */ | ||
| 24 | +@Component("weather_query") | ||
| 25 | +public class WeatherQueryToolFunction | ||
| 26 | + implements Function<WeatherQueryToolFunction.Request, WeatherQueryToolFunction.Response> { | ||
| 27 | + | ||
| 28 | + private static final String[] WEATHER_CONDITIONS = { "晴朗", "多云", "阴天", "小雨", "大雨", "雷雨", "小雪", "大雪" }; | ||
| 29 | + | ||
| 30 | + @Data | ||
| 31 | + @JsonClassDescription("查询指定城市的天气信息") | ||
| 32 | + public static class Request { | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 城市名称 | ||
| 36 | + */ | ||
| 37 | + @JsonProperty(required = true, value = "city") | ||
| 38 | + @JsonPropertyDescription("城市名称,例如:北京、上海、广州") | ||
| 39 | + private String city; | ||
| 40 | + | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + @Data | ||
| 44 | + @AllArgsConstructor | ||
| 45 | + @NoArgsConstructor | ||
| 46 | + public static class Response { | ||
| 47 | + | ||
| 48 | + /** | ||
| 49 | + * 城市名称 | ||
| 50 | + */ | ||
| 51 | + private String city; | ||
| 52 | + | ||
| 53 | + /** | ||
| 54 | + * 天气信息 | ||
| 55 | + */ | ||
| 56 | + private WeatherInfo weatherInfo; | ||
| 57 | + | ||
| 58 | + @Data | ||
| 59 | + @AllArgsConstructor | ||
| 60 | + @NoArgsConstructor | ||
| 61 | + public static class WeatherInfo { | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 温度(摄氏度) | ||
| 65 | + */ | ||
| 66 | + private Integer temperature; | ||
| 67 | + | ||
| 68 | + /** | ||
| 69 | + * 天气状况 | ||
| 70 | + */ | ||
| 71 | + private String condition; | ||
| 72 | + | ||
| 73 | + /** | ||
| 74 | + * 湿度百分比 | ||
| 75 | + */ | ||
| 76 | + private Integer humidity; | ||
| 77 | + | ||
| 78 | + /** | ||
| 79 | + * 风速(km/h) | ||
| 80 | + */ | ||
| 81 | + private Integer windSpeed; | ||
| 82 | + | ||
| 83 | + /** | ||
| 84 | + * 查询时间 | ||
| 85 | + */ | ||
| 86 | + private String queryTime; | ||
| 87 | + | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + @Override | ||
| 93 | + public Response apply(Request request) { | ||
| 94 | + // 检查城市名称是否为空 | ||
| 95 | + if (StrUtil.isBlank(request.getCity())) { | ||
| 96 | + return new Response("未知城市", null); | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + // 获取天气数据 | ||
| 100 | + String city = request.getCity(); | ||
| 101 | + Response.WeatherInfo weatherInfo = generateMockWeatherInfo(); | ||
| 102 | + return new Response(city, weatherInfo); | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + /** | ||
| 106 | + * 生成模拟的天气数据 | ||
| 107 | + * 在实际应用中,应替换为真实 API 调用 | ||
| 108 | + */ | ||
| 109 | + private Response.WeatherInfo generateMockWeatherInfo() { | ||
| 110 | + int temperature = RandomUtil.randomInt(-5, 30); | ||
| 111 | + int humidity = RandomUtil.randomInt(1, 100); | ||
| 112 | + int windSpeed = RandomUtil.randomInt(1, 30); | ||
| 113 | + String condition = RandomUtil.randomEle(WEATHER_CONDITIONS); | ||
| 114 | + return new Response.WeatherInfo(temperature, condition, humidity, windSpeed, | ||
| 115 | + LocalDateTimeUtil.format(LocalDateTime.now(), NORM_DATETIME_PATTERN)); | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | +} | ||
| 0 | \ No newline at end of file | 119 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/tool/package-info.java
0 → 100644
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java
| 1 | package cn.iocoder.yudao.framework.ai.config; | 1 | package cn.iocoder.yudao.framework.ai.config; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.util.StrUtil; | 3 | import cn.hutool.core.util.StrUtil; |
| 4 | +import cn.hutool.extra.spring.SpringUtil; | ||
| 4 | import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory; | 5 | import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory; |
| 5 | import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactoryImpl; | 6 | import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactoryImpl; |
| 6 | import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; | 7 | import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel; |
| @@ -17,6 +18,7 @@ import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStore | @@ -17,6 +18,7 @@ import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStore | ||
| 17 | import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties; | 18 | import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties; |
| 18 | import org.springframework.ai.embedding.BatchingStrategy; | 19 | import org.springframework.ai.embedding.BatchingStrategy; |
| 19 | import org.springframework.ai.embedding.TokenCountBatchingStrategy; | 20 | import org.springframework.ai.embedding.TokenCountBatchingStrategy; |
| 21 | +import org.springframework.ai.model.tool.ToolCallingManager; | ||
| 20 | import org.springframework.ai.openai.OpenAiChatModel; | 22 | import org.springframework.ai.openai.OpenAiChatModel; |
| 21 | import org.springframework.ai.openai.OpenAiChatOptions; | 23 | import org.springframework.ai.openai.OpenAiChatOptions; |
| 22 | import org.springframework.ai.openai.api.OpenAiApi; | 24 | import org.springframework.ai.openai.api.OpenAiApi; |
| @@ -70,6 +72,7 @@ public class YudaoAiAutoConfiguration { | @@ -70,6 +72,7 @@ public class YudaoAiAutoConfiguration { | ||
| 70 | .maxTokens(properties.getMaxTokens()) | 72 | .maxTokens(properties.getMaxTokens()) |
| 71 | .topP(properties.getTopP()) | 73 | .topP(properties.getTopP()) |
| 72 | .build()) | 74 | .build()) |
| 75 | + .toolCallingManager(getToolCallingManager()) | ||
| 73 | .build(); | 76 | .build(); |
| 74 | return new DeepSeekChatModel(openAiChatModel); | 77 | return new DeepSeekChatModel(openAiChatModel); |
| 75 | } | 78 | } |
| @@ -96,6 +99,7 @@ public class YudaoAiAutoConfiguration { | @@ -96,6 +99,7 @@ public class YudaoAiAutoConfiguration { | ||
| 96 | .maxTokens(properties.getMaxTokens()) | 99 | .maxTokens(properties.getMaxTokens()) |
| 97 | .topP(properties.getTopP()) | 100 | .topP(properties.getTopP()) |
| 98 | .build()) | 101 | .build()) |
| 102 | + .toolCallingManager(getToolCallingManager()) | ||
| 99 | .build(); | 103 | .build(); |
| 100 | return new DouBaoChatModel(openAiChatModel); | 104 | return new DouBaoChatModel(openAiChatModel); |
| 101 | } | 105 | } |
| @@ -122,6 +126,7 @@ public class YudaoAiAutoConfiguration { | @@ -122,6 +126,7 @@ public class YudaoAiAutoConfiguration { | ||
| 122 | .maxTokens(properties.getMaxTokens()) | 126 | .maxTokens(properties.getMaxTokens()) |
| 123 | .topP(properties.getTopP()) | 127 | .topP(properties.getTopP()) |
| 124 | .build()) | 128 | .build()) |
| 129 | + .toolCallingManager(getToolCallingManager()) | ||
| 125 | .build(); | 130 | .build(); |
| 126 | return new SiliconFlowChatModel(openAiChatModel); | 131 | return new SiliconFlowChatModel(openAiChatModel); |
| 127 | } | 132 | } |
| @@ -155,6 +160,7 @@ public class YudaoAiAutoConfiguration { | @@ -155,6 +160,7 @@ public class YudaoAiAutoConfiguration { | ||
| 155 | .maxTokens(properties.getMaxTokens()) | 160 | .maxTokens(properties.getMaxTokens()) |
| 156 | .topP(properties.getTopP()) | 161 | .topP(properties.getTopP()) |
| 157 | .build()) | 162 | .build()) |
| 163 | + .toolCallingManager(getToolCallingManager()) | ||
| 158 | .build(); | 164 | .build(); |
| 159 | return new HunYuanChatModel(openAiChatModel); | 165 | return new HunYuanChatModel(openAiChatModel); |
| 160 | } | 166 | } |
| @@ -181,6 +187,7 @@ public class YudaoAiAutoConfiguration { | @@ -181,6 +187,7 @@ public class YudaoAiAutoConfiguration { | ||
| 181 | .maxTokens(properties.getMaxTokens()) | 187 | .maxTokens(properties.getMaxTokens()) |
| 182 | .topP(properties.getTopP()) | 188 | .topP(properties.getTopP()) |
| 183 | .build()) | 189 | .build()) |
| 190 | + .toolCallingManager(getToolCallingManager()) | ||
| 184 | .build(); | 191 | .build(); |
| 185 | return new XingHuoChatModel(openAiChatModel); | 192 | return new XingHuoChatModel(openAiChatModel); |
| 186 | } | 193 | } |
| @@ -210,4 +217,8 @@ public class YudaoAiAutoConfiguration { | @@ -210,4 +217,8 @@ public class YudaoAiAutoConfiguration { | ||
| 210 | return new TokenCountBatchingStrategy(); | 217 | return new TokenCountBatchingStrategy(); |
| 211 | } | 218 | } |
| 212 | 219 | ||
| 220 | + private static ToolCallingManager getToolCallingManager() { | ||
| 221 | + return SpringUtil.getBean(ToolCallingManager.class); | ||
| 222 | + } | ||
| 223 | + | ||
| 213 | } | 224 | } |
| 214 | \ No newline at end of file | 225 | \ No newline at end of file |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
| @@ -23,6 +23,7 @@ import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAutoConfiguration; | @@ -23,6 +23,7 @@ import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAutoConfiguration; | ||
| 23 | import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; | 23 | import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; |
| 24 | import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi; | 24 | import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi; |
| 25 | import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; | 25 | import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; |
| 26 | +import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; | ||
| 26 | import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel; | 27 | import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel; |
| 27 | import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingOptions; | 28 | import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingOptions; |
| 28 | import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; | 29 | import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; |
| @@ -58,11 +59,14 @@ import org.springframework.ai.embedding.BatchingStrategy; | @@ -58,11 +59,14 @@ import org.springframework.ai.embedding.BatchingStrategy; | ||
| 58 | import org.springframework.ai.embedding.EmbeddingModel; | 59 | import org.springframework.ai.embedding.EmbeddingModel; |
| 59 | import org.springframework.ai.image.ImageModel; | 60 | import org.springframework.ai.image.ImageModel; |
| 60 | import org.springframework.ai.minimax.MiniMaxChatModel; | 61 | import org.springframework.ai.minimax.MiniMaxChatModel; |
| 62 | +import org.springframework.ai.minimax.MiniMaxChatOptions; | ||
| 61 | import org.springframework.ai.minimax.MiniMaxEmbeddingModel; | 63 | import org.springframework.ai.minimax.MiniMaxEmbeddingModel; |
| 62 | import org.springframework.ai.minimax.MiniMaxEmbeddingOptions; | 64 | import org.springframework.ai.minimax.MiniMaxEmbeddingOptions; |
| 63 | import org.springframework.ai.minimax.api.MiniMaxApi; | 65 | import org.springframework.ai.minimax.api.MiniMaxApi; |
| 66 | +import org.springframework.ai.model.function.FunctionCallbackResolver; | ||
| 64 | import org.springframework.ai.model.tool.ToolCallingManager; | 67 | import org.springframework.ai.model.tool.ToolCallingManager; |
| 65 | import org.springframework.ai.moonshot.MoonshotChatModel; | 68 | import org.springframework.ai.moonshot.MoonshotChatModel; |
| 69 | +import org.springframework.ai.moonshot.MoonshotChatOptions; | ||
| 66 | import org.springframework.ai.moonshot.api.MoonshotApi; | 70 | import org.springframework.ai.moonshot.api.MoonshotApi; |
| 67 | import org.springframework.ai.ollama.OllamaChatModel; | 71 | import org.springframework.ai.ollama.OllamaChatModel; |
| 68 | import org.springframework.ai.ollama.OllamaEmbeddingModel; | 72 | import org.springframework.ai.ollama.OllamaEmbeddingModel; |
| @@ -90,10 +94,7 @@ import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservat | @@ -90,10 +94,7 @@ import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservat | ||
| 90 | import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention; | 94 | import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention; |
| 91 | import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; | 95 | import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; |
| 92 | import org.springframework.ai.vectorstore.redis.RedisVectorStore; | 96 | import org.springframework.ai.vectorstore.redis.RedisVectorStore; |
| 93 | -import org.springframework.ai.zhipuai.ZhiPuAiChatModel; | ||
| 94 | -import org.springframework.ai.zhipuai.ZhiPuAiEmbeddingModel; | ||
| 95 | -import org.springframework.ai.zhipuai.ZhiPuAiEmbeddingOptions; | ||
| 96 | -import org.springframework.ai.zhipuai.ZhiPuAiImageModel; | 97 | +import org.springframework.ai.zhipuai.*; |
| 97 | import org.springframework.ai.zhipuai.api.ZhiPuAiApi; | 98 | import org.springframework.ai.zhipuai.api.ZhiPuAiApi; |
| 98 | import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi; | 99 | import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi; |
| 99 | import org.springframework.beans.BeansException; | 100 | import org.springframework.beans.BeansException; |
| @@ -110,6 +111,7 @@ import java.util.Timer; | @@ -110,6 +111,7 @@ import java.util.Timer; | ||
| 110 | import java.util.TimerTask; | 111 | import java.util.TimerTask; |
| 111 | 112 | ||
| 112 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | 113 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| 114 | +import static org.springframework.ai.retry.RetryUtils.DEFAULT_RETRY_TEMPLATE; | ||
| 113 | 115 | ||
| 114 | /** | 116 | /** |
| 115 | * AI Model 模型工厂的实现类 | 117 | * AI Model 模型工厂的实现类 |
| @@ -308,7 +310,9 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -308,7 +310,9 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 308 | */ | 310 | */ |
| 309 | private static DashScopeChatModel buildTongYiChatModel(String key) { | 311 | private static DashScopeChatModel buildTongYiChatModel(String key) { |
| 310 | DashScopeApi dashScopeApi = new DashScopeApi(key); | 312 | DashScopeApi dashScopeApi = new DashScopeApi(key); |
| 311 | - return new DashScopeChatModel(dashScopeApi); | 313 | + DashScopeChatOptions options = DashScopeChatOptions.builder().withModel(DashScopeApi.DEFAULT_CHAT_MODEL) |
| 314 | + .withTemperature(0.7).build(); | ||
| 315 | + return new DashScopeChatModel(dashScopeApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); | ||
| 312 | } | 316 | } |
| 313 | 317 | ||
| 314 | /** | 318 | /** |
| @@ -385,7 +389,8 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -385,7 +389,8 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 385 | private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) { | 389 | private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) { |
| 386 | ZhiPuAiApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiApi(apiKey) | 390 | ZhiPuAiApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiApi(apiKey) |
| 387 | : new ZhiPuAiApi(url, apiKey); | 391 | : new ZhiPuAiApi(url, apiKey); |
| 388 | - return new ZhiPuAiChatModel(zhiPuAiApi); | 392 | + ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().model(ZhiPuAiApi.DEFAULT_CHAT_MODEL).temperature(0.7).build(); |
| 393 | + return new ZhiPuAiChatModel(zhiPuAiApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); | ||
| 389 | } | 394 | } |
| 390 | 395 | ||
| 391 | /** | 396 | /** |
| @@ -403,7 +408,8 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -403,7 +408,8 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 403 | private MiniMaxChatModel buildMiniMaxChatModel(String apiKey, String url) { | 408 | private MiniMaxChatModel buildMiniMaxChatModel(String apiKey, String url) { |
| 404 | MiniMaxApi miniMaxApi = StrUtil.isEmpty(url) ? new MiniMaxApi(apiKey) | 409 | MiniMaxApi miniMaxApi = StrUtil.isEmpty(url) ? new MiniMaxApi(apiKey) |
| 405 | : new MiniMaxApi(url, apiKey); | 410 | : new MiniMaxApi(url, apiKey); |
| 406 | - return new MiniMaxChatModel(miniMaxApi); | 411 | + MiniMaxChatOptions options = MiniMaxChatOptions.builder().model(MiniMaxApi.DEFAULT_CHAT_MODEL).temperature(0.7).build(); |
| 412 | + return new MiniMaxChatModel(miniMaxApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); | ||
| 407 | } | 413 | } |
| 408 | 414 | ||
| 409 | /** | 415 | /** |
| @@ -412,7 +418,8 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -412,7 +418,8 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 412 | private MoonshotChatModel buildMoonshotChatModel(String apiKey, String url) { | 418 | private MoonshotChatModel buildMoonshotChatModel(String apiKey, String url) { |
| 413 | MoonshotApi moonshotApi = StrUtil.isEmpty(url)? new MoonshotApi(apiKey) | 419 | MoonshotApi moonshotApi = StrUtil.isEmpty(url)? new MoonshotApi(apiKey) |
| 414 | : new MoonshotApi(url, apiKey); | 420 | : new MoonshotApi(url, apiKey); |
| 415 | - return new MoonshotChatModel(moonshotApi); | 421 | + MoonshotChatOptions options = MoonshotChatOptions.builder().model(MoonshotApi.DEFAULT_CHAT_MODEL).build(); |
| 422 | + return new MoonshotChatModel(moonshotApi, options, getFunctionCallbackResolver(), DEFAULT_RETRY_TEMPLATE); | ||
| 416 | } | 423 | } |
| 417 | 424 | ||
| 418 | /** | 425 | /** |
| @@ -449,7 +456,7 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -449,7 +456,7 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 449 | // 获取 AzureOpenAiChatProperties 对象 | 456 | // 获取 AzureOpenAiChatProperties 对象 |
| 450 | AzureOpenAiChatProperties chatProperties = SpringUtil.getBean(AzureOpenAiChatProperties.class); | 457 | AzureOpenAiChatProperties chatProperties = SpringUtil.getBean(AzureOpenAiChatProperties.class); |
| 451 | return azureOpenAiAutoConfiguration.azureOpenAiChatModel(openAIClient, chatProperties, | 458 | return azureOpenAiAutoConfiguration.azureOpenAiChatModel(openAIClient, chatProperties, |
| 452 | - null, null, null); | 459 | + getToolCallingManager(), null, null); |
| 453 | } | 460 | } |
| 454 | 461 | ||
| 455 | /** | 462 | /** |
| @@ -704,4 +711,8 @@ public class AiModelFactoryImpl implements AiModelFactory { | @@ -704,4 +711,8 @@ public class AiModelFactoryImpl implements AiModelFactory { | ||
| 704 | return SpringUtil.getBean(ToolCallingManager.class); | 711 | return SpringUtil.getBean(ToolCallingManager.class); |
| 705 | } | 712 | } |
| 706 | 713 | ||
| 714 | + private static FunctionCallbackResolver getFunctionCallbackResolver() { | ||
| 715 | + return SpringUtil.getBean(FunctionCallbackResolver.class); | ||
| 716 | + } | ||
| 717 | + | ||
| 707 | } | 718 | } |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/util/AiUtils.java
| @@ -13,6 +13,8 @@ import org.springframework.ai.openai.OpenAiChatOptions; | @@ -13,6 +13,8 @@ import org.springframework.ai.openai.OpenAiChatOptions; | ||
| 13 | import org.springframework.ai.qianfan.QianFanChatOptions; | 13 | import org.springframework.ai.qianfan.QianFanChatOptions; |
| 14 | import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; | 14 | import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; |
| 15 | 15 | ||
| 16 | +import java.util.Set; | ||
| 17 | + | ||
| 16 | /** | 18 | /** |
| 17 | * Spring AI 工具类 | 19 | * Spring AI 工具类 |
| 18 | * | 20 | * |
| @@ -21,22 +23,27 @@ import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; | @@ -21,22 +23,27 @@ import org.springframework.ai.zhipuai.ZhiPuAiChatOptions; | ||
| 21 | public class AiUtils { | 23 | public class AiUtils { |
| 22 | 24 | ||
| 23 | public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) { | 25 | public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) { |
| 26 | + return buildChatOptions(platform, model, temperature, maxTokens, null); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens, | ||
| 30 | + Set<String> toolNames) { | ||
| 24 | // noinspection EnhancedSwitchMigration | 31 | // noinspection EnhancedSwitchMigration |
| 25 | switch (platform) { | 32 | switch (platform) { |
| 26 | case TONG_YI: | 33 | case TONG_YI: |
| 27 | - // TODO functions | ||
| 28 | - return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens).build(); | 34 | + return DashScopeChatOptions.builder().withModel(model).withTemperature(temperature).withMaxToken(maxTokens) |
| 35 | + .withFunctions(toolNames).build(); | ||
| 29 | case YI_YAN: | 36 | case YI_YAN: |
| 30 | return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); | 37 | return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); |
| 31 | case ZHI_PU: | 38 | case ZHI_PU: |
| 32 | - // TODO functions | ||
| 33 | - return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); | 39 | + return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) |
| 40 | + .functions(toolNames).build(); | ||
| 34 | case MINI_MAX: | 41 | case MINI_MAX: |
| 35 | - // TODO functions | ||
| 36 | - return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); | 42 | + return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) |
| 43 | + .functions(toolNames).build(); | ||
| 37 | case MOONSHOT: | 44 | case MOONSHOT: |
| 38 | - // TODO functions | ||
| 39 | - return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build(); | 45 | + return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) |
| 46 | + .functions(toolNames).build(); | ||
| 40 | case OPENAI: | 47 | case OPENAI: |
| 41 | case DEEP_SEEK: // 复用 OpenAI 客户端 | 48 | case DEEP_SEEK: // 复用 OpenAI 客户端 |
| 42 | case DOU_BAO: // 复用 OpenAI 客户端 | 49 | case DOU_BAO: // 复用 OpenAI 客户端 |
| @@ -44,17 +51,14 @@ public class AiUtils { | @@ -44,17 +51,14 @@ public class AiUtils { | ||
| 44 | case XING_HUO: // 复用 OpenAI 客户端 | 51 | case XING_HUO: // 复用 OpenAI 客户端 |
| 45 | case SILICON_FLOW: // 复用 OpenAI 客户端 | 52 | case SILICON_FLOW: // 复用 OpenAI 客户端 |
| 46 | return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) | 53 | return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens) |
| 47 | -// .toolNames() TODO | ||
| 48 | - .toolNames("listDir") | ||
| 49 | - .build(); | 54 | + .toolNames(toolNames).build(); |
| 50 | case AZURE_OPENAI: | 55 | case AZURE_OPENAI: |
| 51 | // TODO 芋艿:貌似没 model 字段???! | 56 | // TODO 芋艿:貌似没 model 字段???! |
| 52 | - // TODO 芋艿:.toolNames() TODO | ||
| 53 | - return AzureOpenAiChatOptions.builder().deploymentName(model).temperature(temperature).maxTokens(maxTokens).build(); | 57 | + return AzureOpenAiChatOptions.builder().deploymentName(model).temperature(temperature).maxTokens(maxTokens) |
| 58 | + .toolNames(toolNames).build(); | ||
| 54 | case OLLAMA: | 59 | case OLLAMA: |
| 55 | - // TODO 芋艿:.toolNames() TODO | ||
| 56 | return OllamaOptions.builder().model(model).temperature(temperature).numPredict(maxTokens) | 60 | return OllamaOptions.builder().model(model).temperature(temperature).numPredict(maxTokens) |
| 57 | - .toolNames("listDir").build(); | 61 | + .toolNames(toolNames).build(); |
| 58 | default: | 62 | default: |
| 59 | throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); | 63 | throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform)); |
| 60 | } | 64 | } |