Skill: wowcharlotte/scholar-search-skills (version 1.0.0)

--- name: scholar-search-skills description: 学术论文搜索与下载工具。当用户要求搜索某一主题的科研论文时触发此技能，支持从 arXiv、ICLR、ICML、NeurIPS 等来源搜索、筛选和下载论文，并生成结构化摘要和 BibTeX 引用。 --- # Scholar Search Skills ## 概述 本技能用于系统性地搜索、筛选和下载学术论文，适用于研究课题调研、文献综述等场景。集成了 arxiv-search、docling 等工具，支持自动化摘要提取和引用追踪。 ## 触发条件 当用户提出以下需求时触发： - "帮我搜索关于 XXX 的论文" - "查找 XXX 相关的学术文献" - "搜索 XXX 主题的研究论文" - "找一些 XXX 领域的核心文献" - "帮我下载 XXX 主题的论文" ## 工作流 ### 步骤 1: 确认基本信息 与用户确认以下信息： 1. **研究主题/论文标题**：用户想要搜索的具体主题 2. **关键词**：用逗号分隔，建议使用英文关键词 - 如果用户提供中文关键词，自动翻译为英文 - 示例：`Agent, Indirect Prompt Injection Attack, Black-box Defense` 3. **核心论文（可选）**：用户已有关键文献列表 4. **筛选条件（可选）**： - 时间范围：默认近3年（2023-2026） - 来源：arXiv、ICLR、ICML、NeurIPS - 引用量阈值（如有要求） ### 步骤 2: 创建工作目录 ```bash mkdir -p ~/papers/<研究主题>/{core-papers,cited-papers,output} ``` 创建文献清单文件： - `PAPER_LIST.md` - 论文列表 - `PAPER_SUMMARIES.md` - 论文摘要 - `references.bib` - BibTeX 引用格式 ### 步骤 3: 论文搜索 #### 3.1 使用 arxiv-search (优先) ```bash # 安装的 arxiv-search 技能位置 ls ~/.agents/skills/arxiv-search/ ``` 如果 arxiv-search 可用，使用它进行搜索： - 调用其搜索 API 或脚本 - 获取更精准的论文匹配 #### 3.2 使用 web_fetch 备选 ``` https://arxiv.org/search/?searchtype=all&query=<关键词>&start=0 ``` ### 步骤 4: 下载论文 ```bash # 下载 PDF wget -q "https://arxiv.org/pdf/<arXiv_ID>" -O "<标题缩写>_<arXiv_ID>.pdf" # 示例 wget -q "https://arxiv.org/pdf/2309.15817" -O "lm_agents_risks_2309.15817.pdf" ``` ### 步骤 5: PDF 解析 (使用 docling) #### 5.1 安装 docling ```bash npx skills add existential-birds/beagle@docling -g -y ``` #### 5.2 解析论文 使用 docling 提取论文关键信息： - 标题、作者、机构 - 摘要 - 方法 - 实验结果 - 结论 - 参考文献 ```python # docling 使用示例 from docling.document_converter import DocumentConverter converter = DocumentConverter() result = converter.convert("paper.pdf") # 提取结构化信息 ``` ### 步骤 6: 生成结构化摘要 为每篇论文创建摘要模板： ```markdown ### <论文标题> - **作者**: Authors - **arXiv**: [ID](https://arxiv.org/abs/ID) - **日期**: YYYY-MM - **核心贡献**: 1. 贡献点1 2. 贡献点2 - **方法**: - **实验结果**: - **链接**: [PDF](./path/to/file.pdf) - **BibTeX**: ```bibtex @article{...} ``` ``` ### 步骤 7: 引用追踪 (Google Scholar) 1. 访问 Google Scholar 搜索核心论文 2. 获取 "Cited by" 列表 3. 筛选与研究主题相关的引用论文 4. 下载高相关度论文 ```bash # 搜索引用论文 # 使用 web_fetch 访问 Google Scholar ``` ### 步骤 8: 自动化筛选评分 #### 8.1 关键词匹配评分 ```python def relevance_score(title, abstract, keywords): score = 0 for kw in keywords: if kw.lower() in title.lower(): score += 3 if kw.lower() in abstract.lower(): score += 1 return score ``` #### 8.2 排序和过滤 - 按相关性分数降序排列 - 过滤低于阈值的论文 - 优先选择高引用量论文 ### 步骤 9: 输出格式生成 #### 9.1 文献列表 (PAPER_LIST.md) ```markdown | # | 论文标题 | arXiv ID | 相关度 | 日期 | 类型 | |---|----------|----------|--------|------|------| | 1 | Title | xxxx.xxxxx | ★★★★★ | 2024 | 核心 | ``` #### 9.2 BibTeX (references.bib) ```bibtex @article{ruan2023toolemu, title={Identifying the Risks of LM Agents with an LM-Emulated Sandbox}, author={Ruan, Yangjun and Dong, Honghua and Wang, Andrew and others}, journal={arXiv preprint arXiv:2309.15817}, year={2023} } ``` #### 9.3 文献综述大纲 ```markdown # <研究主题> 文献综述 ## 1. 引言 ## 2. 背景与相关工作 ## 3. 核心文献分析 ## 4. 最新研究进展 ## 5. 未来研究方向 ``` ## 已集成 Skills | Skill | 用途 | |-------|------| | arxiv-search | 专业 arXiv 论文搜索 | | docling | PDF 解析和信息提取 | | academic-researcher | 学术研究工作流 | | latex-paper-en | 论文格式模板 | ## 常用搜索查询 ### arXiv 搜索 - 基础：`https://arxiv.org/search/?searchtype=all&query=<关键词>&start=0` - 日期过滤：`https://arxiv.org/search/?searchtype=all&query=<关键词>&date-filter_by=all_dates&size=50` ### 会议论文 - ICLR: https://openreview.net/group?id=ICLR.cc/2025/Conference - NeurIPS: https://proceedings.neurips.cc/ - ICML: https://proceedings.mlr.press/ ### Google Scholar - 引用追踪：访问论文主页查看 "Cited by" ## 注意事项 1. **时间筛选**：默认要求 2023-2026 年的论文 2. **去重**：下载前检查是否已存在相同 arXiv ID 的论文 3. **增量更新**：已有文献库时，仅搜索新增论文 4. **来源说明**：大多数顶会论文会先在 arXiv 预发布 5. **版权**：仅下载用于研究，仅提供摘要和下载链接 ## 输出文件清单 | 文件 | 说明 | |------|------| | `core-papers/` | 核心文献 PDF | | `cited-papers/` | 引用文献 PDF | | `PAPER_LIST.md` | 论文清单 | | `PAPER_SUMMARIES.md` | 结构化摘要 | | `references.bib` | BibTeX 引用 | | `literature_review.md` | 文献综述大纲 (可选) | ## 关键词相关性打分 ### 使用脚本 本技能包含 `scripts/score_papers.py` 用于关键词相关性打分。 #### 安装依赖 ```bash pip install scikit-learn rapidfuzz ``` #### 基本用法 ```bash # 单一论文打分 python3 scripts/score_papers.py \ --keywords "agent,LLM,security,prompt injection" \ --title "Identifying the Risks of LM Agents with an LM-Emulated Sandbox" \ --abstract "Recent advances in Language Model (LM) agents..." # 批量打分 (CSV 格式) python3 scripts/score_papers.py \ --keywords "agent,LLM,security" \ --papers paper_list.csv \ --output results.json # 自定义权重 python3 scripts/score_papers.py \ --keywords "agent,defense" \ --title "xxx" \ --abstract "xxx" \ --title-weight 0.7 \ --abstract-weight 0.3 ``` #### CSV 格式要求 ```csv title,abstract,arxiv_id,year 论文标题,论文摘要,2309.15817,2023 ... ``` ### 打分算法 #### 1. 模糊匹配 (Fuzzy Matching) - **部分匹配 (partial_ratio)**: 关键词在文本中的部分匹配程度 - **词序无关匹配 (token_sort_ratio)**: 忽略词序的匹配程度 - **集合匹配 (token_set_ratio)**: 关键词集合与文本的交集比例 #### 2. TF-IDF 余弦相似度 - 将论文文本和关键词转换为 TF-IDF 向量 - 计算余弦相似度 #### 3. 加权组合 ``` 最终分数 = 0.6 × 模糊匹配分 + 0.4 × TF-IDF分数 标题权重 = 0.6 (可调整) 摘要权重 = 0.4 (可调整) ``` #### 输出示例 ``` ================================================================================ arXiv ID 年份 最终分 标题 ================================================================================ 2309.15817 2023 0.8523 Identifying the Risks of LM Agents... 2403.14771 2024 0.7832 InjecAgent: Benchmarking Indirect... ... ================================================================================ 匹配关键词统计: agent: 15 LLM: 12 security: 8 ``` ### 集成到工作流 在步骤 8 (自动化筛选评分) 中使用： ```bash # 1. 生成 CSV echo "title,abstract,arxiv_id,year" > paper_list.csv # ... 添加论文信息 ... # 2. 打分排序 python3 scripts/score_papers.py \ --keywords "agent,prompt injection,defense" \ --papers paper_list.csv \ --output scored_papers.json # 3. 筛选高分论文 (分数 > 0.6) jq '.[] | select(.final_score > 0.6)' scored_papers.json ```

