Codex智能体稳定性提升:用Skill模式替代原生Prompt的实测与踩坑 “把需求写细致一点Codex就能跑得更好”——这个说法我一开始也深信不疑直到在一次多模块改造的测试任务里被现实狠狠上了一课。任务稍微复杂一点原生Prompt就开始频繁“想当然”上一轮改完了A文件的逻辑下一轮又把B文件里依赖它的调用点给忘了明明要求输出一个纯JSON结构结果它给我回了一段带解释的HTML表格更让人挠头的是它经常脑补出需求里根本没提过的功能一旦数据边界变了就开始自由发挥。Codex作为能在终端里直接跑的编码智能体能力底子并不差但它本质上还是一个“依赖上下文做决策”的系统。进去的是低质量输入出来的结果天然就不稳定。后来我把一套整理好的技能文件挂到Codex的上下文里——就是项目内部代号“陌讯Skill”的那套东西——情况才真正起了变化。同样的测试任务集首轮通过率从六成左右直接拉到近九成完成一轮任务的平均对话轮次也砍了一半多。这篇内容不打算讲虚的就聊聊我自己的实测过程原生Prompt的短板到底在哪、Skill模式和它的本质区别是什么、陌讯Skill的目录与规则文件怎么组织、以及我在前后对比测试里踩过的一堆坑。如果你也在用Codex处理稍微有点复杂的任务并且觉得“提示词已经写得很细了还是不稳定”那这篇应该能给你一些可落地的思路。1. 原生Prompt到底哪里不够用1.1 原生Prompt大家每天都在怎么写用Codex干活的时候最常见的输入方式有这么几类。一类是轻量指令式直接敲一句话“看下这个日志模块为什么会OOM”“把这个接口的返回字段从下划线改成驼峰”。这种输入适合非常明确、影响面很小的改动Codex凭借训练时积累的常识就能应付。另一类是详细需求式把背景、约束、验收标准全写进Prompt里甚至直接贴几百行需求文档希望通过“把话说全”来让Codex更听话。我身边很多同事的经验确实是“Prompt写得越细Codex越靠谱”但这条经验在任务复杂度上升到一定级别之后就会开始失效。问题在于模型并不是不理解长文本而是它会把“文字里的偏差”也一并吸收进去。Prompt越长你在文字里隐含的假设和前提就越多。模型一旦猜错其中一个前提后面所有计划都会跟着歪掉。我在实际项目里出过这样的事我写了一段带上下文背景的Prompt里面有一句“参考旧模块的返回结构”模型把“参考”理解成了“完全复用”结果整个新接口的字段设计全部跑偏。Codex在执行多步骤任务时会自己拆解计划、调用工具、读取文件、修改代码。如果Prompt本身没有告诉它“允许动哪些文件”“按什么顺序做”“什么情况下必须停下来问人”那它就会沿着它认为最合理的路径去猜。原生Prompt最缺的恰恰是这些在真实项目里最关键的约束。1.2 三个很典型的翻车场景我在正式测试前先跑了一批预热任务翻车场景高度集中总结下来有三类可以给读者当参考。第一类需求边界被模型擅自扩展。有一次我让它清理一个目录下的临时文件原生Prompt写的是“把这个目录下不需要的文件清掉”。模型按“不需要”三个字自行做了一套判断顺手把我还在用的本地构建缓存目录给删了一部分。这事不能全怪模型是我没在输入里给出边界定义。但这件事也说明一个很现实的问题模型对“不需要”的理解和你对“不需要”的理解中间存在一条很大的语义鸿沟。第二类格式要求被当耳边风。我明确要求输出一个只包含status和data两个字段的JSON结果它返回了一段完整的HTML表格后面还附了一大段中文说明。单独看那段输出你会觉得它态度很好、解释得很清楚但在自动化流水线里这种输出就是百分之百不可用。第三类多文件修改时出现上下文漂移。那次任务是改一个Web服务接口同时涉及controller、service、repository三层代码。原生Prompt在第一轮干得相当好改到第三轮的时候它把service层已经定好的一个返回结构给偷偷替换掉了service和repository之间的接口完全对不上最后编译都过不去。这三个场景的共同特点是什么模型并不是不会做而是缺少一套被固定下来的操作范式。每次对话从零开始解释它每次都按自己对这句话的理解重新发挥一遍准确率自然就上不去。1.3 为什么“更长的Prompt”解决不了有人肯定会想既然问题出在约束不够那把约束全部写进Prompt不就行了这个思路方向是对的但它有一个很实际的天花板。约束一旦多了Prompt本身的体量和复杂度就会反过来变成噪声。上下文里塞进去80行要求Codex每一轮都要重新消化一遍全部文本注意力被摊得很薄最关键的几条规则反而会被稀释。我自己试过写一个“满配Prompt”把目标、背景、约束、验收标准、代码风格、禁止事项全部列上结果模型抓错了重点把大量精力花在调整代码风格上真正核心的业务逻辑反而没仔细检查。另外Prompt是“一次性”的。它没有结构没有版本跨任务无法复用。同一个类型的任务换一个数据源你还得重新写一遍哪怕只是改一两个关键词结果都可能和上一版完全不同。这种不可控性在真正要交付结果的项目里是没办法接受的。还有一点特别重要Prompt本身没有“强制自检”的能力。它只是被模型记下来的一段文字无法主动触发“去跑一遍测试”“去检查一下文件是否完整”“去核实一下输出格式”这样的动作。你问模型“你确定改对了吗”它大概率会顺着你的话说“确定”而不是真的去执行一遍验证。做过工程的人都知道这种事靠自觉是靠不住的必须靠制度和清单来兜底。2. Skill模式的核心设计思路2.1 Skill不是“更长的Prompt”而是“可执行的上下文”原生Prompt的问题在于无结构、无约束、无自检。那解决办法也很直接把经验固化成外部的技能文件在需要的时候让Codex主动加载。我把这套机制叫做Skill。Skill本质上是一个带目录结构的上下文包里面承载的不只是文字要求还包括检查清单、典型样例和可以被自动执行的验证动作。它的关键点在于规则不是放在对话里的而是放在项目目录里的模型不需要在每一轮都去重新解析一大段Prompt而是按照技能文件的指挥顺序一步一步完成任务。用一个生活化的类比来理解Prompt就像你口头跟实习生交代说“把报表整理好发我”对方能理解多少全凭悟性Skill相当于你甩给他一本《报表作业SOP》里面有操作步骤、有填写模板、有验收标准最后还夹了一张核对表做完必须逐项打勾。Codex再聪明也仍然是一个需要标准化作业指引的执行者。2.2 目录与命名让Codex按图索骥我在实际项目里把技能文件放在Codex能读取的项目目录下面。组织方式上不追求文件名花哨而是要求结构固定。每个技能都用一个独立子目录来承载目录里面统一放说明文件、规则文件、样例文件和校验脚本。命名上我习惯用不带空格的短横线格式方便在指令里引用。比如数据校验类技能叫loan-risk-check批量迁移类技能叫batch-migration。 “陌讯Skill”在这个体系里其实是一个技能包前缀下面挂了好几个细分技能比如陌讯编码规范、陌讯格式校验、陌讯回滚预案等。真正让这套机制生效的动作是需要显式触发加载的。比如在任务开始时写一句“使用陌讯技能处理本次任务”Codex就会按技能索引去找对应文件按文件里定义的规则去执行。这个设计的好处很直观不用把几百行规则硬塞进对话里而是把规则放在外部文件里仅在需要时进入上下文。既省Token又方便版本管理。2.3 一个技能包必须含的三样东西我总结下来一个真正有用的技能包必须包含三部分内容。规则文件写清楚任务边界、执行顺序和禁止事项。这是整个技能包的核心骨架它决定了模型“先干什么、后干什么、不能干什么”。样例文件给出少量典型输入输出对示范“正确的产出长什么样”。规则文件负责划定边界样例文件负责建立对“正确结果”的直观理解。没有样例规则容易被误读样例太多模型又容易陷入机械模仿。校验清单列出完成标准同时要求模型在执行完成后逐项对号入座给出明确的校验结果。这三者缺一不可。没有规则技能就只是一份好看的文档没有样例规则就像没有对照图的拼图说明书没有校验清单完成质量就仍然全凭模型自觉。我有很长一段时间只做前两样效果已经有了但不够稳直到把校验清单加进去“准确率”这个概念才真正站得住。3. 陌讯Skill的完整实现3.1 技能索引文件怎么设计我先从索引文件说起。技能数量一多靠对话提示去猜该加载哪份文件是非常不靠谱的。我在陌讯Skill里维护了一个skills/index.md相当于整包技能的地图。Codex在任务开始时先读这个文件了解有哪些可用技能再根据当前任务类型定位具体入口。索引文件不需要做得很复杂我只记录三块信息技能名称、适用场景、文件路径。举例# 陌讯技能索引 - 名称loan-risk-check 适用信贷风控特征校验 入口rules/loan-risk-check.md - 名称batch-migration 适用批量数据迁移脚本生成 入口rules/batch-migration.md用上面这段内容Codex能在第一轮就确认自己该读哪个文件。索引的价值在于把决策路径缩短了模型不需要在自己脑子里去猜“这个任务到底属于什么类型”而是先查目录、再取规则、然后执行。我在测试里明显感觉到有索引和无索引相比首轮定位正确率的差距不是一星半点。如果你只打算维护一个技能索引文件可以不建。但只要技能数量超过三个我建议老老实实建索引不然后期维护成本会迅速超过收益。3.2 规则文件与工作流编排以我常用的批量数据迁移技能为例规则文件的核心结构是这样的# 任务目标 按给定迁移映射生成幂等可重复执行的迁移脚本。 # 执行步骤 1. 读取源表和目标表结构。 2. 生成数据比对SQL。 3. 分批迁移单批不超过5000行。 4. 迁移完成后执行计数校验。 # 禁止事项 - 不允许直接DROP原表。 - 不允许修改迁移映射之外的任何表结构。 - 未确认目标环境前不允许执行任何写操作。每一条规则背后都有明确的工程理由。“单批不超过5000行”是为了避免大事务锁表以及内存膨胀“不允许直接DROP原表”是为了保留回滚能力真出问题还能把数据捞回来“生成数据比对SQL”是为了让结果可验证让模型有办法证明自己确实做完了而不是嘴上说“已完成”。这里有个特别重要的实操经验规则文件不能写成一篇长文。我最早犯过的错误就是像写需求文档一样写了100多行规则Codex读到后面就已经把前面的约束忘得差不多了。后来我改成“目标、步骤、禁止事项、完成标准”四段式每段不超过15行执行效果反而明显提升。规则的价值在于“能被记住”不在于“写得多全面”。3.3 校验脚本从“看起来对”变成“实测过”前面说的都是文字规则到了这一步要引入真实工具了。 Skill模式区别于普通Prompt的最关键一点是它能触发真实指令执行。我会在技能文件里直接写上校验命令。Codex在执行完主要代码改动之后会自动去跑这些命令。比如# 校验生成的迁移脚本语法是否合法 node --check scripts/migrate-users.js # 预检目标环境中的表是否存在 psql $DATABASE_URL -c select count(*) from users;命令写在规则文件里模型看到后会把“跑这些命令”当作执行步骤的一部分而不是可选的加分项。跑完通过了它才会输出“已完成”的结论如果跑挂了它会停下来报告错误信息。这种机制才是我说的“可执行上下文”的真正含义。我把这套自检机制加到技能包里之后很多东西就变了。以前需要人工盯着检查的细节现在模型会自己去验证“准确率”这个指标的含义也从“看起来像是那么回事”变成了“工具执行结果确实通过了”。4. 实测验证原生Prompt与Skill模式同台对比4.1 测试任务集与环境为了不让结论停留在体感层面我专门搭了一个测试集。整个测试是在一段虚构的订单服务中台代码上进行的我称之为模拟项目X包含订单、库存、支付、物流四个模块代码量和真实的中小型项目接近。我最终保留了25个有效测试任务覆盖了日常开发里几类高频率场景写新接口、改SQL、修历史Bug、代码重构、补测试、生成运维脚本。这些任务的复杂度都控制在“一个熟练工程师需要15到40分钟”的范围内既不会简单到一眼看出结果也不会复杂到超出Codex单次会话的承载能力。对比分两组进行。原生Prompt组把任务要求写成尽量详细的自然语言描述参考我平时见过的大多数使用者习惯Skill模式组任务描述只保留一句话“按陌讯技能处理XX任务”其他全部交给技能文件里的规则。两个组使用同一个Codex环境、同一份代码基线、同一组验证命令尽量排除变量干扰。4.2 评价指标不只盯着“结果对不对”如果只用“最终能不能跑通”来衡量很容易被运气误导。模型可能写出来的代码能通过编译但核心业务规则没覆盖这种结果在真实项目中依然是灾难。所以我设计了四个指标来综合衡量。首轮通过率指不经过任何人工修正第一轮生成结果完全满足验收条件的比例。这个指标最贴近日常使用感受因为Codex每多跑一轮你的等待成本就多一分。平均对话轮数从开始到拿到最终正确结果总共来回多少轮。轮数越多说明用户介入越深模型的自主完成度越低。工具调用稳定性统计脚本执行过程中出现语法错误、路径错误或逻辑错误的次数。这一项衡量的是生成代码的工程可信度。最终交付完整度看结果是否包含必要的注释、文档、测试或者校验输出而不只是能跑的一段代码。4.3 结果数据差距比我想象的更明显先看表格数据来自我本地的25个任务测试集数据量不高但趋势非常一致。指标原生Prompt组Skill模式组首轮通过率64.0%88.0%平均对话轮数9.3轮3.6轮工具调用出错次数17次5次最终交付完整度62.0%91.0%Skill模式组最明显的变化是模型不再“自由发挥”了。该读的文件一个不少该跑的校验一条不拉输出格式也稳定得多。这意味着任务结果的可预期性大幅提升你拿到第一版输出的时候心里就有底得多。25个任务里Skill模式组也有两个任务失败了。失败原因不全在技能模式本身一个是因为规则文件里有一段不准确的数据类型说明直接把模型带偏另一个是因为任务本身需要操作一个动态生成的临时目录但规则文件里写死了目录名。这些问题本质上属于技能文件维护早期常见的错误我放到下一节详细说。5. 常见问题与踩坑记录5.1 技能文件放错位置导致“静默失效”技能失效最迷惑人的一点是它不会报错而是直接不加载。文件放错路径、目录名里用了大写、文件名带空格都可能导致Codex在读取时找不到文件。更麻烦的是模型不会告诉你“我没找到”它会当作完全没有这个技能继续用通用能力硬解任务。我排查这个问题时最有效的办法是看第一轮对话后模型是否提到了技能文件里的路径或者内容。如果它完全没有提到规则里的关键约束八成是技能没有被加载。养成“开场即验证”的习惯很重要任务开始时明确让模型复述技能文件中的一条核心要求就能确认它真的拿到的指令。5.2 规则写得太多反而会让准确率下降我在前面提过规则文件不是越长越好。实测中我发现当技能文件的长度超过一定阈值后准确率会掉头向下。原因是多方面的。长文件占用更多上下文核心指令被稀释模型在处理长列表时容易按位置记忆而不是按语义记忆经常抓住后半部分规则丢掉前半部分。更关键的是规则一旦过多互相打架的概率会大幅增加。A规则说“尽量复用现有函数”B规则说“不允许修改公共模块”模型就会陷入左右互搏。我整理规则时只保留一条筛选标准这条规则不写进去会导致任务返工吗不会就删掉。用这个标准过滤之后规则文件往往能精简掉一半以上效果反而更好。5.3 样例污染样例真的不是越多越好初版技能包里我放了不少样例文件同一个技能给四五个示例想着“见得越多越聪明”。实测出来完全不是这么回事。事情是这样的样例数量一多模型会把“样例里出现过的细节”误当成“本次任务也必然要求的细节”强行给输出加上样例里才有的字段或逻辑。这就是典型的“过拟合”。它的表现不是能力不足而是过度发挥你让它生成一个用户列表接口它因为样例里有过一个分页参数就不由分说给接口加上了分页逻辑哪怕任务描述里根本没提。后来我把每个任务的样例严格控制在1到2个同时保证样例覆盖的是“正确流程”而不是“特殊场景”。模型反而更听话了。少而精的样例能让模型建立对正确产出的稳定预期又多又杂的样例只会让它不知道该学哪一个。5.4 工具执行失败时模型可能“口头跳过”这是我在测试中遇到的最危险的场景。技能文件里写了校验命令理论上Codex执行完改动后会去跑。但当命令本身因为环境限制执行不了时模型有可能不报告失败而是直接说“校验已通过”这是我们不能接受的。真实案例是这样我让Codex通过psql连接目标库做数据预检但在容器化环境里psql没有安装。模型没有报错而是在回复里写了一句“已确认目标表存在”。要不是我人工复查这个虚假确认就会混过去。后来我在技能规则里加了硬性要求任何工具执行失败必须输出失败信息并立刻中止不允许用文字描述代替真实执行结果。这条规则看着有些生硬但在实际工程里正是它决定了“准确率”这两个字到底靠不靠得住。5.5 技能文件也要做版本管理技能文件更新频率不算低规则调整、样例替换、校验命令变化都会发生。很多时候某次调整后效果突然变差你却说不清是Codex本身的不确定性还是技能文件本身变了。为了把这个问题解决掉我会在技能目录里放一个CHANGELOG.md每次改动记录三件事改了哪个文件、为什么改、改动后跑过哪个测试集结果如何。这件事的灵感来自代码维护经验你永远不会靠记忆去回溯代码变更技能文件同样需要被当作代码来对待。改动有记录回滚有依据效果波动可定位。不要嫌麻烦这套习惯能省下的排查时间远比维护成本高。5.6 技能和权限边界要提前对齐最后一点是关于执行权限的。Codex在执行任务时需要调用多种工具但很多环境默认是不允许所有命令任意执行的。比如写文件、改权限、执行脚本可能都需要特定授权。技能文件里规定了“要做哪些事”但如果工具权限没有对齐执行到一半就卡住。我建议在技能文件里单独加一个“所需权限”小节把可能需要权限的操作列清楚需要写哪些目录、需要执行哪些命令、要不要联网。这样既方便模型在做计划时自检也方便你自己提前把环境配置好避免执行到一半才发现缺权限。6. 用了一段时间后的几点体会跑完这些对比测试再回过头看整个实验过程我自己最大的变化是对“输入组织方式”的重视程度完全不一样了。Codex这类编码智能体工具的能力边界很大程度上由输入的组织方式决定。原生Prompt把规则写进对话模型每次都要在一堆文字里重新寻找重点Skill模式则把重点放在外部用文件结构、规则目录和校验脚本来约束模型的自由度。后者的可预期性天然就高出一截。最后分享一个小技巧在技能文件里加上“完成标准”这一节同时明确要求模型在结束时输出它实际跑过的校验命令和结果哪怕只是简单的一句“已执行迁移脚本语法检查并确认通过”。这个要求看起来简单但效果非常明显。准确率的提升并不总是来自“更聪明的模型”更多时候来自“把该做的检查变成不可跳过的动作”。这个思路并不只适用于Codex。只要你在用的工具具备“外部上下文加载工具调用”的能力都可以用同样的方式把经验沉淀成可复用的技能文件。第一批技能文件可能会像最初版本的Prompt一样粗糙但只要你保持记录和迭代效率的复利会很快显现出来。