LangGraph 系列 3:启动本地 Agent Server 并使用 Studio 调试


上一篇已经创建天气 Agent,并确认 graph 的类型是 CompiledStateGraph。本篇启动两个本地服务:Qwen3 负责模型推理,Agent Server 负责运行 Graph、执行工具并提供 HTTP 接口。

最终需要验证的不只是“服务端口能访问”,还要确认模型真的生成了工具调用,工具结果也确实返回模型。

1. 两个服务分别做什么

本例包含两个独立服务:

服务 端口 作用
Qwen3 MLX Server 18080 提供 OpenAI 兼容模型接口
LangGraph Agent Server 2024 加载 Graph、管理运行并执行工具

Agent Server 不包含 Qwen3 权重。它运行到模型节点时,会通过 ChatOpenAI 请求 127.0.0.1:18080/v1。

下图把服务边界和 Graph 内部循环放在一起:Studio 或 API 客户端只请求 2024 端口;模型节点请求 18080 端口;工具节点在 Agent Server 进程中执行;开发状态写入 .langgraph_api。

本地 Agent Server 的服务边界与工具调用循环

2. 启动本地 Qwen3

前面的普通聊天接口可以使用主 .venv,但 Agent 工具调用还要求推理服务返回完整的 Tool Call ID。主环境中的旧版 MLX-LM 会返回 id=None,ToolNode 无法据此创建匹配的 ToolMessage。

因此从本篇开始为模型服务创建独立的 .venv_tool_server。直接依赖固定为:

mlx-lm==0.31.1
mlx==0.31.0
transformers==5.3.0

这些依赖已经写入 p03_local_agent_server/requirements-server.in,完整安装结果保存在同目录的 requirements-server-lock.txt。

打开第一个终端,进入 llm_learning 根目录并创建环境:

cd source/_posts/llm_learning

python3.12 -m venv \
  --prompt llm_learning_tool_server \
  .venv_tool_server

source .venv_tool_server/bin/activate
python -m pip install \
  -r langgraph/p03_local_agent_server/requirements-server-lock.txt
python -m pip check

实际检查结果:

No broken requirements found.

保持这个环境处于激活状态,启动模型服务:

"$VIRTUAL_ENV/bin/python" -m mlx_lm server \
  --model model \
  --host 127.0.0.1 \
  --port 18080 \
  --chat-template-args '{"enable_thinking": false}'

服务启动后不要关闭终端。另开一个终端检查模型接口:

curl --noproxy '*' \
  http://127.0.0.1:18080/v1/models

只要返回 object: list,并且 data 中存在模型,说明 Qwen3 服务已经就绪。

如果这一步失败,不要继续排查 LangGraph。Agent Server 可以启动,但运行到模型节点时一定会报连接错误。

3. 启动 Agent Server

打开第二个终端:

cd source/_posts/llm_learning
source .venv_langgraph/bin/activate
cd langgraph/p02_local_agent_project

确认已经准备 .env:

cp .env.example .env

启动本地服务:

langgraph dev \
  --host 127.0.0.1 \
  --port 2024 \
  --no-browser

–no-browser 表示启动后不自动打开 Studio,方便先观察终端日志。启动成功时会给出三个地址:

API: http://127.0.0.1:2024
Studio UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024
API Docs: http://127.0.0.1:2024/docs

This in-memory server is designed for development and testing.

启动日志还会出现:

Importing graph with id weather_agent
Using auth of type=noop
Starting In-Memory runtime

这些稳定内容分别说明:

  1. langgraph.json 中的 weather_agent 已被加载。
  2. 本地开发服务没有配置鉴权。
  3. 当前使用内存运行时,不是生产部署。

文章不保留服务启动时间、动态请求 ID 等每次运行都会变化的字段。

4. 编写健康检查脚本

langgraph/p03_local_agent_server/01_check_server.py 的完整代码如下:

"""检查本地 LangGraph Agent Server 的健康接口和 OpenAPI 文档。"""

import httpx


# trust_env=False 可以避免 VPN 或系统代理转发本机请求。
with httpx.Client(base_url="http://127.0.0.1:2024", trust_env=False) as client:
    health_response = client.get("/ok")
    health_response.raise_for_status()

    openapi_response = client.get("/openapi.json")
    openapi_response.raise_for_status()
    openapi = openapi_response.json()

