发布日期

第 02 讲 · 源码地图与调试环境搭建

monorepo 结构、依赖关系梳理、调试环境搭建与读源码方法论

学习目标

  • 看懂本仓库的 monorepo 结构与库之间的依赖关系。
  • 搭好一个能 断点调试 LangGraph 源码 的环境。
  • 掌握一套"读执行引擎源码"的方法论。

仓库结构:monorepo

本仓库是 monorepo,每个库在 libs/ 下独立成包:

libs/
├── checkpoint              # 检查点基础接口 + InMemory + serde(无外部存储依赖)
├── checkpoint-postgres     # Postgres 检查点实现
├── checkpoint-sqlite       # SQLite 检查点实现
├── checkpoint-conformance  # 检查点一致性测试套件
├── langgraph               # 核心框架:Pregel 引擎 + channels + graph
├── prebuilt                # 高层 API:create_react_agent / ToolNode
├── cli                     # 命令行工具
├── sdk-py                  # Python SDK(调 LangGraph Server)
└── sdk-js                  # JS/TS SDK

依赖关系(决定改动影响面)

checkpoint
├── checkpoint-postgres
├── checkpoint-sqlite
├── prebuilt
└── langgraph

prebuilt
└── langgraph
  • checkpoint 会影响所有下游(langgraph / prebuilt / 两个存储实现)。
  • langgraph 会影响 prebuilt
  • 这也是本课的学习顺序依据:先 checkpoint/channels 基础,再 langgraph 引擎,最后 prebuilt

langgraph 核心包的内部地图

libs/langgraph/langgraph/
├── pregel/            ★执行内核(本课模块四)
│   ├── main.py        Pregel 类:对外 Runnable API
│   ├── _loop.py       PregelLoop:超步状态机(tick/after_tick)
│   ├── _runner.py     PregelRunner:并行调度节点
│   ├── _algo.py       prepare_next_tasks / apply_writes 核心算法
│   ├── _read.py       PregelNode / ChannelRead
│   ├── _write.py      ChannelWrite
│   ├── _executor.py   BackgroundExecutor 并发
│   ├── _retry.py      重试/超时
│   ├── _checkpoint.py 与检查点的桥接
│   ├── _call.py       函数式 API 子任务调度
│   └── _io.py         输入/输出映射
├── channels/          ★通道(本课模块二)
├── graph/             ★图构建(本课模块三)
│   ├── state.py       StateGraph + compile()
│   ├── _node.py       节点
│   ├── _branch.py     条件边
│   └── message.py     add_messages reducer
├── func/              函数式 API(@entrypoint/@task)
├── stream/            流式输出 v3
├── types.py           Command/Send/Interrupt/RetryPolicy...
├── runtime.py         Runtime 运行时注入
└── constants.py       START/END/常量

把这张图贴在手边,后面每讲都会回到某个文件。

环境搭建(poetry)

每个库自带 Makefile 与依赖声明。以核心库为例:

cd libs/langgraph

# 安装依赖(仓库使用 uv/poetry,均可;以 poetry 为例)
poetry install

# 三件套(改代码后、提 PR 前必跑)
make format   # 代码格式化
make lint     # 静态检查
make test     # 全量测试

# 只跑某个测试文件
TEST=tests/test_pregel.py make test

提示:仓库根 AGENTS.md 规定了"改某个库就在该库目录跑 format/lint/test"。

调试方法论:怎么读执行引擎源码

执行引擎代码调用层级深、回调多,硬读容易迷路。推荐三招:

1. 用最小图 + 断点,自顶向下跟一次 run

Pregel.streampregel/main.py)打断点,用第 1 讲的最小图 invoke, 单步进入 SyncPregelLoop.__enter___firstwhile loop.tick(),亲眼看超步循环转起来。

2. 打印通道版本,观察"触发"

apply_writespregel/_algo.py)和 prepare_next_tasks 里打印 channel_versionsversions_seen,你会直观看到"版本变了 → 节点被触发"。

3. 用 stream_mode="debug" 当 X 光机

不改源码也能观察:

for ev in app.stream({"count": 0}, stream_mode="debug"):
    print(ev["type"], ev.get("step"), ev.get("payload", {}).get("name"))

debug 流会吐出每个超步的 task 开始/结束、checkpoint 等事件,是理解机制的利器(第 20 讲细讲)。

使用场景

  • 给团队定制 LangGraph:先用本图定位要改的库,按依赖关系评估影响面与回归范围。
  • 给社区贡献 PR:跑通三件套是被 review 的前提。
  • 线上排障:用 debug 流 + checkpoint 历史复盘"图为什么卡住/走错分支"。

动手实验

  1. cd libs/langgraph && make test,确认环境可跑通(哪怕只跑一个文件 TEST=tests/test_pregel.py make test)。
  2. Pregel.stream 入口打一个断点,用第 1 讲的最小图单步进入 loop.tick(),记录它循环了几次。
  3. 把同一个图换成 stream_mode="debug" 跑一遍,对照断点观察到的超步数。

阅读作业

  • 通读根目录 AGENTS.md 与各库的 Makefile,了解构建/测试约定。
  • 浏览 libs/langgraph/langgraph/pregel/__init__.py,看核心包对外导出了哪些符号。

小结

  • monorepo:checkpoint(地基)→ langgraph(引擎)→ prebuilt(上层)。
  • 调试三招:最小图 + 断点、打印版本号、stream_mode="debug"
  • 带着这张地图,下一讲正式进入数据流的底座——Channel。