本节在全册位置:图写好只是开始。最小部署用 LangGraph Server 把编译好的图以 API 暴露;规模化用 LangGraph Platform(托管)或自托管,配 Docker 与后台任务。选择看团队要不要管基础设施。我们的建议:先别碰重架构,用最轻的形态验证价值。
本地开发用 langgraph up 起 Server,支持流式与检查点。Platform 额外给鉴权、伸缩、后台运行与 Studio 调试。纯自托管则把图包进 FastAPI,自己接 Postgres 检查点。延迟敏感场景把检查点库放同区。类比建筑:Server 像样板间,Platform 像精装交付,自托管像毛坯自己装。过早上 Platform 是浪费,小团队先用 Server。
# 6.1 LangGraph 应用的部署策略 from fastapi import FastAPI from langgraph.checkpoint.postgres import PostgresSaver app = FastAPI() # g 为已 compile(checkpointer=...) 的图 @app.post("/run") async def run(req: dict): cfg = {"configurable": {"thread_id": req["thread_id"]}} return g.invoke(req["input"], cfg) # 不需要自己写 FastAPI 也能对外提供服务
# 检查点库与图服务分开部署,便于各自伸缩
背景:两人团队要快速给内部用,没精力管 K8s 与鉴权。
操作:用 langgraph up 起 Server,前端直连,PG 做检查点。
结果:半天上线,不碰 K8s,先把价值跑出来。
解读:部署形态匹配团队体量,过早上 Platform 是浪费,也拖慢验证节奏。
变式:有合规要求时转自托管,数据库与密钥都在自己机房,图代码一行不用改。

最小部署用 LangGraph Server 暴露 API,规模化用 Platform 或自托管,按要不要管基础设施选型。
延迟敏感场景把检查点库放同区,自托管把图包进 FastAPI 自己接 Postgres 检查点。
部署形态匹配团队体量,过早上 Platform 是浪费,先以最轻形态验证价值。
两人团队直接上 K8s 自托管,精力耗在运维而非业务;先用 langgraph up 验证价值,有合规要求再转自托管,图代码一行不用改。
规模上去后,检查点库的连接管理与 thread_id 策略直接决定并发能力。
# 生产用 Postgres 检查点,按 thread 隔离会话 from langgraph.checkpoint.postgres import PostgresSaver conn = os.environ["PG_DSN"] # 从密钥管理读取,不进状态 saver = PostgresSaver(conn) saver.setup() # 首次建表 g = builder.compile(checkpointer=saver) # 每个用户/会话一个 thread_id,状态互不串 cfg = {"configurable": {"thread_id": f"user-{uid}"}} g.invoke(input, cfg) # 检查点库与图服务分开伸缩,峰值各自扩
| 部署形态 | 运维负担 | 适用 |
|---|---|---|
| 本地 Server | 低 | 验证期 |
| 自托管 | 中 | 有合规 |
| Platform | 低 | 要伸缩 |
⚠️ 常见坑:检查点库和图服务塞同一实例,流量一高两者抢资源双双变慢;分开部署、连接池设上限,才能各自伸缩。
💡 关键直觉:thread_id 是会话状态的钥匙,设计成「一会话一 id」而非「全局共享」,否则并发会互相覆盖状态。
LangGraph Server 的入口是 langgraph.json,它声明「哪个函数是图、哪些依赖要加载」。先把配置文件讲清楚,再讲部署就有的放矢:
{ "dependencies": ["."], "graphs": { "research": "./graphs/research.py:graph", "chat": "./graphs/chat.py:build_graph" } }
graphs 段把「路由名」映射到「模块:函数」,起服务后每个路由对应一个可调用的图端点。本地用 langgraph up 启动,它会加载配置、拉起服务并带一套可调试的界面。三个常见问题:依赖没写全(新加的包要列进 dependencies);函数不是编译后的图(要返回 compile 的产物);路径写错(相对 langgraph.json 所在目录)。先在本地把配置文件调通,再谈上云,成本最低。
生产部署最常见的形态是「Web 层无状态 + 检查点层有状态」,两者解耦后才能各自伸缩:
# Web 层:只负责接收请求、透传 thread_id,不保存任何会话 @app.post("/run") async def run(req: dict): cfg = {"configurable": {"thread_id": req["thread_id"]}} return g.invoke(req["input"], cfg)
无状态的 Web 实例可以随便加机器,会话状态全部落在检查点库。这条组合的关键是 thread_id 的生成与传递:由调用方生成(如用户 id + 会话号),每次请求都带上,Web 层不感知会话细节。配套两个约定:thread_id 必须在所有需要恢复状态的接口里传递;新会话用新 id,避免串会话。把这个模式想清楚,扩容就只是加机器的问题。
部署前按这份清单逐项核对,能挡住大部分上线事故:
| 检查项 | 内容 | 不过的后果 |
|---|---|---|
| 检查点库 | 生产用 Postgres,同区部署 | 断点续跑失效、延迟高 |
| 密钥管理 | 密钥进环境变量/密钥管理 | 密钥落检查点泄露 |
| 超时设置 | 流式接口单独长超时 | 长回答被网关切断 |
| 并发上限 | 扇出节点设信号量 | 打垮下游 |
| 回滚预案 | 旧版本图可快速切回 | 发布事故无法恢复 |
| 观测接入 | 轨迹与指标进监控 | 黑盒上线 |
清单不是一次性的:每次改图结构、加工具、换模型都要重跑一遍相关项。把它固化成部署模板或发布检查流程,比靠记忆强得多。