print(f"健康检查状态码:{health_response.status_code}")
print(f"OpenAPI 状态码:{openapi_response.status_code}")
print(f"API 标题:{openapi['info']['title']}")
print(f"接口数量:{len(openapi['paths'])}")

运行:

cd source/_posts/llm_learning
source .venv_langgraph/bin/activate

python langgraph/p03_local_agent_server/01_check_server.py

真实结果:

健康检查状态码:200
OpenAPI 状态码:200
API 标题:LangSmith Deployment
接口数量:47

这里的 LangSmith Deployment 是这一版 Agent Server OpenAPI 文档中的标题,不表示代码已经部署到 LangSmith 云端。服务仍然监听本机 127.0.0.1:2024。

5. 查看本地 API 文档

浏览器打开:

http://127.0.0.1:2024/docs
LangGraph Agent Server API 文档

页面中可以看到 Assistants、Threads 和 Runs 等接口:

  • Assistant:已经配置好的 Graph 实例。
  • Thread:一次可持续保存状态的会话。
  • Run:在某个 Thread 中执行一次 Assistant。

本篇只认识这些对象,不展开 SDK 和 REST API 的完整调用方式。

6. 使用 Studio 调试 Graph

终端会给出 Studio URL:

https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024

Studio 是托管的调试页面,baseUrl 指向本机 Agent Server。页面负责发送请求和展示过程,Agent 和工具仍然在本机执行。

这种连接方式与官方 Studio 本地开发说明一致。LangSmith Studio 本地开发

打开页面后选择 weather_agent,输入:

北京天气怎么样?

正常情况下可以看到四条关键消息:

HumanMessage
→ AIMessage,包含 get_weather 工具调用
→ ToolMessage,包含天气工具返回值
→ AIMessage,生成最终回答

本次真实运行结果如下。截图已经展开模型节点和工具节点,可以直接看到 get_weather 的城市参数、工具返回值和最终回答。顶部动态 Thread ID、相对时间和页面提示没有保留,避免这些运行时信息干扰正文。

Studio 中的天气 Agent 工具调用过程

如果 Studio 页面因为网络、浏览器本地网络策略或代理设置无法连接,先使用 /docs 和健康检查确认本地服务。Studio 只是调试界面,不能代替本地 API 是否正常的判断。

7. 验证 Agent 是否真的调用工具

本次通过 Agent Server 发起运行后,得到的稳定结果如下。动态消息 ID 已省略:

1. human
   content=北京天气怎么样?
2. ai
   tool=get_weather args={'city': '北京'}
3. tool
   content=北京今天的天气晴朗,气温是 28 摄氏度。
4. ai
   content=北京今天的天气晴朗,气温是 28 摄氏度。

这段结果能证明完整链路已经运行:

  1. 用户问题进入 Agent。
  2. Qwen3 没有直接编造答案,而是生成 get_weather 工具调用。
  3. LangGraph 执行工具并创建 ToolMessage。
  4. 工具结果再次交给模型,模型生成最终回答。

天气内容来自模拟函数,不是实时天气接口。这个案例验证的是 Agent 执行过程,不是天气数据准确性。

8. 为什么能够看到一张 Graph

上一篇只写了 create_agent(),没有手动创建节点和边,但它会在内部生成一张编译图。

这张图至少包含以下逻辑:

开始
→ 模型节点
→ 判断 AIMessage 是否包含 tool_calls
→ 有工具调用:进入工具节点,再返回模型节点
→ 没有工具调用:结束

因此天气案例虽然代码很少,仍然具备 LangGraph 的状态和循环结构。后续学习 StateGraph 时,会把这些节点、状态和条件边显式写出来。

9. 本地服务的数据会保存吗

langgraph dev 使用面向开发和测试的单进程内存运行时,但“内存运行时”不等于“退出进程后所有本地状态都会消失”。这一版开发服务器会把 checkpoint 和 store 数据写入项目目录下的 .langgraph_api/:

p02_local_agent_project/
  .langgraph_api/
    .langgraph_checkpoint.*.pckl
    .langgraph_store.*.pckl

因此,只要没有删除 .langgraph_api/,重新启动同一个项目后,开发环境中的 Thread 状态可以继续读取。这个目录是本地运行数据,已经被 .gitignore 排除,不应提交到代码仓库。

LangGraph CLI 也将 langgraph dev 定义为会把状态保存到本地目录的轻量开发服务器。LangGraph CLI

