GPTMap

Prompt 评测与失败排查实战:LLM-as-judge + 回归测试 + Debug 模式

Prompt 上 production 后怎么做评测:LLM-as-judge 自动打分、回归测试集、失败 case 分类(幻觉 / 偏题 / 格式错误)、Debug 模式 + token / cost 可视化。

TL;DR
Prompt 写好只是开始——上 production 后你需要:(1) LLM-as-judge 自动打分(GPT-5.6 Sol 当裁判,pairwise / score 模式);(2) 回归测试集(50-200 case 含边界 + 失败 case,避免 prompt 改一个 case 跑坏其他);(3) 失败 case 分类(幻觉 / 偏题 / 格式错误 / 长度超限,每个对应不同修法);(4) Debug 模式(log 完整 prompt + response + token + latency,定位时一目了然);(5) 成本可视化(每个 prompt 的 avg token / 月成本趋势)。本文给一个完整的『Prompt 上线 + 持续监控』pipeline。
Prompt 评测与失败排查是指在 Prompt 上 production 后,用 LLM-as-judge + 回归测试 + 失败 case 分类 + Debug 日志,对 prompt 的输出质量、成本、稳定性做持续监控与迭代的过程,区别于一次性 prompt 编写。

操作步骤

  1. 准备回归测试集

    收集 50-200 个测试 case:30% happy path + 40% 边界 case + 30% 历史失败 case。JSONL 格式 git 版本化,每条含 input / expected_output / category。

  2. 配 LLM-as-judge

    用 GPT-5.6 Sol 当 judge,写明确的 rubric prompt:『请基于以下维度评分(0-10):准确性 / 流畅度 / 风格 / 格式合规。给出每个维度分数 + 总体评分 + 1 句话理由。』

  3. 上线 Debug 日志

    每次调用记录:input hash、完整 prompt、output、token usage、latency、模型版本、judge score。结构化 JSON 存 S3 / 数据库。

  4. 接成本监控

    每天算每个 prompt 的 avg token × 调用次数 × 模型费率 = 月成本趋势。超阈值告警。

  5. A/B 上线 + 灰度

    新 prompt 走 A/B 流量 10% → 1-2 周 → 全量上线。每次上线必须跑回归集 + 看 judge score + 业务指标三重确认。

Prompt 写好只是开始——上 production 后你需要:LLM-as-judge 自动打分、回归测试集、失败 case 分类、Debug 日志、成本可视化。本文给一个完整的『Prompt 上线 + 持续监控』pipeline。

为什么需要评测?

一个 Prompt 上 production 后面临三个问题:

  1. 稳定性:模型版本更新 / prompt 微调可能导致输出漂移。
  2. 回归:改了 prompt 想让某个 case 变好,可能跑坏 5 个其他 case。
  3. 失败排查:用户报告『AI 答错了』——具体错在哪?怎么修?

评测不是『写完 prompt 就完事』,是产品日常。

1. LLM-as-judge 自动打分

让 GPT-5.6 当裁判给 prompt 输出打分。关键原则:judge 用更强的模型(Sol),选手用什么模型都行。

模式 A:score(0-10 打分)

import openai

client = openai.OpenAI()

JUDGE_PROMPT = """
你是 prompt 输出评测专家。给定 input、prompt 输出、参考 answer,按 0-10 分评分:

- 0-3:完全错误 / 跑题 / 幻觉
- 4-6:部分正确但有显著问题
- 7-9:基本正确,有小瑕疵
- 10:完全正确且符合 prompt 风格

输出 JSON:{"score": <int>, "reason": "<一句话>"}

Input: {input}
Output: {output}
Reference: {reference}
"""

def judge(input_text, output, reference):
    response = client.responses.create(
        model="gpt-5.6-sol",  # 用 Sol 当 judge
        input=[{
            "role": "user",
            "content": JUDGE_PROMPT.format(
                input=input_text,
                output=output,
                reference=reference,
            ),
        }],
        text={
            "format": {
                "type": "json_schema",
                "name": "judge_result",
                "strict": True,
                "schema": {
                    "type": "object",
                    "properties": {
                        "score": {"type": "integer", "minimum": 0, "maximum": 10},
                        "reason": {"type": "string"},
                    },
                    "required": ["score", "reason"],
                    "additionalProperties": False,
                },
            },
        },
    )
    return json.loads(response.output_text)

# 用法
result = judge("什么是 GPT-5.6?", "GPT-5.6 是 OpenAI 2026-07-09 发布的旗舰模型家族。", "OpenAI 旗舰模型")
print(result["score"], result["reason"])

模式 B:pairwise(A vs B)

新 prompt 改版时常用——A vs B 哪个更好?

