第2392篇:RAG系统的用户意图理解——准确处理模糊和复杂查询
大约 6 分钟
第2392篇:RAG系统的用户意图理解——准确处理模糊和复杂查询
适读人群:希望提升RAG查询处理能力的AI工程师 | 阅读时长:约18分钟 | 核心价值:掌握用户意图识别和查询改写技术,解决模糊查询和隐含意图的工程难题
做RAG系统维护时,有一类问题让我反复头疼:用户的查询和实际意图之间总有落差。
用户输入"那个促销活动",AI不知道"那个"指的是哪个。
用户输入"规定是什么",不知道是哪条规定。
用户输入"和上次一样处理",不知道上次是什么情况。
这些模糊、不完整、带上下文依赖的查询,是RAG系统实际落地时遇到最多的真实场景。把这类问题处理好,系统的整体满意度能显著提升。
用户意图的几种类型
/**
* 用户意图的分类框架
*
* 类型1:明确意图
* "Q3的销售额是多少?"
* 意图清晰,直接检索即可
*
* 类型2:模糊意图(缺乏具体性)
* "最新政策是什么?"
* 哪方面的政策?什么时间的最新?
* 需要澄清或推断
*
* 类型3:隐含意图(字面意思和实际需求不同)
* "这个功能怎么用?"
* 用户可能是想了解某个功能,也可能是遇到了问题
* 实际想要的是解决方案,不只是功能说明
*
* 类型4:上下文依赖意图
* "上次说的那个方案呢?"
* 依赖对话历史,没有历史上下文无法理解
*
* 类型5:复合意图
* "帮我查一下A产品的规格,还有看看有没有类似产品"
* 包含多个独立的意图
*/查询改写:把模糊查询变成可检索的查询
@Service
public class QueryRewriter {
private final ChatClient chatClient;
/**
* 查询改写的主要场景:
* 1. 补全缺失的上下文信息
* 2. 扩展同义词
* 3. 把隐晦的表达转成明确的查询词
*/
public RewrittenQuery rewrite(String originalQuery, ConversationContext context) {
// 第一步:检测是否需要改写
QueryAnalysis analysis = analyzeQuery(originalQuery, context);
if (!analysis.isNeedsRewriting()) {
return RewrittenQuery.unchanged(originalQuery);
}
// 第二步:根据分析结果选择改写策略
return switch (analysis.getIssueType()) {
case REFERENCES_CONTEXT -> rewriteWithContext(originalQuery, context);
case TOO_VAGUE -> expandWithSynonyms(originalQuery);
case MISSING_SUBJECT -> clarifySubject(originalQuery, context);
default -> RewrittenQuery.unchanged(originalQuery);
};
}
/**
* 处理代词引用("那个"、"这个"、"上面说的"等)
*/
private RewrittenQuery rewriteWithContext(String query, ConversationContext context) {
if (context.isEmpty()) {
// 没有上下文,无法解析引用,需要让用户澄清
return RewrittenQuery.needsClarification(
query,
"请问您指的是哪个具体内容?"
);
}
String prompt = """
用户的问题包含对之前对话的引用,请结合对话历史将其改写为独立、完整的查询。
对话历史:
%s
当前问题:%s
改写要求:
1. 替换代词(这个、那个、它等)为具体内容
2. 保持问题的原始意图
3. 生成一个可以独立理解的查询
输出JSON:
{
"rewritten_query": "改写后的查询",
"confidence": 0-1的置信度
}
""".formatted(context.getFormattedHistory(), query);
String response = chatClient.prompt(prompt).call().content();
return parseRewrittenQuery(response, query);
}
/**
* 用同义词扩展模糊查询
*/
private RewrittenQuery expandWithSynonyms(String query) {
String prompt = """
以下查询比较简短,请生成3个不同的检索查询,覆盖可能的意图。
原始查询:%s
输出JSON:
{
"queries": ["查询1", "查询2", "查询3"],
"primary_query": "最可能的主要意图的查询"
}
""".formatted(query);
String response = chatClient.prompt(prompt).call().content();
return parseExpandedQuery(response, query);
}
}意图识别和分类
@Service
public class IntentClassifier {
/**
* 意图分类:帮助系统决定如何处理不同类型的查询
*/
public Intent classify(String query, ConversationContext context) {
// 快速规则分类(低成本)
Intent ruleIntent = classifyByRules(query);
if (ruleIntent.getConfidence() > 0.9) {
return ruleIntent;
}
// LLM分类(高准确率,但有延迟)
return classifyByLLM(query, context);
}
private Intent classifyByRules(String query) {
// 问候类
if (isGreeting(query)) {
return Intent.of(IntentType.GREETING, 0.95f);
}
// 感谢类
if (isThanks(query)) {
return Intent.of(IntentType.THANKS, 0.95f);
}
// 纯事实查询
if (query.matches(".*(是什么|定义|含义|概念).*")) {
return Intent.of(IntentType.FACTUAL_QUERY, 0.8f);
}
// 操作指导类
if (query.matches(".*(如何|怎么|步骤|流程|操作).*")) {
return Intent.of(IntentType.HOW_TO, 0.8f);
}
// 比较类
if (query.matches(".*(区别|差异|对比|vs|和.*相比).*")) {
return Intent.of(IntentType.COMPARISON, 0.8f);
}
return Intent.of(IntentType.GENERAL, 0.5f);
}
private Intent classifyByLLM(String query, ConversationContext context) {
String prompt = """
请对以下用户查询进行意图分类:
查询:%s
可能的意图类型:
- FACTUAL_QUERY: 查询事实信息
- HOW_TO: 想了解操作方法
- TROUBLESHOOTING: 遇到问题想解决
- COMPARISON: 想比较两个选项
- GENERAL: 一般性问题
输出JSON:
{
"intent_type": "意图类型",
"confidence": 0-1,
"sub_intents": ["可能的次要意图"]
}
""".formatted(query);
String response = chatClient.prompt(prompt).call().content();
return parseIntent(response);
}
}主动澄清:当不确定时,主动问
@Service
public class ActiveClarificationService {
/**
* 决策:什么时候主动问用户,而不是猜测
*
* 太频繁问:用户体验差(感觉被审问)
* 太少问:猜错了,给出无效回答
*
* 平衡策略:
* - 问题极其模糊时,问
* - 澄清后能显著提升回答质量时,问
* - 如果猜测的置信度>0.8,不问,直接回答(并说明假设)
*/
public ClarificationDecision decide(String query, Intent intent) {
// 情况1:问题过短,明显需要更多信息
if (query.trim().length() < 5) {
return ClarificationDecision.askWithQuestion(
generateClarificationQuestion(query, intent)
);
}
// 情况2:包含不可解析的代词,且没有上下文
if (containsUnresolvedPronouns(query) && !hasContext()) {
return ClarificationDecision.askWithQuestion(
"请问您是在询问哪方面的内容?"
);
}
// 情况3:检索结果的置信度极低(<0.3),说明问题太模糊
// 这个逻辑在检索后执行
// 其他情况:不问,尝试直接回答
return ClarificationDecision.tryDirectAnswer();
}
/**
* 生成智能的澄清问题
* 不是问"你想要什么"这种无效问题
* 而是给出几个具体选项
*/
private String generateClarificationQuestion(String query, Intent intent) {
String prompt = """
用户的查询较模糊,需要澄清。请生成一个帮助用户明确意图的问题。
用户查询:%s
要求:
1. 给出2-3个具体选项(而不是开放性问题)
2. 选项要覆盖最可能的情况
3. 语气友好,不要让用户感觉被审查
例如好的澄清问题:
"您想了解的是:A) 产品规格 B) 价格信息 C) 使用方法?"
生成澄清问题:
""".formatted(query);
return chatClient.prompt(prompt).call().content().trim();
}
}多轮对话的意图追踪
@Service
public class ConversationIntentTracker {
/**
* 在多轮对话中跟踪意图演变
*
* 用户可能在对话过程中逐步细化意图:
* 轮1:"查一下退款政策"
* 轮2:"那如果是旺季呢?"
* 轮3:"最晚几天能到账?"
*
* 每一轮都依赖前一轮的上下文
*/
public ResolvedIntent resolveWithContext(String currentQuery,
ConversationHistory history) {
if (history.isEmpty()) {
return ResolvedIntent.standalone(currentQuery);
}
// 检测当前查询是否是对前一条的追问
boolean isFollowUp = detectFollowUp(currentQuery, history.getLastTurn());
if (isFollowUp) {
// 合并上下文,生成完整查询
String expandedQuery = expandWithHistory(currentQuery, history);
return ResolvedIntent.followUp(currentQuery, expandedQuery, history);
}
// 新话题
return ResolvedIntent.newTopic(currentQuery);
}
private boolean detectFollowUp(String query, ConversationTurn lastTurn) {
// 追问的典型特征
List<String> followUpPatterns = Arrays.asList(
"那", "如果", "还有", "那么", "另外",
"之后", "怎么", "为什么", "再问一下"
);
return followUpPatterns.stream().anyMatch(query::startsWith)
|| query.length() < 15 // 很短的查询通常是追问
|| containsPronouns(query);
}
private String expandWithHistory(String query, ConversationHistory history) {
String historyText = history.getRecentTurns(3).stream()
.map(t -> String.format("用户:%s\nAI:%s", t.getUserMessage(), t.getAiResponse()))
.collect(Collectors.joining("\n"));
String prompt = """
对话历史:
%s
用户的追问:%s
请将追问改写为独立、完整的查询(不依赖上下文也能理解):
""".formatted(historyText, query);
return chatClient.prompt(prompt).call().content().trim();
}
}用户意图理解是RAG系统里"藏在细节里的魔鬼"。很多系统在完美的、干净的测试问题上效果很好,但遇到真实用户的模糊、随意、带上下文的问题就表现很差。投入时间在意图理解上,会让系统的实际落地效果大幅提升——而且这种提升是用户能直接感受到的。
