阿里开源WebSailor:Web Agent智能体大模型-超人推理能力训练方法-深度源码解析

阿里开源WebSailor:Web Agent智能体大模型-超人推理能力训练方法-深度源码解析

概述

在大语言模型发展的道路上,如何让AI具备超越人类认知局限的能力一直是研究者追求的目标。最近,阿里巴巴通义实验室发布的WebSailor方法引起了广泛关注。

这项研究直指当前开源模型的痛点:在复杂信息搜索任务上表现远逊于专有系统。本文将深入剖析WebSailor的技术细节,探讨它如何通过创新的训练方法赋予开源Agent超人级别的推理能力。

原始论文: https://arxiv.org/abs/2507.02592

GitHub仓库: https://github.com/Alibaba-NLP/WebAgent/

模型地址:https://modelscope.cn/models/iic/WebSailor-32B

在这里插入图片描述

背景与动机

现状分析

当前开源大模型在处理复杂信息搜索任务时面临严峻挑战。以BrowseComp基准测试为例,这是专门用于评测浏览器Agent能力的测试集,大多数开源模型在上面的准确率接近于零。相比之下,专有系统如DeepResearch已经展现出了超人水平的表现。

为什么会出现如此巨大的差距?研究团队认为核心原因在于:开源模型缺乏在海量信息环境中系统性降低极端不确定性的能力

任务难度分级

img

将信息搜索任务按照不确定性程度分为三个等级:

Level 1: 不确定性低,易于解决,如通过模型内部知识或单次直接搜索即可回答的问题。
Level 2: 初始不确定性高但有明确解决路径的任务,如多跳问答,通过固定序列的推理步骤可系统性地降低不确定性。
Level 3: 不确定性高且难以降低(WebSailor工作重点)。实体以复杂、突发的方式耦合,缺乏预定义的推理路径,需要创造性探索和新颖推理模式。

class TaskLevel(Enum):
    """信息搜索任务难度分级"""
    
    LEVEL_1 = "不确定性低"  # 可通过内部知识或单次搜索解决
    LEVEL_2 = "初始不确定性高但有明确解决路径的"  # 如多跳问答,有固定推理序列
    LEVEL_3 = "不确定性高且难以降低"  # 实体复杂耦合,无预定义推理路径

第1级具有相对简单的逻辑结构,可以直接回答,或通过一次简单的工具调用得到答案。

第2级类似于多跳问题,需要通过一系列固定的推理步骤来获得解决方案。

第3级在经过混淆处理后,表现出最复杂且多变的结构,这使得其难以通过人工方式进行定义,并且其固有的不确定性也难以降低。

现状是现有训练范式主要关注Level 1和Level 2任务,导致模型在面对Level 3挑战时束手无策。WebSailor的核心贡献就是专门针对Level 3任务设计了完整的训练方案。

WebSailor技术架构

WebSailor 采用 ReAct 框架,该框架允许 agent 在接收问题后进行多次 Thought-Action-Observation 迭代。在每次迭代中,LLM 根据现有 context 生成一个 Thought,然后执行一个可解析的 Action(工具调用),环境随后返回一个 Observation。

Action 空间包括生成最终答案和调用两种工具:

  • Search 工具: 用于访问 Google 搜索引擎进行信息检索。它允许同时搜索多个查询,并为每个查询返回前 10 个结果,包括标题、摘要和对应的 URL。
  • Visit 工具: 用于访问特定网页。输入包括多个网页及其对应的访问目标。首先,利用 Jina 获取网页的完整内容,然后使用 Qwen-2.5-72B 作为摘要模型,根据目标提取相关信息。

Agent框架设计

WebSailor采用经典的ReAct框架,通过Thought-Action-Observation循环进行推理:

