资讯动态

LangGraph部署实战:脚本直跑、官方Server与自定义微服务选型指南

发布时间:2026/10/26 3:51:48 来源:尧图企业网站定制
把 LangGraph 的图画完、脚本跑通其实只完成了整个交付里最轻松的那 20%。真正让人头疼的是后面这 80%同事和下游系统怎么调它多个用户同时跑同一个图状态会不会互相串进程一重启对话历史和中间上下文还在不在这些问题不解决LangGraph 就永远只是个“开发机上的玩具”。我最早用 LangGraph 做内部自动化工具时就是这样脚本在本地跑得飞起一说要部署就卡壳。前前后后试了脚本直跑、官方 LangGraph Server、自己用 FastAPI 封装微服务这三条路踩了不少坑也算摸出了一套比较清晰的选型逻辑。这篇就把这三条部署路径完整拆开讲一遍适合已经把图跑通、正琢磨怎么把 LangGraph 从脚本变成服务的朋友。1. 部署之前先把“脚本”和“服务”的差距想清楚1.1 脚本能跑不等于业务能用“脚本能跑”和“服务能用”之间隔着的东西比大多数人想象的多。脚本阶段你只需要考虑一件事调用图、拿结果。但一旦把图暴露给外部变成常驻进程里的 API你立刻要面对的是另一套问题——请求进来怎么鉴权同一个用户的多次请求怎么归到同一个会话进程重启之后用户的上下文还在不在两个用户同时跑同一个图会不会发生资源竞争举个我实际接过的例子某团队做了一个设备老化测试的全自动执行脚本最初用定时任务触发一台机器跑完接着跑下一台完全没有并发需求。后来业务方要求在 Web 界面上实时查看每台设备的测试进度还希望不同测试台位能同时启停这时候原来那个“跑完就算完”的脚本就彻底不够用了。它需要变成一个有接口、有状态、能并发、可观测的服务。这个从脚本到服务的转变本质上就是 LangGraph 部署要做的事。1.2 三条路径的定位差异经常有人问我“LangGraph 到底怎么部署”其实答案不是一个而是三个。按从轻到重排分别是脚本直跑、LangGraph Server、自定义微服务。这三条路径不是谁替代谁的关系而是不同阶段、不同约束下的选择。我在跑通图之后的第一反应是找“官方部署教程”结果发现官方给的是 LangGraph Server但它并不是所有场景的最优解。路径形态上手成本功能完整度运维负担典型场景脚本直跑进程内直接调用最低基础invoke/stream 都有状态靠开发自己落盘无开发调试、批处理、定时任务、内部工具LangGraph ServerREST API 服务中高内置线程会话、持久化、流式、多图管理中快速上线、需要标准 API、不想重复造轮子自定义微服务自封装 HTTP/gRPC 服务高取决于你从鉴权到调度都可以定制高需要对接企业网关、特殊鉴权、融合现有微服务架构简单说脚本帮你快速验证业务逻辑LangGraph Server 帮你省掉大部分工程化体力活自定义微服务则把控制权完整拿回自己手里。明确自己在哪个阶段选路径就不会犹豫。2. 路径一脚本直跑开发阶段永远的最优解2.1 脚本直跑能覆盖哪些场景脚本直跑最容易被低估但它其实是三条路里性价比最高的一条。只要你的使用方是“人”而不是“系统”脚本就足够了。比如本地调试新增的节点逻辑、跑一批离线数据处理、每天早上用 cron 触发一个 LangGraph agent 做巡检和摘要、给团队内部提供一个命令行工具。这些场景的共同特点是没有外部系统实时调用不需要常驻进程不追求 7x24 高可用。我在一个定时巡检项目里就是先用脚本跑的。每天早上九点一个 cron 任务拉起 Python 脚本构建图、注入昨天的设备日志、让 agent 输出诊断建议、把结果写进数据库。整个流程跑了一个多月非常稳没有引入任何服务化成本。如果你做的事本质上是“定时把事情做完”脚本直跑就是最合理的部署形态。2.2 脚本直跑的正确姿势脚本直跑虽然简单但有一些姿势必须摆对否则后面迁到服务时会吃大亏。第一环境变量必须走配置文件。不要在源码里写死模型 API key用 python-dotenv 或者 pydantic-settings 读取.env这样脚本能跑将来容器化之后也能平滑切换到容器环境变量。第二如果你想在脚本里保留 agent 的“记忆”一定要显式配置 checkpointer。很多人写脚本时图编译完直接graph.invoke()感觉“也能跑”但进程一结束所有会话状态就全丢了。对单次任务可能无所谓可一旦要做多轮对话或从断点恢复就必须把状态存下来。from langgraph.checkpoint.sqlite import SqliteSaver with SqliteSaver.from_conn_string(memory.db) as saver: app builder.compile(checkpointersaver) config {configurable: {thread_id: batch-01}} result app.invoke( {messages: [{role: user, content: 开始执行今天的巡检任务}]}, configconfig )这里的thread_id相当于给每次会话发了一把钥匙相同thread_id的调用共享上下文不同thread_id天然隔离。这个习惯从脚本阶段就养成后面切换任何服务方案都会顺畅很多。第三需要看中间过程时用graph.stream()而不是invoke()。脚本阶段养成用 stream 观察节点执行顺序的习惯对排查问题非常有帮助。2.3 脚本直跑的三大天花板脚本直跑能覆盖的场景很广但它有绕不开的三个天花板。第一个是没有网络边界。脚本只能在你那台机器上跑别的服务通过 HTTP 完全调不到你的图除非你再手工包一层接口。第二个是状态默认在进程内或本地文件。单实例没问题一旦要多副本部署做负载均衡内存里的状态就会散落在不同进程里用户请求打到不同机器上上下文就串不起来了。第三个是没有鉴权和限流。脚本谁拿到就能跑内部开发环境无所谓但只要涉及业务数据就必须考虑访问控制和调用配额。另外还有一点常被忽略脚本是一次性进程没法优雅处理长任务。一个 run 跑五分钟脚本进程就干等五分钟这在批处理场景下可以接受但在线场景完全不行。当你发现这三个天花板已经开始卡住你的业务时就是该往第二条路径迁移的时候了。3. 路径二LangGraph Server用官方方案快速服务化3.1 官方 Server 帮你解决掉了什么LangGraph Server 是 LangGraph 官方提供的服务化方案核心思路是声明式配置加一行命令启动。它把你写好的图自动包装成一套 REST API并且把服务化最脏最累的活基本包完了。具体来说它解决了我在脚本阶段最头疼的四件事。第一是线程机制服务器端天然管理 thread_id每个对话一个线程状态隔离和持久化都不需要自己写。第二是执行生命周期一次运行从排队、执行到完成、失败整个生命周期有标准 API 可查。第三是流式输出官方方案直接支持 SSE 流式返回前端打字机效果开箱即用。第四是多图服务你可以在一个 Server 里注册多个 graph通过不同的名字暴露出来不用为每个图单独起服务。说人话就是你把图画好HTTP 协议那套、状态存储那套、并发调度那套官方 Server 基本都替你兜住了。3.2 本地跑起来的完整步骤第一步是安装命令行工具。在干净的环境里执行pip install langgraph-cli。这里提醒一句Windows PowerShell 下如果提示无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称基本就是 Python Scripts 目录没进 PATH或者装了多个 Python 版本最快的临时方案是改用python -m pip install langgraph-cli。第二步是在项目根目录创建langgraph.json配置文件声明你的图在哪、用什么依赖、环境变量怎么引用。{ node_version: 20, graphs: { agent: ./src/agent/graph.py:graph }, dependencies: [requirements.txt], env: { OPENAI_API_KEY: ${OPENAI_API_KEY} } }graphs字段是核心格式是“服务名: 文件路径: 变量名”告诉 Server 你的图对象在哪个模块的哪个变量里。env里用${}引用宿主机环境变量避免把 key 硬编码进配置文件。这里有个细节不同版本的 langgraph-cli 对node_version和dockerfile_lines字段的处理有差异建议装完 CLI 后先跑一遍自带的 init 模板再照着模板改自己的配置。第三步是启动。开发调试用langgraph dev它默认起在 8123 端口支持代码热更新生产构建用langgraph up会基于配置构建 Docker 镜像。启动后可以先用 curl 验证接口通不通整体模型是“先建线程再在线程上跑运行”curl -X POST http://localhost:8123/threads \ -H Content-Type: application/json curl -X POST http://localhost:8123/threads/{thread_id}/runs \ -H Content-Type: application/json \ -d {assistant_id: agent, input: {messages: [{role: user, content: 你好}]}}具体字段在不同版本里略有调整但“thread 管会话、run 管执行”的设计思想是稳定的。把这两个概念吃透兰图上手会快很多。3.3 开发模式、自托管和云平台的取舍langgraph dev启动的是开发模式功能上不设限但没有鉴权只适合本机联调。真正上生产主流做法是langgraph up构建镜像之后自托管到自己的 Docker 环境或者 K8s 集群。自托管需要自己准备持久化存储官方推荐生产环境用 Postgres 保存 checkpoint这样多副本部署时状态才能真正共享。云平台托管版则是另一个选择。如果团队不想维护任何基础设施直接用托管服务把图 push 上去就能获得一个带鉴权和可观测性的生产 API。但托管版有授权和费用上的考量数据合规要求严格的企业一般还是会选择自托管。我的建议是先在本机用langgraph dev跑通 API让业务方确认接口设计没问题再决定自托管还是用托管版。顺便提一句启动开发模式时你可能遇到版本新加的 license 校验逻辑不同版本要求不一样装完 CLI 先确认一下当前版本对开发模式有没有额外鉴权要求省得在启动环节卡半天。3.4 什么时候别选 LangGraph Server官方方案很省事但它是一套“标准服务”不是万能服务。遇到下面几类需求时我会直接考虑第三条路。第一企业里往往有统一的 API 网关和鉴权体系比如基于 OAuth 的 SSO、自定义签名、多租户权限模型。LangGraph Server 自带的鉴权只管“谁能调用这个 Server”管不了“这个用户能不能访问这一批数据”深度集成时你会在网关层做大量适配甚至觉得官方 API 碍手碍脚。第二你的图依赖特殊运行环境。比如模型要内置到同一进程里跑推理或者需要 GPU 资源官方 Docker 镜像的构建方式不一定满足你。第三你需要的是事件驱动和任务队列。如果业务流程不是简单的请求-响应而是“消息进来、排队、异步执行、完成后回调”LangGraph Server 的 run 模型会比较受限这时候自己封装一层往往更顺手。4. 路径三自定义微服务把控制权握在自己手里4.1 为什么还需要自己封装一层既然官方有现成的 Server为什么还有人愿意自己封装我总结下来就三个原因标准之外的需求、现有架构的约束、以及长期成本的控制。在企业里我经常遇到一个场景团队已经有成熟的微服务架构服务之间走公司自研的 RPC 协议每个服务要接入统一注册中心、统一日志平台、统一灰度发布。这时候你把一个 LangGraph Server 独立丢在旁边它跟整个微服务架构格格不入。与其让团队为 LangGraph 单独搞一套接入规范不如直接用 FastAPI 把它封装成一个标准的微服务融入既有的框架和规范。这其实就是“微服务拆分”思维的延续技术选型要让位于组织架构和运维体系。另外自定义封装在多租户隔离和业务鉴权上有着绝对优势。你可以直接在入口层根据 token 解析出用户身份和权限再把 thread_id 映射到业务实体上这在官方 Server 里做起来要别扭很多。4.2 一个能上线的 FastAPI 封装案例用 FastAPI 封装 LangGraph 的骨架非常简洁核心就三步选一个持久化方案、编译一次图、暴露接口。# app.py from fastapi import FastAPI from langgraph.checkpoint.sqlite import SqliteSaver from your_graph import build_graph app FastAPI(titleLangGraph Agent Service) DB_PATH runtime/checkpoints.sqlite def get_agent(): saver SqliteSaver.from_conn_string(DB_PATH) return build_graph().compile(checkpointersaver) agent get_agent() app.post(/v1/invoke) def invoke(body: dict): thread_id body.get(thread_id, default) config {configurable: {thread_id: thread_id}} result agent.invoke({messages: body[messages]}, configconfig) return {thread_id: thread_id, output: result}注意一个关键点agent必须在模块加载时编译一次并复用绝不能放在请求处理函数里每次重新构建。我见过有人图省事这么干结果并发一上来每个请求都重新编译图模型调用频率和内存占用直接爆掉。如果追求更好的并发性能可以把 SqliteSaver 换成 Postgres 版本。SQLite 在单机低并发下够用但多实例部署时多个进程写同一个 SQLite 文件会产生锁竞争生产环境建议直接用 Postgres 存储 checkpoint所有实例共享同一份状态。4.3 流式输出、异步和长任务的追加设计只做同步的/invoke接口通常不够用。LangGraph 的价值很大一部分在于“边跑边看中间结果”所以流式接口基本是标配。FastAPI 里用 SSE 很容易实现from fastapi.responses import StreamingResponse import json app.post(/v1/stream) def stream(body: dict): config {configurable: {thread_id: body.get(thread_id, default)}} def gen(): for event in agent.stream({messages: body[messages]}, configconfig): yield fdata: {json.dumps(event, ensure_asciiFalse)}\n\n return StreamingResponse(gen(), media_typetext/event-stream)前端拿到这个 SSE 流就可以像 ChatGPT 那样逐字输出。这里有一个很隐蔽的坑如果你们前面还有一层 Nginx 或 API 网关一定要确认它没有开启响应缓冲否则流式接口会被网关攒住用户看到的就是“一直转圈然后一次性吐出一大段”。再往深一层说如果单个 run 的执行时间超过了外部网关的超时限制比如网关固定 60 秒超时agent 一跑就是三分钟那同步接口根本扛不住。这时候就得引入任务队列了。我常用的模式是接口收到请求后丢进 RabbitMQ 或者 Celery立刻返回一个 task_id后台 worker 执行 LangGraph run前端轮询 task 状态跑完后按需取结果或由回调推送。用 LangGraph 做真正的业务 agent这几乎是绕不开的一步。4.4 自封装要付出的代价上面这些设计都是可控的但必须承认自封装是一条“收获大、账单一目了然”的路。你自己要对鉴权、限流、日志、监控、健康检查、优雅停机、配置管理负责。写服务的人往往只把心思放在“把图包上 HTTP”这一步结果上线第一周就被各种问题教做人内存泄漏、线程安全、孤儿进程、依赖冲突。我的经验是如果团队没有专职 SRE或者项目周期很紧先别急着自封装。如果必须自封装优先把两件事做掉——第一是结构化日志每个请求都要能跟踪 request_id、thread_id、run_id方便出问题时回溯第二是健康检查接口比如/health返回当前进程的存活状态和关键依赖的连通性。这两件事不做后面排查问题的成本会让你后悔当初的“图省事”。5. 三条路径怎么选我的实战决策逻辑5.1 一张决策表说了这么多落地的时候到底怎么选我给自己总结了一张决策表每次做技术方案时直接对照你的现状推荐路径核心理由还在验证业务逻辑单人单机脚本直跑零改造成本迭代最快别过早抽象需要尽快给业务方一个可调用的 APILangGraph Server官方一站状态持久化和流式都现成团队已有微服务架构要对齐规范自定义微服务能融入网关、鉴权、注册中心流量不大但要求稳定高可用LangGraph Server 自托管运维成本可控官方功能全流量大、任务长、调度复杂自定义微服务加消息队列定制能力和扩展性最强这张表是我在几个项目里反复调整后定下来的核心逻辑是先算总拥有成本再谈技术优劣。官方方案虽然省代码但如果它和你现有架构的集成成本很高那它反而是贵的。5.2 从脚本迁到服务我踩过的三个坑从脚本直跑切到服务化是我跌跟头最多的一段路复盘下来有三个坑几乎每个项目都会踩到。第一个坑是Graph 对象重复初始化。脚本阶段每次跑完进程就退出重建 graph 没有代价。但服务是常驻进程如果在每个请求里都重新build_graph().compile()等于每次请求都把整个图的定义重新解析一遍还要加载模型配置性能和内存都是灾难。正确做法是模块加载时编译并缓存进程内无状态复用。第二个坑是状态没落盘。脚本阶段用内存 checkpointer 省事迁到服务后依然这么干结果服务一重启所有用户的对话上下文全丢。排查了半天最后发现编译时压根没传 checkpointer或者传了一个:memory:的实例。这个坑特别隐蔽因为服务不重启时一切正常一重启就露馅。第三个坑是环境变量没传进容器。本地脚本读.env没问题用langgraph up或者 Docker 打包后容器里的进程根本读不到宿主机上的.env。最典型的症状是服务启动一切正常一调用就报鉴权失败或模型 401。排查思路也很简单先进容器里env看环境变量在不在再检查代码里读 key 的方式。5.3 我的建议排序如果是小团队或者刚起步的项目我的排序非常明确先脚本再 LangGraph Server最后才是自封装。不要一上来就自己造轮子部署层面的过度设计是很多项目延期和返工的头号原因。我见过太多团队把大量时间花在“设计一个完美的微服务架构”上结果业务逻辑本身还没验证。正确的节奏应该是第一天用脚本把 agent 的业务跑通跑通之后立刻用 LangGraph Server 快速暴露 API 给业务方等真的遇到官方方案覆盖不了的场景再启动自封装。这样每一步的投入都对应明确的收益不会在部署上无限内耗。6. 部署过程常见问题速查6.1 环境启动类问题环境问题占了部署初期的低头大多数。最经典的当然是 pip 相关的问题。Windows 下pip命令不被识别报无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称这几乎每天都有新手来问。原因无非两个Python 的 Scripts 目录没有加进 PATH或者系统里装了多个 Python 版本pip 装到了另一个版本之下。最快的临时方案是用python -m pip install xxx一劳永逸的方案是把正确的 Scripts 目录加进用户 PATH 并重启终端。另一个高频问题是版本不一致。langgraph 生态由主库、cli、checkpoint 插件等多个包组成如果主库和 CLI 版本跨度很大Server 启动后功能会表现得非常诡异。经验做法是装完所有依赖后跑一下pip freeze | grep langgraph然后手动把相关包钉到一个经测试的版本组合上。端口占用也值得提一句。langgraph dev默认端口是 8123本地同时调试多个服务时很容易撞。用--port指定端口或者先查占用进程再处理都能快速解决。6.2 服务运行类问题服务一旦跑起来问题就从“起不起来”升级成“能不能稳定地做正确的事”。现象可能原因排查与解决调用就超时上游模型服务网络不通在容器内直接 curl 模型 API检查网络白名单和代理配置响应正常但上下文不连续没配置 checkpointer或 thread_id 在多个请求间变化确认编译时传了持久化 checkpointer在日志里跟踪 thread_id流式接口一直不输出SSE 被网关或代理缓冲了关闭网关的 response buffering或改用 WebSocket多个用户的数据串了config 里 thread_id 写死成了同一个值每个请求从参数或用户 token 里动态解析 thread_id这些问题的共性是看起来是 LangGraph 的问题实际上是你自己的工程配置问题。排查时不要急于改代码先看日志再查配置最后才动业务逻辑。6.3 服务生命周期的运维习惯从脚本变成服务之后“进程活着”只是起点不是终点。很多从 Windows 环境开始接触服务管理的人习惯把所有服务都想象成 services.msc 里的那种“系统服务”形态——能添加删除、能设置启动模式。这个思维是对的只是 Linux 和容器环境里的对应工具不一样。Linux 环境部署自封装 FastAPI 服务时我推荐直接用 systemd 托管配好开机自启和崩溃重启[Unit] Descriptionlanggraph-agent-service Afternetwork.target [Service] Userdemo WorkingDirectory/opt/agent ExecStart/opt/agent/.venv/bin/uvicorn app:app --host 0.0.0.0 --port 8000 Restartalways RestartSec5 EnvironmentFile/etc/agent.env [Install] WantedBymulti-user.target如果你用 Docker 部署则用容器重启策略来兜底比如--restart unless-stopped再配一个进程级健康检查。这个习惯太容易被忽略了。我接手过不少“服务偶发挂掉”的问题最后发现根源就是启动脚本里没有配Restartalways进程一崩整个服务就静默宕机直到有人发现才手动拉起来。服务化之后你至少要让进程具备自愈能力这是和脚本阶段最大的区别之一。6.4 上线前的自查清单最后整理一份我自己每次上线 LangGraph 服务前都会过一遍的清单。先从业务层面确认checkpointer 是否使用了独立存储多实例部署时用的是数据库共享状态还是进程内状态会话隔离是否验证过两个不同 thread_id 并行跑同一个图时数据互不干扰。再从运维层面确认结构化日志里有没有带上 thread_id、request_id出问题时能不能快速定位到具体节点健康检查接口有没有配好监控和告警能不能覆盖到。最后从安全层面确认模型 API key 是通过运行环境注入的而不是直接写死在镜像层里否则docker history一查就能翻出所有人的密钥入口处有没有基本的限流和鉴权哪怕是团队内部使用也值得加一层保险。这个清单看起来都是小事但它们决定了你的 LangGraph 服务是从“能跑”变成“能用”还是从“能跑”变成“时常出事”。我见过太多项目在部署后的一两个月里被这些看似琐碎的问题反复折磨提前把清单过一遍能省掉后面的大量返工。写到这里我最大的体会是LangGraph 部署这件事难的从来不是 LangGraph 本身而是你愿不愿意把“脚本思维”切换成“服务思维”。我在实际项目里反复建议团队第一版就用脚本直跑把业务逻辑彻底验证完第二版切到 LangGraph Server用一个标准 API 尽快给到业务方等确实碰到官方方案覆盖不了的需求再动手自封装微服务。最后分享一个小技巧无论走哪条路径一定在图的所有节点都打上日志并且给每一次运行统一标注 thread_id 和 run_id。否则一旦出了事你连“它到底执行到哪个节点”都说不清楚。这条经验帮我省下的排查时间远远超过当初写日志花掉的那点功夫。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