AI-Scientist-v2 深度实战:当 AI 从「辅助工具」进化成「第一作者」——从树搜索自动化到顶会同行评审的完全指南(2026)
引言:当实验室里不再需要人类
2026年3月,一篇完全由AI自主完成的学术论文被ICLR 2026 workshop接收。在这份论文的作者栏里,没有任何一个真实人类的姓名——它的"第一作者"是Sakana AI联合牛津大学、不列颠哥伦比亚大学等机构开发的AI-Scientist-v2系统。这篇论文的审稿意见写道:"方法论扎实,实验设计合理,唯一的遗憾是创新性不足以达到主会标准,但作为工作坊级论文完全合格。"
这不是科幻。这是AI-Scientist-v2——一个从科研创意生成、实验执行、论文撰写到同行评审的完整自动化闭环系统——在2026年给出的真实答卷。
更让人震惊的数字是:跑通整个流程,只需约20美元。
本文将深入剖析AI-Scientist-v2的核心架构、树搜索算法的工程实现、并行代理系统的设计细节,以及它对科研工作者和工程师们意味着什么。我们会深入代码,理解它为什么能实现"零人工干预"的端到端科研流程,以及如何将它应用到实际的机器学习研究中。
1. 从辅助工具到第一作者:AI Scientist的进化史
1.1 传统AI辅助科研的局限性
在AI-Scientist-v2之前,"AI辅助科研"的概念一直被局限在一个非常狭小的范围内:
- 语法检查与润色:Grammarly风格的论文改写
- 图表绘制:基于模板的Figure生成
- 数据分析:基于固定模板的数据跑脚本
- 文献检索:Semantic Scholar API调用
这些工具的共同特点是:都是人类写好模板,AI只是执行者。AI从来没有能力从零开始构建一个科研想法,更无法判断一个实验结果是否值得写成论文。
1.2 AI Scientist v1:开创性的第一步
AI Scientist的v1版本(Sakana AI, 2024)是这个方向的先驱者。它首次展示了一个完整的自动化科研流程:
创意生成 → 代码实现 → 实验执行 → 结果分析 → 论文撰写
v1的核心问题在于:
- 线性探索:每条实验路径都是串行执行的,一旦走错路就要回溯重来
- 模板依赖:代码实现部分仍然依赖预定义的模板
- 论文质量:生成的论文"机器味"太重,语法和逻辑都有明显问题
1.3 AI Scientist v2:三大核心突破
v2版本从根本上重构了系统的每个环节:
突破一:树搜索驱动的探索策略
不再是"一条路走到黑"的线性执行,而是构建一棵实验树,在每个分支点并行探索多个方向,并在适当时机回溯和剪枝。这解决了v1的最大痛点——探索效率问题。
突破二:零模板代码生成
给一个研究方向描述,AI-Scientist-v2能从零写出完整的可运行实验代码。这依赖于大语言模型的代码生成能力和自我纠错机制。
突破三:多模态论文打磨
使用多模态大模型反复打磨论文的语言表达、图表清晰度和逻辑连贯性,让最终产出的论文在语言层面接近人类学者的写作风格。
2. 核心架构:端到端自动化科研系统的工程实现
2.1 系统总体架构
AI-Scientist-v2的系统架构分为三大串行核心阶段和一个并行执行层:
Stage 1: 创意生成 (Idea Generation)
LLM → 高维度科研创意 + 方案创新
↓
Stage 2: 树搜索实验执行 (Agentic Tree Search)
ParallelAgent → 多分支并行实验 → 节点评估 → 剪枝/扩展
↓
Stage 3: 论文撰写与评审 (Paper Writing & Review)
多模态打磨 → 格式化输出 → 自动评审
2.2 并行代理系统的实现细节
这是整个系统最工程化的部分。核心类ParallelAgent位于ai_scientist/treesearch/parallel_agent.py:
from typing import List, Dict, Optional
from dataclasses import dataclass, field
from enum import Enum
import asyncio
from concurrent.futures import ProcessPoolExecutor
import numpy as np
class NodeState(Enum):
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
PRUNED = "pruned"
@dataclass
class ExperimentNode:
node_id: str
parent_id: Optional[str]
depth: int
idea: str
code: Optional[str] = None
results: Optional[Dict] = None
score: float = 0.0
state: NodeState = NodeState.PENDING
children: List[str] = field(default_factory=list)
metadata: Dict = field(default_factory=dict)
class GPUResourceManager:
def __init__(self, total_gpus: int = 4, memory_per_gpu_gb: int = 80):
self.total_gpus = total_gpus
self.memory_per_gpu = memory_per_gpu_gb * 1024
self.available_memory = [self.memory_per_gpu] * total_gpus
self.allocated_nodes: Dict[str, int] = {}
def allocate(self, node_id: str, required_memory_mb: int) -> Optional[int]:
for gpu_id in range(self.total_gpus):
if self.available_memory[gpu_id] >= required_memory_mb:
self.available_memory[gpu_id] -= required_memory_mb
self.allocated_nodes[node_id] = gpu_id
return gpu_id
return None
def release(self, node_id: str) -> None:
if node_id in self.allocated_nodes:
gpu_id = self.allocated_nodes[node_id]
self.available_memory[gpu_id] += 20 * 1024
del self.allocated_nodes[node_id]
class ParallelAgent:
def __init__(
self,
max_parallel: int = 4,
max_depth: int = 5,
exploration_weight: float = 1.414,
pruning_threshold: float = 0.3,
):
self.max_parallel = max_parallel
self.max_depth = max_depth
self.exploration_weight = exploration_weight
self.pruning_threshold = pruning_threshold
self.nodes: Dict[str, ExperimentNode] = {}
self.root_id: Optional[str] = None
self.gpu_manager = GPUResourceManager()
self.executor = ProcessPoolExecutor(max_workers=max_parallel)
def ucb1_score(self, node: ExperimentNode, parent_visits: int) -> float:
if node.state != NodeState.COMPLETED:
return float('inf')
exploitation = node.score
exploration = self.exploration_weight * np.sqrt(
np.log(parent_visits) / node.metadata.get('visits', 1)
)
return exploitation + exploration
def should_prune(self, node: ExperimentNode) -> bool:
if node.parent_id is None:
return False
parent = self.nodes.get(node.parent_id)
if not parent or parent.state != NodeState.COMPLETED:
return False
return node.score < parent.score * self.pruning_threshold
async def expand_node(self, node_id: str, llm_client) -> List[str]:
node = self.nodes[node_id]
node.state = NodeState.RUNNING
child_ideas = await llm_client.generate_child_ideas(
parent_idea=node.idea,
num_children=3,
parent_results=node.results,
)
child_ids = []
for i, idea in enumerate(child_ideas):
child_id = f"{node_id}_child_{i}"
child_node = ExperimentNode(
node_id=child_id,
parent_id=node_id,
depth=node.depth + 1,
idea=idea,
metadata={'visits': 1}
)
self.nodes[child_id] = child_node
node.children.append(child_id)
child_ids.append(child_id)
code_tasks = [
llm_client.generate_experiment_code(idea=idea)
for idea in child_ideas
]
child_codes = await asyncio.gather(*code_tasks)
experiment_tasks = []
for child_id, code in zip(child_ids, child_codes):
self.nodes[child_id].code = code
gpu_id = self.gpu_manager.allocate(child_id, required_memory_mb=20*1024)
if gpu_id is None:
continue
future = self.executor.submit(
self._run_single_experiment, child_id, code
)
experiment_tasks.append((child_id, future))
for child_id, future in experiment_tasks:
try:
results = future.result(timeout=600)
child_node = self.nodes[child_id]
child_node.results = results
child_node.score = self._evaluate_results(results)
child_node.state = NodeState.COMPLETED
self.gpu_manager.release(child_id)
except Exception as e:
self.nodes[child_id].state = NodeState.FAILED
self.gpu_manager.release(child_id)
node.state = NodeState.COMPLETED
return child_ids
def _evaluate_results(self, results: Dict) -> float:
performance = results.get('test_accuracy', 0)
stability = results.get('std', 1.0)
novelty = results.get('novelty_score', 0.5)
return 0.6 * performance + 0.2 * (1 - min(stability, 1.0)) + 0.2 * novelty
2.3 树搜索 vs 线性搜索:效率对比
AI-Scientist-v2引入树搜索后,实验效率有了质的飞跃。假设研究方向:优化Transformer架构的自注意力机制。
v1的线性搜索方式:方向A → 失败 → 回溯 → 方向B → 一般 → 回溯 → 方向C → 效果尚可 → 继续 → ...
v2的树搜索方式:
- 根节点:优化注意力机制
- 分支1(FlashAttention系):子节点效果好 → 继续扩展KV Cache压缩 → 最优解
- 分支2(稀疏注意力系):效果差 → 剪枝
- 分支3(线性注意力系):效果一般 → 保留观察
树搜索在第2层就已经锁定了KV Cache压缩是最优方向,而线性搜索可能要探索十几条路径才能发现这一点。GPU利用率因此提升40%以上。
3. 创意生成:LLM如何"想"出一个科研idea
3.1 科研创意的LLM生成机制
树搜索的每一步扩展,都需要从父节点的想法生成子想法。这不是简单的"改参数",而是需要真正的创新性发散:
IDEA_GENERATION_PROMPT = """
你是一位机器学习领域的顶级研究员。请基于父节点研究方向,生成3个具有创新性的子研究方向。
父节点研究方向: {parent_idea}
父节点实验结果: {parent_results}
已探索方向(请避免重复): {explored_directions}
每个方向必须满足:
1. 与父节点有明确的继承关系
2. 在方法或应用上有实质性创新
3. 有明确的可行性
4. 与已探索方向有显著区别
"""
3.2 代码生成:从描述到可运行实验
async def generate_experiment_code(idea: str) -> str:
code_gen_prompt = f"""
你是一位精通PyTorch的研究工程师。请基于科研方向生成完整可运行实验代码。
科研方向: {idea}
要求:
1. 代码可直接运行(import、参数、训练循环完整)
2. 使用PyTorch框架
3. 包含数据加载、模型定义、训练、评估完整流程
4. 输出标准化JSON结果(test_accuracy, std, novelty_score等)
5. 支持单GPU运行
"""
code = await llm_client.generate(
prompt=code_gen_prompt,
max_tokens=8192,
temperature=0.7,
)
# 自我纠错
try:
compile(code, '<string>', 'exec')
except SyntaxError as e:
code = await llm_client.fix_syntax(code, error=str(e))
return code
4. GPU资源管理与并行执行
4.1 为什么GPU管理如此重要
机器学习实验是GPU密集型任务。AI-Scientist-v2的树搜索会同时运行多个实验,如果GPU管理不当,会导致显存溢出、计算冲突和资源浪费。
4.2 智能GPU调度策略
class IntelligentGPUScheduler:
def __init__(self, gpu_count: int):
self.gpu_count = gpu_count
self.gpu_status = [
{
'total_memory': 80 * 1024,
'used_memory': 0,
'active_tasks': [],
'utilization_history': [],
}
for _ in range(gpu_count)
]
def predict_memory_requirement(self, code: str) -> int:
memory_mb = 5 * 1024
if 'transformer' in code.lower() or 'bert' in code.lower():
memory_mb += 30 * 1024
elif 'resnet' in code.lower():
memory_mb += 15 * 1024
return memory_mb
def allocate_best_gpu(self, task_id: str, required_memory: int) -> Optional[int]:
candidates = []
for gpu_id in range(self.gpu_count):
available = self.gpu_status[gpu_id]['total_memory'] - \
self.gpu_status[gpu_id]['used_memory']
if available >= required_memory:
utilization = self.gpu_status[gpu_id]['utilization_history'][-1] \
if self.gpu_status[gpu_id]['utilization_history'] \
else 0.5
score = (available / self.gpu_status[gpu_id]['total_memory']) * \
(1 - utilization)
candidates.append((gpu_id, score))
if not candidates:
return None
candidates.sort(key=lambda x: x[1], reverse=True)
chosen_gpu = candidates[0][0]
self.gpu_status[chosen_gpu]['used_memory'] += required_memory
self.gpu_status[chosen_gpu]['active_tasks'].append(task_id)
return chosen_gpu
4.3 利用率提升40%的三大原因
- 并行执行遮蔽延迟:浅层节点完成后立即开始下一个迭代,GPU始终有任务
- 智能任务优先级:得分高的节点获得更多扩展机会
- 剪枝减少无效计算:无效路径被提前终止,资源立即分配给其他分支
5. 论文撰写与多模态打磨
PAPER_WRITING_PROMPT = """
你是一位学术写作专家。请基于实验结果撰写完整学术论文。
结构要求:
1. 摘要(Abstract):200词以内,突出贡献
2. 引言(Introduction):背景 → 问题 → 现有方案 → 我们的方案 → 贡献点
3. 方法(Method):详细技术描述
4. 实验(Experiments):设置、基线、结果
5. 相关工作(Related Work)
6. 结论(Conclusion)
写作要求:
- 使用学术写作规范表达
- 避免过于绝对的表述
- 诚实讨论局限性
- 图表描述精确,便于读者复现
"""
MULTIMODAL_REFINEMENT_PROMPT = """
你是一位资深审稿人。请对论文进行语言打磨:
1. 语法自然流畅
2. 表达清晰准确
3. 减少"机器味"(避免"我们的方法"等套路化表达)
4. 增加专业感和严谨性
"""
6. 自动同行评审
class ReviewAgent:
REVIEW_ASPECTS = ['novelty', 'technical_quality', 'clarity', 'experiment_design', 'related_work']
async def review(self, paper_text: str, figures: List[str]) -> Dict:
review_results = {}
for aspect in self.REVIEW_ASPECTS:
aspect_review = await self.llm_client.generate(
prompt=self._build_aspect_prompt(aspect, paper_text),
max_tokens=1024,
)
review_results[aspect] = self._parse_review(aspect_review)
overall_score = sum(
review_results[a]['score'] for a in self.REVIEW_ASPECTS
) / len(self.REVIEW_ASPECTS)
return {
'scores': review_results,
'overall_score': overall_score,
'recommendation': self._get_recommendation(overall_score),
}
7. 实战:从零开始跑通完整流程
7.1 环境准备
git clone https://github.com/SakanaAI/AI-Scientist-v2
cd AI-Scientist-v2
conda create -n ai-scientist python=3.11
conda activate ai-scientist
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
pip install -r requirements.txt
export OPENAI_API_KEY="sk-..."
export CUDA_VISIBLE_DEVICES="0,1,2,3"
7.2 配置文件
# bfts_config.yaml
experiment:
max_parallel: 4
max_depth: 5
max_iterations: 50
pruning_threshold: 0.3
gpu:
count: 4
memory_per_gpu_gb: 80
llm:
model: "gpt-4o"
api_provider: "openai"
temperature: 0.7
max_tokens: 8192
research:
seed_idea: "优化Transformer架构的注意力机制效率"
domain: "machine_learning"
max_budget_usd: 100
7.3 运行成本分析
| 阶段 | LLM调用成本 | GPU成本 | 总计 |
|---|---|---|---|
| 创意生成(50次迭代) | ~$8 | $0 | $8 |
| 代码生成 | ~$2 | $0 | $2 |
| 实验执行 | ~$0 | ~$6 | $6 |
| 论文撰写 | ~$3 | $0 | $3 |
| 评审 | ~$1 | $0 | $1 |
| 总计 | ~$14 | ~$6 | ~$20 |
8. 对科研生态的深远影响
8.1 AI Scientist带来了什么
- 从"人工密集"到"算力密集":3-6个月压缩到2周,20美元
- 试错成本大幅降低:高风险方向可以快速探索
- 研究效率指数级提升:相同时间探索10倍多的方向
- 科研民主化:资源有限的团队也能进行大规模系统性研究
8.2 挑战与局限性
- 创新性的天花板:受限于训练数据中的知识,无法产生真正的范式转换
- 实验代码的可靠性:需要人工审核来兜底
- 科研伦理问题:authorship(署名权)的法律和伦理界定模糊
- 同质化风险:大家探索相似分支,需要多样性度量来对抗
9. 技术细节深入:树搜索算法的数学原理
9.1 UCB1在科研探索中的应用
UCB1公式:UCB1(a) = X̄_a + C × sqrt(ln N / n_a)
- X̄_a:平均得分(exploitation)
- N:父节点总访问次数
- n_a:节点a访问次数
- C:探索参数(通常取√2)
9.2 剪枝的数学保证
剪枝条件:X̄_a < X̄_parent × τ
基于贝叶斯推断:子节点表现显著低于父节点时,高置信度认为这不是最优路径。
10. 总结与展望
AI-Scientist-v2代表了一种全新的科研范式:从人类主导的"经验-假设-实验"循环,到AI驱动的"生成-搜索-评估"循环。
核心价值:
- 大幅降低科研试错成本
- 系统性覆盖科研空间
- 将人类智慧聚焦于最高层次的创新
未来展望:
- 更强大的LLM驱动的科研探索
- 多智能体协作的科研团队
- AI-native的科研平台
无论如何,科研的本质——提出好问题、定义有价值的课题——始终需要人类智慧来引导。AI-Scientist-v2不是终点,而是AI辅助科研道路上的一个重要里程碑。
参考文献:
- Sakana AI. "The AI-Scientist-v2: Workshop-Level Automated Scientific Discovery via Agentic Tree Search." 2026.
- Silver et al. "Mastering the game of Go with deep neural networks and tree search." Nature, 2016.
- Zhou et al. "The AI Scientist: Towards Automated Scientific Discovery." ICLR Workshop, 2024.