class WebSailorAgent:
    """WebSailor Agent实现"""
    
    def __init__(self, llm_model, search_tool, visit_tool):
        self.llm = llm_model
        self.search_tool = search_tool  # Google搜索接口
        self.visit_tool = visit_tool    # 网页访问工具
        self.max_iterations = 50        # 最大推理轮数
        
    def solve_task(self, question):
        """执行任务求解流程"""
        trajectory = []
        context = f"Question: {question}\n"
        
        for i in range(self.max_iterations):
            # 生成思考
            thought = self.llm.generate_thought(context)
            trajectory.append(("thought", thought))
            
            # 决定行动
            action = self.llm.generate_action(context + thought)
            trajectory.append(("action", action))
            
            # 执行行动并获取观察
            if action.type == "search":
                observation = self.search_tool.search(action.queries)
            elif action.type == "visit":
                observation = self.visit_tool.visit_pages(action.urls, action.targets)
            elif action.type == "answer":
                return action.answer, trajectory
                
            trajectory.append(("observation", observation))
            context += f"\nThought_{i}: {thought}\nAction_{i}: {action}\nObservation_{i}: {observation}"
            
        return None, trajectory

工具系统实现

WebSailor配备了两个核心工具:

class SearchTool:
    """搜索工具实现"""
    
    def search(self, queries):
        """
        同时搜索多个查询
        Args:
            queries: List[str] - 搜索查询列表
        Returns:
            List[Dict] - 每个查询返回前10个结果
        """
        results = []
        for query in queries:
            # 调用Google搜索API
            search_results = google_search_api(query, num_results=10)
            results.append({
                'query': query,
                'results': [
                    {
                        'title': r.title,
                        'snippet': r.snippet,
                        'url': r.url
                    } for r in search_results
                ]
            })
        return results

class VisitTool:
    """网页访问工具实现"""
    
    def __init__(self, summarizer_model="Qwen-2.5-72B"):
        self.jina_client = JinaClient()  # 网页内容提取
        self.summarizer = load_model(summarizer_model)
        
    def visit_pages(self, urls, targets):
        """
        访问网页并提取相关信息
        Args:
            urls: List[str] - 网页URL列表
            targets: List[str] - 每个网页的信息提取目标
        Returns:
            List[Dict] - 提取的相关信息
        """
        results = []
        for url, target in zip(urls, targets):
            # 获取网页完整内容
            full_content = self.jina_client.get_page_content(url)
            
            # 使用大模型提取相关信息
            relevant_info = self.summarizer.extract_info(
                content=full_content,
                target=target,
                max_length=2000  # 控制提取长度
            )
            
            results.append({
                'url': url,
                'target': target,
                'extracted_info': relevant_info
            })
        return results

核心创新:SailorFog-QA数据集

SailorFog-QA 是一个用于训练模型处理高不确定性难以降低不确定性的复杂信息查找任务(即 Level 3 任务)的数据集。其合成过程主要包括两个方面:构建复杂的信息图谱作为结构基础,以及通过子图采样和信息模糊化生成高不确定性问题。

具体合成步骤如下:

  1. 构建难以降低不确定性的结构基础
    • 首先,通过随机游走(random walks)从真实世界网站构建知识图谱,旨在形成非线性、复杂交联的结构。
    • 种子实体选择:从 Wikidata 的 SPARQL 服务中获取一个模糊的、较不常见的实体作为图谱的起始点,以确保初始的挑战性。
    • 信息收集:利用模拟网络浏览功能,通过搜索(search)和访问(visit)工具(例如使用 Jina.ai 和 Qwen-2.5-72B 作为摘要模型)收集起始实体在互联网上的非结构化文本和特征。
    • 图谱扩展:从收集到的原始信息中提取相关的实体及其之间的关系,形成初始的节点(entities)和边(relationships)。
    • 迭代式增长:以一定的概率,选择现有节点并寻找新的、独特的实体进行连接。这种随机过程有助于避免生成简单的线性关系链(Level 2 任务的特点),而是促进形成一个密集互联、具有复杂重叠关系路径的图谱。
    • 最终形成的图谱为缺乏预定义推理路径的问题提供了结构基础,迫使代理在复杂的信息网络中导航,而非遵循直线路径。
  2. 通过子图采样和模糊化生成高不确定性问题
    • 这种结构复杂性和信息模糊性的结合,直接增加了问题的初始不确定性,迫使代理必须进行推理、比较和信息整合,而不仅仅是简单的查找。
    • 将精确的日期转换为模糊的时间段(如“2010年代早期”)。
    • 部分遮蔽人名(如“由名字首字母为‘F’的人创立的机构”)。
    • 定性描述定量属性(如“市场份额小于1%”)。
    • 子图采样:从上述构建的复杂图谱中采样具有多样化拓扑结构的子图,每个子图代表一个独特的耦合实体和关系组合。
    • 问题与答案生成:基于采样的子图,自动生成对应的问题和答案。
    • 信息模糊化:为了增加初始不确定性,刻意通过信息模糊化技术引入歧义。

