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





QT多线程的5种用法,通过使用线程解决UI主界面的耗时操作代码,防止界面卡死。...
U8W/U8W-Mini使用与常见问题解决
stm32使用HAL库配置串口中断收发数据(保姆级教程)
分享几个国内免费的ChatGPT镜像网址(亲测有效)
Allegro16.6差分等长设置及走线总结