Spring AI Alibaba - RAG与ToolCalling

RAG检索增强生成

RAG:训练大模型特定领域知识或专有数据。

RAG:一种从数据中查找相关信息,并在将提示发送给LLM之前将其注入到提示中的方法,这样,LLM就能获得相关信息,并基于这些信息回答,降低幻觉概率。

检索出来的信息使大模型回答更准确。

先查资料后回答的机制。

RAG流程分为2个阶段:索引 index 和检索 Retrieval

RAG 通过引入外部知识源来增强LLM的输出能力。

传统的LLM通常基于其训练数据生成相应,但这些数据可能过时或不够全面。

RAG允许模型在生成答案之前,从特定的知识库中检索相关信息,从而提供更准确的回答。

LLM的缺陷

LLM的知识不是实时的,不具备知识更新。

LLM不具备私有的业务知识

LLM有时会在回答中生成看似合理但实际错误的信息。

RAG索引

无标题16

RAG检索

无标题17

代码演示

根据给出的错误编码给出异常解释

  1. 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>
    
  2. 配置文件如下

    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
    
  3. 在resources文件夹下添加ops.txt文件,内容如下:

    00000 系统OK正确执行后的返回
    A0001 用户端错误一级宏观错误码
    A0100 用户注册错误二级宏观错误码
    B1111 支付接口超时
    C2222 Kafka消息解压严重
    
  4. 添加配置类

    @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();
        }
    }
    
  5. 控制类

    @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 才能正常调用

痛点:微服务之间不能共享。

代码演示

  1. 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>
    
  2. 配置文件如下

    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}
    
  3. 配置类和往常一样

  4. 控制类不使用 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);
        }
    
    }
    
  5. 控制类使用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();
        }
    }
    
posted @ 2026-11-09 16:20  NE_STOP  阅读(21)  评论(0)    收藏  举报