【AI应用开发】怎么降低 Agent 幻觉?有几种可行方案?
目录
- Agent 幻觉的三种类型
- 根因分析
- 八种降低幻觉的方案
- 方案一:强制引用溯源
- 方案二:事实核查层
- 方案三:多模型交叉验证
- 方案四:检索增强(RAG)
- 方案五:输出约束与校验
- 方案六:不确定性量化
- 方案七:人类反馈闭环
- 方案八:Anti-Hallucination Prompt
- 组合策略与效果评估
1. Agent 幻觉的三种类型
类型A: 事实幻觉 用户: "2024年GDP增速多少?" Agent: "根据最新数据,2024年GDP增速为5.2%。" ← 编造的具体数字 类型B: 来源幻觉 用户: "退货政策是什么?" Agent: "根据《消费者权益保护法》第25条..." ← 编造的法律条文引用 类型C: 逻辑幻觉 工具返回: 订单已发货 Agent: "您的订单将在今天下午3点送达。" ← 没有物流数据支持,自己推断的Agent 比普通 LLM 更容易产生幻觉,因为 Agent 在多步推理中每一步都可能产生幻觉,然后后续步骤基于虚假信息继续推理,形成"幻觉级联"(Hallucination Cascade)。
2. 根因分析
| 原因 | 说明 |
|---|---|
| 知识边界模糊 | LLM 不知道自己不知道什么 |
| 概率本质 | LLM 本质是"合理续写",不是"事实核查" |
| 上下文干扰 | Agent 多步推理中,前面的误解影响后续判断 |
| 工具返回误解 | LLM 曲解了工具返回的数据 |
| 缺乏验证机制 | 默认信任 LLM 输出,没有独立的事实核查 |
3. 八种降低幻觉的方案
防御层次: 第一道防线(Prompt层): Prompt 约束 + Few-Shot 第二道防线(检索层): RAG 检索增强 第三道防线(输出层): 强制引用 + 输出校验 第四道防线(验证层): 事实核查 + 交叉验证 第五道防线(反馈层): 不确定性量化 + 人工反馈4. 方案一:强制引用溯源
要求 Agent 的每个事实性声明都必须标注来源:
CITATION_PROMPT="""你是一个严谨的智能助手。遵循以下铁律: 1. 对于任何事实性陈述(数字、日期、政策、流程、价格等),必须标注来源 2. 来源标注格式: [来源: 工具名/文档名] 3. 如果某个信息来自你的"知识"(训练数据),而不是当前工具返回的结果, 必须标注为 [来源: 模型内部知识 - 请核实] 4. 如果不确定某个信息是否正确,请直接说明"根据现有信息无法确定" 5. 绝对禁止编造以下内容: - 具体的数字和统计数据 - 法律法规条款 - 价格和金额 - 人名、日期、地点 示例: ✅ "根据知识库,退货需要在收到商品后7天内申请 [来源: 退货政策文档]" ✅ "订单 ORD-2024-00123 的状态为'已发货' [来源: query_order 工具返回]" ❌ "您的包裹将在明天送达"(没有物流数据支持) ❌ "根据公司规定,退款在3-5个工作日到账"(没有确认该规定的来源) """4.1 自动化引用验证
classCitationValidator:"""验证 Agent 回答中的引用是否真实"""defvalidate(self,response:str,tool_results:list,retrieved_docs:list)->dict:""" 检查 Agent 回答中标注的引用是否真实来自工具返回 """# 1. 提取回答中标注的引用cited_sources=re.findall(r'\[来源: (.+?)\]',response)# 2. 收集所有真实的来源real_sources=set()forrintool_results:real_sources.add(r["tool"])fordinretrieved_docs:real_sources.add(d.get("metadata",{}).get("source",""))# 3. 交叉检查violations=[]forcitedincited_sources:# 检查引用是否在真实来源中ifcited=="模型内部知识 - 请核实":violations.append({"type":"uncited_knowledge","source":cited,"risk":"medium"})elifcitednotinreal_sourcesand"请核实"notincited:violations.append({"type":"fake_citation","claimed":cited,"risk":"high"})# 4. 检查是否有事实性声明没有标注来源fact_statements=self._extract_fact_statements(response)cited_sentences=self._find_cited_sentences(response)uncited_facts=[fforfinfact_statementsiffnotincited_sentences]ifuncited_facts:violations.append({"type":"uncited_facts","examples":uncited_facts[:3],"risk":"medium"})return{"has_violations":len(violations)>0,"violations":violations,"hallucination_risk":"high"ifany(v["risk"]=="high"forvinviolations)else"medium"ifviolationselse"low"}def_extract_fact_statements(self,text):"""提取事实性声明(含数字、日期、专有名词的句子)"""facts=[]forsentinre.split(r'[。!\n]',text):ifre.search(r'\d+|[A-Z]{2,}|政策|规定|法律|价格|金额',sent):facts.append(sent.strip())returnfactsdef_find_cited_sentences(self,text):"""找出已标注来源的句子"""cited=set()formatchinre.finditer(r'\[来源: (.+?)\]',text):# 找到引用前的句子before=text[:match.start()]sentences=re.split(r'[。!\n]',before)ifsentences:cited.add(sentences[-1].strip())returncited5. 方案二:事实核查层
在 Agent 输出最终回答前,增加一个独立的事实核查步骤:
classFactChecker:"""独立的事实核查模块"""def__init__(self,llm,search_tool):self.llm=llm self.search_tool=search_tool FACT_CHECK_PROMPT="""你是事实核查员。对以下 Agent 回答进行逐条核查: Agent 回答: {agent_response} 该回答基于以下工具返回数据: {tool_data} 核查规则: 1. 每条声明必须有工具数据或检索结果支撑 2. 数字、日期、名称必须精确匹配来源 3. 推断性结论标记为"推断"(非事实) 4. 超出工具数据范围的内容标记为"未验证" 输出 JSON: {{ "verified": [ {{"statement": "声明内容", "source": "来源", "confidence": 0.9}} ], "unverified": [ {{"statement": "声明内容", "reason": "无法在工具数据中找到支撑", "risk": "high/medium/low"}} ], "contradictions": [ {{"statement": "声明内容", "conflict_with": "冲突来源", "resolution": "建议"}} ], "overall_hallucination_risk": "high/medium/low" }}"""asyncdefcheck(self,agent_response,tool_results,retrieved_docs):tool_data=json.dumps([{"tool":r["tool"],"result":str(r["result"])[:500]}forrintool_results],ensure_ascii=False,indent=2)result=awaitself.llm.chat(self.FACT_CHECK_PROMPT.format(agent_response=agent_response,tool_data=tool_data))check_result=json.loads(JSONFixer.extract_and_fix(result))# 如果风险高,可以触发重试或标记ifcheck_result.get("overall_hallucination_risk")=="high":return{"approved":False,"check_result":check_result,"action":"rewrite_or_flag"}return{"approved":len(check_result.get("unverified",[]))<=1,"check_result":check_result,"action":"approved"iflen(check_result.get("unverified",[]))<=1else"review"}6. 方案三:多模型交叉验证
classCrossModelValidator:"""用不同模型验证输出一致性"""def__init__(self,primary_model,secondary_model):self.primary=primary_model# 例如 GPT-4oself.secondary=secondary_model# 例如 Claude 3.5asyncdefgenerate_with_verification(self,prompt,context):# 1. 主模型生成primary_response=awaitself.primary.chat(prompt,context)# 2. 验证模型独立生成(不看到主模型的答案)secondary_response=awaitself.secondary.chat(prompt,context)# 3. 一致性分析consistency=awaitself._analyze_consistency(primary_response,secondary_response)ifconsistency["agreement_score"]<0.7:# 分歧大 → 让验证模型仲裁returnawaitself._arbitrate(primary_response,secondary_response,consistency,prompt)# 一致 → 返回主模型答案returnprimary_responseasyncdef_analyze_consistency(self,resp1,resp2):"""分析两个答案的一致性"""analysis_prompt=f"""比较以下两个回答的一致性: 回答A:{resp1[:1000]}回答B:{resp2[:1000]}输出 JSON: {{ "agreement_score": 0.0-1.0, // 整体一致性 "agreed_facts": ["事实1", ...], // 双方一致的事实 "disagreed_facts": [ // 有分歧的事实 {{"fact": "...", "version_a": "...", "version_b": "...", "severity": "high/medium/low"}} ], "recommendation": "trust_a / trust_b / merge / flag" }}"""result=awaitself.secondary.chat(analysis_prompt)returnjson.loads(JSONFixer.extract_and_fix(result))7. 方案四:检索增强(RAG)
这是最有效的方案之一——用检索到的真实文档约束 LLM 的输出:
classRAGConstrainedAgent:"""检索约束的 Agent —— 强制基于检索结果回答"""def__init__(self,llm,retriever):self.llm=llm self.retriever=retrieverasyncdefanswer(self,question):# 检索相关文档docs=awaitself.retriever.search(question,k=5)# 强制约束 Promptprompt=f"""基于以下文档回答问题。 === 铁律 === 1. 只能使用文档中的信息,不得添加任何文档中不存在的信息 2. 如果文档中没有答案,直接说"根据现有资料无法回答" 3. 引用文档内容时必须标注来源段落 4. 如果不同文档有矛盾,列出矛盾并说明,不要自行判断 === 参考文档 ==={self._format_docs(docs)}=== 问题 ==={question}=== 回答 ===(严格遵守铁律)"""response=awaitself.llm.chat(prompt)# 后验证:检查回答是否超出文档范围returnawaitself._verify_against_docs(response,docs)asyncdef_verify_against_docs(self,response,docs):"""验证回答是否都在文档范围内"""all_doc_text=" ".join(d["content"]fordindocs)verify_prompt=f"""检查以下回答中的每条声明,是否能在参考文档中找到依据。 回答:{response[:1000]}文档:{all_doc_text[:2000]}对回答中每条事实性声明,标注: verified / not_found / contradicted 如果不确定,标记为 uncertain 输出 JSON: {{"statements": [{{"text": "...", "status": "verified/not_found/contradicted"}}]}}"""result=awaitself.llm.chat(verify_prompt)verification=json.loads(JSONFixer.extract_and_fix(result))# 清理违规内容hallucinated=[sforsinverification.get("statements",[])ifs["status"]in("not_found","contradicted")]return{"response":response,"hallucinated_statements":hallucinated,"clean":len(hallucinated)==0}8. 方案五:输出约束与校验
classOutputGuard:"""输出安全守卫"""BLACKLIST_PATTERNS=[r"根据《.{2,20}法》第\d+条",# 编造法律条文r"据(最新|权威)数据(显示|表明)",# 模糊引用r"\d{4}年.{0,5}(增长|下降)\d+%",# 精确统计数字]defcheck(self,response:str,available_data:dict)->dict:"""检查输出中是否有不安全的模式"""warnings=[]# 1. 黑名单模式检查forpatterninself.BLACKLIST_PATTERNS:matches=re.findall(pattern,response)formatchinmatches:# 检查是否在可用数据中ifmatchnotinstr(available_data):warnings.append({"type":"suspicious_pattern","match":match,"risk":"high"})# 2. 数值范围检查numbers=re.findall(r'\d+\.?\d*',response)fornum_strinnumbers:num=float(num_str)ifnum>1000000:# 大数值更可能是编造的warnings.append({"type":"large_number","value":num,"risk":"medium"})# 3. 时间检查dates=re.findall(r'(\d{4})年(\d{1,2})月(\d{1,2})日',response)fromdatetimeimportdatetime now=datetime.now()fory,m,dindates:dt=datetime(int(y),int(m),int(d))ifdt>now:warnings.append({"type":"future_date","date":f"{y}-{m}-{d}","risk":"high"})return{"safe":len(warnings)==0,"warnings":warnings,"recommendation":"rewrite"ifany(w["risk"]=="high"forwinwarnings)else"review"}9. 方案六:不确定性量化
让 LLM 自己标注回答中每个部分的确定性:
UNCERTAINTY_PROMPT="""在回答用户问题时,同时对每段回答标注确定性级别: 确定性级别定义: - [确定] = 信息直接来自工具返回或检索文档 - [基本确定] = 信息来自推理,但有充分依据 - [不确定] = 信息来自一般知识,未经验证 - [无法确定] = 纯粹推测 格式: [确定] 您的订单 ORD-2024-00123 状态为"已发货"。 [基本确定] 根据发货时间,预计2-3天后到达。 [不确定] 物流公司通常在上午派送。 [无法确定] 具体派送时间可能需要联系物流公司确认。 """10. 方案七:人类反馈闭环
classHumanFeedbackLoop:"""人工反馈闭环 —— 标记幻觉用于持续改进"""def__init__(self):self.feedback_db=[]# 实际应用应持久化defcollect(self,session_id,response,user_feedback):"""收集用户对幻觉的反馈"""ifuser_feedback.get("has_hallucination"):self.feedback_db.append({"session_id":session_id,"response":response,"hallucination_detail":user_feedback.get("detail"),"timestamp":time.time()})defgenerate_anti_hallucination_examples(self)->list:"""从反馈中生成反面示例用于 Few-Shot Prompt"""examples=[]forfbinself.feedback_db[-10:]:examples.append(f""" ❌ 错误回答(有幻觉):{fb['response'][:200]}问题:{fb['hallucination_detail']}""")returnexamplesdefget_stats(self)->dict:"""获取幻觉统计"""return{"total_feedback":len(self.feedback_db),"hallucination_rate":(len(self.feedback_db)/max(1,len(self.feedback_db))),}11. 方案八:Anti-Hallucination Prompt
ANTI_HALLUCINATION_SYSTEM_PROMPT="""# 角色与原则 你是严格遵循事实的智能助手。 # 绝对禁止(违反即失败) 1. 禁止编造任何不在工具返回或检索结果中的数字、日期、人名、地名 2. 禁止编造法律条文、公司政策、产品功能 3. 禁止说"根据最新数据/研究显示"除非你有具体的数据来源 4. 禁止对工具返回的结果做超出范围的推断 5. 禁止填补信息空白——不知道就说不知道 # 必须遵守 1. 每条事实性信息标注来源 2. 区分"工具返回的数据"和"自己的推断" 3. 对推断性内容使用"推测"、"可能"、"建议核实"等措辞 4. 工具返回空或报错时,如实告知用户,不要编造替代答案 5. 如果回答依赖的信息不完整,主动告知用户缺失了哪些信息 # 不确定性表达指南 - 100%确定 → "根据[来源],[事实]" - 90%确定 → "根据[来源]推测,[结论]" - 50-90% → "[分析],但需要进一步确认[细节]" - <50% → "我不确定。根据现有信息无法判断。建议您[替代方案]" # 典型错误(不要犯) ❌ 工具返回"订单已发货" → "您的包裹明天到达"(没有物流轨迹,纯推测) ❌ 工具返回"库存充足" → "库存在100件以上"(工具没说具体数字) ❌ 检索到"退货政策" → "根据《消费者权益保护法》..."(文档里没有这条法律) ❌ 查询失败 → "虽然查不到,但一般来说..."(承认失败,不要替代编造) """12. 组合策略与效果评估
classAntiHallucinationAgent:"""综合防幻觉 Agent —— 集成多种方案"""def__init__(self):self.citation_validator=CitationValidator()self.fact_checker=FactChecker(llm,search_tool)self.output_guard=OutputGuard()self.cross_validator=CrossModelValidator(llm,claude)# 统计self.stats={"total":0,"hallucination_blocked":0}asyncdefsafe_generate(self,question,tools,context):self.stats["total"]+=1# 1. 检索增强docs=awaitself.retriever.search(question,k=5)# 2. Agent 生成(使用 Anti-Hallucination Prompt)response=awaitself.agent.run(question,system_prompt=ANTI_HALLUCINATION_SYSTEM_PROMPT,tools=tools,context={"retrieved_docs":docs})# 3. 引用验证citation_check=self.citation_validator.validate(response,context["tool_results"],docs)ifcitation_check["has_violations"]:# 标记但可能仍返回(取决于风险等级)pass# 4. 输出安全检查guard_check=self.output_guard.check(response,context)ifnotguard_check["safe"]:self.stats["hallucination_blocked"]+=1return"抱歉,生成的回答存在不确定性,请重新描述您的问题。"# 5. 可选项:事实核查(高价值场景)ifself._is_high_stakes(question):fact_check=awaitself.fact_checker.check(response,context["tool_results"],docs)ifnotfact_check["approved"]:# 重试或降级returnawaitself._regenerate_with_facts(question,tools,context,fact_check)returnresponse方案效果对比
| 方案 | 幻觉降低 | 额外延迟 | 额外成本 | 适用 |
|---|---|---|---|---|
| 强制引用 | -30% | +0.1s | 低 | 所有场景 |
| RAG约束 | -50% | +0.5s | 中 | 知识密集型 |
| 事实核查 | -40% | +2s | 高 | 高风险场景 |
| 交叉验证 | -45% | +3s | 很高 | 关键决策 |
| 不确定性量化 | -20% | +0.1s | 低 | 用户体验 |
| 输出守卫 | -15% | +0.05s | 低 | 安全兜底 |
推荐组合: 强制引用 + RAG约束 + 输出守卫(覆盖 80% 场景,低延迟低成本)。高风险场景额外加事实核查。