关于 academic-citation-manager
面向学术写作的引用管理技能,以 BibTeX 为规范数据源,支持 DOI 等来源的元数据获取、格式转换、去重和校验。适用于 LaTeX、Markdown 和自动化文档流程。
主要功能
- 01从 DOI 等来源获取引文元数据
- 02转换多种引用格式
- 03检测并合并重复条目
- 04通过 CrossRef 和 OpenAlex 校验
- 05生成格式化参考文献
适用场景
- 01整理不同来源的参考文献
- 02按期刊要求切换引用格式
- 03检查 DOI、年份和重复条目
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wentorai/ research-plugins --skill academic-citation-manager安装后在 Claude Code 中输入/academic-citation-manager确认已加载。
详细介绍
学术引文管理器
管理多种格式的学术引文(BibTeX、APA 第 7 版、MLA 第 9 版、Chicago、Vancouver、IEEE),支持通过 DOI 自动检索、格式转换、去重和验证。此技能涵盖引文的完整生命周期,从初始收集到最终的稿件格式设置。
概述
引文管理是学术写作中长期存在的一大难题。研究人员从多个来源(数据库、PDF、同事、网页)收集参考文献,以不同格式存储,并且必须按各目标期刊要求的特定样式输出。引文错误——作者姓名拼写错误、年份有误、DOI 无效、格式不一致——是稿件被编辑直接拒稿和受到审稿人批评的常见原因。
此技能提供了一套全面的引文管理工作流,功能超越了图形界面参考文献管理器。它可以在数秒内通过 DOI 检索完整元数据、转换任意引文格式、检测并合并重复条目、根据 CrossRef 和 OpenAlex 数据库验证条目,并生成符合任何主流引文样式的格式规范的参考文献列表。
此方案基于文本且可脚本化,非常适合与 LaTeX 工作流、Markdown 写作流程和自动化文档生成集成。所有引文数据均以标准 BibTeX 格式作为规范来源存储,并可按需转换为其他格式,以满足特定稿件的要求。
引文检索
通过 DOI
import requests
def get_bibtex_from_doi(doi):
"""Retrieve BibTeX entry from a DOI via CrossRef."""
url = f"https://doi.org/{doi}"
headers = {"Accept": "application/x-bibtex"}
response = requests.get(url, headers=headers, allow_redirects=True)
if response.status_code == 200:
return response.text
return None
# Example
bibtex = get_bibtex_from_doi("10.1038/s41586-021-03819-2")
print(bibtex)
# @article{Jumper_2021,
# title={Highly accurate protein structure prediction with AlphaFold},
# author={Jumper, John and Evans, Richard and ...},
# journal={Nature},
# volume={596},
# pages={583--589},
# year={2021},
# publisher={Springer}
# }
通过 OpenAlex
def get_citation_from_openalex(work_id):
"""Retrieve citation data from OpenAlex API."""
url = f"https://api.openalex.org/works/{work_id}"
headers = {"User-Agent": "ResearchPlugins/1.0 (https://wentor.ai)"}
response = requests.get(url, headers=headers)
if response.status_code == 200:
data = response.json()
return format_as_bibtex(data)
return None
def format_as_bibtex(oa_data):
"""Convert OpenAlex data to BibTeX."""
authorships = oa_data.get("authorships", [])
author_str = " and ".join(a["author"]["display_name"] for a in authorships)
first_author = authorships[0]["author"]["display_name"].split()[-1] if authorships else "Unknown"
year = str(oa_data.get("publication_year", ""))
key = f"{first_author}_{year}"
venue = oa_data.get("primary_location", {}) or {}
journal = (venue.get("source") or {}).get("display_name", "")
return f"""@article{{{key},
title={{{oa_data.get('title', '')}}},
author={{{author_str}}},
year={{{year}}},
journal={{{journal}}},
doi={{{oa_data.get('doi', '')}}}
}}"""
通过 arXiv ID
def get_bibtex_from_arxiv(arxiv_id):
"""Retrieve BibTeX from arXiv."""
import feedparser
url = f"http://export.arxiv.org/api/query?id_list={arxiv_id}"
feed = feedparser.parse(url)
if feed.entries:
entry = feed.entries[0]
authors = " and ".join(a["name"] for a in entry.authors)
first_author = entry.authors[0]["name"].split()[-1]
year = entry.published[:4]
return f"""@article{{{first_author}_{year},
title={{{entry.title.replace(chr(10), ' ')}}},
author={{{authors}}},
year={{{year}}},
journal={{arXiv preprint arXiv:{arxiv_id}}},
url={{https://arxiv.org/abs/{arxiv_id}}}
}}"""
return None
格式转换
BibTeX 转 APA 第 7 版
def bibtex_to_apa7(entry):
"""Convert a parsed BibTeX entry to APA 7th edition format."""
authors = format_apa_authors(entry["author"])
year = entry.get("year", "n.d.")
title = entry["title"]
journal = entry.get("journal", "")
volume = entry.get("volume", "")
issue = entry.get("number", "")
pages = entry.get("pages", "")
doi = entry.get("doi", "")
# Article format
citation = f"{authors} ({year}). {title}. "
if journal:
citation += f"*{journal}*"
if volume:
citation += f", *{volume}*"
if issue:
citation += f"({issue})"
if pages:
citation += f", {pages}"
citation += "."
if doi:
citation += f" https://doi.org/{doi}"
return citation
def format_apa_authors(author_string):
"""Format author names in APA style: Last, F. M."""
authors = [a.strip() for a in author_string.split(" and ")]
formatted = []
for author in authors:
parts = author.split(", ") if ", " in author else author.rsplit(" ", 1)[::-1]
if len(parts) >= 2:
last = parts[0]
firsts = parts[1].split()
initials = " ".join(f"{f[0]}." for f in firsts)
formatted.append(f"{last}, {initials}")
else:
formatted.append(parts[0])
if len(formatted) == 1:
return formatted[0]
elif len(formatted) == 2:
return f"{formatted[0]}, & {formatted[1]}"
elif len(formatted) <= 20:
return ", ".join(formatted[:-1]) + f", & {formatted[-1]}"
else:
return ", ".join(formatted[:19]) + f", ... {formatted[-1]}"
格式示例
同一条参考文献的不同样式:
BibTeX:
@article{Jumper_2021,
title={Highly accurate protein structure prediction with AlphaFold},
author={Jumper, John and Evans, Richard and Pritzel, Alexander},
journal={Nature},
volume={596},
pages={583--589},
year={2021},
doi={10.1038/s41586-021-03819-2}
}
APA 第 7 版: Jumper, J., Evans, R., & Pritzel, A. (2021). Highly accurate protein structure prediction with AlphaFold. Nature, 596, 583-589. https://doi.org/10.1038/s41586-021-03819-2
MLA 第 9 版: Jumper, John, Richard Evans, and Alexander Pritzel. "Highly Accurate Protein Structure Prediction with AlphaFold." Nature, vol. 596, 2021, pp. 583-89.
Chicago(作者-日期制): Jumper, John, Richard Evans, and Alexander Pritzel. 2021. "Highly Accurate Protein Structure Prediction with AlphaFold." Nature 596: 583-89.
Vancouver: Jumper J, Evans R, Pritzel A. Highly accurate protein structure prediction with AlphaFold. Nature. 2021;596:583-9.
IEEE: J. Jumper, R. Evans, and A. Pritzel, "Highly accurate protein structure prediction with AlphaFold," Nature, vol. 596, pp. 583-589, 2021.
去重
检测重复条目
from difflib import SequenceMatcher
def find_duplicates(bib_entries, threshold=0.85):
"""Find duplicate BibTeX entries by title similarity."""
duplicates = []
titles = [(key, normalize_title(entry["title"]))
for key, entry in bib_entries.items()]
for i in range(len(titles)):
for j in range(i + 1, len(titles)):
similarity = SequenceMatcher(
None, titles[i][1], titles[j][1]
).ratio()
if similarity >= threshold:
duplicates.append({
"entry_a": titles[i][0],
"entry_b": titles[j][0],
"similarity": similarity
})
return duplicates
def normalize_title(title):
"""Normalize title for comparison."""
import re
title = title.lower()
title = re.sub(r'[{}\\]', '', title) # Remove LaTeX formatting
title = re.sub(r'[^a-z0-9\s]', '', title) # Remove punctuation
title = ' '.join(title.split()) # Normalize whitespace
return title
def merge_duplicates(entry_a, entry_b):
"""Merge two duplicate entries, preferring the more complete one."""
merged = {}
all_fields = set(list(entry_a.keys()) + list(entry_b.keys()))
for field in all_fields:
val_a = entry_a.get(field, "")
val_b = entry_b.get(field, "")
# Prefer the longer (more complete) value
merged[field] = val_a if len(str(val_a)) >= len(str(val_b)) else val_b
return merged
验证
CrossRef 验证
def validate_citation(doi):
"""Validate a citation against CrossRef metadata."""
url = f"https://api.crossref.org/works/{doi}"
response = requests.get(url)
if response.status_code != 200:
return {"valid": False, "error": "DOI not found in CrossRef"}
data = response.json()["message"]
return {
"valid": True,
"title": data.get("title", [None])[0],
"authors": [f"{a.get('family', '')}, {a.get('given', '')}"
for a in data.get("author", [])],
"year": data.get("published-print", {}).get("date-parts", [[None]])[0][0],
"journal": data.get("container-title", [None])[0],
"type": data.get("type", "unknown")
}
常见引文错误
| 错误 | 检测方法 | 修复方法 |
|---|---|---|
| 缺少 DOI | 检查 doi 字段是否为空 | 按标题查询 CrossRef |
| 年份错误 | 与 CrossRef 数据对比 | 使用 CrossRef 年份 |
| 作者姓名变体 | 使用 ORCID 进行模糊匹配 | 统一为 ORCID 姓名 |
| 重复条目 | 标题相似度 > 85% | 合并为单个条目 |
| URL 无效 | HTTP HEAD 请求返回 4xx/5xx | 更新或移除 URL |
| 条目不完整 | 缺少该样式要求的字段 | 通过 DOI 检索 |
与写作工具集成
LaTeX
% In document preamble
\usepackage[backend=biber,style=apa]{biblatex}
\addbibresource{references.bib}
% In text
\textcite{Jumper_2021} showed that...
As demonstrated by previous work \parencite{Jumper_2021}...
% At end of document
\printbibliography
Pandoc Markdown
Previous work [@Jumper_2021] showed that...
## References
pandoc paper.md --citeproc --bibliography=references.bib \
--csl=apa.csl -o paper.pdf
参考资料
- CrossRef API:https://api.crossref.org
- OpenAlex API:https://api.openalex.org
- APA 第 7 版手册:https://apastyle.apa.org/products/publication-manual-7th-edition
- BibTeX 文档:http://www.bibtex.org
- CSL 样式仓库:https://github.com/citation-style-language/styles