path: README.md
content: # Scholar Search Skills

学术论文搜索与下载工具

## 功能特性

- 📚 从 arXiv、ICLR、ICML、NeurIPS 等来源搜索论文
- 🔍 关键词相关性打分（模糊匹配 + TF-IDF 余弦相似度）
- 📥 批量下载论文 PDF
- 📝 自动生成结构化摘要
- 📖 支持 BibTeX 引用格式
- 🔗 引用追踪（Google Scholar）

## 触发条件

当用户提出以下需求时触发：
- "帮我搜索关于 XXX 的论文"
- "查找 XXX 相关的学术文献"
- "搜索 XXX 主题的研究论文"

## 安装

```bash
# 方式1: 使用 clawhub
npx clawhub install scholar-search-skills

# 方式2: 手动安装
git clone https://github.com/<your-username>/scholar-search-skills.git
cd scholar-search-skills
# 复制到 OpenClaw skills 目录
```

## 使用方法

### 1. 基本搜索

告诉助手你的研究主题，例如：
- "帮我搜索 LLM 安全相关的论文"
- "查找 prompt injection 防御的文献"

### 2. 关键词相关性打分

```bash
pip install scikit-learn rapidfuzz

# 单一论文
python3 scripts/score_papers.py \
    --keywords "agent,LLM,security" \
    --title "论文标题" \
    --abstract "论文摘要"

# 批量打分
python3 scripts/score_papers.py \
    --keywords "agent,LLM,security" \
    --papers paper_list.csv \
    --output results.json
```

