model_handler.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. """
  4. AI模型处理器
  5. 用于管理生成、与嵌入模型的创建和配置
  6. 支持的模型类型:
  7. - doubao: 豆包模型
  8. - qwen: 通义千问模型
  9. - deepseek: DeepSeek模型
  10. - lq_qwen3_8b: 本地Qwen3-8B模型
  11. - lq_qwen3_8b_lq_lora: 本地Qwen3-8B-lq-lora模型
  12. - lq_qwen3_4b: 本地Qwen3-4B模型
  13. - qwen_local_14b: 本地Qwen3-14B模型
  14. - lq_qwen3_8b_emd: 本地Qwen3-Embedding-8B嵌入模型
  15. - siliconflow_embed: 硅基流动Qwen3-Embedding-8B嵌入模型
  16. - lq_bge_reranker_v2_m3: 本地BGE-reranker-v2-m3重排序模型
  17. - qwen3_5_35b_a3b: DashScope Qwen3.5-35B-A3B模型(默认兜底模型)
  18. - qwen3_5_27b: DashScope Qwen3.5-27B模型
  19. - qwen3_5_122b_a10b: DashScope Qwen3.5-122B-A10B模型
  20. """
  21. # 禁用 transformers 的深度学习框架检测,避免启动时耗时扫描
  22. import os
  23. os.environ["TRANSFORMERS_VERBOSITY"] = "error"
  24. os.environ["HF_HUB_DISABLE_TELEMETRY"] = "1"
  25. import requests
  26. from langchain_openai import ChatOpenAI, OpenAIEmbeddings
  27. from foundation.infrastructure.config.config import config_handler
  28. from foundation.observability.logger.loggering import review_logger as logger
  29. class ModelConnectionError(Exception):
  30. """模型连接错误"""
  31. pass
  32. class ModelConfigError(Exception):
  33. """模型配置错误"""
  34. pass
  35. class ModelAPIError(Exception):
  36. """模型API调用错误"""
  37. pass
  38. class ModelHandler:
  39. """
  40. AI模型处理器类,用于管理多种AI模型的创建和配置
  41. """
  42. # 模型连接超时时间配置(秒)
  43. CONNECTION_TIMEOUT = 30
  44. REQUEST_TIMEOUT = 120
  45. MAX_RETRIES = 2
  46. def __init__(self):
  47. """
  48. 初始化模型处理器
  49. 加载配置处理器,用于后续读取各种模型的配置信息
  50. """
  51. self.config = config_handler
  52. self._model_cache = {} # 模型实例缓存
  53. def _check_connection(self, base_url: str, api_key: str = None, timeout: int = 5) -> bool:
  54. """
  55. 检查模型服务连接是否可用
  56. Args:
  57. base_url: 模型服务地址
  58. api_key: API密钥(可选)
  59. timeout: 超时时间(秒)
  60. Returns:
  61. bool: 连接是否可用
  62. """
  63. try:
  64. # 构造健康检查URL
  65. health_url = f"{base_url.rstrip('/')}/models"
  66. headers = {}
  67. if api_key and api_key != "dummy":
  68. headers["Authorization"] = f"Bearer {api_key}"
  69. response = requests.get(
  70. health_url,
  71. headers=headers,
  72. timeout=timeout
  73. )
  74. # 200-299 都认为可用
  75. return 200 <= response.status_code < 300
  76. except requests.exceptions.Timeout:
  77. logger.warning(f"连接超时: {base_url}")
  78. return False
  79. except requests.exceptions.ConnectionError as e:
  80. logger.warning(f"连接错误: {base_url}, 错误: {e}")
  81. return False
  82. except Exception as e:
  83. logger.warning(f"连接检查异常: {base_url}, 错误: {e}")
  84. return False
  85. def _handle_model_error(self, model_name: str, error: Exception, fallback_model=None):
  86. """
  87. 统一处理模型错误
  88. Args:
  89. model_name: 模型名称
  90. error: 异常对象
  91. fallback_model: 降级模型实例(可选)
  92. Returns:
  93. 降级模型实例,如果不可用则返回None
  94. """
  95. error_type = type(error).__name__
  96. error_msg = str(error)
  97. logger.error(f"模型初始化失败 [{model_name}]: {error_type} - {error_msg}")
  98. # 如果提供了降级模型,记录日志并返回
  99. if fallback_model:
  100. logger.warning(f"使用降级模型: {fallback_model.__class__.__name__}")
  101. return fallback_model
  102. # 如果没有降级模型,返回None让调用方处理
  103. return None
  104. def get_models(self):
  105. """
  106. 获取AI模型实例
  107. Returns:
  108. ChatOpenAI: 配置好的AI模型实例
  109. Note:
  110. 根据配置文件中的MODEL_TYPE参数选择对应模型
  111. 支持的模型类型:doubao, qwen, deepseek, lq_qwen3_8b, lq_qwen3_8b_lora, lq_qwen3_4b, qwen_local_14b
  112. 默认返回豆包模型
  113. """
  114. model_type = self.config.get("model", "MODEL_TYPE")
  115. logger.info(f"正在初始化AI模型,模型类型: {model_type}")
  116. # 检查缓存
  117. cache_key = f"chat_{model_type}"
  118. if cache_key in self._model_cache:
  119. logger.info(f"使用缓存的模型: {model_type}")
  120. return self._model_cache[cache_key]
  121. model = None
  122. try:
  123. if model_type == "doubao":
  124. model = self._get_doubao_model()
  125. elif model_type == "qwen":
  126. model = self._get_qwen_model()
  127. elif model_type == "qwen3_30b":
  128. model = self._get_qwen3_30b_model()
  129. elif model_type == "deepseek":
  130. model = self._get_deepseek_model()
  131. elif model_type == "lq_qwen3_8b":
  132. model = self._get_lq_qwen3_8b_model()
  133. elif model_type == "lq_qwen3_8b_lq_lora":
  134. model = self._get_lq_qwen3_8b_lora_model()
  135. elif model_type == "lq_qwen3_4b":
  136. model = self._get_lq_qwen3_4b_model()
  137. elif model_type == "qwen_local_14b":
  138. model = self._get_qwen_local_14b_model()
  139. elif model_type == "qwen3_5_35b_a3b":
  140. model = self._get_qwen3_5_35b_a3b_model()
  141. elif model_type == "qwen3_5_27b":
  142. model = self._get_qwen3_5_27b_model()
  143. elif model_type == "qwen3_5_122b_a10b":
  144. model = self._get_qwen3_5_122b_a10b_model()
  145. else:
  146. logger.warning(f"未知的模型类型 '{model_type}',使用默认 qwen3_5_35b_a3b 模型")
  147. model = self._get_qwen3_5_35b_a3b_model()
  148. if model:
  149. self._model_cache[cache_key] = model
  150. logger.info(f"AI模型初始化完成: {model_type}")
  151. return model
  152. else:
  153. raise ModelAPIError(f"模型初始化返回None: {model_type}")
  154. except Exception as e:
  155. logger.error(f"获取模型失败 [{model_type}]: {e}")
  156. # 使用 qwen3_5_35b_a3b 作为兜底降级方案
  157. if model_type != "qwen3_5_35b_a3b":
  158. logger.info("尝试使用 qwen3_5_35b_a3b 模型作为降级方案")
  159. try:
  160. fallback_model = self._get_qwen3_5_35b_a3b_model()
  161. if fallback_model:
  162. self._model_cache[cache_key] = fallback_model
  163. logger.warning("已切换到 qwen3_5_35b_a3b 降级模型")
  164. return fallback_model
  165. except Exception as fallback_error:
  166. logger.error(f"降级模型也失败: {fallback_error}")
  167. # 如果所有模型都失败,抛出异常
  168. raise ModelConnectionError(f"无法初始化任何模型服务: {e}")
  169. def get_model_by_name(self, model_type: str = None):
  170. """
  171. 根据模型名称动态获取指定的AI模型实例
  172. Args:
  173. model_type: 模型类型名称,如果为None则使用配置文件中的默认模型
  174. 支持的模型类型:doubao, qwen, qwen3_30b, deepseek, gemini,
  175. lq_qwen3_8b, lq_qwen3_8b_lq_lora,
  176. lq_qwen3_4b, qwen_local_14b
  177. Returns:
  178. ChatOpenAI: 配置好的AI模型实例
  179. Note:
  180. 该方法支持动态切换模型,不受配置文件中的默认MODEL_TYPE限制
  181. 如果model_type为None,则使用配置文件中的默认模型
  182. 如果model_type无效,则使用gemini作为降级模型
  183. """
  184. # 如果未指定模型类型,使用配置文件中的默认模型
  185. if model_type is None:
  186. model_type = self.config.get("model", "MODEL_TYPE")
  187. logger.info(f"动态获取AI模型,模型类型: {model_type}")
  188. # 检查缓存
  189. cache_key = f"chat_{model_type}"
  190. if cache_key in self._model_cache:
  191. logger.info(f"使用缓存的模型: {model_type}")
  192. return self._model_cache[cache_key]
  193. model = None
  194. try:
  195. if model_type == "doubao":
  196. model = self._get_doubao_model()
  197. elif model_type == "qwen":
  198. model = self._get_qwen_model()
  199. elif model_type == "qwen3_30b":
  200. model = self._get_qwen3_30b_model()
  201. elif model_type == "deepseek":
  202. model = self._get_deepseek_model()
  203. elif model_type == "lq_qwen3_8b":
  204. model = self._get_lq_qwen3_8b_model()
  205. elif model_type == "lq_qwen3_8b_lq_lora":
  206. model = self._get_lq_qwen3_8b_lora_model()
  207. elif model_type == "lq_qwen3_4b":
  208. model = self._get_lq_qwen3_4b_model()
  209. elif model_type == "qwen_local_14b":
  210. model = self._get_qwen_local_14b_model()
  211. elif model_type == "qwen3_5_35b_a3b":
  212. model = self._get_qwen3_5_35b_a3b_model()
  213. elif model_type == "qwen3_5_27b":
  214. model = self._get_qwen3_5_27b_model()
  215. elif model_type == "qwen3_5_122b_a10b":
  216. model = self._get_qwen3_5_122b_a10b_model()
  217. else:
  218. logger.warning(f"未知的模型类型 '{model_type}',使用默认 qwen3_5_35b_a3b 模型")
  219. model = self._get_qwen3_5_35b_a3b_model()
  220. if model:
  221. self._model_cache[cache_key] = model
  222. logger.info(f"AI模型动态初始化完成: {model_type}")
  223. return model
  224. else:
  225. raise ModelAPIError(f"模型初始化返回None: {model_type}")
  226. except Exception as e:
  227. logger.error(f"动态获取模型失败 [{model_type}]: {e}")
  228. # 使用 qwen3_5_35b_a3b 作为兜底降级方案
  229. if model_type != "qwen3_5_35b_a3b":
  230. logger.info("尝试使用 qwen3_5_35b_a3b 模型作为降级方案")
  231. try:
  232. fallback_model = self._get_qwen3_5_35b_a3b_model()
  233. if fallback_model:
  234. self._model_cache[cache_key] = fallback_model
  235. logger.warning("已切换到 qwen3_5_35b_a3b 降级模型")
  236. return fallback_model
  237. except Exception as fallback_error:
  238. logger.error(f"降级模型也失败: {fallback_error}")
  239. # 如果所有模型都失败,抛出异常
  240. raise ModelConnectionError(f"无法初始化任何模型服务: {e}")
  241. def get_embedding_model(self):
  242. """
  243. 获取Embedding模型实例
  244. Returns:
  245. OpenAIEmbeddings: 配置好的Embedding模型实例
  246. Note:
  247. 根据配置文件中的EMBEDDING_MODEL_TYPE参数选择对应模型
  248. 支持的模型类型:lq_qwen3_8b_emd, siliconflow_embed
  249. 默认返回本地 lq_qwen3_8b_emd 模型
  250. """
  251. embedding_model_type = self.config.get("model", "EMBEDDING_MODEL_TYPE", "lq_qwen3_8b_emd")
  252. logger.info(f"正在初始化Embedding模型,模型类型: {embedding_model_type}")
  253. # 检查缓存
  254. cache_key = f"embed_{embedding_model_type}"
  255. if cache_key in self._model_cache:
  256. logger.info(f"使用缓存的Embedding模型: {embedding_model_type}")
  257. return self._model_cache[cache_key]
  258. model = None
  259. try:
  260. if embedding_model_type == "siliconflow_embed":
  261. model = self._get_siliconflow_embedding_model()
  262. elif embedding_model_type == "lq_qwen3_8b_emd":
  263. model = self._get_lq_qwen3_8b_emd()
  264. else:
  265. # 默认返回本地模型
  266. logger.warning(f"未知的Embedding模型类型 '{embedding_model_type}',使用默认本地模型")
  267. model = self._get_lq_qwen3_8b_emd()
  268. if model:
  269. self._model_cache[cache_key] = model
  270. logger.info(f"Embedding模型初始化完成: {embedding_model_type}")
  271. return model
  272. else:
  273. raise ModelAPIError(f"Embedding模型初始化返回None: {embedding_model_type}")
  274. except Exception as e:
  275. logger.error(f"获取Embedding模型失败 [{embedding_model_type}]: {e}")
  276. raise ModelConnectionError(f"无法初始化Embedding模型服务: {e}")
  277. def _get_doubao_model(self):
  278. """
  279. 获取豆包模型
  280. Returns:
  281. ChatOpenAI: 配置好的豆包模型实例
  282. """
  283. try:
  284. doubao_url = self.config.get("doubao", "DOUBAO_SERVER_URL")
  285. doubao_model_id = self.config.get("doubao", "DOUBAO_MODEL_ID")
  286. doubao_api_key = self.config.get("doubao", "DOUBAO_API_KEY")
  287. # 验证配置完整性
  288. if not all([doubao_url, doubao_model_id, doubao_api_key]):
  289. missing = []
  290. if not doubao_url:
  291. missing.append("DOUBAO_SERVER_URL")
  292. if not doubao_model_id:
  293. missing.append("DOUBAO_MODEL_ID")
  294. if not doubao_api_key:
  295. missing.append("DOUBAO_API_KEY")
  296. raise ModelConfigError(f"豆包模型配置不完整,缺少: {', '.join(missing)}")
  297. # 检查连接
  298. if not self._check_connection(doubao_url, doubao_api_key):
  299. logger.warning(f"豆包模型服务连接失败: {doubao_url}")
  300. raise ModelConnectionError(f"无法连接到豆包模型服务: {doubao_url}")
  301. llm = ChatOpenAI(
  302. base_url=doubao_url,
  303. model=doubao_model_id,
  304. api_key=doubao_api_key,
  305. temperature=0.7,
  306. timeout=self.REQUEST_TIMEOUT,
  307. extra_body={
  308. "enable_thinking": False,
  309. })
  310. logger.info(f"豆包模型初始化成功: {doubao_model_id}")
  311. return llm
  312. except ModelConfigError:
  313. raise
  314. except ModelConnectionError:
  315. raise
  316. except Exception as e:
  317. error = ModelAPIError(f"豆包模型初始化异常: {e}")
  318. return self._handle_model_error("doubao", error)
  319. def _get_qwen_model(self):
  320. """
  321. 获取通义千问模型
  322. Returns:
  323. ChatOpenAI: 配置好的通义千问模型实例
  324. """
  325. try:
  326. qwen_url = self.config.get("qwen", "QWEN_SERVER_URL")
  327. qwen_model_id = self.config.get("qwen", "QWEN_MODEL_ID")
  328. qwen_api_key = self.config.get("qwen", "QWEN_API_KEY")
  329. # 验证配置完整性
  330. if not all([qwen_url, qwen_model_id, qwen_api_key]):
  331. missing = []
  332. if not qwen_url:
  333. missing.append("QWEN_SERVER_URL")
  334. if not qwen_model_id:
  335. missing.append("QWEN_MODEL_ID")
  336. if not qwen_api_key:
  337. missing.append("QWEN_API_KEY")
  338. raise ModelConfigError(f"通义千问模型配置不完整,缺少: {', '.join(missing)}")
  339. # 检查连接
  340. if not self._check_connection(qwen_url, qwen_api_key):
  341. logger.warning(f"通义千问模型服务连接失败: {qwen_url}")
  342. raise ModelConnectionError(f"无法连接到通义千问模型服务: {qwen_url}")
  343. llm = ChatOpenAI(
  344. base_url=qwen_url,
  345. model=qwen_model_id,
  346. api_key=qwen_api_key,
  347. temperature=0.7,
  348. timeout=self.REQUEST_TIMEOUT,
  349. extra_body={
  350. "enable_thinking": False,
  351. })
  352. logger.info(f"通义千问模型初始化成功: {qwen_model_id}")
  353. return llm
  354. except ModelConfigError:
  355. raise
  356. except ModelConnectionError:
  357. raise
  358. except Exception as e:
  359. error = ModelAPIError(f"通义千问模型初始化异常: {e}")
  360. return self._handle_model_error("qwen", error)
  361. def _get_qwen3_30b_model(self):
  362. """
  363. 获取Qwen3-30B模型
  364. Returns:
  365. ChatOpenAI: 配置好的Qwen3-30B模型实例
  366. """
  367. try:
  368. qwen3_30b_url = self.config.get("qwen3_30b", "QWEN3_30B_SERVER_URL")
  369. qwen3_30b_model_id = self.config.get("qwen3_30b", "QWEN3_30B_MODEL_ID")
  370. qwen3_30b_api_key = self.config.get("qwen3_30b", "QWEN3_30B_API_KEY")
  371. # 验证配置完整性
  372. if not all([qwen3_30b_url, qwen3_30b_model_id, qwen3_30b_api_key]):
  373. missing = []
  374. if not qwen3_30b_url:
  375. missing.append("QWEN3_30B_SERVER_URL")
  376. if not qwen3_30b_model_id:
  377. missing.append("QWEN3_30B_MODEL_ID")
  378. if not qwen3_30b_api_key:
  379. missing.append("QWEN3_30B_API_KEY")
  380. raise ModelConfigError(f"Qwen3-30B模型配置不完整,缺少: {', '.join(missing)}")
  381. # 检查连接
  382. if not self._check_connection(qwen3_30b_url, qwen3_30b_api_key):
  383. logger.warning(f"Qwen3-30B模型服务连接失败: {qwen3_30b_url}")
  384. raise ModelConnectionError(f"无法连接到Qwen3-30B模型服务: {qwen3_30b_url}")
  385. llm = ChatOpenAI(
  386. base_url=qwen3_30b_url,
  387. model=qwen3_30b_model_id,
  388. api_key=qwen3_30b_api_key,
  389. temperature=0.7,
  390. timeout=self.REQUEST_TIMEOUT,
  391. extra_body={
  392. "enable_thinking": False,
  393. })
  394. logger.info(f"Qwen3-30B模型初始化成功: {qwen3_30b_model_id}")
  395. return llm
  396. except ModelConfigError:
  397. raise
  398. except ModelConnectionError:
  399. raise
  400. except Exception as e:
  401. error = ModelAPIError(f"Qwen3-30B模型初始化异常: {e}")
  402. return self._handle_model_error("qwen3_30b", error)
  403. def _get_deepseek_model(self):
  404. """
  405. 获取DeepSeek模型
  406. Returns:
  407. ChatOpenAI: 配置好的DeepSeek模型实例
  408. """
  409. try:
  410. deepseek_url = self.config.get("deepseek", "DEEPSEEK_SERVER_URL")
  411. deepseek_model_id = self.config.get("deepseek", "DEEPSEEK_MODEL_ID")
  412. deepseek_api_key = self.config.get("deepseek", "DEEPSEEK_API_KEY")
  413. # 验证配置完整性
  414. if not all([deepseek_url, deepseek_model_id, deepseek_api_key]):
  415. missing = []
  416. if not deepseek_url:
  417. missing.append("DEEPSEEK_SERVER_URL")
  418. if not deepseek_model_id:
  419. missing.append("DEEPSEEK_MODEL_ID")
  420. if not deepseek_api_key:
  421. missing.append("DEEPSEEK_API_KEY")
  422. raise ModelConfigError(f"DeepSeek模型配置不完整,缺少: {', '.join(missing)}")
  423. # 检查连接
  424. if not self._check_connection(deepseek_url, deepseek_api_key):
  425. logger.warning(f"DeepSeek模型服务连接失败: {deepseek_url}")
  426. raise ModelConnectionError(f"无法连接到DeepSeek模型服务: {deepseek_url}")
  427. llm = ChatOpenAI(
  428. base_url=deepseek_url,
  429. model=deepseek_model_id,
  430. api_key=deepseek_api_key,
  431. temperature=0.7,
  432. timeout=self.REQUEST_TIMEOUT,
  433. extra_body={
  434. "enable_thinking": False,
  435. })
  436. logger.info(f"DeepSeek模型初始化成功: {deepseek_model_id}")
  437. return llm
  438. except ModelConfigError:
  439. raise
  440. except ModelConnectionError:
  441. raise
  442. except Exception as e:
  443. error = ModelAPIError(f"DeepSeek模型初始化异常: {e}")
  444. return self._handle_model_error("deepseek", error)
  445. def _get_lq_qwen3_8b_model(self):
  446. """
  447. 获取本地Qwen3-8B-Instruct模型
  448. Returns:
  449. ChatOpenAI: 配置好的本地Qwen3-8B模型实例
  450. """
  451. try:
  452. server_url = "http://192.168.91.253:9002/v1"
  453. model_id = "Qwen3-8B"
  454. # 检查本地服务连接
  455. if not self._check_connection(server_url, "dummy", timeout=3):
  456. logger.warning(f"本地Qwen3-8B模型服务连接失败: {server_url}")
  457. raise ModelConnectionError(f"无法连接到本地Qwen3-8B模型服务: {server_url}")
  458. llm = ChatOpenAI(
  459. base_url=server_url,
  460. model=model_id,
  461. api_key="dummy",
  462. temperature=0.7,
  463. timeout=self.REQUEST_TIMEOUT,
  464. )
  465. logger.info(f"本地Qwen3-8B模型初始化成功: {model_id}")
  466. return llm
  467. except ModelConnectionError:
  468. raise
  469. except Exception as e:
  470. error = ModelAPIError(f"本地Qwen3-8B模型初始化异常: {e}")
  471. return self._handle_model_error("lq_qwen3_8b", error)
  472. def _get_lq_qwen3_8b_lora_model(self):
  473. """
  474. 获取本地Qwen3-8B-lq-lora模型
  475. Returns:
  476. ChatOpenAI: 配置好的本地Qwen3-8B-lq-lora模型实例
  477. """
  478. try:
  479. server_url = self.config.get("lq_qwen3_8B_lora", "LQ_QWEN3_8B_LQ_LORA_SERVER_URL")
  480. model_id = self.config.get("lq_qwen3_8B_lora", "LQ_QWEN3_8B_LQ_LORA_MODEL_ID")
  481. api_key = self.config.get("lq_qwen3_8B_lora", "LQ_QWEN3_8B_LQ_LORA_API_KEY", "dummy")
  482. # 验证配置完整性
  483. if not all([server_url, model_id]):
  484. missing = []
  485. if not server_url:
  486. missing.append("LQ_QWEN3_8B_LQ_LORA_SERVER_URL")
  487. if not model_id:
  488. missing.append("LQ_QWEN3_8B_LQ_LORA_MODEL_ID")
  489. raise ModelConfigError(f"本地Qwen3-8B-lq-lora模型配置不完整,缺少: {', '.join(missing)}")
  490. # 检查本地服务连接
  491. if not self._check_connection(server_url, api_key, timeout=3):
  492. logger.warning(f"本地Qwen3-8B-lq-lora模型服务连接失败: {server_url}")
  493. raise ModelConnectionError(f"无法连接到本地Qwen3-8B-lq-lora模型服务: {server_url}")
  494. llm = ChatOpenAI(
  495. base_url=server_url,
  496. model=model_id,
  497. api_key=api_key,
  498. temperature=0.7,
  499. timeout=self.REQUEST_TIMEOUT,
  500. )
  501. logger.info(f"本地Qwen3-8B-lq-lora模型初始化成功: {model_id}")
  502. return llm
  503. except ModelConfigError:
  504. raise
  505. except ModelConnectionError:
  506. raise
  507. except Exception as e:
  508. error = ModelAPIError(f"本地Qwen3-8B-lq-lora模型初始化异常: {e}")
  509. return self._handle_model_error("lq_qwen3_8b_lora", error)
  510. def _get_lq_qwen3_4b_model(self):
  511. """
  512. 获取本地Qwen3-4B-Instruct模型
  513. Returns:
  514. ChatOpenAI: 配置好的本地Qwen3-4B模型实例
  515. """
  516. try:
  517. server_url = "http://192.168.91.253:9001/v1"
  518. model_id = "Qwen3-4B"
  519. # 检查本地服务连接
  520. if not self._check_connection(server_url, "dummy", timeout=3):
  521. logger.warning(f"本地Qwen3-4B模型服务连接失败: {server_url}")
  522. raise ModelConnectionError(f"无法连接到本地Qwen3-4B模型服务: {server_url}")
  523. llm = ChatOpenAI(
  524. base_url=server_url,
  525. model=model_id,
  526. api_key="dummy",
  527. temperature=0.7,
  528. timeout=self.REQUEST_TIMEOUT,
  529. )
  530. logger.info(f"本地Qwen3-4B模型初始化成功: {model_id}")
  531. return llm
  532. except ModelConnectionError:
  533. raise
  534. except Exception as e:
  535. error = ModelAPIError(f"本地Qwen3-4B模型初始化异常: {e}")
  536. return self._handle_model_error("lq_qwen3_4b", error)
  537. def _get_qwen_local_14b_model(self):
  538. """
  539. 获取本地Qwen3-14B-Instruct模型
  540. Returns:
  541. ChatOpenAI: 配置好的本地Qwen3-14B模型实例
  542. """
  543. try:
  544. server_url = "http://192.168.91.253:9003/v1"
  545. model_id = "Qwen3-14B"
  546. # 检查本地服务连接
  547. if not self._check_connection(server_url, "dummy", timeout=3):
  548. logger.warning(f"本地Qwen3-14B模型服务连接失败: {server_url}")
  549. raise ModelConnectionError(f"无法连接到本地Qwen3-14B模型服务: {server_url}")
  550. llm = ChatOpenAI(
  551. base_url=server_url,
  552. model=model_id,
  553. api_key="dummy",
  554. temperature=0.7,
  555. timeout=self.REQUEST_TIMEOUT,
  556. )
  557. logger.info(f"本地Qwen3-14B模型初始化成功: {model_id}")
  558. return llm
  559. except ModelConnectionError:
  560. raise
  561. except Exception as e:
  562. error = ModelAPIError(f"本地Qwen3-14B模型初始化异常: {e}")
  563. return self._handle_model_error("qwen_local_14b", error)
  564. def _get_qwen3_5_35b_a3b_model(self):
  565. """
  566. 获取 DashScope Qwen3.5-35B-A3B 模型
  567. Returns:
  568. ChatOpenAI: 配置好的 DashScope Qwen3.5-35B-A3B 模型实例
  569. """
  570. try:
  571. url = self.config.get("qwen3_5_35b_a3b", "DASHSCOPE_SERVER_URL")
  572. model_id = self.config.get("qwen3_5_35b_a3b", "DASHSCOPE_MODEL_ID")
  573. api_key = self.config.get("qwen3_5_35b_a3b", "DASHSCOPE_API_KEY")
  574. # 验证配置完整性
  575. if not all([url, model_id, api_key]):
  576. missing = []
  577. if not url:
  578. missing.append("DASHSCOPE_SERVER_URL")
  579. if not model_id:
  580. missing.append("DASHSCOPE_MODEL_ID")
  581. if not api_key:
  582. missing.append("DASHSCOPE_API_KEY")
  583. raise ModelConfigError(f"DashScope Qwen3.5-35B 模型配置不完整,缺少: {', '.join(missing)}")
  584. llm = ChatOpenAI(
  585. base_url=url,
  586. model=model_id,
  587. api_key=api_key,
  588. temperature=0.7,
  589. timeout=self.REQUEST_TIMEOUT,
  590. )
  591. logger.info(f"DashScope Qwen3.5-35B 模型初始化成功: {model_id}")
  592. return llm
  593. except ModelConfigError:
  594. raise
  595. except Exception as e:
  596. return self._handle_model_error("qwen3_5_35b_a3b", ModelAPIError(str(e)))
  597. def _get_qwen3_5_27b_model(self):
  598. """
  599. 获取 DashScope Qwen3.5-27B 模型
  600. Returns:
  601. ChatOpenAI: 配置好的 DashScope Qwen3.5-27B 模型实例
  602. """
  603. try:
  604. url = self.config.get("qwen3_5_27b", "DASHSCOPE_SERVER_URL")
  605. model_id = self.config.get("qwen3_5_27b", "DASHSCOPE_MODEL_ID")
  606. api_key = self.config.get("qwen3_5_27b", "DASHSCOPE_API_KEY")
  607. # 验证配置完整性
  608. if not all([url, model_id, api_key]):
  609. missing = []
  610. if not url:
  611. missing.append("DASHSCOPE_SERVER_URL")
  612. if not model_id:
  613. missing.append("DASHSCOPE_MODEL_ID")
  614. if not api_key:
  615. missing.append("DASHSCOPE_API_KEY")
  616. raise ModelConfigError(f"DashScope Qwen3.5-27B 模型配置不完整,缺少: {', '.join(missing)}")
  617. llm = ChatOpenAI(
  618. base_url=url,
  619. model=model_id,
  620. api_key=api_key,
  621. temperature=0.7,
  622. timeout=self.REQUEST_TIMEOUT,
  623. )
  624. logger.info(f"DashScope Qwen3.5-27B 模型初始化成功: {model_id}")
  625. return llm
  626. except ModelConfigError:
  627. raise
  628. except Exception as e:
  629. return self._handle_model_error("qwen3_5_27b", ModelAPIError(str(e)))
  630. def _get_qwen3_5_122b_a10b_model(self):
  631. """
  632. 获取 DashScope Qwen3.5-122B-A10B 模型
  633. Returns:
  634. ChatOpenAI: 配置好的 DashScope Qwen3.5-122B-A10B 模型实例
  635. """
  636. try:
  637. url = self.config.get("qwen3_5_122b_a10b", "DASHSCOPE_SERVER_URL")
  638. model_id = self.config.get("qwen3_5_122b_a10b", "DASHSCOPE_MODEL_ID")
  639. api_key = self.config.get("qwen3_5_122b_a10b", "DASHSCOPE_API_KEY")
  640. # 验证配置完整性
  641. if not all([url, model_id, api_key]):
  642. missing = []
  643. if not url:
  644. missing.append("DASHSCOPE_SERVER_URL")
  645. if not model_id:
  646. missing.append("DASHSCOPE_MODEL_ID")
  647. if not api_key:
  648. missing.append("DASHSCOPE_API_KEY")
  649. raise ModelConfigError(f"DashScope Qwen3.5-122B 模型配置不完整,缺少: {', '.join(missing)}")
  650. llm = ChatOpenAI(
  651. base_url=url,
  652. model=model_id,
  653. api_key=api_key,
  654. temperature=0.7,
  655. timeout=self.REQUEST_TIMEOUT,
  656. )
  657. logger.info(f"DashScope Qwen3.5-122B 模型初始化成功: {model_id}")
  658. return llm
  659. except ModelConfigError:
  660. raise
  661. except Exception as e:
  662. return self._handle_model_error("qwen3_5_122b_a10b", ModelAPIError(str(e)))
  663. def _get_lq_qwen3_8b_emd(self):
  664. """
  665. 获取本地Qwen3-Embedding-8B嵌入模型
  666. Returns:
  667. OpenAIEmbeddings: 配置好的本地Qwen3-Embedding-8B嵌入模型实例
  668. """
  669. try:
  670. server_url = "http://192.168.91.253:9003/v1"
  671. model_id = "Qwen3-Embedding-8B"
  672. # 检查本地服务连接
  673. if not self._check_connection(server_url, "dummy", timeout=3):
  674. logger.warning(f"本地Qwen3-Embedding-8B模型服务连接失败: {server_url}")
  675. raise ModelConnectionError(f"无法连接到本地Qwen3-Embedding-8B模型服务: {server_url}")
  676. # 使用 langchain_openai 的 OpenAIEmbeddings
  677. embeddings = OpenAIEmbeddings(
  678. base_url=server_url,
  679. model=model_id,
  680. api_key="dummy", # 本地模型使用虚拟API key
  681. timeout=self.REQUEST_TIMEOUT,
  682. )
  683. logger.info(f"本地Qwen3-Embedding-8B模型初始化成功: {model_id}")
  684. return embeddings
  685. except ModelConnectionError:
  686. raise
  687. except Exception as e:
  688. error = ModelAPIError(f"本地Qwen3-Embedding-8B模型初始化异常: {e}")
  689. return self._handle_model_error("lq_qwen3_8b_emd", error)
  690. def _get_siliconflow_embedding_model(self):
  691. """
  692. 获取硅基流动Qwen3-Embedding-8B嵌入模型
  693. Returns:
  694. OpenAIEmbeddings: 配置好的硅基流动Qwen3-Embedding-8B嵌入模型实例
  695. """
  696. try:
  697. server_url = self.config.get("siliconflow_embed", "SLCF_EMBED_SERVER_URL")
  698. api_key = self.config.get("siliconflow_embed", "SLCF_EMBED_API_KEY")
  699. model_id = self.config.get("siliconflow_embed", "SLCF_EMBED_MODEL_ID", "Qwen/Qwen3-Embedding-8B")
  700. dimensions = self.config.get("siliconflow_embed", "SLCF_EMBED_DIMENSIONS", "4096")
  701. # 验证配置完整性
  702. if not all([server_url, api_key, model_id]):
  703. missing = []
  704. if not server_url:
  705. missing.append("SLCF_EMBED_SERVER_URL")
  706. if not api_key:
  707. missing.append("SLCF_EMBED_API_KEY")
  708. if not model_id:
  709. missing.append("SLCF_EMBED_MODEL_ID")
  710. raise ModelConfigError(f"硅基流动Embedding模型配置不完整,缺少: {', '.join(missing)}")
  711. # 检查连接
  712. if not self._check_connection(server_url, api_key):
  713. logger.warning(f"硅基流动Embedding模型服务连接失败: {server_url}")
  714. raise ModelConnectionError(f"无法连接到硅基流动Embedding模型服务: {server_url}")
  715. # 使用 langchain_openai 的 OpenAIEmbeddings
  716. embeddings = OpenAIEmbeddings(
  717. base_url=server_url,
  718. model=model_id,
  719. api_key=api_key,
  720. timeout=self.REQUEST_TIMEOUT,
  721. )
  722. logger.info(f"硅基流动Embedding模型初始化成功: {model_id} (dimensions: {dimensions})")
  723. return embeddings
  724. except ModelConfigError:
  725. raise
  726. except ModelConnectionError:
  727. raise
  728. except Exception as e:
  729. error = ModelAPIError(f"硅基流动Embedding模型初始化异常: {e}")
  730. return self._handle_model_error("siliconflow_embed", error)
  731. # 创建全局实例
  732. model_handler = ModelHandler()
  733. def get_models():
  734. """
  735. 获取模型的全局函数
  736. Returns:
  737. tuple: (llm, chat, embed) - LLM模型、聊天模型和嵌入模型实例
  738. 注意:当前llm和chat使用相同模型实例,embed暂时返回None
  739. Note:
  740. 这是一个便捷函数,直接使用全局model_handler实例获取模型
  741. """
  742. try:
  743. llm = model_handler.get_models()
  744. # 暂时返回相同的模型作为chat和embed
  745. return llm, llm, None
  746. except Exception as e:
  747. logger.error(f"获取模型失败: {e}")
  748. raise ModelConnectionError(f"无法获取模型服务: {e}")