跳转到内容

6.5 子图架构总结与工程化指南

经过前面四节的深入探讨,我们已经全面了解了 LangGraph 中子图的架构设计、状态映射、测试策略和动态选择机制。这一节作为第6章的收尾,我们会把所有内容整合成一个完整的工程化指南,帮助你在实际项目中系统地应用子图架构。

子图架构决策框架

当你在面对一个需要划分子图的系统时,可以按照下面的决策流程来系统地进行架构设计:

第一步:评估是否需要子图

├─ 节点总数 < 5 且逻辑简单 → 可能不需要子图
│   └─ 直接用单层图即可

└─ 节点总数 ≥ 5 或 有复用需求 → 需要子图

第二步:识别功能域(功能内聚)

├─ 列出所有业务步骤
├─ 按职责分组(每个组回答"它做什么?")
├─ 每个组就是一个候选子图
└─ 确保每组可以用一句话描述其目的

第三步:定义接口(接口稳定)

├─ 为每个子图定义 Input 类型(需要的字段)
├─ 为每个子图定义 Output 类型(产生的字段)
├─ 区分公共字段和私有字段
└─ 接口一旦确定,尽量避免频繁修改

第四步:选择通信模式

├─ 字段名一致?→ 自动匹配模式
├─ 字段名不同但转换简单?→ 包装函数手动映射
├─ 多个子图共享基类?→ 继承模式
└─ 运行时动态决定?→ 注册表/工厂模式

第五步:规划测试策略

├─ 每个子图:单元测试(独立、隔离、Mock 外部依赖)
├─ 关键路径:集成测试(验证数据流正确性)
├─ 全链路:端到端测试(完整流程验证)
└─ CI/CD 中加入覆盖率检查

完整项目结构参考

一个使用子图架构的典型 LangGraph 项目目录结构如下:

project/
├── graphs/
│   ├── __init__.py
│   ├── parent/
│   │   ├── __init__.py          # 父图定义
│   │   ├── orchestrator.py      # 编排各子图的包装节点
│   │   └── state.py             # 父图的状态类型定义
│   │
│   ├── classification/
│   │   ├── __init__.py
│   │   ├── graph.py             # 分类子图定义
│   │   ├── state.py             # 分类子图的状态类型
│   │   ├── nodes/
│   │   │   ├── categorize.py    # 各节点的实现
│   │   │   └── extract_entities.py
│   │   └── tests/
│   │       └── test_classification.py
│   │
│   ├── retrieval/
│   │   ├── __init__.py
│   │   ├── graph.py
│   │   ├── state.py
│   │   ├── nodes/
│   │   └── tests/
│   │
│   ├── generation/
│   │   ├── __init__.py
│   │   ├── graph.py
│   │   ├── state.py
│   │   ├── nodes/
│   │   └── tests/
│   │
│   └── quality/
│       ├── __init__.py
│       ├── graph.py
│       ├── state.py
│       ├── nodes/
│       └── tests/

├── registry.py                   # 子图注册表
├── config.py                    # 配置管理
└── tests/
    ├── test_integration.py       # 集成测试
    └── test_e2e.py              # 端到端测试

这种结构的核心原则是每个子图是一个独立的包,包含自己的状态定义、图定义、节点实现和测试。父图在单独的 parent/ 包中,只负责编排。registry.py 提供全局的子图注册和查找机制。

常见反模式速查表

最后,我们把整个第6章讨论过的反模式整理成一个速查表,供你在设计和审查子图架构时快速对照:

反模式问题正确做法
God Graph(20+节点的平铺图)认知负担重、难维护按 3-7 个节点一组拆分为子图
子图之间直接访问内部状态高耦合、改一处波及多处通过明确定义的接口通信
所有子图共享同一个 State 类型字段污染、语义不清每个子图有独立的 Input/Output 类型
包装函数中硬编码字段映射映射逻辑分散、难以追踪使用统一的映射工具类或配置
子图没有独立的测试集成问题发现晚、修复成本高每个子图有完整的单元测试套件
动态选择时没有默认回退未知类型导致崩溃始终提供 default handler 或明确报错
注册表分散在各模块重复注册或遗漏集中式 Registry 单例管理
工厂函数创建的子图没有缓存重复创建浪费资源缓存已编译的子图实例
子图嵌套超过 3 层调试困难、状态复杂扁平化为最多 2 层嵌套
忘记在包装函数中处理异常子图异常冒泡到父图在包装函数中 try-except 并优雅降级

从这里到哪里:后续章节预告

子图架构是构建复杂 LangGraph 系统的基础设施。掌握了子图之后,你还需要了解:

  • 持久化与时间旅行(第7章):如何让子图的执行状态可以被保存和恢复?特别是当子图中包含 Interrupt 时,checkpointing 的行为是怎样的?
  • 多 Agent 协作(第8章):多个 Agent(每个 Agent 本身可能就是一个复杂的子图)如何协同工作?Supervisor 模式、Map-Reduce 并行等高级编排方式。
  • 实战项目(第9-10章):把前面学到的所有知识综合运用到两个完整的项目中——智能客服工单系统和自主研究助手。

这些章节都会大量用到本章讨论的子图技术,所以确保你对本章的内容有了扎实的理解后再继续前进。

基于 MIT 许可发布