ddl_parser.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import os
  2. import requests
  3. import re
  4. import json
  5. from flask import current_app
  6. class DDLParser:
  7. def __init__(self, api_key=None):
  8. """
  9. 初始化DDL解析器
  10. 参数:
  11. api_key: LLM API密钥,如果未提供,将从应用配置或环境变量中获取
  12. """
  13. # 如果在Flask应用上下文中,则从应用配置获取参数
  14. self.api_key = api_key or current_app.config.get('LLM_API_KEY')
  15. self.base_url = current_app.config.get('LLM_BASE_URL')
  16. self.model_name = current_app.config.get('LLM_MODEL_NAME')
  17. self.headers = {
  18. "Authorization": f"Bearer {self.api_key}",
  19. "Content-Type": "application/json"
  20. }
  21. def parse_ddl(self, sql_content):
  22. """
  23. 解析DDL语句,返回标准化的结构
  24. 参数:
  25. sql_content: 要解析的DDL语句
  26. 返回:
  27. 解析结果的JSON对象
  28. """
  29. prompt = self._optimize_prompt()
  30. payload = {
  31. "model": self.model_name,
  32. "messages": [
  33. {
  34. "role": "system",
  35. "content": "你是一个专业的数据库分析专家,擅长解析SQL DDL语句并提取表结构信息。"
  36. },
  37. {
  38. "role": "user",
  39. "content": f"{prompt}\n\n{sql_content}"
  40. }
  41. ]
  42. }
  43. try:
  44. response = requests.post(
  45. f"{self.base_url}/chat/completions",
  46. headers=self.headers,
  47. json=payload,
  48. timeout=30
  49. )
  50. response.raise_for_status()
  51. result = response.json()
  52. if "choices" in result and len(result["choices"]) > 0:
  53. content = result["choices"][0]["message"]["content"]
  54. try:
  55. json_match = re.search(r'```json\s*([\s\S]*?)\s*```', content)
  56. if json_match:
  57. json_content = json_match.group(1)
  58. else:
  59. json_content = content
  60. parsed_result = json.loads(json_content)
  61. return parsed_result
  62. except json.JSONDecodeError as e:
  63. return {
  64. "code": 500,
  65. "message": f"无法解析返回的JSON: {str(e)}",
  66. "original_response": content
  67. }
  68. return {
  69. "code": 500,
  70. "message": "无法获取有效响应",
  71. "original_response": result
  72. }
  73. except requests.RequestException as e:
  74. return {
  75. "code": 500,
  76. "message": f"API请求失败: {str(e)}"
  77. }
  78. def _optimize_prompt(self):
  79. """返回优化后的提示词模板"""
  80. return """
  81. 请解析以下DDL建表语句,并按照指定的JSON格式返回结果:
  82. 规则说明:
  83. 1. 从DDL语句中识别所有表名,并在data对象中为每个表创建条目,表名请使用小写,可能会有多个表。
  84. 2. 对于每个表,提取所有字段信息,包括名称、数据类型和注释。
  85. 3. 字段中文名称(name)的确定规则:
  86. - 如有COMMENT注释,直接使用注释内容
  87. - 如无注释但字段名有明确含义,将英文名翻译为中文
  88. - 如字段名是无意义的拼音缩写,则name为空字符串
  89. 4. 数据库连接串处理:
  90. - 将连接串识别后并拆解为:主机名/IP地址、端口、数据库名称、用户名、密码。
  91. - 根据连接串格式识别数据库类型,数据库类型请使用小写,参考例子,如 mysql/postgresql/sqlserver/oracle/db2
  92. - data_source.name格式为: "{hostname或ip地址(点替换为中划线)}_{端口}_{数据库名称}",如某个元素无法识别,则跳过不添加到data_source.name
  93. - 无法确定数据库类型时,type设为"unknown"
  94. - 如果从ddl中没有识别到数据库连接串,则json不返回"data_source"标签
  95. 5. 参考格式如下:
  96. {
  97. "users": { //表名
  98. "name": "用户表", //表的中文名,来自于COMMENT注释或LLM翻译,如果无法确定,则name为空字符串
  99. "schema": "public",
  100. "meta": [{
  101. "en_name": "id",
  102. "data_type": "integer",
  103. "name": "用户ID"
  104. },
  105. {
  106. "en_name": "username",
  107. "data_type": "varchar",
  108. "name": "用户名"
  109. }
  110. ]
  111. },
  112. "data_source": [{
  113. "name": "10-52-31-104_5432_inventory_management", //hostname或ip地址(点替换为中划线)_端口_数据库名称
  114. "type": "postgresql",
  115. "host": "10.52.31.104",
  116. "port": 5432 "database": "inventory_management"
  117. }]
  118. }
  119. 请仅返回JSON格式结果,不要包含任何其他解释文字。
  120. """