前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
返回 AI 情报前线
All News · 全部资讯9240
  • React Flow + TypeScript 构建节点式流编辑器
  • Gemini 3.7 Flash 现身 SWE-bench:65% 通过率非 Pro 版
  • LLM 推理日志加密块藏有 704 个隐私泄露风险
  • SharePoint CVE-2026-55040 正被大规模利用
  • 非开发者用Agent Coding构建生产系统的经验: Eval是关键
  • Vim技能衰退与AI代码审查实践
  • 1.5B 参数模型本地跑 Shell 命令,1 秒出结果
  • llm-gemini 0.33:Simon Willison的AI CLI工具链支持Gemini 3.7
  • 多Agent平台工程:自适应推理深度节省thinking token
  • Oracle pgvector 生产翻车:1万向量后 RAG 质量急剧下降
  • OpenAI官方研究:组织如何使用ChatGPT
  • OpenAI 推出 Ultrafast 模式:GPT-5.6 Sol 速度快 14 倍
  • 有人在法律文书中隐藏提示注入,让 AI 裁定对其有利
  • 本地LLM vs 云端API:决策框架与成本计算器
  • ChatGPT Work和Codex新增Mac活动记忆功能
  • 程序员用编译器把《DOOM》渲染算法直接转成神经网络权重
  • GitOps风格管理AI Agent记忆与工具配置版本
  • Gemini 3.7 Flash 发布:编程能力提升50%降价一半
  • TraceMotive:面向 AI Agent 执行的本地优先调试器
  • AI 生成代码应视为假设:给免费模型答案分配错误预算
  • 依赖距离检测:AI 改动的爆炸半径分析脚本
  • 别盲目合并 AI Patch:Git Worktree Replay Gate 方法论
  • Anthropic研究:AI Agent会争抢地盘并自发合作
  • Claude Code 新会话默认启用 Auto 模式
  • OpenAI 发布 GPT-5.6 三子型号系列
  • Cerebras 刷新 GPT-5.6 推理速度纪录
  • AI Agent生产落地:从Prompt链式调用到基础设施架构
  • AI原生企业构建:从Copilot到自主运营
  • Google Meet 测试版推出 AI 会议笔记功能,Gemini 自动生成纪要
  • Gemini 3.7 Flash 发布: 编程/Agent 能力大幅提升,每百万 Token 0.75 美元
  • 测试套件绕过隔离写入生产数据库的真实事故复盘
  • AI 生成的认证中间件: undefined === undefined 导致认证绕过
  • DeepSeek开源Agent Harness:一切皆插件的Node.js运行时
  • Node.js 多 Provider 代码审查摘要方案:JSON Output 实战对比
  • Hugging Face整合机器人开发工具链
  • Trace 不是 Governance:Agent 系统长期运行的架构边界
  • LangGraph 多 Agent 流水线实战:18 天开发 doc2slides 的工程权衡
  • 自建 LLM 路由:用对模型省 28x 成本
  • 训练数据仅占 0.3%,却占输出 36%:微调模型的数据污染陷阱
  • Gemini 3.7 Flash发布
  • Gemini 3.7 Flash三周内连发
  • Google Sheets Canvas:自然语言构建交互式数据看板
  • CI/CD AI Agent 部署前需加人工审批门
  • MCP + RSS 目录:让 AI 工具获取领域最新信息
  • Agent工具调用200 OK背后的谎言:完成所有权问题
  • Google Sheets Canvas功能详解:打造互动数据看板
  • DeepSeek V4 Pro正式版发布,Agent框架Harness开源,API涨价
  • GPU 数值格式详解:FP32/BF16/FP8/FP4 交互式理解指南
  • 长时 Agent 循环如何设计记忆机制
  • 深度体验DeepSeek Harness:涨价也在情理之中
  • AWS AgentCore:统一监控多云/本地AI Agent
  • 已加载 51 / 9240
8.0
热点
AI SCORE
编程提效2026-08-14 02:30

依赖距离检测:AI 改动的爆炸半径分析脚本

dev.to · AI#代码依赖分析#AI改动审查#Python工具
Editor brief · 编辑速览

AI 生成的 patch 可能影响看似无关的文件,通过静态分析模块依赖链来量化改动影响范围。

文章思维导图
Knowledge map
拖拽缩放
Full translation

完整中文译文

AI 生成的补丁可以通过所有测试,却仍然在三步之遥的导入处引发故障。diff 很小,测试全绿,改动的函数看起来也没问题。但破坏性只有在合并之后才显现出来——一个从未触碰过的调用方导入了新的函数签名。

在信任一个补丁之前,评审者需要一张依赖距离表:哪些本地模块直接导入了被改动的模块,哪些在下一步才导入,下游链路有多深。这是一个图搜索问题,而不是靠感觉判断。真正有用的信号不是模型的补丁本身,而是代码库的静态结构。

本文从 Git 和 Python 标准库构建一个爆炸半径脚本。它是有意确定性的,所以在笔记本或免费 CI 服务器上运行方式完全一致。最后一个可选步骤仅用免费模型来总结已经计算好的列表。

将文件保存为 blast_radius.py,从 Git 仓库任意位置运行。

#!/usr/bin/env python3
import ast
import os
import subprocess
import sys
from collections import defaultdict, deque

ROOT = subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], text=True).strip()