## 输出文件

| 文件 | 说明 |
|------|------|
| `core-papers/` | 核心文献 PDF |
| `cited-papers/` | 引用文献 PDF |
| `PAPER_LIST.md` | 论文清单 |
| `PAPER_SUMMARIES.md` | 结构化摘要 |
| `references.bib` | BibTeX 引用 |

## 依赖

- Python 3.8+
- scikit-learn
- rapidfuzz

## 工作流

```
1. 确认基本信息 (主题/关键词/筛选条件)
       ↓
2. 搜索论文 (arxiv-search / web_fetch)
       ↓
3. 下载 PDF
       ↓
4. 关键词相关性打分
       ↓
5. 生成摘要 + BibTeX
```

## 目录结构

```
scholar-search-skills/
├── SKILL.md                      # Skill 定义文件
├── scripts/
│   └── score_papers.py           # 关键词打分脚本
├── references/
│   ├── bibtex_template.md        # BibTeX 模板
│   └── summary_template.md       # 摘要模板
└── README.md                     # 本文件
```

## License

MIT License

## 作者

Your Name <[REDACTED_SECRET]>


path: references/bibtex_template.md
content: # BibTeX 引用格式模板

## 常用引用类型

### arXiv 预印本

```bibtex
@article{作者年份标题,
  title = {标题},
  author = {作者1 and 作者2 and 作者3},
  journal = {arXiv preprint arXiv:XXXX.XXXXX},
  year = {2024},
  eprint = {XXXX.XXXXX},
  archivePrefix = {arXiv},
  primaryClass = {cs.AI},
  url = {https://arxiv.org/abs/XXXX.XXXXX}
}
```

