Spring AI Alibaba - RAG与ToolCalling
RAG检索增强生成
RAG:训练大模型特定领域知识或专有数据。
RAG:一种从数据中查找相关信息,并在将提示发送给LLM之前将其注入到提示中的方法,这样,LLM就能获得相关信息,并基于这些信息回答,降低幻觉概率。
检索出来的信息使大模型回答更准确。
先查资料后回答的机制。
RAG流程分为2个阶段:索引 index 和检索 Retrieval
RAG 通过引入外部知识源来增强LLM的输出能力。
传统的LLM通常基于其训练数据生成相应,但这些数据可能过时或不够全面。
RAG允许模型在生成答案之前,从特定的知识库中检索相关信息,从而提供更准确的回答。
LLM的缺陷
LLM的知识不是实时的,不具备知识更新。
LLM不具备私有的业务知识
LLM有时会在回答中生成看似合理但实际错误的信息。
RAG索引

RAG检索

代码演示
根据给出的错误编码给出异常解释
-
pom依赖如下
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter-dashscope</artifactId> </dependency> <!-- Redis向量数据库--> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-vector-store-redis</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--hutool--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.22</version> </dependency> </dependencies> -
配置文件如下
server.port=8012 # ????????? server.servlet.encoding.charset=UTF-8 server.servlet.encoding.enabled=true server.servlet.encoding.force=true spring.application.name=saa12-RAG4AI0px #springai ????config spring.ai.dashscope.api-key=${aliQwen-apikey} spring.ai.dashscope.chat.options.model=deepseek-r1 spring.ai.dashscope.embedding.options.model=text-embedding-v3 # Redis Stack spring.data.redis.host=192.168.220.128 spring.data.redis.port=6379 spring.data.redis.password=redis-stack spring.data.redis.username=default spring.ai.vectorstore.redis.initialize-schema=true spring.ai.vectorstore.redis.index-name=rag-index spring.ai.vectorstore.redis.prefix=rag-prefix -
在resources文件夹下添加ops.txt文件,内容如下:
00000 系统OK正确执行后的返回 A0001 用户端错误一级宏观错误码 A0100 用户注册错误二级宏观错误码 B1111 支付接口超时 C2222 Kafka消息解压严重 -
添加配置类
@Configuration public class InitVectorDatabaseConfig { @Autowired private VectorStore vectorStore; @Autowired private RedisTemplate<String,String> redisTemplate; @Value("classpath:ops.txt") private Resource opsFile; @PostConstruct public void init() { //1 读取文件 TextReader textReader = new TextReader(opsFile); textReader.setCharset(Charset.defaultCharset()); //2 文件转换为向量(开启分词) List<Document> list = new TokenTextSplitter().transform(textReader.read()); //3 写入向量数据库RedisStack //vectorStore.add(list); // 解决上面第3步,向量数据重复问题,使用redis setnx命令处理 //4 去重复版本 String sourceMetadata = (String)textReader.getCustomMetadata().get("source"); String textHash = SecureUtil.md5(sourceMetadata); String redisKey = "vector-xxx:" + textHash; // 判断是否存入过,redisKey如果可以成功插入表示以前没有过,可以加入向量数据 Boolean retFlag = redisTemplate.opsForValue().setIfAbsent(redisKey, "1"); System.out.println("****retFlag : "+retFlag); if(Boolean.TRUE.equals(retFlag)) { //键不存在,首次插入,可以保存进向量数据库 vectorStore.add(list); }else { //键已存在,跳过或者报错 //throw new RuntimeException("---重复操作"); System.out.println("------向量初始化数据已经加载过,请不要重复操作"); } } }@Configuration @Slf4j public class RedisConfig { /** * RedisTemplate配置 * redis序列化的工具配置类,下面这个请一定开启配置 * 127.0.0.1:6379> keys * * 1) "ord:102" 序列化过 * 2) "\xac\xed\x00\x05t\x00\aord:102" 野生,没有序列化过 * this.redisTemplate.opsForValue(); //提供了操作string类型的所有方法 * this.redisTemplate.opsForList(); // 提供了操作list类型的所有方法 * this.redisTemplate.opsForSet(); //提供了操作set的所有方法 * this.redisTemplate.opsForHash(); //提供了操作hash表的所有方法 * this.redisTemplate.opsForZSet(); //提供了操作zset的所有方法 * @param redisConnectionFactor * @return */ @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactor) { RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactor); //设置key序列化方式string redisTemplate.setKeySerializer(new StringRedisSerializer()); //设置value的序列化方式json,使用GenericJackson2JsonRedisSerializer替换默认序列化 redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.afterPropertiesSet(); return redisTemplate; } }@Configuration public class SaaLLMConfig { private final String DEEPSEEk_MODEL="deepseek-v3"; private final String QWEN_MODEL="qwen-max"; // 方式一 @Value("${spring.ai.dashscope.api-key}") private String apiKey; // 配置大模型对外暴露的api @Bean(name = "deepseekChatModel") public ChatModel deepseekChatModel() { return DashScopeChatModel.builder() // 配置apikey .dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build()) // 配置模型 .defaultOptions(DashScopeChatOptions.builder().withModel(DEEPSEEk_MODEL).build()) .build(); } @Bean(name = "qwenChatModel") public ChatModel qwenChatModel() { return DashScopeChatModel.builder() // 配置apikey .dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build()) // 配置模型 .defaultOptions(DashScopeChatOptions.builder().withModel(QWEN_MODEL).build()) .build(); } @Bean(name = "deepseekChatClient") public ChatClient deepseekChatClient(@Qualifier("deepseekChatModel") ChatModel deepseekChatModel) { return ChatClient.builder(deepseekChatModel) .build(); } @Bean("qwenChatClient") public ChatClient qwenChatClient(@Qualifier("qwenChatModel") ChatModel qwenChatModel) { return ChatClient.builder(qwenChatModel) .build(); } } -
控制类
@RestController public class RagController { @Resource(name = "qwenChatClient") private ChatClient chatClient; @Resource private VectorStore vectorStore; /** * http://localhost:8012/rag4aiops?msg=00000 * http://localhost:8012/rag4aiops?msg=C2222 * @param msg * @return */ @GetMapping("/rag4aiops") public Flux<String> rag(@RequestParam("msg") String msg) { String systemInfo = """ 你是一个运维工程师,按照给出的编码给出对应故障解释,否则回复找不到信息。 """; RetrievalAugmentationAdvisor advisor = RetrievalAugmentationAdvisor.builder() .documentRetriever(VectorStoreDocumentRetriever.builder().vectorStore(vectorStore).build()) .build(); return chatClient .prompt() .system(systemInfo) .user(msg) .advisors(advisor) .stream() .content(); } }
Tool Calling 工具调用
LLM的外部utils工具类
Tool Calling:允许大模型与一组API或工具交互,将LLM的智能与外部工具或API无缝连接,从而增强大模型功能。
LLM本身不调用工具,只是指示应该调用哪个工具,以及如何调用。
用于访问实时数据,
前提是大模型支持functioncall 才能正常调用
痛点:微服务之间不能共享。
代码演示
-
pom依赖如下
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter-dashscope</artifactId> </dependency> <!-- Redis向量数据库--> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-vector-store-redis</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--hutool--> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.22</version> </dependency> </dependencies> -
配置文件如下
server.port=8012 # ????????? server.servlet.encoding.charset=UTF-8 server.servlet.encoding.enabled=true server.servlet.encoding.force=true spring.application.name=saa13-toolcalling #springai ????config spring.ai.dashscope.api-key=${aliQwen-apikey} -
配置类和往常一样
-
控制类不使用 ToolCalling
@RestController public class NoToolCallingController { @Resource(name = "deepseekChatModel") private ChatModel deepseekChatModel; @Resource(name = "qwenChatModel") private ChatModel qwenChatModel; @Resource(name = "deepseekChatClient") private ChatClient deepseekChatClient; @Resource(name = "qwenChatClient") private ChatClient qwenChatClient; /** * // 无法获取到实时的时间 * @param * @return */ @GetMapping("/notoolcalling/chat") public Flux<String> chat(@RequestParam(name="msg",defaultValue = "你是谁?现在几点了?") String msg) { return qwenChatModel.stream(msg); } } -
控制类使用ToolCalling
@RestController public class ToolCallingController { @Resource(name = "deepseekChatModel") private ChatModel deepseekChatModel; @Resource(name = "qwenChatModel") private ChatModel qwenChatModel; @Resource(name = "deepseekChatClient") private ChatClient deepseekChatClient; @Resource(name = "qwenChatClient") private ChatClient qwenChatClient; /** * // * @param * @return */ @GetMapping("/toolcalling/chat") public String chat(@RequestParam(name="msg",defaultValue = "你是谁?现在几点了?") String msg) { // 1.工具注册到工具集合里 ToolCallback[] tools = ToolCallbacks.from(new DateTimeTools()); // 2. 将工具集配置进ChatOptions对象 ChatOptions options = ToolCallingChatOptions.builder().toolCallbacks(tools).build(); // 3. 构建提示词 Prompt prompt = new Prompt(msg, options); // 4, 调用模型进行流式响应 return qwenChatModel.call(prompt).getResult().getOutput().getText(); } /** * // * @param * @return */ @GetMapping("/toolcalling/chat2") public Flux<String> chat2(@RequestParam(name="msg",defaultValue = "你是谁?现在几点了?") String msg) { return deepseekChatClient.prompt(msg) .tools(new DateTimeTools()) .stream() .content(); } }工具类:
public class DateTimeTools { // @Tool注解用于标记一个方法为工具方法,description属性用于描述该工具方法的功能, // returnDirect:是否需要直接返回结果,true表示直接返回结果给客户,不走大模型; // false(默认)表示返回一给大模型,模型可以根据结果进行进一步的处理 @Tool(description = "获取当前的时间",returnDirect = false) public String getCurrentDateTime() { return java.time.LocalDateTime.now().toString(); } }
本文来自博客园,作者:NE_STOP,转载请注明原文链接:https://www.cnblogs.com/alineverstop/p/23603889
浙公网安备 33010602011771号