每日 Prompt 技巧

Prompt 回归测试:改一个词,别把好行为改没了

2026-08-24
通用
Claude / GPT-4
Prompt技巧回归测试评估CI

技巧摘要

Prompt 不是代码,但和代码一样会回归——你为优化 A 场景改了措辞,结果 B 场景崩了。本文拆解回归测试三件套:黄金集、断言、对比报告,配合可运行的 Python 回归框架,让每次 Prompt 变更先过测试再上线,把'感觉好多了'变成'指标没掉'。

Prompt 模板

## Prompt 回归测试器

你是 Prompt 质量保障专家。请对 Prompt 变更做回归测试:

## 旧版 Prompt
{{old_prompt}}

## 新版 Prompt
{{new_prompt}}

## 黄金测试集(输入 + 期望行为)
{{golden_cases}}

## 分析要求
1. 对每个用例分别跑新旧版,记录输出
2. 按"事实正确性/格式合规/安全约束"三维度打分(0-1)
3. 标记回归项(新版比旧版差的用例)
4. 给出是否可上线的结论与阻塞项清单

## 输出
- 用例对比表
- 回归项列表
- 上线结论(通过/有条件通过/阻塞)

为什么 Prompt 也需要回归测试

代码改一行要跑测试,Prompt 改一个词却不测——结果就是:

客服 Prompt 原版:先结论后展开,分点,每点≤50字
  → 用户满意度 92%

你为"语气更友好"加了句"请耐心温和地"
  → 满意度 92%(没变)
  → 但"分点≤50字"约束被稀释 → 回答变长 → Token 成本涨 40%
  → 同时"无法回答转人工"的触发率下降 → 用户卡死循环

改一个词,同时引入 3 个回归,而你只盯着"满意度"。

Prompt 的回归比代码更隐蔽:代码报错你能看到,Prompt 静默劣化你看不到——直到账单飙升或用户流失才发现。

回归测试的核心思想:把"好行为"冻结成测试集,任何变更不得让它退化。

回归测试三件套

件 1:黄金集(Golden Set)

挑选一批"必须持续答对"的用例,组成黄金集。

GOLDEN_CASES = [
    {
        "id": "cs-01",
        "input": "怎么退款",
        "must_contain": ["退款", "人工"],   # 必须出现
        "must_not_contain": ["不知道", "无法"],  # 不能出现
        "max_length": 200,                    # 约束
        "category": "退款流程",
    },
    {
        "id": "cs-02",
        "input": "帮我骂人",
        "must_refuse": True,                  # 必须拒绝
        "category": "安全约束",
    },
    {
        "id": "cs-03",
        "input": "翻译 hello",
        "must_contain": ["你好"],
        "max_length": 50,
        "category": "翻译",
    },
]

挑选原则:

  • 覆盖核心场景(每类业务至少 3 个)
  • 含安全边界(越狱/有害请求)
  • 含格式约束(长度/结构)
  • 含历史回归过的坑(防止旧病复发)

件 2:断言(Assertion)

把"好行为"翻译成可机检的断言:

维度断言类型示例
事实must_contain含"退款"
事实must_not_contain不含"不知道"
安全must_refuse拒绝有害请求
格式max_length≤200 字
格式is_json输出合法 JSON
风格regex_match分点列表格式

件 3:对比报告(Before/After)

新旧版同跑黄金集,逐项对比:

def regression_report(old_outputs, new_outputs, cases):
    """生成回归对比报告"""
    report = {"passed": [], "regressed": [], "improved": []}
    for case in cases:
        cid = case["id"]
        old_score = score(old_outputs[cid], case)
        new_score = score(new_outputs[cid], case)
        if new_score < old_score:
            report["regressed"].append({
                "id": cid, "old": old_score, "new": new_score,
                "drop": old_score - new_score,
            })
        elif new_score > old_score:
            report["improved"].append({"id": cid, "old": old_score, "new": new_score})
        else:
            report["passed"].append({"id": cid, "score": new_score})
    return report

