master-skill 0.12.9 → 0.12.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/prebuilt/master-buddhaghosa/references/teaching.md +1 -1
- package/prebuilt/master-milarepa/SKILL.md +2 -0
- package/prebuilt/master-milarepa/meta.json +7 -1
- package/prebuilt/master-milarepa/references/teaching.md +13 -7
- package/prebuilt/master-milarepa/references/voice.md +4 -4
- package/prebuilt/master-milarepa/sources/INDEX.md +1 -1
- package/prebuilt/master-yinguang/references/voice.md +3 -3
- package/requirements.txt +2 -0
- package/tools/fojin-known-absent.json +11 -0
- package/tools/verify_sources.py +162 -0
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
{
|
|
10
10
|
"name": "master-skill",
|
|
11
11
|
"description": "FoJin-powered Buddhist AI persona framework — source-grounded, boundary-aware, fidelity-tested, runtime-ready. 15 prebuilt masters across 印度/汉传/藏传/南传 plus compare, debate, and curriculum meta-skills.",
|
|
12
|
-
"version": "0.12.
|
|
12
|
+
"version": "0.12.10",
|
|
13
13
|
"source": "./",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "xr843",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "master-skill",
|
|
3
3
|
"description": "FoJin-powered Buddhist AI persona framework — source-grounded, boundary-aware, fidelity-tested, runtime-ready. 15 prebuilt masters across 印度/汉传/藏传/南传 plus compare, debate, and curriculum meta-skills.",
|
|
4
|
-
"version": "0.12.
|
|
4
|
+
"version": "0.12.10",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "xr843",
|
|
7
7
|
"email": "xr843@users.noreply.github.com"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "master-skill",
|
|
3
3
|
"displayName": "Master Skill",
|
|
4
4
|
"description": "FoJin-powered Buddhist AI persona framework — source-grounded, boundary-aware, fidelity-tested, runtime-ready. 15 prebuilt masters across 印度/汉传/藏传/南传.",
|
|
5
|
-
"version": "0.12.
|
|
5
|
+
"version": "0.12.10",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "xr843",
|
|
8
8
|
"email": "xr843@users.noreply.github.com"
|
package/gemini-extension.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "master-skill",
|
|
3
3
|
"description": "FoJin-powered Buddhist AI persona framework — source-grounded, boundary-aware, fidelity-tested, runtime-ready. 15 prebuilt masters across 印度/汉传/藏传/南传.",
|
|
4
|
-
"version": "0.12.
|
|
4
|
+
"version": "0.12.10",
|
|
5
5
|
"contextFileName": "GEMINI.md"
|
|
6
6
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "master-skill",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "FoJin-powered Buddhist AI persona framework — source-grounded, boundary-aware, fidelity-tested, runtime-ready. 15 pre-built masters across 印度 / 汉传 / 藏传 / 南传, plus /compare-masters, /master-debate, and /master-curriculum.",
|
|
6
6
|
"bin": {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"version": 1,
|
|
8
8
|
"claim_policy": "declared_sources_only",
|
|
9
9
|
"required_for": ["doctrinal_claim", "practice_guidance", "text_interpretation"],
|
|
10
|
-
"allowed_source_types": ["tibetan_canon"],
|
|
10
|
+
"allowed_source_types": ["cbeta", "tibetan_canon"],
|
|
11
11
|
"minimum_claim_coverage": 0.9,
|
|
12
12
|
"live_retrieval_allowed": true
|
|
13
13
|
},
|
|
@@ -30,6 +30,12 @@
|
|
|
30
30
|
"type": "tibetan_canon",
|
|
31
31
|
"id": "BDRC:W1GS56158",
|
|
32
32
|
"title": "密勒日巴尊者传(rNam thar)"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"type": "cbeta",
|
|
36
|
+
"id": "B11n0073",
|
|
37
|
+
"title": "木纳记(尊者传汉译,惹穹多杰札把著、张心若笔录)",
|
|
38
|
+
"note": "CBETA 大藏经补编第 11 册,29 卷;《密勒日巴尊者传》的 1930 年代汉译,含所录道歌。FoJin 未收"
|
|
33
39
|
}
|
|
34
40
|
],
|
|
35
41
|
"version": "1.0.0",
|
|
@@ -20,15 +20,17 @@
|
|
|
20
20
|
|
|
21
21
|
米拉日巴反复在道歌中强调四共加行——暇满难得、人身无常、业果不虚、轮回是苦。这是噶举派一切修持的起点,没有这四者的觉受,谈大手印都是空中楼阁。
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
尊者在戒学歌里说暇满难得:
|
|
24
24
|
|
|
25
|
-
>
|
|
25
|
+
> "人身难得无暇我亦知,思无常罪苦轮回自輭。"
|
|
26
|
+
|
|
27
|
+
> 出处:《木纳记》卷十一(B11n0073);《道歌集》相关诸歌见 BDRC W1KG1252
|
|
26
28
|
|
|
27
29
|
### 2. 出离心与精进闭关
|
|
28
30
|
|
|
29
31
|
噶举派以"实修"为命脉,米拉日巴以亲身雪山闭关树立典范。教导弟子:闻思之后必须实修,实修之根本在于具格上师指导下的长期闭关,不在文字辩论。
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
山居胜于名寺、粗食胜于丰筵,是《道歌集》闭关诸歌反复申说的意思。(转述,非原文;张澄基汉译本有版权,本仓库无法逐字核对。)
|
|
32
34
|
|
|
33
35
|
> 出处:《道歌集·闭关诸歌》主旨(BDRC W1KG1252)
|
|
34
36
|
|
|
@@ -36,9 +38,11 @@
|
|
|
36
38
|
|
|
37
39
|
噶举派被称为"口耳传承"(snyan brgyud),其根本依止上师亲口传授。米拉日巴反复教导:对上师的信心是一切证悟的根本,没有信心则一切教法皆不入心。玛尔巴对其的"折磨"(修建石塔)非懲罚,而是净除黑魔术业障的方便。
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
尊者负母骨离乡时长歌:
|
|
40
42
|
|
|
41
|
-
>
|
|
43
|
+
> "上师不动慈悲口,细译麻把教令勅。故乡有若牢狱鬼,无常如幻上师说,上师恩德最无上。"
|
|
44
|
+
|
|
45
|
+
> 出处:《木纳记》卷十四(B11n0073)
|
|
42
46
|
|
|
43
47
|
### 4. 那洛六法(Naro Chodruk)
|
|
44
48
|
|
|
@@ -54,9 +58,11 @@
|
|
|
54
58
|
|
|
55
59
|
米拉日巴的大手印不重经院辩证,而以道歌直指:
|
|
56
60
|
|
|
57
|
-
|
|
61
|
+
尊者为贡汤施主唱"六个力量"之歌:
|
|
62
|
+
|
|
63
|
+
> "此心犹如虚空遍,照境法身清净明。……无来无去无所住,三世平等一定住。心中无有生死因,本来清净如虚空。白红云堆自开散,四大不断如足迹。"
|
|
58
64
|
|
|
59
|
-
>
|
|
65
|
+
> 出处:《木纳记》卷二十三(B11n0073)
|
|
60
66
|
|
|
61
67
|
### 6. 觉受 (nyams) 与证悟 (rtogs pa) 的区分
|
|
62
68
|
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
米拉日巴尊者以**道歌(mGur)**说法,不立繁复经院术语。语言朴实、带浓厚山野气,善以**自然界譬喻**:雪山喻坚定、风喻无常、岩石喻法身、河流喻心相续。常以**亲身经历**为教材——早年咒杀仇家的罪业、玛尔巴上师的折磨、雪山中以荨麻为食的闭关。语气坦率、不矫饰,对修行人的怠惰直接呵斥,但对真诚求法者极为慈悲。
|
|
28
28
|
|
|
29
29
|
**示例句(皆有典籍出处):**
|
|
30
|
-
1. "
|
|
31
|
-
2. "
|
|
32
|
-
3. "
|
|
30
|
+
1. "人身难得无暇我亦知,思无常罪苦轮回自輭。"(《木纳记》卷十一)
|
|
31
|
+
2. "上师不动慈悲口,细译麻把教令勅。……上师恩德最无上。"(《木纳记》卷十四)
|
|
32
|
+
3. "此心犹如虚空遍,照境法身清净明。……本来清净如虚空。"(《木纳记》卷二十三)
|
|
33
33
|
|
|
34
|
-
>
|
|
34
|
+
> **重要**:以上三句皆为《木纳记》原文(简体转写,省略处用「……」)。大手印见修行果的四分说是后世噶举的归纳,不要写成尊者原话。
|
|
35
35
|
|
|
36
36
|
### 常用比喻
|
|
37
37
|
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
## 说明
|
|
18
18
|
|
|
19
19
|
- 所有片段节选自 BDRC(Buddhist Digital Resource Center)公开元数据所对应的藏文典籍
|
|
20
|
-
-
|
|
20
|
+
- 汉译参考:张澄基《密勒日巴大师全集》(慧炬出版社);CBETA 另收《木纳记》B11n0073(张心若笔录的尊者传汉译,29 卷,含所录道歌,FoJin 未收),人设所引原文据此
|
|
21
21
|
- 英译参考:Garma C. C. Chang, *The Hundred Thousand Songs of Milarepa*; Andrew Quintman, *The Life of Milarepa* (Penguin Classics)
|
|
22
22
|
- 用于教学引用,不代表完整经义;深入研究请查阅 BDRC、84000、Lotsawa House 全本
|
|
23
23
|
- **密法相关章节本目录刻意不收录**(拙火、那洛六法、生起圆满次第具体修法步骤),密法须依止具格上师亲传
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
印光大师以书信体弘法为主,文风恳切直接,文言白话兼用,严厉而不失慈悲。常以长者对晚辈的口吻说法,语重心长,不务虚华。
|
|
27
27
|
|
|
28
28
|
**示例句:**
|
|
29
|
-
1. "
|
|
30
|
-
2. "
|
|
31
|
-
3. "
|
|
29
|
+
1. "愿离娑婆,如狱囚之冀出牢狱。愿生极乐,如穷子之思归故乡。"(正编卷一·与徐福贤女士书)
|
|
30
|
+
2. "念佛之人,当吃长素。如或不能,当持六斋,或十斋。"(续编卷上·答曾怡芝居士四问)
|
|
31
|
+
3. "欲学佛法,先须克己慎独,事事皆从心地中真实做出。"(正编卷一·与丁福保居士书)
|
|
32
32
|
|
|
33
33
|
### 常用比喻
|
|
34
34
|
|
package/requirements.txt
CHANGED
|
@@ -66,6 +66,17 @@
|
|
|
66
66
|
"reason": "大藏经补编(B)与《佛教大藏经》(G)所收的法尊译本不在 FoJin 收录范围内。2026-09-15 以 tools/fojin_bridge.py 直接查证:lookup-cbeta 对 B0067、B0048、B0068、B0029、G2518 以及完整号 B10n0067、G148n2518 均无结果,而同一次请求里的 T0235 正常解析为 text_id=7 —— 是缺这些书,不是接口不通。",
|
|
67
67
|
"verified_absent_on": "2026-09-15",
|
|
68
68
|
"consequence": "master-atisha 引用《菩提道灯论》时拿不到 fojin.app 活链接。离线审计不受影响;周检 3f 的原文取自 CBETA API,照常核对「原典」块。"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"cbeta_id": "B11n0073",
|
|
72
|
+
"short_cbeta_id": "B0073",
|
|
73
|
+
"title": "木纳记(尊者传汉译)",
|
|
74
|
+
"used_by": [
|
|
75
|
+
"master-milarepa"
|
|
76
|
+
],
|
|
77
|
+
"reason": "大藏经补编(B)不在 FoJin 收录范围内。2026-09-16 以 tools/fojin_bridge.py 查证:lookup-cbeta 对 'B0073' 与 'B11n0073' 均无结果,而同一次请求里的 'T0235' 正常解析为 text_id=7 —— 是缺这部书,不是接口不通。",
|
|
78
|
+
"verified_absent_on": "2026-09-16",
|
|
79
|
+
"consequence": "master-milarepa 引用《木纳记》时拿不到 fojin.app 活链接。离线审计不受影响;周检 3f 的原文取自 CBETA API,照常核对引文。"
|
|
69
80
|
}
|
|
70
81
|
]
|
|
71
82
|
}
|
package/tools/verify_sources.py
CHANGED
|
@@ -955,6 +955,152 @@ def classify_bdrc_records(
|
|
|
955
955
|
return mismatched, unknown
|
|
956
956
|
|
|
957
957
|
|
|
958
|
+
CBETA_SEARCH_URL = "https://cbdata.dila.edu.tw/stable/search"
|
|
959
|
+
|
|
960
|
+
# 「当原话呈现」的三种写法:voice.md 的编号示例句、teaching.md 的引用块、
|
|
961
|
+
# 行内带书名号的「云/曰」。模板句(含「……」或「/」选项)、统一拒答话术、
|
|
962
|
+
# 以及人设自己标了「转述/非原文/主旨」的行都不是引文,不收。
|
|
963
|
+
_QUOTE_SAMPLE = re.compile(r'^\s*\d+\.\s*[“"「『]([^”"」』\n]{8,200})')
|
|
964
|
+
_QUOTE_BLOCK = re.compile(r'^\s*>\s*[“"「『]([^”"」』\n]{8,200})')
|
|
965
|
+
_QUOTE_SAID = re.compile(r'(?:云|曰|偈云|经云|论云)\s*[::]?\s*[“"「『]([^”"」』\n]{8,200})')
|
|
966
|
+
_QUOTE_BOILER = re.compile(
|
|
967
|
+
r"具格上师|亲近善知识|不可由文字|网络传授|须依止|本平台|不得对个体|面对面访谈"
|
|
968
|
+
r"|如需深入学习|SuttaCentral|BDRC|fojin"
|
|
969
|
+
)
|
|
970
|
+
_QUOTE_PARAPHRASE = re.compile(r"转述|非原文|主旨|整理|概括|要旨|讲解")
|
|
971
|
+
_QUOTE_HAN = re.compile(r"[\u3400-\u9fff]")
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
def collect_persona_quotes() -> list[tuple[str, str, str]]:
|
|
975
|
+
"""(位置, 祖师目录, 引文):人设 references / sources 里当原话呈现的句子。
|
|
976
|
+
|
|
977
|
+
3f 只看摘录里的「原典」块,而编造的语录恰恰长在别处 —— 2026-09-15 一次手工
|
|
978
|
+
核查在 voice.md 的「示例句」里查出玄奘「因明立量,非为诤胜」、智顗「功在渐次,
|
|
979
|
+
证在圆融」等五条查无出处,还有三条是灌顶、澄观、彭际清的话挂在祖师名下。
|
|
980
|
+
"""
|
|
981
|
+
base = Path(PREBUILT_DIR)
|
|
982
|
+
quotes: list[tuple[str, str, str]] = []
|
|
983
|
+
for meta_path in sorted(base.glob("*/meta.json")):
|
|
984
|
+
master = meta_path.parent.name
|
|
985
|
+
for path in sorted((meta_path.parent / "references").glob("*.md")) + sorted(
|
|
986
|
+
(meta_path.parent / "sources").glob("*.md")
|
|
987
|
+
):
|
|
988
|
+
where_base = path.relative_to(base).as_posix()
|
|
989
|
+
for number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
|
|
990
|
+
if "出处" in line or "引用格式" in line or _QUOTE_PARAPHRASE.search(line):
|
|
991
|
+
continue
|
|
992
|
+
for pattern, needs_title in ((_QUOTE_SAMPLE, False), (_QUOTE_BLOCK, False), (_QUOTE_SAID, True)):
|
|
993
|
+
match = pattern.search(line)
|
|
994
|
+
if not match:
|
|
995
|
+
continue
|
|
996
|
+
if needs_title and "《" not in line:
|
|
997
|
+
break
|
|
998
|
+
quote = match.group(1).split("——", 1)[0]
|
|
999
|
+
if (
|
|
1000
|
+
len(_QUOTE_HAN.findall(quote)) >= 8
|
|
1001
|
+
and "……" not in quote
|
|
1002
|
+
and "/" not in quote
|
|
1003
|
+
and not _QUOTE_BOILER.search(quote)
|
|
1004
|
+
):
|
|
1005
|
+
quotes.append((f"{where_base}:{number}", master, quote))
|
|
1006
|
+
break
|
|
1007
|
+
return quotes
|
|
1008
|
+
|
|
1009
|
+
|
|
1010
|
+
def persona_source_families() -> dict[str, set[str]]:
|
|
1011
|
+
"""{祖师目录: 声明来源的家族集合}。只声明 CBETA 的人设,引文必须在 CBETA 里。"""
|
|
1012
|
+
families: dict[str, set[str]] = {}
|
|
1013
|
+
for meta_path in sorted(Path(PREBUILT_DIR).glob("*/meta.json")):
|
|
1014
|
+
meta = json.loads(meta_path.read_text(encoding="utf-8"))
|
|
1015
|
+
kinds = {str(src.get("type")) for src in meta.get("sources") or [] if src.get("type")}
|
|
1016
|
+
if kinds:
|
|
1017
|
+
families[meta_path.parent.name] = kinds
|
|
1018
|
+
return families
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
def declared_cbeta_works() -> dict[str, list[str]]:
|
|
1022
|
+
"""{祖师目录: [CBETA API 的 work 参数]},取自各 meta.json 声明的 cbeta 来源。"""
|
|
1023
|
+
works: dict[str, list[str]] = {}
|
|
1024
|
+
for meta_path in sorted(Path(PREBUILT_DIR).glob("*/meta.json")):
|
|
1025
|
+
meta = json.loads(meta_path.read_text(encoding="utf-8"))
|
|
1026
|
+
found = {
|
|
1027
|
+
_cbeta_api_work(str(src.get("id")))
|
|
1028
|
+
for src in meta.get("sources") or []
|
|
1029
|
+
if src.get("type") == "cbeta" and _cbeta_api_work(str(src.get("id")))
|
|
1030
|
+
}
|
|
1031
|
+
if found:
|
|
1032
|
+
works[meta_path.parent.name] = sorted(found)
|
|
1033
|
+
return works
|
|
1034
|
+
|
|
1035
|
+
|
|
1036
|
+
# CBETA 全文检索只认繁体:简体「应无所住而生其心」查 0 条,繁体 343 条(2026-09-16
|
|
1037
|
+
# 实测,另试过 lang/variants/simplified 等七种参数,都不会放宽)。opencc 的 `s2t`
|
|
1038
|
+
# 会出「爲」「衆」这类异体,CBETA 用「為」「眾」,照样查不到,所以用 `s2tw` 再补一层
|
|
1039
|
+
# 归一 —— 少了这一层,《坛经》《中论》的真引文都会被报成查无此句。
|
|
1040
|
+
_TRADITIONAL_FIX = str.maketrans({"爲": "為", "衆": "眾", "眞": "真", "僞": "偽"})
|
|
1041
|
+
|
|
1042
|
+
|
|
1043
|
+
def to_traditional(text: str) -> str:
|
|
1044
|
+
import opencc
|
|
1045
|
+
|
|
1046
|
+
for config in ("s2tw", "s2tw.json"):
|
|
1047
|
+
try:
|
|
1048
|
+
return opencc.OpenCC(config).convert(text).translate(_TRADITIONAL_FIX)
|
|
1049
|
+
except Exception: # noqa: BLE001 — 配置名在不同发行包里写法不同
|
|
1050
|
+
continue
|
|
1051
|
+
raise RuntimeError("opencc has no s2tw config")
|
|
1052
|
+
|
|
1053
|
+
|
|
1054
|
+
def cbeta_search_hits(clause: str, work: str | None = None) -> int | None:
|
|
1055
|
+
"""CBETA 全文检索命中数;`work` 限定在一部书里。接口出错返回 None(未知)。"""
|
|
1056
|
+
import urllib.error
|
|
1057
|
+
import urllib.request
|
|
1058
|
+
|
|
1059
|
+
params = {"q": clause, "rows": 1}
|
|
1060
|
+
if work:
|
|
1061
|
+
params["work"] = work
|
|
1062
|
+
url = f"{CBETA_SEARCH_URL}?{urllib.parse.urlencode(params)}"
|
|
1063
|
+
try:
|
|
1064
|
+
with urllib.request.urlopen(url, timeout=30) as response:
|
|
1065
|
+
return json.loads(response.read().decode("utf-8")).get("num_found") or 0
|
|
1066
|
+
except (urllib.error.URLError, OSError, ValueError, TypeError):
|
|
1067
|
+
return None
|
|
1068
|
+
|
|
1069
|
+
|
|
1070
|
+
def classify_persona_quotes(
|
|
1071
|
+
quotes: list[tuple[str, str, str]],
|
|
1072
|
+
families: dict[str, set[str]],
|
|
1073
|
+
works: dict[str, list[str]],
|
|
1074
|
+
search,
|
|
1075
|
+
) -> tuple[list[tuple[str, str, str]], list[tuple[str, str]]]:
|
|
1076
|
+
"""把人设里当原话引的句子分成「CBETA 没有这句」与「比不了」两类。
|
|
1077
|
+
|
|
1078
|
+
判「没有」只对**声明来源全是 CBETA** 的人设成立。master-xuyun 的语录出自
|
|
1079
|
+
《虚云和尚法汇》、master-yinguang 的出自《文钞》,两部都不在 CBETA —— 对它们,
|
|
1080
|
+
查不到只说明这一步够不着,不是伪造。查得到却不在声明作品里的(玄奘引窥基所记
|
|
1081
|
+
的唯识比量、蕅益《要解》在净土十要本),同样记为未知:行文里往往已注明他书。
|
|
1082
|
+
"""
|
|
1083
|
+
mismatched: list[tuple[str, str, str]] = []
|
|
1084
|
+
unknown: list[tuple[str, str]] = []
|
|
1085
|
+
for where, master, quote in quotes:
|
|
1086
|
+
clauses = sorted((c for c in quote_clauses(quote) if len(c) >= 4), key=len, reverse=True)
|
|
1087
|
+
if not clauses:
|
|
1088
|
+
unknown.append((where, "no clause long enough to search"))
|
|
1089
|
+
continue
|
|
1090
|
+
clause = to_traditional(clauses[0])
|
|
1091
|
+
anywhere = search(clause, None)
|
|
1092
|
+
if anywhere is None:
|
|
1093
|
+
unknown.append((where, "CBETA did not answer"))
|
|
1094
|
+
elif not anywhere:
|
|
1095
|
+
if families.get(master) == {"cbeta"}:
|
|
1096
|
+
mismatched.append((where, quote, clause))
|
|
1097
|
+
else:
|
|
1098
|
+
unknown.append((where, f"{master} also declares non-CBETA sources"))
|
|
1099
|
+
elif not any(search(clause, work) for work in works.get(master, [])):
|
|
1100
|
+
unknown.append((where, "only in works this persona does not declare"))
|
|
1101
|
+
return mismatched, unknown
|
|
1102
|
+
|
|
1103
|
+
|
|
958
1104
|
def verify_ids(bridge, cbeta_map: dict[str, list[str]], titles: dict[str, str]) -> dict[str, dict]:
|
|
959
1105
|
"""Verify all CBETA IDs and return {full_cbeta_id: {text_id, short_id, title, ...}}.
|
|
960
1106
|
|
|
@@ -1274,6 +1420,21 @@ def _run_legacy_link_verification(*, fix: bool) -> int:
|
|
|
1274
1420
|
if not bdrc_mismatched and not bdrc_unknown:
|
|
1275
1421
|
print(f" All {len(bdrc_sources)} declared BDRC work ids resolve to a record with the declared title")
|
|
1276
1422
|
|
|
1423
|
+
# Step 3h: 人设里当原话引的句子,CBETA 里有没有(见 classify_persona_quotes)。
|
|
1424
|
+
print("\n[3h/4] Checking quoted lines in persona docs against CBETA...")
|
|
1425
|
+
persona_quotes = collect_persona_quotes()
|
|
1426
|
+
quote_line_mismatched, quote_line_unknown = classify_persona_quotes(
|
|
1427
|
+
persona_quotes, persona_source_families(), declared_cbeta_works(), cbeta_search_hits
|
|
1428
|
+
)
|
|
1429
|
+
for where, quote, clause in quote_line_mismatched:
|
|
1430
|
+
print(f" [WRONG] {where}: CBETA has no 「{clause}」 — 「{quote[:40]}」")
|
|
1431
|
+
if quote_line_unknown:
|
|
1432
|
+
print(f" Could not check {len(quote_line_unknown)} quoted line(s) — unknown, not wrong:")
|
|
1433
|
+
for where, reason in quote_line_unknown:
|
|
1434
|
+
print(f" {where}: {reason}")
|
|
1435
|
+
if not quote_line_mismatched and not quote_line_unknown:
|
|
1436
|
+
print(f" All {len(persona_quotes)} quoted lines are in CBETA, in a work the persona declares")
|
|
1437
|
+
|
|
1277
1438
|
# Step 4: Update URLs
|
|
1278
1439
|
# Build replacement map: full_cbeta_id -> str(internal_text_id)
|
|
1279
1440
|
id_replacement_map: dict[str, str] = {}
|
|
@@ -1321,6 +1482,7 @@ def _run_legacy_link_verification(*, fix: bool) -> int:
|
|
|
1321
1482
|
print(f" Doc citation links to another work: {len(doc_mismatched)}")
|
|
1322
1483
|
print(f" Excerpt quotes not in the cited text: {len(quote_mismatched)}")
|
|
1323
1484
|
print(f" BDRC records that do not match: {len(bdrc_mismatched)}")
|
|
1485
|
+
print(f" Quoted lines CBETA does not have: {len(quote_line_mismatched)}")
|
|
1324
1486
|
if unknown_to_cbeta:
|
|
1325
1487
|
print(f" CBETA unreachable for: {len(unknown_to_cbeta)} (not counted as wrong)")
|
|
1326
1488
|
if dry_run and all_changes:
|