跳到主要内容

巧妙之处 · 边界 · 对比 · 代码地图

7.1 巧妙之处(可借鉴的技术)

① 自描述路由 —— agent/集合自己说明用途,LLM 来选。 不写一堆 if-else 规则,而是给每个候选项挂一句 __description__,把选择权交给 LLM。describe_class 装饰器 + RAGRouter 的描述收集,让「加一个新 agent」只需写一句描述。见 agent/base.py:7-25rag_router.py:46-54

② 句窗双文本 —— 检索用小块、作答用大块。 用一份数据同时满足「检索要精」和「作答要全」两个相反诉求:小块算向量,metadata["wider_text"] 存加宽上下文给 LLM。见 splitter.py:46-77。这是整个项目最值得抄进自己 RAG 系统的一招。

③ LLM-as-reranker 的严格判定。 DeepSearch 对每块命中单独问 LLM「YES/NO」,且要求含 YES 且不含 NO 才收(deep_search.py:159),挡掉「NO, but it might...」这类模糊回答。质量换 token。

④ 解析层处处容错。 LLM 输出从不可信:literal_eval 从代码块/杂文里抠 list(llm/base.py:88-110)、find_last_digit 兜底解析编号(rag_router.py:89-93)、支撑文档索引做越界保护(chain_of_rag.py:194-198)。这套「不信任 LLM 输出」的防御habit 很值得学。

remove_think 统一剥离推理痕迹。 推理型模型会吐 <think>...</think>,所有读 LLM 输出的地方都先过 remove_think(llm/base.py:114-120),把思考过程切掉只留结论。

7.2 边界与局限(诚实)

  • 在线/联网搜索没接。 deep_search.py:231search_res_from_internet = [] # TODO 始终为空。README 说「必要时整合在线内容」,但本 commit 的检索只走私有向量库。
  • token 成本高。 DeepSearch 对每个命中块都发一次 rerank 调用,再加拆问题/反思/汇总——一次「写报告」查询可能产生几十上百次 LLM 调用。项目通篇返回 token 计数,某种程度就是在提醒你这件事。
  • 轮间串行。 并行只在「一轮内多子问题」(asyncio.gather),轮与轮必须等反思结果,延迟随 max_iter 线性增长。
  • 单进程单配置。 装配用模块级全局变量(configuration.py:176-183)+ init_configglobal 赋值,同进程只能有一套配置,不便多租户。
  • 去重只看原文相等。 deduplicate_resultstext 精确比对(vector_db/base.py:71-76),语义重复但文字不同的块不会被去掉。
  • 强依赖 LLM 听话。 拆子问题、选集合、reranking 全靠 LLM 返回可解析的格式;弱模型上容错层会频繁触发甚至抛错(literal_eval 解析失败会 raise,llm/base.py:104-107)。

7.3 横向对比(同 shelf 的 deep-research 兄弟)

DeepSearcher 在「deep research」这一类里的定位:

维度DeepSearcher 的取舍
数据源私有向量库优先(企业内网知识),联网是 TODO。多数开源 deep-research 反过来——以 web 搜索为主。
迭代形状提供两种并存:DeepSearch(广度/写报告)、ChainOfRAG(深度/多跳),再用 LLM 路由二选一。
「停」的判据反思返回空 / 轮数兜底 / 可选 early-stopping,都交给 LLM 判断,无硬规则。
可插拔反射式工厂,LLM/向量库/loader 全部按字符串名热插拔。

一句话:它是「向量库版的 deep research」——把通常对着 Google 做的「搜索→反思→再搜」搬到了企业私有数据上,这是它区别于 web-first 同类的核心取舍。

7.4 代码地图(导航索引)

主题文件关键符号
在线提问入口deepsearcher/online_query.pyquery, retrieve, naive_rag_query
离线灌库入口deepsearcher/offline_loading.pyload_from_local_files, load_from_website
agent 抽象基类 + 自描述装饰器deepsearcher/agent/base.pyRAGAgent, BaseAgent, describe_class
DeepSearch 核心循环deepsearcher/agent/deep_search.pyDeepSearch.async_retrieve, _generate_sub_queries, _search_chunks_from_vectordb, _generate_gap_queries
ChainOfRAG 逐跳追事实deepsearcher/agent/chain_of_rag.pyChainOfRAG.retrieve, _retrieve_and_answer, _get_supported_docs, _check_has_enough_info
选 agent 的路由deepsearcher/agent/rag_router.pyRAGRouter._route, find_last_digit
选集合的路由deepsearcher/agent/collection_router.pyCollectionRouter.invoke
朴素 RAG(基线)deepsearcher/agent/naive_rag.pyNaiveRAG.retrieve, NaiveRAG.query
LLM 输出解析/容错deepsearcher/llm/base.pyBaseLLM.literal_eval, remove_think, ChatResponse
检索结果 / 去重deepsearcher/vector_db/base.pyRetrievalResult, deduplicate_results, CollectionInfo, BaseVectorDB
句窗切块(双文本)deepsearcher/loader/splitter.py_sentence_window_split, split_docs_to_chunks, Chunk
嵌入抽象 / 批量deepsearcher/embedding/base.pyBaseEmbedding.embed_query, embed_chunks, dimension
配置 + 反射工厂 + 装配deepsearcher/configuration.pyConfiguration, ModuleFactory._create_module_instance, init_config
混合检索 / RRF 融合(示例后端)deepsearcher/vector_db/milvus.pysearch_data(use_hybrid, RRFRanker)

兄弟文档链接

  • 总库 deep-research 原理页(检索→反思迭代、停机判据的跨项目对比)
  • web-first 的 deep-research 兄弟子库 doc(对照「私有库 vs. 联网」这条主轴)