# ResearchRadar · 乙→甲 对接文档
> 版本:v1.0 | 日期:2026-05-04 | 撰写:乙
---
## 目录
1. [乙完成情况总览](#1-乙完成情况总览)
2. [甲需交付的3个模块(API契约)](#2-甲需交付的3个模块api契约)
- [2.1 paper_fetcher.py](#21-paper_fetcherpy)
- [2.2 repo_searcher.py](#22-repo_searcherpy)
- [2.3 repo_fetcher.py](#23-repo_fetcherpy)
3. [数据流全景图](#3-数据流全景图)
4. [对接步骤(技术 + 沟通)](#4-对接步骤技术--沟通)
5. [对接后效果](#5-对接后效果)
6. [故障排查指南](#6-故障排查指南)
7. [已知局限与后续迭代](#7-已知局限与后续迭代)
---
## 1. 乙完成情况总览
### 1.1 已完成的7个文件
| # | 文件名 | 类型 | 功能 | 状态 |
|---|--------|------|------|------|
| 1 | `llm_utils.py` | 公共工具 | OpenAI 客户端、LLM调用(3次重试)、JSON安全解析(6策略)、输出校验 | ✅ 完成 |
| 2 | `direction_analyzer.py` | Agent 1 | LLM解析研究方向→子领域+方法族+搜索词 | ✅ 完成 |
| 3 | `repo_evaluator.py` | Agent 2 | LLM六维度评分(可复现性5维80分 + 对比实验适配度1维20分) | ✅ 完成 |
| 4 | `run.py` | 调度器 | 串联3层架构,数据传递,方法族匹配,耗时统计 | ✅ 完成 |
| 5 | `app.py` | UI层 | Gradio界面 + Markdown研报格式化(四段式) | ✅ 完成 |
| 6 | `state.py` | 数据模型 | dataclass定义(MethodFamily, DirectionAnalysis, CandidateRepo, EvalResult, ResearchReport) | ✅ 完成 |
| 7 | `test_agents.py` | 测试工具 | 一键测试两个Agent,6个测试用例,断言+汇总 | ✅ 完成 |
### 1.2 诚实评估:哪些是真正完成的,哪些有前提条件
**可以100%确认完成的:**
- Agent 1(方向解析):独立可运行,3篇论文测试通过。输入标题+摘要→输出结构化的研究方向JSON。
- Agent 2(仓库评估):独立可运行,3个场景测试通过(高分/低分/中等)。输入仓库元信息+README+依赖→输出六维度评分JSON。
- 公共工具层(llm_utils.py):API重试、JSON解析容错、输出校验全部就绪。
- 测试套件(test_agents.py):一键运行,6/6通过。
**代码已完成但需甲交付后才能验证的:**
- `run.py`:调度逻辑完整,含耗时统计、方法族匹配(中英双语关键词)、错误处理链路。但**无法运行**——它依赖甲的3个模块,当前导入即报错。
- `app.py`:Gradio界面 + Markdown格式化完整。同样**无法启动**——它导入 `run`,而 `run` 导入甲的模块。
**已知局限(非bug,是MVP范围内的取舍):**
- `_match_family` 仓库→方法族匹配是关键词级别的,非LLM语义匹配。当LLM返回中文方法族名(如"基于重建的方法")时,依赖中英双语关键词映射来兜底,但不能保证100%准确。正确率估计70-80%。任务书计划中 Week 3 用 LLM 替换。
- LLM输出依赖提示词约束,偶有格式偏差。已通过 `parse_json_safe`(6种解析策略)+ `validate_*_output`(字段填充默认值)双重兜底,确保下游不崩溃。
### 1.3 自检命令
```bash
# 甲模块尚未交付时,只能运行这两个:
python direction_analyzer.py # 测试 Agent 1(3篇论文)
python repo_evaluator.py # 测试 Agent 2(3个仓库场景)
python test_agents.py # 一键测试两个Agent(推荐)
# 甲模块交付后:
python run.py https://arxiv.org/abs/2011.08785 # 完整链路
python app.py # 启动 Gradio 界面
```
---
## 2. 甲需交付的3个模块(API契约)
> **重要**:以下契约是双方代码能对接的**唯一接口标准**。函数名、参数名、返回值类型、dict的key名都必须严格一致。如有变动,必须双方同步确认。
### 2.1 paper_fetcher.py
**职责**:根据 arxiv URL 获取论文元信息。
#### 函数签名
```python
def fetch_paper_info(arxiv_url: str) -> dict:
"""从 arxiv URL 获取论文信息。
Args:
arxiv_url: arxiv 论文 URL,如 "https://arxiv.org/abs/2011.08785"
Returns:
dict,必须包含以下字段。如获取失败,抛出 Exception。
"""
```
#### 返回值 dict 字段定义
| 字段名 | 类型 | 必需 | 说明 | 示例 |
|--------|------|------|------|------|
| `arxiv_id` | `str` | ✅ 必需 | arxiv ID | `"2011.08785"` |
| `title` | `str` | ✅ 必需 | 论文标题(完整,不要截断) | `"PaDiM: a Patch Distribution Modeling..."` |
| `abstract` | `str` | ✅ 必需 | 论文摘要(完整) | `"We present a new framework..."` |
| `authors` | `list[str]` | ✅ 必需 | 作者列表 | `["Thomas Defard", ...]` |
| `published` | `str` | 推荐 | 发表日期(ISO格式字符串即可) | `"2021-03-15"` 或 `"2021-03-15T00:00:00Z"` |
| `categories` | `list[str]` | 推荐 | arxiv 分类标签 | `["cs.CV", "cs.LG"]` |
#### 乙的调用上下文
```python
# run.py 第74行:
paper = fetch_paper_info(arxiv_url)
# 紧接着使用的字段(第78-82行):
title = paper.get("title", "")
abstract = paper.get("abstract", "")
# authors 和 categories 仅用于打印日志
# app.py 额外使用 arxiv_id 和 published 显示在研报中
```
#### 实现提示(给甲)
- arxiv 有公开 API:`https://export.arxiv.org/api/query?id_list={arxiv_id}`
- 返回 XML 格式,需要解析 `
`, ``, ``, ``, `` 等标签
- 也可以用 `arxiv` Python 包(`pip install arxiv`)
- 如果网络不通,可以直接抛出异常,run.py 会捕获并返回错误信息
- 建议在 `__main__` 里写个自测:`python paper_fetcher.py` 打印一篇论文的信息
---
### 2.2 repo_searcher.py
**职责**:根据搜索查询列表在 GitHub 上搜索仓库,去重后返回。
#### 函数签名
```python
def search_repos(queries: list[str], max_per_keyword: int = 5) -> list[dict]:
"""在 GitHub 搜索仓库。
Args:
queries: 搜索查询字符串列表,如 ["padim anomaly detection pytorch", ...]
max_per_keyword: 每个查询最多保留的仓库数
Returns:
list[dict]: 候选仓库列表,每个 dict 字段见下表。已去重(按 full_name)。
"""
```
#### 返回值 list[dict] 字段定义
| 字段名 | 类型 | 必需 | 说明 | 示例 |
|--------|------|------|------|------|
| `full_name` | `str` | ✅ 必需 | 仓库全名(owner/repo) | `"openvinotoolkit/anomalib"` |
| `html_url` | `str` | ✅ 必需 | 仓库 URL | `"https://github.com/openvinotoolkit/anomalib"` |
| `description` | `str` | ✅ 必需 | 仓库描述(可为空字符串) | `"An anomaly detection library..."` |
| `stars` | `int` | ✅ 必需 | Star 数量 | `4000` |
| `language` | `str` | ✅ 必需 | 主要语言 | `"Python"` |
| `updated_at` | `str` | ✅ 必需 | 最后更新时间(ISO格式) | `"2026-01-15T00:00:00Z"` |
| `topics` | `list[str]` | ✅ 必需 | GitHub topics 标签(可为空列表) | `["anomaly-detection", "pytorch"]` |
| `match_keyword` | `str` | 推荐 | 命中了哪个搜索查询(用于研报展示) | `"padim anomaly detection pytorch"` |
#### 乙的调用上下文
```python
# run.py 第119行:
candidates = search_repos(all_queries, max_per_keyword=5)
# all_queries 示例(18个搜索词):
# ["patch distribution modeling anomaly detection pytorch",
# "padim implementation pytorch",
# "memory bank anomaly detection code",
# ...,
# "industrial anomaly detection pytorch", ← broad_queries
# "unsupervised anomaly localization github",
# "MVTec AD anomaly detection code"]
# 紧接着(第127行)取前 top_n=5 个:
candidates = candidates[:top_n]
# 然后对每个 candidate 使用字段(第152-166行):
full_name = repo["full_name"] # 用于拆分 owner/name 和显示
repo.get("html_url", "") # 用于研报链接
repo.get("description", "") # 用于方法族匹配
repo.get("stars", 0) # 用于评分输入和显示
repo.get("match_keyword", "") # 用于研报展示
```
#### 重要约束
- **去重**:同一个 `full_name` 只能出现一次。多个搜索查询可能命中同一个仓库。
- **排序**:建议按 `stars` 降序排列后再去重,这样保留的是 star 最高的版本。
- **max_per_keyword**:乙传5,意味着每个搜索查询最多返回5个仓库。如果 `all_queries` 有18个,理论最多90个,去重后通常剩10-30个。乙再截断到5个。
- **GitHub API**:可以用 `GET /search/repositories?q=...`,未认证限制 10 req/min,认证后 30 req/min。如果18个查询全走API可能触发限流。建议:如果有 GitHub token,放到 header 里。
#### 实现提示(给甲)
```python
# GitHub Search API 示例
import requests
url = "https://api.github.com/search/repositories"
params = {"q": query, "sort": "stars", "order": "desc", "per_page": max_per_keyword}
headers = {"Accept": "application/vnd.github.v3+json"}
# 如果有 token: headers["Authorization"] = "token ghp_xxx"
response = requests.get(url, params=params, headers=headers)
data = response.json()
for item in data.get("items", []):
# 提取字段构建 dict
...
```
---
### 2.3 repo_fetcher.py
**职责**:获取单个仓库的 README 和依赖文件内容。
#### 函数签名
```python
def fetch_readme(owner: str, name: str) -> str | None:
"""获取仓库 README 文件内容。
Args:
owner: 仓库所有者,如 "openvinotoolkit"
name: 仓库名,如 "anomalib"
Returns:
README 文件全文(字符串),获取失败返回 None。
"""
def fetch_dependencies(owner: str, name: str) -> dict[str, str]:
"""获取仓库的依赖文件。
Args:
owner: 仓库所有者
name: 仓库名
Returns:
dict: {文件名: 文件内容},如 {"requirements.txt": "torch>=1.10\n...", ...}
找不到任何依赖文件时返回空 dict {}。
常见目标文件:requirements.txt, environment.yml, setup.py, setup.cfg, Pipfile, pyproject.toml
"""
```
#### 乙的调用上下文
```python
# run.py 第165-167行(在 for 循环内对每个仓库调用):
readme = fetch_readme(owner, name) # → str | None
deps = fetch_dependencies(owner, name) # → dict[str, str]
evaluation = evaluate_repo(repo, readme, deps, matched_family)
# repo_evaluator.py 内部(第105行):
readme_text = (readme or "README 未找到")[:2500] # 截断到 2500 字符
# deps 被格式化为文本后同样截断每个文件到 1000 字符
```
#### 重要约束
- `fetch_readme`:GitHub API 路径 `GET /repos/{owner}/{name}/readme`,返回 base64 编码内容,需解码。
- `fetch_dependencies`:需要逐个检查依赖文件是否存在。GitHub API `GET /repos/{owner}/{name}/contents/{path}`。**不要**把整个仓库 clone 下来——MVP 阶段一个 API 调用即可。
- 两个函数都**不能抛出异常**。失败时返回 `None` 或 `{}`。乙的调度器已经对每个仓库做了 try/except,但如果函数内部抛出异常,评估循环可能会中断(当前只捕获了评估失败,未捕获 fetch 失败——这是一个乙已知的防御缺口,见7.1节)。
#### 实现提示(给甲)
```python
import requests
import base64
def fetch_readme(owner, name):
url = f"https://api.github.com/repos/{owner}/{name}/readme"
headers = {"Accept": "application/vnd.github.v3+json"}
resp = requests.get(url, headers=headers)
if resp.status_code != 200:
return None
content = resp.json().get("content", "")
return base64.b64decode(content).decode("utf-8", errors="replace")
def fetch_dependencies(owner, name):
target_files = ["requirements.txt", "environment.yml", "setup.py",
"setup.cfg", "Pipfile", "pyproject.toml"]
result = {}
for fname in target_files:
url = f"https://api.github.com/repos/{owner}/{name}/contents/{fname}"
resp = requests.get(url, headers={"Accept": "application/vnd.github.v3+json"})
if resp.status_code == 200:
content = resp.json().get("content", "")
result[fname] = base64.b64decode(content).decode("utf-8", errors="replace")
return result
```
---
## 3. 数据流全景图
```
用户输入 arxiv URL
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ [1/5] paper_fetcher.py ← 甲(Workflow) │
│ fetch_paper_info(url) → dict │
│ {arxiv_id, title, abstract, authors, published, categories} │
└──────────────────────────┬──────────────────────────────────────┘
│ title, abstract
▼
┌─────────────────────────────────────────────────────────────────┐
│ [2/5] direction_analyzer.py ← 乙(Agent 1) │
│ analyze_direction(title, abstract) → dict │
│ {subfield, subfield_trend, method_families, broad_queries} │
│ │
│ method_families: [ │
│ {family_name, description, representative_work, │
│ search_queries: ["...", "..."]} │
│ ] │
└──────────────────────────┬──────────────────────────────────────┘
│ all_queries = flatten(search_queries) + broad_queries
│ 约 12-18 个搜索字符串
▼
┌─────────────────────────────────────────────────────────────────┐
│ [3/5] repo_searcher.py ← 甲(Workflow) │
│ search_repos(queries, max_per_keyword=5) → list[dict] │
│ [{full_name, html_url, description, stars, language, │
│ updated_at, topics, match_keyword}] │
│ │
│ 去重 → 截断 top_n=5 → 传给下一步 │
└──────────────────────────┬──────────────────────────────────────┘
│ 每个候选仓库: full_name → owner/name
▼
┌─────────────────────────────────────────────────────────────────┐
│ [4/5] 对每个仓库并行: │
│ │
│ repo_fetcher.py ← 甲(Workflow) │
│ fetch_readme(owner, name) → str | None │
│ fetch_dependencies(owner, name) → dict[str, str] │
│ │
│ run.py ← 乙(调度器) │
│ _match_family(repo, families) → str (方法族名 or "") │
│ │
│ repo_evaluator.py ← 乙(Agent 2) │
│ evaluate_repo(repo, readme, deps, method_family) → dict │
│ {reproducibility_score, benchmark_fitness_score, │
│ overall_score, verdict, env_score, doc_score, │
│ code_score, community_score, dep_score, │
│ benchmark_score, reasoning, risks, │
│ benchmark_readiness, suggested_use} │
└──────────────────────────┬──────────────────────────────────────┘
│ 按 overall_score 降序排列
▼
┌─────────────────────────────────────────────────────────────────┐
│ [5/5] app.py ← 乙(UI) │
│ format_report(result) → Markdown 研报 │
│ │
│ 研报结构: │
│ 一、论文信息(arxiv_id, title, authors, categories) │
│ 二、研究方向全景(子领域 + 趋势 + 方法族谱系表格) │
│ 三、仓库评估排名(每个仓库六维度明细 + 分析 + 风险点) │
│ 四、对比实验推荐总结(按 ready/partial/not_ready 分组) │
└─────────────────────────────────────────────────────────────────┘
```
### 数据量级参考
| 阶段 | 输入 | 输出 | 预计耗时 |
|------|------|------|----------|
| 1. 论文信息 | 1个URL | 1个dict | ~1s(网络) |
| 2. 方向解析 | 标题+摘要 | 1个dict(3-6方法族+3宽泛) | ~5-15s(LLM) |
| 3. 仓库搜索 | ~15个查询 | ~5个候选仓库 | ~10-30s(GitHub API × 15) |
| 4. 仓库评估 | 5个仓库 × (README+依赖) | 5个evaluation dict | ~25-75s(LLM × 5) |
| **合计** | | | **约40-120秒** |
---
## 4. 对接步骤(技术 + 沟通)
### 4.1 甲交付文件时
甲每次交付一个模块,乙方按以下流程验证。
#### 阶段A:甲交付 `paper_fetcher.py`
**甲需确保:**
- [ ] 文件名:`paper_fetcher.py`
- [ ] 放入路径:`D:\Desktop\AI_opencode_judging\paper_fetcher.py`
- [ ] 文件内包含函数 `fetch_paper_info(arxiv_url: str) -> dict`
- [ ] 自测可运行:`python paper_fetcher.py` 能打印一篇论文的信息
- [ ] 返回值包含所有必需字段(见2.1节)
**乙验证步骤:**
```bash
# 1. 确认文件存在
ls paper_fetcher.py
# 2. 检查函数签名
python -c "from paper_fetcher import fetch_paper_info; help(fetch_paper_info)"
# 3. 测试实际调用
python -c "
from paper_fetcher import fetch_paper_info
paper = fetch_paper_info('https://arxiv.org/abs/2011.08785')
for k in ['arxiv_id', 'title', 'abstract', 'authors']:
print(f'{k}: {str(paper.get(k))[:80]}...')
"
# 4. 确认 run.py 不再报 paper_fetcher 的 ImportError
python -c "import run" 2>&1 | head -5
# 预期:不再出现 "缺少模块: paper_fetcher.py",而是报下一个缺失模块
```
#### 阶段B:甲交付 `repo_searcher.py`
**甲需确保:**
- [ ] 文件名:`repo_searcher.py`
- [ ] 函数 `search_repos(queries: list[str], max_per_keyword: int = 5) -> list[dict]`
- [ ] 自测可运行:用几个构造的搜索词调用,打印返回的仓库数量和名称
- [ ] 去重已实现(同一 full_name 只出现一次)
- [ ] 每个 dict 包含所有必需字段(见2.2节)
**乙验证步骤:**
```bash
# 1. 确认搜索可用
python -c "
from repo_searcher import search_repos
repos = search_repos(['padim anomaly detection pytorch'], max_per_keyword=3)
print(f'找到 {len(repos)} 个仓库')
for r in repos:
print(f' {r[\"full_name\"]} ⭐{r[\"stars\"]} — {r.get(\"match_keyword\", \"?\")}')
"
# 2. 确认 run.py 不再报 repo_searcher 的 ImportError
python -c "import run" 2>&1 | head -5
# 预期:不再出现 "缺少模块: repo_searcher.py",而是报下一个缺失模块
```
#### 阶段C:甲交付 `repo_fetcher.py`
**甲需确保:**
- [ ] 文件名:`repo_fetcher.py`
- [ ] 函数 `fetch_readme(owner: str, name: str) -> str | None`
- [ ] 函数 `fetch_dependencies(owner: str, name: str) -> dict[str, str]`
- [ ] 自测可运行:对 anomalib 仓库调用两个函数,打印结果
**乙验证步骤:**
```bash
# 1. 确认能获取 README
python -c "
from repo_fetcher import fetch_readme
readme = fetch_readme('openvinotoolkit', 'anomalib')
print(f'README 长度: {len(readme) if readme else 0} 字符')
print((readme or '')[:200])
"
# 2. 确认能获取依赖
python -c "
from repo_fetcher import fetch_dependencies
deps = fetch_dependencies('openvinotoolkit', 'anomalib')
print(f'找到 {len(deps)} 个依赖文件: {list(deps.keys())}')
for fname, content in deps.items():
print(f' {fname}: {len(content)} 字符')
"
# 3. 确认 run.py 不再报任何 ImportError
python -c "import run; print('run.py 导入成功!')"
# 预期:run.py 导入成功!
```
### 4.2 全部交付后的联调
```bash
# 第一步:确保乙的两个 Agent 正常
python test_agents.py
# 预期:6/6 通过
# 第二步:用一篇工业缺陷检测论文跑完整链路
python run.py https://arxiv.org/abs/2011.08785
# 预期:
# [1/5] 获取论文信息 → 显示标题、作者、分类、耗时
# [2/5] 解析研究方向 → 显示子领域、趋势、方法族、生成的搜索词数
# [3/5] 搜索 GitHub → 显示候选仓库列表(最多5个)
# [4/5] 评估仓库 → 逐个显示评分
# [5/5] 完成 → 打印完整 Markdown 研报
# 第三步:启动 Gradio 界面
python app.py
# 浏览器打开 http://127.0.0.1:7860
# 粘贴 arxiv URL,点击 Submit,查看研报
```
### 4.3 对接时的沟通要点
1. **统一开发环境**
- Python 版本:3.10+ (乙当前使用 3.11+,`str | None` 类型注解需要 3.10+)
- 依赖安装:`pip install -r requirements.txt`(openai, gradio, requests 已列好)
- 甲如需新增依赖,需同步更新 `requirements.txt`
2. **GitHub API 限流**
- 未认证:10 次/分钟。18个搜索查询会超限。
- 认证:30 次/分钟。建议甲申请一个 GitHub Personal Access Token(免费,Settings → Developer settings → Tokens),不需要任何权限(public repo 只读不需要 scope)。
- Token 配置方式:甲自由选择(环境变量 `GITHUB_TOKEN`、配置文件、或硬编码——但别提交到 git)。
3. **错误处理约定**
- Workflow 函数(甲的模块):尽量不抛异常。获取失败返回 `None`、`[]`、`{}` 等空值。
- Agent 函数(乙的模块):LLM 解析失败抛 `ValueError`,API 调用失败抛 `RuntimeError`。
- 调度器(run.py):捕获所有异常,转为 `{"error": "..."}` 或 fallback 值,保证流程不崩溃。
4. **Git 协作建议**
```
D:\Desktop\AI_opencode_judging\
├── .gitignore ← 建议添加:__pycache__/, .env, *.pyc
├── requirements.txt
├── state.py ← 双方共享的数据模型参考
├── paper_fetcher.py ← 甲
├── repo_searcher.py ← 甲
├── repo_fetcher.py ← 甲
├── llm_utils.py ← 乙
├── direction_analyzer.py ← 乙
├── repo_evaluator.py ← 乙
├── run.py ← 乙
├── app.py ← 乙
├── test_agents.py ← 乙
└── test_set/ ← 甲放置测试数据
```
---
## 5. 对接后效果
### 5.1 完整研报示例(结构展示)
对接完成后,输入 `https://arxiv.org/abs/2011.08785`(PaDiM),输出研报结构如下:
```markdown
# ResearchRadar 研究方向全景研报
---
## 一、论文信息
| 项目 | 内容 |
|------|------|
| **标题** | PaDiM: a Patch Distribution Modeling Framework for Anomaly Detection and Localization |
| **arXiv** | [2011.08785](https://arxiv.org/abs/2011.08785) |
| **作者** | Thomas Defard, Aleksandr Setkov, Angelique Loesch, Romaric Audigier |
| **发表时间** | 2021-03-15 |
| **分类** | cs.CV, cs.LG |
---
## 二、研究方向全景
### 子领域定位
**工业图像异常检测与定位**
### 当前趋势
2024-2025年主流趋势包括利用预训练视觉模型提取局部特征,结合概率建模进行无监督异常检测,强调高效推理和细粒度定位。
### 方法族谱系
| # | 方法族 | 核心特点 | 代表工作 |
|---|--------|----------|----------|
| 1 | **Patch Distribution Modeling** | 预训练CNN提取patch特征+高斯分布建模 | PaDiM (2021) |
| 2 | **Memory Bank-based Methods** | 存储正常样本特征到记忆库进行检索 | PatchCore (2022) |
| 3 | **Knowledge Distillation Methods** | 师生网络,学生学习模仿教师输出 | STFPM (2021) |
| 4 | **Reconstruction-based Methods** | 自编码器重建正常样本,异常重建误差大 | AutoEncoder |
| 5 | **Synthetic Defect Methods** | 生成合成缺陷用于训练 | DRAEM (2021) |
---
## 三、开源实现评估排名
### 1. [openvinotoolkit/anomalib](https://github.com/openvinotoolkit/anomalib) `[Embedding-based]`
✅ **可复现性: 80/80** | 🟢 **对比实验适配度: 20/20** | **综合: 100/100**
| 维度 | 得分 | 满分 | 说明 |
|------|:----:|:----:|------|
| 环境配置 | 15 | 15 | 依赖文件完整性 |
| 文档质量 | 20 | 20 | 安装/训练/数据说明 |
| 代码可用性 | 20 | 20 | 训练+推理脚本 |
| 社区活跃度 | 10 | 10 | Star + 更新频率 |
| 依赖健康度 | 15 | 15 | 依赖兼容性 |
| **对比实验** | **20** | **20** | **benchmark + 预训练权重** |
⭐ Star: 4000 | 匹配词: `anomaly detection library pytorch`
**分析**: anomalib提供了完整的benchmark框架,支持MVTec AD等标准数据集,有预训练权重和标准化评估脚本,可以直接用于对比实验。
**对比实验建议**: 可以直接作为对比实验 baseline
---
### 2. [其他仓库...]
(每个仓库同样格式的六维度明细表)
---
## 四、对比实验推荐总结
### 🟢 可直接用于对比实验
- **[openvinotoolkit/anomalib](https://github.com/openvinotoolkit/anomalib)** — 可以直接作为对比实验 baseline
### 🟡 需要少量修改后可用
- **[其他仓库...]** — 需要适配数据集格式
### 🔴 仅适合参考代码实现
- **[其他仓库...]** — 仅提供推理demo,无训练脚本...
---
> ResearchRadar · 论文研究方向全景 + 开源代码评估 + 对比实验推荐
```
### 5.2 Gradio 界面效果
浏览器打开 `http://127.0.0.1:7860`,会看到:
- 标题栏:ResearchRadar — 研究方向全景 + 开源代码评估
- 输入框:论文 arxiv 链接(带3个示例一键填充)
- 输出区:Markdown 渲染的完整研报(如5.1节所示)
- 底部示例:PaDiM / Transformer / DRAEM 三篇论文快捷入口
### 5.3 命令行输出效果
```
============================================================
[1/5] 正在获取论文信息...
标题: PaDiM: a Patch Distribution Modeling Framework for Anomaly Detection and Localization
作者: Thomas Defard, Aleksandr Setkov, Angelique Loesch
分类: cs.CV, cs.LG
耗时: 0.8s
[2/5] 正在解析研究方向(Agent 1:LLM 调用)...
子领域: 工业图像异常检测与定位
趋势: 2024-2025年主流趋势包括利用预训练视觉模型提取局部特征...
方法族 (5 个):
- Patch Distribution Modeling: 预训练CNN提取patch特征+高斯分布建模...
- Memory Bank-based Methods: 存储正常样本特征到记忆库...
- Knowledge Distillation Methods: 师生网络学习模仿...
- Reconstruction-based Methods: 自编码器重建正常样本...
- Synthetic Defect Methods: 生成合成缺陷用于训练...
生成 18 个搜索查询
耗时: 8.3s
[3/5] 正在搜索 GitHub 仓库(18 个查询)...
去重后获得 5 个候选仓库
耗时: 22.1s
候选列表:
1. openvinotoolkit/anomalib ⭐4000
2. hcw-00/PatchCore ⭐850
3. taikiinoue45/STFPM ⭐320
4. VitjanZ/DRAEM ⭐180
5. xiahaifeng1995/PaDiM-Anomaly-Detection ⭐150
[4/5] 正在评估 5 个仓库...
(1/5) 评估 openvinotoolkit/anomalib [Embedding-based]...
(2/5) 评估 hcw-00/PatchCore [Memory Bank]...
(3/5) 评估 taikiinoue45/STFPM [Teacher-Student]...
(4/5) 评估 VitjanZ/DRAEM [Synthetic Defect]...
(5/5) 评估 xiahaifeng1995/PaDiM-Anomaly-Detection [Embedding-based]...
评估耗时: 42.5s
[5/5] 完成!
最高分: openvinotoolkit/anomalib (100/100)
总耗时: 73.7s
============================================================
[此处输出完整 Markdown 研报,同5.1节]
```
---
## 6. 故障排查指南
### 6.1 甲模块尚未交付
| 症状 | 原因 | 处理 |
|------|------|------|
| `python run.py ...` 报错 `缺少模块: paper_fetcher.py` | 甲未交付 | 等待甲,或先用 `python test_agents.py` 验证乙的部分 |
| `python app.py` 报错 `无法导入 run.py` | 同上 | 同上 |
### 6.2 API 相关
| 症状 | 原因 | 处理 |
|------|------|------|
| `LLM API 调用失败(已重试 3 次)` | DeepSeek API 不可达或欠费 | 检查 `llm_utils.py` 中的 API Key 是否有效;检查网络 |
| GitHub API `403 rate limit exceeded` | GitHub 限流 | 甲需添加 GitHub Token;或减少搜索查询数 |
| arxiv API 超时 | arxiv 服务器慢 | 重试;甲可加 timeout 参数 |
### 6.3 数据格式不匹配
| 症状 | 原因 | 处理 |
|------|------|------|
| `KeyError: 'full_name'` | 甲的 `search_repos` 返回的 dict 缺少字段 | 对照本文档第2.2节的字段表补齐 |
| `ValueError: 无法解析 LLM 返回的 JSON` | LLM 返回格式异常 | 查看终端输出中的"原始返回(前500字符)";联系乙优化解析逻辑 |
| 研报显示 "N/A" 过多 | 甲返回的 dict 中可选字段缺失 | 检查 paper dict 是否含 `published`, `categories` 等字段 |
### 6.4 评分异常
| 症状 | 原因 | 处理 |
|------|------|------|
| 所有仓库得分相近 | LLM 评分不够有区分度 | 检查传给 LLM 的 README/deps 信息是否足够丰富 |
| 明显优质仓库得分很低 | `fetch_readme` 或 `fetch_dependencies` 返回了空值 | 检查 API 调用是否成功,网络是否可达 |
| 所有仓库 `[未归类]` | `_match_family` 中英双语匹配都未命中 | 检查 LLM 返回的方法族名是否非常见表述;可在 `_DOMAIN_KEYWORDS` 补充关键词 |
---
## 7. 已知局限与后续迭代
### 7.1 乙已识别但未修复的防御缺口
1. **仓库评估循环中的异常粒度不够细**(`run.py` 第164-170行):当前 `try/except` 包裹了 `fetch_readme` + `fetch_dependencies` + `evaluate_repo` 的整段。如果 `fetch_readme` 抛出异常(虽然它不应该),整个仓库的评估都会标记为 error,但无法知道是获取失败还是评估失败。改进方向:分步 try/except。
2. **`_match_family` 使用关键词匹配而非语义匹配**:已通过中英双语关键词映射尽力提升匹配率,但正确率仍非100%。任务书计划 Week 3 用 LLM 做精准归类。
3. **`parse_json_safe` 策略4(最外层 `{...}`的贪婪匹配)**:如果 LLM 一次返回了多个 JSON 对象(如先解释再给 JSON),策略4的 `\{[\s\S]*\}` 会从第一个 `{` 匹配到最后一个 `}`,产生一个非法 JSON 字符串。此情况极少出现,策略5(清理尾部逗号)可能也修不好,最终会触发策略6(关键词定位)或失败。当前通过约束 Prompt("不要任何额外文字")来避免。
4. **无重试机制在 GitHub API 层面**:`run.py` 没有对甲的 Workflow 函数做重试。如果 GitHub API 临时返回 503,仓库搜索或 README 获取会直接失败。建议甲在自己的函数内部加重试(1-2次即可)。
### 7.2 MVP 后建议迭代方向
| 优先级 | 内容 | 负责 | 预计工作量 |
|--------|------|------|------------|
| P0 | 甲交付3个Workflow模块 | 甲 | 2-3天 |
| P0 | 联调 + 端到端测试 | 甲乙一起 | 1天 |
| P1 | `_match_family` 升级为 LLM 语义匹配 | 乙 | 半天 |
| P1 | fetch_readme 失败时优雅降级(不阻断评估) | 乙 | 1小时 |
| P2 | 支持更多论文源(不仅是 arxiv) | 甲 | 1天 |
| P2 | 评估维度权重可配置 | 乙 | 2小时 |
| P3 | 缓存 LLM 评估结果(避免重复调用花钱) | 乙 | 半天 |
| P3 | 支持批量论文输入 | 甲+乙 | 1天 |
---
## 附录A:自检 Checklist(乙的代码)
在甲交付前,乙可用此清单自查:
- [x] `python test_agents.py` → 6/6 通过
- [x] `python direction_analyzer.py` → 3篇论文正常输出
- [x] `python repo_evaluator.py` → 3个场景评分合理
- [x] `llm_utils.py` → call_llm 有3次重试、parse_json_safe 有6种策略
- [x] `run.py` → 导入失败时抛出 ImportError 而非 sys.exit
- [x] `_DOMAIN_KEYWORDS` → 中英双语关键词已覆盖6大方法族类别
- [x] `app.py` → 导入失败有明确错误提示
- [x] `state.py` → 数据模型定义完整
- [x] `requirements.txt` → openai, gradio, requests 已列出
## 附录B:自检 Checklist(甲的3个模块)
甲交付时请对照此清单确认:
**paper_fetcher.py**
- [ ] `fetch_paper_info(arxiv_url)` 函数存在
- [ ] 返回值包含 `arxiv_id`, `title`, `abstract`, `authors`(必需)
- [ ] 返回值包含 `published`, `categories`(推荐)
- [ ] `python paper_fetcher.py` 自测可运行
**repo_searcher.py**
- [ ] `search_repos(queries, max_per_keyword=5)` 函数存在
- [ ] 返回值每个 dict 包含所有必需字段(见2.2节)
- [ ] 已按 `full_name` 去重
- [ ] 已按 `stars` 降序排列
- [ ] 处理了 GitHub API 限流(加 token 或减少请求频率)
- [ ] `python repo_searcher.py` 自测可运行
**repo_fetcher.py**
- [ ] `fetch_readme(owner, name)` 函数存在,失败返回 `None`
- [ ] `fetch_dependencies(owner, name)` 函数存在,失败返回 `{}`
- [ ] 覆盖了 `requirements.txt`, `environment.yml`, `setup.py`, `setup.cfg`, `Pipfile`, `pyproject.toml`
- [ ] 不抛出异常
- [ ] `python repo_fetcher.py` 自测可运行
---
> 本文档随代码同步维护。如有疑问或需要修改契约,请甲乙双方当面确认后再改代码,避免信息不对称。