前言
RAG(检索增强生成)系统在企业落地过程中,表面上看是"检索+生成"两步走,但实际生产环境中会遇到各种复杂情况。本文深入拆解三个最核心的问题:长尾查询、歧义指令、复杂推理错误,每个问题都从"是什么→为什么→怎么解决"三个层次展开,并配合真实案例帮助理解。
一、长尾查询
1.1 什么是长尾查询?
长尾查询指的是那些过于垂直、细分、低频的用户查询。这类查询在训练数据中出现频率极低,在知识库中也几乎找不到高度匹配的文档。
用一张图来理解:
查询频率
↑
高 │██
│████
│██████
│█████████
低 │████████████████████████████████ ← 长尾查询在这里
└─────────────────────────────────→ 查询种类
头部: "公司休假政策是什么"(高频,知识库必有)
长尾: "2021年石家庄军人遗孀宅基地继承特殊优待政策"(极低频,知识库几乎没有)
典型特征:
✦ 查询词高度专业化、地域化、时间化
✦ 知识库中检索相似度普遍低于 0.5
✦ LLM会用"听起来合理的幻觉"填补空白
✦ 用户得到的是一段正确的废话
1.2 真实案例还原
场景: 企业法律咨询RAG系统,知识库包含各类法律法规文档。
用户提问:
"2021年河北省石家庄市关于农村宅基地继承纠纷中,
涉及已故军人遗孀的特殊优待政策是什么?"
系统内部发生了什么:
Step 1:向量检索
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
召回文档 相似度得分
─────────────────────────────────────────────
《农村土地承包法》第三章 0.41
《退役军人保障法》总则 0.38
河北省土地管理条例(2018版) 0.35
宅基地政策FAQ(通用版) 0.31
─────────────────────────────────────────────
❌ 没有任何文档相似度超过 0.5
❌ 没有石家庄市地方性法规
❌ 没有"军人遗孀 × 宅基地继承"交叉专项政策
LLM拿到低相关文档后的回答:
"根据相关法律规定,农村宅基地继承应遵循《继承法》
基本原则,军人遗孀享有优先保护权利,建议向当地
村委会咨询具体政策。"
问题分析:
⚠️ 回答模糊,没有任何实质性内容
⚠️ LLM在用"正确的废话"拼凑答案
⚠️ 用户得到的是幻觉,却无法分辨真假
⚠️ 严重情况下会误导用户做出错误决策
1.3 为什么会出现这个问题?
根本原因链:
知识库覆盖不足
↓
检索到的文档与查询相关性极低
↓
LLM上下文信息严重不足
↓
LLM倾向于用已有知识"补全"答案(而非承认不知道)
↓
产生看似合理、实则错误的幻觉回答
1.4 解决方案
方案一:HyDE(假设文档嵌入)
核心思路: 不直接用用户查询做检索,而是先让LLM生成一段"假设性的答案文档",再用这段文字做检索。因为假设文档的语言风格更接近真实文档,相似度会显著提升。
python
def hyde_retrieval(query, llm, retriever):
"""
HyDE:Hypothetical Document Embeddings
假设文档嵌入检索
"""
# Step 1:让LLM生成假设性答案文档
hypothetical_doc = llm.generate(f"""
请为以下问题写一段可能存在的政策文本,
大约200字,使用正式的政策文件语气:
问题:{query}
""")
# 生成的假设文档大概是这样:
# "...根据《退役军人保障法》及地方实施细则,
# 烈士、因公牺牲军人的遗孀在宅基地继承中享有:
# 1.申请资格保留至再婚或自愿放弃
# 2.宅基地面积不低于当地最低标准..."
# Step 2:用假设文档的embedding做检索
# (语言风格更接近真实政策文档)
results = retriever.search(hypothetical_doc)
# 效果:相似度从 0.38 提升到 0.62 ✅
return results
为什么有效:
原始查询: "军人遗孀宅基地继承优待政策"
↓ embedding后,语义偏向"用户提问"风格
假设文档: "...遗孀享有宅基地申请优先权,
面积标准不低于..."
↓ embedding后,语义偏向"政策文本"风格
真实文档也是"政策文本"风格
→ 假设文档与真实文档的相似度,远高于原始查询与真实文档的相似度
方案二:查询扩展 + 多路召回融合
核心思路: 生成多个语义相近的查询变体,分别检索后用RRF算法融合排序,提高整体召回率。
python
def multi_query_retrieval(original_query, llm, retriever):
"""
多路召回:用多个查询变体检索,融合结果
"""
# Step 1:生成查询变体
variants = llm.generate(f"""
原始查询:{original_query}
请生成4个语义相近但表达不同的查询:
要求:改变措辞,保持核心意图不变
""")
all_queries = [original_query] + variants
# 例如:
# ["军人遗孀宅基地继承优待政策",
# "军人遗孀农村土地权益保障",
# "涉军优抚对象宅基地政策",
# "退役军人家属宅基地继承规定",
# "已故军人配偶农村宅基地权利"]
# Step 2:每个查询独立检索
all_results = {}
for query in all_queries:
results = retriever.search(query)
all_results[query] = results
# Step 3:RRF融合(Reciprocal Rank Fusion)
return reciprocal_rank_fusion(all_results)
def reciprocal_rank_fusion(results_dict, k=60):
"""
RRF算法:综合多路检索结果的排名,给出最终排序
核心公式:score(doc) = Σ 1/(k + rank_i)
排名越靠前,得分越高;多路都召回的文档得分累加
"""
doc_scores = {}
for query, results in results_dict.items():
for rank, doc in enumerate(results):
doc_id = doc['id']
if doc_id not in doc_scores:
doc_scores[doc_id] = 0
# 排名越靠前,分数越高
doc_scores[doc_id] += 1 / (k + rank + 1)
# 按总分排序
sorted_docs = sorted(
doc_scores.items(),
key=lambda x: x[1],
reverse=True
)
return sorted_docs
方案三:置信度检测 + 诚实降级
核心思路: 系统主动检测自身的回答质量,当置信度不足时,诚实告知用户,而不是给出幻觉答案。
python
def confidence_aware_response(query, retrieved_docs, llm):
"""
置信度感知的回答策略
"""
# 计算最高相似度
max_similarity = max(doc['score'] for doc in retrieved_docs)
avg_similarity = sum(doc['score'] for doc in retrieved_docs) / len(retrieved_docs)
# 三档置信度策略
if max_similarity >= 0.75:
# 高置信度:正常回答
return {
"status": "confident",
"answer": llm.generate_answer(query, retrieved_docs),
"disclaimer": None
}
elif max_similarity >= 0.5:
# 中置信度:回答但加免责声明
return {
"status": "uncertain",
"answer": llm.generate_answer(query, retrieved_docs),
"disclaimer": "⚠️ 以下信息可能不完整,建议进一步核实"
}
else:
# 低置信度:诚实说明,引导正确渠道
return {
"status": "insufficient",
"answer": f"""
您的问题涉及较为专业的细分领域,
我的知识库中暂无精确匹配的文档。
我能提供的相关信息:
✓ 《退役军人保障法》一般性规定(可信度:高)
✓ 河北省土地管理通用政策(可信度:中)
✗ 石家庄市2021年专项细则(知识库未收录)
建议:拨打石家庄市退役军人事务局热线咨询
""",
"disclaimer": None
}
方案四:知识库主动扩充(数据飞轮)
长期解决方案:建立知识库自动扩充机制
用户提问
↓
系统检测到低置信度查询
↓
记录到"待补充清单"
↓
人工/自动补充相关文档
↓
更新知识库
↓
下次同类查询命中率提升
这个循环持续运转 → 知识库越来越完善
1.5 方案选型建议
┌───────────────────────────────────────────────────┐
│ 长尾查询解决方案选型 │
├────────────┬───────────────────────────────────────┤
│ 场景 │ 推荐方案 │
├────────────┼───────────────────────────────────────┤
│ 实时查询 │ HyDE + 多路召回(效果好,稍慢) │
│ 高并发场景 │ 查询扩展缓存(提前计算变体) │
│ 长期运营 │ 数据飞轮(持续扩充知识库) │
│ 兜底策略 │ 置信度检测(任何场景都应该有) │
└────────────┴───────────────────────────────────────┘
核心原则:宁可说"我不知道",也不给用户一个听起来
正确实则是幻觉的答案。
二、歧义指令
2.1 什么是歧义指令?
歧义指令指的是用户的查询存在多种合理的理解方向,RAG系统无法判断用户真正的意图,导致检索方向错误或多个方向混合,最终输出一个"四不像"的答案。
歧义的常见类型:
┌──────────────┬──────────────────────────┬────────────────────────┐
│ 歧义类型 │ 示例 │ 问题所在 │
├──────────────┼──────────────────────────┼────────────────────────┤
│ 词汇歧义 │ "苹果的最新动态" │ 水果 or 公司? │
│ 范围歧义 │ "总结一下" │ 总结哪个?多详细? │
│ 指代歧义 │ "它什么时候发布的" │ "它"指什么? │
│ 意图歧义 │ "怎么处理投诉" │ 客服?HR?技术?合规? │
│ 时态歧义 │ "最新的政策" │ 具体是哪年哪月? │
│ 角色歧义 │ "如何申请" │ 申请人视角?审批人视角? │
└──────────────┴──────────────────────────┴────────────────────────┘
2.2 真实案例还原
场景: 企业内部知识库系统,包含客服手册、HR政策、技术文档、合规指南。
用户提问:
"怎么处理投诉?"
这句话有四种完全不同的合理理解:
理解A → 客服人员视角
"客户对产品不满,客服如何处理客户投诉?"
应检索:客服手册、投诉处理SOP
理解B → HR人员视角
"员工对公司管理不满,HR如何处理员工申诉?"
应检索:HR政策、劳动仲裁流程
理解C → 技术人员视角
"用户提交了系统Bug投诉工单,如何处理?"
应检索:技术支持文档、工单系统规范
理解D → 合规人员视角
"监管部门对公司发出投诉,如何应对?"
应检索:合规手册、法务处理流程
RAG系统的错误处理过程:
Step 1:向量检索(不知道选哪个方向,全召回)
召回文档 相似度得分
─────────────────────────────────────────────
客服投诉处理手册第3章 0.71
HR员工申诉政策2023版 0.69 ← 分数都差不多!
技术工单处理规范 0.67
合规投诉应对指南 0.65
─────────────────────────────────────────────
Step 2:LLM拿到四个方向的文档,生成了"四不像"回答
"处理投诉需要注意以下几点:
1. 记录投诉内容并安抚客户情绪(客服内容)
2. 员工有权申请劳动仲裁(HR内容)
3. 工单需在24小时内响应(技术内容)
4. 向监管部门提交情况说明材料(合规内容)"
❌ 四个场景混在一起,用户完全懵了
❌ 每个部分都"正确",整体却毫无用处
2.3 为什么会出现这个问题?
根本原因链:
用户表达习惯简短、省略背景
↓
查询词语义空间覆盖多个方向
↓
向量检索召回多方向的文档(相似度相近)
↓
LLM没有足够信息判断用户真实意图
↓
LLM"好意"地把所有相关内容都融合进答案
↓
回答看似全面,实则对任何人都没有用
2.4 解决方案
方案一:歧义检测 + 主动澄清(最直接)
核心思路: 系统主动检测是否存在歧义,如果存在,直接向用户提问澄清,而不是猜测。
python
class AmbiguityDetector:
def detect_and_clarify(self, query, retriever, llm):
"""
歧义检测:分析检索结果,判断是否存在多意图
"""
# Step 1:检索并分析结果分布
results = retriever.search(query, top_k=10)
# 对文档按主题分组
topic_groups = self.cluster_by_topic(results)
# Step 2:计算各主题的最高得分
topic_scores = {
topic: max(doc['score'] for doc in docs)
for topic, docs in topic_groups.items()
}
# 示例结果:
# {"客服投诉": 0.71, "HR申诉": 0.69,
# "技术工单": 0.67, "合规应对": 0.65}
# Step 3:判断是否歧义
# 前两个主题的得分差 < 0.1,判定为歧义
sorted_topics = sorted(
topic_scores.items(),
key=lambda x: x[1],
reverse=True
)
top1_score = sorted_topics[0][1]
top2_score = sorted_topics[1][1]
is_ambiguous = (top1_score - top2_score) < 0.1
if is_ambiguous:
# Step 4:生成澄清问题
clarification = self.generate_clarification(
query, sorted_topics
)
return {
"is_ambiguous": True,
"response": clarification
}
else:
# 意图明确,正常回答
return {
"is_ambiguous": False,
"intent": sorted_topics[0][0]
}
def generate_clarification(self, query, topics):
return f"""
您好!您的问题"{query}"在我们系统中有几种不同的场景,
请问您指的是哪种情况?
[1] 🎧 客服场景:如何回应和处理客户的产品投诉
[2] 👥 HR场景:员工对公司管理的内部申诉流程
[3] 🖥️ 技术场景:Bug或故障投诉工单的处理规范
[4] ⚖️ 合规场景:应对监管部门的投诉举报
请选择编号,或用自己的话描述您的具体场景。
"""
用户回复 1 后的效果:
python
# 消歧后,精准检索单一方向
refined_query = "客服处理客户产品投诉流程"
results = retriever.search(refined_query)
# 回答变得精准:
"""
根据《客服手册》第3章,客户投诉处理流程如下:
第一步:2分钟内响应,使用标准话术
"感谢您的反馈,我们非常重视您的意见..."
第二步:记录投诉类型
├── 产品质量投诉 → 转品控部门,48小时回复
├── 服务态度投诉 → 主管介入,24小时处理
└── 物流问题投诉 → 联系物流商,提供追踪
第三步:处理完毕后,48小时内回访确认满意度
数据来源:客服手册v2.1第3章(2023年10月更新)
"""
✅ 精准、有用、来源清晰
方案二:上下文自动消歧(多轮对话)
核心思路: 利用对话历史自动推断用户意图,无需打断用户。
python
class ContextAwareRAG:
def resolve_with_context(self, current_query, conversation_history):
"""
利用对话历史自动消歧
"""
# 分析最近5轮对话
recent_context = conversation_history[-5:]
# 示例对话历史:
# [{"role": "user", "content": "我是新来的客服专员"},
# {"role": "assistant", "content": "欢迎!有什么可以帮您?"}]
# 提取上下文关键信息
context_clues = self.extract_clues(recent_context)
# context_clues = {"用户角色": "客服专员", "部门": "客服"}
# 根据上下文重写查询
if context_clues.get("用户角色") == "客服专员":
resolved_query = f"客服专员如何{current_query}"
# → "客服专员如何处理投诉"
confidence = "high"
elif context_clues.get("上一话题") == "技术问题":
resolved_query = f"技术支持{current_query}"
confidence = "medium"
else:
# 上下文不足,仍需澄清
resolved_query = current_query
confidence = "low"
return {
"resolved_query": resolved_query,
"confidence": confidence,
"reasoning": f"根据您之前提到的'{context_clues}',
推断您询问的是客服场景"
}
方案三:多假设并行处理(批处理/无法追问场景)
核心思路: 当系统无法向用户追问时(如批处理),同时检索所有可能方向,让LLM选择最可能的意图。
python
def multi_hypothesis_rag(ambiguous_query, retriever, llm):
"""
多假设并行:同时检索所有可能方向,LLM综合判断
适用于:批处理场景、无法交互的场景
"""
# Step 1:生成所有可能的查询假设
hypotheses = {
"客服场景": "客服处理客户投诉流程",
"HR场景": "HR处理员工内部申诉流程",
"技术场景": "技术工单投诉处理规范",
"合规场景": "合规监管投诉应对流程"
}
# Step 2:并行检索所有假设
all_results = {}
for scenario, query in hypotheses.items():
all_results[scenario] = retriever.search(query)
# Step 3:让LLM综合判断
context_text = "\n\n".join([
f"=== {scenario}方向的检索内容 ===\n{format_docs(docs)}"
for scenario, docs in all_results.items()
])
final_answer = llm.generate(f"""
用户原始问题:{ambiguous_query}
以下是针对不同理解方向检索到的内容:
{context_text}
请:
1. 判断用户最可能的意图
2. 基于对应方向的内容给出答案
3. 如果实在无法判断,列出所有可能并请用户确认
""")
return final_answer
2.5 方案选型建议
┌───────────────────────────────────────────────────────┐
│ 歧义指令解决方案选型 │
├─────────────────┬─────────────────────────────────────┤
│ 场景 │ 推荐方案 │
├─────────────────┼─────────────────────────────────────┤
│ 实时对话系统 │ 歧义检测 + 主动追问(用户体验最好) │
│ 多轮对话系统 │ 上下文自动消歧(无打扰,体验流畅) │
│ 批处理/无交互 │ 多假设并行 + LLM判断 │
│ 企业特定场景 │ 用户画像预设(根据角色预判意图) │
└─────────────────┴─────────────────────────────────────┘
核心原则:问清楚再回答,比猜错了再解释,
效率高得多,用户体验也好得多。
三、复杂推理错误
3.1 什么是复杂推理错误?
复杂推理错误指的是用户的问题需要多个步骤才能得出答案,但RAG系统只做了一次检索,获取的信息不足以支撑完整推理,导致推理链在某个环节断裂或出错。
常见的复杂推理类型:
┌───────────────────┬──────────────────────────────────────────┐
│ 推理类型 │ 示例 │
├───────────────────┼──────────────────────────────────────────┤
│ 多跳推理 │ "A公司CEO的母校校长是谁?"(需要3步) │
│ 数值计算 │ "连续涨薪后的最终工资"(复利 vs 简单加法) │
│ 时序推理 │ "入职N天后的具体日期和待遇变化" │
│ 条件推理 │ "同时满足A和B条件才能申请,我能申请吗?" │
│ 比较推理 │ "A方案和B方案哪个更划算?" │
│ 反事实推理 │ "如果没有采取X措施,会发生什么?" │
└───────────────────┴──────────────────────────────────────────┘
3.2 真实案例一:多跳推理失败
场景: 企业HR智能助手,知识库包含薪资政策、考勤制度、员工手册。
用户提问:
"我是P6级别的员工,在北京工作,
今年绩效是B+,年终奖大概能拿多少?"
正确的推理链需要3跳:
跳1:P6级别 ──────→ 对应薪资Band是多少?
↓
薪资Band:25k-35k/月,中位数30k
跳2:北京 + B+绩效 ──→ 年终奖系数是多少?
↓
绩效系数:B+ = 1.2个月薪资
跳3:月薪中位数 × 绩效系数 → 年终奖金额
↓
30,000 × 1.2 = 36,000元
❌ 系统的错误处理(只做了一次检索):
python
# 系统只做了一次检索
query = "P6员工北京B+绩效年终奖"
results = retriever.search(query)
# 只召回到:
# 文档1:年终奖发放时间说明(相关但不够)
# 文档2:绩效等级定义(只说B+是"良好")
# 文档3:北京地区地域补贴说明(不含年终奖)
# LLM拿着这些残缺信息,凑出了一个答案:
"""
根据公司政策,B+绩效属于良好等级,
年终奖通常在1-3个月薪资之间,
北京地区员工享有额外补贴,
建议咨询HR获取准确数字。
"""
❌ 完全没有用!
❌ "1-3个月"是编造的区间,毫无根据
❌ 推理链在跳1就断了,没有拿到薪资Band数据
✅ 迭代检索修复:
python
class IterativeRAG:
def solve_multi_hop(self, original_query):
"""
迭代检索:每一跳的结果作为下一跳的输入
"""
reasoning_chain = [] # 记录完整推理过程
accumulated_context = ""
# === 第1跳:查P6薪资Band ===
q1 = "P6级别对应的薪资Band范围是多少"
docs1 = self.retriever.search(q1)
answer1 = self.llm.extract(
question=q1,
documents=docs1
)
# answer1 = "P6对应Band为25k-35k/月,中位数约30k"
accumulated_context += f"P6薪资信息:{answer1}\n"
reasoning_chain.append({
"step": 1,
"question": q1,
"answer": answer1
})
# === 第2跳:查B+绩效系数(基于第1跳结果)===
q2 = "北京地区员工绩效B+对应的年终奖系数"
docs2 = self.retriever.search(q2)
answer2 = self.llm.extract(
question=q2,
documents=docs2,
context=accumulated_context # 带上第1跳的结果
)
# answer2 = "B+绩效系数为1.2个月薪资"
accumulated_context += f"B+绩效系数:{answer2}\n"
reasoning_chain.append({
"step": 2,
"question": q2,
"answer": answer2
})
# === 第3跳:综合计算 ===
q3 = "年终奖计算公式"
docs3 = self.retriever.search(q3)
# 调用计算工具(不让LLM自己算,避免数学错误)
calculation = calculator.eval("30000 * 1.2")
# calculation = 36000.0
# === 最终综合回答 ===
final_answer = self.llm.synthesize(f"""
原始问题:{original_query}
推理过程:
步骤1:P6薪资Band中位数约30,000元/月
步骤2:B+绩效对应系数1.2个月薪资
步骤3:30,000 × 1.2 = 36,000元
请基于以上推理过程,给出完整、清晰的回答。
同时注明数据来源。
""")
return {
"answer": final_answer,
"reasoning_chain": reasoning_chain # 可供用户核查
}
✅ 修复后的回答:
根据公司薪酬制度(2023版):
推理过程:
① P6级别薪资Band:25k-35k/月,中位数 30,000元
来源:薪酬管理制度v2.3 第4章
② B+绩效对应年终奖系数:1.2个月薪资
来源:绩效管理手册 第7条
③ 计算:30,000元 × 1.2 = 36,000元
✅ 预计年终奖约为 36,000元
(实际以HR最终核定为准,可能因入职时间等因素略有差异)
3.3 真实案例二:数值计算错误
用户提问:
"我月薪20000元,今年涨薪15%,明年再涨10%,
两年后月薪是多少?"
❌ LLM的错误推理:
LLM的思路(错误):
"涨薪15% + 再涨10% = 总涨幅25%
20000 × (1 + 25%) = 25000元"
错误原因:
连续涨薪是复利计算,不是简单相加!
正确计算:20000 × 1.15 × 1.10 = 25300元
误差:300元(看起来不多,但对于薪资谈判可能很关键)
✅ 工具调用修复:
python
def tool_augmented_calculation(query, llm, tools):
"""
数值推理:让LLM负责逻辑,工具负责计算
"""
# LLM分解计算步骤,但不直接给出数字
reasoning_plan = llm.generate(f"""
问题:{query}
请分解计算步骤,用工具调用格式输出,不要自己算数字:
格式:CALCULATE(表达式)
""")
# LLM输出:
# 思考:连续涨薪需要用复利计算,不能简单相加
# 第一年后:CALCULATE(20000 * 1.15)
# 第二年后:CALCULATE(23000 * 1.10)
# 解析并执行工具调用
results = {}
results['step1'] = tools['calculator'].eval("20000 * 1.15")
# = 23000.0
results['step2'] = tools['calculator'].eval("23000 * 1.10")
# = 25300.0
# LLM用计算结果生成最终回答
final_answer = llm.generate(f"""
计算结果:
第一年后月薪:{results['step1']}元
第二年后月薪:{results['step2']}元
请给出清晰的回答,并说明为什么不是简单的15%+10%=25%
""")
return final_answer
# ✅ 最终回答:
"""
两年后月薪计算(复利):
第一年:20,000 × 1.15 = 23,000元
第二年:23,000 × 1.10 = 25,300元
✅ 两年后月薪为 25,300元
注意:连续涨薪是复利计算,总涨幅为26.5%,
而非简单的15%+10%=25%。
差额300元,涨薪越大、年限越长,差距越明显。
"""
3.4 真实案例三:时序推理错误
用户提问:
"我3月15日入职,试用期3个月,
转正后才能申请年假,
我最早什么时候能申请年假?
那时候能有几天年假?"
这道题需要多步时序推理:
① 计算转正日期:3月15日 + 3个月 = ?
② 查询年假政策:转正当年按剩余月份折算
③ 计算剩余月份:转正日到12月31日有几个月?
④ 计算年假天数:剩余月份/12 × 年假基准天数
❌ 错误回答:
"试用期3个月后即可转正,转正后可享受5天年假。"
问题:
❌ 没有给出具体转正日期
❌ 5天是全年基准,不是当年折算后的天数
❌ 完全忽略了"按剩余月份折算"的规则
✅ ReAct框架修复(推理与行动交替):
python
class ReActRAG:
def solve(self, query):
"""
ReAct:Reasoning + Acting 交替执行
思考 → 行动 → 观察 → 思考 → 行动...
"""
scratchpad = "" # 记录完整推理轨迹
# === 轮次1 ===
thought1 = "需要先计算转正日期,3月15日加3个月"
action1 = "date_calculator"
input1 = {"start": "2024-03-15", "add_months": 3}
obs1 = self.tools['date_calculator'](**input1)
# obs1 = "2024-06-15"
scratchpad += f"""
思考:{thought1}
行动:{action1}({input1})
观察:转正日期为 {obs1}
"""
# === 轮次2 ===
thought2 = "需要查询年假折算政策"
action2 = "retriever"
input2 = "转正当年年假按剩余月份折算规则"
obs2 = self.retriever.search(input2)
# obs2 = "转正当年年假按剩余月份折算,工龄<1年享5天/年"
scratchpad += f"""
思考:{thought2}
行动:{action2}("{input2}")
观察:{obs2}
"""
# === 轮次3 ===
thought3 = "计算2024年6月15日到12月31日有多少个月"
action3 = "date_calculator"
input3 = {
"from_date": "2024-06-15",
"to_date": "2024-12-31",
"unit": "months"
}
obs3 = self.tools['date_calculator'](**input3)
# obs3 = 6.5
scratchpad += f"""
思考:{thought3}
行动:{action3}({input3})
观察:剩余 {obs3} 个月
"""
# === 轮次4 ===
thought4 = "计算年假天数:6.5/12 × 5天,向下取整"
action4 = "calculator"
input4 = "floor(6.5 / 12 * 5)"
obs4 = self.tools['calculator'].eval(input4)
# obs4 = 2
scratchpad += f"""
思考:{thought4}
行动:{action4}("{input4}")
观察:年假天数 = {obs4} 天
"""
# === 最终回答 ===
final = self.llm.generate(f"""
基于以下推理过程,给出清晰的最终回答:
{scratchpad}
""")
return final
# ✅ 最终回答:
"""
📅 根据公司员工手册第8章第3条,您的情况分析如下:
入职日期:2024年3月15日
转正日期:2024年6月15日(3个月试用期届满)
2024年年假计算:
转正后剩余月份:6.5个月(6月15日至12月31日)
年假折算:6.5 ÷ 12 × 5天 = 2.7天 → 取整 2天
✅ 最早申请年假时间:2024年6月15日(转正当日)
✅ 2024年可用年假:2天
✅ 2025年起(满1年):享受完整5天年假
数据来源:《员工手册》第8章第3条(2023年版)
"""
3.5 解决方案汇总
python
"""
三种核心解法,对应不同复杂度场景
"""
# 解法一:问题分解 + 迭代检索
# 适用:多跳推理(需要多步信息收集)
def iterative_retrieval(complex_query):
sub_questions = decompose(complex_query)
context = ""
for q in sub_questions:
docs = retriever.search(q + context)
answer = llm.extract(q, docs)
context += answer
return synthesize(complex_query, context)
# 解法二:工具调用
# 适用:数值计算、日期计算、数据库查询
def tool_augmented(query):
plan = llm.plan(query) # LLM只负责规划
result = tools.execute(plan) # 工具负责精确执行
return llm.explain(result) # LLM负责解释结果
# 解法三:ReAct框架
# 适用:时序推理、条件推理(需要灵活交替推理和检索)
def react(query):
while not finished:
thought = llm.think(query, scratchpad)
action = llm.decide(thought)
observation = execute(action)
scratchpad += (thought, action, observation)
return llm.conclude(scratchpad)
3.6 方案选型建议
┌────────────────────────────────────────────────────────┐
│ 复杂推理解决方案选型 │
├──────────────────┬─────────────────────────────────────┤
│ 问题类型 │ 推荐方案 │
├──────────────────┼─────────────────────────────────────┤
│ 2-4跳信息收集 │ 问题分解 + 迭代检索 │
│ 数值/日期计算 │ 工具调用(计算器、日期计算器) │
│ 复杂条件判断 │ ReAct框架(灵活推理) │
│ 数据库查询统计 │ Text-to-SQL │
│ 高风险业务场景 │ 以上方案 + 人工验证 │
└──────────────────┴─────────────────────────────────────┘
核心原则:问题太复杂,就把它拆小;
需要计算,就用工具而不是让LLM心算。
四、三大问题全景对比
┌──────────────┬──────────────────┬─────────────────┬─────────────────┐
│ │ 长尾查询 │ 歧义指令 │ 复杂推理错误 │
├──────────────┼──────────────────┼─────────────────┼─────────────────┤
│ 核心症状 │ 检索相似度全低 │ 回答东拼西凑 │ 推理链断裂 │
│ │ 回答空洞是幻觉 │ 答非所问 │ 数字计算错误 │
├──────────────┼──────────────────┼─────────────────┼─────────────────┤
│ 根本原因 │ 知识库没有覆盖 │ 查询含多种意图 │ 单次检索信息不足 │
├──────────────┼──────────────────┼─────────────────┼─────────────────┤
│ 核心解法 │ HyDE │ 歧义检测 │ 问题分解 │
│ │ 多路召回融合 │ 主动澄清 │ 迭代检索 │
│ │ 置信度降级 │ 上下文消歧 │ 工具调用 │
├──────────────┼──────────────────┼─────────────────┼─────────────────┤
│ 记忆口诀 │ 问题太偏 │ 问题太模糊 │ 问题太复杂 │
│ │ 扩展+诚实 │ 问清再回答 │ 分步再推理 │
└──────────────┴──────────────────┴─────────────────┴─────────────────┘
五、系统架构建议
用户查询
↓
┌──────────────────────────────────┐
│ 查询理解层 │
│ · 歧义检测(意图分布分析) │
│ · 复杂度评估(推理跳数估算) │
│ · 实体提取(关键词识别) │
└─────────────────┬────────────────┘
↓
┌──────────────────────────────────┐
│ 自适应检索层 │
│ 简单明确 → 单次标准检索 │
│ 长尾查询 → HyDE + 多路召回 │
│ 歧义查询 → 澄清 or 多假设并行 │
│ 复杂推理 → 问题分解 + 迭代检索 │
└─────────────────┬────────────────┘
↓
┌──────────────────────────────────┐
│ 推理与生成层 │
│ · CoT思维链推理 │
│ · ReAct行动框架 │
│ · 外部工具调用(计算/查询) │
│ · 自我验证与修正 │
└─────────────────┬────────────────┘
↓
┌──────────────────────────────────┐
│ 质量控制层 │
│ · 置信度评分 │
│ · 来源引用验证 │
│ · 幻觉检测 │
│ · 用户反馈收集 → 持续改进 │
└──────────────────────────────────┘
六、总结
| 问题 | 本质 | 最重要的一个解法 |
|---|---|---|
| 长尾查询 | 知识边界问题 | 诚实的置信度降级 |
| 歧义指令 | 意图理解问题 | 主动追问澄清 |
| 复杂推理 | 信息整合问题 | 问题分解+迭代检索 |
最后一句话:
RAG系统的核心不是让LLM"更聪明",
而是让它在对的时候说对的话,
在不确定的时候说不确定,
在问题复杂的时候拆解再回答。
欢迎交流讨论,共同完善RAG系统的工程实践 🙌