大语言模型的幻觉问题:一个前端开发者的踩坑实录

当 AI 一本正经地胡说八道,我们该怎么办?

开篇:从一个诡异的 bug 说起

前几天我在做一个前端 AI 助手项目,用户可以询问一些前端技术问题,后端接 Claude API 返回答案。测试的时候,我问了一个问题:

“React 19 的 useCache hook 怎么用?”

AI 非常流畅地给出了详细答案,包括完整的代码示例、参数说明、注意事项,看起来头头是道。我当时还挺开心,觉得这个效果不错。

但当我想在实际项目中用这个 useCache 的时候,发现了一个尴尬的事实:React 19 根本没有 useCache 这个 hook

AI 完全是"编"出来的,而且编得如此逼真,连参数类型都给你安排好了。

这就是我第一次真正意识到大语言模型"幻觉"(Hallucination)问题的严重性。作为一个前端开发者,这让我很困惑——明明是这么强大的 AI,怎么会这么离谱地犯错?

于是我花了几周时间深入研究这个问题。今天这篇文章,就来聊聊我对大语言模型幻觉问题的理解、遇到的坑,以及实际项目中如何应对。

什么是大语言模型的幻觉?

先给一个定义

**幻觉(Hallucination)**是指大语言模型生成的内容看起来流畅、合理,但实际上是不正确、不存在或者与事实相悖的。

换个前端开发者能理解的说法:就像我们的代码编译通过了、运行也没报错,但逻辑是完全错的——表面看着没问题,实际是个大 bug。

幻觉的几种类型

在我实际使用和调试过程中,我总结了以下几类常见的幻觉:

1. 事实性幻觉(Factual Hallucination)

AI 编造不存在的事实。比如上面提到的 useCache hook,或者:

用户:谁发明了 JavaScript?
AI:JavaScript 是由 Brendan Eich 在 1995 年于 Netscape 公司发明的,
    他仅用了 10 天就完成了第一个版本。(✓ 正确)

用户:React Router v7 有什么新特性?
AI:React Router v7 引入了全新的 useRouteCache API... (❌ 编的)

2. 逻辑性幻觉(Logical Hallucination)

推理过程看起来合理,但结论是错的:

// AI 给的"优化建议"
// "这样可以避免重复渲染"
const memoizedValue = useMemo(() => {
  return props.data; // 其实这毫无意义,props.data 每次都是新引用
}, [props.data]);

3. 引用性幻觉(Reference Hallucination)

编造不存在的论文、文档或代码库:

AI:根据 MDN 文档的说明,Array.prototype.groupBy() 方法在 ES2022 中已被标准化...

实际上截至 2024 年,groupBy 的提案还在 Stage 3,而且叫 Object.groupBy(),不是数组方法。

4. 自我矛盾幻觉(Self-Contradictory Hallucination)

同一段对话中前后说法矛盾:

用户:Vue 3 的 Composition API 和 React Hooks 哪个性能更好?

AI:从性能角度来说,Vue 3 的响应式系统因为使用 Proxy 实现,
    在细粒度更新上更有优势...

用户:那为什么大型应用更多用 React?

AI:这主要是因为 React 的虚拟 DOM diff 算法在大规模更新时
    性能更优越...(和前面矛盾了)

幻觉产生的原因

作为一个习惯看源码的前端开发者,我想搞清楚 LLM 为什么会产生幻觉。研究了一段时间后,我的理解是这样的:

1. 本质是概率模型,不是知识库

大语言模型本质上是在预测"下一个 token 是什么",它并不是在查询一个事实数据库。

用前端的类比来说:

  • 传统数据库查询就像 db.query('SELECT * FROM facts WHERE topic="React"'),返回的一定是数据库里有的数据
  • 而 LLM 更像是 generateText({ context: "React", style: "technical", confidence: 0.95 }),它是在"生成"看起来合理的文本
# LLM 的核心逻辑(简化版)
def generate_next_token(context):
    # 计算所有可能 token 的概率分布
    probabilities = model.forward(context)

    # 采样得到下一个 token(不是查询)
    next_token = sample(probabilities, temperature=0.7)

    return next_token

这就导致了一个问题:模型可能会生成它从未在训练数据中见过的内容组合,只要这个组合"在概率上看起来合理"。

2. 训练数据的问题

LLM 的训练数据来自互联网,而互联网上本身就有很多错误信息。如果模型学习了错误的内容,它就会自信地输出错误答案。

更麻烦的是,模型没有办法区分"我确定知道"和"我在猜测"。它的自信程度不代表准确度。

3. 知识截止时间

这个问题我在做项目时感受特别深。当用户问:

“Bun 1.0 正式版和 Node.js 比性能怎么样?”