PAIRWISE_PROMPT = """
你是 prompt 输出评测专家。给定同一 input 的两个输出(A 和 B),判断哪个更好。

评估维度:准确性 / 流畅度 / 风格 / 格式合规。
胜者标记为 'A' / 'B' / 'tie'。
理由 1 句话。

Input: {input}
Output A: {output_a}
Output B: {output_b}
"""

def pairwise(input_text, output_a, output_b):
    response = client.responses.create(
        model="gpt-5.6-sol",
        input=[{
            "role": "user",
            "content": PAIRWISE_PROMPT.format(
                input=input_text,
                output_a=output_a,
                output_b=output_b,
            ),
        }],
        text={
            "format": {
                "type": "json_schema",
                "name": "pairwise_result",
                "strict": True,
                "schema": {
                    "type": "object",
                    "properties": {
                        "winner": {"type": "enum", "values": ["A", "B", "tie"]},
                        "reason": {"type": "string"},
                    },
                    "required": ["winner", "reason"],
                    "additionalProperties": False,
                },
            },
        },
    )
    return json.loads(response.output_text)

用法:跑 50+ case,新 prompt 在 pairwise 里赢 ≥ 60% 才考虑上线。

模式 C:rubric(多维度)

RUBRIC_PROMPT = """
你是 prompt 输出评测专家。给定 input、output,按以下 rubric 评分(每个 0-10):

1. 准确性(factual correctness):输出事实是否正确?
2. 流畅度(fluency):语言是否自然?
3. 风格匹配(style match):是否符合 prompt 要求的风格?
4. 格式合规(format compliance):是否符合 prompt 要求的格式?

每个维度给分 + 1 句话理由。最后给总体评分 = 加权平均(准确性 0.4 + 流畅度 0.2 + 风格 0.2 + 格式 0.2)。

输出 JSON 严格按 schema。

Input: {input}
Output: {output}
"""

用法:需要细分质量时用——比如客服 prompt 准确性最重要(40% 权重),其他维度均匀。

2. 回归测试集

每次改 prompt 之前必跑回归,避免跑坏老 case。

# tests/prompts/customer-support.jsonl
{"id": "happy-001", "input": "我的订单 12345 还没收到", "category": "happy", "expected": "查询订单状态后回复"}
{"id": "happy-002", "input": "怎么退订?", "category": "happy", "expected": "说明退订流程"}
{"id": "edge-001", "input": "我要投诉!", "category": "edge", "expected": "道歉 + 转接人工"}
{"id": "edge-002", "input": "1111111111111111 是订单号吗?", "category": "edge", "expected": "提示用户订单号格式错误"}
{"id": "failure-001", "input": "OpenAI 是哪家公司?", "category": "failure", "expected": "正确回答 OpenAI 公司信息"}
{"id": "failure-002", "input": "GPT-5.6 发布日期?", "category": "failure", "expected": "2026-07-09,不编造"}

