OpenSeeker-v2: Pushing the Limits of Search Agents with Informative and High-Difficulty Trajectories
Paper: arXiv:2605.04036 Code: PolarSeeker/OpenSeeker Code reference:
main@954a286a(2026-05-22)
1. Motivation (研究动机)
当前 frontier search agents 的主要瓶颈不是单纯“有没有浏览器工具”,而是强搜索能力的训练路线被工业级闭源管线垄断:论文把典型 recipe 概括为大规模 pre-training / continual pre-training (CPT) → SFT → RL,多阶段管线依赖大算力、私有数据与复杂训练基础设施,导致学术团队很难复现或迭代同等规模的 Deep Research agent。
OpenSeeker-v2 想回答一个更尖锐的问题:如果只使用标准 SFT,能不能通过更难、更信息密集的 ReAct trajectories,把约 30B 规模 search agent 推到接近甚至超过重型 CPT+SFT+RL 管线的水平?这与 OpenSeeker: Democratizing Frontier Search Agents by Fully Open-Sourcing Training Data 的延续关系很明确:v1 已经证明 open-source SFT baseline 可行,v2 进一步把注意力从“训练阶段复杂度”转回“trajectory 数据质量”。
这个问题值得研究的原因有三点。第一,如果 SFT-only 能成立,search-agent 研究的门槛会从昂贵 RL/CPT pipeline 转向可检查、可扩展的数据合成策略。第二,deep search 的核心能力是长程信息搜集、多跳证据聚合、工具调用规划;这些能力很可能更依赖训练轨迹中是否真的包含长 horizon 搜索,而不只是优化算法是否复杂。第三,OpenSeeker-v2 的结果为 academic/open-source 社区提供了一个强 baseline:用 10.6k 条高难轨迹训练的 30B ReAct agent,在四个 benchmark 上达到 46.0 / 58.1 / 34.6 / 78.0。
2. Idea (核心思想)
核心 insight 是:search agent 的能力上限可以被训练 trajectories 的信息密度和难度显著抬高;当轨迹本身迫使模型做多跳探索、使用更丰富工具、走更长工具调用链时,标准 SFT 也能学习到强长程搜索策略。论文并不是提出一个复杂 RL objective,而是把“训练样本是否足够难”当作主要杠杆。
OpenSeeker-v2 的关键创新很简单:在数据合成阶段扩大 source graph 的拓扑扩展半径,让问题天然需要跨多个节点聚合证据;扩大工具集合,让 trajectory 展示更多功能模式;再用 strict low-step filtering 删除太容易的轨迹,保证训练集有最低难度地板。最终只保留 10.6k 条样本,但这些样本平均工具调用步数达到 64.67,高于 OpenSeeker-v1 的 46.97 和 RedSearcher 的 36.01。
与 Tongyi DeepResearch、RedSearcher、WebSailor-V2-RL 这类重型或 RL-enhanced 路线相比,OpenSeeker-v2 的根本差别是把训练 recipe 从“多阶段优化”简化为“SFT on harder trajectories”。这并不说明 RL 对 search agent 没价值,而是说明在约 30B ReAct-based agent 这个 setting 下,数据合成质量本身已经能带来非常大的增益。
3. Method (方法)
3.1 Overall framework:SFT-only search agent from high-difficulty trajectories
论文没有给出传统意义上的 pipeline architecture diagram;Figure 1 是一个 performance overview,用来说明 OpenSeeker-v2 在同等模型规模和 ReAct paradigm 内的相对位置。方法本身可以拆成四步:从 source graph 合成更复杂 query;让 agent 在更大工具集合下产生 ReAct trajectory;按工具调用步数过滤掉简单样本;最后用标准 SFT 训练 Qwen3-30B-A3B-Thinking-2507 初始化的 search agent。
Figure 1 解读:这张图不是训练架构图,而是把 OpenSeeker-v2 放在四个 deep-search benchmark 的 leaderboard 语境中。它强调同一件事:OpenSeeker-v2 使用 SFT-only,却在 BrowseComp、BrowseComp-ZH、HLE 和 xbench 上超过多个约 30B 的 ReAct-based search agents,甚至超过使用 CPT+SFT+RL 的 Tongyi DeepResearch。读图时应把它理解为“数据质量路线”的结果证据,而不是一个额外模型模块。
直觉上,SFT 不是只能学习短 answer pattern;如果 demonstration trajectories 本身包含足够长的 search loop,模型会被迫模仿“先提出检索假设 → 调用工具 → 从 observation 中抽取证据 → 修正查询/继续访问 → 最后回答”的行为分布。OpenSeeker-v2 的设计就是把简单 lookup 样本从训练集中剔除,让 SFT gradient 主要来自长 horizon 的行为轨迹。
3.2 Scaling graph size:用更大的 source subgraph 生成多跳问题
论文把源信息表示为图: 对每个 seed node ,原始 pipeline 会围绕 seed 构造一个局部子图。OpenSeeker-v2 把 expansion budget 从 提高到 ,其中 ,得到更大的 evidence subgraph: 随后 query generator 在扩展后的证据上下文上采样问题: 这个组件的输入是 source graph 与 seed node,输出是更大、更丰富的 evidence subgraph 以及由它条件生成的问题 。作者需要它,是因为一个小局部图容易产生单点事实查询;扩大拓扑上下文会增加可行 reasoning paths 的数量和多样性,使问题更可能需要多跳证据聚合。如果移除这一步,后续 trajectory 即使很长也可能只是重复浅层搜索,而不是由任务结构强制出的长程探索。
def synthesize_query_from_expanded_graph(source_graph, seed_node, K, query_generator):
"""Paper-level pseudocode; data-synthesis code is not released in PolarSeeker/OpenSeeker."""
evidence_subgraph = expand_graph(source_graph, seed_node, budget=K)
query = query_generator.sample(condition=evidence_subgraph)
return query, evidence_subgraph3.3 Expanding the tool set:让 ReAct trajectory 展示更多功能模式
给定合成问题 ,OpenSeeker-v2 让 agent 在扩展工具集合 下生成多步 ReAct-style trajectory: 其中 是第 次工具调用, 是工具返回 observation, 是 action 前的 reasoning trace, 是最终回答前的 reasoning, 是答案。这个组件的输入是 query 与工具集合 ,输出是带 reasoning / action / observation 交替结构的 trajectory 。
released code 中,v2 推理侧的工具集合由 tools_visit + tools_e2b 组成:search、visit、create_sandbox、run_command、run_python_code、upload_file_from_local_to_sandbox、download_file_from_internet_to_sandbox。这支持普通网页搜索/访问,也支持在 E2B sandbox 内执行轻量 shell/Python 或文件搬运。它对应论文里的“broader functionality”,但注意:repo 只给出推理/评测工具接口,没有公开生成训练 trajectory 的完整数据合成代码。
def execute_tool_call(tool_name: str, tool_args: dict, filter_huggingface: bool = True) -> str:
"""Mirrors src/llm_tool_openseeker_v2.py::_execute_tool at main@954a286a."""
if tool_name == "search":
return Search({"filter_huggingface": filter_huggingface}).call(tool_args)
if tool_name in ("visit", "visit_summary"):
return Visit().call(tool_args)
validate_e2b_tool_args(tool_name, tool_args)
if tool_name == "create_sandbox":
return create_sandbox(timeout=int(tool_args.get("timeout", 600)))
if tool_name == "run_command":
return run_command(tool_args["command"], tool_args["sandbox_id"])
if tool_name == "run_python_code":
return run_python_code(tool_args["code_block"], tool_args["sandbox_id"])
if tool_name == "upload_file_from_local_to_sandbox":
return upload_file_from_local_to_sandbox(
tool_args["sandbox_id"], tool_args["local_file_path"], tool_args.get("sandbox_file_path", "/home/user")
)
if tool_name == "download_file_from_internet_to_sandbox":
return download_file_from_internet_to_sandbox(
tool_args["sandbox_id"], tool_args["url"], tool_args.get("sandbox_file_path", "/home/user")
)
return "Unknown tool or call tool with incorrect format."3.4 Strict low-step filtering:删除“太容易”的 trajectory
为了避免训练集被浅层 lookup 任务污染,论文定义了低步数过滤: 这里 是 trajectory 的工具调用步数, 是预设最小工具调用阈值。输入是 raw synthesized dataset ,输出是 filtered dataset 。它优化的不是一个 differentiable loss,而是训练数据分布:过滤后 SFT 看到的 demonstration 更偏向长程、多工具、多证据链。如果移除它,模型会从大量短轨迹中学习到“快速直接答复”或“浅检索即停止”的策略,削弱长 horizon search 习惯。
def strict_low_step_filter(raw_dataset, t_min: int):
"""Paper-level pseudocode; released repo does not expose the trajectory-filtering script."""
kept = []
for query, trajectory in raw_dataset:
tool_steps = count_tool_calls(trajectory)
if tool_steps >= t_min:
kept.append((query, trajectory))
return kept3.5 SFT objective:论文只说明 standard SFT,没有给完整训练配置
论文正文只写 OpenSeeker-v2 “trains the search agent with a standard SFT objective over the filtered dataset”,没有展开 token-level loss、learning rate、batch size、训练步数、训练硬件或 optimizer。为了理解,可把它视作在 filtered trajectories 上做标准 next-token negative log-likelihood: 但这条公式是 standard SFT 的解释性写法,不是论文显式给出的新公式。可复现性上要注意:released repo 当前公开的是模型运行和评测 harness,不包含 SFT training script、数据合成脚本或超参配置。因此所有训练细节中只有论文明确写出的数值可作为事实:10.6k training samples、Qwen3-30B-A3B-Thinking-2507 初始化、30B total / 3B activated、256k context、最多 200 次 tool calls、SFT-only、no RL、no additional hyperparameter tuning。
3.6 Released-code inference loop:v2 agent 如何跑 ReAct 工具调用
released code 的核心是 src/llm_tool_openseeker_v2.py::call_llm_with_tool 和 solve_query_with_tools。它用 OpenAI-compatible /v1/completions endpoint 调用本地/远端 SGLang-served model,把 system/user/tool messages 渲染进 chat template;每轮从模型输出里解析 <tool_calls_begin> ... <tool_call>...</tool_call> 形式的 JSON 工具调用;执行工具并把 observation 追加回 messages;直到模型产生 <answer> 或达到 tool_count_max。
def solve_query_with_tools(query: str, model_endpoint: str, tool_count_max: int = 200, max_tokens: int = 32768):
"""Code-grounded pseudocode from src/llm_tool_openseeker_v2.py and eval/generate_answer_v2.py."""
messages = [
{"role": "system", "content": "You are a tool-augmented QA agent..."},
{"role": "user", "content": query},
]
tool_count = 0
trace = []
while True:
prompt = render_chat_template(messages, tools=tools_visit + tools_e2b)
completion = stream_completion(model_endpoint, prompt, max_tokens=max_tokens)
reasoning, content = split_think_and_content(completion)
cleaned_text, tool_calls, parse_error = parse_tool_calls_from_text(content)
messages.append({"role": "assistant", "content": completion})
trace.append({"type": "model_message", "reasoning": reasoning, "tool_calls": tool_calls})
if has_answer_tag(content):
break
if not tool_calls:
tool_count += 1
messages.append({"role": "tool", "name": "unknown", "content": parse_error})
if tool_count >= tool_count_max:
break
continue
for call in tool_calls:
output = execute_tool_call(call["function"]["name"], call["function"]["arguments"])
messages.append({"role": "tool", "name": call["function"]["name"], "content": output})
trace.append({"type": "tool_response", "content": output})
tool_count += 1
if tool_count >= tool_count_max:
disable_further_tools_and_force_final_answer()
break
return {"answer": extract_last_answer(messages), "tool_calls": tool_count, "trace": trace}3.7 Evaluation harness:批量生成 answer 并记录 full trajectory
eval/generate_answer_v2.py 负责从 JSONL dataset 读取 query,默认给 query 追加 \boxed{} 最终答案格式要求;并发调用 solve_query_with_tools;把 final_response、tool_calls、elapsed_seconds、context_chars、context_est_tokens、finish_reason、full_traj 和 trace 写入 result_tool{tool_count_max}.jsonl。默认 tool_count_max=200,max_worker=60,filter_huggingface=True,这与论文“最多 200 tool calls、mask Hugging Face links 避免泄漏”的评测描述一致。
async def generate_answers_v2(dataset_path, out_dir, max_worker: int = 60, tool_count_max: int = 200):
"""Code-grounded pseudocode from eval/generate_answer_v2.py at main@954a286a."""
examples = read_jsonl(dataset_path)
examples = append_box_instruction(examples)
result_path = out_dir / f"result_tool{tool_count_max}.jsonl"
semaphore = asyncio.Semaphore(max_worker)
async def process_one(example):
async with semaphore:
result = await asyncio.to_thread(
solve_query_with_tools,
example["query"],
max_tokens=32768,
tool_count_max=tool_count_max,
filter_huggingface=True,
return_full_traj=True,
)
row = dict(example)
row.update({
"final_response": result["answer"],
"tool_calls": result["tool_calls"],
"full_traj": result["full_traj"],
"trace": result["trace"],
})
append_jsonl(result_path, row)
await asyncio.gather(*(process_one(x) for x in examples))3.8 论文公式与 released code 实现差异
论文公式与 released code 实现差异:论文的核心方法是 training-data synthesis 与 SFT training,而 PolarSeeker/OpenSeeker 在 main@954a286a 主要公开了模型 serving、tool-augmented inference、evaluation scripts 和工具接口;没有公开 Expand(\mathcal{G}, v_{\mathrm{seed}}, K)、、low-step filtering、SFT training loop、学习率/batch/optimizer 等训练配置。另一个小差异是 run_openseeker.sh 默认 MODEL_PATH="path/to/OpenSeeker-v1-30B-SFT",README 的 v2 使用说明要求用户手动改成 OpenSeeker-v2-30B-SFT。
Code reference:
main@954a286a(2026-05-22) — pseudocode and mapping based on this commit
| Paper Concept | Source File | Key Class/Function |
|---|---|---|
| OpenSeeker-v2 tool-augmented ReAct inference | src/llm_tool_openseeker_v2.py | call_llm_with_tool, solve_query_with_tools, _parse_tool_calls_from_text |
| Expanded tool set for v2 inference | src/llm_tool_openseeker_v2.py | tools_visit, tools_e2b, tools_all = tools_visit + tools_e2b, _execute_tool |
| Web search tool | src/tools/search.py | Search.call, Serper/Tavily dispatch, Hugging Face filtering |
| Web visit / summarization tool | src/tools/visit.py | Visit.call, readpage_jina, summary API call |
| Sandbox/code tools | src/tools/e2b_sandbox_tools.py | create_sandbox, run_command, run_python_code, upload/download helpers |
| v2 batch answer generation and metrics | eval/generate_answer_v2.py | main, process_one, compute_metrics, result JSONL/log paths |
| Model serving config | run_openseeker.sh | SGLang launch, CONTEXT_LENGTH=256000, default GPU_IDS="0,1,2,3", MAX_RUNNING_REQUESTS=2000 |
| Data synthesis / graph expansion / low-step filtering / SFT training | Not released in repo | Paper-only formulas and descriptions; no source implementation found in this repo |
4. Experimental Setup (实验设置)
Training data and model
- Training data: OpenSeeker-v2 使用 10.6k 条 high-difficulty synthesized trajectories;OpenSeeker-v1 对照为 11.7k 条。论文没有披露原始 source graph 的规模、 / 的具体取值、、过滤前样本数或各工具类型比例。
- Base model: Qwen3-30B-A3B-Thinking-2507,30B total parameters,inference 时 3B activated parameters。
- Context / tool budget: 256k context window;每条 trajectory 最多允许 200 次 tool calls。released repo 的
run_openseeker.sh也设置CONTEXT_LENGTH=256000;eval/generate_answer_v2.py默认tool_count_max=200。 - Training recipe: SFT-only;论文明确说 without RL or additional hyperparameter tuning,但没有给 learning rate、batch size、训练步数、训练硬件或 optimizer。released repo 没有 training config,因此这些数字不能从代码补全。
Evaluation datasets / benchmarks
论文评测四个 deep-search benchmark:BrowseComp、BrowseComp-ZH、Humanity’s Last Exam (HLE)、xbench-DeepSearch。正文第 2.2 节写成 “five challenging agentic benchmarks”,但随后只列出四个名字;表格和摘要也都是四个指标,因此这里按四个 benchmark 记录。论文没有给出各 benchmark 的 evaluation sample count,只说明它们覆盖 diverse deep research tasks,并在调用 web search tools 时 mask Hugging Face 相关链接以避免泄漏。
Baselines
主要对比对象是同规模 ReAct-based search agents:WebSailor-V2-30B-SFT/RL、WebLeaper-30B-SFT/RL、Tongyi DeepResearch、RedSearcher-30B、OpenSeeker-v1-30B-SFT。论文也列出闭源 proprietary models(Claude-4-Opus、Claude-4.5-Sonnet、Gemini-3-pro、OpenAI-o3、OpenAI Deep Research、GPT-5-High)和更大 open-source models(DeepSeek-V3.1/V3.2-671B、GLM-4.6/4.7-357B、Minimax-M2-230B)作为 broader reference。
Metrics
表格中的数值是各 benchmark 的 percent score / accuracy-style score,越高越好。# Samples 指训练数据样本数;Training 指训练阶段组合(CPT、SFT、RL);Academic 表示是否由纯学术团队完成;BC-ZH 是 BrowseComp-ZH。
5. Experimental Results (实验结果)
5.1 Main benchmark numbers
| Model | # Samples | Training | Academic | BrowseComp | BC-ZH | HLE | xbench |
|---|---|---|---|---|---|---|---|
| Claude-4-Opus | ? | ? | × | 18.8 | 37.4 | - | - |
| Claude-4.5-Sonnet | ? | ? | × | 24.1 | 42.4 | 32.0 | - |
| Gemini-3-pro | ? | ? | × | 37.8 | 66.8 | 45.8 | - |
| OpenAI-o3 | ? | ? | × | 49.1 | 68.7 | 20.2 | 65.0 |
| OpenAI Deep Research | ? | ? | × | 51.5 | 42.9 | 26.6 | - |
| GPT-5-High | ? | ? | × | 54.9 | 63.0 | 41.7 | - |
| DeepSeek-V3.1-671B | ? | ? | × | 30.0 | 49.2 | 29.8 | 71.2 |
| DeepSeek-V3.2-671B | ? | ? | × | 51.4 | 65.0 | 40.8 | - |
| GLM-4.6-357B | ? | ? | × | 45.1 | 49.5 | 30.4 | - |
| GLM-4.7-357B | ? | ? | × | 52.0 | 66.6 | 42.8 | - |
| Minimax-M2-230B | ? | ? | × | 44.0 | 48.5 | - | - |
| WebSailor-V2-30B-SFT | ? | SFT | × | 24.4 | 28.3 | 23.9 | 61.7 |
| WebSailor-V2-30B-RL | ? | SFT + RL | × | 35.3 | 44.1 | 30.6 | 73.7 |
| WebLeaper-30B-SFT | 15k | SFT | × | 27.7 | - | - | 66.0 |
| WebLeaper-30B-RL | ? | RL | × | 38.8 | - | - | 72.0 |
| Tongyi DeepResearch | ? | CPT + SFT + RL | × | 43.4 | 46.7 | 32.9 | 75.0 |
| RedSearcher-30B | ? | CPT + SFT + RL | × | 42.1 | 49.8 | 34.3 | - |
| OpenSeeker-v1-30B-SFT | 11.7k | SFT | ✓ | 29.5 | 48.4 | - | 74.0 |
| OpenSeeker-v2-30B-SFT | 10.6k | SFT | ✓ | 46.0 | 58.1 | 34.6 | 78.0 |
最重要的结论是:在约 30B ReAct-based search agents 里,OpenSeeker-v2 用 SFT-only 达到最强整体表现。相对 Tongyi DeepResearch,OpenSeeker-v2 在 BrowseComp 上高 2.6 分(46.0 vs 43.4),在 BrowseComp-ZH 上高 11.4 分(58.1 vs 46.7),在 HLE 上高 1.7 分(34.6 vs 32.9),在 xbench 上高 3.0 分(78.0 vs 75.0)。相对 RedSearcher-30B,OpenSeeker-v2 在 BrowseComp 上高 3.9 分(46.0 vs 42.1),在 BrowseComp-ZH 上高 8.3 分(58.1 vs 49.8),在 HLE 上高 0.3 分(34.6 vs 34.3)。
5.2 Scaling from OpenSeeker-v1 to v2
OpenSeeker-v2 相比 OpenSeeker-v1 的增益很大,而且训练样本数还略少:BrowseComp 从 29.5 提升到 46.0(+16.5),BrowseComp-ZH 从 48.4 提升到 58.1(+9.7),xbench 从 74.0 提升到 78.0(+4.0)。这支持作者的判断:OpenSeeker 框架在 SFT setting 下尚未饱和,继续提升 synthesized QA tasks 的难度与信息丰富度仍然可能带来可观收益。
Figure 2 解读:这张图比较 RedSearcher、OpenSeeker-v1 和 OpenSeeker-v2 训练数据中的工具调用步数分布。OpenSeeker-v2 的平均 tool calls 是 64.67,高于 OpenSeeker-v1 的 46.97 和 RedSearcher 的 36.01;因此 v2 不只是“样本更少但更干净”,而是把训练分布推向更长、更难的搜索过程。这个结果与低步数过滤的设计相互印证:过滤短轨迹后,模型在 SFT 中看到的 demonstration 更集中于长程信息寻找。
5.3 Ablation / component evidence
论文没有给出标准 ablation table,例如单独移除 graph scaling、tool expansion 或 low-step filtering 后的分数变化。因此组件贡献主要由两个间接证据支持:第一,v2 对 v1 在同一模型规模和 SFT-only recipe 下显著提升;第二,Figure 2 显示 v2 数据的平均轨迹长度显著更高。这个 evidence 足以支持“更难 trajectory 有帮助”的方向性结论,但不能精确量化三个 modifications 各自贡献多少。
5.4 Limitations and caveats
作者没有单列 limitations section。读者需要注意的可复现性 caveats 是:论文没有披露 、、数据合成 prompts、过滤前后样本量、SFT 超参、硬件和训练脚本;released code 也没有这些训练/数据构造部分,只提供模型 serving、工具调用和评测 harness。因此,这篇报告更像一个强 empirical recipe / technical report,而不是完整可复现实验包。
总体结论是:OpenSeeker-v2 用 10.6k 条高难信息密集 trajectories 证明,约 30B ReAct search agent 不一定必须依赖 CPT+SFT+RL 才能达到强性能;在这个任务族里,trajectory 难度、信息密度和长程工具调用分布本身就是非常强的 scaling lever。