模型的训练数据有截止日期,如果这个日期在 Bun 1.0 发布之前,它要么会说不知道(比较好),要么会根据之前的 beta 版信息"推测"(很危险)。

4. 上下文长度限制

虽然现在的模型支持很长的上下文(比如 Claude 支持 200K tokens),但在处理长文档时,模型可能会"遗忘"或混淆前面的内容。

我在做长文档总结功能时就遇到过这个问题——文档前半部分的关键信息在总结中被"篡改"了。

实际项目中的应对策略

好,理解了原因,那实际做项目时该怎么办?下面分享我在英博云平台部署模型、做 AI 应用时总结的一些经验。

策略一:RAG(检索增强生成)

这是目前最实用的方案。简单说就是:让模型基于你提供的真实数据来回答,而不是靠它自己的"记忆"。

// 简化的 RAG 流程
async function ragQuery(userQuestion) {
  // 1. 把用户问题向量化
  const questionEmbedding = await embeddings.create({
    model: "text-embedding-ada-002",
    input: userQuestion
  });

  // 2. 在向量数据库中搜索相关文档
  const relevantDocs = await vectorDB.search({
    vector: questionEmbedding,
    topK: 5
  });

  // 3. 把相关文档作为上下文,让 LLM 基于这些内容回答
  const response = await llm.chat({
    messages: [
      {
        role: "system",
        content: `请基于以下参考资料回答用户问题。如果资料中没有相关信息,请明确说"我没有找到相关信息"。

参考资料:
${relevantDocs.map(doc => doc.content).join('\n\n')}`
      },
      {
        role: "user",
        content: userQuestion
      }
    ]
  });

  return response;
}

实战踩坑

  • 向量搜索的相关性很关键。我一开始用简单的余弦相似度,搜出来的结果经常不太相关,后来换了 Cohere 的 rerank API 做二次排序,效果好了很多
  • 文档切分(chunking)的粒度很重要。太大搜不准,太小上下文不完整。我最后用的是按段落切分 + 滑动窗口的方式

策略二:多轮验证和自我纠错

让模型自己检查自己的答案:

async function verifiedAnswer(question) {
  // 第一轮:生成答案
  const initialAnswer = await llm.chat({
    messages: [{ role: "user", content: question }]
  });

  // 第二轮:让模型审查自己的答案
  const verification = await llm.chat({
    messages: [
      { role: "user", content: question },
      { role: "assistant", content: initialAnswer },
      {
        role: "user",
        content: `请审查你刚才的回答:
1. 有没有事实性错误?
2. 有没有编造不存在的内容?
3. 逻辑是否正确?

如果有问题,请指出并给出修正后的答案。`
      }
    ]
  });

  return verification;
}

效果:这个方法在我的测试中能发现约 30-40% 的明显错误。但它也有局限——如果模型"坚信"某个错误信息,它在验证环节也会说"没问题"。

策略三:结构化输出 + 强制引用

要求模型必须给出信息来源,并且用结构化格式输出:

const systemPrompt = `你是一个技术助手。回答时请遵循以下规则:

1. 必须以 JSON 格式输出
2. 每个技术观点必须标注来源
3. 如果不确定,confidence 设为 low

输出格式:
{
  "answer": "主要回答内容",
  "sources": [
    {"claim": "具体观点", "source": "来源", "confidence": "high/medium/low"}
  ],
  "caveats": ["需要注意的事项"]
}`;

实战效果:当模型被迫要给出来源时,它会更谨慎。如果它无法给出具体来源(而不是编一个),这本身就是一个警示信号。

策略四:领域限定 + 拒答机制

在实际产品中,明确告诉模型什么该答、什么不该答:

const boundedSystemPrompt = `你是一个 React 技术助手。

## 你可以回答的问题:
- React 官方文档中有说明的内容
- React 生态(Redux, React Router 等主流库)的使用问题
- 基于 React 的最佳实践

## 你必须拒绝回答的问题:
- React 官方没有发布的特性(即使用户说"我听说会有")
- 非 React 技术栈的问题
- 任何需要实时信息的问题(如"最新版本是什么")

## 不确定时的处理:
直接说"我不确定这个信息是否准确,建议查阅官方文档"`;

策略五:温度参数调节

这是最简单但常被忽视的方法:

// 需要创意时(如写文案)
const creativeResponse = await llm.chat({
  messages: [...],
  temperature: 0.9  // 高温度,更多随机性
});

// 需要准确时(如技术问答)
const factualResponse = await llm.chat({
  messages: [...],
  temperature: 0.1  // 低温度,更确定性
});

我的经验

  • 技术问答:temperature 0.1-0.3
  • 代码生成:temperature 0.2-0.4
  • 文案创作:temperature 0.7-0.9

低温度不能完全消除幻觉,但能显著降低"随机编造"的概率。

英博云平台的实战经验

