初始归档:健康管理资料库(大医网/药膳/体质监测/中医理论等12个资料集,7712个文件)

This commit is contained in:
512song committed 2026-09-23 21:59:25 +08:00
commit 80ae3811cf
7712 files changed
+4628547

No files matched your search

@@ -0,0 +1,180 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
构建《中医入门》应用理论依据库 → 02_加工数据/理论依据库.json
用途: 为体质调理/药膳推荐/处方分析等应用提供可调用的理论依据(原文+行号溯源)
每条依据含: 原文引文 + 行号(指向 02_加工数据/中医入门.md)
"""
import json, os, re, datetime
BOOK = "/home/songyi/Documents/ai_agent_scraper_study/data/中医入门资料库/02_加工数据/中医入门.md"
OUT = "/home/songyi/Documents/ai_agent_scraper_study/data/中医入门资料库/02_加工数据/理论依据库.json"
TODAY = datetime.date.today().isoformat()
lines = open(BOOK, encoding="utf-8").read().splitlines()
text = "\n".join(lines)
def ln(idx): # 字符偏移 → 行号(1-based)
return text[:idx].count("\n") + 1
def find_seg(start, end):
i, j = text.find(start), text.find(end)
return (i, j) if 0 <= i < j else (i, len(text))
lib = {"metadata": {
"生成日期": TODAY,
"来源": "中医入门资料库/02_加工数据/中医入门.md(秦伯未《中医入门》体例,用户定级: 重要理论基础)",
"用途": "应用调用层——体质调理/药膳推荐/处方分析/配伍安全核查时,调用本库返回《中医入门》理论依据(原文+行号)",
"查询接口": "../05_脚本工具/theory_api.py"}}
# ============ 八法 ============
i0, j0 = find_seg("三、八法", "四、常用治法")
bafa = {}
for m in re.finditer(r'^\d+[\.、]\s*([^\s::,。]{1,2}法)[::]\s*(.+)$', text[i0:j0], re.M):
name = m.group(1)
seg_after = text[i0+j0 if False else i0+m.end(): j0]
first = seg_after.split("。")[0]
bafa[name] = {"定义原文": (m.group(2).split("。")[0] + "。").strip() if m.group(2) else "",
"行号": ln(i0 + m.start())}
lib["八法"] = bafa
# ============ 常用治法 72 ============
i0, j0 = find_seg("四、常用治法", "## 第三章")
zhifa, missing = {}, []
for m in re.finditer(r'^(\d+)[\.、,,]?\s*([^\s::,。]{2,12}法)[::]\s*(.*)$', text[i0:j0], re.M):
num, name, rest = int(m.group(1)), m.group(2), m.group(3)
use = re.search(r'用于(.*?)(?:药如|。|$)', rest)
yao = re.search(r'药如([^.。;;]*)', rest)
zhifa[str(num)] = {"名称": name,
"适应症": use.group(1).strip(",,。 ") if use else None,
"药如": ([x.strip() for x in re.split(r'[、,,]', yao.group(1)) if x.strip()] if yao else []),
"行号": ln(i0 + m.start())}
nums = sorted(int(k) for k in zhifa)
gaps = [n for n in range(nums[0], nums[-1]+1) if n not in nums]
lib["治法72"] = {"条目": zhifa, "总数": len(zhifa), "编号范围": [nums[0], nums[-1]],
"缺号": gaps, "缺号说明": "原书编号缺号或OCR漏行,如实记录" if gaps else None}
# ============ 七方 ============
# 切片止于七方段结尾(其后是张景岳八阵与汪昂22类剂型分类,"22. 救急方"属剂型分类,非七方)
i0, j0 = find_seg("二、七方", "三、剂型")
cut = text.find("七方是方剂组成的法则之一", i0)
if 0 < cut < j0: j0 = cut
qifang, cur, cur_pos = {}, None, None
for m in re.finditer(r'^\d+[\.、]\s*([^\s::,。]{1,3})方[::]\s*(.+)$|^(二、七方)', text[i0:j0], re.M):
if m.group(3):
cur = None; continue
cur = m.group(1) + "方"
para = m.group(2) # (.+)$ 已捕获整段(每段一行),无需再找空行
# 清洗举例药名: 剥离连接词前缀("如下法中的大承气汤"→"大承气汤")
examples = []
for tok in re.findall(r'([\u4e00-\u9fff]{2,9}(?:汤|丸|散|饮|膏))', para):
tok = re.split(r'[如用和及等是的中的将]', tok)[-1]
if len(tok) >= 3 and tok not in examples: examples.append(tok)
qifang[cur] = {"定义原文": para[:150].strip(),
"书内举例": examples,
"行号": ln(i0 + m.start())}
lib["七方"] = qifang
# ============ 效能分类 15 ============
i0, j0 = find_seg("二、效能", "#### 1. 扶正类")
xiaoneng = {}
for m in re.finditer(r'^(\d+)[\.、]\s*([^\s::,。]{2,8}药)[::]\s*(.+)$', text[i0:j0], re.M):
seg = m.group(3)
reps = []
for km in re.finditer(r'([\u4e00-\u9fff]{2,8})如([、\u4e00-\u9fff()]{2,60})', seg):
for h in re.split(r'[、]', km.group(2)):
h = re.sub(r'([^)]*)', '', h).strip("等")
if h: reps.append(h)
xiaoneng[m.group(2)] = {"定义原文": seg.split("。")[0] + "。", "代表药": reps[:24],
"行号": ln(i0 + m.start())}
lib["效能分类"] = xiaoneng
# ============ 扶正/祛邪功效药组 ============
groups = []
def parse_groups(start, end, kind):
i0, j0 = find_seg(start, end)
cur_sys = ""
for m in re.finditer(r'^\((\d+)\)\s*属于(.+?):|^[^\n#\d(].*?——.*$', text[i0:j0], re.M):
line = m.group(0).strip()
if m.group(1):
cur_sys = m.group(2); continue
eff, _, rhs = line.partition("——")
label = f"{kind}·{cur_sys}·{eff.strip()}" if cur_sys else f"{kind}·{eff.strip()}"
herbs = []
for h in re.split(r'[、,,]', rhs.strip().strip("。")):
h = re.sub(r'([^)]*)', '', h).strip()
h = re.sub(r'^(亦可用鲜|亦可用)', '', h).strip()
if h: herbs.append(h)
groups.append({"组": label, "药": herbs, "行号": ln(i0 + m.start())})
parse_groups("#### 1. 扶正类", "#### 2. 祛邪类", "扶正类")
parse_groups("#### 2. 祛邪类", "三、归经", "祛邪类")
lib["功效药组"] = {"条目": groups, "总数": len(groups)}
# ============ 示例方 40 ============
i0, j0 = find_seg("一、基本方剂", "## 第四章")
shili = {}
for m in re.finditer(r'^(\d+)[\.、]\s*([^::\n]+)[::]\s*(.+)$', text[i0:j0], re.M):
name = m.group(2).strip()
rest = m.group(3)
comp = [c.strip() for c in rest.split("。")[0].split("、") if c.strip()]
pos = re.search(r'为([^,。]{2,12})主方', rest)
zhi = re.search(r'用于([^。]*)', rest)
shili[name] = {"序号": int(m.group(1)), "组成_书": comp,
"主方定位": pos.group(1) if pos else None,
"主治": zhi.group(1).strip() if zhi else None,
"行号": ln(i0 + m.start())}
lib["示例方40"] = shili
# ============ 二十八脉主症 ============
i0, j0 = find_seg("二十八脉的主症", "## 第三章")
mai = {}
for m in re.finditer(r'^([^\s,。]{1,2})脉主(.+)$', text[i0:j0], re.M):
mai[m.group(1) + "脉"] = {"主症原文": m.group(2).strip().rstrip("。"), "行号": ln(i0 + m.start())}
lib["脉象主症"] = {"条目": mai, "总数": len(mai),
"说明": "标题为二十八脉,实列27条,长脉仅见总括韵文(原书编排,如实记录)"}
# ============ 用药安全 ============
ph_i = text.find("一、配合"); ph_j = text.find("二、用量")
ph = text[ph_i:ph_j]
i18 = ph.find("十八反歌:")
seg18_prose = ph[ph.find("歌中所提十八种药"):ph.find("歌中所提十九种药")]
seg19_prose = ph[ph.find("歌中所提十九种药"):ph.find("此外,妊娠禁忌")]
# 畏对提取: 从"如硫黄畏朴硝"起取,避开首句"即表示相畏比较显著"的噪音
iex = seg19_prose.find("如硫黄畏朴硝")
pairs = [(a.replace("如", "", 1) if a.startswith("如") else a, b)
for a, b in re.findall(r'([\u4e00-\u9fff]{1,4})畏([\u4e00-\u9fff]{1,5})', seg19_prose[iex:])
if len(a) >= 2 and len(b) >= 2]
for extra in [("川乌", "犀角"), ("草乌", "犀角")]: # "川乌、草乌畏犀角"正则只捕草乌,补川乌
if extra not in pairs: pairs.append(extra)
i19 = ph.find("十九畏歌:")
seg_preg = ph[ph.find("此外,妊娠禁忌"):ph.find("经验告诉我们")]
safety = {"十八反": {
"相反组": [
{"组": ["乌头"], "组_通行扩展": ["川乌", "草乌", "附子"],
"反": ["半夏", "瓜蒌", "贝母", "白蔹", "白及"], "反_通行扩展": [],
"说明": "书列'乌头';附子为乌头子根,通行本明列附子反半夏/瓜蒌等,安全核查按通行扩展执行"},
{"组": ["甘草"], "组_通行扩展": [], "反": ["海藻", "大戟", "甘遂", "芫花"], "反_通行扩展": [],
"说明": None},
{"组": ["藜芦"], "组_通行扩展": [],
"反": ["人参", "沙参", "细辛", "芍药"], "反_通行扩展": ["丹参", "玄参", "苦参"],
"说明": "书列'诸参'散文明列人参、沙参;通行本扩展丹参/玄参/苦参"}],
"歌诀原文": re.search(r'十八反歌:[^\n]+', ph).group(0),
"散文原文": seg18_prose.strip().split("\n")[0],
"行号": ln(ph_i + i18),
"通行扩展说明": "歌诀'诸参辛芍反藜芦',通行本藜芦组扩展含丹参/玄参/苦参;乌头组扩展含附子。扩展项在核查结果中标注[通行扩展]"},
"十九畏": {
"畏对": pairs,
"散文原文": seg19_prose.strip().split("\n")[0],
"行号": ln(ph_i + i19)},
"妊娠禁忌": {"药": [], "原文摘录": seg_preg.strip()[:400], "行号": ln(ph_i + ph.find("此外,妊娠禁忌"))}}
for m in re.finditer(r'(植物|动物|矿物)药如([^;。]+)', seg_preg):
for x in re.split(r'[、]', m.group(2)):
x = x.strip()
if 2 <= len(x) <= 6: safety["妊娠禁忌"]["药"].append(x)
lib["用药安全"] = safety
# ============ 输出 ============
json.dump(lib, open(OUT, "w", encoding="utf-8"), ensure_ascii=False, indent=1)
print("理论依据库:", OUT, f"({os.path.getsize(OUT)} bytes)")
print(f"八法 {len(bafa)} | 治法 {len(zhifa)}(缺号{gaps}) | 七方 {len(qifang)} | 效能 {len(xiaoneng)} | "
f"药组 {len(groups)} | 示例方 {len(shili)} | 脉 {len(mai)} | 十八反组3 | 十九畏 {len(safety['十九畏']['畏对'])}对 | 妊娠禁忌 {len(safety['妊娠禁忌']['药'])}味")
@@ -0,0 +1,233 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
中医入门资料库 03_关联融合(反向标注模式)
方向: 大医网实体(方剂1000/药材1000) → 中医入门理论锚点(用户定级: 重要理论基础)
理论锚点:
药材侧: 气味效能15分类 + 扶正7系38条/祛邪18条功效药组 + 十八反/十九畏/妊娠禁忌
方剂侧: 七方(7类) + 剂型(丸散膏丹等) + 40首示例方 + 八法/72治法提及方例
匹配: 每个大医网药材查询自己在书侧理论锚点中的出现位置(正名/别名/炮制归一)
每个大医网方剂查询: 书侧40示例方同名 + 组成药味触发的功效组归属 + 名称含"汤散丸膏"剂型线索
输出: 03_关联融合/大医网-中医入门_理论锚点反向标注.json
"""
import json, os, re, datetime, collections
BASE = "/home/songyi/Documents/ai_agent_scraper_study/data"
DY = os.path.join(BASE, "大医网/01_来源数据")
BOOK = os.path.join(BASE, "中医入门资料库/02_加工数据/中医入门.md")
OUTDIR = os.path.join(BASE, "中医入门资料库/03_关联融合")
TODAY = datetime.date.today().isoformat()
# 炮制前缀(候选侧可安全剥离);产地/品质限定词(土/川/广/云/怀/杭/紫/白等)不剥——
# "土人参/土白术"等是独立民间药名,剥离会造成假阳性(白木耳式陷阱的镜像)
PREP_PREFIXES = sorted(["生晒","炙","煨","煅","焦","炒","制","飞","清","法","霜","酒",
"醋","盐","蜜","麸","鲜","干","姜","蒸","煮","泡","燀"], key=len, reverse=True)
BOOK_ALIAS = { # 书侧简写 → 药典正名
"山萸":"山茱萸","萸肉":"山茱萸","麻仁":"火麻仁","云苓":"茯苓","赤苓":"茯苓",
"云茯神":"茯神","银花":"金银花","杭菊花":"菊花","花粉":"天花粉","寸冬":"麦冬",
"生地":"生地黄","熟地":"熟地黄","鲜生地":"生地黄","肉果":"肉豆蔻","煨肉果":"肉豆蔻",
"淡苁蓉":"肉苁蓉","吴萸":"吴茱萸","川连":"黄连","潼沙苑":"沙苑子","仙灵脾":"淫羊藿",
"旱莲草":"墨旱莲","丹皮":"牡丹皮","茅根":"白茅根","鲜茅根":"白茅根","象贝母":"浙贝母",
"象贝":"浙贝母","川贝":"川贝母","紫河车":"紫河车","白蒺藜":"刺蒺藜","菟丝饼":"菟丝子",
"桑椹子":"桑椹","使君肉":"使君子","山查炭":"山楂","焦麦芽":"麦芽","炒莱菔子":"莱菔子",
"驴皮胶":"阿胶","龟版":"龟板","菟蓉":"肉苁蓉","金铃子":"川楝子","延胡":"延胡索",
"参三七":"三七","全当归":"当归","当归身":"当归","冬虫草":"冬虫夏草","蝉衣":"蝉蜕",
"瓜蒌仁":"瓜蒌子","瓜蒌":"瓜蒌","益智仁":"益智仁","左牡蛎":"牡蛎","生石决":"石决明",
"羚羊尖":"羚羊角","炙全蝎":"全蝎","炒谷芽":"谷芽","春砂仁":"砂仁","白蔻仁":"豆蔻",
"御米壳":"罂粟壳","煨葛根":"葛根","炙升麻":"升麻","软柴胡":"柴胡","煨草果":"草果",
"炒苡仁":"薏苡仁","饭赤豆":"赤小豆","白藓皮":"白鲜皮","炙兜铃":"马兜铃","淡豆豉":"淡豆豉",
"黑山栀":"栀子","炒竹茹":"竹茹","炙桑皮":"桑白皮","甜杏仁":"甜杏仁","苦杏仁":"苦杏仁",
"炒牛蒡":"牛蒡子","苦桔梗":"桔梗","石草":"石韦","淡菓蓉":"肉苁蓉","复盆子":"覆盆子",
"菖蒲":"石菖蒲","萹蓄草":"萹蓄","海金砂":"海金沙","杜仲":"杜仲","虎骨":"虎骨",
}
def norm(t): # 仅剥炮制前缀 + 别名归一(不剥产地/品质限定词)
c = t.strip()
for p in PREP_PREFIXES:
if c.startswith(p) and len(c) > len(p)+1:
c = c[len(p):]; break
return BOOK_ALIAS.get(c, c)
text = open(BOOK, encoding="utf-8").read()
# ============ 构建书侧理论锚点 ============
# A. 药材理论锚点: 效能15分类 + 扶正/祛邪功效药组 + 用药安全
anchors_herb = collections.defaultdict(lambda: {"效能分类": [], "功效药组": [], "用药安全": []})
def add(anchor, herb_raw, kind, label):
n = norm(herb_raw)
if len(n) >= 2: anchors_herb[n][kind].append(label)
if n != herb_raw and len(herb_raw) >= 2: # 炮制品/原名也留痕
anchors_herb[herb_raw][kind].append(label + "(原词)")
# A1. 效能15分类
xn = text[text.find("二、效能"):text.find("#### 1. 扶正类")]
for cat_m in re.finditer(r'^(\d+)\.\s*([^\s::,。]{2,8}药):(.+)$', xn, re.M):
cat = cat_m.group(2)
for m in re.finditer(r'([\u4e00-\u9fff、]{2,24})如([、\u4e00-\u9fff()a-zA-Z0-9]{2,60})', cat_m.group(3)):
for h in re.split(r'[、]', m.group(2)):
h = re.sub(r'([^)]*)', '', h).strip("等")
if h: add(None, h, "效能分类", cat)
# A2. 扶正/祛邪功效药组
def parse_groups(start, end, kind):
seg = text[text.find(start):text.find(end)]
cur_sys = ""
for line in seg.splitlines():
s = line.strip()
m_sys = re.match(r'^\((\d+)\)\s*属于(.+?):', s)
if m_sys: cur_sys = m_sys.group(2); continue
if not re.match(r'^[^#\d(].*——', s): continue
eff, _, rhs = s.partition("——")
label = f"{kind}·{eff.strip()}"
if cur_sys: label = f"{kind}·{cur_sys}·{eff.strip()}"
for h in re.split(r'[、,,]', rhs.strip().strip("。")):
h = re.sub(r'([^)]*)', '', h).strip()
h = re.sub(r'^(亦可用鲜|亦可用)', '', h).strip()
if h: add(None, h, "功效药组", label)
parse_groups("#### 1. 扶正类", "#### 2. 祛邪类", "扶正类")
parse_groups("#### 2. 祛邪类", "三、归经", "祛邪类")
# A3. 用药安全: 十八反/十九畏/妊娠禁忌
ph = text[text.find("一、配合"):text.find("二、用量")]
i = ph.find("歌中所提十八种药")
seg18 = ph[i:ph.find("歌中所提十九种药")] # 不能用"十九畏歌"作边界——开篇引言句已含该词
for h in ["半夏","瓜蒌","贝母","白蔹","白及","海藻","大戟","甘遂","芫花","甘草","人参","沙参","细辛","芍药","藜芦","乌头"]:
if h in seg18: anchors_herb[h]["用药安全"].append("十八反")
i = ph.find("歌中所提十九种药")
seg19 = ph[i:ph.find("此外,妊娠禁忌")]
for pair in re.findall(r'([\u4e00-\u9fff]{1,4})畏([\u4e00-\u9fff]{1,5})', seg19):
for h in pair:
if len(h) >= 2:
anchors_herb[h]["用药安全"].append("十九畏")
if "川乌" in seg19: # "川乌、草乌畏犀角"正则只捕到草乌,补川乌
anchors_herb["川乌"]["用药安全"].append("十九畏")
segpreg = ph[ph.find("此外,妊娠禁忌"):ph.find("经验告诉我们")]
for h in re.findall(r'如([^;。]{2,200}?)[;。]', segpreg):
for x in re.split(r'[、]', h):
x = x.strip()
if 2 <= len(x) <= 5: anchors_herb[x]["用药安全"].append("妊娠禁忌")
# B. 方剂理论锚点: 七方/剂型/40示例方/治法方例
anchors_fang = {"七方举例": {}, "示例方": {}, "剂型": "丸散膏丹酒汤等"}
qf = text[text.find("二、七方"):text.find("三、剂型")]
cur = None
for para in re.split(r'\n\n+', qf):
m = re.match(r'^\d+[\.、]\s*([^\s::,。]{1,3})方', para.strip())
if m: cur = m.group(1) + "方"
elif cur:
for f in re.findall(r'([\u4e00-\u9fff]{2,8}(?:汤|丸|散|饮|膏))', para):
anchors_fang["七方举例"].setdefault(f, []).append(cur)
sec = text[text.find("一、基本方剂"):text.find("二、处方举例")]
for n, name, rest in re.findall(r'^(\d+)\.\s*([^::\n]+)[::]\s*(.+)$', sec, re.M):
anchors_fang["示例方"][name.strip()] = {
"序号": int(n), "组成_书": [c.strip() for c in rest.split("。")[0].split("、") if c.strip()],
"定位": (re.search(r'为([^,。]{2,12})主方', rest).group(1) if re.search(r'为([^,。]{2,12})主方', rest) else None),
"主治": (rest.split("。")[1].replace("用于","").strip() if len(re.findall(r'。', rest))>1 and "用于" in rest else None)}
# 72治法中的方药举例 → 治法标签(终点用"## 第三章",因"## 第一节"在第一章也出现)
zf = text[text.find("四、常用治法"):text.find("## 第三章")]
zhifa_herb = collections.defaultdict(list)
for n, name, rest in re.findall(r'^(\d+)\.\s*([^\s::,。]{2,12}法):?(.*)$', zf, re.M):
m = re.search(r'药如([、\u4e00-\u9fff()]{2,50})', rest)
if m:
for h in re.split(r'[、]', m.group(1)):
h = re.sub(r'([^)]*)','',h).strip()
if h: zhifa_herb[norm(h)].append(f"治法{n}.{name}")
print(f"理论锚点: 归一药材 {len(anchors_herb)} 个 | 示例方 {len(anchors_fang['示例方'])} | 七方举例 {len(anchors_fang['七方举例'])} | 治法关联药 {len(zhifa_herb)}")
# ============ 大医网药材 → 理论锚点 ============
yao_out = []
for fn in sorted(os.listdir(os.path.join(DY, "中药材"))):
d = json.load(open(os.path.join(DY, "中药材", fn), encoding="utf-8"))
nm = (d.get("名称") or "").strip()
cands = {nm}
for a in re.split(r'[、,,;;]', d.get("别名") or ""):
a = a.strip()
if len(a) >= 2: cands.add(a)
hits = []
for c in list(cands):
cn = norm(c)
for key in {c, cn}:
if key in anchors_herb:
v = anchors_herb[key]
kinds = [k for k in ("效能分类","功效药组","用药安全") if v[k]]
hits.append({"命中名": key, "书侧原词": list({c} | ({cn} if cn!=c else set())) and (c if key==c else cn),
"类型": kinds,
"效能分类": v["效能分类"], "功效药组": v["功效药组"][:8],
"用药安全": v["用药安全"],
"治法关联": zhifa_herb.get(key, [])[:6]})
seen, uniq = set(), []
for h in hits:
sig = h["命中名"]
if sig not in seen: seen.add(sig); uniq.append(h)
yao_out.append({
"大医网药材": nm, "id": fn.split("_")[0],
"别名": [a for a in cands if a != nm][:12],
"理论锚点": uniq,
"锚点类型": sorted({k for h in uniq for k in h["类型"]}),
"关联强度": len(uniq)})
covered = sum(1 for y in yao_out if y["理论锚点"])
print(f"药材反向标注: {covered}/1000 有理论锚点 ({covered//10}%)")
# ============ 大医网方剂 → 理论锚点 ============
fang_out = []
for fn in sorted(os.listdir(os.path.join(DY, "方剂"))):
d = json.load(open(os.path.join(DY, "方剂", fn), encoding="utf-8"))
nm = (d.get("名称") or "").strip()
nm_clean = re.sub(r'[《》].*$', '', nm).strip() # 去出处后缀
rec = {"大医网方剂": nm, "id": fn.split("_")[0], "理论锚点": []}
# B1 40示例方同名
if nm_clean in anchors_fang["示例方"]:
s = anchors_fang["示例方"][nm_clean]
rec["理论锚点"].append({"类型": ["示例方"], "示例方": nm_clean, "书侧序号": s["序号"],
"书侧组成": s["组成_书"], "主方定位": s["定位"], "主治": s["主治"]})
# B2 七方举例
if nm_clean in anchors_fang["七方举例"]:
rec["理论锚点"].append({"类型": ["七方举例"], "归属": anchors_fang["七方举例"][nm_clean]})
# B3 剂型线索(名称后缀 → 剂型理论)
m = re.search(r'(丸|散|膏|丹|酒|汤|饮)', nm)
if m:
rec["理论锚点"].append({"类型": ["剂型理论"], "剂型": m.group(1),
"理论": "丸散膏丹酒汤等剂型各具性质效用(中医入门·方剂之部·剂型)"})
# B4 组成药味 → 功效药组/治法推断(方解-组成仅5%填充,填充者才可算)
comp = d.get("方解-组成")
if comp:
herhs = [norm(x) for x in re.split(r'[、,,]', str(comp)) if x.strip()]
groups = collections.Counter()
zhifa = []
for h in herhs:
if h in anchors_herb:
for g in anchors_herb[h]["功效药组"]: groups[g] += 1
zhifa += zhifa_herb.get(h, [])
if groups:
rec["理论锚点"].append({"类型": ["功效药组推断"], "top功效组": [g for g,_ in groups.most_common(5)],
"组内命中": sum(1 for h in herhs if h in anchors_herb), "组成总数": len(herhs)})
if zhifa:
rec["理论锚点"].append({"类型": ["治法关联"], "治法": sorted(set(zhifa))[:8]})
rec["锚点类型"] = sorted({t for a in rec["理论锚点"] for t in a["类型"]})
rec["关联强度"] = len(rec["理论锚点"])
fang_out.append(rec)
n_any = sum(1 for f in fang_out if f["理论锚点"])
n_shili = sum(1 for f in fang_out if "示例方" in f["锚点类型"])
n_qifang = sum(1 for f in fang_out if "七方举例" in f["锚点类型"])
n_subst = sum(1 for f in fang_out if set(f["锚点类型"]) - {"剂型理论"})
n_only_jx = sum(1 for f in fang_out if f["锚点类型"] == ["剂型理论"])
print(f"方剂反向标注: {n_any}/1000 有锚点 | 实质锚点(示例方/七方/功效组/治法) {n_subst} | 仅剂型线索 {n_only_jx} | 示例方同名 {n_shili} | 七方举例 {n_qifang}")
# ============ 输出 ============
result = {
"metadata": {
"timestamp": TODAY,
"方向": "大医网实体 → 中医入门理论锚点(反向标注;中医入门为理论依据层,用户定级: 重要理论基础)",
"source_theory": "中医入门资料库/02_加工数据/中医入门.md(秦伯未《中医入门》体例)",
"source_entity": "大医网/01_来源数据/{方剂1000, 中药材1000}(随机抽样库)",
"锚点体系": {"药材": ["效能15分类", "扶正7系+祛邪功效药组", "十八反/十九畏/妊娠禁忌", "72治法药例"],
"方剂": ["40首示例方(含主方定位)", "七方举例", "剂型理论", "组成→功效组/治法推断"]},
"说明": "大医网为随机抽样1000,书侧示例方未在其中属正常(理论层不依赖抽样覆盖);药材锚点覆盖率反映抽样命中理论药组的比例",
"stats": {"药材有锚点": covered, "药材总数": len(yao_out),
"方剂有锚点": n_any, "方剂实质锚点": n_subst, "方剂仅剂型": n_only_jx,
"方剂示例方同名": n_shili, "方剂七方举例": n_qifang}},
"药材反向标注": yao_out,
"方剂反向标注": fang_out,
}
out = os.path.join(OUTDIR, "大医网-中医入门_理论锚点反向标注.json")
json.dump(result, open(out, "w", encoding="utf-8"), ensure_ascii=False, indent=1)
print("\n输出:", out, f"({os.path.getsize(out)} bytes)")
@@ -0,0 +1,215 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
《中医入门》理论依据查询接口(应用调用层)
用法:
from theory_api import TCMTheory
t = TCMTheory()
t.herb("黄芪") # 药材理论依据: 效能分类/功效药组/治法/用药安全
t.formula("大承气汤") # 方剂理论依据: 主方定位/主治/七方归属/组成药组
t.safety_check(["甘草","大戟"]) # 配伍安全核查: 十八反/十九畏/妊娠禁忌
t.method("补中益气") # 治法查询(名称模糊)
t.constitution("气虚") # 体质调理依据(按气虚/阳虚/阴虚等关键词聚合)
依据均含 行号(02_加工数据/中医入门.md) 可人工溯源。
"""
import json, os, re
LIB = os.path.join(os.path.dirname(__file__), "../02_加工数据/理论依据库.json")
BOOK = os.path.join(os.path.dirname(__file__), "../02_加工数据/中医入门.md")
# 书侧简写 → 正名(与 reverse_tag 脚本一致的归一)
BOOK_ALIAS = {"山萸":"山茱萸","萸肉":"山茱萸","麻仁":"火麻仁","云苓":"茯苓","赤苓":"茯苓",
"云茯神":"茯神","银花":"金银花","杭菊花":"菊花","花粉":"天花粉","寸冬":"麦冬",
"生地":"生地黄","熟地":"熟地黄","鲜生地":"生地黄","肉果":"肉豆蔻","淡苁蓉":"肉苁蓉",
"吴萸":"吴茱萸","川连":"黄连","潼沙苑":"沙苑子","仙灵脾":"淫羊藿","旱莲草":"墨旱莲",
"丹皮":"牡丹皮","茅根":"白茅根","象贝母":"浙贝母","川贝":"川贝母","白蒺藜":"刺蒺藜",
"菟丝饼":"菟丝子","桑椹子":"桑椹","使君肉":"使君子","山查炭":"山楂","焦麦芽":"麦芽",
"炒莱菔子":"莱菔子","驴皮胶":"阿胶","龟版":"龟板","菟蓉":"肉苁蓉","金铃子":"川楝子",
"延胡":"延胡索","参三七":"三七","全当归":"当归","当归身":"当归","冬虫草":"冬虫夏草",
"蝉衣":"蝉蜕","瓜蒌仁":"瓜蒌子","左牡蛎":"牡蛎","生石决":"石决明","羚羊尖":"羚羊角",
"炙全蝎":"全蝎","炒谷芽":"谷芽","春砂仁":"砂仁","白蔻仁":"豆蔻","御米壳":"罂粟壳",
"煨葛根":"葛根","炙升麻":"升麻","软柴胡":"柴胡","煨草果":"草果","炒苡仁":"薏苡仁",
"饭赤豆":"赤小豆","白藓皮":"白鲜皮","炙兜铃":"马兜铃","黑山栀":"栀子","炒竹茹":"竹茹",
"炙桑皮":"桑白皮","炒牛蒡":"牛蒡子","苦桔梗":"桔梗","石草":"石韦","淡菓蓉":"肉苁蓉",
"复盆子":"覆盆子","菖蒲":"石菖蒲","萹蓄草":"萹蓄","海金砂":"海金沙",}
PREP_PREFIX = ["生晒","炙","煨","煅","焦","炒","制","飞","清","法","霜","酒","醋","盐",
"蜜","麸","鲜","干","姜","蒸","煮"] # 只剥炮制前缀;产地品质限定词不剥(土人参陷阱)
def _norm(name: str):
c = name.strip()
for p in PREP_PREFIX:
if c.startswith(p) and len(c) > len(p) + 1:
c = c[len(p):]; break
return BOOK_ALIAS.get(c, c)
class TCMTheory:
def __init__(self):
self.lib = json.load(open(LIB, encoding="utf-8"))
self.book_lines = open(BOOK, encoding="utf-8").read().splitlines()
# 索引: 药名(正名+别名+炮制品) → 锚点
self.herb_index = {}
for cat, info in self.lib["效能分类"].items():
for h in info["代表药"]:
self.herb_index.setdefault(_norm(h), []).append({"通道": "效能分类", "值": cat, "行号": info["行号"]})
for g in self.lib["功效药组"]["条目"]:
for h in g["药"]:
self.herb_index.setdefault(_norm(h), []).append({"通道": "功效药组", "值": g["组"], "行号": g["行号"]})
for num, z in self.lib["治法72"]["条目"].items():
for h in z["药如"]:
self.herb_index.setdefault(_norm(h), []).append({"通道": "治法", "值": f"治法{num}.{z['名称']}", "行号": z["行号"]})
self.safety_index = {}
for grp in self.lib["用药安全"]["十八反"]["相反组"]:
for a in grp["组"]:
self.safety_index.setdefault(a, []).append(("十八反", f'与{"、".join(grp["反"])}相反', "书列"))
for a in grp.get("组_通行扩展", []):
self.safety_index.setdefault(a, []).append(("十八反", f'与{"、".join(grp["反"])}相反', "通行扩展"))
for h in grp["反"]:
who = "、".join(grp["组"] + grp.get("组_通行扩展", []))
self.safety_index.setdefault(h, []).append(("十八反", f"反{who}", "书列"))
for h in grp.get("反_通行扩展", []):
who = "、".join(grp["组"] + grp.get("组_通行扩展", []))
self.safety_index.setdefault(h, []).append(("十八反", f"反{who}", "通行扩展"))
for a, b in self.lib["用药安全"]["十九畏"]["畏对"]:
self.safety_index.setdefault(a, []).append(("十九畏", f"畏{b}", "书列"))
self.safety_index.setdefault(b, []).append(("十九畏", f"为{a}所畏", "书列"))
for h in self.lib["用药安全"]["妊娠禁忌"]["药"]:
self.safety_index.setdefault(h, []).append(("妊娠禁忌", "妊娠禁忌药", "书列"))
def _quote(self, line_no, width=60):
if 1 <= line_no <= len(self.book_lines):
return self.book_lines[line_no - 1][:width]
return None
# ---------- 药材理论依据 ----------
def herb(self, name: str):
n = _norm(name)
hits = self.herb_index.get(n, []) or self.herb_index.get(name.strip(), [])
if not hits:
return {"药材": name, "结果": "《中医入门》理论药组未收录(该书为入门纲要,未收不等于无理论依据)"}
out = {"药材": name, "正名": n, "依据": []}
seen = set()
for h in hits:
key = (h["通道"], h["值"])
if key in seen: continue
seen.add(key)
out["依据"].append({**h, "原文": self._quote(h["行号"])})
out["用药安全"] = self._safety_of(n)
return out
# ---------- 方剂理论依据 ----------
def formula(self, name: str):
s = self.lib["示例方40"].get(name.strip())
if not s:
# 组成含书侧示例方药的模糊建议
return {"方剂": name, "结果": "非书侧40示例方;可按组成逐味调用 herb() 获取理论依据",
"剂型理论": self._jixing_hint(name)}
out = {"方剂": name, "主方定位": s["主方定位"], "主治": s["主治"],
"组成_书": s["组成_书"], "行号": s["行号"], "原文": self._quote(s["行号"], 120),
"七方归属": [q for q, v in self.lib["七方"].items() if name in v["书内举例"]] or None,
"组成理论": []}
safety_all = []
for h in s["组成_书"]:
hb = self.herb(h)
ch = [f'{d["通道"]}:{d["值"]}' for d in hb.get("依据", [])][:3]
out["组成理论"].append({"药": h, "依据": ch})
if hb.get("用药安全"): safety_all.append({h: hb["用药安全"]})
if safety_all: out["组成用药安全"] = safety_all
return out
# ---------- 配伍安全核查 ----------
def safety_check(self, herbs):
herbs = [h.strip() for h in herbs if h.strip()]
conflicts, warnings = [], []
normalized = {h: _norm(h) for h in herbs}
names = list(normalized.items())
# 十八反: 组内对立(含通行扩展,结果标注来源)
for grp in self.lib["用药安全"]["十八反"]["相反组"]:
side_a = {(h, n) for h, n in names
if n in grp["组"] or n in grp.get("组_通行扩展", [])}
side_b = {(h, n) for h, n in names
if n in grp["反"] or n in grp.get("反_通行扩展", [])}
if side_a and side_b:
src = "书列"
a_ext = any(n in grp.get("组_通行扩展", []) for _, n in side_a)
b_ext = any(n in grp.get("反_通行扩展", []) for _, n in side_b)
if a_ext or b_ext: src = "通行扩展"
conflicts.append({"类型": "十八反", "来源": src,
"组": sorted(h for h, _ in side_a), "反": sorted(h for h, _ in side_b),
"行号": self.lib["用药安全"]["十八反"]["行号"]})
# 十九畏: 组内畏对
pairset = {frozenset(p) for p in self.lib["用药安全"]["十九畏"]["畏对"]}
for i in range(len(names)):
for j in range(i + 1, len(names)):
if frozenset([names[i][1], names[j][1]]) in pairset:
warnings.append({"类型": "十九畏", "对": [names[i][0], names[j][0]],
"行号": self.lib["用药安全"]["十九畏"]["行号"]})
# 妊娠禁忌
preg = [h for h, n in names
if n in self.lib["用药安全"]["妊娠禁忌"]["药"] or h in self.lib["用药安全"]["妊娠禁忌"]["药"]]
return {"核查药味": herbs, "十八反冲突": conflicts or None,
"十九畏警式": warnings or None,
"妊娠禁忌药": preg or None,
"结论": "存在配伍禁忌,须调整" if conflicts else
("有相畏提示,慎用并权衡" if warnings else
("含妊娠禁忌药,孕妇须避" if preg else "书侧十八反/十九畏/妊娠禁忌未命中"))}
# ---------- 治法 ----------
def method(self, keyword: str):
out = []
for num, z in self.lib["治法72"]["条目"].items():
hay = z["名称"] + (z["适应症"] or "")
if keyword in z["名称"] or keyword in hay or z["名称"][:2] in keyword:
out.append({"治法": f"{num}.{z['名称']}", "适应症": z["适应症"],
"药如": z["药如"], "行号": z["行号"],
"原文": self._quote(z["行号"])})
for bn, bv in self.lib["八法"].items():
if keyword in bn or keyword in bv["定义原文"] or bn[0] in keyword:
out.append({"八法": bn, "定义": bv["定义原文"], "行号": bv["行号"]})
return {"关键词": keyword, "命中": out or None}
# ---------- 体质调理依据 ----------
CONSTITUTION_SYNONYMS = {
"气虚": ["补气", "中气"], "血虚": ["补血", "养血"], "阴虚": ["补阴", "滋阴", "养阴"],
"阳虚": ["补阳", "助阳", "温阳", "扶阳"], "痰湿": ["化湿", "化痰", "淡渗"],
"湿热": ["清湿热", "化湿", "利湿"], "血瘀": ["活血", "破瘀"], "气滞": ["行气", "破气", "疏肝"],
"寒": ["温中", "祛寒", "散寒", "扶阳"], "热": ["清热", "泻火", "凉血"], "食积": ["消食", "消导"]}
def constitution(self, keyword: str):
"""按 气虚/血虚/阴虚/阳虚/痰湿/湿热/血瘀/气滞/寒/热/食积 等关键词聚合理论依据"""
syns = self.CONSTITUTION_SYNONYMS.get(keyword, [keyword])
res = {"关键词": keyword, "同义词展开": syns, "补益药组": [], "治法": [], "八法": []}
for g in self.lib["功效药组"]["条目"]:
if any(s in g["组"] for s in syns):
res["补益药组"].append({"组": g["组"], "药": g["药"], "行号": g["行号"]})
for num, z in self.lib["治法72"]["条目"].items():
hay = z["名称"] + (z["适应症"] or "")
if any(s in hay for s in syns):
res["治法"].append({"治法": f"{num}.{z['名称']}", "药如": z["药如"], "行号": z["行号"]})
for bn, bv in self.lib["八法"].items():
if any(s in bv["定义原文"] for s in syns):
res["八法"].append({"八法": bn, "行号": bv["行号"]})
res["提示"] = "示例方与药组的完整组成请调用 formula()/herb() 逐层获取" if (res["补益药组"] or res["治法"]) else None
return res
# ---------- 内部 ----------
def _safety_of(self, n):
recs = self.safety_index.get(n, [])
out = {}
for typ, desc, src in recs:
out.setdefault(typ, []).append(f"{desc}[{src}]" if src != "书列" else desc)
return out or None
def _jixing_hint(self, name):
m = re.search(r'(丸|散|膏|丹|酒|汤|饮)', name)
if not m: return None
jx = m.group(1)
return {"剂型": jx, "说明": "丸散膏丹等剂型各具性质效用(方剂之部·剂型)"}
if __name__ == "__main__":
t = TCMTheory()
print("== herb 黄芪 =="); print(json.dumps(t.herb("黄芪"), ensure_ascii=False, indent=1)[:600])
print("\n== formula 大承气汤 =="); print(json.dumps(t.formula("大承气汤"), ensure_ascii=False, indent=1)[:700])
print("\n== safety 甘草+大戟 =="); print(json.dumps(t.safety_check(["甘草", "大戟", "茯苓"]), ensure_ascii=False, indent=1))
print("\n== method 补中 =="); print(json.dumps(t.method("补中"), ensure_ascii=False, indent=1)[:400])
print("\n== constitution 气虚 =="); print(json.dumps(t.constitution("气虚"), ensure_ascii=False, indent=1)[:500])