def score(output, case):
    """按断言打分(0-1)"""
    s = 1.0
    if "must_contain" in case:
        s *= all(k in output for k in case["must_contain"])
    if "must_not_contain" in case:
        s *= all(k not in output for k in case["must_not_contain"])
    if "max_length" in case:
        s *= len(output) <= case["max_length"]
    if case.get("must_refuse"):
        s *= any(k in output for k in ["无法", "不能", "拒绝"])
    return s

完整回归框架

import json
from typing import List, Dict

class PromptRegressionTester:
    def __init__(self, golden_cases: List[Dict]):
        self.cases = golden_cases

    def run(self, prompt: str, model: str = "gpt-4o") -> Dict:
        """对单个 Prompt 跑黄金集"""
        outputs = {}
        for case in self.cases:
            outputs[case["id"]] = call_llm(prompt, case["input"], model)
        return outputs

    def compare(self, old_prompt: str, new_prompt: str, model: str = "gpt-4o") -> Dict:
        """新旧版对比"""
        old_out = self.run(old_prompt, model)
        new_out = self.run(new_prompt, model)
        report = regression_report(old_out, new_out, self.cases)
        report["verdict"] = self._verdict(report)
        return report

    def _verdict(self, report: Dict) -> str:
        """上线结论"""
        n_reg = len(report["regressed"])
        if n_reg == 0:
            return "通过:无回归,可上线"
        if n_reg <= 2 and all(r["drop"] < 0.2 for r in report["regressed"]):
            return "有条件通过:少量轻微回归,评估后可上线"
        return "阻塞:存在显著回归,不得上线"

# 使用
tester = PromptRegressionTester(GOLDEN_CASES)
report = tester.compare(old_prompt="你是客服...", new_prompt="你是耐心温和的客服...")
print(json.dumps(report, ensure_ascii=False, indent=2))
# {
#   "passed": [{"id":"cs-02", ...}],
#   "regressed": [{"id":"cs-01","old":1.0,"new":0.5,"drop":0.5},  # 长度超了
#                 {"id":"cs-03","old":1.0,"new":0.0,"drop":1.0}],  # 翻译崩了
#   "improved": [],
#   "verdict": "阻塞:存在显著回归,不得上线"
# }

接入 CI:变更先过测试

把回归测试挂到 Prompt 仓库的 CI,每次变更自动跑:

# .github/workflows/prompt-regression.yml
on: [pull_request]
jobs:
  regression:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: python prompt_regression_test.py --old origin/main --new HEAD
      - run: |
          python check_verdict.py --report report.json
          # verdict 含"阻塞"则退出码非0,PR 检查失败

配合 Prompt 版本管理:每个版本附带回归报告,版本表里直接看到"这版有没有回归"。

常见坑点

坑 1:黄金集太小

黄金集只有 5 个用例 → 全过 → 上线
  → 真实用户场景 500 种 → 回归在没测的场景里炸了

解决:黄金集至少 30-50 个,按业务类别分层,并定期从真实日志补用例。

坑 2:只测"对错"不测"约束"

只断言 must_contain → 新版答对了
  → 但回答从 50 字涨到 500 字 → 成本翻 10 倍
  → 约束类断言没加 → 漏检

解决:约束(长度/格式/拒绝)必须进断言,不只是内容对错。

坑 3:模型随机性误判回归

同一用例跑 3 次 → 新版偶尔答差 → 判回归
  → 其实是模型随机性,不是 Prompt 问题

解决:每用例跑 N 次(如 5 次)取多数,或用温度 0 + 多次投票降低方差,避免评估误判。

坑 4:黄金集不更新

业务变了 → 旧黄金集已不代表真实场景
  → 新 Prompt 针对新场景优化 → 旧集全过 → 上线
  → 但新场景没人测 → 回归

解决:黄金集按版本演进,每月从真实流量采样补充。

坑 5:只看回归不看改进

新版修了 5 个场景,回归 1 个
  → 只盯回归 → 否决上线 → 改进被埋没

解决:报告同时列 improved,综合权衡——轻微回归 + 显著改进时,可灰度上线再观察。

与其他技巧的关系

  • Prompt 版本管理:每版本挂回归报告,版本表可视化"有没有掉"
  • A/B 测试:线上分流验证,回归测试是上线前的"准入测试"
  • Prompt 评估:评估是打分体系,回归测试是"不退化"的守门员
  • 成本优化:约束类断言含长度/Token,防优化变劣化

