langgraph快速入门 —— 从本地脚本到 HTTP 服务

第五篇:从本地脚本到 HTTP 服务

前四篇都在本机跑脚本。最后一篇处理两件事:出问题时怎么看见内部发生了什么,以及怎么把图变成一个别人能调用的服务。涉及 test8.py 和 test21/test21.py。

先能看见,再谈优化

test8.py 接的是 LangSmith:

复制代码
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_API_KEY"] = "..."
os.environ["LANGSMITH_PROJECT"] = "MultiAgent-DeepSeek"

开完这三个环境变量,图里每个节点、每次模型调用、每次工具调用都会上报到 smith.langchain.com。页面上能看到调用树、耗时、输入输出,还能把测试集固定下来做回归。

写 Agent 的时候,print 只能看到自己埋的点。追踪能看到模型每一轮收到什么、回了什么,调提示词靠它省时间。

同时提醒一句:test8.py 里的 key 是明文写在代码里的,这行得挪到 .env,密钥最好也去平台重置一次。这个脚本是我最早写的,当时没在意。

图得一直活着

test21 目录里三个文件,.env、langgraph.json、test21.py。langgraph.json 告诉平台去哪找图:

复制代码
{
  "dependencies": ["."],
  "graphs": { "piano_agent": "./test21.py:graph" },
  "env": "./.env"
}

第一次我直接把 test18.py 的 MySQL 代码抄过来,服务起不来。原因是 from_conn_string() 在 1.2.x 里是上下文管理器,with 块结束连接就关了,而平台要求图在模块级常驻。

改成手动建连接,连接一直开着:

复制代码
conn = pymysql.connect(**conn_params, autocommit=True)
checkpointer = PyMySQLSaver(conn)
graph = builder.compile(checkpointer=checkpointer, name="piano_agent_graph")

compile() 里的 name 是给平台看的图名。本地用 langgraph dev 起服务,之后就能通过 HTTP 调这个图,thread_id、断点、历史这些能力和脚本里一样。

五个已经确认的坑

写到这里,把能复现的问题列清楚。

test19.py 漏了 from langgraph.graph import StateGraph, START, END,运行直接 NameError,补一行就好。

test20.py 依赖 PostgresSaver,环境里没装 langgraph-checkpoint-postgres,也没起 PG 服务。换成 SqliteSaver 或 PyMySQLSaver 就能验证声明式中断。

test8.py 的密钥明文,要挪走并重置。

节点函数返回完整 state 会覆盖别的节点写进去的字段。只返回增量,合并交给 reducer。

没有挂 checkpointer 时,interrupt 和时间回溯都不工作。报错信息不明显,容易误判成 API 用法写错。

完整示例

部署需要一个目录、三份文件。第一份是图本身,注意连接放在模块级,图名写进 compile():

复制代码
# agent_graph.py
import sqlite3
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.sqlite import SqliteSaver

class State(TypedDict):
    prompt: str
    content: str

def generate(state: State):
    return {"content": f"编曲方案:{state['prompt']},速度 80 BPM,C 大调"}

def save(state: State):
    return {"content": state["content"] + "(已存档)"}

builder = StateGraph(State)
builder.add_node("generate", generate)
builder.add_node("save", save)
builder.add_edge(START, "generate")
builder.add_edge("generate", "save")
builder.add_edge("save", END)

conn = sqlite3.connect("platform_checkpoints.sqlite", check_same_thread=False)
checkpointer = SqliteSaver(conn)
checkpointer.setup()

graph = builder.compile(
    checkpointer=checkpointer,
    name="piano_agent_graph",
)

第二份是 langgraph.json,写给平台看:

复制代码
{
  "dependencies": ["."],
  "graphs": { "piano_agent": "./agent_graph.py:graph" },
  "env": "./.env"
}

第三份是调用脚本,用 HTTP 打这个服务:

复制代码
# call_api.py
import requests

BASE = "http://127.0.0.1:2024"

response = requests.post(
    f"{BASE}/runs/wait",
    json={
        "assistant_id": "piano_agent",
        "input": {"prompt": "写一段睡前钢琴曲的方案", "content": ""},
        "config": {"configurable": {"thread_id": "api-1"}},
    },
)
print(response.json())

运行顺序:

复制代码
langgraph dev
python call_api.py

服务起来之后,call_api.py 会打印返回的 state,content 字段里能看到两段拼接后的文本。把 thread_id 换成另一个值,服务端会当成新会话,历史不会串。

要接 MySQL,把 agent_graph.py 里的连接换成 PyMySQLSaver,其余不用动。如果服务起不来,先检查有没有把连接写进 with 块里。

系列收尾

五篇写完,22 个脚本其实只讲了一件事:把流程从代码里拆出来,变成一张能暂停、能回放的图。模型是零件,图是骨架,checkpointer 让骨架活起来。

LangGraph 的学习曲线陡的地方在概念,不在 API。StateGraph、add_conditional_edges、compile 这几个调用看一遍就会,真正花时间的是想清楚状态该有哪些字段、什么时候该停、记忆放哪一层。

下一步我打算把 test21 里的 piano_agent 补成完整项目:supervisor 调度、工具调用、MySQL 存档、审核中断、LangSmith 追踪,一条链路跑通。散脚本学得快,串成项目才记得住。

系列目录:

  • 第一篇:一张状态图是怎么跑起来的

  • 第二篇:Agent 是怎么转起来的

  • 第三篇:让图记住,也让它停下来

  • 第四篇:多个 Agent 怎么分工

  • 第五篇:从本地脚本到 HTTP 服务

相关推荐
去伪存真1 小时前
AI Agent 性能评估指标与自动化测试落地方案
前端·agent
lucas_AI2 小时前
美团 LongCat-2.5 发布:把「看屏幕」塞进主干,把「踩过的坑」变成训练题
agent
玩泥巴的2 小时前
源生成与 AOT 就绪的微信生态 .NET SDK:结构、能力边界与 API 定位方法
ai·微信·.net·agent·aot·roslyn
大鹏的NLP博客2 小时前
从格赖斯合作原则看大语言模型与提示词工程
人工智能·自然语言处理·大模型·agent
沉默王二2 小时前
真绝了,国产模型 Token 自由了!
人工智能·openai·agent
DigitalOcean2 小时前
Spot GPU 与预留 GPU:AI 初创公司的实用成本指南
agent
寻道码路2 小时前
AI Agent 工程化开发实战(五):持久化与断点恢复——Checkpointer 与 Store
人工智能·大模型·checkpoint·持久化·ai agent·langgraph·ai工程化
jerrywus2 小时前
你还在手动seo? 那你可以试试claude-seo skill
agent·claude·seo
Sailing3 小时前
🚀🚀【封闭结束7】Agent 为什么总是“失忆”?从长期记忆到上下文工程的生产实践
前端·人工智能·agent