三级容灾:当辅助 LLM 挂了,你的摘要怎么办?

发布时间:2026/7/13 10:37:23

三级容灾:当辅助 LLM 挂了,你的摘要怎么办? 三级容灾当辅助 LLM 挂了你的摘要怎么办专栏信息《从零到一构建跨平台 AI 助手WeClaw 实战指南》专栏本文是模块八第 4 篇讲解摘要生成的可靠性工程设计。作者与项目作者简介翁勇刚 WENG YONGGANG新概念龙虾-WeClaw 开发团队负责人一群专注于跨平台 AI 应用的实践者理念“再复杂的技术也能用代码讲清楚”项目地址https://github.com/wyg5208/weclaw.git官网地址https://weclaw.link作者 CSDNhttps://blog.csdn.net/yweng18摘要本文结构概览本文从辅助模型 API 不可用的真实故障场景出发分析旧方案的致命缺陷然后详解三级容灾架构的设计与实现最后通过实测验证降级表现。背景上下文压缩通常由一个辅助模型执行成本更低、速度更快。但当辅助模型的 API 超时、限流或预算耗尽时压缩流程会失败。核心问题压缩失败时早期上下文怎么办旧方案直接截断等于丢失了全部历史记忆。解决方案三级容灾机制——Level 1 辅助模型、Level 2 主模型重试30s 超时、Level 3 静态回退零 LLM 成本。关键成果任何情况下都不会丢失上下文最坏情况保留结构化文件摘要冷却协调机制避免每轮尝试两个模型的浪费静态回退方案零 LLM 成本仍能保留关键信息适合读者构建生产级 LLM Agent 的开发者关注系统可靠性和容灾设计阅读时长约 10 分钟关键词三级容灾、摘要生成、降级策略、静态回退、可靠性工程一、故障场景辅助模型突然不工作了1.1 正常流程用户对话 → 上下文超限 → 调用辅助模型生成摘要 → 用摘要替换旧消息 → 继续对话1.2 故障发生用户对话 → 上下文超限 → 调用辅助模型 → API 超时/限流/余额不足 → 辅助模型不可用的常见原因故障类型典型表现发生频率API 超时请求 30s 无响应中等网络波动速率限制HTTP 429 Too Many Requests高免费额度余额不足HTTP 402/403低但致命服务不可用HTTP 503 Service Unavailable低云厂商故障模型下线HTTP 404 Not Found极低但不可恢复1.3 旧方案的致命缺陷# 旧方案压缩失败直接截断asyncdefcompress_context(self,messages,threshold):try:summaryawaitself.auxiliary_model.summarize(messages[:-RECENT_N])return[summary_msg]messages[-RECENT_N:]exceptException:# 压缩失败那就直接截断吧logger.error(Compression failed, falling back to truncation)returnmessages[-RECENT_N:]# 早期上下文全部丢失后果用户在 30 分钟前讨论的所有内容——分析结论、搜索到的关键数据、达成的决策——全部被丢弃。AI 像失忆了一样重新开始。二、三级容灾设计[图片: 三级容灾流程图 | 生成方式: 文生图 PROMPT: “A three-level fallback waterfall diagram for LLM summary generation: Level 1 Auxiliary Model (green box, labeled ‘cheap fast’), Level 2 Main Model Retry with 30s timeout (yellow box, labeled ‘expensive fallback’), Level 3 Static Fallback (red box, labeled ‘zero cost, extract file paths and tool names’), with arrows showing degradation path from top to bottom, clean flowchart style, white background”]2.1 架构概览Level 1: 辅助模型 (AuxiliaryClient) │ 成功 → 返回 LLM 摘要 ✓ │ 失败 ↓ Level 2: 主模型重试 (30s 超时, 429/503 快速跳过) │ 成功 → 返回 LLM 摘要 ✓ │ 失败 ↓ Level 3: 静态回退 (提取文件路径工具名, 零 LLM 成本) │ 必定成功 → 返回结构化摘要 ✓2.2 Level 1辅助模型辅助模型是压缩的首选方案classAuxiliaryClient:轻量级辅助模型客户端专门用于上下文压缩def__init__(self,model_id,api_key,base_urlNone):self.model_idmodel_id self.api_keyapi_key self.cooldown_until0# 冷却期时间戳asyncdefsummarize(self,messages,focus_topicNone):调用辅助模型生成摘要iftime.time()self.cooldown_until:raiseCooldownError(Auxiliary model in cooldown)promptbuild_summary_prompt(messages,focus_topic)responseawaitself._call_api(prompt,timeout45)returnresponse.content为什么用辅助模型而不是主模型维度主模型辅助模型成本高按 token 计费低廉价小模型延迟中排队推理低专用通道质量高中摘要够用就行用途核心推理辅助任务2.3 Level 2主模型重试当辅助模型失败时用主模型做第二次尝试asyncdeftry_main_model_fallback(self,messages,focus_topic):Level 2: 主模型重试try:# 关键30s 超时避免阻塞主循环promptbuild_summary_prompt(messages,focus_topic)responseawaitasyncio.wait_for(self.main_model.complete(prompt),timeout30.0)returnresponse.contentexceptasyncio.TimeoutError:logger.warning(Main model timeout (30s))returnNoneexceptRateLimitError:# 429 限流快速跳过不等待logger.warning(Main model rate limited, skipping)returnNone关键设计30 秒硬超时主模型的首要职责是核心推理不能为了压缩阻塞太久429/503 快速跳过限流和服务不可用时立即放弃不做无谓等待2.4 Level 3静态回退最精妙的设计当两个 LLM 都失败时用纯代码提取关键信息零 LLM 成本defgenerate_static_fallback(self,messages):Level 3: 静态回退——零 LLM 成本的结构化摘要file_paths[]tool_names[]user_goals[]formsginmessages:# 提取文件路径contentmsg.get(content,)pathsextract_file_paths(content)file_paths.extend(paths)# 提取工具调用fortcinmsg.get(tool_calls,[]):nametc.get(function,{}).get(name,unknown)tool_names.append(name)# 提取用户请求保留最后 3 条ifmsg.get(role)user:user_goals.append(content[:200])# 构建结构化摘要summaryf[自动摘要 - 静态回退模式] 用户最近请求:{; .join(user_goals[-3:])}操作过的文件:{, .join(set(file_paths[-20:]))}使用过的工具:{, .join(set(tool_names))}消息总数:{len(messages)}条 returnsummary静态回退的优势零延迟纯字符串操作微秒级完成零成本不调用任何 LLM API零失败不可能出错纯代码逻辑保留关键信息文件路径、工具名称、用户请求——足以让模型知道之前做了什么2.5 静态回退的实际效果[自动摘要 - 静态回退模式] 用户最近请求: 分析半导体板块走势; 帮我看腾讯的港股; 给出投资建议 操作过的文件: /data/reports/semiconductor.pdf, /data/stock/00700.HK.csv 使用过的工具: stock_query, web_search, read_file, write_file 消息总数: 47 条虽然不如 LLM 摘要那样自然流畅但关键信息都保留了用户做了什么、操作了哪些文件、用了哪些工具。三、冷却协调机制3.1 问题每轮都尝试两个模型如果辅助模型持续不可用比如余额耗尽每轮压缩都会尝试辅助模型 → 失败尝试主模型 → 成功这意味着每轮都有两次 API 调用其中第一次必定失败。3.2 解决方案冷却期# 辅助模型进入冷却期后直接跳到 Level 2asyncdefcompress(self,messages,focus_topicNone):# Level 1: 辅助模型try:returnawaitself.auxiliary.summarize(messages,focus_topic)exceptCooldownError:logger.debug(Auxiliary in cooldown, trying main model)exceptExceptionase:logger.warning(fAuxiliary failed:{e})# 辅助模型失败 → 进入 5 分钟冷却期self.auxiliary.cooldown_untiltime.time()300# Level 2: 主模型resultawaitself.try_main_model_fallback(messages,focus_topic)ifresult:returnresult# Level 3: 静态回退returnself.generate_static_fallback(messages)冷却期逻辑辅助模型失败 → 进入 5 分钟冷却期冷却期内直接跳到 Level 2不做无谓尝试冷却期过后自动恢复尝试四、依赖注入为什么不能直接调 LLM4.1 反面案例# 错误做法直接依赖具体的 LLM 调用方式classContextEngine:asyncdefsummarize(self,messages):# 直接调用 litellm → 紧耦合fromlitellmimportcompletionreturnawaitcompletion(modelgpt-4o-mini,messagesmessages)4.2 正确做法通过 ModelRegistry 注入# 正确做法通过依赖注入获取模型客户端classContextEngine:def__init__(self,model_registry):self.registrymodel_registry# 注入模型注册表asyncdefsummarize(self,messages):# 通过注册表获取辅助模型aux_clientself.registry.get_auxiliary_client()main_clientself.registry.get_main_client()# Level 1try:returnawaitaux_client.summarize(messages)exceptException:pass# Level 2try:returnawaitmain_client.complete(messages)exceptException:pass# Level 3returnself.generate_static_fallback(messages)好处可测试性Mock 注入轻松模拟各种故障场景可配置性不同场景使用不同的辅助模型可替换性更换 LLM 提供商时只需修改注册表五、实测模拟辅助模型故障5.1 测试场景pytest.mark.asyncioasyncdeftest_fallback_to_main_model():辅助模型失败时降级到主模型# Mock 辅助模型抛异常aux_clientMockAuxiliaryClient(side_effectTimeoutError)main_clientMockMainClient(return_valueSummary from main model)engineContextEngine(model_registryMockRegistry(aux_client,main_client))resultawaitengine.compress(test_messages)assertresultSummary from main modelassertaux_client.call_count1assertmain_client.call_count1pytest.mark.asyncioasyncdeftest_fallback_to_static():两个 LLM 都失败时降级到静态回退aux_clientMockAuxiliaryClient(side_effectTimeoutError)main_clientMockMainClient(side_effectRateLimitError)engineContextEngine(model_registryMockRegistry(aux_client,main_client))resultawaitengine.compress(test_messages)assert自动摘要inresultassert静态回退inresultassertstock_queryinresult# 工具名被提取pytest.mark.asyncioasyncdeftest_cooldown_skip():冷却期内跳过辅助模型aux_clientMockAuxiliaryClient(side_effectTimeoutError)main_clientMockMainClient(return_valueSummary)engineContextEngine(model_registryMockRegistry(aux_client,main_client))# 第一次调用辅助失败进入冷却awaitengine.compress(test_messages)assertaux_client.call_count1# 第二次调用冷却中直接跳过辅助模型awaitengine.compress(test_messages)assertaux_client.call_count1# 没有再次调用assertmain_client.call_count25.2 运行结果$ pytest tests/test_fallback.py -v test_fallback_to_main_model PASSED test_fallback_to_static PASSED test_cooldown_skip PASSED六、总结与展望6.1 核心要点回顾压缩不能失败三级容灾确保任何情况下都有摘要产出静态回退是兜底零 LLM 成本也能保留文件路径和工具名称冷却期避免浪费辅助模型故障后不做无谓重试6.2 一个设计原则“系统最脆弱的环节决定了整体的可靠性。在 LLM Agent 中外部 API 是最不可靠的环节——因此你的容灾设计必须假设 API 随时可能挂掉。”下期预告《Token-Budget 尾部保护别再按轮次保护上下文了》为什么保护最近 12 轮是个糟糕的策略Token-budget 如何按实际 token 消耗精确保护边界对齐和用户消息锚定的实现细节敬请期待版权声明本文为 CSDN 博主「翁勇刚」的原创文章遵循 CC 4.0 BY-SA 版权协议转载请附上原文出处链接及本声明。

相关新闻