### 会议论文 (ICLR/ICML/NeurIPS)

```bibtex
@inproceedings{作者年份标题,
  title = {标题},
  author = {作者1 and 作者2 and 作者3},
  booktitle = {International Conference on Learning Representations (ICLR)},
  year = {2024},
  url = {https://openreview.net/forum?id=xxx}
}

@inproceedings{作者年份标题,
  title = {标题},
  author = {作者1 and 作者2 and 作者3},
  booktitle = {Advances in Neural Information Processing Systems (NeurIPS)},
  year = {2024},
  volume = {37},
  pages = {1--15}
}
```

### 期刊论文

```bibtex
@article{作者年份标题,
  title = {标题},
  author = {作者1 and 作者2 and 作者3},
  journal = {Nature Machine Intelligence},
  year = {2024},
  volume = {6},
  number = {3},
  pages = {234--245},
  publisher = {Nature Publishing Group}
}
```

## 自动生成脚本

可以使用以下 Python 脚本从 arXiv 信息生成 BibTeX：

```python
def generate_bibtex(arxiv_id, title, authors, year):
    # 清理作者格式
    author_list = authors.replace(", ", " and ")
    
    # 生成引用键
    first_author = author_list.split(" and ")[0].split()[-1].lower()
    cite_key = f"{first_author}{year}"
    
    bibtex = f"""@article{{{cite_key},
  title = {{{title}}},
  author = {{{author_list}}},
  journal = {{arXiv preprint arXiv:{arxiv_id}}},
  year = {{{year}}},
  eprint = {{{arxiv_id}}},
  archivePrefix = {{arXiv}},
  primaryClass = {{cs.AI}},
  url = {{https://arxiv.org/abs/{arxiv_id}}}
}}"""
    return bibtex

# 示例
print(generate_bibtex(
    "2309.15817",
    "Identifying the Risks of LM Agents with an LM-Emulated Sandbox",
    "Yangjun Ruan, Honghua Dong, Andrew Wang, Silviu Pitis, Yongchao Zhou, Jimmy Ba, Yann Dubois, Chris J. Maddison, Tatsunori Hashimoto",
    2023
))
```


path: references/summary_template.md
content: # 论文摘要模板

## 完整结构

```markdown
## <论文标题>

### 基本信息
- **作者**: 作者列表
- **机构**: 机构列表
- **arXiv ID**: XXXX.XXXXX
- **链接**: [arXiv](https://arxiv.org/abs/XXXX.XXXXX) | [PDF](./core-papers/xxx.pdf)
- **日期**: YYYY年MM月
- **来源**: arXiv / ICLR 2024 / NeurIPS 2024 / ICML 2024

### 摘要
> 原文摘要

### 核心贡献
1. 贡献点1
2. 贡献点2
3. 贡献点3

### 方法
- 方法1
- 方法2
- 方法3

### 实验
- 数据集
- 基线模型
- 主要结果

### 局限性
- 局限点1
- 局限点2

### 相关工作
- 相关论文1
- 相关论文2

### 引用
- 被引次数
- 引用论文列表

### 个人笔记
> 阅读时的思考和问题
```

## 快速模板

```markdown
### <标题>

- **作者**: ...
- **arXiv**: [ID](url)
- **核心贡献**: 
  1. ...
- **方法**: ...
- **链接**: [PDF](./xxx.pdf)
```

## 关键词提取模板

