- 发布日期
第 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.stream(pregel/main.py)打断点,用第 1 讲的最小图 invoke, 单步进入 SyncPregelLoop.__enter__ → _first → while loop.tick(),亲眼看超步循环转起来。
2. 打印通道版本,观察"触发"
在 apply_writes(pregel/_algo.py)和 prepare_next_tasks 里打印 channel_versions 与 versions_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 历史复盘"图为什么卡住/走错分支"。
动手实验
cd libs/langgraph && make test,确认环境可跑通(哪怕只跑一个文件TEST=tests/test_pregel.py make test)。- 在
Pregel.stream入口打一个断点,用第 1 讲的最小图单步进入loop.tick(),记录它循环了几次。 - 把同一个图换成
stream_mode="debug"跑一遍,对照断点观察到的超步数。
阅读作业
- 通读根目录
AGENTS.md与各库的Makefile,了解构建/测试约定。 - 浏览
libs/langgraph/langgraph/pregel/__init__.py,看核心包对外导出了哪些符号。
小结
- monorepo:
checkpoint(地基)→langgraph(引擎)→prebuilt(上层)。 - 调试三招:最小图 + 断点、打印版本号、
stream_mode="debug"。 - 带着这张地图,下一讲正式进入数据流的底座——Channel。