ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Windows下Codex写中文文档乱码?用Skill一劳永逸

Windows下Codex写中文文档乱码?用Skill一劳永逸 谁要是没在 Windows 上被中文乱码折磨过几回那基本不算正经写过文档。上周我用 codex 在命令行里生成一份 Markdown 技术方案打开文件的那一刻差点崩溃——标题是锟斤拷正文是烫烫烫表格里全是问号整篇文档跟被加密了一样。我第一反应是重新生成但结果依然如故。问题不在 codex 的生成质量而在 Windows 下文件编码和终端代码页Code Page之间的错位。这篇文章就围绕这个场景把Windows codex 中文乱码这条链路拆开揉碎然后给你一个能落地的 skill 方案让 codex 在 Windows 下写中文文档时自动规避乱码。适合所有在 Windows 上用 codex、Claude Code 或类似 AI 编程助手写中文文档、代码注释、Markdown 的开发者也适合刚接触 skill 机制、想知道它到底怎么定义规则、怎么让 AI 稳定执行编码约定的人。1. 乱码问题出在哪一环codex 在 Windows 下写中文文档的全链路1.1 那个满屏锟斤拷的下午先说结论codex 本身生成内容的能力没有毛病中文文档内容是对的但在 Windows 环境下内容从生成到落盘再到被打开中间隔着好几层编码转换任何一层对不上你看到的就全是乱码。我当时的操作很简单codex进入对话模式让它写一篇《项目部署说明.md》内容包含了标题、段落、表格、代码块。codex 在终端里正常输出我看着也没问题但当我在 Notepad 和 VSCode 里打开这个文件时中文字符全部变成了锟斤拷和方块。更诡异的是同一个文件用系统自带的记事本打开又正常。这就指向了一个经典场景文件实际编码是 UTF-8但某些编辑器按 GBK 解码或者文件被保存成了 GBK而 codex 和 VSCode 默认按 UTF-8 处理。Windows 中文系统的默认活动代码页通常是 936GBK而 AI 工具生成的文本几乎都是 UTF-8。两边不对表乱码就来了。1.2 三条链路三种乱码在 Windows 下用 codex 写中文文档乱码其实分三条链路症状不同根因也不同。我建议先分清你到底踩了哪条链路的坑再动手解决。链路发生位置典型症状根因终端输出链路codex 在 cmd / PowerShell 里直接打印中文中文变成鈥樷、问号终端代码页与输出编码不一致文件生成链路codex 生成的 .md / .txt / .html 文件打开后标题正文全乱文件写入编码与编辑器解码编码不一致代码运行链路codex 生成的脚本在 Windows 控制台运行print(中文) 输出乱码脚本编码/运行时标准输出编码不匹配注意第三种链路最常见也最隐蔽codex 生成的 Python 脚本里明明写着print(部署完成)但你一运行控制台输出却是鍙戦儕瀹屾垚。这不是脚本内容坏了而是 Python 在 Windows 控制台以 GBK 解码了 UTF-8 源文件里的字符串或者输出时被控制台重新编码了一遍。1.3 定位问题的一句话方法论我的经验是遇到乱码先别急着改文件先回答一个问题这个乱码是文件本身坏了还是文件没坏显示它的程序搞错了编码?判断方法很简单把同一个文件拖到 Chrome 浏览器里打开或者用 VSCode 右下角手动切换重新打开以编码为 UTF-8如果文字恢复说明文件是好的只是打开姿势不对如果还是乱说明文件内容在写入时就已经被错误编码了。这个判断决定了后面你是该改编辑器设置还是该改 codex 的生成规则——而后者正是 skill 最擅长管的。2. 编码三件事UTF-8、GBK、BOM 怎么影响你看到的文字2.1 UTF-8 和 GBK 到底差在哪要说清楚乱码绕不开 UTF-8 和 GBK 这两个词。GBK 是 Windows 中文版历史上最常用的编码一个汉字占两个字节向下兼容 GB2312中文系统里的记事本默认读写很多场景都围绕 GBK 展开。UTF-8 是国际化编码一个汉字通常占三个字节能覆盖全球所有字符也是 AI 工具、Git、Linux、macOS 和现代 Web 的默认选择。关键在于同一段中文用 UTF-8 编码和用 GBK 编码得到的字节序列完全不同。你把 UTF-8 的字节序列交给一个按 GBK 解码的程序它就会把这串字节拆成 GBK 的字符组合结果就是一堆完全不相干的中文生僻字和符号——这就是锟斤拷的来源。锟斤拷这三个字大有来头UTF-8 里有一个常见替换字符 UFFFD它的 UTF-8 编码是EF BF BD。这段字节被 GBK 解码时EF对应锟BF对应斤BD对应拷于是连续多个替换字符就变成了连续多个锟斤拷。所以当你看到满屏锟斤拷时几乎可以断定原始内容经过了一次错误的 GBK 解码或者以 GBK 方式读取了 UTF-8 数据且中间经过了 UFFFD 替换。2.2 BOM 为什么在 Windows 下不能随便删BOMByte Order Mark是写在文件开头的特殊字符用来标记编码格式。UTF-8 的 BOM 是EF BB BFWindows 记事本看到这个字节序列就能确定这是 UTF-8 文件。VSCode、Notepad 也能通过 BOM 自动识别编码。很多 Linux 和 macOS 开发者讨厌 BOM因为它在文件头部多了三个无意义字节可能干扰 shell 脚本执行、Node.js 模块加载。但 Windows 下情况不同没有 BOM 的 UTF-8 文件老版本记事本会默认按 ANSI即 GBK解码中文必乱。所以我的建议很直接在 Windows 下用 codex 生成中文文档优先保存为 UTF-8 with BOMPython 里叫utf-8-sigVSCode 里叫UTF-8 with BOM。当你打开 Markdown、TXT、HTML 这类文档时BOM 就是给 Windows 编辑器认亲的那张身份证。2.3 chcp 65001 与使用 Unicode UTF-8 提供全球语言支持的关系除了文件编码终端代码页也得管。Windows 命令行默认活动代码页是 936也就是 GBK。想让终端正确显示和输出 UTF-8 内容可以在 cmd 里执行chcp 65001这会把当前控制台的活动代码页切换为 UTF-8。在 PowerShell 里除了chcp 65001最好再设置一下控制台的输出编码[Console]::OutputEncoding [System.Text.Encoding]::UTF8 $OutputEncoding [System.Text.Encoding]::UTF8第一条命令让控制台解码外部程序输出时用 UTF-8第二条让 PowerShell 管道输出时用 UTF-8。两条一起设置能解决九成终端输出乱码问题。如果你想一劳永逸还可以去 Windows 设置里开启使用 Unicode UTF-8 提供全球语言支持设置 - 时间和语言 - 语言和区域 - 管理语言设置 - 更改系统区域设置 - 勾选 Beta 版选项。开启后全系统默认代码页变成 65001乱码会大幅减少但一些老软件可能会出现新的乱码或字体问题这个取舍要看你的具体环境不必强行全局开启。3. 设计乱码终结者skill目录、规则、脚本一把梭3.1 skill 的存放位置和加载机制你也许听说过 skill它是给 AI 助手定义行为准则的一种机制你写一份规则文档AI 在生成内容时按这份规则执行。codex 支持在用户目录或项目目录下放置 skills每个 skill 是一个文件夹里面有一个SKILL.md这个文件就是全部规则的载体。在 Windows 下skill 的典型路径是C:\Users\你的用户名\.codex\skills\chinese-doc-encoding\SKILL.md如果你想让某个项目单独使用这个 skill也可以放到项目目录下项目目录\.codex\skills\chinese-doc-encoding\SKILL.mdskill 文件夹的名字建议用英文小写加连字符文件夹内只放SKILL.md和配套脚本。加载时codex 会根据请求内容匹配 skill 的描述一旦匹配上就把SKILL.md里的规则当作系统约束来执行。3.2 SKILL.md 逐段拆解给 codex 立规矩SKILL.md开头的一段 YAML frontmatter 用于声明技能名称和触发条件然后是正文规则。我这里给出一份可直接抄的SKILL.md--- name: chinese-doc-encoding description: 在 Windows 环境下编写中文文档、代码注释、脚本内容时统一使用 UTF-8 编码策略避免中文乱码。适用于生成 Markdown、TXT、HTML、Python、JS 等文件。 --- # 中文文档编码规范 当用户环境是 Windows 时你必须遵循以下规则 1. 生成任何包含中文的文档Markdown、TXT、HTML、LaTeX 等一律按 UTF-8 编码保存若目标文件会被记事本、旧版编辑器打开则保存为 UTF-8 with BOMutf-8-sig。 2. 生成 HTML 文件时在 head 中显式声明 meta charsetutf-8不要省略。 3. 生成 Markdown 文件时文件头不强行要求添加编码声明但要保证文件内容本身是以 UTF-8 写的不要在 Markdown 代码块中插入编码无关字符。 4. 生成 Python 脚本时若脚本包含中文字符串字面量应给出运行提示用户可先执行 chcp 65001或在脚本中通过 sys.stdout.reconfigure(encodingutf-8) 处理输出。 5. 生成 JS/TS 脚本时若命令行环境为 Windows cmd建议脚本内避免直接打印非 ASCII 字符或提示用户切换代码页。 6. 用户要求修复已有乱码文件时调用配套 fix_encoding.py 脚本处理不要直接手改二进制。 7. 向用户提供任何乱码或编码问题的建议时先让用户执行 chcp 查看当前活动代码页再根据输出决定方案。 8. 不要建议用户删掉 UTF-8 BOM除非明确知道读取方是 Linux 工具链。 # 可用工具 - fix_encoding.py自动检测文件编码并转换为指定编码。这份规则要解决的核心问题其实就八个字生成时定编码修复时走脚本。你写文档codex 生成 UTF-8你丢给它乱码文件它调用脚本修复你在命令行跑脚本出现乱码它知道先让你查代码页。规则清晰codex 的发挥才不会飘移。3.3 配套修复脚本 fix_encoding.py乱码文件的后悔药光有规则还不够你最好给 codex 一把实际能用的手术刀。我在 skill 目录里放了一个fix_encoding.py用来检测文件编码并重新保存。脚本逻辑很简单尝试用 UTF-8 解码失败就尝试 GBK 解码之后按你指定的目标编码重新写回。#!/usr/bin/env python3 # -*- coding: utf-8 -*- 检测并修复 Windows 常见中文乱码文件。 import sys from pathlib import Path def detect_and_decode(data: bytes): # 优先按 UTF-8 解码 try: return data.decode(utf-8) except UnicodeDecodeError: pass # 再按 GBK 解码 try: return data.decode(gbk) except UnicodeDecodeError: return None def main(): if len(sys.argv) 2: print(用法: python fix_encoding.py 文件路径 [--to utf-8-bom|utf-8|gbk]) return target Path(sys.argv[1]) if not target.exists(): print(f文件不存在: {target}) return raw target.read_bytes() text detect_and_decode(raw) if text is None: print(无法自动识别原始编码请用编辑器手动处理。) return mode sys.argv[2] if len(sys.argv) 2 else --to utf-8-bom if mode --to gbk: target.write_text(text, encodinggbk) print(已转换为 GBK:, target) elif mode --to utf-8: target.write_text(text, encodingutf-8) print(已转换为 UTF-8(无BOM):, target) else: target.write_text(text, encodingutf-8-sig) print(已转换为 UTF-8 with BOM:, target) if __name__ __main__: main()这个脚本不是万能的。它适用于文件内容仍然是完整的 UTF-8 或 GBK 字节只是被错误解码过或被多个编辑器反复保存的场景。如果乱码内容在保存过程中已经被替换字符UFFFD破坏了原始信息比如那串锟斤拷已经写进了文件那么解码再转码也无法还原那时候只能从源头重新生成。我加粗提醒一句修复脚本是后悔药不是时光机。3.4 低版本 / 特殊环境下用 AGENTS.md 平替如果你用的是某个还没支持 skills 机制的 codex 版本或者团队协作时希望规则跟着项目走而不依赖个人目录那可以用AGENTS.md平替。在项目根目录放一个AGENTS.md把上面SKILL.md里的规则抄进去就行。codex 读取项目指令时会自动加载AGENTS.md效果等同于一个常驻 skill。区别在于AGENTS.md是项目级的跟着仓库走每个人 clone 下来都生效skill 是用户级的只管你自己。我自己现在的习惯是个人全局环境放 skill重要项目里再放一份精简版AGENTS.md。双保险的好处是即使某天 codex 升级后 skill 加载逻辑变了项目级指令依然兜底。4. 实测记录装上 skill 后 codex 的输出变化4.1 怎么确认 skill 被 codex 正常加载每次装完 skill最怕的就是根本没被加载你还以为规则生效了。确认方法有两个。第一在对话里直接问 codex你目前是否了解 chinese-doc-encoding 这个技能如果它回答出了规则里的关键条目说明加载成功。第二故意让它生成一个只需要带 BOM判断的场景比如让它写一段 Python 代码并说明编码处理方式如果它在我没提示的情况下主动提到utf-8-sig或chcp 65001说明 skill 已经进入它的行为约束。4.2 测试一生成一篇带中文标题、代码块、表格的 Markdown我让 codex 写一篇《Windows 下 Docker 部署注意事项.md》并特意要求包含表格和代码块。生成完成后我在 VSCode 里打开中文显示全部正常用系统记事本打开也正常——这正是带 BOM 的 UTF-8 文件的优势。VSCode 右下角显示UTF-8 with BOM一目了然。之后我又用 Git 提交了这个文件命令行里git diff时中文没有乱码这一点也值得注意Git 对带 BOM 的 UTF-8 文件处理很稳定。再看终端链路。我在 cmd 里用type命令直接查看这个文件因为之前执行过chcp 65001输出正常我又在没切代码页的新终端试了一次中文变成了鈥? 这就是终端链路还没解决时的标准症状。所以记得skill 让文件生成对了但终端能否正确显示还得靠代码页。4.3 测试二生成含中文字符串的 Python 脚本并运行第二个测试更接近日常让 codex 写一个deploy_check.py里面有几行print(部署成功)之类的中文输出。加了 skill 之后codex 在脚本开头自动给出了运行建议在终端先执行chcp 65001。我照做后运行输出正常不照做直接跑乱码又出现了。顺便提一个不错的做法如果脚本是你自己会长期用的可以在 Python 代码里直接固定输出编码import sys if sys.stdout.encoding and sys.stdout.encoding.lower() ! utf-8: sys.stdout.reconfigure(encodingutf-8)这样只要终端代码页是 65001输出就能稳定不乱。codex 在 skill 规则约束下会自动生成这类代码节省了我手动告诉它的时间。4.4 测试三对已有乱码文件执行修复最后一个测试是对之前那份乱码文档执行修复。我拿了一个已经变成锟斤拷的旧文件运行python fix_encoding.py 项目部署说明.md --to utf-8-bom脚本识别出文件原始内容是 UTF-8 字节被 GBK 解码后重新保存的情况于是先按 GBK 解码文本再以utf-8-sig编码写回。完成后重新打开内容恢复。不过要提醒的是这个场景能修复的前提是文件内容没有被替换字符完全破坏如果你在编辑器里手动保存过一次导致 UFFFD 真的写入文件那脚本也救不回来。所以我现在的习惯是发现乱码后立刻停手不要反复用不同编辑器打开保存直接交给脚本处理。5. 装完 skill 依旧乱码按这条链路逐级排查5.1 先分清文件乱和显示乱装了 skill 还乱大概率不是 skill 没生效而是你把问题类型判断错了。我这里总结一个字少事大的排查顺序按照这个顺序来基本十分钟定位用 VSCode 打开文件看右下角显示的编码是什么。执行重新打开以编码分别尝试UTF-8和GBK看哪种恢复正常。如果某一种编码下完全正常说明文件内容是完好的只是默认编码选错。此时不要动文件内容修改编辑器或系统的默认编码策略即可。如果两种编码都乱那要考虑内容是否真的损坏进入第 5.2 节。5.2 编辑器默认编码与自动检测的坑VSCode 默认files.encoding是 UTF-8这对大多数场景是好事但对从旧环境拿来的 GBK 文档反而不友好。如果你经常和 GBK 文件打交道可以在用户设置里加上files.encoding: utf8, files.autoGuessEncoding: trueautoGuessEncoding开启后VSCode 会尝试自动猜测文件编码GBK 中文文档能正常显示的概率大大提高。但注意自动猜测不是万无一失如果文件字节序列比较短比如只有一行中文猜测可能出错这时候手动选择编码是最可靠的。5.3 终端代码页、输出编码和重定向排查命令行场景时记住一个固定动作先执行chcp看看当前代码页。如果输出是Active code page: 65001那问题大概率不在终端而在程序的输出编码如果是936那就先执行chcp 65001再试。还要注意一个隐蔽场景重定向输出时乱码但屏幕上正常。命令codex generate output.log这种用法会把程序输出重定向到文件而此时终端的代码页可能不再对输出生效程序会按系统默认 ANSI 编码写入文件最后你在 VSCode 里打开 log 就乱。解决方式是让程序自己声明输出编码或者在命令前设置环境变量。例如在 PowerShell 里$env:PYTHONIOENCODING utf-8 python script.py output.log这个坑特别容易和文件生成链路混淆。我踩过一次之后现在凡是要重定向中文输出都会先想清楚写入端编码是什么而不是只看屏幕显示。5.4 接口请求异常导致的假乱码那些长得像乱码的报错信息最后一种乱码其实不是编码问题而是信息错乱。有时候 codex 请求服务时返回异常终端会吐出一大段调试信息里面夹杂着codex endpoint /responses、failed while handling之类的字样并带着一串长长的错误对象和转义字符。这段内容在终端里因为颜色转义和缩进问题中文字符会挤在一起看起来就像乱码但本质是请求异常。遇到这种情况别急着改编码。先看请求是否成功、返回的 HTTP 状态码是什么、报错关键字是什么。如果是接口服务或本地依赖服务没启动导致的异常中文文档写得再规范也没用因为问题发生在内容生成之前。我的一般顺序是先处理掉这类请求层报错再回头检查文档编码。不要把两类问题混在一起排查否则你会在错误的方向上浪费两小时。关于这个 skill 的后续扩展想法刚开始我只把chinese-doc-encoding当成防乱码工具用了一段时间发现它还能做更多事比如在生成文档时自动附加编码说明段落让团队里不熟悉编码的同事也知道这个文件为什么带 BOM、执行前为什么先切代码页。你也可以把fix_encoding.py扩展成批量扫描整个目录对文件名里带中文的 Markdown 文件提前做编码体检把潜在乱码扼杀在提交之前。我的体会是编码这种事靠人记不如靠规则管把这个场景固化成一个 skillcodex 每次生成文档时都自动把关Windows 下的中文文档工作流才算真正舒坦。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进