Skip to content

Commit fadd721

Browse files
committed
Add domain-aware asset routing
1 parent 183522e commit fadd721

2 files changed

Lines changed: 111 additions & 2 deletions

File tree

scripts/common.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,101 @@ def resolve_note_output_mode(config: dict[str, Any]) -> tuple[str, Path]:
798798
return ("workspace", workspace_root / output_dir)
799799

800800

801+
DOMAIN_RULES: list[tuple[str, tuple[str, ...]]] = [
802+
("心理健康", ("mental health", "depression", "anxiety", "psychiatric", "psychology", "clinical", "patient", "counsel", "therapy")),
803+
("大模型", ("large language model", "llm", "foundation model", "gpt", "transformer", "instruction tuning", "pretrain", "pre-training", "language model", "agent", "reasoning")),
804+
("多模态", ("multimodal", "vision-language", "audio-visual", "video-language", "image-text", "cross-modal")),
805+
("计算机视觉", ("computer vision", "image classification", "object detection", "segmentation", "vision transformer", "visual recognition")),
806+
("强化学习", ("reinforcement learning", "policy optimization", "bandit", "markov decision process", "rl")),
807+
("语音", ("speech", "asr", "automatic speech recognition", "text-to-speech", "speaker recognition", "audio")),
808+
("推荐系统", ("recommendation", "recommender", "ctr prediction", "ranking system")),
809+
("机器人", ("robot", "robotics", "manipulation", "navigation", "control policy")),
810+
("图学习", ("graph neural network", "graph learning", "molecular graph", "gnn")),
811+
("机器学习", ("machine learning", "deep learning", "neural network", "representation learning")),
812+
]
813+
814+
815+
def infer_domain_label(title: str, abstract: str = "") -> str:
816+
lower = normalize_whitespace(f"{title} {abstract}").lower()
817+
scored: list[tuple[int, str]] = []
818+
for label, keywords in DOMAIN_RULES:
819+
score = sum(1 for keyword in keywords if keyword in lower)
820+
if score > 0:
821+
scored.append((score, label))
822+
if scored:
823+
scored.sort(key=lambda item: (-item[0], item[1]))
824+
return scored[0][1]
825+
paper_type, _ = infer_paper_type(title, abstract)
826+
if paper_type == "clinical_or_psychology_empirical":
827+
return "心理健康"
828+
if paper_type == "AI_method":
829+
return "机器学习"
830+
return "未分类"
831+
832+
833+
def is_probable_paper_folder(path: Path) -> bool:
834+
if not path.is_dir():
835+
return False
836+
marker = path / f"{path.name}.md"
837+
return marker.exists()
838+
839+
840+
def existing_domain_dirs(config: dict[str, Any]) -> list[str]:
841+
output_mode, root_path = resolve_note_output_mode(config)
842+
papers_dir = str(config.get("papers_dir", "20_Research/Papers")).strip() or "20_Research/Papers"
843+
base_dir = root_path / Path(papers_dir) if output_mode == "obsidian" else root_path
844+
if not base_dir.exists() or not base_dir.is_dir():
845+
return []
846+
names: list[str] = []
847+
for child in sorted(base_dir.iterdir()):
848+
if not child.is_dir():
849+
continue
850+
if is_probable_paper_folder(child):
851+
continue
852+
names.append(child.name)
853+
return names
854+
855+
856+
def domain_name_score(domain_name: str, label: str, title: str, abstract: str) -> int:
857+
name = domain_name.strip().lower()
858+
score = 0
859+
if name == label.lower():
860+
score += 100
861+
lower = normalize_whitespace(f"{title} {abstract}").lower()
862+
for rule_label, keywords in DOMAIN_RULES:
863+
if rule_label.lower() != name:
864+
continue
865+
score += sum(10 for keyword in keywords if keyword in lower)
866+
if name in lower:
867+
score += 15
868+
aliases = {
869+
"大模型": ("llm", "large language model", "language model", "transformer", "agent", "multimodal"),
870+
"心理健康": ("depression", "anxiety", "mental health", "clinical", "patient", "therapy"),
871+
}
872+
for canonical, terms in aliases.items():
873+
if canonical.lower() == name:
874+
score += sum(4 for term in terms if term in lower)
875+
return score
876+
877+
878+
def resolve_domain_subdir(config: dict[str, Any], *, title: str, abstract: str = "", subdir: str = "") -> str:
879+
if subdir.strip():
880+
return subdir.strip()
881+
label = infer_domain_label(title, abstract)
882+
existing = existing_domain_dirs(config)
883+
if existing:
884+
best_name = ""
885+
best_score = -1
886+
for domain_name in existing:
887+
score = domain_name_score(domain_name, label, title, abstract)
888+
if score > best_score:
889+
best_name = domain_name
890+
best_score = score
891+
if best_name and best_score > 0:
892+
return best_name
893+
return label
894+
895+
801896
def resolve_obsidian_note_path(
802897
config: dict[str, Any],
803898
*,

scripts/materialize_figure_asset.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,14 @@
77
import shutil
88
from pathlib import Path
99

10-
from common import emit, maybe_load_json_record, resolve_note_output_mode, resolve_obsidian_note_path, runtime_config
10+
from common import (
11+
emit,
12+
maybe_load_json_record,
13+
resolve_domain_subdir,
14+
resolve_note_output_mode,
15+
resolve_obsidian_note_path,
16+
runtime_config,
17+
)
1118

1219

1320
def parser() -> argparse.ArgumentParser:
@@ -34,11 +41,17 @@ def main() -> None:
3441
config = runtime_config()
3542
if args.vault:
3643
config["obsidian_vault"] = args.vault
44+
resolved_subdir = resolve_domain_subdir(
45+
config,
46+
title=title,
47+
abstract=str(record.get("abstract", "")),
48+
subdir=args.subdir,
49+
)
3750

3851
note_path = resolve_obsidian_note_path(
3952
config,
4053
title=title,
41-
subdir=args.subdir,
54+
subdir=resolved_subdir,
4255
filename=args.filename,
4356
)
4457
source_image = Path(args.source_image).expanduser().resolve()
@@ -66,6 +79,7 @@ def main() -> None:
6679
"relative_markdown_embed": relative_markdown_embed,
6780
"label": args.label,
6881
"output_mode": output_mode,
82+
"subdir": resolved_subdir,
6983
}
7084

7185
if output_mode == "obsidian":

0 commit comments

Comments
 (0)