Системный подход к отладке
Двоичный поиск по разделам запроса: удаляйте половину, проводите тестирование и сужайте область поиска проблемы.
«Системный подход к отладке» — бесплатный урок AI Prompt Engineering на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения 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 для сеанса отладки запроса имеет следующую структуру:
- Одно предложение с описанием роли (если необходимо)
- Одно предложение с инструкцией по задаче
- Инструкция по формату
- Минимальные входные данные, воспроизводящие сбой
Если этот четырёхстрочный запрос по-прежнему даёт сбой, проблема заключается в модели или формате. Возвращайте сложность по одному разделу за раз, пока сбой не появится снова — этот раздел и является причиной.
# 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-сравнение: сравните альтернативные формулировки проблемного раздела
- Сопоставьте: сравните рабочую и нерабочую версии запроса, чтобы найти регрессию
- Документируйте: заносите в журнал каждую проверку, гипотезу и результат
- Сделайте устойчивее: добавьте исправленный случай в набор проверок
Следующий урок: стратегии журналирования и документирования для долгосрочного сопровождения запросов.
Часто задаваемые вопросы
Урок «Системный подход к отладке» бесплатный?
Да — полный текст урока «Системный подход к отладке» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Prompt Engineering, подпишись на CoddyKit PRO. Курс AI Prompt Engineering содержит 4 уроков всего.
Чему я научусь в уроке «Системный подход к отладке»?
Двоичный поиск по разделам запроса: удаляйте половину, проводите тестирование и сужайте область поиска проблемы. Ты практикуешь AI Prompt Engineering с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать AI Prompt Engineering?
Предыдущий опыт не требуется. AI Prompt Engineering на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Системный подход к отладке»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке AI Prompt Engineering?
Да. Каждый урок AI Prompt Engineering включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Диагностика неожиданных результатов
- Анализ первопричин запросов
- Системный подход к отладке
- Стратегии журналирования и документирования