我扒了 npm 三年数据:AI 没把依赖变胖,是把包变多了

28 阅读6分钟

现在写代码的日常是这样的:让 agent 建项目、让它装依赖、让它跑测试,一天下来 npm install 能跑几十次。我的直觉一直是个判断:AI 写码这一年,node_modules 肯定更惨了——AI 又不心疼依赖,见包就装。

前几天收拾一个 2023 年的老项目,对比了一下 node_modules,发现事情不对劲。于是我花了一个下午,扒了 npm 上 66 个包三年的数据:54 个主流前端/Node 包,加 12 个 AI SDK。

结论先说:和直觉完全相反,单个包全在瘦身。但真正的膨胀藏在你看不见的地方。

我是怎么扒的

npm 的 registry 是全公开的,不需要任何爬虫:

// registry.npmjs.org/<pkg> 返回完整 metadata
// 对比两个时点的「最新稳定版」的 dependencies 数量
const meta = await fetch(`https://registry.npmjs.org/${pkg}`).then(r => r.json());
const latest = meta['dist-tags'].latest;
const depsNow = Object.keys(meta.versions[latest].dependencies || {}).length;

// 再从 meta.time 里找出 2023-10-16 之前的最后一个稳定版,同样数一遍

两个时点:2023 年 10 月(AI 编码工具刚起步)和现在。直接依赖、传递依赖、周下载量,三层对照。

第一层:直接依赖,全生态在瘦身

54 个主流包,直接依赖总数 547 → 488,降了 10.8%。跌 19 个,涨 10 个,剩下 24 个没动。

跌得最狠的几个:

包2023 版本依赖数现在版本依赖数
tailwindcss3.3.3224.3.30
esbuild0.19.4220.28.20
vitest0.34.6245.0.513
next13.5.41616.4.26
webpack5.89.0245.112.017
eslint8.51.03710.12.030

工具链这三年不是在堆依赖,是在砍依赖。esbuild 和 tailwind 直接干到零依赖,next 砍掉一大半,连 webpack 都在悄悄减负。

第二层:AI SDK 瘦得更夸张

这是最反直觉的部分。AI 包——大家印象里「随便装、不管依赖」的重灾区——单包瘦身幅度全场最大:

包直接依赖变化传递依赖变化
langchain22 → 480 → 23(-71%)
openai8 → 066 → 24(-64%)
@anthropic-ai/sdk8 → 367 → 32(-52%)
ai (Vercel)7 → 389 → 41(-54%)

openai 的 SDK 现在是真·零依赖。langchain 三年前那个 0.0.x 版本要拉 80 个包,现在本体只要 23 个。

传递依赖我是实测的:npm install <pkg>@<版本> --package-lock-only,直接数 lock 文件里的包数。对照组里 vite 147→157、react 160→158、express 231→221,主流包传递依赖基本持平或微降。

到这里,如果只看这些数据,结论应该是「AI 让生态更健康了」。

但我拼完 langchain 的生态之后,笑不出来了。

第一个坑:零依赖是统计口径游戏

esbuild 显示 0 依赖,去翻它的 metadata,optionalDependencies 里躺着 26 个平台二进制包——@esbuild/darwin-arm64、@esbuild/linux-x64…… 你 npm install 的时候照样按平台拉一个下来,只是不进「依赖数」这个统计口径。

typescript 更有意思,7.0.2 把 20 个平台二进制同时写进 dependencies 和 optionalDependencies,两个口径双写。同一份数据,你想让它胖就胖、想让它瘦就瘦。

所以「依赖数变少」和「npm install 拉的东西变少」,是两回事。

第二个坑:拆包,把重量转移到你看不见的地方

langchain 本体传递依赖从 80 降到 23,看起来健康极了。

但 1.x 的 langchain 本体几乎干不了活,接模型要装 @langchain/openai,用社区集成要装 @langchain/community。我把「langchain + @langchain/openai」这个最小组合作实测:

2023 年的 langchain 单包:80 个传递依赖。 现在的 langchain + @langchain/openai:222 个。

涨了 2.8 倍。包本身瘦了,生态拼起来胖了。你看 package.json 里只写了一行 langchain,node_modules 里躺的是 222 个包。

第三个坑:新物种根本不进你的对照组

我统计的 12 个主流 AI 包里,7 个在 2023 年 10 月根本不存在:@modelcontextprotocol/sdk、@google/genai、@langchain/core、@mastra/core……

它们不是「变胖了」,是凭空长出来的。而它们的下载量:

包2023 周下载现在周下载
@modelcontextprotocol/sdk0(不存在)6,499 万
@anthropic-ai/sdk21.7 万4,646 万(214 倍)
ai (Vercel)5.9 万3,118 万(525 倍)
@google/genai0(不存在)2,472 万
@mastra/core0(不存在)227 万

参照一下:eslint 是 npm 生态的下载量大户,周下载 1.59 亿。MCP SDK 一个三年前不存在的包,现在已经到了它的四成。

这不是尝鲜,是标配。每个正经项目现在都多了一整层 AI 依赖:模型 SDK 至少一个,agent 框架看情况,向量库、MCP server 按需。

所以膨胀去哪了

把三层数据拼起来,答案就出来了:

依赖膨胀没有消失,是从「单包深度」转移到了「package.json 长度」。

三年前你担心的是「这个包怎么拉了 200 个依赖」;现在单个包确实瘦了,但你项目里 AI 相关的包从 0 个变成了 5 个起步,每个背后还拖着自己的生态。langchain 教科书式地演示了这个过程:本体瘦身 71%,组合膨胀 178%。

我的立场很明确:这不是 AI 的锅,是生态的锅。 AI 只是让「装包」这个动作变便宜了——agent 一句话就装上了,人甚至不需要过目。真正把体积做进去的,是拆包策略和平台二进制这些发布侧的选择。

反驳我也简单:如果没有 AI 写码,这些新物种包你一个都不会装。评论区见。

附:AI 依赖体检清单

给项目做个体检,四条就够:

检查项命令 / 方法处置
AI 包数量grep -E "openai|anthropic|langchain|mastra|genai" package.json同一能力两家的 SDK,砍一家
传递依赖总量npm install --package-lock-only 后数 lock 的 packages 节点环比上个版本,涨幅 >30% 查一下谁带的
假零依赖包查 optionalDependencies 里的平台二进制无法处置,但计体积时别漏算
已死 AI 依赖npm ls --depth=0 对照实际 importagent 装了没用上的,直接删

最后留个实测数据。我在空目录里装了个纯 express 服务:65 个包,node_modules 3.8 兆。加上 openai、@anthropic-ai/sdk、@modelcontextprotocol/sdk 这四个最普通的 AI 依赖重装一遍:104 个包,85 兆。

包数只多了 60%,体积涨了 22 倍——平台二进制们不在「依赖数」里,但它们实实在在躺在磁盘上。

你项目里现在躺着几个 AI 依赖?评论区报个数。