```markdown
### 关键词
- 关键词1 (出现次数: N)
- 关键词2 (出现次数: N)
- 关键词3 (出现次数: N)
```


path: scripts/score_papers.py
content: #!/usr/bin/env python3
"""
关键词相关性打分工具
使用模糊匹配和余弦相似度计算论文与关键词的相关性

使用方法:
    python3 score_papers.py --keywords "agent,prompt injection,defense" --papers paper_list.csv
    python3 score_papers.py --keywords "LLM,security,attack" --title "xxx" --abstract "xxx"
"""

import argparse
import os
import re
import csv
import json
from typing import List, Dict, Tuple
from collections import Counter

# 尝试导入可选依赖
try:
    from sklearn.feature_extraction.text import TfidfVectorizer
    from sklearn.metrics.pairwise import cosine_similarity
    SKLEARN_AVAILABLE = True
except ImportError:
    SKLEARN_AVAILABLE = False

try:
    from rapidfuzz import fuzz
    RAPIDFUZZ_AVAILABLE = True
except ImportError:
    try:
        from fuzzywuzzy import fuzz
        RAPIDFUZZ_AVAILABLE = True
    except ImportError:
        RAPIDFUZZ_AVAILABLE = False
        fuzz = None


def preprocess_text(text: str) -> str:
    """预处理文本：转小写、移除特殊字符"""
    if not text:
        return ""
    text = text.lower()
    text = re.sub(r'[^\w\s]', ' ', text)
    text = re.sub(r'\s+', ' ', text).strip()
    return text


def tokenize(text: str) -> List[str]:
    """分词"""
    return preprocess_text(text).split()


def fuzzy_keyword_score(text: str, keywords: List[str]) -> float:
    """
    使用模糊匹配计算关键词相关度
    结合多种模糊匹配策略
    """
    if not text or not keywords:
        return 0.0
    
    text_lower = text.lower()
    scores = []
    
    for keyword in keywords:
        keyword_lower = keyword.lower().strip()
        if not keyword_lower:
            continue
            
        # 1. 简单包含检查
        if keyword_lower in text_lower:
            scores.append(1.0)
            continue
        
        # 2. 模糊匹配 (如果可用)
        if RAPIDFUZZ_AVAILABLE:
            # partial_ratio: 部分匹配
            partial = fuzz.partial_ratio(keyword_lower, text_lower) / 100.0
            # token_sort_ratio: 词序无关匹配
            token_sort = fuzz.token_sort_ratio(keyword_lower, text_lower) / 100.0
            # token_set_ratio: 集合匹配
            token_set = fuzz.token_set_ratio(keyword_lower, text_lower) / 100.0
            
            # 加权平均
            fuzzy_score = max(partial, token_sort, token_set)
            scores.append(fuzzy_score)
        else:
            # 备选：基于字符 n-gram 的简单匹配
            score = simple_ngram_match(keyword_lower, text_lower)
            scores.append(score)
    
    if not scores:
        return 0.0
    
    # 返回最高分和平均分的加权组合
    max_score = max(scores)
    avg_score = sum(scores) / len(scores)
    
    # 组合策略：突出最高匹配，同时考虑整体相关性
    return 0.6 * max_score + 0.4 * avg_score


def simple_ngram_match(keyword: str, text: str, n: int = 3) -> float:
    """简单的 n-gram 匹配 (无外部依赖时使用)"""
    if len(keyword) < n:
        return 1.0 if keyword in text else 0.0
    
    keyword_ngrams = set(keyword[i:i+n] for i in range(len(keyword) - n + 1))
    text_ngrams = set(text[i:i+n] for i in range(len(text) - n + 1))
    
    if not keyword_ngrams:
        return 0.0
    
    intersection = keyword_ngrams & text_ngrams
    return len(intersection) / len(keyword_ngrams)


