本指南涵盖了将 Deep Agent 从本地原型部署到生产环境的考虑因素。它将逐步介绍内存范围界定、执行环境配置、添加防护措施以及连接前端。
代理使用来自内存和执行环境的信息来完成任务。
在生产环境中,有几个基本要素决定了信息的共享和访问方式:
线程 :单个对话。消息历史记录和临时文件默认限定在线程范围内,不会跨线程传递。
用户 :与您的代理交互的人。内存和文件可以是用户私有的,也可以跨用户共享。身份验证和授权来自您的认证层 。
助理 :配置的代理实例。内存和文件可以绑定到一个助理,也可以在所有助理之间共享。
本页涵盖:
LangSmith 部署
将 Deep Agent 投入生产的最快方法是使用 deepagents deploy ,它会打包您的代理配置,并通过一个命令将其部署为 LangSmith 部署。或者,您可以直接配置 LangSmith 部署 。无论哪种方式,都会为您的代理配置所需的基础设施:助理 、线程 、运行 、存储和检查点器,因此您无需自行设置。它还开箱即用地提供认证 、Webhook 、cron 作业 和可观测性 ,并且可以通过 MCP 或 A2A 公开您的代理。
对于基于 CLI 的方法,请参阅使用 CLI 部署 。对于手动设置,请参阅 LangSmith 部署快速入门 。
除非另有说明,本页上的所有代码片段都使用以下 langgraph.json:
{
" 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 步骤、存储索引、认证处理程序等),请参阅应用程序结构 。
生产考虑因素
多租户
当您的代理服务于多个用户时,您需要处理三个问题:验证每个用户的身份、控制他们可以访问的内容,以及管理代理代表他们操作时使用的凭证。
用户身份和访问控制
LangSmith 部署 支持自定义认证 来建立用户身份,以及授权处理程序 来控制对线程、助理和存储命名空间等资源的访问。授权处理程序在认证成功后运行,并且可以:
为资源标记所有权元数据(例如,owner: user_id)
返回过滤器,以便用户只能看到自己的资源
对未经授权的操作返回 HTTP 403 拒绝访问
有关分步教程,请参阅使对话私有化 。有关演练,请观看自定义认证视频 。
您如何界定内存范围 和执行环境 决定了用户之间共享哪些数据。有关详细信息,请参阅以下部分。
团队访问控制 (RBAC)
LangSmith 的基于角色的访问控制 管理您团队中谁可以部署、配置和监控代理。这与上述最终用户授权是分开的。
角色 访问权限 工作区管理员 完全权限,包括设置和成员管理 工作区编辑者 创建和修改资源,但不能删除运行或管理成员 工作区查看者 只读访问
企业版计划提供具有细粒度权限的自定义角色。有关完整的权限模型,请参阅 RBAC 参考 。
最终用户凭证
当您的代理需要代表用户调用外部 API(例如,读取他们的 GitHub 仓库、发送 Slack 消息、查询他们的数据仓库)时,您需要一种方法将用户的凭证传递给代理,而无需硬编码。
通过代理认证进行 OAuth。 代理认证 提供托管的 OAuth 2.0 流程。配置 OAuth 提供程序后,代理可以请求限定于每个用户的令牌。首次使用时,代理会中断 执行并呈现 OAuth 同意 URL。用户认证后,代理使用有效令牌恢复。令牌会自动存储和刷新。
import { Client } from "@langchain/auth" ;
const authClient = new Client () ;
// 在代理的工具内部:
// 通过 runtime.serverInfo 访问经过认证的用户
const authResult = await authClient . authenticate ( {
provider : "github" ,
scopes : [ "repo" , "read:org" ] ,
userId : runtime . serverInfo . user . identity ,
} ) ;
// 使用 authResult.token 代表用户进行 GitHub API 调用
沙盒的凭证注入。 如果您的代理在沙盒 内运行代码来调用外部 API,沙盒认证代理 可以自动将凭证注入出站请求,因此沙盒代码永远不会收到原始 API 密钥。有关设置详细信息,请参阅管理机密 。
工作区机密。 对于跨所有用户共享的 API 密钥(例如,您组织的 LLM 提供程序密钥、搜索 API 密钥),请将它们存储为 LangSmith 中的工作区机密 。有关详细信息,请参阅管理机密 。
基于 LLM 的应用程序严重受 I/O 限制:调用语言模型、数据库和外部服务。异步编程让这些操作并发运行而不是阻塞,从而提高吞吐量和响应能力。
LangChain 遵循在异步方法名称前加 a 的约定(例如,ainvoke、abefore_agent、astream)。同步和异步变体位于同一个类或命名空间中。
构建生产环境时:
创建异步工具。 LangChain 在单独的线程中运行同步工具以避免阻塞,但原生异步可以完全避免线程开销。
使用异步中间件方法。 自定义中间件 应实现异步钩子(例如,使用 abefore_agent 而不是 before_agent)。
对外部资源生命周期使用异步。 创建沙盒 或连接到 MCP 服务器 涉及网络调用,应等待。这就是为什么配置这些资源的图工厂 是异步的。
持久性
Deep Agent 在 LangGraph 上运行,LangGraph 开箱即用地提供持久执行 。持久化 层在每个步骤检查点状态,因此因故障、超时或人机回环 暂停而中断的运行可以从其最后记录的状态恢复,而无需重新处理之前的步骤。对于生成许多子代理的长时间运行的 Deep Agent,这意味着运行中失败不会丢失已完成的工作。
检查点还支持:
无限期中断 。 人机回环工作流可以暂停几分钟或几天,并在离开的地方准确恢复。
时间旅行 。 每个检查点步骤都是一个快照,您可以回退到该状态,如果出现问题,可以从早期状态重放。
安全处理敏感操作。 对于涉及付款或其他不可逆操作的工作流,检查点提供审计跟踪和恢复点,以检查导致操作的确切状态。
没有内存,每次对话都从头开始。内存让您的代理能够跨对话保留信息(用户偏好、学习的指令、过去的经验),以便随着时间的推移个性化其行为。有关内存类型的概述,请参阅内存概念指南 。
范围界定
内存始终在对话之间持久化。主要问题是如何在用户和助理边界之间界定范围。正确的范围取决于谁应该查看和修改数据:
范围 命名空间 用例 示例 用户 (推荐默认)(user_id)每个用户的偏好和上下文 “我更喜欢简洁的回复” 助理 (assistant_id)一个助理的共享指令 “将帖子限制在 280 个字符” 全局 (org_id)所有用户和助理的只读策略 “切勿披露内部定价”
共享内存(助理、用户或组织范围)是提示注入的载体。如果一个用户可以写入另一个用户对话读取的内存,恶意用户可能会将指令注入该共享状态。在适当的地方强制执行只读访问。例如,使组织范围的策略只能通过应用程序代码写入,而不能由代理本身写入。使用权限 以声明方式拒绝写入共享路径,或使用后端策略钩子 进行自定义验证逻辑。
在 Deep Agent 中,内存作为文件存储在虚拟文件系统中。默认情况下,文件仅在单个对话中有效。要持久化它们,请将路径(如 /memories/)路由到一个 StoreBackend ,该后端写入 LangGraph Store 。使用 CompositeBackend 为代理提供临时暂存空间和持久的长期内存 。
下面显示的 rt.serverInfo 和 rt.executionInfo 命名空间模式需要 deepagents>=1.9.0。
按 user_id 划分命名空间。每个用户都有自己的私有内存。这是推荐的默认设置,因为大多数应用程序部署单个助理。 import { createDeepAgent , CompositeBackend , StateBackend , StoreBackend } from "deepagents" ;
export const agent = createDeepAgent ( {
backend : new CompositeBackend (
new StateBackend () ,
{
"/memories/" : new StoreBackend ( {
namespace : ( rt ) => [
rt . serverInfo . assistantId ,
rt . serverInfo . user . identity ,
] ,
} ) ,
},
) ,
systemPrompt : `You have persistent memory at /memories/.
Read /memories/instructions.txt at the start of each conversation for
accumulated knowledge and preferences. When you learn something that
should persist, update that file.` ,
} ) ;
按 assistant_id 划分命名空间。内存由同一助理的所有用户共享,因此任何用户都可以读取或更新它。用于共享指令或适用于使用给定助理的每个人的常识(例如,“始终以正式语气回复”)。 import { createDeepAgent , CompositeBackend , StateBackend , StoreBackend } from "deepagents" ;
export const agent = createDeepAgent ( {
backend : new CompositeBackend (
new StateBackend () ,
{
"/memories/" : new StoreBackend ( {
namespace : ( rt ) => [rt . serverInfo . assistantId] ,
} ) ,
},
) ,
} ) ;
仅按 user_id 划分命名空间。内存跟随用户跨所有助理。用于全局用户配置文件(姓名、时区、通信偏好),无论用户与哪个助理对话都应适用。 import { createDeepAgent , CompositeBackend , StateBackend , StoreBackend } from "deepagents" ;
export const agent = createDeepAgent ( {
backend : new CompositeBackend (
new StateBackend () ,
{
"/memories/" : new StoreBackend ( {
namespace : ( rt ) => [rt . serverInfo . user . identity] ,
} ) ,
},
) ,
} ) ;
按 org_id 划分命名空间。内存由所有用户和所有助理共享。通常用于组织范围的策略(合规规则、品牌指南),这些策略对代理应为只写。写入权限应仅限于应用程序代码,以防止提示注入。 import { createDeepAgent , CompositeBackend , StateBackend , StoreBackend } from "deepagents" ;
export const agent = createDeepAgent ( {
backend : new CompositeBackend (
new StateBackend () ,
{
"/memories/" : new StoreBackend ( {
namespace : ( rt ) => [rt . context . orgId] ,
} ) ,
},
) ,
} ) ;
您还可以使用 Store API 从应用程序代码读取和写入存储。有关示例,请参阅高级用法 。
有关完整的命名空间工厂 API,请参阅命名空间工厂 。有关自改进指令和知识库等内存模式,请参阅长期内存 。
执行环境
在本地,代理可以直接读写磁盘上的文件并运行 shell 命令。在生产环境中,您需要考虑隔离和持久性。正确的设置取决于您的代理是否需要执行代码:
文件系统后端 :如果您的代理只读写文件就足够了。选择符合您持久性需求的后端:临时暂存空间、持久存储或两者的混合。
沙盒 :添加一个隔离的容器,其中包含用于运行 shell 命令的 execute 工具。如果您的代理需要运行代码、安装包或执行任何超出文件 I/O 的操作,请使用沙盒。
文件系统
根据需要持久化的内容选择后端:
有关后端的完整列表以及如何构建自定义后端,请参阅后端 。
FilesystemBackend 和 LocalShellBackend 直接访问主机。不要在部署的代理中使用它们。
如果您的代理需要运行代码(而不仅仅是读写文件),请使用沙盒 。沙盒提供文件系统和用于运行 shell 命令的 execute 工具,所有这些都在隔离的容器内。这种隔离还可以保护您的主机:如果代理的代码耗尽内存或崩溃,只有沙盒会受到影响。您的服务器会继续运行。
生命周期
关键决策是沙盒的存活时间。每个对话是获得一个新的沙盒,还是对话共享一个持久环境?
范围 沙盒 ID 存储位置 生命周期 示例用例 线程范围 线程 元数据每个对话一个新沙盒,在 TTL 时清理 数据分析机器人,每次对话都从干净状态开始 助理范围 助理 配置跨所有对话共享 编程助理,在对话之间维护克隆的仓库
下面的示例使用异步图工厂 而不是静态图,因为沙盒需要 thread_id 或 assistant_id 来查找或创建正确的沙盒。图工厂不接收完整的 Runtime(没有 server_info 或 execution_info);而是接受 RunnableConfig 并从 config["configurable"] 读取 thread_id 和 assistant_id。工厂是异步的,因为沙盒创建是一个 I/O 密集型操作,需要仅在调用时可用的每个运行信息。
每个对话都有自己的沙盒。图工厂 从运行配置中读取 thread_id,因此每个线程 都会自动获得自己的隔离环境。提供程序的基于标签的查找处理跨运行的重复数据删除。当沙盒 TTL 过期时清理。 import { Daytona } from "@daytonaio/sdk" ;
import { DaytonaSandbox } from "@langchain/daytona" ;
import { createDeepAgent } from "deepagents" ;
import type { LangGraphRunnableConfig } from "@langchain/langgraph" ;
const client = new Daytona () ;
export async function agent ( config : LangGraphRunnableConfig ) {
const threadId = config . configurable ?. thread_id as string ;
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) } ) ;
}
所有对话共享一个沙盒。图工厂 从 config["configurable"] 读取助理 ID,因此同一助理上的每个线程都会返回到同一个环境。文件、安装的包和克隆的仓库在对话之间持久存在。 import { Daytona } from "@daytonaio/sdk" ;
import { DaytonaSandbox } from "@langchain/daytona" ;
import { createDeepAgent } from "deepagents" ;
import type { LangGraphRunnableConfig } from "@langchain/langgraph" ;
const client = new Daytona () ;
export async function agent ( config : LangGraphRunnableConfig ) {
const assistantId = config . configurable ?. assistant_id as string ;
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) } ) ;
}
助理范围的沙盒会随时间累积文件、安装的包和其他沙盒内状态。使用沙盒提供程序配置 TTL,使用快照定期重置,或实施清理逻辑以防止沙盒的磁盘和内存无限增长。
因为 agent 变量是一个异步函数(而不是已编译的图),服务器将其视为图工厂 ,并在每次运行时调用它,注入配置。工厂通过提供程序的基于标签的搜索查找或创建沙盒,并返回连接到该沙盒的新代理图。
使用 langgraph deploy 部署后,使用 SDK 从应用程序代码调用代理。客户端代码与范围无关。范围界定完全在上面的代理工厂中处理,但行为有所不同:
每个线程都有自己的沙盒。同一线程内的后续消息重用同一个沙盒,但新线程总是从干净状态开始,没有之前对话的残留文件或安装的包。 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 : "Install pandas and analyze 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 : "Now plot the results" } ] } },
)) {
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 : "What packages are installed?" } ] } },
)) {
console . log (chunk . data) ;
}
所有线程共享一个沙盒。当沙盒具有昂贵的重建状态(例如克隆的仓库、安装的依赖项或构建产物)时,这非常有用。同一助理上的任何对话都会从上一个对话离开的地方继续,而无需重复设置。 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 : "Clone https://github.com/org/repo and install dependencies" } ] } },
)) {
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 : "Run the test suite and fix any failures" } ] } },
)) {
console . log (chunk . data) ;
}
文件传输
沙盒是隔离的容器,因此您的应用程序代码无法直接访问其中的文件。使用 upload_files() 和 download_files() 在沙盒边界之间移动数据:
在代理运行之前播种沙盒 :上传用户文件、技能 脚本、配置或持久内存 ,以便代理从一开始就拥有所需的内容
代理完成后检索结果 :下载生成的工件(报告、图表、导出)并同步更新的内存以供将来对话使用
有关特定于提供程序的文件传输示例,请参阅处理文件 。有关提供程序设置、安全性和生命周期模式,请参阅完整的沙盒指南 。
代理需要执行的技能 脚本必须在代理运行之前上传到沙盒中。您可能还想同步内存 ,以便代理可以在容器内读取和更新它们。使用带有 before_agent 和 after_agent 钩子的自定义中间件 在沙盒边界之间移动文件: 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 . serverInfo . user . identity ;
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 . serverInfo . user . identity ;
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 ( {
namespace : ( rt ) => [ "skills" , rt . serverInfo . user . identity] ,
} ) ,
"/memories/" : new StoreBackend ( {
namespace : ( rt ) => [ "memories" , rt . serverInfo . user . identity] ,
} ) ,
},
) ;
export const agent = createDeepAgent ( {
backend ,
middleware : [ createSandboxSyncMiddleware (backend)] ,
} ) ;
管理机密
沙盒是隔离的容器,因此来自主机的环境变量在其中不可用。有两种方法可以向沙盒代码提供 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 工作区设置 中的机密。在创建引用它们的模板之前,请先在那里配置机密。
工作区机密。 对于不需要基于代理注入的 API 密钥(例如,由代理服务器本身使用,而不是沙盒代码),请将它们存储为 LangSmith 中的工作区机密 。这些在运行时作为环境变量对工作区中的所有代理可用。
避免通过环境变量或文件上传将机密传递到沙盒中。代理可以读取沙盒内任何可访问的文件或环境变量,包括凭据。认证代理使机密完全脱离沙盒。
防护措施
生产环境中的代理自主运行,这意味着它们可能会无限循环、达到速率限制或处理包含敏感信息的用户数据。Deep Agent 提供两层保护:
权限 :声明性的允许/拒绝规则,控制代理可以读取或写入哪些文件和目录。使用权限将代理隔离到工作目录、保护敏感文件或强制执行只读内存。
中间件 :包装模型和工具调用的钩子,用于速率限制、错误处理和数据隐私。
速率限制
这里的速率限制指的是限制代理自身在一次运行中的 LLM 和工具使用,而不是传入请求的 API 网关速率限制。
如果没有限制,一个混乱的代理可能会在几分钟内耗尽您的 LLM API 预算,因为它在同一个工具调用上循环或进行数百次模型调用。对每次运行的模型调用和工具执行设置上限:
import { createAgent , modelCallLimitMiddleware , toolCallLimitMiddleware } from "langchain" ;
const agent = createAgent ( {
model : "google_genai:gemini-3.1-pro-preview" ,
middleware : [
modelCallLimitMiddleware ( { runLimit : 50 } ) ,
toolCallLimitMiddleware ( { runLimit : 200 } ) ,
] ,
} ) ;
使用 run_limit 限制单次调用内的调用(每次轮次重置)。使用 thread_limit 限制整个对话的调用(需要检查点器)。有关完整配置,请参阅 ModelCallLimitMiddleware 和 ToolCallLimitMiddleware 。
错误处理
并非所有错误都应以相同方式处理。瞬态故障(网络超时、速率限制)应自动重试。LLM 可以恢复的错误(错误的工具输出、解析失败)应反馈给模型。需要人工输入的错误应暂停代理。有关完整分解和代码示例,请参阅适当处理错误 。
中间件处理瞬态情况。模型调用和工具调用各自都有自己的重试中间件,具有指数退避。如果您的主要模型提供程序完全宕机,后备中间件会切换到替代方案:
import {
createAgent ,
modelFallbackMiddleware ,
modelRetryMiddleware ,
toolRetryMiddleware ,
} from "langchain" ;
const agent = createAgent ( {
model : "google_genai:gemini-3.1-pro-preview" ,
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 限定于特定工具,而不是重试所有工具。失败的文件系统 read_file 不会从重试中受益,但超时的网络搜索可能会。有关完整配置,请参阅 ModelRetryMiddleware 和 ModelFallbackMiddleware 。
数据隐私
如果您的代理处理可能包含电子邮件、信用卡号或其他个人身份信息 (PII) 的用户输入,您可以在其到达模型或存储在日志中之前检测并处理它:
import { createAgent , piiMiddleware } from "langchain" ;
const agent = createAgent ( {
model : "google_genai:gemini-3.1-pro-preview" ,
middleware : [
piiMiddleware ( "email" , { strategy : "redact" , applyToInput : true } ) ,
piiMiddleware ( "credit_card" , { strategy : "mask" , applyToInput : true } ) ,
] ,
} ) ;
策略包括 redact(替换为 [REDACTED_EMAIL])、mask(部分掩码,如 ****-****-****-1234)、hash(确定性哈希)和 block(引发错误)。您还可以为特定于域的模式编写自定义检测器。
有关完整配置,请参阅 piiMiddleware 。
有关可用中间件的完整列表,请参阅预构建中间件 。
Deep Agent 使用 useStream 将您的 UI 连接到代理后端。useStream 是一个前端钩子(适用于 React、Vue、Svelte 和 Angular),可实时流式传输消息、子代理进度和来自代理的自定义状态。
在本地,useStream 指向 http://localhost:2024。在生产环境中,将其指向您的 LangSmith 部署 并配置重新连接,以便用户在连接断开时不会丢失进度。
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 以避免切断长时间运行的执行:
stream . submit (
{ messages : [ { type : "human" , content : text } ] },
{
streamSubgraphs : true ,
config : { recursionLimit : 10000 },
},
) ;
有关深度代理特定的 UI 模式,例如子代理卡片、待办事项列表和自定义状态渲染,请参阅前端指南 。