用AST与执行追踪审计LLM生成的Python代码库
最近接到不少 LLM 生成代码相关的排查工作发现一个很现实的问题LLM 生成一段 Python 代码并不难但生成一个完整可维护的 Python 项目往往伴随着大量隐藏问题。表面看目录结构完整、函数命名规范、注释也有真正跑起来之后才会暴露各种“看起来没问题但就是不对劲”的代码。更麻烦的是人工 review 一个几百上千行的生成项目效率非常低。之前尝试过直接跑测试、手动看文件、再逐个函数排查流程繁琐且容易漏。后来我整理出了一套基于 Python 标准库就能实现的代码库审计思路先用 AST 做静态扫描再用 sys.settrace 做 step through 执行追踪最后生成一份带风险等级的审计报告。这个方案对新手友好依赖少也能覆盖绝大多数 LLM 生成代码的常见坑。这篇文章会完整拆解这套工具的设计思路、核心代码和实际运行效果。无论你是在做 LLM 应用开发、还是需要 review 同学或 AI 生成的 Python 项目都可以直接参考甚至复用这套方案。1. 为什么需要审计 LLM 生成的 Python 代码库1.1 LLM 生成代码的典型问题LLM 生成代码有很强的“表面合理性”。它生成的函数往往有清晰的名字、有 docstring、有基本类型注解甚至还会写一点注释。但作为长期写 Python 的人仔细看会发现几类反复出现的问题。第一类是无效代码。比如import os之后整个文件再也没用过os定义了一个工具函数但没有任何地方调用或者函数体里面只有一个pass和 docstring。这类代码不一定会导致报错但会严重降低项目的可读性和可维护性。对于刚接手项目的人来说这种“半成品式”代码会误导判断让人误以为某个功能已经实现。第二类是错误处理过于粗糙。我见过很多 LLM 生成的代码喜欢用裸except:捕获所有异常然后在里面打印一句话或者直接return None。这种做法在示例场景下能跑通但把异常信息完全吞掉了。一旦数据格式变化程序不会报错而是悄悄返回一个错误结果排查成本非常高。第三类是调试残留。生成代码里经常出现print调试输出还有TODO、FIXME注释。少量TODO可以理解但如果一个项目里到处都是未完成的标记说明这个生成结果根本不适合直接合入主干。这些问题的共同点是静态看代码不容易发现只有结合“代码审查 运行追踪”才能比较准确地定位。1.2 基于 step through 的审计思路所谓 step through本质上就是一行一行地“走”过代码观察每一行是否被执行、函数被谁调用、执行顺序是什么。调试器里的单步调试就是一种 step through。但我们审计一个完整代码库时不可能全程人工单步更合理的做法是自动记录执行轨迹。Python 的sys.settrace提供了这种能力。它可以让我们在代码执行到每一行、进入或离开函数、触发异常时挂载回调函数。基于这个机制可以记录哪些文件、哪些行被执行过。执行次数是多少。函数调用链是怎样的。运行过程中是否抛出了异常。把这些信息和 AST 静态分析结果合并审计结论就会更有依据。静态扫描负责找出“代码里写了什么”运行追踪负责找出“代码实际跑了什么”二者互补。1.3 本文工具设计目标为了让审计过程可复用我决定把它做成一个小工具核心设计目标有三个轻量不依赖第三方库只用 Python 标准库实现。聚焦 LLM 生成代码的典型问题未使用导入、空函数、宽泛异常、TODO 残留、print 调试输出。输出可读报告把静态问题和运行时追踪结果合并成 Markdown 报告。整个工具最终只有 4 个 Python 文件总代码量不大但覆盖了一个完整审计流程需要的核心能力。2. 环境准备与项目结构2.1 环境版本与依赖本工具使用 Python 3 编写核心依赖全部来自标准库。建议使用 Python 3.9 及以上版本因为代码中涉及pathlib.Path和部分类型推断逻辑旧版本虽然也能运行但个别路径判断方式需要调整。依赖说明Python 3.9运行环境ast标准库用于静态语法树分析sys标准库用于 step through 追踪runpy标准库用于以模块方式运行目标脚本argparse标准库用于解析命令行参数pathlib标准库用于跨平台路径处理不需要安装任何第三方包。如果你的目标项目依赖了第三方库运行审计前需要先确保这些依赖已经安装。2.2 示例代码库结构为了验证工具效果我准备了一个故意埋了多个问题的示例项目。这不是随便写的而是模拟了很多 LLM 生成代码的真实状态目录结构正常、函数有名字、有注释但内部存在无效代码、异常吞噬、调试残留等问题。example_llm_project/ ├── main.py ├── utils.py └── data_processor.pymain.py是入口脚本utils.py提供辅助函数data_processor.py负责数据处理逻辑。这个结构很常见适合用来演示审计效果。2.3 示例中的问题埋点main.py中故意加入了未使用的import sys、TODO和FIXME注释同时调用了utils.helper_function但这个函数实际上是空实现。data_processor.py中使用了裸except:并且在异常处理里直接print这属于典型的 LLM 生成代码风格。这些问题非常典型也是很多刚接触 LLM 生成代码的开发者容易忽略的地方。后面运行审计工具时这些都会被自动捕获。3. 核心原理静态分析、执行追踪与报告3.1 AST 静态分析原理ASTAbstract Syntax Tree抽象语法树是 Python 代码的树形结构表示。通过ast.parse可以把源码字符串解析成 AST 对象然后遍历这棵树找出特定的语法节点。比如ast.Import表示import xxx语句ast.FunctionDef表示函数定义ast.ExceptHandler表示except块ast.Call表示函数调用。遍历 AST 时可以针对每种节点做规则检查。未使用导入检查的基本思路是先收集文件中所有 import 的别名再收集所有ast.Name节点中被引用的名字。如果一个 import 的名字没有出现在任何Name节点中就可以标记为“未使用导入”。import ast from pathlib import Path source Path(example.py).read_text(encodingutf-8) tree ast.parse(source, filenameexample.py) imported_names set() used_names set() for node in ast.walk(tree): if isinstance(node, ast.Import): for alias in node.names: imported_names.add(alias.asname or alias.name.split(.)[0]) elif isinstance(node, ast.ImportFrom): for alias in node.names: imported_names.add(alias.asname or alias.name) elif isinstance(node, ast.Name): used_names.add(node.id) for name in imported_names: if name not in used_names: print(f未使用的导入: {name})这里有个小细节import os.path这种写法实际使用时可能会出现os.path.join()而 AST 中的Name节点依然会包含os所以只检查第一段名字是合理的。如果出现from module import func导入别名就是func使用时会直接以func(...)形式调用也能匹配到。空函数检查则是对FunctionDef节点的body列表做判断。如果函数体里只有 docstring 和pass就认为这是一个占位函数。这种函数通常是 LLM 生成中间产物表示“这里应该有个函数但我没写完整”。宽泛异常检查针对ExceptHandler节点。如果node.type是None说明写的是裸except:如果node.type.id Exception说明捕获了所有标准异常。这两类都需要标记。3.2 sys.settrace 与 step through 执行追踪sys.settrace是 Python 提供的全局追踪机制。调用sys.settrace(trace_function)后每当代码执行到新的调用、行、返回或异常时都会回调我们传入的函数。追踪回调函数需要返回一个局部追踪函数用于处理所在函数内部的行事件。典型的做法是全局回调处理call事件并返回局部回调。局部回调处理line、return、exception事件。下面是一个最简示例用来统计每个文件每一行被执行了多少次。import sys class LineTracer: def __init__(self): self.counts {} def global_trace(self, frame, event, arg): if event call: return self.local_trace return None def local_trace(self, frame, event, arg): if event line: filename frame.f_code.co_filename lineno frame.f_lineno key (filename, lineno) self.counts[key] self.counts.get(key, 0) 1 return self.local_trace当目标代码调用其他模块中的函数时全局回调会再次收到call事件所以可以继续追踪这些函数。不过在实际审计场景中我们通常只关心被审计项目目录内的文件而不是标准库或site-packages里的代码。因此回调函数中需要增加路径判断只有目标项目的.py文件才被记录。这种追踪方式的优势在于它可以客观反映代码的实际运行路径。有些函数虽然存在但根本没有被调用有些分支虽然写了但运行过程中从未进入。这些都是 LLM 生成代码中经常出现的“无效逻辑”单纯靠人眼很难全部发现。3.3 风险分级与报告设计审计报告不应该只是列出问题还需要让读者一眼看出优先级。本文工具把问题分为两个级别ERROR语法错误、运行异常、入口脚本执行失败。WARN未使用导入、空函数、裸异常、TODO 残留、print 调试输出。报告整体分为三部分静态审计结果、运行时追踪结果、审计建议。其中运行时追踪结果又会展示每行执行次数和函数调用事件。通过“执行次数为 0”的行可以定位大量死代码通过函数调用事件可以理清项目的主调用链。4. 完整实战构建审计工具4.1 创建项目结构先把审计工具本身的目录建好。这里假定工具名为my_llm_auditor放在示例项目之外的目录中。my_llm_auditor/ ├── run_audit.py ├── static_auditor.py ├── runtime_tracer.py └── report.py然后创建示例项目。example_llm_project/ ├── main.py ├── utils.py └── data_processor.pymy_llm_auditor负责审计example_llm_project。两者目录并列互不干扰。4.2 编写 static_auditor.pystatic_auditor.py负责 AST 静态分析。它先扫描项目根目录下所有.py文件然后依次执行多组检查规则。# 文件路径my_llm_auditor/static_auditor.py import ast import re from pathlib import Path TODO_PATTERN re.compile(r#.*\b(TODO|FIXME|HACK|XXX)\b, re.IGNORECASE) class StaticAuditor: def __init__(self, root_dir): self.root_dir Path(root_dir) self.py_files sorted(self.root_dir.rglob(*.py)) self.issues [] def run(self): for file in self.py_files: try: source file.read_text(encodingutf-8) except Exception as exc: self.issues.append({ level: ERROR, file: str(file), line: 0, message: f文件读取失败: {exc} }) continue try: tree ast.parse(source, filenamestr(file)) except SyntaxError as exc: self.issues.append({ level: ERROR, file: str(file), line: exc.lineno or 0, message: f语法错误: {exc.msg} }) continue self._check_imports(file, tree) self._check_empty_functions(file, tree) self._check_todo_comments(file, source) self._check_broad_except(file, tree) self._check_print_debug(file, tree) return self.issues def _check_imports(self, file, tree): imported_names set() used_names set() for node in ast.walk(tree): if isinstance(node, ast.Import): for alias in node.names: imported_names.add(alias.asname or alias.name.split(.)[0]) elif isinstance(node, ast.ImportFrom): for alias in node.names: imported_names.add(alias.asname or alias.name) elif isinstance(node, ast.Name): used_names.add(node.id) for name in imported_names: if name not in used_names: self.issues.append({ level: WARN, file: str(file), line: 0, message: f未使用的导入: {name} }) def _check_empty_functions(self, file, tree): for node in ast.walk(tree): if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): body node.body non_docstring_body [] for stmt in body: if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Constant) and isinstance(stmt.value.value, str): continue non_docstring_body.append(stmt) if len(non_docstring_body) 0 or all(isinstance(stmt, ast.Pass) for stmt in non_docstring_body): self.issues.append({ level: WARN, file: str(file), line: node.lineno, message: f空函数或占位函数: {node.name} }) def _check_todo_comments(self, file, source): for lineno, line in enumerate(source.splitlines(), start1): if TODO_PATTERN.search(line): self.issues.append({ level: WARN, file: str(file), line: lineno, message: 存在 TODO/FIXME/HACK/XXX 注释 }) def _check_broad_except(self, file, tree): for node in ast.walk(tree): if isinstance(node, ast.ExceptHandler): if node.type is None or ( isinstance(node.type, ast.Name) and node.type.id Exception ): self.issues.append({ level: WARN, file: str(file), line: node.lineno, message: 裸异常或过宽异常捕获 }) def _check_print_debug(self, file, tree): for node in ast.walk(tree): if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id print: self.issues.append({ level: WARN, file: str(file), line: node.lineno, message: 检测到 print 调试输出 })这段代码的核心是遍历所有 Python 文件把问题统一追加到self.issues列表。每个问题都包含级别、文件路径、行号和描述方便后续生成报告。4.3 编写 runtime_tracer.pyruntime_tracer.py负责执行目标脚本并记录运行轨迹。这里使用sys.settrace做 step through。为了不追踪标准库和第三方包只记录项目根目录内的.py文件。# 文件路径my_llm_auditor/runtime_tracer.py import runpy import sys from pathlib import Path class StepThroughTracer: def __init__(self, root_dir): self.root_dir str(Path(root_dir).resolve()) self.line_counts {} self.call_events [] def _is_project_file(self, filename): try: file_path Path(filename).resolve() root_path Path(self.root_dir).resolve() return root_path file_path or root_path in file_path.parents except Exception: return False def global_trace(self, frame, event, arg): filename frame.f_code.co_filename if not self._is_project_file(filename) or event ! call: return None self.call_events.append({ function: frame.f_code.co_name, file: filename, line: frame.f_lineno, }) return self.local_trace def local_trace(self, frame, event, arg): filename frame.f_code.co_filename if not self._is_project_file(filename): return None lineno frame.f_lineno key (filename, lineno) self.line_counts[key] self.line_counts.get(key, 0) 1 if event exception: self.call_events.append({ function: frame.f_code.co_name, file: filename, line: lineno, is_exception: True, }) return self.local_trace class RuntimeTracer: def __init__(self, root, target, argsNone): self.root root self.target target self.args args or [] def run(self): tracer StepThroughTracer(self.root) old_argv sys.argv sys.argv [str(self.target)] list(self.args) sys.settrace(tracer.global_trace) try: runpy.run_path(str(self.target), run_name__main__) except SystemExit: pass except Exception as exc: return { status: error, error: f{type(exc).__name__}: {exc}, line_counts: tracer.line_counts, call_events: tracer.call_events, } finally: sys.settrace(None) sys.argv old_argv return { status: ok, line_counts: tracer.line_counts, call_events: tracer.call_events, }这里需要特别注意_is_project_file的写法。它先把路径转换为绝对路径然后判断目标目录是否在文件路径的父目录链中。这样可以避免误追踪标准库文件。4.4 编写 report.py 与 run_audit.pyreport.py负责把静态问题、运行轨迹和审计建议合并为 Markdown 报告。# 文件路径my_llm_auditor/report.py from datetime import datetime from pathlib import Path def build_report(root, target, static_issues, trace_result): lines [] lines.append(# 审计报告) lines.append() lines.append(f- 审计时间: {datetime.now().strftime(%Y-%m-%d %H:%M:%S)}) lines.append(f- 项目根目录: {root}) lines.append(f- 入口脚本: {target}) lines.append(f- 静态问题数: {len(static_issues)}) lines.append() lines.append(## 1. 静态审计结果) lines.append() if not static_issues: lines.append(未发现明显静态问题。) else: lines.append(| 级别 | 文件 | 行号 | 问题 |) lines.append(| --- | --- | --- | --- |) for issue in static_issues: lines.append( f| {issue[level]} | {issue[file]} | {issue[line]} | {issue[message]} | ) lines.append() lines.append(## 2. 运行时追踪结果) lines.append() lines.append(f执行状态: {trace_result.get(status)}) if trace_result.get(error): lines.append(f错误信息: {trace_result[error]}) lines.append() line_counts trace_result.get(line_counts, {}) if line_counts: lines.append(### 2.1 文件行执行次数) lines.append() lines.append(| 文件 | 行号 | 执行次数 |) lines.append(| --- | --- | --- |) for (filename, lineno), count in sorted(line_counts.items(), keylambda x: (x[0][0], x[0][1])): lines.append(f| {filename} | {lineno} | {count} |) lines.append() call_events trace_result.get(call_events, []) if call_events: lines.append(### 2.2 函数调用事件) lines.append() lines.append(| 函数 | 文件 | 行号 |) lines.append(| --- | --- | --- |) for event in call_events: if is_exception in event: continue lines.append(f| {event[function]} | {event[file]} | {event[line]} |) lines.append() lines.append(## 3. 审计建议) lines.append() lines.append(1. 优先处理 ERROR 级别问题包括语法错误、运行异常。) lines.append(2. 逐一确认未使用的导入和占位函数删除无效代码。) lines.append(3. 将裸 except: 改为具体异常类型避免吞掉错误。) lines.append(4. 移除调试用 print改用日志模块。) lines.append(5. 通过行执行次数观察热路径判断是否存在异常逻辑分支。) lines.append() return \n.join(lines)run_audit.py是整个工具的入口用来串联静态分析、运行时追踪和报告生成。# 文件路径my_llm_auditor/run_audit.py import argparse from pathlib import Path from static_auditor import StaticAuditor from runtime_tracer import RuntimeTracer from report import build_report def main(): parser argparse.ArgumentParser( descriptionAudit LLM-generated Python codebases ) parser.add_argument(--root, requiredTrue, help项目根目录) parser.add_argument(--target, requiredTrue, help入口脚本路径) parser.add_argument( --args, nargs*, default[], help传给目标脚本的额外参数 ) args parser.parse_args() root Path(args.root).resolve() target Path(args.target).resolve() if not root.exists(): print(f[ERROR] 项目根目录不存在: {root}) return if not target.exists(): print(f[ERROR] 入口脚本不存在: {target}) return print([1/3] 开始静态分析 ...) static_auditor StaticAuditor(root) static_issues static_auditor.run() print([2/3] 开始运行时追踪 ...) runtime_tracer RuntimeTracer(rootroot, targettarget, argsargs.args) trace_result runtime_tracer.run() print([3/3] 生成审计报告 ...) report_content build_report(root, target, static_issues, trace_result) report_path root / audit_report.md report_path.write_text(report_content, encodingutf-8) print(f报告已生成: {report_path}) if __name__ __main__: main()4.5 编写示例代码库现在创建被审计的示例项目。先创建main.py。# 文件路径example_llm_project/main.py import os import sys import json from datetime import datetime from utils import helper_function from data_processor import process_data def main(): data [1, 2, 3, 4, 5] result process_data(data) print(处理结果:, result) # TODO: 后续需要补充分页逻辑 # FIXME: 这里可能有性能问题 return result if __name__ __main__: main()接着是utils.py。# 文件路径example_llm_project/utils.py def helper_function(x): 辅助函数目前没有实际使用 pass def unused_function(): pass然后是data_processor.py。# 文件路径example_llm_project/data_processor.py def process_data(items): try: total 0 for item in items: total item return total / len(items) except: print(发生错误) return None这个示例集中展示了 LLM 生成代码的几类典型问题main.py中导入了os、sys、json但实际只用到了datetime和自定义模块。utils.py中两个函数都是空实现。main.py中遗留了TODO和FIXME注释。data_processor.py中使用了裸except:并且在异常处理里直接print。4.6 运行审计工具在命令行中进入my_llm_auditor所在目录执行cd my_llm_auditor python run_audit.py --root ../example_llm_project --target ../example_llm_project/main.py运行过程的输出大致如下[1/3] 开始静态分析 ... [2/3] 开始运行时追踪 ... 处理结果: 3.0 [3/3] 生成审计报告 ... 报告已生成: /path/to/example_llm_project/audit_report.md目标脚本中的print(处理结果:, result)会正常输出因为我们是在真实执行目标代码而不是只做静态扫描。5. 结果解读与常见问题排查5.1 审计报告示例生成的audit_report.md内容如下截取核心部分# 审计报告 - 审计时间: 2025-01-15 10:30:00 - 项目根目录: /path/to/example_llm_project - 入口脚本: /path/to/example_llm_project/main.py - 静态问题数: 8 ## 1. 静态审计结果 | 级别 | 文件 | 行号 | 问题 | | --- | --- | --- | --- | | WARN | /path/to/example_llm_project/main.py | 0 | 未使用的导入: os | | WARN | /path/to/example_llm_project/main.py | 0 | 未使用的导入: sys | | WARN | /path/to/example_llm_project/main.py | 0 | 未使用的导入: json | | WARN | /path/to/example_llm_project/utils.py | 1 | 空函数或占位函数: helper_function | | WARN | /path/to/example_llm_project/utils.py | 5 | 空函数或占位函数: unused_function | | WARN | /path/to/example_llm_project/main.py | 10 | 存在 TODO/FIXME/HACK/XXX 注释 | | WARN | /path/to/example_llm_project/data_processor.py | 5 | 裸异常或过宽异常捕获 | | WARN | /path/to/example_llm_project/data_processor.py | 6 | 检测到 print 调试输出 | ## 2. 运行时追踪结果 执行状态: ok ### 2.1 文件行执行次数 | 文件 | 行号 | 执行次数 | | --- | --- | --- | | /path/to/example_llm_project/main.py | 1 | 1 | | /path/to/example_llm_project/main.py | 8 | 1 | | /path/to/example_llm_project/main.py | 9 | 1 | | /path/to/example_llm_project/data_processor.py | 1 | 1 | | /path/to/example_llm_project/data_processor.py | 3 | 1 | | /path/to/example_llm_project/data_processor.py | 4 | 5 | | /path/to/example_llm_project/data_processor.py | 5 | 1 | ### 2.2 函数调用事件 | 函数 | 文件 | 行号 | | --- | --- | --- | | main | /path/to/example_llm_project/main.py | 8 | | process_data | /path/to/example_llm_project/data_processor.py | 9 |这份报告的价值在于静态问题被自动列出来了同时通过行执行次数可以看出utils.py中的函数没有任何执行记录进一步确认它们是死代码。5.2 常见问题与排查思路问题现象常见原因解决思路静态分析报告为空但运行时报错目标脚本依赖外部参数或环境变量先用--args传入参数或先手动运行一次确认入口运行追踪只记录了 main.py没有记录模块文件_is_project_file路径判断不准确检查 root 和 target 是否传入绝对路径必要时改为绝对路径比较runpy.run_path执行后抛异常目标脚本内部存在明显逻辑错误这是审计的预期结果报告中会记录错误信息追踪到了大量第三方库代码root 目录设置过大把 root 精确到被审计项目根目录不要指向整个工作区报告里出现大量未使用导入LLM 生成代码常见的“复制粘贴式导入”在静态分析基础上逐个人工确认删除确认无用的导入5.3 如何避免误判AST 静态分析是“按规则找问题”不是“理解业务逻辑”。比如某个导入虽然当前文件没有直接使用但可能是通过__getattr__或动态导入间接使用。遇到这类情况建议把报告中标记的“未使用导入”当成“待确认导入”而不是直接删除。print标记同理。有些脚本工具本身就以print作为主要输出方式这种不算调试残留。之所以仍然标记出来是为了提醒你确认它的输出意图。6. 最佳实践与工程建议6.1 安全运行 LLM 生成代码在运行 LLM 生成的代码之前建议先在隔离环境中执行。最直接的方式是使用虚拟环境或容器避免它直接访问生产目录、环境变量和真实数据库。由于工具会真实执行目标脚本执行前必须确认脚本内容是可信的。假如你要审计来自外部来源的 LLM 生成项目请先人工浏览代码确认没有危险操作后再运行审计工具。审计工具本身只是提高效率不能替代必要的安全审查。6.2 把审计工具接入项目流程这篇中的工具虽然简单但可以继续扩展。你可以在 CI 流程中增加一个审计步骤让每次提交 LLM 生成代码时自动生成报告并把报告上传到构建产物中。更实用的做法是把问题按优先级排序。比如 ERROR 级别问题直接阻断合并WARN 级别问题进入人工确认队列。这样既能防止 AI 生成代码直接混入主干又不会因为过度敏感而阻塞开发。6.3 结合人工 review 的边界自动化审计工具能发现很多表面问题但无法判断业务逻辑是否正确。比如一个排序算法写错了或者一个数据聚合逻辑算出来的结果不符合业务预期AST 和运行轨迹很难自动识别。所以建议把工具定位成“第一道过滤器”而不是“最终裁判”。先用工具快速扫一遍把低级问题清理干净再让有经验的开发者集中精力看核心业务逻辑。这样整体 review 效率会明显提升。6.4 代码可维护性的几个具体建议如果审计报告中出现了大量占位函数或未使用导入说明这个生成结果还不够干净。下一步可以要求生成工具补充测试或者人工补全实现。对于except:这类宽泛异常一定要改成具体异常类型。比如try: total sum(items) except ZeroDivisionError: return None对于调试输出建议统一使用logging模块。这样在生产环境可以按级别过滤不需要再修改代码。7. 总结与下一步学习方向这个审计工具的核心价值是把“人工 review LLM 生成代码”的重复性工作自动化了一部分。静态 AST 分析负责扫描结构问题sys.settrace负责执行轨迹追踪两者结合可以比较全面地暴露无效代码、异常吞噬、调试残留和运行异常。整个工具只依赖 Python 标准库代码量不大适合作为基础模板继续扩展。如果你希望把它做得更完善可以从几个方向入手增加复杂度分析计算圈复杂度找出过于复杂的函数。增加 import 依赖图展示模块之间的依赖关系。集成pytest在 step through 之前先跑一轮测试。把报告输出为 HTML 或 JSON方便接入其他系统。在实际使用中我最大的感受是审计工具能快速定位“哪里不对劲”但最终判断“代码是否符合业务预期”仍然需要人。它应该成为你审查流程中的一个加速器而不是替代品。如果你也在处理 LLM 生成代码的项目建议先从一个小型示例项目开始跑通这套流程再逐步接入真实项目。等熟悉了 AST 和sys.settrace之后你会发现自己对 Python 代码的理解又深了一层。