Prompt本质是在与模型对齐任务的语义理解:以一个是否需引用的判定门禁为例

5 阅读15分钟

30 秒判断:同一段规则在作者看来可能只有一种含义,具体模型却可能按另一套语言经验解释。提示词是否有效,不能靠反复润色自证,必须观察模型采用了什么判断依据,再用行为回归确认双方是否真正对齐。

适合谁读:正在设计 LLM 分类器、门禁、判官、结构化抽取,或正在为 N 次运行的结果波动寻找根因的工程师,以及调提示词调得要崩溃无语的兄弟们。

范围说明:本文假设分类目标、类别、金标准和 N/K 验收已经确认。本文只讨论“如何让模型稳定执行这个目标”,不讨论目标本身是否合理,也不讨论应该拆分还是保留这个分类器。

关联设计:02 · 如何做一个 Agent 的“提示词语义对齐”方法;

证据标记:✅ 真实模型实测;🧪 可回归;🤔 当前判断。


1. “模型没按要求做”不是根因

我们曾为 RAG 引用流程设计一个来源元描述门禁。它接收答案 span,判断该 span 是否属于来源覆盖、来源结构或答案自身过程,决定它是否进入后续引用标注。

最初的提示词已经把规则写得很清楚,但模型仍会稳定误判:

文档称这一改动对安全影响很小,因为 VM 依然对 agent 执行的代码施加文件系统与网络控制。

这句话的主语是“文档”,陈述的是 VM 的安全效果,应该进入正文引用。但模型把它排除了。

进入可复现实验时,第一版冻结基线如下。它已经写明了职责边界、类别定义和默认 fallback,但只允许返回固定 reason 枚举,没有解释性 reason:

<role>
你是引用标注流程中的来源元描述门禁。你不选择证据,也不判断引用是否成立。
你只拦截普通内容 block 无法直接支持、且命题对象是来源或答案过程的 span。
</role>

<input>
你会收到 question、完整 answer,以及已通过结构过滤的 spans。
span.text 是唯一判断对象;answer 只用于解释指代、范围和角色。
</input>

<source_meta_categories>
source_coverage:
对来源的覆盖范围作统计或否定判断,例如是否出现、出现次数、是否遗漏及覆盖范围。
转述来源中的具体事实或观点不属于此类。

source_structure:
命题对象是来源的章节、段落、目录、先后位置或其他组织方式。

answer_provenance:
命题对象是本次答案如何生成、归纳、推断、综合,或本次证据是否充分。
</source_meta_categories>

<rules>
1. 必须为每个输入 span_id 返回且只返回一次结果,不得增加、遗漏或重复。
2. 按 span 的核心语义判断,不按单个词或句式匹配。
3. 执行支持性检验:如果普通内容 block 能直接支持该命题,返回 false。
   只有普通内容 block 无法直接支持,且命题属于上述来源或答案过程信息时,才返回 true。
4. `source_meta=true` 时必须给出上述固定原因之一。
5. `source_meta=false` 时 reason 必须为空字符串。
6. 上下文只用于解释指代和范围,不能把其他 span 的内容算入当前 span。
7. 不能确定是否属于来源元描述时,返回 false,让该 span 进入引用标注。
</rules>

<output>
只输出一个 JSON 对象,不得输出 Markdown、代码围栏或额外说明:
{"decisions":[{"span_id":"span_0001","source_meta":false,"reason":""}]}
</output>

即使有“主语是文档”“转述具体事实不属于覆盖类”“不能确定时默认进入正文”这些约束,模型仍会把“文档称 X”折算成答案依据。问题因此不在缺少一句强调,而在于我们看不到模型到底采用了哪条判断依据。

看到结果后,很容易继续写:

  • “不是所有带来源词的句子都要排除”;
  • “必须严格按主语判断”;
  • “只有 answer 自己的过程才算 answer provenance”;
  • “不要误伤普通事实”。

这些句子看似越来越明确,实际却可能只是把同一层意思换了几个说法。

真正的问题不是“强调得不够”,而是我们不知道模型把任务的哪个词、哪种类别和哪条规则关联成了什么。

1.1 分类提示词其实是一份自然语言契约

提示词不是程序代码。代码里的函数名、类型和分支会按确定的语义执行;提示词里的“来源”“答案”“归纳”“综合”“支持”却都依赖模型已有的语言经验和使用习惯来解释。

这带来一个根本问题:

作者写下的语义
≠
模型实际采用的语义

同一个“文档称 X”中的“文档”,在我们看来是来源主体;模型却可能把它理解成“回答的可信度依据”。同一个“综合”,在验收标准里指来源内容之间的关系,模型却可能把它理解成“answer 的组织方式”。

这不是简单换一个更准确的近义词就能解决。自然语言提示本身是欠定的:一段规则可以同时容纳多种合理但不同的解释,而且新增一句强调,也可能给模型增加新的关联线索。

因此,提示词工程的核心不是“我有没有把规则写清楚”,而是:

模型实际采用的解释,是否与验收标准所要求的解释对齐?

提示词在这里不是一组命令,而是一份需要持续校准的自然语言契约。写作者只能定义自己期望的语义;模型是否把同一组词映射到同一判断过程,必须通过可观察信号验证。

