feat(llm): 五层重试的次数与间隔可配
一次模型调用的失败由内到外经过五层重试:建连(SDK)、空响应(SDK)、同 provider 安全窗口、轮询熔断、意图重跑。此前五层的次数与间隔全是写死的常量,遇到脾气各异 的中转站只能改代码。现在都能在 LLM 页配置,不配就是原来的默认值。 配置语义五层统一(db.RetryRule):次数 0=内置默认 / -1=关掉这层 / >0=该值; 间隔 0=原本的指数退避 / >0=改用固定间隔。0 被"未配置"占用,故关闭用 -1。 写库前夹紧到 次数 [-1,20]、间隔 [0,1h],DB 另有 CHECK 兜底。 粒度按层的性质分:前三层跟着端点走,可在每个模型配置里逐字段覆盖全局默认(只钉 间隔的仍继承全局次数);熔断与意图重跑是进程级语义,只有全局一份。 生效路径:①②是 provider 构建参数,保存后重建缓存;③随每次 hooks.current() 解析,故障转移换配置时重试节奏跟着换;④在失败路径上每次都要读,由 applyRetryPolicy 推给 llmpool.Registry;⑤每条意图收尾读一次。全部热生效,无需重启。 连接测试刻意不带这些参数——它有 30s 硬超时,叠上用户配的重试只会把本来能用的 端点测成超时失败。 norma 升 v0.3.5(llm.Config 新增 RetryInterval / EmptyResponseRetries / EmptyResponseInterval)。旧库升级后行为逐字节不变:新列默认 0,settings 键不 存在即全默认。设计见 docs/LLM重试设计.md。
This commit is contained in:
1 parent
377ccc7e5a
commit
a50bf02428
21 files changed
+1216
-146
No files matched your search
@@ -45,6 +45,8 @@ import type {
|
||||
LLMProfile,
|
||||
LLMRecordDetail,
|
||||
LLMRecordItem,
|
||||
LLMRetryOverride,
|
||||
LLMRetryPolicy,
|
||||
LLMTask,
|
||||
MCPServer,
|
||||
MCPTool,
|
||||
@@ -724,6 +726,7 @@ export const api = {
|
||||
max_tokens?: number; // 单次回复输出上限;0=不发送,由服务端默认值决定
|
||||
max_tokens_field?: string; // ""=max_tokens(默认) | "max_completion_tokens"(仅 openai 格式)
|
||||
session_header_key?: string; // 非空=每次请求带该 HTTP 头,头值=当前会话 session id;""=不发送
|
||||
retry?: LLMRetryOverride; // 本配置的重试覆盖;各项留 0 = 跟随全局重试策略
|
||||
}) => post<{ id: number }>("/llm/profiles", p),
|
||||
deleteLLMProfile: (id: string) => del<{ deleted: number }>(`/llm/profiles/${id}`),
|
||||
activateLLMProfile: (id: string) => post<{ ok: boolean }>("/llm/profiles/active", { id: Number(id) }),
|
||||
@@ -731,6 +734,9 @@ export const api = {
|
||||
llmPool: () => get<LLMPoolStatus>("/llm/pool"),
|
||||
// 清除熔断,让下一次调用立刻重试该配置;不传 id = 全部清除。
|
||||
resetLLMPool: (id?: string) => post<LLMPoolStatus>("/llm/pool/reset", { id: id ? Number(id) : 0 }),
|
||||
// 全局重试策略(五层各自的次数+间隔)。全 0 = 全部走内置默认。
|
||||
llmRetryPolicy: () => get<LLMRetryPolicy>("/llm/retry-policy"),
|
||||
saveLLMRetryPolicy: (p: LLMRetryPolicy) => post<LLMRetryPolicy>("/llm/retry-policy", p),
|
||||
fetchLLMModels: (provider: string, base_url: string, api_key: string, proxy = "", profile_id?: number) =>
|
||||
post<{ ok: boolean; error?: string; models?: string[] }>("/llm/models", {
|
||||
provider,
|
||||
|
||||
@@ -916,6 +916,32 @@ export interface LLMProfile {
|
||||
// 自定义会话头名:非空时每次请求带该 HTTP 头,头值=当前会话/意图的 session id。
|
||||
// ""=不发送。用于按 session-id 头做提示缓存/粘性路由的网关。
|
||||
session_header_key?: string;
|
||||
// 本配置对重试的覆盖(建连/空响应/同 provider 安全窗口)。留空/全 0 = 跟随全局策略。
|
||||
retry?: LLMRetryOverride;
|
||||
}
|
||||
|
||||
// ---- LLM 重试策略 ----
|
||||
// 一层重试的两个旋钮。两者都是「0 = 未配置」:
|
||||
// attempts 0=用默认次数 | -1=关闭该层重试 | >0=重试次数
|
||||
// interval_ms 0=用默认的指数退避 | >0=改用这个固定毫秒间隔
|
||||
export interface LLMRetryRule {
|
||||
attempts: number;
|
||||
interval_ms: number;
|
||||
}
|
||||
|
||||
// 单个 LLM 配置能覆盖的三层(都是「跟着端点走」的重试)。
|
||||
export interface LLMRetryOverride {
|
||||
connect: LLMRetryRule; // 建连重试:连接重置/超时/429/5xx,流开始前
|
||||
empty: LLMRetryRule; // 空响应重试:完成但没有任何内容(仅 openai 格式)
|
||||
stream: LLMRetryRule; // 同 provider 安全窗口重试:未交付输出前的断流重放
|
||||
}
|
||||
|
||||
// 全局策略 = 上面三层的默认值 + 两层只有全局的:
|
||||
// breaker 轮询熔断(attempts=连续几次瞬时失败熔断,interval_ms=固定冷却时长)
|
||||
// intent 意图重跑(worker 以 model_error 收场后整条意图重跑)
|
||||
export interface LLMRetryPolicy extends LLMRetryOverride {
|
||||
breaker: LLMRetryRule;
|
||||
intent: LLMRetryRule;
|
||||
}
|
||||
|
||||
// ---- LLM 轮询(故障转移)----
|
||||
|
||||
Reference in new issue
Block a user