一、架构拆解
我们可以看到,DeepSearh 的主要流程是通过将用户的 Query 分解成多个子问题,然后通过查询向量数据库与搜索引擎获取知识后生成答案,通读 Zillize 给出的 DeepSearher 的 main.py,我们不难发现,其由一下几部分构成
1. 配置初始化
2. 本地文件加载
3. Web 搜索
4. 查询流程
二、Query 流程拆解
在 DeepSearcher 中,主要的查询放在了 online_query.query()
中,
python
def query(original_query: str, max_iter: int = 3) -> Tuple[str, List[RetrievalResult], int]:
"""
Query the knowledge base with a question and get an answer.
This function uses the default searcher to query the knowledge base and generate
an answer based on the retrieved information.
Args:
original_query: The question or query to search for.
max_iter: Maximum number of iterations for the search process.
Returns:
A tuple containing:
- The generated answer as a string
- A list of retrieval results that were used to generate the answer
- The number of tokens consumed during the process
"""
default_searcher = configuration.default_searcher
return default_searcher.query(original_query, max_iter=max_iter)
其中,我们从全局的 configuration 实例中,获取了我们默认的 DeepResearh Agent
,我们可以从 deepsearcher.configuration.init_config()
中看到相关的配置
python
default_searcher = RAGRouter(
llm=llm,
rag_agents=[
DeepSearch(
llm=llm,
embedding_model=embedding_model,
vector_db=vector_db,
max_iter=config.query_settings["max_iter"],
route_collection=True,
text_window_splitter=True,
),
ChainOfRAG(
llm=llm,
embedding_model=embedding_model,
vector_db=vector_db,
max_iter=config.query_settings["max_iter"],
route_collection=True,
text_window_splitter=True,
),
],
)
那么我们便可以看到,当前一共是有两类 agent,一个是 DeepSearch
,一个是 ChainOfRAG
,对于这两部分,我们可以在 deep searcher.agent.seep_search.py
和 deep searcher.agent.chain_of_rag.py
中找到。
因此我们可以整理出当一个请求来到我们的 DeepSearcher 的时候,我们的问题会经过 RAGRouter 选择合适的 Agent 调用对应的 query()
方法,我们将大致流程整理一下,并形成了下列流程图:
DeepSearch 流程解析
想象一下DeepSearch是一位穿着风衣、戴着放大镜的经济学侦探,擅长搜索事件的蛛丝马迹,而 query
方法正是它调研的全过程。让我们用轻松幽默的方式一探究竟!
🔍 侦探接案
python
def query(self, query: str, **kwargs) -> Tuple[str, List[RetrievalResult], int]:
这就像福尔摩斯接到了一个神秘来电:"喂,是侦探社吗?我想知道 DeepSeek 是不是想吞并 OpenAI!"
侦探礼貌地记下问题,并默默思考:"这需要一些调查工作..."
🏃♂️ 奔赴"犯罪现场"
python
all_retrieved_results, n_token_retrieval, additional_info = self.retrieve(query, **kwargs)
侦探立刻叫来华生(retrieve
方法):"华生,我们得去各个知识库找线索!"
他们带着问题冲出门去,跑遍了所有可能藏有线索的数据库角落,最后气喘吁吁地带回一堆文件和票据(token消耗)。
🤷♂️ 线索全无的尴尬时刻
python
if not all_retrieved_results or len(all_retrieved_results) == 0:
return f"No relevant information found for query '{query}'.", [], n_token_retrieval
有时候,侦探会尴尬地发现一无所获:"呃...这个案子很特殊,华生。我们没找到任何线索。或许敌人太狡猾了,或者根本就没有这个想法?"
📚 整理证据材料
python
all_sub_queries = additional_info["all_sub_queries"]
chunk_texts = []
for chunk in all_retrieved_results:
if self.text_window_splitter and "wider_text" in chunk.metadata:
chunk_texts.append(chunk.metadata["wider_text"])
else:
chunk_texts.append(chunk.text)
侦探把所有收集到的报纸剪报、证人证词和现场照片摊在桌子上:"看看我们收集了什么!这里有 DeepSeek 最近的动态,这里有 DeepSeek 的资金流动证明,还有这份 DeepSeek 科研的最新进展..."
他戴上老花镜,按照线索的重要性把它们排列整齐。
🧠 "华生,开始头脑风暴!"
python
log.color_print(
f"<think> Summarize answer from all {len(all_retrieved_results)} retrieved chunks... </think>\n"
)
summary_prompt = SUMMARY_PROMPT.format(
question=query,
mini_questions=all_sub_queries,
mini_chunk_str=self._format_chunk_texts(chunk_texts),
)
侦探在办公室踱来踱去,嘴里念叨着:"我们找到了{len(all_retrieved_results)}
条线索...把这些拼起来应该能看出些什么..."
他在思维宫殿中开始构建案情,把分散的线索组成一个连贯的故事。
💡 福尔摩斯的结论时刻
python
chat_response = self.llm.chat([{"role": "user", "content": summary_prompt}])
log.color_print("\n==== FINAL ANSWER====\n")
log.color_print(chat_response.content)
"啊哈!"侦探突然拍手,眼睛闪闪发光,"我知道了!一切都说得通了!"
华生一脸困惑:"什么说得通了?"
侦探开始绘声绘色地解释 DeepSeek的老谋深算,引经据典,逻辑严密,华生目瞪口呆!
📊 案件存档
python
return (
chat_response.content,
all_retrieved_results,
n_token_retrieval + chat_response.total_tokens,
)
侦探收拾好办公桌,把案件总结、所有证据和调查费用清单(token消耗)整齐地装进文件夹:
"华生,把这个归档,案子解决了!哦对了,别忘了给客户发账单,这个月的房租还没交呢!"
ChainOfRAG 流程解析
想象一下ChainOfRAG是一位戴着眼镜、永远不会长大的名侦探柯南,擅长根据线索进行推理,而query
方法就是他那著名的"真相只有一个"的推理过程!
🎯 案件启动
python
def query(self, query: str, **kwargs) -> Tuple[str, List[RetrievalResult], int]:
柯南接到了一个复杂的案件:"柯南,我想知道为什么恐龙会灭绝?"
小侦探推了推眼镜,眼中闪过一丝精光:"看来这个案件需要层层递进的调查..."
🔍 搜集线索(多轮询问)
python
all_retrieved_results, n_token_retrieval, additional_info = self.retrieve(query, **kwargs)
柯南立刻行动起来:他没有像福尔摩斯那样直奔图书馆,而是分头询问了多位"证人"。
每问完一个问题,他都会根据得到的答案调整下一个问题,就像玩"二十个问题"游戏一样,一步步接近真相。
"请问,陨石撞击是在什么时候发生的?" "你能告诉我当时的气候变化情况吗?" "恐龙种群在灭绝前有什么变化?"
📝 整理调查笔记
python
intermediate_context = additional_info["intermediate_context"]
柯南拿出他的小笔记本,翻看着刚才的一系列问题和答案:"先是问了陨石撞击,然后是气候变化,接着是种群状况..."
他把所有的中间线索组织起来,像拼图一样寻找它们之间的联系。
🧠 "事件真相浮出水面!"
python
log.color_print(
f"<think> Summarize answer from all {len(all_retrieved_results)} retrieved chunks... </think>\n"
)
chat_response = self.llm.chat(
[
{
"role": "user",
"content": FINAL_ANSWER_PROMPT.format(
retrieved_documents=self._format_retrieved_results(all_retrieved_results),
intermediate_context="\n".join(intermediate_context),
query=query,
),
}
]
)
柯南双手抱胸,闭上眼睛,沉思片刻。在他的"推理空间"里,所有的线索开始飞舞:
"我找到了{len(all_retrieved_results)}
条相关证据... 中间经过了几轮提问... 证人们的反应都指向..."
突然,柯南睁开眼睛,脸上露出自信的微笑。
💡 "真相只有一个!"
python
log.color_print("\n==== FINAL ANSWER====\n")
log.color_print(chat_response.content)
柯南微微一笑,指着前方(其实什么都没有),用他标志性的自信语气开始最终推理:
"其实啊,恐龙灭绝是多种因素共同导致的!首先是约6600万年前的希克苏鲁伯陨石撞击事件,造成了全球性的环境灾难。这一撞击释放的能量相当于10亿颗广岛原子弹..."
小兰、博士和少年侦探团都目瞪口呆地看着他。
📊 案件存档
python
return (
chat_response.content,
all_retrieved_results,
n_token_retrieval + chat_response.total_tokens,
)
柯南合上他的笔记本,把最终报告、所有证据和调查费用(令人震惊的token数量)交给了委托人:
"案件解决了!不过别忘了,侦探可是很贵的哦!"然后他做了个胜利的手势,"下一个谜题在哪里呢?"
为什么有了 DeepSearch 了还需要 ChainOfRAG?
与DeepSearch不同,ChainOfRAG更像是一个善于提问的侦探,他知道一个好问题胜过十个半拉子答案。他不会一次性收集所有可能的线索,而是通过不断提出新的精准问题,一步步接近真相的核心。
当福尔摩斯还在疯狂收集线索时,柯南已经通过几个关键问题找到了破案的捷径!👦🔍🕶️
下面我们用一张图来总结一下当用户的 query
到达了 DeepSearcher 之后会发生什么:
三、收官之战:什么是 DeepSearch?什么是 DeepResearch?
-
DeepSearch 是一种高级的网页搜索代理系统。它通过不断循环执行"搜索 → 阅读 → 推理"的过程,直到找到最优答案或达到预设的停止条件,如令牌使用限制或失败尝试次数。在搜索阶段,DeepSearch利用搜索引擎广泛获取互联网信息;阅读阶段深入分析特定网页内容;推理阶段评估当前状态,决定是否拆解原始问题为更小的子问题,或尝试其他搜索策略。
-
DeepResearch 是在 DeepSearch 基础上构建的一个典型应用,专注于自动生成结构化、逻辑清晰的长篇研究报告。其工作流程包括:首先根据研究主题生成报告的详细目录;然后针对每个章节,调用 DeepSearch 进行深入的信息搜索和内容生成;最后,将所有章节内容整合,确保报告的连贯性和深度。
简单来讲,DeepSearch 是一个高级的AI搜索方法,强调通过多次搜索和推理获取高质量答案;而 DeepResearch 则是基于 DeepSearch 的应用,旨在自动生成深入的研究报告等内容。也就是我们今天探究的这个应用便是 DeepResearch 的体现,而其背后的原理即为 DeepSearch。