概述

前面几篇我们已经讲过:

  • create_agent() 如何创建一个能调用工具的 Agent。
  • Middleware 如何控制模型调用、工具调用和人工审批。
  • LangGraph 如何表达复杂状态图。
  • 多 Agent 如何通过 Supervisor 或 Swarm 协作。

这些内容解决的是“怎么把 LLM 应用写出来”。

但真实项目还会遇到另一个问题:

写出来之后,怎么让前端、其他服务、测试工具、外部系统调用它?

也就是部署问题。

同时,LLM 应用还有一个比传统接口更麻烦的问题:

用户说答案错了,你怎么知道错在 Prompt、模型、检索、工具,还是 Agent 决策?

这就是观测问题。

本文围绕两个工具展开:

  • LangServe:把 LangChain Runnable / Chain 暴露成 REST API。
  • LangSmith:记录每次 LLM 应用调用的 trace,帮助调试、评估和监控。

LangServe 解决“怎么调用”,LangSmith 解决“怎么知道它到底跑了什么”。

先建立全局图:从 Chain 到 HTTP API 再到 Trace

一个最小 LangChain 应用通常长这样:

chain = prompt | model | parser
result = chain.invoke({"question": "LangServe 是什么?"})

这只能在 Python 进程里调用。

如果前端要调用它,就需要一层 HTTP 服务。

LangServe 做的事情可以理解成:

Runnable / Chain / Agent
        |
        v
FastAPI + add_routes
        |
        v
REST API:
    POST /xxx/invoke
    POST /xxx/batch
    POST /xxx/stream
    GET  /xxx/input_schema
    GET  /xxx/output_schema
    GET  /xxx/playground

LangSmith 则在运行时记录:

用户请求
  |
  v
Prompt 格式化
  |
  v
模型调用
  |
  v
工具调用 / 检索 / Agent 决策
  |
  v
最终输出

这些步骤会形成一个 trace。

你可以在 LangSmith 里看到每一步的:

  • 输入。
  • 输出。
  • 耗时。
  • token 消耗。
  • 错误堆栈。
  • 子步骤层级关系。
  • metadata 和 tags。

LangServe 把 Runnable 变成服务,LangSmith 把服务调用变成可观察的执行链路。

当前选型提醒:什么时候还适合 LangServe?

因为 LangServe 已经 deprecated,先把边界说清楚。

场景 建议
维护已有 LangServe 项目 可以继续维护,注意版本和安全配置
部署简单 LCEL Chain LangServe 仍然容易理解和上手
部署普通 Runnable API LangServe 可作为轻量方案或教学方案
新建复杂 Agent / LangGraph 应用 优先考虑 LangGraph Platform
需要长期任务、状态恢复、人审、任务队列 优先考虑 LangGraph Platform 或自建后端
需要完整权限、租户隔离、审计 不要只靠 LangServe,应该结合 FastAPI / 网关 / 权限系统

本文会重点讲两件事:

  1. 如何用 LangServe 快速把 Chain 暴露成 API。
  2. 如何用 LangSmith 给这个 API 加全链路 trace。

更准确的理解是:

LangServe 是理解 LangChain Runnable 服务化的最短路径;生产新项目要结合官方当前推荐和自身架构选型。

LangServe 适合简单 Runnable 服务化和旧项目维护,复杂 Agent 新项目应优先评估 LangGraph Platform。

第一步:安装依赖

一个最小服务需要:

  • langserve
  • fastapi
  • uvicorn
  • langchain-core
  • 对应模型包,例如 langchain-openai

安装命令:

pip install "langserve[server]" fastapi uvicorn langchain-core langchain-openai

如果你还要写客户端:

pip install "langserve[client]"

如果希望服务端和客户端都装:

pip install "langserve[all]"

LangSmith tracing 需要:

pip install langsmith

通常 langchain 相关包会间接依赖 LangSmith,但显式安装能减少环境不一致问题。

服务端关注 langserve[server],客户端关注 langserve[client],观测关注 langsmith

第二步:写一个最小 Chain

先写一个普通 LCEL Chain。

from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI


prompt = ChatPromptTemplate.from_messages([
    (
        "system",
        "你是一个严谨的 LangChain 助手。回答要准确、简洁、可执行。",
    ),
    (
        "user",
        "请用中文回答这个问题:{question}",
    ),
])

