【AI应用开发】怎么降低 Agent 幻觉?有几种可行方案?
目录
- [Agent 幻觉的三种类型](#Agent 幻觉的三种类型)
- 根因分析
- 八种降低幻觉的方案
- 方案一:强制引用溯源
- 方案二:事实核查层
- 方案三:多模型交叉验证
- 方案四:检索增强(RAG)
- 方案五:输出约束与校验
- 方案六:不确定性量化
- 方案七:人类反馈闭环
- [方案八:Anti-Hallucination Prompt](#方案八: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 的每个事实性声明都必须标注来源:
python
CITATION_PROMPT = """你是一个严谨的智能助手。遵循以下铁律:
1. 对于任何事实性陈述(数字、日期、政策、流程、价格等),必须标注来源
2. 来源标注格式: [来源: 工具名/文档名]
3. 如果某个信息来自你的"知识"(训练数据),而不是当前工具返回的结果,
必须标注为 [来源: 模型内部知识 - 请核实]
4. 如果不确定某个信息是否正确,请直接说明"根据现有信息无法确定"
5. 绝对禁止编造以下内容:
- 具体的数字和统计数据
- 法律法规条款
- 价格和金额
- 人名、日期、地点
示例:
✅ "根据知识库,退货需要在收到商品后7天内申请 [来源: 退货政策文档]"
✅ "订单 ORD-2024-00123 的状态为'已发货' [来源: query_order 工具返回]"
❌ "您的包裹将在明天送达"(没有物流数据支持)
❌ "根据公司规定,退款在3-5个工作日到账"(没有确认该规定的来源)
"""
4.1 自动化引用验证
python
class CitationValidator:
"""验证 Agent 回答中的引用是否真实"""
def validate(self, response: str,
tool_results: list,
retrieved_docs: list) -> dict:
"""
检查 Agent 回答中标注的引用是否真实来自工具返回
"""
# 1. 提取回答中标注的引用
cited_sources = re.findall(r'\[来源: (.+?)\]', response)
# 2. 收集所有真实的来源
real_sources = set()
for r in tool_results:
real_sources.add(r["tool"])
for d in retrieved_docs:
real_sources.add(d.get("metadata", {}).get("source", ""))
# 3. 交叉检查
violations = []
for cited in cited_sources:
# 检查引用是否在真实来源中
if cited == "模型内部知识 - 请核实":
violations.append({
"type": "uncited_knowledge",
"source": cited,
"risk": "medium"
})
elif cited not in real_sources and "请核实" not in cited:
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 = [f for f in fact_statements
if f not in cited_sentences]
if uncited_facts:
violations.append({
"type": "uncited_facts",
"examples": uncited_facts[:3],
"risk": "medium"
})
return {
"has_violations": len(violations) > 0,
"violations": violations,
"hallucination_risk": "high" if any(
v["risk"] == "high" for v in violations
) else "medium" if violations else "low"
}
def _extract_fact_statements(self, text):
"""提取事实性声明(含数字、日期、专有名词的句子)"""
facts = []
for sent in re.split(r'[。!\n]', text):
if re.search(r'\d+|[A-Z]{2,}|政策|规定|法律|价格|金额', sent):
facts.append(sent.strip())
return facts
def _find_cited_sentences(self, text):
"""找出已标注来源的句子"""
cited = set()
for match in re.finditer(r'\[来源: (.+?)\]', text):
# 找到引用前的句子
before = text[:match.start()]
sentences = re.split(r'[。!\n]', before)
if sentences:
cited.add(sentences[-1].strip())
return cited
5. 方案二:事实核查层
在 Agent 输出最终回答前,增加一个独立的事实核查步骤:
python
class FactChecker:
"""独立的事实核查模块"""
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"
}}"""
async def check(self, agent_response, tool_results, retrieved_docs):
tool_data = json.dumps(
[{"tool": r["tool"], "result": str(r["result"])[:500]}
for r in tool_results],
ensure_ascii=False, indent=2
)
result = await self.llm.chat(
self.FACT_CHECK_PROMPT.format(
agent_response=agent_response,
tool_data=tool_data
)
)
check_result = json.loads(JSONFixer.extract_and_fix(result))
# 如果风险高,可以触发重试或标记
if check_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" if len(check_result.get("unverified", [])) <= 1 else "review"
}
6. 方案三:多模型交叉验证
python
class CrossModelValidator:
"""用不同模型验证输出一致性"""
def __init__(self, primary_model, secondary_model):
self.primary = primary_model # 例如 GPT-4o
self.secondary = secondary_model # 例如 Claude 3.5
async def generate_with_verification(self, prompt, context):
# 1. 主模型生成
primary_response = await self.primary.chat(prompt, context)
# 2. 验证模型独立生成(不看到主模型的答案)
secondary_response = await self.secondary.chat(prompt, context)
# 3. 一致性分析
consistency = await self._analyze_consistency(
primary_response, secondary_response
)
if consistency["agreement_score"] < 0.7:
# 分歧大 → 让验证模型仲裁
return await self._arbitrate(
primary_response, secondary_response,
consistency, prompt
)
# 一致 → 返回主模型答案
return primary_response
async def _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 = await self.secondary.chat(analysis_prompt)
return json.loads(JSONFixer.extract_and_fix(result))
7. 方案四:检索增强(RAG)
这是最有效的方案之一------用检索到的真实文档约束 LLM 的输出:
python
class RAGConstrainedAgent:
"""检索约束的 Agent ------ 强制基于检索结果回答"""
def __init__(self, llm, retriever):
self.llm = llm
self.retriever = retriever
async def answer(self, question):
# 检索相关文档
docs = await self.retriever.search(question, k=5)
# 强制约束 Prompt
prompt = f"""基于以下文档回答问题。
=== 铁律 ===
1. 只能使用文档中的信息,不得添加任何文档中不存在的信息
2. 如果文档中没有答案,直接说"根据现有资料无法回答"
3. 引用文档内容时必须标注来源段落
4. 如果不同文档有矛盾,列出矛盾并说明,不要自行判断
=== 参考文档 ===
{self._format_docs(docs)}
=== 问题 ===
{question}
=== 回答 ===(严格遵守铁律)"""
response = await self.llm.chat(prompt)
# 后验证:检查回答是否超出文档范围
return await self._verify_against_docs(response, docs)
async def _verify_against_docs(self, response, docs):
"""验证回答是否都在文档范围内"""
all_doc_text = " ".join(d["content"] for d in docs)
verify_prompt = f"""检查以下回答中的每条声明,是否能在参考文档中找到依据。
回答: {response[:1000]}
文档: {all_doc_text[:2000]}
对回答中每条事实性声明,标注: verified / not_found / contradicted
如果不确定,标记为 uncertain
输出 JSON: {{"statements": [{{"text": "...", "status": "verified/not_found/contradicted"}}]}}"""
result = await self.llm.chat(verify_prompt)
verification = json.loads(JSONFixer.extract_and_fix(result))
# 清理违规内容
hallucinated = [s for s in verification.get("statements", [])
if s["status"] in ("not_found", "contradicted")]
return {
"response": response,
"hallucinated_statements": hallucinated,
"clean": len(hallucinated) == 0
}
8. 方案五:输出约束与校验
python
class OutputGuard:
"""输出安全守卫"""
BLACKLIST_PATTERNS = [
r"根据《.{2,20}法》第\d+条", # 编造法律条文
r"据(最新|权威)数据(显示|表明)", # 模糊引用
r"\d{4}年.{0,5}(增长|下降)\d+%", # 精确统计数字
]
def check(self, response: str, available_data: dict) -> dict:
"""检查输出中是否有不安全的模式"""
warnings = []
# 1. 黑名单模式检查
for pattern in self.BLACKLIST_PATTERNS:
matches = re.findall(pattern, response)
for match in matches:
# 检查是否在可用数据中
if match not in str(available_data):
warnings.append({
"type": "suspicious_pattern",
"match": match,
"risk": "high"
})
# 2. 数值范围检查
numbers = re.findall(r'\d+\.?\d*', response)
for num_str in numbers:
num = float(num_str)
if num > 1000000: # 大数值更可能是编造的
warnings.append({
"type": "large_number",
"value": num,
"risk": "medium"
})
# 3. 时间检查
dates = re.findall(r'(\d{4})年(\d{1,2})月(\d{1,2})日', response)
from datetime import datetime
now = datetime.now()
for y, m, d in dates:
dt = datetime(int(y), int(m), int(d))
if dt > now:
warnings.append({
"type": "future_date",
"date": f"{y}-{m}-{d}",
"risk": "high"
})
return {
"safe": len(warnings) == 0,
"warnings": warnings,
"recommendation": "rewrite" if any(
w["risk"] == "high" for w in warnings
) else "review"
}
9. 方案六:不确定性量化
让 LLM 自己标注回答中每个部分的确定性:
python
UNCERTAINTY_PROMPT = """在回答用户问题时,同时对每段回答标注确定性级别:
确定性级别定义:
- [确定] = 信息直接来自工具返回或检索文档
- [基本确定] = 信息来自推理,但有充分依据
- [不确定] = 信息来自一般知识,未经验证
- [无法确定] = 纯粹推测
格式:
[确定] 您的订单 ORD-2024-00123 状态为"已发货"。
[基本确定] 根据发货时间,预计2-3天后到达。
[不确定] 物流公司通常在上午派送。
[无法确定] 具体派送时间可能需要联系物流公司确认。
"""
10. 方案七:人类反馈闭环
python
class HumanFeedbackLoop:
"""人工反馈闭环 ------ 标记幻觉用于持续改进"""
def __init__(self):
self.feedback_db = [] # 实际应用应持久化
def collect(self, session_id, response, user_feedback):
"""收集用户对幻觉的反馈"""
if user_feedback.get("has_hallucination"):
self.feedback_db.append({
"session_id": session_id,
"response": response,
"hallucination_detail": user_feedback.get("detail"),
"timestamp": time.time()
})
def generate_anti_hallucination_examples(self) -> list:
"""从反馈中生成反面示例用于 Few-Shot Prompt"""
examples = []
for fb in self.feedback_db[-10:]:
examples.append(f"""
❌ 错误回答(有幻觉): {fb['response'][:200]}
问题: {fb['hallucination_detail']}
""")
return examples
def get_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
python
ANTI_HALLUCINATION_SYSTEM_PROMPT = """# 角色与原则
你是严格遵循事实的智能助手。
# 绝对禁止(违反即失败)
1. 禁止编造任何不在工具返回或检索结果中的数字、日期、人名、地名
2. 禁止编造法律条文、公司政策、产品功能
3. 禁止说"根据最新数据/研究显示"除非你有具体的数据来源
4. 禁止对工具返回的结果做超出范围的推断
5. 禁止填补信息空白------不知道就说不知道
# 必须遵守
1. 每条事实性信息标注来源
2. 区分"工具返回的数据"和"自己的推断"
3. 对推断性内容使用"推测"、"可能"、"建议核实"等措辞
4. 工具返回空或报错时,如实告知用户,不要编造替代答案
5. 如果回答依赖的信息不完整,主动告知用户缺失了哪些信息
# 不确定性表达指南
- 100%确定 → "根据[来源],[事实]"
- 90%确定 → "根据[来源]推测,[结论]"
- 50-90% → "[分析],但需要进一步确认[细节]"
- <50% → "我不确定。根据现有信息无法判断。建议您[替代方案]"
# 典型错误(不要犯)
❌ 工具返回"订单已发货" → "您的包裹明天到达"(没有物流轨迹,纯推测)
❌ 工具返回"库存充足" → "库存在100件以上"(工具没说具体数字)
❌ 检索到"退货政策" → "根据《消费者权益保护法》..."(文档里没有这条法律)
❌ 查询失败 → "虽然查不到,但一般来说..."(承认失败,不要替代编造)
"""
12. 组合策略与效果评估
python
class AntiHallucinationAgent:
"""综合防幻觉 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}
async def safe_generate(self, question, tools, context):
self.stats["total"] += 1
# 1. 检索增强
docs = await self.retriever.search(question, k=5)
# 2. Agent 生成(使用 Anti-Hallucination Prompt)
response = await self.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
)
if citation_check["has_violations"]:
# 标记但可能仍返回(取决于风险等级)
pass
# 4. 输出安全检查
guard_check = self.output_guard.check(response, context)
if not guard_check["safe"]:
self.stats["hallucination_blocked"] += 1
return "抱歉,生成的回答存在不确定性,请重新描述您的问题。"
# 5. 可选项:事实核查(高价值场景)
if self._is_high_stakes(question):
fact_check = await self.fact_checker.check(
response, context["tool_results"], docs
)
if not fact_check["approved"]:
# 重试或降级
return await self._regenerate_with_facts(
question, tools, context, fact_check
)
return response
方案效果对比
| 方案 | 幻觉降低 | 额外延迟 | 额外成本 | 适用 |
|---|---|---|---|---|
| 强制引用 | -30% | +0.1s | 低 | 所有场景 |
| RAG约束 | -50% | +0.5s | 中 | 知识密集型 |
| 事实核查 | -40% | +2s | 高 | 高风险场景 |
| 交叉验证 | -45% | +3s | 很高 | 关键决策 |
| 不确定性量化 | -20% | +0.1s | 低 | 用户体验 |
| 输出守卫 | -15% | +0.05s | 低 | 安全兜底 |
推荐组合: 强制引用 + RAG约束 + 输出守卫(覆盖 80% 场景,低延迟低成本)。高风险场景额外加事实核查。