- 音视频
- AI 应用
- 后端
- 前端
【免费下载链接】autoclip
AutoClip : AI-powered video clipping and highlight generation · 一款智能高光提取与剪辑的二创工具
AutoClip 是一套基于 FastAPI 的 AI 视频智能高光提取与剪辑系统,其后端在 backend/core/error_middleware.py 与 backend/utils/error_handler.py 中实现了一套分层、统一的错误处理机制:所有异常在业务层被归类、在中间件层被标准化为一致的 JSON 响应、在基础设施层可自动重试与熔断。本指南以项目内实际代码为准,带你掌握 AutoClip 错误分类体系、AutoClipsException自定义异常、@handle_errors装饰器、error_context上下文管理器、统一错误响应格式、HTTP 状态码映射,以及如何用测试与日志工具验证整套错误处理链路。
📋 概述
AutoClip 的错误处理机制由三大部分构成,各司其职:
- 异常定义层(backend/utils/error_handler.py):定义
ErrorCategory、ErrorLevel枚举与基础异常AutoClipsException及其语义化子类; - 中间件层(backend/core/error_middleware.py):注册全局异常处理器,将业务异常统一转换为 JSON 错误响应;
- 服务层(backend/services/exceptions.py):提供
ServiceError服务异常体系,覆盖配置、文件、任务、项目、并发等业务维度。
整个链路在 backend/app_factory.py 中通过app.add_exception_handler(Exception, global_exception_handler)挂载到 FastAPI 应用上,因此只要 API 进程内抛出任何异常,都会被统一捕获、记录并格式化输出,前端拿到的一律是标准结构,便于展示与排查。
🏗️ 错误处理架构
错误分类
AutoClip 将所有错误划分为 7 个语义分类,定义于 backend/utils/error_handler.py:
class ErrorCategory(Enum): CONFIGURATION = "CONFIGURATION" # 配置错误 NETWORK = "NETWORK" # 网络错误 API = "API" # API错误 FILE_IO = "FILE_IO" # 文件IO错误 PROCESSING = "PROCESSING" # 处理错误 VALIDATION = "VALIDATION" # 验证错误 SYSTEM = "SYSTEM" # 系统错误分类的价值在于:错误分类直接决定两件事——HTTP 状态码的映射,以及全局异常处理器输出的error.code前缀(格式为AUTOCLIPS_{CATEGORY}),这让运维人员无需查看堆栈即可从响应码快速判断故障域。
错误级别
同一分类下的错误还有严重级别之分(error_handler.py):
class ErrorLevel(Enum): DEBUG = "DEBUG" INFO = "INFO" WARNING = "WARNING" ERROR = "ERROR" CRITICAL = "CRITICAL"ErrorLevel在AutoClipsException构造时默认取ERROR,但不同子类会覆盖默认级别,例如ValidationError默认使用WARNING(error_handler.py),因为"参数校验不通过"通常属于预期内的用户输入问题,而非系统故障。级别会传递给全局ErrorHandler.handle_error()以决定写入日志的等级(logger.debug/info/warning/error/critical对应调用),见 error_handler.py。
基础异常类与语义化子类
AutoClipsException是整套体系的地基(error_handler.py),构造函数签名如下:
AutoClipsException( message: str, # 用户友好的错误消息 category: ErrorCategory, # 错误分类(必填) level: ErrorLevel = ErrorLevel.ERROR, # 错误级别 details: Optional[Dict[str, Any]] = None, # 调试信息字典 original_exception: Optional[Exception] = None, # 原始异常,保持异常链 )除基类外,项目还预置了一批按分类封装的子类,使用时可省去重复指定category与level:
| 子类 | 分类 | 附加参数 | 源码位置 |
|---|---|---|---|
ConfigurationError | CONFIGURATION | details | error_handler.py |
NetworkError | NETWORK | details,original_exception | error_handler.py |
APIError | API | status_code(自动写入 details) | error_handler.py |
FileIOError | FILE_IO | file_path(自动写入 details) | error_handler.py |
ProcessingError | PROCESSING | step(自动写入 details) | error_handler.py |
ValidationError | VALIDATION | field(自动写入 details),级别为 WARNING | error_handler.py |
to_dict()方法将异常序列化为{"message", "category", "level", "details", "timestamp", "original_exception"},而str()输出格式为[CATEGORY] message,例如[NETWORK] 网络错误。这两个约定被日志记录与测试断言广泛依赖,可参见 backend/tests/test_error_handler.py 中对字符串表示的断言。
🚀 使用方法
1. 抛出自定义异常
在业务代码中主动抛出分类明确的异常,是整个体系的第一步:
from backend.utils.error_handler import AutoClipsException, ErrorCategory # 抛出配置错误 raise AutoClipsException( message="API密钥未配置", category=ErrorCategory.CONFIGURATION, details={"config_key": "DASHSCOPE_API_KEY"} ) # 抛出文件错误 raise AutoClipsException( message="文件不存在", category=ErrorCategory.FILE_IO, details={"file_path": "/path/to/file.mp4"} )实际项目中,backend/services/enhanced_progress_service.py 等多处服务代码正是以这种方式抛出AutoClipsException的。
2. 使用错误处理装饰器
@handle_errors装饰器(backend/core/error_middleware.py)会将函数内抛出的任意未知异常自动转换为指定分类的AutoClipsException,同时保留original_exception以维持异常链:
from backend.core.error_middleware import handle_errors from backend.utils.error_handler import ErrorCategory @handle_errors(ErrorCategory.PROCESSING) async def process_video(video_path: str): # 函数内的任何异常都会被自动转换为AutoClipsException if not os.path.exists(video_path): raise FileNotFoundError("视频文件不存在") # 处理逻辑... return result实现细节上,装饰器通过asyncio.iscoroutinefunction(func)自动区分异步与同步函数,分别返回async_wrapper与sync_wrapper;两个包装器都对AutoClipsException与ServiceError直接放行(不重复包装),仅对未知异常做转换。这也意味着@handle_errors与 Service 层异常体系天然兼容。
3. 使用错误上下文管理器
对于一段不宜拆成独立函数的代码块,可以用error_context上下文管理器(backend/core/error_middleware.py)在指定范围内完成同样的自动转换:
from backend.core.error_middleware import error_context from backend.utils.error_handler import ErrorCategory def upload_file(file_path: str): with error_context(ErrorCategory.FILE_IO, {"file_path": file_path}): # 在这个上下文中抛出的任何异常都会被转换为AutoClipsException with open(file_path, 'r') as f: content = f.read() return content注意:error_context与 backend/utils/error_handler.py 中的同名实现略有差异——utils 版会按分类进一步转为APIError/NetworkError/FileIOError等具体子类,并自动注入original_exception_type到 details 中;中间件版则统一转为基础AutoClipsException。两者有一个共同约定:若上下文内抛出的已是AutoClipsException,则原样重抛,绝不二次包装(对应测试 test_error_handler.py)。
4. 在API路由中使用
在 FastAPI 路由内,推荐采用"捕获-分类-重抛"的写法,让全局异常处理器统一收口:
from fastapi import APIRouter, HTTPException from backend.utils.error_handler import AutoClipsException, ErrorCategory router = APIRouter() @router.get("/projects/{project_id}") async def get_project(project_id: str): try: # 业务逻辑 project = await get_project_from_db(project_id) if not project: raise AutoClipsException( message=f"项目不存在: {project_id}", category=ErrorCategory.VALIDATION, details={"project_id": project_id} ) return project except AutoClipsException: # 重新抛出,让全局异常处理器处理 raise except Exception as e: # 其他异常会被转换为AutoClipsException raise AutoClipsException( message="获取项目失败", category=ErrorCategory.SYSTEM, original_exception=e )补充提示:除
AutoClipsException外,全局异常处理器同样识别 backend/services/exceptions.py 中的ServiceError体系(含ConfigurationError、FileOperationError、ProcessingError、TaskError、ProjectError、ConcurrentError等),并依据error_code映射到对应状态码(映射表见 backend/core/error_middleware.py)。因此业务层也可直接抛ServiceError子类获得同样统一的出口。
📊 错误响应格式
所有错误响应都遵循统一格式:
{ "error": { "code": "AUTOCLIPS_VALIDATION", "message": "项目不存在: abc123", "details": { "project_id": "abc123" }, "request_id": "req_123456", "timestamp": 1640995200.0 } }字段说明
code: 错误代码,格式为AUTOCLIPS_{CATEGORY}或HTTP_{STATUS_CODE}message: 错误消息,用户友好的描述details: 错误详情,包含调试信息request_id: 请求ID,用于追踪timestamp: 错误发生时间戳
该结构的生成逻辑在 backend/core/error_middleware.py:ErrorResponse.to_dict()负责组装字段,create_error_response()负责填充时间戳并返回JSONResponse。request_id取自request.state.request_id,若请求未被上游中间件注入则可能为None,前端可用它关联日志做全链路排查。
需要指出的是,仓库中还提供了第二套更细粒度的响应格式backend/utils/error_response.py:它定义了ErrorCode枚举(覆盖RESOURCE_NOT_FOUND、RATE_LIMIT_EXCEEDED、TIMEOUT_ERROR等近 40 种细分错误码)、user_message用户友好文案自动映射、ISO 格式时间戳,以及更精细的 HTTP 状态码映射(如 401/403/413/415/422/429/504),供 backend/core/error_middleware_v2.py 这类 V2 版本使用。两套体系的核心思想一致:响应结构永远只有error一个顶层键,内部字段稳定不变。
🔧 HTTP状态码映射
| 错误分类 | HTTP状态码 | 说明 |
|---|---|---|
| CONFIGURATION | 500 | 配置错误 |
| NETWORK | 503 | 网络错误 |
| API | 502 | API错误 |
| FILE_IO | 500 | 文件IO错误 |
| PROCESSING | 500 | 处理错误 |
| VALIDATION | 400 | 验证错误 |
| SYSTEM | 500 | 系统错误 |
该映射实现在 backend/core/error_middleware.py 的get_status_code_for_category(),由handle_autoclips_exception()在生成响应时调用。配合 backend/core/error_middleware.py,最终code会被格式化为AUTOCLIPS_{category.value},例如AUTOCLIPS_VALIDATION。
此外,ServiceError体系还有一张独立的业务状态码映射(backend/core/error_middleware.py),覆盖更贴近业务的场景:FILE_NOT_FOUND → 404、TASK_ALREADY_RUNNING → 409、LOCK_ACQUISITION_FAILED → 423、TIMEOUT_ERROR → 504等,适合资源级语义的错误。
📝 最佳实践
1. 错误消息编写
# ✅ 好的错误消息 raise AutoClipsException( message="视频文件格式不支持,请使用MP4格式", category=ErrorCategory.VALIDATION, details={"supported_formats": ["mp4", "avi", "mov"]} ) # ❌ 不好的错误消息 raise AutoClipsException( message="Error: Invalid file", category=ErrorCategory.VALIDATION )好的消息面向最终用户(中文、可操作、给出解决方案),而details面向开发者(携带机器可读的上下文)。backend/utils/error_response.py 更进一步维护了一份"错误码 → 用户友好文案"的映射字典,例如FILE_TOO_LARGE → "文件过大,请选择较小的文件",若调用方未显式提供user_message,系统会自动兜底填充。
2. 错误详情包含
# ✅ 包含有用的调试信息 raise AutoClipsException( message="处理视频失败", category=ErrorCategory.PROCESSING, details={ "project_id": project_id, "step": "video_cutting", "error_code": "FFMPEG_ERROR", "file_size": file_size } )details的设计准则是"足以让排障者无需复现即可定位":哪个项目(project_id)、哪一步(step)、底层什么错误(error_code)、现场数据(file_size)。AutoClip 的流水线由 backend/pipeline 下的step1_outline至step6_video六步构成,因此ProcessingError特意内置了step参数(error_handler.py),场景测试 backend/tests/test_error_scenarios.py 也验证了步骤执行失败时异常的抛出路径。
3. 错误分类选择
# ✅ 根据错误性质选择正确的分类 if not api_key: raise AutoClipsException( message="API密钥未配置", category=ErrorCategory.CONFIGURATION # 配置问题 ) if response.status_code == 429: raise AutoClipsException( message="API调用频率超限", category=ErrorCategory.API # API问题 ) if not os.path.exists(file_path): raise AutoClipsException( message="文件不存在", category=ErrorCategory.FILE_IO # 文件问题 )分类正确性的实际收益在中间件层兑现:NETWORK → 503(服务暂时不可用)、API → 502(上游坏网关),前端可以根据状态码决定是提示重试还是提示检查配置,而不是一律 500。测试 backend/tests/test_error_scenarios.py 覆盖了缺失 API 密钥、非法处理参数、prompt 文件缺失等配置类错误场景。
4. 异常链保持
# ✅ 保持原始异常信息 try: result = some_risky_operation() except Exception as e: raise AutoClipsException( message="操作失败", category=ErrorCategory.SYSTEM, original_exception=e # 保持原始异常 )original_exception在to_dict()中被序列化为字符串(error_handler.py),随日志与错误摘要留存;同时ServiceError体系也提供等价的cause参数(backend/services/exceptions.py),错误传播测试 test_error_scenarios.py 验证了cause链的完整性。
🧪 测试错误处理
1. 测试自定义异常
import pytest from backend.utils.error_handler import AutoClipsException, ErrorCategory def test_custom_exception(): with pytest.raises(AutoClipsException) as exc_info: raise AutoClipsException( message="测试错误", category=ErrorCategory.VALIDATION ) assert exc_info.value.category == ErrorCategory.VALIDATION assert exc_info.value.message == "测试错误"仓库内置了完整的错误处理单元测试 backend/tests/test_error_handler.py,覆盖范围包括:
TestAutoClipsException:异常创建、to_dict()序列化、str()表示;TestSpecificExceptions:APIError/NetworkError/FileIOError/ProcessingError/ValidationError的分类、级别与 details 注入;TestRetryConfig、TestCircuitBreaker:重试配置默认值与熔断器状态机(CLOSED → OPEN → HALF_OPEN → CLOSED);TestRetryDecorator、TestErrorContext、TestErrorHandler、TestSafeExecute:重试成功/失败、上下文异常转换与保留、错误摘要统计、通用异常转换(默认归为SYSTEM分类)。
2. 测试API错误响应
from fastapi.testclient import TestClient from backend.main import app client = TestClient(app) def test_api_error_response(): response = client.get("/api/v1/projects/nonexistent") assert response.status_code == 400 assert "error" in response.json() assert response.json()["error"]["code"] == "AUTOCLIPS_VALIDATION"说明:以上断言依赖路由内抛出的
AutoClipsException分类为VALIDATION。若目标路由抛出的分类不同(例如SYSTEM),则status_code与code会相应变为 500 与AUTOCLIPS_SYSTEM,编写测试时请以 backend/core/error_middleware.py 的映射为准。
🔍 错误监控和日志
1. 错误日志格式
所有错误都会自动记录到日志,格式如下:
2024-01-01 12:00:00 - ERROR - 未处理的异常: AutoClipsException: 项目不存在: abc123 request_id: req_123456 path: /api/v1/projects/abc123 method: GET traceback: [完整的堆栈跟踪]该日志由全局异常处理器在响应前生成(backend/core/error_middleware.py):使用logger.error(..., extra={...})一次性写入request_id、path、method、traceback(来自traceback.format_exc())。这意味着所有未处理异常都有完整堆栈落盘,配合应用的日志配置(见 backend/app_factory.py,日志级别与输出文件由get_logging_config()提供),可对故障现场进行完整复盘。
2. 错误统计
可以通过日志分析工具统计错误:
# 统计错误类型 grep "AUTOCLIPS_" backend.log | cut -d' ' -f4 | sort | uniq -c # 统计错误频率 grep "ERROR" backend.log | wc -l代码层面同样提供了统计能力:backend/utils/error_handler.py 的ErrorHandler维护进程内错误日志队列,get_error_summary()返回{"total_errors", "error_counts", "latest_error"},且同名 name 的熔断器只创建一次(get_circuit_breaker);backend/core/error_middleware_v2.py 的ErrorMonitor则按{异常类型}:{上下文}聚合计数,并保留最近 1000 条错误历史。测试 test_error_handler.py 对get_error_summary()的分类计数结果做了断言。
🚨 常见错误处理场景
1. 文件操作错误
@handle_errors(ErrorCategory.FILE_IO) async def save_file(file_path: str, content: bytes): try: with open(file_path, 'wb') as f: f.write(content) except PermissionError: raise AutoClipsException( message="没有文件写入权限", category=ErrorCategory.FILE_IO, details={"file_path": file_path} ) except OSError as e: raise AutoClipsException( message="文件系统错误", category=ErrorCategory.FILE_IO, details={"file_path": file_path, "os_error": str(e)} )AutoClip 的整个处理流程高度依赖文件系统(SRT 字幕、视频、缩略图、prompt 文件等),backend/tests/test_error_scenarios.py 用"不存在的 SRT 文件""无效 SRT 格式""只读文件写权限""损坏的 YAML 配置"四类场景验证了文件错误的处理路径,其中ProcessingContext.set_srt_path()对不存在的文件直接抛FileNotFoundError。
2. API调用错误
@handle_errors(ErrorCategory.API) async def call_external_api(url: str, data: dict): try: async with aiohttp.ClientSession() as session: async with session.post(url, json=data) as response: if response.status == 429: raise AutoClipsException( message="API调用频率超限", category=ErrorCategory.API, details={"url": url, "status": 429} ) return await response.json() except aiohttp.ClientError as e: raise AutoClipsException( message="网络请求失败", category=ErrorCategory.NETWORK, details={"url": url, "error": str(e)} )注意这里"API 返回 429"与"底层连接失败"被区分成两个不同分类(APIvsNETWORK),因为前者是上游的业务限流(映射 502),后者是网络不可达(映射 503),两者的恢复手段完全不同。AutoClip 的 LLM 调用与 B 站/YouTube 下载均属于此类外部依赖场景。
3. 数据处理错误
@handle_errors(ErrorCategory.PROCESSING) async def process_video_data(video_path: str): try: # 处理逻辑 result = await video_processor.process(video_path) return result except VideoProcessingError as e: raise AutoClipsException( message="视频处理失败", category=ErrorCategory.PROCESSING, details={ "video_path": video_path, "error_code": e.code, "step": e.step }, original_exception=e )进阶:重试与熔断机制
除上述被动处理外,backend/utils/error_handler.py 还提供了主动容错的两件套,适合网络、API 类可重试错误:
RetryConfig:默认max_retries=3、base_delay=1.0、max_delay=60.0、exponential_base=2.0,默认仅对NetworkError/APIError/ConnectionError/TimeoutError/OSError重试;retry_with_backoff()装饰器按指数退避计算延迟min(base * exp_base**attempt, max_delay),重试耗尽后抛回原始异常(对应测试 test_error_handler.py);CircuitBreaker:默认failure_threshold=5、recovery_timeout=60.0,状态机 CLOSED → OPEN → HALF_OPEN;OPEN 期间直接拒绝执行并抛AutoClipsException(级别 WARNING),超过恢复时间后放行一次探测调用,成功即回到 CLOSED(对应测试 test_error_handler.py);safe_execute(func, ..., retry_config=..., context=...):将"重试 + 分类转换 + 错误记录"串成一行调用(error_handler.py)。
在更上层的 API 维度,backend/api/v1/enhanced_retry.py 提供了针对项目的智能重试端点:RetryStrategy支持download_only/processing_only/full_retry/smart_retry四种策略,determine_retry_strategy()依据"视频文件是否存在 + 项目状态"自动判断(视频缺失 → 完整重试;有视频但处理失败 → 仅重试处理),并通过GET /api/v1/retry/projects/{id}/retry-strategy暴露策略建议,是错误恢复能力在业务层的落地示例。
📚 相关文档
- 统一错误处理指南(本文档)
- 后端架构说明
- 系统架构总览
- 开发者指南
- FAQ 常见问题
- 快速参考手册
- 核心实现:错误处理中间件、异常定义与重试熔断、统一错误响应、服务异常体系
- 测试参考:错误处理单元测试、错误场景测试
- 音视频
- AI 应用
- 后端
- 前端
【免费下载链接】autoclip
AutoClip : AI-powered video clipping and highlight generation · 一款智能高光提取与剪辑的二创工具
相关推荐
AutoClip统一错误处理体系全解:7大错误分类的设计思路
AutoClip统一错误处理体系全解:7大错误分类的设计思路 AutoClip 是一款 AI 视频剪辑与高光提取工具,它能自动把长视频切出精彩片段。由于整条流水
音视频AI 应用后端前端CANN Runtime 错误处理实战:从 `checkCudaErrors` 到统一诊断的四层错误检查体系
CANN Runtime 错误处理实战:从 checkCudaErrors 到统一诊断的四层错误检查体系 本指南以 CANN runtime 仓库中 1_err
CANNAscend人工智能性能剖析系统编程Cursor Talk To Figma MCP 上手指南:如何把 Figma 重复操作交给 AI
Cursor Talk To Figma MCP 上手指南:如何把 Figma 重复操作交给 AI 15:00,主管要求把 60 张 SKU 卡片统一换成品牌主
人工智能AI 应用MCP 服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考