1. 乱码问题是怎么开始的1.1 问题现场Codex写文档文档“花”了Windows下用Codex编写文档时遇到中文乱码说句实话这是我在实际使用中最先踩到、也最让人心烦的一个问题。刚开始用Codex生成项目README时一切都很顺利但打开生成的 Markdown 文件却发现中文全部变成了一串不可读的符号终端里显示的是乱码用记事本打开又是另一种乱码。如果你也经历过这种场面应该能明白那种“明明代码没写错文档却没法看”的挫败感。这个问题的本质并不玄学。Windows简体中文环境的默认编码通常是GBK代码页936而Codex这类现代AI编程工具包括它生成的文档、代码和终端输出默认走的是UTF-8。两边的字节解码方式完全不同相当于同一篇中文内容被人用两把不匹配的钥匙来回开锁开错一次内容就变成了一堆“符”。如果只是偶尔一次手动把文件另存为UTF-8就能解决但现实情况是Codex会反复读取项目中的文件、增量修改文档、在终端输出内容任何一个环节的编码不一致乱码就会再次出现让人感觉像在打地鼠。后来我决定不再做“被动修复”而是把整套解决方案固化成一份可复用的“技能包”也就是很多人说的skill。这个skill要解决的问题非常明确在Windows环境下让Codex从一开始就按照正确的编码规范去工作——读取文件时识别编码、生成文件时统一UTF-8、写完文档后自动自查并在遇到已经乱码的文件时给出可执行的修复方案。1.2 为什么要用skill来解决而不是靠手动改有人可能会问直接写个Python脚本不就能批量转换文件编码了吗为什么要绕一圈去做成skill这个问题我当时也认真想过。脚本确实能解决“文件已经是乱码”的情况但它解决不了“Codex下一次生成文档时又乱码”的情况。乱码的本质是环境、工具和内容三方之间没有约定好统一的编码规则而skill的价值就在于把规则、行动步骤和工具脚本打包在一起让AI在写文档之前就先“看一眼环境”再按约定去工作。我用的是社区里比较流行的skill组织方式在Codex配置目录下放一个独立的skill文件夹里面包含一个SKILL.md主文件用来描述这个skill的触发条件和执行步骤再配一个scripts子目录存放编码检测与修复脚本。这样当Codex在Windows下处理中文文档时它就是一套给AI看的“操作手册”同时也是一份给人类维护者看的“项目资料”。你可以把它理解成给新同事做的入职指引不但告诉他“遇到乱码怎么办”还告诉他“怎么从源头不要制造乱码”。这套方案适合谁如果你跟我一样在Windows上使用Codex写Markdown、生成技术文档、写Python代码或者维护一个中英文混排的项目那这个skill几乎可以无缝拿过去用。如果你只是偶尔遇到一次乱码也可以只取其中的检查命令和转换脚本不必整套引入。但从长期体验来看做成skill绝对比每次手动处理要值。2. 摆脱乱码前先弄懂Windows的编码体系2.1 代码页、ANSI与UTF-8乱码的三层来源在Windows上处理中文乱码绕不开三个基础概念代码页、ANSI和UTF-8。代码页是Windows用来映射字符与字节的规则简体中文系统用的默认代码页是936对应的编码叫GBK。GBK是一种变长编码一个汉字占两个字节和UTF-8里一个汉字占三四个字节的规则完全不同。当一段GBK字节被当作UTF-8解码时原本成对出现的字节被拆散解码器找不到对应字符就会吐出一堆“”或者更奇怪的符号。UTF-8这边还有两个变体带BOM和不带BOM。BOM是文件开头的几个特殊字节用来告诉编辑器“我是UTF-8”。旧版Windows记事本特别依赖BOM来识别UTF-8没有BOM时它会默认按ANSI也就是GBK读取这就造成了一个常见怪相文件本身没问题打开方式出了问题。而新版Windows Terminal、VSCode、Git这些工具基本都默认按UTF-8处理BOM反而可能带来额外干扰。所以乱码说起来是编码问题实际上是三层错位叠加的结果文件保存时用的编码、打开文件时猜测的编码、终端显示时使用的代码页只要有一层对不上最终展示出来的就是乱码。编码名称代码页/典型场景常见坑GBK936简体中文Windows的ANSI默认被UTF-8工具读取时中文乱码UTF-8无BOM65001现代编辑器、Git、Codex旧版记事本会误认为ANSIUTF-8带BOM65001便于旧软件识别在Linux/Git里可能引入多余字节UTF-161200PowerShell重定向输出普通文本工具读取困难2.2 从终端到文件编码在传递过程中的错位我现在把乱码问题分成了两个战场一个是“文件里的乱码”另一个是“终端里的乱码”。这两个战场的源头不一样但经常同时出现很容易把人绕晕。文件里的乱码主要发生在Codex读取或写入文件时。Codex本身生成内容时用的是UTF-8如果项目里某个旧文件是GBK保存的Codex读取后就会把内容里的中文字符理解成其他语言再往下写的时候就等于把错误内容重新编码一遍乱码就这样被“复写”进新文件。终端里的乱码则更多是控制台代码页和程序输出编码不一致导致的。Windows下老的终端默认代码页可能是936而Codex输出中文时按UTF-8来终端拿到一串UTF-8字节却用GBK去显示结果必然是乱码。比较新的Windows Terminal在多数情况下能自动处理但仍受系统区域设置影响。这也是为什么在skill里我坚持加“终端编码检查”这一条原因。检查终端编码有很多种方式最朴素的是在PowerShell里运行chcp命令它会返回当前控制台代码页。如果是936就明确告诉Codex“当前是简体中文GBK环境”再根据场景决定要不要临时切换到65001。注意chcp 65001只改变当前命令行窗口的代码页它不会修改文件内容也不会影响系统全局设置。所以它适合作为临时手段不适合硬塞给用户。3. 给Codex设计skill整体思路与结构3.1 skill到底是什么以及它怎么生效如果说Codex本身是一个经验丰富的工程师那么skill就是一份“针对特定场景的作业指导书”。它不是传统意义上的插件不会接管Codex的内部运行机制而是通过清晰的文字指令和可执行脚本引导Codex在特定条件下按预设步骤行动。我发现很多人容易把skill和普通提示词混为一谈实际上还是有区别的提示词是当时随口说的一句话而skill是一个可复用的、有结构、有工具的完整包它可以在多次会话里反复使用。在Codex场景下skill通常是放在项目或用户配置目录里的一组文件核心是SKILL.md。每当Codex收到与skill描述相关的任务时就会读取这个文件把里面的规则当作行动准则。比如我这套“修复中文乱码”的skill描述字段里写明了“当在Windows环境下生成或编辑中文文档时请先检查编码环境”Codex看到这个触发条件就会在执行任务前主动加载对应的规则。这比每次手动提醒AI“注意编码”要可靠得多相当于把经验固化成了流程。3.2 skill的目录结构和核心文件我用的skill目录结构并不复杂核心只有三层.codex/skills/fix-chinese-encoding/ ├── SKILL.md ├── scripts/ │ └── fix_encoding.py └── examples/ └── settings_recommendations.jsonSKILL.md是灵魂它的功能是给Codex完整的行为指引。文件开头有一段元信息包括名称和触发描述下面我贴一个简化版本--- name: fix-chinese-encoding description: 在Windows下处理中文乱码当生成、编辑或读取中文文档时自动检查并统一使用UTF-8编码。 --- ## 执行步骤 1. 运行 chcp 检查当前控制台代码页。 2. 若目标文档存在非UTF-8编码使用 scripts/fix_encoding.py 检测并转换。 3. 所有新生成的文档统一保存为 UTF-8 无 BOM。 4. 转换前先备份原文件避免不可逆损坏。scripts目录下放的是真正干活的工具脚本。examples目录则放一些推荐配置样例比如VSCode的settings.json推荐写法、EditorConfig配置等方便其他人直接参考。把目录结构设计成这个样子还有一层考虑如果以后这个项目要被别的AI工具复用只需要把SKILL.md的首部描述稍微改一下工具脚本完全可以通用。因为编码治理这件事本质上跟用哪个AI工具没有关系它解决的是操作系统和内容规范之间的冲突。4. 实操建立中文乱码修复skill的内容4.1 第一步环境检查让Codex先看“天时”我在写SKILL.md时第一条规则就是环境检查。不管Codex要生成文档还是修改已有的中文内容先花三秒钟确认当前Windows环境的编码状态比事后发现问题再返工要高效得多。我在skill里给Codex预设了这样一组巡检命令chcp # 查看当前控制台代码页 [Console]::InputEncoding [Console]::OutputEncoding $OutputEncoding # PowerShell用于外部程序交互的编码先说chcp这个命令在传统cmd窗口和新版Windows Terminal里都能用。返回936说明当前是GBK环境返回65001说明在UTF-8模式。我要提醒的是chcp 65001这类切换命令并非万能它不能改变PowerShell内部变量也不能强制所有控制台程序乖乖输出UTF-8。所以技能里不只让Codex执行命令还让它把检查结果写进日志供后续判断使用。$OutputEncoding在Windows PowerShell 5.1下有个坑它默认不是UTF-8这会导致外部程序接收到的中文参数或输出内容出问题。如果你在skill里让Codex调用Python脚本处理中文字符串建议在脚本开头先设置环境变量$env:PYTHONUTF8 1 $OutputEncoding [System.Text.UTF8Encoding]::new() [Console]::OutputEncoding [System.Text.UTF8Encoding]::new()这样做的目的是让Codex在执行子进程时从“上级”到“下级”都能走UTF-8通道。很多乱码查到最后并不是文件编码问题而是PowerShell传给子进程的参数直接变成了问号。4.2 第二步写文件统一规则从源头规避乱码这套skill的核心规则只有一条所有新生成的文档统一保存为UTF-8无BOM。为什么强烈建议无BOM因为大多数现代工具链都能直接识别无BOM的UTF-8Git、Python、JavaScript、VSCode都没有问题。BOM虽然对旧版记事本友好但在部分Linux工具、shell脚本和一些解析器里会出现意外报错属于得不偿失。在SKILL.md里我还给Codex加了几条更具体的“书写规范”写Markdown、纯文本、配置文件时显式使用UTF-8编码创建文件。写Python文件时如果不确定运行环境可以在文件头部保留# -*- coding: utf-8 -*-注释虽然Python 3默认UTF-8已经不需要这个声明但它能提醒其他维护者保持编码一致。如果遇到需要兼容老旧软件的场景不要直接“全盘GBK”而是先问清楚到底哪个软件不兼容再决定是否单独生成一份带BOM的副本。有一个常见误区是把“统一成UTF-8”理解成“禁止GBK”。不是这样。项目里如果已经有一批老文件是GBK编码直接强行全部转成UTF-8可能会让历史版本管理变混乱因为改动记录会被大量编码转换“刷屏”。所以我给skill定的策略是新文件一律UTF-8老文件在确认内容完整后可批量转换转换前必须备份。4.3 第三步把已有乱码文件批量修复环境检查解决的是“避免未来乱码”但要处理手头已经乱码的文件还是得上工具。我在skill的scripts目录里放了一个轻量Python脚本核心是对文件做编码探测并转换成指定编码。下面是我简化后的实现import argparse from pathlib import Path def guess_encoding(raw: bytes): if raw.startswith(b\xef\xbb\xbf): return utf-8-sig for enc in (utf-8, gbk, big5, shift_jis): try: raw.decode(enc) return enc except UnicodeDecodeError: continue return None def main(): parser argparse.ArgumentParser(description检测并转换文本文件编码) parser.add_argument(path, nargs, help文件或目录路径) parser.add_argument(--target, defaultutf-8, help目标编码默认utf-8) parser.add_argument(--backup, actionstore_true, help转换前生成.bak备份) args parser.parse_args() for p in args.path: p Path(p) if p.is_dir(): files list(p.rglob(*.txt)) list(p.rglob(*.md)) else: files [p] for f in files: raw f.read_bytes() enc guess_encoding(raw) if enc is None: print(f[跳过] {f}: 无法判断编码) continue if enc args.target: print(f[正常] {f}: 已经是 {enc}) continue if enc utf-8-sig and args.target utf-8: text raw.decode(utf-8-sig) else: text raw.decode(enc) if args.backup: f.write_bytes(raw) f.rename(f.with_suffix(f.suffix .bak)) f.write_text(text, encodingargs.target, errorsstrict) print(f[修复] {f}: {enc} - {args.target}) if __name__ __main__: main()这个脚本的思路是“先探测再转换”。探测顺序很重要先用BOM判断再用UTF-8严格解码最后用GBK尝试。为什么UTF-8判断放在GBK前面因为UTF-8有严格的字节结构约束一段合法的UTF-8几乎不可能是合法的GBK反过来却会因为GBK的宽松特性产生误判。脚本里还加了--backup参数转换前先原名备份一份强烈建议日常使用都带上这个参数。这里我自己踩过一个大坑有一批文件表面上是GBK乱码实际是文件内容在更早的时候就已损坏原文的部分字节已经被替换成了问号。这种文件靠编码转换是救不回来的。所以脚本里探测不到编码时会直接跳过而不是用errorsreplace强行替换字符。乱码修复这事能救多少是多少没必要创造新的坏数据。4.4 怎么引导Codex调用这个skillSKILL.md写好了脚本也放进去了接下来关键一步是怎么让Codex在合适的时候使用它。我的做法分成两种触发方式。第一种是自动触发。在SKILL.md的描述字段里明确写到“当任务是编写或修改中文文档且系统为Windows时请加载本技能”。Codex在拿到任务时会先判断任务特征一旦匹配上就自动加载技能规则。这种方式的效率最高也是我最终推荐的方式。第二种是手动触发。如果遇到当前任务没有自动匹配的情况就直接在对话里说明“调用中文乱码修复skill检查这个目录下的所有Markdown文件编码”。我建议在手动触发之后让Codex先输出一份环境报告再给出修复计划不要直接上手改文件。AI工具有时候太勤快一上来就批量改完结果用户根本没来得及备份这种事故我见过多次。所以在SKILL.md里我特意加了一条“执行修改前必须列出将受影响文件清单等待确认”。5. 在Windows环境下的测试与踩坑记录5.1 设计整体测试用例skill要落地不能光靠理论。我专门搭了一个测试目录模拟日常工作中最典型的三种文件状态demo/ ├── plain_utf8.md # 正常UTF-8无BOM文件 ├── utf8_bom.md # UTF-8带BOM文件 ├── gbk_note.md # GBK编码老文件 └── broken_note.md # 内容损坏的伪乱码文件plain_utf8.md用来验证脚本不会做无谓的转换utf8_bom.md用来验证脚本能把BOM去掉gbk_note.md用来验证核心转换能力broken_note.md则用来验证脚本遇到无法探测的损坏文件时能优雅地跳过而不是破坏文件内容。每个文件里都放了几句中文和英文混杂的文本这样能直观看到转换前后的差异。测试环境我特意选了一台区域设置还是“中文简体中国”的Windows机器让chcp返回936。这非常关键因为很多乱码只在936环境下才会出现如果在干净的国际版环境测试根本复现不了。5.2 跑一遍修复流程看到乱码变正常让我用实测记录来说明整个修复流程。在测试目录上运行脚本输出类似下边这样[正常] demo/plain_utf8.md: 已经是 utf-8 [修复] demo/utf8_bom.md: utf-8-sig - utf-8 [修复] demo/gbk_note.md: gbk - utf-8 [跳过] demo/broken_note.md: 无法判断编码第一份文件正常跳过说明脚本没把UTF-8无BOM文件当成GBK误转这符合预期。第二份文件去掉了BOM修复后我再用十六进制工具查看文件头已经看不到 EF BB BF 这几个字节。第三份文件是关键它原先用GBK保存转换后再用VSCode打开中文完全正常。第四份文件被跳过证明它的内容确实已经损坏不是单纯编码问题。在Codex侧的验证也走了一遍。我直接让Codex按照skill里的规则做一次“编码审计”它先执行了chcp告诉我当前代码页是936随后逐文件扫描编码状态最后生成一份简短的报告。整个过程中没有出现乱码复写的情况因为Codex在读取GBK文件时先做了识别再按UTF-8写入。这就是把规则前置的好处它不会想当然地把所有内容都按UTF-8读出来再原样写回去。5.3 踩坑记录chcp 65001不是万能的写这个skill的过程中我自己就踩了好几个坑这里挑三个最典型的说。第一个坑是被chcp 65001误导。有段时间我以为只要把控制台代码页切到65001所有终端输出就不会乱码。实际上很多老程序在初始化时已经读取了当时的代码页并且写死在自己的缓冲区里你后面再怎么chcp它照样输出乱码。更离谱的是在某些Windows PowerShell 5.1的版本里chcp 65001反而会引发奇怪的交互问题比如输出对齐异常。所以现在的skill设计里我不会拿chcp 65001当唯一解药只在排查时作为快速验证手段。第二个坑是PowerShell的重定向输出编码。我在测试时曾经把Codex的输出直接重定向到文件结果发现生成的文件是UTF-16编码而Codex自己读这个文件时又按UTF-8读导出的日志直接乱码。后来我在skill里明确约定涉及输出到文件的操作不要用重定向而是让Codex直接通过Python脚本写文件并且显式指定UTF-8。第三个坑是修复脚本的“过度转换”。最初版本的脚本在探测到GBK编码后会立刻执行转换但没考虑过原文件可能是某类特殊配置文件转换后反而破坏了程序对它的识别。后来我给脚本增加了扩展名白名单默认只处理 .md、.txt、.py、.json 这些常见文本格式其他扩展名需要人工确认后再处理。宁可做得保守一点也不要因为一个批量操作把用户的文档链搞坏。6. 常见问题与经验避坑指南6.1 常见乱码场景排查速查表这套skill做得差不多了以后我顺手整理了一份乱码排查速查表覆盖了我在Windows下遇到过的几个高频场景。给读者做一个快速对照能省不少排查时间。症状可能原因处理建议VSCode终端中文输出乱码终端代码页或输出编码不匹配设置系统区域或调低files.encoding试运行chcp 65001Codex生成的Markdown中文乱码文件被存成了GBK或读取时编码判断错用 skill 里的 fix_encoding.py 转换文件到UTF-8记事本打开UTF-8文件乱码新文件无BOM记事本按ANSI读取保留BOM保存或升级系统开启Beta版UTF-8git diff 显示中文乱码Git默认按字节显示非ASCII路径设置git config core.quotepath falsePython控制台输出中文乱码Windows控制台代码页与UTF-8不一致设置PYTHONUTF81或切换代码页到65001旧项目里的中文注释乱码编译器/编辑器用不同编码读取源码先按GBK读取再整体迁到UTF-8无BOM这张表里最让我意外的其实是记事本。很多人以为Windows自带的记事本永远不会乱码结果恰恰相反在旧版本和高版本的系统区域设置不一致时无BOM的UTF-8文件被记事本打开后一样满天飞字。解决办法也很简单如果项目非要兼容这种环境就单独生成一份带BOM的副本底层规范仍然保持UTF-8无BOM。6.2 独门经验怎么让乱码“根本不来”做完了所有补救工作我再分享几个把乱码“扼杀在摇篮里”的习惯这些是我在长期WindowsCodex工作流里验证有效的。第一VSCode的files.encoding一定要设成utf8files.autoGuessEncoding建议打开。前者保证VSCode新建文件默认UTF-8后者让旧文件即使没有BOM也能被自动识别。这两个设置值看似不起眼但能解决很大一部分“为什么我打开别人电脑上的文件就乱码”的困惑。第二在项目根目录放一份.editorconfig把编码规则固化成代码库约定。下面是个常用模板root true [*] charset utf-8 end_of_line lf insert_final_newline trueEditorConfig这种文件的好处是即使不用Codex其他协作者或AI工具打开项目时也能看到统一的编码规则减少人与人、机器与机器之间的默认值差异。第三Windows系统里的“Beta版使用Unicode UTF-8提供全球语言支持”选项这个区域设置打开后系统底层ANSI编码会切换成UTF-8能极大减少乱码。只在全新或可重装的环境里推荐因为它会影响老软件对中文资源文件的读取曾经把一些旧版中文软件搞出一堆乱码。第四别把“乱码修复”和“内容恢复”划等号。我在skill里反复强调检测环节目的就是不让AI工具遇到任何看似乱码的文本都盲目转码。正确的做法是先判断原文件到底属于哪种编码再判断它是不是已经不可逆损坏了。对于真正损坏的文件最好的处理方式是提醒用户回到版本库找回历史版本而不是用“猜”的方式去改写内容。7. 写在最后的个人建议7.1 我给skill做了哪些减法这套技能包用了一段时间后我反而删掉了不少最初设计的东西。最开始我在SKILL.md里塞了大量规则从“所有文件名不能有空格”到“目录深度不能超过三层”结果Codex每次加载时都被这些无关信息干扰反而容易忽略真正重要的编码规则。后来我把SKILL.md精简成“一次检查、一条主规则、一个修复脚本”效果反而好很多。Codex毕竟是执行任务不是背教材。给它的指令越精炼它执行得越准确。如果你也想做类似的skill我的建议是先从一个具体问题切入而不是一次解决所有疑难杂症。比如这版的skill只解决Windows下的中文乱码那我就不去管Linux环境的编码问题也不去管PDF导出乱码问题。一个问题一个技能用够了再合并这是维护技能包时最省心的路径。7.2 后续还可以扩展的点就这个乱码修复场景我个人觉得还可以往后延伸两步。一步是引入“分级修复”模式比如只检测不修复、只修复无BOM问题、全量修复并生成报告这样适合不同风险偏好的用户。另一步是给转换脚本增加“乱码内容摘要”功能通过统计替换字符的位置和比例判断哪些文件可能是真损坏哪些只是编码误判让AI工具在报告里直接给出处理建议而不是等着人去看一堆十六进制。说句实话Windows下中文乱码这个问题完全消除很难因为它跟操作系统的历史包袱、第三方软件的编码习惯、甚至用户的区域设置都绑在一起。但只要把规则前置、工具齐备乱码出现的频率就能降到你几乎感觉不到的程度。我在实际使用中的体会是编码问题的核心并非“找到一个万能工具”而是“让所有参与者统一标准”。这个skill所做的事本质上就是在给AI、编辑器和终端建一个共同的约定。
