| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- # -*- coding: utf-8 -*-
- """问题解析与意图识别:将口语化问题转为结构化查询与类型标签
- 基于 jieba 分词 + 初中化学领域词库 + 规则进行轻量级解析,
- 为后续知识图谱检索与大模型提示词提供结构化信息。
- """
- from enum import Enum
- from typing import Any, Dict, List
- try:
- import jieba
- except ImportError: # 运行环境若未安装 jieba,则退化为纯规则模式
- jieba = None # type: ignore
- class QuestionType(str, Enum):
- """问题类型枚举:概念理解、实验操作、习题求解等"""
- CONCEPT = "concept"
- EXPERIMENT = "experiment"
- EXERCISE = "exercise"
- EQUATION = "equation" # 方程式书写/配平/离子方程式
- ION_COEXIST = "ion_coexist" # 离子共存/能否大量共存
- OTHER = "other"
- # 初中化学核心主题词与其关联的知识图谱扩展检索词
- _TOPIC_KG_EXPANSIONS: Dict[str, List[str]] = {
- # 复分解反应条件示例:扩展到沉淀/气体/水、离子共存规则、溶解性表等
- "复分解反应": [
- "复分解反应",
- "复分解反应条件",
- "生成沉淀",
- "生成气体",
- "生成水",
- "离子共存规则",
- "酸碱盐溶解性表",
- "离子反应",
- ],
- "离子反应": [
- "离子反应",
- "生成沉淀",
- "生成气体",
- "生成水",
- "离子共存规则",
- "酸碱盐溶解性表",
- ],
- "氧化还原反应": [
- "氧化还原反应",
- "化合价变化",
- "得电子",
- "失电子",
- "氧化剂",
- "还原剂",
- ],
- "中和反应": [
- "中和反应",
- "酸碱反应",
- "放热",
- "盐和水",
- "酸碱指示剂",
- "滴定",
- ],
- "酸碱指示剂": [
- "酸碱指示剂",
- "酚酞",
- "石蕊",
- "甲基橙",
- "指示剂变色范围",
- ],
- "物质的量": [
- "物质的量",
- "摩尔",
- "摩尔质量",
- "阿伏伽德罗常数",
- "气体摩尔体积",
- ],
- "溶液": [
- "溶液",
- "溶质",
- "溶剂",
- "质量分数",
- "溶解度",
- "饱和溶液",
- ],
- # 氧气及其制取与性质
- "氧气": [
- "氧气",
- "实验室制氧气",
- "高锰酸钾制氧气",
- "制取氧气的化学方程式",
- "氧气的物理性质",
- "氧气的化学性质",
- "氧气的制备和性质实验",
- "燃烧与灭火",
- ],
- # 二氧化碳及相关实验
- "二氧化碳": [
- "二氧化碳",
- "实验室制二氧化碳",
- "二氧化碳的物理性质",
- "二氧化碳的化学性质",
- "二氧化碳的制备和性质实验",
- "澄清石灰水",
- ],
- # 氧化还原的易错点补充:学生常说“氧化还原”而不是完整说“氧化还原反应”
- "氧化还原": [
- "氧化还原反应",
- "氧化还原",
- "化合价变化",
- "氧化剂",
- "还原剂",
- ],
- # 中和相关的常见混淆点
- "中和": [
- "中和反应",
- "酸碱中和",
- "酸碱指示剂",
- "盐和水",
- ],
- # 金属活动性顺序与置换反应
- "金属活动性顺序": [
- "金属活动性顺序",
- "置换反应",
- "金属与酸反应",
- "金属与盐溶液反应",
- ],
- # 气体制备与收集方法
- "气体制备与收集": [
- "实验室制氧气",
- "实验室制二氧化碳",
- "排水集气法",
- "向上排空气法",
- "向下排空气法",
- "气体的干燥",
- ],
- # 酸碱盐整体主题
- "酸碱盐": [
- "酸",
- "碱",
- "盐",
- "酸碱中和",
- "酸碱指示剂",
- "中和反应",
- ],
- }
- # 初中化学专业词汇词库(仅做识别,不直接决定题型)
- _DOMAIN_TERMS: Dict[str, str] = {
- # 物质构成
- "原子": "structure",
- "分子": "structure",
- "离子": "structure",
- "元素": "structure",
- "原子核": "structure",
- "核外电子": "structure",
- "氧气": "substance",
- "空气": "substance",
- # 氧化还原
- "氧化还原反应": "redox",
- "氧化剂": "redox",
- "还原剂": "redox",
- "化合价": "redox",
- "得电子": "redox",
- "失电子": "redox",
- "氧化还原": "redox",
- "氧化反应": "redox",
- "还原反应": "redox",
- # 电解质、电离
- "电解质": "electrolyte",
- "弱电解质": "electrolyte",
- "强电解质": "electrolyte",
- "电离": "electrolyte",
- "离子反应": "electrolyte",
- # 溶液
- "溶液": "solution",
- "溶质": "solution",
- "溶剂": "solution",
- "溶解度": "solution",
- "饱和溶液": "solution",
- "不饱和溶液": "solution",
- "质量分数": "solution",
- # 酸碱盐与指示剂
- "酸": "acid_base",
- "碱": "acid_base",
- "盐": "acid_base",
- "中和反应": "acid_base",
- "酸碱指示剂": "acid_base",
- "酚酞": "acid_base",
- "石蕊": "acid_base",
- "甲基橙": "acid_base",
- "中和": "acid_base",
- "酸碱中和": "acid_base",
- "酸性": "acid_base",
- "碱性": "acid_base",
- "pH": "acid_base",
- # 物质的量与计算
- "物质的量": "calc",
- "摩尔": "calc",
- "摩尔质量": "calc",
- "阿伏伽德罗常数": "calc",
- "气体摩尔体积": "calc",
- "溶液配制": "calc",
- # 常见实验与装置
- "高锰酸钾": "experiment",
- "高锰酸钾制氧气": "experiment",
- "实验室制氧气": "experiment",
- "实验室制二氧化碳": "experiment",
- "检验氧气": "experiment",
- "检验二氧化碳": "experiment",
- "酒精灯": "experiment",
- "量筒": "experiment",
- "滴定管": "experiment",
- "排水集气法": "experiment",
- "向上排空气法": "experiment",
- "向下排空气法": "experiment",
- "集气瓶": "experiment",
- "试管": "experiment",
- "烧杯": "experiment",
- "锥形瓶": "experiment",
- "坩埚": "experiment",
- "蒸发皿": "experiment",
- "漏斗": "experiment",
- "滤纸": "experiment",
- "玻璃棒": "experiment",
- "集气": "experiment",
- "过滤": "experiment",
- "蒸发": "experiment",
- "蒸馏": "experiment",
- # 反应类型与典型情景
- "化合反应": "reaction_type",
- "分解反应": "reaction_type",
- "置换反应": "reaction_type",
- "复分解反应": "reaction_type",
- "燃烧": "reaction_type",
- # 常见物质与实验现象
- "二氧化碳": "substance",
- "氢气": "substance",
- "氮气": "substance",
- "水": "substance",
- "石灰水": "substance",
- "澄清石灰水": "substance",
- # 典型初中化学实验情景
- "镁条燃烧": "experiment",
- "镁条在空气中燃烧": "experiment",
- "铁丝在氧气中燃烧": "experiment",
- "铁钉生锈": "experiment",
- "水的电解": "experiment",
- "锌和盐酸反应": "experiment",
- "锌粒与盐酸反应": "experiment",
- }
- _EXPERIMENT_KEYWORDS = [
- "实验",
- "装置",
- "步骤",
- "操作",
- "需要注意",
- "注意事项",
- "现象",
- "为什么要",
- "为啥要",
- "为啥要塞",
- "怎么做",
- "如何操作",
- "收集气体",
- "收集方式",
- "排水集气法",
- "向上排空气法",
- "向下排空气法",
- "装配",
- "组装",
- "点燃",
- "熄灭",
- "实验室制氧气",
- "实验室制二氧化碳",
- "检验氧气",
- "检验二氧化碳",
- ]
- _EXERCISE_KEYWORDS = [
- "求",
- "计算",
- "质量",
- "体积",
- "浓度",
- "配制",
- "生成多少",
- "生成的气体",
- "生成的沉淀",
- "x 的值",
- "已知",
- ]
- _CONCEPT_KEYWORDS = [
- "是什么",
- "定义",
- "本质",
- "特征",
- "条件",
- "意义",
- "原因",
- "道理",
- ]
- def _guess_question_type(q: str) -> QuestionType:
- """根据关键词粗略区分问题类型"""
- # 先识别方程式/离子共存类型(你选择先只打标签,求解交给大模型)
- if "化学方程式" in q or "离子方程式" in q or "配平" in q:
- return QuestionType.EQUATION
- if "能否大量共存" in q or "能否共存" in q or "是否能共存" in q:
- return QuestionType.ION_COEXIST
- for kw in _EXPERIMENT_KEYWORDS:
- if kw in q:
- return QuestionType.EXPERIMENT
- for kw in _EXERCISE_KEYWORDS:
- if kw in q:
- return QuestionType.EXERCISE
- for kw in _CONCEPT_KEYWORDS:
- if kw in q:
- return QuestionType.CONCEPT
- # 若包含“为什么/为啥”等多为概念或原理说明
- if "为什么" in q or "为啥" in q:
- return QuestionType.CONCEPT
- return QuestionType.OTHER
- def _extract_topic_keywords(q: str) -> List[str]:
- """基于规则提取与初中化学相关的核心主题/关键词
- 这里采用简单的子串匹配与规则扩展,后续可以接入分词与专业词库替换。
- """
- keywords: List[str] = []
- # 1) 使用 jieba 分词(若可用)从分词结果中匹配专业词汇
- if jieba is not None:
- for w in jieba.lcut(q):
- if w in _DOMAIN_TERMS and w not in keywords:
- keywords.append(w)
- # 2) 针对复分解反应等重点考点做显式主题识别(子串匹配兜底)
- if "复分解反应" in q and "复分解反应" not in keywords:
- keywords.append("复分解反应")
- if "离子反应" in q and "离子反应" not in keywords:
- keywords.append("离子反应")
- if "高锰酸钾" in q and "高锰酸钾" not in keywords:
- keywords.append("高锰酸钾")
- if "高锰酸钾制氧气" not in keywords:
- keywords.append("高锰酸钾制氧气")
- if "电解质" in q and "电解质" not in keywords:
- keywords.append("电解质")
- if "氧化还原反应" in q and "氧化还原反应" not in keywords:
- keywords.append("氧化还原反应")
- if "氧化还原" in q and "氧化还原反应" not in keywords:
- keywords.append("氧化还原反应")
- if "中和反应" in q and "中和反应" not in keywords:
- keywords.append("中和反应")
- if "中和" in q and "中和反应" not in keywords:
- keywords.append("中和反应")
- if ("物质的量" in q or "摩尔" in q or "mol" in q) and "物质的量" not in keywords:
- keywords.append("物质的量")
- if "酸碱指示剂" in q and "酸碱指示剂" not in keywords:
- keywords.append("酸碱指示剂")
- if ("溶液" in q or "溶解度" in q or "饱和溶液" in q) and "溶液" not in keywords:
- keywords.append("溶液")
- if "氧气" in q and "氧气" not in keywords:
- keywords.append("氧气")
- if "制氧气" in q and "实验室制氧气" not in keywords:
- keywords.append("实验室制氧气")
- if "氧气" not in keywords:
- keywords.append("氧气")
- if "二氧化碳" in q and "二氧化碳" not in keywords:
- keywords.append("二氧化碳")
- if "制二氧化碳" in q and "实验室制二氧化碳" not in keywords:
- keywords.append("实验室制二氧化碳")
- if "二氧化碳" not in keywords:
- keywords.append("二氧化碳")
- if "金属活动性顺序" in q and "金属活动性顺序" not in keywords:
- keywords.append("金属活动性顺序")
- if "空气" in q and "空气" not in keywords:
- keywords.append("空气")
- # 若未识别到任何主题,则用原始问题作为检索关键词
- if not keywords:
- keywords.append(q)
- # 去重保持顺序
- seen = set()
- deduped: List[str] = []
- for k in keywords:
- if k not in seen:
- seen.add(k)
- deduped.append(k)
- return deduped
- def _expand_kg_terms(base_keywords: List[str]) -> List[str]:
- """根据主题配置扩展知识图谱检索词"""
- terms: List[str] = []
- for kw in base_keywords:
- expanded = _TOPIC_KG_EXPANSIONS.get(kw, [kw])
- for t in expanded:
- if t not in terms:
- terms.append(t)
- return terms
- def analyze_question(text: str) -> Dict[str, Any]:
- """将学生的自然语言问题解析为结构化信息
- 返回内容示例:
- {
- "clean_text": "为啥高锰酸钾制氧要塞棉花",
- "type": "experiment",
- "keywords": ["高锰酸钾", "高锰酸钾制氧气"],
- "kg_terms": ["高锰酸钾制氧气", "高锰酸钾"]
- }
- """
- q = (text or "").strip()
- q = q.replace("?", "?").replace("!", "!").replace("。", ".")
- q_type = _guess_question_type(q)
- base_keywords = _extract_topic_keywords(q)
- kg_terms = _expand_kg_terms(base_keywords)
- return {
- "clean_text": q,
- "type": q_type.value,
- "keywords": base_keywords,
- "kg_terms": kg_terms,
- }
|