zhiyi commited on
Commit
6fbd77b
·
0 Parent(s):

Deploy ZeroGPU algorithm research deck

Browse files
.dockerignore ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ .git
2
+ .env
3
+ __pycache__
4
+ *.pyc
5
+ .pytest_cache
6
+ output
7
+ tests
.gitignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ .env
2
+ __pycache__/
3
+ *.pyc
4
+ .pytest_cache/
5
+ output/
DEPLOYMENT.md ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Deploy to wingsxyp/algorithm-research-deck
2
+
3
+ Target Space: `https://huggingface.co/spaces/wingsxyp/algorithm-research-deck`
4
+
5
+ ## Token roles
6
+
7
+ - **Write token**: use only on your computer for Git authentication and pushing files to the Space repository.
8
+ - **Read token**: add to the Space as the Secret `HF_TOKEN`; the running application uses it to download the configured open model.
9
+ - Never commit either token. Never put the write token in Space Secrets.
10
+
11
+ ## Space settings
12
+
13
+ The repository README configures a Gradio Space. In the Space settings, select ZeroGPU hardware if it is not already selected.
14
+
15
+ Add this Secret:
16
+
17
+ ```text
18
+ HF_TOKEN=<your read token>
19
+ ```
20
+
21
+ Add these Variables:
22
+
23
+ ```text
24
+ HF_MODEL_ID=Qwen/Qwen2.5-7B-Instruct
25
+ CONTACT_EMAIL=<your email for polite arXiv requests>
26
+ ```
27
+
28
+ The model must be public or accessible to the account that owns the read token. A gated model requires accepting its terms with the same account.
29
+
30
+ ## Local authentication and push
31
+
32
+ From the project root:
33
+
34
+ ```powershell
35
+ git init
36
+ git add .
37
+ git commit -m "Deploy ZeroGPU algorithm research deck"
38
+ git branch -M main
39
+ git remote add origin https://huggingface.co/spaces/wingsxyp/algorithm-research-deck
40
+ git push -u origin main
41
+ ```
42
+
43
+ When Git asks for credentials:
44
+
45
+ ```text
46
+ Username: wingsxyp
47
+ Password: <your write token>
48
+ ```
49
+
50
+ The password field does not display typed characters. This is expected.
51
+
52
+ ## Verify
53
+
54
+ Open the Space build logs. A successful build installs Gradio, Spaces, Transformers, PyTorch dependencies, and launches `app.py`. Test `Retrieval-Augmented Generation` in Quick mode first.
55
+
56
+ Verify:
57
+
58
+ - arXiv search completes
59
+ - ZeroGPU allocation starts for model synthesis
60
+ - the presentation appears in the right panel
61
+ - HTML, JSON, and ZIP downloads work
62
+ - the downloaded HTML opens independently in a browser
63
+
64
+ If model loading fails, first confirm `HF_MODEL_ID`, the read token's model access, and ZeroGPU quota. Do not replace the read token with the write token.
README.md ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Algorithm Research Deck
3
+ emoji: 🔬
4
+ colorFrom: blue
5
+ colorTo: purple
6
+ sdk: gradio
7
+ sdk_version: 5.29.0
8
+ app_file: app.py
9
+ pinned: false
10
+ license: apache-2.0
11
+ ---
12
+
13
+ # Algorithm Research Deck
14
+
15
+ Turn an algorithm research topic into an interactive, paper-backed HTML presentation. The Space searches arXiv on CPU, uses ZeroGPU for open-model synthesis, previews the result, and provides HTML, JSON, and ZIP downloads.
16
+
17
+ ## Space configuration
18
+
19
+ Secret:
20
+
21
+ - `HF_TOKEN`: a read token used only to download the configured model. Public models may not require it, but setting it avoids anonymous download limits.
22
+
23
+ Variables:
24
+
25
+ - `HF_MODEL_ID`: defaults to `Qwen/Qwen2.5-7B-Instruct`
26
+ - `CONTACT_EMAIL`: contact address included in polite arXiv requests
27
+
28
+ The token used on your computer to push this repository must be a write token. Never add that write token to Space Secrets.
app.py ADDED
@@ -0,0 +1,375 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import html
4
+ import io
5
+ import json
6
+ import os
7
+ import re
8
+ import uuid
9
+ import zipfile
10
+ from datetime import datetime, timezone
11
+ from pathlib import Path
12
+ from typing import Any
13
+ from urllib.parse import quote_plus
14
+ from xml.etree import ElementTree
15
+
16
+ import gradio as gr
17
+ import httpx
18
+ import spaces
19
+ import torch
20
+ from pydantic import BaseModel, Field
21
+ from pypdf import PdfReader
22
+ from transformers import AutoModelForCausalLM, AutoTokenizer
23
+
24
+
25
+ BASE_DIR = Path(__file__).resolve().parent
26
+ STATIC_DIR = BASE_DIR / "static"
27
+ DATA_DIR = Path("/data/research-decks") if Path("/data").exists() else BASE_DIR / "output"
28
+ DATA_DIR.mkdir(parents=True, exist_ok=True)
29
+ MODEL_ID = os.getenv("HF_MODEL_ID", "Qwen/Qwen2.5-7B-Instruct")
30
+ _model = None
31
+ _tokenizer = None
32
+
33
+
34
+ class ResearchRequest(BaseModel):
35
+ topic: str = Field(min_length=2, max_length=180)
36
+ purpose: str = Field(default="算法团队内部技术分享", max_length=500)
37
+ audience: str = Field(default="熟悉机器学习基础的算法工程师", max_length=300)
38
+ focus: str = Field(default="", max_length=1200)
39
+ date_range: str = Field(default="经典工作至今", max_length=120)
40
+ depth: str = Field(default="standard", pattern="^(quick|standard|deep)$")
41
+ language: str = Field(default="中文", max_length=40)
42
+ paper_urls: list[str] = Field(default_factory=list, max_length=12)
43
+
44
+
45
+ DEPTHS = {
46
+ "quick": {"discover": 30, "screen": 10, "deep": 2},
47
+ "standard": {"discover": 60, "screen": 20, "deep": 4},
48
+ "deep": {"discover": 100, "screen": 35, "deep": 8},
49
+ }
50
+
51
+
52
+ def safe_slug(value: str) -> str:
53
+ slug = re.sub(r"[^a-zA-Z0-9]+", "-", value).strip("-").lower()
54
+ return (slug or "algorithm-topic")[:64]
55
+
56
+
57
+ def arxiv_search(topic: str, limit: int) -> list[dict[str, Any]]:
58
+ query = quote_plus(f'all:"{topic}"')
59
+ url = (
60
+ "https://export.arxiv.org/api/query"
61
+ f"?search_query={query}&start=0&max_results={limit}"
62
+ "&sortBy=submittedDate&sortOrder=descending"
63
+ )
64
+ headers = {"User-Agent": f"algorithm-research-deck/1.0 ({os.getenv('CONTACT_EMAIL', 'contact-not-set')})"}
65
+ with httpx.Client(timeout=30, follow_redirects=True, headers=headers) as client:
66
+ response = client.get(url)
67
+ response.raise_for_status()
68
+ ns = {"a": "http://www.w3.org/2005/Atom"}
69
+ root = ElementTree.fromstring(response.text)
70
+ papers = []
71
+ for entry in root.findall("a:entry", ns):
72
+ abs_url = (entry.findtext("a:id", default="", namespaces=ns) or "").strip()
73
+ authors = [x.findtext("a:name", default="", namespaces=ns) for x in entry.findall("a:author", ns)]
74
+ papers.append({
75
+ "id": abs_url.rsplit("/", 1)[-1],
76
+ "title": " ".join((entry.findtext("a:title", default="", namespaces=ns) or "").split()),
77
+ "summary": " ".join((entry.findtext("a:summary", default="", namespaces=ns) or "").split()),
78
+ "authors": authors,
79
+ "published": entry.findtext("a:published", default="", namespaces=ns),
80
+ "updated": entry.findtext("a:updated", default="", namespaces=ns),
81
+ "url": abs_url,
82
+ "pdf_url": abs_url.replace("/abs/", "/pdf/") + ".pdf",
83
+ })
84
+ return papers
85
+
86
+
87
+ def normalize_user_papers(urls: list[str]) -> list[dict[str, Any]]:
88
+ result = []
89
+ for raw in urls:
90
+ match = re.search(r"arxiv\.org/(?:abs|pdf)/([\w.\-/]+)", raw)
91
+ if match:
92
+ paper_id = match.group(1).removesuffix(".pdf")
93
+ result.append({
94
+ "id": paper_id,
95
+ "title": f"User supplied arXiv paper {paper_id}",
96
+ "summary": "User-supplied paper; prioritize during screening.",
97
+ "authors": [],
98
+ "published": "",
99
+ "updated": "",
100
+ "url": f"https://arxiv.org/abs/{paper_id}",
101
+ "pdf_url": f"https://arxiv.org/pdf/{paper_id}.pdf",
102
+ "user_supplied": True,
103
+ })
104
+ return result
105
+
106
+
107
+ def deduplicate(papers: list[dict[str, Any]]) -> list[dict[str, Any]]:
108
+ seen = set()
109
+ output = []
110
+ for paper in papers:
111
+ key = re.sub(r"v\d+$", "", paper["id"])
112
+ if key not in seen:
113
+ seen.add(key)
114
+ output.append(paper)
115
+ return output
116
+
117
+
118
+ @spaces.GPU(duration=120)
119
+ def generate_text(system: str, prompt: str, max_tokens: int) -> str:
120
+ global _model, _tokenizer
121
+ token = os.getenv("HF_TOKEN") or None
122
+ if _tokenizer is None:
123
+ _tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, token=token)
124
+ if _model is None:
125
+ _model = AutoModelForCausalLM.from_pretrained(
126
+ MODEL_ID,
127
+ token=token,
128
+ torch_dtype=torch.bfloat16,
129
+ device_map="auto",
130
+ )
131
+ messages = [{"role": "system", "content": system}, {"role": "user", "content": prompt}]
132
+ model_input = _tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
133
+ inputs = _tokenizer(model_input, return_tensors="pt").to(_model.device)
134
+ with torch.inference_mode():
135
+ generated = _model.generate(
136
+ **inputs,
137
+ max_new_tokens=max_tokens,
138
+ do_sample=False,
139
+ repetition_penalty=1.05,
140
+ )
141
+ return _tokenizer.decode(generated[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
142
+
143
+
144
+ def chat_json(system: str, prompt: str, max_tokens: int = 6000) -> dict[str, Any]:
145
+ content = generate_text(system, prompt, max_tokens)
146
+ try:
147
+ return json.loads(content)
148
+ except json.JSONDecodeError:
149
+ match = re.search(r"\{[\s\S]*\}", content)
150
+ if not match:
151
+ raise RuntimeError("The model did not return valid JSON")
152
+ return json.loads(match.group(0))
153
+
154
+
155
+ def select_papers(request: ResearchRequest, papers: list[dict[str, Any]], screen_limit: int, deep_limit: int) -> list[str]:
156
+ compact = [{k: p[k] for k in ("id", "title", "summary", "authors", "published", "url")} for p in papers[:screen_limit]]
157
+ prompt = json.dumps({
158
+ "topic": request.topic,
159
+ "purpose": request.purpose,
160
+ "focus": request.focus,
161
+ "papers": compact,
162
+ "instruction": f"Select at most {deep_limit} pivotal papers. Return JSON with selected_ids and a short selection_reason per id.",
163
+ }, ensure_ascii=False)
164
+ result = chat_json("You select technically pivotal ML papers. Prefer lineage coverage and evidence over recency.", prompt, 900)
165
+ valid = {p["id"] for p in papers}
166
+ return [x for x in result.get("selected_ids", []) if x in valid][:deep_limit]
167
+
168
+
169
+ def extract_pdf(paper: dict[str, Any], max_chars: int = 14000) -> str:
170
+ with httpx.Client(timeout=45, follow_redirects=True) as client:
171
+ response = client.get(paper["pdf_url"])
172
+ response.raise_for_status()
173
+ if len(response.content) > 20 * 1024 * 1024:
174
+ return "PDF omitted because it exceeds the 20 MB limit."
175
+ reader = PdfReader(io.BytesIO(response.content))
176
+ pages = []
177
+ indexes = list(range(min(8, len(reader.pages))))
178
+ if len(reader.pages) > 8:
179
+ indexes += list(range(max(8, len(reader.pages) - 3), len(reader.pages)))
180
+ for index in dict.fromkeys(indexes):
181
+ text = reader.pages[index].extract_text() or ""
182
+ pages.append(f"[Page {index + 1}]\n{text}")
183
+ if sum(map(len, pages)) >= max_chars:
184
+ break
185
+ return "\n".join(pages)[:max_chars]
186
+
187
+
188
+ def build_report(request: ResearchRequest, papers: list[dict[str, Any]], deep_texts: dict[str, str]) -> dict[str, Any]:
189
+ evidence = []
190
+ for paper in papers:
191
+ item = {k: paper.get(k) for k in ("id", "title", "summary", "authors", "published", "updated", "url")}
192
+ if paper["id"] in deep_texts:
193
+ item["selected_full_text_extract"] = deep_texts[paper["id"]]
194
+ evidence.append(item)
195
+ payload = {
196
+ "request": request.model_dump(),
197
+ "research_cutoff": datetime.now(timezone.utc).date().isoformat(),
198
+ "evidence": evidence,
199
+ "schema": {
200
+ "title": "string",
201
+ "subtitle": "string",
202
+ "executive_summary": ["string"],
203
+ "lineage": [{"year": "string", "method": "string", "paper_id": "string", "bottleneck": "string", "innovation": "string", "limitation": "string"}],
204
+ "sections": [{"title": "string", "takeaway": "string", "body": ["string"], "paper_ids": ["string"], "details": "string"}],
205
+ "comparisons": [{"method": "string", "mechanism": "string", "strengths": "string", "limitations": "string", "engineering": "string"}],
206
+ "open_questions": ["string"],
207
+ },
208
+ }
209
+ return chat_json(
210
+ "You are a rigorous algorithm research lead. Return only JSON matching the supplied schema. "
211
+ "Trace classical ideas to current methods, distinguish preprints, avoid incomparable benchmark claims, "
212
+ "and cite only supplied paper IDs. Write concise presentation-ready content.",
213
+ json.dumps(payload, ensure_ascii=False),
214
+ 3800,
215
+ )
216
+
217
+
218
+ def esc(value: Any) -> str:
219
+ return html.escape(str(value or ""))
220
+
221
+
222
+ def render_presentation(report: dict[str, Any], papers: list[dict[str, Any]], cutoff: str) -> str:
223
+ deck_css = (STATIC_DIR / "deck.css").read_text(encoding="utf-8")
224
+ deck_js = (STATIC_DIR / "deck.js").read_text(encoding="utf-8").replace("</script", "<\\/script")
225
+ citations = {p["id"]: p for p in papers}
226
+ lineage = report.get("lineage", [])
227
+ sections = report.get("sections", [])
228
+ summary = report.get("executive_summary", [])
229
+ comparisons = report.get("comparisons", [])
230
+
231
+ timeline = "".join(
232
+ f'<button class="milestone" data-index="{i}" aria-pressed="{str(i == 0).lower()}"><span>{esc(x.get("year"))}</span>{esc(x.get("method"))}</button>'
233
+ for i, x in enumerate(lineage)
234
+ )
235
+ timeline_data = html.escape(json.dumps(lineage, ensure_ascii=False), quote=True)
236
+ summary_html = "".join(f"<li>{esc(x)}</li>" for x in summary)
237
+ section_html = "".join(
238
+ f'''<section class="slide" id="section-{i + 1}">
239
+ <p class="eyebrow">{i + 3:02d}</p><h2>{esc(x.get('title'))}</h2>
240
+ <p class="takeaway">{esc(x.get('takeaway'))}</p>
241
+ <ul>{''.join(f'<li>{esc(b)}</li>' for b in x.get('body', []))}</ul>
242
+ <details><summary>方法细节与证据</summary><p>{esc(x.get('details'))}</p><p class="sources">论文:{', '.join(esc(pid) for pid in x.get('paper_ids', []))}</p></details>
243
+ </section>'''
244
+ for i, x in enumerate(sections)
245
+ )
246
+ rows = "".join(
247
+ f"<tr><th>{esc(x.get('method'))}</th><td>{esc(x.get('mechanism'))}</td><td>{esc(x.get('strengths'))}</td><td>{esc(x.get('limitations'))}</td><td>{esc(x.get('engineering'))}</td></tr>"
248
+ for x in comparisons
249
+ )
250
+ refs = "".join(
251
+ f'<li id="ref-{esc(pid)}"><a href="{esc(p.get("url"))}" target="_blank" rel="noopener noreferrer">{esc(p.get("title"))}</a><br><span>{esc(", ".join(p.get("authors", [])[:6]))} · {esc((p.get("published") or "")[:10])} · arXiv:{esc(pid)}</span></li>'
252
+ for pid, p in citations.items()
253
+ )
254
+ open_questions = "".join(f"<li>{esc(x)}</li>" for x in report.get("open_questions", []))
255
+ title = esc(report.get("title", "Algorithm Research"))
256
+ subtitle = esc(report.get("subtitle", ""))
257
+ return f'''<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
258
+ <title>{title}</title><style>{deck_css}</style></head><body>
259
+ <nav><strong>{title}</strong><span id="counter"></span><button id="prev" aria-label="上一页">←</button><button id="next" aria-label="下一页">→</button><button onclick="window.print()">保存 PDF</button></nav>
260
+ <main>
261
+ <section class="slide active" id="cover"><p class="eyebrow">ALGORITHM RESEARCH · {esc(cutoff)}</p><h1>{title}</h1><p class="subtitle">{subtitle}</p><p>使用方向键翻页,滚动和打印模式同样可用。</p></section>
262
+ <section class="slide" id="summary"><p class="eyebrow">01</p><h2>核心结论</h2><ol>{summary_html}</ol></section>
263
+ <section class="slide" id="lineage" data-lineage="{timeline_data}"><p class="eyebrow">02</p><h2>技术路线</h2><div class="timeline">{timeline}</div><article id="milestone-detail" aria-live="polite"></article></section>
264
+ {section_html}
265
+ <section class="slide" id="comparison"><h2>方法对比</h2><div class="table-wrap"><table><thead><tr><th>方法</th><th>机制</th><th>优势</th><th>局限</th><th>工程考虑</th></tr></thead><tbody>{rows}</tbody></table></div></section>
266
+ <section class="slide" id="questions"><h2>开放问题</h2><ul>{open_questions}</ul></section>
267
+ <section class="slide references" id="references"><h2>论文与研究截止日期</h2><p>检索截止:{esc(cutoff)}。预印本结论可能随修订发生变化。</p><ol>{refs}</ol></section>
268
+ </main><div class="progress"><span id="progress-bar"></span></div><script>{deck_js}</script></body></html>'''
269
+
270
+
271
+ def create_bundle(job_dir: Path, html_path: Path, report_path: Path) -> Path:
272
+ bundle = job_dir / "research-package.zip"
273
+ with zipfile.ZipFile(bundle, "w", zipfile.ZIP_DEFLATED) as archive:
274
+ archive.write(html_path, html_path.name)
275
+ archive.write(report_path, report_path.name)
276
+ return bundle
277
+
278
+
279
+ def preview_frame(content: str) -> str:
280
+ source = html.escape(content, quote=True)
281
+ return f'<iframe title="Interactive research presentation" srcdoc="{source}" style="width:100%;height:76vh;border:1px solid #30394a;border-radius:10px;background:#080b12" sandbox="allow-scripts allow-popups"></iframe>'
282
+
283
+
284
+ def run_research_ui(
285
+ topic: str,
286
+ purpose: str,
287
+ audience: str,
288
+ focus: str,
289
+ date_range: str,
290
+ depth: str,
291
+ language: str,
292
+ paper_urls: str,
293
+ progress=gr.Progress(),
294
+ ):
295
+ request = ResearchRequest(
296
+ topic=topic,
297
+ purpose=purpose,
298
+ audience=audience,
299
+ focus=focus,
300
+ date_range=date_range,
301
+ depth=depth.lower(),
302
+ language=language,
303
+ paper_urls=[x.strip() for x in paper_urls.splitlines() if x.strip()],
304
+ )
305
+ budget = DEPTHS[request.depth]
306
+ progress(0.08, desc="正在检索 arXiv")
307
+ papers = deduplicate(normalize_user_papers(request.paper_urls) + arxiv_search(request.topic, budget["discover"]))
308
+ if not papers:
309
+ raise gr.Error("没有检索到论文,请尝试英文关键词或提供 arXiv 链接。")
310
+ screen = papers[: budget["screen"]]
311
+ progress(0.28, desc="ZeroGPU 正在筛选关键论文")
312
+ selected_ids = select_papers(request, screen, budget["screen"], budget["deep"])
313
+ if not selected_ids:
314
+ selected_ids = [p["id"] for p in screen[: budget["deep"]]]
315
+ progress(0.48, desc="正在读取关键论文")
316
+ deep_texts = {}
317
+ for index, paper in enumerate(screen):
318
+ if paper["id"] in selected_ids:
319
+ try:
320
+ deep_texts[paper["id"]] = extract_pdf(paper)
321
+ except Exception as exc:
322
+ deep_texts[paper["id"]] = f"PDF extraction failed: {exc}"
323
+ progress(0.48 + 0.16 * (index + 1) / len(screen), desc="正在提取论文证据")
324
+ progress(0.68, desc="ZeroGPU 正在生成技术路线")
325
+ report = build_report(request, screen, deep_texts)
326
+ cutoff = datetime.now(timezone.utc).date().isoformat()
327
+ content = render_presentation(report, screen, cutoff)
328
+ job_dir = DATA_DIR / uuid.uuid4().hex
329
+ job_dir.mkdir(parents=True, exist_ok=True)
330
+ slug = safe_slug(request.topic)
331
+ html_path = job_dir / f"{slug}-research.html"
332
+ report_path = job_dir / f"{slug}-research.json"
333
+ html_path.write_text(content, encoding="utf-8")
334
+ report_path.write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8")
335
+ bundle_path = create_bundle(job_dir, html_path, report_path)
336
+ progress(1, desc="完成")
337
+ status = f"✅ 已完成:检索 {len(papers)} 篇,筛选 {len(screen)} 篇,深读 {len(deep_texts)} 篇。"
338
+ return status, preview_frame(content), str(html_path), str(report_path), str(bundle_path)
339
+
340
+
341
+ CSS = """
342
+ .gradio-container{max-width:none!important}.preview-column{min-width:0}.input-column{max-width:430px}
343
+ """
344
+
345
+ with gr.Blocks(title="Algorithm Research Deck", css=CSS, theme=gr.themes.Soft()) as demo:
346
+ gr.Markdown("# Algorithm Research Deck\n从经典方法到最新论文,生成可保存的交互式 HTML 技术演示。")
347
+ with gr.Row():
348
+ with gr.Column(scale=1, elem_classes="input-column"):
349
+ topic = gr.Textbox(label="调研主题", placeholder="例如:Diffusion Transformer")
350
+ purpose = gr.Textbox(label="用途", value="算法团队内部技术分享")
351
+ audience = gr.Textbox(label="受众", value="熟悉机器学习基础的算法工程师")
352
+ focus = gr.Textbox(label="重点关注", lines=3, placeholder="技术路线、推理加速、工程部署……")
353
+ with gr.Row():
354
+ depth = gr.Radio(["Quick", "Standard", "Deep"], value="Quick", label="研究深度")
355
+ language = gr.Dropdown(["中文", "English"], value="中文", label="输出语言")
356
+ date_range = gr.Textbox(label="时间范围", value="经典工作至今")
357
+ paper_urls = gr.Textbox(label="指定 arXiv 链接", lines=3, placeholder="每行一个,可选")
358
+ submit = gr.Button("开始调研", variant="primary")
359
+ status = gr.Markdown("等待任务")
360
+ html_file = gr.File(label="保存交互式 HTML")
361
+ json_file = gr.File(label="保存研究 JSON")
362
+ zip_file = gr.File(label="下载完整包")
363
+ with gr.Column(scale=2, elem_classes="preview-column"):
364
+ preview = gr.HTML("<div style='height:76vh;display:grid;place-items:center;color:#758099'>交互演示将在这里生成</div>")
365
+
366
+ submit.click(
367
+ run_research_ui,
368
+ inputs=[topic, purpose, audience, focus, date_range, depth, language, paper_urls],
369
+ outputs=[status, preview, html_file, json_file, zip_file],
370
+ )
371
+
372
+ demo.queue(default_concurrency_limit=1, max_size=8)
373
+
374
+ if __name__ == "__main__":
375
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ gradio==5.29.0
2
+ spaces==0.42.1
3
+ httpx==0.28.1
4
+ huggingface-hub==0.31.2
5
+ pypdf==5.5.0
6
+ transformers==4.51.3
7
+ accelerate==1.6.0
research-ppt/SKILL.md ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ name: research-ppt
3
+ description: Research an algorithm, model, machine-learning method, or AI engineering topic from foundational work through the latest papers, then create an evidence-backed interactive HTML presentation. Use for technical landscape reviews, paper surveys, algorithm comparisons, and interview or team knowledge-sharing decks; do not use for general market research or static PowerPoint output.
4
+ ---
5
+
6
+ # Algorithm Research Deck
7
+
8
+ Turn a technical topic for algorithm engineers into a self-contained interactive HTML presentation. Build a traceable lineage from foundational ideas to current research, explain why each transition happened, and keep paper claims faithful to the original evidence.
9
+
10
+ ## Inputs and defaults
11
+
12
+ Use the user's topic, audience level, purpose, scope, language, and desired depth. Ask only when an omitted choice would materially change the technical treatment. Otherwise use these defaults:
13
+
14
+ - Audience: algorithm engineers familiar with machine learning fundamentals
15
+ - Purpose: understand concepts, method evolution, representative papers, tradeoffs, and implementation implications
16
+ - Scope: foundational work through the newest credible work available on the research date
17
+ - Output: one responsive, self-contained `.html` file
18
+ - Language: the user's language; preserve standard English technical terms where translation would reduce precision
19
+ - Length: approximately 12-18 presentation sections, adjusted to the topic
20
+ - Research budget: token-efficient standard mode unless the user requests exhaustive coverage
21
+
22
+ Record the research cutoff date visibly. Treat preprints published near that cutoff as preliminary evidence.
23
+
24
+ ## Workflow
25
+
26
+ ### 1. Define the technical question
27
+
28
+ Frame the topic as a decision or learning question. Define the task, inputs and outputs, assumptions, evaluation criteria, and exclusions. Read [algorithm-research.md](references/algorithm-research.md) before planning.
29
+
30
+ Decompose the work into questions such as:
31
+
32
+ - What problem does the method solve, and what formal objective defines it?
33
+ - Which classical ideas made the modern approach possible?
34
+ - Which bottleneck caused each major transition in the technical lineage?
35
+ - Which papers represent the lineage rather than merely being recent?
36
+ - How do methods compare on mechanism, complexity, data, compute, latency, quality, robustness, and reproducibility?
37
+ - What should an algorithm engineer implement, test, or monitor?
38
+
39
+ ### 2. Search the literature
40
+
41
+ Read [academic-search.md](references/academic-search.md) and [token-efficient-research.md](references/token-efficient-research.md). Use available academic-search tools first, including AlphaXiv, arXiv, Semantic Scholar, OpenAlex, Crossref, Papers with Code, or connected scholarly MCP tools. When a named tool is unavailable, use another scholarly index or the official arXiv API/site and disclose the limitation only if it materially reduces coverage.
42
+
43
+ Search iteratively:
44
+
45
+ 1. terminology and survey discovery
46
+ 2. foundational and highly influential work
47
+ 3. milestone methods and their successors
48
+ 4. recent work, sorted and filtered by date
49
+ 5. citations, replications, benchmarks, and documented limitations
50
+
51
+ Do not rank papers by recency alone. Include a paper when it changes the problem formulation, architecture, objective, training/inference strategy, efficiency frontier, evaluation practice, or real-world feasibility.
52
+
53
+ Maintain a paper ledger with:
54
+
55
+ `paper | year | venue/status | canonical URL | problem | core idea | delta vs predecessor | evidence | datasets | metrics | compute/setup | limitations | code | lineage stage | confidence`
56
+
57
+ Use the ledger as the compact research memory. Do not keep full paper text in the working context after extracting verified notes and precise source locations.
58
+
59
+ ### 3. Read and interpret papers
60
+
61
+ Escalate reading depth only when a paper survives the previous screening tier. Do not download or ingest every candidate PDF. Use the full paper for pivotal papers and disputed claims; inspect only the sections needed to verify supporting claims. For pivotal papers, extract:
62
+
63
+ - problem formulation and assumptions
64
+ - objective or key equations, with every symbol defined
65
+ - architecture or algorithm steps
66
+ - claimed contribution and the evidence supporting it
67
+ - baseline fairness, dataset, metric, and compute conditions
68
+ - ablations and failure cases
69
+ - reproducibility details and official code availability
70
+ - what later work retained, replaced, or challenged
71
+
72
+ Default standard-mode budget:
73
+
74
+ - discover and deduplicate at most 60 candidate papers from metadata
75
+ - screen at most 20 papers using abstracts, surveys, citation context, and canonical metadata
76
+ - perform structured section-level reading on at most 8 papers
77
+ - deeply read at most 4 pivotal papers, including only necessary appendix sections
78
+
79
+ These are ceilings rather than quotas. Expand a tier only when coverage has a clear gap, a central claim remains unresolved, or the user requests exhaustive research. Prefer one additional targeted paper over widening every tier.
80
+
81
+ Separate author claims from your synthesis. Do not infer superiority across papers whose datasets, metrics, model scales, or compute budgets are not comparable. Never treat an abstract, AlphaXiv summary, search snippet, or benchmark leaderboard as a substitute for the paper's experimental context.
82
+
83
+ ### 4. Construct the technical lineage
84
+
85
+ Organize the narrative by conceptual transitions rather than a flat chronological paper list:
86
+
87
+ `problem and classical baseline → bottleneck → new mechanism → evidence → remaining limitation → next transition`
88
+
89
+ Read [technical-deck-structure.md](references/technical-deck-structure.md). Include the classic-to-current route even when older work no longer leads benchmarks. Explain why it mattered and which idea survived.
90
+
91
+ ### 5. Build the interactive HTML presentation
92
+
93
+ Read [html-presentation.md](references/html-presentation.md) and create the deliverable in a task-owned `output/` directory. The final file must open locally in a modern browser without a build step.
94
+
95
+ Use interactions only when they improve technical understanding. Strong candidates include:
96
+
97
+ - clickable technical-route timeline with method details
98
+ - paper filters by year, family, task, or evidence status
99
+ - method comparison table with sortable or selectable dimensions
100
+ - architecture or algorithm step-through
101
+ - metric and compute tradeoff chart with hover details
102
+ - collapsible equations, experiment details, caveats, and citations
103
+
104
+ Keep navigation presentation-friendly with keyboard controls, visible progress, direct section links, and a printable fallback. The first view of every section must remain meaningful without interaction.
105
+
106
+ ### 6. Verify
107
+
108
+ Before delivery:
109
+
110
+ - Re-open every pivotal paper and verify titles, authors, dates, URLs, equations, datasets, metrics, and claimed results.
111
+ - Confirm that the lineage includes foundational, milestone, strong current, and important dissenting or negative evidence where available.
112
+ - Confirm every comparison names the experimental conditions that make it valid.
113
+ - Test keyboard navigation, mouse/touch interactions, filters, expandable content, external links, and print layout.
114
+ - Inspect at desktop and mobile widths for overflow, clipped equations, unreadable figures, and inaccessible controls.
115
+ - Check the browser console for errors and ensure the HTML works without a local server unless clearly disclosed.
116
+ - Confirm that no large raw PDF text, duplicated abstracts, or unused research dumps were embedded in the final HTML or retained as deliverables.
117
+
118
+ Deliver the final `.html` and mention material evidence gaps, inaccessible papers, or unverified preprints. Provide the paper ledger separately only when requested.
119
+
120
+ ## Boundaries
121
+
122
+ - Never invent papers, citations, benchmark values, equations, code repositories, or publication status.
123
+ - Label arXiv-only work as a preprint and distinguish first submission from the latest revision.
124
+ - Do not call a method state of the art without a task, dataset, metric, date, and comparable evaluation setting.
125
+ - Do not equate citation count, social attention, or benchmark rank with technical validity.
126
+ - Do not recommend production adoption solely from paper results.
research-ppt/agents/openai.yaml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ interface:
2
+ display_name: "算法技术调研演示"
3
+ short_description: "从经典方法到最新论文完成算法调研并生成交互式 HTML 演示"
4
+ default_prompt: "使用 $research-ppt 调研一个算法主题,梳理从经典方法到最新论文的技术路线,并生成交互式 HTML 演示。"
research-ppt/references/academic-search.md ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Academic search and paper verification
2
+
3
+ ## Tool routing
4
+
5
+ Use connected scholarly tools when available. Prefer AlphaXiv for paper discussion and accessible explanations, arXiv for canonical preprint metadata and versions, Semantic Scholar or OpenAlex for citation graph discovery, Crossref or the venue site for publication metadata, and Papers with Code or official repositories for implementations and benchmark context.
6
+
7
+ Tool output is discovery material, not authority. Verify important claims against the paper, official supplement, dataset page, or repository.
8
+
9
+ When dedicated tools are unavailable:
10
+
11
+ - query the arXiv API or arxiv.org with title, author, category, and date filters
12
+ - search official proceedings such as ACL Anthology, CVF Open Access, PMLR, OpenReview, ACM, IEEE, or publisher DOI pages as appropriate
13
+ - follow references and citing papers from a verified seed paper
14
+ - use general web search only to locate canonical sources
15
+
16
+ Do not automate authentication, bypass access controls, or rely on inaccessible paywalled text. Use a lawful author manuscript or preprint when available.
17
+
18
+ ## Query design
19
+
20
+ Build synonym sets for the task, mechanism, historical terminology, and benchmark. Combine them with:
21
+
22
+ - `survey`, `review`, or `tutorial`
23
+ - `benchmark`, `dataset`, `ablation`, `replication`, `negative result`, or `limitations`
24
+ - method-family names and predecessor names
25
+ - venue/category and date constraints
26
+
27
+ Run a forward search from classical terms and a backward search from current terminology. Record queries that materially define coverage.
28
+
29
+ ## Metadata rules
30
+
31
+ - Use DOI links for published versions when available and arXiv links for preprint versions.
32
+ - Record both first-submission and latest-revision dates when recency matters.
33
+ - Preserve the declared venue status: accepted, under review, workshop, technical report, or preprint.
34
+ - Deduplicate conference, journal, and arXiv versions; cite the version actually read.
35
+ - Treat withdrawn papers and papers with material corrections explicitly.
36
+
37
+ ## Reading priority
38
+
39
+ Read pivotal papers deeply. For supporting papers, retrieve only the sections needed to verify the exact claim. Prioritize method, experiment setup, results, ablations, limitations, and only the relevant appendix passages. Use external explanations to clarify, never to overwrite the authors' definitions or evidence.
40
+
41
+ Do not repeatedly retrieve the same abstract, metadata, or paper section. Reuse the paper ledger and record a precise section, figure, table, equation, or page locator for later verification. Fetch the source again only when the stored note cannot support the final claim.
42
+
43
+ For equations, transcribe from the source and verify notation. For diagrams, create an original explanatory representation or clearly label an adapted figure with its source and license status.
research-ppt/references/algorithm-research.md ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Algorithm research framing
2
+
3
+ ## Coverage model
4
+
5
+ Frame the survey across five layers when relevant:
6
+
7
+ 1. Concept: task definition, assumptions, notation, and evaluation target
8
+ 2. Classical foundations: statistical, optimization, signal-processing, information-theoretic, or early neural methods
9
+ 3. Modern technical routes: the main competing families and the bottleneck each addresses
10
+ 4. Current frontier: recent credible methods, scaling behavior, efficiency, robustness, and open problems
11
+ 5. Engineering implications: data, training, inference, evaluation, monitoring, and failure handling
12
+
13
+ Depth matters more than filling all five layers. Omit a layer when it does not help explain the topic.
14
+
15
+ ## Technical comparison dimensions
16
+
17
+ Select only dimensions that affect the user's decision:
18
+
19
+ - formulation and inductive bias
20
+ - training objective and optimization behavior
21
+ - asymptotic complexity and measured resource use
22
+ - dataset requirements and sensitivity to distribution shift
23
+ - inference latency, throughput, memory, and deployment constraints
24
+ - quality metrics and statistical uncertainty
25
+ - robustness, safety, interpretability, and failure modes
26
+ - implementation complexity, code maturity, and reproducibility
27
+
28
+ Normalize notation across papers before comparison. If authors use the same term differently, preserve the distinction.
29
+
30
+ ## Freshness strategy
31
+
32
+ For mature fields, start from authoritative surveys and trace backward to primary work, then perform a separate recent-paper search from the survey cutoff to the current date. For fast-moving fields, search the last 12-24 months explicitly and use citation graphs to connect recent papers to foundational work.
33
+
34
+ Avoid arbitrary "top 10 papers" selections. Prefer a small set of lineage-defining papers plus enough alternative and negative evidence to show the actual decision space.
research-ppt/references/evidence-and-citations.md ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Evidence and citations
2
+
3
+ Use this reference for factual claims outside the papers themselves, such as framework releases, model cards, benchmark governance, hardware characteristics, or production case studies. Use [academic-search.md](academic-search.md) for paper-specific discovery and verification.
4
+
5
+ ## Source hierarchy
6
+
7
+ Prefer sources in this order when they can answer the claim:
8
+
9
+ 1. Primary records: papers, official datasets, benchmark specifications, standards, model cards, and product documentation
10
+ 2. Reputable analysis: established research organizations, academic reviews, and transparent industry reports
11
+ 3. Credible reporting with named sources and clear dates
12
+ 4. Vendor or consultancy material, used with its commercial incentive visible
13
+ 5. Aggregators and informal commentary, used only as leads or clearly labeled perspective
14
+
15
+ Recency matters most for fast-changing metrics, product capabilities, policy status, and forecasts. Older sources remain useful for definitions, history, and foundational research.
16
+
17
+ ## Claim discipline
18
+
19
+ For each claim, retain the exact source passage, table cell, or dataset field until final verification. Preserve:
20
+
21
+ - metric definition and unit
22
+ - numerator and denominator
23
+ - time period and publication date
24
+ - geography and population
25
+ - actual, estimate, target, or forecast status
26
+ - scenario or methodology when relevant
27
+
28
+ When calculating a growth rate, share, index, or conversion, cite the inputs and keep the formula in working notes. Do not mix nominal and real values, calendar and fiscal years, or stocks and flows without explicit normalization.
29
+
30
+ ## Confidence
31
+
32
+ Use a simple internal rating:
33
+
34
+ - High: direct primary evidence with a clear definition
35
+ - Medium: reputable secondary evidence or consistent triangulation
36
+ - Low: single interested source, unclear methodology, old data, or unresolved disagreement
37
+
38
+ Low-confidence evidence can frame an open question but should not carry the central conclusion without a visible caveat.
39
+
40
+ ## Presentation citations
41
+
42
+ In the HTML, map citations to the precise claim, equation, chart, table, or figure. Include author or organization, title, publication date, canonical URL, and access date. Add section, equation, figure, table, or appendix identifiers when relevant. For datasets, include version, split, filters, and retrieval date.
43
+
44
+ Use concise inline citation markers linked to a complete references section. Show the source beside central quantitative evidence and any adapted figure. Avoid a generic bibliography that leaves claim provenance ambiguous.
45
+
46
+ For web research completed on the current date, use the environment's current date as the access date. Never guess missing metadata.
research-ppt/references/html-presentation.md ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Interactive HTML presentation
2
+
3
+ ## Delivery contract
4
+
5
+ Create one self-contained `output/<topic-slug>-research.html` that opens locally in a modern browser. Inline the content, styles, scripts, and compact data. If a library is essential, bundle it locally when feasible; otherwise use a version-pinned CDN and disclose that offline use needs network access. Do not require npm, a build step, or a local server for ordinary viewing.
6
+
7
+ Use semantic HTML, responsive CSS, and plain JavaScript unless the existing project already provides an appropriate framework. Avoid generated application scaffolding for a single presentation.
8
+
9
+ ## Presentation behavior
10
+
11
+ - Treat each major section as a slide-like scene with a stable anchor.
12
+ - Support previous/next controls, left/right arrow keys, Home/End, and a clickable progress indicator or table of contents.
13
+ - Preserve normal scrolling as a fallback, especially on mobile and for printing.
14
+ - Keep browser back/forward behavior meaningful when section hashes change.
15
+ - Provide a print stylesheet that removes controls, expands essential collapsed material, and avoids splitting core figures where possible.
16
+ - Respect `prefers-reduced-motion`; do not use looping or decorative animation.
17
+
18
+ ## Technical interactions
19
+
20
+ Use native buttons, links, details/summary, selects, and checkboxes. Every control needs a visible label, keyboard access, focus indication, and an initial state that already communicates the main point.
21
+
22
+ For equations, prefer MathJax or KaTeX only when needed and pin the version. Provide readable fallback text. For charts and route maps, prefer semantic HTML, CSS, or inline SVG; use D3 only for data-rich interactions. Do not redraw paper figures without explaining that they are simplified interpretations.
23
+
24
+ External paper links must point to canonical destinations and open safely. Place citations adjacent to claims and include a complete reference section with DOI/arXiv/venue links.
25
+
26
+ ## Visual and accessibility requirements
27
+
28
+ - Design first for a 16:9 desktop presentation while remaining usable at 320px width.
29
+ - Avoid fixed-height text regions and horizontal page overflow.
30
+ - Maintain strong contrast and do not encode meaning with color alone.
31
+ - Provide accessible names or descriptions for charts and diagrams.
32
+ - Keep code, tables, equations, and long URLs from overflowing.
33
+ - Use progressive disclosure for derivations and experimental details, not for the central conclusion.
34
+
35
+ ## Validation
36
+
37
+ Open the file through a browser automation or rendering tool when available. Test section navigation, hashes, filters, toggles, accordions, paper links, print preview, responsive layouts, and the browser console. If automated browser access is unavailable, perform static checks and disclose only limitations that affect confidence in the deliverable.
research-ppt/references/technical-deck-structure.md ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Technical presentation structure
2
+
3
+ Use this as a flexible narrative, not a mandatory section count.
4
+
5
+ 1. Topic, audience, and research cutoff
6
+ 2. Executive technical conclusions
7
+ 3. Problem definition, assumptions, and evaluation criteria
8
+ 4. Classical foundations and baseline
9
+ 5. Technical-route map from classical to current
10
+ 6. One or more transition sections: bottleneck, mechanism, evidence, limitation
11
+ 7. Current method-family comparison
12
+ 8. Representative paper deep dives
13
+ 9. Benchmark evidence with comparability caveats
14
+ 10. Efficiency, data, and deployment tradeoffs
15
+ 11. Failure cases, unresolved debates, and negative evidence
16
+ 12. Engineering implications and recommended experiments
17
+ 13. Outlook with observable research signposts
18
+ 14. Search method and references
19
+
20
+ ## Section rules
21
+
22
+ - Introduce notation before equations and define every symbol near first use.
23
+ - Explain mechanism before reporting benchmark deltas.
24
+ - Pair every milestone with the limitation that motivated the next transition.
25
+ - Use the same comparison dimensions across method families.
26
+ - Give paper deep dives a reason for inclusion: foundational, transition-defining, current strong evidence, alternative route, or corrective result.
27
+ - Keep conclusions conditional on dataset, scale, compute, and evaluation protocol.
28
+
29
+ ## Useful interactive views
30
+
31
+ - Timeline: selecting a milestone reveals its problem, idea, evidence, and limitation.
32
+ - Route map: selecting a branch highlights predecessor and successor relationships.
33
+ - Comparison matrix: users choose a deployment constraint and see relevant tradeoffs.
34
+ - Algorithm walkthrough: step controls update pseudocode, tensors, or data flow.
35
+ - Evidence chart: users can toggle only genuinely comparable experimental groups.
36
+ - Paper drawer: citation, abstract-level summary, verified contribution, limitations, and canonical links.
research-ppt/references/token-efficient-research.md ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Token-efficient research
2
+
3
+ ## Default funnel
4
+
5
+ Use staged retrieval so that detailed content enters context only after a paper proves useful.
6
+
7
+ | Tier | Default ceiling | Read | Purpose |
8
+ | --- | ---: | --- | --- |
9
+ | Discovery | 60 papers | title, authors, year, venue/status, abstract snippet, citation links | maximize recall and identify terminology |
10
+ | Screening | 20 papers | full abstract, introduction conclusion, survey or citation context | select lineage-defining and contrary evidence |
11
+ | Structured reading | 8 papers | method overview, relevant experiments, limitations, selected figures/tables | extract comparable evidence |
12
+ | Deep reading | 4 papers | necessary full sections, equations, ablations, appendix details | support pivotal explanations and disputed claims |
13
+
14
+ The ceilings are defaults, not targets. Stop earlier when the technical lineage is covered and new papers repeat known mechanisms or evidence.
15
+
16
+ ## Promotion criteria
17
+
18
+ Promote a paper to the next tier only when at least one condition holds:
19
+
20
+ - it introduced or materially changed a lineage branch
21
+ - it provides central comparative evidence
22
+ - it challenges a conclusion or documents a failure mode
23
+ - it is necessary to connect the classical and current terminology
24
+ - it supplies implementation details needed by the target audience
25
+
26
+ Do not promote a paper because it is merely recent, highly cited, or frequently mentioned.
27
+
28
+ ## Compact notes
29
+
30
+ After reading, store conclusions in the paper ledger rather than retaining copied prose. Use concise paraphrases plus exact anchors:
31
+
32
+ `claim | evidence value | condition | source section/figure/table/equation | confidence`
33
+
34
+ Keep direct quotations only when wording itself matters. Store equations only when they are explained in the presentation. Retain benchmark rows only for methods that are meaningfully comparable.
35
+
36
+ ## Retrieval discipline
37
+
38
+ - Start with metadata APIs or search results, then open canonical sources only for shortlisted papers.
39
+ - Prefer HTML source or extracted text for targeted reading. Use PDF page images only when layout, figures, formulas, or tables require them.
40
+ - Retrieve selected sections or page ranges when tools support it.
41
+ - Avoid sending an entire survey and every paper it cites into the same context.
42
+ - Use a survey to map terminology and lineage, then verify important statements in primary papers.
43
+ - Deduplicate arXiv, conference, journal, and repository versions before reading.
44
+ - Do not include full abstracts or raw paper text in the final HTML; include concise synthesis and citations.
45
+
46
+ ## Stop conditions
47
+
48
+ Stop expanding the search when all of the following are true:
49
+
50
+ - each major lineage branch has at least one verified representative
51
+ - every central conclusion has adequate evidence
52
+ - recent searches return mostly duplicate mechanisms or weaker evidence
53
+ - important disagreements and limitations are represented
54
+ - the research cutoff and remaining gaps can be stated honestly
55
+
56
+ If a central claim remains unresolved after two targeted searches, present the uncertainty instead of continuing broad retrieval indefinitely.
57
+
58
+ ## User-selectable depth
59
+
60
+ Interpret user requests as follows unless they define their own budget:
61
+
62
+ - Quick: 30 discovery, 10 screening, 4 structured, 2 deep
63
+ - Standard: 60 discovery, 20 screening, 8 structured, 4 deep
64
+ - Deep: 100 discovery, 35 screening, 15 structured, 8 deep
65
+
66
+ For Deep mode, warn only if external API cost, rate limits, or a materially long run is expected. Never trade source verification away merely to meet a token target; narrow scope or expose uncertainty instead.
static/deck.css ADDED
@@ -0,0 +1 @@
 
 
1
+ :root{font-family:Inter,"Microsoft YaHei",sans-serif;color:#eef3fb;background:#080b12;color-scheme:dark;--accent:#7aa2ff;--muted:#9aa8bd}*{box-sizing:border-box}body{margin:0;background:radial-gradient(circle at 80% 10%,#17213d 0,#080b12 35%);color:#eef3fb}nav{position:fixed;z-index:10;left:0;right:0;top:0;height:56px;display:flex;align-items:center;gap:10px;padding:0 3vw;background:rgba(8,11,18,.9);backdrop-filter:blur(12px);border-bottom:1px solid #273143}nav strong{margin-right:auto;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}nav button{border:1px solid #344158;background:#131c2b;color:#eef3fb;border-radius:7px;padding:7px 10px;cursor:pointer}.slide{display:none;min-height:100vh;padding:11vh 8vw 9vh;align-content:center;max-width:1400px;margin:auto}.slide.active{display:grid}.eyebrow{letter-spacing:.14em;color:var(--accent);font-size:12px}h1{font-size:clamp(42px,7vw,94px);max-width:1050px;line-height:1.02;margin:.25em 0}h2{font-size:clamp(30px,4vw,58px);margin:.2em 0 .6em}.subtitle,.takeaway{font-size:clamp(20px,2.4vw,34px);color:#c8d3e7;max-width:1000px}li,p,td,th{font-size:clamp(16px,1.25vw,21px);line-height:1.55}li{margin:.45em 0}.timeline{display:flex;gap:10px;overflow-x:auto;padding:12px 0}.milestone{min-width:150px;text-align:left;padding:13px;border:1px solid #344158;border-radius:9px;background:#101827;color:#eaf0fb;cursor:pointer}.milestone span{display:block;color:var(--accent);font-size:12px}.milestone[aria-pressed="true"]{border-color:var(--accent);background:#182643}#milestone-detail{margin-top:18px;padding-top:16px;border-top:1px solid #344158}.table-wrap{overflow:auto}table{width:100%;border-collapse:collapse}th,td{text-align:left;vertical-align:top;border-bottom:1px solid #344158;padding:12px}details{margin-top:20px;border-top:1px solid #344158;padding-top:14px}summary{cursor:pointer;color:var(--accent)}.sources,.references span{font-size:13px;color:var(--muted)}.references li{font-size:14px}.references a{color:#a9c3ff}.progress{position:fixed;bottom:0;left:0;right:0;height:4px;background:#1b2434}.progress span{display:block;height:100%;background:var(--accent);transition:width .25s}@media(max-width:700px){nav strong{display:none}.slide{display:block;min-height:auto;padding:90px 22px 50px;border-bottom:1px solid #273143}.slide.active{display:block}.progress{display:none}}@media print{nav,.progress{display:none}.slide{display:block;min-height:auto;break-after:page;padding:10mm;background:#fff;color:#111}.references{break-after:auto}body{background:#fff;color:#111}.subtitle,.takeaway,.sources,.references span{color:#333}a{color:#111}}
static/deck.js ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ const slides=[...document.querySelectorAll('.slide')];let current=0;
2
+ const counter=document.getElementById('counter');const bar=document.getElementById('progress-bar');
3
+ function show(index,push=true){current=Math.max(0,Math.min(slides.length-1,index));slides.forEach((s,i)=>s.classList.toggle('active',i===current));counter.textContent=`${current+1} / ${slides.length}`;bar.style.width=`${(current+1)/slides.length*100}%`;if(push)history.replaceState(null,'',`#${slides[current].id}`);slides[current].scrollIntoView({block:'start'});}
4
+ document.getElementById('prev').onclick=()=>show(current-1);document.getElementById('next').onclick=()=>show(current+1);
5
+ document.addEventListener('keydown',e=>{if(['ArrowRight','PageDown'].includes(e.key))show(current+1);if(['ArrowLeft','PageUp'].includes(e.key))show(current-1);if(e.key==='Home')show(0);if(e.key==='End')show(slides.length-1);});
6
+ const initial=slides.findIndex(s=>`#${s.id}`===location.hash);show(initial>=0?initial:0,false);
7
+ const lineage=document.getElementById('lineage');if(lineage){const data=JSON.parse(lineage.dataset.lineage||'[]');const detail=document.getElementById('milestone-detail');function pick(i){const x=data[i]||{};detail.innerHTML=`<h3>${escapeText(x.method||'')}</h3><p><strong>突破:</strong>${escapeText(x.innovation||'')}</p><p><strong>解决的瓶颈:</strong>${escapeText(x.bottleneck||'')}</p><p><strong>遗留限制:</strong>${escapeText(x.limitation||'')}</p>`;document.querySelectorAll('.milestone').forEach((b,j)=>b.setAttribute('aria-pressed',String(i===j)));}document.querySelectorAll('.milestone').forEach((b,i)=>b.onclick=()=>pick(i));if(data.length)pick(0);}
8
+ function escapeText(value){const d=document.createElement('div');d.textContent=value;return d.innerHTML;}
tests/test_app.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import unittest
2
+
3
+ from app import deduplicate, render_presentation, safe_slug
4
+
5
+
6
+ class AppTests(unittest.TestCase):
7
+ def test_slug(self):
8
+ self.assertEqual(safe_slug("Diffusion Transformer 研究"), "diffusion-transformer")
9
+
10
+ def test_deduplicate_versions(self):
11
+ papers = [{"id": "1234.5678v1"}, {"id": "1234.5678v2"}]
12
+ self.assertEqual(len(deduplicate(papers)), 1)
13
+
14
+ def test_render_is_downloadable_document(self):
15
+ report = {"title": "Test", "executive_summary": ["Result"], "lineage": [], "sections": [], "comparisons": [], "open_questions": []}
16
+ output = render_presentation(report, [], "2026-01-01")
17
+ self.assertIn("<!doctype html>", output)
18
+ self.assertIn("保存 PDF", output)
19
+
20
+
21
+ if __name__ == "__main__":
22
+ unittest.main()