返回博客

LLM Fallback Prompt 把「借来的公司名」写成事实:Identifying Field 必须剥离

做 LLM 生成功能时,有一类 bug 极难自查:明明传给模型的数据"看起来是对的",输出却是错的——具体公司名、地址、法人出现在了完全不该出现的记录里。我在一个内部工作流系统里就踩了这个坑,折腾了好几轮才找到根因。

现象

系统需要对某份结构化记录批量生成一段自然语言描述。部分记录本身没有公司信息(例如个人参保类条目),代码里写了一个 fallback 逻辑:从同一客户的其他记录里借用最近一条有公司的条目,把它的公司字段喂给 LLM,意图是让 LLM"感知行业氛围",生成风格连贯的描述。

问题是:LLM 输出里出现了这样的句子——

"在某某有限公司担任项目经理,负责信息技术相关项目……"

而这条记录本身跟那家公司毫无关系,只是个人参保条目。客户看到直接说"这是胡说",调试者打印 prompt 数据一看——公司名确实传进去了,数据层面是"对的"。

根因

问题出在 prompt 模板的语义引导上。模板长这样:

公司名:${companyName}
所属行业:${industry}
经营范围:${businessScope}
岗位:${jobTitle}

请生成一段工作职责描述。

LLM 看到 公司名:XX 有限公司 这个字段后,会把它当作事实断言直接 echo 进陈述句——这是 LLM 的天然行为,它没有能力区分"这个字段是真实主体"还是"这只是风格参考"。

Fallback 的本意是借用临近实体的行业/经营范围作风格 hint,但若不剥掉 identifying field,LLM 就会把那家公司的名字写进输出,造成张冠李戴。

坑:fallback 实体的公司名留在 prompt 里会被 LLM 当事实 echo

Prompt 模板里凡是包含专名(公司名、注册地址、法人姓名、统一信用代码等 identifying field),LLM 就会倾向于把它们写进陈述句。即使你在代码注释里写 // NOTE: 这是 fallback 借来的,不要写进输出,LLM 也看不到 JS 注释

同理,把字段改成 公司名:(参考)XX 公司 也无济于事——LLM 仍会 echo "在 XX 公司"。

正解

按字段语义分两类,fallback 路径只保留 style hint,identifying field 全部清空:

| 类别 | 典型字段 | fallback 时处理 | |---|---|---| | Identifying | 公司名、法人、注册地址、统一信用代码、公司 ID | 清空 | | Style hint | 所属行业、企业类型、经营范围、规模 | 保留 |

效果:LLM 看到 公司名:(空)\n所属行业:信息技术\n经营范围:软件开发、技术服务… 后会输出——

"负责信息技术相关项目管理,统筹软件开发与技术服务工作……"

——风格连贯,且绝不会编造具体公司。

代码层面,主路径和 fallback 路径显式分叉,不要用同一个 generate() 然后在数据里偷偷清字段:

if (mainEntity) {
  // 真有公司 · 全字段喂 LLM
  return generate({
    companyName: period.companyName || mainEntity.name,
    jobTitle: title,
    companyInfo: {
      name: mainEntity.name,
      industry: mainEntity.industry,
      companyType: mainEntity.companyType,
      businessScope: mainEntity.businessScope,
      registeredAddress: mainEntity.registeredAddress,
    },
  })
}
 
// fallback 模式 · 风格 hint only · identifying field 全空
return generate({
  companyName: '',
  jobTitle: title,
  companyInfo: {
    name: '',                                      // ✗ 清空
    industry: fallbackEntity?.industry,            // ✓ 保留
    companyType: fallbackEntity?.companyType,      // ✓ 保留
    businessScope: fallbackEntity?.businessScope,  // ✓ 保留
    registeredAddress: '',                         // ✗ 清空
  },
})

路径分叉显式,审 PR 的人和未来的自己都能一眼看清意图,不会在下次迭代时"顺手"把公司名加回去。

正解:fallback 路径 identifying field 清空,只留 style hint

主路径与 fallback 路径显式分两个调用,而非用同一份数据悄悄清字段。这样意图透明,也防止日后维护时无意中把 identifying field 复原。

如果 LLM 偶尔还是脑补出行业典型公司名(训练数据里的),可在 system prompt 加硬规则:"不要在输出里 mention 任何具体公司名、地址或人名,只描述岗位本职职责。"

排查思路

遇到 LLM 输出里出现"不该有的具名实体",按以下顺序排查:

  1. 打印完整 prompt(不是业务日志,是真正送给 LLM 的字符串)
  2. 搜 prompt 里有没有 identifying field —— 有就是根因
  3. 找 identifying field 是从哪条数据来的 —— 主路径还是 fallback
  4. 若是 fallback 带来的:剥离 identifying field,只保留 style hint

一句话外卖

Fallback 实体只能贡献「风格」,不能贡献「身份」——送给 LLM 之前,identifying field 必须清空。