# 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 格式,需要解析 ``, `<summary>`, `<author>`, `<published>`, `<category>` 等标签 - 也可以用 `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` 自测可运行 --- > 本文档随代码同步维护。如有疑问或需要修改契约,请甲乙双方当面确认后再改代码,避免信息不对称。