现在写代码的日常是这样的:让 agent 建项目、让它装依赖、让它跑测试,一天下来 npm install 能跑几十次。我的直觉一直是个判断:AI 写码这一年,node_modules 肯定更惨了------AI 又不心疼依赖,见包就装。
前几天收拾一个 2023 年的老项目,对比了一下 node_modules,发现事情不对劲。于是我花了一个下午,扒了 npm 上 66 个包三年的数据:54 个主流前端/Node 包,加 12 个 AI SDK。
结论先说:和直觉完全相反,单个包全在瘦身。但真正的膨胀藏在你看不见的地方。
我是怎么扒的
npm 的 registry 是全公开的,不需要任何爬虫:
javascript
// 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 版本 | 依赖数 | 现在版本 | 依赖数 |
|---|---|---|---|---|
| tailwindcss | 3.3.3 | 22 | 4.3.3 | 0 |
| esbuild | 0.19.4 | 22 | 0.28.2 | 0 |
| vitest | 0.34.6 | 24 | 5.0.5 | 13 |
| next | 13.5.4 | 16 | 16.4.2 | 6 |
| webpack | 5.89.0 | 24 | 5.112.0 | 17 |
| eslint | 8.51.0 | 37 | 10.12.0 | 30 |
工具链这三年不是在堆依赖,是在砍依赖。esbuild 和 tailwind 直接干到零依赖,next 砍掉一大半,连 webpack 都在悄悄减负。
第二层:AI SDK 瘦得更夸张
这是最反直觉的部分。AI 包------大家印象里「随便装、不管依赖」的重灾区------单包瘦身幅度全场最大:
| 包 | 直接依赖变化 | 传递依赖变化 |
|---|---|---|
| langchain | 22 → 4 | 80 → 23(-71%) |
| openai | 8 → 0 | 66 → 24(-64%) |
| @anthropic-ai/sdk | 8 → 3 | 67 → 32(-52%) |
| ai (Vercel) | 7 → 3 | 89 → 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/sdk | 0(不存在) | 6,499 万 |
| @anthropic-ai/sdk | 21.7 万 | 4,646 万(214 倍) |
| ai (Vercel) | 5.9 万 | 3,118 万(525 倍) |
| @google/genai | 0(不存在) | 2,472 万 |
| @mastra/core | 0(不存在) | 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 |
| 传递依赖总量 | npm install --package-lock-only 后数 lock 的 packages 节点 |
环比上个版本,涨幅 >30% 查一下谁带的 |
| 假零依赖包 | 查 optionalDependencies 里的平台二进制 | 无法处置,但计体积时别漏算 |
| 已死 AI 依赖 | npm ls --depth=0 对照实际 import |
agent 装了没用上的,直接删 |
最后留个实测数据。我在空目录里装了个纯 express 服务:65 个包,node_modules 3.8 兆。加上 openai、@anthropic-ai/sdk、@modelcontextprotocol/sdk 这四个最普通的 AI 依赖重装一遍:104 个包,85 兆。
包数只多了 60%,体积涨了 22 倍------平台二进制们不在「依赖数」里,但它们实实在在躺在磁盘上。
你项目里现在躺着几个 AI 依赖?评论区报个数。