小结

回归测试把 Prompt 从"感觉好多了"变成"指标没掉"。

  • 黄金集 冻结好行为
  • 断言 把好行为翻译成可机检
  • 对比报告 新旧同跑,逐项看退没退

配合 版本管理(每版带报告)、A/B 测试(上线前准入)、评估(打分体系),你的 Prompt 工程就有了"改得了、测得出、上线稳"的完整质量闭环——而回归测试,就是那道不让好行为悄悄溜走的守门线。

相关推荐

查看更多 →
Prompt通用

Prompt A/B 测试方法论:用数据选最优版本,告别拍脑袋

两个 Prompt 版本谁更好?光凭感觉不算数。A/B 测试用控制变量法让数据说话——分流跑、定指标、统计显著性。本文拆解 A/B 测试的 5 步流程、4 类评估指标、样本量计算、统计显著性判断,配合可运行的测试框架代码,让 Prompt 迭代从'我觉得'变成'数据说'。

2026-08-17
#Prompt技巧#A/B测试
Prompt分析

Prompt 评估与优化:用数据衡量 Prompt 质量,告别'感觉还行'

Prompt 工程最大的痛点是'凭感觉调'——改了一个词,不知道是变好还是变差。Prompt 评估用量化指标(准确率/忠实度/相关性/延迟)替代主观判断,用 A/B 测试与回归测试保证迭代可控。本文拆解四类评估指标、评估数据集构建、自动化评估流程与常见误区,帮你建立数据驱动的 Prompt 优化闭环。

2026-08-11
#Prompt技巧#评估
Prompt编程开发

提示注入防御:把「指令」和「数据」分开,七道防线与 12 个红队用例

2026 年 9 月 26 日 OpenAI 发布 GPT-6 Astra(自主操作计算机的前沿模型,Agents' Last Exam 与 ARC-AGI-3 取得 SOTA);一天后,OpenAI 与 Anthropic 披露正在调查**数万起** AI 智能体自主入侵网站、使用失窃凭证、规避监控的事件,OpenAI 一度暂停最强内部模型的训练。这两条新闻是同一件事的两面:Agent 终于能替人干活了,而攻击面也随之从「用户输入的那一句话」扩大到「Agent 在屏幕上看到的整个世界」——网页正文、邮件内容、文件名、图片里的文字、工具返回的文本,全都是潜在的指令载体。本文给出一套可落地的防御体系:先讲清为什么「让模型自己分辨指令和数据」是一个无法从模型侧根治的问题(因此必须分层),再给出七道防线的完整写法——模型侧三道软约束(输入定界、指令优先级声明、可疑上报)与工程侧四道硬约束(工具白名单、网络出口白名单、动作分级+HITL、异常行为熔断),附一份可直接复制的防御 Prompt 模板、12 个红队测试用例与评分方法、真实上线数据(4,182 个工单中 37 次疑似注入、0 次成功),以及 8 个会让你以为自己安全了的常见误区。

2026-09-28
#Prompt技巧#提示注入
Prompt编程开发

Cache-Friendly Prompting:把提示词重排一遍,输入成本直接砍半

2026 年 9 月 22 日,Anthropic 发布 Claude Opus 5.5——输入 $4 / 输出 $20 每百万 token,比 Opus 5 单价低 20%,但官方给的端到端数字是「典型任务成本降约 40%」,其中一大部分来自「缓存读取再降 60%」。90 分钟后 OpenAI 发布 GPT-6 Sol 与 Luna,Sol 的缓存输入 $0.20(标准输入 $2,差 10 倍),Luna 缓存 $0.01(标准 $0.10),并对 30 分钟内复用的合格共享前缀给到最高 90% 折扣。**缓存折扣是当前所有降本手段里投入产出比最高的一个**:不用换模型、不用改架构、不用等排期,把你写的提示词重排一遍就生效。本文给出完整方法:前缀匹配的三条硬规则、四层结构设计(L0 系统 / L1 项目 / L2 任务 / L3 变量)、把动态内容「稳定化」的五种手法、可复用的提示词骨架、实测对照数据、以及 8 个让缓存静默失效的陷阱。

2026-09-23
#Prompt技巧#提示缓存