在一个RAG系统中,根据用户的不同意图来定制提示词,是提升系统智能性、准确性和效率的关键。这相当于为系统装上了一个“大脑”,让它能理解用户到底想要什么,从而动态调整自己的行为。 简单来说,用户的提问千差万别,但一个固定的“检索→生成”流程无法应对所有情况。定制化提示词就是为了解决这个问题。 最最基本的就是我们根据意图识别的结果,把非客服相关的问题,理由给一个通用的LLM,让他做通用回答。 而针对客服相关的问题,我们也可以针对具体的问题分类定制提示词,这样可以让LLM更加专注和聚焦问题,比如要回答汽车的使用相关的知识,那肯定是专业性优先,而如果是回答售前问题,那肯定热情度优先。所以,我们需要针对不同的意图来做定制的提示词。 比如我们的售前咨询的提示词:
你是一位专业的汽车销售顾问。请基于以下参考资料,为有购车意向的用户提供详尽、真实的购车建议,包括车型推荐、配置对比、价格区间及购车流程等信息。
回答时语气亲切、专业,激发用户的购买兴趣,但不得夸大或捏造产品信息。
若用户询问具体订单、库存或优惠政策等需实时确认的信息,请引导用户通过正式销售渠道(如到店咨询、官网预约等)进一步了解。
若参考资料中无法找到相关信息,请引导用户通过正式销售渠道(如到店咨询、官网预约等)进一步了解。
问题:{{userMessage}}
参考资料:{{contents}}
关键约束:
严禁在回答中出现“根据参考资料”、“根据文档”、“根据您提供的信息”等字眼。
直接将上下文中的知识内化为你的知识,用自然、流畅的语气直接回答。
如果上下文中没有相关信息,请直接告知用户你暂时无法回答该具体问题,或者根据你已有的通用知识进行补充说明(视你的业务需求而定),但不要编造上下文不存在的事实。
回答风格应像是在与用户面对面交谈,而不是在引用文献。
我们的技术支持提示词:
你是一位资深汽车技术工程师。请依据以下参考资料,对用户提出的技术问题、使用问题等给出准确、严谨的解答,包括车辆系统原理、技术规格参数、故障诊断思路及操作指引等。
回答应条理清晰、术语准确,避免模糊表述。
若参考资料中无法找到相关技术信息,请直接告知用户:这个问题我暂时无法回答!
问题:{{userMessage}}
参考资料:{{contents}}
关键约束:
严禁在回答中出现“根据参考资料”、“根据文档”、“根据您提供的信息”等字眼。
直接将上下文中的知识内化为你的知识,用自然、流畅的语气直接回答。
如果上下文中没有相关信息,请直接告知用户你暂时无法回答该具体问题,不要编造上下文不存在的事实。
回答风格应像是在与用户面对面交谈,而不是在引用文献。
基于以上,我们针对不同的意图,创建不同的提示词文件:
resources
prompts
car-before-sales-query-prompt.txt
car-complaints-query-prompt.txt
car-maintenance-query-prompt.txt
car-marketing-query-prompt.txt
car-other-query-prompt.txt
car-tech-support-query-prompt.txt
接着定义一个枚举,把这些意图定义出来:
package cn.hollis.llm.mentor.know.engine.ai.constant;
import cn.hollis.llm.mentor.know.engine.ai.model.IntentRecognitionResult;
public enum KnowEngineIntent {
/**
* 汽车售前咨询
*/
CAR_BEFORE_SALES_QUERY("car-before-sales-query-prompt.txt"),
/**
* 汽车投诉查询
*/
CAR_COMPLAINTS_QUERY("car-complaints-query-prompt.txt"),
/**
* 汽车保养查询
*/
CAR_MAINTENANCE_QUERY("car-maintenance-query-prompt.txt"),
/**
* 汽车营销查询
*/
CAR_MARKETING_QUERY("car-marketing-query-prompt.txt"),
/**
* 汽车技术支持查询
*/
CAR_TECH_SUPPORT_QUERY("car-tech-support-query-prompt.txt"),
/**
* 汽车其他相关问题
*/
CAR_OTHER_QUERY("car-other-query-prompt.txt");
private final String fileName;
KnowEngineIntent(String fileName) {
this.fileName = fileName;
}
public String getFileName() {
return fileName;
}
public static KnowEngineIntent getIntent(IntentRecognitionResult intentRecognitionResult) {
return switch (intentRecognitionResult.intent()) {
case "售前咨询与购买" -> CAR_BEFORE_SALES_QUERY;
case "投诉与维权" -> CAR_COMPLAINTS_QUERY;
case "售后维修与保养" -> CAR_MAINTENANCE_QUERY;
case "汽车营销政策" -> CAR_MARKETING_QUERY;
case "车辆使用与技术指导" -> CAR_TECH_SUPPORT_QUERY;
default -> CAR_OTHER_QUERY;
};
}
}
每一个意图关联一个文件名,文件名就是上面我们在classpath中提前放置的提示词文件的名称。 这个枚举提供一个方法,可以把意图识别的结果IntentRecognitionResult 转成我们的KnowEngineIntent这个枚举的具体的某一个枚举项,方便后面使用。 接着,我们定义一个PormptService,这里面提供一个方法,可以根据意图获取到对应的提示词的内容:
package cn.hollis.llm.mentor.know.engine.ai.service;
import cn.hollis.llm.mentor.know.engine.ai.constant.KnowEngineIntent;
import cn.hollis.llm.mentor.know.engine.ai.model.IntentRecognitionResult;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Service
public class PromptService {
private final Map<KnowEngineIntent, String> promptCache = new ConcurrentHashMap<>();
private final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
/**
* 根据意图获取提示词
*/
public String getPrompt(KnowEngineIntent intent) {
return promptCache.computeIfAbsent(intent, this::loadPromptFromFile);
}
/**
* 根据意图获取提示词
*/
public String getPrompt(IntentRecognitionResult intent) {
return promptCache.computeIfAbsent(KnowEngineIntent.getIntent(intent), this::loadPromptFromFile);
}
/**
* 从文件加载提示词(带缓存)
*/
private String loadPromptFromFile(KnowEngineIntent intent) {
try {
Resource resource = resolver.getResource("classpath:/prompts/" + intent.getFileName());
return FileCopyUtils.copyToString(new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8));
} catch (IOException e) {
// 如果指定意图的文件不存在,返回默认提示词
if (intent != KnowEngineIntent.CAR_OTHER_QUERY) {
return getPrompt(KnowEngineIntent.CAR_OTHER_QUERY);
}
throw new RuntimeException("默认提示词文件缺失", e);
}
}
}
然后我们只需要在需要提示词的地方(意图识别后),调用getPrompt方法就能拿到对应的提示词了。 然后我们把这个提示词定义到ContentInjector中,用于提示词和文档的拼接:
ContentInjector contentInjector = DefaultContentInjector.builder()
.promptTemplate(new PromptTemplate(promptService.getPrompt(intentRecognitionResult)
))
.build();
需要注意的是,默认的提示词中,支持的占位符是{{userMessage}}和{{contents}}
所以我们自定义的提示词,也需要用这两个。不能换成其他的。
接着可以通过一个测试的controller做测试:
/**
* 用于ai的各个模块的功能测试
*/
@RestController
@RequestMapping("/ai/module")
public class RagModuleController {
@Autowired
private ChatModel chatModel;
@Autowired
private PromptService promptService;
@GetMapping("testPromptRouter")
public String testPromptRouter(String question) {
IntentRecognitionResult intentRecognitionResult = AiServices.builder(IntentRecognitionService.class).chatModel(chatModel).build().chat(question);
return promptService.getPrompt(intentRecognitionResult);
}
}
测试结果: