sql_tool.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. from functools import lru_cache
  2. from langchain_community.utilities import SQLDatabase
  3. from langchain_core.tools import tool
  4. from agent.config import load_config
  5. @lru_cache(maxsize=1)
  6. def get_database() -> SQLDatabase:
  7. config = load_config()
  8. return SQLDatabase.from_uri(config.database_url)
  9. def is_readonly_sql(sql: str) -> bool:
  10. cleaned_sql = sql.strip().rstrip(";").strip()
  11. lowered_sql = cleaned_sql.lower()
  12. readonly_prefixes = ("select", "show", "describe", "desc", "explain")
  13. forbidden_keywords = (
  14. "insert",
  15. "update",
  16. "delete",
  17. "drop",
  18. "alter",
  19. "create",
  20. "truncate",
  21. "replace",
  22. "grant",
  23. "revoke",
  24. )
  25. if not lowered_sql.startswith(readonly_prefixes):
  26. return False
  27. if ";" in cleaned_sql:
  28. return False
  29. return not any(f" {keyword} " in f" {lowered_sql} " for keyword in forbidden_keywords)
  30. @tool
  31. def list_tables() -> str:
  32. """
  33. 列出当前数据库中可以使用的所有表名。
  34. 当用户询问数据库里有哪些表、需要做数据分析、需要查询某个业务数据,
  35. 或者你不确定应该查询哪张表时,应该先调用这个工具。
  36. 这个工具不需要参数,只返回表名列表,不会查询表里的具体数据。
  37. """
  38. db = get_database()
  39. table_names = db.get_usable_table_names()
  40. return "\n".join(table_names) if table_names else "当前数据库中没有可用的数据表。"
  41. @tool
  42. def get_table_schema(table_name: str) -> str:
  43. """
  44. 查看指定数据表的字段结构、字段类型和部分样例信息。
  45. 当你准备生成 SQL 之前,应该先调用这个工具确认表结构,
  46. 不要凭空猜测字段名。参数 table_name 必须是数据库中真实存在的表名,
  47. 例如:chat_history。一次只传入一个表名。
  48. """
  49. db = get_database()
  50. return db.get_table_info([table_name])
  51. @tool
  52. def ask_database(sql: str) -> str:
  53. """
  54. 执行只读 SQL 查询并返回查询结果。
  55. 当你已经知道要查询的表和字段后,调用这个工具执行 SQL。
  56. 参数 sql 必须是一条完整的只读 SQL 语句,只允许 SELECT、SHOW、
  57. DESCRIBE、DESC、EXPLAIN。不要传入 INSERT、UPDATE、DELETE、DROP、
  58. ALTER、CREATE 等会修改数据库的语句。查询数据时建议使用 LIMIT 50
  59. 限制返回行数,避免一次返回太多数据。
  60. """
  61. if not is_readonly_sql(sql):
  62. return "拒绝执行:只允许单条只读 SQL,例如 SELECT、SHOW、DESCRIBE、EXPLAIN。"
  63. db = get_database()
  64. return db.run(sql)