体系的なデバッグアプローチ
プロンプトの各セクションを二分探索します。半分を削除してテストし、問題を絞り込みます。
「体系的なデバッグアプローチ」はCoddyKit上の無料AI Prompt Engineeringレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応の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削除テスト
二分探索をより簡単にした方法として、各セクションを体系的に1つずつ削除し、削除によって問題が解決するかを確認します。これは、プロンプトが明確に区切られたセクション(system instructions、context、examples、format spec)で構成されている場合に有効です。
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テストとは、1つのセクションについて2つのバージョンを作成し、同じ入力に対する出力を比較することです。削除テストとは異なり、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))差分テスト
差分テストでは、ほぼ同一の2つのプロンプトを比較し、リグレッションの原因となった変更を見つけます。「先週は動作していた」のに、現在は動作しない場合に役立ちます。
古いプロンプトと新しいプロンプトの差分を取り、変更されたセクションを特定してから、変更された各セクションを単独でテストします。
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入力のテストとプロンプトのテスト
テストする対象には、プロンプトと入力の2つの側面があります。プロンプトが単純な入力では機能しても、複雑な入力では失敗する場合があります。便利なデバッグ方法は、複雑な入力でプロンプトが失敗したときに、より単純な入力を試してプロンプト自体に問題がないことを確認することです。
# 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文の役割(必要な場合)
- 1文のタスク指示
- 形式の指示
- 失敗を再現する最小限の入力
この4行のプロンプトでも失敗する場合、問題はモデルまたは形式にあります。失敗が再発するまで、セクションを1つずつ追加して複雑さを戻します。そのセクションが原因です。
# 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テストで統計的に有意な差が見られない
代替策:function calling(構造化出力)に切り替える、後処理の検証ステップを追加する、タスクを2つのより簡単なプロンプトに分解する、またはモデルをアップグレードします。
修正と堅牢化
根本原因を見つけて修正したら、同様の失敗を防ぐためにプロンプトを堅牢化します:
- 失敗したテストケースを回帰テストとしてテストスイートに追加します
- 「入力が通常と異なる場合でも、必ず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)理解度チェック
プロンプトの二分探索デバッグで、プロンプトの前半を削除したところ失敗がなくなりました。これは何を示していますか。
復習:体系的なデバッグ
プロンプトをデバッグする体系的なアプローチ:
- 再現:temperature=0に設定し、5回実行して失敗が一貫して発生することを確認します
- 最小化:プロンプトのセクションを二分探索し、失敗する最小プロンプトを見つけます
- A/Bテスト:失敗するセクションの別の表現を比較します
- 差分:動作するプロンプトと失敗するプロンプトのバージョンを比較し、リグレッションを見つけます
- 記録:各テスト、仮説、結果を記録します
- 堅牢化:修正したケースをテストスイートに追加します
次のレッスンでは、長期的なプロンプト保守のためのログ記録とドキュメント作成の戦略を扱います。
よくある質問
「体系的なデバッグアプローチ」レッスンは無料ですか?
はい。「体系的なデバッグアプローチ」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Prompt Engineeringコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Prompt Engineeringコースには全4レッスンが含まれています。
「体系的なデバッグアプローチ」で何を学びますか?
プロンプトの各セクションを二分探索します。半分を削除してテストし、問題を絞り込みます。 ブラウザで直接実行するハンズオンコードでAI Prompt Engineeringを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
AI Prompt Engineeringを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのAI Prompt Engineeringは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「体系的なデバッグアプローチ」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このAI Prompt Engineeringレッスンでコードを書いて実行できますか?
はい。すべてのAI Prompt Engineeringレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- 予期しない出力を診断する
- プロンプトの根本原因分析
- 体系的なデバッグアプローチ
- ログ記録とドキュメント化の戦略