当 AI 一本正经地告诉你错误答案时,它不是在撒谎,它只是产生了幻觉。本文将深入分析幻觉的成因,并提供一套从架构到代码的完整解决方案。

一、什么是 AI 幻觉?

幻觉(Hallucination) 是指大语言模型生成看似合理但实际错误的内容。它的危险之处在于:模型会用非常自信的语气,陈述完全错误的事实。

典型案例

用户:帮我查一下订单 #12345 的状态

AI(幻觉):订单 #12345 已于 2024-01-15 发货,预计 3 天内送达。

实际:数据库中根本没有这个订单

当没有实际的查询业务数据库,这种"自信的错误"比直接说"我不知道"更危险,因为它会误导用户做出错误决策。

幻觉的类型

类型

示例

危害等级

事实错误

"北京人口是 500 万"

🔴 高

虚构引用

引用不存在的论文、文档

🔴 高

逻辑谬误

推理链条看似合理但结论错误

🟡 中

过度泛化

用个例推导普遍规律

🟡 中

上下文混淆

将 A 项目的配置说成 B 项目的

🟠 低


二、幻觉的四大根因

2.1 知识边界模糊

模型无法精确区分"我知道"和"我不知道"的边界。

# 模型的"知识"来源
knowledge_sources = {
    "训练数据": "截止到某个时间点的互联网文本",
    "上下文": "当前对话中的信息",
    "工具结果": "通过 API/数据库查询获得的数据"
}

# 问题:模型无法准确判断某条知识来自哪个源
# 导致:将训练数据中的过时信息当作当前事实

2.2 概率生成的本质

LLM 本质是预测下一个 token 的概率分布,而非检索事实:

def generate(prompt):
    # 模型生成的是"最可能的回答",而非"最真实的回答"
    # 对于 "北京人口是..."
    # 可能生成:500万、2000万、3000万...取决于训练数据分布
    return most_likely_continuation(prompt)

2.3 上下文理解偏差

用户意图被错误解读:

用户:这个项目用 React 18 的新特性了吗?

AI:是的,项目使用了 React 18 的 Concurrent Rendering、
    Suspense、Server Components 等特性。

实际:项目确实用 React 18,但只用了自动更新,没有使用任何新特性

2.4 缺乏自我验证机制

传统软件有断言、测试、类型检查,但 AI Agent 缺乏这些约束:

# 传统代码有运行时验证
def divide(a, b):
    assert b != 0, "除数不能为 0"
    return a / b

# AI Agent 没有验证机制
def answer_question(query):
    # 没有 assert,没有类型检查
    # 模型"认为"自己是对的,但可能完全错误
    return llm.generate(query)

三、工程化解决方案

方案一:RAG(检索增强生成)

核心思想:让模型基于真实文档回答,而非依赖"记忆"。

from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA

class RAGAgent:
    def __init__(self, documents_path):
        # 1. 文档向量化
        self.embeddings = OpenAIEmbeddings()
        self.vectorstore = Chroma.from_documents(
            documents=load_documents(documents_path),
            embedding=self.embeddings
        )

        # 2. 构建检索链
        self.llm = ChatOpenAI(temperature=0)  # 降低随机性
        self.qa_chain = RetrievalQA.from_chain_type(
            llm=self.llm,
            retriever=self.vectorstore.as_retriever(
                search_kwargs={"k": 5}  # 检索 top-5 相关文档
            ),
            return_source_documents=True  # 返回来源
        )

    def answer(self, query):
        result = self.qa_chain({"query": query})

        # 3. 构建带引用的回答
        answer = result["result"]
        sources = result["source_documents"]

        if sources:
            citations = "\n\n".join([
                f"📄 来源 [{i+1}]:{doc.metadata['source']}"
                for i, doc in enumerate(sources)
            ])
            return f"{answer}\n\n{citations}"
        else:
            return f"⚠️ 未找到相关文档,以下回答可能不准确:\n{answer}"