SailorFog-QA 的关键优势

  • 真实世界基础:数据根植于真实的互联网信息,反映了代理在实践中面临的挑战。
  • 复杂推理模式多样性:多样化的子图拓扑结构自然地生成了需要多步骤演绎、组合分析和比较分析等复杂推理模式的问题。
  • 高可扩展性:潜在子图的数量随着图谱规模的增长呈非线性增加,从而能够高效地进行大规模数据合成。

这些生成的 Level 3 任务非常具有挑战性,即使是强大的专有模型(如 o3)也可能需要多达40次工具调用才能解决,并且人工研究人员在常规时间限制内也难以解决,因为它们缺乏明确的搜索起点,需要进行广泛的非线性探索。

复杂信息图谱构建

WebSailor的一大创新是通过自动化方法构建高质量的Level 3训练数据。其核心思路是先构建复杂的信息图谱,再从中采样生成训练任务:

class KnowledgeGraphBuilder:
    """知识图谱构建器"""
    
    def __init__(self):
        self.graph = nx.DiGraph()  # 使用有向图存储
        self.wikidata_client = WikidataClient()
        self.web_tools = WebTools()
        
    def build_complex_graph(self, num_iterations=100):
        """构建复杂信息图谱"""
        # 1. 选择种子实体(选择较少见的实体增加挑战性)
        seed_entity = self.wikidata_client.get_obscure_entity()
        self.graph.add_node(seed_entity.id, **seed_entity.attributes)
        
        # 2. 迭代扩展图谱
        for _ in range(num_iterations):
            # 随机选择现有节点
            if random.random() < 0.7:  # 70%概率从现有节点扩展
                current_node = random.choice(list(self.graph.nodes()))
                
                # 搜索相关信息
                search_results = self.web_tools.search(current_node)
                
                # 提取新实体和关系
                new_entities = self.extract_entities(search_results)
                
                # 添加到图谱(避免简单线性结构)
                for entity in new_entities:
                    self.graph.add_node(entity.id, **entity.attributes)
                    # 建立多重连接关系
                    self.graph.add_edge(current_node, entity.id, 
                                      relation=entity.relation)
                    
                    # 概率性地与其他节点建立连接(增加复杂性)
                    if random.random() < 0.3:
                        other_node = random.choice(list(self.graph.nodes()))
                        if other_node != entity.id:
                            self.graph.add_edge(entity.id, other_node,
                                              relation="implicit_connection")
        
        return self.graph

信息模糊化技术

为了增加任务的不确定性,WebSailor采用了多种信息模糊化技术:

class InformationObfuscator:
    """信息模糊化处理器"""
    
    def obfuscate(self, subgraph_info):
        """对子图信息进行模糊化处理"""
        obfuscated_info = {}
        
        for entity, attributes in subgraph_info.items():
            obfuscated_attrs = {}
            
            for key, value in attributes.items():
                if key == "date":
                    # 日期模糊化:2012-03-15 -> "2010年代早期"
                    obfuscated_attrs[key] = self.obfuscate_date(value)
                    
                elif key == "person_name":
                    # 人名部分遮蔽:"Frank Miller" -> "名字首字母为'F'的人"
                    obfuscated_attrs[key] = self.obfuscate_name(value)
                    
                elif key == "quantity":
                    # 定量转定性:"0.8%" -> "市场份额小于1%"
                    obfuscated_attrs[key] = self.obfuscate_quantity(value)
                    
                else:
                    obfuscated_attrs[key] = value
                    
            obfuscated_info[entity] = obfuscated_attrs
            
        return obfuscated_info
    
    def obfuscate_date(self, date_str):
        """日期模糊化实现"""
        year = int(date_str.split('-')[0])
        if 2010 <= year < 2015:
            return "2010年代早期"
        elif 2015 <= year < 2020:
            return "2010年代后期"
        # ... 更多规则
        
    def obfuscate_name(self, name):
        """人名模糊化实现"""
        first_letter = name[0].upper()
        return f"名字首字母为'{first_letter}'的人"
        
    def obfuscate_quantity(self, quantity):
        """数量模糊化实现"""
        value = float(quantity.strip('%'))
        if value < 1:
            return "小于1%"
        elif value < 5:
            return "1%到5%之间"
        # ... 更多规则

