十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Ledgerful:用账本式核对检测 AI 编程代码幻觉的本地工具

Ledgerful:用账本式核对检测 AI 编程代码幻觉的本地工具 AI 编程助手写出来的代码表面看起来非常完整但里面很可能藏着不存在的包、不存在的 API、不存在的环境变量。社区把这种问题叫做“AI 幻觉”落到代码上就是模型根据上下文补全了一个“听起来合理”的符号而这类符号在本地依赖、项目文件或任何真实文档里都找不到。Ledgerful 是一个为了解决这个问题而生的小型本地工具。它不做代码生成也不做自动修复只负责把代码里引用的每个外部符号当成一条账目记录下来然后和本地已知事实做核对最后把可疑条目列出来方便开发者逐条确认。这篇文章用一个可以本地运行的最小实现把 Ledgerful 的检测机制讲清楚。1. 先理解 AI 代码幻觉为什么需要“账本式核对”1.1 幻觉在代码里的几种典型表现AI 编程模型生成代码时会基于训练数据中的统计模式补全内容。训练数据里经常出现requests、os、json这类常见模块模型会倾向于把它们放在代码里这部分通常没有问题。真正危险的是模型创建了一个“在数据分布上很像真实 API”的符号而这个符号在任何真实环境中都不存在。实际项目里代码幻觉可以归成几类。幻觉类型典型写法为什么难以发现不存在的第三方包import fake_magic_lib本地没有这个包但代码逻辑看起来完整不存在的函数或类train_model_from_scratch(...)工具类、模型类数量多人眼不会逐行核对不存在的命令行参数subprocess.run([deploy, --force-all])命令行参数没有统一注册表不存在的路径或配置项Path(/etc/myapp/secret.conf)路径和配置项通常只在运行时报错不存在的环境变量os.getenv(CLOUD_REGION_ID)环境变量来源隐蔽缺少声明文件这些问题的共同点是代码仍然能通过语法检查甚至能通过静态阅读只有在真正运行到对应分支时才会暴露。更麻烦的是AI 生成的代码往往一次涉及很多文件人工逐行 review 的成本很高而且模型生成的内容在语言层面非常流畅容易让人失去警惕。1.2 人工 review 为什么不够如果把“找幻觉”完全交给人工就相当于让开发者对每一个 import、每一个函数调用、每一条命令都做一遍本地核实。对于小型 demo 还能接受一旦进入真实业务仓库代码量变大上下文变长人工核对很容易漏掉隐藏在某个分支里的错误符号。另一个问题是 review 的不可重复性。上次碰巧查出了问题不代表下次同样能发现。代码审查依赖人的经验、注意力甚至当天的状态。本地工具则可以把核对过程固化成规则先提取引用再和事实源比较最后输出报告。同样的输入一定会得到同样的结果规则更新后可以重新扫描历史代码这种可回放能力是人工 review 不具备的。还有一层原因AI 编程助手往往在生成代码时给出了“看起来合理”的解释开发者容易把这种解释当作证据。比如模型可能写一段注释“这里调用内部工具库的初始化函数”实际上这个内部工具库根本不存在。只有把代码里的符号列出来逐个和本地事实核对才能发现模型描述和真实环境之间的差距。1.3 账本视角每条引用都是一笔待审计的条目Ledgerful 这个名字来自 ledger意思是账本。账本式的检查逻辑很简单代码里出现的每一个外部引用都是一笔需要确认的记录。导入了一个模块就要确认这个模块能在当前环境里被找到。调用了一个函数就要确认这个函数定义在当前文件里或者从某个真实导入的模块里来。写了一条命令就要确认这个命令在 PATH 中或项目脚本里真实存在。访问了一个路径或环境变量就要确认它在项目配置、部署脚本或系统环境中被定义过。这个思路和财务审计很像。审计不是去判断每一笔交易道德上“对不对”而是核对凭证、流水和库存是否对得上。Ledgerful 把代码当作流水把本地文件系统、已安装依赖、项目配置当作凭证逐条核对最后把对不上的记录标记出来。注意这里的核心目标不是证明代码一定正确而是把“没有被证明正确”的部分暴露出来让开发者做最后判断。2. 设计检测模型代码里的引用如何变成一条可验证记录2.1 事实来源要分层级一个本地工具能核对的“事实”来自哪里决定了它的能力边界。Ledgerful 会优先使用成本最低、最可靠的事实源也就是本地环境本身。可以把事实来源分成四层层级事实来源例子可靠性获取成本1Python 标准库模块清单sys.stdlib_module_names高极低2当前虚拟环境已安装的包importlib.metadata高低3项目内部文件和符号目录结构、本文件内定义函数、内部模块中高低4外部文档或团队知识库内部域名、内部配置项、业务白名单中高设计原则是本地事实优先外部知识库可选。Level 1 和 Level 2 是绝对事实Level 3 要根据项目根目录做路径判断Level 4 则需要人工维护规则。这在工程上的意义是默认情况下工具不需要访问网络也不需要登录远程服务只靠本地环境就能完成大部分幻觉检测。对于包含敏感代码的仓库这同时降低了数据和隐私风险。2.2 Ledger 记录结构每个被扫描出来的引用最终都会变成一条 JSON 记录。记录里除了“符号叫什么”还要包含“在哪一行”“为什么被提取出来”“当前状态是什么”“依据是什么”。一个最小记录结构如下{ id: 4f3a1c2b-8d2e-4f41-9c1b-123456789abc, file: examples/bad_entry.py, line: 3, column: 7, kind: module, name: fake_magic_lib, evidence: import fake_magic_lib, status: suspicious, reason: not_found_in_local_index, checked_at: 2025-01-01T12:00:0008:00 }字段含义kind引用类型比如module、function、command、path、env_var。name被引用的具体名称。evidence从源代码里截取的证据片段方便后续人工复核。status验证结果状态机见下一节。reason标记为什么被判定为当前状态方便排错。把记录写成 JSON 而不是直接打印文本是为了后续能接入自动化流程。IDE 插件可以读取 JSON 在编辑器里标红CI 可以解析 JSON 决定是否阻断合并报告生成器可以把 JSON 转成 Markdown 或 HTML。2.3 状态机verified / suspicious / unknownLedgerful 的基本状态不需要设计得太复杂三个状态就足够用。verified已经找到可靠的事实来源。比如模块名存在于标准库清单中或者函数定义存在于当前文件的 AST 中。suspicious没有找到事实来源而且类型比较敏感。比如某个 import 找不到对应包某个命令不在白名单里这种状态值得人工确认。unknown无法判断。比如字符串里的命令是动态拼接的或者调用了getattr(obj, name)这类引用无法靠静态扫描确定工具选择“不做结论”更稳妥。状态转移规则很简单先尝试从低层事实源查证查到就标记为verified查不到并且不能确定动态性标记为suspicious明显动态或带变量拼接标记为unknown。这个状态设计能防止扫描器因为信息不足而乱报。2.4 一个最小规则配置为了让使用者能控制哪些内容被忽略、哪些命令被信任需要一个规则文件。这里使用 JSON 而不是 YAML主要是不引入第三方库{ ignore_patterns: [ __init__, test_, conftest ], known_commands: [ pip, python, ls, cat, grep, git ], known_paths: [ /tmp, /usr/local/etc ], project_roots: [ src, app ] }这个文件后续会作为扫描器的输入参数用来控制行为。ignore_patterns跳过指定文件或目录known_commands直接信任常用命令known_paths避免常见路径被误报project_roots告诉工具哪里算项目内部代码。3. 环境准备与最小项目结构3.1 运行环境与依赖Ledgerful 的最小实现只依赖 Python 3.10 及以上版本的标准库。选择 3.10 是因为sys.stdlib_module_names在这个版本中稳定可用如果没有这个属性就需要额外维护一份标准库清单这会让工具退化。推荐环境环境项推荐值说明Python3.10使用sys.stdlib_module_names和ast第三方依赖无核心逻辑全部使用标准库操作系统Linux / macOS / Windows路径处理使用pathlib目标项目以 Python 为主的仓库当前实现只扫描.py文件不需要安装任何包下载项目结构后直接用命令行运行这是“本地工具”最重要的一点拿到仓库就能扫不污染目标项目的依赖环境。3.2 初始化项目结构和文件下面是一个最小的项目布局ledgerful/ ├── ledgerful/ │ ├── __init__.py │ ├── cli.py │ ├── scanner.py │ ├── models.py │ └── rules.json ├── examples/ │ └── bad_entry.py └── README.md每个文件的职责文件作用ledgerful/__init__.py空文件标识包ledgerful/rules.json忽略规则、信任命令、项目根目录配置ledgerful/models.py定义 Ledger 记录结构ledgerful/scanner.py核心扫描器负责解析 AST、提取引用、核验状态ledgerful/cli.py命令行入口处理参数和输出examples/bad_entry.py示例文件故意包含幻觉代码3.3 配置加载逻辑rules.json放在包目录里意味着使用者可以复制一份到项目根目录再通过命令行参数指定。这样既保留了默认行为又允许不同项目有不同的白名单。加载配置的代码可以很简单import json from pathlib import Path DEFAULT_RULES Path(__file__).parent / rules.json def load_rules(path: Path DEFAULT_RULES) - dict: if not Path(path).exists(): return {} with open(path, r, encodingutf-8) as f: return json.load(f)这里不需要复杂配置框架。对本地扫描工具来说配置要解决的问题只有两件事哪些内容不扫哪些内容直接信任。规则太多反而会让工具难以维护和解释。4. 用 Python AST 实现引用提取模块、调用、命令和路径4.1 从 import 和 from import 提取模块名Python 的ast标准库可以把源码解析成语法树Ledgerful 需要继承ast.NodeVisitor在访问不同节点时提取引用。第一步是处理import和from ... import ...import ast class ReferenceExtractor(ast.NodeVisitor): def __init__(self): self.references [] def visit_Import(self, node): for alias in node.names: self.references.append({ line: node.lineno, column: node.col_offset, kind: module, name: alias.name, evidence: fimport {alias.name} }) self.generic_visit(node) def visit_ImportFrom(self, node): module node.module or for alias in node.names: full_name f{module}.{alias.name} if module else alias.name self.references.append({ line: node.lineno, column: node.col_offset, kind: module, name: module, evidence: ffrom {module} import {alias.name} }) self.references.append({ line: node.lineno, column: node.col_offset, kind: function, name: alias.name, evidence: ffrom {module} import {alias.name} }) self.generic_visit(node)这里把from xx import yy同时拆成模块引用和函数引用xx需要被验证为真实模块yy需要被验证为真实导出符号。后者比前者难因为需要解析xx模块的内部结构初级版本可以先只验证模块名。4.2 从函数调用和属性链提取名称函数调用比 import 复杂。subprocess.run(...)、os.path.join(...)、torch.nn.Linear(...)都是调用但它们指向的符号层级不同。一个比较实用的策略是提取属性链的根模块和最终函数名def get_call_chain(node): parts [] while isinstance(node, ast.Attribute): parts.append(node.attr) node node.value if isinstance(node, ast.Name): parts.append(node.id) return ..join(reversed(parts)) class ReferenceExtractor(ast.NodeVisitor): def visit_Call(self, node): chain get_call_chain(node.func) if chain: self.references.append({ line: node.lineno, column: node.col_offset, kind: function, name: chain, evidence: fcall {chain} }) self.generic_visit(node)比如subprocess.run([ls])会提取出subprocess.run然后 Ledgerful 会先验证subprocess是标准库模块再进一步检查run是否存在于subprocess的属性中。后一步在生产环境里需要结合真实模块检查单独靠 AST 无法知道run是否存在所以初始版本对属性链只验证根模块。4.3 从字符串常量提取命令行和路径AI 幻觉不一定只发生在代码符号上也可能出现在字符串里。最常见的是subprocess.run(make deploy --prod)这类命令以及Path(/opt/myapp/config.yml)这类路径。对于字符串常量可以用简单规则提取import re import ast COMMAND_PATTERN re.compile(r^[a-zA-Z0-9_-](?: [a-zA-Z0-9_./:-])$) URL_PATTERN re.compile(r^https?://) class ReferenceExtractor(ast.NodeVisitor): def visit_Constant(self, node): if isinstance(node.value, str): text node.value.strip() if COMMAND_PATTERN.match(text) and not text.startswith(http): command_name text.split()[0] self.references.append({ line: node.lineno, column: node.col_offset, kind: command, name: command_name, evidence: text }) elif URL_PATTERN.match(text): self.references.append({ line: node.lineno, column: node.col_offset, kind: url, name: text, evidence: text }) self.generic_visit(node)这只是一个启发式实现不可能覆盖所有字符串但已经能抓住相当一部分由 AI 编造的命令和 URL。这些引用会被标记为suspicious或直接进入规则白名单校验。4.4 和本地事实源交叉验证提取到引用后需要对每个引用做验证。验证的核心是三个本地事实源标准库模块列表、已安装包列表、项目内部文件。import sys import importlib.metadata from pathlib import Path class LedgerValidator: def __init__(self, project_root: Path, rules: dict): self.project_root Path(project_root) self.rules rules try: self.stdlib_modules set(sys.stdlib_module_names) except AttributeError: self.stdlib_modules set() self.installed_packages self._load_installed_packages() def _load_installed_packages(self): result set() for dist in importlib.metadata.distributions(): name dist.metadata.get(Name) if name: result.add(name.lower()) return result def verify(self, ref: dict) - dict: name ref[name].lower() kind ref[kind] if kind module: if name in self.stdlib_modules or name.split(.)[0] in self.stdlib_modules: ref[status] verified ref[reason] found_in_stdlib elif name.split(.)[0] in self.installed_packages: ref[status] verified ref[reason] found_in_installed_packages else: ref[status] suspicious ref[reason] not_found_in_local_index elif kind function: # 简单策略根模块能验证就视为 verified否则 suspicious root name.split(.)[0] if root in self.stdlib_modules or root in self.installed_packages: ref[status] verified ref[reason] root_module_verified else: ref[status] suspicious ref[reason] root_module_not_found elif kind command: known_commands set(self.rules.get(known_commands, [])) if name in known_commands: ref[status] verified ref[reason] in_known_commands else: ref[status] suspicious ref[reason] unknown_command else: ref[status] unknown ref[reason] not_implemented return ref需要注意importlib.metadata.distributions()列出的是当前 Python 环境能看到的包也就是说工具必须在目标项目的虚拟环境里运行。这一点会在后面排错部分继续展开。5. 运行 Ledgerful 分析一个刻意写错的样例5.1 准备样例文件为了演示检测效果创建一个examples/bad_entry.py里面故意包含不存在的包和未定义函数import fake_magic_lib from deep_ai_toolkit import AutoDataset def run_pipeline(data_path: str) - None: ds AutoDataset.load(data_path) model fake_magic_lib.QuantumModel(10) result train_model_from_scratch(model, ds) print(result) if __name__ __main__: run_pipeline(./data/train.csv)这个例子里的fake_magic_lib通常不存在deep_ai_toolkit也可能不存在train_model_from_scratch在当前文件里没有定义。如果这段代码由 AI 编程助手生成开发者很容易被“看起来专业”的函数名迷惑。5.2 执行扫描命令假设项目根目录已经包含ledgerful包执行python -m ledgerful.cli scan examples/bad_entry.py --root .最终命令行入口需要简单实现传入扫描路径和项目根目录。下面是一个可用的cli.pyimport argparse from pathlib import Path from ledgerful.scanner import scan_file from ledgerful.models import format_report def main(): parser argparse.ArgumentParser(descriptionLedgerful local hallucination scanner) parser.add_argument(path, helpfile or directory to scan) parser.add_argument(--root, default., helpproject root for path resolution) args parser.parse_args() target Path(args.path) root Path(args.root) references scan_file(target, root) print(format_report(target, references)) if __name__ __main__: main()这里省略了models.py中format_report的具体实现它只需要把references列表变成下面这种可读文本。5.3 解读报告扫描后的输出大致如下FILE: examples/bad_entry.py L3 suspicious module fake_magic_lib L4 verified module deep_ai_toolkit L4 suspicious function AutoDataset L8 suspicious function train_model_from_scratch L11 unknown command/path ./data/train.csv结合程序逻辑解释输出fake_magic_lib不在标准库也不在当前环境的已安装包列表里所以是suspicious。deep_ai_toolkit如果安装过则模块名验证为verified但AutoDataset是否能被导出当前实现无法直接确认所以标记为suspicious。这是为了让开发者多留意一层。train_model_from_scratch在当前文件 AST 中没有定义出现又不在已导入模块的已知导出里所以是suspicious。./data/train.csv是一个相对路径且不是明显的命令当前规则没有命中所以标记为unknown代表需要结合项目文件判断。5.4 把这个工具放进 AI 编程助手的工作流Ledgerful 单独跑一次只能解决“事后检查”。更有效的方式是把扫描器接到 AI 编程助手的产出路径上生成代码后先扫一遍再提交。一个简单的做法是在 git 提交前针对改动文件执行扫描git diff --name-only --diff-filterACM | grep \.py$ | xargs python -m ledgerful.cli scan --root .如果希望把它作为 pre-commit 钩子可以写成#!/usr/bin/env bash set -e files$(git diff --cached --name-only --diff-filterACM | grep \.py$ || true) if [ -n $files ]; then python -m ledgerful.cli scan $files --root . fi注意在 pre-commit 钩子里阻断所有suspicious可能过于严格第一版建议只输出报告让开发者在提交前人工处理。6. 误报是本地工具最大的敌人现象、原因和排查路径6.1 常见误报表格本地扫描工具最怕的不是漏报而是误报太多。误报一多开发者就会放弃阅读输出。下面是几个常见误报场景。问题现象常见原因检查方式处理建议标准库模块被标记为 suspiciousPython 版本过低或sys.stdlib_module_names不可用运行python --version检查是否 3.10升级 Python或维护标准库清单 fallback已安装包被标记为 suspicious当前 shell 的 Python 和目标项目虚拟环境不一致运行which python检查虚拟环境是否激活确保在项目虚拟环境中执行扫描项目内相对导入被误报把src目录以外的项目内部模块当作第三方包查看project_roots配置在 rules.json 中增加项目根目录动态生成的命令名被误报命令包含变量拼接如f{tool} start查看evidence字段确认字符串含变量增加忽略规则或人工标记为 unknown常见路径被误报没有维护known_paths检查 rules.json 中的路径白名单把固定路径加入白名单AI 生成的 pyproject 依赖名各不相同同一包在不同环境 diff 后仍有差异查看是否存在importlib.metadata包名规范化问题对包名做 lower case 和去下划线处理6.2 动态导入和魔法字符串没法被 AST 捕获怎么办AST 只能捕获静态可读的代码结构。动态导入、eval、getattr和字符串拼接都会让名字丢失来源信息。例如module_name load_config(ai_tool) tool __import__(module_name) method_name config[entry] result getattr(tool, method_name)()这类代码里load_config、__import__、getattr都是合法的但是 Ledgerful 无法从 AST 知道实际的模块名和方法名。面对这种情况正确做法是把它们标记为unknown而不是直接当成suspicious。因为能力不足而产生的“无法确认”不应该被当作“存在幻觉”的证据。实际处理时可以增加启发式扫描配置文件和模块名之间的映射如果配置中存在某个模块名字符串就记录unknown。这一层并不复杂但能减少源码中的魔法字符串成为盲区。6.3 扫描器自己报错的排查链路Ledgerful 本身也会出错。常见的错误包括错误现象检查步骤解决方向SyntaxError在扫描某个文件时出现确认目标文件是否包含 Python 3.10 不支持的新语法升级 Python或跳过该文件ModuleNotFoundError: importlib.metadata检查 Python 版本Python 3.8 需要改用importlib_metadata兼容包AttributeError: module sys has no attribute stdlib_module_names检查 Python 版本Python 3.10 以下提供 fallback 列表扫描结果为空检查传入的路径是否为空目录是否用了.而不是.py文件确认路径是真实文件或包含 Python 文件的目录排查顺序建议先确认扫描路径是否存在。再确认 Python 版本和虚拟环境。然后确认规则文件是否被正确加载。最后查看单个文件的 AST 输出判断是提取逻辑问题还是目标代码问题。这样能把“用户代码有问题”和“扫描器自身有问题”区分开。7. 从个人脚本走向团队可用报告、增量扫描和接入检查清单7.1 输出可读报告和 JSON 报告个人使用可以只看文本输出团队协作则需要机器可读的格式。可以在cli.py中增加--output参数支持json和text两种模式。JSON 输出的例子{ summary: { total: 12, verified: 8, suspicious: 3, unknown: 1 }, references: [ { file: examples/bad_entry.py, line: 3, kind: module, name: fake_magic_lib, status: suspicious } ] }团队 CI 可以解析这个 JSON在评论机器人或代码托管平台上直接显示。需要谨慎的是CI 通常不知道目标项目的本地虚拟环境里有哪些包所以 CI 中的扫描应该使用项目锁文件或 CI 构建环境而不是随便一个预装环境。7.2 增量扫描只在 diff 行上跑全量扫描在早期可以用但项目变大后会产生大量噪音。更好的策略是只扫描当前分支变更的部分。一个可行方案def get_changed_python_files(basemain): import subprocess result subprocess.run( [git, diff, base, --name-only, --diff-filterACM], capture_outputTrue, textTrue, checkTrue ) return [line for line in result.stdout.splitlines() if line.endswith(.py)]拿到文件列表后还需要把行号过滤到 diff 涉及的行。这一步可以让扫描器接收一个“只报告这些行”的参数在scan_file中根据记录的行号做过滤。增量扫描的意义不是减少漏报而是降低误报对开发者的打扰让每次提交只关注本次改动引入的问题。7.3 接入 AI 助手后的检查清单把 Ledgerful 接入 AI 编程助手的日常使用流程后推荐维护下面这张检查清单检查对象检查内容处理方式依赖文件pip install命令是否来自 AI 生成的提示实际执行前先查看包名和版本import 语句新加入的模块是否在标准库或本地环境中如果未安装执行安装或检查拼写函数调用调用的函数是否在当前文件或导入模块中定义用 IDE 跳转到定义处确认命令行新出现的命令是否真实存在于 PATH在终端执行which或直接运行查看文件路径路径是否存在权限是否正确用ls或test -f验证环境变量变量名是否有拼写问题是否能从配置中找到定义搜索仓库中是否有相同的字符串配置项配置键是否和实际读取代码一致对比示例配置和读取逻辑回归测试AI 改动是否影响原有功能跑相关测试用例后再合并这张清单可以打印出来也可以沉淀为仓库里的REVIEW_CHECKLIST.md。它和扫描工具互补工具负责快速列出可疑点清单负责保证人工确认的完整性。7.4 学习环境与生产环境的差异学习环境和生产环境对工具的要求完全不同。对比项学习环境生产环境环境变量只需要一个 Python 3.10 环境虚拟环境必须和项目锁文件一致规则配置可以用默认 rules.json需要维护项目专属白名单扫描范围单文件或小目录建议增量扫描或改动文件扫描输出文本报告足够需要 JSON 输出、CI 集成和告警处理策略看到 suspicious 就人工看需要分级阻断合并 / 警告 / 记录数据安全本地扫描即可确保不把代码上传到任何远程服务生产环境里更推荐把 Ledgerful 作为“AI 代码变更检查”的一环而不是唯一的保障。8. 几个值得继续扩展的方向8.1 本地知识库把内部包、内部域名、内部配置项纳入事实源大型团队通常会有公司内部的 Python 包、内部域名和内部配置项。这些内容不在标准库也不在 PyPI 的公开信息里所以默认扫描器一定会误报。解决方案是维护一个团队级facts.json记录内部包、内部域名、内部路径和内部环境变量。扫描时优先加载这份文件再执行 level 1 到 level 4 的验证。这个扩展并不复杂但对降低误报率非常有效。8.2 从名称相似度到语义候选一个更高级的思路是当某个符号找不到事实来源时不要只报“不存在”而是给出相似候选。比如 AI 生成transormer时可以提示“本地存在transformers两者名字接近”。实现上可以用编辑距离、前缀匹配或基于 token 的相似度。需要控制候选数量否则输出又会变成另一种噪音。建议设计为可选插件默认关闭。8.3 依赖图谱和真实调用链目前的实现是逐文件独立扫描无法回答这类问题“这个函数确实存在但调用链上有没有其它虚假符号”更完善的工具应该能构建项目级依赖图自动追踪from A import B从 A 模块导出 B 的真实定义位置。依赖图还有助于识别循环导入和幽灵导出。所谓幽灵导出就是一个模块在__init__.py里导出了某个名字但这个名字在模块内部从来没有定义过。这类问题靠单文件扫描无法发现依赖图谱扫描则可以直接定位。8.4 最小落地建议如果你只打算在一周内把这样的工具用上建议按以下顺序推进先实现import和from ... import ...的扫描这部分最容易稳定。再实现函数调用链提取验证根模块是否可信。维护一份项目专属rules.json把常用命令和固定路径加进去。先以报告形式运行两周观察误报率再决定是否进入 CI。进入 CI 后先对suspicious警告不阻断积累一段数据后再按严重程度分级。Ledgerful 的核心价值不在于“证明代码没有幻觉”而在于把需要人工确认的范围缩小到可控大小。对 AI 编程时代来说能缩小确认范围就已经能省下大量 review 时间。先让工具在本地跑起来再根据项目实际情况调整规则比一开始就追求完美模型更实际。
返回列表