LLM 的幻觉问题——工程层面怎么缓解而不是消灭
LLM 的幻觉问题——工程层面怎么缓解而不是消灭
适读人群:AI 应用工程师、对 AI 可靠性有要求的开发者 | 阅读时长:约 12 分钟 | 核心价值:幻觉问题的工程实践解法,从缓解而不是消灭的角度思考
我做过一个医疗信息问答的项目。用户可以问关于药品、症状、检查项目的问题。
上线前,产品经理问我一个问题:"AI 会不会给出错误的医疗建议?"
我当时的回答是:"我们加了很多 prompt 约束,让它说不确定的事情要提示用户去咨询医生。"
产品经理再问:"但它会不会给出一个听起来很有把握、实际上是错的医疗信息?"
我沉默了一会儿,然后说:"会。这是现在所有 LLM 的基本性质,没有从根本上解决的方法。我们能做的是降低发生概率,并且在发生时有兜底机制。"
这是对幻觉问题最诚实的描述:它无法被消灭,只能被工程手段控制到可接受的范围。
幻觉的几种类型,工程师要区分
不是所有"错误输出"都叫幻觉,区分清楚有助于选对解法:
事实性幻觉:模型陈述了不存在或不正确的事实。 例:说某个 API 有某个参数,实际上没有。
引用幻觉:模型编造了引用来源。 例:说"根据某某研究显示……",这个研究根本不存在。
逻辑幻觉:推理链条是错的,但每一步看起来都有道理。 例:数学计算题,中间过程看起来很认真,结果是错的。
格式幻觉:要求特定格式输出,模型给了格式但内容是编造的。 例:要求 JSON 输出,JSON 结构正确,但某些字段的值是瞎填的。
幻觉的根本原因:LLM 是基于统计概率生成下一个 token,它在"表达流畅"和"内容准确"之间做权衡,而训练过程不能保证准确性优先于流畅性。
工程缓解手段 1:RAG 锚定事实来源
幻觉最常发生在"模型需要从训练数据里回忆知识"的场景。如果你把需要的知识通过 RAG 实时提供给模型,模型就从"回忆"变成"阅读理解",幻觉率大幅降低。
但这不是银弹。模型仍然可能:
- 误读提供的文档
- 在文档没有直接回答时"脑补"答案
- 混淆多个文档里的不同信息
关键是在 prompt 里明确限制:
from openai import OpenAI
client = OpenAI()
SYSTEM_PROMPT = """你是一个基于提供文档的问答助手。
规则:
1. 只基于【文档内容】回答问题,不要使用你自己的"知识"
2. 如果文档中没有足够信息回答问题,明确说"文档中没有找到相关信息",不要猜测
3. 如果你引用了文档中的信息,说明来自哪个文档的哪个部分
4. 对于医疗、法律、金融等专业问题,即使文档中有相关内容,也要提示用户咨询专业人士
禁止:
- 在文档没有明确支持的情况下,做出任何断言
- 使用"通常来说"、"一般认为"等表达来引入文档之外的信息"""
def rag_with_citation(question: str, retrieved_docs: list[dict]) -> dict:
context = ""
for i, doc in enumerate(retrieved_docs, 1):
context += f"\n【文档{i}】来源:{doc['source']}\n{doc['content']}\n"
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": f"文档内容:{context}\n\n问题:{question}"}
],
temperature=0.1 # 降低随机性
)
answer = response.choices[0].message.content
# 返回答案和引用的文档(让用户可以核查)
return {
"answer": answer,
"sources": [doc["source"] for doc in retrieved_docs],
"confidence": "based_on_documents" # 标记这是文档支持的回答
}工程缓解手段 2:事实验证层
对于高风险输出,加一层独立的验证。让另一个 LLM 调用或者规则引擎检查输出。
这有两种常见模式:
模式 A:LLM 自我一致性检查(多次采样投票)
import json
from collections import Counter
def self_consistency_check(question: str, n_samples: int = 5) -> dict:
"""
对同一个问题采样多次,取一致的答案
如果答案不一致,说明置信度低
"""
answers = []
for _ in range(n_samples):
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "user", "content": f"{question}\n\n请直接给出答案,不需要解释。"}
],
temperature=0.7 # 高温度让答案有差异
)
answers.append(response.choices[0].message.content.strip())
# 统计最常见的答案
counter = Counter(answers)
most_common, count = counter.most_common(1)[0]
consistency = count / n_samples
return {
"answer": most_common,
"consistency": consistency, # 1.0 = 完全一致,0.2 = 5次答案全不同
"all_answers": answers,
"reliable": consistency >= 0.6 # 60% 以上一致才算可靠
}
# 用法:如果 reliable=False,不直接展示给用户,而是触发人工审查
result = self_consistency_check("二甲双胍的常见副作用有哪些?")
if not result["reliable"]:
# 转给专业医生审查
flag_for_human_review(result)模式 B:独立验证 Agent
def verify_factual_claims(original_answer: str, context_docs: list[str]) -> dict:
"""
用另一个 LLM 调用验证原始回答中的事实性声明
"""
verification_prompt = f"""请检查以下回答中的每一个事实性陈述,并评估它是否得到了提供的文档支持。
回答内容:
{original_answer}
参考文档:
{chr(10).join(context_docs)}
请用 JSON 格式列出所有事实性声明及其验证状态:
{{
"claims": [
{{
"claim": "具体的事实陈述",
"supported_by_docs": true/false,
"doc_reference": "来自哪个文档的哪部分,或'无支持'"
}}
],
"overall_reliability": "high/medium/low",
"concerns": "如果有需要注意的地方"
}}"""
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": verification_prompt}],
response_format={"type": "json_object"},
temperature=0
)
return json.loads(response.choices[0].message.content)工程缓解手段 3:置信度估计和不确定性表达
让模型学会说"我不确定",比让它给出一个自信但错误的答案更有价值。
问题是,LLM 默认倾向于给出确定性的答案(训练数据里有大量如此的示例)。需要通过 prompt 工程来改变这个行为:
UNCERTAINTY_PROMPT = """在回答时,请区分以下三种情况并明确标注:
1. [确定] 你在提供的文档中找到了明确的支持
2. [推断] 文档中有相关信息,但你的回答是基于推断,可能不完全准确
3. [不确定] 文档中没有足够信息,你不应该给出答案
示例格式:
[确定] 该药物的常规剂量为每日 500mg。(来源:文档第3段)
[推断] 基于文档提供的症状描述,可能与高血压有关,但需要医生诊断确认。
[不确定] 关于长期使用的副作用,文档中没有提供足够信息,建议咨询专科医生。"""工程缓解手段 4:人工兜底机制
这是最后一道防线,也是最容易被低估的。
不是所有场景都需要完全自动化。对于高风险的输出(医疗建议、法律判断、金融决策),设计一个清晰的人工审查路径比花时间调 prompt 更有实际价值。
from enum import Enum
from dataclasses import dataclass
from typing import Optional
import time
class RiskLevel(Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
@dataclass
class AIResponse:
content: str
risk_level: RiskLevel
confidence: float
requires_human_review: bool
review_reason: Optional[str] = None
def classify_response_risk(response: str, query_category: str) -> RiskLevel:
"""根据查询类别和响应内容判断风险级别"""
high_risk_categories = {"medical", "legal", "financial", "safety"}
if query_category in high_risk_categories:
return RiskLevel.HIGH
# 关键词检测(粗糙但有效)
high_risk_keywords = ["剂量", "副作用", "禁忌", "手术", "诉讼", "合同", "投资"]
if any(kw in response for kw in high_risk_keywords):
return RiskLevel.MEDIUM
return RiskLevel.LOW
def process_with_human_fallback(
query: str,
query_category: str,
ai_response: str,
consistency_score: float
) -> AIResponse:
risk_level = classify_response_risk(ai_response, query_category)
requires_review = (
risk_level == RiskLevel.HIGH or
consistency_score < 0.6 or
len(ai_response) < 50 # 回答过短,可能在回避
)
review_reason = None
if requires_review:
reasons = []
if risk_level == RiskLevel.HIGH:
reasons.append(f"高风险类别: {query_category}")
if consistency_score < 0.6:
reasons.append(f"一致性低: {consistency_score:.0%}")
review_reason = "; ".join(reasons)
return AIResponse(
content=ai_response,
risk_level=risk_level,
confidence=consistency_score,
requires_human_review=requires_review,
review_reason=review_reason
)
def handle_user_query(user_id: str, query: str, category: str) -> dict:
# 获取 AI 回答
result = self_consistency_check(query)
response_obj = process_with_human_fallback(
query=query,
query_category=category,
ai_response=result["answer"],
consistency_score=result["consistency"]
)
if response_obj.requires_human_review:
# 加入人工审查队列
queue_for_review(user_id, query, response_obj)
return {
"response": "您的问题需要专业人员确认,我们会在 2 小时内回复您。",
"status": "pending_review"
}
return {
"response": response_obj.content,
"status": "ai_answered",
"confidence": response_obj.confidence
}实际情况下的优先级
我的经验是按这个顺序来做:
必须做(成本低,收益高):
- RAG 而不是让模型凭记忆回答
- prompt 里明确要求不确定时说不确定
- 高风险类别的人工兜底路径
应该做(成本中,有明显价值):
- 事实性验证层(至少对高风险输出)
- 置信度估计
- 用户可见的引用来源
可以考虑(成本高,效果边际递减):
- 自我一致性多次采样(成本是原来的 N 倍)
- 复杂的多模型验证流水线
幻觉问题是 LLM 的基本性质,没有简单的终极解法。工程师的责任不是等待一个"不幻觉的模型",而是在现有技术条件下,把风险控制在业务可接受的范围内。这是比讨论哪个模型更聪明更有实际价值的工作。