训练数据生成流程

class SailorFogQAGenerator:
    """SailorFog-QA数据集生成器"""
    
    def __init__(self):
        self.graph_builder = KnowledgeGraphBuilder()
        self.obfuscator = InformationObfuscator()
        self.question_generator = QuestionGenerator()
        
    def generate_dataset(self, num_samples=10000):
        """生成完整数据集"""
        dataset = []
        
        # 构建大规模复杂图谱
        knowledge_graph = self.graph_builder.build_complex_graph(
            num_iterations=1000
        )
        
        for _ in range(num_samples):
            # 1. 采样子图(确保拓扑结构多样性)
            subgraph = self.sample_diverse_subgraph(knowledge_graph)
            
            # 2. 信息模糊化
            obfuscated_info = self.obfuscator.obfuscate(
                self.extract_subgraph_info(subgraph)
            )
            
            # 3. 生成问题和答案
            question, answer = self.question_generator.generate_qa(
                subgraph, obfuscated_info
            )
            
            # 4. 使用专家模型生成解决轨迹
            expert_trajectory = self.generate_expert_trajectory(
                question, answer, knowledge_graph
            )
            
            dataset.append({
                'question': question,
                'answer': answer,
                'trajectory': expert_trajectory,
                'difficulty_metrics': self.calculate_difficulty(expert_trajectory)
            })
            
        return dataset
    
    def sample_diverse_subgraph(self, graph, min_nodes=5, max_nodes=15):
        """采样具有多样化拓扑结构的子图"""
        num_nodes = random.randint(min_nodes, max_nodes)
        
        # 使用不同策略确保拓扑多样性
        strategies = ['bfs', 'dfs', 'random_walk', 'community']
        strategy = random.choice(strategies)
        
        if strategy == 'bfs':
            # 广度优先采样(趋向于星形结构)
            start_node = random.choice(list(graph.nodes()))
            subgraph_nodes = list(nx.bfs_tree(graph, start_node, 
                                             depth_limit=3))[:num_nodes]
                                             
        elif strategy == 'dfs':
            # 深度优先采样(趋向于链式结构)
            start_node = random.choice(list(graph.nodes()))
            subgraph_nodes = list(nx.dfs_tree(graph, start_node, 
                                            depth_limit=num_nodes))
                                            
        elif strategy == 'random_walk':
            # 随机游走采样(更自然的结构)
            subgraph_nodes = self.random_walk_sample(graph, num_nodes)
            
        elif strategy == 'community':
            # 社区采样(密集连接的子图)
            subgraph_nodes = self.community_sample(graph, num_nodes)
            
        return graph.subgraph(subgraph_nodes)

专家推理重构技术

虽然开源的大型推理模型(LRMs)如 QwQ-32B 可以解决一些复杂问答,但直接使用其原始输出进行微调效果不佳。存在两大问题:

  • 风格污染: 专家 LRMs 具有强烈且通常冗长的推理风格(当然,这也有可能囿于现有专家自身能力不足),直接模仿可能限制 agent 发展自己的探索策略。
  • 上下文过载(Context Overload): 冗长的推理链条在复杂 web agent 任务中容易超出上下文窗口限制,导致性能下降和可读性差。

如何解决?首先,通过提示的方法让开源 LRM 生成完整的解决方案轨迹,包括其原始 thought。然后,选择性地丢弃其的冗长 thought,仅保留成功的 Action-Observation 序列。接着,对动作序列中的每一步,利用另一个强大的指令遵循模型 (原文未明确说明具体是哪个模型),根据历史 context、专家选择的 action 和随后的 observation,重建简洁、面向行动的 thought)。这种方法通过强制使用“short-CoT”风格,确保最终的推理链足够紧凑,适用于长时任务,从而可扩展地生成高质量监督数据,灌输复杂推理模式而无直接模仿的副作用。

