classifier.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. # agent/classifier.py
  2. import re
  3. from typing import Dict, Any, List, Optional
  4. from dataclasses import dataclass
  5. @dataclass
  6. class ClassificationResult:
  7. question_type: str
  8. confidence: float
  9. reason: str
  10. method: str
  11. class QuestionClassifier:
  12. """
  13. 增强版问题分类器:基于高速公路服务区业务上下文的智能分类
  14. """
  15. def __init__(self):
  16. # 从配置文件加载阈值参数
  17. try:
  18. from agent.config import get_current_config, get_nested_config
  19. config = get_current_config()
  20. self.high_confidence_threshold = get_nested_config(config, "classification.high_confidence_threshold", 0.7)
  21. self.low_confidence_threshold = get_nested_config(config, "classification.low_confidence_threshold", 0.4)
  22. self.max_confidence = get_nested_config(config, "classification.max_confidence", 0.9)
  23. self.base_confidence = get_nested_config(config, "classification.base_confidence", 0.4)
  24. self.confidence_increment = get_nested_config(config, "classification.confidence_increment", 0.08)
  25. self.llm_fallback_confidence = get_nested_config(config, "classification.llm_fallback_confidence", 0.5)
  26. self.uncertain_confidence = get_nested_config(config, "classification.uncertain_confidence", 0.2)
  27. print("[CLASSIFIER] 从配置文件加载分类器参数完成")
  28. except ImportError:
  29. self.high_confidence_threshold = 0.7
  30. self.low_confidence_threshold = 0.4
  31. self.max_confidence = 0.9
  32. self.base_confidence = 0.4
  33. self.confidence_increment = 0.08
  34. self.llm_fallback_confidence = 0.5
  35. self.uncertain_confidence = 0.2
  36. print("[CLASSIFIER] 配置文件不可用,使用默认分类器参数")
  37. # 基于高速公路服务区业务的精准关键词
  38. self.strong_business_keywords = {
  39. "核心业务实体": [
  40. "服务区", "档口", "商铺", "收费站", "高速公路",
  41. "驿美", "驿购", # 业务系统名称
  42. "北区", "南区", "西区", "东区", "两区", # 物理分区
  43. "停车区", "公司", "管理公司", "运营公司", "驿美运营公司" # 公司相关
  44. ],
  45. "支付业务": [
  46. "微信支付", "支付宝支付", "现金支付", "行吧支付", "金豆支付",
  47. "支付金额", "订单数量", "营业额", "收入", "营业收入",
  48. "微信", "支付宝", "现金", "行吧", "金豆", # 简化形式
  49. "wx", "zfb", "rmb", "xs", "jd" # 系统字段名
  50. ],
  51. "经营品类": [
  52. "餐饮", "小吃", "便利店", "整体租赁",
  53. "驿美餐饮", "品牌", "经营品类", "商业品类"
  54. ],
  55. "车流业务": [
  56. "车流量", "车辆数量", "客车", "货车",
  57. "过境", "危化品", "城际", "车辆统计",
  58. "流量统计", "车型分布"
  59. ],
  60. "地理路线": [
  61. "大广", "昌金", "昌栗", "线路", "路段", "路线",
  62. "高速线路", "公路线路"
  63. ],
  64. "系统查询指示词": [
  65. "当前系统", "当前数据库", "当前数据", "数据库"
  66. "本系统", "系统", "数据库中", "数据中",
  67. "现有数据", "已有数据", "存储的数据",
  68. "平台数据", "我们的数据库", "这个系统"
  69. ]
  70. }
  71. # 查询意图词(辅助判断)
  72. self.query_intent_keywords = [
  73. "统计", "查询", "分析", "排行", "排名",
  74. "报表", "报告", "汇总", "计算", "对比",
  75. "趋势", "占比", "百分比", "比例",
  76. "最大", "最小", "最高", "最低", "平均",
  77. "总计", "合计", "累计", "求和", "求平均",
  78. "生成", "导出", "显示", "列出", "共有"
  79. ]
  80. # 非业务实体词(包含则倾向CHAT)
  81. self.non_business_keywords = [
  82. # 农产品/食物
  83. "荔枝", "苹果", "西瓜", "水果", "蔬菜", "大米", "小麦",
  84. "橙子", "香蕉", "葡萄", "草莓", "樱桃", "桃子", "梨",
  85. # 技术概念
  86. "人工智能", "机器学习", "编程", "算法", "深度学习",
  87. "AI", "神经网络", "模型训练", "数据挖掘",
  88. # 身份询问
  89. "你是谁", "你是什么", "你叫什么", "你的名字", "你是什么AI",
  90. "什么模型", "大模型", "AI助手", "助手", "机器人",
  91. # 天气相关
  92. "天气", "气温", "下雨", "晴天", "阴天", "温度",
  93. "天气预报", "气候", "降雨", "雪天",
  94. # 其他生活常识
  95. "怎么做饭", "如何减肥", "健康", "医疗", "病症",
  96. "历史", "地理", "文学", "电影", "音乐", "体育",
  97. "娱乐", "游戏", "小说", "新闻", "政治", "战争",
  98. "足球", "NBA", "篮球", "乒乓球", "冠军", "夺冠",
  99. "高考",
  100. # 旅游出行
  101. "旅游","景点","门票","酒店","机票","航班","高铁","的士",
  102. #情绪
  103. "伤心","开心","无聊","生气","孤独","累了","烦恼","心情","难过","抑郁",
  104. #商业
  105. "股票","基金","理财","投资","经济","通货膨胀","上市",
  106. #哲学
  107. "人生意义","价值观","道德","信仰","宗教","爱情",
  108. #地理
  109. "全球","全国","亚洲","发展中","欧洲","美洲","东亚","东南亚","南美","非洲","大洋"
  110. ]
  111. # SQL关键词(技术层面的数据库操作)
  112. # business_score +3
  113. self.sql_patterns = [
  114. r"\b(select|from|where|group by|order by|having|join|update)\b",
  115. r"\b(数据库|表名|表|字段名|SQL|sql|database|table)\b"
  116. ]
  117. # 聊天关键词(平台功能和帮助)
  118. self.chat_keywords = [
  119. "你好啊", "谢谢", "再见", "怎么样", "如何", "为什么", "什么是",
  120. "介绍", "解释", "说明", "帮助", "操作", "使用方法", "功能",
  121. "教程", "指南", "手册","讲解"
  122. ]
  123. # 追问关键词(用于检测追问型问题)
  124. self.follow_up_keywords = [
  125. "还有", "详细", "具体", "更多", "继续", "再", "也",
  126. "那么", "另外", "其他", "以及", "还", "进一步",
  127. "深入", "补充", "额外", "此外", "同时", "并且"
  128. ]
  129. # 话题切换关键词(明显的话题转换)
  130. self.topic_switch_keywords = [
  131. "你好", "你是", "介绍", "功能", "帮助", "使用方法",
  132. "平台", "系统", "AI", "助手", "谢谢", "再见"
  133. ]
  134. def classify(self, question: str, context_type: Optional[str] = None, routing_mode: Optional[str] = None) -> ClassificationResult:
  135. """
  136. 主分类方法:支持渐进式分类策略
  137. Args:
  138. question: 当前问题
  139. context_type: 上下文类型 ("DATABASE" 或 "CHAT"),可选
  140. routing_mode: 路由模式,可选,用于覆盖配置文件设置
  141. """
  142. # 确定使用的路由模式
  143. if routing_mode:
  144. QUESTION_ROUTING_MODE = routing_mode
  145. print(f"[CLASSIFIER] 使用传入的路由模式: {QUESTION_ROUTING_MODE}")
  146. else:
  147. try:
  148. from app_config import QUESTION_ROUTING_MODE
  149. print(f"[CLASSIFIER] 使用配置文件路由模式: {QUESTION_ROUTING_MODE}")
  150. except ImportError:
  151. QUESTION_ROUTING_MODE = "hybrid"
  152. print(f"[CLASSIFIER] 配置导入失败,使用默认路由模式: {QUESTION_ROUTING_MODE}")
  153. # 根据路由模式选择分类策略
  154. if QUESTION_ROUTING_MODE == "database_direct":
  155. return ClassificationResult(
  156. question_type="DATABASE",
  157. confidence=1.0,
  158. reason="配置为直接数据库查询模式",
  159. method="direct_database"
  160. )
  161. elif QUESTION_ROUTING_MODE == "chat_direct":
  162. return ClassificationResult(
  163. question_type="CHAT",
  164. confidence=1.0,
  165. reason="配置为直接聊天模式",
  166. method="direct_chat"
  167. )
  168. elif QUESTION_ROUTING_MODE == "llm_only":
  169. return self._enhanced_llm_classify(question)
  170. else:
  171. # hybrid模式:使用渐进式分类策略
  172. return self._progressive_classify(question, context_type)
  173. def _progressive_classify(self, question: str, context_type: Optional[str] = None) -> ClassificationResult:
  174. """
  175. 渐进式分类策略:
  176. 1. 首先只基于问题本身分类
  177. 2. 如果置信度不够且有上下文,考虑上下文辅助
  178. 3. 检测话题切换,避免错误继承
  179. """
  180. print(f"[CLASSIFIER] 渐进式分类 - 问题: {question}")
  181. if context_type:
  182. print(f"[CLASSIFIER] 上下文类型: {context_type}")
  183. # 第一步:只基于问题本身分类
  184. primary_result = self._hybrid_classify(question)
  185. print(f"[CLASSIFIER] 主分类结果: {primary_result.question_type}, 置信度: {primary_result.confidence}")
  186. # 如果没有上下文,直接返回主分类结果
  187. if not context_type:
  188. print(f"[CLASSIFIER] 无上下文,使用主分类结果")
  189. return primary_result
  190. # 如果置信度足够高,直接使用主分类结果
  191. if primary_result.confidence >= self.high_confidence_threshold:
  192. print(f"[CLASSIFIER] 高置信度({primary_result.confidence}≥{self.high_confidence_threshold}),使用主分类结果")
  193. return primary_result
  194. # 检测明显的话题切换
  195. if self._is_topic_switch(question):
  196. print(f"[CLASSIFIER] 检测到话题切换,忽略上下文")
  197. return primary_result
  198. # 如果置信度较低,考虑上下文辅助
  199. if primary_result.confidence < self.medium_confidence_threshold:
  200. print(f"[CLASSIFIER] 低置信度({primary_result.confidence}<{self.medium_confidence_threshold}),考虑上下文辅助")
  201. # 检测是否为追问型问题
  202. if self._is_follow_up_question(question):
  203. print(f"[CLASSIFIER] 检测到追问型问题,继承上下文类型: {context_type}")
  204. return ClassificationResult(
  205. question_type=context_type,
  206. confidence=0.75, # 给予中等置信度
  207. reason=f"追问型问题,继承上下文类型。原分类: {primary_result.reason}",
  208. method="progressive_context_inherit"
  209. )
  210. # 中等置信度或其他情况,保持主分类结果
  211. print(f"[CLASSIFIER] 保持主分类结果")
  212. return primary_result
  213. def _is_follow_up_question(self, question: str) -> bool:
  214. """检测是否为追问型问题"""
  215. question_lower = question.lower()
  216. # 检查追问关键词
  217. for keyword in self.follow_up_keywords:
  218. if keyword in question_lower:
  219. return True
  220. # 检查问号开头的短问题(通常是追问)
  221. if question.strip().startswith(('还', '再', '那', '这', '有')) and len(question.strip()) < 15:
  222. return True
  223. return False
  224. def _is_topic_switch(self, question: str) -> bool:
  225. """检测是否为明显的话题切换"""
  226. question_lower = question.lower()
  227. # 检查话题切换关键词
  228. for keyword in self.topic_switch_keywords:
  229. if keyword in question_lower:
  230. return True
  231. # 检查问候语模式
  232. greeting_patterns = [
  233. r"^(你好|您好|hi|hello)",
  234. r"(你是|您是).*(什么|谁|哪)",
  235. r"(介绍|说明).*(功能|平台|系统)"
  236. ]
  237. for pattern in greeting_patterns:
  238. if re.search(pattern, question_lower):
  239. return True
  240. return False
  241. def _hybrid_classify(self, question: str) -> ClassificationResult:
  242. """
  243. 混合分类模式:规则预筛选 + 增强LLM分类
  244. 这是原来的 classify 方法逻辑
  245. """
  246. # 第一步:规则预筛选
  247. rule_result = self._rule_based_classify(question)
  248. # 如果规则分类有高置信度,直接使用
  249. if rule_result.confidence >= self.high_confidence_threshold:
  250. return rule_result
  251. # 第二步:使用增强的LLM分类
  252. llm_result = self._enhanced_llm_classify(question)
  253. # 选择置信度更高的结果
  254. if llm_result.confidence > rule_result.confidence:
  255. return llm_result
  256. else:
  257. return rule_result
  258. def _rule_based_classify(self, question: str) -> ClassificationResult:
  259. """基于规则的预分类"""
  260. question_lower = question.lower()
  261. # 检查非业务实体词
  262. non_business_matched = []
  263. for keyword in self.non_business_keywords:
  264. if keyword in question_lower:
  265. non_business_matched.append(keyword)
  266. # 如果包含非业务实体词,直接分类为CHAT
  267. if non_business_matched:
  268. return ClassificationResult(
  269. question_type="CHAT",
  270. confidence=0.85,
  271. reason=f"包含非业务实体词: {non_business_matched}",
  272. method="rule_based_non_business"
  273. )
  274. # 检查强业务关键词
  275. business_score = 0
  276. business_matched = []
  277. for category, keywords in self.strong_business_keywords.items():
  278. if category == "系统查询指示词": # 系统指示词单独处理
  279. continue
  280. for keyword in keywords:
  281. if keyword in question_lower:
  282. business_score += 2 # 业务实体词权重更高
  283. business_matched.append(f"{category}:{keyword}")
  284. # 检查系统查询指示词
  285. system_indicator_score = 0
  286. system_matched = []
  287. for keyword in self.strong_business_keywords.get("系统查询指示词", []):
  288. if keyword in question_lower:
  289. system_indicator_score += 1
  290. system_matched.append(f"系统查询指示词:{keyword}")
  291. # 检查查询意图词
  292. intent_score = 0
  293. intent_matched = []
  294. for keyword in self.query_intent_keywords:
  295. if keyword in question_lower:
  296. intent_score += 1
  297. intent_matched.append(keyword)
  298. # 检查SQL模式
  299. sql_patterns_matched = []
  300. for pattern in self.sql_patterns:
  301. if re.search(pattern, question_lower, re.IGNORECASE):
  302. business_score += 3 # SQL模式权重最高
  303. sql_patterns_matched.append(pattern)
  304. # 检查聊天关键词
  305. chat_score = 0
  306. chat_matched = []
  307. for keyword in self.chat_keywords:
  308. if keyword in question_lower:
  309. chat_score += 1
  310. chat_matched.append(keyword)
  311. # 系统指示词组合评分逻辑
  312. if system_indicator_score > 0 and business_score > 0:
  313. # 系统指示词 + 业务实体 = 强组合效应
  314. business_score += 3 # 组合加分
  315. business_matched.extend(system_matched)
  316. elif system_indicator_score > 0:
  317. # 仅有系统指示词 = 中等业务倾向
  318. business_score += 1
  319. business_matched.extend(system_matched)
  320. # 分类决策逻辑
  321. total_business_score = business_score + intent_score
  322. # 强业务特征:包含业务实体 + 查询意图
  323. if business_score >= 2 and intent_score >= 1:
  324. confidence = min(self.max_confidence, 0.8 + (total_business_score * 0.05))
  325. return ClassificationResult(
  326. question_type="DATABASE",
  327. confidence=confidence,
  328. reason=f"强业务特征 - 业务实体: {business_matched}, 查询意图: {intent_matched}, SQL: {sql_patterns_matched}",
  329. method="rule_based_strong_business"
  330. )
  331. # 中等业务特征:包含多个业务实体词
  332. elif business_score >= 4:
  333. confidence = min(self.max_confidence, 0.7 + (business_score * 0.03))
  334. return ClassificationResult(
  335. question_type="DATABASE",
  336. confidence=confidence,
  337. reason=f"中等业务特征 - 业务实体: {business_matched}",
  338. method="rule_based_medium_business"
  339. )
  340. # 聊天特征
  341. elif chat_score >= 1 and business_score == 0:
  342. confidence = min(self.max_confidence, self.base_confidence + (chat_score * self.confidence_increment))
  343. return ClassificationResult(
  344. question_type="CHAT",
  345. confidence=confidence,
  346. reason=f"聊天特征: {chat_matched}",
  347. method="rule_based_chat"
  348. )
  349. # 不确定情况
  350. else:
  351. return ClassificationResult(
  352. question_type="UNCERTAIN",
  353. confidence=self.uncertain_confidence,
  354. reason=f"规则分类不确定 - 业务分:{business_score}, 意图分:{intent_score}, 聊天分:{chat_score}",
  355. method="rule_based_uncertain"
  356. )
  357. def _load_business_context(self) -> str:
  358. """从文件中加载数据库业务范围描述"""
  359. try:
  360. import os
  361. current_dir = os.path.dirname(os.path.abspath(__file__))
  362. prompt_file = os.path.join(current_dir, "tools", "db_query_decision_prompt.txt")
  363. with open(prompt_file, 'r', encoding='utf-8') as f:
  364. content = f.read().strip()
  365. if not content:
  366. raise ValueError("业务上下文文件为空")
  367. return content
  368. except FileNotFoundError:
  369. error_msg = f"无法找到业务上下文文件: {prompt_file}"
  370. print(f"[ERROR] {error_msg}")
  371. raise FileNotFoundError(error_msg)
  372. except Exception as e:
  373. error_msg = f"读取业务上下文文件失败: {str(e)}"
  374. print(f"[ERROR] {error_msg}")
  375. raise RuntimeError(error_msg)
  376. def _enhanced_llm_classify(self, question: str) -> ClassificationResult:
  377. """增强的LLM分类:包含详细的业务上下文"""
  378. try:
  379. from common.vanna_instance import get_vanna_instance
  380. vn = get_vanna_instance()
  381. # 动态加载业务上下文(如果失败会抛出异常)
  382. business_context = self._load_business_context()
  383. # 构建包含业务上下文的分类提示词
  384. classification_prompt = f"""
  385. 请判断以下用户问题是否需要查询我们的数据库。
  386. 用户问题:{question}
  387. {business_context}
  388. === 判断标准 ===
  389. 1. **DATABASE类型** - 需要查询数据库:
  390. - 涉及上述业务实体和指标的查询、统计、分析、报表
  391. - 包含业务相关的时间查询
  392. - 例如:业务数据统计、收入排行、流量分析、占比分析等
  393. 2. **CHAT类型** - 不需要查询数据库:
  394. - 生活常识:水果蔬菜上市时间、动植物知识、天气等
  395. - 身份询问:你是谁、什么模型、AI助手等
  396. - 技术概念:人工智能、编程、算法等
  397. - 平台使用:功能介绍、操作帮助、使用教程等
  398. - 旅游出行:旅游景点、酒店、机票、高铁、的士等
  399. - 情绪:开心、伤心、无聊、生气、孤独、累了、烦恼、心情、难过、抑郁
  400. - 商业:股票、基金、理财、投资、经济、通货膨胀、上市
  401. - 哲学:人生意义、价值观、道德、信仰、宗教、爱情
  402. - 政策:政策、法规、法律、条例、指南、手册、规章制度、实施细则
  403. - 地理:全球、中国、亚洲、发展中、欧洲、美洲、东亚、东南亚、南美、非洲、大洋
  404. - 体育:足球、NBA、篮球、乒乓球、冠军、夺冠
  405. - 文学:小说、新闻、政治、战争、足球、NBA、篮球、乒乓球、冠军、夺冠
  406. - 娱乐:游戏、小说、新闻、政治、战争、足球、NBA、篮球、乒乓球、冠军、夺冠、电影、电视剧、音乐、舞蹈、绘画、书法、摄影、雕塑、建筑、设计、
  407. - 健康:健康、医疗、病症、健康、饮食、睡眠、心理、养生、减肥、美容、护肤
  408. - 其他:高考、人生意义、价值观、道德、信仰、宗教、爱情、全球、全国、亚洲、发展中、欧洲、美洲、东亚、东南亚、南美、非洲、大洋
  409. - 例如:"荔枝几月份上市"、"今天天气如何"、"你是什么AI"、"怎么使用平台"
  410. **重要提示:**
  411. - 只有涉及高速公路服务区业务数据的问题才分类为DATABASE
  412. - 只要不是涉及高速公路服务区业务数据的问题都应分类为CHAT
  413. 请基于问题与我们高速公路服务区业务数据的相关性来分类。
  414. 格式:
  415. 分类: [DATABASE/CHAT]
  416. 理由: [详细说明问题与业务数据的相关性,具体分析涉及哪些业务实体或为什么不相关]
  417. 置信度: [0.0-1.0之间的数字]
  418. """
  419. # 专业的系统提示词
  420. system_prompt = """你是一个专业的业务问题分类助手。你具有以下特长:
  421. 1. 深度理解业务领域和数据范围
  422. 2. 准确区分业务数据查询需求和一般性问题
  423. 3. 基于具体业务上下文进行精准分类,而不仅仅依赖关键词匹配
  424. 4. 对边界情况能够给出合理的置信度评估
  425. 请严格按照业务相关性进行分类,并提供详细的分类理由。"""
  426. # 使用 Vanna 实例的 chat_with_llm 方法
  427. response = vn.chat_with_llm(
  428. question=classification_prompt,
  429. system_prompt=system_prompt
  430. )
  431. # 解析响应
  432. return self._parse_llm_response(response)
  433. except (FileNotFoundError, RuntimeError) as e:
  434. # 业务上下文加载失败,返回错误状态
  435. print(f"[ERROR] LLM分类失败,业务上下文不可用: {str(e)}")
  436. return ClassificationResult(
  437. question_type="CHAT", # 失败时默认为CHAT,更安全
  438. confidence=0.1, # 很低的置信度表示分类不可靠
  439. reason=f"业务上下文加载失败,无法进行准确分类: {str(e)}",
  440. method="llm_context_error"
  441. )
  442. except Exception as e:
  443. print(f"[WARNING] 增强LLM分类失败: {str(e)}")
  444. return ClassificationResult(
  445. question_type="CHAT", # 失败时默认为CHAT,更安全
  446. confidence=self.llm_fallback_confidence,
  447. reason=f"LLM分类异常,默认为聊天: {str(e)}",
  448. method="llm_error"
  449. )
  450. def _parse_llm_response(self, response: str) -> ClassificationResult:
  451. """解析LLM响应"""
  452. try:
  453. lines = response.strip().split('\n')
  454. question_type = "CHAT" # 默认为CHAT
  455. reason = "LLM响应解析失败"
  456. confidence = self.llm_fallback_confidence
  457. for line in lines:
  458. line = line.strip()
  459. if line.startswith("分类:") or line.startswith("Classification:"):
  460. type_part = line.split(":", 1)[1].strip().upper()
  461. if "DATABASE" in type_part:
  462. question_type = "DATABASE"
  463. elif "CHAT" in type_part:
  464. question_type = "CHAT"
  465. elif line.startswith("理由:") or line.startswith("Reason:"):
  466. reason = line.split(":", 1)[1].strip()
  467. elif line.startswith("置信度:") or line.startswith("Confidence:"):
  468. try:
  469. conf_str = line.split(":", 1)[1].strip()
  470. confidence = float(conf_str)
  471. # 确保置信度在合理范围内
  472. confidence = max(0.0, min(1.0, confidence))
  473. except:
  474. confidence = self.llm_fallback_confidence
  475. return ClassificationResult(
  476. question_type=question_type,
  477. confidence=confidence,
  478. reason=reason,
  479. method="enhanced_llm"
  480. )
  481. except Exception as e:
  482. return ClassificationResult(
  483. question_type="CHAT", # 解析失败时默认为CHAT
  484. confidence=self.llm_fallback_confidence,
  485. reason=f"响应解析失败: {str(e)}",
  486. method="llm_parse_error"
  487. )