test_valid_sql_standalone.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. #!/usr/bin/env python3
  2. """
  3. 独立测试 valid_sql 错误处理流程
  4. 不修改任何现有代码,只模拟测试场景
  5. """
  6. import asyncio
  7. import logging
  8. import json
  9. # 配置日志
  10. logging.basicConfig(
  11. level=logging.INFO,
  12. format='%(asctime)s - %(levelname)s - %(message)s'
  13. )
  14. logger = logging.getLogger(__name__)
  15. class MockValidSqlTool:
  16. """模拟 valid_sql 工具的行为"""
  17. @staticmethod
  18. def valid_sql(sql: str) -> str:
  19. """模拟 valid_sql 工具的验证逻辑"""
  20. logger.info(f"🔧 [Mock Tool] valid_sql - 待验证SQL: {sql}")
  21. # 模拟语法错误检测
  22. if "AS service_alias" in sql and "WHERE" in sql:
  23. logger.warning(" SQL验证失败:语法错误 - WHERE子句后不能直接使用AS别名")
  24. return "SQL验证失败:语法错误。详细错误:syntax error at or near \"AS\""
  25. # 模拟表不存在检测
  26. if "non_existent_table" in sql:
  27. logger.warning(" SQL验证失败:表不存在")
  28. return "SQL验证失败:表不存在。详细错误:relation \"non_existent_table\" does not exist"
  29. # 模拟字段不存在检测
  30. if "non_existent_field" in sql:
  31. logger.warning(" SQL验证失败:字段不存在")
  32. return "SQL验证失败:字段不存在。详细错误:column \"non_existent_field\" does not exist"
  33. logger.info(" ✅ SQL验证通过")
  34. return "SQL验证通过:语法正确且字段存在"
  35. class MockLLM:
  36. """模拟 LLM 的响应行为"""
  37. @staticmethod
  38. async def respond_to_validation_error(question: str, error_sql: str, error_message: str) -> str:
  39. """模拟 LLM 对验证错误的响应"""
  40. logger.info(f"🧠 [Mock LLM] 处理验证错误")
  41. logger.info(f"问题: {question}")
  42. logger.info(f"错误SQL: {error_sql}")
  43. logger.info(f"错误信息: {error_message}")
  44. # 模拟不同类型的错误处理
  45. if "语法错误" in error_message:
  46. if "AS service_alias" in error_sql:
  47. response = """我发现了SQL语法错误。在WHERE子句后不能直接使用AS别名。
  48. 正确的SQL应该是:
  49. ```sql
  50. SELECT service_name, pay_sum FROM bss_business_day_data WHERE service_name = '庐山服务区'
  51. ```
  52. 或者如果需要别名,应该这样写:
  53. ```sql
  54. SELECT service_name AS service_alias, pay_sum FROM bss_business_day_data WHERE service_name = '庐山服务区'
  55. ```
  56. 问题在于AS别名应该在SELECT子句中定义,而不是在WHERE子句后。"""
  57. elif "表不存在" in error_message:
  58. response = """抱歉,您查询的表不存在。根据我的了解,系统中没有名为"non_existent_table"的表。
  59. 可用的表包括:
  60. - bss_business_day_data (业务日数据表)
  61. - bss_car_day_count (车辆日统计表)
  62. - bss_company (公司信息表)
  63. 请确认您要查询的表名是否正确。"""
  64. elif "字段不存在" in error_message:
  65. response = """抱歉,您查询的字段不存在。根据我的了解,bss_business_day_data表中没有名为"non_existent_field"的字段。
  66. 该表的主要字段包括:
  67. - service_name (服务区名称)
  68. - pay_sum (支付金额)
  69. - business_date (业务日期)
  70. 请确认您要查询的字段名是否正确。"""
  71. else:
  72. response = f"SQL验证失败:{error_message}。请检查SQL语句的语法和字段名称。"
  73. logger.info(f"🤖 [Mock LLM] 响应: {response[:100]}...")
  74. return response
  75. class StandaloneValidSqlTester:
  76. """独立的 valid_sql 测试类"""
  77. def __init__(self):
  78. self.mock_valid_sql = MockValidSqlTool()
  79. self.mock_llm = MockLLM()
  80. def test_valid_sql_direct(self, sql: str) -> str:
  81. """直接测试 valid_sql 工具"""
  82. logger.info(f"🔧 直接测试 valid_sql 工具")
  83. logger.info(f"SQL: {sql}")
  84. result = self.mock_valid_sql.valid_sql(sql)
  85. logger.info(f"结果: {result}")
  86. return result
  87. async def test_llm_response_to_error(self, question: str, error_sql: str, error_message: str):
  88. """测试 LLM 对验证错误的响应"""
  89. logger.info(f"🧠 测试 LLM 对验证错误的响应")
  90. response = await self.mock_llm.respond_to_validation_error(question, error_sql, error_message)
  91. return response
  92. async def test_three_scenarios():
  93. """测试三种错误场景"""
  94. logger.info("🧪 测试三种 valid_sql 错误场景")
  95. # 三种测试用例
  96. test_cases = [
  97. {
  98. "name": "表不存在",
  99. "question": "查询员工表的信息",
  100. "sql": "SELECT * FROM non_existent_table LIMIT 1"
  101. },
  102. {
  103. "name": "字段不存在",
  104. "question": "查询每个服务区的经理姓名",
  105. "sql": "SELECT non_existent_field FROM bss_business_day_data LIMIT 1"
  106. },
  107. {
  108. "name": "语法错误",
  109. "question": "查询服务区数据 WHERE",
  110. "sql": "SELECT service_name, pay_sum FROM bss_business_day_data WHERE service_name = '庐山服务区' AS service_alias"
  111. }
  112. ]
  113. tester = StandaloneValidSqlTester()
  114. for i, test_case in enumerate(test_cases, 1):
  115. logger.info(f"\n{'='*50}")
  116. logger.info(f"测试用例 {i}: {test_case['name']}")
  117. logger.info(f"{'='*50}")
  118. # 1. 直接测试 valid_sql
  119. direct_result = tester.test_valid_sql_direct(test_case["sql"])
  120. # 2. 测试 LLM 响应
  121. llm_response = await tester.test_llm_response_to_error(
  122. test_case["question"],
  123. test_case["sql"],
  124. direct_result
  125. )
  126. # 简单的结果分析
  127. logger.info(f"\n📊 结果分析:")
  128. if "失败" in direct_result:
  129. logger.info("✅ valid_sql 正确捕获错误")
  130. else:
  131. logger.warning("⚠️ valid_sql 可能未正确捕获错误")
  132. if llm_response and ("错误" in llm_response or "抱歉" in llm_response or "SQL" in llm_response):
  133. logger.info("✅ LLM 正确处理验证错误")
  134. else:
  135. logger.warning("⚠️ LLM 可能未正确处理验证错误")
  136. logger.info(f"\n📝 LLM 完整响应:")
  137. logger.info(llm_response)
  138. async def main():
  139. """主函数"""
  140. logger.info("🚀 独立 valid_sql 测试")
  141. await test_three_scenarios()
  142. logger.info("\n✅ 测试完成")
  143. if __name__ == "__main__":
  144. asyncio.run(main())