# 使用示例
agent = RAGAgent("./docs/")
print(agent.answer("公司的请假制度是什么?"))

输出效果

根据员工手册,请假制度如下:
- 年假:工龄满 1 年后,每年 5 天
- 病假:每年累计不超过 30 天
- 事假:需提前 3 天申请

📄 来源 [1]:docs/员工手册.pdf
📄 来源 [2]:docs/考勤制度.md

为什么有效?

  1. 强制基于事实:回答必须来自检索到的文档
  2. 可追溯:每个回答都有明确来源
  3. 边界清晰:没有相关文档时,模型会明确表示不确定

方案二:工具调用约束

核心思想:强制 Agent 通过工具获取数据,而非凭空回答。

from langchain.tools import Tool
from langchain.agents import initialize_agent

# 定义数据查询工具
def query_database(sql: str) -> str:
    """查询数据库,返回准确数据"""
    try:
        result = db.execute(sql)
        return json.dumps(result, ensure_ascii=False)
    except Exception as e:
        return f"查询失败:{e}"

def search_document(query: str) -> str:
    """搜索文档,返回相关内容"""
    docs = retriever.search(query, top_k=3)
    return "\n".join([doc.content for doc in docs])

tools = [
    Tool(
        name="DatabaseQuery",
        func=query_database,
        description="查询数据库获取订单、用户等数据。输入:SQL 查询语句"
    ),
    Tool(
        name="DocumentSearch",
        func=search_document,
        description="搜索文档获取业务规则、政策说明。输入:搜索关键词"
    )
]

# 创建受约束的 Agent
agent = initialize_agent(
    tools=tools,
    llm=ChatOpenAI(temperature=0),
    agent="zero-shot-react-description",
    verbose=True  # 显示推理过程
)

# Agent 会自动判断是否需要调用工具
result = agent.run("订单 #12345 现在什么状态?")

执行过程

Thought: 需要查询数据库获取订单状态
Action: DatabaseQuery
Action Input: SELECT status FROM orders WHERE id = '12345'
Observation: [{"status": "not_found"}]

Thought: 数据库中没有该订单
Final Answer: 抱歉,系统中没有找到订单 #12345,请确认订单号是否正确。

关键优势

  • 数据查询类问题必须调用工具
  • 工具返回的结果是确定性数据,不会产生幻觉
  • Agent 的角色从"生成答案"变为"调用工具 + 组织语言"

方案三:多 Agent 交叉验证

核心思想:让多个 Agent 独立回答,找出共识部分。

from typing import List, Dict
import asyncio

class VerificationSystem:
    def __init__(self, num_agents=3):
        # 使用不同温度增加多样性
        self.agents = [
            ChatOpenAI(temperature=0.1 * i)
            for i in range(num_agents)
        ]

    async def get_answer(self, agent, query: str) -> str:
        """单个 Agent 回答"""
        return await agent.agenerate([query])

    async def verify(self, query: str) -> Dict:
        """多 Agent 验证"""
        # 1. 并行获取多个回答
        answers = await asyncio.gather(*[
            self.get_answer(agent, query)
            for agent in self.agents
        ])

        # 2. 提取事实性声明
        all_claims = [
            extract_claims(answer)
            for answer in answers
        ]

        # 3. 找出共识
        consensus = find_intersection(all_claims)

        # 4. 找出分歧
        disagreements = find_differences(all_claims)

        return {
            "verified_answer": consensus,
            "uncertain_parts": disagreements,
            "confidence": len(consensus) / (len(consensus) + len(disagreements)),
            "raw_answers": answers
        }

def find_intersection(claims_list: List[List[str]]) -> List[str]:
    """找出所有 Agent 都同意的声明"""
    if not claims_list:
        return []

    result = set(claims_list[0])
    for claims in claims_list[1:]:
        result &= set(claims)

    return list(result)

# 使用示例
system = VerificationSystem(num_agents=3)
result = await system.verify("公司的年假制度是什么?")

