错误处理与重试逻辑
错误处理与重试逻辑
相关源文件
本章引用的主要源码文件:
api/apps/llm_app.pyconf/llm_factories.jsonrag/llm/__init__.pyrag/llm/chat_model.pyrag/llm/cv_model.pyrag/llm/embedding_model.pyrag/llm/rerank_model.pyrag/llm/sequence2txt_model.pyrag/llm/tts_model.pyweb/src/components/svg-icon.tsxweb/src/constants/llm.tsweb/src/pages/user-setting/setting-model/constant.tsweb/src/utils/common-util.ts
本文档详细介绍了 RAGFlow 大语言模型(LLM)集成系统中实现的复杂错误处理与重试机制。该系统提供了带有指数退避策略的自动重试逻辑、错误分类以及可配置的重试策略,用于处理与外部大语言模型(LLM)提供商通信时出现的瞬时故障。
关于大语言模型(LLM)抽象层和模型类型的信息,请参阅 5.1 LLMBundle 与模型类型。关于特定提供商的实现,请参阅 5.2 提供商实现。
概述
RAGFlow 在所有大语言模型(LLM)类型(包括对话、嵌入向量、重排序、计算机视觉(CV)、文本转语音(TTS)等)中实现了统一的错误处理与重试系统。该系统会自动将错误分类为可重试和不可重试两类,使用随机化指数退避策略重试瞬时故障,并提供细粒度的错误信息用于诊断。
核心的横切逻辑主要存在于每个模型领域的抽象 Base 类中,例如 rag/llm/chat_model.py(第 113-125 行)中的 Base 类以及 rag/llm/cv_model.py(第 42-51 行)中的计算机视觉(CV)模型。
来源: rag/llm/chat_model.py:39-51,rag/llm/chat_model.py:113-125,rag/llm/cv_model.py:42-51
错误分类系统
LLMErrorCode 枚举
系统定义了一个 LLMErrorCode 字符串枚举,用于分类与大语言模型(LLM)提供商通信时返回或遇到的常见错误条件。这套标准化的错误码使得跨提供商和模型类型的统一错误处理逻辑成为可能。
| 错误码 | 描述 |
|---|---|
ERROR_RATE_LIMIT | 速率限制超出(例如 HTTP 429 或限流) |
ERROR_AUTHENTICATION | API 密钥无效或授权错误(例如 HTTP 401) |
ERROR_INVALID_REQUEST | 请求格式错误或参数无效(例如 HTTP 400) |
ERROR_SERVER | 提供商端服务器错误(HTTP 500、502、503、504 等) |
ERROR_TIMEOUT | 客户端或服务器端请求超时 |
ERROR_CONNECTION | 网络问题,如 DNS 故障或端点不可达 |
ERROR_CONTENT_FILTER | 因策略或安全规则导致内容被过滤/屏蔽 |
ERROR_MODEL | 模型未找到、不可用或超过最大轮次 |
ERROR_QUOTA | 配额/计费问题,如余额不足 |
ERROR_MAX_RETRIES | 达到最大重试次数 |
ERROR_GENERIC | 未分类或未知的错误类型 |
class LLMErrorCode(StrEnum):
ERROR_RATE_LIMIT = "RATE_LIMIT_EXCEEDED"
ERROR_AUTHENTICATION = "AUTH_ERROR"
ERROR_INVALID_REQUEST = "INVALID_REQUEST"
ERROR_SERVER = "SERVER_ERROR"
ERROR_TIMEOUT = "TIMEOUT"
ERROR_CONNECTION = "CONNECTION_ERROR"
ERROR_MODEL = "MODEL_ERROR"
ERROR_MAX_ROUNDS = "ERROR_MAX_ROUNDS"
ERROR_CONTENT_FILTER = "CONTENT_FILTERED"
ERROR_QUOTA = "QUOTA_EXCEEDED"
ERROR_MAX_RETRIES = "MAX_RETRIES_EXCEEDED"
ERROR_GENERIC = "GENERIC_ERROR"
来源: rag/llm/chat_model.py:39-51
错误分类方法
在对话模型的 Base 类(rag/llm/chat_model.py)中,_classify_error 方法使用一组与错误码关联的关键词列表来分类异常的错误消息。错误消息字符串会被转换为小写,并通过正则表达式与已知模式进行匹配:
- 配额相关关键词包括
"quota"、"capacity"、"billing"、"欠费"。 - 速率限制关键词包括
"rate limit"、"429"、"too many requests"。 - 认证检查关键词包括
"auth"、"apikey"、"401"、"permission"。 - 服务器错误检测关键词包括
"503"、"500"、"unavailable"。 - 超时检查关键词包括
"timeout"、"timed out"。 - 连接问题检查关键词包括
"connect"、"network"、"dns"。 - 内容过滤检查关键词包括
"content"、"policy"、"blocked"。 - 模型错误检查关键词包括
"model"、"not found"。
如果以上均不匹配,则错误默认为 ERROR_GENERIC。
def _classify_error(self, error):
error_str = str(error).lower()
keywords_mapping = [
(["quota", "capacity", "credit", "billing", "balance", "欠费"], LLMErrorCode.ERROR_QUOTA),
(["rate limit", "429", "tpm limit", "too many requests", "requests per minute"], LLMErrorCode.ERROR_RATE_LIMIT),
(["auth", "key", "apikey", "401", "forbidden", "permission"], LLMErrorCode.ERROR_AUTHENTICATION),
(["invalid", "bad request", "400", "format", "malformed", "parameter"], LLMErrorCode.ERROR_INVALID_REQUEST),
(["server", "503", "502", "504", "500", "unavailable"], LLMErrorCode.ERROR_SERVER),
(["timeout", "timed out"], LLMErrorCode.ERROR_TIMEOUT),
(["connect", "network", "unreachable", "dns"], LLMErrorCode.ERROR_CONNECTION),
(["filter", "content", "policy", "blocked", "safety", "inappropriate"], LLMErrorCode.ERROR_CONTENT_FILTER),
(["model", "not found", "does not exist", "not available"], LLMErrorCode.ERROR_MODEL),
(["max rounds"], LLMErrorCode.ERROR_MODEL),
]
for words, code in keywords_mapping:
if re.search("({})".format("|".join(words)), error_str):
return code
return LLMErrorCode.ERROR_GENERIC
错误分类决策树
来源: rag/llm/chat_model.py:130-149
重试配置参数
重试机制通过参数化来提供对不同环境和使用场景的灵活性。这些参数可以在模型实例化时提供,也可以从环境变量中读取,支持全局默认值与每个实例的覆盖值。
| 参数 | 环境变量 | 默认值 | 描述 |
|---|---|---|---|
max_retries | LLM_MAX_RETRIES | 5 | 对瞬时错误的最大重试次数 |
base_delay | LLM_BASE_DELAY | 2.0 | 重试退避的基础延迟(秒) |
max_rounds | 不适用 | 5 | ReAct/工具的最大推理轮次 |
timeout | LLM_TIMEOUT_SECONDS | 600 | OpenAI 客户端的请求超时时间 |
这些参数在公共 Base 类的构造函数中设置,例如在对话模型(rag/llm/chat_model.py 第 114-122 行)中:
class Base(ABC):
def __init__(self, key, model_name, base_url, **kwargs):
timeout = int(os.environ.get("LLM_TIMEOUT_SECONDS", 600))
self.client = OpenAI(api_key=key, base_url=base_url, timeout=timeout)
# ...
self.max_retries = kwargs.get("max_retries", int(os.environ.get("LLM_MAX_RETRIES", 5)))
self.base_delay = kwargs.get("retry_interval", float(os.environ.get("LLM_BASE_DELAY", 2.0)))
self.max_rounds = kwargs.get("max_rounds", 5)
来源: rag/llm/chat_model.py:113-125,rag/llm/cv_model.py:44-47
重试策略与退避
带抖动的指数退避
RAGFlow 采用随机化延迟计算来进行重试尝试,以减轻同步重试导致的负载峰值问题(即"惊群"问题)。延迟的计算方式是将基础延迟乘以一个介于 10 到 150 之间的随机浮点数因子。
def _get_delay(self):
return self.base_delay * random.uniform(10, 150)
这会产生一个较宽的延迟范围:如果 base_delay 为 2 秒,则实际重试延迟在 20 到 300 秒之间,引入了显著的抖动。
重试延迟计算流程
重试会在尝试之间等待计算出的延迟时间。
来源: rag/llm/chat_model.py:127-129
可重试错误与不可重试错误
系统区分了哪些错误码值得重试:
- 可重试: 速率限制错误(
ERROR_RATE_LIMIT)、瞬时服务器错误(ERROR_SERVER)。 - 不可重试: 认证错误、无效请求错误、内容过滤、配额问题和通用错误。
对话模型 Base 类的内部重试逻辑(处理对话完成或流式传输期间的异常)应用此分类来决定重试行为。
捕获异常后的流程如下:
- 使用
_classify_error分类错误。 - 如果重试次数超过
max_retries,返回MAX_RETRIES_EXCEEDED错误。 - 如果错误可重试,计算延迟并在休眠后重试。
- 否则,中止重试并返回一个以
"**ERROR**"为前缀的错误消息字符串。
ChatModel 中的重试决策流程图
来源: rag/llm/chat_model.py:130-149,rag/llm/chat_model.py:212-243
集成与实现细节
API 层超时保护
用于设置大语言模型(LLM)API 密钥的 API 端点(api/apps/llm_app.py)使用 asyncio.wait_for 实现了上限超时,以限制请求持续时间(默认为 10 秒)。这可以保护用户界面(UI)免受模型内部逻辑中长时间挂起重试的影响。
API 中的示例:
timeout_seconds = int(os.environ.get("LLM_TIMEOUT_SECONDS", 10))
# 使用示例测试嵌入编码
arr, tc = await asyncio.wait_for(
asyncio.to_thread(mdl.encode, ["Test if the api key is available"]),
timeout=timeout_seconds,
)
这会在交互式用户界面(UI)层对大语言模型(LLM)调用实施时间限制,补充了模型类中嵌入的重试逻辑。
API 超时与重试流程
来源: api/apps/llm_app.py:87,api/apps/llm_app.py:101-104,api/apps/llm_app.py:124
模型特定的错误处理注意事项
- 内置嵌入模型(
rag/llm/embedding_model.py)使用带有线程锁的单例初始化,并相应地委托编码调用。它在内部处理错误。 - Xinference ASR 模型(
rag/llm/sequence2txt_model.py)直接发起 HTTP 请求,使用标准异常处理,返回以"**ERROR**"为前缀的错误。 - 计算机视觉(CV)模型(
rag/llm/cv_model.py)使用 try-except 包装对话调用,并在异常时生成错误字符串。 - Fish Audio TTS(
rag/llm/tts_model.py)捕获 HTTP 错误并抛出带有"**ERROR**"消息格式的RuntimeError。
这些模型展示了 RAGFlow 如何在不同的交互模型中保持一致的错误消息和重试模式。
来源: rag/llm/embedding_model.py:123-157 rag/llm/sequence2txt_model.py:82-125, 176-210 rag/llm/cv_model.py:141-150, 152-173 rag/llm/tts_model.py:146-185
错误消息协议
终端错误始终作为以 "**ERROR**" 为前缀的字符串结果返回。这种模式有两个目的:
- 在编程上区分错误情况与正常响应。
- 允许下游组件(如前端用户界面(UI)或代理工作流引擎)检测失败并显示用户友好的消息或采取纠正措施。
此外,由于 Token 或上下文长度限制导致的截断响应会通过特殊的通知字符串指示(例如,中文的 "······\n由于大模型的上下文窗口大小限制,回答已经被大模型截断。" 或对应的英文消息),这些也在 rag/llm/chat_model.py 中定义。
来源: rag/llm/chat_model.py:59-61,rag/llm/cv_model.py:150,rag/llm/sequence2txt_model.py:123
总结图表:自然语言到代码实体的映射
大语言模型(LLM)错误处理组件与关键代码实体
跨大语言模型(LLM)模型的重试工作流
本文介绍了强大的错误分类与重试系统,该系统通过智能处理瞬时问题和故障,确保 RAGFlow 与多个大语言模型(LLM)提供商之间实现流畅且具有弹性的交互。
来源:
rag/llm/chat_model.py:39-51, 113-149, 212-243rag/llm/cv_model.py:42-51,141-173rag/llm/sequence2txt_model.py:32-125,176-210rag/llm/tts_model.py:146-185api/apps/llm_app.py:79-151