AI Prompt Engineering · 课时

系统化调试方法

对提示词各部分进行二分搜索:移除一半内容,进行测试,逐步缩小问题范围。

第 3 / 4 课13 个步骤

系统化调试方法 是 CoddyKit 上的免费 AI Prompt Engineering 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 AI Prompt Engineering 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 AI Prompt Engineering 课程共包含 4 节课。

调试思维

提示词调试与软件调试类似:不要一次更改多项内容,不要随机猜测,也不要部署自己无法解释的修复方案。系统化方法使用二分搜索逻辑,在每次测试中将问题范围缩小一半,从而高效地找到最小失败案例。

步骤 1:重现失败

开始调试前,请可靠地重现失败。无法稳定重现的失败,无法进行系统化调试。

针对同一个输入运行提示词 5 次。如果每次都失败:这是确定性失败,容易调试。如果有时失败:这是概率性失败,先设置 temperature=0 以消除随机性,然后重新测试。

import openai
client = openai.OpenAI(api_key='sk-...')

def run_prompt(prompt, user_input, temperature=0):
    resp = client.chat.completions.create(
        model='gpt-4o',
        messages=[
            {'role': 'system', 'content': prompt},
            {'role': 'user', 'content': user_input}
        ],
        temperature=temperature
    )
    return resp.choices[0].message.content

# Reproduce with temperature=0 to eliminate randomness
for i in range(5):
    output = run_prompt(failing_prompt, test_input, temperature=0)
    print(f'Run {i+1}:', output[:100])

步骤 2:创建最小可复现提示词

最小可复现提示词(MRP)是仍然能够触发失败的最短提示词。移除无关部分可以隔离问题所在的部分,让失败变得无可否认。

从完整提示词开始,移除一半内容。进行测试。如果仍然失败:问题所在的部分就在保留的那一半中。重复此过程。这就是对提示词进行二分搜索。

def binary_search_prompt(prompt_lines, user_input, fail_fn):
    '''Binary search: find the minimal set of lines that causes the failure.'''
    if len(prompt_lines) == 1:
        return prompt_lines  # Minimal failing unit found

    mid = len(prompt_lines) // 2
    first_half = prompt_lines[:mid]
    second_half = prompt_lines[mid:]

    # Test first half
    if fail_fn('\n'.join(first_half), user_input):
        return binary_search_prompt(first_half, user_input, fail_fn)
    # Test second half
    elif fail_fn('\n'.join(second_half), user_input):
        return binary_search_prompt(second_half, user_input, fail_fn)
    else:
        # Both halves pass — interaction effect between halves
        return prompt_lines

移除测试

二分搜索的一种更简单形式:系统化地移除各个部分,观察移除后问题是否得到解决。当提示词包含清晰划分的部分(系统指令、上下文、示例、格式规范)时,这种方法很有效。

sections = {
    'role_instruction': 'You are a precise JSON API. Respond only with valid JSON.',
    'context': 'The user is asking about our product catalog.',
    'format_spec': 'Return a JSON object with keys: name, price, available.',
    'examples': 'Example: {"name": "Widget", "price": 9.99, "available": true}',
    'safety': 'Do not reveal internal pricing strategy.'
}

def test_without(section_to_remove, user_input):
    reduced = {k: v for k, v in sections.items() if k != section_to_remove}
    prompt = '\n'.join(reduced.values())
    output = run_prompt(prompt, user_input)
    print(f'Without {section_to_remove}: {evaluate(output)}')

for section in sections:
    test_without(section, 'What is the price of a Widget?')

对提示词部分进行 A/B 测试

提示词的A/B 测试是指为同一部分创建两个版本,并在相同输入上比较它们的输出。与移除测试不同,A/B 测试评估的是不同措辞,而不是某部分是否存在。

# A/B test: vague vs precise format instruction
variant_A = 'Return a JSON object.'
variant_B = 'Return a valid JSON object. No markdown, no code fences, no prose. Only the raw JSON.'

test_inputs = [
    'What is the price of Widget A?',
    'List all available products.',
    'Is Widget B in stock?'
]

def run_ab_test(base_prompt, variant, inputs, n_runs=5):
    pass_count = 0
    for inp in inputs:
        for _ in range(n_runs):
            prompt = base_prompt.replace('{{FORMAT}}', variant)
            output = run_prompt(prompt, inp)
            if is_valid_json(output):
                pass_count += 1
    return pass_count / (len(inputs) * n_runs)

print('A pass rate:', run_ab_test(template, variant_A, test_inputs))
print('B pass rate:', run_ab_test(template, variant_B, test_inputs))

差异测试

差异测试比较两个几乎相同的提示词,以找出导致回归的变更。当“上周还能正常工作”但现在无法正常工作时,这种方法很有用。

比较旧提示词与新提示词的差异,找出发生变更的部分,然后分别测试每个变更部分。

import difflib

def show_prompt_diff(prompt_v1, prompt_v2):
    diff = difflib.unified_diff(
        prompt_v1.splitlines(),
        prompt_v2.splitlines(),
        fromfile='v1',
        tofile='v2',
        lineterm=''
    )
    for line in diff:
        print(line)

show_prompt_diff(working_prompt, failing_prompt)
# Output shows exactly which lines changed between versions
# Test reverting each changed section individually