model = ChatOpenAI(model="gpt-4o-mini", temperature=0)

chain = prompt | model | StrOutputParser()

本地直接调用:

answer = chain.invoke({
    "question": "LangServe 的作用是什么?"
})

print(answer)

这时它还只是一个 Python 对象。

下一步把它挂到 FastAPI 上。

只要对象是 Runnable,就可以被 LangServe 暴露成 API。

第三步:用 add_routes() 一行发布

创建 server.py

from fastapi import FastAPI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langserve import add_routes


prompt = ChatPromptTemplate.from_messages([
    (
        "system",
        "你是一个严谨的 LangChain 助手。回答要准确、简洁、可执行。",
    ),
    (
        "user",
        "请用中文回答这个问题:{question}",
    ),
])

model = ChatOpenAI(model="gpt-4o-mini", temperature=0)
chain = prompt | model | StrOutputParser()


app = FastAPI(
    title="LangChain Demo Server",
    version="1.0",
    description="A simple API server for LangChain runnables",
)


add_routes(
    app,
    chain,
    path="/qa",
)


@app.get("/")
def health_check():
    return {"status": "ok"}


if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)

启动服务:

uvicorn server:app --host 127.0.0.1 --port 8000 --reload

现在你得到了这些端点:

端点 作用
POST /qa/invoke 单次调用
POST /qa/batch 批量调用
POST /qa/stream 流式输出
GET /qa/input_schema 查看输入 schema
GET /qa/output_schema 查看输出 schema
GET /qa/config_schema 查看配置 schema
GET /qa/playground/ 打开调试 Playground
GET /docs FastAPI Swagger 文档

注意:

LangServe 默认不会给 / 自动生成页面。上面加 health_check() 是为了让根路径有健康检查结果。

add_routes(app, chain, path="/qa") 会把 Runnable 的标准调用能力映射成一组 HTTP API。

第四步:用 HTTP 调用 /invoke

curl 调用:

curl -X POST "http://127.0.0.1:8000/qa/invoke" \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "question": "LangSmith 能解决什么问题?"
    }
  }'

请求体里最重要的是:

{
  "input": {
    "question": "..."
  }
}

因为 Chain 的输入是:

{"question": "..."}

所以 HTTP 请求要把它放到 input 字段里。

响应大致是:

{
  "output": "LangSmith 用于记录、调试和分析 LLM 应用的执行过程...",
  "metadata": {
    "run_id": "..."
  }
}

不同版本的响应字段可能有细微差异,但核心都是:

  • input 放调用输入。
  • output 返回调用结果。
  • metadata 可能包含运行相关信息。

LangServe HTTP 协议把 Runnable 输入包在 input 字段里,把 Runnable 输出放在 output 字段里。

第五步:用 Python 客户端调用 RemoteRunnable

LangServe 不只能用 HTTP 调,还可以像本地 Runnable 一样远程调用。

from langserve import RemoteRunnable


remote_chain = RemoteRunnable("http://127.0.0.1:8000/qa/")

result = remote_chain.invoke({
    "question": "LangServe 和 FastAPI 是什么关系?"
})

print(result)

这对服务拆分很有用。

例如你可以这样组织:

前端服务
    |
    v
业务后端
    |
    v
RemoteRunnable -> LangServe 服务
    |
    v
LLM / Tool / Retriever

在业务后端看来,远程服务仍然像 Runnable 一样:

remote_chain.invoke(...)
remote_chain.batch(...)
remote_chain.stream(...)

RemoteRunnable 让远端 LangServe 服务在调用体验上接近本地 Runnable。

第六步:打开 Playground

启动服务后,访问:

http://127.0.0.1:8000/qa/playground/

Playground 可以直接在浏览器里测试输入输出。

它适合:

  • 快速验证 Chain 是否能跑。
  • 给产品或测试同学试用。
  • 观察输入 schema。
  • 调试 streaming 输出。
  • 演示简单 Runnable。

但要注意:

Playground 不是生产管理后台。生产环境是否开放 Playground,要结合认证、网络隔离和数据安全决定。

如果服务里会处理敏感数据,不要把 Playground 暴露到公网。

Playground 很适合开发和演示,但生产环境要谨慎开放。

第七步:发布 Agent 服务

LangServe 不只可以发布 Chain,也可以发布 Agent。

例如一个带工具的 Agent:

from langchain.agents import create_agent
from langchain.tools import tool
from langserve import add_routes


@tool
def get_order_status(order_id: str) -> str:
    """根据订单号查询订单状态。"""
    data = {
        "A1001": "已发货,预计明天送达。",
        "A1002": "正在仓库拣货,预计今天 18:00 前发出。",
    }
    return data.get(order_id, "没有查到这个订单。")


agent = create_agent(
    model="openai:gpt-4o-mini",
    tools=[get_order_status],
    system_prompt=(
        "你是客服助手。"
        "用户问订单状态时必须调用工具,不要编造订单信息。"
    ),
    name="support_agent",
)


add_routes(
    app,
    agent,
    path="/support-agent",
)

调用时要传 Agent 需要的 messages

curl -X POST "http://127.0.0.1:8000/support-agent/invoke" \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "messages": [
        {
          "role": "user",
          "content": "帮我查一下订单 A1001 到哪了?"
        }
      ]
    }
  }'

不过这里要再次提醒:

对复杂 LangGraph / 长任务 / 多 Agent 系统,新项目更建议评估 LangGraph Platform 或自建更明确的 API 层。LangServe 更适合简单 Runnable 或已有 LangServe 项目。

Agent 也是 Runnable,但复杂 Agent 的生产部署不能只看“能不能 add_routes”。

第八步:配置 LangSmith tracing

现在服务能调用了,但还看不到内部执行过程。

开启 LangSmith tracing 的当前推荐环境变量是:

export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY="你的 LangSmith API Key"
export LANGSMITH_PROJECT="langserve-demo"

如果你的 LangSmith 账号不在默认 US 区域,还要设置:

export LANGSMITH_ENDPOINT="https://eu.api.smith.langchain.com"

Windows PowerShell 写法:

$env:LANGSMITH_TRACING="true"
$env:LANGSMITH_API_KEY="你的 LangSmith API Key"
$env:LANGSMITH_PROJECT="langserve-demo"

然后重新启动服务:

uvicorn server:app --host 127.0.0.1 --port 8000 --reload

再调用一次:

curl -X POST "http://127.0.0.1:8000/qa/invoke" \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "question": "为什么 LLM 应用需要 trace?"
    }
  }'

如果配置正确,你就能在 LangSmith 项目 langserve-demo 里看到这次调用的 trace。

补充说明:

  • 旧资料里常见 LANGCHAIN_TRACING_V2=true
  • 当前 LangSmith 文档推荐使用 LANGSMITH_TRACING=true
  • 如果你维护旧项目,可能会同时看到 LANGCHAIN_PROJECTLANGSMITH_PROJECT 两种写法。
  • 新项目建议使用 LANGSMITH_* 这一组命名。

LangChain / LangGraph 应用接入 LangSmith,通常只需要配置环境变量,不必改业务代码。

LangSmith Trace 里能看到什么?

一次 Chain 调用的 trace 通常是树状结构:

chain
  |
  +-- ChatPromptTemplate
  |
  +-- ChatOpenAI
  |
  +-- StrOutputParser

如果是 RAG:

rag_chain
  |
  +-- retriever
  |     |
  |     +-- vectorstore search
  |
  +-- prompt
  |
  +-- model
  |
  +-- parser

如果是 Agent:

agent
  |
  +-- model call
  |
  +-- tool call: get_order_status
  |
  +-- model call
  |
  +-- final response

每个节点都可能包含:

  • 输入。
  • 输出。
  • 开始时间。
  • 结束时间。
  • 耗时。
  • token 用量。
  • 模型参数。
  • 错误信息。
  • tags。
  • metadata。

这对排查问题非常关键。

例如用户反馈:

客服机器人把订单状态答错了。

你可以看 trace:

  1. 模型有没有调用 get_order_status
  2. 工具入参是不是正确订单号?
  3. 工具返回了什么?
  4. 模型有没有篡改工具结果?
  5. 最终输出和工具结果是否一致?

这比只看接口日志有用得多。

LangSmith trace 的价值,是把 LLM 应用从“只知道最终答案”变成“能看到每一步为什么这样回答”。

给 Trace 加 tags 和 metadata

只打开 tracing 还不够。

生产系统里,你通常还需要知道:

  • 这个请求来自哪个环境?
  • 哪个用户触发?
  • 哪个租户触发?
  • 哪个版本的 Prompt?
  • 哪个业务场景?
  • 是不是灰度流量?

