您现在的位置是:首页 >技术教程 >Spring AI——CompressionQueryTransformer(压缩)和RewriteQueryTransformer(改写)网站首页技术教程

Spring AI——CompressionQueryTransformer(压缩)和RewriteQueryTransformer(改写)

IT丶Jason 2026-03-24 00:01:04
简介Spring AI——CompressionQueryTransformer(压缩)和RewriteQueryTransformer(改写)

最近在研究Spring AI,在RAG预检索部分有两个做转换输入查询的类,分别是CompressionQueryTransformer和RewriteQueryTransformer。

1、CompressionQueryTransformer

CompressionQueryTransformer:它的作用原文直译如下:

原文直译

首先要构建一个Query对象,构建的时候传入聊天历史List<Message>和用户提问content,然后通

        Query query = Query.builder()
                .text(content) // 用户输入问题
                .history(messages) // 聊天历史/对话历史
                .build();

过CompressionTransformer.builder()来构建CompressionTransformer,CompressionTransformer可以构建的内容有:

  • chatClientBuilder(ChatClient.Builder clientBuilder):ChatClient会话的构建器(Builder)
  • promptTemplate(PromptTemplate promptTemplate):系统提示词模板PromptTemplate 

至少需要传入clientBuilder, 否则无法工作,prompt如果自己有定制可以传入。构建完成后再调用它的transform()方法就可以得到压缩后的query。

        CompressionQueryTransformer compressionQueryTransformer = CompressionQueryTransformer.builder()
                .chatClientBuilder(chatClientBuilder)
                .build();
        Query transformed = compressionQueryTransformer.transform(query);
        log.info("压缩->:{}", transformed.text());

2、RewriteQueryTransformer

RewriteQueryTransformer:原文直译如下:

 RewriteQueryTransformer改写也跟CompressionTransformer压缩部分类似,我的实现传入了用户query和对话历史以外,还传入了系统prompt。这样改写后的query质量相对会更好(但是还是属于概率事件,影响因素很多包括你的prompt和历史对话的关联性等)。这里注意的一点是传入的prompt是强制需要占位符query和target的,可以根据自己的需求来定义占位符。

        promptTemplate.render(
                Map.of("query", transformed.text(),
                        "target", transformed.history()));
        // Query改写
        RewriteQueryTransformer queryTransformer = RewriteQueryTransformer.builder()
                .chatClientBuilder(chatClientBuilder)
                .promptTemplate(promptTemplate)
                .build();
        Query rewritedQuery = queryTransformer.transform(transformed);
        System.err.println(rewritedQuery.history());
        log.info("改写->:{}", rewritedQuery.text());

我的Prompt,其中{query}和{target}都是占位符,通过PromptTemplate的render()方法映射占位符:

        promptTemplate.render(
                Map.of("query", transformed.text(),
                        "target", transformed.history()));
'''
传入的query是用户的新问,请根据聊天历史target将用户查询改写为更加精确的问题,并保留核心关键词和消除歧义,避免添加额外任何与聊天历史和query无关的内容。
最后只输出改写后的query,不要输出其他任何冗余内容。

原始查询:{query}
聊天历史:{target}

改写后的查询:
'''

3、两者区别

CompressionQueryTransformer和RewriteQueryTransformer的最大区别在于它们对查询(query)的处理目标和方式不同:

  • 处理目标

    • CompressionQueryTransformer:主要目标是在不改变原始查询意图的前提下,对查询进行压缩,使其变得更加简洁、紧凑。这有助于减少查询的长度,提高处理效率,同时尽可能地保留原始查询的核心信息。例如,将一个冗长的查询语句简化为更短的表达,但仍然能够准确传达相同的意思。
    • RewriteQueryTransformer:侧重于对查询进行改写,通过重新组织语言、替换词汇、调整语法结构等方式,使改写后的查询更符合特定的要求或风格,比如更自然、更流畅、更易于理解等。它可能会改变查询的表述方式,但不一定会改变其核心意图。
  • 处理方式

    • CompressionQueryTransformer:通常会采用一些压缩算法或策略,如删除不必要的词汇、合并重复或相似的部分、使用更简洁的表达方式等。它需要准确地识别出查询中的冗余信息,并进行合理的压缩,以确保压缩后的查询仍然能够有效地传达原始意图。
    • RewriteQueryTransformer:更多地依赖于对语言的理解和生成能力。它会分析原始查询的语义和语法结构,然后根据预设的规则或模型训练的结果,对查询进行重新组织和表述。这可能涉及到词汇的替换、句式的调整、逻辑关系的优化等多个方面。

 4、完整代码

