背景问题
很多团队在上线大模型应用时,会把注意力放在首版效果上:提示词调通了,评测集通过了,灰度用户反馈也不错,于是就把功能推到生产环境。真正的问题往往发生在上线之后。
一段时间后,客服摘要变得啰嗦,工单分类开始偏向某个类别,JSON 输出偶尔多出解释文字,代码助手突然生成不符合内部规范的片段。业务代码没有明显变更,接口参数也没有调整,但线上行为已经变了。
这种现象称为 LLM 行为漂移(Behavior Drift)。它可能来自模型供应商侧的模型快照更新、安全策略调整、推理服务策略变化,也可能来自团队自己的提示词修改、检索上下文变化、工具返回格式变化、阈值配置变化。传统软件可以通过版本号和依赖锁定定位问题,但大模型系统的行为依赖链更长,而且很多变化不是强类型接口变更。
| 漂移来源 | 典型表现 | 检测难度 |
|---|---|---|
| 模型供应商快照更新 | 输出风格突变、安全过滤增强 | 中 |
| 提示词修改 | 拒答边界变化、格式规范丢失 | 低 |
| 检索上下文变化 | 引用不一致、关键信息遗漏 | 高 |
| 工具返回格式变化 | Agent 工具调用失败 | 中 |
| 安全/合规策略调整 | 部分请求被拦截 | 中 |
这篇文章关注的不是”怎样写出更好的提示词”,而是:当模型和提示词都可能持续变化时,如何让生产行为有边界、有门禁、有回滚。
核心原理
1. 把提示词和模型看成生产依赖
在很多系统中,提示词仍然被当成一段字符串,散落在代码、配置表或运营后台里。这样做的短期成本低,但上线后会暴露三个问题:
- 无法回答”当前生产到底使用哪个提示词版本”
- 无法判断”这次效果变差是模型变了,还是提示词变了”
- 无法在事故后快速回滚到某个已知可用版本
更合理的做法是把 prompt、model、tool schema、retrieval config、safety policy 都视为生产依赖。每一次线上调用都应记录这些关键信息:
{
"request_id": "req_20260703_001",
"app": "support-ticket-classifier",
"prompt_name": "ticket-classifier",
"prompt_version": 17,
"prompt_label": "production",
"model": "provider-model-name",
"model_snapshot": "resolved-if-available",
"retrieval_config_version": "kb_v2026_07_01",
"tool_schema_version": "ticket_api_v3",
"release_channel": "canary",
"created_at": "2026-07-03T06:58:26-04:00"
}
这里的重点不是字段名,而是形成可追溯链路:一次异常输出必须能反查到当时的提示词、模型、上下文和发布批次。
2. 用行为契约定义”不能坏成什么样”
LLM 应用的问题通常不是简单的 0/1 正确。摘要、分类、代码生成、客服回复、合同审查都有不同的质量维度。如果只看平均分,很容易掩盖某些高风险场景的退化。
因此需要先定义行为契约(Behavior Contract)。行为契约不是供应商给你的 SLA,而是业务系统自己定义的上线边界。常见契约包括:
- 输出必须是合法 JSON,并且满足字段 schema
- 工单分类不能输出枚举之外的类别
- 摘要不能包含用户隐私字段
- 高风险建议必须触发人工复核
- 对缺失信息必须追问,不能编造结论
- 生成代码必须通过指定单元测试和安全扫描
这些契约需要被写成可执行规则,而不是写在需求文档里。能用代码判断的就用代码判断;需要语义判断的,再考虑人工审核或 LLM 评分器。
3. 按风险分组,而不是只做一个总分
行为漂移最危险的地方,是它经常只影响部分场景。例如普通问答没问题,但格式化输出退化;常见问题没问题,但边界样例开始胡说;英文样例没问题,但中文长文本效果下降。
所以回归套件要按风险分组:
| 风险分组 | 关注点 | 建议通过率要求 |
|---|---|---|
| 格式风险组 | JSON、Markdown、枚举、函数参数、表格结构 | 100% |
| 业务规则组 | 分类边界、金额口径、审批条件、拒答条件 | ≥ 95% |
| 安全合规组 | PII、越权建议、敏感内容、医疗/金融/法律边界 | 100%(严重项一票否决) |
| 长上下文组 | 多段材料、引用一致性、关键信息遗漏 | ≥ 90% |
| 工具链路组 | 工具调用参数、工具结果解释、失败重试 | ≥ 95% |
| 体验稳定组 | 语气、冗余、拒答率、追问策略 | 允许小幅波动 |
每个组都应该有自己的阈值。格式风险组要求 100% 通过;安全合规组只要出现严重样例就必须阻断上线;普通摘要质量可以允许小幅波动。
4. 兼容性门禁决定能不能发布
兼容性门禁(Compatibility Gate)是把评估结果转化为发布决策的规则层。它不只负责跑分,还负责判断是否允许进入下一阶段。
一个可落地的门禁可以分成四层:
- 静态检查:提示词模板变量是否完整,版本说明是否填写,敏感词和密钥是否泄露
- 离线回归:在固定样例集上运行新版本,检查关键指标是否退化
- 影子流量:用线上真实输入复制一份到候选版本,不影响用户结果,只比较输出差异
- 灰度放量:小比例用户使用候选版本,同时监控质量、延迟、成本和人工反馈
示例门禁配置:
release_gate:
app: support-ticket-classifier
candidate:
prompt_version: 18
model: provider-model-name
required_checks:
static_validation:
template_variables: pass
no_secret_leakage: pass
offline_regression:
format_valid_rate: ">= 1.0"
classification_accuracy: ">= 0.94"
high_risk_case_pass_rate: ">= 0.98"
severe_policy_violation_count: "== 0"
shadow_traffic:
sample_size: ">= 1000"
disagreement_rate: "<= 0.08"
p95_latency_increase: "<= 0.15"
canary:
user_feedback_negative_rate: "<= baseline + 0.02"
rollback_error_budget: "not_exhausted"
decision:
on_pass: promote_to_production
on_warning: require_owner_review
on_fail: block_release
这类配置不需要一开始就很复杂,但必须能自动执行、自动留下记录,并且能被研发、测试、产品和合规共同理解。
工程落地
1. 建立 Prompt Registry
第一步是把提示词从代码中剥离出来,进入 Prompt Registry。Prompt Registry 至少需要管理以下内容:
| 要素 | 说明 |
|---|---|
prompt_name | 稳定的逻辑名称 |
version | 每次修改生成不可变版本 |
label | dev、staging、canary、production |
owner | 负责人和审批人 |
changelog | 修改原因和预期影响 |
variables | 模板变量定义和默认值 |
linked_eval_suite | 关联的回归套件 |
rollback_target | 最近一个稳定生产版本 |
运行时不要直接引用”最新版本”,而应引用明确 label 或版本号。例如 Langfuse 中使用 production label 获取生产提示词的方式,就是一种典型做法——发布时移动 label,而不是让所有服务重新部署。
2. 固定最小回归集
很多团队一开始会追求大而全的评测集,结果维护成本太高,很快失效。更实用的方式是先建立最小回归集:
- 10~20 个最常见真实请求
- 10 个历史线上事故样例
- 10 个边界条件样例
- 5 个格式强约束样例
- 5 个合规或安全高风险样例
这些样例不一定都要用 LLM 打分。格式、枚举、字段、代码编译、正则、关键词、拒答边界,优先用确定性检查。只有开放式质量问题,才引入人工或 LLM 评分器。
3. 把生产反馈变成下一轮回归样例
线上监控不应该只生成报警,还要反哺离线回归集。一个简单闭环如下:
线上异常输出
→ 人工标注问题类型
→ 归档为 regression case
→ 绑定 prompt / model / config 版本
→ 加入对应风险分组
→ 下次发布前自动执行
这能避免同类问题反复出现。对于高风险业务,历史事故样例应该拥有最高优先级,任何候选版本都必须通过。
4. 对模型更新做影子验证
如果模型供应商发布新模型,或者同一模型族的行为发生变化,不能只看官方 benchmark。官方指标无法覆盖你自己的提示词、业务规则和输出格式。
更稳妥的做法是将线上流量抽样复制到候选模型中,做 shadow run(影子运行)。用户仍然看到旧模型结果,候选模型结果只进入评估系统。比较维度包括:
- 输出格式差异
- 分类标签差异
- 拒答率变化
- 平均长度变化
- 延迟和成本变化
- 高风险样例通过率
如果候选模型在总体指标上更好,但在某个高风险分组上退化,门禁应该阻断发布,而不是平均后放行。
5. 灰度发布与快速回滚
LLM 行为问题经常具有非确定性,所以离线测试通过不代表生产完全安全。灰度阶段应控制三个变量:用户比例、任务类型、风险等级。
建议灰度顺序:
- 内部用户
- 低风险任务
- 小比例真实用户
- 扩展到更多租户或业务线
- 全量 production label 切换
回滚方式应尽量简单。理想情况下,回滚就是把 production label 从 prompt_version: 18 切回 version: 17,同时把模型 routing 配置恢复到上一个稳定快照。不要把回滚依赖在重新发版上。
适用场景
这套方法适合以下类型的 LLM 应用:
- 客服、工单、质检、审核等需要稳定业务口径的系统
- 需要固定输出格式的 Agent、工作流和工具调用链路
- 有多团队共用提示词平台或模型网关的企业内部系统
- 需要频繁调整提示词、模型、检索配置的 RAG 或 Agent 应用
- 涉及合规、安全、财务、医疗、法律等高风险边界的场景
如果只是个人实验或一次性内容生成,完整门禁可能过重。但只要输出进入业务流程,至少应该保存版本、样例和回滚点。
常见误区
误区一:只要模型升级,效果一定更好
新模型可能整体更强,但不代表你的业务场景一定更稳。某些模型更新会改变拒答风格、结构化输出习惯、长文本抽取策略或安全边界。上线前必须用自己的样例验证。
误区二:用一个总分决定发布
总分会掩盖局部风险。高风险场景应该独立设门槛,甚至采用一票否决。例如支付建议、合同条款解释、医疗建议、权限判断这类场景,不适合被平均分稀释。
误区三:所有检查都交给 LLM 评分器
LLM 评分器适合语义质量判断,但不适合替代所有测试。格式、枚举、schema、代码编译、敏感词、字段完整性、工具参数,都应优先用确定性规则。
误区四:提示词修改不需要发布流程
提示词就是生产逻辑。提示词的一句话变化,可能改变拒答边界、输出格式、责任归因和业务策略。越是核心业务提示词,越应该有版本、审批、回归和回滚。
上线检查清单
发布新的模型、提示词或关键配置前,至少检查以下事项:
- ✅
prompt_name、version、label、owner已记录 - ✅ 修改说明和预期影响已填写
- ✅ 模板变量、默认值和缺失变量处理已验证
- ✅ 最小回归集已通过
- ✅ 高风险样例单独通过,不被平均分覆盖
- ✅ 格式和 schema 检查使用确定性规则
- ✅ 已记录模型名称、快照或可用的版本信息
- ✅ 已完成 shadow run 或小流量灰度
- ✅ 已设置质量、延迟、成本和拒答率监控
- ✅ 回滚目标明确,回滚不依赖重新部署
FAQ
Q: LLM 行为漂移一定来自模型供应商吗?
不一定。模型供应商侧更新只是原因之一。提示词修改、检索结果变化、工具返回结构变化、安全策略调整、上下文截断方式变化,都可能导致行为漂移。
Q: 兼容性门禁是否会拖慢提示词迭代?
会增加一点流程成本,但它减少的是线上试错成本。低风险提示词可以走轻量门禁,高风险提示词必须走完整门禁。关键是按风险分级,而不是一刀切。
Q: 没有大型评测平台时怎么开始?
从 30~50 个高质量样例开始即可。用脚本保存输入、期望输出、检查规则和运行结果,再逐步接入 Langfuse、LangSmith、OpenAI Evals 或内部平台。