Skip to content

Repository files navigation

DeepSearch

一个面向复杂问题的开源深度检索 Agent:自主拆解问题、规划搜索路径、访问网页、提取证据,并通过多源验证生成简洁答案。

Python FastAPI License

DeepSearch 基于 ReAct(Reason + Act)范式构建。它不是一次性“搜索后总结”的固定流水线,而是让模型在推理、检索、阅读和验证之间动态循环:先拆解问题,再根据当前证据决定下一步行动,直到满足约束或触发运行时兜底。

为什么需要 DeepResearch

普通搜索解决的是“把相关页面找出来”;一次性问答解决的是“根据已有上下文写一段话”。但真实问题往往同时包含时间、人物、地点、因果关系或格式要求,答案分散在多个来源中,页面之间还可能存在噪声、缺失和表述差异。此时,先搜到一个看似合理的结果就开始总结,容易出现过早收敛、漏掉约束和把推测当成事实的问题。

DeepResearch 的核心意义,是把“回答问题”变成一个可检查的研究过程:

  • 先理解,再检索:把原问题拆成实体、约束和关系,围绕高区分度线索规划查询。
  • 边搜边验证:搜索负责发现候选,网页访问负责阅读原文,多个独立来源共同确认关键事实。
  • 让证据驱动下一步:每轮观察结果都会回到 Agent 上下文,模型可以继续换关键词、换搜索源或深入页面,而不是被固定流程锁死。
  • 把不确定性显式化:搜索失败、网页不可访问、内容被过滤、上下文过长或任务超时,都有可观察的降级路径。
  • 在有限预算内完成闭环:研究不是无限搜索,而是在轮次、时间和上下文预算内持续提高答案可信度,并在必要时安全收束。

因此,DeepSearch 关注的不只是最终文本,还关注答案背后的“问题拆解 → 检索 → 证据提取 → 交叉验证 → 格式校验”链路。本项目把这条链路封装为可复用的 HTTP 服务,方便接入知识助手、研究工具和需要实时信息的业务系统。

特性

  • 多跳检索:将复杂问题拆成可独立验证的子问题,逐步收敛候选答案。
  • 中英文双路搜索:支持 Google/Serper 与阿里 IQS,并按模型选择或查询语言自动路由。
  • 搜索自动降级:弱结果会切换搜索源;双路结果均较差时自动简化查询重试。
  • 目标导向网页阅读:优先通过 Jina Reader 获取正文,失败后使用 httpx + BeautifulSoup 降级抓取。
  • 证据压缩:使用独立摘要模型围绕当前目标提取 evidencesummary,减少无关上下文。
  • 长任务保护:包含 SSE 心跳、超时、Token 上限、最大轮次、内容过滤重试和强制回答机制。
  • 宽容的工具协议:兼容 XML 标签、Qwen function 标签和裸 JSON 三种工具调用格式。
  • 可重复评估:提供 JSONL 批量运行与断点续跑脚本,便于比较 Prompt 和工具策略。

架构

DeepSearch 系统架构

架构图以当前源码为准,包含主调用链、ReAct 回路,以及搜索、网页访问和运行时保护的降级路径。可编辑的 Mermaid 源码见 docs/architecture.mmd

一次请求的核心流程如下:

  1. FastAPI 接收问题,以普通 JSON 或带心跳的 SSE 方式保持请求。
  2. Agent 注入系统提示词、当前日期与用户问题,调用主推理模型。
  3. 模型输出答案或工具调用;工具结果经过截断后作为观察信息写回上下文。
  4. search 在双搜索源之间路由和降级;visit 并发读取网页并提取目标证据。
  5. Agent 持续执行 Think → Act → Observe,最终完成多源验证并归一化答案。

项目结构

.
├── agent.py                 # FastAPI 服务入口,支持 JSON 与 SSE heartbeat
├── agent_loop.py            # ReAct 主循环、工具调度和运行时保护
├── prompts.py               # System、User 与网页证据提取 Prompt
├── tools_search.py          # Serper / IQS 搜索、路由、Key 轮换与降级
├── tools_visit.py           # 网页抓取、正文清洗、并发访问与证据摘要
├── eval.py                  # JSONL 批量运行脚本
├── test_eval.py             # 带参考答案的本地评估脚本
├── skills.py                # Agent Skills 发现与加载能力
├── skills/                  # 示例 Skills
├── docs/
│   ├── architecture.mmd     # Mermaid 架构图源码
│   └── architecture.svg     # README 使用的渲染结果
└── requirements.txt

快速开始

1. 准备环境