def tfidf_cosine_similarity(texts: List[str], query: str) -> List[float]:
    """
    使用 TF-IDF 和余弦相似度计算相关性
    """
    if not SKLEARN_AVAILABLE or not texts:
        return [0.0] * len(texts)
    
    try:
        vectorizer = TfidfVectorizer(
            lowercase=True,
            stop_words='english',
            ngram_range=(1, 2),
            max_features=5000
        )
        
        # 将查询加入语料库一起向量化
        all_texts = texts + [query]
        tfidf_matrix = vectorizer.fit_transform(all_texts)
        
        # 计算查询与每个文档的余弦相似度
        query_vector = tfidf_matrix[-1]
        doc_vectors = tfidf_matrix[:-1]
        
        similarities = cosine_similarity(query_vector, doc_vectors)[0]
        
        return similarities.tolist()
    except Exception as e:
        print(f"TF-IDF 计算错误: {e}")
        return [0.0] * len(texts)


def hybrid_score(
    title: str, 
    abstract: str, 
    keywords: List[str],
    title_weight: float = 0.6,
    abstract_weight: float = 0.4
) -> Dict:
    """
    混合打分：结合模糊匹配和 TF-IDF 余弦相似度
    
    Args:
        title: 论文标题
        abstract: 论文摘要
        keywords: 关键词列表
        title_weight: 标题权重
        abstract_weight: 摘要权重
    
    Returns:
        包含各项分数的字典
    """
    # 1. 模糊匹配分数
    fuzzy_title_score = fuzzy_keyword_score(title, keywords)
    fuzzy_abstract_score = fuzzy_keyword_score(abstract, keywords)
    
    # 2. TF-IDF 余弦相似度
    combined_text = f"{title} {abstract}"
    tfidf_scores = tfidf_cosine_similarity([combined_text], " ".join(keywords))
    tfidf_score = tfidf_scores[0] if tfidf_scores else 0.0
    
    # 3. 加权组合
    fuzzy_combined = (
        title_weight * fuzzy_title_score + 
        abstract_weight * fuzzy_abstract_score
    )
    
    # 最终分数：模糊匹配 60% + TF-IDF 40%
    final_score = 0.6 * fuzzy_combined + 0.4 * tfidf_score
    
    return {
        "fuzzy_title_score": round(fuzzy_title_score, 4),
        "fuzzy_abstract_score": round(fuzzy_abstract_score, 4),
        "tfidf_score": round(tfidf_score, 4),
        "final_score": round(final_score, 4),
        "match_keywords": find_matched_keywords(title, abstract, keywords)
    }


def find_matched_keywords(title: str, abstract: str, keywords: List[str]) -> List[str]:
    """找出匹配的关键词"""
    text = f"{title} {abstract}".lower()
    matched = []
    
    for kw in keywords:
        kw_lower = kw.lower().strip()
        if kw_lower in text:
            matched.append(kw)
        elif RAPIDFUZZ_AVAILABLE:
            if fuzz.partial_ratio(kw_lower, text) > 80:
                matched.append(kw)
    
    return matched


def score_papers_from_file(paper_file: str, keywords: List[str]) -> List[Dict]:
    """
    从 CSV 文件读取论文并打分
    
    CSV 格式:
    title,abstract,arxiv_id,year
    """
    papers = []
    
    # 尝试不同编码
    for encoding in ['utf-8', 'gbk', 'latin-1']:
        try:
            with open(paper_file, 'r', encoding=encoding) as f:
                reader = csv.DictReader(f)
                for row in reader:
                    papers.append(row)
            break
        except UnicodeDecodeError:
            continue
    
    results = []
    for i, paper in enumerate(papers):
        title = paper.get('title', '')
        abstract = paper.get('abstract', '')
        
        scores = hybrid_score(title, abstract, keywords)
        scores['arxiv_id'] = paper.get('arxiv_id', f'paper_{i}')
        scores['title'] = title[:100] + '...' if len(title) > 100 else title
        scores['year'] = paper.get('year', 'N/A')
        
        results.append(scores)
    
    # 按分数排序
    results.sort(key=lambda x: x['final_score'], reverse=True)
    
    return results


