自定义 Deep Agents
通过模型、工具、提示、中间件、子代理等配置,打造专属智能体
掌握 createDeepAgent 的全部配置选项为什么需要自定义
默认的 Deep Agent 能写代码、管文件,但你的业务远不止于此
只用默认配置的局限
- 只能用默认模型,无法切换更便宜或更强的选项
- 没有自定义工具,无法搜索网页、查询数据库
- 系统提示是通用的,缺少业务专有知识
- 没有中间件,无法拦截和记录工具调用
- 没有子代理,复杂任务容易上下文爆炸
自定义后的能力
- 灵活切换 OpenAI、Anthropic、Google 等多家模型
- 添加网页搜索、天气查询等自定义工具
- 用
systemPrompt注入业务专有指令 - 用中间件实现日志、重试、缓存等横切逻辑
- 用子代理隔离细节工作,避免上下文膨胀
createDeepAgent 就像一个万能开关面板——每个配置项控制一个维度。本章带你逐个拨动这些开关,把通用代理变成你的专属智能体。
比喻只是为了帮你理解——实际上,createDeepAgent 只是一个工厂函数,它根据你传入的配置对象组装出一个 LangGraph graph。所谓"开关"就是对象上的各个字段,不存在真的面板。
全景配置一览
createDeepAgent 提供的配置项多达十余个,但核心只有五个
先看一眼完整的配置参数,心里有个地图。后续每一节深入一个配置项。
const agent = createDeepAgent({
// 核心五件套
model?: string | BaseLanguageModel, // 1. 模型 —— 字符串 "provider:model" 或模型实例
tools?: TTools | StructuredTool[], // 2. 自定义工具 —— Agent 通过 LLM 自主选择调用
systemPrompt?: string | SystemMessage, // 3. 系统提示 —— 叠加在 SDK 默认提示之上
middleware?: TMiddleware, // 4. 中间件 —— 拦截模型/工具调用的钩子
subagents?: TSubagents, // 5. 子代理 —— 隔离上下文的任务委派
// 文件与存储
backend?: AnyBackendProtocol, // 虚拟文件系统后端(默认 StateBackend)
store?: BaseStore, // 跨线程持久存储(StoreBackend 需要此项)
checkpointer?: BaseCheckpointSaver, // 检查点存储器(人在回路中功能必需)
// 交互与记忆
interruptOn?: Record<string, boolean>, // 人在回路中断规则 —— 指定哪些工具需审批
memory?: string[], // 记忆文件路径 —— 每次调用都加载
skills?: string[], // 技能文件路径 —— 按需加载,节省 token
// 高级选项
name?: string, // 代理名称
permissions?: FilesystemPermission[], // 文件系统权限 —— 控制读写范围
responseFormat?: TResponse, // 结构化输出 schema —— 返回 JSON 而非自由文本
});
本章覆盖范围
- 模型选择与三种初始化方式
- 自定义工具的定义与注册
- 系统提示的组装机制
- 中间件的内置与自定义
- 子代理、后端、人机交互、技能、记忆
不在本章范围
- 后端的详细对比(见第 09 章)
- 子代理的高级用法(见第 10-11 章)
- 沙箱环境(见第 16 章)
- 配置文件 profiles(见第 18 章)
选择模型
三种方式传入模型:字符串、initChatModel、模型类实例
最简单的方式是传入 "provider:model" 格式的字符串。一行代码切换模型提供商。
方式一:字符串(推荐)
传入 "provider:model",SDK 自动初始化。
最简方式
方式二:initChatModel
需要自定义 temperature 等参数时使用。
需要调参
方式三:模型类实例
完全控制所有模型参数,直接实例化。
完全控制
// 方式一:字符串(最简) —— SDK 自动初始化模型,适合快速验证
const agent = createDeepAgent({ model: "openai:gpt-5.4" });
const agent = createDeepAgent({ model: "anthropic:claude-sonnet-4-6" });
const agent = createDeepAgent({ model: "google-genai:gemini-3.1-pro-preview" });
// 方式二:initChatModel(需要调参) —— 通过 provider 字段指定提供商
import { initChatModel } from "langchain";
const model = await initChatModel("openai:gpt-5.5", { temperature: 0 });
const agent = createDeepAgent({ model });
// 方式三:模型类实例(完全控制) —— 可设置 apiKey、temperature 等所有参数
import { ChatOpenAI } from "@langchain/openai";
const agent = createDeepAgent({
model: new ChatOpenAI({
model: "gpt-5.4",
apiKey: "your-api-key", // 也可从环境变量自动读取
temperature: 0, // 0 = 确定性输出,1 = 更有创意
}),
});
支持的提供商
Deep Agents 支持主流 LLM 提供商,用统一的接口接入
| 提供商 | provider 前缀 | 示例模型 | 安装包 |
|---|---|---|---|
| OpenAI | openai: |
gpt-5.4 | @langchain/openai |
| Anthropic | anthropic: |
claude-sonnet-4-6 | @langchain/anthropic |
| Google Gemini | google-genai: |
gemini-3.1-pro-preview | @langchain/google-genai |
| Azure OpenAI | azure_openai: |
gpt-5.4 | @langchain/azure-openai |
| AWS Bedrock | bedrock: |
anthropic.claude-sonnet-4-6 | @langchain/aws |
| Ollama(本地) | ollama: |
devstral-2 | 无需额外包 |
# 安装示例(以 OpenAI 为例)
$ npm install @langchain/openai deepagents
# Anthropic
$ npm install @langchain/anthropic deepagents
OPENAI_API_KEY,Anthropic 需要 ANTHROPIC_API_KEY。Azure 还需要 AZURE_OPENAI_ENDPOINT。
自定义工具
用 langchain 的 tool 函数定义工具,让代理拥有新能力
Deep Agent 已经内置了文件管理和规划工具。当你需要搜索网页、查询天气、调用内部 API 时,就需要自定义工具。
import { tool } from "langchain";
import { createDeepAgent } from "deepagents";
import { z } from "zod";
// 定义一个网络搜索工具 —— tool() 的第一个参数是执行函数,第二个是元数据
const internetSearch = tool(
async ({ query, maxResults = 5 }) => {
// 动态导入 TavilySearch,避免在不需要搜索时加载该模块
const { TavilySearch } = await import("@langchain/tavily");
const search = new TavilySearch({
maxResults, // 返回结果数量
tavilyApiKey: process.env.TAVILY_API_KEY, // 密钥从环境变量读取
});
return await search._call({ query }); // 执行搜索,返回结果给 Agent
},
{
name: "internet_search", // Agent 调用时的名称
description: "运行网络搜索,获取最新信息", // LLM 据此判断何时调用
schema: z.object({
query: z.string().describe("搜索查询关键词"), // .describe() 帮助 LLM 理解参数用途
maxResults: z.number().optional().default(5),
}),
}
);
// 注册到代理 —— Agent 会根据任务需要自主决定是否调用搜索工具
const agent = createDeepAgent({
model: "openai:gpt-5.4",
tools: [internetSearch],
});
name(代理调用时的名称)、description(代理理解何时使用)、schema(参数类型校验,用 Zod 定义)。description 的质量直接决定代理能否正确选择工具。
系统提示
注入业务专有指令,让代理行为符合你的期望
Deep Agent 自带内置系统提示(规划、文件管理指导等)。你的自定义提示会叠加在内置提示之上,而不是替换它。
import { createDeepAgent } from "deepagents";
const researchInstructions =
"你是一位专业研究员。" +
"你的工作是进行深入的研究,然后撰写精炼的报告。" +
"请始终引用来源,区分事实与观点。";
const agent = createDeepAgent({
model: "anthropic:claude-sonnet-4-6",
systemPrompt: researchInstructions,
});
好的系统提示
- 明确角色定位("你是...")
- 说明工作流程(先做什么,再做什么)
- 给出约束条件("始终引用来源")
- 避免模糊指令("尽量做好")
需要注意
- 不要试图替换 SDK 内置提示
- 不要把所有逻辑塞进提示里——用工具
- 提示过长会挤占上下文窗口
- 复杂逻辑用 skills(技能)替代
提示组装机制
系统提示由四个命名部分按固定顺序拼接而成
Deep Agent 从最多四个部分构建最终的系统提示。这确保了你的指令始终优先,而模型特定优化始终在最后。
| 名称 | 来源 | 说明 |
|---|---|---|
| USER | 你的 systemPrompt 参数 |
始终在最前面,优先级最高 |
| BASE | SDK 默认提示 | 除非被 CUSTOM 替换,否则始终存在 |
| CUSTOM | HarnessProfile 的 base_system_prompt | 设置后直接替换 BASE |
| SUFFIX | HarnessProfile 的 system_prompt_suffix | 始终在最后面,最靠近对话历史 |
// 典型场景:使用 Anthropic 模型 + 自定义提示
const agent = createDeepAgent({
model: "anthropic:claude-sonnet-4-6",
systemPrompt: "你是 ACME Corp. 的客户支持代理。",
});
// 最终结果 = USER + BASE + SUFFIX
// = "你是 ACME Corp. 的客户支持代理。"
// + "\n\n"
// + SDK 默认代理提示(BASE)
// + "\n\n"
// + Claude 特定优化指导(SUFFIX)
相同的叠加规则适用于声明式子代理——每个子代理针对其自身的模型重新运行配置文件解析。子代理的 systemPrompt 扮演 BASE 角色。
| spec["system_prompt"] | 配置文件 base_system_prompt (CUSTOM) | 配置文件 system_prompt_suffix (SUFFIX) | 最终子代理系统提示 |
|---|---|---|---|
| authored | - | - | authored |
| authored | - | ✓ | authored + SUFFIX |
| authored | ✓ | - | CUSTOM |
| authored | ✓ | ✓ | CUSTOM + SUFFIX |
通用子代理多层回退:自动添加的通用子代理遵循相同的叠加规则,但多了一层——GP 基础提示的解析优先级为 general_purpose_subagent.system_prompt → HarnessProfile.base_system_prompt → SDK GP 默认值。GP 特定的意图在 GP 子代理中优先。
内置中间件
Deep Agent 开箱即用的中间件,覆盖常见需求
中间件是横切关注点的解决方案——不侵入业务逻辑,却能拦截和增强每一步操作。
| 中间件 | 功能 | 何时使用 |
|---|---|---|
TodoListMiddleware |
跟踪和管理待办事项列表 | 多步骤任务需要进度追踪时 |
FilesystemMiddleware |
处理文件读写和目录导航 | 默认启用,通常不需手动配置 |
SubAgentMiddleware |
生成和协调子代理 | 需要委派任务给专门代理时 |
SummarizationMiddleware |
压缩长对话历史 | 对话超出上下文窗口时 |
PatchToolCallsMiddleware |
修复中断的工具调用消息 | 工具调用被取消或中断时 |
AnthropicPromptCachingMiddleware |
减少 Anthropic token 消耗 | 使用 Anthropic 模型时 |
memory 参数时自动加入 MemoryMiddleware;提供 skills 参数时自动加入 SkillsMiddleware;提供 interruptOn 参数时自动加入 HumanInTheLoopMiddleware。
预置中间件
LangChain 提供额外的预置中间件,添加各种功能:
- 重试——自动重试失败的工具调用
- 回退——主模型失败时切换备用模型
- PII 检测——过滤敏感个人信息
- 摘要——
createSummarizationMiddleware压缩长对话历史
特定提供商的中间件
针对特定 LLM 提供商优化的中间件:
- 官方集成——LangChain 团队维护的提供商适配中间件
- 社区集成——社区贡献的第三方中间件
- 覆盖缓存优化、速率限制、格式转换等场景
自定义中间件
用 createMiddleware 拦截工具调用,实现日志、监控等横切逻辑
自定义中间件的核心是 wrapToolCall 钩子——在工具执行前后插入你的逻辑。
import { tool, createMiddleware } from "langchain";
import { createDeepAgent } from "deepagents";
import { z } from "zod";
// 一个天气工具
const getWeather = tool(
({ city }: { city: string }) => `${city} 的天气是晴天。`,
{
name: "get_weather",
description: "获取某个城市的天气。",
schema: z.object({ city: z.string() }),
}
);
// 自定义中间件:在每次工具调用前后记录日志
const logToolCallsMiddleware = createMiddleware({
name: "LogToolCallsMiddleware",
// wrapToolCall 钩子:拦截每次工具调用,可在执行前后插入自定义逻辑
wrapToolCall: async (request, handler) => {
const toolName = request.toolCall.name;
console.log(`[调用] ${toolName}`, request.toolCall.args); // 记录调用前的参数
const result = await handler(request); // 调用 handler 执行原始工具
console.log(`[完成] ${toolName}`); // 记录调用完成
return result; // 将结果返回给 Agent
},
});
// 中间件按数组顺序依次执行,可叠加多个
const agent = createDeepAgent({
model: "openai:gpt-5.4",
tools: [getWeather],
middleware: [logToolCallsMiddleware],
});
如果你需要在多次 hook 调用之间追踪值(例如计数器或累积数据),请使用图状态(graph state)。图状态在设计上是按线程隔离的,因此在并发环境下更新是安全的。
推荐做法(使用图状态):
const customMiddleware = createMiddleware({
name: "CustomMiddleware",
beforeAgent: async (state) => {
return { x: (state.x ?? 0) + 1 }; // 更新图状态
},
});
不要这样做(修改外部变量):
let x = 1;
const customMiddlewareBad = createMiddleware({
name: "CustomMiddleware",
beforeAgent: async () => {
x += 1; // 修改变量会导致竞态条件!
},
});
原地修改(例如修改 state.x、修改共享变量,或在 hook 中更改其他共享值)可能会导致难以排查的 bug 和竞态条件,因为许多操作是并发运行的(子代理、并行工具调用,以及不同线程上的并行调用)。
代码解释器
添加 eval 工具,让代理在沙箱中执行 JavaScript
解释器为代理提供了一个 QuickJS 运行时。当代理需要批量处理数据、组合工具调用、在代码中处理错误时,解释器非常有用。
import { createDeepAgent } from "deepagents";
import { createCodeInterpreterMiddleware } from "@langchain/quickjs";
// 注意:npm 包 @langchain/quickjs 的部分版本实际导出的是 createQuickJSMiddleware
// (此时工具名为 js_eval 而非 eval)。若上述 import 报错,请改用:
// import { createQuickJSMiddleware } from "@langchain/quickjs";
// middleware: [createQuickJSMiddleware()]
// 两者是同一能力的不同封装层,详见官方文档。
// 添加 eval 工具:Agent 可以在 QuickJS 沙箱中执行 JavaScript
// 适用于批量处理数据、组合工具调用、复杂错误处理等场景
const agent = createDeepAgent({
model: "anthropic:claude-sonnet-4-6",
middleware: [createCodeInterpreterMiddleware()], // 自动为 Agent 添加 eval 工具
});
适用场景
- 需要以编程方式组合多个工具调用
- 批量处理结构化数据
- 在代码中实现错误处理和重试逻辑
- 转换复杂的数据格式
注意事项
- 运行在 QuickJS 沙箱中,非 Node.js 环境
- 不能访问文件系统或网络
- 适合纯计算和数据处理任务
- 需要完整 shell 环境请使用沙箱后端
子代理
隔离细节工作,避免主代理上下文爆炸
当任务变得复杂时,主代理的上下文会被大量中间结果塞满。子代理帮你把详细工作外包出去,主代理只看到最终结果。
import { tool } from "langchain";
import { createDeepAgent, type SubAgent } from "deepagents";
import { z } from "zod";
// 研究子代理——拥有独立上下文、工具和提示,不污染主代理
const researchSubagent: SubAgent = {
name: "research-agent", // 主代理通过 task 工具按名称委派
description: "深入研究特定问题的子代理", // 帮助主代理判断何时委派
systemPrompt: "你是一个优秀的研究员,请引用来源。", // 子代理的专属提示词
tools: [internetSearch], // 可选:子代理专用工具(不继承主代理工具)
model: "anthropic:claude-sonnet-4-6", // 可选:覆盖主代理的模型
};
const agent = createDeepAgent({
model: "openai:gpt-5.4",
subagents: [researchSubagent],
});
systemPrompt 扮演 BASE 角色,模型的 SUFFIX 会自动追加。
后端选择
虚拟文件系统后端决定了代理的文件存储和访问方式
Deep Agent 的工具使用虚拟文件系统。选择不同的后端,文件存储的位置和生命周期完全不同。
StateBackend(默认)
文件存储在 LangGraph 状态中,线程级持久化。
适用:无副作用的对话场景
安全隔离
FilesystemBackend
直接读写本地文件系统。代理能改你的真实文件。
适用:开发工具场景
需谨慎
StoreBackend
跨线程持久存储。数据在所有会话间共享。
适用:多用户部署、长期记忆
跨线程
LocalShellBackend
在宿主机上直接执行 shell 命令。提供文件系统工具以及 execute 工具。
适用:需要在本地环境执行命令的自动化场景
高危
CompositeBackend
混合后端——不同路径指向不同后端。
适用:同时需要临时和持久文件
灵活组合
ContextHubBackend
LangSmith Hub 仓库中的持久文件系统存储。
适用:利用 LangSmith Hub 托管的共享上下文
Hub 集成
// StateBackend(默认,无需显式配置)—— 文件存储在 LangGraph 状态中,线程级持久化
const agent = createDeepAgent();
// StoreBackend(跨线程持久化)—— 数据在不同对话间共享
import { StoreBackend } from "deepagents";
import { InMemoryStore } from "@langchain/langgraph";
const store = new InMemoryStore(); // 生产环境应使用持久化存储
const agent = createDeepAgent({
backend: new StoreBackend({ namespace: (rt) => [rt.serverInfo.user.identity] }), // 按用户隔离
store, // StoreBackend 需要传入 store 实例
});
// CompositeBackend(混合)—— 不同路径路由到不同后端
import { CompositeBackend, StateBackend } from "deepagents";
const agent = createDeepAgent({
backend: new CompositeBackend(new StateBackend(), { // 默认路径用 StateBackend
"/memories/": new StoreBackend({ namespace: () => ["memories"] }), // /memories/ 路径用 StoreBackend
}),
store,
});
// LocalShellBackend(本地 shell 执行——高危!)—— 直接在宿主机上执行命令
import { LocalShellBackend } from "deepagents";
const backend = new LocalShellBackend({ workingDirectory: "." }); // 工作目录设为当前目录
const agent = createDeepAgent({
model: "google-genai:gemini-3.1-pro-preview",
backend,
});
人在回路中
为敏感操作配置人工审批,防止代理越权执行
有些操作(删除文件、发送邮件)需要人工确认后才能执行。通过 interruptOn 配置审批规则。
import { tool } from "langchain";
import { createDeepAgent } from "deepagents";
import { MemorySaver } from "@langchain/langgraph";
import { z } from "zod";
const removeFile = tool(
async ({ path }: { path: string }) => `Deleted ${path}`,
{
name: "remove_file",
description: "删除文件",
schema: z.object({ path: z.string() }),
}
);
const notifyEmail = tool(
async ({ to, subject, body }) => `Sent email to ${to}`,
{
name: "notify_email",
description: "发送邮件",
schema: z.object({
to: z.string(),
subject: z.string(),
body: z.string(),
}),
}
);
const checkpointer = new MemorySaver(); // 内存检查点存储器,生产环境用数据库
const agent = createDeepAgent({
model: "openai:gpt-5.4",
tools: [removeFile, notifyEmail],
interruptOn: {
remove_file: true, // true = 每次调用前暂停,支持审批/拒绝/编辑
notify_email: { allowedDecisions: ["approve", "reject"] }, // 只允许批准或拒绝,不允许编辑参数
},
checkpointer, // 必需!暂停/恢复依赖检查点,没有它 interruptOn 会被忽略
});
技能系统
用 SKILL.md 文件为代理提供专业知识和操作指南
技能(Skills)不同于工具。工具是低级功能(搜索、读写文件),技能包含完整的任务说明、参考信息和模板。技能文件只在代理判断有用时才加载——渐进式披露,节省 token。
import { createDeepAgent, StateBackend, type FileData } from "deepagents";
import { MemorySaver } from "@langchain/langgraph";
import { createCodeInterpreterMiddleware } from "@langchain/quickjs";
const checkpointer = new MemorySaver();
const backend = new StateBackend();
// 从 GitHub 获取技能文件
const skillUrl = "https://raw.githubusercontent.com/langchain-ai/deepagentsjs/main/examples/skills/langgraph-docs/SKILL.md";
const skillContent = await (await fetch(skillUrl)).text();
const agent = createDeepAgent({
model: "openai:gpt-5.4",
backend,
checkpointer,
skills: ["/skills/"], // 技能源路径,Agent 会按需加载该目录下的 SKILL.md
middleware: [createCodeInterpreterMiddleware({ skillsBackend: backend })],
});
// 首次调用时通过 files 参数将技能文件写入后端
const result = await agent.invoke({
messages: [{ role: "user", content: "什么是 LangGraph?" }],
files: { "/skills/langgraph-docs/SKILL.md": createFileData(skillContent) }, // 技能内容
}, { configurable: { thread_id: `thread-${Date.now()}` } }); // thread_id 用于检查点隔离
记忆系统
用 AGENTS.md 文件为代理提供跨会话的持久上下文
记忆(Memory)使用 AGENTS.md 文件为代理提供额外的上下文信息。不同于技能的渐进式加载,记忆文件在每次调用时都会被读取。
import { createDeepAgent, type FileData } from "deepagents";
import { MemorySaver } from "@langchain/langgraph";
const checkpointer = new MemorySaver();
// 获取记忆文件
const agentsMd = await (await fetch("https://.../AGENTS.md")).text();
const agent = createDeepAgent({
model: "openai:gpt-5.4",
memory: ["/AGENTS.md"], // 记忆文件路径,每次调用都会加载(与技能的按需加载不同)
checkpointer,
});
// 通过 files 参数传入记忆文件内容,之后每次调用都会自动读取
const result = await agent.invoke({
messages: [{ role: "user", content: "你的记忆里有什么?" }],
files: { "/AGENTS.md": createFileData(agentsMd) }, // 首次调用时写入记忆文件
}, { configurable: { thread_id: "12345" } }); // 固定 thread_id 以复用检查点
技能 vs 记忆
| 维度 | 技能 | 记忆 |
|---|---|---|
| 加载时机 | 代理判断有用时 | 每次调用都读取 |
| 内容类型 | 操作指南 | 背景知识 |
| token 消耗 | 按需加载 | 始终占用 |
适用场景
- 用户偏好和个性化设置
- 项目背景和业务规则
- 数据库 Schema 和 API 文档
- 跨会话需要保持的上下文
结构化输出
让代理返回符合 Schema 的结构化数据,而非自由文本
通过 responseFormat 参数,你可以指定代理输出的结构。模型生成的数据会被捕获、验证,放在 structuredResponse 字段中返回。
import { createDeepAgent } from "deepagents";
import { z } from "zod";
// 定义输出结构 —— 用 Zod schema 约束 Agent 返回的字段和类型
const weatherReportSchema = z.object({
location: z.string().describe("天气报告的地点"),
temperature: z.number().describe("当前温度(摄氏度)"),
condition: z.string().describe("当前天气状况"),
humidity: z.number().describe("湿度百分比"),
forecast: z.string().describe("未来24小时预报"),
});
// 设置 responseFormat 后,Agent 会返回结构化 JSON 而非自由文本
const agent = createDeepAgent({
responseFormat: weatherReportSchema, // schema 中的 .describe() 帮助模型理解字段含义
tools: [internetSearch],
});
const result = await agent.invoke({
messages: [{ role: "user", content: "旧金山天气怎么样?" }],
});
// 结构化结果在 structuredResponse 字段中,类型安全可直接使用
console.log(result.structuredResponse);
// { location: "San Francisco", temperature: 18.3,
// condition: "Sunny", humidity: 48,
// forecast: "Clear skies with mild temperatures..." }
.describe() 会帮助模型理解每个字段的含义。
沙箱环境
在隔离环境中运行代理代码,不触碰本地文件系统
沙箱是一种特殊的后端——代理在隔离环境中运行,拥有自己的文件系统和 shell。你希望代理能写文件、跑命令,但不想让它改你本地机器时,用沙箱。
import { createDeepAgent } from "deepagents";
import { ChatAnthropic } from "@langchain/anthropic";
import { DenoSandbox, createDenoSandboxFactoryFromSandbox } from "@langchain/deno";
// 创建并初始化沙箱 —— 隔离环境,Agent 的代码执行不影响本地机器
const sandbox = await DenoSandbox.create({
memoryMb: 1024, // 内存限制:1GB
lifetime: "10m", // 存活时间:10 分钟后自动销毁
});
try {
const agent = createDeepAgent({
model: new ChatAnthropic({ model: "claude-opus-4-6" }),
systemPrompt: "You are a JavaScript coding assistant.",
backend: createDenoSandboxFactoryFromSandbox(sandbox), // 用沙箱构造后端工厂,Agent 在其中执行代码
});
const result = await agent.invoke({
messages: [{
role: "user",
content: "创建一个简单的 HTTP 服务器并用 curl 测试",
}],
});
} finally {
await sandbox.close(); // 务必关闭沙箱释放资源!放在 finally 块中确保异常时也能清理
}
sandbox.close() 释放资源,建议放在 finally 块中。
动手练习
构建一个带搜索、审批和子代理的研究助手
现在轮到你了。综合运用本章所学,构建一个研究助手:
需求:
1. 使用 Anthropic 的 claude-sonnet-4-6 模型
2. 拥有网络搜索能力(使用 TavilySearch)
3. 系统提示:专注于技术研究,引用来源
4. 拥有一个"写作子代理",负责将研究结果整理成报告
5. 发送邮件的操作需要人工审批
6. 使用 StateBackend 作为文件系统
任务 1:基础配置
- 选择模型并设置 API Key
- 编写系统提示(角色 + 工作流程)
- 定义 TavilySearch 工具并注册
任务 2:进阶功能
- 创建写作子代理(独立的 systemPrompt)
- 为邮件发送配置 interruptOn
- 添加 checkpointer 支持中断恢复
任务 3:测试验证
- 测试搜索工具是否被正确调用
- 验证邮件发送前是否会暂停等待审批
- 观察子代理的上下文是否隔离
本章回顾
你已经掌握了 Deep Agent 的全部配置维度
核心知识点
- 模型:字符串 / initChatModel / 模型类,三种方式灵活切换
- 工具:用
tool()定义,三要素是 name、description、schema - 系统提示:USER -> BASE/CUSTOM -> SUFFIX 的固定组装顺序
- 中间件:内置六种 + 自定义
createMiddleware - 子代理:隔离上下文,可独立配置工具和模型
- 后端:State / Filesystem / Store / Composite 四种选择
关键原则
- description 的质量决定工具选择准确性
- USER 提示始终优先,SUFFIX 始终在最后
- 中间件不要修改外部变量,使用图状态
- 人在回路中必须配合 checkpointer
- 技能按需加载,记忆每次都读取
- 结构化输出比解析自由文本更可靠
延伸阅读
继续本课程
- 下一章:Deep Agents 对比 -- 与其他框架的异同
- 后端详解 -- 各后端的深入对比
- 子代理详解 -- 子代理的高级用法
官方文档
- Deep Agents 完整文档索引
- API 参考
- GitHub 仓库 -- 源码和示例
深入主题
- 示例技能 -- 可直接使用的技能模板
- AGENTS.md 规范 -- 记忆文件的标准格式
- LangGraph.js 文档 -- 底层运行时