可以通过 Runnable config 加 metadata 和 tags。

result = chain.invoke(
    {"question": "LangSmith 如何定位问题?"},
    config={
        "tags": ["demo", "langserve", "qa"],
        "metadata": {
            "env": "dev",
            "user_id": "user_123",
            "tenant_id": "tenant_a",
            "prompt_version": "qa-v1",
        },
    },
)

通过 HTTP 调用时,也可以在请求体里传 config

{
  "input": {
    "question": "LangSmith 如何定位问题?"
  },
  "config": {
    "tags": ["demo", "langserve", "qa"],
    "metadata": {
      "env": "dev",
      "user_id": "user_123",
      "tenant_id": "tenant_a",
      "prompt_version": "qa-v1"
    }
  }
}

这样 LangSmith 里就能按标签和元数据筛选 trace。

常见 tags:

  • prod
  • staging
  • rag
  • agent
  • customer-support
  • daily-report

常见 metadata:

  • user_id
  • tenant_id
  • request_id
  • prompt_version
  • model_name
  • feature_flag

tags 用来粗粒度分类,metadata 用来记录可查询的业务上下文。

手动埋点:用 @traceable 包住普通函数

并不是所有逻辑都天然在 LangChain Runnable 里。

例如你可能有一个普通函数:

def normalize_question(question: str) -> str:
    return question.strip().replace("\n", " ")

如果想让它也出现在 LangSmith trace 里,可以用 @traceable

from langsmith import traceable


@traceable(run_type="chain", name="normalize_question")
def normalize_question(question: str) -> str:
    return question.strip().replace("\n", " ")

也可以把工具函数标成 tool span:

from langsmith import traceable


@traceable(run_type="tool", name="query_order_database")
def query_order_database(order_id: str) -> str:
    return "订单已发货"

这样你的 trace 就不只包含模型调用,还能包含业务逻辑。

对于复杂应用,建议把这些步骤都显式埋点:

  • 输入清洗。
  • 意图识别。
  • 权限校验。
  • 检索。
  • 重排序。
  • 工具调用。
  • 输出后处理。

LangChain 对象会自动 trace,普通业务函数可以用 @traceable 手动纳入 trace。

流式输出:/stream/stream_log

LLM 应用经常需要流式输出。

LangServe 提供:

  • /stream:流式返回 Runnable 输出。
  • /stream_log:流式返回输出和中间步骤日志。
  • /astream_events 或相关事件流端点:用于消费更细粒度事件,具体取决于版本。

前端聊天场景通常关注 /stream

curl -X POST "http://127.0.0.1:8000/qa/stream" \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "question": "请用三段话解释 LangSmith trace。"
    }
  }'

调试场景更关注中间步骤:

curl -X POST "http://127.0.0.1:8000/qa/stream_log" \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "question": "请解释 RAG 的执行流程。"
    }
  }'

不过真实生产里,流式接口还要处理:

  • 客户端断开连接。
  • 超时。
  • 重试。
  • 网关 buffering。
  • SSE / WebSocket 选择。
  • 前端增量渲染。
  • trace 是否完整提交。

不要只在本地 curl 成功,就认为生产流式链路已经完成。

/stream 解决用户体验,/stream_log 更适合开发调试和中间步骤观察。

Chat Playground:让对话接口更像聊天窗口

如果你的 Runnable 输入是聊天消息,可以启用 chat playground。

示例:

from typing import List, Union
from pydantic import BaseModel, Field
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_openai import ChatOpenAI
from langserve import add_routes


class ChatInput(BaseModel):
    messages: List[Union[HumanMessage, AIMessage, SystemMessage]] = Field(
        ...,
        description="The chat messages representing the current conversation.",
    )


prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个企业知识库助手。"),
    MessagesPlaceholder(variable_name="messages"),
])

chat_chain = prompt | ChatOpenAI(model="gpt-4o-mini")

add_routes(
    app,
    chat_chain.with_types(input_type=ChatInput),
    path="/chat",
    playground_type="chat",
)

访问:

http://127.0.0.1:8000/chat/playground/

就能看到更接近聊天体验的 Playground。

注意输入结构:

{
  "messages": [
    {
      "type": "human",
      "content": "你好,帮我总结一下 LangServe。"
    }
  ]
}