class ReasoningReconstructor:
    """推理过程重构器"""
    
    def __init__(self, expert_model="QwQ-32B", reconstructor_model="Qwen-2.5-72B"):
        self.expert = load_model(expert_model)
        self.reconstructor = load_model(reconstructor_model)
        
    def generate_training_data(self, question, answer):
        """生成训练数据"""
        # 1. 让专家模型生成完整解决方案
        expert_trajectory = self.expert.solve_with_tools(
            question, 
            verbose=True  # 包含详细推理过程
        )
        
        # 2. 提取成功的action序列
        successful_actions = self.extract_successful_actions(
            expert_trajectory, answer
        )
        
        # 3. 重构简洁的thought
        reconstructed_trajectory = []
        context = f"Question: {question}\n"
        
        for i, (action, observation) in enumerate(successful_actions):
            # 使用重构模型生成简洁thought
            prompt = self.build_reconstruction_prompt(
                context, action, observation
            )
            
            concise_thought = self.reconstructor.generate(
                prompt,
                max_tokens=150,  # 限制长度
                style="action_oriented"  # 面向行动的风格
            )
            
            reconstructed_trajectory.append({
                'thought': concise_thought,
                'action': action,
                'observation': observation
            })
            
            context += f"\nThought_{i}: {concise_thought}"
            context += f"\nAction_{i}: {action}"
            context += f"\nObservation_{i}: {observation}"
            
        return reconstructed_trajectory
    
    def build_reconstruction_prompt(self, context, action, observation):
        """构建重构提示"""
        return f"""给定以下上下文和专家选择的行动,
                生成一个简洁的、以行动为导向的想法,解释为什么采取此行动,
                以及我们正在寻找什么信息。

                上下文:
                {context}

                专家的行动:{action}

                行动的观察:{observation}

                生成一个想法,满足以下要求:
                1. 简洁(不超过 50 个字)
                2. 与所采取的行动直接相关
                3. 解释推理过程,无需冗长的推测
                4. 专注于信息收集策略

                Thought:"""

两阶段训练策略

WebSailor 使用两阶段训练。首先采用了一个适度的Rejection Sampling Fine-Tuning (RFT)阶段作为“冷启动”。这一初始阶段旨在使模型具备基本的工具使用能力,并遵循长程推理的基本框架。随后,利用强化学习(RL)进一步提升其推理能力,提高其样本效率,并使其能够更充分地利用高质量、复杂的训练数据。

训练细节: SFT 阶段使用 Megatron ,RL 训练使用 verl 。具体超参数设置在附录中详细给出。

阶段1:RFT冷启动

  • 这个初始阶段旨在为模型提供基本的工具使用能力和遵循长链推理框架的能力。
  • 过滤: 对专家生成的轨迹进行三阶段过滤:
    1. 只保留最终答案正确的轨迹;
    2. 丢弃长度超过 32k token 的轨迹;
    3. 只保留工具调用次数超过 5 次的轨迹,以确保任务复杂性。
  • 训练目标: 专门增强 agent 的决策能力,即生成有效 thought 和 action 的能力。环境中 observation () 对应的 token 会被 mask 掉,不参与损失计算。
  • 研究表明,即使是仅包含 2k 多个高质量示例的适度 RFT 冷启动也是不可或缺的。
class RFTColdStart:
    """Rejection Sampling Fine-Tuning冷启动"""
    
    def __init__(self, base_model, training_config):
        self.model = base_model
        self.config = training_config
        
    def prepare_training_data(self, raw_trajectories):
        """准备RFT训练数据"""
        filtered_data = []
        
        for trajectory in raw_trajectories:
            # 三阶段过滤
            if not self.is_correct_answer(trajectory):
                continue  # 过滤1:只保留答案正确的
                
            if self.calculate_length(trajectory) > 32000:
                continue  # 过滤2:长度限制
                
            if self.count_tool_calls(trajectory) < 5:
                continue  # 过滤3:确保复杂度
                
            # 构建训练样本
            training_sample = self.build_training_sample(trajectory)
            filtered_data.append(training_sample)
            
        return filtered_data
    
    def build_training_sample(self, trajectory):
        """构建训练样本,只训练thought和action"""
        tokens = []
        labels = []
        
        for step in trajectory:
            if step['type'] == 'thought':
                thought_tokens = self.tokenize(step['content'])
                tokens.extend(thought_tokens)
                labels.extend(thought_tokens)  # 训练thought
                
            elif step['type'] == 'action':
                action_tokens = self.tokenize(step['content'])
                tokens.extend(action_tokens)
                labels.extend(action_tokens)  # 训练action
                
            elif step['type'] == 'observation':
                obs_tokens = self.tokenize(step['content'])
                tokens.extend(obs_tokens)
                labels.extend([-100] * len(obs_tokens))  # mask observation
                
        return {
            'input_ids': tokens,
            'labels': labels
        }
    
    def train(self, training_data):
        """执行RFT训练"""
        # 使用Megatron进行分布式训练
        trainer = MegatronTrainer(
            model=self.model,
            args=self.config,
            train_dataset=training_data,
            compute_metrics=self.compute_metrics
        )
        
        trainer.train()
        return self.model

阶段2:DUPO强化学习

在 RFT 冷启动之后,DUPO 用于进一步提升 agent 的推理能力和样本效率。RL agent 的 rollout 涉及与环境的多轮交互,导致其速度远慢于标准 RL。DUPO 引入了两种动态采样策略来解决这一问题:

  • 训练前: 过滤掉过于简单的案例(所有 8 个 rollout 都正确的案例)。

    • rollout = 模型从 prompt 出发,一次生成完整的回答序列(或和环境交互的一整条轨迹)。
  • 训练中: 对于batch中标准差非零的样本(即并非所有 rollout 都完全正确或完全不正确,换句话说部分rollout成功,部分失败,存在标准差),通过复制这些样本来扩充填满batch,而不是使用 padding。这比 DAPO 的动态采样方法(去环境中拉取新样本)提高了约 2-3 倍的速度。

  • 损失计算:在计算策略损失时,与监督微调(SFT)类似同样会 mask 掉 observation。

  • 优势估计:DUPO遵循GPRO(Group-Relative Policy Optimization)方法来估计组内相对优势

  • 策略梯度损失:采用了DAPO中的token级别的策略梯度损失和更高的clip技术

  • DUPO的训练目标:DUPO的训练目标是帮助模型发现并内化超越直接模仿的复杂问题解决策略

  • DUPO的奖励机制:为了避免奖励作弊(reward hacking),WebSailor采用了基于规则的奖励机制。这个奖励机制结合了两个部分:

    • 格式验证(0.1权重):检查rollout轨迹是否遵循预定义的格式,例如不同内容段是否正确地用<think><tool_call>等标签包裹,以及序列是否符合ReAct框架。
    • 答案验证(0.9权重):使用另一个LLM作为判断器来确定最终预测是否正确

img

从图可以看出,RL阶段(绿色部分注明了提升幅度)对模型的性能,尤其是在BrowseComp这种高难度任务上,带来了巨大的提升。

研究表明,传统RL方法在长序列Agent任务上效率极低。DUPO通过动态采样策略显著提升了训练效率:

class DUPOTrainer:
    """Duplicating Sampling Policy Optimization训练器"""
    
    def __init__(self, model, reward_model, training_config):
        self.policy_model = model
        self.reward_model = reward_model
        self.config = training_config
        
    def train_step(self, batch_questions):
        """单步DUPO训练"""
        # 1. 生成多个rollouts
        all_rollouts = []
        all_rewards = []
        
        for question in batch_questions:
            rollouts = []
            rewards = []
            
            # 每个问题生成8个rollouts
            for _ in range(8):
                trajectory = self.policy_model.generate_trajectory(question)
                reward = self.calculate_reward(trajectory, question)
                
                rollouts.append(trajectory)
                rewards.append(reward)
                
            all_rollouts.append(rollouts)
            all_rewards.append(rewards)
        
        # 2. 动态采样策略
        training_batch = self.dynamic_sampling(
            batch_questions, all_rollouts, all_rewards
        )
        
        # 3. 计算策略梯度损失
        loss = self.compute_dupo_loss(training_batch)
        
        # 4. 更新模型
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()
        
        return loss.item()
    
    def dynamic_sampling(self, questions, rollouts, rewards):
        """DUPO动态采样"""
        training_batch = []
        
        for q_idx, (question, q_rollouts, q_rewards) in enumerate(
            zip(questions, rollouts, rewards)
        ):
            rewards_array = np.array(q_rewards)
            
            # 过滤过于简单的案例(所有rollout都正确)
            if np.all(rewards_array == 1.0):
                continue
                
            # 计算标准差
            std = np.std(rewards_array)
            
            if std > 0:
                # 部分成功案例,通过复制扩充batch
                num_copies = max(1, int(4 * std))  # 根据标准差决定复制数
                
                for _ in range(num_copies):
                    training_batch.append({
                        'question': question,
                        'rollouts': q_rollouts,
                        'rewards': q_rewards
                    })
            else:
                # 标准差为0,正常添加
                training_batch.append({
                    'question': question,
                    'rollouts': q_rollouts,
                    'rewards': q_rewards
                })
                
        return training_batch
    
    def compute_dupo_loss(self, batch):
        """计算DUPO损失"""
        total_loss = 0
        
        for sample in batch:
            # 计算组内相对优势(GPRO方法)
            advantages = self.compute_group_advantages(sample['rewards'])
            
            for rollout, advantage in zip(sample['rollouts'], advantages):
                # Token级别的策略梯度损失
                trajectory_loss = 0
                
                for step in rollout:
                    if step['type'] in ['thought', 'action']:
                        # 只对thought和action计算损失
                        log_probs = self.policy_model.get_log_probs(
                            step['content'], 
                            step['context']
                        )
                        
                        # 应用advantage和clip
                        ratio = torch.exp(log_probs - step['old_log_probs'])
                        clipped_ratio = torch.clamp(
                            ratio, 
                            1 - self.config.clip_epsilon,
                            1 + self.config.clip_epsilon
                        )
                        
                        loss = -torch.min(
                            ratio * advantage,
                            clipped_ratio * advantage
                        ).mean()
                        
                        trajectory_loss += loss
                        
                total_loss += trajectory_loss
                
        return total_loss / len(batch)
    
    def calculate_reward(self, trajectory, question):
        """计算奖励(避免reward hacking)"""
        # 格式验证(权重0.1)
        format_score = self.validate_format(trajectory)
        
        # 答案验证(权重0.9)
        final_answer = self.extract_final_answer(trajectory)
        answer_score = self.reward_model.judge_answer(
            question, 
            final_answer
        )
        
        return 0.1 * format_score + 0.9 * answer_score
    
    def validate_format(self, trajectory):
        """验证轨迹格式"""
        # 检查是否遵循ReAct框架
        for i in range(0, len(trajectory), 3):
            if i >= len(trajectory):
                break
                
            if trajectory[i]['type'] != 'thought':
                return 0.0
            if i+1 < len(trajectory) and trajectory[i+1]['type'] != 'action':
                return 0.0
            if i+2 < len(trajectory) and trajectory[i+2]['type'] != 'observation':
                return 0.0
                
        # 检查标签格式
        if not all(self.check_tags(step) for step in trajectory):
            return 0.0
            
        return 1.0

实验结果

文章在多个挑战性基准测试上对 WebSailor 进行了全面评估,并与多种基线方法进行比较。

评测基准和基线模型

1、**模型与基准测试:**WebSailor 在 Qwen-2.5-3B, Qwen-2.5-7B, Qwen-2.5-32B 和 Qwen-2.5-72B 模型上进行了 RFT 和 RL 训练。 主要评估基准包括:

  • BrowseComp-en / BrowseComp-zh: OpenAI 引入的最具挑战性的基准之一,评估 AI agent 在互联网上定位复杂、多方面信息的熟练度,需要复杂的浏览策略和推理能力。

img

  • GAIA: 一个需要多模态和工具使用能力的通用基准,本文仅使用其中 103 个纯文本验证子集。
  • Xbench-DeepSearch: 一个新的、动态的、专业对齐的基准,专注于评估 AI agent 的工具使用能力,特别是在深度信息检索和复杂搜索任务中。

2、基线方法:

  • Direct Inference (直接推理): 模型仅依靠其内部知识回答问题,包括 Qwen-2.5、GPT-4o、GPT-4.1、QwQ-32B、o4-mini 和 DeepSeek-R1 等。
  • Proprietary Browsing Agents (专有浏览 agent): 如 OpenAI DeepResearch、Grok-DeepResearch 和 Doubao with Deep Think and Search。
  • Open-source Agents (开源 agent): 包括 Search-o1、WebThinker、R1-Searcher 和 WebDancer 等,它们大多采用 ReAct 框架。

