跳到主要内容

文档索引

在以下地址获取完整的文档索引:https://docs.langchain.org.cn/llms.txt

在进一步探索之前,请使用此文件发现所有可用页面。

本指南涵盖了将 Deep Agent 从本地原型转变为生产部署的注意事项。它介绍了记忆范围划定、配置执行环境、添加护栏以及连接前端。

概览

智能体利用来自记忆及其执行环境的信息来完成任务。在生产环境中,有一些基本原语决定了信息的共享和访问方式:
  • 线程 (Thread):单次对话。消息历史记录和草稿文件默认作用域为该线程,且不会结转到其他线程。
  • 用户 (User):与您的智能体交互的人。记忆和文件可以对用户私有,也可以跨用户共享。身份识别和授权来自您的身份验证层
  • 助手 (Assistant):配置好的智能体实例。记忆和文件可以绑定到一个助手,也可以在所有助手之间共享。
本页涵盖:

LangSmith 部署

deepagents deploy 打包您的智能体配置——记忆、沙箱、技能、MCP 服务器——并将其发布到 LangSmith 部署中 将 Deep Agent 推向生产环境最快的方法是 deepagents deploy,只需一条命令即可打包您的智能体配置并将其作为 LangSmith 部署发布。或者,您也可以直接配置 LangSmith Deployment。无论哪种路径都会配置智能体所需的基础设施:助手 (assistants)线程 (threads)运行 (runs)、存储空间和检查点管理器,因此您无需自行设置。它还提供开箱即用的 身份验证Webhooks计划任务 (cron jobs)可观测性,并可通过 MCPA2A 公开您的智能体。 有关基于 CLI 的方法,请参阅 使用 CLI 部署。有关手动设置,请参阅 LangSmith 部署快速入门 本页上的所有代码片段除非另有说明,否则均使用以下 langgraph.json
langgraph.json
{
  "dependencies": ["."],
  "graphs": {
    "agent": "./agent.py:agent"
  },
  "env": ".env"
}
langgraph.json 是告知 LangGraph 平台如何构建和运行应用程序的配置文件。它位于项目根目录,是本地开发(使用 langgraph dev)和生产部署所必需的。关键字段包括:
字段描述
dependencies (依赖)要安装的包。["."] 将当前目录作为包安装(从 requirements.txtpyproject.tomlpackage.json 中读取)。
graphs (图)将图 ID 映射到其代码位置。每个条目为 "<id>": "./<file>:<variable>",其中 <id> 是您通过 API 调用图时使用的名称,而 <variable> 是从 <file> 导出的编译图或构造函数。
env包含环境变量(API 密钥、机密)的 .env 文件路径。这些在构建时设置并在运行时可用。
有关完整的配置选项(自定义 Docker 步骤、存储索引、身份验证处理程序等),请参阅 应用程序结构

生产环境注意事项

多租户

当您的智能体服务于多个用户时,您需要处理三个关注点:验证每个用户的身份、控制他们可以访问的内容,以及管理智能体代表他们操作时使用的凭据。 三个身份验证层构成:终端用户身份验证、智能体代行用户身份验证以及团队 RBAC

用户身份和访问控制

