DeepSearch:WebThinker开启AI搜索研究新纪元!
1,项目简介
WebThinker 是一个深度研究智能体,使 LRMs 能够在推理过程中自主搜索网络、导航网页,并撰写研究报告。这种技术的目标是革命性的:让用户通过简单的查询就能在互联网的海量信息中进行深度搜索、挖掘和整合,从而为知识密集型领域(如金融、科学、工程)的研究人员大幅降低信息收集的时间和成本。
项目地址:https://github.com/RUC-NLPIR/WebThinker
评价:很垃圾,需要接入微软宙斯的东西,Bing搜索。这些都需要替换成国产的,而且质量一般,不兼容图片等等等,远远不如字节的 DeerFlow 强大。
现有的开源深度搜索智能体通常采用检索增强生成(Retrieval-Augmented Generation, RAG)技术,依循预定义的工作流程,这限制了 LRM 探索更深层次网页信息的能力,也阻碍了 LRM 与搜索引擎之间的紧密交互。
- 传统 RAG:仅进行浅层搜索,缺乏思考深度和连贯性
- 进阶 RAG:使用预定义工作流,包括查询拆解、多轮 RAG 等,但仍缺乏灵活性
- WebThinker:在连续深思考过程中自主调用工具,实现端到端任务执行
WebThinker 使 LRM 能够在单次生成中自主执行操作,无需遵循预设的工作流程,从而实现真正的端到端任务执行。
2,替换原LLM
【替换Bing的检索】run_web_thinker.py & run_web_thinker_report.py
async def generate_response(client: AsyncOpenAI,prompt: str,semaphore: asyncio.Semaphore,generate_mode: str = "chat",temperature: float = 0.0,top_p: float = 1.0,max_tokens: int = 32768,repetition_penalty: float = 1.0,top_k: int = 1,min_p: float = 0.0,model_name: str = "QwQ-32B",stop: List[str] = [END_SEARCH_QUERY],retry_limit: int = 3,bad_words: List[str] = [f"{END_SEARCH_RESULT}\n\n{tokenizer.eos_token}"], ) -> Tuple[str, str]:"""Generate a single response with retry logic"""for attempt in range(retry_limit):try:async with semaphore:if generate_mode == "chat":messages = [{"role": "user", "content": prompt}]if 'qwq' in model_name.lower() or 'deepseek' in model_name.lower() or 'r1' in model_name.lower():formatted_prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)else:formatted_prompt = aux_tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)if ('deepseek' in model_name.lower() or 'r1' in model_name.lower()) and "<think>\n" not in formatted_prompt:formatted_prompt = formatted_prompt + "<think>\n"else:formatted_prompt = promptresponse = await client.completions.create(model=model_name,prompt=formatted_prompt,temperature=temperature,top_p=top_p,max_tokens=max_tokens,stop=stop,extra_body={'top_k': top_k,'include_stop_str_in_output': True,'repetition_penalty': repetition_penalty,# 'bad_words': bad_words,# 'min_p': min_p},timeout=3600,)return formatted_prompt, response.choices[0].textexcept Exception as e:print(f"Generate Response Error occurred: {e}, Starting retry attempt {attempt + 1}")# print(prompt)if "maximum context length" in str(e).lower():# If length exceeds limit, reduce max_tokens by halfmax_tokens = max_tokens // 2print(f"Reducing max_tokens to {max_tokens}")if attempt == retry_limit - 1:print(f"Failed after {retry_limit} attempts: {e}")return "", ""await asyncio.sleep(1 * (attempt + 1))return "", ""
替换为:
async def generate_response(client: AsyncOpenAI,prompt: str,semaphore: asyncio.Semaphore,generate_mode: str = "chat",temperature: float = 0.0,top_p: float = 1.0,max_tokens: int = 32768,repetition_penalty: float = 1.0,top_k: int = 1,min_p: float = 0.0,model_name: str = "QwQ-32B",stop: List[str] = [END_SEARCH_QUERY],retry_limit: int = 3,bad_words: List[str] = [f"{END_SEARCH_RESULT}\n\n{tokenizer.eos_token}"], ) -> Tuple[str, str]:"""Generate a single response with retry logic"""for attempt in range(retry_limit):try:async with semaphore:if generate_mode == "chat":messages = [{"role": "user", "content": prompt}]if 'qwq' in model_name.lower() or 'deepseek' in model_name.lower() or 'r1' in model_name.lower():formatted_prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)else:formatted_prompt = aux_tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)if ('deepseek' in model_name.lower() or 'r1' in model_name.lower()) and "<think>\n" not in formatted_prompt:formatted_prompt = formatted_prompt + "<think>\n"else:formatted_prompt = promptclient = OpenAI(api_key="sk-*****",base_url="https://api.deepseek.com/beta")response = client.chat.completions.create(model="deepseek-chat",temperature=temperature,messages=[{"role": "system", "content": formatted_prompt},],stream=False)return formatted_prompt, response.choices[0].message.contentexcept Exception as e:print(f"Generate Response Error occurred: {e}, Starting retry attempt {attempt + 1}")# print(prompt)if "maximum context length" in str(e).lower():# If length exceeds limit, reduce max_tokens by halfmax_tokens = max_tokens // 2print(f"Reducing max_tokens to {max_tokens}")if attempt == retry_limit - 1:print(f"Failed after {retry_limit} attempts: {e}")return "", ""await asyncio.sleep(1 * (attempt + 1))return "", ""
def extract_relevant_info(search_results):"""Extract relevant information from Bing search results.Args:search_results (dict): JSON response from the Bing Web Search API.Returns:list: A list of dictionaries containing the extracted information."""useful_info = []if 'webPages' in search_results and 'value' in search_results['webPages']:for id, result in enumerate(search_results['webPages']['value']):info = {'id': id + 1, # Increment id for easier subsequent operations'title': result.get('name', ''),'url': result.get('url', ''),'site_name': result.get('siteName', ''),'date': result.get('datePublished', '').split('T')[0],'snippet': result.get('snippet', ''), # Remove HTML tags# Add context content to the information'context': '' # Reserved field to be filled later}useful_info.append(info)return useful_info
替换为:
def extract_relevant_info(search_results):"""Extract relevant information from search results.Compatible with custom search result JSON (not Bing).Args:search_results (dict): JSON response containing search results.Returns:list: A list of dictionaries containing the extracted information."""useful_info = []if 'results' in search_results and isinstance(search_results['results'], list):for id, result in enumerate(search_results['results']):info = {'id': id + 1,'title': result.get('title', ''),'url': result.get('url', ''),'site_name': '', # 该结构中无 site_name 字段'date': '', # 该结构中无 datePublished 字段'snippet': result.get('content', ''), # 使用 content 字段'context': ''}useful_info.append(info)return useful_info
3,替换Bing搜索
目前Bing关闭的API,原方式无法继续使用。修改 bing_search.py:
async def bing_web_search_async(query, subscription_key, endpoint, market='en-US', language='en', timeout=20):"""Perform an asynchronous search using the Bing Web Search API.Args:query (str): Search query.subscription_key (str): Subscription key for the Bing Search API.endpoint (str): Endpoint for the Bing Search API.market (str): Market, e.g., "en-US" or "zh-CN".language (str): Language of the results, e.g., "en".timeout (int): Request timeout in seconds.Returns:dict: JSON response of the search results. Returns empty dict if all retries fail."""headers = {"Ocp-Apim-Subscription-Key": subscription_key}params = {"q": query,"mkt": market,"setLang": language,"textDecorations": True,"textFormat": "HTML"}max_retries = 5retry_count = 0while retry_count < max_retries:try:response = session.get(endpoint, headers=headers, params=params, timeout=timeout)response.raise_for_status()search_results = response.json()return search_resultsexcept Exception as e:retry_count += 1if retry_count == max_retries:print(f"Bing Web Search Request Error occurred: {e} after {max_retries} retries")return {}print(f"Bing Web Search Request Error occurred, retrying ({retry_count}/{max_retries})...")time.sleep(1) # Wait 1 second between retries
替换为:
async def bing_web_search_async(query, subscription_key, endpoint, market='en-US', language='en', timeout=20):client = TavilyClient("tvly-dev-*****")response = client.search(query=query)response.raise_for_status()search_results = response.json()print("=========",search_results)return search_results
4,命令行启动
【问题解决模式】
python scripts/run_web_thinker.py \--single_question "What is OpenAI Deep Research?" \--bing_subscription_key "YOUR_BING_SUBSCRIPTION_KEY" \ # 用于调用 Bing 搜索API实现搜索增强(如 RAG)--api_base_url "YOUR_API_BASE_URL" \ # 主模型的 API 接口基础地址,如 http://localhost:8000/v1--model_name "QwQ-32B" \ # 主模型名称,可为本地模型或远程托管模型--tokenizer_path "PATH_TO_YOUR_TOKENIZER" \ # 主模型使用的分词器路径,如 ./tokenizers/qwq32b/--aux_api_base_url "YOUR_AUX_API_BASE_URL" \ # 辅助模型 API 接口地址--aux_model_name "Qwen2.5-32B-Instruct" \ # 辅助模型名称,用于多模型结果对比、增强或检验--aux_tokenizer_path "PATH_TO_YOUR_AUX_TOKENIZER" # 辅助模型的分词器路径
【报告生成模式】
python scripts/run_web_thinker_report.py \--single_question "What are the models of OpenAI and what are the differences?" \--bing_subscription_key "YOUR_BING_SUBSCRIPTION_KEY" \--api_base_url "YOUR_API_BASE_URL" \--model_name "QwQ-32B" \--aux_api_base_url "YOUR_AUX_API_BASE_URL" \--aux_model_name "Qwen2.5-32B-Instruct" \--tokenizer_path "PATH_TO_YOUR_TOKENIZER" \--aux_tokenizer_path "PATH_TO_YOUR_AUX_TOKENIZER"
5,web 启动
cd demo streamlit run run_demo.py
【报错】There is no current event loop in thread 'ScriptRunner.scriptThread'.
【解决】修改 bin_search.py 457 行:关闭加锁。
self.lock = asyncio.Lock() 👇 self.lock = None
【报错】Generate Response Error occurred: Connection error
【解决】修改 settings.py _load_client()
def _load_client(self, api_base_url, aux_api_base_url):self.client = AsyncOpenAI(api_key="empty",base_url=api_base_url,)self.aux_client = AsyncOpenAI(api_key="empty",base_url=aux_api_base_url,) 👇 def _load_client(self, api_base_url, aux_api_base_url):client = OpenAI(api_key="sk-***",base_url="https://api.deepseek.com/beta")self.client = clientself.aux_client = client
# bing_search.py use_model_name='QwQ-32B', aux_model_name='Qwen2.5-72B-Instruct', 👇 use_model_name='deepseek-chat', aux_model_name='deepseek-chat',
【报错】Invalid max_tokens value, the valid range of max_tokens is [1, 8192]
【解决】修改 bing_search.py
response = await client.completions.create(model=env.aux_model_name,max_tokens=4096,prompt=prompt,timeout=3600,) 👇 response = client.chat.completions.create(model=env.aux_model_name,messages=[{"role": "system", "content": prompt},],)
【报错】Generate Response Error occurred: Error code: 400 - {'error': {'message': 'Invalid max_tokens value, the valid range of max_tokens is [1, 8192]', 'type': 'invalid_request_error', 'param': None, 'code': 'invalid_request_error'}}, Starting retry attempt 1
【解决】修改 run_login.py generate_response()
async def generate_response(client: AsyncOpenAI,prompt: str,temperature: float = 0.0,top_p: float = 1.0,max_tokens: int = 4096,repetition_penalty: float = 1.0,top_k: int = 1,min_p: float = 0.0,model_name: str = "QwQ-32B",stop: List[str] = ["<|end_search_query|>"],retry_limit: int = 3, ):"""Generate a streaming response with retry logic"""for attempt in range(retry_limit):try:client = OpenAI(api_key="sk-****",base_url="https://api.deepseek.com/beta")response = client.chat.completions.create(model="deepseek-chat",temperature=temperature,messages=[{"role": "system", "content": prompt},],stream=True)async for chunk in response:if chunk.choices[0].message.content:yield chunk.choices[0].message.contentreturn except Exception as e:print(f"Generate Response Error occurred: {e}, Starting retry attempt {attempt + 1}")if attempt == retry_limit - 1:print(f"Failed after {retry_limit} attempts: {e}")await asyncio.sleep(0.5 * (attempt + 1))yield ""
【报错】Generate Response Error occurred: 'async for' requires an object with __aiter__ method, got Stream, Starting
【解决】DeepSeek的输出不是标准的迭代格式,修改 run_logit.py generate_response()
async for chunk in response:if chunk.choices[0].message.content:yield chunk.choices[0].message.contentreturn 👇 yield response.choices[0].message.content
相关文章:

DeepSearch:WebThinker开启AI搜索研究新纪元!
1,项目简介 WebThinker 是一个深度研究智能体,使 LRMs 能够在推理过程中自主搜索网络、导航网页,并撰写研究报告。这种技术的目标是革命性的:让用户通过简单的查询就能在互联网的海量信息中进行深度搜索、挖掘和整合,从…...

springCloud/Alibaba常用中间件之Setinel实现熔断降级
文章目录 SpringCloud Alibaba:依赖版本补充Sentinel:1、下载-运行:Sentinel(1.8.6)下载sentinel:运行:Sentinel <br> 2、流控规则① 公共的测试代码以及需要使用的测试Jmeter①、流控模式1. 直接:2. 并联:3. 链路: ②、流控效果1. 快速…...
从裸机开发到实时操作系统:FreeRTOS详解与实战指南
从裸机开发到实时操作系统:FreeRTOS详解与实战指南 本文将带你从零开始,深入理解嵌入式系统中的裸机开发与实时操作系统,以FreeRTOS为例,全面剖析其核心概念、工作原理及应用场景。无论你是嵌入式新手还是希望提升技能的开发者&am…...

Deeper and Wider Siamese Networks for Real-Time Visual Tracking
现象: the backbone networks used in Siamese trackers are relatively shallow, such as AlexNet , which does not fully take advantage of the capability of modern deep neural networks. direct replacement of backbones with existing powerful archite…...
简单介绍C++中线性代数运算库Eigen
Eigen 是一个高性能的 C 模板库,专注于线性代数、矩阵和向量运算,广泛应用于科学计算、机器学习和计算机视觉等领域。以下是对 Eigen 库的详细介绍: 1. 概述 核心功能:支持矩阵、向量运算,包括基本算术、矩阵分解&…...
Python爬虫实战:研究decrypt()方法解密
1. 引言 1.1 研究背景与意义 在当今数字化时代,网络数据蕴含着巨大的价值。然而,许多网站为了保护其数据安全和商业利益,会采用各种加密手段对传输的数据进行处理。这些加密措施给数据采集工作带来了巨大挑战。网络爬虫逆向解密技术应运而生,它通过分析和破解网站的加密机…...

黑马程序员C++2024版笔记 第0章 C++入门
1.C代码的基础结构 以hello_world代码为例: 预处理指令 #include<iostream> using namespace std; 代码前2行是预处理指令,即代码编译前的准备工作。(编译是将源代码转化为可执行程序.exe文件的过程) 主函数 主函数是…...
c#定义占用固定字节长度的结构体字段
在c中,经常类似这样定义结构体: struct DEMO_STRUCT {int a;int b;char c[128]; }; 定义这个结构体,占用了136个字节的内存空间,关键的是,它的内存块是连续的,其中c占用了128个字节 然后如果想在c#中定义…...

foxmail - foxmail 启用超大附件提示密码与帐号不匹配
foxmail 启用超大附件提示密码与帐号不匹配 问题描述 在 foxmail 客户端中,启用超大附件功能,输入了正确的账号(邮箱)与密码,但是提示密码与帐号不匹配 处理策略 找到 foxmail 客户端目录/Global 目录下的 domain.i…...

Crowdfund Insider聚焦:CertiK联创顾荣辉解析Web3.0创新与安全平衡之术
近日,权威金融科技媒体Crowdfund Insider发布报道,聚焦CertiK联合创始人兼CEO顾荣辉教授在Unchained Summit的主题演讲。报道指出,顾教授的观点揭示了Web3.0生态当前面临的挑战,以及合规与技术在推动行业可持续发展中的关键作用。…...
EDR与XDR如何选择适合您的网络安全解决方案
1. 什么是EDR? 端点检测与响应(EDR) 专注于保护端点设备(如电脑、服务器、移动设备)。通过在端点安装代理软件,EDR实时监控设备活动,检测威胁并快速响应。 EDR核心功能 实时监控:…...

PowerBI链接EXCEL实现自动化报表
PowerBI链接EXCEL实现自动化报表 曾经我将工作中一天的工作缩短至2个小时,其中最关键的一步就是使用PowerBI链接Excel做成一个自动化报表,PowerBI更新源数据,Excel更新报表并且保留报表格式。 以制作一个超市销售报表为例,简单叙…...

腾讯云MCP数据智能处理:简化数据探索与分析的全流程指南
引言 在当今数据驱动的商业环境中,企业面临着海量数据处理和分析的挑战。腾讯云MCP(Managed Cloud Platform)提供的数据智能处理解决方案,为数据科学家和分析师提供了强大的工具集,能够显著简化数据探索、分析流程,并增强数据科学…...

Android framework 中间件开发(一)
在Android开发中,经常会调用到一些系统服务,这些系统服务简化了上层应用的开发,这便是中间件的作用,中间件是介于系统和应用之间的桥梁,将复杂的底层逻辑进行一层封装,供上层APP直接调用,或者将一些APP没有权限一些操作放到中间件里面来实施. 假设一个需求,通过中间件调节系统亮…...
Lua中使用module时踩过的坑
在lua中设置某个全局对象(假如对象名为LDataUser)为nil时, LDataUser并不会变成nil, 但在有些情况下设置LDataUser nil时却真变成了nil,然后会导致后续再使用LDataUser时会抛nil异常, 后来发现是使用module搞的鬼,下面看看豆包AI给的解释,还…...

MATLAB中的概率分布生成:从理论到实践
MATLAB中的概率分布生成:从理论到实践 引言 MATLAB作为一款强大的科学计算软件,在统计分析、数据模拟和概率建模方面提供了丰富的功能。本文将介绍如何使用MATLAB生成各种常见的概率分布,包括均匀分布、正态分布、泊松分布等,并…...

C# 面向对象 构造函数带参无参细节解析
继承类构造时会先调用基类构造函数,不显式调用基类构造函数时,默认调用基类无参构造函数,但如果基类没有写无参构造函数,会无法调用从而报错;此时,要么显式的调用基类构造函数,并按其格式带上参…...
轨迹误差评估完整流程总结(使用 evo 工具)
roslaunch .launch rosbag play your_dataset.bag -r 2.0 ✅ 第二步:录制估计轨迹 bash 复制编辑 rosbag record -O traj_only.bag /aft_mapped_to_init 运行一段时间后 CtrlC 停止,生成 traj_only.bag 第三步:提取估计轨迹和真值轨迹为…...
Spring Boot 跨域问题全解:原理、解决方案与最佳实践
精心整理了最新的面试资料和简历模板,有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 一、跨域问题的本质 1.1 什么是跨域? 跨域(Cross-Origin)问题源于浏览器的同源策略(Same-Origin Policy&…...
vhca_id 简介,以及同 pf, vf 的关系
vhca_id 指的是 Virtual Host Channel Adapter ID(虚拟主机通道适配器编号),它是 NVIDIA(Mellanox)网络设备虚拟化架构中的一个核心概念。 它与 PF(物理功能)、VF(虚拟功能ÿ…...
LlamaIndex 第九篇 Indexing索引
索引概述 数据加载完成后,您将获得一个文档对象(Document)列表(或节点(Node)列表)。接下来需要为这些对象构建索引(Index),以便开始执行查询。 索引(Index) 是一种数据结构,能够让我们快速检索…...
微信小程序原生swiper高度自适应图片,不同屏幕适配,正方形1:1等比例图片轮播
🤵 作者:coderYYY 🧑 个人简介:前端程序媛,目前主攻web前端,后端辅助,其他技术知识也会偶尔分享🍀欢迎和我一起交流!🚀(评论和私信一般会回!!) 👉 个人专栏推荐:《前端项目教程以及代码》 ✨一、前言分析 一开始只设了图片的mode="widthFix" st…...

在 C# 中将 DataGridView 数据导出为 CSV
在此代码示例中,我们将学习如何使用 C# 代码将 DataGridView 数据导出到 CSV 文件并将其保存在文件夹中。 在这个程序中,首先,我们必须连接到数据库并从中获取数据。然后,我们将在数据网格视图中显示该数据,…...
解锁 CPU 性能天花板:多维优化策略深度剖析
在数字世界的底层战场,CPU 如同指挥千军万马的将军,掌控着程序运行的节奏与效率。无论是大型服务器应用,还是手机端的轻量化程序,CPU 性能的优化都如同解锁隐藏力量的密码,能让程序在执行效率上实现质的飞跃。本文将深…...
Android SwitchButton 使用详解:一个实际项目的完美实践
Android SwitchButton 使用详解:一个实际项目的完美实践 引言 在最近开发的 Android 项目中,我遇到了一个需要自定义样式开关控件的需求。经过多方比较,最终选择了功能强大且高度可定制的 SwitchButton 控件。本文将基于实际项目中的使用案…...
Kafka如何实现高性能
Kafka如何实现高性能 Kafka之所以能成为高性能消息系统的标杆,是通过多层次的架构设计和优化实现的。 一、存储层优化 1. 顺序I/O设计 日志结构存储:所有消息追加写入,避免磁盘随机写分段日志:将日志分为多个Segment文件&…...

MySQL中表的增删改查(CRUD)
一.在表中增加数据(Create) INSERT [INTO] TB_NAME [(COLUMN1,COLUMN2,...)] VALUES (value_list1),(value_list2),...;into可以省略可仅选择部分列选择插入,column即选择的列, 如图例可以选择仅在valuelist中插入age和id如果不指…...

项目思维vs产品思维
大家好,我是大明同学。 这期内容,我们来聊一下项目思维和产品思维的区别。 项目是实施关键,力求每一步都精准到位;产品则是战略导向,确保所选之路正确无误。若缺乏优异成果,即便按时完成,也只…...

游戏引擎学习第285天:“Traversables 的事务性占用”
回顾并为当天的工作做准备 我们有一个关于玩家移动的概念,玩家可以在点之间移动,而且当这些点移动时,玩家会随之移动。现在这个部分基本上已经在工作了。我们本来想实现的一个功能是:当玩家移动到某个点时,这个点能“…...
基于DWT的音频水印算法
基于离散小波变换(DWT)的音频水印算法是一种结合信号处理与信息隐藏的技术,旨在将版权信息或标识隐蔽地嵌入音频信号中,同时保证不可感知性和鲁棒性。以下是该算法的核心步骤及关键技术点: 1. 算法基本原理 DWT…...