基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
  • 知识卡片碎片时间过考点
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
    • AI 热点每日 AI 资讯与动向
AI 工作台
简历
旧版
AIHT
内容
精选全部 AI 动态热点榜AI 日报收藏
分类
模型行业编程产品技术观点开源
更多
Agent 接入
返回首页开发者导航
AIHT
精选 / 资讯详情
8.0
热点
AI SCORE
技术实践2026-11-05 20:29

Agent 上线前要补齐的运行控制

dev.to · AI#LangChain#LangGraph#Agent
Editor brief · 编辑速览

文章以实际运行的代码片段介绍 LangChain 与 LangGraph 的生产能力,关注循环失控、接口故障和危险工具调用。另提示 create_react_agent 的弃用迁移,以及通过中间件介入模型和工具调用。

文章思维导图
Knowledge map
拖拽缩放
AI deep read

AI 深度解读

Agent 能回答天气,不代表它能熬过上线后的第一周。作者把关注点放在循环失控、接口重试、模型故障、危险操作和进程崩溃上,结论很明确:这些问题需要运行时规则,不能只靠提示词约束。

这份材料的价值在于,每项示例都附了作者的运行结果,也暴露了失败尝试。不过,标题虽然写着 10 项,提供的原文在第 9 项代码中途截断,第 10 项完全缺失。下面只解读能够确认的部分。

先管住调用,再谈 Agent 有多聪明

作者使用的环境是 langchain 1.4.9、langchain-core 1.7.6、langgraph 1.2.20、langchain-openai 1.7.4 和 Python 3.13。前 5 项使用真实模型,整组调用花费远低于 1 美分;后面的 LangGraph 运行时实验无需模型。

旧教程里的 create_react_agent 已有弃用提示:从 LangGraph V1.0 开始弃用,计划在 V2.0 移除,入口应迁移到 from langchain.agents import create_agent。但换入口只是起点。

Agent 是模型反复调用工具,直到任务完成的循环。中间件(Middleware)包在循环外,可以检查、修改或终止模型和工具调用。作者用一个永远返回「部署中」的工具,测试以下限制:

    middleware=[
        ModelCallLimitMiddleware(run_limit=4, exit_behavior="end"),
        ToolCallLimitMiddleware(tool_name="check_deploy_status", run_limit=3),
    ],

要看清两处 run_limit:一个限制模型调用,一个限制指定工具。实验中,工具执行 3 次后被拦截,模型仍想继续,最终由模型调用上限结束运行。

提示词表达意图,中间件执行边界。

exit_behavior="end" 返回结束消息,调用方需要识别它;希望明确失败,可以用 "error"。拿到正常返回值,不能直接认定任务成功。

网络重试也适合放在这一层。作者让 search_runbook 前两次抛出 ConnectionError,配置 ToolRetryMiddleware(max_retries=3, retry_on=(ConnectionError,), initial_delay=0.2, backoff_factor=2.0, jitter=False),第三次成功。实际间隔是 [0.2, 0.41] 秒,模型没有看到前两次错误。

这里的关键是筛选异常和确认幂等性,即重复执行不会重复产生业务结果。检索可以重试,扣款需要幂等键。重试耗尽后,默认 on_failure="continue" 把错误文本交给模型;"error" 则终止运行。

模型回退同样有代价。作者账户上的 gpt-4.1-nano 返回 404,ModelFallbackMiddleware("openai:gpt-4o-mini") 成功接管,返回元数据中的模型是 gpt-4o-mini-2024-07-18。这只能证明该账户当时的现象,不能据此断言模型已全面退役。作者还检查了源码:任何异常都可能触发回退,请求自身的错误也可能被掩盖,因此要记录实际回答的模型。

危险操作要按调用审批,权限还得落到底层

langchain 1.4.6 新增的 interrupt_mode="per_call",让每次工具调用拥有独立的暂停和 ID。它解决的是同一轮模型发出多个操作时,审批结果如何准确对应的问题。

作者让 Agent 并行重启 checkout,并把 search 扩到 40 个副本。人工批准重启,拒绝扩容并说明最多 12 个。结果重启执行,40 副本的扩容没有执行;模型随后提出 12 副本的新调用,再次暂停等待批准。

拒绝一次,不会自动批准模型修改后的方案。

暂停需要检查点保存器(checkpointer)保存状态。示例使用 InMemorySaver,通过 thread_id="incident-42" 关联运行,以中断 ID 组织答案,再用 Command(resume=answers) 恢复;调用指定 version="v2"。扩容工具的 allowed_decisions 被限制为 ["approve", "reject"]。

中间件顺序也有要求:HumanInTheLoopMiddleware 必须排在工具重试或错误处理中间件之前,排在它前面的中间件恢复时会再次执行。

自定义规则可以使用 wrap_tool_call。调用 handler(request) 就执行工具,直接返回 ToolMessage 就跳过。作者据此拦截包含 DROP、DELETE、TRUNCATE、UPDATE 的 SQL,确实阻止了删除工具运行。