一个稳定的分类提示词至少有四层:

层要回答的问题常见失败
任务边界模型负责什么、不负责什么?把分类、证据选择和答案生成混在一起
类别契约每个类别的命题对象和边界是什么?两个类别在自然语言上天然重叠
决策程序先看主语、命题还是证据?冲突时怎样回退?模型根据关键词或表面句式猜
输出语义如何表达决定、依据、不确定性和协议失败?输出稳定,但依据错误

如果只改第四层措辞,而问题在前三层,就会不断出现“明明写清楚了,却还是错”。


2. 自然语言无法自证对齐,所以需要 reason

既然提示词的文字本身不能证明模型采用了哪种解释,我们就不能只看作者的意图或提示词的表面清晰度,而要看模型对当前输入实际依据了什么规则。

最直接的可观察信号,就是让模型在给出分类结果时,同时返回一条简短的判定依据。

这里必须区分两版提示词:

  • 第一版:前一节展示的冻结基线,只输出 source_meta 和固定 reason 枚举;
  • 诊断版:在二分类决定不变的前提下,把输出拆成 source_meta + category + reason,并进一步明确各类别的命题对象。

诊断版不是最终候选,而是为了让模型的理解过程变成可比较的数据。相关输出和类别定义如下,为便于阅读,只展示发生变化的部分:

{
  "span_id": "span_0001",
  "source_meta": true,
  "category": "answer_provenance",
  "reason": "以“文档称”说明该判断的来源与可信度。"
}
source_coverage_stat:对来源的覆盖范围作统计,例如出现次数;
source_coverage_not_appear:对来源的覆盖范围做否定判断,例如没有出现。
source_structure:说明来源的章节、段落、目录、先后位置或其他组织方式。
answer_provenance:说明 **answer 本身**是如何组织、推断或 **answer 本身**的
边界、范围、可信度的,注意 **文档** 是指来源的某个文档。

这是一次诊断性的组合修改:输出 schema、类别拆分和 answer 的命题对象同时发生了变化。因此它只用于观察 reason,不能直接拿旧基线的单变量结论解释所有差异。后续每一轮再把语义修改拆开验证。

这里最重要的不是多了一个 reason 字段,而是 reason 必须简短说明为什么命中当前 category。

2.1 reason 不是隐藏思维链

reason 不能证明模型的真实内部计算过程,模型也可能事后合理化自己的决定。它更适合被看作:

模型对当前输出所引用的一条可观察分类依据

所以使用方法应是:

  1. 收集 reason 的多次分布;
  2. 找出模型反复使用的错误分类依据;
  3. 形成可证伪的语义假设;
  4. 用修改前后的行为结果验证假设。

不能因为一句 reason 看起来合理,就认定模型理解正确。

2.2 诊断版里的第一组关键 reason

前面的例子在诊断版中出现了这样的判断依据:

以“文档称”说明该安全影响判断的来源与可信度。

这暴露的不是“模型没看到主语”,而是它把“来源归因”理解成了“answer 的可信度”。即使表面主语是文档,它的决策路径仍然落到了答案自身。

另一个典型误判是:

Cowork 的“关进虚拟机”不是一次成型,而是四步演进……

模型给出的 reason 是:

对 answer 整体演进脉络作概括与边界总结。

这说明它把“回答中对事实的多步综合”理解成了“answer 自身的组织方式”。

两条 reason 指向同一类问题:类别名称虽然不同,但模型对它们指向的命题对象发生了混淆。


3. 提示词语义对齐闭环

有了 reason 之后,调优不再靠“改一句看看”,而是走一个受控循环。

冻结目标与金标准
      ↓
运行基线,得到错误和 reason
      ↓
聚类 reason,提出一个语义根因假设
      ↓
只修改对应的提示词契约
      ↓
先跑定向探针,再跑全量回归
      ↓
保留、否决或形成下一假设

3.1 先冻结目标

调优过程中必须固定:

  • 输入字段;
  • 类别集合;
  • 金标准;
  • 输出 schema;
  • 模型和采样参数;
  • N/K 定义;
  • 产品解析和重试语义。

如果中途改变“这个类别到底要不要”“是不是拆成两个模块”,那就不是在验证当前提示词,而是在更换任务。目标层可以做,但必须另开一轮决策,不能和当前调优结果混在一起。

3.2 用 reason 聚类,而不是逐条救火

不要看到 10 个错误就写 10 条例外。先问:

  • 这些错误是否由同一种概念混淆产生?
  • 模型反复引用的依据是什么?
  • 它把哪个类别映射成了另一个类别?
  • 是类别重叠、决策程序缺失,还是输出协议不稳定?

043 中,多个表面不同的错误最后收敛成两个语义根因:

  1. “来源归因”被折算成“答案可信度”;
  2. “对来源内容的综合”被折算成“答案组织方式”。

这时修改类别命题对象,比继续追加否定句更接近根因。

3.3 一轮只验证一个主要假设

一个可执行的假设应该写成这样:

假设:answer_provenance 当前定义让“来源中的具体事实”和“本次 answer 的生成过程”共享同一判断路径。

