Обработка ошибок и отказоустойчивость навыков
Навык, который работает только в идеальных условиях, хрупок.
Надёжный навык должен предвидеть распространённые ошибки, давать понятную обратную связь и по возможности автоматически восстанавливаться.
Три уровня обработки ошибок
| Уровень | Расположение | Способ обработки |
|---|---|---|
| Ошибки валидации ввода | Перед выполнением | Проверьте ввод; если он недействителен, отклоните его напрямую и объясните причину |
| Ошибки времени выполнения | Во время выполнения скрипта | Перехватывайте исключения и выводите структурированную информацию об ошибке |
| Аномальные результаты | После выполнения | Проверьте, соответствует ли вывод ожиданиям; если нет, запустите процесс исправления |
Определение стратегий обработки ошибок в SKILL.md
SKILL.md должен включать чёткий раздел обработки ошибок, который сообщает Claude, что делать при возникновении проблем.
Пример
### Проблемы с входными данными
- Файл не существует: сообщите пользователю, что путь к файлу неверен, и попросите его повторно загрузить файл
- Формат файла не поддерживается: перечислите поддерживаемые форматы (pdf, docx, txt) и попросите пользователя преобразовать файл и повторить попытку
- Файл пуст: сообщите пользователю, что содержимое файла пусто и его невозможно обработать
### Проблемы с выполнением
- Выполнение скрипта не удалось: покажите пользователю полную информацию об ошибке, не скрывайте её
- Зависимости не установлены: сначала попробуйте автоматическую установку (pipinstall), если это не удастся, сообщите пользователю, что нужно установить вручную
- Тайм-аут: если он превышает60секунд без завершения, выведите текущий прогресс и спросите пользователя, продолжать ли ожидание
### Проблемы с результатом
- Выходной файл пуст: перепроверьте логику обработки и объясните пользователю возможные причины
- Формат вывода не соответствует ожиданиям: покажите фактический вывод и спросите пользователя, устраивает ли его это
Стандартные шаблоны обработки ошибок в скриптах
Скрипты Python должны использовать структуру try-except и единообразно возвращать JSON, содержащий поле status.
Пример
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, от которых зависит скрипт, не установлены, скрипт может автоматически попытаться установить их.
Пример
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"
)