tools.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. """
  2. 数据库查询工具集
  3. """
  4. import oracledb
  5. from typing import Dict, Any
  6. from config import DatabaseConfig
  7. from hello_agents import HelloAgentsLLM
  8. class OracleQueryTool:
  9. """Oracle数据库查询工具"""
  10. def __init__(self, config: DatabaseConfig):
  11. self.config = config
  12. self.connection = None
  13. def connect(self) -> bool:
  14. """连接到Oracle数据库"""
  15. try:
  16. self.connection = oracledb.connect(
  17. user=self.config.username,
  18. password=self.config.password,
  19. host=self.config.host,
  20. port=self.config.port,
  21. service_name=self.config.service_name
  22. )
  23. return True
  24. except Exception as e:
  25. print(f"数据库连接失败: {e}")
  26. return False
  27. def disconnect(self):
  28. """断开数据库连接"""
  29. if self.connection:
  30. self.connection.close()
  31. self.connection = None
  32. def execute_query(self, sql: str) -> Dict[str, Any]:
  33. """执行SQL查询并返回结果"""
  34. if not self.connection:
  35. if not self.connect():
  36. return {"success": False, "error": "无法连接到数据库"}
  37. try:
  38. cursor = self.connection.cursor()
  39. cursor.execute(sql)
  40. columns = [col[0] for col in cursor.description]
  41. rows = cursor.fetchall()
  42. cursor.close()
  43. return {
  44. "success": True,
  45. "columns": columns,
  46. "rows": rows,
  47. "row_count": len(rows),
  48. "sql": sql
  49. }
  50. except Exception as e:
  51. return {"success": False, "error": str(e), "sql": sql}
  52. def get_schema_info(self) -> str:
  53. """获取数据库表结构信息"""
  54. if not self.connection:
  55. if not self.connect():
  56. return "无法连接到数据库"
  57. try:
  58. cursor = self.connection.cursor()
  59. cursor.execute("""
  60. SELECT table_name
  61. FROM user_tables
  62. ORDER BY table_name
  63. """)
  64. tables = [row[0] for row in cursor.fetchall()]
  65. schema_info = []
  66. for table in tables:
  67. cursor.execute(f"""
  68. SELECT column_name, data_type, nullable
  69. FROM user_tab_columns
  70. WHERE table_name = UPPER('{table}')
  71. ORDER BY column_id
  72. """)
  73. columns = cursor.fetchall()
  74. col_desc = ", ".join([
  75. f"{col[0]} ({col[1]})"
  76. for col in columns
  77. ])
  78. schema_info.append(f"表 {table}: {col_desc}")
  79. cursor.close()
  80. return "\n".join(schema_info)
  81. except Exception as e:
  82. return f"获取表结构失败: {e}"
  83. class SQLGeneratorTool:
  84. """SQL生成工具 - 使用LLM将自然语言转换为SQL"""
  85. def __init__(self, llm: HelloAgentsLLM):
  86. self.llm = llm
  87. self.system_prompt = """你是一个专业的SQL查询生成助手。你的任务是将用户的自然语言查询转换为准确的Oracle SQL语句。
  88. # 规则:
  89. 1. 只返回SQL语句,不要包含任何解释或额外文字
  90. 2. 使用Oracle SQL语法
  91. 3. 表名和字段名使用大写
  92. 4. 日期格式使用 'YYYY-MM-DD'
  93. 5. 字符串使用单引号
  94. 6. 确保SQL语句安全,避免SQL注入
  95. # 数据库表结构:
  96. {schema_info}
  97. # 示例:
  98. 用户输入: 查询所有员工信息
  99. 输出: SELECT * FROM EMPLOYEES
  100. 用户输入: 查询工资大于5000的员工
  101. 输出: SELECT * FROM EMPLOYEES WHERE SALARY > 5000
  102. 现在,请根据用户的自然语言输入生成对应的SQL语句。
  103. """
  104. def generate_sql(self, natural_query: str, schema_info: str) -> str:
  105. """生成SQL语句"""
  106. prompt = self.system_prompt.format(schema_info=schema_info)
  107. messages = [
  108. {"role": "system", "content": prompt},
  109. {"role": "user", "content": natural_query}
  110. ]
  111. response = self.llm.invoke(messages)
  112. sql = response.strip()
  113. if sql.startswith("```sql"):
  114. sql = sql[6:]
  115. if sql.startswith("```"):
  116. sql = sql[3:]
  117. if sql.endswith("```"):
  118. sql = sql[:-3]
  119. return sql.strip()
  120. def validate_sql(self, sql: str) -> tuple[bool, str]:
  121. """验证SQL语句的基本语法"""
  122. sql_upper = sql.upper().strip()
  123. if not sql_upper.startswith(("SELECT", "WITH")):
  124. return False, "只允许SELECT查询语句"
  125. dangerous_keywords = ["DROP", "DELETE", "UPDATE", "INSERT", "TRUNCATE", "ALTER", "CREATE"]
  126. for keyword in dangerous_keywords:
  127. if keyword in sql_upper:
  128. return False, f"不允许使用 {keyword} 语句"
  129. return True, "SQL语句验证通过"
  130. def format_query_result(result: Dict[str, Any]) -> str:
  131. """格式化查询结果为表格"""
  132. if not result["success"]:
  133. return f"查询失败: {result['error']}"
  134. if result["row_count"] == 0:
  135. return "查询成功,但没有找到匹配的数据。"
  136. columns = result["columns"]
  137. rows = result["rows"]
  138. col_widths = []
  139. for i, col in enumerate(columns):
  140. max_width = max(len(str(col)), max(len(str(row[i])) for row in rows))
  141. col_widths.append(max_width + 2)
  142. separator = "+" + "+".join("-" * width for width in col_widths) + "+"
  143. header = "|" + "|".join(
  144. str(col).center(width) for col, width in zip(columns, col_widths)
  145. ) + "|"
  146. data_rows = []
  147. for row in rows:
  148. data_row = "|" + "|".join(
  149. str(cell).center(width) for cell, width in zip(row, col_widths)
  150. ) + "|"
  151. data_rows.append(data_row)
  152. table = [separator, header, separator] + data_rows + [separator]
  153. return "\n".join(table)