LangGraph部署全解:FastAPI、LangGraph Server与Platform选型指南 跑通一个 LangGraph 的 Agent在我的经验里从来都不是难事定义好节点、给边加上条件路由、调用一下graph.compile()本地拿一段文本或者一个thread_id直接往下灌输出就出来了。真正开始头疼的是把它部署成“别人也能用的服务”——你会发现脚本世界里那套“跑完就结束”的思维基本失效。LangGraph 图本质上是一个带持久化、可中断、可能等待人工介入的状态机把它塞进一个普通的 Web 进程远不像包一个 Flask 接口那么简单。这篇文章想完整聊聊 LangGraph 从脚本到服务的三条部署路径FastAPI 手写封装、官方 LangGraph Server、以及 LangGraph Platform。我不会只列步骤而是会讲清楚每条路径解决什么问题、留下什么坑、适合什么样的项目和团队。如果你正在用 FastAPI LangChain LangGraph 搭 AI Agent正卡在“本地能跑但不知道怎么上线”这一步这篇应该能给你省下不少折腾时间。1. 先搞清楚部署难点LangGraph 应用服务化到底难在哪很多人第一次把 LangGraph 往 Web 服务里搬的时候第一反应是写个 FastAPI 端点收到请求就调一下compiled_graph.invoke()把结果返回不就行了吗表面看确实是这样但实际踩过几轮之后你会发现麻烦都藏在“图的生命周期”和“HTTP 请求的生命周期”不匹配这件事上。1.1 图不是“跑一次就结束”的程序普通 Python 脚本是一次性的读输入、跑逻辑、输出、退出。但 LangGraph 图是一个有状态的状态机它天然支持 checkpoint检查点、Human-in-the-loop人工介入、时间旅行time travel这些能力。也就是说同一个 thread 的对话可以被中断、持久化、恢复甚至回到之前的某个状态重新执行。这就带来一个最基本的部署问题如果进程重启了这轮对话还能不能继续脚本模式完全不需要回答这个问题因为每次跑都是从头开始。而一旦做成服务用户可能上午问了一半下午回来继续问。如果状态只存在进程内存里重启就全丢了。所以“部署”的第一步其实是先想清楚状态要放在哪里。这个选择会直接决定后续所有架构。1.2 图的执行周期比 HTTP 请求长得多普通 API 的请求响应是毫秒级到秒级但一个 Agent 图的执行链路可能是这样的用户输入 → LLM 第一次生成5-20 秒 → 决定调用工具 → 工具执行可能调外部服务又是几秒到几十秒 → LLM 汇总再花十几秒 → 输出。整个链路跑完一两分钟很正常。更麻烦的是LangGraph 还支持interrupt()。图执行到某个节点可能会停下来等人单击“确认”或者填一个表单这个“人等”的过程可能是几分钟也可能是几天。当你把这种长周期执行放进 HTTP 服务的模型里问题就接连出现了客户端等不等得起服务端的连接和线程被占住了怎么办中途进程崩溃了已经执行到一半的图怎么恢复这些都不是靠写一个端点能解决的。1.3 “部署路径”的实质是信任边界不同我后来想明白了一件事三条部署路径的区别表面看是工具不同本质上是“你愿意把哪些东西交给框架托管”。FastAPI 自封装是你自己管状态、并发、流式、可观测性用 LangGraph Server是把 HTTP 层和状态层交给官方的那一套抽象上 LangGraph Platform则是把整个执行生命周期都托管出去。这三条路线的选型本质上是在“控制力”和“省心事”之间作权衡没有绝对的好坏只有合不合适的阶段。2. 路径一FastAPI 手写封装把 CompiledGraph 变成 HTTP 接口先聊最朴素、也最容易被低估的一条路自己用 FastAPI 封装一个服务。很多项目其实只需要一个内部接口、一个内部工具并不值得为它引入整套平台。而且手写一遍封装能强迫你真正理解 CompiledGraph 的生命周期后面迁移到官方方案时心里也有底。2.1 为什么是 FastAPIFastAPI 几乎是 Python 服务化最顺手的方案原生 async 支持、Pydantic 做请求校验、自动生成 OpenAPI schema。对于 LangGraph 这种本身就是 Python 生态的框架来说它是最低摩擦的 HTTP 壳。实际上很多生产项目就是用 FastAPI LangChain LangGraph 搭的我在标题相关的那套 Agent 方案里第一版也是这么做的。这个组合的好处是每层都可以单独替换FastAPI 管接口LangChain 管模型和工具调用抽象LangGraph 管状态和业务流程编排。2.2 最小可用实现一个 Invoke 端点先看一个最基础的实现不含流式先把“脚本变接口”这件事落地# app.py from fastapi import FastAPI from fastapi.responses import StreamingResponse from pydantic import BaseModel from agent_builder import build_agent app FastAPI() compiled_graph build_agent() # 进程启动时只编译一次 class InvokeRequest(BaseModel): thread_id: str messages: list[dict] app.post(/invoke) def invoke(req: InvokeRequest): config {configurable: {thread_id: req.thread_id}} result compiled_graph.invoke({messages: req.messages}, configconfig) return {messages: result[messages]}这段代码本身很简单但里面有一个非常关键的细节compiled_graph必须在模块级别只编译一次不能放在函数里每次请求都编译。compile()这个过程会做状态 schema 校验、节点检查、边结构预处理开销比你想象的大而且毫无必要。我在早期版本里干过这种傻事结果并发一上来服务端的编译 CPU 占用直接飙满。2.3 thread_id 是一等公民不是可选项注意请求体里我放了一个thread_id这不是可有可无的东西。LangGraph 的 checkpointer 把所有对话状态都挂在 thread 上同一个 thread_id 的连续请求共享历史上下文不同的 thread_id 完全隔离。它就像数据库里的主键——没有它你的图就退化成无状态函数每次请求都是空对话所谓“多轮记忆”自然就失效了。所以在自封装这条路径上第一步就是想清楚thread_id从哪来。是由前端生成还是后端生成用户刷新页面是否会导致丢失这些问题在写脚本时完全不存在但一旦做服务就是第一道绕不过去的坎。我后面会单独讲 thread_id 的完整设计这里先记住一个结论请求体里必须显式携带它不要每次服务端自动生成新的。2.4 流式响应用 SSE 而不是硬等invoke的问题在于客户端要等到图全部跑完才能收到一个完整响应。如果整个 Agent 链路要跑几十秒用户体验会很差而且中间层如果还有网关、负载均衡长连接还容易被切断。所以只要不是内部极低并发的场景我都建议直接用流式接口。LangGraph 本身提供graph.stream()配合 FastAPI 的StreamingResponse就能很轻地做成 SSEServer-Sent Events接口import json from fastapi.responses import StreamingResponse app.post(/stream) def stream(req: InvokeRequest): config {configurable: {thread_id: req.thread_id}} def generate(): for chunk in compiled_graph.stream( {messages: req.messages}, configconfig, stream_modeupdates ): yield fdata: {json.dumps(chunk, ensure_asciiFalse)}\n\n return StreamingResponse(generate(), media_typetext/event-stream)为什么选 SSE 而不是 WebSocket因为 SSE 基于 HTTP基础设施兼容性最好Nginx、各种网关都能直接透传不需要额外维护连接状态。WebSocket 当然能做双向通信但你的场景是“用户发一次Agent 流式回一次”这个模型 SSE 已经覆盖了没必要引入更高的复杂度。前端用EventSource或者 fetch reader 都能接。2.5 这条路上最容易踩的三个坑超时问题。Agent 执行链路长外部模型 API 偶尔会卡住如果不在客户端和网关层设置合理的超时连接会被一直占住。建议在服务入口设置一个略高于 Agent 最坏执行时间的超时阈值同时让前端能够中断请求并释放资源。同步阻塞问题。如果 FastAPI 的端点用async def但里面调用的是同步的graph.invoke()那整个事件循环会被阻塞。LangGraph 其实提供了ainvoke/astream但要注意异步调用时 checkpointer 也需要安装异步驱动。早期MemorySaver的异步实现有过一些坑如果并发要求高建议直接上PostgresSaver或RedisSaver并启用异步版本别在内存版上死磕。幂等性问题。请求重试是不可避免的但一个 Agent 执行过程中可能已经调用了外部工具比如发了邮件、扣了钱、创建了订单。重试时如果原样再发一遍这些有副作用的工具就可能被执行两次。解决思路是引入一个任务去重机制在请求体里带run_id或者在消费端记录每个 thread 的最后一次提交指纹重复提交直接返回上次结果。2.6 适合什么人走这条路真需要做鉴权、限流、日志、审计希望完全掌控 HTTP 行为的团队内部调用、并发不大、不需要长时间挂起的任务以及部署环境受限不方便引入官方 Docker 镜像的场景。反过来说如果团队有好几个人一起开发 Agent需要可视化调试工具或者你的业务流程需要定时触发、异步后台执行、人工审批挂起那靠手写 FastAPI 把这些全做了会非常痛苦就是下一条路径该出场的时候了。3. 路径二LangGraph Server 原生部署把调试与持久化交给官方LangGraph 官方很早就意识到“部署”这个环节的价值推出了一整套服务化方案LangGraph Server。它不是让你自己封装接口而是直接把图编译成有一个标准 REST API 的服务配合 LangGraph Studio 这类调试工具开发体验会比自封装舒服不少。3.1 langgraph.json图的部署声明LangGraph Server 的核心是一个配置文件langgraph.json。它的作用是把“哪个文件里的哪个编译对象以什么名字暴露出去”声明清楚{ dependencies: [.], graphs: { agent: ./agent.py:app }, env: .env, python_version: 3.11 }在agent.py里你需要暴露一个编译后的图对象from langgraph.graph import StateGraph, START, END from langgraph.checkpoint.memory import MemorySaver def create_graph(): builder StateGraph(...) builder.add_node(...) builder.add_edge(...) return builder.compile(checkpointerMemorySaver()) app create_graph()只要这个文件存在运行langgraph dev就能在本地拉起一个开发服务并且自动带你进入 LangGraph Studio 的可视化调试界面。我第一次用这个工具的时候最大的感受是终于不用靠 print 和日志猜图走到哪一步了。你能直接看到每个节点当前的状态、消息列表、工具调用结果甚至可以把图回滚到之前某个节点修改参数重新执行。这种调试能力对复杂 Agent 流程来说几乎是刚需。3.2 Server 内置的 API 模型Assistant、Thread、RunLangGraph Server 内置的 REST API 把服务层抽象成了三个概念理解它们后面用 Platform 时也能立刻上手Assistant图定义和运行时配置的绑定。可以理解成服务器上的“一份图模板”。Thread一次会话/一个任务的持久化载体对应 LangGraph 里的 thread_id。Run真正执行图的一次运行。一个 thread 上可以有多次 run每次 run 会生成一个 run_id。实际的使用流程是先创建 thread然后在 thread 上提交 run。比如用 curl 创建线程curl -X POST http://localhost:8123/threads \ -H Content-Type: application/json \ -d {assistant_id: agent}拿到 thread_id 之后再对它提交消息curl -X POST http://localhost:8123/threads/your-thread-id/runs \ -H Content-Type: application/json \ -d {messages: [{role: user, content: 你好}]}这个设计最舒服的地方在于它把“对话级状态”直接从业务层剥离了。你不需要自己去实现“根据 thread_id 读取历史消息再塞回图里”服务端全包了。而且这些 API 会自动生成 OpenAPI schema前端联调、SDK 生成都省了很多手工劳动。3.3 生产化用 Docker 跑起来并配置持久化存储开发时可以用langgraph dev但生产环境自然要容器化。LangGraph 官方提供了构建好的服务镜像配合 CLI 可以做本地构建。基本流程是langgraph build -t my-agent:latest这个命令会根据 langgraph.json 生成一个包含你的代码和官方服务层的镜像然后你可以用langgraph up在本地跑起来或者自己推送到自建的容器仓库里部署。这里有一个坑默认的开发配置用的是内存存储一旦服务重启所有对话状态全部消失。生产环境必须配置持久化的 checkpointer。最常见的方案是 Postgres 或 Redis在 langgraph.json 里声明{ dependencies: [.], graphs: { agent: ./agent.py:app }, checkpointer: { type: postgres, config: { postgres_uri: postgresql://user:passhost/db } } }如果走 Docker 部署通常是配环境变量而不是把数据库地址直接写在代码里。另外 LangGraph Server 的镜像本身比较大构建时间长CI/CD 流水线的超时时间要放宽这个不提前做好心理准备很容易在第一次上线时被打个措手不及。3.4 走这条路前先想清楚的事LangGraph Server 帮我们解决了“自己封装 HTTP 接口”的大部分重复劳动但它的控制面同样是官方的抽象如果你需要一些完全自定义的 HTTP 接口比如一个非流式的批处理入口、一个需要特殊鉴权逻辑的回调会发现自由度比 FastAPI 低不少。它的定位是“标准 Agent 服务”不是“任何功能的 Web 框架”。我个人的判断是团队多人协作开发 Agent、需要共享调试环境、需要状态持久化但不想从头搭基础设施时LangGraph Server 是非常舒服的中间态。它比自封装多了一层标准化的 API 模型又比 Platform 少了一些云端依赖适合在自建服务器上部署。4. 路径三LangGraph Platform把执行生命周期整体托管如果说 LangGraph Server 解决了“把图跑成一个服务”那 LangGraph Platform 解决的是“把图跑成一个可靠的生产任务系统”。这个阶段最典型的诉求已经不是接口长什么样了而是进程崩了能不能自动恢复任务能不能定时触发人审流程挂起后怎么恢复所有执行过程能不能被完整观测4.1 Platform 比 Server 多出来的关键能力这里列一下我在实际使用中感受最明显的差异点持久化与 Durable ExecutionPlatform 把每个 run 的执行状态持久化进程崩溃、机器重启后任务会自动恢复继续执行而不是从头再来。对于长耗时 Agent 流程这个能力非常有价值。后台执行与定时任务LangGraph Server 本身不太强调跑后台任务但到了生产场景“凌晨定时拉取数据后生成报告并推送通知”这类需求非常常见。Platform 支持 cron 调度在提交 run 的时候带上调度表达式平台就会按时触发不需要你额外搭一套 Celery 之类的任务系统。人机协同的工程化支持LangGraph 的interrupt()机制在 Platform 上被完整落地成了一套 API 行为。图执行到中断节点时会主动挂起并返回等待状态之后你通过特定接口提交“确认继续”或“修改输入”图会从暂停的位置接着跑。这套流程如果自己用 FastAPI 封装要处理挂起状态存储、恢复时机、并发保护非常痛苦。可观测性Platform 自带 tracing 与执行历史配合 LangSmith 能做到从用户输入到每一步工具调用的全链路追踪。相比自建日志这个体验差距是代际的。4.2 托管方式云版还是自托管LangGraph Platform 有两种形态托管的 LangGraph Cloud以及可以部署到自己环境的自托管版本。云版适合想快速上线、不想碰运维的团队直接把代码仓库连到平台配置好环境变量平台自动构建服务、管理数据库和队列。自托管则适合数据必须留在内部网络、或合规要求更高的场景通常需要你维护 Kubernetes 环境和一些基础设施组件。无论哪种形态langgraph.json 仍然是核心入口。生产环境下配置会像这样{ dependencies: [./agent], graphs: { agent: ./agent/graph.py:graph, planner: ./planner/graph.py:graph }, env: .env, checkpointer: { type: redis, config: { redis_uri: redis://... } } }可以同时暴露多个图每个图作为一个 assistant服务端会为每个 assistant 独立管理线程和运行。4.3 代价自由度、成本和调试边界Platform 不是银弹。第一个代价是“黑盒”托管的执行引擎你改不了遇到平台本身的 bug 或者不符合预期行为时只能等官方修复或者退回自研。第二个代价是成本特别是云版按调用量、存储量计费的模式流量上来之后费用会相当可观。第三个代价是迁移成本一旦你的业务深度依赖 Platform 的 cron、后台 run、interrupt 恢复协议未来想迁回自建方案会非常费劲。所以我的建议很直接如果你还在做 Demo、验证业务可行性先别上 Platform如果业务已经进入生产阶段并且你发现自己在 FastAPI 方案里开始手写“任务恢复”“定时触发”“挂起恢复”这些通用能力时就该认真考虑把它换掉了。5. 三条路径怎么选一次讲清楚决策清单前面三条路径都过了一遍这里把对比整理成一张表方便你做决策的时候直接对照参考。对比项路径一FastAPI 自封装路径二LangGraph Server路径三LangGraph Platform启动成本低写代码即可中需要配置与镜像高需要平台账号或自建基础设施状态持久化自己接 checkpointer内置接口需配 Postgres/Redis托管开箱即用可视化调试无需要打日志LangGraph Studio 支持平台支持 云端 tracingHTTP API完全自定义官方标准 REST API官方 REST API SDK流式支持手写 SSE原生支持原生支持定时任务与后台执行自建任务队列不内置内置 cron / background runs人机协同挂起恢复自己实现较痛苦接口支持但需定制成熟方案运维复杂度低中高云版最低自托管较高定制自由度最高中低决策清单按我自己的经验排序的话单个服务、内部工具、并发不大明确只需要一个 HTTP 包装选路径一。这是成本最低的方案代码都在自己手里将来迁移也最没有包袱。团队共同开发Agent 流程复杂到需要可视化调试或者需要标准化给前端对接选路径二。LangGraph Server 的价值不在“少写几个端点”而在开发协作和 API 模型的标准化。这里的调试收益只要用过一次 LangGraph Studio 就很难退回纯日志模式。生产环境、长耗时任务、需要定时触发、需要人工审批、要求进程崩溃后任务能恢复选路径三。这些能力自己从零搭每个都是不小的工程Platform 的意义是把“执行生命周期”整体托管。还有一个务实的打法先用路径一快速验证业务逻辑把 Agent 的编排和工具调用打磨成熟再在项目标准化阶段按需迁到路径二或三。LangGraph 的核心业务代码和部署方案本身就是解耦的迁移时改动量通常集中在配置和接口调用层业务图结构基本不动。我已经在项目里这么干过两次比一开始就纠结“上不上平台”要省心得多。6. 无论选哪条路部署前都要想清楚的三个问题最后这部分是我最想强调的。因为不管走哪条部署路径下面这三个问题都是躲不掉的而且大概率会在你上线后第一周内出来找你。6.1 thread_id 怎么设计才算合格千万别用“每轮对话自动生成一个 uuid”这种偷懒方案。用户刷新一下页面 thread_id 就没了那这个 Agent 就跟失忆症患者一样永远不记得上一轮聊了什么。thread_id 本质上扮演的是业务路由、状态隔离和审计的主键我在实际项目里推荐复合结构比如{customer_id}:{session_id}或者{tenant_id}:{user_id}:{conversation_id}这样做有三个好处多租户天然隔离后续检查数据、排障时能快速定位到具体用户如果你用的是支持前缀分片的存储还能按这个结构做水平扩展。还有一个坑同一个 thread_id 上不要并发跑多个 run。LangGraph 的 checkpointer 在同一个 thread 上并发写入时会有竞争状态可能互相覆盖。如果用户的同一个会话里需要同时执行多个并行任务在业务层创建多个 thread而不是塞进同一个 thread_id。6.2 并发与异步别让 FastAPI 的 async 反噬你很多人在 FastAPI 端点上用async def图里却调用同步的graph.invoke()。一个耗时的图跑起来事件循环被死死占住其他所有请求全部排队整个服务的并发能力直接归零。正确的姿势是用ainvoke/astream并且给 checkpointer 配异步驱动。这里还想提醒一个容易被忽略的点Agent 里的工具调用Tool也可能是同步的。比如工具里直接用了requests.get()那么就算图本身是异步的这个工具调用一样会阻塞。LangGraph 的工具节点设计时最好统一走异步签名或者用asyncio.to_thread把阻塞调用丢到线程池里。这个细节在本地跑脚本时完全没感觉一旦上线并发上来就是性能瓶颈的重灾区。另外服务端不仅要考虑 Agent 本身的并发还要考虑外部依赖的连接池LLM API 的客户端连接、数据库连接、Redis 连接都需要单独配置合理的池大小。模型响应慢不是你不配连接池的理由恰恰因为模型响应慢池子里的连接被占住的时间才更长才更容易打满。6.3 可观测性没有 trace 的 Agent 服务等于盲盒脚本时代你可以在图里到处print看看每一步走到哪了。服务化之后print 基本失效你只知道用户问了一句什么最后回了一句什么中间哪个工具被调用了、哪个节点抛异常了全靠猜。所以部署前一定把 tracing 接好。LangGraph 对 LangSmith 的集成几乎是开箱即用的import os os.environ[LANGCHAIN_TRACING_V2] true os.environ[LANGCHAIN_API_KEY] your-api-key os.environ[LANGCHAIN_PROJECT] your-agent-name只要这几个环境变量在LangGraph 会自动把每次 run 的节点执行、模型调用、工具返回全部上报。整个链路里每次 LLM 调用耗多久、工具返回了什么报文都是可视的。不用 LangSmith 的话用 Langfuse、Langtrace 这类兼容 OpenTelemetry 的工具也行但关键是一定要有 trace。除了外部 tracing服务入口的业务日志也别省。我的习惯是给每次请求打这么几个字段thread_id、用户标识、跑的图名称、工具调用次数、总耗时、结束状态。上线第一天就能拿这些指标回答“Agent 到底跑得快不快、在哪些环节卡住了”。最后分享一点个人实际体会。三条路径我陆续都用过最大的感受是别把部署路径当成技术排名问题它更像一个边界问题你到底希望自己的 Agent 系统里有多少东西是“自己可控的”又有多少是“交给框架托管”。如果只跑通 MVPFastAPI 完全够如果要天天和各种业务系统握手LangGraph Server 的 API 模型会替你省掉很多事如果真到了生产阶段线程恢复、定时任务、人工确认这些才是大头Platform 的意义不在“部署”本身而在把整套执行生命周期托管掉。你自己搭也好、用官方也好先想清楚三件事状态放哪、并发怎么管、出了问题能不能看见。想清楚这三件事任何一条路径都不会把你带进死胡同。