需要 Python 3.10 或更高版本。

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

2. 配置服务

在项目根目录创建 .env

# 必填:主推理模型与网页摘要模型
DASHSCOPE_API_KEY=your_dashscope_api_key
AGENT_MODEL=qwen3.5-plus

# 至少配置一个可用搜索源
SERPER_API_KEYS=key_1,key_2
IQS_API_KEY=your_iqs_api_key

# 可选:不配置时网页访问自动降级为 httpx + BeautifulSoup
JINA_ENABLED=1
JINA_API_KEYS=key_1,key_2

搜索源可以单独配置,但双路都可用时,中英文检索和自动降级能力更完整。多个 Serper/Jina Key 使用英文逗号分隔,遇到限流时会自动轮换。

3. 启动服务

uvicorn agent:app --host 0.0.0.0 --port 8000

服务启动后,可访问 http://localhost:8000/docs 查看交互式 API 文档。

API 使用

JSON 响应

curl -X POST http://localhost:8000/ \
  -H 'Content-Type: application/json' \
  -d '{"question":"解释 RAG 与 DeepSearch 的差异,并给出适用场景。"}'

响应:

{
  "answer": "..."
}

SSE 长连接

对于耗时较长的深度检索,可请求 SSE。服务端每 5 秒发送一条 heartbeat,任务完成后发送 Message 事件。

curl -N -X POST http://localhost:8000/ \
  -H 'Content-Type: application/json' \
  -H 'Accept: text/event-stream' \
  -d '{"question":"梳理一个需要多来源交叉验证的问题。"}'

核心机制

ReAct 循环

agent_loop.py 维护完整对话上下文。每一轮由模型决定:

  • 输出 <tool_call>:解析参数并执行 searchvisit
  • 输出 <answer>:经过早答检查后归一化并返回;
  • 未产生有效动作:注入 nudge,要求继续调用工具或回答。

复杂问题若在前三轮且 120 秒内过早回答,系统会拦截一次,并要求重新检查约束和补充确认来源。

搜索策略

tools_search.py 支持在单次工具调用中提交多条查询:

  • 显式指定 google 时使用 Serper;指定 bing 时使用 IQS;
  • 未指定时,中文查询默认使用 IQS,其他查询默认使用 Serper;
  • 主搜索源结果过短、为空或报错时,切换另一搜索源;
  • 两个搜索源均不理想时,简化查询后再次尝试。

bing 是模型工具协议中的兼容名称,当前代码实际接入的是阿里 IQS,并非 Microsoft Bing API。

网页证据提取

tools_visit.py 支持并发访问多个 URL。单页处理顺序为:

Jina Reader → httpx 直连 → BeautifulSoup 正文清洗 → qwen-plus 目标摘要

摘要模型只围绕本轮 goal 提取证据;抓取或摘要失败会以可观察的错误结果返回主循环,而不会让整个请求直接中断。

运行时保护

机制 当前实现
主循环最大轮次 100 轮
单次请求超时 540 秒
上下文估算上限 500,000 tokens
单次工具结果上限 15,000 字符
单网页正文上限 50,000 字符
单 URL 访问上限 120 秒
内容过滤 两次中性措辞重试,随后清理上下文并强制回答
SSE 心跳 每 5 秒一次

这些值目前定义在 agent_loop.pyagent.pytools_visit.py 中,可根据模型上下文、网关限制和成本预算调整。

批量运行

eval.pyquestion.jsonl 读取问题,结果按行追加写入,重复执行时会跳过已完成 ID:

python eval.py 0 20 my_results.jsonl

参数依次为起始 ID、结束 ID(不包含)和输出文件。test_eval.py 适用于包含参考答案的本地 JSONL 数据,并输出归一化后的精确匹配统计。

建议用于自有数据时采用以下格式:

{"id": 0, "question": "你的问题"}

扩展方向

  • agent_loop.py::_execute_tool 中注册新的检索或数据工具。
  • prompts.py 中扩展工具描述与输出约束。
  • 为搜索结果增加去重、来源可信度评分和引用输出。
  • 将运行参数迁移为环境变量或配置文件,便于不同部署环境调优。
  • 接入可观测性系统,记录每轮耗时、Token、搜索源与降级原因。

安全提示

  • 不要提交 .env、API Key 或包含敏感信息的运行结果。
  • 网页内容属于不可信输入;用于生产环境时,建议增加 URL 白名单、内网地址拦截和提示词注入防护。
  • 当前服务未内置身份认证、限流和租户隔离,公开部署前应在网关层补齐。

License

本项目基于 MIT License 开源。

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages