结论先行
到达这一层,你面对的核心问题是:如何让 LangGraph 从“能跑的图”变成“生产系统的一部分”。这需要掌握三个此前未涉及的维度:Functional API(用代码而非图结构编排流程)、时间旅行与状态编辑(运行时修改执行路径),以及生产级可观测性与成本控制。这些能力决定了你的 Agent 能否在真实环境中稳定、可控、可审计地运行。
一、Functional API:不画图也能编排
1.1 为什么需要另一种 API
StateGraph 用“节点+边”的声明式方式定义流程,适合结构清晰、需要可视化的工作流。但当逻辑本身是代码形态的——比如一堆顺序执行的函数调用,中间穿插条件判断——硬画成图反而别扭。
Functional API 用两个原语解决这个问题:@entrypoint 定义工作流入口,@task 定义离散的工作单元。两者共享与 StateGraph 完全相同的底层运行时(Pregel),因此可以和 Graph API 混合使用:一个 entrypoint 内部可以调用编译好的 StateGraph,一个 StateGraph 的节点也可以是 task。
1.2 Task 的异步与缓存
@task 装饰的函数返回的是 future 对象,通过 .result() 同步取值或 await 异步取值。这意味着多个 task 可以并发提交,不必串行等待。
Task 支持超时与重试的声明式配置:
fromlanggraph.typesimportRetryPolicy,NodeTimeoutError@task(timeout=1.0,retry_policy=RetryPolicy(retry_on=NodeTimeoutError),)asyncdefcall_api(url:str)->str:...超时在每个重试周期内独立计时,重试时计时器重置。对于确定性且耗时的 task,可以用 CachePolicy(ttl=…) 启用缓存,避免同一输入重复计算。
1.3 Entrypoint 与短期记忆
@entrypoint(checkpointer=…) 启用持久化后,每次调用会保存检查点。下次用相同的 thread_id 调用时,可以通过 previous 参数访问上一次的返回值。
@entrypoint(checkpointer=checkpointer)defmy_workflow(number:int,*,previous:Any=None)->int:previous=previousor0returnnumber+previous my_workflow.invoke(1,config)# 1my_workflow.invoke(2,config)# 3如果希望返回值和保存值解耦,使用 entrypoint.final(value=…, save=…):调用方看到 value,检查点保存 save。
1.4 错误恢复的天然优势
Functional API 在错误恢复上有独特优势:当一个 task 执行完毕,结果就被持久化。后续 task 失败时,恢复执行不会重新运行已完成的 task。这与 StateGraph 的检查点机制效果一致,但编写方式更接近普通代码。
二、时间旅行与状态编辑
2.1 从历史检查点恢复
编译了 checkpointer 的图,每一步都会保存完整的 StateSnapshot。通过 getStateHistory(config) 可以获取按时间倒序排列的全部快照。
Replay(重放):用一个历史检查点的 checkpoint_id 调用图,LangGraph 会跳过该检查点之前的节点(结果已保存),从该点之后重新执行。重新执行意味着 LLM 调用、API 请求、interrupt 都会再次触发。
history=list(app.get_state_history(config))past_checkpoint=history[2]# 第三个最近的检查点past_config={"configurable":{"thread_id":"thread-1","checkpoint_id":past_checkpoint.config["configurable"]["checkpoint_id"],}}app.invoke({"new_input":"..."},past_config)2.2 运行时编辑状态
updateState() 允许在不修改原始检查点的前提下,创建一个包含修改值的新检查点。更新会经过节点的 reducer 处理——有 reducer 的字段会累加而非覆盖。
app.update_state(config,{"corrected_value":"new value"},as_node="agent",# 更新“伪装”成来自 agent 节点)app.invoke(None,config)# 从更新后的状态继续as_node 参数控制执行流:更新后 LangGraph 认为哪个节点刚刚完成,从而决定下一步走向。
2.3 持久化模式(Durability Modes)
这是生产环境的关键配置,控制检查点写入的时机:
进程可能在执行中途崩溃时,“exit” 模式丢失全部中间状态,“async” 有极小概率丢失最后一个检查点,“sync” 保证每一步都已落盘。
三、生产级可观测性与成本控制
3.1 结构化 Trace
LangGraph 节点通过 OpenTelemetry 标准发出 span,可以集成到已有的基础设施监控中,而非孤立在单独的 Agent 调试工具里。配置方式包括:
trace_all_langgraph_nodes=True 默认追踪所有节点
通过节点 metadata 的 otel_trace: True/False 精细控制
自定义状态中消息嵌套较深时,用 message_keys 和 message_paths 指定位置
3.2 Token 预算作为运行时策略
生产系统中,成本控制不应是事后统计,而应是运行时约束。参考 agent-forge 的设计模式:每个节点执行都经过一个 BudgetTracker,记录 token 和成本消耗,超过预算时抛出 BudgetExceeded 终止运行。
预算追踪器放在 contextvars 中,因此并发运行的多个 thread 不会互相污染用量统计。
3.3 重试与降级链
节点的容错需要两层策略:重试(瞬时故障)和降级链(持续故障)。FallbackChain 模式:主模型调用失败时,自动尝试备用 provider,而不是直接抛错给用户。
四、Checkpointer 的生产选择
开发阶段用 InMemorySaver,生产环境需要根据基础设施选择:
MongoDBSaver 的一个独特优势:状态检查点、工具调用记录和知识库检索可以共用同一个 DocumentDB 集群,无需单独维护向量存储。
五、进阶实践的核心判断
到达这个阶段,你面临的不再是“这个 API 怎么用”,而是架构决策:
什么时候用 Functional API 而非 StateGraph? 当流程逻辑本质上是代码形态、需要大量顺序函数调用、且不需要可视化拓扑时。两者可以混合:在 StateGraph 节点内部调用 entrypoint,或在 entrypoint 内调用编译好的子图。
什么时候需要时间旅行? 调试复杂分支决策时(回溯“为什么走了这条路”)、人工修正状态后继续执行时、以及 A/B 测试不同路径时。
生产系统的底线是什么? 每个 LLM 调用都有超时和重试策略,每个运行都有 token 预算,每个节点执行都有 trace span。这些不是“优化项”,而是运行的基本前提。