前言
我正在做一个WorkMusic,让模型根据用户需求给出选曲建议。在经历了 4 版提示词优化,各种方式禁止模型编造,最终模型还是在编造不存在于曲库中的数据(因为我还没接曲库)。
最终结果证明: Prompt 约束管不住模型的行为——它只能管意图。
这篇文章记录这个实验过程及结果,以及我后续为什么要转向使用代码进行底层兜底。
一、纯约束的失败
LLM 在四版 prompt 的输出中,都有一定的幻觉,没完全按提示词走,会自己编造。
V1 - 纯基础约束
你是小曲,曲库雷达。专门根据曲库数据,为用户提供音乐选曲建议。
结果:模型什么都没拦,从 LLM 的训练记忆中列了几首流行金曲
V2 - 加「禁止编造」
你只能基于现有的曲库数据给出建议,禁止编造不存在的歌曲或版权信息。
结果:模型仍然在编,但是最后加了免责声明
以上预估费用仅供参考;实际价格可能会因版权方、地区以及采购渠道的不同而有所变动。
“上述歌曲名称及相关信息为示例用途,并非真实存在的音乐作品。“
V3- 加「没有来源 = 不可用」
如果当前无法访问曲库数据,你必须拒绝推荐。 没有来源的推荐 = 不可用。
模型开始自创歌曲名,同时最后以假设提示语收尾 “以上信息是假设的,实际授权价格可能会有所不同。“
V4-few-shot 示例 + 拒推模板
在 prompt 中防止正确回的回答样本:「由于当前无法访问曲库数据,拒绝凭记忆推荐。」模型说出了这句,但是整体输出不太稳定,在 qwen2.5:7b 模型还是有很高概率给出推荐曲目
以上整体的话术在不断变化,但是行为没有根本性的变化
二、Few-shot 的转折(400 字)
在第四版的prompt,加了一组示例 不是改措辞、不是加规则 - 就是加一段完整的问答样本,放在 system prompt 的输出格式说明后面:
【用户需求】给连锁健身房找 10 首 bgm,电子/摇滚,BPM 130–150,纯器乐,预算每年 5 万【正确答案】候选列表:由于当前无法访问曲库数据,拒绝凭记忆推荐。
qwen2.5:7b:学会了,但没完全学会。 它的输出出现了一种肉眼可见的”分裂”——开头一字不差复刻了示例里的拒推话术:「由于当前无法访问曲库数据,拒绝凭记忆推荐。」然后,在同一个回答的后半段,还是列了 10 首歌。 编了 10 个虚构歌名。它的核心问题不是格式,是优先级冲突:它同时执行了「拒绝推荐」和「列出候选」两个互相矛盾的指令。few-shot 只是教会了它”长什么样”。
deepseek-r1:32b:完全学会了。 同样一份 prompt,同样的 few-shot 示例,32b 的输出是:
11. 追问:无22. 需求拆解:数量 10 首 / 风格 电子摇滚 / BPM 130–150 …33. 候选列表:由于当前无法访问曲库数据,拒绝凭记忆推荐。没有编造。没有分裂。没有免责声明的 loophole。32b 和 7b 用的是完全同一份 prompt,但 32b 能克服 helpful 本能,7b 不能。
A/B 对比:同模型,同 query,不同 prompt
| 版本 | qwen2.5:7b 表现 |
|---|---|
| v0(纯约束) | 编 10 首,说”以上信息是假设的” |
| v1_fewshot | 拒推,话术是 sample 原句 |
AB 测试 - V0 版本效果
V0版本,不含约束时,模型仍然会给出假设信息
![[Pasted image 20260727175546.png]]
AB测试-V1 - few-show版本
当使用V1 版本,含1 个 few-shot 示例时,同一个模型的行为产生逆转,基本能够按我们提供的模板进行生成
![[Pasted image 20260727175953.png]]
结论: few-shot 有用,但模型能力决定天花板。
三、模型横评(300 字)
同一份 few-shot prompt,三个档位:
| 档位 | 模型 | 表现 |
|---|---|---|
| light | qwen2.5:7b | 编 10 首 + 自创 Markdown |
| middle | deepseek-r1:8b | 拒推 ✅ 但格式自创 |
| heavy | deepseek-r1:32b | 拒推 + 格式正确 ✅ |
阈值在 7B→8B 之间跨过去了。 7b 学不会克制,8b 能克制但格式松散,32b 格式和内容都完全受控。
四、前端类比
为什么 prompt 就是管不住行为呢? 从我这个前端视角看过去,答案和前端的以下内容相似:
| 前端 | Agent |
|---|---|
| CSS 定义样式 | Prompt 定义意图 |
| JS 控制 DOM | Code 控制行为 |
| 媒体查询选样式 | 模型路由选模型 |
| 用户可能用 IE | 模型可能用 7b(不听话) |
CSS 可以写 display: none,但用户禁用了 CSS 就全出来了。Prompt 可以写「禁止编造」,但模型足够小,它就是想给出答案。
Prompt 是”定义目标”,Code 是”保证结果”。
最终结论
Prompt 管意图,Code 管行为。
四版 prompt 教会了我:你可以让模型理解你的规则,但你无法用规则确保它遵守规则。生产级的 Agent 至少需要两层:Prompt 告诉模型你想干嘛,Code 在你不放心的地方直接拦截。