不同版本对 message 序列化细节可能略有差异。

如果遇到 schema 报错,优先检查:

  • 输入是否是 dict
  • messages 是否是 list。
  • 每条 message 是否能被 Pydantic 正确解析。
  • Runnable 输出是否是 AIMessage 或 string。

chat playground 适合对话型 Runnable,但输入输出 schema 必须符合它的约束。

反馈按钮和公开 Trace 链接

LangServe 支持和 LangSmith 结合做反馈。

add_routes() 里可以打开:

add_routes(
    app,
    chat_chain.with_types(input_type=ChatInput),
    path="/chat",
    playground_type="chat",
    enable_feedback_endpoint=True,
    enable_public_trace_link_endpoint=True,
)

效果是:

  • 用户可以对回复点 thumbs up / thumbs down。
  • 可以生成公开 trace 链接,方便调试和分享。

但这里要非常谨慎:

公开 trace 可能暴露 Prompt、用户输入、工具返回、检索内容和中间状态。只建议在 demo 或测试环境使用。

生产系统建议:

  • 默认关闭 public trace。
  • 对 trace 做数据脱敏。
  • 用权限系统控制谁能看 trace。
  • 不在 trace 里记录密钥、token、身份证、手机号等敏感信息。

反馈端点有助于收集质量信号,公开 trace 只适合受控场景。

一个完整示例:客服 Agent + LangServe + LangSmith

下面组合一个更接近真实项目的例子。

文件结构:

support_app/
  server.py
  chains.py
  tools.py

tools.py

from langchain.tools import tool


@tool
def query_order_status(order_id: str) -> str:
    """根据订单号查询订单状态。用户问订单、物流、发货时使用。"""
    orders = {
        "A1001": "订单 A1001 已发货,物流单号 SF123456,预计明天送达。",
        "A1002": "订单 A1002 正在仓库拣货,预计今天 18:00 前发出。",
    }
    return orders.get(order_id, f"没有查到订单 {order_id}。")


@tool
def search_policy(query: str) -> str:
    """查询客服政策。用户问退款、售后、发票等规则时使用。"""
    policies = {
        "退款": "退款需在签收后 7 天内申请。",
        "发票": "电子发票会在订单完成后 24 小时内开具。",
    }
    return policies.get(query, "没有找到相关政策。")

chains.py

from langchain.agents import create_agent
from tools import query_order_status, search_policy


support_agent = create_agent(
    model="openai:gpt-4o-mini",
    tools=[query_order_status, search_policy],
    system_prompt=(
        "你是企业客服助手。"
        "用户询问订单、物流、退款、发票、售后时,优先调用工具。"
        "如果工具没有结果,明确说明没有查到,不要编造。"
        "回答要简洁、准确。"
    ),
    name="support_agent",
)

server.py

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from langserve import add_routes
from chains import support_agent


app = FastAPI(
    title="Support Agent API",
    version="1.0",
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
    expose_headers=["*"],
)


@app.get("/")
def health_check():
    return {"status": "ok"}


add_routes(
    app,
    support_agent,
    path="/support",
)

环境变量:

export OPENAI_API_KEY="你的 OpenAI API Key"
export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY="你的 LangSmith API Key"
export LANGSMITH_PROJECT="support-agent"

启动:

uvicorn server:app --host 127.0.0.1 --port 8000 --reload

调用:

curl -X POST "http://127.0.0.1:8000/support/invoke" \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "messages": [
        {
          "role": "user",
          "content": "帮我查一下订单 A1001 的物流状态"
        }
      ]
    },
    "config": {
      "tags": ["support", "demo"],
      "metadata": {
        "user_id": "user_123",
        "channel": "web"
      }
    }
  }'

在 LangSmith 里你应该能看到:

support_agent
  |
  +-- model call
  |
  +-- tool call: query_order_status
  |
  +-- model call
  |
  +-- final response

这样,当用户说“你查错订单了”,你可以直接看 trace 里的工具入参和工具返回。

接口服务负责对外调用,LangSmith trace 负责解释每一次调用内部到底发生了什么。

生产化注意事项一:认证和权限

LangServe 基于 FastAPI。

这意味着认证、权限、限流、审计最好通过 FastAPI、API Gateway 或公司统一网关来做。

不要把一个没有认证的 LLM API 暴露到公网。

