乐于分享
好东西不私藏

AI 写代码这么强,为什么连创建文件都做不对?

AI 写代码这么强,为什么连创建文件都做不对?

本文由AI创作

事情要从我深夜排查的一个诡异 Bug 说起。

一个接口调用反复失败。报错信息含含糊糊,只甩回来一句"参数解析异常"。

参数文件是我手写的一个 JSON,也就十几个字符。肉眼检查了不下十遍,一个字都没错。换编码,换换行符,重传了五六次——

还是同样的报错。

最后没办法,我把请求体的原始字节 dump 了出来。

文件头赫然躺着三个字节:

EF BB BF

UTF-8 BOM。

你可能对这个缩写没什么感觉。我换个说法:这是 1990 年代微软记事本遗留下来的"文件头标记",二十多年过去了,绝大多数现代工具都已经抛弃了它。

但它就在我面前。在一个 2026 年创建的、只有一行 { "param1": "test" } 的 JSON 文件里,复活了。

这不是最离谱的部分。

最离谱的是——这个文件,是一个 AI Coding Agent 生成的。

═════════

谁干的?逐字节追凶

三个字节的 BOM,到底是谁塞进去的?

嫌疑人就三个:编辑器、AI 的专用写文件工具、Shell 命令。

编辑器第一个排除。现代编辑器保存 JSON,默认都不带 BOM,这事不是它干的。

那就是 AI 的环节出了问题。这个 AI 编程平台提供了一个叫 Write 的专用命令,专门用来创建和修改文件。按理说,用 Write 写出来的文件应该是干干净净的 UTF-8,没有 BOM,没有多余的换行符。

验证一下。用 Write 工具创建几个测试文件,逐字节对比:

text
Write 工具输出:7B 20 22 70 61 72 61 6D 31 22 3A 20 22 74 65 73 74 22 20 7D(无 BOM,无 CRLF,纯 UTF-8)

干净。Write 工具确实没问题。

那嫌疑只剩一个:AI 在某个环节没用 Write,而是调了 Shell。

继续验证。把 Windows 上 PowerShell 能写文件的方法全测一遍:

text
Set-Content -Encoding UTF8  →EF BB BF ... 0D 0A       ← UTF-8 BOM,CRLF 换行Out-File -Encoding UTF8  →EF BB BF ... 0D 0A       ← UTF-8 BOM,CRLF 换行> 重定向(默认编码)  →FF FE ... 0D 00 0A 00    ← UTF-16LE BOM,每个字符占两字节

全带 BOM。一个不落。

真相大白了。

那个带 BOM 的 JSON 文件,根本就不是 Write 工具创建的。是 AI 在某个步骤,随手敲了一行 PowerShell 命令——Set-Content、或者 Out-File、或者一个 > 重定向——把它写进去的。

三种方法,各有各的坑:前两种给你贴 EF BB BF(UTF-8 BOM),第三种更狠——整个文件编码变成 UTF-16LE,体积直接翻倍。不管哪种,都不是你想要的纯净 UTF-8。

AI 在"写一个文件"这么简单的事上,选了它觉得最顺手的那条路:调 Shell。

而 PowerShell 的默认行为,不是给你干干净净的 UTF-8,是给你各种花式 BOM——要么 UTF-8 带 EF BB BF,要么更离谱的 UTF-16LE 带 FF FE

复现环境说明

以上测试基于 Windows PowerShell 5.1PSVersion 5.1.26100.4768,Desktop 版)。

如果你的环境里执行 > 重定向也输出了 EF BB BF,大概率是因为系统预设了 $PSDefaultParameterValues。比如我当初排查这个 Bug 的机器上,不知什么时候被写入了:

text
$PSDefaultParameterValues = @{    'Set-Content:Encoding' = 'utf8'    'Out-File:Encoding'    = 'utf8'    'Get-Content:Encoding' = 'utf8'    'Add-Content:Encoding' = 'utf8'}

这会让 Out-File(以及依赖它的 > 重定向)的默认编码从 UTF-16LE 变成 UTF-8 with BOM——三种方法全部输出 EF BB BF,看起来"整齐一致",掩盖了真正的默认行为差异。你要复现本文的 Bug,请先确认你的 $PSDefaultParameterValues 是否为空

═════════

AI 的七寸:它有专用工具,但它不一定用

BOM 本身不是什么大事,三个字节,修一下几秒钟的事。

但这件事真正让我警铃大作的,不是 BOM——

是 AI 明明有专为这个任务设计的工具,它却选择了不用。