def changed_py_files():
    out = subprocess.check_output(
        ['git', 'diff', '--name-only', 'HEAD~1', 'HEAD'],
        text=True,
        cwd=ROOT,
    )
    return {
        os.path.relpath(line.strip(), ROOT)
        for line in out.splitlines()
        if line.strip().endswith('.py')
    }

def tracked_py_files():
    out = subprocess.check_output(['git', 'ls-files', '*.py'], text=True, cwd=ROOT)
    return {
        os.path.relpath(line.strip(), ROOT)
        for line in out.splitlines()
        if line.strip().endswith('.py')
    }

def module_name(path):
    path = path.replace(os.sep, '/')
    if path.startswith('./'):
        path = path[2:]
    if path.endswith('__init__.py'):
        path = path[: -len('__init__.py')]
        path = path.rstrip('/') or '.'
        return '.'.join(part for part in path.split('/') if part)
    return path[:-3].replace('/', '.')

def direct_imports(path):
    full = os.path.join(ROOT, path)
    try:
        with open(full, encoding='utf-8') as f:
            tree = ast.parse(f.read(), filename=path)
    except (SyntaxError, UnicodeDecodeError):
        return set()
    found = set()
    for node in ast.walk(tree):
        if isinstance(node, ast.Import):
            for alias in node.names:
                found.add(alias.name.split('.')[0])
        elif isinstance(node, ast.ImportFrom) and node.module:
            found.add(node.module.split('.')[0])
    return found

def build_graph(paths):
    graph = defaultdict(set)
    for path in paths:
        graph[module_name(path)].update(direct_imports(path))
    return graph

def consumers(graph):
    result = defaultdict(set)
    for src, deps in graph.items():
        for dep in deps:
            result[dep].add(src)
    return result

def affected_modules(changed, graph):
    changed_mods = {module_name(p) for p in changed}
    consumer_map = consumers(graph)
    seen = {}
    queue = deque((m, 0) for m in changed_mods)
    while queue:
        mod, dist = queue.popleft()
        if mod in seen:
            continue
        seen[mod] = dist
        for user in consumer_map.get(mod, []):
            if user not in seen:
                queue.append((user, dist + 1))
    return sorted(seen.items(), key=lambda kv: (kv[1], kv[0]))

if __name__ == '__main__':
    changed = changed_py_files()
    if not changed:
        print('No Python files changed.')
        sys.exit(0)
    graph = build_graph(tracked_py_files())
    rows = affected_modules(changed, graph)
    for dist, mod in rows:
        print(f'{dist:>2}  {mod}')

输出列表中,距离 0 是被改动的模块,距离 1 是直接导入方,更远的是传递性导入方。常见工具模块的断裂可能产生很长的尾部分支,而叶子模块的改动几乎不会产生任何影响。这正是评审在阅读 diff 之前所需的那种分类信息。

为什么用 BFS 而不是完整依赖图

递归导入遍历能告诉你仓库中的每一个模块,但不告诉你它们与改动的接近程度。广度优先搜索保留距离信息,因为它按层级扩展。直接调用方在被间接调用方之前被访问,且每个模块都保留从被改动文件出发的最短距离。

脚本仅存储本地源码导入。它有意忽略已安装包和标准库模块的调用方发现;那些模块不是你的补丁的评审者。这保持了图的精简和输出的聚焦——只关注你真正能编辑的文件。

可选的免费模型摘要

披露:本文是 MonkeyCode 产品推广的一部分。

原始距离表本身已经有用,但长长的列表仍然可能嘈杂。对于这种情况,免费模型端点可以将排序后的行转化为两条简短笔记:哪些直接调用方值得优先阅读,哪些传递性集群大概可以安全跳过。MonkeyCode 的免费模型访问和免费服务器选项使这一步成本极低,但脚本不需要任何特殊模型行为——只需普通的摘要能力。

让模型远离决策循环。用户只提供图的输出并请求组织整理,而不是审批。如果模型建议了一个修复,请将该建议视为另一个 AI 生成的补丁,并将其纳入相同的评审工作流程。

局限性

它只能看到静态的 Python 导入。importlib 动态调用、基于字符串的导入、插件和条件加载可能对图不可见。

它追踪的是本地源码导入,而不是跨二进制文件、生成代码或其他语言的运行时依赖。

它构建的是语法图,而不是语义图。只有在被改动的符号实际破坏了其契约时,下游调用方才会受到影响。

它假设使用 Git 和 Python 3.9+,且在具有常规包布局的仓库中效果最佳。

谁不应该使用此工具

拥有大量动态导入、混合语言 monorepo 或插件密集型运行时的团队,不应该将此图视为完整的。它是一张分类地图,而不是安全证明。同样,只有少数模块的扁平小项目可能不需要它;直接阅读 diff 更快。

从距离表开始

下次你评审一个 AI 生成的补丁时,在打开代码之前先运行爆炸半径脚本。距离 1 处的模块是大多数意外藏身之处,而且往往是补丁没有触碰过的文件。

Original source

本文由 AI 翻译整理自 dev.to · AI,原文版权归原作者所有。

阅读英文原文
上一篇
AI 生成代码应视为假设:给免费模型答案分配错误预算
下一篇
别盲目合并 AI Patch:Git Worktree Replay Gate 方法论