feat(metadata): 元数据引擎第 3 步之一——一致性与冲突感知的状态机
之前只要 confidence>=0.6 且 validation_count>=2 就升级 verified,
两次读到不同结果也会被当成"验证通过",错误标签一旦写入很难纠正。
新增 MetadataStability(纯函数,可单测):
- 首次见到 → pending(first_seen),次数 1
- 与上次记录标题/艺术家冲突 → pending(conflict),次数重置为 1
- 一致且次数 >= 2、置信度达标 → verified(stable)
- 置信度不足 → pending(low_confidence),次数继续累计
- 字段缺失不算冲突;比较时忽略大小写与首尾空格
metadata_service 改为调用该状态机,并打印判定原因:
🧭 [MetadataService] 状态机: stable → verified(validations=2)
测试:新增 test/metadata_stability_test.dart 7 个用例,
连同原有 9 个共 16 个,flutter test 全绿。
已构建并安装 release 验证。
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
// test/metadata_stability_test.dart
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:qingting_player/metadata/metadata_model.dart';
|
||||
import 'package:qingting_player/metadata/metadata_stability.dart';
|
||||
|
||||
void main() {
|
||||
CandidateMetadata candidate({double confidence = 0.8}) => CandidateMetadata(
|
||||
metadata: const NormalizedMetadata(title: '夜曲', artist: '周杰伦'),
|
||||
confidence: confidence,
|
||||
);
|
||||
|
||||
group('MetadataStability', () {
|
||||
test('首次见到 → pending,次数 1', () {
|
||||
final d = MetadataStability.decide(
|
||||
previous: null,
|
||||
candidate: candidate(),
|
||||
title: '夜曲',
|
||||
artist: '周杰伦',
|
||||
);
|
||||
expect(d.status, 'pending');
|
||||
expect(d.reason, 'first_seen');
|
||||
expect(d.validationCount, 1);
|
||||
});
|
||||
|
||||
test('第二次结果一致且置信度达标 → verified', () {
|
||||
final d = MetadataStability.decide(
|
||||
previous: {
|
||||
'title': '夜曲',
|
||||
'artist': '周杰伦',
|
||||
'confidence': 0.8,
|
||||
'validation_count': 1,
|
||||
},
|
||||
candidate: candidate(),
|
||||
title: '夜曲',
|
||||
artist: '周杰伦',
|
||||
);
|
||||
expect(d.status, 'verified');
|
||||
expect(d.reason, 'stable');
|
||||
expect(d.validationCount, 2);
|
||||
});
|
||||
|
||||
test('标题或艺术家冲突 → 次数重置为 1,保持 pending', () {
|
||||
final d = MetadataStability.decide(
|
||||
previous: {
|
||||
'title': '夜曲',
|
||||
'artist': '周杰伦',
|
||||
'confidence': 0.9,
|
||||
'validation_count': 5,
|
||||
},
|
||||
candidate: candidate(),
|
||||
title: '完全不同的歌',
|
||||
artist: '别的歌手',
|
||||
);
|
||||
expect(d.status, 'pending');
|
||||
expect(d.reason, 'conflict');
|
||||
expect(d.validationCount, 1);
|
||||
});
|
||||
|
||||
test('置信度不足 → pending,但次数继续累计', () {
|
||||
final d = MetadataStability.decide(
|
||||
previous: {
|
||||
'title': '夜曲',
|
||||
'artist': '周杰伦',
|
||||
'confidence': 0.4,
|
||||
'validation_count': 3,
|
||||
},
|
||||
candidate: candidate(confidence: 0.5),
|
||||
title: '夜曲',
|
||||
artist: '周杰伦',
|
||||
);
|
||||
expect(d.status, 'pending');
|
||||
expect(d.reason, 'low_confidence');
|
||||
expect(d.validationCount, 4);
|
||||
});
|
||||
|
||||
test('一致但只验证过一次 → pending', () {
|
||||
final d = MetadataStability.decide(
|
||||
previous: {
|
||||
'title': '夜曲',
|
||||
'artist': '周杰伦',
|
||||
'confidence': 0.8,
|
||||
'validation_count': 0,
|
||||
},
|
||||
candidate: candidate(),
|
||||
title: '夜曲',
|
||||
artist: '周杰伦',
|
||||
);
|
||||
expect(d.status, 'pending');
|
||||
expect(d.reason, 'need_second_validation');
|
||||
expect(d.validationCount, 1);
|
||||
});
|
||||
|
||||
test('字段缺失不算冲突(旧记录没有艺术家)', () {
|
||||
final d = MetadataStability.decide(
|
||||
previous: {
|
||||
'title': '夜曲',
|
||||
'artist': '',
|
||||
'confidence': 0.7,
|
||||
'validation_count': 1,
|
||||
},
|
||||
candidate: candidate(),
|
||||
title: '夜曲',
|
||||
artist: '周杰伦',
|
||||
);
|
||||
expect(d.status, 'verified');
|
||||
expect(d.validationCount, 2);
|
||||
});
|
||||
|
||||
test('大小写与首尾空格不影响一致性判断', () {
|
||||
final d = MetadataStability.decide(
|
||||
previous: {
|
||||
'title': ' Night Song ',
|
||||
'artist': 'JAY',
|
||||
'confidence': 0.8,
|
||||
'validation_count': 1,
|
||||
},
|
||||
candidate: candidate(),
|
||||
title: 'night song',
|
||||
artist: 'jay',
|
||||
);
|
||||
expect(d.status, 'verified');
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user