让大模型写代码,多半会遇到这几种情况:同一段需求问两次结果不一样、生成的函数缺边界处理、改一处又把别处改坏、换台机器或换个接口跑出来行为又变了。很多时候不是模型不行,而是提示结构和采样参数没管住。这篇按可执行的顺序,把提示模板、参数组合、输出约束和回归校验拆开讲,并说明哪些做法在什么场景下不适用。
让大模型写代码,多半会遇到这几种情况:同一段需求问两次结果不一样、生成的函数缺边界处理、改一处又把别处改坏、换台机器或换个接口跑出来行为又变了。很多时候不是模型不行,而是提示结构和采样参数没管住。下面按可执行顺序,把提示模板、参数组合、输出约束和回归校验拆开讲,顺便说清哪些做法在什么场景下不适用。整篇只给判断标准和动作,具体数字一律去站内页面看当时的口径。
一个前提要先摆正:参数控制解决的是「同一模型在同一条需求上输出收敛」,它不能把一个本身模糊的需求变精确。如果你的需求描述里还留着「性能好一点」「尽量兼容」这种词,先把它们换成可验证的条件,再谈调温度还是调 top_p。顺序反了,后面所有排查都会在错误的地基上进行。
先搞清代码输出为什么不稳定
输出不稳定大致三个来源。第一是采样随机性,模型每一步都在按概率挑 token,温度不做约束自然会漂。第二是提示里缺约束,比如没写清错误分支该抛异常还是返回空值,模型只能猜。第三是上下文里混了无关材料,贴进来的旧代码和别的模块把注意力带偏。先定位是哪一类,再动手调,不然就是把随机性从一种形式换成另一种形式。
- 采样随机性主导
- 表现是同一条提示每次结构都不一样,但逻辑都说得通,这时优先降温度、固定随机种子
- 约束缺失主导
- 表现是结构稳定但细节每次换花样,比如返回值类型飘,这时改提示模板而不是改参数
- 上下文污染主导
- 表现是模型频繁引用你没提的函数名或框架,这时先删无关上下文再重跑
- 同一模型两次结果不同未必是坏事,创意性补全和确定性改写对稳定性的要求本来不一样。
- 判断自己属于哪类:需求描述本身模糊时,调参只是把随机性挪个位置,不会让它变准。
- 一个可操作的判别动作:把温度调到很低再跑三次,如果结果仍然发散,问题基本不在采样参数上。
提示结构:把需求拆成模型能校验的字段
固定五段式:任务、输入、输出契约、约束条件、禁止项。任务说清做什么,输入说清拿什么做,输出契约写清函数签名、返回类型、错误分支怎么处理,约束条件写清运行环境和依赖限制,禁止项写清不许引入什么库、不许改哪些文件。五段里最容易被省略、也最值钱的是输出契约和禁止项,省了它们,模型就只能按自己的习惯补。
| 段落 | 必须写清的东西 | 常见漏写导致的后果 |
|---|---|---|
| 任务 | 动词加目标对象,一句话说清 | 模型自行扩大或缩小改动范围 |
| 输入 | 给出输入的结构和取值范围 | 边界条件全靠模型猜 |
| 输出契约 | 函数签名、返回类型、错误分支 | 返回值类型在多次生成间飘 |
| 约束条件 | 运行环境、依赖、性能口径 | 引入项目里没有的库 |
| 禁止项 | 不许动哪些文件、不许用哪些写法 | 顺手改坏无关代码 |
示例放在约束之后,一到两个就够。示例太多会挤占上下文,也会让模型照抄示例里的业务细节,把示例里的命名和字段当成必须遵守的规范。要求模型先列假设再写代码,能提前暴露它猜错的地方,比事后读代码找 bug 便宜得多。假设列错了,你改提示;代码写错了,你得读懂它才能改。
采样参数:temperature、top_p、重复惩罚怎么配
确定性改代码、补测试这类活在低温度区间;开放式设计和写文档可以放高一点。具体区间不要凭记忆写死,到 /ai-dict/category/tips 对照当时的说明,那里对不同任务给了建议范围,也会跟着模型迭代更新。记住一个原则:温度管的是每一步挑 token 的胆量,不是管输出长度的,别用它来控篇幅。
- temperature 和 top_p 别同时大幅调整,一次只动一个,否则出问题分不清是谁造成的。
- 重复惩罚调高能压住复读,但会波及代码里本来就该重复的变量名和调用模式,代码任务上要保守。
- 换模型等于换了一套概率分布,旧参数组合要重新验证,不能直接沿用。
- 把最终采用的参数写进配置文件或调用封装,而不是散在每个人的聊天记录里。
# 记录一次调用用的参数,方便回溯
# model / temperature / top_p / seed / prompt_version
# 把这五个字段连同输出一起存,出问题能倒查输出约束:让模型只吐能直接用的东西
要求只输出代码块、不要解释,能省掉大量手工摘取。需要解释时,让它放在代码块之后的固定标记里,比如统一用一行分隔符隔开,方便脚本切分。涉及结构化结果时先约定 schema 再生成,别指望模型自由发挥完自己再解析,解析失败你还得反推它想表达什么。
- 约定输出边界在提示里写清代码块的开始和结束标记,以及解释必须出现的位置。
- 长文件分段生成按函数或按文件切段,每段给明确的起止锚点,比一次要整文件更容易对齐。
- 生成后先做语法检查用编译器或静态检查工具过一遍,语法不过的直接回炉,别进人工审查队列。
分段生成时,每段的锚点要写具体,比如从哪个函数结尾开始、到哪个函数之前结束。锚点含糊的话,模型会自作主张补上不存在的代码,拼接时冲突。分段还有一个好处:某一段不合格只需重跑那一段,不用整文件重来。
上下文管理:塞什么、不塞什么
只贴跟这次改动直接相关的文件和函数,整仓库塞进去通常会让模型抓错重点。给出项目已有的命名习惯和一个代表性文件,比写一长串代码规范更有效,因为模型能从真实代码里看出风格,而不是从抽象规则里推断。上下文长度快到上限时优先删历史对话,保留任务描述和输出契约,具体上限到 /ai-dict/category/basics 查当时的口径。
| 内容类型 | 该不该塞 | 说明 |
|---|---|---|
| 本次要改的文件 | 该塞 | 范围控制在相关函数 |
| 同项目代表性文件 | 该塞 | 一个即可,用来对齐风格 |
| 整个仓库 | 一般不塞 | 容易让模型抓错重点 |
| 历史对话 | 快满时先删 | 保留任务描述和输出契约 |
| 无关模块代码 | 不塞 | 会诱导模型引入不存在的依赖 |
跑一遍再信:把参数变更纳入回归
准备一组固定的小任务集,覆盖你日常最常让模型做的那几类活,每次换模型、换参数、换提示模板都重跑一遍,看通过率和改动量。任务集不要大,十来条足够,关键是要固定下来,能横向比。比较的时候看两个东西:一次通过的比例,以及为了跑通平均需要人工改多少处。
- 固化任务集挑十到二十条有代表性的小任务,每条带明确的验收判断。
- 记录运行元数据把模型标识、参数、提示版本一起存档,出问题能定位到是哪一步引入的。
- 变更后重跑换模型、换参数、换接口都触发一次全量重跑,对比通过率和人工改动量。
- 接口层先量基线换服务前到 /tools/speed-test 量一遍,别把延迟抖动当成模型变化。
回归集的价值不在于一次跑出的绝对数字,而在于两次之间的差。差变大了,先怀疑参数或提示改动,再怀疑服务方切换。中转站的质量波动也会影响结果,换站之前先去 /rankings 看看当时的稳定性口径,把服务因素和模型因素分开判断。
什么情况下这套做法不适用
完全探索性的原型阶段,过早锁死参数会挡住模型的发散价值,这时候让温度高一点、允许它给出不同方案反而是对的。需求本身每天都在变的时候,先把需求稳定下来再谈参数固化,否则你固化的是昨天的需求。小模型在长上下文多约束下容易顾此失彼,这时候把任务拆开分给不同调用,比在一个提示里堆约束更管用。
- 原型探索期:允许发散,先找方向再收敛,不要急着固定参数。
- 需求高频变动期:先稳住需求描述,参数固化留到需求冻结之后。
- 小模型长上下文场景:拆任务比调参更有效,一个提示只压一件事。
- 纯格式转换这类确定性任务:用规则或模板更省事,不必动用大模型。
参数管的是收敛方式,需求管的是收敛目标;目标没定清楚,参数调得再细也是在错误的点上收敛。
还有一类不适合的场景:任务本身可以用脚本或正则稳定解决。这种活交给代码更便宜也更可复现,硬套大模型只会把确定性换成概率性。判断标准很简单——如果输出空间是可穷举的,就别用生成式方案。
下一步把你的提示模板和参数配置落到一份文件里,然后到 /tools/speed-test 量一次接口基线,再去 /price-watch 看看当时的计费口径,三样凑齐才谈得上稳定复现。→常见问题
提示里写「请严格按以下格式输出」,模型还是加了解释,怎么处理?
多半是格式要求和内容要求混在一段里,模型分不清优先级。把输出格式单独拎出来放在提示末尾,用明确的起止标记说清哪里是代码哪里是说明;如果还不行,就在调用侧做截取,只取代码块部分,别指望靠措辞完全约束住。
降低温度就能让生成的代码可复现吗?
只能提高相似度,做不到完全一致。服务端批次、并发、硬件和版本差异都会让输出有出入。要真可复现,得固定模型标识、参数、提示版本,并且在本地或同一环境跑;跨服务商比对时把差异当成两回事分别排查。
参数调了半天效果还是不行,是该换模型吗?
先确认是提示问题还是模型能力问题。拿同一份提示去 /models 里挑一两个同类模型跑一遍,如果都达不到要求,说明是任务超出这个档位;如果只有当前模型不行,再考虑换。换之前先去 /plans 比一下额度和计费口径。
生成的代码能直接进生产吗?
不能跳过人工评审。模型擅长把结构写出来,但对项目私有约定、边界条件和安全边界的理解来自你给的信息。落到项目里之前至少过一遍:依赖是否真实存在、错误分支是否补齐、有没有引入不该有的外部调用。
团队里每个人参数都不一样,要不要统一?
建议至少把输出格式和回归任务集统一,参数本身可以按任务类型分档。做法是定义两三档预设,写进项目文档,个人微调只在档内浮动。全部放开的话,代码评审时很难判断某次生成的异常是提示问题还是参数问题。