最近在做文本质量评测时我把大量精力从“让LLM来评判LLM”切换到了“用几何方法检测AI幻觉”这条路上。这个决定有点反直觉毕竟现在大家都习惯用大模型当裁判觉得这样才够“智能”。但我实际跑下来的感受是几何方法更轻、更透明、更稳定而且它真的能抓到一个很本质的问题——生成内容离真实事实到底有多远。这个方向适合谁呢主要面向正在做RAG问答系统、Agent工具调用、或需要对大模型输出做质量兜底的人。如果你已经受够了LLM评判带来的高成本、不稳定结果或者希望有一个不依赖第三方模型的内部检测模块那这篇文章应该能给你一些非常具体的参考。我不会在这里堆理论而是把我在一个模拟项目X里的完整实践——包括嵌入空间里的距离度量、密度估计、轨迹分析、阈值标定以及踩过的坑——全部拆开讲清楚。你会发现很多听起来像数学玩具的概念放到真实文本上时反而比一个“满嘴正确废话”的评判模型更可靠。1. 为什么“不用LLM评判”是更现实的方案先说个大背景。AI幻觉是指模型生成了一段内容听起来流畅自洽但事实上是编造出来的。更麻烦的是它往往藏在细节里一个不存在的引用、一个错误的日期、一段看似合理但完全不支持结论的“论据”。在大模型落地到客服、医疗问答、企业知识库这些场景时幻觉不是学术问题而是实打实的信任风险。1.1 传统做法LLM评判的本质与代价最常见的检测方式是让另一个大模型去读答案并做一致性打分。这个思路本身没什么问题但放在真实环境里会有一连串痛点。第一个痛点是成本。假设每天有百万次请求每次检测都要再调用一次大模型意味着GPU开销直接翻倍。就算用更小的模型也是一笔不小的支出。第二个痛点是稳定性。大模型的打分受温度、提示词、模型版本影响极大同一个答案可能这次给90分下次给70分半年前换了个提示词模板整个体系的分数都漂移了你很难向业务方解释清楚。第三个痛点是幻觉的传染性评判模型自己也会幻觉尤其在长篇文本和细粒度事实一致性上它可能把正确的答案误判为错误的或者反过来。我还遇到过更尴尬的场景如果生成模型和评判模型同源比如都用同一个基座微调那评判模型很可能对生成模型的错误模式“习惯成自然”睁一只眼闭一只眼。这种情况下LLM评判就成了“自己考自己”参考意义大打折扣。1.2 几何检测与其它替代路线的横评那不用LLM评判还能用什么业界其实有几条路线基于规则的关键词匹配、基于结构化事实抽取的比对、基于NLI自然语言推理的蕴含判断以及我们今天聊的几何方法。规则匹配太脆换个说法就失效事实抽取比对依赖于信息抽取质量模型漏抽了关键实体后续比对就白做NLI方案需要专门训练或调用专用模型泛化性一般。相比之下几何方法的核心优势在于它完全不“读”文本只做向量空间里的几何计算。你不需要标注数据不需要训练分类器不需要第二个大模型只需要一个嵌入模型和一个合理的度量方式。它的适用边界也很清晰嵌入模型的质量决定上限几何假设决定策略是否有效。但在工程可维护性上几何方法几乎完胜——模型固定、逻辑固定、结果可复现这在生产系统里非常宝贵。1.3 模拟项目X里的第一组对比数据为了验证这个判断我在模拟项目X里做了一个小范围对比。场景是“给定一段知识库上下文检测答案是否忠实于上下文”大概用了2000条问答对其中约15%是故意构造的幻觉样本包括错误事实、无中生有的引用和过度的推断。我分别用三个方案跑了一遍一个强一点的通用大模型当评判者、一个微调过的NLI模型、一个基于SBERT嵌入的几何距离方案。结果很有意思在F1分数上几何方案能到82%LLM评判约86%NLI方案约85%几何方法稍弱但没落后太多。可如果看单条检测的延迟和成本几何方案是毫秒级、免费LLM评判是秒级、按token付费。再考虑到稳定性同一个批次跑三次LLM评判的分数方差是几何方案的十几倍。这组数据让我下决心把几何方法深挖下去。2. 几何方法背后的直觉与数学骨架很多朋友听到“几何方法”就觉得门槛高其实它的核心思想特别朴素把一句话放进一个高维空间变成一个点然后比较点和点之间的位置关系。如果你的答案是贴近事实的它就应该离事实的语义区域很近如果是编的它就应该飘到某些语义的“无人区”。2.1 把句子放上“语义地图”嵌入空间的基本玩法嵌入模型本质上是把语言映射成向量。学过一点NLP的朋友都知道这空间不是随便乱摆的它经过大量语料训练后天然会形成一种“语义地图”意思相近的句子距离近无关的句子距离远甚至某些方向对应着某种语义属性。我们不关心整个地图长什么样只关心地图上的相对位置。这跟你用地图找路是一个道理。你把“今天天气很好”和“今天阳光灿烂”投到地图上两个点会挨得很近把“今天天气很好”和“这台打印机坏了”投进去两个点就隔了十万八千里。幻觉检测要做的就是判断“模型生成的这句话”这个点和“它声称依据的材料”这个点到底该不该挨得这么近或这么远。操作上句子嵌入这一步有很多现成工具。我常用的是sentence-transformers选择一个合适的预训练模型然后调用 encode 函数把文本变成768维或1024维的向量。这一步是整个方案的基石因为它决定了你在哪个坐标系里做判断选错了模型后面所有几何计算都是白搭。2.2 三条核心几何线索距离、密度、路径轨迹真正让我觉得几何方法有潜力的地方是可以从三个互相独立的角度去观测幻觉距离、密度、轨迹。距离是最直观的。把事实上下文表示成一个向量通常是多个证据句向量的聚合再把生成答案表示成另一个向量计算两者之间的夹角或欧氏距离。答案越离谱距离越大。它的优点是简单、快速、非常容易调缺点是只做一个全局度量容易忽略局部细节。密度比距离更进一步。在嵌入空间里事实不是孤零零的一个点而是围绕某个主题形成的一团“点云”。比如知识库里有一百条关于某产品参数的资料它们会聚成一团。如果一个答案声称是关于这个产品的但它嵌入后落在点云非常稀疏的外围那它大概率有问题。这个思路很像现实中的“社群”住在你小区附近的人哪怕是陌生人你们之间也有某种共性一个声称住在你小区但出现在荒山野岭的人你很难不怀疑他。轨迹则是我最近在深入的方向。它能利用生成过程中的中间状态模型在生成每个token时内部其实都有一层隐藏向量把这些隐藏向量串起来就得到了一条“思考路径”。幻觉生成时这条路径往往会表现出某种异常——比如在某一点突然跳变或者形成一个很大的环。测量路径的曲率、转折频率或整体偏移就能捕捉到那些单个句子看着正常、但上下文衔接很怪的幻觉。2.3 这些线索为什么能抓到幻觉很多人会问嵌入空间真能区分事实和幻觉吗我的理解是它不一定能区分“真”和“假”但它能区分“常态”和“异态”。这其实已经够用了。正常情况下模型生成的忠实答案源于它对上下文的理解和复述这种复述在语义上会非常接近原文。而幻觉发生时模型往往是受到了训练先验的“诱惑”比如看到“某一年的诺贝尔奖得主”就开始回忆训练语料里的排列组合而不是基于给定上下文作答。于是这段生成内容的语义重心就会偏离在几何上表现为距离变大、密度降低、轨迹异常。我打个比方。如果说大模型生成答案是“沿着一条神经网络的高速公路开车”那忠实生成就是稳稳行驶在连接上下文的既定路线上幻觉则是突然驶离了公路开进了一片没有导航数据的地方。几何方法检测的正是“你是否开在铺装路面上”——虽然它不知道每条路通向哪但它能判断你是不是偏离了路网太远。3. 实操从嵌入到幻觉分数的完整落地流程这一节我尽量按可以直接“抄作业”的方式写。你不需要像我一样从零搭但理解了每一步的意图之后换成自己的数据和模型时就不会手足无措。3.1 环境准备与思路选型我用的环境很简单Python 3.10sentence-transformers 库numpy 和 scikit-learn 做计算。没有用GPU因为嵌入模型用的中等尺寸在CPU上批量编码也只需要几秒钟。如果你们有GPU那体验会更好但几何计算本身就是向量运算瓶颈不在计算而在嵌入生成。选嵌入模型时我有几个硬性标准。第一要支持领域术语。通用模型虽然什么都知道一点但很多专业词汇在它的词典里根本不存在或者被拆成奇怪的token。第二维度别太高不然算距离时矩阵太大内存吃紧。第三尽量选对句对匹配任务优化过的模型而不是只做句子分类的模型。我最终选了bge系列的某个中尺寸模型它在中英文上的语义匹配能力比较均衡维度是768维速度和精度都合适。提示如果你处理的语料非常垂直比如医学或法律强烈建议先用一个小的领域语料测试一下嵌入模型的相似度区分能力。选错模型后面所有几何度量的意义都会失真。3.2 句子级与段落级的两套度量实现第一步是把文本归一化。去噪、去重、清理引用标记。这一段看似普通但直接影响嵌入质量因为嵌入模型对标点、空格很敏感同一个句子加个换行符可能就飘出好远。第二步是获取向量。我写了一个简洁的接口输入文本输出向量统一做了归一化方便后续计算余弦相似度。第三步是定义距离度量。句子级检测时问题可以简单定义为生成句子与它声称依赖的证据句子之间的余弦距离。这里我有意没有直接用欧氏距离因为经过L2归一化后的余弦距离更稳定它只关心方向不关心向量的长度。长度在嵌入空间中其实没啥意义但方向能很好地反映语义走向。段落级检测时我会采取“窗口化”策略把长答案切分成若干个句子或窗口每个窗口和它所对应的证据片段单独计算距离然后取最异常窗口的分数作为整个段落的代表分数。这里有个关键决策不用平均而是取最大值。原因是幻觉往往只发生在段落中的一小段如果平均所有窗口那一个大错误会被一整段正确内容稀释掉这跟质检逻辑是相符的一个产品有一处严重缺陷整体就该判不合格而不是因为其他部分合格就打满分。3.3 阈值不算玄学基于数据分布的标定方法有了几何距离怎么定“多远才算幻觉”这是最容易被问的问题。我给出的答案不是拍脑袋设一个数而是基于分布标定。我准备了500条已知正常的生成样本和500条已确认的幻觉样本。对这两组样本分别计算几何距离画出分布曲线。正常情况下两条分布曲线会有明显的分隔正常样本集中在一个较近的距离范围内幻觉样本则呈现一条长长的尾巴。我的阈值点就选在两条分布重叠最少的位置通常用recall和precision的平衡点来验证。如果重叠太多说明嵌入空间对当前领域的分辨率不够那就得换模型或增加密度判别作为辅助。这个流程强调了任何阈值都必须基于真实数据来标定不能拿一个公开的“0.3”或“0.5”直接套用。因为不同领域、不同模型、不同文本粒度距离的绝对数值差别很大。我见过有人拿别人的阈值套自己的数据结果误报率高达40%骂骂咧咧地放弃了整个方案。3.4 一个可直接运行的参考实现为了方便你直观理解我给出一个缩略版的参考实现。它做的是“上下文—答案”句子级余弦距离检测可以当作一个起点模板。import numpy as np from sentence_transformers import SentenceTransformer # 初始化嵌入模型 model SentenceTransformer(BAAI/bge-small-zh-v1.5) def embed(texts): vecs model.encode(texts, normalize_embeddingsTrue, batch_size64) return np.asarray(vecs) def detect_hallucination(context, answer, threshold0.35): # 将长文本切成句子作为证据单元 evidence_sents [s.strip() for s in context.split(。) if len(s.strip()) 5] answer_sents [s.strip() for s in answer.split(。) if len(s.strip()) 5] if len(evidence_sents) 0 or len(answer_sents) 0: return None # 首先为所有证据句编码 ev_vecs embed(evidence_sents) scores [] for sent in answer_sents: a_vec embed([sent])[0].reshape(1, -1) # 取与该答案句最相关的证据句 sims (a_vec ev_vecs.T).flatten() max_sim sims.max() scores.append(1 - max_sim) # 转为距离 # 用“最坏片段”作为整段答案的分数 anomaly_score max(scores) return anomaly_score threshold, anomaly_score # 用例 context 某公司2023年营收为120亿元同比增长8%。净利润为15亿元。 answer 该公司2023年营收为120亿元利润增长显著。 print(detect_hallucination(context, answer))这段代码逻辑很直白对答案的每个句子找和它最像的一条证据算距离最后取最大距离作为整体异常分数。它没有处理上下文切句的细节也没有做证据聚类但作为快速验证足够了。3.5 在成本与延迟上做什么取舍几何方法一个很大的优势是离线预计算。知识库是相对固定的我可以把所有证据句子的向量一次性算好存成向量索引。线上请求时只需对答案句子编码然后做一次矩阵乘法和一次最大值计算延迟通常在几十毫秒内。我在模拟项目X里做了一组基准测试20万个证据句子在只利用CPU和内存索引的情况下单次检测的平均延迟是15毫秒左右。如果换成FAISS做索引可以进一步压到几毫秒。相比一次大模型调用动辄几秒这种性能差距对高并发场景非常友好。代价是嵌入模型的部署和更新。如果你换了领域模型或知识库大改证据向量需要重新计算。这种“批量离线任务”工程上很好处理大概半小时的耗时属于可接受范围。4. 我踩过的坑与排查实录这部分才是最有价值的。几何方法看似简单实际落地时会有很多奇怪的状况。我把几个典型问题记录下来帮你少走弯路。4.1 嵌入模型的领域适配问题向量相似但事实错误最坑的第一个问题嵌入模型只擅长“语义相似”不擅长“事实一致”。举一个我在模拟项目X里遇到的真实例子。上下文是“某产品支持蓝牙5.2协议”模型生成的是“某产品支持蓝牙5.0协议”。从语义相似度看这两个句子几乎完全一致几何距离很小被判定为正常。但从事实角度它就是硬伤级别的幻觉。这个问题解决不了只能缓解。我用的办法是引入“关键实体校验”作为预处理先用一个轻量NER模型抽出数字、型号、日期、专有名词等关键元素如果这些元素在上下文里找不到完全一致的对应就直接标记为高风险跳过几何计算。几何方法负责捕捉“意象层面”的偏离实体校验负责捕捉“事实层面”的错误两条腿走路比单靠一种方式强得多。注意别指望任何嵌入模型能天然理解“5.2和5.0不一样”。对数值变化非常敏感的场景光靠纯几何是兜不住的必须有实体级的约束。4.2 长文本信息被“平均稀释”的问题另一个大坑是长答案的聚合方式。一开始我偷懒把整个答案一次性编码成一个向量然后和上下文算距离。结果发现很多长答案哪怕有一半内容都是编的被整体平均后距离依然很小。这是因为嵌入模型在编码长序列时会做全局信息汇聚局部异常被淹没在大部分正常内容中。后来我改成了“窗口级局部异常检测”也就是前面提到的切句和取最大值策略。这样一改检测准确率立刻提升了一个档次。大家在做这类检测时我强烈建议放弃“全局向量”思路改成“局部最可疑”思路。4.3 分布漂移所造成的误报上下文变了你的阈值也得变我在模拟项目X里还有一个很尴尬的经历开始在小规模语料上调好的阈值拿到更大规模的知识库上后误报率直线上升。原因很简单不同来源的知识库在嵌入空间里的分布天然不一样。一个紧凑的知识库和一个内容杂乱的网页库正常答案的“距离基线”本身就不同。解决方式也很简单按知识库分片每个分片单独标定阈值。或者定期用一小批人工标注样本校准阈值比如每周一次自动回测。这其实是落地任何检测系统都会遇到的“分布漂移”问题几何方法也不能幸免。关键是你在设计系统时提前留好“再训练”和“重定阈值”的接口别把阈值写死在代码里。4.4 用降维可视化来调试肉眼比数字更能发现问题这是我强烈推荐的一个调试技巧。当你觉得检测结果怪怪的但不知道问题出在哪时可以把证据句向量和答案句向量投射到二维平面用t-SNE或PCA可视化一下。人的视觉系统在发现“离群点”和“异常团簇”方面的能力远超纯看数字。我在一次调试中发现大量误报答案其实都聚在一个远离正常证据的区域。后来一查是嵌入模型把不同语体口语和书面语分得太开导致一个明明正确的口语化答案被判定为异常。如果不做可视化这个问题我很难发现。这个习惯我一直保留到现在每次换领域、换模型的时候先可视化一批样本心里就有谱了。4.5 如何解释给业务方用话术让几何方法可被接受技术上搞定了还有一个躲不掉的问题怎么向非技术同事解释几何检测的原理和可信度。你说“我们在向量空间里做密度估计”对方肯定一头雾水。我常用的解释方式是“我们让每一段文本变成地图上的一个坐标点。如果答案和它引用的材料实际距离很远或者它自己孤零零地落在无人区那说明它更可能是自己编的。”再用一个具体案例演示一遍别人很快就理解了。再配合一个简单的可视化图把异常样本用红点标出来正常样本用蓝点标出来业务方自己都能看出“那些红点确实比较离谱”。这种“可解释性”反而成了几何方法隐含的巨大优势——毕竟在面对质疑时一张分布图比一个“AI法官给出了72分”更能说服人。5. 后续还能怎么扩展从检测幻觉到预防幻觉最后说说这个方向的扩展空间。几何方法不只是用来做检测还可以反过来影响生成过程实现“边生成边纠偏”。我在最近的实验里尝试了一种廉颇做法在生成的关键节点把当前已生成的部分和历史证据做距离感知如果发现偏差正在放大就提醒解码器压低某些候选token的权重。因为是在向量空间里操作这个过程几乎是实时的不需要额外的大模型做反思。这种“几何感知解码”目前还没有完全成熟但我觉得这是比“检测后拦截”更优雅的一种形态。等我把这块的实验再打磨得更完整些会单独写一篇文章分享。另外几何方法可以和知识图谱结合。如果把结构化事实也映射到同样的向量空间那检测就不只停留在“和上下文一致”还能延伸到“和世界知识一致”。这会大幅拓宽幻觉检测的适用范围也是我个人非常看好的方向。整个实践下来我的体会是几何方法不是万能药它没办法自动理解事实细节没法替代细致的事实校验。但是它提供了一个极其轻量且稳定的“第一道防线”用极低的成本筛掉大部分明显偏离的生成内容然后你再把少量可疑样本交给更强的模型或人工做深入判定。这套“几何粗筛 LLM精判”组合拳无论在性能还是效果上都比单一方案稳妥得多。如果你也想搭建一套幻觉检测体系我建议从简单的余弦距离开始跑通之后再加密度和轨迹分析一步步演进你会看到这个方向越来越值钱。