Indentity.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. import uuid
  2. from flask import Flask, request, jsonify, render_template
  3. import json
  4. import re
  5. import requests
  6. import os
  7. app = Flask(__name__, template_folder='templates')
  8. OLLAMA_URL = 'http://192.168.31.210:11434/api/generate'
  9. MATCH_URL = 'http://192.168.31.19:8352/api/kg/match'
  10. QUERY_URL = 'http://localhost:5000/query'
  11. MODEL_NAME = "deepseek-r1:32b"
  12. MAPPING_FILE = '省间关系映射.json'
  13. JAVA_TASK_URL = "http://localhost:8080/business/task" # 添加任务api
  14. JAVA_LOG_API = "http://localhost:8080/business/log" #添加日志api
  15. def log_to_java(
  16. task_guid: str,
  17. type_guid: str,
  18. content: str,
  19. proname: str,
  20. proid: str,
  21. level: str
  22. ):
  23. """
  24. 向 Java 后端写入日志记录
  25. 参数说明:
  26. - task_guid: 任务编号(Java 创建任务接口返回的)
  27. - type_id: 类型编号(如 "1" 表示任务创建)
  28. - log_detail: 日志详情(描述信息)
  29. - program_name: 程序名称(默认为 web_extract)
  30. - program_id: 程序 ID(默认为 py001)
  31. - log_level: 日志级别(INFO/WARN/ERROR)
  32. 返回:
  33. - True 表示成功,False 表示失败
  34. """
  35. log_data = {
  36. "taskGuid": task_guid,
  37. "typeGuid": type_guid,
  38. "content": content,
  39. "proname": proname,
  40. "proid": proid,
  41. "level": level.upper()
  42. }
  43. headers = {'Content-Type': 'application/json'}
  44. print(f"发送日志请求到 {JAVA_LOG_API},请求数据为:{log_data}")
  45. try:
  46. response = requests.post(JAVA_LOG_API, json=log_data, headers=headers)
  47. print("响应状态码:", response.status_code)
  48. print("响应内容:", response.text)
  49. response.raise_for_status()
  50. return {"success": True}
  51. except Exception as e:
  52. print(f"[日志记录失败] {e}")
  53. return {"success": False, "error": str(e)}
  54. def call_java_add_task(data):
  55. task_guid = str(uuid.uuid4()) # ✅ 生成 UUID 字符串
  56. task_payload = {
  57. # "taskContent": data.get("问句"),
  58. "taskContent": data,
  59. "taskGuid": task_guid # ✅ 把 uuid 一起传给后端
  60. }
  61. headers = {
  62. "Content-Type": "application/json"
  63. }
  64. response = requests.post(JAVA_TASK_URL, json=task_payload, headers=headers)
  65. return {
  66. "taskGuid": task_guid, # ✅ 返回给前端或后续流程使用
  67. "javaResponse": response.json()
  68. }
  69. def replace_province_with_code(response_data, mapping_file_path):
  70. with open(mapping_file_path, 'r', encoding='utf-8') as f:
  71. code_to_name = json.load(f)
  72. name_to_code = {v: k for k, v in code_to_name.items()}
  73. unit_name = response_data.get('条件', {}).get('单位', '')
  74. def normalize_name(name):
  75. suffixes = ['省', '市', '自治区', '特别行政区']
  76. for suffix in suffixes:
  77. if name.endswith(suffix):
  78. return name.replace(suffix, '')
  79. return name
  80. normalized_name = normalize_name(unit_name)
  81. if normalized_name in name_to_code:
  82. response_data['条件']['单位'] = name_to_code[normalized_name]
  83. else:
  84. print(f"⚠️ 未找到“{normalized_name}”对应的代码,保持原值。")
  85. return response_data
  86. def extract_valid_json_outside_think(text):
  87. cleaned = re.sub(r'<think>[\s\S]*?</think>', '', text)
  88. brace_stack = []
  89. json_start = None
  90. for i, char in enumerate(cleaned):
  91. if char == '{':
  92. if not brace_stack:
  93. json_start = i
  94. brace_stack.append('{')
  95. elif char == '}':
  96. if brace_stack:
  97. brace_stack.pop()
  98. if not brace_stack:
  99. json_str = cleaned[json_start:i + 1]
  100. try:
  101. return json.loads(json_str)
  102. except json.JSONDecodeError:
  103. continue
  104. raise ValueError("❌ 无法从 think 外部提取合法 JSON")
  105. @app.route("/")
  106. def index():
  107. return render_template("index.html")
  108. @app.route("/web_extract", methods=["POST"])
  109. def web_extract():
  110. try:
  111. data = request.get_json()
  112. question = data.get("question", "").strip()
  113. if not question:
  114. return jsonify({"error": "缺少问句参数"}), 400
  115. try:
  116. # 调用 Java 接口新增任务
  117. java_result = call_java_add_task(question)
  118. # 声明任务的id
  119. task_id = java_result['taskGuid']
  120. # 新增提交日志
  121. log_to_java(
  122. task_guid=task_id,
  123. type_guid="",
  124. content=f"成功调用模型,传递参为:{question}\n",
  125. proname="提交问句",
  126. proid="1",
  127. level="INFO"
  128. )
  129. except Exception as e:
  130. # 如果无法获取 task_id,记录一个默认或空的 task_guid
  131. log_to_java(
  132. task_guid="",
  133. type_guid="",
  134. content=f"调用任务创建接口失败或写日志失败,问句为:{question}\n错误信息:{str(e)}",
  135. proname="提交问句",
  136. proid="1",
  137. level="ERROR"
  138. )
  139. return jsonify({"error": "任务创建失败", "detail": str(e)}), 500
  140. prompt = f"""
  141. 现在我要让你在一个或者多个问句中抽取出三元组,其中条件包含:年份、月份、日、省份(问句中有哪些条件就写哪些条件,没有的无需添加到结果中),实体包含:送出电量、送出电量占售电量的比、送出均价、受入电量、送出电量占售电量的比、受入均价。注意:实体在问句中的表示可能不那么准确,请你判断出和我给你实体意思最相近的问句中的实体。
  142. 问句如下:
  143. {question}
  144. 输出格式为:
  145. {{
  146. "问句": 问句,
  147. "实体": ["送出电量"],
  148. "条件": {{
  149. "单位":"山东省",
  150. "年":"2024",
  151. "月":"1",
  152. "日":"1"
  153. }}
  154. }}
  155. 输出结果只显示上述内容,不输出其他任何东西。
  156. """
  157. payload = {
  158. "model": MODEL_NAME,
  159. "prompt": prompt,
  160. "stream": False
  161. }
  162. # 调用deepseek对问句进行语义分析
  163. try:
  164. response = requests.post(OLLAMA_URL, json=payload)
  165. response.raise_for_status() # 确保 HTTP 状态码为 200,否则抛出异常
  166. result = response.json()
  167. # 请求成功,记录 INFO 日志
  168. log_to_java(
  169. task_guid=task_id,
  170. type_guid="",
  171. content=f"成功调用模型,入参为:{json.dumps(payload, ensure_ascii=False)}\n返回结果为:{json.dumps(result, ensure_ascii=False)}",
  172. proname="DeepSeek语义识别",
  173. proid="2",
  174. level="INFO"
  175. )
  176. except Exception as e:
  177. # 请求失败或解析失败,记录 ERROR 日志
  178. log_to_java(
  179. task_guid=task_id,
  180. type_guid="",
  181. content=f"调用模型失败,入参为:{json.dumps(payload, ensure_ascii=False)}\n,错误信息:{str(e)}",
  182. proname="DeepSeek语义识别",
  183. proid="2",
  184. level="ERROR"
  185. )
  186. return jsonify({"error": "调用大模型失败", "detail": str(e)}), 500
  187. # 对获取到的数据进行处理
  188. response_str = result.get("response", "")
  189. response_data = extract_valid_json_outside_think(response_str)
  190. updated_data = replace_province_with_code(response_data, MAPPING_FILE)
  191. # return jsonify(updated_data)
  192. try:
  193. match_response = requests.post(MATCH_URL, json=updated_data)
  194. match_result = match_response.json()
  195. # 匹配成功日志
  196. log_to_java(
  197. task_guid=task_id,
  198. type_guid="", # 匹配成功日志类型编号
  199. content=f"关键词匹配成功,入参为:{json.dumps(updated_data, ensure_ascii=False)}\n响应内容:{match_result}",
  200. proname="关键词匹配",
  201. proid="3",
  202. level="INFO"
  203. )
  204. except Exception as e:
  205. log_to_java(
  206. task_guid=task_id,
  207. type_guid="", # 匹配异常日志类型编号
  208. content=f"关键词匹配失败,入参为:{json.dumps(updated_data, ensure_ascii=False)}\n响应内容:{str(e)}",
  209. proname="关键词匹配",
  210. proid="3",
  211. level="ERROR"
  212. )
  213. return jsonify({"error": "请求匹配服务时发生异常", "detail": str(e)}), 500
  214. # return jsonify({"success": True}), 200
  215. # 问答模块
  216. try:
  217. query_payload = match_result["data"]
  218. query_response = requests.post(QUERY_URL, json=query_payload)
  219. query_response.raise_for_status() # 会在 4xx/5xx 抛出异常
  220. query_result = query_response.json()
  221. log_to_java(
  222. task_guid=task_id,
  223. type_guid="",
  224. content=f"问答成功,入参为:{json.dumps(query_payload, ensure_ascii=False)}\n响应内容:{query_result}",
  225. proname="问答程序",
  226. proid="4",
  227. level="INFO"
  228. )
  229. except requests.exceptions.RequestException as e:
  230. log_to_java(
  231. task_guid=task_id,
  232. type_guid="",
  233. content=f"问答失败:入参为:{json.dumps(query_payload, ensure_ascii=False)}\n响应内容:{str(e)}",
  234. proname="问答程序",
  235. proid="4",
  236. level="ERROR"
  237. )
  238. return jsonify({"error": "查询接口请求失败", "details": str(e)}), 500
  239. return jsonify(query_result), 200
  240. except Exception as e:
  241. return jsonify({"error": str(e)}), 500
  242. if __name__ == "__main__":
  243. app.run(host="0.0.0.0", port=8866, debug=True)