至少要考虑:

  • API Key 或 JWT。
  • 用户身份。
  • 租户隔离。
  • 请求频率限制。
  • IP 白名单。
  • 操作审计。
  • 工具级权限控制。

简单 FastAPI 依赖示例:

from fastapi import Depends, Header, HTTPException


def verify_api_key(x_api_key: str = Header(...)):
    if x_api_key != "dev-secret":
        raise HTTPException(status_code=401, detail="Invalid API key")


add_routes(
    app,
    chain,
    path="/qa",
    dependencies=[Depends(verify_api_key)],
)

真实项目不要硬编码 key,应接入配置中心或密钥管理服务。

LangServe 负责 Runnable API 化,不等于自动解决生产权限。

生产化注意事项二:输入输出 Schema

LangServe 会基于 Runnable 推断 schema。

简单 Chain 通常没问题:

{"question": "..."}

但复杂输入建议显式定义类型。

from pydantic import BaseModel, Field


class QAInput(BaseModel):
    question: str = Field(description="用户问题")
    user_id: str = Field(description="用户 ID")


typed_chain = chain.with_types(input_type=QAInput)

add_routes(
    app,
    typed_chain,
    path="/qa",
)

这样有几个好处:

  • Swagger 文档更清楚。
  • Playground 更容易填写。
  • 请求错误更早暴露。
  • 前后端协作更稳定。

输出也可以定义:

class QAOutput(BaseModel):
    answer: str
    sources: list[str] = []

如果你的业务需要结构化结果,不要让模型自由输出一段自然语言再让后端正则解析。

生产接口最好显式定义输入输出 schema,减少调用方和模型输出的不确定性。

生产化注意事项三:CORS 和前端调用

如果前端浏览器直接调用 LangServe,需要配置 CORS。

from fastapi.middleware.cors import CORSMiddleware


app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://your-frontend.example.com"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
    expose_headers=["*"],
)

开发环境可以临时用:

allow_origins=["http://localhost:3000"]

不建议生产直接:

allow_origins=["*"]

尤其是带 cookie、token 或内部接口时。

CORS 是浏览器安全边界,不要为了省事在生产里无脑放开。

生产化注意事项四:错误处理和超时

LLM API 可能失败:

  • 网络超时。
  • 模型限流。
  • 工具报错。
  • 输入 schema 错误。
  • 输出解析失败。
  • Agent 循环超限。

建议:

  • 给模型配置 timeout。
  • 给外部工具配置 timeout。
  • 对可恢复错误做 retry。
  • 对不可恢复错误返回清晰错误码。
  • 不把内部堆栈直接暴露给前端。
  • 在 LangSmith trace 中保留足够调试信息。

模型示例:

model = ChatOpenAI(
    model="gpt-4o-mini",
    temperature=0,
    timeout=30,
    max_retries=2,
)

工具里也要有超时:

import requests


def call_external_api(url: str) -> dict:
    response = requests.get(url, timeout=10)
    response.raise_for_status()
    return response.json()

LLM 服务不是普通函数调用,超时、重试和错误边界必须显式设计。

生产化注意事项五:Trace 数据安全

LangSmith trace 很有用,但也可能记录敏感数据。

可能进入 trace 的内容包括:

  • 用户原始输入。
  • Prompt。
  • 检索到的文档片段。
  • 工具参数。
  • 工具返回。
  • 模型输出。
  • metadata。

因此要注意:

  • 不要把密钥写进 Prompt。
  • 不要把 access token 放进 metadata。
  • 对手机号、身份证、邮箱等做脱敏。
  • 对高敏业务设置数据保留策略。
  • 控制 LangSmith 项目访问权限。
  • 公开 trace 链接只用于 demo 或受控排查。

如果涉及强合规业务,要先和安全、法务、数据治理团队确认 trace 策略。

可观测性不能以泄露敏感数据为代价。

常见问题一:服务能跑,但 /docs 没有接口

如果你使用旧版本 LangServe 和 Pydantic v2,可能遇到 OpenAPI 文档生成问题。

LangServe README 里提到:

  • LangServe >= 0.3 支持 Pydantic 2。
  • LangServe <= 0.2.0 在 FastAPI + Pydantic v2 混用时,部分 OpenAPI docs 可能无法正常生成。

