base_tool.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. import re
  2. import html
  3. import requests
  4. import json
  5. from typing import List, Dict, Any, Optional, Callable
  6. from pathlib import Path
  7. def html_to_text(html_content: str) -> str:
  8. """HTML转文本"""
  9. if not html_content:
  10. return ""
  11. clean = re.compile(r"<[^>]+>")
  12. text = clean.sub("", html_content)
  13. text = html.unescape(text)
  14. return re.sub(r"\s+", " ", text).strip()
  15. def get_unique_match_count(search_text: str, filter_words: List[str]) -> int:
  16. """获取唯一匹配计数"""
  17. sorted_keywords = sorted(filter_words, key=len, reverse=True)
  18. match_count = 0
  19. remaining_text = search_text.lower()
  20. for keyword in sorted_keywords:
  21. kw_lower = keyword.lower()
  22. if kw_lower in remaining_text:
  23. match_count += 1
  24. remaining_text = remaining_text.replace(kw_lower, "", 1)
  25. return match_count
  26. def call_csharp_api(
  27. backend_url: str, token: str, uoName: str, functionName: str, SParms: dict
  28. ) -> str:
  29. """调用C# API的通用方法"""
  30. print(f"🔧 API调用调试信息:")
  31. print(f" - 后端地址: {backend_url}")
  32. print(f" - Token: {'已配置' if token else '未配置'}")
  33. print(f" - 功能: {functionName}")
  34. print(f" - 参数: {SParms}")
  35. if not backend_url or not token:
  36. error_msg = f"错误:未配置后端地址或认证令牌。后端: {backend_url or '未配置'}, Token: {'已配置' if token else '未配置'}"
  37. print(f"❌ {error_msg}")
  38. return error_msg
  39. headers = {
  40. "Accept": "application/json, text/plain, */*",
  41. "Content-Type": "application/json",
  42. "X-TOKEN": token,
  43. "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
  44. }
  45. payload = {
  46. "token": token,
  47. "CList": [
  48. {
  49. "uoName": uoName,
  50. "functionName": functionName,
  51. "SParms": SParms,
  52. "ifcommit": True,
  53. "returnStrList": [],
  54. }
  55. ],
  56. "language": "zh-cn",
  57. }
  58. try:
  59. print(f"🌐 发送API请求到: {backend_url}")
  60. response = requests.post(backend_url, headers=headers, json=payload, timeout=30)
  61. print(f"📡 响应状态码: {response.status_code}")
  62. if response.status_code == 200:
  63. data = response.json()
  64. return process_api_response(data)
  65. else:
  66. error_msg = f"API请求失败,状态码: {response.status_code}"
  67. print(f"❌ {error_msg}")
  68. return error_msg
  69. except Exception as e:
  70. error_msg = f"API调用异常: {str(e)}"
  71. print(f"❌ {error_msg}")
  72. return error_msg
  73. def process_api_response(data: Dict[str, Any]) -> str:
  74. """处理API响应"""
  75. try:
  76. inner_json_str = data.get("reJob", {}).get("0", "{}")
  77. inner_data = json.loads(inner_json_str)
  78. if "err_msg" in inner_data:
  79. return f"API返回错误: {inner_data['err_msg']}"
  80. if "data" in inner_data:
  81. data_list = inner_data["data"]
  82. if not data_list:
  83. return "NO_DATA"
  84. if isinstance(data_list[0], dict):
  85. headers = list(data_list[0].keys())
  86. result = [",".join(headers)]
  87. for row in data_list:
  88. result.append(",".join([str(row.get(h, "")) for h in headers]))
  89. return "\n".join(result)
  90. return json.dumps(data, ensure_ascii=False)
  91. except Exception as e:
  92. return f"响应处理错误: {str(e)}"
  93. # 工具配置管理函数
  94. def load_tool_config(
  95. config_path: Path, get_default_config: Optional[Callable] = None
  96. ) -> Dict[str, Any]:
  97. """
  98. 加载工具配置的通用函数
  99. Args:
  100. config_path: 配置文件路径
  101. get_default_config: 获取默认配置的回调函数,如果不提供则返回空字典
  102. """
  103. if not config_path.exists():
  104. print(f"警告: 配置文件不存在: {config_path}")
  105. if get_default_config:
  106. return get_default_config()
  107. return {}
  108. try:
  109. with open(config_path, "r", encoding="utf-8") as f:
  110. return json.load(f)
  111. except json.JSONDecodeError as e:
  112. print(f"错误: 配置文件格式不正确: {e}")
  113. if get_default_config:
  114. return get_default_config()
  115. return {}
  116. except Exception as e:
  117. print(f"错误: 读取配置文件失败: {e}")
  118. if get_default_config:
  119. return get_default_config()
  120. return {}
  121. def assemble_tool_description(tool_config: Dict[str, Any]) -> str:
  122. """组装工具描述,将所有键值组合成一个完整的字符串"""
  123. if not tool_config:
  124. return ""
  125. description_parts = []
  126. # 基础描述
  127. if "基础描述" in tool_config:
  128. description_parts.append(tool_config["基础描述"])
  129. # 功能说明
  130. if "功能说明" in tool_config:
  131. description_parts.append(f"\n功能: {tool_config['功能说明']}")
  132. # 入参说明
  133. if "入参说明" in tool_config:
  134. if isinstance(tool_config["入参说明"], dict):
  135. description_parts.append("\n参数:")
  136. for param, desc in tool_config["入参说明"].items():
  137. description_parts.append(f" {param}: {desc}")
  138. else:
  139. description_parts.append(f"\n参数说明: {tool_config['入参说明']}")
  140. # 返回值说明
  141. if "返回值说明" in tool_config:
  142. if isinstance(tool_config["返回值说明"], dict):
  143. description_parts.append("\n返回:")
  144. for key, value in tool_config["返回值说明"].items():
  145. if isinstance(value, list):
  146. description_parts.append(f" {key}:")
  147. for item in value:
  148. description_parts.append(f" - {item}")
  149. else:
  150. description_parts.append(f" {key}: {value}")
  151. else:
  152. description_parts.append(f"\n返回结果: {tool_config['返回值说明']}")
  153. # 输出格式要求
  154. if "输出格式要求" in tool_config:
  155. if isinstance(tool_config["输出格式要求"], list):
  156. description_parts.append("\n输出要求:")
  157. for requirement in tool_config["输出格式要求"]:
  158. description_parts.append(f" - {requirement}")
  159. else:
  160. description_parts.append(f"\n注意: {tool_config['输出格式要求']}")
  161. # 使用示例
  162. if "使用示例" in tool_config:
  163. description_parts.append(f"\n示例: {tool_config['使用示例']}")
  164. return "\n".join(description_parts)
  165. def get_tool_prompt(
  166. tool_name: str, default_config_func: Optional[Callable] = None
  167. ) -> str:
  168. """
  169. 获取工具的完整提示词
  170. Args:
  171. tool_name: 工具名称
  172. default_config_func: 获取默认配置的函数
  173. """
  174. # 计算配置文件路径
  175. current_file = Path(__file__)
  176. config_path = current_file.parent.parent / "config" / "tool_config.json"
  177. # 加载配置
  178. config = load_tool_config(config_path, default_config_func)
  179. # 获取工具配置
  180. tool_config = config.get(tool_name, {})
  181. # 如果配置为空且提供了默认配置函数,使用默认配置
  182. if not tool_config and default_config_func:
  183. default_config = default_config_func()
  184. if isinstance(default_config, dict) and tool_name in default_config:
  185. tool_config = default_config[tool_name]
  186. elif isinstance(default_config, dict) and not default_config:
  187. # 如果返回的是整个配置字典
  188. tool_config = default_config
  189. else:
  190. tool_config = {}
  191. # 组装描述
  192. if tool_config:
  193. return assemble_tool_description(tool_config)
  194. else:
  195. return f"执行 {tool_name} 功能"