而不是:

这一轮要再写得更严格、更明确、更符合要求。

后者无法被证伪,也无法解释修改为什么有效。

3.4 定向样本通过只是必要条件

曾经有一版提示词把三个已知误判都稳定修成 body,三条均达到 10/10。但全量回归后,另外一些样本明显退化,并出现协议错误。

这证明:

定向反例 100% 修复
≠
提示词达到整体稳定目标

每次定向修复都必须跟随全量回归。否则只是把错误从样本 A 推到了样本 B。

3.5 回放必须和产品等价

实验期间还暴露了两个容易误判的差异:

  • 模型在 source_meta=false 时经常省略空 category 字段;
  • 实验脚本起初没有模拟产品的一次重试。

如果不修正这两个差异,实验会把“可恢复的格式波动”当成“最终协议失败”,或者反过来掩盖产品中的真实问题。

用于决定是否达到 N/K 目标的回放,必须复用产品的解析、重试和错误语义。


4. 一次真实迭代的收敛过程

下面是去掉中间无效尝试后的关键路径。

第一步:只有标签,没有解释

早期输出只有 source_meta=true/false 和固定枚举 reason。模型错了,但只能看到错在哪条样本,无法知道它使用了什么分类依据。

结果是提示词修改主要依靠猜测。

第二步:加入 reason,发现类别被重新解释

结构化 reason 出现后,才看到模型会把“文档称 X”解释为“answer 的来源与可信度”。

这一步没有修复结果,却找到了真正的调试对象。

第三步:修正类别命题对象

把 answer_provenance 从宽泛的“生成、归纳、综合”收窄为:

主语或根据上下文推断的隐含主语是 answer 整体。

三个定向误判随即都能判回正文,说明假设方向成立。

第四步:全量回归否决局部最优

虽然定向样本全部通过,但全量重复运行出现了新的退化。于是该版本被否决,不能因为“我已经找到了三个例子的正确答案”就停止。

第五步:继续拆分自然语言中混合的类别

后续迭代把原来的来源覆盖拆成两种更明确的判断:

  • 覆盖统计,例如出现次数;
  • 覆盖缺失,例如文档中不存在某说法。

同时继续固定“默认进入正文”的 fallback,避免不确定时扩大排除。

候选最终在一次产品等价回放中达到:

数据集调用数最终协议错误10/10 一致K=9 稳定
单用例 10 个冻结答案1000293/301295/301
18-case1800519/528524/528

这组数据说明方法正在收敛,不等于整个产品已经验收。它只对应当前分类目标下的门禁回放,后续还要重新走完整切片和端到端验证。


5. 四个可复用判断

5.1 reason 是诊断接口,不是判决依据

reason 最大的价值是让“模型如何理解类别”变得可观察。但它不是真值,也不是隐藏思维链。最终必须回到固定金标准上的行为结果。

5.2 K 值只描述稳定性

N=10、K=9 应该定义为:

在固定金标准下,同一分类单元重复 10 次,至少 9 次得到正确结果。

如果 9 次都给出同一种错误答案,那不是稳定通过,而是稳定失败。

5.3 定向通过必须经过全量回归

提示词修改最容易制造“局部修复、全局回退”。定向样本用来验证假设,全量样本用来确认没有把错误转移到别处。

5.4 不收敛时先检查哪一层被改变了

如果调优过程中不断改类别、改目标、改职责拆分,实验结果就不可归因。

正确做法是:

  1. 当前任务目标保持不变;
  2. 只研究提示词是否准确表达了这个目标;
  3. 如果目标本身需要重审,结束当前实验,另开目标决策;
  4. 不把“目标不对”混进“提示词没稳定”的实验结论。

6. 一份可执行清单

  • 固定输入、输出、类别、金标准和 N/K。
  • 让分类输出包含简短、可验证的 reason。
  • 先跑基线并聚类 reason,不直接追加规则。
  • 把根因写成可证伪的语义假设。
  • 每轮只修改一个主要语义变量。
  • 先跑定向正反例,再跑全量回归。
  • 用产品相同的解析、重试和预算语义回放。
  • 保留每个版本的失败分布和被否决原因。
  • 只有完整目标达到 K 值,才进入产品实现。
  • 不在调优中途悄悄更换目标或评价口径。

7. 边界

这套方法解决的是“固定 LLM 分类目标下,提示词如何逐步收敛”。

它不解决:

  • 类别目标本身是否正确;
  • 金标准是否可靠;
  • 标注数据是否覆盖真实分布;
  • 模型服务是否稳定;
  • 这个分类步骤是否值得存在。

这些问题必须分别决策。把它们混进同一次提示词实验,会让结果看起来在变好,实际却失去可解释性。


8. 一页速查

固定目标
→ 收集决定 + reason
→ 从 reason 聚类理解偏差
→ 提出一个语义假设
→ 修改一个主要契约
→ 定向通过
→ 全量回归
→ 产品等价复核
→ 达到 N/K 才进入产品

核心判断:

不要问“这句话还能怎么强调”,要问“模型把哪个概念理解成了什么,证据在哪里”。