Write 是什么?是这个平台为文件写入专门封装的原子操作。它保证编码干净、不引入副作用、不产生历史包袱。它就是为"创建一个 JSON 配置文件"这个场景而生的。

Shell 命令是什么?一把万能扳手。它能拧螺丝,也能撬钉子、刮墙面、砸核桃。AI 用它来创建文件,不是因为"最合适",而是因为——

"也能行。"

但"也能行"和"最合适"之间那条裂缝,就是 BOM 诞生的地方。

而且这根本不是一个孤例。如果你日常在用 AI 写代码,下面这些场面你一定不陌生:

  • AI 用 sed 改一行配置,而不是用搜索替换工具,结果少闭合了一个引号,整个文件炸了
  • AI 用 cat <<EOF > file 写 JSON,转义字符层层嵌套,最后生成的全是 \",解析都解析不了
  • AI 用 npm install --force 解决依赖冲突,而不是花两分钟分析一下到底哪里冲突了

这些场景的底层模式一模一样:

AI 选工具的逻辑,不是"哪个最安全、副作用最小"。是"哪个命令行能跑通"。

═════════

为什么会这样?不是笨,是机制问题

你可能会想:现在的 AI 模型这么强,为什么连"创建文件应该用专用工具"这么简单的道理都不懂?

这不是傻。这是工具调用的底层机制决定的。

AI Coding Agent 选工具的过程,本质上是一个概率决策。它的 Prompt 里列了所有可用工具——Write 也在里面,RunCommand 也在里面。当你说"创建一个文件",它要从这堆选项里挑一个。

靠什么挑?

靠模型在训练数据里见过的模式。靠当前的上下文。靠工具描述文本的语义匹配。

在模型的视角里,Set-Content -Path ... -Encoding UTF8 和 Write(file_path, content) 都是"合法的文件创建方式"。没有谁能告诉它"'Write' 更安全,因为不引入 BOM"。

它没有"专用工具优先"这个硬约束,也不具备"Shell 命令可能夹带 BOM"这个隐性知识。

它只是选了一个在概率分布上得分更高的路径。

更麻烦的是多步骤任务。先创建文件、再写入内容、再执行验证——每一步都可能触发新一轮的工具选择。第一轮规规矩矩用了 Write,第二轮手一滑调了 Shell,BOM 就悄悄进来了。

而且一旦文件带上了 BOM,后续 Write 尝试覆写同样内容时,可能会直接跳过——因为内容没变。

BOM 就此扎根。

这不是 AI "犯错了"。这是 AI 在做它最擅长的事——找一条能到终点的路。它只是不负责告诉你,这条路是不是最干净的,路边会不会踩到钉子,你明天要不要加班。

═════════

那怎么办?

搞清楚原理以后,解决思路反而清楚了。三个建议,都很务实:

第一,关键操作指定专用工具。

写 Prompt 或设计 AI 工作流时,如果涉及创建文件,显式告诉它用 Write,不要让它自己选。 把"创建文件"和"执行命令"这两件事在语义上切开——前者叫 write,后者叫 run。别含糊。

第二,关键输出做后置校验。

在流程的尾巴上加一道检查。JSON 文件就跑一次 python -m json.tool,或者让 AI 用十六进制看一眼文件头。这不是过度防御——在"AI 输出不可靠"这个前提下,这就是合理的工程实践。

那三个字节的 BOM,查一下就现行。但如果没人看、没人查,它能一直潜伏到生产环境,给你炸一个大的。

第三,接受 AI 的"野路子倾向",把它当特性,别当 Bug。

AI 会用 Shell 绕路,这不是偶发事故,是概率模型的底层属性。

与其指望它永远选对工具、永远走正道,不如在设计流程时,默认假设"它随时可能用命令行写文件",然后在接收端做好防御。

就像你写后端 API,从来不会假设前端一定传对了字段。把 AI 的输出,也当成不可信的外部输入来处理。

═════════

写在最后

一个 1990 年代定义的三个字节,在 2026 年的 AI 生成代码里出现了。

这不是编码史的老黄历没翻过去。这是 AI 工具链的一幅真实切面:

一套精心设计的原语工具,被扔进了一个概率驱动的决策黑盒。AI 有时会精准地用 Write 写出干净的 UTF-8,有时会随手敲一行 PowerShell,然后给你留下一个 EF BB BF

它在工具选择上没有对错观,只有概率。

而你的工程质量,不能押在概率上。

扣子智能体教程

https://space.bilibili.com/26079586