LangServe 部署 + LangSmith 可观测集成
一、概念速查
| 组件 | 职责 | 入口 |
|---|---|---|
| LangServe | 将 LCEL Runnable 序列部署为 REST API | langserve>=0.3.0 |
| LangSmith | 分布式追踪、调试、评估、监控 | langsmith>=0.2.0 |
| LangGraph | 有状态 Agent 编排层(可选叠加) | langgraph>=0.2.0 |
LangServe 做的事情本质上很简单:接收一个 Runnable 对象,为它生成 POST /invoke、POST /batch、POST /stream 和 POST /stream_log 四个端点,挂载到 FastAPI 上。LangSmith 则通过环境变量注入的 OpenTelemetry 兼容层,自动捕获每次调用的完整 run tree——从入口链到内部的 model call、retriever、tool 调用,全部归入一个 project 下。
# langserve>=0.3.0, langchain>=0.3.0, langsmith>=0.2.0
import os
from fastapi import FastAPI
from langserve import add_routes
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_API_KEY"] = "lsv2_..."
os.environ["LANGSMITH_PROJECT"] = "my-agent-prod"
app = FastAPI(title="My Agent")
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant."),
("human", "{input}"),
])
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
chain = prompt | llm
add_routes(app, chain, path="/chat")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8080)
上述代码部署后,POST /chat/invoke 即暴露为可用接口。LangSmith 端自动开始接收追踪数据,无需额外埋点。
二、底层原理
LangServe 的 add_routes 内部做了三件事:
- 反射 Runnable 接口 — 检查传入对象是否实现了
invoke、batch、stream、astream_events等方法,为每个方法生成对应的路由处理器。 - 注册异常处理中间件 — 捕获链执行中的各类
LangChainException,映射为结构化 JSON 错误响应。 - 注入 Callback 链 — 将 LangChain 的
BaseCallbackHandler机制与 FastAPI 的请求生命周期绑定。每个 HTTP 请求触发一个 root run,其内部所有子调用(LLM、retriever、tool)自动成为该 run 的子节点。
LangSmith 的追踪原理依赖 RunTree 数据结构:
Root Run (invoke)
├── LLM Run (ChatOpenAI) — token usage, latency
├── Retriever Run (vectorstore) — score, source_documents
└── Tool Run (function call) — input, output
每个 run 携带 name、run_type、inputs、outputs、start_time、end_time、parent_run_id、tags、metadata 等字段,通过 POST /runs 批量异步上报。LangSmith API 以 project 为单位归集这些 run,生成延迟分位数、错误率、token 消耗等时序指标,并提供 drill-down 到单个 trace 的界面。
三、架构设计原则
原则 1:部署层与编排层分离
LangServe 是传输层,它只负责把 LCEL Runnable 变成 HTTP 端点。有状态编排(多轮对话、条件分支、人机交互)应交给 LangGraph,再将编译好的 Graph 对象传给 add_routes:
# langgraph>=0.2.0, langserve>=0.3.0
from langgraph.graph import StateGraph
from langserve import add_routes
graph = StateGraph(...)
app_chain = graph.compile()
add_routes(app, app_chain, path="/agent")
不要试图在 LangServe 路由层处理会话状态——它的设计是无状态的每次调用独立。
原则 2:环境维度的 project 隔离
LangSmith project 应按照环境(dev / staging / prod)命名,而非按照功能维度混合:
os.environ["LANGSMITH_PROJECT"] = os.getenv("DEPLOY_ENV", "dev") + "-my-agent"
这样在 LangSmith 面板上可以分别观察各环境的延迟分布和错误率,并针对 prod 单独设置告警。
原则 3:自定义标注与反馈回路
LangSmith 默认的自动追踪已经足够,但可观测的真正价值在于标注。在每个关键业务节点注入 metadata 和 tags:
chain.invoke(
{"input": "退款流程怎么走"},
config={
"metadata": {"user_id": "u_12345", "source": "web"},
"tags": ["intent:refund", "priority:high"],
"run_name": "customer-support-pipeline",
},
)
这些标注会出现在 LangSmith trace 详情页,支持过滤和聚合。进一步,通过 LangSmith SDK 将用户满意度评分写回对应 run,形成从 trace → eval → feedback 的完整闭环。
999

被折叠的 条评论
为什么被折叠?



