Appearance
LangSmith Studio
前置阅读:Agent 实战指南 · 中间件概览
本节解决什么问题
create_agent 返回的是一个 LangGraph CompiledStateGraph--它内部有模型节点、工具节点和循环边。当 Agent 行为不符合预期时,仅靠 print 调试消息列表很难定位问题出在模型推理、工具参数还是中间件拦截。LangSmith Studio 提供了一个可视化界面,让你看到图的拓扑结构、每一步的 state 快照、工具调用的入参出参,以及中间件对消息的修改。
核心概念
必须深刻理解,不能跳过:
create_agent的返回值就是一个 LangGraph 图。
create_agent(model, tools, system_prompt=..., middleware=...)返回CompiledStateGraph。这意味着:
- 它有节点(model node、tools node)和边(模型→工具→模型的循环)。
- 它的执行状态是
{"messages": [...]},每一步都会向messages追加消息。- LangSmith Studio 可视化的就是这个图--Studio 不是 LangChain 独有的调试器,它是 LangGraph 的通用可视化工具。
- 中间件(middleware)会注入额外的节点或修改 state,在 Studio 的图拓扑中可以看到这些变化。
前端类比
LangSmith Studio 之于 Agent,类似于 React DevTools 之于 React 应用。React DevTools 让你检查组件树(component tree)、查看 props/state 变化、定位渲染性能瓶颈;Studio 让你检查 Agent 的图拓扑(graph topology)、查看 messages state 变化、定位工具调用和中间件的行为差异。两者的核心理念相同:让不可见的执行过程变得可视化、可回溯。
原生语义:Studio 连接到一个运行中的 LangGraph 服务器(本地 langgraph dev 或远程 Agent Server),通过 LangGraph 的 introspection API 读取图结构和运行状态。它不是一个独立的运行时,而是一个观察和控制层--你在 Studio 中看到的执行,就是 agent.invoke() 的真实执行过程。
当前推荐写法:langgraph dev + langgraph.json
安装 CLI
bash
# 使用 uv(推荐)
uv pip install "langgraph-cli[inmem]"
# 或使用 pip
pip install "langgraph-cli[inmem]"[inmem] extra 安装内存模式的依赖,适合本地开发调试。生产部署使用 langgraph up 或 langgraph build(见 部署)。
项目结构
my-agent/
├── app.py # Agent 定义
├── langgraph.json # Studio 配置
├── pyproject.toml # 依赖声明
└── .env # 环境变量(API Key 等)langgraph.json 配置
json
{
"graphs": {
"enviro_agent": "./app:agent"
},
"dependencies": ["."],
"env": ".env"
}graphs:键是图名称(在 Studio 中显示),值是模块路径:变量名。dependencies:项目依赖目录列表(含pyproject.toml的目录)。env:环境变量文件路径。
最小可运行示例
python
# app.py
import os
from langchain.agents import create_agent
from langchain.agents.middleware import PIIMiddleware
from langchain.chat_models import init_chat_model
from langchain.tools import tool
@tool
def lookup_factor(keyword: str) -> str:
"""根据关键词查询环保因子信息。
Args:
keyword: 因子名称或别名(如 COD、二氧化硫)
"""
# EnviroNexus 知识库的确定性实体匹配示例
factors = {
"COD": {"unit": "mg/L", "standard": "GB 11914-89"},
"二氧化硫": {"unit": "mg/m³", "standard": "HJ 482-2009"},
}
result = factors.get(keyword)
return str(result) if result else f"未找到因子: {keyword}"
# 模型配置使用环境变量,不写死具体版本
model = init_chat_model(os.environ["LLM_MODEL"])
agent = create_agent(
model=model,
tools=[lookup_factor],
system_prompt=(
"你是 EnviroNexus 环保知识库助手。"
"用户询问环保因子时,使用 lookup_factor 工具查询。"
"回答中必须包含标准号和单位。"
),
middleware=[
PIIMiddleware("email", strategy="redact"),
],
)启动 Studio
bash
langgraph dev启动后终端会输出类似:
Started server on http://127.0.0.1:2024
Opening Studio at https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024在浏览器中打开 Studio 链接,即可看到 enviro_agent 图的可视化界面。
Studio 核心能力
| 能力 | 描述 |
|---|---|
| 图拓扑可视化 | 展示 create_agent 生成的节点和边(model node、tools node、middleware 注入的节点) |
| 交互式运行 | 在 Playground 中输入消息,实时查看 Agent 执行过程 |
| State 快照 | 每一步执行后的完整 {"messages": [...]} 状态 |
| 工具调用检查 | 查看工具名称、入参 JSON、返回值和延迟 |
| 中间件行为 | 观察中间件如何修改消息(如 PII 脱敏、摘要压缩) |
| 时间旅行 | 回溯到历史 checkpoint,从任意节点重新执行(fork) |
| 运行对比 | 并排比较不同输入或不同配置的运行结果 |
可视化 create_agent 的图结构
create_agent 生成的图在 Studio 中呈现为以下拓扑:
当添加中间件后,Studio 会显示中间件注入的额外节点。例如 PIIMiddleware 会在模型调用前后增加 PII 处理步骤,SummarizationMiddleware 会在消息数量超过阈值时触发摘要节点。
查看 State 和 Messages
在 Studio 的 Playground 中运行 Agent 后,右侧面板显示每一步的 state 快照:
Step 1 - agent (model node)
messages:
[0] HumanMessage: "查询 COD 的标准号"
[1] AIMessage (tool_calls):
- lookup_factor(keyword="COD")
Step 2 - tools (tool node)
messages:
[0] HumanMessage: "查询 COD 的标准号"
[1] AIMessage (tool_calls): lookup_factor(keyword="COD")
[2] ToolMessage: "{'unit': 'mg/L', 'standard': 'GB 11914-89'}"
Step 3 - agent (model node)
messages:
...
[3] AIMessage: "COD 的检测标准是 GB 11914-89,单位为 mg/L。"每个消息对象可以展开查看完整属性:content、tool_calls、usage(token 用量)、response_metadata 等。
调试中间件
中间件是 create_agent 最难调试的部分--它们在模型调用前后修改 state,但 print 只能看到最终结果。Studio 让你看到中间件在每个步骤的行为。
示例:调试 PII 脱敏
python
import os
from langchain.agents import create_agent
from langchain.agents.middleware import PIIMiddleware
from langchain.chat_models import init_chat_model
agent = create_agent(
model=init_chat_model(os.environ["LLM_MODEL"]),
tools=[],
system_prompt="你是客服助手,回答用户问题。",
middleware=[
PIIMiddleware("email", strategy="redact"),
PIIMiddleware("phone", strategy="mask"),
],
)
result = agent.invoke({
"messages": [{
"role": "user",
"content": "我的邮箱是 alice@example.com,电话是 13800138000,帮我修改密码。",
}],
})
for msg in result["messages"]:
print(f"[{type(msg).__name__}] {msg.content}")在 Studio 中运行这段代码,你可以看到:
- before_model 阶段:
PIIMiddleware将输入消息中的alice@example.com替换为[REDACTED_EMAIL],13800138000替换为138****8000。 - 模型调用:模型收到的是脱敏后的消息,不会看到真实邮箱和电话。
- after_model 阶段:模型输出不包含 PII(因为输入已脱敏)。
示例:调试 SummarizationMiddleware
python
from langchain.agents.middleware import SummarizationMiddleware
agent = create_agent(
model=init_chat_model(os.environ["LLM_MODEL"]),
tools=[lookup_factor],
system_prompt="你是环保知识库助手。",
middleware=[
SummarizationMiddleware(
model=init_chat_model(os.environ["LLM_MODEL"]),
trigger=("messages", 6), # 消息数超过 6 条时触发摘要
keep=("messages", 2), # 保留最近 2 条消息
),
],
)在 Studio 中持续对话直到消息超过 6 条,你可以看到摘要节点被触发,旧消息被压缩为一条 SystemMessage 摘要。
使用 @traceable 追踪自定义函数
除了 Agent 内部的自动追踪,你可以在自定义函数上使用 @traceable 装饰器,让它们在 Studio 和 LangSmith Trace 中可见:
python
from langsmith import traceable
@traceable(name="enrich_query")
def enrich_query(query: str) -> str:
"""对用户查询做预处理(如同义词扩展)。"""
synonyms = {"化学需氧量": "COD", "SO2": "二氧化硫"}
for full, abbr in synonyms.items():
query = query.replace(full, abbr)
return query
@traceable(name="build_response")
def build_response(factor_info: str) -> dict:
"""将因子信息组装为结构化响应。"""
return {"factor": factor_info, "source": "EnviroNexus"}
# 调用 Agent 时,这些函数会作为子节点出现在 Trace 中
user_query = "查询化学需氧量的标准号"
enriched = enrich_query(user_query)
result = agent.invoke({"messages": [{"role": "user", "content": enriched}]})在 Studio 的 Trace 视图中,enrich_query 和 build_response 会作为嵌套节点显示,帮助你定位预处理或后处理逻辑中的问题。
实战:调试一个工具调用失败的 Agent
问题场景
Agent 在查询环保因子时返回了错误的因子信息:
python
import os
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.tools import tool
@tool
def lookup_factor(keyword: str) -> str:
"""根据关键词查询环保因子信息。
Args:
keyword: 因子名称或别名
"""
factors = {
"COD": {"unit": "mg/L", "standard": "GB 11914-89"},
"二氧化硫": {"unit": "mg/m³", "standard": "HJ 482-2009"},
}
result = factors.get(keyword)
return str(result) if result else f"未找到因子: {keyword}"
agent = create_agent(
model=init_chat_model(os.environ["LLM_MODEL"]),
tools=[lookup_factor],
system_prompt="你是环保知识库助手。用户询问因子时使用 lookup_factor 工具。",
)
# 用户输入"化学需氧量",但工具只接受"COD"
result = agent.invoke({
"messages": [{"role": "user", "content": "查询化学需氧量的标准号"}],
})
print(result["messages"][-1].content)
# 输出可能包含"未找到因子: 化学需氧量"在 Studio 中调试
- 启动 Studio:
langgraph dev,在浏览器中打开 Studio。 - 运行 Agent:在 Playground 中输入"查询化学需氧量的标准号"。
- 查看工具调用:展开 Step 1 的 AIMessage,发现
tool_calls中keyword="化学需氧量"。 - 查看工具返回:展开 Step 2 的 ToolMessage,内容是
"未找到因子: 化学需氧量"。 - 定位根因:工具的
factors字典只支持缩写"COD",不支持中文全称"化学需氧量"。
修复方案
在工具中加入别名映射:
python
@tool
def lookup_factor(keyword: str) -> str:
"""根据关键词查询环保因子信息。
Args:
keyword: 因子名称或别名(如 COD、化学需氧量、二氧化硫、SO2)
"""
# 别名到标准名的映射
alias_map = {
"化学需氧量": "COD",
"SO2": "二氧化硫",
}
normalized = alias_map.get(keyword, keyword)
factors = {
"COD": {"unit": "mg/L", "standard": "GB 11914-89"},
"二氧化硫": {"unit": "mg/m³", "standard": "HJ 482-2009"},
}
result = factors.get(normalized)
return str(result) if result else f"未找到因子: {keyword}"修复后在 Studio 中重新运行,确认工具返回了正确的因子信息。
时间旅行与状态回溯
Studio 支持 LangGraph 的 time travel 能力。每次运行 Agent 时,如果配置了 Checkpointer,每一步都会生成一个 checkpoint。在 Studio 中你可以:
- 回溯:点击任意历史 checkpoint,查看该步的完整 state。
- Fork:从某个 checkpoint 创建一个副本,修改输入后重新执行。
- 重放:使用相同的输入重新运行,对比行为差异。
python
from langgraph.checkpoint.memory import InMemorySaver
agent = create_agent(
model=init_chat_model(os.environ["LLM_MODEL"]),
tools=[lookup_factor],
system_prompt="你是环保知识库助手。",
checkpointer=InMemorySaver(), # 启用 checkpoint 后 Studio 才能时间旅行
)
config = {"configurable": {"thread_id": "debug-session-1"}}
result = agent.invoke(
{"messages": [{"role": "user", "content": "查询 COD 标准"}]},
config=config,
)常见错误与旧版 API 对照
旧版(仅用于读旧项目,新项目不得采用)
LangChain 0.x 时代使用 create_tool_calling_agent + AgentExecutor + LangServe Playground 做可视化调试。以下代码在 LangChain 1.0 中 无法运行(ImportError):
python
# ❌ 0.x 写法 -- 1.0 已删除,仅用于理解旧项目
from langchain.agents import create_tool_calling_agent, AgentExecutor # ImportError
from langchain.prompts import ChatPromptTemplate
from langserve import add_routes # LangServe 2024-11 弃用
prompt = ChatPromptTemplate.from_messages([
("system", "..."),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"), # 0.x 专属占位符
])
agent = create_tool_calling_agent(llm, tools, prompt) # 已删除
executor = AgentExecutor(agent=agent, tools=tools) # 已删除
add_routes(app, executor, path="/agent") # LangServe 弃用迁移方式:改用 create_agent + langgraph dev + LangSmith Studio。create_agent 不需要 ChatPromptTemplate 和 agent_scratchpad,系统提示词通过 system_prompt= 参数传入。
常见问题排查
| 问题 | 原因 | 解决方案 |
|---|---|---|
langgraph dev 报 ModuleNotFoundError | langgraph.json 中的模块路径不对 | 确认 ./app:agent 对应 app.py 中的 agent 变量 |
| Studio 中看不到图 | langgraph.json 的 graphs 字段为空 | 添加 "graphs": {"name": "./module:var"} |
| Studio 连接失败 | 本地服务器未启动或端口被占用 | 检查 langgraph dev 是否在运行,默认端口 2024 |
| 中间件行为不可见 | 中间件未正确添加到 middleware= 列表 | 确认中间件实例传入 create_agent(middleware=[...]) |
| 时间旅行不可用 | 未配置 Checkpointer | 添加 checkpointer=InMemorySaver() |
适用与不适用场景
适用
- 开发阶段调试 Agent 的工具调用链路
- 验证中间件(PII 脱敏、摘要压缩、HITL 中断)的行为
- 对比不同
system_prompt或不同模型对同一输入的行为差异 - 回溯历史运行,定位偶发性错误
- 向非技术人员展示 Agent 的决策过程
不适用
- 生产环境监控(使用 可观测性 和 LangSmith Trace)
- 自动化回归测试(使用 测试)
- 性能基准测试(Studio 有额外 introspection 开销,不代表生产性能)
- 需要 CI/CD 集成的场景(使用
pytest+ LangSmith Evaluation)
层级边界
LangSmith Studio 调试的是 LangGraph 图。create_agent 产生的图相对简单(model + tools 循环),如果需要更复杂的图拓扑(条件分支、并行节点、子图),应直接使用 LangGraph 的 StateGraph API,详见 LangGraph 入门。如果你在构建长周期研究、多文件多 Subagent 的高级 Agent,Studio 同样可以调试 Deep Agents 产生的图,详见 Deep Agents 生态定位。