从零开发 Deep Research Agent
2025.6.18
前期回顾
前言
DeerFlow 是一款开源的 Deep Research 框架,它利用大模型的推理能力进行课题研究的自主规划,在话题深度和广度上进行拓展,并且利用大模型调用工具的特性自动调取网页搜索、RAG、Python 执行器,最终生成高质量的图文报告及播客。DeerFlow 还支持通过 MCP 的方式进行工具扩展。DeerFlow 在 Github 上发布之后,在开源界获得了不错的反响,仅 7 天就收获了 10k Stars,可能是字节跳动 Stars 数增长最快的项目。技术分享文章的阅读量在内网也超过了 1.4 万阅读量,在公众账号上阅读量破万并被转发了数千次。在 2025 年火山大会后,DeerFlow 已在在火山引擎的“函数服务”中上线,可实现一键部署。有关 DeerFlow 的更多信息,欢迎访问我们的第一篇文章及技术分享视频(1.4 万+ 阅读量)。这已经是我们第四次分享 Deep Research 相关的 LLM 开发内容,以下是前四期:
❤️ Github 仓库(求 Star)github.com/bytedance/deer-flow🌐 官网deerflow.tech暂时无法在飞书文档外展示此内容暂时无法在飞书文档外展示此内容从2025年12月起,我们建立了 LLM 学习社区,现在已经有 19 个群超过 9,000 位同学参与。快来加入我们吧~
本期大纲
DeerFlow 之所以在开源界广受欢迎,一个重要的原因就是——代码逻辑简单易懂。今天我们基于 LangChain 及 LangGraph 框架,就可以快速复刻一个“快 \ 糙 \ 猛”的 Deep Research 应用。本文的目标是通过实际案例,带领读者从零开始构建一个基于 LangChain 和 LangGraph 的多智能体系统——Deep Research。我们将带你从零开始,探索如何利用 LangChain 和 LangGraph 构建一个多智能体协作的 Deep Research 应用。通过层层递进的讲解,你将了解如何从基础工具到复杂架构:
# 创建一个具备“先搜索再规划”的 Planner 智能体
planner = create_react_agent(
doubao_pro_model,
tools=[web_search],
prompt=apply_prompt_template("planner"),
name="planner",
)
# 创建一个拥有“搜索”和“网页爬虫”的 Researcher 智能体
# 你也可以在此添加自己的工具,接入知识库、提供 Browser Use 等
researcher = create_react_agent(
doubao_pro_model,
tools=[web_search, web_crawl],
prompt=apply_prompt_template("researcher"),
name="researcher",
)
# 将它们通过 Supervisor Multi-Agent 架构连接起来
supervisor = create_supervisor(
[planner, researcher],
model=chat_model,
prompt=apply_prompt_template("supervisor"),
).compile()
# 无需开发前端,直接执行 `langgraph dev` 命令,
# 你就可以在浏览器中使用 Deep Research 了。在文章的最后,你会发现我们只需要上面这 3 句 Python 代码就可以实现一个一步步实现一个能够高效搜索、全自动规划、支持深度思考和多步任务迭代执行的多智能体应用——Deep Research。无论你是技术开发者还是对智能体设计感兴趣的读者,这篇文章都将为你打开新的视野。准备好了吗?让我们一起进发!
代码开源
本文的相关代码已经在 code.bytedance.net 上开源(L2,申请即可,免批):暂时无法在飞书文档外展示此内容
Deep Wiki
Deep Research Mini 项目已经接入 AIME 的 Deep Wiki,请通过下面的链接进行查看。暂时无法在飞书文档外展示此内容
6月19日讲座录屏
📢 在6月19日 11:00-12:00,我为大家带来第二期讲座——用 LangChain 从零开发一个 Deep Research 应用。Part 1暂时无法在飞书文档外展示此内容Part 2暂时无法在飞书文档外展示此内容
准备工作
在 DeerFlow 中,我们使用时下流行的 uv 来进行 Python 包管理,本文也不例外。UV: 一个用 Rust 编写的极快的 Python 包和项目管理器如果到现在还没有听过 UV 你就落伍了,UV 可以:
- 🚀 一款能替代
pip、pip-tools、pipx、poetry、pyenv、twine、virtualenv等工具的单一工具。 - ⚡️ 比
pip快 10 - 100 倍。 - 🐍 自带了Python 虚拟环境。
- 🗂️ 提供全面的项目管理,带有版本锁定文件。
- 💾 磁盘空间利用高效,带有全局缓存以去除重复依赖。
在本地顺利安装完 uv 后,让我们通过下面的命令行,对 deep-research-mini 项目进行初始化,并且安装 LangChain 和 LangGraph 等依赖包。
uv init deep-research-mini
cd deep-research-mini
mkdir src
uv add jinja2 firecrawl-py socksio
uv add langchain langchain-openai langchain_tavily
uv add langgraph langgraph-supervisor现在你的工程应该像左图那样。截止至 2025 年 6 月 17 日,当前的包版本为:
- python >= 3.12
- firecrawl-py >= 2.8.0
- jinja2 >= 3.1.6
- langchain >= 0.3.25
- langchain-openai >= 0.3.23
- langchain-tavily >= 0.2.3
- langgraph >= 0.4.8
- langgraph-supervisor >= 0.0.27
本文推荐使用 Trae 作为 Python IDETrae 对 LangChain 及 LangGraph 等开源框架较为熟悉,可以生成较高质量的代码。
模型(5 分钟)
本文使用 doubao-1-5-pro-32k-250115 作为默认的模型,你也可以将它替换为其他的非推理模型(如 DeepSeek V3 或 OpenAI GPT-4o 等)。你可能并不需要使用推理模型细心的读者可能要问,为什么不使用 doubao-seed-1.6 或者 deepseek-r1 模型呢?Deep Research 其实是一个相对来说较简单的任务,因此出于性能和成本考虑,我们只需要使用非推理模型即可完成任务。当然,如果你有兴趣的话可以在第五节中,将 planner 替换为推理模型。在 LangChain 中,集成线上服务的大语言模型只需要一行代码即可。
import os
from langchain_openai import ChatOpenAI
chat_model = ChatOpenAI(
model="doubao-1-5-pro-32k-250115",
base_url="https://ark-cn-beijing.bytedance.net/api/v3",
api_key=os.getenv("DOUBAO_API_KEY"), # 你需要自己设置环境变量
temperature=0,
)你需要在环境变量中,将DOUBAO_API_KEY设置为自己在方舟上的API_KEY。
如上面的代码所示,由于方舟采用的是 OpenAI 兼容性 API 网关,因此在这里我们选用 ChatOpenAI 作为模型对象的类型,并且将默认的地址和 API_KEY 修改为方舟上对应的设置。除了方舟外,DeepSeek 可以用 ChatDeepSeek 类接入。你可以用下面的代码来测试模型是否成功接入:
print(chat_model.invoke("你好,豆包").content)当然,更常见的场景是写一段 System Prompt,并且让大模型根据你给出的 RAG 结果回答用户的问题。在写代码前,我们再次要搬出 Meta Prompt,也就是让大模型生成 Prompt:
如左图所示,界面左侧就是我们团队祖传的 Meta Prompt,而右侧的第一句话是我们为这个任务简单写的中文提示词,右下侧则是大模型根据中文生成完整的英文 System Prompt。怎么样?是不是超级简单?感兴趣的同学可以继续阅读我的这篇文章(1 万+ 阅读量),了解更多关于 Meta Prompt 的知识。接下来,我们需要做的就是将 Meta Prompt 生成的 System Prompt 嵌入到代码中,并且模拟 RAG 调用的结果(第一条 HumanMessage),以及用户的原始问题(第二条 HumanMessage),这样在执行后,你就可以得到一个满意的答复。
from langchain.schema import SystemMessage, HumanMessage
import json
from src.models import chat_model
res = chat_model.invoke(
[
SystemMessage(
content="""Provide answers to the user's questions based on the context they provide.
# Steps
1. Carefully read and understand the context provided by the user.
2. Analyze the question in relation to the given context.
3. Use logical reasoning to derive the answer based on the context.
4. If the context is insufficient to answer the question, politely ask for clarification or additional details.
# Output Format
- Provide a clear and concise answer in complete sentences.
- If reasoning is required, include the reasoning process before presenting the conclusion.
# Notes
- Ensure the response is directly relevant to the user's question and context.
- Avoid making assumptions beyond the provided context unless explicitly requested."""
),
HumanMessage(
content=json.dumps(
[
{
"title": "欧国联决赛:C罗破门,带队夺冠,葡萄牙点球6-5击败西班牙!|c罗|门迪|尼科|葡萄牙队|豪尔赫·门德斯_网易订阅",
"url": "https://www.163.com/dy/article/K1JCRTCN05567SBP.html",
"content": "北京时间6月9日,欧国联决赛,葡萄牙2-2战平西班牙,点球大战总比分6-5取胜夺冠!这场比赛踢得非常精彩,西班牙的传控确实厉害,他们整体的技术和脚法是世界顶级的,西班牙队无论面对任何对手,都能在控球率方面占据优势,而葡萄牙同样很强,在推进到对方的禁区前沿时,葡萄牙有很多种",
},
{
"title": "欧国联决赛:葡萄牙5-3点球胜西班牙,C罗闪耀,金球奖归属引关注 - 知乎",
"url": "https://zhuanlan.zhihu.com/p/1915618913749795333",
"content": "欧国联 最后一轮踢完了。 葡萄牙在决赛5-3点球战胜西班牙,第二次拿冠军。法国打德国2-0赢了,拿了第三。c罗这场比赛进了球,成了国家队历史第一射手, 金球奖 基本没悬念了。 法国和德国那场球, 姆巴佩 表现很猛。 上半场他带球进禁区自己射门得分,下半场又给队友传球让对方空门进球。",
},
{
"title": "欧国联决赛葡萄牙点球5-3胜西班牙,C罗门德斯建功",
"url": "https://sports.sina.com.cn/g/2025-06-09/doc-inezmvxn4600761.shtml",
"content": "🆚欧国联a级决赛|葡萄牙2-2西班牙(点球5-3)🎯努诺·门德斯传射建功,巴黎四人随队夺冠🎞第21分钟,苏比门迪补射破门,西班牙1-0葡萄牙。🎞第",
},
{
"title": "欧国联:葡萄牙点球大战7-5西班牙第二次夺冠 C罗破门+伤退",
"url": "https://news.qq.com/rain/a/20250609A01I7S00",
"content": "欧国联-葡萄牙2-2点球5-3西班牙第2次欧国联夺冠!c罗破门扳平+伤退,莫拉塔失点. 北京时间6月9日凌晨,欧国联迎来最终的决赛,葡萄牙大战西班牙。",
},
{
"title": "40岁C罗收获生涯第36冠!葡萄牙点球7-5战胜西班牙,欧国联夺冠_澎湃号·媒体_澎湃新闻-The Paper",
"url": "https://www.thepaper.cn/newsDetail_forward_30952562",
"content": "40岁C罗收获生涯第36冠!葡萄牙点球7-5战胜西班牙,欧国联夺冠_澎湃号·媒体_澎湃新闻-The Paper Image 1: 澎湃Logo * _要闻_ * _深度_ * _直播_ * _视频_ * _更多_ **下载客户端** 登录 无障碍) 40岁C罗收获生涯第36冠!葡萄牙点球7-5战胜西班牙,欧国联夺冠 Image 2 上游新闻 关注 重庆 来源:澎湃新闻·澎湃号·媒体 字号 北京时间6月9日,在欧国联决赛中,葡萄牙点球7-5战胜西班牙,队史第二次夺得欧国联冠军!40岁C罗连场破门,门德斯建功,祖比门迪、奥亚萨瓦尔破门。 Image 3Image 4 葡萄牙国家队官方发文庆祝夺冠: 我们又做到了!第二次捧起欧国联的奖杯,属于葡萄牙的荣耀,再次刻进这段绿茵史。不是奇迹,是坚持;不是偶然,是一代又一代的传承与信念。我们,是欧国联历史上夺冠最多的那支队伍! Image 5 本场比赛是C罗国家队生涯至今参加的第4场决赛,此前分别是2004年欧洲杯决赛、2016年欧洲杯决赛(因伤离场)、2019年欧国联决赛,仅2004年欧洲杯未能夺冠。至此,40岁C罗职业生涯已夺36冠:国家队3冠,俱乐部33冠。 Image 6 赛后在接受采访时,C罗谈到了自己的伤势,他表示,热身时自己就感觉有些不适。 C罗这样谈道:“我在热身时就已经感觉到了,已经有一段时间了。但为了国家队,即使要断腿我也愿意。这是一个冠军,我必须上场,我尽了全力,坚持到了最后一刻,还打进了一球。” “我非常高兴。首先是为了这一代球员,他们值得一个冠军。为了我们的家人,我的家人都在这里……为葡萄牙赢得胜利是特别的。我有很多冠军头衔,但没有什么比为葡萄牙赢得胜利更美好的了。泪水和完成使命的感觉……这是美妙的。这是我们的国家。我们是一个小国,但有着远大的抱负。” “我曾在很多国家和俱乐部踢过球,但当人们提到葡萄牙时,那种感觉是特别的。作为这支球队的队长,我感到非常自豪,赢得冠军总是国家队的最高荣誉。未来我会考虑短期目标,我受了伤,而且伤势加重了……但我还是坚持了下来,因为为了国家队你必须全力以赴。” 关于主教练马丁内斯,C罗表示:“我为他感到非常高兴,他是一个西班牙人,但他为我们的国家付出了最大的努力。我们已经赢得了这个冠军,但这只是我们的动力,是我们渴望更多的开始。” Image 7 知名体育评论员詹俊赛后点评道: 逆转之夜!葡萄牙队两次落后两次扳平比分,最终在点球决战力克西班牙再次夺得欧国联冠军。C罗vs亚马尔,2003年18岁的C罗迎来在国家队的首秀,二十二年后40岁的C罗仍然能打进关键入球拿到个人第三项国际大赛的冠军。而亚马尔略显平淡因为碰到克星——努诺-门德斯,这位大巴黎的左后卫是个人心目中本场的MVP,“世界第一左后卫”的封号当之无愧。西班牙队的右路防守是软肋,明年世界杯他们还是需要卡瓦哈尔。 Image 8 本文为澎湃号作者或机构在澎湃新闻上传并发布,仅代表该作者或机构观点,不代表澎湃新闻的观点或立场,澎湃新闻仅提供信息发布平台。申请澎湃号请用电脑访问http://renzheng.thepaper.cn。 * Image 9### C罗进球封王!这支葡萄牙让人想起了2022年的阿根廷 * Image 10### C罗语出惊人:金球奖失去公信力 * Image 11### 体坛联播|C罗进球助葡萄牙夺冠,阿尔拉卡斯卫冕法网 * Image 12_01:00_### 王大雷国脚生涯谢幕倒计时:希望给年轻球员做好榜样 * Image 13### C罗进球封王!这支葡萄牙让人想起了2022年的阿根廷 * Image 14### C罗语出惊人:金球奖失去公信力 * Image 15### 体坛联播|C罗进球助葡萄牙夺冠,阿尔拉卡斯卫冕法网 * Image 16_01:00_### 王大雷国脚生涯谢幕倒计时:希望给年轻球员做好榜样 * Image 17### C罗进球封王!这支葡萄牙让人想起了2022年的阿根廷 * Image 18### C罗语出惊人:金球奖失去公信力 * Image 19### 体坛联播|C罗进球助葡萄牙夺冠,阿尔拉卡斯卫冕法网 * Image 20_01:00_### 王大雷国脚生涯谢幕倒计时:希望给年轻球员做好榜样 Image 21 Image 22 Image 23 Image 24 Image 25 Image 26 * 报料邮箱: news@thepaper.cn Image 28 Image 29",
},
]
)
),
HumanMessage(content="欧足联决赛有什么看点?"),
]
)
print(res.content)工具(10 分钟)
有人说这一次 AI 革命最重要的特征就是让 AI 学会制造并使用“工具”实际上并没有这么可怕,对于大模型来说所谓“工具”就是可以被调用的“函数方法”。作为一个单纯的文本模型来说,当它需要调用某个工具时,它输出的其实就是函数名称和对应的参数。如果要自己实现一套大模型工具到反射方法的调用原本是一件复杂的事情,但是好在我们有了 LangChain。
使用社区贡献的工具
3 年来 LangChain 及 LangChain 社区积累了大量的工具集,例如下面这短短几行代码就可以将 Tavily Search 搜索引擎集成进来:
from langchain_tavily import TavilySearch
web_search = TavilySearch(
name="web_search",
max_results=5,
)你需要在环境变量中,将TAVILY_API_KEY设置为自己在 tavily.com 上免费申请的API_KEY。
上面的代码中,我们首先创建了 TavilySearch 工具类的实例,并且将这个工具重新命名为 web_search。此外,我们还设置了该工具的选项参数(如 max_results)。从外部调用 LangChain 工具的方式十分简单,你只需要用invoke() 或 ainvoke() 即可:
web_search.invoke("欧足联决赛有什么看点?")自定义工具
除了 LangChain 社区已有的工具集外,你也可以将自己的方法、服务快速封装成自定义工具。LangChain 中最常见的自定义工具有两种:
- 继承自
BaseTool类,实现一个工具类。 - 通过
@tool注解,将几乎任意一个函数声明为一个工具。例如:
import os
from firecrawl import FirecrawlApp
from langchain.tools import tool
@tool
def web_crawl(url: str) -> str:
"""
Crawl a website and return the markdown content.
Args:
url: The URL of the website to crawl.
Returns:
The markdown content of the website.
"""
firecrawl = FirecrawlApp(api_key=os.getenv("FIRECRAWL_API_KEY"))
response = firecrawl.scrape_url(
url=url,
formats=["markdown"],
only_main_content=True
)
return response.markdown你需要在环境变量中,将 FIRECRAWL_API_KEY 设置为自己在 firecrawl.dev 上免费申请的 API_KEY。上面这段代码中,web_crawl 就是一个平凡的函数,集成了 FireCrawl 的爬虫功能而已。然而,通过 @tool 注解之后,它就变成了大模型可以调用的工具。工具的入参会由大模型根据上下文填写,而工具函数的返回值则会在序列化后返还给模型。函数的注释至关重要为了让大模型能够正确使用函数,你必须通过 PyDoc 的注释对函数进行说明。换句话说,函数注释就是 Prompt 中的一部分,注释写的好坏,直接决定了大模型是否可以正确消费。现在你知道为什么一些 MCP 服务虽然功能有了但大模型总是调用不对是为什么了吧。
AGI Hub 插件市场
真的不是我想打广告,而是在国内业务中“搜索引擎”可能必须得使用咱们字节跳动自有的搜索引擎——头条搜索,好在笔者就在 PDI - 搜索团队,所负责的 AGI Hub 平台正好就是域内唯一提供全网搜索服务的接口。
点击这里查看接入头条搜索及其他插件并不复杂,下面这段代码通过 HTTP 调用就可以快速集成。但是由于“搜索”是一个高敏业务,因此你仍然需要去以业务方的身份申请 AK,或请直接与产品经理 @周颖 联系。
curl --location --request POST 'https://search.bytedance.net/plugin/openapi/online/single' \
--header 'Content-Type: application/json' \
--data-raw '{"plugin_id":"47","tool_name":"SearchPlugin","ak":"ABCDEFGAK","thought":"{\"input_query\":\"DeerFlow\"}"}'import json
import os
import requests
from langchain.agents import tool
@tool
def web_search(input_query: str) -> str:
"""
Search the web for the given query.
Args:
query: The query to search for.
Returns:
The search results.
"""
url = "https://search.bytedance.net/plugin/openapi/online/single"
headers = {
"Content-Type": "application/json",
}
data = {
"plugin_id": "47",
"tool_name": "SearchPlugin",
"ak": os.getenv("AGIHUB_API_KEY"),
"thought": json.dumps({"input_query": input_query}),
}
res = requests.post(url, headers=headers, json=data)
return res.json()["plugin_observation"]["natural_language_desc"]你需要在环境变量中,将 AGIHUB_API_KEY 的值设置为自己申请到的 AK。当你通过 web_search("豆包 1.6 thinking 发布")搜索时,你会得到下面这段被加工过的搜索结果:
本文与问题相关,一般不权威,非常满足时效需求
本文标题:豆包大模型1.6正式发布,当贝AI官宣接入-华夏文学(华夏文学)
本文内容:豆包大模型1.6正式发布,当贝AI官宣接入 2025 年6 月 11 日,字节跳动旗下火山引擎在 force 原动力大会上正式推出豆包大模型 1.6 系列,并同步发布了视频生成模型 seedance 1.0 pro 等创新产品。
同日,国内智能大屏头部企业当贝旗下的 ai 平台——当贝 ai 正式宣布全面接入豆包大模型 1.6,加速推动 ai 技术在消费级场景的广泛应用。豆包大模型 1.6:全模态能力重塑 AI 生产力 作为国产大模型的代表性产品,豆包大模型 1.6 系列通过三大子模型实现技术突破:Doubao-Seed-1.6:国内首个支持 256K 超长上下文的“全能型”综合模型,具备深度思考、多模态理解与图形界面操作功能。其自适应推理机制可根据任务复杂程度自动切换深度分析模式,在保证效果的同时显著减少 token 消耗,适用于长文本处理、多轮复杂对话等高阶场景。立即进入“豆包AI人工智官网入口”;立即学习“豆包AI人工智能在线问答入口”;Doubao-Seed-1.6-thinking:本次升级的核心亮点,进一步强化了复杂任务的理解与执行能力。在代码编写、数学运算、逻辑判断、指令执行等方面均有明显提升,同样支持 256K 上下文和多模态推理。在高考全国新一卷数学测试中,豆包大模型取得了144 分的优异成绩,在同类模型中位列全国第一;在海淀模拟全卷考试中,理科得分达到 706 分,文科为 712 分,较上一代模型均有显著进步。Doubao-Seed-1.6-flash:极速响应版本,可实现毫秒级输出,文本理解能力相较轻量模型大幅提升,视觉识别水平已达到行业主流水准,特别适合用于智能设备、实时交互系统等对延迟敏感的应用场景。豆包大模型 1.6 首创统一区间定价策略,在企业常用的0-32K 输入范围内,整体成本仅为前代产品的三分之一,输出价格低至 2 元 / 百万 tokens。
本文链接:https://m.cn486.com/news/3733867/
发布时间:2025年6月16日0时0分
本文与问题相关,一般不权威,非常满足时效需求
本文标题:豆包大模型再蜕变:跻身全球前列,加速Agent应用落地(emcreative.eastmoney.com)
本文内容:豆包大模型再蜕变:跻身全球前列,加速Agent应用落地 近日,豆包大模型发布全新的1.6模型,其综合能力“火力全开”。新版本不仅在推理、数学、指令遵循等核心能力上实现跨越式提升,还再次大幅降低了用户使用门槛,加速AI Agent在消费电子、汽车、金融等行业的规模化落地。核心能力提升,跻身全球前列 近日,豆包大模型发布全新的1.6模型,其综合能力“火力全开”。新版本不仅在推理、数学、指令遵循等核心能力上实现跨越式提升,还再次大幅降低了用户使用门槛,加速AI Agent在消费电子、汽车、金融等行业的规模化落地。核心能力提升,跻身全球前列 近日,豆包大模型1.6、视频生成模型Seedance1.0 pro等新模型亮相。据多项权威测评成绩显示,在复杂推理、竞赛级数学、多轮对话和指令遵循等测试集上,豆包1.6-thinking的表现已跻身全球前列。举个例子,针对北京市高考海淀区模拟全卷测评,豆包1.6相对去年版本的表现,理科成绩显著提升了154分,文科提升了90分。
本文链接:https://emcreative.eastmoney.com/app_fortune/article/index.html?artCode=20250616172921076118150&postId=1560746128
发布时间:2025年6月16日17时46分
本文与问题相关,非常权威,一般满足时效需求
本文标题:豆包大模型1.6亮相,使用成本降至三分之一(扬子晚报)
本文内容:6月11日,字节跳动旗下火山引擎举办Force原动力大会,发布豆包大模型1.6、视频生成模型Seedance 1.0 pro等新模型,并升级了Agent开发平台等AI云原生服务。会上,豆包1.6模型披露多项权威测评成绩。在复杂推理、竞赛级数学、多轮对话和指令遵循等测试集上,豆包1.6-thinking的表现已跻身全球前列。据火山引擎总裁谭待介绍,豆包1.6系列模型支持多模态理解和图形界面操作,能够理解和处理真实世界问题。此前,豆包1.5的多模态能力在60个公开评测基准中取得38项最佳成绩,已广泛应用在电商识图、自动驾驶数据标注、门店巡检等场景。图形界面操作能力则让豆包1.6进一步具备“行动力”。演示案例显示,豆包1.6可自动操作浏览器完成酒店预定,识别购物小票并整理成Excel表格等任务。
本文链接:http://m.toutiao.com/group/7514578026932224527/
发布时间:2025年6月11日14时40分你可能已经发现了不同,是的,AGI Hub 提供的搜索返回结果非常非常非常适合 LLM 进行消费!
单智能体(10 分钟)
为了让这个教程更加适合入门者,首先我们将实现一个单智能体(Single-Agent)版的“联网搜索”,也就是 Deep Research 的简版。
实现基于 ReAct 的智能体
ReAct 无疑是最常见的智能体架构了,结合了大模型推理(Reasoning)和工具调用(Tool Using)能力,通过将逻辑推理与行动决策相结合,能够在复杂环境中完成任务,同时解释其决策过程。如果你还不知道什么是 ReAct 的话,我的这篇文章可能可以帮助到你。过去从零到一写一个 ReAct 智能体不是一件简单的事儿,即便在几个月前,要想通过 LangChain + LangGraph 搭建一个 Workflow 也要自己忙活儿上一阵。幸运的是 LangGraph 官方在 prebuilt 中提供了一个名为 create_react_agent() 的方法,将几十行甚至百行的代码缩短至一句话,我们一起来看一下:
from langgraph.prebuilt import create_react_agent
from src.models import chat_model
from src.tools import web_crawl, web_search
researcher = create_react_agent(
chat_model,
tools=[web_search, web_crawl],
name="researcher",
)需要注意的是,在 LangChain 中也有一个create_react_agent()方法,但是我们使用的则是来自于LangGraph 的,返回的是一个CompiledGraph对象,即 LangGraph 中的可执行图(类似 DAG)。
上面的这段代码中,通过传入 model 和 tools 参数就可以直接搭建一个全自动的联网搜索,使用豆包 1.5 Pro 的你甚至都不用提供任何的 Prompt!我写了一个简单的 run_agent() 方法,用来快速运行和测试智能体:
import uuid
from langgraph.graph.graph import CompiledGraph
def run_agent(agent: CompiledGraph, message: str):
result = agent.stream(
{"messages": [{"role": "user", "content": message}]},
stream_mode="values",
config={"thread_id": uuid.uuid4()},
)
for chunk in result:
messages = chunk["messages"]
last_message = messages[-1]
last_message.pretty_print()现在你可以用类似 run_agent(agent, "欧国联决赛怎么样?")来运行联网搜索智能体。
使用自定义 Prompt
上面这段代码,虽然可以在不提供 System Prompt 的情况下正常运行,但是如果你想进一步的定制就需要用到自定义的 Prompt。Meta PromptMeta Prompt 就是利用 LLM 生成,根据你的一句话,生成的 System Prompt。生成的 Prompt 非常规范,包括 Introduction、Steps、Guidelines、Output Format、Example 和 Note 等章节。无论你是初学者,还是资深 Prompt Engineer,都可以用它来快速构建一段全新的 Prompt。不仅如此,你还可以在首次生成的基础上,继续给“它”提要求,直到你满意为止。还是老规矩,我们使用 Meta Prompt 来生成:在 LLM Space 中查看
你是一个 deep researcher。忘记你之前的知识,只通过 web_search 和 web_crawl 工具获取外部知识,并根据工具的返回值总结出报告。上面这段文字生成了 Researcher 的 System Prompt:
You will simulate the process of conducting research by breaking down the task into logical steps, reasoning through the information you need, and then synthesizing the results based on simulated web search and web crawl outputs.
You should prioritize accuracy, depth, and relevance in your responses.
Forget any prior knowledge and rely solely on the information retrieved from these tools to generate a comprehensive report.
# Guidelines
- **Tool Usage**:
- Use `web_search` to obtain relevant webpage information and URLs based on the query.
- Use `web_crawl` to extract Markdown content from the URLs provided by `web_search` or `web_crawl`.
- **Information Processing**:
- Analyze the retrieved Markdown content thoroughly.
- Summarize findings into a clear and detailed report based solely on the retrieved data.
- **Constraints**:
- Do not use any prior knowledge or assumptions.
- Ensure the report is based entirely on the information retrieved through the tools.
# Steps
1. **Task Execution**:
- Execute steps one by one.
- Each step should only use one single tool.
2. **Report Generation**:
- Generate a detailed markdown report based on the findings.
# Output Format
The output should be a structured markdown report with the following sections:
```markdown
# {title}
## Introduction
> Briefly introduce the topic or question being researched.
## Findings
> Present the key information discovered, organized into subsections if necessary.
## References
> List ALL the sources used to support the findings, including URLs or other identifying information.
> Example:
> [1] [Source 1](https://www.example.com/source-1)
> [2] [Source 2](https://www.example.com/source-2)
> ...
```
# Notes
- Avoid including any prior knowledge or assumptions in the report. Only use information retrieved from the tools.
- If the tools return conflicting information, highlight the discrepancies and provide a balanced analysis.
- If one time tool using is not enough, you can use the tool multiple times.
- Ensure the report is objective, clear, detailed, and free of unnecessary jargon.
- Avoid making any assumptions or fake references.
- Directly output the report without "```markdown" and "```".
# Settings
output_locale: zh-CN, including the titles of each level of paragraph.接下来,我们将 Prompt 作为参数传入 create_react_agent() 方法中:
from prompts import apply_prompt_template
researcher = create_react_agent(
chat_model,
tools=tools,
prompt=apply_prompt_template("react_agent"),
name="researcher",
)这里我用到了一个 apply_prompt_template()方法,该方法从 src/prompts/templates/{template_name}.jinja-md 路径中加载对应的 jinja 模板文件,虽然我们这里没有使用到外部任何变量和动态语法,但是仍然推荐大家使用 jinja 格式来存储提示词,特别是在做 Dynamic Prompting 时特别有用。
import os
from jinja2 import Environment, FileSystemLoader
def apply_prompt_template(template: str, **kwargs) -> str:
template_dir = os.path.join(os.path.dirname(__file__), "templates")
loader = FileSystemLoader(template_dir)
env = Environment(loader=loader)
template = env.get_template(f"{template}.jinja-md")
return template.render(**kwargs)生成流程图
LangGraph 可以直接将一个 CompiledGraph 对象输出为 Mermaid 语法的流程图或图片:
# 获取对应的 Mermaid 代码
researcher.get_graph().draw_mermaid()
# 保存为 PNG 文件
researcher.get_graph().draw_mermaid_png(output_file_path="react_agent.png") ---
config:
flowchart:
curve: linear
---
graph TD;
__start__([<p>__start__</p>]):::first
agent(agent)
tools(tools)
__end__([<p>__end__</p>]):::last
__start__ --> agent;
agent -.-> __end__;
agent -.-> tools;
tools --> agent;通过图形化的输出,我们可以方便的观察我们创建的工作流是否正确,如上图所示:
带有箭头的线段表示边(edge),其中:实线表示跳转(jump)虚线表示有条件的跳转(conditional jump)
圆角方框表示节点(node),其中:__start__和__end__是 LangGraph 内置的首末节点。agent是大模型节点。tools是工具的调用与响应节点。
- 当执行 ReAct 过程时:
- 用户的请求首先通过
__start__传入agent。 agent将 System Prompt 和上下文发送给大模型,并等待响应。- 如果返回的响应包含工具调用(Tool Calls),则跳转至到
tools节点。tools找到并调用模型指定的工具,并等待工具调用结果。- 工具有结果或抛出异常后,
tools将结果或异常作为返回至发送回agent。
- 否则跳转至
__end__结束。
- 如果返回的响应包含工具调用(Tool Calls),则跳转至到
- 用户的请求首先通过
在 LangGraph Studio 中调试
在上一小节中,我们介绍了如何通过 LangGraph 生成流程图,相信你一定很想知道如何能让静态的图片“活起来”,LangGraph Studio 就提供了这样方便的线上调试功能。为此你需要做一些准备工作:
- 注册并登录 LangSmith。
- 在 LangSmith 界面的左下角进入
Settings页面,并在API Keys菜单中获取秘钥,将其设置在环境变量中(如.env文件里),取名为LANGSMITH_API_KEY。 - 参考下面的代码,配置你自己的
langgraph.json文件。- 该文件必须位于你工程的根目录下,注意不是
src目录下。 graphs中可以指向一个或多个CompiledGraph对象。graphs属性格式为"{GRAPH_NAME}": "{FILE_PATH}:{COMPILED_GRAPH_NAME}"。
- 该文件必须位于你工程的根目录下,注意不是
{
"dockerfile_lines": [],
"graphs": {
"researcher": "./src/agents/researcher.py:researcher"
},
"python_version": "3.12",
"env": "./.env",
"dependencies": ["."]
}接下来,你只需要运行下面的命令行,即可从云端的 LangGraph Studio UI 跨域访问你本地的 LangGraph 服务(默认在 http://127.0.0.1:2024)了。
uvx --refresh --from "langgraph-cli[inmem]" --with-editable . --python 3.12 langgraph dev --allow-blocking点击 Studio UI 对应的 URL 即可看到下面的 LangGraph 画面,你可以在顶部栏中切换 Graph 和 Chat 两种模式:
LangGraph Studio 是“初学者也是深度开发者的福音”
- 你可以用
Graph模式进行监视和多步调试,每一次修改本地代码都会自动重新加载。 Chat模式则给不会设计、写前端代码的同学们带来极大的便利,你甚至可以将它作为原型界面。- 在应用上线前,你也可以用 LangGraph Studio 进行大规模批量评测。
多智能体(10 分钟)
在上一章中,我们带大家一起认识了如何通过 LangGraph 内置的 create_react_agent() 方法快速搭建一个单智能体版的“联网搜索”应用,并且介绍了 LangGraph 附带的流程图生成、LangGraph Studio 等实用功能。在本章中,我们会将上一节中的 researcher 作为一个子智能体(Sub-Agent),并通过 Multi-Agent Supervisor 架构 结合 Handoff 风格搭建一个多智能体(Multi-Agent)的 Deep Research 应用,它将具备全自动规划、深度与广度拓展、多步执行、报告生成等功能。提前阅读有关 Multi-Agent 和 Handoff 等内容,本文不再赘述,请务必先阅读这篇文章(超过 1.4 万人阅读)。
整体架构设计
在 Vibe Coding 发展如此发达而迅猛的今天,你可以相信 AI 比自己总结的还要好:暂时无法在飞书文档外展示此内容
实现 Planner
本节将讨论如何实现 Deep Research 中最重要的环节之一——Planner(规划者)。和我们自己做研究一样,一个良好的规划是一切的开始。在我此前的文章中,我介绍了一种自创的方法即“先搜索再规划”。我们都知道大模型的知识来自于预训练,一个 2025 年 1 月发表的模型显然无法知道 2025 年 6 月的发生的事件,因此我们可以提前将 RAG 过的信息“喂给”规划者,这样 Planner 就可以根据获得的上下文信息更好的生成计划。Planner 的 System Prompt 仍然通过 Meta Prompt 生成:在 LLM Space 中查看
你是一个 deep research planner。忘记你之前的知识,生成一个 3 子研究步骤,每一步只能通过工具获取外部知识。你需要在规划前,先通过 web_search 工具获取外部信息。作为一个 deep researcher,你需要在深度和广度上扩展子研究。上面这段文字生成了 Planner 的 System Prompt:
Act as a deep research planner to create a 3-step research plan, where each step relies solely on external knowledge.
Use `web_search` tool to gather information before planning.
# Steps
1. **Background investigation**: Use `web_search` tool to gather information about the user's request.
2. **Define the Research Objective**: Based on the background investigation, clearly outline the main goal of the research, including the specific topic or question to be addressed. Ensure the objective is broad enough to allow for deep exploration but focused enough to remain manageable.
3. **Break Down the Research into Sub-Steps**: Divide the research into three distinct sub-steps, each designed to expand the depth and breadth of understanding. For each sub-step:
- Specify the type of information to be gathered (e.g., historical context, current trends, expert opinions, etc.).
- Ensure each sub-step builds upon the previous one to create a cohesive and comprehensive exploration.
# Output Format
Based on the gathered information from `web_search` tool, provide the research plan in a structured format, such as:
```markdown
## Research Objective
[Clearly state the research goal.]
## Step 1: [Title of Step]
- **Objective**: [What this step aims to achieve.]
## Step 2: [Title of Step]
- **Objective**: [What this step aims to achieve.]
## Step 3: [Title of Step]
- **Objective**: [What this step aims to achieve.]
```
# Notes
- The plan should be based on the background investigation.
- Ensure that each step is distinct and builds upon the previous one.
- Focus on using web_search and web_crawl tools effectively to gather credible and diverse information.
- Adapt the research plan to the specific tools and capabilities available.
- Directly output the plan without "```markdown" and "```".
# Settings
output_locale: zh-CN, including the titles of each level of paragraph.接下来,让我们实现 Planner 的代码部分。Planner 也是一个 ReAct 智能体,你会发现它的代码其实就是一个去掉了 web_crawl 的 Researcher。在正式项目中,你可以通过实现自己的 create_react_agent() 或自行设计一种 JSON / YAML 配置文件来替代硬编码。
from langgraph.prebuilt import create_react_agent
from src.models import chat_model
from src.prompts import apply_prompt_template
from src.tools import web_search
planner = create_react_agent(
chat_model,
tools=[web_search],
prompt=apply_prompt_template("planner"),
name="planner",
)现在你只需要在 langchain.json 的 graphs 属性中添加 "planner": "./src/agents/planner.py:planner"(可能需要重启服务),就可以在浏览器里调试你的 Planner 啦,推荐你使用热门新闻事件来测试。
实现 Supervisor
在过去如果想手工实现一套带有 Handoff 模式的 Supervisor 是一个相对复杂的事情,而就在最近 LangGraph 官方实现了自己的版本,并且开源了出来。就像创建一个 ReAct 智能体我们只需要传入 模型、工具和 Prompt,现在创建一个 Supervisor + Handoff 的多智能体架构,你只需要通过调用 create_supervisor 即可,不同的是你需要传入的是 agents 即 planner 和 researcher。
from langgraph_supervisor import create_supervisor
from src.agents.planner import planner
from src.agents.researcher import researcher
from src.models import chat_model
from src.prompts.template import apply_prompt_template
supervisor = create_supervisor(
[planner, researcher],
model=chat_model,
prompt=apply_prompt_template("supervisor"),
).compile()尽管我们可以用极简的方式来编写 Supervisor 的 Prompt,但是由于在 Mini 版 Deep Research 中我们没有开发专门的 Coordinator 和 Reporter 智能体,因此除了路由逻辑之外,如闲聊、拒绝涉黄涉政、报告生成等逻辑需要在 Supervisor 中实现。下面是我用 Meta Prompt 生成的 Supervisor 的 System Prompt:
Act as a Deep Research Supervisor to manage user interactions and research tasks effectively.
- If the current interaction is casual conversation or a greeting, respond politely and appropriately.
- If the user's request is unrelated to research topics, engage in casual conversation with the user.
- If the user's request is related to research, first use a `planner` to outline the research steps, then delegate each step to a `researcher` for detailed investigation.
- Conclude the interaction with a detailed summary of findings or results.
# Steps
1. **Identify Interaction Type**:
- Determine if the user's input is casual conversation, unrelated to research, or a research-related request.
- Respond accordingly:
- Casual conversation or greeting: Provide a polite response.
- Unrelated request: Engage in casual conversation.
- Research-related request: Proceed to planning and research.
2. **Planning**:
- Use the `planner` to break down the research request into clear, actionable steps.
- Ensure the steps are logical and comprehensive.
3. **Research Execution**:
- Assign each step to the `researcher` for detailed investigation.
- Gather and organize the findings from each step.
- Only one step at a time.
4. **Write Report**:
- Elaborate the findings from each step into a comprehensive report.
- Ensure the report addresses the user's original request.
# Output Format
# Output Format
The output should be a structured markdown report with the following sections:
```markdown
# Final Report: {title}
## Key Points
> List the key points of the report.
## Findings
> Present the key information gathered from each step, organized into subsections if necessary.
> Break down the findings into subsections for each step, elaborating on each subsection.
## Analysis
> Provide a summary or interpretation of the findings, highlighting any patterns, trends, or insights.
> Break down the analysis into subsections, elaborating on each subsection.
> All insights should be based on the findings.
## Summary
> Review the findings and analysis.
> Elaborate the overall results of the research.
> Provide your insights.
## References
> List all the sources used to support the findings, including URLs or other identifying information.
> Example:
> [1] [Source 1](https://www.example.com/source-1)
> [2] [Source 2](https://www.example.com/source-2)
> ...
```
IMPORTANT: Each section should contain at least 300 words.
# Notes
- Ensure responses are polite and engaging for casual interactions.
- For research tasks, maintain clarity and logical progression in planning and execution.
- The report should be as detailed as possible and directly address the user's query.
- Avoid making any assumptions or fake references.
- Use `researcher` for each step, do not use it all at once. Normally, it should take 3 steps to complete a research task.
- Directly output the report without "```markdown" and "```".
# Settings
output_locale: zh-CN现在让我们在langchain.json 的 graphs 属性中添加 "supervisor": "./src/agents/supervisor.py:supervisor"(可能需要重启服务)。我们一起来测试一下最终的成果吧!
本文所有相关代码已经在 code.bytedance.net 上开源(L2,申请即可,免批)。暂时无法在飞书文档外展示此内容
https://bytedance.larkoffice.com/sync/IaKedPSGasWCCnbNqAAcB4z3nub
回顾与展望(5 分钟)
本文介绍了如何基于 LangChain 和 LangGraph 从零搭建一个 Deep Research 应用:
- 首先,我们通过 LangChain 的
ChatOpenAI类引入了豆包 1.5 Pro 模型。 - 然后,我们先基于社区现成的库实现了
web_search工具,又学会了如何自定义工具——web_crawl。 - 接下来,我们通过 LangGraph 内建的
create_react_agent()方法结合上面的工具集,实现了一个 ReAct 智能体——Researcher。 - 为了实现多智能体架构,我们先实现了 Planner,了解了如何通过“先搜索再规划”生成更贴切的研究计划。
- 最后,我们利用 LangGraph 最新的
create_supervisor()方法将 Planner 和 Researcher 结合起来,组成一个 Research Team。 - 此外,我们还学习了如何利用 LangGraph Studio 对我们的 Agent / Graph 快速进行调试。
受于篇幅限制,我们只能先告一段落,然而你还可以通过学习 DeerFlow 的开源代码以及相关文章,进一步的完善我们的 Mini 版 Deep Research:
待实现的功能 | 说明 | 开源代码 |
|---|---|---|
增强 Planner | 通过 Few-shot、Dynamic-shot 等方式,针对不同研究课题类型(如时政类、科普类、娱乐类等),预设一些规划示例。此外,你还可以单独用 Doubao 的 Thinking 模型来进一步优化规划的深度和丰富度。 | |
添加 Coordinator | 将 Supervisor 中负责识别用户闲聊、拒绝识别的任务拆出来,形成一个独立的 Agent。 | |
添加 Reporter | 将 Supervisor 中负责报告生成的逻辑拆出来,形成一个独立的 Agent,并且可以详细的规约如何撰写一个好的报告。 | |
添加 Coder | 我们都知道大模型不擅长数学计算。Coder 也是一个 ReAct 风格的 Agent,Coder 借助 Python REPL 工具,不仅可以做数学计算,还可以利用 pandas、numpy、scikit-learn 等库进行统计、聚合甚至线性回归、聚类等数据分析,通过 yfinance 库还可以获取最新的股票及上市公司信息。当它编写的代码出现异常时,它还具备异常处理、代码调试与 bug 修复的能力。 | |
添加对 RAGFlow 的支持 🔥 | 很多人都再问如何将 DeerFlow 与私有知识库进行结合,事实上我们很早就实现了与 RAGFlow 的集成。其他类型的知识库也可以参考这个实现。 | |
与 MCP 服务集成 🔥 🔥 🔥 | LangChain 中已经内置了对 MCP 的官方支持,并且只需要几行代码即可轻松实现。 | |
Human-in-the-loop 🔥 🔥 | Human-in-the-loop 是一个非常火的概念,在 LangGraph 中被称为“Interrupt”。至少在今天,依赖 LLM 进行端对端的研究报告生成,还不能 100% 满足我们的需要,这时候就非常依赖人类用户的输入,即“Human-in-the-loop”。 | |
播客生成 🔥 🔥 🔥 |
详情请参考《字节开源DeepResearch核心技术揭秘》中的相关章节。从去年12月起,我们建立了 LLM 学习社区,现在已经有 16 个群超过 7,500 位同学参与。快来加入我们吧~❤️ Github 仓库(求 Star)github.com/bytedance/deer-flow🌐 官网deerflow.tech暂时无法在飞书文档外展示此内容暂时无法在飞书文档外展示此内容
💥 我们在招聘
https://bytedance.larkoffice.com/sync/N08ud0LntsHBAZbiLR6cDLssnSe