配置

  • 数量:50-200 case
  • 分布:30% happy path + 40% 边界 case + 30% 历史失败 case
  • 格式:JSONL,每行一个 case,含 input / category / expected
  • 存储:git 版本化(tests/prompts/*.jsonl
import json

def run_regression(prompt_template, test_file, judge_fn):
    with open(test_file) as f:
        cases = [json.loads(line) for line in f]

    results = []
    for case in cases:
        output = run_prompt(prompt_template, case["input"])
        score = judge(case["input"], output, case["expected"])
        results.append({
            "id": case["id"],
            "category": case["category"],
            "score": score["score"],
            "reason": score["reason"],
        })

    avg_score = sum(r["score"] for r in results) / len(results)
    failed = [r for r in results if r["score"] < 7]

    print(f"Avg score: {avg_score:.2f}")
    print(f"Failed cases: {len(failed)}/{len(results)}")
    for r in failed:
        print(f"  {r['id']}: {r['reason']}")

    return avg_score, failed

3. 失败 case 分类

每次失败 case 必须归类:

类别现象修法
幻觉模型编造事实加 grounding 上下文 + RAG
偏题答非所问prompt 加约束 + few-shot
格式错误输出不符合 schemastrict JSON Schema
长度超限输出过长 / 过短设 max_tokens 或摘要 prompt
语气不符风格 / 语气不对prompt 明确语气 + few-shot 示例
超时latency > 阈值换小模型 + streaming

自动分类器

FAILURE_CATEGORIES = ["幻觉", "偏题", "格式错误", "长度超限", "语气不符", "超时"]

def classify_failure(input_text, output, reference):
    response = client.responses.create(
        model="gpt-5.6-sol",
        input=[{
            "role": "user",
            "content": f"""判断下面 prompt 输出属于哪种失败:

Input: {input_text}
Output: {output}
Reference: {reference}

失败类别:{FAILURE_CATEGORIES}
""",
        }],
        text={
            "format": {
                "type": "json_schema",
                "name": "failure_classification",
                "strict": True,
                "schema": {
                    "type": "object",
                    "properties": {
                        "category": {"type": "enum", "values": FAILURE_CATEGORIES},
                        "confidence": {"type": "number", "minimum": 0, "maximum": 1},
                    },
                    "required": ["category", "confidence"],
                    "additionalProperties": False,
                },
            },
        },
    )
    return json.loads(response.output_text)

# 用法:自动归档失败 case
result = classify_failure(input, output, reference)
# {"category": "幻觉", "confidence": 0.92}

4. Debug 模式

每次调用必 log 关键字段:

import hashlib
import json
import time

def run_prompt_with_debug(prompt_template, input_data, **kwargs):
    # 计算 input hash(用于去重 + 关联回归集)
    input_hash = hashlib.md5(json.dumps(input_data, sort_keys=True).encode()).hexdigest()

    # 渲染 prompt
    full_prompt = prompt_template.format(**input_data)

    # 调用 + 计时
    start = time.time()
    response = client.responses.create(
        model="gpt-5.6-terra",
        input=[{"role": "user", "content": full_prompt}],
        **kwargs,
    )
    latency_ms = (time.time() - start) * 1000

    # Log 完整数据
    log_entry = {
        "timestamp": datetime.utcnow().isoformat(),
        "input_hash": input_hash,
        "input": input_data,
        "full_prompt": full_prompt,    # 完整 prompt,不要只 log diff
        "output": response.output_text,
        "token_usage": {
            "input_tokens": response.usage.input_tokens,
            "output_tokens": response.usage.output_tokens,
            "cached_tokens": response.usage.input_tokens_details.cached_tokens,
        },
        "latency_ms": latency_ms,
        "model": response.model,
        "model_version": "gpt-5.6-terra-2026-08-08",  # 锁定具体版本
    }

    # 自动跑 judge + 失败 case 归档
    if "reference" in input_data:
        judge_result = judge(input_data, response.output_text, input_data["reference"])
        log_entry["judge_score"] = judge_result["score"]

        if judge_result["score"] < 7:
            # 失败 case 自动归档
            archive_failure(log_entry)

    save_log(log_entry)
    return response.output_text

关键字段

  • input_hash:去重 + 关联回归集
  • full_prompt:完整 prompt(不要只 log diff,定位时需要)
  • token_usage:input + cached + output 各自计数
  • latency_ms:从调用开始到拿到 response
  • model_version:锁定具体版本(不是 alias)
  • judge_score:自动打分

5. 成本可视化

每个 prompt 算月成本:

# 假设
prompt_id = "customer-support-v3"
monthly_calls = 100_000
avg_input_tokens = 800
avg_output_tokens = 200
cache_hit_rate = 0.4  # 40% 调用命中 cache
model = "gpt-5.6-terra"

# GPT-5.6 Terra: $2.50/$15 per MTok, cache 1/4
non_cached_input_cost = avg_input_tokens * (1 - cache_hit_rate) * 2.50 / 1_000_000
cached_input_cost = avg_input_tokens * cache_hit_rate * (2.50 / 4) / 1_000_000
output_cost = avg_output_tokens * 15 / 1_000_000

cost_per_call = non_cached_input_cost + cached_input_cost + output_cost
monthly_cost = cost_per_call * monthly_calls

print(f"Cost per call: ${cost_per_call:.4f}")
print(f"Monthly cost: ${monthly_cost:.2f}")

# 监控指标
# - 月成本趋势(升幅 > 30% 告警)
# - 单次成本 > 阈值告警(可能被滥用)
# - Cache hit rate 下降告警(prompt 可能变了)

6. A/B 上线 + 灰度

新 prompt 上线流程:

本地回归集 OK  →  A/B  10% 流量 1 周  →  灰度 50% 流量 1 周  →  全量上线
                ↑ judge score +0.5+       ↑ 业务指标对齐          ↑
def ab_test_prompts(input_data, old_prompt, new_prompt, traffic_split=0.5):
    """A/B test with traffic split"""
    bucket = hash(input_data["user_id"]) % 100  # 0-99
    if bucket < traffic_split * 100:
        # A bucket
        return run_prompt(old_prompt, input_data, log_extra={"variant": "A"})
    else:
        # B bucket
        return run_prompt(new_prompt, input_data, log_extra={"variant": "B"})

A/B 期间必看指标

  • judge score(必须新 prompt ≥ 旧 +0.5)
  • 业务指标(用户满意度、任务完成率、跳出率)
  • 成本(不能涨 > 50%)

A/B 1-2 周新 prompt 在所有指标上不弱于旧,才能全量上线。

7. 完整 pipeline 示意

[Prompt 改版]
     ↓
[本地回归集跑一遍] ← tests/prompts/*.jsonl
     ↓
[judge score 全部 ≥ 7]? ─ 否 → 改 prompt 重试
     ↓ 是
[A/B 10% 流量]
     ↓
[1 周后看指标] ─ judge / 业务 / 成本 都对齐?─ 否 → 回滚
     ↓ 是
[灰度 50% 流量]
     ↓
[1 周后全量上线]

常见问题

1. LLM-as-judge 和人工评测哪个准?

两种方法都要用:(1) LLM-as-judge 跑量大、成本低、自动化——适合每日千+ 次评测;(2) 人工评测更准但贵——适合每月 50-200 个关键 case 做 calibration。最佳实践:LLM-as-judge 做日常 gate,人工评测每 2 周做一次『校准』——比较 judge score 与人工 score 的 correlation,目标 > 0.8。correlation < 0.7 说明 judge prompt 写得不对,需要重写。

2. 回归测试集放哪里?

三个位置:(1) 代码 repo 里 tests/prompts/<feature>.jsonl —— git 版本化 + CI 自动跑;(2) Prompt 管理平台(OpenAI 没有官方平台,但 Anthropic Prompt Manager / Humanloop / LangSmith 可以);(3) 数据库表——灵活但 git 难追。生产推荐 (1) + (2):repo 里存核心 happy path + 失败 case 快照,平台存完整历史 + 元数据。

3. 怎么判断 prompt 改版是好是坏?

三步:(1) 在回归测试集上跑新旧两版 prompt,比较 judge score(必须 score up +0.5+ 且不破老 case);(2) A/B 流量 10% 上线新 prompt,监控业务指标(用户满意度 / 任务完成率 / 跳出率);(3) 灰度 1-2 周后全量上线。注意:A/B 阶段必须新旧 prompt 同时服务同一群用户,否则用户分布偏差会掩盖效果差异。

4. 幻觉类失败 case 怎么排查?

三步:(1) 找到幻觉的具体事实——模型说了什么、事实是什么、官方源在哪;(2) 检查 prompt 里有没有 grounding 上下文(『请基于以下文档回答』+ 文档原文);(3) 加 RAG——让模型先去检索相关文档再回答。常见反模式:让模型凭记忆回答开放事实(『GPT-5.6 的发布日期是哪天』)——这种问题必须接 RAG 或 knowledge base。

5. 评测 prompt 的成本怎么算?

三层:(1) 评测本身成本——LLM-as-judge 调用 × 输入 token × judge 模型费率;(2) 被评测 prompt 的成本——运行成本;(3) 评测频率成本——每天跑回归集 × token。100 case × 500 input token × $2.50/MTok(GPT-5.6 Terra)= $0.125/day = $3.75/月。GPT-5.6 Sol 当 judge 会贵 2-3 倍。建议:日常回归用 Luna/Sol(便宜 + 准),critical gate 用 Sol。

下一步

关键要点

  • LLM-as-judge 三种模式:(1) score(0-10 打分)+ threshold;(2) pairwise(A vs B 谁更好)+ ELO 排名;(3) rubric(多维度评分如准确性 / 流畅度 / 风格)+ 加权汇总。GPT-5.6 Sol 当裁判与 GPT-5.6 Terra 当选手,judge 需要更强的 reasoning
  • 回归测试集必须 50+ case,包含 happy path + 边界 case + 历史失败 case。每次 prompt 改完跑一次,确认新 prompt 没破老 case。建议用 JSONL 格式 + git 版本化
  • 失败 case 四类:(1) 幻觉(模型编造事实)——加 grounding 上下文;(2) 偏题(答非所问)——prompt 加约束或 few-shot;(3) 格式错误(输出不符合 schema)——strict JSON Schema;(4) 长度超限——设 max_tokens 或摘要 prompt
  • Debug 模式必 log 的字段:input hash、完整 prompt(不要只 log diff)、output、token usage(input / output / cached)、latency、模型版本、judge score。每次失败 case 自动归档到 regression set
  • 成本可视化:每个 prompt 算 avg input / output token × 调用次数 × 模型费率 = 月成本。趋势监控:某 prompt 月成本 > 阈值 → 告警(可能被滥用或 prompt 变长)

常见问题

两种方法都要用:(1) LLM-as-judge 跑量大、成本低、自动化——适合每日千+ 次评测;(2) 人工评测更准但贵——适合每月 50- 200 个关键 case 做 calibration。最佳实践:LLM-as-judge 做日常 gate,人工评测每 2 周做一次『校准』——比较 judge score 与人工 score 的 correlation,目标 > 0.8。correlation < 0.7 说明 judge prompt 写得不对,需要重写。

官方参考

相关文章

订阅 GPTMap Weekly

每周一封邮件,精选 OpenAI 重要更新、深度解读与最佳实践。无广告,可随时退订。

GPTMap Editorial发布于 2026-08-17 13 分钟阅读
测试环境(EEAT)
最后测试时间:2026-08-17
使用模型:gpt-5.6