feature_engineering.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. from flask import Blueprint, render_template, request, jsonify
  2. import requests
  3. import json
  4. import logging
  5. feature_engineering_bp = Blueprint('feature_engineering', __name__)
  6. # 配置日志
  7. logging.basicConfig(level=logging.INFO)
  8. logger = logging.getLogger(__name__)
  9. @feature_engineering_bp.route('/')
  10. def feature_engineering():
  11. """特征工程主页面"""
  12. return render_template('feature_engineering.html')
  13. @feature_engineering_bp.route('/api/test-deepseek', methods=['POST'])
  14. def test_deepseek_api():
  15. """测试 Deepseek API 连接"""
  16. try:
  17. api_key = request.headers.get('X-API-Key')
  18. if not api_key:
  19. return jsonify({'success': False, 'error': '需要提供 API 密钥'}), 400
  20. # 通过简单请求测试 API
  21. headers = {
  22. 'Authorization': f'Bearer {api_key}',
  23. 'Content-Type': 'application/json'
  24. }
  25. test_data = {
  26. 'model': 'deepseek-chat',
  27. 'messages': [
  28. {'role': 'user', 'content': '你好,这是一条测试消息。'}
  29. ],
  30. 'max_tokens': 10
  31. }
  32. response = requests.post(
  33. 'https://api.deepseek.com/chat/completions ',
  34. headers=headers,
  35. json=test_data,
  36. timeout=10
  37. )
  38. if response.status_code == 200:
  39. return jsonify({'success': True, 'message': 'API 连接成功'})
  40. else:
  41. error_detail = response.text
  42. return jsonify({'success': False, 'error': f'API 返回状态 {response.status_code}:{error_detail}'}), 400
  43. except requests.exceptions.RequestException as e:
  44. logger.error(f"API 测试出错:{str(e)}")
  45. return jsonify({'success': False, 'error': f'网络错误:{str(e)}'}), 500
  46. except Exception as e:
  47. logger.error(f"API 测试中出现意外错误:{str(e)}")
  48. return jsonify({'success': False, 'error': f'未知错误:{str(e)}'}), 500
  49. @feature_engineering_bp.route('/api/get-recommendations', methods=['POST'])
  50. def get_feature_engineering_recommendations():
  51. """从 Deepseek API 获取特征工程建议"""
  52. try:
  53. api_key = request.headers.get('X-API-Key')
  54. if not api_key:
  55. return jsonify({'success': False, 'error': '需要提供 API 密钥'}), 400
  56. data = request.get_json()
  57. current_step = data.get('current_step', 1)
  58. data_field = data.get('data_field', '')
  59. previous_steps = data.get('previous_steps', [])
  60. current_data_state = data.get('current_data_state', '原始数据')
  61. if not data_field:
  62. return jsonify({'success': False, 'error': '请提供数据字段描述'}), 400
  63. # 构建系统提示
  64. system_prompt = get_default_system_prompt_text()
  65. # 构建用户提示
  66. previous_steps_text = "无" if not previous_steps else ",".join([f"步骤 {i+1}:{step}" for i, step in enumerate(previous_steps)])
  67. user_prompt = f"""上下文:
  68. 当前步骤:{current_step}
  69. 当前数据字段:{data_field}
  70. 之前的步骤及所用类别:{previous_steps_text}
  71. 当前数据状态:{current_data_state}"""
  72. # 调用 Deepseek API
  73. headers = {
  74. 'Authorization': f'Bearer {api_key}',
  75. 'Content-Type': 'application/json'
  76. }
  77. api_data = {
  78. 'model': 'deepseek-chat',
  79. 'messages': [
  80. {'role': 'system', 'content': system_prompt},
  81. {'role': 'user', 'content': user_prompt}
  82. ],
  83. 'max_tokens': 8192,
  84. 'temperature': 0.7
  85. }
  86. response = requests.post(
  87. 'https://api.deepseek.com/chat/completions ',
  88. headers=headers,
  89. json=api_data,
  90. timeout=30
  91. )
  92. if response.status_code == 200:
  93. response_data = response.json()
  94. recommendations = response_data['choices'][0]['message']['content']
  95. return jsonify({
  96. 'success': True,
  97. 'recommendations': recommendations,
  98. 'current_step': current_step,
  99. 'data_field': data_field,
  100. 'previous_steps': previous_steps,
  101. 'current_data_state': current_data_state
  102. })
  103. else:
  104. error_detail = response.text
  105. logger.error(f"Deepseek API 错误:{response.status_code} - {error_detail}")
  106. return jsonify({'success': False, 'error': f'API 返回状态 {response.status_code}:{error_detail}'}), 400
  107. except requests.exceptions.RequestException as e:
  108. logger.error(f"API 请求出错:{str(e)}")
  109. return jsonify({'success': False, 'error': f'网络错误:{str(e)}'}), 500
  110. except Exception as e:
  111. logger.error(f"获取建议时出现意外错误:{str(e)}")
  112. return jsonify({'success': False, 'error': f'未知错误:{str(e)}'}), 500
  113. @feature_engineering_bp.route('/api/continue-conversation', methods=['POST'])
  114. def continue_conversation():
  115. """继续对话,回答追问"""
  116. try:
  117. api_key = request.headers.get('X-API-Key')
  118. if not api_key:
  119. return jsonify({'success': False, 'error': '需要提供 API 密钥'}), 400
  120. data = request.get_json()
  121. conversation_history = data.get('conversation_history', [])
  122. user_message = data.get('user_message', '')
  123. custom_system_prompt = data.get('custom_system_prompt', None)
  124. if not user_message:
  125. return jsonify({'success': False, 'error': '请输入您的消息'}), 400
  126. # 构建对话消息
  127. messages = []
  128. # 若提供了自定义系统提示则使用,否则用默认
  129. if custom_system_prompt:
  130. system_prompt = custom_system_prompt
  131. else:
  132. system_prompt = get_default_system_prompt_text()
  133. messages.append({'role': 'system', 'content': system_prompt})
  134. # 加入历史对话
  135. for msg in conversation_history:
  136. messages.append(msg)
  137. # 加入新用户消息
  138. messages.append({'role': 'user', 'content': user_message})
  139. print(user_message)
  140. # 调用 Deepseek API
  141. headers = {
  142. 'Authorization': f'Bearer {api_key}',
  143. 'Content-Type': 'application/json'
  144. }
  145. api_data = {
  146. 'model': 'deepseek-chat',
  147. 'messages': messages,
  148. 'max_tokens': 8192,
  149. 'temperature': 0.7
  150. }
  151. response = requests.post(
  152. 'https://api.deepseek.com/chat/completions ',
  153. headers=headers,
  154. json=api_data,
  155. timeout=30
  156. )
  157. if response.status_code == 200:
  158. response_data = response.json()
  159. assistant_response = response_data['choices'][0]['message']['content']
  160. return jsonify({
  161. 'success': True,
  162. 'response': assistant_response
  163. })
  164. else:
  165. error_detail = response.text
  166. logger.error(f"Deepseek API 错误:{response.status_code} - {error_detail}")
  167. return jsonify({'success': False, 'error': f'API 返回状态 {response.status_code}:{error_detail}'}), 400
  168. except requests.exceptions.RequestException as e:
  169. logger.error(f"API 请求出错:{str(e)}")
  170. return jsonify({'success': False, 'error': f'网络错误:{str(e)}'}), 500
  171. except Exception as e:
  172. logger.error(f"继续对话时出现意外错误:{str(e)}")
  173. return jsonify({'success': False, 'error': f'未知错误:{str(e)}'}), 500
  174. def get_default_system_prompt_text():
  175. """获取默认系统提示文本"""
  176. return """你是一位特征工程专家助手。你的任务是针对给定的数据字段,设计一个最多包含 6 步的特征工程流水线。每一步都要根据当前数据状态、已完成的步骤以及总体目标,从 15 个类别中推荐最合适的特征工程类别。
  177. 说明:
  178. 每一步你都会收到:
  179. 当前步骤序号。
  180. 当前数据字段及其描述。
  181. 已完成的步骤及所用类别(如有)。
  182. 当前数据状态(例如:已归一化、已过滤等)。
  183. 你的任务是:
  184. 列出当前步骤最可行的特征工程类别,从以下 15 类中选择:
  185. 基础算术与数学运算
  186. 逻辑与条件运算
  187. 时间序列:变化检测与值比较
  188. 时间序列:统计特征工程
  189. 时间序列:排序、缩放与归一化
  190. 时间序列:衰减、平滑与翻转控制
  191. 时间序列:极值与位置识别
  192. 横截面:排序、缩放与归一化
  193. 横截面:回归与中性化
  194. 横截面:分布变换与截断
  195. 变换与过滤操作
  196. 分组聚合与统计摘要
  197. 分组排序、缩放与归一化
  198. 分组回归与中性化
  199. 分组插补与回填
  200. 对于每个推荐的类别,请按以下格式呈现:
  201. 重复完整上下文。
  202. 明确指出下一步选择的类别。
  203. 给出简要选择原因。
  204. 输出格式:
  205. 第 X 步可行类别:
  206. 第 X 步 选项 1:上下文:当前步骤:[序号] 当前数据字段:[描述] 之前步骤及所用类别:[列表] 当前数据状态:[详细描述之前步骤如何将数据转换到当前状态及其逻辑] 下一步选择:[类别名称] 原因:[解释]
  207. 第 X 步 选项 2:上下文:当前步骤:[序号] 当前数据字段:[描述] 之前步骤及所用类别:[列表] 当前数据状态:[详细描述之前步骤如何将数据转换到当前状态及其逻辑] 下一步选择:[类别名称] 原因:[解释]
  208. ...(继续列出所有可行选项)
  209. 额外说明:
  210. 仅推荐在当前数据状态及已完成的步骤下逻辑合理且有意义的类别。
  211. 若某类别在此步不适用,请勿列出。
  212. 解释需简洁明了。
  213. 除非特别要求,否则不要给出具体操作符。
  214. 每一步你都会收到如下输入:
  215. 上下文:
  216. 当前步骤:
  217. 当前数据字段:
  218. 之前步骤及所用类别:
  219. 当前数据状态:
  220. 收到输入后,请按上述格式回复。
  221. 重要:选项之后不要添加任何总结、建议、理由或其他解释。严格按照上述格式提供选项即可。不要添加“最推荐选择”、“理由”、“最佳选择”或“是否继续”等部分。列出所有选项后立即结束。
  222. """
  223. @feature_engineering_bp.route('/api/get-default-system-prompt', methods=['GET'])
  224. def get_default_system_prompt():
  225. """获取默认系统提示"""
  226. try:
  227. return jsonify({
  228. 'success': True,
  229. 'default_system_prompt': get_default_system_prompt_text()
  230. })
  231. except Exception as e:
  232. logger.error(f"获取默认系统提示出错:{str(e)}")
  233. return jsonify({'success': False, 'error': f'未知错误:{str(e)}'}), 500