print(f"✅ 确认内容:{result['verified_answer']}")
print(f"⚠️ 不确定:{result['uncertain_parts']}")
print(f"📊 置信度:{result['confidence']:.2%}")

验证流程

Agent 1: 年假每年 5 天,工龄满 5 年增加到 10 天
Agent 2: 年假制度:工龄 1 年 5 天,工龄 5 年 10 天
Agent 3: 年假根据工龄递增,1 年 5 天,5 年 10 天,10 年 15 天

共识:年假 5 天起,工龄影响年假天数
分歧:工龄 10 年是否增加到 15 天(只有 Agent 3 提到)

方案四:置信度自评

核心思想:让模型评估自己的回答可靠性。

class ConfidenceAwareAgent:
    def __init__(self):
        self.llm = ChatOpenAI(temperature=0)

    def answer_with_confidence(self, query: str) -> Dict:
        # 1. 生成回答
        answer = self.llm.generate(query)

        # 2. 自评置信度
        confidence_prompt = f"""
        评估以下回答的准确性(输出 0.0-1.0 的分数):

        问题:{query}
        回答:{answer}

        评分标准:
        - 1.0:有明确文档/数据支持
        - 0.8-0.9:基于可靠推理,逻辑严密
        - 0.6-0.7:有一定依据,但存在不确定因素
        - 0.4-0.5:主要靠推断,可能不准确
        - 0.0-0.3:高度不确定或可能错误

        只输出数字,不要解释。
        """

        confidence = float(self.llm.generate(confidence_prompt))

        # 3. 根据置信度调整输出
        if confidence >= 0.8:
            return {
                "answer": answer,
                "status": "✅ 高置信度",
                "confidence": confidence
            }
        elif confidence >= 0.6:
            return {
                "answer": f"(仅供参考){answer}",
                "status": "⚠️ 中等置信度",
                "confidence": confidence
            }
        else:
            return {
                "answer": "抱歉,我对这个问题的回答不够确定,建议您查阅官方文档或咨询相关人员。",
                "status": "❌ 低置信度",
                "confidence": confidence,
                "raw_answer": answer
            }

# 使用
agent = ConfidenceAwareAgent()
result = agent.answer_with_confidence("Python 3.12 有哪些新特性?")
print(f"{result['status']}(置信度:{result['confidence']:.2f})")
print(result['answer'])

输出示例

✅ 高置信度(置信度:0.92)
Python 3.12 的主要新特性包括:
1. 改进的错误消息
2. GIL 移除计划(PEP 703)
3. 类型提示增强

⚠️ 中等置信度(置信度:0.65)
(仅供参考)Python 3.12 可能引入了新的语法特性...

❌ 低置信度(置信度:0.25)
抱歉,我对这个问题的回答不够确定,建议您查阅官方文档或咨询相关人员。

方案五:事实核查 Agent

核心思想:专门的 Agent 验证输出中的事实性声明。

class FactChecker:
    def __init__(self, knowledge_base):
        self.kb = knowledge_base
        self.llm = ChatOpenAI(temperature=0)

    def extract_factual_claims(self, text: str) -> List[str]:
        """提取文本中的事实性声明"""
        prompt = f"""
        从以下文本中提取所有事实性声明(可以被验证真假的陈述):

        文本:{text}

        输出格式(每行一个声明):
        1. [声明内容]
        2. [声明内容]
        """
        return self.llm.generate(prompt).split("\n")

    def verify_claim(self, claim: str) -> Dict:
        """验证单个声明"""
        # 1. 在知识库中搜索证据
        evidence = self.kb.search(claim, top_k=3)

        if not evidence:
            return {
                "claim": claim,
                "status": "unverified",
                "reason": "未找到相关证据"
            }

        # 2. 让模型判断证据是否支持声明
        verification_prompt = f"""
        判断以下证据是否支持该声明:

        声明:{claim}
        证据:{evidence}

        输出:
        - "supported":证据明确支持
        - "contradicted":证据反驳
        - "insufficient":证据不足
        """

        status = self.llm.generate(verification_prompt)

        return {
            "claim": claim,
            "status": status,
            "evidence": evidence
        }

    def check(self, text: str) -> Dict:
        """完整的事实核查流程"""
        claims = self.extract_factual_claims(text)
        results = [self.verify_claim(claim) for claim in claims]

        # 统计
        supported = [r for r in results if r["status"] == "supported"]
        contradicted = [r for r in results if r["status"] == "contradicted"]
        unverified = [r for r in results if r["status"] == "unverified"]

        return {
            "total_claims": len(claims),
            "supported": len(supported),
            "contradicted": len(contradicted),
            "unverified": len(unverified),
            "details": results,
            "overall_accuracy": len(supported) / len(claims) if claims else 0
        }