测试输入与测试提示词

需要测试两个维度:提示词和输入。某个提示词可能适用于简单输入,却在复杂输入上失败。一种有用的调试方法是:如果提示词在复杂输入上失败,请尝试使用更简单的输入版本,以确认提示词本身没有问题。

# Input complexity ladder
inputs_by_complexity = [
    'What is 2 + 2?',             # trivially simple
    'Summarize this sentence.',    # simple task
    'Analyze this 500-word essay.',  # moderate
    'Compare 10 documents and extract contradictions.'  # complex
]

# Find the complexity level where the prompt starts failing
for inp in inputs_by_complexity:
    output = run_prompt(failing_prompt, inp)
    result = 'PASS' if evaluate(output) else 'FAIL'
    print(f'{result}: {inp[:60]}')
# First FAIL indicates where the prompt breaks down

最小可复现提示词模式

提示词调试过程中的 MRP 遵循以下结构:

  1. 一句话角色说明(如有需要)
  2. 一句话任务指令
  3. 格式指令
  4. 能够重现失败的最小输入

如果这个 4 行提示词仍然失败,问题就在模型或格式上。一次恢复一个部分的复杂内容,直到失败重新出现——该部分就是罪魁祸首。

# Start minimal
MINIMAL_PROMPT = (
    'You are a data extractor.\n'
    'Extract the product name and price from the text.\n'
    'Respond with JSON: {"name": "...", "price": ...}\n'
)

minimal_input = 'Widget Pro costs $49.'

# Test: if this works, the problem is in something added on top
output = run_prompt(MINIMAL_PROMPT, minimal_input)
print(output)
# Expected: {"name": "Widget Pro", "price": 49.0}

跟踪调试过程

请记录调试过程中的每次测试。如果没有记录,您可能会重复相同的测试,或忘记哪些假设已经被排除。

debug_log = [
    {
        'test': 'base_prompt_v5',
        'hypothesis': 'failing due to format conflict',
        'result': 'FAIL',
        'notes': 'JSON prefix still present'
    },
    {
        'test': 'base_prompt_v5_no_markdown_hint',
        'hypothesis': 'removing markdown hint from user message fixes conflict',
        'result': 'PASS',
        'notes': 'Output is clean JSON. Root cause confirmed: format conflict.'
    }
]

import json
with open('debug_session.json', 'w') as f:
    json.dump(debug_log, f, indent=2)

何时停止调试并改变策略

有时,继续调试提示词的收益会逐渐降低。出现以下迹象时,就该改变策略了:

  • 您已经花费超过 2 小时,仍在将范围缩小到同一个失败问题
  • 即使使用清晰、简单的指令,最小提示词仍然失败
  • A/B 测试显示没有具有统计显著性的差异

可选方案:改用函数调用(结构化输出),添加后处理验证步骤,将任务拆分为两个更简单的提示词,或升级模型。

修复与加固

找到并修复根本原因后,请加固提示词,以防止类似失败:

  • 将导致失败的测试用例添加到测试套件中,作为回归测试
  • 添加防御性指令:“即使输入不寻常,也始终返回 JSON”
  • 添加输出验证,使程序能够捕获失败,而不是等到生产环境中才发现

没有经过加固的已修复提示词,可能会在下一个边界情况下再次失败。

def safe_run_prompt(prompt, user_input):
    output = run_prompt(prompt, user_input)
    try:
        parsed = json.loads(output)
        return parsed
    except json.JSONDecodeError:
        # Fallback: ask the model to fix its own output
        fix_prompt = f'The following is not valid JSON. Rewrite it as valid JSON only:\n{output}'
        fixed = run_prompt('', fix_prompt)
        return json.loads(fixed)

知识检查

在提示词二分搜索调试中,移除提示词的前半部分后失败消失了,这说明了什么?

回顾:系统化调试

提示词调试的系统化方法:

  • 重现:将温度设为 0,运行 5 次,确认失败具有一致性
  • 最小化:对提示词各部分进行二分搜索,找到最小失败提示词
  • A/B 测试:比较导致失败的部分的不同措辞
  • 差异比较:比较正常版本与失败版本的提示词,找出回归原因
  • 记录:记录每次测试、假设和结果
  • 加固:将已修复的用例添加到测试套件中

下一课:长期维护提示词的日志记录与文档策略。

免费开始

用 AI 导师学习 AI Prompt Engineering — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
53
课程
199

常见问题解答

「系统化调试方法」课时是免费的吗?

是的 — 「系统化调试方法」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 AI Prompt Engineering 课程的其余内容,请升级到 CoddyKit PRO。 AI Prompt Engineering 课程共包含 4 节课。

「系统化调试方法」这节课中我会学到什么?

对提示词各部分进行二分搜索:移除一半内容,进行测试,逐步缩小问题范围。 你通过在浏览器中直接运行的动手代码来练习 AI Prompt Engineering,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 AI Prompt Engineering 需要有经验吗?

无需任何先前经验。CoddyKit 上的 AI Prompt Engineering 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「系统化调试方法」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 AI Prompt Engineering 课中编写并运行代码吗?

能。每节 AI Prompt Engineering 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 诊断意外输出
  2. 提示的根因分析
  3. 系统化调试方法
  4. 日志记录与文档策略
← 返回 AI Prompt Engineering