@@ -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+
801896def resolve_obsidian_note_path (
802897 config : dict [str , Any ],
803898 * ,
0 commit comments