tool_factory.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. import os
  2. import sys
  3. import importlib
  4. import inspect
  5. from pathlib import Path
  6. from typing import List
  7. from langchain.tools import BaseTool
  8. def get_all_tools() -> List[BaseTool]:
  9. """
  10. 自动发现并返回所有工具 - 修复检查逻辑
  11. """
  12. tools = []
  13. print("🛠️ 开始自动发现工具...")
  14. # 获取项目根目录
  15. project_root = Path(__file__).parent.parent
  16. tools_dir = Path(__file__).parent
  17. # 将项目根目录添加到Python路径
  18. if str(project_root) not in sys.path:
  19. sys.path.insert(0, str(project_root))
  20. # 扫描工具文件
  21. tool_files = []
  22. # 模式1: 编译前的.py文件
  23. for file_path in tools_dir.glob("*_tools.py"):
  24. if file_path.is_file():
  25. module_name = file_path.stem
  26. tool_files.append(module_name)
  27. print(f"📦 发现工具文件: {module_name}")
  28. # 模式2: 编译后的.pyd文件
  29. for file_path in tools_dir.glob("*_tools.cp*.pyd"):
  30. if file_path.is_file():
  31. # 从文件名中提取模块名,如: ware_tools.cp313-win_amd64.pyd -> ware_tools
  32. module_name = file_path.stem.split(".")[0]
  33. if module_name not in tool_files: # 避免重复添加
  34. tool_files.append(module_name)
  35. print(f"📦 发现编译后工具文件: {module_name}")
  36. if not tool_files:
  37. tool_files = ["knowledge_tools", "sale_tools", "ware_tools"]
  38. print("⚠️ 使用默认工具列表")
  39. for module_name in tool_files:
  40. try:
  41. # 导入模块
  42. full_module_path = f"tools.{module_name}"
  43. module = importlib.import_module(full_module_path)
  44. # print(f"✅ 加载模块: {module_name}")
  45. # 查找工具 - 使用更全面的方法
  46. tool_count = 0
  47. # 方法1: 检查模块的所有属性
  48. for attr_name in dir(module):
  49. if attr_name.startswith("_"):
  50. continue
  51. attr = getattr(module, attr_name)
  52. # # 详细调试信息
  53. # print(f" 🔍 检查 {attr_name}:")
  54. # print(f" 类型: {type(attr)}")
  55. # 检查是否是BaseTool实例
  56. if isinstance(attr, BaseTool):
  57. tools.append(attr)
  58. tool_count += 1
  59. # print(f" ✅ 发现BaseTool工具: {getattr(attr, 'name', attr_name)}")
  60. continue
  61. # 检查是否是函数且具有工具属性
  62. if callable(attr):
  63. # print(f" 是否有name属性: {hasattr(attr, 'name')}")
  64. # if hasattr(attr, "name"):
  65. # print(f" name值: {getattr(attr, 'name', '无')}")
  66. # print(f" 是否有description属性: {hasattr(attr, 'description')}")
  67. # if hasattr(attr, "description"):
  68. # print(
  69. # f" description前50字: {getattr(attr, 'description', '')[:50]}"
  70. # )
  71. # 检查是否是工具函数
  72. if is_tool_function(attr):
  73. tools.append(attr)
  74. tool_count += 1
  75. # print(f" ✅ 发现工具函数: {attr_name}")
  76. # 方法2: 检查模块的全局变量
  77. print(f" 🔍 检查模块全局变量...")
  78. for name, value in module.__dict__.items():
  79. if name.startswith("_"):
  80. continue
  81. if isinstance(value, BaseTool):
  82. if value not in tools: # 避免重复添加
  83. tools.append(value)
  84. tool_count += 1
  85. # print(
  86. # f" ✅ 从全局变量发现BaseTool工具: {getattr(value, 'name', name)}"
  87. # )
  88. if tool_count == 0:
  89. # print(f" ⚠️ 模块 {module_name} 中未发现工具")
  90. # 尝试手动创建工具
  91. manual_tools = create_tools_manually(module_name, module)
  92. if manual_tools:
  93. tools.extend(manual_tools)
  94. tool_count = len(manual_tools)
  95. # print(f" 🔧 手动创建了 {tool_count} 个工具")
  96. else:
  97. print(f" 📊 模块 {module_name} 中发现 {tool_count} 个工具")
  98. except Exception as e:
  99. print(f"❌ 加载模块 {module_name} 失败: {e}")
  100. print(f"🎯 总共发现 {len(tools)} 个工具")
  101. # # 打印工具详情
  102. # for i, tool in enumerate(tools):
  103. # tool_name = getattr(tool, "name", f"tool_{i+1}")
  104. # tool_desc = getattr(tool, "description", "无描述")
  105. # print(f" {i+1}. {tool_name}: {tool_desc[:50]}...")
  106. return tools
  107. def create_tools_manually(module_name: str, module) -> List[BaseTool]:
  108. """手动创建工具 - 针对@tool装饰器的问题"""
  109. tools = []
  110. if module_name == "knowledge_tools":
  111. # 手动导入并创建知识库工具
  112. try:
  113. from langchain.tools import tool
  114. # 检查模块中是否有工具函数
  115. if hasattr(module, "get_knowledge_list"):
  116. func = getattr(module, "get_knowledge_list")
  117. if callable(func):
  118. # 使用@tool装饰器重新创建工具
  119. tool_instance = tool(func)
  120. tools.append(tool_instance)
  121. print(f" 🔧 手动创建工具: get_knowledge_list")
  122. if hasattr(module, "get_knowledge_content"):
  123. func = getattr(module, "get_knowledge_content")
  124. if callable(func):
  125. tool_instance = tool(func)
  126. tools.append(tool_instance)
  127. print(f" 🔧 手动创建工具: get_knowledge_content")
  128. except Exception as e:
  129. print(f" ❌ 手动创建知识库工具失败: {e}")
  130. elif module_name == "sale_tools":
  131. # 手动创建销售工具
  132. try:
  133. from langchain.tools import tool
  134. if hasattr(module, "get_sale_amt"):
  135. func = getattr(module, "get_sale_amt")
  136. if callable(func):
  137. tool_instance = tool(func)
  138. tools.append(tool_instance)
  139. print(f" 🔧 手动创建工具: get_sale_amt")
  140. except Exception as e:
  141. print(f" ❌ 手动创建销售工具失败: {e}")
  142. return tools
  143. def is_tool_instance(obj) -> bool:
  144. """检查是否是工具实例"""
  145. if not callable(obj):
  146. return False
  147. # 检查是否是BaseTool实例
  148. if isinstance(obj, BaseTool):
  149. return True
  150. # 检查是否有工具的标准属性
  151. tool_attrs = ["name", "description"]
  152. if all(hasattr(obj, attr) for attr in tool_attrs):
  153. return True
  154. return False
  155. def is_tool_function(obj) -> bool:
  156. """检查是否是工具函数"""
  157. if not callable(obj):
  158. return False
  159. # 检查是否被@tool装饰
  160. if hasattr(obj, "_is_tool") and getattr(obj, "_is_tool", False):
  161. return True
  162. # 检查是否有tool属性
  163. if hasattr(obj, "tool"):
  164. return True
  165. # 检查是否是函数且具有工具属性
  166. if callable(obj) and hasattr(obj, "name") and hasattr(obj, "description"):
  167. return True
  168. return False
  169. # 测试函数
  170. def test_tool_detection():
  171. """测试工具检测"""
  172. print("🧪 测试工具检测...")
  173. # 导入一个模块测试
  174. import tools.knowledge_tools as kt
  175. print(f"模块: {kt}")
  176. for attr_name in dir(kt):
  177. if not attr_name.startswith("_"):
  178. attr = getattr(kt, attr_name)
  179. print(f"\n🔍 检查 {attr_name}:")
  180. print(f" 类型: {type(attr)}")
  181. print(f" 可调用: {callable(attr)}")
  182. if callable(attr):
  183. print(f" 是否有name属性: {hasattr(attr, 'name')}")
  184. if hasattr(attr, "name"):
  185. print(f" name值: {getattr(attr, 'name', '无')}")
  186. print(f" 是否有description属性: {hasattr(attr, 'description')}")
  187. if hasattr(attr, "description"):
  188. print(
  189. f" description前50字: {getattr(attr, 'description', '')[:50]}"
  190. )
  191. print(f" 是否被@tool装饰: {is_tool_function(attr)}")
  192. print(f" 是否是工具实例: {is_tool_instance(attr)}")