// RAG Service实现类
package tb.ai.service.service.impl;

import org.springframework.core.io.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.rag.Query;
import org.springframework.ai.rag.preretrieval.query.transformation.CompressionQueryTransformer;
import org.springframework.ai.rag.preretrieval.query.transformation.QueryTransformer;
import org.springframework.ai.rag.preretrieval.query.transformation.RewriteQueryTransformer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import tb.ai.service.config.properties.DeepSeekProperties;
import tb.ai.service.service.RAGService;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author JasonLong
 * @date 2025-02-11 01:39
 * keeping loving and keep study
 * RAG Service实现类
 */
@Service
public class RAGServiceImpl implements RAGService {

    private static final Logger log = LoggerFactory.getLogger(RAGServiceImpl.class);

    @Autowired
    ChatMemory chatMemory;

    private static final String SYSTEM_PROMPT_PATH = "classpath:static/qc_sys_text.st";

    private PromptTemplate promptTemplate;

    ResourceLoader resourceLoader;

    ChatClient.Builder chatClientBuilder;

    @Autowired
    public RAGServiceImpl(ResourceLoader resourceLoader, ChatClient.Builder chatClientBuilder) {
        this.resourceLoader = resourceLoader;
        this.chatClientBuilder = chatClientBuilder;
        // 尝试加载系统提示词模板,最好是在构造时加载一次
        try {
            this.promptTemplate = loadPromptTemplate();
        } catch (IOException e) {
            throw new RuntimeException("无法加载系统提示词模板", e);
        }
    }

    /**
     * 加载系统提示词模板文件
     */
    private PromptTemplate loadPromptTemplate() throws IOException {
        Resource loaderResource = resourceLoader.getResource(SYSTEM_PROMPT_PATH);
        Path path = loaderResource.getFile().toPath();
        if (Files.exists(path)) {
            String templateContent = Files.readString(path);
            return new PromptTemplate(templateContent);
        } else {
            throw new IOException("加载系统提示词模板文件失败");
        }
    }

    /**
     * 预检索:
     * Query压缩->Query改写->end
     *
     * @param content
     */
    @Override
    public String preRetrieval(String content, String conversationId) {

        // 获取聊天历史,转换为可变的ArrayList
        List<Message> messages = new ArrayList<>(chatMemory.get(conversationId, 10));

        // 如果历史记录为空,添加默认提示
        if (messages.isEmpty()) {
            messages.add(new SystemMessage("无历史记录"));
        }

        // Query压缩
        Query query = Query.builder()
                .text(content)
                .history(messages)
                .build();
        CompressionQueryTransformer compressionQueryTransformer = CompressionQueryTransformer.builder()
                .chatClientBuilder(chatClientBuilder)
                .build();
        Query transformed = compressionQueryTransformer.transform(query);
        log.info("压缩->:{}", transformed.text());

        promptTemplate.render(
                Map.of("query", transformed.text(),
                        "target", transformed.history()));
        // Query改写
        RewriteQueryTransformer queryTransformer = RewriteQueryTransformer.builder()
                .chatClientBuilder(chatClientBuilder)
                .promptTemplate(promptTemplate)
                .build();
        Query rewritedQuery = queryTransformer.transform(transformed);
        System.err.println(rewritedQuery.history());
        log.info("改写->:{}", rewritedQuery.text());

        return rewritedQuery.text();
    }
}


// ChatMemory 对话历史 配置类(基于内存)
package tb.ai.service.config.chat_memory;

import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author JasonLong
 * @date 2025-02-10 13:39
 * keeping loving and keep study
 */
@Configuration
public class ChatMemoryConfig {

    @Bean
    public InMemoryChatMemory chatMemory() {
        return new InMemoryChatMemory();
    }
}

Over

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。