这与前面 SQLite 聊天历史、Chroma 知识库不同:

  • SQLite 和 Chroma 是应用主动选择的数据存储。
  • .langgraph_api/ 是 langgraph dev 为本地开发保存的 checkpoint 和 store 文件。
  • 本地 pickle 文件可以支持开发过程中的重启恢复,但不是生产级数据库,也不适合多实例并发、备份恢复和长期运维。

所以,langgraph dev 适合本机调试,不能因为能够恢复本地状态就直接作为生产部署方案。后面学习 Checkpointer 和正式部署时,还需要使用适合生产环境的持久化后端。

本次使用一个测试 Thread 写入四条消息,正常停止并重新启动 Agent Server,再读取同一个 Thread。动态 Thread ID 已省略,稳定结果如下:

重启前消息数量:4
重启后 Thread 状态:idle
重启后消息数量:4
最后一条消息类型:ai
最后一条消息内容:北京今天的天气晴朗,气温是 28 摄氏度。

测试完成后已经删除这个测试 Thread。重启前后消息数量和最后一条消息保持一致,说明本地开发状态确实从 .langgraph_api/ 恢复。

10. 常见问题

10.1 Agent Server 启动成功,但运行时报 502

先检查 Qwen3:

curl --noproxy '*' \
  http://127.0.0.1:18080/v1/models

Agent Server 自己正常不代表模型服务正常。

10.2 端口已经被占用

检查端口:

lsof -nP -iTCP:18080 -sTCP:LISTEN
lsof -nP -iTCP:2024 -sTCP:LISTEN

结束旧进程或换一个端口。修改模型端口时,也要同步修改 graph.py 中的 base_url。

10.3 graph 导入失败

检查 langgraph.json:

"weather_agent": "./src/weather_agent/graph.py:graph"

文件路径和冒号后的变量名都必须存在。

10.4 模型没有调用工具

依次检查:

  1. 工具是否具有清晰的函数名。
  2. 参数是否有类型标注。
  3. 是否编写了工具文档字符串。
  4. 系统提示词是否明确要求天气问题调用工具。
  5. 模型服务是否支持 OpenAI 工具调用格式。

10.5 工具节点提示 unexpected type: str

如果服务日志出现:

Tool get_weather returned unexpected type: <class 'str'>

先检查模型返回的 AIMessage.tool_calls。如果工具调用的 id 是 None,工具执行结果就无法与这次调用配对。不要在工具函数里手工伪造固定 ID,应使用本篇锁定的 .venv_tool_server 启动 Qwen3,再确认每次工具调用都获得独立 ID。

10.6 Studio 无法连接本地服务

先排除本地服务问题:

curl --noproxy '*' http://127.0.0.1:2024/ok

如果返回 200,再检查 VPN、系统代理、浏览器对外部 HTTPS 页面访问本地 HTTP 地址的限制。即使 Studio 暂时不可用,也可以通过本地 API 文档和后续 SDK 完成测试。

10.7 是否需要 LangSmith API Key

本篇使用本地 in-memory 模式,并设置:

LANGSMITH_TRACING=false

本次服务启动日志明确显示没有设置控制平面或 License Key,并跳过了元数据循环。本地健康检查和 Agent 运行仍然成功。

11. 停止服务

分别回到两个终端按 Control + C:

  1. 先停止 Agent Server。
  2. 再停止 Qwen3 MLX Server。

停止后可以检查端口是否释放:

lsof -nP -iTCP:2024 -sTCP:LISTEN
lsof -nP -iTCP:18080 -sTCP:LISTEN

12. 小结

本篇完成了本地 Qwen3、LangGraph Agent Server、API 文档和天气工具调用的完整验证。到这里,Graph 已经不只是 Python 进程中的对象,而是可以由本地 HTTP 服务加载和运行的 Agent。

检查项 实际结果
Qwen3 /v1/models 返回 200
Agent Server /ok 返回 200
/openapi.json 返回 200
OpenAPI 接口数量 47
Graph ID weather_agent
模型服务环境 .venv_tool_server
MLX-LM 0.31.1
用户问题 北京天气怎么样?
工具调用 get_weather(city=”北京”)
工具消息 北京今天的天气晴朗,气温是 28 摄氏度。
最终回答 北京今天的天气晴朗,气温是 28 摄氏度。
运行模式 本地 in-memory 开发服务

文章作者: hnbian
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hnbian !
评论
  目录