3、评估指标: 默认使用 pass@k 评估,并报告 pass@1。准确率通过 LLM 作为判断器来确定。

综合分析

  • 直接推理的不足: 所有直接推理模型(包括强大的专有模型 GPT-4.1)在 BrowseComp-en/zh 上的表现都非常差,准确率通常接近于零,这表明这些任务需要与外部信息源(如 web)动态交互才能收集到必要证据。
  • WebSailor 确立开源 agent 的新 SOTA: WebSailor 在开源 agent 中树立了新标杆,在极具挑战性的 BrowseComp-en/zh 基准上优势最为显著。这验证了其核心假设:在合成的复杂、难以降低不确定性数据上训练,赋予了 agent 鲁棒且可泛化的推理策略。例如,WebSailor-7B 在 BrowseComp-en 上实现了 6.7% 的准确率,果断超越了基于更大 32B 模型的 agent(如 WebDancer-32B 和 WebThinker-RL)。
  • 与专有系统持平: WebSailor 的最重要发现之一是它弥补了开源系统与领先专有系统之间的差距。在 BrowseComp-zh 上,WebSailor-72B 的性能与顶尖专有 agent Doubao 持平。尽管 SOTA 系统 DeepResearch 仍保持领先,但 WebSailor 的表现标志着一个重要里程碑,证明通过复杂的数据合成和 DUPO 等有针对性的训练策略,开源模型能够提升到以前专有系统独有的能力水平。
  • GAIA 性能: 尽管 WebSailor 在所有基准上表现强劲,但在 GAIA 上的优势更为适度。研究人员手动检查发现,这部分是由于 GAIA 任务中很大一部分需要数学和计算能力,而 WebSailor 未专门针对这些能力进行优化。

img

关键发现

  1. 规模效应有限:WebSailor-7B的表现已经超越了基于32B模型的其他方法,说明性能提升主要来自训练方法创新而非单纯的模型规模扩大。

  2. 与专有系统的差距显著缩小:WebSailor-72B在BrowseComp-zh上的表现已经接近顶级专有系统Doubao的水平。

  3. 向下兼容性良好:尽管专门针对Level 3任务训练,WebSailor在简单任务(如SimpleQA)上的表现也优于其他方法。

实际案例分析

让我们通过两个具体案例来理解WebSailor的工作方式:

案例1:复杂实体关系查询
# 问题:找出在2010年代早期创立的、名字首字母为'F'的人所创办的公司,
#       该公司后来被市场份额小于1%的科技巨头收购,收购金额是多少?

# WebSailor的推理过程
trajectory = [
    {
        'thought': "需要先找出2010年代早期名字首字母为F的创业者",
        'action': "search(['founders with name starting F 2010-2015', 'tech entrepreneurs F 2010s'])",
        'observation': "Found: Frank Johnson (2011), Felix Chen (2012), Fiona Roberts (2013)..."
    },
    {
        'thought': "逐一查询这些创业者创办的公司",
        'action': "search(['Frank Johnson founded company', 'Felix Chen startup 2012', 'Fiona Roberts company 2013'])",
        'observation': "Frank Johnson founded DataSync in 2011, Felix Chen founded CloudAPI in 2012..."
    },
    {
        'thought': "查询这些公司的收购情况",
        'action': "visit(['https://techcrunch.com/datasync-acquisition', 'https://bloomberg.com/cloudapi-deal'], ['acquisition details', 'buyer and amount'])",
        'observation': "DataSync was acquired by MicroTech (0.7% market share) for $45M in 2018..."
    },
    # ... 继续推理直到找到答案
]
案例2:隐含信息推理
# 问题:某个欧洲国家的前总理在离任后创办的智库,
#       其第三任主席后来成为了哪个国际组织的领导人?

# WebSailor需要进行多层推理:
# 1. 识别可能的欧洲前总理
# 2. 查找他们创办的智库
# 3. 追踪智库的历任主席
# 4. 确定第三任主席的后续职业发展

# 这类问题没有直接的搜索路径,需要创造性的探索策略

技术创新总结

WebSailor的成功归功于几个关键创新:

  1. 系统性的Level 3任务训练:通过SailorFog-QA数据集,首次让模型大规模接触高不确定性任务。

  2. 推理重构技术:避免了直接模仿专家模型带来的风格污染问题。

  3. 高效的RL训练

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值