Self-Correction: Механизмы самопроверки и исправления ошибок
Введение: Почему агенты ошибаются и как это исправить
Добро пожаловать в один из самых критически важных уроков курса. Мы уже научились создавать агентов, которые могут использовать инструменты и планировать действия. Но давайте будем честны: даже самые продвинутые модели, включая Gemini 3, иногда ошибаются. Они могут сгенерировать невалидный JSON, вызвать функцию с неверными аргументами или просто «загаллюцинировать» факт, которого не существует.
В классическом программировании ошибка часто приводит к падению программы (Exception). В мире LLM ошибка может выглядеть как вполне уверенный, но абсолютно неверный ответ. Для Enterprise-решений это недопустимо.
Self-Correction (Самокоррекция) — это способность агента самостоятельно обнаруживать свои ошибки, анализировать их причины и предпринимать попытки исправления без вмешательства человека. Это именно то, что отличает игрушку от надежного бизнес-инструмента. В этом уроке мы разберем архитектурные паттерны, которые превращают вашего агента из «стажера» в «опытного сотрудника», способного проверять свою работу.
Анатомия ошибки и цикл Рефлексии
Прежде чем писать код, давайте разберем механику. Почему модель вообще может исправить сама себя? Если она ошиблась первый раз, почему второй раз будет успешным?
Ответ кроется в изменении контекста. Когда модель генерирует ответ, она работает в режиме «потока». Но если мы попросим её посмотреть на свой предыдущий ответ и найти в нем ошибку, мы переключаем её в режим «аналитика». Это похоже на то, как программист пишет код (быстро, могут быть баги), а затем делает Code Review (медленно, вдумчиво).
Цикл Reflecion (Рефлексия):
- Action (Действие): Агент выполняет задачу (например, пишет SQL-запрос).
- Evaluation (Оценка): Агент или внешний валидатор проверяет результат (запрос не выполнился, база данных вернула ошибку).
- Feedback (Обратная связь): Ошибка подается обратно в контекст модели.
- Refinement (Уточнение): Агент генерирует новое решение, учитывая ошибку.
С Gemini 3, благодаря огромному контекстному окну, мы можем хранить всю историю этих «неудачных попыток», что позволяет модели учиться на ошибках прямо в процессе выполнения задачи.
import google.generativeai as genai
import os
# Настройка клиента (предполагается, что API ключ установлен)
# genai.configure(api_key=os.environ["GEMINI_API_KEY"])
model = genai.GenerativeModel('gemini-1.5-pro-latest')
def generate_with_self_correction(prompt, max_retries=3):
history = []
# Первичный запрос
full_prompt = f"Задача: {prompt}\nРеши задачу и выведи только ответ."
for attempt in range(max_retries):
print(f"--- Попытка {attempt + 1} ---")
# Генерация ответа (в реальном кейсе здесь может быть история чата)
response = model.generate_content(full_prompt)
result = response.text.strip()
print(f"Агент предложил: {result}")
# ЭТАП ВАЛИДАЦИИ (Simulated Validator)
# Представим, что мы просим агента сгенерировать код Python, который выводит 'Hello World'
# Простой валидатор: проверяет, есть ли слово 'print' и запускается ли код
validation_error = None
if "print" not in result:
validation_error = "Ошибка: Код не содержит функции print()."
else:
try:
# В продакшене используйте песочницу для выполнения кода!
exec(result)
print("✅ Проверка пройдена!")
return result
except Exception as e:
validation_error = f"Ошибка выполнения кода: {str(e)}"
# Если мы здесь, значит есть ошибка
print(f"❌ Обнаружена проблема: {validation_error}")
# ЭТАП РЕФЛЕКСИИ
# Добавляем контекст ошибки в следующий промпт
full_prompt = (
f"Предыдущее решение: {result}\n"
f"Сообщение об ошибке: {validation_error}\n"
f"Проанализируй ошибку и предложи исправленное решение. Думай шаг за шагом."
)
return "Не удалось найти решение после нескольких попыток."
# Пример вызова
# Допустим, модель сначала забудет скобки (в Python 2 стиле), а потом исправится
task = "Напиши код на Python 3, который выводит фразу 'Hello Gemini'"
final_result = generate_with_self_correction(task)
Стратегии обработки ошибок инструментов (Tool Use Correction)
Самый частый сценарий для Enterprise-агентов — это использование внешних инструментов (Function Calling). Здесь ошибки делятся на два типа:
- Hallucinated Arguments: Агент придумал аргумент, которого нет в сигнатуре функции.
- Logic/Runtime Errors: Аргументы валидны, но API вернул ошибку (например, «Товар не найден» или «Дата занята»).
В Gemini 3 API механизм Function Calling нативно поддерживает возврат результата функции обратно модели. Это идеальное место для внедрения самокоррекции. Если ваш API возвращает JSON с ошибкой (например, {"status": "error", "msg": "Invalid date format"}), модель Gemini автоматически воспримет это как сигнал к действию, если вы правильно настроите системный промпт.
Ключевая техника: Никогда не скрывайте Traceback от модели. В промышленных системах мы часто логируем ошибки для разработчиков, а пользователю показываем «Что-то пошло не так». Для агента всё наоборот: ему нужно показать «сырую» техническую ошибку, чтобы он понял, что именно исправить.
from google.protobuf import struct_pb2
# Эмуляция функции бронирования
def book_meeting(date: str, email: str):
# Простая валидация формата даты
if "2024" not in date:
return {"error": "Date must be in 2024. Format: YYYY-MM-DD"}
if "@" not in email:
return {"error": "Invalid email format"}
return {"success": True, "booking_id": "12345"}
tools_config = [book_meeting]
# Чат с моделью
chat = model.start_chat(enable_automatic_function_calling=True)
# Сценарий: Пользователь вводит неверные данные
# Модель попытается вызвать функцию, получит JSON с ошибкой,
# проанализирует его и либо сама исправит (если знает контекст),
# либо спросит пользователя уточнение.
response = chat.send_message(
"Забронируй встречу на 5 мая для user_at_gmail.com"
# Обратите внимание: год не указан, email странный, но валидный для упрощения
)
# Внутри (невидимо для нас) происходит следующее:
# 1. Model -> call book_meeting("05-05", "user_at_gmail.com")
# 2. Tool -> returns {"error": "Date must be in 2024..."}
# 3. Model -> Analysing error -> "Ага, нужен год."
# 4. Model -> call book_meeting("2024-05-05", "user_at_gmail.com")
# 5. Tool -> returns {"success": True...}
# 6. Model -> "Встреча успешно забронирована!"
print(response.text)
Архитектурный паттерн: Critic-Actor (Критик-Исполнитель)
Для сложных задач, где стоимость ошибки высока (например, финансовые транзакции или генерация юридических документов), полагаться на самопроверку одной и той же сессии модели бывает рискованно. Модель может быть «предвзята» к своему собственному ответу.
Решение: паттерн Critic-Actor.
- Actor (Исполнитель): Генерирует черновик решения. У него высокая «температура» (креативность).
- Critic (Критик): Отдельная сессия (или даже другая, более мощная модель), которой подается задача и решение Исполнителя. Задача Критика — только искать ошибки. У него низкая температура (строгость).
Этот диалог может продолжаться несколько итераций. Исследования показывают, что разделение ролей повышает качество генерации кода и сложных рассуждений на 20-30% по сравнению с простым Chain-of-Thought.
Совет из практики: В промпте Критика явно укажите: «Будь придирчивым. Твоя задача — найти уязвимости безопасности и логические несостыковки. Если ошибок нет, ответь 'OK'».
Создайте систему из двух агентов (ролей) для генерации SQL-запросов. <br>1. Агент-Генератор получает схему БД и вопрос на естественном языке, генерирует SQL.<br>2. Агент-Валидатор проверяет этот SQL. Валидатор не имеет доступа к БД, но должен проверить синтаксис (например, отсутствие DROP TABLE) и соответствие вопросу.<br>3. Если Валидатор находит ошибку, Генератор должен переписать запрос.<br><br>Напишите псевдокод или Python-код, реализующий этот цикл (максимум 2 итерации).
Опасности и ограничения (Guardrails)
Внедряя самокоррекцию, важно помнить о рисках бесконечных циклов. Если агент не знает ответа или инструмент сломан фундаментально, он может бесконечно пытаться «исправить» запрос, сжигая ваши токены и деньги.
Best Practices:
- Жесткий лимит итераций: Обычно 3 попытки достаточно. Если агент не справился за 3 раза, лучше вернуть ошибку человеку или эскалировать задачу.
- Детекция зацикливания: Проверяйте, не генерирует ли агент тот же самый ошибочный ответ, что и в прошлый раз.
- Температура: При повторных попытках (retry) полезно немного повысить температуру (на 0.1-0.2), чтобы выбить модель из локального минимума и заставить искать альтернативные пути.
Самокоррекция — это не панацея, а инструмент повышения надежности (Robustness). Она увеличивает время отклика (Latency) и стоимость, но критически важна для автономных систем.
В паттерне Critic-Actor, какова основная роль агента-Критика?