LangSmith 部署 支持 自定义身份验证 以确立用户身份,以及 授权处理程序 (authorization handlers) 以控制对线程、助手和存储命名空间等资源的访问。授权处理程序在身份验证成功后运行,可以:
  • 为资源标记所有权元数据(例如 owner: user_id
  • 返回过滤器,以便用户只能看到自己的资源
  • 对未授权操作返回 HTTP 403 拒绝访问
有关分步教程,请参阅 使对话私密化。有关演示,请观看 自定义身份验证视频 您如何 划定记忆作用域执行环境 决定了用户之间共享哪些数据。详情请参阅下文各节。

团队访问控制 (RBAC)

LangSmith 的 基于角色的访问控制 (RBAC) 管理团队中谁可以部署、配置和监控智能体。这与上述终端用户授权是分开的。
角色权限
工作空间管理员 (Workspace Admin)完整权限,包括设置和成员管理
工作空间编辑员 (Workspace Editor)创建和修改资源,但不能删除运行记录或管理成员
工作空间查看员 (Workspace Viewer)只读访问权限
企业版计划提供具有精细权限的自定义角色。有关完整权限模型,请参阅 RBAC 参考

终端用户凭据

当您的智能体需要代表用户调用外部 API(例如读取其 GitHub 仓库、发送 Slack 消息、查询其数据仓库)时,您需要一种方法将用户的凭据传递给智能体,而无需硬编码。 通过 Agent Auth 实现 OAuth。 Agent Auth 提供托管的 OAuth 2.0 流程。配置 OAuth 提供商后,智能体可以请求针对每个用户限定作用域的令牌。首次使用时,智能体通过 中断 (interrupts) 执行并呈现 OAuth 同意 URL。用户验证后,智能体凭有效令牌恢复执行。令牌会自动存储和刷新。
from langchain_auth import Client
from langchain.tools import tool, ToolRuntime

auth_client = Client()

# Inside your agent's tool:
@tool
async def github_action(runtime: ToolRuntime):
    """Perform an action on behalf of the user via GitHub."""
    auth_result = await auth_client.authenticate(
        provider="github",
        scopes=["repo", "read:org"],
        user_id=runtime.server_info.user.identity,
    )
    # Use auth_result.token for GitHub API calls on the user's behalf
沙箱凭据注入。 如果您的智能体在调用外部 API 的 沙箱 内运行代码,沙箱身份验证代理 可以自动将凭据注入到传出请求中,因此沙箱代码永远不会收到原始 API 密钥。有关设置详情,请参阅 管理密钥 工作空间机密 (Workspace secrets)。 对于所有用户共享的 API 密钥(例如您组织的 LLM 提供商密钥、搜索 API 密钥),请将它们作为 LangSmith 中的 工作空间机密 存储。有关详情,请参阅 管理密钥

异步

基于 LLM 的应用程序在很大程度上是 I/O 密集型的:调用语言模型、数据库和外部服务。异步编程允许这些操作并发运行而不是阻塞,从而提高吞吐量和响应速度。
LangChain 遵循在异步方法名前加 a 的约定(例如 ainvokeabefore_agentastream)。同步和异步变体存在于同一个类或命名空间中。
构建生产环境时:
  • 创建异步工具。 LangChain 在单独的线程中运行同步工具以避免阻塞,但原生异步可以完全避免线程开销。
  • 使用异步中间件方法。 自定义 中间件 应实现异步钩子(例如 abefore_agent 而不是 before_agent)。
  • 对外部资源生命周期使用异步。 创建 沙箱 或连接到 MCP 服务器 涉及网络调用,应使用 await 等待。这就是配置这些资源的 图工厂 (graph factories) 是异步的原因。

持久性

Deep Agents 运行在 LangGraph 上,它提供开箱即用的 持久化执行 (durable execution)持久性层在每一步都会对状态进行检查点记录,因此因故障、超时或 人机交互 暂停而中断的运行会从上次记录的状态恢复,而无需重新处理之前的步骤。对于产生许多子智能体的长时间运行的 Deep Agent 而言,这意味着运行中途失败不会丢失已完成的工作。 持久化执行:当一个工作节点在运行中崩溃时,另一个工作节点会从最新的检查点接管运行 检查点机制还支持:
  • 无限期 中断 (interrupts) 人机交互工作流可以暂停数分钟或数天,并从中断处准确恢复。
  • 时间旅行 (Time travel) 每个记录了检查点的步骤都是一个可以回退的快照,允许您在出现问题时从较早的状态重新运行。
  • 安全处理敏感操作。 对于涉及支付或其他不可逆操作的工作流,检查点提供了审计线索和恢复点,以便检查导致某项操作的确切状态。
LangSmith 部署 会自动配置持久化检查点管理器。如果您是自行托管,请参阅 持久性 以获取设置说明。

内存

如果没有记忆,每次对话都会从头开始。记忆允许您的智能体跨对话保留信息(用户偏好、学到的指令、过去的经验),以便随着时间的推移实现个性化行为。有关记忆类型的概述,请参阅 记忆概念指南 短期记忆通过检查点限定在单个线程中;长期记忆通过存储跨线程持久化

作用域划定

记忆在对话中始终是持久的。主要问题是如何在用户和助手边界之间划定作用域。正确的作用域取决于谁应该看到和修改数据:
范围命名空间用例示例
用户 (User)(推荐默认值)(user_id)针对每个用户的偏好和上下文“我更喜欢简练的回答”
助手 (Assistant)(assistant_id)针对一个助手的共享指令“帖子内容上限为 280 个字符”
全局(org_id)适用于所有用户和助手的只读策略“绝不披露内部定价”
共享记忆(助手、用户或组织范围)是提示注入 (prompt injection) 的一个媒介。如果一个用户可以向另一个用户的对话所读取的记忆中写入内容,恶意用户就可能向该共享状态注入指令。请在适当的地方强制执行只读访问。例如,使组织级策略只能通过应用程序代码写入,而不能由智能体本身写入。使用 权限 (permissions) 声明式地拒绝向共享路径写入,或使用 后端策略钩子 (backend policy hooks) 实现自定义验证逻辑。

配置

在 Deep Agents 中,记忆以文件的形式存储在虚拟文件系统中。默认情况下,文件仅在单次对话中持续。要持久化它们,请将 /memories/ 等路径路由到写入 LangGraph 存储StoreBackend。使用 CompositeBackend 为智能体同时提供临时草稿空间和持久化 长期记忆
下面显示的 rt.server_infort.execution_info 命名空间模式需要 deepagents>=0.5.0
您还可以使用 Store API 从应用程序代码中读取和写入存储空间。有关示例,请参阅 高级用法 有关完整的命名空间工厂 API,请参阅 命名空间工厂。有关自我改进指令和知识库等记忆模式,请参阅 长期记忆

执行环境

在本地,智能体可以读写磁盘上的文件并直接运行 Shell 命令。在生产环境中,您需要考虑隔离和持久性。正确的设置取决于您的智能体是否需要执行代码:
  • 如果您的智能体只需要读写文件,文件系统后端 (Filesystem backends) 就足够了。根据您的持久化需求选择后端:临时草稿空间、持久存储或两者的混合。
  • 沙箱 (Sandboxes) 添加了一个隔离的容器,并带有一个用于运行 Shell 命令的 execute 工具。如果您的智能体需要运行代码、安装包或执行文件 I/O 以外的任何操作,请使用沙箱。

文件系统

根据需要持久化的内容选择后端:
  • StateBackend(默认):临时草稿空间,作用域为单次对话。每一步都会记录检查点,因此请避免写入大文件。
  • StoreBackend:跨对话持续存在的持久化存储。使用 命名空间工厂 划定作用域。
  • CompositeBackend:混合使用。默认为临时草稿空间,对 /memories/ 等特定路径使用持久化路由。
有关后端完整列表及如何构建自定义后端,请参阅 后端
FilesystemBackendLocalShellBackend 会直接访问宿主机。请勿在部署的智能体中使用它们。

沙盒

如果您的智能体需要运行代码(不仅是读写文件),请使用 沙箱 (sandbox)。沙箱在一个隔离的容器内同时提供文件系统和用于运行 Shell 命令的 execute 工具。这种隔离还保护了您的宿主机:如果智能体的代码耗尽内存或崩溃,只有沙箱会受到影响,您的服务器将继续运行。

生命周期

关键决策是沙箱的存续时间。是每次对话获得一个全新的沙箱,还是对话共享一个持久化环境?
范围沙箱 ID 存储在生命周期示例用例
限定在线程作用域线程 元数据每次对话全新,到期 (TTL) 后清理每次对话都从头开始的数据分析机器人
助手作用域 (Assistant-scoped)助手 配置跨所有对话共享在对话之间维持已克隆仓库的代码助手
以下示例使用异步 图工厂 而不是静态图,因为沙箱需要 thread_idassistant_id 来查找或创建正确的沙箱。图工厂不会收到完整的 Runtime(没有 server_infoexecution_info);相反,它接受 RunnableConfig 并从 config["configurable"] 中读取 thread_idassistant_id。该工厂是异步的,因为沙箱创建是 I/O 密集型操作,需要仅在调用时才可用的单次运行信息。
每个对话都有自己的沙箱。图工厂 从运行配置中读取 thread_id,因此每个 线程 都会自动获得自己的隔离环境。提供商基于标签的查找会处理跨运行的去重。当沙箱 TTL (生存时间) 到期时会被清理。
agent.py
from daytona import CreateSandboxFromSnapshotParams, Daytona
from deepagents import create_deep_agent
from langchain_core.runnables import RunnableConfig
from langchain_daytona import DaytonaSandbox

client = Daytona()


async def agent(config: RunnableConfig):
    thread_id = config["configurable"]["thread_id"]
    try:
        sandbox = await client.find_one(labels={"thread_id": thread_id})
    except Exception:
        sandbox = await client.create(
            CreateSandboxFromSnapshotParams(
                labels={"thread_id": thread_id},
                auto_delete_interval=3600,  # TTL: clean up when idle
            )
        )
    return create_deep_agent(model="google_genai:gemini-3.1-pro-preview", backend=DaytonaSandbox(sandbox=sandbox))
因为 agent 变量是一个异步函数(而不是编译后的图),服务器会将其视为 图工厂,并在每次运行时调用它并注入配置。工厂通过提供商基于标签的搜索查找或创建沙箱,并返回一个连接到该沙箱的新智能体图。 一旦使用 langgraph deploy 部署,即可在您的应用程序代码中使用 SDK 调用智能体。无论作用域如何,客户端代码都是相同的。作用域完全在上述智能体工厂中处理,但行为有所不同:
每个线程获得自己的沙箱。同一线程内的后续消息重用同一沙箱,但新线程始终从头开始,没有先前对话遗留的文件或安装包。
client.py
from langgraph_sdk import get_client

client = get_client(url="<DEPLOYMENT_URL>", api_key="<LANGSMITH_API_KEY>")

# Conversation 1: install pandas and analyze data
thread_1 = await client.threads.create()
async for chunk in client.runs.stream(
    thread_1["thread_id"],
    "agent",
    input={"messages": [{"role": "human", "content": "Install pandas and analyze sales_data.csv"}]},
    stream_mode="updates",
):
    print(chunk.data)

# Follow-up in the same conversation — pandas is still installed
async for chunk in client.runs.stream(
    thread_1["thread_id"],
    "agent",
    input={"messages": [{"role": "human", "content": "Now plot the results"}]},
    stream_mode="updates",
):
    print(chunk.data)

# Conversation 2: fresh sandbox — pandas is NOT installed, no files from conversation 1
thread_2 = await client.threads.create()
async for chunk in client.runs.stream(
    thread_2["thread_id"],
    "agent",
    input={"messages": [{"role": "human", "content": "What packages are installed?"}]},
    stream_mode="updates",
):
    print(chunk.data)

文件传输

沙箱是隔离的容器,因此您的应用程序代码无法直接访问其中的文件。使用 upload_files()download_files() 在沙箱边界移动数据:
  • 在智能体运行前初始化沙箱:上传用户文件、技能 脚本、配置或 持久化记忆,以便智能体从一开始就拥有所需的一切
  • 在智能体完成后检索结果:下载生成的产物(报告、绘图、导出的文件)并同步更新后的记忆,以供未来的对话使用
有关特定提供商的文件传输示例,请参阅 使用文件。有关提供商设置、安全性及生命周期模式,请参阅完整的 沙箱指南
智能体需要执行的 技能 脚本必须在智能体运行前上传到沙箱中。您可能还希望同步 记忆,以便智能体在容器内读取和更新它们。使用带有 before_agentafter_agent 钩子的 自定义中间件 跨沙箱边界移动文件:
agent.py
from deepagents import create_deep_agent
from langchain.agents.middleware import AgentMiddleware, AgentState
from langgraph.runtime import Runtime


def _safe_filename(key: str) -> str:
    """Reject keys that contain path traversal or glob characters."""
    name = key.split("/")[-1]
    if ".." in name or any(c in name for c in ("*", "?")):
        raise ValueError(f"Invalid key: {key}")
    return name


class SandboxSyncMiddleware(AgentMiddleware):
    """Sync skills and memories between the store and the sandbox."""

    def __init__(self, backend: CompositeBackend):
        super().__init__()
        self.backend = backend

    async def abefore_agent(self, state: AgentState, runtime: Runtime) -> None:
        """Upload skill scripts and memories into the sandbox."""
        user_id = runtime.server_info.user.identity  
        store = runtime.store
        files = []
        for item in await store.asearch(("skills", user_id)):
            name = _safe_filename(item.key)
            files.append((f"/skills/{name}", item.value["content"].encode()))
        for item in await store.asearch(("memories", user_id)):
            name = _safe_filename(item.key)
            files.append((f"/memories/{name}", item.value["content"].encode()))
        if files:
            await self.backend.upload_files(files)

    async def aafter_agent(self, state: AgentState, runtime: Runtime) -> None:
        """Sync updated memories back to the store."""
        user_id = runtime.server_info.user.identity  
        store = runtime.store
        items = await store.asearch(("memories", user_id))
        results = await self.backend.download_files(
            [f"/memories/{item.key}" for item in items]
        )
        for result in results:
            if result.content is not None:
                await store.aput(
                    ("memories", user_id),
                    result.path.split("/")[-1],
                    {"content": result.content.decode()},
                )


backend = CompositeBackend(
    default=DaytonaSandbox(sandbox=sandbox),
    routes={
        "/skills/": StoreBackend(
            rt,
            namespace=lambda rt: ("skills", rt.server_info.user.identity),
        ),
        "/memories/": StoreBackend(
            rt,
            namespace=lambda rt: ("memories", rt.server_info.user.identity),
        ),
    },
)

agent = create_deep_agent(
    model="google_genai:gemini-3.1-pro-preview",
    backend=backend,
    middleware=[SandboxSyncMiddleware(backend)],
)

管理密钥 (Secrets)

沙箱是隔离的容器,因此宿主机的环境变量在沙箱内不可用。有两种方法可以向沙箱代码提供 API 密钥和其他机密信息: 身份验证代理 (推荐)。 沙箱身份验证代理 会拦截来自沙箱的传出请求并自动注入身份验证标头。沙箱代码正常调用外部 API,代理根据目标主机添加正确的凭据。这意味着 API 密钥永远不会出现在沙箱代码、环境变量或日志中。 沙箱身份验证代理将凭据注入到传出请求中,因此机密信息永远不会进入沙箱
{
  "proxy_config": {
    "rules": [
      {
        "name": "openai-api",
        "match_hosts": ["api.openai.com"],
        "inject_headers": {
          "Authorization": "Bearer ${OPENAI_API_KEY}"
        }
      },
      {
        "name": "anthropic-api",
        "match_hosts": ["api.anthropic.com"],
        "inject_headers": {
          "x-api-key": "${ANTHROPIC_API_KEY}"
        }
      }
    ]
  }
}
${SECRET_KEY} 引用解析为您在 LangSmith 工作空间设置 中存储的机密。在创建引用机密的模板之前,请先在那里配置好机密。 工作空间机密 (Workspace secrets)。 对于不需要通过代理注入的 API 密钥(例如智能体服务器本身而非沙箱代码使用的密钥),请将其作为 LangSmith 中的 工作空间机密 存储。这些密钥在运行时作为环境变量提供给工作空间中的所有智能体。
避免通过环境变量或文件上传将机密传递给沙箱。智能体可以读取沙箱内任何可访问的文件或环境变量,包括凭据。身份验证代理则将机密完全排除在沙箱之外。

防护措施

智能体在生产环境中自主运行,这意味着它们可能会无限循环、达到速率限制或处理包含敏感信息的用户数据。Deep Agents 提供了两层保护:
  • 权限 (Permissions):控制智能体可以读取或写入哪些文件和目录的声明式允许/拒绝规则。使用权限将智能体隔离在工作目录中,保护敏感文件,或强制执行只读记忆。
  • 中间件 (Middleware):包装模型和工具调用的钩子,用于速率限制、错误处理和数据隐私。
Middleware hooks—before_model, wrap_model_call, wrap_tool_call, after_model—wrap the agent loop so policies run deterministically around every relevant step

速率限制

这里的速率限制是指在一次运行中限制智能体自身的 LLM 和工具使用量,而不是针对传入请求的 API 网关速率限制。 如果不加限制,一个陷入混乱的智能体可能会因循环执行同一个工具调用或进行数百次模型调用,在几分钟内耗尽您的 LLM API 预算。请为单次运行的模型调用和工具执行次数设置上限:
from deepagents import create_deep_agent
from langchain.agents.middleware import ModelCallLimitMiddleware, ToolCallLimitMiddleware

agent = create_deep_agent(
    model="google_genai:gemini-3.1-pro-preview",
    middleware=[
        ModelCallLimitMiddleware(run_limit=50),
        ToolCallLimitMiddleware(run_limit=200),
    ],
)
使用 run_limit 来限制单次调用的模型调用次数(每轮重置)。使用 thread_limit 来限制整个对话的模型调用次数(需要检查点管理器)。有关完整配置,请参阅 ModelCallLimitMiddlewareToolCallLimitMiddleware

处理错误

并非所有错误都应以相同方式处理。暂时性故障(网络超时、速率限制)应自动重试。LLM 可以恢复的错误(错误的工具输出、解析失败)应反馈给模型。需要人工干预的错误应暂停智能体。有关包含代码示例的完整细分,请参阅 恰当处理错误 中间件负责处理暂时性故障。模型调用和工具调用各自都有带指数退避的重试中间件。如果您的主模型提供商完全宕机,备用中间件会切换到备选方案:
from deepagents import create_deep_agent
from langchain.agents.middleware import (
    ModelFallbackMiddleware,
    ModelRetryMiddleware,
    ToolRetryMiddleware,
)

agent = create_deep_agent(
    model="google_genai:gemini-3.1-pro-preview",
    middleware=[
        # Retry model calls on rate limits, timeouts, and 5xx errors
        ModelRetryMiddleware(max_retries=3, backoff_factor=2.0, initial_delay=1.0),
        # If the primary model is fully down, fall back to an alternative
        ModelFallbackMiddleware("gpt-5.4"),
        # Retry specific tools that hit external APIs (not all tools)
        ToolRetryMiddleware(
            max_retries=2,
            tools=["search", "fetch_url"],
            retry_on=(TimeoutError, ConnectionError),
        ),
    ],
)
ToolRetryMiddleware 的作用域限定为特定工具,而不是重试一切。失败的文件系统 read_file 重试通常无益,但超时的网页搜索重试可能会成功。有关完整配置,请参阅 ModelRetryMiddlewareModelFallbackMiddleware

数据隐私

如果您的智能体处理的用户输入可能包含电子邮件、信用卡号或其他个人身份信息 (PII),您可以在其到达模型或存储在日志中之前检测并处理它。
from deepagents import create_deep_agent
from langchain.agents.middleware import PIIMiddleware

agent = create_deep_agent(
    model="google_genai:gemini-3.1-pro-preview",
    middleware=[
        PIIMiddleware("email", strategy="redact", apply_to_input=True),
        PIIMiddleware("credit_card", strategy="mask", apply_to_input=True),
    ],
)
策略包括 redact(替换为 [REDACTED_EMAIL])、mask(部分遮盖如 ****-****-****-1234)、hash(确定性哈希)和 block(抛出错误)。您还可以针对特定领域的模式编写自定义检测器。有关完整配置,请参阅 PIIMiddleware 有关可用中间件的完整列表,请参阅 预建中间件

前端

Deep Agents 使用 useStream 将您的 UI 连接到智能体后端。useStream 是一个前端钩子(适用于 React、Vue、Svelte 和 Angular),可实时从智能体传输消息、子智能体进度和自定义状态。 在本地,useStream 指向 https://:2024。在生产环境中,请将其指向您的 LangSmith 部署 并配置重连,以免用户在连接断开时丢失进度。
import { useStream } from "@langchain/react";

function App() {
  const stream = useStream<typeof agent>({
    apiUrl: "https://your-deployment.langsmith.dev",
    assistantId: "agent",
    reconnectOnMount: true,    // Resume stream after page refresh or navigation
    fetchStateHistory: true,   // Load full thread history on mount
  });
}
reconnectOnMount 会自动接管正在进行的运行。如果用户在智能体工作时刷新页面,他们将看到工作继续进行,而不是白屏。fetchStateHistory 加载该线程的完整对话历史记录,以便返回的用户能看到之前的消息。 对于产生许多子智能体的 Deep Agent 工作流,提交时请设置较高的 recursionLimit,以避免切断长时间运行的执行:
stream.submit(
  { messages: [{ type: "human", content: text }] },
  {
    streamSubgraphs: true,
    config: { recursionLimit: 10000 },
  },
);
有关 Deep Agent 特有的 UI 模式(如子智能体卡片、待办事项列表和自定义状态渲染),请参阅 前端指南
© . This site is unofficial and not affiliated with LangChain, Inc.