Обработка ошибок и отказоустойчивость навыков

Навык, который работает только в идеальных условиях, хрупок.

Надёжный навык должен предвидеть распространённые ошибки, давать понятную обратную связь и по возможности автоматически восстанавливаться.


Три уровня обработки ошибок

Уровень Расположение Способ обработки
Ошибки валидации ввода Перед выполнением Проверьте ввод; если он недействителен, отклоните его напрямую и объясните причину
Ошибки времени выполнения Во время выполнения скрипта Перехватывайте исключения и выводите структурированную информацию об ошибке
Аномальные результаты После выполнения Проверьте, соответствует ли вывод ожиданиям; если нет, запустите процесс исправления

Определение стратегий обработки ошибок в SKILL.md

SKILL.md должен включать чёткий раздел обработки ошибок, который сообщает Claude, что делать при возникновении проблем.

Пример

## Обработка ошибок

### Проблемы с входными данными
- Файл не существует: сообщите пользователю, что путь к файлу неверен, и попросите его повторно загрузить файл
- Формат файла не поддерживается: перечислите поддерживаемые форматы (pdf, docx, txt) и попросите пользователя преобразовать файл и повторить попытку
- Файл пуст: сообщите пользователю, что содержимое файла пусто и его невозможно обработать

### Проблемы с выполнением
- Выполнение скрипта не удалось: покажите пользователю полную информацию об ошибке, не скрывайте её
- Зависимости не установлены: сначала попробуйте автоматическую установку (pipinstall), если это не удастся, сообщите пользователю, что нужно установить вручную
- Тайм-аут: если он превышает60секунд без завершения, выведите текущий прогресс и спросите пользователя, продолжать ли ожидание

### Проблемы с результатом
- Выходной файл пуст: перепроверьте логику обработки и объясните пользователю возможные причины
- Формат вывода не соответствует ожиданиям: покажите фактический вывод и спросите пользователя, устраивает ли его это

Стандартные шаблоны обработки ошибок в скриптах

Скрипты Python должны использовать структуру try-except и единообразно возвращать JSON, содержащий поле status.

Пример

# Путь к файлу: scripts/error_handling_demo.py
import json
import sys
import os

class SkillError(Exception):
    """Базовый класс пользовательского исключения Skill"""
    def __init__(self, message: str, code: str):
        self.message = message
        self.code    = code     # Коды ошибок, помогающие программе определить тип ошибки
        super().__init__(message)

class InputError(SkillError):
    """Ошибка валидации входных данных"""
    pass

class ProcessError(SkillError):
    """Ошибка обработки"""
    pass

def validate_input(file_path: str) -> None:
    """Проверить входные данные, при ошибке вызвать InputError"""
    if not file_path:
        raise InputError("Путь к файлу не указан", "MISSING_FILE")
    if not os.path.exists(file_path):
        raise InputError(f"Файл не существует: {file_path}", "FILE_NOT_FOUND")
    if os.path.getsize(file_path) == 0:
        raise InputError(f"Содержимое файла пусто: {file_path}", "EMPTY_FILE")

def process(file_path: str) -> dict:
    """Основная логика обработки"""
    try:
        validate_input(file_path)

        with open(file_path, "r", encoding="utf-8") as f:
            content = f.read()

        # Фактическая логика обработки (здесь упрощена до подсчёта строк)
        lines = content.split("\n")
        return {
            "status": "success",
            "data": {
                "file": file_path,
                "lines": len(lines),
                "chars": len(content)
            }
        }

    except InputError as e:
        # Ошибка ввода: пользователь может исправить её и повторить попытку
        return {
            "status":  "input_error",
            "code":    e.code,
            "message": e.message,
            "hint":    "Пожалуйста, проверьте, правильный ли путь к файлу, существует ли файл и не пуст ли он"
        }
    except UnicodeDecodeError:
        # Ошибка кодировки: дайте конкретные рекомендации по исправлению
        return {
            "status":  "process_error",
            "code":    "ENCODING_ERROR",
            "message": "Кодировка файла не UTF-8, и его невозможно прочитать",
            "hint":    "Сохраните файл в кодировке UTF-8 и повторите попытку"
        }
    except Exception as e:
        # Непредвиденная ошибка: полностью запишите её для упрощения отладки
        return {
            "status":  "unexpected_error",
            "code":    "UNKNOWN",
            "message": str(e),
            "hint":    "Предоставьте приведённую выше информацию об ошибке разработчику"
        }

