ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

AI编程幻觉防不胜防?用本地账本式审计让AI代码可追溯

AI编程幻觉防不胜防?用本地账本式审计让AI代码可追溯 看到 “Ledgerful – I built a local tool to catch when AI invents stuff in my code” 这个项目标题时我的第一反应不是好奇它的技术栈而是想起自己真实踩过的坑。AI 编程工具确实比两年前强太多但它有一种很典型的失败方式生成的代码语法完整、命名合理、注释也写得有模有样真正跑起来才发现它引用的方法根本不存在文件路径是编出来的甚至某个依赖的版本号也纯属想象。它不是故意骗你它只是在用语言模型惯常的方式圆一个最像样的回答。这个项目标题点中了一个更真实的问题AI 生成代码最大的风险不是“报错”而是它“自信地编造”。绝大多数时候你不会一眼看出来因为你默认一个能写出完整函数的人不会凭空捏造一个 API 名称。Ledgerful 想做的就是把这个风险从隐性变成显性。这篇文章我会从这个问题本身聊起再拆开“本地账本”这种设计思路为什么有效最后给一套不需要依赖任何特定工具就能用起来的防幻觉流程。重点不是推荐某个项目而是帮你建立对 AI 编程输出的“审计意识”。1. 先搞清楚 AI 为什么会“一本正经地编造代码”1.1 语言模型的“答案”不是查出来的是猜出来的要理解为什么 AI 会编造代码先要放下一个直觉模型不是一个数据库它不会在生成前先查一遍“项目里到底有没有这个方法”。从底层机制看大语言模型的生成过程是逐 token 预测。它每写一个单词都会根据前面的上下文计算所有可能 token 的概率分布然后选一个概率较高的输出。这意味着当它遇到某个不太确定的 API 时它做的并不是去代码库索引里验证“这个函数是否存在”而是根据训练数据里见过的相似代码补一个“最像样的名字”出来。这个区别非常关键。如果 AI 是查表式工作那么不存在的函数就不会被“想出来”。但因为是概率生成它完全可能编出一个风格很接近真实库的假方法名。比如你让它调用某个向量数据库的客户端它可能会写出client.query_embedding_similar()。这个名字看起来非常专业但实际 SDK 里根本没有这个方法。所以AI 编造代码不是“坏了”而是它的工作方式天然带有这种倾向。我们嘲笑 AI 一本正经胡说八道其实是因为它被训练成要输出“流畅且确信”的文本而不是输出“经过验证的事实”。在写代码这个场景里流畅且确信恰好就是最容易骗过代码审核的风格。1.2 比错误更麻烦的是错误看起来太正常AI 编程工具真正让人头疼的地方是它的错误不容易被当场识破。一个人类新手如果不知道某个 API通常会写一个占位符或者留下TODO。但 AI 不会它会非常自然地写出from langchain_experimental.retrievers import VectorStoreRetriever retriever VectorStoreRetriever.from_vectorstore(store, search_typesimilarity)这段代码的问题可能不在缩进不在变量命名而在from_vectorstore这个参数应该怎么写、是否真的存在、依赖版本是否兼容。每一样看着都正常组合起来却跑不通。更糟的是你在 Code Review 时很难找到一条明确的证据说它错了因为它错在“对某个外部世界的知识盲区”。这也是我后来对 AI 生成代码格外警惕的原因如果它写得很烂你会立刻发现问题如果它写得过于丝滑反而要多留一个心眼。因为越流畅的代码越容易让你跳过验证步骤。AI 幻觉的危险不在于“看起来是错的”而在于“看起来是答案”。1.3 我会重点盯住这几类“看起来合理”的幻觉根据实际使用 AI 编程工具的经验我一般会把幻觉分成几类来检查每一类检查方式都不一样。编造 API 和库方法方法名很合理但不存在于当前版本的库里。这类问题的特点是语法没问题IDE 也可能没爆红因为有些语言是动态导入。编造文件路径和项目结构AI 会假设项目里存在src/utils/helpers.py但实际你自己的代码根本没有这个文件。这类幻觉在改动多文件时会特别明显。编造依赖版本和安装命令AI 可能提示你安装某个包的latest版本或者写出一个并不存在的版本号。这类问题通常要等环境部署阶段才暴露。错误合并上下文AI 把上一次对话里的某个假 API 带进了下一个任务导致错误被“传染”。这种幻觉最难防因为它是跨任务累积的。所以我后来处理 AI 生成代码不再只问“功能对不对”而是先问“这些引用的来源是什么”。如果来源不可验证那它的置信度再高也只能当作待查项。2. Ledgerful 在做什么把 AI 的“创作痕迹”变成可审计的本地账本2.1 为什么叫“Ledgerful”而不是“CodeDiff”如果只是想检测 AI 生成的代码改动做一个普通的 diff 工具就够了。但 “Ledgerful” 这个名字很有意思它强调的是 “ledger”也就是账本。账本的核心不是记录结果而是记录每一笔操作的发生过程。在财务场景里哪怕最终金额没变如果中间某笔账记不清楚审计就会出问题。代码生成也是一样真正重要的不只是 AI 改了哪些文件还包括它为什么这么改、它引用了什么依据、它当时认为哪些 API 存在。所以我的理解是Ledgerful 不只是给你看一份差异报告而是给 AI 的每次生成都留下一条可追溯的流水记录模型输入是什么、模型输出了什么、它声称引用的文件或方法有哪些、这些引用是否有实际依据。这套流水一旦建起来AI 代码的第一个问题就不再是“写得对不对”而是“这个说法的证据链是否完整”。这种设计思路其实把代码审查从“看结果正确性”变成“看过程可验证性”。我觉得这是它和普通 AI 辅助工具最大的区别。2.2 本地化是取舍不是口号项目标题里专门强调 “local tool”。有人可能觉得本地化只是一个隐私口号但实际落地时这决定了工具能不能真正解决编造问题。AI 在生成代码时经常需要读取项目上下文。如果这些上下文被上传到第三方平台等于把项目的命名习惯、模块结构、业务代码目录全部暴露给外部服务。很多团队不会接受这一点尤其是在有内部规范或客户代码的情况下。本地工具至少可以让代码内容留在开发者自己的机器上只在本地完成记录、分析和比对。从工程角度看本地化也意味着更低的延迟和更自由的控制。你可以在 git hook 里直接调用也可以在 CI 里再加一道检查而不必等一个外部接口返回结果。更重要的是本地账本能和本地文件系统、本地依赖环境直接交叉验证这一步是纯云端工具很难做好的。因为 AI 是否编造很多时候只能通过比对当前仓库的真实状态来确定。当然本地化不等于万能。一个本地工具同样需要合理设计规则否则它也只是多了一个会“误报”的脚本。2.3 它能 catch 的是“没有依据的自信”从项目标题来看Ledgerful 想抓的核心不是“AI 写错了”而是“AI 发明了不存在的东西”。这两者并不完全一样。举个例子AI 写了一个函数调用但参数个数不对。这属于“写错了”静态类型检查或单元测试很快能发现。但如果 AI 写了一个不存在的方法名而且整个项目里也没有定义它那就属于“invented stuff”。前一种错误靠测试能兜住后一种错误如果没人验证引用很容易漏过去。所以 Ledgerful 真正要做的事是把每一行 AI 生成代码里涉及的引用函数、类、模块、文件路径、依赖包拿出来和本地仓库的真实信息做比对。如果某个引用找不到对应实体它就标记出来告诉你这里有一个很可能是编造出来的断言。这个思路让我很认同。它没有试图去读懂所有代码逻辑而是先做一个最诚实的事确认 AI 说的每一个“外部事实”是否真实存在。只要做到这一步就已经能挡住大量低级但隐蔽的幻觉。注意我能从项目标题中读出这个设计方向但 Ledgerful 内部的实现细节公开信息有限你如果要用它还是先以官方文档和本地源码为准。这里更重要的是理解它解决问题的思路而不是把它当成一个默认配置就能生效的万能插件。3. 没有 Ledgerful 也能用一套账本式防幻觉的最小落地流程3.1 先建立一个“从提示词到引用”的审计闭环在很多人的工作流里AI 编程工具的输出是最难审计的。原因很简单你给了一段提示词它直接返回一大段代码中间的过程你几乎没有记录。等出现问题时你根本不知道它当初是怎么想的。解决这个问题的第一步不是找一个高级检测工具而是先建立一条可追溯的链路任务开始时记录当前代码库的状态。AI 生成过程中保存提示词和输出。生成之后提取输出里的所有引用点。把引用点与本地代码库、依赖环境、文件结构做比对。最后把低可信引用单独列出来交给人工复核。这套流程不需要依赖某一家厂商也不绑定某个特殊工具。只要你在本地养成了记录和验证的习惯哪怕用手动脚本也能完成。3.2 一个可以照做的“账本式防幻觉五步法”我建议把它命名为“账本式防幻觉五步法”因为它的核心不是提高生成质量而是给生成结果建立证据链。第一步记录基线。 在让 AI 开始改代码之前先记录当前分支的提交哈希、文件树、依赖锁定文件以及一个干净的 git status。这样后面无论 AI 怎么改你都能知道哪些变化是它带来的。第二步生成摘要。 让 AI 完成改动后不要急着合并。先让它输出它认为自己修改了哪些文件、依赖了哪些模块、调用了哪些方法。这个输出一定要单独保存下来因为它就是后续验证的依据。第三步提取引用清单。 从 AI 生成的代码里把 import、from、文件路径、类名、函数名、变量名全部提取出来。不需要自己一行行看可以用 grep 或简单的 AST 脚本辅助。第四步交叉验证。 对每一个引用逐项检查如果是本地模块看文件是否存在如果是第三方库看是否安装在当前虚拟环境里如果是项目内部方法看定义点是否存在。这一步能抓到大量编造内容。第五步人工复核和测试。 把没有通过交叉验证的条目单独汇总人工判断到底是真实存在但漏检了还是 AI 编造出来的。确认过的部分再跑测试。测试通过的代码才能进入提交。这个五步法不一定需要每次都严格走完。但对于关键改动、跨模块改动、或者是 AI 前期自动生成的基础代码它非常值得执行。3.3 简化版检查脚本怎么写如果你不想一开始就上复杂工具可以先写一个非常简陋的检查脚本。重点是理解思路不要把它当成生产级方案。下面这个 Python 脚本只是示意结构作用是提取新增代码里的 import 和 from 引用方便后续逐一验证# check_imports.py — 示意结构不要直接当生产脚本使用 import ast import sys from pathlib import Path def walk_imports(code: str): tree ast.parse(code) for node in ast.walk(tree): if isinstance(node, ast.Import): for alias in node.names: print(待验证 import:, alias.name.split(.)[0]) elif isinstance(node, ast.ImportFrom): module node.module or print(待验证 from:, module.split(.)[0]) if __name__ __main__: code Path(sys.argv[1]).read_text(encodingutf-8) walk_imports(code)用这个脚本提取出模块名之后再放到项目对应的虚拟环境里验证python -c import requests; print(requests.__version__)如果这个模块根本没安装或者版本不满足项目要求那它就是一个需要核实的信号。不要直接相信 AI 在注释里写的安装命令。这里有一个很容易踩的坑验证 import 时一定要用项目的虚拟环境而不是全局 Python 环境。不同环境下装没装某个包结果是完全不一样的用错环境会导致误判或者漏判。3.4 什么时候算“通过”按“账本式防幻觉五步法”走完代码可以进入正常的 CI 流程。但当检查结果出现以下情况时先别急着合并AI 引用了不存在的文件路径。AI 引用的方法在当前依赖版本里找不到。AI 给出了一个模糊的第三方库名称但没有版本。AI 在说明里声称某个功能存在但代码里并没有对应的实现。这些信号不一定都是幻觉但它们都有同一个共性证据链不完整。按我的经验不完整证据链比明显错误更值得警惕。因为明显错误会被测试发现而证据链缺失的代码往往会长期躺在仓库里等到某一天环境变化才突然爆雷。4. 哪些“编造”能逮住哪些根本逮不住4.1 能逮住引用、路径、依赖、版本这类“事实性幻觉”账本式验证最适合处理的场景是一类可以被静态事实检验的幻觉。这类幻觉的核心特征是你只要拿它和真实世界做一次比对就能确定真假。比如 AI 写了一个导入语句from internal_client import query_recommendations如果项目里根本没有internal_client模块这就是一个非常干净利落的“无依据引用”。本地账本工具完全可以自动发现它。它不需要理解业务逻辑不需要跑测试只需要做一次文件系统检查就可以给出“未找到”结论。类似地文件路径、依赖包名、SDK 方法名只要依赖关系清晰都很适合用本地工具检查。在我的经验里这类事实性幻觉大概占了 AI 编造问题的七成以上。所以先把这一层防住收益非常明显。4.2 逮不住函数存在但用法错、业务逻辑错、隐蔽副作用账本式验证的局限性也很明显它无法判断“函数存在但用法不对”的情况。比如一个库里确实定义了create_client(timeout5)但 AI 却写成create_client(retry_count3)。从引用验证的角度看create_client是存在的所以它不会被标记为“编造”。但参数名完全错误。这类问题必须靠类型检查器、IDE 提示或者运行时报错才能发现。更难的还有业务逻辑错误。AI 可能使用完全真实存在的 API写了一个语义上错误的条件判断比如把0写成了0。它不会触发引用检查问题也不会让你的测试立刻失败但如果边界情况没覆盖到就会产生隐蔽的 bug。这类问题的难点在于它们缺少一个“明确的验证原点”。账本只能告诉你“这个说法有没有依据”但如果“依据本身用错了”账本就帮不上忙了。4.3 工具与人的边界我整理了一个简单的判断表方便你决定哪些事该交给工具哪些事还该靠人检查层次典型问题本地账本工具能否覆盖建议补充手段引用层模块不存在、文件路径虚构能覆盖文件系统比对、依赖检查类型层参数个数不对、返回类型不匹配部分覆盖pyright / mypy / IDE 静态检查语义层算法逻辑错误、业务规则理解错误不能覆盖代码评审、单元测试、行为测试环境层版本不兼容、依赖冲突部分覆盖锁定依赖、CI 环境验证长期维护层上下文污染、幻觉跨任务传播需要账本记录才能发现沉淀审计记录、定期回顾这个表也解释了为什么我不建议把 Ledgerful 这类工具当成“AI 代码万能质检器”。它的真正位置是在“引用层”补上传统测试工具的盲区。再往上的检查需要的是测试、类型系统和人的判断。如果 AI 生成的代码能通过类型检查、单元测试和语义 review那它大概率不是凭空编造只是在已知规则里做出了一个选择。此时你应该关注的是这个选择是不是当前场景最合理的那个。5. 从“抓幻觉”到“可信 AI 生成代码”还差哪几块拼图5.1 账本记录的是“发生了什么”不是“什么是正确的”Ledgerful 这类工具让我看到的价值不只是“抓幻觉”而是它改变了一种工作习惯以前我们看 AI 生成代码时习惯直接跳到“代码能不能跑”现在开始有人关注“这段代码的依据是什么”。但也要清醒账本记录永远只是“事实层”它不负责判断“正确性”。哪怕 AI 引用的每个 API 都存在业务逻辑仍可能完全错误。所以本地账本更大的意义是让整个过程变得可追溯而不是让整个过程变得正确。正确性问题需要另一套机制来兜底。比如测试覆盖、类型检查、代码评审、灰度发布。如果团队还没有这些基础设施那么即使有了账本也只能做到“不会因为幻觉而跑崩”而不能保证“生成的代码一定符合业务预期”。5.2 长期要补的是语义验证、测试生成、团队纪律从我个人经验看如果要让 AI 生成代码真正进入生产环境除了账本意识还需要补上三块拼图。第一是语义验证能力。它需要把“函数存在”升级为“函数行为符合预期”。静态类型检查只是起点更完整的方案是让模型生成对应的单元测试用测试结果来约束代码语义。第二是变更影响分析。AI 改一个函数时可能影响所有调用方。如果工具能自动列出“这次改动会影响哪些模块”再让开发者在这些模块上跑测试就能把大量潜在问题挡在合并之前。第三是团队纪律。一个开发者愿意用账本流程并不难难的是整个团队在面对 AI 生成代码时都坚持同样的标准。如果只有一个人做审计其他人把 AI 输出直接合入主分支那么账本的价值会被拉低。可追溯这件事必须变成团队习惯才有长期效果。5.3 我的建议路径先跑通、再比较、再固化如果你看了这篇文章想在自己的项目里试试“账本式防幻觉”我建议按这个路径推进先记录不急着判定。挑两个 AI 参与生成的任务手动记录提示词、生成代码和参考引用。加比对找痛点。用简单脚本或 Ledgerful 这类工具把引用清单和本地仓库做交叉验证看能找出多少个可疑项。工程化定规则。把经过验证的检查流程放进 pre-commit hook、CI 或团队模板里变成默认动作。这个路径的核心是“先跑通小样本”。不要一上来就设计一个非常复杂的规则系统也不要期望第一次就能把所有幻觉都拦下来。先用少量任务验证你的检查逻辑是否有效再逐步扩大范围。这样不仅能降低误报率也能帮你更清楚看见 AI 工具在你们项目里的薄弱环节。实践上我通常会在每天任务开始时先建立一次代码基线。这个过程只花几十秒但后面所有 AI 改动都有了对照点。没有对照点AI 的“自信”就永远无法被证明是“可靠的”。结尾我现在再看 AI 编程工具已经不再追求它“一次写正确”。我更关心的是如果它写错了我能不能尽快、准确地发现。Ledgerful 让我欣赏的地方是把 “AI 可能编造内容”当成一个默认假设来处理而不是等到出了问题再去逐行猜。代码审计的从来不只是结果更是依据。这一课同样适用在每天和 AI 联手的开发者身上。
返回列表