我在英博云平台部署过几个涉及 AI 的项目,分享一些具体经验:

案例:技术文档问答系统

需求是做一个企业内部的技术文档问答系统,用户可以问技术问题,系统基于公司内部文档回答。

架构设计

用户提问
    ↓
[问题分类器] → 判断是否在知识库范围内
    ↓
[向量检索] → 从文档库中检索相关内容
    ↓
[LLM 生成] → 基于检索内容生成答案
    ↓
[置信度评估] → 判断答案可靠性
    ↓
返回答案(附带来源链接和置信度)

部署配置(在英博云平台上):

# 向量数据库
vector_db:
  type: milvus
  dimension: 1536
  index_type: IVF_FLAT

# LLM 服务
llm:
  model: claude-3-sonnet
  temperature: 0.2
  max_tokens: 2048

# 检索配置
retrieval:
  top_k: 10
  rerank: true
  rerank_model: bge-reranker-large

踩坑记录

  1. 向量维度选择:一开始用的 768 维的小模型做 embedding,检索效果不好。换成 1536 维的 OpenAI embedding 后,相关性提升很明显

  2. 重排序很重要:单纯靠向量相似度排序,前 5 条结果里可能只有 2 条真正相关。加了 rerank 之后,相关性从约 60% 提升到 90%

  3. chunk size 的选择:最开始用 500 token 一个 chunk,但这样很多段落被截断了。后来改成按自然段落切分,不足 200 token 的合并,超过 800 token 的按句子切分

效果数据

上线一个月后的统计:

  • 用户对答案"有帮助"的反馈率:78%
  • 明确被用户标记为"回答错误"的:3.2%
  • "找不到相关信息"的诚实拒答率:15%

对比没有 RAG 直接用 LLM 的基线:

  • "有帮助"反馈率:45%
  • “回答错误”:22%

可以看到 RAG 确实能显著降低幻觉问题。

前端开发者视角的思考

作为一个前端开发者,我经常思考 AI 幻觉问题对我们日常工作的影响:

1. 用 AI 写代码时的注意事项

现在很多人用 Copilot、Claude 来辅助写代码。我的建议是:

相信但验证

// AI 生成的代码
const debounce = (fn, delay) => {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
};

// 问题:箭头函数里的 this 是什么?
// 这段代码在严格模式下 this 会是 undefined

让 AI 解释它的代码

如果 AI 给了你一段代码,让它解释每一行的作用。如果解释不清楚或者解释和代码不匹配,很可能有问题。

2. 用 AI 查技术问题时

不要直接复制答案去用,尤其是:

  • 具体的 API 参数
  • 库的版本兼容性
  • "新特性"相关的内容

多一步验证

AI 说:React 18 可以用 useId() 生成唯一 ID

验证步骤:
1. 去 React 官网确认这个 hook 存在
2. 看看是否真的是 React 18 引入的
3. 看官方示例和 AI 说的用法是否一致

3. 构建 AI 应用时

如果你在做包含 AI 功能的产品,幻觉问题是必须认真对待的:

  • 法律风险:如果 AI 给出错误的医疗、法律建议,可能有法律问题
  • 用户信任:一次严重的幻觉可能让用户永远不再信任你的产品
  • 调试困难:AI 的错误不像传统 bug 那样可复现,很难排查

未来展望

幻觉问题是当前大语言模型的核心挑战之一,学术界和工业界都在积极研究。一些有意思的方向:

1. 更好的训练方法

比如 RLHF(从人类反馈中强化学习)已经在减少有害输出方面很有效,类似的方法也可以用来减少幻觉。

2. 模型自知性

让模型更好地知道"自己不知道什么"。目前的研究方向包括:

  • 让模型输出置信度
  • 训练模型在不确定时主动说"我不确定"

3. 实时检索增强

把外部知识库的检索深度整合到模型推理过程中,而不是现在 RAG 这种"先搜后答"的两阶段方式。

4. 多模型交叉验证

用多个不同的模型回答同一个问题,如果答案一致则可信度高,不一致则需要进一步验证。

总结

大语言模型的幻觉问题,本质上是因为 LLM 是概率模型,它在"生成"看起来合理的文本,而不是在"检索"事实。

作为前端开发者,我们在使用 AI 工具时需要:

  1. 保持怀疑:AI 的自信程度不代表准确度
  2. 主动验证:重要信息一定要二次确认
  3. 用好工具:RAG、低温度、结构化输出等都能帮助减少幻觉

在构建 AI 应用时,更要把幻觉问题作为核心考量,设计好防护机制,对用户负责。

最后说一句:AI 会犯错,但它依然是强大的生产力工具。关键是我们要学会正确地使用它,取其精华,避其不足。


参考资源


如果这篇文章对你有帮助,欢迎点赞收藏。有问题可以在评论区讨论,我会尽量回复。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