if __name__ == "__main__":
    file_path = sys.argv[1] if len(sys.argv) > 1 else ""
    result    = process(file_path)
    print(json.dumps(result, ensure_ascii=False, indent=2))
# 文件存在时的成功输出:
{
  "status": "success",
  "data": {
    "file": "/mnt/user-data/uploads/example.txt",
    "lines": 128,
    "chars": 5432
  }
}

# 文件不存在时的错误输出:
{
  "status": "input_error",
  "code": "FILE_NOT_FOUND",
  "message": "文件不存在:/mnt/user-data/uploads/missing.txt",
  "hint": "请检查文件路径是否正确,文件是否存在且非空"
}

Шаблон отказоустойчивости для автоматической установки зависимостей

Когда пакеты Python, от которых зависит скрипт, не установлены, скрипт может автоматически попытаться установить их.

Пример

# Путь к файлу: scripts/auto_install_demo.py
import subprocess
import sys

def ensure_package(package_name: str, import_name: str = None) -> bool:
    """
Убедиться, что пакет Python установлен; если нет, установить его автоматически

Параметры:
package_name: имя пакета, используемое для установки через pip (например, "python-docx")
import_name: имя, используемое для импорта (например, "docx"), по умолчанию совпадает с package_name
    """

    import_name = import_name or package_name
    try:
        __import__(import_name)
        return True  # Уже установлен
    except ImportError:
        print(f"Установка зависимости: {package_name}...")
        result = subprocess.run(
            [sys.executable, "-m", "pip", "install", package_name,
             "--break-system-packages", "--quiet"],
            capture_output=True, text=True
        )
        if result.returncode == 0:
            print(f"Установка успешно завершена: {package_name}")
            return True
        else:
            print(f"Установка не удалась: {package_name}")
            print(result.stderr)
            return False

# Пример использования: убедитесь, что все зависимости готовы в начале скрипта
if not ensure_package("pandas"):
    sys.exit(1)
if not ensure_package("openpyxl"):
    sys.exit(1)

# После того как зависимости готовы, импортируйте как обычно
import pandas as pd
print("Все зависимости готовы, начинаем выполнение...")

Автоматическая установка подходит только для разработки и легковесных сценариев. В производственных Skills зависимости должны быть задокументированы и предварительно установлены пользователем, а не устанавливаться автоматически во время выполнения.


Рекомендации по написанию сообщений об ошибках

Хорошие сообщения об ошибках помогают пользователям быстро находить проблемы, а плохие только сбивают с толку.

Измерение Плохое написание Хорошее написание
Опишите проблему "Произошла ошибка" "Файл report.csv не существует в каталоге загрузки"
Объясните причину (None) "Возможно, имя файла написано с ошибкой, или файл ещё не был загружен"
Укажите действие (None) "Пожалуйста, загрузите файл повторно, или проверьте, правильно ли указано имя файла"

Пример

# Плохое сообщение об ошибке
raise Exception("Ошибка")

# Хорошее сообщение об ошибке
raise InputError(
    "Файл example_data.csv не найден в каталоге загрузки."
    "Возможные причины: имя файла написано с ошибкой, или файл ещё не был загружен."
    "Пожалуйста, загрузите файл заново и попробуйте снова.",
    код="FILE_NOT_FOUND"
)
Другие расширения