L04 代码 — 被卡住时怎么办
目标:读取一份 TSV 结果文件,判断当前是否已触发 L1/L2/L3 转向阈值,并输出具体建议。
运行方式:
sh
# 先用 L03 的模拟器生成结果文件,再运行检测器
python ../lecture-03-five-stage-loop-internals/code/loop_simulator.py
python pivot_detector.py autoresearch-results.tsv第一步:定义三个转向阈值
python
import csv, sys, os
# 三个级别的转向阈值(连续 revert 次数)
L1_THRESHOLD = 3 # 策略切换:换个方向,别换方法
L2_THRESHOLD = 5 # 范式转移:质疑根本假设
L3_THRESHOLD = 10 # 根本性反思:重新审视研究目标本身
PIVOT_ADVICE = {
1: "L1 转向:在当前范式内切换策略——换一种算法变体或参数方向。",
2: "L2 转向:更换思路——尝试完全不同的算法族或架构。",
3: "L3 转向:根本性反思——重新审视研究目标本身是否正确。",
}关键行:L1_THRESHOLD = 3 — 3 次连续失败是信号,不是坏运气。框架主动识别这个信号。
第二步:加载 TSV 文件
python
def load_tsv(path: str) -> list[dict]:
if not os.path.exists(path):
print(f"[error] 找不到文件: {path}")
sys.exit(1)
rows = []
with open(path, newline="") as f:
reader = csv.DictReader(f, delimiter="\t")
for row in reader:
rows.append(row)
return rows第三步:统计连续 revert 次数(从末尾往前数)
python
def analyse(rows: list[dict]) -> None:
if not rows:
print("TSV 中没有数据。")
return
# 从最后一行向前数,遇到非 revert 就停
streak = 0
for row in reversed(rows):
if row.get("status", "").strip().lower() == "revert":
streak += 1
else:
break # 只统计末尾连续的 revert,中间的不算关键行:for row in reversed(rows) — 只统计末尾连续的 revert 次数,中间某轮成功了就重置计数。
第四步:计算统计摘要并输出转向建议
python
total = len(rows)
kept = sum(1 for r in rows if r.get("status","").strip().lower() == "keep")
keep_rate = kept / total if total else 0.0
scores = []
for r in rows:
try:
scores.append(float(r["metric"]))
except (KeyError, ValueError):
pass
best = max(scores) if scores else float("nan")
worst = min(scores) if scores else float("nan")
print("=== 转向分析 ===")
print(f"总迭代次数 : {total}")
print(f"保留 / 回滚 : {kept} / {total - kept} (保留率 {keep_rate:.0%})")
print(f"历史最佳分数 : {best:.4f}")
print(f"末尾连续回滚 : {streak} 次")
print()
# 判断转向级别
if streak >= L3_THRESHOLD:
level = 3
elif streak >= L2_THRESHOLD:
level = 2
elif streak >= L1_THRESHOLD:
level = 1
else:
level = 0
if level == 0:
print(f"[ok] 暂不需要转向(streak={streak},L1 阈值={L1_THRESHOLD})。")
else:
print(f"[!] {PIVOT_ADVICE[level]}")
print(f" 末尾 {streak} 次连续回滚超过了"
f" {'L3' if level==3 else 'L2' if level==2 else 'L1'} 阈值。")
def main():
path = sys.argv[1] if len(sys.argv) > 1 else "autoresearch-results.tsv"
rows = load_tsv(path)
analyse(rows)
if __name__ == "__main__":
main()预期输出(触发 L2 的示例)
=== 转向分析 ===
总迭代次数 : 18
保留 / 回滚 : 6 / 12 (保留率 33%)
历史最佳分数 : 0.7310
末尾连续回滚 : 6 次
[!] L2 转向:更换思路——尝试完全不同的算法族或架构。
末尾 6 次连续回滚超过了 L2 阈值。动手改一改
- 把
L1_THRESHOLD = 3改成1,用 L03 生成的 TSV 跑一遍,第一个转向在第几轮触发? - 在 TSV 里手动添加一行
status=keep(在末尾的 revert 之后),再运行,streak是否归零? - 如果
scores列表为空(TSV 里没有metric列),程序会崩溃吗?float("nan")的作用是什么? - 为你自己的研究场景写出 L1 和 L2 转向的具体内容:L1 会换什么?L2 会质疑什么假设?