四、实战案例:构建"诚实"的客服 Agent

结合以上方案,构建一个完整的客服系统:

from dataclasses import dataclass
from typing import Optional, List

@dataclass
class AgentResponse:
    answer: str
    confidence: float
    sources: List[str]
    warnings: List[str]
    needs_human_review: bool

class HonestCustomerServiceAgent:
    """一个"诚实"的客服 Agent,会明确表达不确定性"""

    def __init__(self):
        # RAG 系统
        self.rag = RAGAgent("./knowledge_base/")

        # 事实核查
        self.fact_checker = FactChecker("./knowledge_base/")

        # 多 Agent 验证
        self.verifiers = VerificationSystem(num_agents=3)

        # 工具集
        self.tools = {
            "query_order": query_database,
            "search_policy": search_document,
            "check_inventory": check_inventory
        }

    async def answer(self, query: str) -> AgentResponse:
        """主回答流程"""

        # Step 1: 判断是否需要工具
        if self._needs_tool(query):
            tool_name = self._select_tool(query)
            tool_result = self.tools[tool_name](query)

            return AgentResponse(
                answer=tool_result,
                confidence=1.0,  # 工具结果置信度高
                sources=[f"工具:{tool_name}"],
                warnings=[],
                needs_human_review=False
            )

        # Step 2: RAG 检索
        rag_result = self.rag.answer(query)

        # Step 3: 多 Agent 验证
        verification = await self.verifiers.verify(query)

        # Step 4: 事实核查
        fact_check = self.fact_checker.check(rag_result)

        # Step 5: 综合判断
        final_confidence = (
            verification["confidence"] * 0.4 +
            fact_check["overall_accuracy"] * 0.6
        )

        warnings = []
        needs_review = False

        # 低置信度警告
        if final_confidence < 0.6:
            warnings.append("此回答置信度较低,建议人工复核")
            needs_review = True

        # 未验证的事实
        for detail in fact_check["details"]:
            if detail["status"] == "unverified":
                warnings.append(f"「{detail['claim']}」未找到证据支持")

        # Step 6: 构建最终回答
        if final_confidence >= 0.8:
            answer = rag_result
        elif final_confidence >= 0.6:
            answer = f"(供参考){rag_result}"
        else:
            answer = (
                "抱歉,我对这个问题的回答不够确定。\n"
                "建议您:\n"
                "1. 查阅官方文档\n"
                "2. 联系人工客服\n\n"
                f"初步判断:{rag_result}"
            )
            needs_review = True

        return AgentResponse(
            answer=answer,
            confidence=final_confidence,
            sources=[],
            warnings=warnings,
            needs_human_review=needs_review
        )

    def _needs_tool(self, query: str) -> bool:
        """判断是否需要调用工具"""
        keywords = ["订单", "库存", "余额", "状态", "查询"]
        return any(kw in query for kw in keywords)

    def _select_tool(self, query: str) -> str:
        """选择合适的工具"""
        if "订单" in query:
            return "query_order"
        elif "库存" in query:
            return "check_inventory"
        else:
            return "search_policy"

