大模型幻觉治理:从常识形式化到Agent规则校验层
先把时间拨回到一次不算复杂的调试现场。我在本地部署了一个带 Agent 能力的问答应用模型用的是开源的中小型大模型。用户问了一个非常生活化的问题“我今天下午想去图书馆还书图书馆会开门吗”模型给出的回复是一大段结构完整的说明里面有“系统正在处理您的请求请稍后查看”这类话术还有一段听起来很合理的开放时间描述。问题在于模型根本不掌握本地图书馆的真实开放时间它也没有去查知识库它只是“看起来像在回答”。当时我最大的感受不是模型能力不够而是它缺少一样很基础的东西它不知道“回答不知道”这件事本身是一种常识。它更不知道当信息不足时正确的做法是先补充必要的信息而不是先写一段漂亮话。这个场景恰好让我重新想起一位老前辈。John McCarthy人工智能这个名词的提出者之一也是逻辑和形式化常识研究路线的奠基人。他在上世纪中叶就提出过一个问题机器如何拥有常识如何把人类默认不说、但对推理至关重要的那部分知识转换成程序可以使用的形式几十年过去这个问题非但没有过时反而因为大模型的流行变得更加尖锐。大模型擅长用自然语言生成“像常识的话”但它未必通过逻辑推理获得这些答案。重新看 McCarthy 的那套思路对今天做 AI 应用、做 Agent 开发、做大模型工程化的人来说不是怀旧而是补课。这篇博客我想把“AI、逻辑与常识形式化”这件事拆开来讲。不打算写成哲学讲义而是想回到工程视角John McCarthy 当年到底为了解决什么难题提出了这条路线它为什么难它和大模型时代有什么关系我们今天能不能用它的思路解决实际开发中的“模型一本正经胡说八道”问题1. 重新看 John McCarthy 这道题它不是哲学课而是 AI 系统的地基问题1.1 人工智能的诞生绕不开“常识”这个关键词提到 John McCarthy绕不开两个标志性事件。第一个是 1956 年的达特茅斯会议按照计算机史上的通行说法“Artificial Intelligence”这个术语在这次会议上被正式使用McCarthy 是发起人之一。第二个是他在 1959 年前后写作并发表的论文题目是 “Programs with Common Sense”核心思想是讨论如何让程序具备常识推理能力。这里需要澄清一个常见误解。McCarthy 做“常识形式化”不是在课堂里讨论“普通人如何思考”而是在回答一个具体的工程问题机器能不能根据目标自主选择要做的运算而不是每一步都由程序员硬编码当年主流的 AI 路线是“启发式搜索”。程序通过搜索组合爆炸式的可能性选出看起来最优的策略。Chess 程序、下棋程序、走迷宫程序都是这种思路的产物。但 McCarthy 认为这条路走不远。原因很简单光靠搜索程序不知道自己为什么要搜索。它缺少一种把“当前目标、可用知识、可选行动、预期结果”组织起来的框架。于是他在论文里提出一个著名的设想“咨询者”。这个程序内部有一条知识库里面有关于外部世界的陈述比如“船可以过河”“渡船通常运行时间有限制”。程序不做程序员的奴隶而是能接受人类用自然语言或逻辑语言给出的建议再根据目标自行推导。用今天的眼光看这就是一个“常识底座 推理引擎 行为执行”的雏形。放在上世纪五十年代这个设想非常超前超前到当时连足够大的内存和足够快的推理速度都不存在。但 McCarthty 想解决的根本问题和大模型时代我们遇到的问题是同一个机器不能只会模仿行为还要能对行为做出可解释的判断。1.2 McCarthy 真正想做的事不是“逻辑万能”而是“知识可被机器检查”很多人提起 McCarthy就以为他主张“一切智能都可以用逻辑表达”。这是不准确的。如果你真的读过关于他思想的文献会发现他更关心的是如何让程序“接受建议”而不是让逻辑替程序做所有决定。在接受建议这个需求下逻辑变成了一个基础设施。因为一个建议要能被程序接受就必须要形式化。形式化意味着程序可以对其进行一致性检查、推理、查询、修正。这和今天我们做业务系统的思路是一样的你希望 AI 能读取数据库、调用接口、根据规则做决策那你就需要一套能让机器理解和操作的知识表示方式而不是让自然语言漂浮在模型上下文中。McCarthy 的核心判断我理解成一句话真正的智能系统必须能把“默认不说的常识”转成“可以被检查和修正的知识”。这句话在今天依然成立。甚至可以说大模型时代最缺的就是这个。模型内部有千亿参数有海量语料统计规律但当模型说“图书馆开门”时你无法检查它是从哪条知识里推导出来的也无法在不重新训练的情况下修正它的错误。因为知识没有被显式形式化它只是参数的一部分。这就是 McCarthy 这条路线真正的价值所在它提醒我们人工智能不只有“行为层面”的智能还有“机制层面”的知识表示与推理。没有一个可检查的知识层智能系统就永远是黑盒。2. 形式化常识为什么难逻辑不是缺严谨性而是缺“默认值”2.1 逻辑擅长演绎但不擅长处理“通常”和“例外”McCarthy 很清楚把常识写进逻辑并不容易。难点不在逻辑本身而在常识的特殊性质。常识和数学公理完全不同。数学公理是尽可能少的、无例外的、长期不变的规则。常识呢常识是高度依赖语境的、有例外的、甚至相互冲突的。一句话逻辑是演绎的常识是概率性的逻辑偏爱“永远成立”常识偏爱“通常成立但有例外”。比如“鸟会飞”。这是一个非常典型的常识判断。但“鸟”这个类别里有企鹅、鸵鸟、几维鸟它们不会飞。如果你把“鸟会飞”写成一条严格的一阶逻辑规则那一旦遇到企鹅整个推理系统就崩溃了。因为你推出“企鹅会飞”这与现实矛盾。McCarthy 面临的问题就是如何让逻辑系统能够表达“除非有例外否则默认鸟会飞”2.2 默认逻辑让规则可以被推翻但只有确实出现反例时才推翻为了解决这个问题McCarthy 在 1980 年前后提出了“默认逻辑”。它的核心想法很简单但影响深远允许规则带有一个默认条件默认条件在没看到反例时可以成立一旦出现反例就撤销这条默认结论。用一句通俗的话说默认逻辑让系统学会了“在没有特殊说明的情况下先按常识走但保留修正可能”。这个设计在工程上非常重要。因为它允许 AI 系统在信息不完整的情况下做出初步判断同时允许在获得新证据时自动纠正结论。这不就是现实世界里人类处理信息的方式吗你约了朋友吃午饭默认朋友会准时到但如果你收到“路上堵车”的消息就会更新预期。默认逻辑只是一个开端。McCarthy 还研究过“时间演算”用来处理事情发生的前后顺序和因果影响研究过“耐心理论”用来表达“知识”“信念”“意图”这类心理状态。这些东西听起来偏哲学但放到今天的 Agent 系统里就是状态机、事件驱动、意图识别和上下文管理的思想源头。2.3 心智状态的表达常识还涉及“知道”和“不知道”真正让情况变复杂的是常识中关于认知状态的部分。人类常识里有大量命题是关于“知道”的。比如你知道自己的生日知道开门前需要插钥匙知道不知道某个答案时可以说“我不知道”。这些知识不是关于外部世界的而是关于“自己知道什么别人知道什么信息是否充分”的。McCarthy 在相关研究里明确指出AI 系统如果要和人协作就必须能表达这类心智状态。否则系统既不知道自己在哪些领域知识充足不知道什么时候该向用户反问也不知道如何描述自己的不确定性。放在今天的大模型应用里这个点格外扎眼。你问一个大模型“你确定这个回答是对的吗”它通常会回答“是的根据我的理解”哪怕这个回答完全是编造出来的。因为模型没有一种机制来判断自己“是否知道”。这不是模型的道德问题而是架构缺陷它没有一个显式的、可检查的知识边界。一个能形式化心智状态的系统至少可以表达这样一条规则如果“用户问的问题不在我的知识库覆盖范围内”那么我应该输出“信息不足无法确认”而不是凭空生成答案。就是这么一条简单的公理大多数大模型应用实现不了。原因不是工程难度而是因为早期架构里没有这个概念。3. 技术史的下一站从 Advice Taker 到知识图谱、本体与大模型护栏3.1 知识图谱和本体工程把常识装进结构而不是参数McCarthy 的路线在二十世纪后半叶演变成了几个分支其中最有名的工程产物是知识图谱和本体工程。知识图谱的逻辑是把世界的实体、概念、属性和关系组织成一张图用节点和边表示。比如“北京”是“中国”的首都“中国”是“中华人民共和国”的简称“长城”位于“北京”地区。这样机器可以通过图谱查询关系进行简单推理。本体工程则更进一步它试图定义某一领域的概念层次、属性、约束和规则。在医疗、法律、金融这些需要严谨逻辑的行业里本体系统至今仍在发挥作用。比如医院的电子病历系统会用本体定义“患者”“诊断”“药物”“过敏反应”之间的关系当医生开出一种与过敏史冲突的药物时系统就会报警。这条路和 McCarthy 的关系非常直接他们都认为知识必须先被表示成机器可以操作的结构才谈得上推理和判断。区别在于McCarthy 希望找到一个通用框架而知识图谱和本体工程落地时往往是面向特定领域的。3.2 大模型的幻觉问题让逻辑层重新变得必要大模型出现后很多人一度认为“不再需要人工写规则了模型自己就能学会一切”。但随着实际业务落地越来越多团队发现了问题。模型的生成过程本质上是“基于上下文预测最可能的下一段文字”。它不像知识图谱那样有明确的查询机制也不像规则引擎那样有可检查的推理链路。所以当模型遇到知识盲区时它并不会像人一样说“我不知道”而是会用已有语料中相似话题的统计规律拼出一段看起来合理但实际上可能是错误的内容。这就是常说的“幻觉”。幻觉为什么难以消灭因为模型没有“常识一致性检查”模块。你可以在提示词里写“请只在确定时回答”但模型本质上不会执行这条指令它只是在生成文本时更谨慎一点。真正的解决方案必须从架构层面引入一个“逻辑校验层”。这个逻辑校验层就是在重拾 McCarthy 当年的想法把常识问题拆成知识表示和推理两部分。对大模型来说生成文本是能力的一部分对业务应用来说还要加一层判断这段文本是否和已知知识一致是否违背了必要前提是否超出了知识覆盖范围3.3 Agent 与工具调用推理外壳就是形式化常识的现代版最近一年做 Agent 应用的人越来越多。所谓 Agent通常指一个可以自主规划任务、调用工具、观察结果、迭代执行的系统。它们不像传统程序那样按固定流程走而是根据目标动态选择工具和路径。这种动态决策恰恰需要大量的“常识”。比如 Agent 不知道当前时间是几点就无法判断是否应该提醒用户“你有一个日程安排在 10 分钟之后”。比如 Agent 没有业务规则约束就可能输出“可以取消订单”这种错误建议。所以在实际工程里成熟的 Agent 框架都会加一层“推理外壳”。这层外壳可能是提示词模板可能是状态机可能是决策树也可能是一组硬性约束规则。它的作用就是确保 Agent 的每一步行动都符合必要的常识而不是完全依赖模型的自由发挥。如果你把状态机里的先决条件、Action 前置检查、上下文校验这些机制拿出来看会发现它们本质上是 McCarthy 把常识形式化 这个命题的工程化变体。只是换了一套语言换了一种部署方式问题内核并没有变。4. 最简工程路径给 AI 应用加一个“常识判断层”4.1 不要一上来就搭复杂的本体系统先定义“不满足输入”很多人读完 McCarthy 的“常识形式化”概念第一反应是那我是不是应该先建一个庞大的知识图谱把世界上的常识全部装进去我的建议是千万不要。知识图谱和本体系统成本很高维护复杂而且容易僵化。对于大多数中小团队和普通业务场景你根本不需要“全世界的常识”只需要“你这个业务场景里最基础的那几十条规则”。最稳妥的做法是先定义“不满足输入”的规则集。也就是在什么情况下程序必须拒绝处理或者返回“信息不足”。举个例子。如果你在做一个日程安排助手你需要定义的基础规则可能包括用户必须提供“开始时间”如果没有就不能创建日程。如果用户只说了“明天下午”而没有说明具体的小时数就属于信息不完整。如果用户提到“明天”但当前时间已经是今晚 23 点以后那就需要提醒“明天其实只有一小时你确定吗”。如果用户要邀请的会议室已经存在冲突就必须返回冲突提示而不是硬生成一个预约成功。这些都是常识都不难写但它们就是程序能不能正常使用的关键。4.2 用约束规则和兜底策略守住模型的生成结果我一般会把“常识判断层”放在模型前后两端做成两道闸门。第一道是输入校验闸门。在把用户问题交给大模型之前先检查必要字段。字段缺失就触发反问流程不让模型自行补全。这样做的好处是防止模型一本正经地把“今天下午有空”这种模糊表述理解成“今天下午 15 点”。第二道是输出校验闸门。模型生成结果后用一组规则检查输出是否合法。比如结果里是否包含虚构的订单号、是否包含现实中不存在的商品、是否和用户提供的上下文冲突。如果冲突就触发兜底策略比如返回“这个问题我暂时无法确认建议人工客服介入”。以 Python 为例一个最小的实现框架大概长这样class CommonSenseGate: def __init__(self, rules, fallback_text): self.rules rules self.fallback_text fallback_text def check(self, text, context): for rule, reason in self.rules: if not rule(text, context): return False, reason return True, text def respond(self, text, context): ok, result self.check(text, context) if not ok: return self.fallback_text.format(reasonresult) return result规则本身很简单可以是一个函数列表def require_has_datetime(text, context): has_time_fragment any(k in text for k in [上午, 下午, 点, :]) has_date_fragment any(k in text for k in [今天, 明天, 周, 月, 日]) return has_time_fragment and has_date_fragment这段代码不复杂但它起到了一个非常关键的作用确保基本信息必须由用户显式提供而不是让模型猜。这就是“常识”落到代码里的样子。4.3 日志、回滚、人工复核常识层真正进入生产的最后三块拼图如果你只是想写一个 demo上面两段代码就够了。但如果你要把常识判断层放进生产环境我建议再补三样东西。第一是日志。每一条被拦截的问题都要记录“为什么被拦截”“命中了哪条规则”。日志不仅能帮你发现规则是否过于激进还能为后续补充规则提供数据依据。第二是人工复核。无论规则写得多完善都可能出现误伤。用户可能确实想表达一种非常规的请求比如“今天下午我没有具体时间你可以帮我推荐一个时间吗”这种请求在规则引擎里会被拦截但人不应该被拒之门外。所以要在拦截后给出兜底入口让人工客服或更高层级的模型介入。第三是规则版本管理。常识会变化。上班时间、节假日、店休信息、权限范围都会变化。如果你的规则是硬编码在代码里的那每一次修改都需要发布。建议把规则做成配置比如 JSON 或 YAML 文件定期更新这样常识判断层才不会变成一个新的僵化系统。4.4 一个分类判断表哪些场景适合用规则层哪些适合用模型不是所有问题都需要常识判断层。我习惯用下面这个表格来做初步决策场景类型是否适合加规则/逻辑层原因典型示例高风险金融操作是必须加错误判断代价极高必须可解释、可审计转账、交易、订单取消日程/预约类是建议加需要处理时间、地点、冲突规则明确会议室预订、出行安排内容生成类否轻量即可目标是多样性和创造力规则过多会限制表达文案生成、故事续写知识问答类视知识库完整性而定知识库完备时可做一致性校验否则容易误判政策问答、产品问答闲聊陪伴类否不建议加用户意图开放规则层会破坏顺畅感情感陪伴、开放式聊天这个表的核心原则是规则层的价值不在于“显得聪明”而在于减少不可控的失败。凡是失败代价高或逻辑强约束的都应该加凡是自由度高或失败代价低的可以减少干预。注意不要一上来就把批量数、并发数和规则数拉满。先用一条样例确认输入、输出和日志都正常再逐步扩大覆盖面。4.5 一个最简单的排查链路如果你已经加了一层常识判断规则但效果不理想我建议按下面的顺序排查先看现象是误拦截太多还是该拦截的没拦住。再看输入检查请求字段是否完整上下文是否传到了判断层。再看规则顺序有些规则应该优先判断比如“空字符串”应该最先拦截而不是放在最后。再看兜底文案兜底文本是否让用户清楚下一步该做什么而不是简单粗暴地拒绝。最后看日志统计命中的规则分布排查是否出现因正则表达式或关键词覆盖不全导致的漏判。5. 真正的边界形式化常识为什么不会取代统计学习也不会全盘退场5.1 知识获取成本太高开放世界依然难写我必须诚实地说McCarthy 的路线有一个天然短板知识获取太贵了。你把一百条常识规则写进去可能要花好几天但大模型通过预训练可能已经隐式地掌握了几万种常见表述方式。要维护一个覆盖真实世界全部常识的知识库成本几乎无限高。这也是为什么纯符号主义在二十世纪八十年代后期进入低谷统计学习方法开始占领主流。这个教训今天依然有效。如果你在做一个小型业务想用一个完备的本体系统给用户提供全量常识那大概率会失败。因为维护不完、更新跟不上、覆盖不到长尾场景。这不是逻辑的问题而是工程成本的问题。5.2 统计模型擅长模式匹配逻辑模型擅长一致性保证所以我的实际立场是形式化常识和统计学习不是替代关系而是互补关系。统计模型的长处在于从海量数据里捕捉模糊的模式。它能写文案、会翻译、能识别意图这非常强大。但它的短处也很明确它无法保证输出和已知事实一致无法在每一步都给你一个可验证的推理依据。逻辑模型的长处恰好相反。它擅长保证一致性、可解释性、可修正性。一旦把规则写对它就会稳定执行。但它的短处是覆盖范围有限维护成本高。聪明的工程就是各取所长。用统计模型做生成和内容理解用逻辑模型做约束和校验用人做最终决策。这个组合的稳定性远高于只用其中之一。5.3 一个务实的组合策略统计生成 逻辑校验 人工兜底如果你的团队今年要做 AI 应用我建议把它当成默认架构第一步用大模型完成自然语言理解和内容生成。第二步加一个规则层负责处理“非黑即白”的强约束问题比如时间、金额、权限、禁售词、敏感信息。第三步对规则层无法判断的模糊地带转人工或转上级模型处理。这个架构的好处是它不会把所有希望押在模型的知识边界上。模型可以犯错但规则层会兜底规则层会误伤但人工兜底会修正。这就形成了一条“从不可控到可控再到可解释”的链路。McCarthy 如果在现场看到这个架构我猜他不会觉得违和。他当年做 Advice Taker核心目标也不是让机器取代人类而是让机器拥有一个能不断接收新建议、新知识、新常识的框架。今天我们用规则层约束大模型输出本质上就是在给智能系统打补丁让它多懂一点“常识”。这个补丁并不会让系统变得万无一失。但它会带来一个非常实际的价值当系统犯错的时候至少你有一个地方可以查看逻辑链路知道是规则覆盖不足还是模型生成跑偏还是用户输入不完整。这种“可定位、可修正、可复盘”的能力是纯黑盒的大模型系统给不了的。[\lim_{x\to\infty} \frac{\text{常识的形式化}}{\text{模型的统计生成}} \text{一个可解释的角落}]