你让 Codex 列举 public/ 下所有资源,或者审计 data/ 下每个文件。Agent 回的列表里安静地漏掉了 PNG 雪碧图、sqlite seed DB、预编译的 .wasm、嵌入字体二进制。更糟的是它一句警告都没——回复读起来好像那些文件根本不存在。Reviewer 发现少了一项,你也不知道还有多少次审计静默丢了二进制。
Codex 的 read 工具默认 UTF-8 文本。非文本字节的文件要么静默 error、要么返回”binary file, skipped”、要么概括成”[redacted: 4MB binary]“——agent 把它当不重要的处理掉。修法不是强行读二进制(产出垃圾),而是教 agent 用不同工具承认二进制:hash、元数据、结构化探针。
常见原因
按命中率排序。
1. Read 工具读非 UTF-8 字节报错,agent 当”没这个文件”
大多数 agent read 工具碰到非文本字节会抛 error 或返回空 body。Agent 不会换工具重试——直接跳过,文件就从它的认知里消失了。
如何判断:让 agent 用显式 ls -la 重新列。文件出现在 ls 里但没出现在 agent 之前的总结里——被静默丢了。
2. 扩展名不在 agent heuristic 的认知里
.parquet、.arrow、.msgpack、.protobuf、自定义 .bin——大多数 agent 的”文本”allowlist 没这些。甚至 .dat 后缀的 JSON 也可能被当二进制。
如何判断:拿 agent 真正读到的文本文件后缀对比一下。不在 .md .txt .json .yaml .ts .js .py .go .rs .toml 之列的就可能被静默跳过。
3. 文件含 BOM 或编码混杂
UTF-16 LE、Latin-1、UTF-8-with-BOM 这种可能让严格 UTF-8 reader 出错。Agent 当”encoding error”跳过、不把问题暴露出来。
如何判断:file path/to/file 显示 UTF-16 或 ISO-8859——Codex reader 大概率拒收了。
4. 文件虽然是文本但超过 read 工具的大小上限
30 MB 的 CSV 是文本,但大多数 agent read 工具在 1-5 MB 处截断 + bail。Bail 的表现和二进制情况完全一样——静默跳过。
如何判断:wc -c file 返回 > 5_000_000 而文件在 agent 输出里没出现。
5. Symlink 指向二进制或 LFS 指针
Git LFS 在仓库里存 130 字节的文本指针,真实二进制在 LFS 服务器。Codex 读到指针、看到一个小小的非内容 blob、当垃圾处理掉。
如何判断:cat file.psd 显示 version https://git-lfs.github.com/spec/v1 后跟一个 OID——真实二进制根本没拉下来。
6. .gitignore / .codexignore 静默过滤掉了
项目的 ignore 文件本来就是要把二进制从 agent 工具里挡掉。但你忘了 ignore 还在生效时,就以为 agent 看到了文件。
如何判断:git check-ignore -v path/to/file 返回匹配规则——agent 遵守了 ignore 文件。
动手前先确认
- 跑
find . -size +1M -type f | head看可能被跳过的有哪些。 - 跑
file path/to/each确认哪些真是二进制、哪些是被误判。 - 决定 agent 需要知道每个二进制的什么信息:存在 + hash、尺寸、schema 还是完整字节。
需要收集的信息
ls -la <dir>全输出 vs agent 认知的文件列表——diff 找出被跳过的条目。- 每个被跳过文件的:扩展名、
file输出、大小、是否是 LFS 指针。 - Agent transcript 里那个文件本应出现但没出现的位置。
- 项目的
.gitignore、.codexignore和任何自定义 ignore 配置。
最短修复路径
按收益从高到低。
Step 1:先给 agent 一个二进制感知的探针工具
把”读这个文件”换成”探测这个文件”:
对 data/ 下每个文件:
1. 跑:file <path> (拿 MIME 类描述)
2. 跑:wc -c <path> (拿字节大小)
3. 跑:sha256sum <path> (拿内容 hash)
4. file 输出以 "ASCII" / "UTF-8" / "JSON" 开头 → 读内容
5. 否则 → 只记 {path, type, size, sha256}
Agent 现在知道二进制存在、是什么类型、指纹是什么——不用碰字节。
Step 2:显式 allowlist 扩展名
在 task prompt 里:
可读文本扩展名:.md .txt .json .jsonl .yaml .yml .toml .ini
.ts .tsx .js .jsx .mjs .cjs .py .rb .go .rs .java .kt .swift
.html .css .scss .sql .sh .dockerfile .env.example
其他扩展名:只记 path + size + sha256。
不要试图读内容。
不要从输出里省略。带 "binary" 标记列出来。
这条消除了静默丢失——每个文件都在输出里出现,要么有内容、要么有 binary 标记。
Step 3:把二进制转成 agent 能读的文本表示
如果内容确实重要:
| 二进制类型 | 转换命令 | 输出 |
|---|---|---|
| PNG / JPG | identify -verbose img.png | 尺寸、色彩空间、元数据 |
pdftotext file.pdf - | 提取的文本 | |
| sqlite | sqlite3 db .schema | schema DDL |
| parquet | parquet-tools schema file.parquet | 列 schema |
| wasm | wasm-objdump -h file.wasm | section headers |
| zip / tar | unzip -l file.zip | manifest |
把转换后的文本喂给 agent,它就能基于内容推理而不是基于字节。
Step 4:Agent 跑之前先解析 LFS 指针
如果 .gitattributes 列了 LFS 跟踪的扩展名:
git lfs install
git lfs pull
用 file my-asset.psd 确认——应该显示 Photoshop image 而不是 ASCII text。不做这步 agent 永远只看到指针。
Step 5:大文本文件读之前先切片
如果 wc -c data.csv 超过 5 MB:
head -200 data.csv > data.head.csv
tail -200 data.csv > data.tail.csv
shuf -n 200 data.csv > data.sample.csv
让 agent 读 head + tail + sample。30 MB 的 CSV 变成 600 行,大多数结构问题就够回答了。
Step 6:让被跳过的文件出现在 agent 的结构化输出里
强制一个不能省略二进制的输出 schema:
输出 JSON:{ read_text: [...], skipped_binary: [...], skipped_too_large: [...], skipped_ignored: [...] }
输入目录里每个文件必须出现在恰好一个数组里。
总数对不上 = 错。重跑。
Schema 让静默丢失不可能发生,任何丢失都是可见的空位。
怎么确认已经修好
- 把 agent 报告的文件列表和
find <dir> -type f做 diff——不应有任何缺失。 - 每个
skipped_binary条目记录的 sha256 和sha256sum <path>对得上。 - 在目录里新增一个二进制文件再跑一次——确认它出现在
skipped_binary里而不是消失。
长期预防
- 任何 agent 文件审计 prompt 默认
read_text + skipped_binaryschema;不给”静默丢”留可能性。 - Agent 模板里 read 步之前永远先加一步 probe(
file+wc -c+sha256sum)。 - 维护一份项目级”二进制但有用”格式清单 + 对应转换命令(PDF → pdftotext、sqlite → schema 等)。
- 任何要让 agent 看资源的 sandbox 先跑
git lfs pull。 - 每季度审一次
.codexignore/.gitignore,搞清楚 agent 看不到什么。 - LLM 训练 / RAG 索引 pipeline 在 LLM 看到语料之前显式做一步”二进制 vs 文本”分拣。
常见坑
- 强行把 4 MB 二进制 base64 塞进 prompt”让它看见”——浪费上下文、模型也无法基于原始字节推理。
- 觉得
.json永远安全可读——50 MB 的 JSON dump 超过 read 上限会被静默跳过。 - 忘了
.env文件常被 ignore 掉——agent 看不见,所以审计漏 env 驱动的配置。 - 把 UTF-16 文件永久当”二进制”——
iconv -f UTF-16 -t UTF-8就修好了。 - 把巨大的 LFS pull 塞进 agent loop 而不是 CI 里预拉。
常见 FAQ
Q:Codex 说”file not found”,但 shell 里能 cat 出来,为什么?
大概率它读了但编码把工具搞挂了,表面成了”not found”。file 跑一下,如果非 UTF-8 先转码。
Q:Agent 把 .png 读成一大段乱码输出,怎么阻止?
你的 read 工具大概率宽松到 base64 编码了二进制。显式 allowlist 文本扩展名,二进制走 probe 工具。
Q:为什么不直接对每个文件 hash + describe,不管类型?
完全可以。又快又安全。大多数模板不这么做是因为源码场景下读文本内容更有用——但 public/ / data/ / assets/ 默认 probe-only 是对的。
Q:把 --max-file-size 调高有用吗?
对大文本文件(原因 #4)有用。对非文本二进制(原因 #1、#2、#3、#5)没用——工具还是解不了字节。