数据截至 (上游 commit d89e37cdfbbe)
巧妙之处 · 边界 · 对比 · 代码地图
7.1 巧妙之处(可借鉴的技术)
① 自描述路由 —— agent/集合自己说明用途,LLM 来选。 不写一堆 if-else 规则,而是给每个候选项挂一句 __description__,把选择权交给 LLM。describe_class 装饰器 + RAGRouter 的描述收集,让「加一个新 agent」只需写一句描述。见 agent/base.py:7-25、rag_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:231的search_res_from_internet = [] # TODO始终为空。README 说「必要时整合在线内容」,但本 commit 的检索只走私有向量库。 - token 成本高。 DeepSearch 对每个命中块都发一次 rerank 调用,再 加拆问题/反思/汇总——一次「写报告」查询可能产生几十上百次 LLM 调用。项目通篇返回 token 计数,某种程度就是在提醒你这件事。
- 轮间串行。 并行只在「一轮内多子问题」(
asyncio.gather),轮与轮必须等反思结果,延迟随max_iter线性增长。 - 单进程单配置。 装配用模块级全局变量(
configuration.py:176-183)+init_config的global赋值,同进程只能有一套配置,不便多租户。 - 去重只看原文相等。
deduplicate_results用text精确比对(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 路由二选一。 |