自研 LangGraph 中间件:实现函数级监控

12 阅读4分钟

💡 如果你正在用 LangGraph 构建生产级 Agent,这篇文章将帮你解决关键问题: 如何监控每个节点的耗时、自动重试异常?(执行层中间件)

LangChain 1.0 更新了,带来了强大的工具链和生态。但在实际将其推向生产环境时,我发现了一个棘手的问题:我的 Agent 就像一个“黑盒”,跑得慢了不知道卡在哪,报错了不知道断在哪。 翻遍了官方文档中的 10 多种 Middleware,似乎都是在处理 Prompt,却鲜有提及代码执行层面的监控。本文就来聊聊这缺失的一环。

本文将澄清 LangChain 官方中间件的真正用途,并分享我如何在开源项目 Deep Research Agent 中,实现了一套真正用于执行控制的轻量级 AOP 系统。

🔍 官方 Middleware:强大的“处理管线”,但侧重点不同

翻阅 LangChain 1.0 丰富的官方文档,你会发现它内置了多达 14 种以上的 Middleware,涵盖了从消息过滤、上下文压缩(Summarization)到多模态处理等方方面面。

这些中间件非常强大,但它们的设计重心更多在于 Message Engineering(消息工程) 层面:

  • 输入侧:如何修剪(Trim)、过滤(Filter)或增强用户的输入 Prompt?
  • 历史侧:如何管理过长的 Chat History(如 SummarizationMiddleware)?

本质上,它们更像是“数据预处理/后处理”的管道。

但在“系统运维(Ops)”层面的留白: 当我们从“Prompt 调优”转向“系统稳定性建设”时,会发现还有一块拼图没有填满。比如:

  1. 📉 精准耗时监控:我想知道“PlanNode”这个纯 Python 函数具体跑了 3.5 秒还是 0.1 秒。
  2. 🐛 函数级异常兜底:当某个代码块抛出 KeyError 时,自动重传或降级,而不是让整个 Agent 崩溃。
  3. 🕵️ 深度代码追踪:在不依赖 LangSmith 的情况下,手动打印出清晰的 Python 执行堆栈日志。

针对这些痛点,此时我们需要的就是另一种 “执行层中间件”(Execution Middleware)

🛠️ 填补空白:手写“执行层” AOP

为了填补这一空白,我在 Deep Research Agent 中设计了一套基于 装饰器模式 的执行层中间件系统。它不关心 Prompt 怎么变,只关心函数是怎么执行的

1. 架构定位差异

特性LangChain 官方 Middleware本项目设计的 AOP Middleware
关注对象Prompt / Messages / HistoryNode Function / Execution Flow
典型用途历史记录摘要、上下文压缩耗时统计、错误重试、日志埋点
技术实现Message ModifiersPython Decorators (AOP)

2. 代码实现:让监控无处不在

我们的设计目标是:零侵入地为 Agent 加上“行车记录仪”

# 中间件管理器核心逻辑
class MiddlewareManager:
    def wrap_node(self, node_func):
        @functools.wraps(node_func)
        async def wrapped_node(state):
            node_name = node_func.__name__
            
            # 【执行前】这里是做性能计时的好地方
            start_time = time.time()
            for mw in self.middlewares:
                await mw.before_node_execution(node_name, state)
            
            try:
                # 【执行中】真正的业务逻辑
                result = await node_func(state)
                
                # 【执行后】记录耗时
                duration = time.time() - start_time
                for mw in self.middlewares:
                    await mw.after_node_execution(node_name, state, result, duration)
                return result
                
            except Exception as e:
                # 【出错时】错误兜底
                await self.on_error(node_name, state, e)
                raise e
        return wrapped_node

3. 深层思考:为什么官方不包含这个?

你可能会问,为什么 LangChain 官方不内置这种执行层 AOP?(除了复杂的 Callback 系统外)

我认为这是因为 LangGraph 的设计哲学是“图即逻辑”。官方倾向于让你通过在图里显式地加 Edge(边)和 Conditional Entry(条件入口)来处理错误重试,而不是像 Web 框架那样通过 Middleware 隐式处理。

但从开发者的角度看,如果每个 Node 都要写一遍 try-exceptstart_time = time.time(),代码会变得极其臃肿。

这就是我们这套轻量级 AOP 方案存在的意义:它用 Python 的灵活性,换取了业务代码的极致整洁。

🚀 总结

  • 如果你主要关注 Prompt 工程(如对话历史管理、敏感词过滤、内容修剪),请直接使用 LangChain 官方内置的 14+ 种 Middleware,它们是处理 Context 的利器。
  • 如果你深受“面条代码”之苦,想要优雅地统一管理代码执行层的日志、监控和异常,那么请参考本项目的 AOP Middleware 实践。

两者并不冲突,甚至是互补的。

🔗 项目源码地址: github.com/changflow/d…

如果你对 Agent 架构设计感兴趣,欢迎来 Repo 提 Issue 讨论!