概述
本指南演示如何从零开始使用深度代理构建一个多步骤网络研究代理。该代理将研究问题分解为聚焦的任务,委派给专门的子代理,并将研究结果综合成一份全面的报告。 你将要构建的代理将能够:- 使用可选的待办事项列表中间件进行研究规划
- 将聚焦的研究任务委派给具有隔离上下文的子代理
- 在收集信息的过程中评估搜索结果并规划下一步行动
- 将研究结果连同适当的引文综合成最终报告
核心概念
本教程涵盖:前提条件
需要以下 API 密钥:设置
1
创建项目目录
mkdir deep-research-agent
cd deep-research-agent
2
安装依赖项
- Claude
- Gemini
npm
npm install deepagents @langchain/anthropic @langchain/core
npm
npm install deepagents @langchain/google-genai @langchain/core
3
设置 API 密钥
- Claude
- Gemini
export ANTHROPIC_API_KEY="your_anthropic_api_key"
export TAVILY_API_KEY="your_tavily_api_key"
export LANGSMITH_API_KEY="your_langsmith_api_key" # Optional
export GOOGLE_API_KEY="your_google_api_key"
export TAVILY_API_KEY="your_tavily_api_key"
export LANGSMITH_API_KEY="your_langsmith_api_key" # Optional
构建代理
在项目目录中创建agent.ts 文件:
1
添加工具
添加自定义搜索工具。
tavily_search 工具使用 Tavily 进行 URL 发现,然后获取完整的网页内容,以便代理可以分析完整的来源,而不是摘要。import { tool } from "langchain";
import { z } from "zod";
async function fetchWebpageContent(
url: string,
timeout = 10_000,
): Promise<string> {
try {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const response = await fetch(url, {
headers: {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
},
signal: controller.signal,
});
clearTimeout(id);
if (!response.ok) {
return `Error fetching ${url}: HTTP ${response.status}`;
}
return await response.text();
} catch (e) {
return `Error fetching ${url}: ${e}`;
}
}
const tavilySearch = tool(
async ({
query,
maxResults = 1,
topic = "general",
}: {
query: string;
maxResults?: number;
topic?: "general" | "news" | "finance";
}) => {
const response = await fetch("https://api.tavily.com/search", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.TAVILY_API_KEY}`,
},
body: JSON.stringify({ query, max_results: maxResults, topic }),
});
const data = (await response.json()) as {
results: Array<{ url: string; title: string }>;
};
const results = data.results ?? [];
const resultTexts: string[] = [];
for (const result of results) {
const content = await fetchWebpageContent(result.url);
resultTexts.push(
`## ${result.title}\n**URL:** ${result.url}\n\n${content}\n---`,
);
}
return (
`Found ${resultTexts.length} result(s) for '${query}':\n\n` +
resultTexts.join("\n")
);
},
{
name: "tavily_search",
description:
"Search the web for information on a given query. Uses Tavily to discover relevant URLs, then fetches and returns full webpage content.",
schema: z.object({
query: z.string().describe("Search query to execute"),
maxResults: z
.number()
.optional()
.default(1)
.describe("Maximum number of results to return (default: 1)"),
topic: z
.enum(["general", "news", "finance"])
.optional()
.default("general")
.describe(
"Topic filter - 'general', 'news', or 'finance' (default: 'general')",
),
}),
},
);
2
添加提示词
将编排工作流和子代理提示模板添加到
agent.ts:const RESEARCH_WORKFLOW_INSTRUCTIONS = `# 研究工作流
请遵循以下工作流处理所有研究请求:
1. **规划**:使用 write_todos 创建待办事项列表,将研究分解为专注的任务
2. **保存请求**:使用 write_file() 将用户的研究问题保存到 \`/research_request.md\`
3. **研究**:使用 task() 工具将研究任务委派给子代理 - 务必使用子代理进行研究,切勿亲自操作
4. **综合**:审查所有子代理的发现并整合引用(在所有发现中,每个唯一 URL 对应一个编号)
5. **撰写报告**:将综合性最终报告写入 \`/final_report.md\`(参见下方报告撰写指南)
6. **验证**:阅读 \`/research_request.md\` 并确认您已使用适当的引用和结构覆盖所有方面
## 研究规划指南
- 将类似的研究任务批量放入单个待办事项以最小化开销
- 对于简单的事实查询问题,使用 1 个子代理
- 对于比较或多方面主题,委派给多个并行子代理
- 每个子代理应研究一个具体方面并返回发现
## 报告撰写指南
将最终报告写入 \`/final_report.md\` 时,请遵循以下结构模式:
**对于比较:**
1. 引言
2. 主题 A 概述
3. 主题 B 概述
4. 详细比较
5. 结论
**对于列表/排名:**
直接列出项目及详情 - 无需引言:
1. 项目 1 及说明
2. 项目 2 及说明
3. 项目 3 及说明
**对于摘要/概述:**
1. 主题概述
2. 关键概念 1
3. 关键概念 2
4. 关键概念 3
5. 结论
**通用指南:**
- 使用清晰的节标题(## 用于节,### 用于子节)
- 默认使用段落形式撰写 - 注重文本内容,而非仅使用项目符号列表
- 切勿使用自指语言(例如“我发现...”、“我研究了...”)
- 以专业报告形式撰写,避免元评论
- 每节应全面且详细
- 仅当列表比叙述更合适时才使用项目符号
**引用格式:**
- 使用 [1], [2], [3] 格式内联引用来源
- 为每个唯一 URL 在**所有**子代理发现中分配一个唯一的引用编号
- 报告末尾附上 ### 来源 部分,列出每个编号的来源
- 来源编号需顺序连续,无间断(1,2,3,4...)
- 格式:[1] 来源标题:URL(每行一个以确保正确渲染列表)
- 示例:
一些重要发现 [1]。另一个关键见解 [2]。
### 来源
[1] AI 研究论文:https://example.com/paper
[2] 行业分析:https://example.com/analysis
`;
const RESEARCHER_INSTRUCTIONS = `你是一名研究助理,正就用户输入的主题进行研究。补充信息:今天的日期是 {date}。
你的职责是使用工具来收集关于用户输入主题的信息。
你可以使用 tavily_search 工具来查找有助于回答研究问题的资源。
你可以串行或并行地调用它,你的研究在一个工具调用循环中进行。
你可访问 tavily_search 工具进行网络搜索。
像一个时间有限的人类研究者一样思考。遵循以下步骤:
1. **仔细阅读问题** - 用户需要哪些具体信息?
2. **从更宽泛的搜索开始** - 首先使用宽泛、全面的查询
3. **每次搜索后暂停并评估** - 我是否有足够信息回答?还缺少什么?
4. **随着信息的收集执行更具体的搜索** - 补上信息缺口
5. **当你能自信地回答时停止** - 不要为了追求完美而持续搜索
**工具调用预算**(防止过度搜索):
- **简单查询**:最多使用 2-3 次搜索工具调用
- **复杂查询**:最多使用 5 次搜索工具调用
- **始终停止**:如果 5 次搜索工具调用后仍找不到合适来源,则停止
**立即停止的条件**:
- 你能全面回答用户的问题
- 你有 3 个或以上与问题相关的例子/来源
- 你最近的 2 次搜索返回了相似的信息
每次搜索后,评估结果后再继续:我找到了什么关键信息?还缺什么?我是否有足够信息回答?是应该继续搜索还是提供答案?
当将你的发现返回给协调器时:
1. **组织你的回答**:用清晰的标题和详细的解释来组织发现
2. **内嵌引用来源**:在引用搜索到的信息时使用 [1]、[2]、[3] 格式
3. **包含来源部分**:以 ### 来源 结尾,列出每个带编号的来源及其标题和 URL
示例:
## 主要发现
情境工程是智能体 AI 的关键技术 [1]。研究表明,正确的情境管理可以将性能提升 40% [2]。
### 来源
[1] 情境工程指南: https://example.com/context-guide
[2] AI 性能研究: https://example.com/study
协调器会将所有子智能体的引用整合到最终报告中。
`;
const SUBAGENT_DELEGATION_INSTRUCTIONS = `# 子代理研究协调
你的角色是通过委派任务给专门的研究子代理来协调研究工作。
## 委派策略
**默认:从 1 个子代理开始** 用于大多数查询:
- "什么是量子计算?" -> 1 个子代理(概述性任务)
- "列出旧金山最好的10家咖啡馆" -> 1 个子代理
- "总结互联网历史" -> 1 个子代理
- "为AI代理研究情境工程" -> 1 个子代理(涵盖所有方面)
**仅当查询明确要求进行比较或存在明显独立的部分时才进行并行处理:**
**明确比较** -> 每个比较对象使用 1 个子代理:
- "比较OpenAI、Anthropic和DeepMind的AI安全方法" -> 3个并行子代理
- "比较Python和JavaScript用于Web开发" -> 2个并行子代理
**明显分离的部分** -> 每个部分使用 1 个子代理(谨慎使用):
- "研究欧洲、亚洲和北美的可再生能源采用情况" -> 3个并行子代理(地理分离)
- 仅当单次综合搜索无法高效覆盖各部分时才使用此模式
## 关键原则
- **倾向于使用单个子代理**:一个综合性的研究任务比多个狭窄的任务更节省token
- **避免过早分解**:不要将"研究X"分解为"研究X概述"、"研究X技术"、"研究X应用" - 直接使用1个子代理处理所有关于X的内容
- **仅对明确比较进行并行处理**:在比较不同实体或地理上分离的数据时使用多个子代理
## 并行执行限制
- 每次迭代最多使用 {maxConcurrentResearchUnits} 个并行子代理
- 在单个响应中发出多个 task() 调用以实现并行执行
- 每个子代理独立返回研究结果
## 研究限制
- 如果在 {maxResearcherIterations} 轮委派后仍未找到足够来源,则停止
- 当你拥有足以全面回答问题的足够信息时停止
- 倾向于专注研究而非穷尽式探索`;
3
启用任务规划
任务规划是可选的。研究工作流使用 在下一步创建代理时将包含此中间件。
write_todos 将问题分解为聚焦的任务,因此在创建代理时请传递 TodoListMiddleware。import { todoListMiddleware } from "langchain";
4
创建代理
将模型初始化和代理创建添加到
agent.ts。包含 todoListMiddleware 以便规划工具可用:import { createDeepAgent } from "deepagents";
import { ChatAnthropic } from "@langchain/anthropic";
import { todoListMiddleware } from "langchain";
// 最大并发研究单元数量
const maxConcurrentResearchUnits = 3;
// 研究员最大迭代次数
const maxResearcherIterations = 3;
// 获取当前日期
const currentDate = new Date().toISOString().split("T")[0];
// 构造系统指令
const INSTRUCTIONS =
RESEARCH_WORKFLOW_INSTRUCTIONS +
"\n\n" +
"=".repeat(80) +
"\n\n" +
SUBAGENT_DELEGATION_INSTRUCTIONS.replace(
"{maxConcurrentResearchUnits}",
String(maxConcurrentResearchUnits),
).replace("{maxResearcherIterations}", String(maxResearcherIterations));
// 定义研究子代理
const researchSubAgent = {
name: "research-agent",
description: "将研究工作委派给子代理。每次给出一个主题。",
systemPrompt: RESEARCHER_INSTRUCTIONS.replace("{date}", currentDate),
tools: [tavilySearch],
};
// 初始化语言模型
const model = new ChatAnthropic({
model: "claude-sonnet-4-5-20250929",
temperature: 0,
});
// 创建深度代理实例
const agent = await createDeepAgent({
model,
tools: [tavilySearch],
systemPrompt: INSTRUCTIONS,
subagents: [researchSubAgent],
middleware: [todoListMiddleware()],
});
运行代理
你可以同步运行代理,即等待完整结果然后打印出来,或者可以在结果流入时进行流式传输更新。 将相应标签页中的代码添加到agent.ts 底部:
- 同步运行
- 流式传输更新
{
async function main() {
const result = await agent.invoke({
messages: [
{
role: "user",
content:
"What are the main differences between RAG and fine-tuning for LLM applications?",
},
],
});
for (const msg of result.messages ?? []) {
if (msg.content) {
console.log(msg.content);
}
}
}
main().catch((err) => {
console.error(err);
process.exitCode = 1;
});
}
{
async function main() {
const stream = await agent.streamEvents(
{
messages: [
{
role: "user",
content: "比较Python与JavaScript在Web开发中的应用",
},
],
},
{ version: "v3" },
);
for await (const message of stream.messages) {
for await (const token of message.text) {
process.stdout.write(token);
}
}
}
main().catch((err) => {
console.error(err);
process.exitCode = 1;
});
}
npx tsx agent.ts
LANGSMITH_API_KEY 环境变量,你可以在 LangSmith 中查看代理的追踪信息,以调试和监控多步骤行为。
完整代码
在 GitHub 上查看完整的深度研究示例。后续步骤
既然你已经构建了代理,可以通过修改代理文件中的提示常量来自定义它,以调整工作流、委派策略或研究员行为。 你还可以调整委派限制,以允许更多的并行子代理或委派轮次。 要了解更多关于本教程中概念的信息,请查看以下资源:- 子代理:学习如何配置具有不同工具和提示的子代理
- 自定义:自定义模型、工具、系统提示以及可选的任务规划
- LangSmith:追踪研究运行并调试多步骤行为
- 深度研究课程:关于使用 LangGraph 进行深度研究的完整课程
Connect these docs to Claude, VSCode, and more via MCP for real-time answers.

