LangChain 上线部署:把链和 Agent 变成能对外服务的 API

个人主页: > for_ever_love__ <(欢迎各位大佬莅临😊)
其他栏目: > 大模型开发从0到1 <
其他栏目: > iOS项目总结大全 <
其他栏目: > 我想学python了 <
其他栏目: > iOS UI <

文章目录

  • [LangChain 上线部署:把链和 Agent 变成能对外服务的 API](#LangChain 上线部署:把链和 Agent 变成能对外服务的 API)
    • 一、两条路线怎么选
    • 二、LangServe:五分钟出一个服务
      • [2.1 调用](#2.1 调用)
      • [2.2 Playground 很实用](#2.2 Playground 很实用)
      • [2.3 部署带记忆的 Agent](#2.3 部署带记忆的 Agent)
    • [三、FastAPI 自己包:完全可控](#三、FastAPI 自己包:完全可控)
      • [3.1 流式输出(SSE)](#3.1 流式输出(SSE))
      • [3.2 并发与超时](#3.2 并发与超时)
    • 四、上线必做的六件事
      • [1. 密钥管理](#1. 密钥管理)
      • [2. 别把内部异常抛给用户](#2. 别把内部异常抛给用户)
      • [3. 请求体要校验](#3. 请求体要校验)
      • [4. 限流](#4. 限流)
      • [5. 成本监控](#5. 成本监控)
      • [6. 日志要带 trace_id](#6. 日志要带 trace_id)
    • 五、部署形态
    • 六、小结

LangChain 上线部署:把链和 Agent 变成能对外服务的 API

链在本地跑通只是第一步。要给别人用,得变成 HTTP 服务,

还得处理流式、并发、超时、日志这几件事。

这篇讲两条路线:LangServe(官方,快)和 FastAPI 自己包(可控) ,

以及上线前必须做的检查项。

一、两条路线怎么选

LangServe 自己用 FastAPI 包
上手速度 几分钟 半天
自动生成的接口 invoke / stream / batch / Playground 全自己写
流式支持 自带 SSE 自己实现
自定义中间件/鉴权 有限 完全自由
和企业现有框架整合 一般 好
适合 原型、内部工具、快速验证 生产、有既有后端体系

我的建议:原型阶段用 LangServe 快速验证,
真正上线时如果团队已有 Python 后端,用 FastAPI 自己包更容易和现有体系对齐。

二、LangServe:五分钟出一个服务

bash 复制代码
pip install "langserve[all]" fastapi uvicorn
python 复制代码
# server.py
from fastapi import FastAPI
from langserve import add_routes
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

app = FastAPI(title="我的 LLM 服务", version="1.0")

chain = (
    ChatPromptTemplate.from_template("用一句话解释:{topic}")
    | ChatOpenAI(model="gpt-4o-mini")
    | StrOutputParser()
)

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

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

启动后自动获得这些接口:

接口 作用
POST /explain/invoke 一次性调用
POST /explain/stream SSE 流式
POST /explain/batch 批量
POST /explain/stream_log 流式返回中间步骤
GET /explain/playground 可视化调试页面
GET /docs OpenAPI 文档

2.1 调用

python 复制代码
from langserve import RemoteRunnable

remote = RemoteRunnable("http://localhost:8000/explain")
print(remote.invoke({"topic": "向量数据库"}))

for chunk in remote.stream({"topic": "向量数据库"}):
    print(chunk, end="", flush=True)

或者直接用 curl:

bash 复制代码
curl -X POST http://localhost:8000/explain/invoke \
  -H "Content-Type: application/json" \
  -d '{"input": {"topic": "向量数据库"}}'

2.2 Playground 很实用

浏览器打开 http://localhost:8000/explain/playground,

有个现成的调试界面,能直接看到流式输出和中间步骤。

给产品、测试同学演示时特别省事,不用自己写前端。

2.3 部署带记忆的 Agent

python 复制代码
from langserve import add_routes
from langgraph.checkpoint.sqlite import SqliteSaver
from langgraph.prebuilt import create_react_agent

saver = SqliteSaver.from_conn_string("checkpoints.db")
agent = create_react_agent(llm, tools, checkpointer=saver)

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

调用时带上 thread_id:

python 复制代码
remote = RemoteRunnable("http://localhost:8000/agent")
remote.invoke(
    {"messages": [("user", "我叫小明")]},
    config={"configurable": {"thread_id": "u-1"}},
)

三、FastAPI 自己包:完全可控

生产环境我更推荐这条路线。核心是三件事:请求体、流式、配置透传。

python 复制代码
from fastapi import FastAPI, HTTPException
from fastapi.responses import StreamingResponse
from pydantic import BaseModel, Field
import json

app = FastAPI()

class AskRequest(BaseModel):
    question: str = Field(min_length=1, max_length=2000)
    session_id: str = Field(default="default")
    stream: bool = Field(default=False)

@app.post("/ask")
async def ask(req: AskRequest):
    config = {"configurable": {"thread_id": req.session_id},
              "recursion_limit": 12}
    try:
        result = await chain.ainvoke({"question": req.question}, config=config)
        return {"answer": result}
    except Exception as e:
        # 别把内部异常直接抛给用户
        logger.exception("ask failed")
        raise HTTPException(status_code=500, detail="服务暂时不可用")

3.1 流式输出(SSE)

这是用户体验的关键。两种粒度:

按 token 流(适合纯文本生成):

python 复制代码
async def token_stream(question: str, config: dict):
    async for chunk in chain.astream({"question": question}, config=config):
        # chunk 是字符串片段(链最后接了 StrOutputParser)
        yield f"data: {json.dumps({'content': chunk}, ensure_ascii=False)}\n\n"
    yield "data: [DONE]\n\n"

@app.post("/ask/stream")
async def ask_stream(req: AskRequest):
    return StreamingResponse(
        token_stream(req.question, config),
        media_type="text/event-stream",
        headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
    )

按步骤流(适合 Agent,让用户看到"在查什么"):

python 复制代码
async def step_stream(question: str, config: dict):
    async for event in agent.astream_events(
        {"messages": [("user", question)]}, config=config, version="v2"
    ):
        kind = event["event"]
        if kind == "on_tool_start":
            yield f"data: {json.dumps({'type': 'tool', 'name': event['name'], 'input': event['data'].get('input')}, ensure_ascii=False)}\n\n"
        elif kind == "on_chat_model_stream":
            delta = event["data"]["chunk"].content
            if delta:
                yield f"data: {json.dumps({'type': 'token', 'content': delta}, ensure_ascii=False)}\n\n"
    yield "data: [DONE]\n\n"

X-Accel-Buffering: no 这个 header 很重要------

Nginx 默认会缓冲响应,不加这个 header 流式就变成了"等全部完成再返回"。

3.2 并发与超时

LLM 调用是 IO 密集,务必用 async:

python 复制代码
# ✅ 异步,多个请求真正并发
result = await chain.ainvoke(inputs)

# ❌ 同步会阻塞事件循环,一个慢请求拖垮所有
result = chain.invoke(inputs)

超时要设两层:

python 复制代码
import asyncio

async def with_timeout(coro, seconds=60):
    try:
        return await asyncio.wait_for(coro, timeout=seconds)
    except asyncio.TimeoutError:
        return "抱歉,处理超时,请稍后再试"

answer = await with_timeout(chain.ainvoke(inputs), 60)

外层还要在网关(Nginx / K8s ingress)设超时,

比应用层稍大一些,避免网关先断导致连接悬挂。

四、上线必做的六件事

1. 密钥管理

python 复制代码
# ❌ 硬编码
os.environ["OPENAI_API_KEY"] = "sk-xxx"

# ✅ 从环境变量 / 密钥服务读
import os
api_key = os.environ["OPENAI_API_KEY"]   # 缺失就报错,别用默认值

永远不要把 key 写进代码或镜像。

2. 别把内部异常抛给用户

python 复制代码
except Exception as e:
    logger.exception("chain failed")            # 详细堆栈进日志
    raise HTTPException(500, "服务暂时不可用")   # 用户只看到这句

堆栈里有文件路径、内部接口,暴露出去是安全问题。

3. 请求体要校验

python 复制代码
class AskRequest(BaseModel):
    question: str = Field(min_length=1, max_length=2000)

不设上限的话,用户传 10 万字进来,token 费用会教你做人。

4. 限流

按用户限流,防止单人刷爆配额:

python 复制代码
from slowapi import Limiter
from slowapi.util import get_remote_address

limiter = Limiter(key_func=get_remote_address)

@app.post("/ask")
@limiter.limit("20/minute")
async def ask(req: AskRequest): ...

5. 成本监控

每次调用记录 token 消耗:

python 复制代码
from langchain_core.callbacks import BaseCallbackHandler

class CostTracker(BaseCallbackHandler):
    def on_llm_end(self, response, **kwargs):
        usage = response.llm_output.get("token_usage", {})
        metrics.record(
            prompt=usage.get("prompt_tokens", 0),
            completion=usage.get("completion_tokens", 0),
        )
        logger.info("token_usage", extra=usage)

上线不看成本,月底账单会很难看。

6. 日志要带 trace_id

python 复制代码
import uuid
trace_id = str(uuid.uuid4())
config = {"configurable": {"thread_id": session_id},
          "metadata": {"trace_id": trace_id}}

用户投诉"刚才答错了"时,有没有 trace_id 决定了你排查要花 5 分钟还是 5 小时。

五、部署形态

dockerfile 复制代码
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]

几个注意点:

  • --workers 数要小于 CPU 核数的 2 倍 ,
    LLM 调用是 IO 密集,但 Pydantic 校验和序列化是 CPU 活;
  • 如果用 InMemorySaver,多 worker 之间状态不共享 ,
    必须换 Sqlite/Postgres,否则用户请求打到不同 worker 会"失忆";
  • 健康检查接口要单独加(/health),别让探针去打 LLM。

六、小结

  • LangServe 适合快速验证 :一行 add_routes 得到 invoke/stream/batch + Playground;
  • FastAPI 自己包适合生产:可控性强,容易和现有后端体系对齐;
  • 流式用 SSE,记得加 X-Accel-Buffering: no,否则 Nginx 会缓冲掉流式效果;
  • 一律用 ainvoke / astream,同步调用会阻塞事件循环;
  • 上线六件事:密钥入环境变量、异常不外抛、请求体限长度、按用户限流、
    记录 token 成本、日志带 trace_id
    ;
  • 多 worker 部署时 InMemorySaver 会失效,必须换持久化 checkpointer。

下一篇是本系列最后一篇:效果评测与回归测试。

相关推荐
染指11102 小时前
145.Agent-LangGraph基础概念和安装和简单代码示例
java·服务器·数据库·langchain·agents
一木 之林3 小时前
LangChain 第6章 Agent 智能体核心:create_agent 统一入口与 ReAct 循环
前端·react.js·langchain
十二同学啊3 小时前
Python FastAPI 项目使用 Docker 部署并映射端口(完整教程)
python·docker·fastapi
小王师傅663 小时前
LangChain 系统化学习入门
学习·langchain
小马不起床4 小时前
零基础玩转 FastAPI_12:前端详情增强与角色权限分配
前端·状态模式·fastapi
for_ever_love__4 小时前
LangChain 多工具协作:一个 Agent 不够用时怎么办
人工智能·langchain·langgraph·协作·多agent·多工具·工具编排
for_ever_love__4 小时前
LangChain 效果评测:没有评测,优化就是在瞎调
人工智能·学习·langchain·rag·评估·langsmith·评测指标
般逗4 小时前
FastAPI 难点解惑 | Python语言的后端框架
fastapi
BD_Marathon5 小时前
ChatPromptTemplate初始化的六种参数类型
langchain