def print_results(results: List[Dict], top_n: int = 10):
    """打印结果"""
    print(f"\n{'='*80}")
    print(f"{'arXiv ID':<15} {'年份':<6} {'最终分':<8} {'标题':<45}")
    print(f"{'='*80}")
    
    for r in results[:top_n]:
        title = r['title'][:42] + '...' if len(r['title']) > 45 else r['title']
        print(f"{r['arxiv_id']:<15} {r['year']:<6} {r['final_score']:<8.4f} {title}")
    
    print(f"{'='*80}")
    print(f"匹配关键词统计:")
    
    # 统计关键词匹配频率
    kw_counter = Counter()
    for r in results:
        for kw in r['match_keywords']:
            kw_counter[kw] += 1
    
    for kw, count in kw_counter.most_common(10):
        print(f"  {kw}: {count}")


def main():
    parser = argparse.ArgumentParser(
        description="论文关键词相关性打分工具",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
示例:
  # 单篇论文打分
  python3 score_papers.py --keywords "agent,LLM,security" --title "xxx" --abstract "xxx"
  
  # 批量打分
  python3 score_papers.py --keywords "agent,LLM,security" --papers papers.csv
  
  # 输出 JSON
  python3 score_papers.py --keywords "agent,LLM" --papers papers.csv --output results.json
        """
    )
    
    parser.add_argument('--keywords', type=str, required=True,
                        help='关键词，用逗号分隔')
    parser.add_argument('--title', type=str,
                        help='论文标题')
    parser.add_argument('--abstract', type=str,
                        help='论文摘要')
    parser.add_argument('--papers', type=str,
                        help='CSV 文件路径，包含 title,abstract,arxiv_id,year 列')
    parser.add_argument('--output', type=str,
                        help='输出 JSON 文件路径')
    parser.add_argument('--top-n', type=int, default=10,
                        help='显示前 N 个结果 (默认: 10)')
    parser.add_argument('--title-weight', type=float, default=0.6,
                        help='标题权重 (默认: 0.6)')
    parser.add_argument('--abstract-weight', type=float, default=0.4,
                        help='摘要权重 (默认: 0.4)')
    
    args = parser.parse_args()
    
    # 解析关键词
    keywords = [k.strip() for k in args.keywords.split(',') if k.strip()]
    
    if not keywords:
        print("错误: 请提供至少一个关键词")
        return
    
    print(f"关键词: {keywords}")
    print(f"使用库: sklearn={'是' if SKLEARN_AVAILABLE else '否'}, rapidfuzz={'是' if RAPIDFUZZ_AVAILABLE else '否'}")
    
    # 单一论文模式
    if args.title:
        result = hybrid_score(
            args.title, 
            args.abstract or "", 
            keywords,
            args.title_weight,
            args.abstract_weight
        )
        
        print(f"\n论文: {args.title}")
        print(f"模糊匹配分数 (标题): {result['fuzzy_title_score']:.4f}")
        print(f"模糊匹配分数 (摘要): {result['fuzzy_abstract_score']:.4f}")
        print(f"TF-IDF 余弦相似度: {result['tfidf_score']:.4f}")
        print(f"最终分数: {result['final_score']:.4f}")
        print(f"匹配关键词: {result['match_keywords']}")
        
        if args.output:
            with open(args.output, 'w', encoding='utf-8') as f:
                json.dump(result, f, ensure_ascii=False, indent=2)
            print(f"\n结果已保存到: {args.output}")
    
    # 批量模式
    elif args.papers:
        if not os.path.exists(args.papers):
            print(f"错误: 文件不存在: {args.papers}")
            return
        
        results = score_papers_from_file(args.papers, keywords)
        
        print_results(results, args.top_n)
        
        if args.output:
            with open(args.output, 'w', encoding='utf-8') as f:
                json.dump(results, f, ensure_ascii=False, indent=2)
            print(f"\n结果已保存到: {args.output}")
    
    else:
        print("错误: 请提供 --title 或 --papers 参数")
        parser.print_help()


if __name__ == '__main__':
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)