ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

AI搜索如何避免幻觉?RAG原理与最小可用实现

AI搜索如何避免幻觉?RAG原理与最小可用实现 最近网上有个特别欢乐的测试场景有网友让 AI 搜索去查一个带有“梗”属性的人物问题结果 AI 一本正经地给出了一个拼接感十足、逻辑完全对不上的答案评论区直接笑喷。这类内容看起来是个段子但背后其实是一个很严肃的工程问题——AI 搜索到底是怎么工作的它为什么会在不熟悉的领域里“理直气壮地胡说”如果我们要自己做一个 AI 搜索工具又该如何尽量避免这种“笑喷”式的翻车这篇文章不会只停留在吃瓜层面。我会从 AI 搜索的技术链路讲起再手把手带大家实现一个“最小可用”的本地 AI 搜索 Demo包含检索模块、提示词组装、大模型生成、低置信度拦截四个环节。最后会给出 AI 幻觉的排查思路和工程化落地的建议适合想入门 AI 应用开发、RAG检索增强生成工程以及被 AI 胡答困扰过的开发者阅读。1. AI 搜索是什么为什么它会“笑喷”1.1 从传统搜索到 AI 搜索传统搜索的工作方式可以概括为“关键词召回 列表展示”。用户在输入框里输入“胡律师是谁”搜索引擎会先做分词、扩展同义词然后从索引里召回一批网页再按权重排个序最后把蓝色链接返回给用户。用户需要自己逐条点开网页阅读、比对、总结整个过程的信息筛选成本其实很高。AI 搜索则往前走了一大步你不需要自己翻链接AI 会自动理解你的问题去检索资料然后把答案用一句或一段自然语言整理出来。很多 AI 搜索产品还会在答案后面附上参考来源、引用位置让你可以顺着线索去验证。它的核心目标是把“找信息”变成“给答案”。两者的关系不是替代而是进化。传统搜索擅长召回AI 搜索擅长总结。这也是为什么现在的 AI 搜索产品普遍保留了“网页结果”和“AI 回答”两个视图。1.2 “AI 搜索胡律师”背后的技术现象回到标题里那个“笑喷”的场景。当用户问一个 AI 搜索产品自己并不了解、或者资料库里根本没有准确信息的问题时AI 可能会发生两种典型的错误第一种是“一本正经地胡说”也就是行业里常说的 AI 幻觉Hallucination。模型会根据训练时见过的相似文本强行生成一段听起来很流畅、但实际没有依据的内容。第二种是“过度自信的拼接”。AI 搜索系统从多个网页里抽出了碎片信息模型在生成时把这些碎片重新组织成句子结果主语、宾语、时间线全部错位读起来很像真的但经不起推敲。如果你仔细观察会发现这类翻车案例有一个共同点问题本身越模糊、越偏门、越缺少高质量资料AI 就越容易翻车。因为生成模型天生有“有问必答”的倾向尽量不要让它面对一个没有答案的问题还要硬答。1.3 AI 搜索的典型应用场景虽然 AI 搜索偶尔会闹笑话但这并不妨碍它成为当前 AI 应用开发中最实用的方向之一。常见的落地场景包括企业知识库问答把公司内部产品文档、工单记录、制度文件做成 AI 搜索员工用自然语言提问快速获取答案。客服自动回复结合常见问题库AI 搜索能够给出准确、统一口径的回答而不是让客服反复复制粘贴。学术资料检索从论文库中检索相关研究内容再用大模型整理成综述或摘要帮助科研人员提高效率。个人本地文档问答把本地 Markdown、PDF、TXT 做成可检索的知识库问“我上个月写的周报里提到了哪个项目”这类问题。舆情与信息分析从新闻、公告等文本中抽取关键信息辅助决策。在这些场景里AI 搜索的核心价值是“把检索到的资料变成可信的回答”。因此评价一个 AI 搜索系统好不好不仅要看答案是否流畅更要看答案是否有依据、能否追溯到原文。2. AI 搜索的工作原理2.1 一条完整的 AI 搜索链路先来看一个工业级 AI 搜索的典型流程我用文字拆成几个步骤用户输入自然语言问题例如“胡律师是谁”。查询理解模块对问题做意图分类和实体抽取可能还会生成多个改写后的查询词。检索系统通过关键词、向量等方式从索引库中召回候选文档。排序与过滤模块对召回结果做打分过滤掉低质量、低相关的片段。上下文组装模块把最相关的 Top-K 个片段拼接到提示词中。生成模型阅读这些片段生成带有引用标记的回答。后处理模块检查回答中的引用是否真实存在输出最终答案。这套链路里最核心的架构就是 RAGRetrieval-Augmented Generation检索增强生成。RAG 不是某个具体产品而是一种工程范式先用检索系统找到外部资料再把资料塞给生成模型让模型“看着资料回答问题”。2.2 关键模块拆解从查询到答案查询理解这是最容易忽略的模块。用户的原始问题往往口语化、有歧义比如“AI搜索会不会笑喷”这种问题如果不做改写检索系统很难召回正确内容。常见的做法包括同义词扩展、缩写还原、问题补全。检索引擎传统倒排索引擅长精确匹配向量检索擅长语义匹配。现在的主流方案是“混合检索”把 BM25 和向量相似度结合兼顾精确性和语义灵活性。重排召回阶段为了保证召回率通常会取回很多文档但相关性不高。重排模型会在小规模候选集上做精细化打分把最相关的内容排到前面再送给生成模型。生成模型生成模型只负责“总结”和“组织语言”不应该凭空产生事实。因此提示词里通常会写“请只根据参考资料回答不要使用你自己的知识”。2.3 为什么 RAG 能降低 AI 幻觉大模型本身的知识来自训练语料存在两个问题知识有截止日期而且面对冷门问题时容易胡编。RAG 通过把外部知识“临时拼进上下文”的方式让模型在回答时拥有最新的、可追溯的参考依据。但要注意RAG 只是降低幻觉的手段不是消除幻觉的银弹。如果检索到的内容本身不相关或者多个片段之间互相矛盾模型仍然会在生成时产生错误。下面我们会通过代码来演示这个问题。3. 环境准备与演示项目设计3.1 环境说明本文用 Python 编写一个极简 AI 搜索 Demo。为了减少第三方依赖检索部分我会用 Python 标准库实现一个最简单的 TF-IDF 权重排序生成部分通过requests调用 OpenAI 兼容的大模型接口。模拟环境如下操作系统Windows / macOS / Linux 均可。Python 版本3.8 及以上。第三方库requests。大模型服务任意支持 OpenAI 格式的服务例如本地部署的模型、云厂商大模型接口或者开源模型网关。开发工具VS Code 或 PyCharm 都可以命令行运行即可。如果你的环境还没有安装requests可以用下面的命令安装pip install requests3.2 项目结构为了便于理解我把不同职责拆分到不同文件里ai_search_demo/ ├── data/ │ └── documents.txt # 本地知识库 ├── search_engine.py # 简易检索器 ├── llm_qa.py # 大模型生成模块 └── main.py # 主入口串联整个流程这个结构刻意保持简单方便你复制到本地直接运行。实际工程中你可以把检索部分替换为 Elasticsearch、向量数据库等成熟组件。3.3 演示知识库设计为了让“AI 搜索胡律师”这个现象可被复现我设计了一个偏门问题与正常问题对照的测试方式。知识库里只放 AI 相关的词条不包含任何真实人名信息。这样当用户问“胡律师是谁”时检索系统找不到足够相关的内容就能触发低置信度拦截而不是让模型硬编答案。4. 动手实现一个“最小可用”AI 搜索4.1 准备本地知识库先创建一个data/documents.txt文件每行一条文档。这里我输入 8 条模拟知识确保检索器有内容可以召回AI Agent 是一种能够感知环境、进行决策并执行动作的智能体它可以调用工具完成复杂任务。 RAG 是检索增强生成的缩写核心思想是先检索外部知识再让大模型基于检索结果生成回答。 AI 幻觉是指模型生成看似流畅但实际错误或无依据内容的现象常见原因包括训练记忆冲突和上下文缺失。 向量数据库专用于存储和查询高维向量在语义检索场景中可以使用余弦相似度召回相近文本。 提示词工程通过设计输入指令来引导大模型输出符合预期的结果是 AI 应用开发的重要环节。 AI 搜索产品通常由查询理解、召回、排序、生成四个模块组成生成阶段需要结合参考来源。 大模型部署分为云端 API 和本地私有化部署两种方式企业需要根据数据安全要求选择方案。 Embedding 模型可以将文本映射为向量实现语义层面的相似度计算是向量检索的基础组件。这个知识库很小但足够演示完整的“检索 - 生成”流程。真实项目中知识库通常是几万到几亿条文档并且需要做分块处理。4.2 实现简易检索器接下来实现search_engine.py。我写了一个简化版 TF-IDF 检索器使用 2-gram 分词然后计算每个词的 TF-IDF 权重最后用余弦相似度排序。生产环境建议用 jieba 或词表分词但这里的实现能把核心逻辑讲清楚。# 文件路径ai_search_demo/search_engine.py import math import re from collections import Counter class SimpleSearchEngine: def __init__(self): self.documents [] self.doc_count 0 self.df Counter() # 包含词的文档数量 self.tf_idf_vectors [] # 每个文档的 TF-IDF 向量 def tokenize(self, text: str): 极简分词去除标点、转小写后按 2-gram 切分。 生产环境建议使用 jieba / 词表分词。 text re.sub(r[。、\s], , text.lower()) if len(text) 1: return [text] return [text[i:i2] for i in range(len(text) - 1)] def add_documents(self, docs): self.documents docs self.doc_count len(docs) doc_token_list [] for doc in docs: tokens self.tokenize(doc) doc_token_list.append(tokens) for token in set(tokens): self.df[token] 1 for tokens in doc_token_list: tf Counter(tokens) max_tf max(tf.values()) if tf else 1 vec {} for token, count in tf.items(): tf_value count / max_tf idf math.log((self.doc_count 1) / (self.df[token] 1)) 1 vec[token] tf_value * idf self.tf_idf_vectors.append(vec) def _cosine_similarity(self, vec1, vec2): common set(vec1.keys()) set(vec2.keys()) dot sum(vec1[token] * vec2[token] for token in common) norm1 math.sqrt(sum(v * v for v in vec1.values())) norm2 math.sqrt(sum(v * v for v in vec2.values())) if norm1 0 or norm2 0: return 0.0 return dot / (norm1 * norm2) def search(self, query: str, top_k: int 3): 返回 [(得分, 文档索引, 文档内容), ...] query_vec {} query_tokens self.tokenize(query) tf Counter(query_tokens) max_tf max(tf.values()) if tf else 1 for token, count in tf.items(): tf_value count / max_tf idf math.log((self.doc_count 1) / (self.df[token] 1)) 1 query_vec[token] tf_value * idf scored [] for idx, doc_vec in enumerate(self.tf_idf_vectors): score self._cosine_similarity(query_vec, doc_vec) scored.append((score, idx, self.documents[idx])) scored.sort(keylambda x: x[0], reverseTrue) return scored[:top_k]代码里需要注意几个关键点IDF 公式里加了平滑项避免某些词在全库中从未出现时计算溢出。2-gram 分词对英文和中文都有效但会牺牲一些语义精度。_cosine_similarity先计算共同词的点积再除以两个向量模长的乘积这是语义检索里最常见的一种相似度计算方法。4.3 组装生成提示词检索到相关文档后需要把文档内容组装成提示词。这一步很关键因为提示词的结构直接影响模型是否“胡说”。我新建一个llm_qa.py把提示词模板和大模型调用封装在一起# 文件路径ai_search_demo/llm_qa.py import requests SYSTEM_PROMPT 你是一个严谨的 AI 搜索助手。 请只根据参考资料回答用户的问题。 如果参考资料中不存在答案请直接回答“未找到相关资料”不要编造内容。 在答案末尾列出参考资料编号例如 [1][2]。 def build_user_prompt(query: str, contexts): 把检索到的上下文拼进用户消息 context_text for idx, ctx in enumerate(contexts, start1): context_text f[{idx}] {ctx}\n return ( f参考资料\n{context_text}\n f用户问题{query}\n f请根据参考资料回答。 ) def call_llm(query: str, contexts, api_key: str, base_url: str, model: str): 调用 OpenAI 兼容接口具体字段以你的模型服务商为准 url f{base_url}/chat/completions headers { Authorization: fBearer {api_key}, Content-Type: application/json, } payload { model: model, messages: [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: build_user_prompt(query, contexts)}, ], temperature: 0.2, max_tokens: 512, } resp requests.post(url, headersheaders, jsonpayload, timeout30) resp.raise_for_status() data resp.json() return data[choices][0][message][content]提示词里最重要的两句话是“只根据参考资料回答”和“如果不存在答案请直接回答未找到”。即使模型能力再强只要提示词里没有这层约束它就可能开启“自由发挥”模式。4.4 加入无答案检测减少“AI 胡诌”到了这一步我们已经能回答知识库内的问题了。但为了演示如何缓解 AI 幻觉我需要再加一个拦截机制当最高检索分数低于某个阈值时不再调用大模型而是直接返回提示信息。修改main.py# 文件路径ai_search_demo/main.py import os from search_engine import SimpleSearchEngine from llm_qa import call_llm KNOWLEDGE_FILE data/documents.txt API_KEY os.environ.get(LLM_API_KEY, your-api-key) BASE_URL os.environ.get(LLM_BASE_URL, https://api.example.com/v1) MODEL os.environ.get(LLM_MODEL, your-model-name) SCORE_THRESHOLD 0.08 def load_documents(path: str): with open(path, r, encodingutf-8) as f: return [line.strip() for line in f if line.strip()] def main(): docs load_documents(KNOWLEDGE_FILE) engine SimpleSearchEngine() engine.add_documents(docs) while True: query input(请输入问题输入 exit 退出).strip() if query.lower() exit: break results engine.search(query, top_k3) if not results or results[0][0] SCORE_THRESHOLD: print([拦截] 未找到足够相关的资料建议换个关键词或补充知识库内容。\n) continue contexts [doc for _, _, doc in results] print(\n 检索结果 ) for score, idx, doc in results: print(f得分 {score:.4f} | 文档 {idx}: {doc}) try: answer call_llm(query, contexts, API_KEY, BASE_URL, MODEL) print(\n AI 回答 ) print(answer) except Exception as e: print(f\n[错误] 调用大模型失败{e}) print() if __name__ __main__: main()这里我设置了一个SCORE_THRESHOLD 0.08。这个值不是固定的不同知识库、不同分词方式都会影响得分分布。实际项目中你可以先收集一批“无答案问题”统计它们的最高检索分数再确定阈值。4.5 运行与验证在项目根目录执行python main.py先输入一个知识库内的问题请输入问题输入 exit 退出什么是 RAG预期输出里检索器应该能召回包含“RAG 是检索增强生成”的文档然后大模型根据文档内容生成答案。再输入一个知识库外的问题请输入问题输入 exit 退出胡律师是谁由于本地知识库中完全没有相关文档2-gram 分词后很难与已有文档产生有效重合最高相似度分数会很低从而触发拦截[拦截] 未找到足够相关的资料建议换个关键词或补充知识库内容。如果我们把这个拦截步骤去掉直接让大模型回答会出现什么情况模型很可能凭借训练记忆给出一个看似完整的答案但那恰恰是“AI 搜索胡律师笑喷”的根源。这里的刻意拦截就是最直观的幻觉缓解方案。5. AI 搜索幻觉的成因与排查思路5.1 为什么 AI 搜索会一本正经地胡说从上面的 Demo 能看出AI 搜索的答案质量由两个阶段共同决定检索阶段和生成阶段。任何一个阶段出问题最终答案都会出问题。具体来说常见成因有这几类检索召回不相关用户问题复杂、多义词、口语化严重导致检索器召回的内容与问题无关。无相关文档时模型只能瞎猜。片段冲突召回的多个片段来自不同文档内容互相矛盾。模型没有足够能力判断该信哪个于是自己拼接出错误结论。模型知识干扰即使提示词要求“只根据参考资料回答”模型仍然会受到自身训练知识的影响把“记忆里见过但资料里没有”的事实写进答案。上下文截断文档太长被截断后只保留开头部分关键答案在中间或被切断导致模型误判。答案否定能力缺失部分模型在训练时被鼓励“尽量回答”导致它很难主动说“我不知道”。5.2 如何定位一次 AI 搜索翻车当 AI 搜索给出错误答案时不要急着怪模型。我一般按这个顺序排查看检索结果把模型输入阶段的 Top-K 文档打印出来人工判断这些文档和问题是否相关。看引用来源如果回答里没有引用任何来源或者引用来源指向无关文档问题很可能出在生成阶段。看提示词确认有没有写“只根据资料回答”“不要编造”等约束。看阈值拦截如果低分问题没有被拦截说明置信度判断没有生效。你可以在main.py里把SCORE_THRESHOLD临时改成0然后问一个知识库外的问题。你会发现模型开始强行作答这就能直接复现“笑喷”现象。5.3 高频问题与解决方案问题现象可能原因解决思路答案看起来流畅但引用来源不存在生成模型自由发挥限制提示词要求引用字段必须来自参考资料生成后核对引用问一个偏门问题AI 开始编人名和经历低分样本未被拦截设置检索分数阈值或增加“无答案分类器”检索到了相关文档但回答却出错上下文冲突或提示词不严谨只取 Top-1 或 Top-3减少无关片段进入上下文同一个问题换几个关键词就搜不到分词或索引粒度太粗改为混合检索BM25 向量召回模型回答很简短缺乏细节资料被截断或文档太短优化分块策略保留更多上下文知识库更新后答案仍是旧的缓存或索引未刷新建立文档更新管道定时重建索引6. 工程化落地的最佳实践6.1 数据与检索层知识库的质量决定了 AI 搜索的天花板。不要把原始 PDF、Word 整篇塞进知识库建议先做文档解析、清洗、分块。分块时要注意按标题语义分块而不是固定按 500 字硬切。保留文档的元信息如来源、日期、作者。同一份文档的不同版本要做好去重。检索层面千万不要只用向量检索。向量检索擅长语义召回但不擅长精确关键词匹配。比如查“RAG 全称是什么”关键词“RAG”可能很有辨识度向量检索反而可能拉回一堆“检索增强”相关但不够精确的内容。更好的方案是“BM25 向量召回再经过 rerank 模型重排”。6.2 提示词与模型层生成阶段最重要的原则是“不让模型自由发挥”。我强烈建议在系统提示词里保留这三个约束只使用参考资料中的信息。如果资料不足请直接回答“未找到相关资料”。回答中必须给出引用编号。温度参数建议调低temperature0.2是一个常用起点。温度越低输出越保守越不容易发散。如果项目允许可以给模型增加一个“后置校验”步骤用规则或另一个小模型检查生成答案中的事实是否都能在参考资料中找到找不到则打回重写。6.3 安全与权限AI 搜索类应用最容易忽略的是权限问题。企业内部知识库可能包含不同密级的内容如果不对用户做权限控制AI 搜索反而成了信息泄露的入口。建议做到以下几点用户身份认证与权限隔离A 用户只能检索到他有权限访问的文档。敏感数据脱敏身份证号、手机号、合同金额等字段在进入索引前做脱敏处理。内容安全审核对用户输入和模型输出都做敏感词、违禁内容过滤。审计日志记录每个用户的提问和回答方便追溯问题。生产环境变更要遵守最小权限原则先灰度再全量。6.4 评估与持续迭代AI 搜索上线只是开始。你需要一个持续的评测集用来衡量每次改动是变好还是变坏。评测集可以包含三类问题标准问题知识库能直接回答的问题。句式变体同一个意思的不同问法检验召回鲁棒性。无答案问题知识库中不存在答案的问题检验系统能否正确拒答。评估指标上除了看“回答正确率”还要关注检索召回率正确的文档是否出现在 Top-K 中。引用准确率回答中的引用是否真的支撑答案。拒答准确率不该答的问题是否成功拦截。收集 bad case 是迭代的关键。每次发现 AI 回答翻车都把该问题加入评测集再针对性优化检索、提示词或阈值。7. 下一步学习路线如果你想沿着 AI 搜索这个方向继续深入下面这条路线值得参考。第一步把本文的 Demo 扩展成“向量检索版”。引入一个 Embedding 模型把文档和问题都映射成向量再通过余弦相似度召回。你会发现语义检索在“换说法”场景下的效果远远优于 2-gram。第二步引入一个真正的向量数据库。熟悉 Milvus、Qdrant 或 Elasticsearch 的向量检索能力学习索引参数、分片策略和混合检索配置。第三步加入 rerank 重排模型。召回 50 条候选用 rerank 模型取 Top 5 进入生成阶段这是提升答案质量性价比最高的环节。第四步给 AI 搜索接入工具调用。例如用户问“今天天气怎么样”AI 先调用天气 API再基于返回结果生成回答。这一步会进入 Agent 开发范畴。第五步搭建一个评测与监控平台。把训练集、评测集、线上日志管理起来形成“发现 bad case - 优化 - 回归测试 - 上线”的闭环。AI 搜索这个方向的技术栈比较宽但核心逻辑始终是“先检索后生成能拒答有引用”。先把最小闭环跑通再针对你的业务场景把每一层做深后面很多问题都会迎刃而解。如果这篇文章帮你理清了 AI 搜索的基本原理欢迎收藏备用后面动手实现时可以直接对照着查。
返回列表