# 使用示例
async def main():
    agent = HonestCustomerServiceAgent()

    queries = [
        "订单 #12345 现在什么状态?",  # 需要工具
        "公司的年假制度是什么?",      # RAG + 验证
        "CEO 的生日是哪天?",         # 应该说不知道
    ]

    for query in queries:
        print(f"\n{'='*50}")
        print(f"问题:{query}")
        result = await agent.answer(query)
        print(f"回答:{result.answer}")
        print(f"置信度:{result.confidence:.2%}")
        print(f"来源:{result.sources}")
        if result.warnings:
            print(f"⚠️ 警告:{result.warnings}")
        if result.needs_human_review:
            print("🔔 需要人工复核")

import asyncio
asyncio.run(main())

输出示例

==================================================
问题:订单 #12345 现在什么状态?
回答:订单 #12345 当前状态为"已发货",物流单号:SF1234567890
置信度:100.00%
来源:['工具:query_order']

==================================================
问题:公司的年假制度是什么?
回答:根据员工手册,年假制度如下:
- 工龄满 1 年:每年 5 天
- 工龄满 5 年:每年 10 天
- 工龄满 10 年:每年 15 天

置信度:85.00%
来源:[]

==================================================
问题:CEO 的生日是哪天?
回答:抱歉,我对这个问题的回答不够确定。
建议您:
1. 查阅官方文档
2. 联系人工客服

置信度:20.00%
⚠️ 警告:['此回答置信度较低,建议人工复核']
🔔 需要人工复核

五、最佳实践总结

5.1 分层防护策略

┌─────────────────────────────────────┐
│  Layer 1: Prompt 约束                │  ← 最简单,效果中等
│  - 明确要求"不知道时说不知道"          │
│  - 要求标注信息来源                   │
└─────────────────────────────────────┘
           ↓
┌─────────────────────────────────────┐
│  Layer 2: RAG + 工具约束              │  ← 效果显著
│  - 强制基于文档/数据回答               │
│  - 禁止凭空生成事实                   │
└─────────────────────────────────────┘
           ↓
┌─────────────────────────────────────┐
│  Layer 3: 多 Agent 验证               │  ← 高可靠性场景
│  - 多个 Agent 独立回答                │
│  - 找出共识,标注分歧                  │
└─────────────────────────────────────┘
           ↓
┌─────────────────────────────────────┐
│  Layer 4: 事实核查                    │  ← 最高可靠性
│  - 提取事实性声明                     │
│  - 逐条验证证据                       │
└─────────────────────────────────────┘

5.2 场景选择指南

场景

推荐方案

理由

闲聊对话

Prompt 约束

幻觉影响小,成本优先

知识问答

RAG + 来源标注

需要准确性,有文档支持

数据查询

工具约束

数据必须准确,不能编造

关键决策

多 Agent + 事实核查

错误代价高,需要高可靠性

内容生成

事实核查

避免传播错误信息

5.3 性能 vs 准确性权衡

方案

延迟

成本

准确性

仅 Prompt

100ms

1x

70%

RAG

500ms

2x

90%

RAG + 验证

1500ms

4x

95%

完整流程

3000ms

6x

98%

建议:根据业务场景选择合适的层级,不必一味追求最高准确性。


六、核心原则

宁可说"不知道",也不要自信地犯错。

在关键业务场景中,一个"诚实但有限"的 Agent 远比一个"博学但不可靠"的 Agent 更有价值。

实施路线图

  1. 第一周:实施 Prompt 约束 + 简单 RAG
  2. 第二周:添加工具调用约束
  3. 第三周:引入置信度评估
  4. 第四周:部署多 Agent 验证(如果需要)

关键指标

# 监控这些指标来评估改进效果
metrics = {
    "幻觉率": "错误但自信的回答比例",
    "拒绝率": "正确拒绝回答的比例",
    "准确率": "回答正确的比例",
    "用户满意度": "用户对回答质量的评分"
}

参考资料

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