> ## Documentation Index
> Fetch the complete documentation index at: https://langchain-zh.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 投入生产

> 利用持久化内存、沙盒、弹性中间件和部署选项，将您的深度智能体投入生产环境

本指南介绍了将深度智能体从本地原型迁移到生产部署时需要考虑的事项。它将逐步讲解如何界定内存范围、配置执行环境、添加防护措施以及连接前端。

## 概述

智能体利用来自内存及其执行环境的信息来完成任务。
在生产环境中，有几个基本要素决定了信息如何共享和访问：

* **线程**：单个对话。默认情况下，消息历史和临时文件的作用域限定在线程内，不会延续。
* **用户**：与您的智能体交互的人。内存和文件可以是用户私有的，也可以在用户之间共享。身份和授权来自您的[身份验证层](/langsmith/auth)。
* **助手**：一个已配置的智能体实例。内存和文件可以绑定到单个助手，或在所有助手之间共享。

本页涵盖：

* **[LangSmith 部署](#langsmith-部署)**：具有身份验证、Webhook 和定时任务的托管基础设施
* **[生产注意事项](#生产注意事项)**：多租户、身份验证、凭证、异步和持久性
* **[内存](#内存)**：跨对话持久化信息
* **[执行环境](#执行环境)**：文件存储和代码执行
* **[防护措施](#防护措施)**：速率限制、错误处理和數據隱私
* **[前端](#前端)**：将您的 UI 连接到已部署的智能体

## LangSmith 部署

将深度智能体投入生产的最快方式是使用 [LangSmith 部署](/langsmith/deployment)。它提供了您的智能体所需的基础设施：[助手](/langsmith/assistants)、[线程](/langsmith/use-threads)、[运行](/langsmith/runs)、存储和检查点器，因此您无需自行设置这些。它还开箱即用地为您提供[身份验证](/langsmith/auth)、[Webhook](/langsmith/use-webhooks)、[定时任务](/langsmith/cron-jobs)和[可观测性](/langsmith/observability)，并可以通过 [MCP](/langsmith/server-mcp) 或 [A2A](/langsmith/server-a2a) 暴露您的智能体。

有关设置说明，请参阅 [LangSmith 部署快速入门](/langsmith/deployment-quickstart)。

本页上的所有代码片段默认使用以下 `langgraph.json`，除非另有说明：

```json langgraph.json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
{
  "dependencies": ["."],
  "graphs": {
    "agent": "./src/agent.ts:agent"
  },
  "env": ".env"
}
```

`langgraph.json` 是告诉 LangGraph 平台如何构建和运行您的应用程序的配置文件。它位于项目的根目录，是本地开发（使用 `langgraph dev`）和生产部署所必需的。关键字段如下：

| 字段             | 描述                                                                                                                        |
| -------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `dependencies` | 要安装的包。`["."]` 将当前目录作为包安装（读取 `requirements.txt`、`pyproject.toml` 或 `package.json`）。                                        |
| `graphs`       | 将图 ID 映射到其代码位置。每个条目格式为 `"<id>": "./<file>:<variable>"`，其中 `<id>` 是您通过 API 调用图时使用的名称，`<variable>` 是从 `<file>` 导出的编译图或构造函数。 |
| `env`          | 指向包含环境变量（API 密钥、密钥）的 `.env` 文件的路径。这些在构建时设置，并在运行时可用。                                                                       |

有关完整的配置选项（自定义 Docker 步骤、存储索引、身份验证处理程序等），请参阅[应用程序结构](/oss/javascript/langgraph/application-structure)。

## 生产注意事项

### 多租户

当您的智能体为多个用户服务时，您需要处理三个问题：验证每个用户的身份、控制他们可以访问的内容，以及管理智能体代表他们行动时使用的凭证。

#### 用户身份和访问控制

[LangSmith 部署](/langsmith/deployment)支持[自定义身份验证](/langsmith/custom-auth)来建立用户身份，并支持[授权处理程序](/langsmith/auth)来控制对线程、助手和存储命名空间等资源的访问。授权处理程序在身份验证成功后运行，可以：

* 使用所有权元数据标记资源（例如，`owner: user_id`）
* 返回过滤器，以便用户只能看到自己的资源
* 对未经授权的操作拒绝访问并返回 HTTP 403

有关逐步教程，请参阅[使对话私有化](/langsmith/resource-auth)。

您如何[界定内存](#界定)和[执行环境](#执行环境)决定了哪些数据在用户之间共享。详情请参阅以下部分。

#### 团队访问控制（RBAC）

LangSmith 的[基于角色的访问控制](/langsmith/rbac)管理您团队中谁可以部署、配置和监控智能体。这与上述最终用户授权是分开的。

| 角色     | 访问权限                   |
| ------ | ---------------------- |
| 工作区管理员 | 完全权限，包括设置和成员管理         |
| 工作区编辑者 | 创建和修改资源，但不能删除运行记录或管理成员 |
| 工作区查看者 | 只读访问权限                 |

企业版计划提供具有细粒度权限的自定义角色。有关完整的权限模型，请参阅 [RBAC 参考](/langsmith/rbac)。

#### 最终用户凭证

当您的智能体需要代表用户调用外部 API 时（例如，读取他们的 GitHub 仓库、发送 Slack 消息、查询他们的数据仓库），您需要一种方法将用户的凭证传递给智能体，而无需硬编码。

**通过 Agent Auth 使用 OAuth。** [Agent Auth](/langsmith/agent-auth) 提供了一个托管式 OAuth 2.0 流程。配置一个 OAuth 提供商，智能体可以请求限定给每个用户的作用域令牌。首次使用时，智能体会[中断](/oss/javascript/langgraph/interrupts)执行并提供一个 OAuth 同意 URL。用户身份验证后，智能体将使用有效令牌恢复执行。令牌会自动存储和刷新。

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { Client } from "@langchain/auth";

const authClient = new Client();

// 在智能体的工具内部：
const authResult = await authClient.authenticate({
  provider: "github",
  scopes: ["repo", "read:org"],
  userId: config.configurable.langgraph_auth_user_id,
});
// 使用 authResult.token 代表用户调用 GitHub API
```

**沙盒的凭证注入。** 如果您的智能体在调用外部 API 的[沙盒](#沙盒)内运行代码，[沙盒身份验证代理](/langsmith/sandbox-auth-proxy)可以自动将凭证注入出站请求，这样沙盒代码就永远不会接收到原始 API 密钥。有关设置详情，请参阅[管理密钥](#管理密钥)。

**工作区密钥。** 对于在所有用户之间共享的 API 密钥（例如，您组织的 LLM 提供商密钥、搜索 API 密钥），请将它们存储为 LangSmith 中的[工作区密钥](/langsmith/setup)。详情请参阅[管理密钥](#管理密钥)。

### 异步

基于 LLM 的应用程序严重受限于 I/O：调用语言模型、数据库和外部服务。异步编程允许这些操作并发运行而不是阻塞，从而提高吞吐量和响应能力。

<Note>
  LangChain 遵循在异步方法名称前加上 `a` 前缀的约定（例如，`ainvoke`、`abefore_agent`、`astream`）。同步和异步变体位于同一个类或命名空间中。
</Note>

为生产环境构建时：

* **创建异步工具。** LangChain 在单独的线程中运行同步工具以避免阻塞，但原生异步完全避免了线程开销。
* **使用异步中间件方法。** 自定义[中间件](/oss/javascript/langchain/middleware/custom)应实现异步钩子（例如，使用 `abefore_agent` 而不是 `before_agent`）。
* **对外部资源生命周期使用异步。** 创建[沙盒](#沙盒)或连接到 [MCP 服务器](/oss/javascript/langchain/mcp)涉及网络调用，应该等待。这就是用于配置这些资源的[图工厂](/langsmith/graph-rebuild)是异步的原因。

### 持久性

深度智能体运行在 LangGraph 上，它开箱即用地提供了[持久执行](/oss/javascript/langgraph/durable-execution)。[持久化](/oss/javascript/langgraph/persistence)层会在每一步检查点保存状态，因此因故障、超时或[人机交互](/oss/javascript/langgraph/interrupts)暂停而中断的运行可以从其最后记录的状态恢复，而无需重新处理之前的步骤。对于生成许多子智能体的长时间运行的深度智能体来说，这意味着运行中途发生的故障不会丢失已完成的工作。

检查点还支持：

* **无限期[中断](/oss/javascript/langgraph/interrupts)。** 人机交互工作流可以暂停几分钟或几天，并从中断处精确恢复。
* **[时光旅行](/oss/javascript/langgraph/use-time-travel)。** 每个检查点步骤都是一个可以回退的快照，如果出现问题，您可以从更早的状态重放。
* **安全处理敏感操作。** 对于涉及支付或其他不可逆操作的工作流，检查点提供了审计跟踪和恢复点，以便检查导致某个操作的确切状态。

<Tip>
  [LangSmith 部署](/langsmith/deployment)会自动配置持久化检查点器。如果您是自托管，请参阅[持久化](/oss/javascript/langgraph/persistence)了解设置说明。
</Tip>

## 内存

没有内存，每次对话都从零开始。内存让您的智能体能够跨对话保留信息（用户偏好、学到的指令、过去的经验），从而随着时间的推移个性化其行为。有关内存类型的概述，请参阅[内存概念指南](/oss/javascript/concepts/memory)。

### 界定

内存总是在对话之间持久存在。主要问题是如何跨用户和助手边界界定其范围。正确的范围取决于谁应该看到和修改数据：

| 范围           | 命名空间             | 使用场景            | 示例               |
| ------------ | ---------------- | --------------- | ---------------- |
| **用户**（推荐默认） | `(user_id)`      | 每个用户的偏好和上下文     | “我喜欢简洁的回答”       |
| **助手**       | `(assistant_id)` | 单个助手的共享指令       | “帖子限制在 280 字符以内” |
| **全局**       | `(org_id)`       | 适用于所有用户和助手的只读策略 | “切勿透露内部定价”       |

<Warning>
  共享内存（助手、用户或组织范围）是提示注入的一个向量。如果一个用户可以写入另一个用户对话读取的内存，那么恶意用户可能会向该共享状态注入指令。在适当的情况下强制执行只读访问。例如，使组织范围的策略只能通过应用程序代码写入，而不能由智能体本身写入。
</Warning>

### 配置

在深度智能体中，内存作为文件存储在虚拟文件系统中。默认情况下，文件仅持续单个对话。要使它们持久化，请将像 `/memories/` 这样的路径路由到写入 LangGraph [存储](/langsmith/custom-store)的 [StoreBackend](https://reference.langchain.com/javascript/deepagents/backends/StoreBackend)。使用 [CompositeBackend](https://reference.langchain.com/javascript/deepagents/backends/CompositeBackend) 为智能体同时提供临时空间和持久的[长期内存](/oss/javascript/deepagents/memory)。

<Tabs>
  <Tab title="用户（推荐）">
    按 `user_id` 划定命名空间。每个用户拥有自己的私有内存。这是推荐的默认设置，因为大多数应用程序部署单个助手。

    ```typescript src/agent.ts theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    import { createDeepAgent, CompositeBackend, StateBackend, StoreBackend } from "deepagents";

    const agent = createDeepAgent({
      backend: (rt) => new CompositeBackend(
        new StateBackend(rt),
        {
          "/memories/": new StoreBackend(rt, {
            namespace: (ctx) => [ctx.runtime.context.userId],
          }),
        },
      ),
      systemPrompt: `您在 /memories/ 拥有持久化内存。

      每次对话开始时，请阅读 /memories/instructions.txt 以获取累积的知识和偏好。当您学到应该持久化的内容时，请更新该文件。`,
    });

    export { agent };
    ```
  </Tab>

  <Tab title="助手">
    按 `assistant_id` 划定命名空间。内存由同一助手的所有用户共享，因此任何用户都可以读取或更新它。用于适用于使用给定助手的每个人的共享指令或知识（例如，“始终以正式语气回复”）。

    ```typescript src/agent.ts theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    import { getConfig } from "@langchain/langgraph";
    import { createDeepAgent, CompositeBackend, StateBackend, StoreBackend } from "deepagents";

    const agent = createDeepAgent({
      backend: (rt) => new CompositeBackend(
        new StateBackend(rt),
        {
          "/memories/": new StoreBackend(rt, {
            namespace: (ctx) => {
              const config = getConfig();
              return [config.metadata.assistantId];
            },
          }),
        },
      ),
    });

    export { agent };
    ```
  </Tab>

  <Tab title="全局">
    按 `org_id` 划定命名空间。内存由所有用户和所有助手共享。通常用于组织范围的策略（合规规则、品牌指南），这些策略对智能体应该是只读的。应限制应用程序代码的写入权限，以防止提示注入。

    ```typescript src/agent.ts theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    import { createDeepAgent, CompositeBackend, StateBackend, StoreBackend } from "deepagents";

    const agent = createDeepAgent({
      backend: (rt) => new CompositeBackend(
        new StateBackend(rt),
        {
          "/memories/": new StoreBackend(rt, {
            namespace: (ctx) => [ctx.runtime.context.orgId],
          }),
        },
      ),
    });

    export { agent };
    ```
  </Tab>
</Tabs>

您也可以使用[存储 API](/langsmith/custom-store) 从应用程序代码读写存储。有关示例，请参阅[从外部代码访问内存](/oss/javascript/deepagents/memory#从外部代码访问内存)。

有关完整的命名空间工厂 API，请参阅[命名空间工厂](/oss/javascript/deepagents/backends#命名空间工厂)。有关自我改进指令和知识库等内存模式，请参阅[长期内存](/oss/javascript/deepagents/memory)。

## 执行环境

在本地，智能体可以直接读写磁盘上的文件并运行 shell 命令。在生产环境中，您需要考虑隔离性和持久性。正确的设置取决于您的智能体是否需要执行代码：

* 如果您的智能体只读写文件，**文件系统后端**就足够了。选择符合您持久性需求的后端：临时空间、持久存储或两者混合。
* **沙盒**添加了一个隔离的容器，其中包含一个用于运行 shell 命令的 `execute` 工具。如果您的智能体需要运行代码、安装包或执行除文件 I/O 之外的任何操作，请使用沙盒。

### 文件系统

根据需要持久化的内容选择后端：

* [StateBackend](https://reference.langchain.com/javascript/deepagents/backends/StateBackend)（默认）：临时空间，作用域限定在单个对话。每一步都会检查点，因此避免写入大文件。
* [StoreBackend](https://reference.langchain.com/javascript/deepagents/backends/StoreBackend)：跨对话持久化的存储。使用[命名空间工厂](/oss/javascript/deepagents/backends#命名空间工厂)界定范围。
* [CompositeBackend](https://reference.langchain.com/javascript/deepagents/backends/CompositeBackend)：混合使用。默认使用临时空间，并为特定路径（如 `/memories/`）提供持久化路由。

有关完整后端列表以及如何构建自定义后端，请参阅[后端](/oss/javascript/deepagents/backends)。

<Warning>
  `FilesystemBackend` 和 `LocalShellBackend` 直接访问主机。不要在已部署的智能体中使用它们。
</Warning>

### 沙盒

如果您的智能体需要运行代码（不仅仅是读写文件），请使用[沙盒](/oss/javascript/deepagents/sandboxes)。沙盒同时提供文件系统和一个用于运行 shell 命令的 `execute` 工具，所有这些都在一个隔离的容器内。这种隔离也保护了您的主机：如果智能体的代码耗尽内存或崩溃，只有沙盒会受到影响。您的服务器会继续运行。

#### 生命周期

关键的决定是沙盒存活多长时间。每个对话获得一个全新的沙盒，还是对话共享一个持久环境？

| 范围       | 沙盒 ID 存储位置                      | 生命周期            | 示例使用场景              |
| -------- | ------------------------------- | --------------- | ------------------- |
| **线程范围** | [线程](/langsmith/use-threads)元数据 | 每个对话全新，按 TTL 清理 | 数据分析机器人，每个对话从干净状态开始 |
| **助手范围** | [助手](/langsmith/assistants)配置   | 跨所有对话共享         | 一个编码助手，跨对话维护一个克隆的仓库 |

<Note>
  下面的示例使用异步[图工厂](/langsmith/graph-rebuild)而不是静态图，因为沙盒需要运行时配置中的 `thread_id` 或 `assistant_id` 来查找或创建正确的沙盒。图工厂在每次运行时接收配置，因此它可以在构建智能体之前解析沙盒。该工厂是异步的，因为沙盒创建是一个 I/O 密集型操作，需要仅在调用时可用的运行时信息（如 `thread_id` 或 `assistant_id`）。
</Note>

<Tabs>
  <Tab title="线程范围（最常见）">
    每个对话获得自己的沙盒。[图工厂](/langsmith/graph-rebuild)从配置中读取 `thread_id`，因此每个[线程](/langsmith/use-threads)自动获得自己隔离的环境。提供商的基于标签的查找处理跨运行的去重。当沙盒 [TTL](/langsmith/configure-ttl) 过期时清理。

    ```typescript src/agent.ts theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    import { Daytona } from "@daytonaio/sdk";
    import { DaytonaSandbox } from "@langchain/daytona";
    import { createDeepAgent } from "deepagents";
    import type { RunnableConfig } from "@langchain/core/runnables";

    const client = new Daytona();

    export async function agent(config: RunnableConfig) {
      const threadId = config.configurable!.thread_id;
      let sandbox;
      try {
        sandbox = await client.findOne({ labels: { thread_id: threadId } });
      } catch {
        sandbox = await client.create({
          labels: { thread_id: threadId },
          autoDeleteInterval: 3600, // TTL: 空闲时清理
        });
      }
      return createDeepAgent({ backend: await DaytonaSandbox.fromId(sandbox.id) });
    }
    ```
  </Tab>

  <Tab title="助手范围">
    所有对话共享一个沙盒。[图工厂](/langsmith/graph-rebuild)从配置元数据中读取[助手](/langsmith/assistants) ID，因此同一助手上的每个线程都返回到相同的环境。文件、已安装的包和克隆的仓库会跨对话持久化。

    ```typescript src/agent.ts theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    import { Daytona } from "@daytonaio/sdk";
    import { DaytonaSandbox } from "@langchain/daytona";
    import { createDeepAgent } from "deepagents";
    import type { RunnableConfig } from "@langchain/core/runnables";

    const client = new Daytona();

    export async function agent(config: RunnableConfig) {
      const assistantId = config.metadata!.assistant_id;
      let sandbox;
      try {
        sandbox = await client.findOne({ labels: { assistant_id: assistantId } });
      } catch {
        sandbox = await client.create({ labels: { assistant_id: assistantId } });
      }
      return createDeepAgent({ backend: await DaytonaSandbox.fromId(sandbox.id) });
    }
    ```

    <Warning>
      助手范围的沙盒会随着时间的推移累积文件、已安装的包和其他沙盒内状态。为您的沙盒提供商配置 TTL，使用快照定期重置，或实施清理逻辑，以防止沙盒的磁盘和内存无限增长。
    </Warning>
  </Tab>
</Tabs>

因为 `agent` 变量是一个异步函数（而不是编译后的图），服务器将其视为[图工厂](/langsmith/graph-rebuild)并在每次运行时调用它，注入配置。该工厂通过提供商的基于标签的搜索查找或创建沙盒，并返回一个连接到该沙盒的新智能体图。

使用 `langgraph deploy` 部署后，使用 SDK 从应用程序代码调用智能体。无论范围如何，客户端代码都是相同的。范围完全在上述智能体工厂中处理，但行为有所不同：

<Tabs>
  <Tab title="线程范围">
    每个线程获得自己的沙盒。同一线程内的后续消息会重用同一个沙盒，但新线程始终从全新状态开始，没有来自之前对话的遗留文件或已安装包。

    ```typescript client.ts theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    import { Client } from "@langchain/langgraph-sdk";

    const client = new Client({ apiUrl: "<DEPLOYMENT_URL>", apiKey: "<LANGSMITH_API_KEY>" });

    // 对话 1：安装 pandas 并分析数据
    const thread1 = await client.threads.create();
    for await (const chunk of client.runs.stream(
      thread1.thread_id,
      "agent",
      { input: { messages: [{ role: "human", content: "安装 pandas 并分析 sales_data.csv" }] } },
    )) {
      console.log(chunk.data);
    }

    // 同一对话中的后续消息 — pandas 仍然安装着
    for await (const chunk of client.runs.stream(
      thread1.thread_id,
      "agent",
      { input: { messages: [{ role: "human", content: "现在绘制结果图表" }] } },
    )) {
      console.log(chunk.data);
    }

    // 对话 2：全新的沙盒 — pandas 未安装，没有对话 1 的文件
    const thread2 = await client.threads.create();
    for await (const chunk of client.runs.stream(
      thread2.thread_id,
      "agent",
      { input: { messages: [{ role: "human", content: "安装了哪些包？" }] } },
    )) {
      console.log(chunk.data);
    }
    ```
  </Tab>

  <Tab title="助手范围">
    所有线程共享一个沙盒。当沙盒具有重建成本高昂的状态（例如克隆的仓库、已安装的依赖项或构建产物）时，这非常有用。同一助手的任何对话都会从上一次对话结束的地方继续，无需重复设置。

    ```typescript client.ts theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    import { Client } from "@langchain/langgraph-sdk";

    const client = new Client({ apiUrl: "<DEPLOYMENT_URL>", apiKey: "<LANGSMITH_API_KEY>" });

    // 对话 1：克隆并设置项目
    const thread1 = await client.threads.create();
    for await (const chunk of client.runs.stream(
      thread1.thread_id,
      "agent",
      { input: { messages: [{ role: "human", content: "克隆 https://github.com/org/repo 并安装依赖" }] } },
    )) {
      console.log(chunk.data);
    }

    // 对话 2：仓库和依赖仍然存在
    const thread2 = await client.threads.create();
    for await (const chunk of client.runs.stream(
      thread2.thread_id,
      "agent",
      { input: { messages: [{ role: "human", content: "运行测试套件并修复失败" }] } },
    )) {
      console.log(chunk.data);
    }
    ```
  </Tab>
</Tabs>

#### 文件传输

沙盒是隔离的容器，因此您的应用程序代码无法直接访问其中的文件。使用 `upload_files()` 和 `download_files()` 跨沙盒边界移动数据：

* **在智能体运行之前预置沙盒**：上传用户文件、[技能](/oss/javascript/deepagents/skills)脚本、配置或[持久化内存](/oss/javascript/deepagents/memory)，以便智能体从一开始就拥有所需内容
* **在智能体运行后检索结果**：下载生成的产物（报告、图表、导出），并将更新的内存同步回，供未来对话使用

有关特定提供商的文件传输示例，请参阅[使用文件](/oss/javascript/deepagents/sandboxes#使用文件)。有关提供商设置、安全性和生命周期模式，请参阅完整的[沙盒指南](/oss/javascript/deepagents/sandboxes)。

<Accordion title="示例：使用自定义中间件同步技能和内存">
  智能体需要执行的[技能](/oss/javascript/deepagents/skills)脚本必须在智能体运行前上传到沙盒中。您可能还想同步[内存](/oss/javascript/deepagents/memory)，以便智能体可以在容器内读取和更新它们。使用带有 `before_agent` 和 `after_agent` 钩子的[自定义中间件](/oss/javascript/langchain/middleware/custom)来跨沙盒边界移动文件：

  ```typescript src/agent.ts theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createMiddleware } from "langchain";
  import { createDeepAgent, CompositeBackend, StoreBackend } from "deepagents";
  import { DaytonaSandbox } from "@langchain/daytona";

  function safeFilename(key: string): string {
    const name = key.split("/").pop()!;
    if (name.includes("..") || /[*?]/.test(name)) {
      throw new Error(`Invalid key: ${key}`);
    }
    return name;
  }

  const createSandboxSyncMiddleware = (backend: CompositeBackend) => {
    return createMiddleware({
      name: "SandboxSyncMiddleware",
      beforeAgent: async (state, runtime) => {
        // 将技能脚本和内存上传到沙盒
        const userId = runtime.context.userId;
        const store = runtime.store;
        const encoder = new TextEncoder();
        const files: [string, Uint8Array][] = [];
        for (const item of await store.search(["skills", userId])) {
          const name = safeFilename(item.key);
          files.push([`/skills/${name}`, encoder.encode(item.value.content)]);
        }
        for (const item of await store.search(["memories", userId])) {
          const name = safeFilename(item.key);
          files.push([`/memories/${name}`, encoder.encode(item.value.content)]);
        }
        if (files.length > 0) {
          await backend.uploadFiles(files);
        }
      },
      afterAgent: async (state, runtime) => {
        // 将更新的内存同步回存储
        const userId = runtime.context.userId;
        const store = runtime.store;
        const items = await store.search(["memories", userId]);
        const results = await backend.downloadFiles(
          items.map((item) => `/memories/${item.key}`),
        );
        const decoder = new TextDecoder();
        for (const result of results) {
          if (result.content) {
            await store.put(
              ["memories", userId],
              result.path.split("/").pop()!,
              { content: decoder.decode(result.content) },
            );
          }
        }
      },
    });
  };

  const backend = new CompositeBackend(
    await DaytonaSandbox.fromId(sandbox.id),
    {
      "/skills/": new StoreBackend(rt, {
        namespace: (ctx) => ["skills", ctx.runtime.context.userId],
      }),
      "/memories/": new StoreBackend(rt, {
        namespace: (ctx) => ["memories", ctx.runtime.context.userId],
      }),
    },
  );

  const agent = createDeepAgent({
    backend,
    middleware: [createSandboxSyncMiddleware(backend)],
  });

  export { agent };
  ```
</Accordion>

#### 管理密钥

沙盒是隔离的容器，因此主机中的环境变量在沙盒内部不可用。有两种方式可以为沙盒代码提供 API 密钥及其他密钥信息：

**身份验证代理（推荐）。** [沙盒身份验证代理](/langsmith/sandbox-auth-proxy) 会拦截沙盒发出的出站请求，并自动注入身份验证标头。沙盒代码正常调用外部 API，代理会根据目标主机添加正确的凭据。这意味着 API 密钥永远不会出现在沙盒代码、环境变量或日志中。

```json theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
{
  "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 [工作区设置](/langsmith/setup) 中存储的密钥。在创建引用这些密钥的模板之前，请先在相应位置配置好密钥。

**工作区密钥。** 对于不需要通过代理注入的 API 密钥（例如代理服务器自身使用的密钥，而非沙盒代码使用的密钥），可将其作为[工作区密钥](/langsmith/setup)存储在 LangSmith 中。这些密钥在运行时作为环境变量提供给工作区中的所有代理使用。

<Warning>
  避免通过环境变量或文件上传的方式将密钥传递到沙盒中。代理可以读取沙盒内任何可访问的文件或环境变量，包括凭据信息。身份验证代理能够将密钥完全隔离在沙盒之外。
</Warning>

## 安全护栏

生产环境中的代理是自主运行的，这意味着它们可能无限循环、触发速率限制，或处理包含敏感信息的用户数据。深度代理支持[中间件](/oss/javascript/langchain/middleware/built-in)，该中间件封装了模型和工具调用，用于处理上述问题。

### 速率限制

此处的速率限制是指限制代理在单次运行中自身对 LLM 和工具的使用量，而非针对传入请求的 API 网关速率限制。

如果没有限制，一个陷入混乱的代理可能会在几分钟内，通过反复调用同一个工具或发出数百次模型调用，耗尽您的 LLM API 预算。您可以针对每次运行，对模型调用和工具执行分别设置上限：

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createAgent, modelCallLimitMiddleware, toolCallLimitMiddleware } from "langchain";

const agent = createAgent({
  model: "claude-sonnet-4-6",
  middleware: [
    modelCallLimitMiddleware({ runLimit: 50 }),
    toolCallLimitMiddleware({ runLimit: 200 }),
  ],
});
```

使用 `run_limit` 限制单次调用内的次数（每轮重置）。使用 `thread_limit` 限制整个对话过程中的次数（需要检查点器）。完整配置请参见 [ModelCallLimitMiddleware](https://reference.langchain.com/javascript/langchain/index/modelCallLimitMiddleware) 和 [ToolCallLimitMiddleware](https://reference.langchain.com/javascript/langchain/index/toolCallLimitMiddleware)。

### 错误处理

并非所有错误都应采用相同的处理方式。瞬时故障（网络超时、速率限制）应自动重试。LLM 可恢复的错误（工具输出错误、解析失败）应反馈给模型。需要人工介入的错误应暂停代理。如需详细了解并查看代码示例，请参阅[正确处理错误](/oss/javascript/langgraph/thinking-in-langgraph#handle-errors-appropriately)。

中间件负责处理瞬时故障。模型调用和工具调用各自带有具备指数退避策略的重试中间件。如果您使用的主模型服务完全宕机，备用中间件将切换到备选模型：

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import {
  createAgent,
  modelFallbackMiddleware,
  modelRetryMiddleware,
  toolRetryMiddleware,
} from "langchain";

const agent = createAgent({
  model: "claude-sonnet-4-6",
  middleware: [
    // 在遇到速率限制、超时和 5xx 错误时重试模型调用
    modelRetryMiddleware({ maxRetries: 3, backoffFactor: 2.0, initialDelayMs: 1000 }),
    // 如果主模型完全宕机，则回退到备选模型
    modelFallbackMiddleware("gpt-4.1"),
    // 对访问外部 API 的特定工具进行重试（非全部工具）
    toolRetryMiddleware({
      maxRetries: 2,
      tools: ["search", "fetch_url"],
      retryOn: [TimeoutError, TypeError],
    }),
  ],
});
```

应将 [ToolRetryMiddleware](https://reference.langchain.com/javascript/langchain/index/toolRetryMiddleware) 的作用范围限定在特定工具上，而非对所有工具进行重试。文件系统操作 `read_file` 失败后重试通常无效，但网络搜索超时后重试很可能有效。完整配置请参见 [ModelRetryMiddleware](https://reference.langchain.com/javascript/langchain/index/modelRetryMiddleware) 和 [ModelFallbackMiddleware](https://reference.langchain.com/javascript/langchain/index/modelFallbackMiddleware)。

### 数据隐私

如果您的代理处理的用户输入可能包含电子邮件、信用卡号或其他个人身份信息，您可以在这些信息到达模型或被记录到日志之前对其进行检测和处理：

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { createAgent, piiMiddleware } from "langchain";

const agent = createAgent({
  model: "claude-sonnet-4-6",
  middleware: [
    piiMiddleware("email", { strategy: "redact", applyToInput: true }),
    piiMiddleware("credit_card", { strategy: "mask", applyToInput: true }),
  ],
});
```

可选的策略包括：`redact`（替换为 `[REDACTED_EMAIL]`）、`mask`（部分掩码，如 `****-****-****-1234`）、`hash`（确定性哈希）和 `block`（抛出错误）。您也可以针对特定领域模式编写自定义检测器。完整配置请参见 @\[PIIMiddleware]。

如需查看完整的中间件列表，请参阅[预置中间件](/oss/javascript/langchain/middleware/built-in)。

## 前端

深度代理使用 [`useStream`](/oss/javascript/langchain/frontend/overview) 将您的用户界面连接到代理后端。`useStream` 是一个前端钩子（适用于 React、Vue、Svelte 和 Angular），可以实时从代理处流式传输消息、子代理进度以及自定义状态。

在本地环境中，`useStream` 指向 `http://localhost:2024`。在生产环境中，请将其指向您的 [LangSmith 部署服务](/langsmith/deployment)，并配置重连机制，以便在用户连接断开时不会丢失进度。

```tsx theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { useStream } from "@langchain/react";

function App() {
  const stream = useStream<typeof agent>({
    apiUrl: "https://your-deployment.langsmith.dev",
    assistantId: "agent",
    reconnectOnMount: true,    // 页面刷新或导航后恢复流式连接
    fetchStateHistory: true,   // 挂载时加载完整的对话线程历史
  });
}
```

`reconnectOnMount` 会自动恢复正在进行的运行任务。如果用户在代理工作时刷新页面，他们不会看到空白屏幕，而是看到任务继续进行。`fetchStateHistory` 会加载该线程的完整对话历史，以便返回的用户能够看到之前的消息。

对于会生成许多子代理的深度代理工作流，在提交时设置较高的 `recursionLimit` 可以避免长时间运行的任务被提前截断：

```tsx theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
stream.submit(
  { messages: [{ type: "human", content: text }] },
  {
    streamSubgraphs: true,
    config: { recursionLimit: 10000 },
  },
);
```

有关深度代理特有的 UI 模式，例如子代理卡片、待办事项列表和自定义状态渲染，请参阅[前端指南](/oss/javascript/deepagents/frontend/overview)。

***

<div className="source-links">
  <Callout icon="edit">
    [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/i18n\zh-CN\oss\deepagents\going-to-production.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
  </Callout>

  <Callout icon="terminal-2">
    [Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers.
  </Callout>
</div>
