你的 Agent 可能并不需要过度工程化:一次从 LangGraph 到极简 Agent 的架构反思

5 阅读5分钟

去年 12 月入职后,我从零构建了一个基于 LangGraph 的 Agent 系统。

当时由于本地部署环境受限,加上显存较小、模型能力有限,为了保证系统可用性,我做了不少工程层面的优化。

现在回头看,其中很多设计其实已不再必要。

随着模型能力的提升,很多曾经必须依赖工程解决的问题,已经可以交给模型本身去处理。

这篇文章主要是一次简单的架构反思:

当模型变强之后,我们是否还需要那么复杂的 Agent 系统?

第一版系统架构:复杂但合理

当时系统的整体流程大致如下:

flowchart TD

A[User Query]

B[指代消解]
C[任务拆解]
D[子任务分类]

E[Agent]
F[MCP Tools]
G[知识库 Tool]

H[输出节点]
I[最终回答]

A --> B
B --> C
C --> D

D --> E

E --> F
E --> G

F --> E
G --> E

E --> H
H --> I

整个系统可以理解为一条 多阶段流水线

指代消解

第一步是处理用户问题中的指代关系。

例如:

它的运行情况如何?

如果不结合上下文,系统其实无法理解 “它” 指代什么。

所以需要先通过模型进行 指代消解(Coreference Resolution),将问题补全为完整语义。

任务拆解

第二步是对复杂问题进行拆解。

例如:

今天电站的运行情况如何,如果出错如何解决?

这句话其实包含两个任务:

  • 查询电站运行情况(工具查询)
  • 查询解决方案(知识库检索)

因此需要进行 任务拆解

子任务分类

拆解后的子任务会被分类为三类:

  • 工具调用
  • 知识库查询
  • 其他

其中:

  • 其他任务会直接过滤
  • 工具和知识库任务进入 Agent 执行

Agent 执行

在当时的架构中,子任务分类之后,会进入一个 Agent

这个 Agent 内部包含两类能力:

  • MCP Server Tools
  • 知识库 Tool(RAG)

知识库查询本质上也被视为一个 Tool,通过 MCP Host 挂载 Server 工具,最终组成 Tools 集合。

Agent 会根据子任务选择调用哪个 Tool。

为了避免上下文丢失,我们采用 串行执行子任务:上一个问题 + 答案会被拼接进上下文中,再传递给 Agent 处理下一个子任务。

输出节点

当所有子任务执行完后:

  1. 将子任务结果组装为 Dict
  2. 交给输出节点
  3. 由模型生成最终回答

一些工程优化

为了让系统在本地模型环境下稳定运行,还做了很多优化:

  • Message 只存储 用户输入 + 最终输出
  • 控制上下文 Token
  • 知识库查询包含
    • Query Rewrite
    • Rerank 排序

这套系统在当时确实 非常精细

但现在回头看,也确实 非常复杂

为什么当时会设计这么复杂?

答案其实很简单:模型能力太弱。

当时本地模型存在很多限制:

  • 上下文窗口较小
  • 推理能力有限
  • Tool Calling 不稳定

所以很多事情必须通过工程解决,例如:

  • 任务拆解
  • 上下文管理
  • 流程编排

工程复杂度,本质上是在 弥补模型能力的不足

一个新的思考

后来我注意到两个东西:

  • Agent Skills
  • OpenClaw

Skills 的出现让 Agent 能力可以模块化组织。

而 OpenClaw 的架构则非常简单,本质上可以理解为:

MCP + Agent + Skills

但它却能够完成很多复杂任务。

这让我开始重新思考一个问题:系统是否被我们过度工程化了?

从这些现象可以看到,我们正在逐渐改变一个默认假设:

过去我们习惯认为模型能力有限,所以很多问题必须通过复杂工程解决。

但现在越来越多案例证明:模型本身已经可以解决很多问题。

与其处处限制模型,不如先让模型尝试解决,再在必要的地方补充工程能力。

模型能力的变化

真正让我下决心重构架构的,是 Qwen3.5 的发布。

我原本项目使用的是 Qwen3 14B AWQ,但实际测试发现 Qwen3.5 4B 性能已经超过。

更关键的是它的上下文长度:

Context Length: 262,144
Extensible up to 1,010,000 tokens

此外还有:

  • 更稳定的 Tool Calling
  • 更强的推理能力
  • 多模态支持

这让我意识到一件事:随着模型能力增强,很多过去的工程优化可能已不再必要。

第二版系统架构:极简 Agent

于是我重新设计了整个系统。

flowchart TD

A[User Query]

B[Main Agent]

C[Sub Agent]
D[Tools]

E[Result]

A --> B
B --> C
C --> D
D --> C
C --> B

B --> E

整体流程:

User Query → Agent → Tool / SubAgent → Result

新旧架构对比

flowchart LR

subgraph 旧架构
A1[指代消解]
A2[任务拆解]
A3[子任务分类]
A4[Agent + Tools]
A5[输出节点]
end

subgraph 新架构
B1[Agent]
B2[Tools / SubAgent]
B3[Result]
end

A1 --> A2 --> A3 --> A4 --> A5
B1 --> B2 --> B3

新架构将多个前置处理节点合并为一个统一的 Agent,大幅简化了流程。

新架构的三个关键设计

Tool 并发执行

传统 ReAct Agent 通常是 串行调用工具

但在很多实际业务中,多个工具之间 没有强依赖关系

因此新的架构支持 并发 Tool 调用,可以显著降低整体延迟。

Sub-Agent 隔离

为了避免工具返回的大量数据污染主 Agent 上下文,我引入了 Sub-Agent

Sub-Agent 的作用是:

  • 执行复杂任务
  • 处理工具返回的数据
  • 只返回精简结果(通过调用 LLM 来压缩)

这样可以避免上下文膨胀。

异步消息压缩

即使模型上下文已经很长,但保持上下文 信噪比 仍然非常重要。

我的设计是:

  • 对话结束后 异步运行
  • 当上下文达到阈值触发
  • 重排 checkpoint + PG 数据
  • 使用小模型进行 消息压缩

写在最后

回头看整个过程,其实是一个很有意思的变化。

过去我们习惯:用工程弥补模型能力

而现在可能需要慢慢转变为:先相信模型,再补工程能力

随着模型能力提升,很多复杂系统其实可以变得非常简单。

如果你对 Agent 架构、工具并发或者消息压缩有其他想法,欢迎一起交流。