把 temperature 降到 0.05 输出还是野 JSON,真正管用的是 schema 把 temperature 降到 0.05 输出还是野 JSON,真正管用的是 schema刚接手公司客服机器人的时候,我觉得自己离“上线”就差一个 API 调参--把温度拉到 0.05,模型总该老老实实吐 JSON 了吧?结果第二天,前端同事把日志甩到群里:Expecting , delimiter: line 5 column 46,一天崩了 11 次。就在我准备再加十句“务必返回合法 JSON”的 prompt 时,一位做 AI 落地的老哥私聊我:“你用深度学习入门那门课的第 6 章讲得很清楚,自回归模型不是调温度就能锁死输出的。你不把 schema 约束写进采样逻辑,temperature 调成 0 也挡不住它随机游走出一个野格式。”我顺着链接找到亚马逊云科技的深度学习入门,花了一个周末从头过完模型输出约束那一节,顺便把 few-shot 模板、思维链提示和结构化输出指令揉进了 prompt。三天后重测同一批 200 条对话,合法 JSON 率从 61% 直接拉到 97%,响应时延反而降了 40%。这就是本文要复盘的事:同一套大模型,为什么我改了 3 版 prompt 就能让结构化输出质量翻倍--以及为什么调 temperature 这个动作,原本就不该是主线。第一版 prompt 翻车:我以为加“必须”二字就足够产品给的需求很直白:用户的每一次问答,后端需要拿到一个结构体,记录意图、情绪、摘要和是否转人工。我写的第一版 prompt 大概是这样的:你是一个客服机器人。对于用户的问题,必须返回 JSON 格式,字段包括 intent, sentiment, summary, transfer。 示例:{intent: 退换货, sentiment: 生气, summary: 客户要求退换货, transfer: true} 务必只返回 JSON,不要添加多余解释。上线当天跑了不到 300 轮,就有二十几条解析失败。我用脚本从日志里捞出来一看,失败的情形分几类:模型在 JSON 前加了“好的,这是你要的结果:”的前导文字。JSON 内的字符串里嵌入了未转义的双引号,直接炸解析器。偶尔在末尾多一个逗号,看起来像 Python 字典写法。我以为把temperature拉到 0.1 就能消灭这种随机性。在深度学习入门之前,我的认知是:temperature越低,模型越“确定”,就越不会放飞自我。所以我做了第一个 A/B 测:配置合法 JSON 率平均时延temperature0.8, 基础 prompt61%1.8stemperature0.1, 基础 prompt63%2.9s合法率几乎没动,时延反而拉长了。这说明输出格式是否合规,和采样温度根本不是同一层的问题。真正的根因,是我从来没有把输出的“结构”写进模型的生成约束里--温度只能影响 token 选择的随机性,却无法阻止模型在 JSON 里埋一颗语法炸弹。我当时并不知道这些,直到在深度学习入门的课程里看到“带约束的序列生成”小节,才弄明白模型输出的 token 序列背后,还有一条我没用上的控制路径。打脸时刻:我以为 prompt 越严厉越好,其实越“死板”越易翻第二版我走向了另一个极端。既然“必须返回 JSON”说得不够狠,我就加码:你是一个不能出错的结构化输出机器人。 你的回复只能包含一个 JSON 对象,不能有任何额外字符。 JSON 必须包含字段:intent, sentiment, summary, transfer。 如果用户问题不完整,也必须返回一个 JSON,其中 transfer 为 false。 绝对不要添加任何说明、前缀或后缀。 {intent: 退换货, sentiment: 生气, summary: 客户要求退换货, transfer: true}这套 prompt 比第一版长了近一倍。结果?前导文字少了,但JSON 内部字符串的非法字符、不闭合的括号、多出来的逗号反而更频发。我陷入了典型的“过度指令陷阱”--模型为了满足“只能是 JSON”的强度约束,开始像背答案一样套一个模板,一旦用户输入稍微偏离训练数据的分布,它就在 JSON 内部犯错。我后来复盘时才意识到,这就是没有理解模型生成机制的下场。深度学习入门里有一个小节讲到,Transformer 解码器每一步都是基于先前已生成的 token 计算下一个 token 的概率分布;你给的指令越极端,模型的注意力越集中在“看起来像 JSON”的形态上,而不是“语法正确的 JSON”。真正锁死格式的方法,不应该在 prompt 里跟模型比嗓门,而是把格式约束从 prompt 层剥离,交给输出 schema 或约束解码。于是我开始重写第三版 prompt,并把 few-shot 模板从 1 个扩展到 3 个,加入角色设定和输出格式声明。代码变长了,但逻辑反倒清晰了:prompt_v3 f [角色] 你是一个严格的客服分析器,只输出合法 JSON。 [任务] 根据用户消息,输出一个 JSON 对象,字段定义如下: - intent: string, 用户意图(退换货/咨询/投诉/其他) - sentiment: string, 用户情绪(生气/着急/平静/高兴) - summary: string, 一句话摘要 - transfer: boolean, 是否需要转人工 [输出规则] 1. 只输出 JSON,不添加任何前缀、后缀或注释。 2. 字符串内如包含双引号,必须用反斜杠转义。 3. 不要输出尾随逗号。 [示例1] 用户:东西坏了,我要退钱! 输出:{{intent: 退换货, sentiment: 生气, summary: 客户要求退款, transfer: true}} [示例2] 用户:这个怎么用? 输出:{{intent: 咨询, sentiment: 平静, summary: 客户询问使用方法, transfer: false}} [示例3] 用户:你们客服态度太差了,叫你们经理! 输出:{{intent: 投诉, sentiment: 生气, summary: 客户投诉服务态度, transfer: true}} [现在] 用户:{user_input} 输出: 但关键不止 prompt。深度学习入门课程里那节“结构化输出与受控生成”让我学会了在调用 API 时传入response_format参数或使用强制语法约束器。这样一来,prompt 只负责意图和内容,schema 负责格式--分工清晰,翻车率直线下降。突破点:把 schema 写进协议,而不是写进“威胁”改完第三版 prompt 后,我跑了一个有 500 条真实客服对话的评测集,每条生成 5 次取多数结果。同时我把温度调回 0.7,让模型有一定的表达弹性。结果如下:版本合法 JSON 率意图识别准确率转人工误判率第 1 版 (基础 temp 0.1)63%74%18%第 2 版 (严厉 prompt temp 0.1)67%71%22%第 3 版 (few-shot schema)97%89%6%提升肉眼可见。而真正让我理解这 97% 为什么能从 63% 跃迁的,是深度学习入门课程里对“注意力机制”和“生成多样性-质量权衡”的讲解。它帮我建立起了一个关键直觉:你想控制模型的输出结构,就要控制它每一步可以选择的 token 范围,而不是在 prompt 里赌它会不会听你的话。我在公司内部做的分享里画了一张对比图,把“粗暴调 temperature”和“加输出 schema”两种策略画成两条曲线--前者是一条几乎平躺的直线,后者在加入 few-shot 和 schema 后陡峭拉升。当时有同事问我,“那为什么一开始不直接学这些?”。我说白了,我之前根本不知道深度学习入门里有这些东西--我以为这类课程只会教反向传播和卷积网络,没想到连生成式 AI 提示词工程这一层的底层原理都拆得很干净。A/B 测试框架:怎样系统化地迭代 prompt踩完这次坑,我把 prompt 迭代流程固化成了一套 A/B 测试小框架,直接嵌进了 CI(持续集成)流程里。每次改 prompt 都会自动跑一次评测集并生成报告。代码骨架大概长这样:import json import time from my_llm_client import ModelAPI # 封装过的调用客户端 def evaluate_prompt(prompt_template, test_cases, n_samples3): results [] for case in test_cases: user_input case[input] expected_intent case[intent] for _ in range(n_samples): prompt prompt_template.format(user_inputuser_input) raw_output ModelAPI.generate(prompt, temperature0.7, max_tokens200, response_formatjson_object) # schema 约束 parsed parse_json_safe(raw_output) results.append({ case_id: case[id], valid_json: parsed is not None, intent_match: parsed.get(intent) expected_intent if parsed else False }) valid_rate sum(r[valid_json] for r in results) / len(results) intent_acc sum(r[intent_match] for r in results if r[valid_json]) / max(sum(r[valid_json] for r in results), 1) return valid_rate, intent_acc # 伪评测集 test_cases [ {id: 1, input: 我要退货,货物损坏了, intent: 退换货}, {id: 2, input: 你们怎么一直不发货, intent: 咨询}, # ... 实际会放几百条 ] prompt_v3 [角色]...(上文模板) valid, acc evaluate_prompt(prompt_v3, test_cases, n_samples5) print(fJSON合法率: {valid:.2%}, 意图准确率: {acc:.2%})这套框架的可贵之处是:每次模型厂商更新版本,我可以一键跑全量回归;遇到新的边缘 case,直接把 example 加进测试集,再用生成式 AI 的思路去扩展 few-shot 模板。我后来把这段经验写进了团队的技术文档里,标题就叫《Prompt 工程的尽头不是调参,是构建测试闭环》。学完后的变化:从调参侠到能讲清“为什么”在系统学过深度学习入门之前,我对模型的理解停留在“输入文本,输出文本,中间是个黑盒”。遇到问题时,我能做的只有三件事:加 prompt、调温度、换更贵的模型。结果就是老板问我“为什么第三版 prompt 比第二版好”,我只能含糊其辞。学完课程之后,变化最明显的是两点:排查问题的速度:之前一个 JSON 解析错误我要翻日志、改 prompt、重跑,平均耗时 40 分钟;现在我可以先判断是“格式指令未生效”还是“模型生成了不在 token 允许集中的字符”,10 分钟定位问题,15 分钟修掉。工时缩小了 60%。可复用的技能树:这套 prompt 方法论迁移到邮件摘要、合同关键字段抽取、代码评审输出等场景都很顺滑。因为万变不离其宗--你总要理解模型如何选择 token,才知道在哪里加约束。这种基础能力,恰恰是亚马逊云科技机器学习类课程里反复强化的点,也是很多做生成式 AI 应用的同行最容易跳过的部分。我甚至开始试着教团队里新来的毕业生:你先别急着写 prompt,先用机器学习基础那门课把特征到模型输出的链路搞明白,再去看生成式 AI 的提示词技巧,这样你写的 prompt 每一次改动都有据可查。给同样处境的人的建议如果你也正为「模型输出的 JSON 永远不对」「加了 30 句要求还是乱来」这种事头疼,下面几条是我用实实在在的出包事故换来的建议:永远先检查输出格式约束机制,而不是调 temperature。生成式 AI 的 API 大多都提供了response_format或类似 schema 参数,这比任何 prompt 里的“必须”都管用。few-shot 示例不要只给一个,给 2~3 个能覆盖不同边界情形的例子,且示例 JSON 要严格符合自己的 schema 定义。搭建评测闭环:写个 Python 脚本,每次改 prompt 或模型版本都自动跑一遍评测集,量化 JSON 合法率和业务指标。评测集要持续从线上 badcase 中补充。花一个周末补一下深度学习入门里的序列生成原理,你会发现自己以前对 prompt 的理解可能只停留在表象。亚马逊云科技的深度学习入门课不要求你手写反向传播,但把注意力机制、解码策略、约束采样这些概念讲得非常落地,看完就能用。警惕“过度指令陷阱”:prompt 越长不一定越好,把格式约束和内容约束分开,分别由 schema 和 prompt 各司其职。团队内建立共享 prompt 库,要求每条 prompt 附带评测结果和迭代日志,避免每个人各自拍脑袋。AI 编程助手比如 CodeWhisperer 在写评测脚本时能帮你省掉大量样板代码,值得用起来。如果时间有限但想快速了解生成式 AI 的落地边界,可以先看面向高管的生成式 AI 这门短课,它会帮你建立业务视角;然后再扎进深度学习入门去啃技术细节,这样学习路径更高效。最后想说:prompt 工程的乐趣,不在于堆砌魔法词汇,而在于你终于能解释清楚为什么这一版比上一版好。这种掌控感,是学完深度学习入门之后才真正出现的。