1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
|
# 验证规则
本文档详细说明四层验证机制的具体规则和匹配算法。
## Layer 1: Format Validation (格式验证)
### BibTeX 格式检查
**必填字段验证:**
不同类型的 BibTeX 条目有不同的必填字段要求:
**@article (期刊论文):**
- 必填: `author`, `title`, `journal`, `year`
- 可选: `volume`, `number`, `pages`, `doi`
**@inproceedings (会议论文):**
- 必填: `author`, `title`, `booktitle`, `year`
- 可选: `pages`, `organization`, `doi`
**@book (书籍):**
- 必填: `author` 或 `editor`, `title`, `publisher`, `year`
- 可选: `volume`, `series`, `address`
**@misc (其他):**
- 必填: `title`
- 可选: `author`, `howpublished`, `year`, `note`
### 格式检查规则
**1. 条目结构检查**
```python
def check_bibtex_structure(entry):
"""检查 BibTeX 条目结构"""
errors = []
# 检查是否有类型
if not entry.get('ENTRYTYPE'):
errors.append("Missing entry type")
# 检查是否有 ID
if not entry.get('ID'):
errors.append("Missing citation key")
# 检查必填字段
required = get_required_fields(entry.get('ENTRYTYPE'))
for field in required:
if not entry.get(field):
errors.append(f"Missing required field: {field}")
return errors
```
**2. 字段格式检查**
```python
def check_field_format(entry):
"""检查字段格式"""
errors = []
# 年份格式检查
if 'year' in entry:
year = entry['year']
if not year.isdigit() or len(year) != 4:
errors.append(f"Invalid year format: {year}")
if int(year) < 1900 or int(year) > 2030:
errors.append(f"Year out of reasonable range: {year}")
# DOI 格式检查
if 'doi' in entry:
doi = entry['doi']
if not doi.startswith('10.'):
errors.append(f"Invalid DOI format: {doi}")
return errors
```
### LaTeX 引用检查
**1. 引用命令检查**
```python
def check_latex_citations(tex_content):
"""检查 LaTeX 引用命令"""
import re
# 查找所有引用命令
cite_pattern = r'\\cite(?:\[[^\]]*\])?\{([^}]+)\}'
citations = re.findall(cite_pattern, tex_content)
# 展开多个引用
all_keys = []
for cite in citations:
keys = [k.strip() for k in cite.split(',')]
all_keys.extend(keys)
return all_keys
```
**2. 引用一致性检查**
```python
def check_citation_consistency(tex_keys, bib_keys):
"""检查引用一致性"""
tex_set = set(tex_keys)
bib_set = set(bib_keys)
# 未定义的引用
undefined = tex_set - bib_set
# 未使用的引用
unused = bib_set - tex_set
return {
'undefined': list(undefined),
'unused': list(unused)
}
```
## Layer 2: Existence Verification (存在性验证)
### API 验证流程
**验证步骤:**
1. 根据引用信息选择 API (DOI → CrossRef, arXiv ID → arXiv, 其他 → Semantic Scholar)
2. 调用 API 获取论文信息
3. 判断论文是否存在
**验证结果:**
- `exists` - 论文存在
- `not_found` - 论文不存在
- `api_error` - API 调用失败,需要人工验证
### 验证代码示例
```python
def verify_existence(citation_info):
"""验证论文存在性"""
# DOI 优先
if citation_info.get('doi'):
result = verify_with_crossref(citation_info['doi'])
if result['status'] == 'success':
return {'exists': True, 'source': 'crossref', 'data': result['data']}
# arXiv ID
if citation_info.get('arxiv_id'):
result = verify_with_arxiv(citation_info['arxiv_id'])
if result['status'] == 'success':
return {'exists': True, 'source': 'arxiv', 'data': result['data']}
# 通用搜索
if citation_info.get('title'):
result = verify_with_semantic_scholar(citation_info['title'])
if result['status'] == 'success' and result['data']:
return {'exists': True, 'source': 'semantic_scholar', 'data': result['data']}
return {'exists': False, 'source': None}
```
## Layer 3: Information Matching (信息匹配)
### 匹配算法
**1. 标题匹配**
使用模糊匹配算法,允许轻微差异:
```python
from difflib import SequenceMatcher
def match_title(title1, title2, threshold=0.85):
"""标题匹配"""
# 标准化:小写、去除标点
def normalize(text):
import re
text = text.lower()
text = re.sub(r'[^\w\s]', '', text)
return ' '.join(text.split())
t1 = normalize(title1)
t2 = normalize(title2)
# 计算相似度
ratio = SequenceMatcher(None, t1, t2).ratio()
return {
'match': ratio >= threshold,
'similarity': ratio
}
```
**2. 作者匹配**
考虑作者顺序和名字格式差异:
```python
def match_authors(authors1, authors2, threshold=0.7):
"""作者匹配"""
def normalize_name(name):
# 处理 "Last, First" 和 "First Last" 格式
parts = name.replace(',', '').split()
return ' '.join(sorted(parts)).lower()
names1 = [normalize_name(a) for a in authors1]
names2 = [normalize_name(a) for a in authors2]
# 计算交集比例
set1 = set(names1)
set2 = set(names2)
intersection = len(set1 & set2)
union = len(set1 | set2)
if union == 0:
return {'match': False, 'similarity': 0}
ratio = intersection / union
return {
'match': ratio >= threshold,
'similarity': ratio
}
```
**3. 年份匹配**
允许 ±1 年的差异(考虑预印本和正式发表的时间差):
```python
def match_year(year1, year2, tolerance=1):
"""年份匹配"""
try:
y1 = int(year1)
y2 = int(year2)
diff = abs(y1 - y2)
return {
'match': diff <= tolerance,
'difference': diff
}
except (ValueError, TypeError):
return {'match': False, 'difference': None}
```
## Layer 4: Content Validation (内容验证)
### 综合匹配评分
综合所有匹配结果,计算总体匹配分数:
```python
def calculate_match_score(citation, api_data):
"""计算综合匹配分数"""
scores = {}
weights = {
'title': 0.4,
'authors': 0.3,
'year': 0.2,
'venue': 0.1
}
# 标题匹配
if citation.get('title') and api_data.get('title'):
result = match_title(citation['title'], api_data['title'])
scores['title'] = result['similarity']
# 作者匹配
if citation.get('authors') and api_data.get('authors'):
result = match_authors(citation['authors'], api_data['authors'])
scores['authors'] = result['similarity']
# 年份匹配
if citation.get('year') and api_data.get('year'):
result = match_year(citation['year'], api_data['year'])
scores['year'] = 1.0 if result['match'] else 0.0
# 计算加权总分
total_score = 0
total_weight = 0
for key, weight in weights.items():
if key in scores:
total_score += scores[key] * weight
total_weight += weight
if total_weight == 0:
return 0
return total_score / total_weight
```
### 验证结果判定
根据匹配分数判定验证结果:
```python
def judge_verification_result(match_score):
"""判定验证结果"""
if match_score >= 0.9:
return {
'status': 'verified',
'level': 'high_confidence',
'message': '✅ 验证通过 - 信息完全匹配'
}
elif match_score >= 0.7:
return {
'status': 'partial_match',
'level': 'medium_confidence',
'message': '⚠️ 部分匹配 - 信息有轻微差异,建议人工确认'
}
elif match_score >= 0.5:
return {
'status': 'low_match',
'level': 'low_confidence',
'message': '❌ 匹配度低 - 信息差异较大,需要人工验证'
}
else:
return {
'status': 'failed',
'level': 'no_confidence',
'message': '❌ 验证失败 - 信息严重不匹配或论文不存在'
}
```
## 完整验证流程
### 主验证函数
```python
def verify_citation_complete(citation):
"""完整的引用验证流程"""
result = {
'citation_key': citation.get('ID'),
'layers': {}
}
# Layer 1: 格式验证
format_errors = check_bibtex_structure(citation)
format_errors.extend(check_field_format(citation))
result['layers']['format'] = {
'passed': len(format_errors) == 0,
'errors': format_errors
}
# Layer 2: 存在性验证
existence = verify_existence(citation)
result['layers']['existence'] = existence
if not existence['exists']:
result['final_status'] = 'not_found'
return result
# Layer 3 & 4: 信息匹配和内容验证
api_data = existence['data']
match_score = calculate_match_score(citation, api_data)
judgment = judge_verification_result(match_score)
result['layers']['matching'] = {
'score': match_score,
'judgment': judgment
}
result['final_status'] = judgment['status']
result['confidence'] = judgment['level']
return result
```
## 验证阈值配置
### 可调整的阈值参数
```python
VERIFICATION_THRESHOLDS = {
# 匹配阈值
'title_similarity': 0.85, # 标题相似度阈值
'author_similarity': 0.70, # 作者相似度阈值
'year_tolerance': 1, # 年份容差
# 判定阈值
'high_confidence': 0.90, # 高置信度阈值
'medium_confidence': 0.70, # 中等置信度阈值
'low_confidence': 0.50, # 低置信度阈值
# 权重配置
'weights': {
'title': 0.4,
'authors': 0.3,
'year': 0.2,
'venue': 0.1
}
}
```
### 阈值调整建议
**严格模式** (用于正式发表):
- title_similarity: 0.90
- author_similarity: 0.80
- high_confidence: 0.95
**宽松模式** (用于初稿):
- title_similarity: 0.80
- author_similarity: 0.60
- high_confidence: 0.85
|