Nodejs也能写Agent - 23.LangGraph篇 - 可靠性与容错
上一篇我们管住了上下文窗口该 trim 的 trim该摘要的摘要RAG 也不再一股脑乱塞。窗口干净了Agent 就稳了吗并没有。生产里 LLM 会超时、会 429、Ollama 会挂、ReAct 会空转烧光额度——喂对了内容照样可能直接崩给你看。本篇管「挂了怎么办」——重试、Fallback、防死循环让异常变成优雅降级而不是用户脸上的 stack trace。老规矩本文以官网最新文档核对过Fault tolerance、Prebuilt middleware、Agents。入口继续createAgent——别再抄createReactAgent。重试 / Fallback / 调用上限优先挂官方中间件别先上手写一整套invokeWithRetry。一、生产里 Agent 怎么死先认清「会死在哪」再谈怎么救。场景典型原因表现超时模型负载高、网络慢、prompt 过长挂起后 timeout429 限流API 配额用尽、并发过高Rate limit exceeded模型不可用Ollama 没起、模型没 pull、服务宕机ECONNREFUSED、model not found上下文超限messages RAG 超窗400 / context length exceeded失控循环模型一直吐tool_calls烧 token、拖超时最后 recursive 爆官网把错误按「谁来修」分得更清楚——不同错误不该用同一招错误类型谁来修策略官方手段瞬时故障网络、限流系统自动指数退避重试modelRetryMiddleware/toolRetryMiddlewareLLM 可恢复工具失败、解析翻车模型错误进ToolMessage让模型改主意工具返回错误串JS 尚无ToolErrorMiddleware用户可修复缺信息、指令不清人暂停等人interrupt/ HITL供应商宕机系统自动换备选模型modelFallbackMiddleware失控循环系统自动封顶调用次数modelCallLimitMiddleware/toolCallLimitMiddlewarerecursionLimit未知异常开发者往上抛别瞎 catch无中间件硬吞工具失败时把错误信息回给模型它往往能自己换招。官网 JS 侧ToolErrorMiddleware尚未提供——继续走「错误内容进 ToolMessage」即可别等一个还不存在的 API。瞬时供应商挂工具可恢复失控循环未知错误发生错误类型?Retry 退避Fallback 模型错误进 ToolMessageCallLimit / recursionLimit向上抛出二、瞬时故障重试网络抖一下、偶发 429立刻失败只会逼用户狂点刷新。策略上策略说明固定间隔每次等一样久简单但 429 时可能越重试越堵指数退避第 n 次等initialDelayMs * backoffFactor^n给服务喘息时间maxRetries一般 23 次封顶别无限重试Chat 模型自身也可能有maxRetries但挂在 Agent 上时官网主路径是中间件——模型调用和工具调用各管各的import{createAgent,modelRetryMiddleware,toolRetryMiddleware,}fromlangchain;import{ChatOllama}fromlangchain/ollama;import{tool}fromlangchain/core/tools;import*aszfromzod;constsearchWebtool(async({q}:{q:string})搜索结果${q},{name:search_web,description:搜索网页外部 API值得重试,schema:z.object({q:z.string()}),});constllmnewChatOllama({model:qwen2.5:7b,temperature:0});constagentcreateAgent({model:llm,tools:[searchWeb],systemPrompt:需要外部信息时调用 search_web。,middleware:[// 模型超时 / 限流 / 5xx 类瞬时错误modelRetryMiddleware({maxRetries:3,backoffFactor:2.0,initialDelayMs:1000,}),// 工具只重试会抖的外部调用本地 read 别无脑重试toolRetryMiddleware({maxRetries:2,tools:[search_web],backoffFactor:2.0,initialDelayMs:500,}),],});要点默认就是指数退避backoffFactor: 0才退回固定间隔。toolRetryMiddleware用tools: [...]收窄范围——官网原话文件系统read失败多半重试也没用网页搜索超时才值得再试。onFailure: continue时重试用尽可返回带错误说明的AIMessage让 Agent 有机会收尾而不是整段炸穿。手写forsleep也能懂原理生产别重复造轮子中间件已经把退避、jitter、失败策略打包好了。三、主模型挂了Fallback重试用尽、或者主模型整机不可用时换备选继续服务主模型 qwen2.5:7b 失败 → 备选 llama3.1:8b或其它已 pull 的模型import{createAgent,modelFallbackMiddleware}fromlangchain;import{ChatOllama}fromlangchain/ollama;constprimarynewChatOllama({model:qwen2.5:7b,temperature:0});constfallbacknewChatOllama({model:llama3.1:8b,temperature:0});constagentcreateAgent({model:primary,tools:[],middleware:[// 主模型失败后按顺序尝试备选可传多个modelFallbackMiddleware(fallback),],});注意备选能力可能不同——回答风格、工具遵从度都会变。日志或 UI 最好标一句「已切换备选模型」别假装什么都没发生。Fallback 解决的是「模型/供应商挂了」解决不了「问题本身无解」。盯 fallback 触发率太高说明主路径在持续抽风。四、防死循环recursionLimit Call LimitReAct 环里模型若一直返回tool_calls而不给最终答案就会空转import{GraphRecursionError}fromlangchain/langgraph;try{awaitagent.invoke({messages:[{role:user,content:帮我做一件超复杂的事}]},{recursionLimit:15}// 生产可配环境变量默认别太大);}catch(err){if(errinstanceofGraphRecursionError){// 别把 stack 甩给用户return任务步骤过多请简化问题后再试。;}throwerr;}但要分清两层闸门闸门数的是什么超限表现recursionLimitLangGraphsuper-step调度滴答不是「模型调用次数」抛GraphRecursionErrormodelCallLimitMiddleware/toolCallLimitMiddleware业务语义上的模型/工具调用次数可exitBehavior: end优雅收束挂了beforeModel/afterModel这类会编译成独立节点的中间件会多占 super-stepwrapModelCall包在原节点里一般不另计。所以recursionLimit要留余量真正「最多调几次模型」交给 Call Limit 更直观。import{createAgent,modelCallLimitMiddleware,toolCallLimitMiddleware,}fromlangchain;import{ChatOllama}fromlangchain/ollama;constagentcreateAgent({model:newChatOllama({model:qwen2.5:7b,temperature:0}),tools:[/* ... */],middleware:[modelCallLimitMiddleware({runLimit:15,// 单次 invoke 内最多 15 次模型调用exitBehavior:end,// 到顶优雅结束而不是甩异常}),toolCallLimitMiddleware({runLimit:30,// 单次 invoke 内工具调用封顶}),],});runLimit一次用户请求内计数下轮重置。threadLimit整条会话累计需要 Checkpointer。两道闸一起上Call Limit 管业务预算recursionLimit防图调度层面真失控。五、缓存与熔断轻量中间件解决「这次调用怎么扛」缓存和熔断解决「别把下游打爆 / 别重复烧钱」。精确缓存 vs 语义缓存类型命中条件MVP 思路精确缓存问题字符串完全一致可 hash内存Map TTL语义缓存embedding 相似度过阈值向量库本篇不展开typeCacheEntry{value:string;expiresAt:number};constexactCachenewMapstring,CacheEntry();constTTL_MS5*60*1000;functioncacheKey(question:string){returnquestion.trim().toLowerCase();}functiongetCached(question:string):string|undefined{consthitexactCache.get(cacheKey(question));if(!hit)returnundefined;if(Date.now()hit.expiresAt){exactCache.delete(cacheKey(question));returnundefined;}returnhit.value;}functionsetCached(question:string,value:string){exactCache.set(cacheKey(question),{value,expiresAt:Date.now()TTL_MS,});}// 「现在几点」「今天天气」——实时题别进缓存注意不同用户 / 不同thread_id是否共享缓存要想清楚带隐私或个性化的回答默认不要全局共享。Circuit Breaker 简述连续失败时与其每次都去撞已经挂掉的 Ollama不如短暂拒绝关闭 → 失败累积 → 打开直接拒→ 冷却后 → 半开放一枪试探→ 成功则关闭classSimpleBreaker{privatefailures0;privateopenUntil0;constructor(privatethreshold5,privatecoolDownMs30_000){}getisOpen(){returnDate.now()this.openUntil;}beforeCall(){if(this.isOpen){thrownewError(服务暂时不可用请稍后重试熔断开启);}}onSuccess(){this.failures0;this.openUntil0;}onFailure(){this.failures1;if(this.failuresthis.threshold){this.openUntilDate.now()this.coolDownMs;this.failures0;}}}constbreakernewSimpleBreaker();asyncfunctioninvokeWithBreaker(run:()Promiseunknown){breaker.beforeCall();try{constresultawaitrun();breaker.onSuccess();returnresult;}catch(e){breaker.onFailure();throwe;}}这是教学级 MVP。完整半开态、按依赖隔离可后续对标 resilience4j 一类库本篇目标是建立心智不是重造运维平台。六、组合生产最小可靠骨架把前面几招叠在一颗createAgent上import{createAgent,modelRetryMiddleware,toolRetryMiddleware,modelFallbackMiddleware,modelCallLimitMiddleware,toolCallLimitMiddleware,}fromlangchain;import{ChatOllama}fromlangchain/ollama;import{GraphRecursionError}fromlangchain/langgraph;import{tool}fromlangchain/core/tools;import*aszfromzod;constgetWeathertool(async({city}:{city:string})${city}晴25°C,{name:get_weather,description:查询城市天气,schema:z.object({city:z.string()}),});constprimarynewChatOllama({model:qwen2.5:7b,temperature:0});constfallbacknewChatOllama({model:llama3.1:8b,temperature:0});constagentcreateAgent({model:primary,tools:[getWeather],systemPrompt:需要天气时调用 get_weather。,middleware:[modelRetryMiddleware({maxRetries:2,backoffFactor:2.0,initialDelayMs:1000,}),toolRetryMiddleware({maxRetries:2,tools:[get_weather],}),modelFallbackMiddleware(fallback),modelCallLimitMiddleware({runLimit:15,exitBehavior:end}),toolCallLimitMiddleware({runLimit:30}),],});exportasyncfunctionsafeInvoke(userText:string){try{returnawaitagent.invoke({messages:[{role:user,content:userText}]},{recursionLimit:25}// 比 callLimit 留余量);}catch(err){if(errinstanceofGraphRecursionError){return{messages:[{role:assistant,content:任务步骤过多请简化问题后再试。,},],};}throwerr;}}retry / fallback / 触顶时你才能在 LangSmith 里看见「到底换过几次、卡在哪」。可靠性没有可观测就是盲修。常见坑无重试直接失败偶发抖动逼用户狂点至少模型侧maxRetries: 2。重试无退避429 时立刻重试只会更堵用指数退避。工具无脑全量重试本地/确定性失败不值得重试tools: [...]收窄。只有recursionLimit、没有 Call Limitsuper-step ≠ 业务调用次数两道闸一起上更稳。Fallback 不告知备选质量不同日志/UI 应标注。缓存误用天气、时间、用户私有回答不该进全局精确缓存。熔断阈值乱调太敏感则误杀太钝则雪崩。瞎 catch 未知异常官网建议处理不了的往上抛方便调试。

相关新闻

VMware与VirtualBox虚拟机与主机双向复制粘贴配置与排错指南

VMware与VirtualBox虚拟机与主机双向复制粘贴配置与排错指南

1. 从“隔阂”到“丝滑”:为什么虚拟机与主机的复制粘贴如此重要如果你和我一样,经常在Windows主机上跑一个Linux虚拟机(比如Ubuntu、CentOS)做开发、测试或者学习,那你一定遇到过这个让人抓狂的场景:在虚拟…

2026/7/22 14:50:27 阅读更多 →
SpringBoot数据库操作方案对比与实战优化

SpringBoot数据库操作方案对比与实战优化

1. SpringBoot数据库操作的核心价值SpringBoot作为Java生态中最流行的应用框架之一,其数据库操作能力直接决定了企业级应用的开发效率。我在实际项目中发现,合理的数据库访问层设计能让后续维护成本降低40%以上。SpringBoot通过自动配置和starter机制&am…

2026/7/22 14:50:27 阅读更多 →
算力的隐形代价:当AI的“水足迹”成为下一个技术瓶颈

算力的隐形代价:当AI的“水足迹”成为下一个技术瓶颈

算力的隐形代价:当AI的“水足迹”成为下一个技术瓶颈 最近,一条关于“2030年AI耗水量够13亿人用一年”的消息冲上了热搜榜。乍一看,这个数字似乎有些耸人听闻,甚至让人觉得又是哪个环保组织的夸张宣传。但作为身处技术一线的开发者…

2026/7/22 14:49:27 阅读更多 →

最新新闻

AI 推理服务的多模型共存:同集群部署不同版本的策略

AI 推理服务的多模型共存:同集群部署不同版本的策略

AI 推理服务的多模型共存:同集群部署不同版本的策略 一、集群里同时跑了 vLLM 0.4、TGI 1.3、TensorRT-LLM 三个版本,资源分配一团糟 AI 推理服务的版本管理比传统微服务复杂得多。传统微服务部署的是"同一个 Docker 镜像的不同版本"——v1、v…

2026/7/22 15:30:54 阅读更多 →
【软件工程】板块总结 + 下期预告(面向对象技术)

【软件工程】板块总结 + 下期预告(面向对象技术)

适合读者:软考中级备考同学 阅读时间:4分钟 内容:软件工程板块知识体系回顾、核心对比表、易错点汇总、下期预告一、板块内容概览 “软件工程”板块共完成 24篇 文章,覆盖以下知识模块:知识模块篇数核心内容软件工程基…

2026/7/22 15:30:54 阅读更多 →
Agent 服务网格化:像治理微服务一样治理智能体集群

Agent 服务网格化:像治理微服务一样治理智能体集群

Agent 服务网格化:像治理微服务一样治理智能体集群 一、当你线上跑了 200 个 Agent 实例,却没有一个统一的流量治理层 Agent 上生产之后,最容易被忽视的一层是"路由层"。大多数团队的做法是:一个 Agent 对应一个 Pod&am…

2026/7/22 15:30:54 阅读更多 →
Rust、Go与Node.js性能对比与优化实践

Rust、Go与Node.js性能对比与优化实践

1. 为什么我们需要关注编程语言的性能? 在当今这个数据爆炸的时代,性能已经成为衡量编程语言价值的重要指标之一。作为一名长期奋战在一线的开发者,我见证了太多因为性能问题导致的系统崩溃和用户体验下降。特别是在处理高并发、大数据量场景…

2026/7/22 15:30:54 阅读更多 →
【软件工程】复习日:开发模型 + 设计方法 + 测试(整理对比表)

【软件工程】复习日:开发模型 + 设计方法 + 测试(整理对比表)

适合读者:软考中级备考同学 阅读时间:5分钟 内容:软件开发模型、结构化设计方法、软件测试的对比表、易错点、记忆口诀汇总一、软件开发模型对比模型核心驱动力需求要求风险控制用户参与度适用场景瀑布模型阶段顺序完全明确弱低(前…

2026/7/22 15:30:54 阅读更多 →
阿里禁用Claude Code事件解析与AI编程工具风险应对

阿里禁用Claude Code事件解析与AI编程工具风险应对

1. 事件背景与行业影响7月10日阿里内部突然发布全员通知,全面禁用Claude Code相关工具的消息在技术圈引发震动。作为国内头部科技企业,这类技术栈的突然调整往往预示着行业风向的变化。根据多方信源交叉验证,此次禁令覆盖阿里集团所有BU&…

2026/7/22 15:29:54 阅读更多 →

日新闻

TI DSP系统配置模块SYSCFG详解:中断机制与主设备优先级配置实战

TI DSP系统配置模块SYSCFG详解:中断机制与主设备优先级配置实战

1. 项目概述与SYSCFG模块的核心价值在嵌入式系统,尤其是像TI C6000系列这样的高性能DSP开发中,我们常常会与芯片手册里那些密密麻麻的寄存器打交道。很多开发者可能更关注算法实现、内存优化或者外设驱动,但对于一个稳定、高效的系统而言&…

2026/7/22 0:00:26 阅读更多 →
微信Server酱:高到达率的应急通知方案实践

微信Server酱:高到达率的应急通知方案实践

1. 为什么我们需要"最次"的通知方案? 在数字化协作环境中,消息通知系统的重要性不言而喻明。但现实情况是,企业级通知方案往往需要复杂的API对接(如企业微信、钉钉、飞书),个人开发者的小项目又经…

2026/7/22 0:00:26 阅读更多 →
甲方要的“简洁“PPT,到底是简洁还是省事?

甲方要的“简洁“PPT,到底是简洁还是省事?

甲方说"简洁一点",乙方听到的是"少做几页"。甲方说"不要太复杂",乙方理解成"别放图表了"。结果交过去,甲方说"我说的简洁不是这个意思"。"简洁"这个词在PPT语境里,是…

2026/7/22 0:00:26 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/22 8:58:19 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/21 5:34:47 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/22 12:54:44 阅读更多 →

月新闻