Look up any arxiv paper on alphaxiv.org to get a structured AI-generated overview. This is faster and more reliable than trying to read a raw PDF.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Operator-side inventory of experiment PRs for auditing a Senpai research track. It outputs merged winners, a compact results table, and full details for deep dives.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
설치 방법 보기설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.
This is a human/operator audit guide, not a live advisor or student skill. Run the script below to fetch all experiment PRs from the advisor branch and organize them into 3 files.
The BASE_BRANCH should be set to the advisor branch (e.g. noam). Check the $ADVISOR_BRANCH env var or the PR base branch.
import subprocess, json, os, re
from datetime import datetime
BASE_BRANCH = os.environ.get("ADVISOR_BRANCH", "noam")
def get_repo_info():
r = subprocess.run(["gh", "repo", "view", "--json", "owner,name"], capture_output=True, text=True)
if r.returncode:
raise RuntimeError(r.stderr)
info = json.loads(r.stdout)
return info["owner"]["login"], info["name"]
def fetch_issue_comments(owner, name, number):
"""Fetch all PR conversation comments for a PR, across every REST page."""
comments = []
page = 1
while True:
endpoint = f"repos/{owner}/{name}/issues/{number}/comments?per_page=100&page={page}"
r = subprocess.run(["gh", "api", endpoint], capture_output=True, text=True)
if r.returncode:
raise RuntimeError(r.stderr)
batch = json.loads(r.stdout)
comments.extend((comment.get("body") or "") for comment in batch)
if len(batch) < 100:
return comments
page += 1
def fetch():
"""Fetch PRs with complete comments via GraphQL plus REST overflow pages."""
owner, name = get_repo_info()
prs = []
cursor = None
while True:
after = f', after: "{cursor}"' if cursor else ""
query = f'''{{
repository(owner: "{owner}", name: "{name}") {{
pullRequests(first: 100, baseRefName: "{BASE_BRANCH}", states: [OPEN, CLOSED, MERGED]{after}) {{
pageInfo {{ hasNextPage endCursor }}
nodes {{
number title body state mergedAt url
labels(first: 10) {{ nodes {{ name }} }}
comments(first: 100) {{
pageInfo {{ hasNextPage }}
nodes {{ body }}
}}
}}
}}
}}
}}'''
r = subprocess.run(["gh", "api", "graphql", "-f", f"query={query}"], capture_output=True, text=True)
if r.returncode:
raise RuntimeError(r.stderr)
data = json.loads(r.stdout)["data"]["repository"]["pullRequests"]
for node in data["nodes"]:
first_comments = [c["body"] or "" for c in node["comments"]["nodes"]]
comments = (
fetch_issue_comments(owner, name, node["number"])
if node["comments"]["pageInfo"]["hasNextPage"]
else first_comments
)
prs.append({
"number": node["number"],
"title": node["title"],
"body": node["body"] or "",
"state": node["state"],
"mergedAt": node["mergedAt"],
"url": node["url"],
"labels": [{"name": l["name"]} for l in node["labels"]["nodes"]],
"comments": comments,
})
if not data["pageInfo"]["hasNextPage"]:
break
cursor = data["pageInfo"]["endCursor"]
return prs
def extract_section(text, header):
"""Extract a markdown section (# or ##) by header name."""
match = re.search(rf'(^#{{1,2}} {header}.*?)(?=^#{{1,2}} |\Z)', text, re.MULTILINE | re.DOTALL)
return match.group(1).strip() if match else None
def find_results_text(pr):
"""Search body then comments (newest first) for the Results section."""
body = pr.get("body", "") or ""
section = extract_section(body, "Results")
if section and "_To be filled" not in section:
return section
for comment in reversed(pr.get("comments", [])):
section = extract_section(comment, "Results")
if section and "_To be filled" not in section:
return section
return None
def extract_val_loss(text):
if not text:
return None
patterns = [
r'val[/_]loss[:\s]*\*?\*?(\d+\.\d+)',
r'\*\*val/loss\*\*[:\s|]*\*?\*?(\d+\.\d+)',
r'val/loss[:\s|]+(\d+\.\d+)',
]
for p in patterns:
m = re.search(p, text, re.IGNORECASE)
if m:
return float(m.group(1))
return None
def has_results(pr):
return find_results_text(pr) is not None
def labels_str(pr):
return ", ".join(l["name"] for l in pr.get("labels", []))
def hypothesis_oneline(body):
"""Extract first sentence of hypothesis."""
sec = extract_section(body or "", "Hypothesis")
if not sec:
return ""
text = re.sub(r'^#{1,2} Hypothesis\n', '', sec).strip()
m = re.match(r'(.+?[.!])\s', text)
return m.group(1) if m else text[:120]
# --- Fetch ---
prs = fetch()
merged = sorted([p for p in prs if p.get("mergedAt")], key=lambda p: p["mergedAt"])
with_results = [p for p in prs if has_results(p) and not p.get("mergedAt")]
no_results = [p for p in prs if not has_results(p)]
ts = datetime.now().strftime("%Y-%m-%d_%H-%M")
outdir = "experiment_log"
os.makedirs(outdir, exist_ok=True)
# --- File 1: Merged winners (chronological) ---
merged_path = f"{outdir}/merged_{ts}.md"
with open(merged_path, "w") as f:
f.write(f"# Merged Experiments ({len(merged)} winners)\n\n")
f.write("These PRs beat baseline and were merged, in chronological order.\n\n")
for pr in merged:
body = pr.get("body", "") or ""
hyp = extract_section(body, "Hypothesis") or ""
results = find_results_text(pr) or ""
val = extract_val_loss(results)
val_str = f" | val_loss: {val}" if val else ""
f.write(f"## #{pr['number']}: {pr['title']}{val_str}\n\n")
if hyp:
f.write(f"{hyp}\n\n")
if results:
f.write(f"{results}\n\n")
f.write("---\n\n")
# --- File 2: Compact results table ---
table_path = f"{outdir}/results_table_{ts}.md"
with open(table_path, "w") as f:
f.write(f"# Experiment Results Table\n\n")
f.write(f"Total: {len(prs)} PRs | Merged: {len(merged)} | ")
f.write(f"Ran (not merged): {len(with_results)} | Never ran: {len(no_results)}\n\n")
f.write("## Merged\n\n")
f.write("| PR | Title | val_loss |\n|---|---|---|\n")
for pr in merged:
results = find_results_text(pr)
val = extract_val_loss(results)
val_str = f"{val:.4f}" if val else "—"
f.write(f"| #{pr['number']} | {pr['title']} | {val_str} |\n")
f.write(f"\n## Ran but not merged ({len(with_results)})\n\n")
f.write("| PR | Title | val_loss | Hypothesis (short) |\n|---|---|---|---|\n")
for pr in sorted(with_results, key=lambda p: p["number"], reverse=True):
body = pr.get("body", "") or ""
results = find_results_text(pr)
val = extract_val_loss(results)
val_str = f"{val:.4f}" if val else "—"
hyp = hypothesis_oneline(body)
f.write(f"| #{pr['number']} | {pr['title']} | {val_str} | {hyp[:80]} |\n")
f.write(f"\n## Never ran ({len(no_results)})\n\n")
f.write("| PR | Title | Hypothesis (short) |\n|---|---|---|\n")
for pr in sorted(no_results, key=lambda p: p["number"], reverse=True):
hyp = hypothesis_oneline(pr.get("body", ""))
f.write(f"| #{pr['number']} | {pr['title']} | {hyp[:80]} |\n")
# --- File 3: Full details (only PRs that ran) ---
full_path = f"{outdir}/full_details_{ts}.md"
with open(full_path, "w") as f:
f.write(f"# Full Experiment Details ({len(merged) + len(with_results)} experiments)\n\n")
for pr in merged + sorted(with_results, key=lambda p: p["number"], reverse=True):
body = pr.get("body", "").strip() or "_No description._"
state = "MERGED" if pr.get("mergedAt") else "CLOSED"
f.write(f"# #{pr['number']}: {pr['title']} [{state}]\n\n{body}\n\n")
results = find_results_text(pr)
if results and results not in body:
f.write(f"{results}\n\n")
f.write("---\n\n")
print(f"Saved {len(prs)} PRs ({len(merged)} merged, {len(with_results)} ran, {len(no_results)} never ran):")
print(f" {merged_path} — merged winners with hypothesis + results")
print(f" {table_path} — compact table of all experiments")
print(f" {full_path} — full PR bodies (only experiments that ran)")
아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.
로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.
아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.
Look up any arxiv paper on alphaxiv.org to get a structured AI-generated overview. This is faster and more reliable than trying to read a raw PDF.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Operator-side analysis of historical ML experiment PRs in Senpai research tracks. Use this skill whenever the user asks to: analyze experiments, categorize PRs, bucket experiments, summarize what's been tried, understand experiment history, review merged vs closed results, or asks "what experiments have we run / worked / failed". Also triggers for: "pull the latest experiments", "what's been tried so far", "category breakdown of PRs", "which experiments succeeded", "noam track analysis". When a branch name is mentioned (e.g. "noam branch", "on the noam branch"), pass it as the base branch to scope the fetch to just those PRs.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Create a typed assignment branch and draft PR for one student. Use when the advisor has a concrete hypothesis and the student has no open `status:wip` or `status:review` assignment.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Create or improve a Senpai target repository's program.md. Use this skill whenever the user wants to point Senpai at a fresh ML or research target repository, define the research objective, primary metric, benchmark contract, allowed edit boundaries, W&B reporting contract, or prepare a repo for autonomous advisor/student experiment loops.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Open GitHub Issues for human input and respond to the researcher team. Use this skill whenever you need to handle a human_issue event, respond to human issues, ask humans a question, or check team communications. Also triggers for: "any human messages?", "check issues", "respond to humans".
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Choose, configure, launch, and collect bounded subagents for research and engineering decisions. Root agents and delegation-capable subagents should read this before delegating: every task requires an explicit model tier, and high-leverage work such as research ideation, round planning, plateau pivots, large research reviews, hard optimization, disputed evidence, and expensive experiment portfolios requires frontier judgment.
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.