解决思路:

  • 优先升级到 langserve>=0.3.0
  • 或者在旧项目中固定 pydantic==1.10.17
  • 检查 FastAPI、Pydantic、LangServe 版本组合。

LangServe 老项目最常见兼容问题之一,就是 Pydantic 版本和 OpenAPI 文档生成。

常见问题二:只访问 /qa,忘了具体端点

很多新手启动服务后访问:

http://127.0.0.1:8000/qa

然后看到 404。

这是因为 LangServe 端点是:

/qa/invoke
/qa/batch
/qa/stream
/qa/playground/

不是 /qa 本身。

Swagger 文档在:

http://127.0.0.1:8000/docs

Playground 在:

http://127.0.0.1:8000/qa/playground/

LangServe 的 path 是端点前缀,不是单独的业务页面。

常见问题三:LangSmith 没有 trace

如果 LangSmith 里看不到 trace,按这个顺序查:

  1. 是否设置了 LANGSMITH_TRACING=true
  2. 是否设置了正确的 LANGSMITH_API_KEY
  3. 是否在同一个 shell 里启动服务。
  4. 是否设置了正确的 LANGSMITH_ENDPOINT
  5. 是否请求真的跑到了 LangChain / LangGraph 对象。
  6. 是否用了不支持自动 tracing 的第三方调用。
  7. 是否服务进程还没来得及提交 trace 就退出了。

如果是普通 OpenAI SDK 调用,不经过 LangChain,可以用:

from openai import OpenAI
from langsmith.wrappers import wrap_openai


client = wrap_openai(OpenAI())

如果是普通函数,可以用:

from langsmith import traceable


@traceable
def my_pipeline(input_text: str) -> str:
    return input_text.upper()

LangChain 对象通常能自动 trace,非 LangChain 代码要用 wrapper 或 @traceable

常见问题四:把 LangSmith 当日志系统替代品

LangSmith 适合观察 LLM 调用链路,但它不应该替代所有业务日志。

你仍然需要:

  • API access log。
  • 错误日志。
  • 审计日志。
  • 业务事件日志。
  • 监控指标。
  • 告警规则。

LangSmith 解决的是:

这次 LLM 应用内部怎么跑的?

传统日志和监控解决的是:

服务是否健康?
QPS 多少?
错误率多少?
接口延迟多少?
谁在什么时候做了什么操作?

二者应该配合,而不是互相替代。

LangSmith 是 LLM 可观测平台,不是完整后端监控系统的替代品。

一张图总结:LangServe + LangSmith 工作流

编写 Runnable / Chain / Agent

FastAPI app

add_routes 注册路由

启动 uvicorn 服务

调用 /invoke /batch /stream

LangChain 执行 Runnable

LangSmith 自动记录 Trace

查看输入 输出 耗时 Token 错误

定位 Prompt / Model / Tool / Retriever 问题

这条链路里,每一层职责都不同:

  • Runnable 负责业务逻辑。
  • FastAPI 负责 HTTP 服务。
  • LangServe 负责把 Runnable 映射成 API。
  • Uvicorn 负责运行 ASGI 服务。
  • LangSmith 负责 trace、调试和观测。
  • 认证、权限、限流、审计需要你结合后端体系补齐。

总结

本文讲了如何用 LangServe 和 LangSmith 把 LangChain 应用推向可调用、可调试的形态。

需要记住这些结论:

  • LangServe 可以用 add_routes() 把 Runnable / Chain / Agent 暴露成 REST API。
  • 常见端点包括 /invoke/batch/stream/stream_log/playground/ 和 schema 接口。
  • RemoteRunnable 可以像调用本地 Runnable 一样调用远程 LangServe 服务。
  • LangSmith tracing 当前推荐环境变量是 LANGSMITH_TRACING=trueLANGSMITH_API_KEYLANGSMITH_PROJECT
  • LangSmith trace 可以看到 Prompt、模型调用、工具调用、检索、输出解析等中间步骤。
  • tags 和 metadata 能帮助按环境、用户、租户、版本筛选 trace。
  • 普通业务函数可以用 @traceable 手动纳入 trace。
  • Playground 和 public trace link 适合开发演示,生产环境要谨慎开放。
  • LangServe 已 deprecated,复杂 Agent / LangGraph 新项目要优先评估 LangGraph Platform。
  • 部署 LLM 应用时,认证、权限、限流、错误处理、数据脱敏和监控都不能省。
Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