ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

对话记忆 L1 提取的“前台截止时间“:Friend 后端整批记忆静默丢失事故的根因、修复与回归保障

对话记忆 L1 提取的“前台截止时间“:Friend 后端整批记忆静默丢失事故的根因、修复与回归保障 对话记忆 L1 提取的前台截止时间Friend 后端整批记忆静默丢失事故的根因、修复与回归保障【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend本篇技术指南围绕 Friend 开源仓库的运维复盘文档 memory-l1-foreground-deadline.md 展开剖析记忆提取管线memory_l1因继承后台网关 15 秒首字节截止时间deadline而在对话终结finalization阶段静默丢失整批记忆的完整事故链。读完本文你将掌握本仓库 LLM 特征路由中前台/后台截止时间的架构设计与声明机制、get_llm的多分支客户端构建路径、严格strict提取接缝的类型化错误转换以及两套回归测试如何钉死前台调用继承后台截止时间Failure-Class: FC-foreground-call-inherits-background-deadline这一故障类。事故现象9–21 场对话/天每场整批记忆静默丢失复盘文档记录的时间窗口是2026-09-01 至 2026-09-05GCP 生产错误流pusher 容器中每天稳定出现以下签名且无任何重试ERROR:utils.llm.working_observations:Error extracting memory L1 archive items: invoke_failed:APITimeoutError关键点在于静默二字这条日志每天出现 9–21 次每次都意味着该场对话的整批已提取记忆extracted-memory batch被丢弃但对话终结流程本身继续成功结束没有任何用户可见的失败提示——直到这批记忆在后续的长期记忆中永久缺席才被发现。从源码可以确认该日志的准确出处在 working_observations.py 中调用model.invoke(messages)的异常分支会记录invoke_failed:%s以异常类型名结尾随后进入严格模式的类型化错误上抛逻辑。事故中的APITimeoutError正是 OpenAI 客户端的超时异常在invoke_failed之后被接缝转换成更上层的领域错误。根因一条未声明的 deadline让整段转录在 15 秒内被掐断L1 提取器的工作形态单次结构化调用读取整段转录记忆 L1 提取的入口是extract_l1_memory_archive_items_from_textworking_observations.py它是记忆三层架构L1/L2/短期记忆中最宽的一次扫描接收完整对话转录文本text参数通过_build_l1_messages构造一次请求系统提示要求Extract what they might want to remember later并显式限制最多输出MAX_WORKING_OBSERVATION_ITEMS 32条带证据引用的候选记忆通过parser PydanticOutputParser(...)强制结构化输出 JSONbelief 模型开关会切换到BeliefWorkingObservationBatchschema由get_llm(memory_l1)工厂构建客户端并model.invoke(messages)单次调用完成全部提取。也就是说一整个小时的对话可能需要数十秒才能生成首字节——这与conv_structure对话结构、conv_app_result应用/模板摘要、daily_summary每日总结等前台兄弟特征完全同构都是用户正在等待的、单次调用要读完整个上下文的整转录调用。前台 vs 后台共享网关传输截止时间的错配问题出在客户端截止时间deadline的来源。get_llm在构建客户端时若调用方未显式传入request_timeout则会回落到特征路由声明的默认值而该默认值对未声明的特征就是共享的后台网关传输截止时间——15 秒首字节gateway_resilience.py 中DEFAULT_GATEWAY_FIRST_BYTE_TIMEOUT_SECONDS 15.0。生产 pusher 以OMI_LLM_GATEWAY_FEATURE_MODEgateway运行should_route_features_through_gateway()返回 True所有特征调用都经网关转发clients.py 的 gateway 分支。由于memory_l1当时没有进入_FOREGROUND_TIMEOUT_FEATURES集合feature_request_timeout(memory_l1)返回None于是 L1 提取器继承了这条为廉价后台任务设计的 15 秒首字节截止时间——而整转录结构化调用的 p90 首字节延迟远超 15 秒。结果是截稿前一定超时超时后整批记忆清零。严格模式放大了损失超时变成整批丢弃如果仅是返回空结果还算温和真正的放大器是对话终结流程以strictTrue调用提取器。在 process_conversation.py 中try: extracted_candidates extract_canonical_l1_memory_candidates( uid, conversation.id, conversation.transcript_segments, user_nameuser_name, languagelanguage, strictTrue, prompt_prefixprompt_prefix, rejected_memory_examples_rejected_memory_examples_for_l1(uid, db_clientdb_client), ) except MemoryExtractionError as exc: return _canonical_extraction_unavailable(conversation, source, exc)strictTrue的含义见 working_observations.py任何 provider 异常都会被转换成边界类型化错误WorkingObservationExtractionErrormemory_contracts.py并携带stageinvoke标记失败阶段except Exception as exc: logger.error(Error extracting memory L1 archive items: invoke_failed:%s, type(exc).__name__) if strict: raise WorkingObservationExtractionError(invoke) from exc return []而_canonical_extraction_unavailableprocess_conversation.py的设计意图非常明确严格模式的存在就是为了让 provider 失败不可能被误判为没有记忆——如果超时被当作正常空结果终结流程就会写入空替换empty replacement进而撤回该来源已有的记忆。跳过替换写入虽然避免了误撤回但代价是该场对话的整批 L1 记忆彻底丢失。同类第五起一个被反复踩中的故障类复盘文档明确指出这是故障类FC-foreground-call-inherits-background-deadline的第五个实例conv_structure与daily_summary2026-08-19 在 POST /test-prompt 与对话终结中先后死于约 15.2–15.8 秒的客户端截止时间conv_app_result2026-09-04 在 c9c040bdc2 中修复当时的提交说明还明确memory 家族未审计left the memory family un-auditedmemory_l12026-09-05 现形。这五次事故沉淀出的一条核心工程教训直接写在 model_config.py 的注释中deadline 是调用call的属性不是调用点call site的属性。把 deadline 留给每个调用点自己去记就必然出现新调用点忘记带截止时间的第五次事故。修复方案一条路由声明让所有构建分支自动继承前台截止时间修复本身只有一行关键代码——把memory_l1加入前台截止时间特征集合model_config.pyFOREGROUND_REQUEST_TIMEOUT_SECONDS 60.0 _FOREGROUND_TIMEOUT_FEATURES frozenset( { conv_structure, conv_app_result, daily_summary, # Fifth instance of the class, 2026-09-05: the L1 memory extractor # (get_llm(memory_l1) behind extract_l1_memory_archive_items_from_text) # runs in conversation finalization with strictTrue, so every extraction # that outlived the 15s background gateway deadline raised # APITimeoutError and dropped that conversations whole memory batch — # prod pusher 2026-09-01..05: Error extracting memory L1 archive items: # invoke_failed:APITimeoutError 9-21×/day, no retry. memory_l1, } )配套的解析函数 feature_request_timeout 是唯一的事实来源def feature_request_timeout(feature: str) - float | None: if feature in _FOREGROUND_TIMEOUT_FEATURES: return FOREGROUND_REQUEST_TIMEOUT_SECONDS return None而get_llmclients.py在入口处统一完成解析确保每一条客户端构建分支都拿到前台截止时间if request_timeout is None: # The deadline is a property of the feature, not of the call site... request_timeout feature_request_timeout(feature)随后该值被透传到get_llm的全部四条构建路径clients.pyGateway 模式OMI_LLM_GATEWAY_FEATURE_MODEgateway无 BYOK keygateway_options[request_timeout] request_timeout后调用get_or_create_omi_gateway_llm(...)直连模式非 gatewayroute_options {**route_options, request_timeout: request_timeout}后调用get_default_client(...)BYOK-Gateway 分支用户自带 key 且经网关byok_gateway_options[request_timeout] request_timeout后调用get_or_create_omi_gateway_llm_for_byok(...)BYOK 直连_create_byok_client内部的传输预算也须不低于前台截止时间。同时保持两层优先级调用方显式传入的request_timeout仍然优先于特征路由声明测试test_an_explicit_request_timeout_still_wins_over_the_route用 42.0 秒钉死了这一语义而像conv_folder对话归类gpt-5-nano轻量任务这样的后台特征feature_request_timeout返回None继续使用默认后台截止时间前台集合保持封闭。顺带一提特征路由的底座memory_l1在 QoS 双档模型表中映射到(gpt-5.6-luna, openai)model_config.pyprovider 是显式声明的而非从模型名推断premium/max/byok三档 profile 共享该表model_config.py因此本次声明对三档 profile 与 BYOK 用户一视同仁。修复后运维可观察行为复盘文档明确了修复落地后的行为契约期望效果invoke_failed:APITimeoutError的 L1 签名应从 pusher 错误流中消失——因为 60 秒前台截止时间已远超整转录调用的实际 p90 首字节延迟行为不变量如果某场对话在 60 秒内仍无法完成提取则维持今天的行为——严格接缝抛出类型化WorkingObservationExtractionError终结流程跳过替换写入不会误撤回已有记忆ERROR 行依旧出现。区别在于这时的超时是真实的provider 容量不足信号而非客户端截止时间配错。回归保障两套测试把故障类钉死在路由层本次修复配套了两套单测从路由声明和配置轴不变性两个方向防止复发。事故级回归test_memory_l1_foreground_deadline.pytest_memory_l1_foreground_deadline.py 直接驱动真实生产函数对抗一个比后台截止时间慢的 provider 桩PROVIDER_FIRST_BYTE_SECONDS 25.0即真实 provider 需要 25 秒才出首字节超过 15 秒后台截止、低于 60 秒前台截止路由声明feature_request_timeout(memory_l1) FOREGROUND_REQUEST_TIMEOUT_SECONDS且前台家族严格封闭于{conv_structure, conv_app_result, daily_summary, memory_l1}后台特征conv_folder保持None构建分支全覆盖gateway 模式、直连模式、BYOK-Gateway 分支分别断言captured[options][request_timeout]等于 60 秒显式request_timeout42.0必须胜出真实提取器端到端严格批次与优雅graceful批次在慢 provider 下都能成功产出 itemsarchive_id以l1_前缀、speaker_label/confidence等字段完整事故复现对照把FOREGROUND_REQUEST_TIMEOUT_SECONDS临时改回 15 秒_extract_strict()精确复现线上签名——WorkingObservationExtractionError(stageinvoke)且__cause__是openai.APITimeoutError优雅模式则返回空列表经由extract_canonical_l1_memory_candidates包装层时抛出MemoryExtractionError。配置轴不变性test_memory_l1_deadline_invariance.pytest_memory_l1_deadline_invariance.py 枚举 L1 车道的全部配置轴证明没有任何一个轴会让截止时间悄悄跌回 15 秒提示前缀 ± 缓存共享会话提示前缀ConversationPromptPrefix开启/关闭 prompt 缓存deadline 均保持 60 秒且缓存键cache_key的额外 kwargs 透传不会重置 deadlinebelief 模型开关该开关只切换解析器 schemaBeliefWorkingObservationBatchvsWorkingObservationBatch绝不触碰请求截止时间显式注入客户端调用方注入现成 client 时完全绕过工厂resolved_deadlines为空工厂不被咨询deadline 由注入方自负BYOK 分支BYOK-Gateway 与 BYOK 直连两条构造路径的request_timeout均不低于前台截止时间边界不变量FOREGROUND_REQUEST_TIMEOUT_SECONDS 120.0——同步终结路径运行在 1500 秒路由预算下单条调用截止时间必须远小于该预算避免一场对话卡死整个 workerPROVIDER_FIRST_BYTE_SECONDS DEFAULT_GATEWAY_FIRST_BYTE_TIMEOUT_SECONDS——若 provider 某天能在后台截止时间内应答则整个故障类自然失效全轴事故对照在每个配置轴上把前台截止时间改回 15 秒都必须复现WorkingObservationExtractionError(stageinvoke)证明测试确实咬得住。此外同一故障类的前一个实例conv_app_result的守护测试位于 test_conversation_structure_provider_timeout.py与本次新增测试共同构成前台家族的整体防护网。可复现验证路径如需在本地复现与验证可以运行这两套守护测试位于 backend/tests/unit/cd backend python -m pytest tests/unit/test_memory_l1_foreground_deadline.py \ tests/unit/test_memory_l1_deadline_invariance.py -v测试通过monkeypatch注入慢 provider 桩25 秒首字节与捕获型get_llm无需真实调用外部 LLM也无需真实网关。想亲手看到事故重演只需将model_config.FOREGROUND_REQUEST_TIMEOUT_SECONDS临时改回DEFAULT_GATEWAY_FIRST_BYTE_TIMEOUT_SECONDS15.0再跑严格提取测试即可复现invoke_failed:APITimeoutError→WorkingObservationExtractionError(stageinvoke)→ 整批记忆丢弃的完整事故链。结语把 deadline 声明在路由上而不是祈祷调用点记得带这起事故的完整价值链可以浓缩为一句话15 秒的后台截止时间被一个整转录前台调用继承strictTrue把超时放大为整批记忆静默丢失而一行路由声明加两套回归测试把整个故障类永久钉死。对于任何构建 LLM 特征路由系统的团队_FOREGROUND_TIMEOUT_FEATURES这套前台特征白名单 路由级 deadline 构建分支全透传 配置轴不变性测试的组合拳都是一份可复用的工程范本——deadline 是调用的属性让路由替你记住它而不是让每个新调用点靠记忆和运气。【免费下载链接】FriendAI that sees your screen, listens to your conversations and tells you what to do项目地址: https://gitcode.com/GitHub_Trending/fr/Friend创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表