但这个例子也展示了两层局限:字符串匹配会误伤 SELECT last_updated FROM orders;工具被拦截后,模型仍向用户提供了删除语句。它保护的是执行入口,并不保证回答内容安全。数据库只读账户才是权限底线,中间件适合作为补充。

保存过状态,不等于崩溃后不会重做

作者对 durability 的实验很有参考价值:在第三步 open_ticket 内调用 os._exit(1),直接杀掉进程,再尝试恢复。

三个模式的结果不同:

  • exit 只在运行退出时保存。强杀后没有状态,恢复抛出 EmptyInputError。
  • async 后台写入。实验节点只耗时微秒,写入没跟上,只保存了空进度,三个步骤全部重跑。
  • sync 在进入下一步前等待保存,保住了 fetch_logs 和 summarise,只重跑 open_ticket。

作者最初用 Python 异常代替强杀,三个模式都保留了进度,因为异常仍允许有序结束。异常测试不能替代崩溃测试。

sync 的代价是每一步等待数据库写入。它适合减少带副作用流程的重做,但不能据此推导出「开工单只会执行一次」:如果外部操作成功、状态尚未保存就崩溃,仍可能重复。业务幂等不能省。

另一类故障是节点卡住。LangGraph 1.2 增加节点级 timeout= 和 error_handler=,后者在重试耗尽后可返回 Command,更新状态并指定下一节点。

作者通过 set_node_defaults 设置 timeout=1.0、RetryPolicy(max_attempts=2, initial_interval=0.1),让模拟 CRM 查询两次超时后转到人工处理节点。终端记录总耗时约 3.0 秒,并非严格的 2.1 秒。

这里有个容易踩中的条件:超时只支持异步节点。默认超时也作用于错误处理节点,因此处理函数必须写成 async def;节点内部若用 time.sleep 阻塞,也不能指望按时取消。默认设置还不会自动继承给子图。

缓存和流式输出,也有各自的边界

节点缓存适合相同输入产生相同输出的昂贵步骤。作者模拟了一次耗时 1.5 秒的检索,并为节点配置:

builder.add_node(
    "retrieve",
    retrieve,
    cache_policy=CachePolicy(ttl=300, key_func=lambda s: s["question"].strip().lower()),
)

重点是 key_func:去掉首尾空格、统一小写后,两种写法共用缓存键。配合 builder.compile(cache=InMemoryCache()),首次查询耗时 1.51 秒,第二次显示 0.00 秒,且带有 'cached': True。

这证明了示例命中缓存,没有证明真实 RAG 的性能收益。检索耗时来自 time.sleep(1.5);缓存结果还保留了第一次问题的原始文本,提醒我们:归一化丢掉的信息,必须确实不影响结果。InMemoryCache 重启即失,原文另提到可使用 SqliteCache。

第 9 项讨论 stream(version="v2") 与 GraphOutput.interrupts。可见代码使用 stream_mode=["updates", "custom"],按 part["type"]、part["data"] 读取事件,并用 get_stream_writer() 发送进度。它试图解决旧接口在不同流模式下元组形状变化,以及暂停信息混入状态 __interrupt__ 的问题。

但原文在打印新版中断信息时截断,未提供完整输出,不能补写验证结论。

我的判断

我认同作者把运行边界放在提示词之外。限额、重试、审批、恢复路径,都属于应用必须明确承担的工程责任;内置中间件比散落在每个工具里的重复代码更方便集中检查。

不过,这些是小规模功能实验。作者没有提供吞吐、延迟分布或故障率对照,也没有证明它们整体优于手写循环、普通任务队列或其他工作流方案。「更适合生产」主要是工程经验判断。

其中,强杀恢复、审批拒绝后再次暂停、缓存返回旧文本,都是很有用的实测细节。SQL 字符串过滤和内存保存器则更适合说明机制。框架能减少胶水代码,权限、幂等和持久化设计仍需要团队自己落实。

能拿来做什么

  • 为轮询部署状态、反复修复代码的 Agent 同时设置模型和工具调用上限,并让应用区分任务完成、额度耗尽和执行异常。
  • 给检索、知识库查询等读取工具集中配置重试,只匹配可恢复异常;给写入操作先设计幂等,再决定是否重试。
  • 为重启、扩容等高影响工具接入逐次审批,在前端用中断 ID 对应操作和结果,修改参数后的调用继续独立审批。
  • 给长流程补上强杀恢复测试和异步超时测试;接入节点缓存前,检查缓存键是否保留了决定结果的必要信息。
Original source

本文由 AI 通读原文后重新编写,观点部分为站内判断,细节以原文为准,来源 dev.to · AI,原文版权归原作者所有。

阅读原文
上一篇
用逐算子对照验证定点 Transformer
下一篇
跨 Shell 参数拆分导致 AI 误删仓库

资讯速览

dev.to · AI · 2026-11-05 20:29

AI 评分 8.0 / 10

相关标签

#LangChain#LangGraph#Agent

继续阅读

  • 开源游戏测试 Agent 的实测与架构
  • 用逐算子对照验证定点 Transformer
  • 跨 Shell 参数拆分导致 AI 误删仓库
  • 盘古新版将上线,重点增强鸿蒙代码生成
  • AI 告警分级为何连续两天漏报