Undefined-bot 2.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. Undefined/__init__.py +3 -0
  2. Undefined/__main__.py +6 -0
  3. Undefined/ai.py +1215 -0
  4. Undefined/config.py +371 -0
  5. Undefined/end_summary_storage.py +48 -0
  6. Undefined/faq.py +244 -0
  7. Undefined/handlers.py +1247 -0
  8. Undefined/injection_response_agent.py +131 -0
  9. Undefined/main.py +126 -0
  10. Undefined/memory.py +120 -0
  11. Undefined/onebot.py +512 -0
  12. Undefined/rate_limit.py +130 -0
  13. Undefined/render.py +123 -0
  14. Undefined/scheduled_task_storage.py +88 -0
  15. Undefined/services/__init__.py +1 -0
  16. Undefined/services/queue_manager.py +206 -0
  17. Undefined/skills/README.md +53 -0
  18. Undefined/skills/__init__.py +10 -0
  19. Undefined/skills/agents/README.md +144 -0
  20. Undefined/skills/agents/__init__.py +116 -0
  21. Undefined/skills/agents/entertainment_agent/config.json +17 -0
  22. Undefined/skills/agents/entertainment_agent/handler.py +220 -0
  23. Undefined/skills/agents/entertainment_agent/intro.md +25 -0
  24. Undefined/skills/agents/entertainment_agent/prompt.md +20 -0
  25. Undefined/skills/agents/entertainment_agent/tools/__init__.py +1 -0
  26. Undefined/skills/agents/entertainment_agent/tools/ai_draw_one/config.json +34 -0
  27. Undefined/skills/agents/entertainment_agent/tools/ai_draw_one/handler.py +62 -0
  28. Undefined/skills/agents/entertainment_agent/tools/ai_study_helper/config.json +22 -0
  29. Undefined/skills/agents/entertainment_agent/tools/ai_study_helper/handler.py +35 -0
  30. Undefined/skills/agents/entertainment_agent/tools/get_current_time/config.json +12 -0
  31. Undefined/skills/agents/entertainment_agent/tools/get_current_time/handler.py +5 -0
  32. Undefined/skills/agents/entertainment_agent/tools/horoscope/config.json +24 -0
  33. Undefined/skills/agents/entertainment_agent/tools/horoscope/handler.py +141 -0
  34. Undefined/skills/agents/entertainment_agent/tools/minecraft_skin/config.json +43 -0
  35. Undefined/skills/agents/entertainment_agent/tools/minecraft_skin/handler.py +55 -0
  36. Undefined/skills/agents/entertainment_agent/tools/novel_search/config.json +25 -0
  37. Undefined/skills/agents/entertainment_agent/tools/novel_search/handler.py +31 -0
  38. Undefined/skills/agents/entertainment_agent/tools/renjian/config.json +12 -0
  39. Undefined/skills/agents/entertainment_agent/tools/renjian/handler.py +30 -0
  40. Undefined/skills/agents/entertainment_agent/tools/wenchang_dijun/config.json +12 -0
  41. Undefined/skills/agents/entertainment_agent/tools/wenchang_dijun/handler.py +44 -0
  42. Undefined/skills/agents/file_analysis_agent/__init__.py +1 -0
  43. Undefined/skills/agents/file_analysis_agent/config.json +21 -0
  44. Undefined/skills/agents/file_analysis_agent/handler.py +248 -0
  45. Undefined/skills/agents/file_analysis_agent/intro.md +22 -0
  46. Undefined/skills/agents/file_analysis_agent/prompt.md +36 -0
  47. Undefined/skills/agents/file_analysis_agent/tools/__init__.py +1 -0
  48. Undefined/skills/agents/file_analysis_agent/tools/analyze_code/config.json +17 -0
  49. Undefined/skills/agents/file_analysis_agent/tools/analyze_code/handler.py +427 -0
  50. Undefined/skills/agents/file_analysis_agent/tools/analyze_multimodal/config.json +25 -0
  51. Undefined/skills/agents/file_analysis_agent/tools/analyze_multimodal/handler.py +178 -0
  52. Undefined/skills/agents/file_analysis_agent/tools/cleanup_temp/config.json +16 -0
  53. Undefined/skills/agents/file_analysis_agent/tools/cleanup_temp/handler.py +35 -0
  54. Undefined/skills/agents/file_analysis_agent/tools/detect_file_type/config.json +17 -0
  55. Undefined/skills/agents/file_analysis_agent/tools/detect_file_type/handler.py +221 -0
  56. Undefined/skills/agents/file_analysis_agent/tools/download_file/config.json +21 -0
  57. Undefined/skills/agents/file_analysis_agent/tools/download_file/handler.py +124 -0
  58. Undefined/skills/agents/file_analysis_agent/tools/extract_archive/config.json +25 -0
  59. Undefined/skills/agents/file_analysis_agent/tools/extract_archive/handler.py +190 -0
  60. Undefined/skills/agents/file_analysis_agent/tools/extract_docx/config.json +17 -0
  61. Undefined/skills/agents/file_analysis_agent/tools/extract_docx/handler.py +78 -0
  62. Undefined/skills/agents/file_analysis_agent/tools/extract_pdf/config.json +21 -0
  63. Undefined/skills/agents/file_analysis_agent/tools/extract_pdf/handler.py +67 -0
  64. Undefined/skills/agents/file_analysis_agent/tools/extract_pptx/config.json +17 -0
  65. Undefined/skills/agents/file_analysis_agent/tools/extract_pptx/handler.py +73 -0
  66. Undefined/skills/agents/file_analysis_agent/tools/extract_xlsx/config.json +17 -0
  67. Undefined/skills/agents/file_analysis_agent/tools/extract_xlsx/handler.py +101 -0
  68. Undefined/skills/agents/file_analysis_agent/tools/get_current_time/config.json +12 -0
  69. Undefined/skills/agents/file_analysis_agent/tools/get_current_time/handler.py +5 -0
  70. Undefined/skills/agents/file_analysis_agent/tools/read_text_file/config.json +21 -0
  71. Undefined/skills/agents/file_analysis_agent/tools/read_text_file/handler.py +90 -0
  72. Undefined/skills/agents/info_agent/config.json +17 -0
  73. Undefined/skills/agents/info_agent/handler.py +220 -0
  74. Undefined/skills/agents/info_agent/intro.md +22 -0
  75. Undefined/skills/agents/info_agent/prompt.md +27 -0
  76. Undefined/skills/agents/info_agent/tools/__init__.py +1 -0
  77. Undefined/skills/agents/info_agent/tools/baiduhot/config.json +18 -0
  78. Undefined/skills/agents/info_agent/tools/baiduhot/handler.py +49 -0
  79. Undefined/skills/agents/info_agent/tools/base64/config.json +22 -0
  80. Undefined/skills/agents/info_agent/tools/base64/handler.py +44 -0
  81. Undefined/skills/agents/info_agent/tools/douyinhot/config.json +18 -0
  82. Undefined/skills/agents/info_agent/tools/douyinhot/handler.py +53 -0
  83. Undefined/skills/agents/info_agent/tools/get_current_time/config.json +12 -0
  84. Undefined/skills/agents/info_agent/tools/get_current_time/handler.py +5 -0
  85. Undefined/skills/agents/info_agent/tools/gold_price/config.json +12 -0
  86. Undefined/skills/agents/info_agent/tools/gold_price/handler.py +58 -0
  87. Undefined/skills/agents/info_agent/tools/hash/config.json +22 -0
  88. Undefined/skills/agents/info_agent/tools/hash/handler.py +43 -0
  89. Undefined/skills/agents/info_agent/tools/history/config.json +12 -0
  90. Undefined/skills/agents/info_agent/tools/history/handler.py +37 -0
  91. Undefined/skills/agents/info_agent/tools/net_check/config.json +17 -0
  92. Undefined/skills/agents/info_agent/tools/net_check/handler.py +117 -0
  93. Undefined/skills/agents/info_agent/tools/news_tencent/config.json +17 -0
  94. Undefined/skills/agents/info_agent/tools/news_tencent/handler.py +38 -0
  95. Undefined/skills/agents/info_agent/tools/qq_level_query/config.json +29 -0
  96. Undefined/skills/agents/info_agent/tools/qq_level_query/handler.py +48 -0
  97. Undefined/skills/agents/info_agent/tools/speed/config.json +17 -0
  98. Undefined/skills/agents/info_agent/tools/speed/handler.py +37 -0
  99. Undefined/skills/agents/info_agent/tools/tcping/config.json +21 -0
  100. Undefined/skills/agents/info_agent/tools/tcping/handler.py +53 -0
  101. Undefined/skills/agents/info_agent/tools/weather_query/config.json +22 -0
  102. Undefined/skills/agents/info_agent/tools/weather_query/handler.py +207 -0
  103. Undefined/skills/agents/info_agent/tools/weibohot/config.json +18 -0
  104. Undefined/skills/agents/info_agent/tools/weibohot/handler.py +49 -0
  105. Undefined/skills/agents/info_agent/tools/whois/config.json +17 -0
  106. Undefined/skills/agents/info_agent/tools/whois/handler.py +63 -0
  107. Undefined/skills/agents/naga_code_analysis_agent/config.json +17 -0
  108. Undefined/skills/agents/naga_code_analysis_agent/handler.py +222 -0
  109. Undefined/skills/agents/naga_code_analysis_agent/intro.md +17 -0
  110. Undefined/skills/agents/naga_code_analysis_agent/prompt.md +19 -0
  111. Undefined/skills/agents/naga_code_analysis_agent/tools/__init__.py +1 -0
  112. Undefined/skills/agents/naga_code_analysis_agent/tools/get_current_time/config.json +12 -0
  113. Undefined/skills/agents/naga_code_analysis_agent/tools/get_current_time/handler.py +5 -0
  114. Undefined/skills/agents/naga_code_analysis_agent/tools/glob/config.json +17 -0
  115. Undefined/skills/agents/naga_code_analysis_agent/tools/glob/handler.py +37 -0
  116. Undefined/skills/agents/naga_code_analysis_agent/tools/list_directory/config.json +17 -0
  117. Undefined/skills/agents/naga_code_analysis_agent/tools/list_directory/handler.py +31 -0
  118. Undefined/skills/agents/naga_code_analysis_agent/tools/read_file/config.json +17 -0
  119. Undefined/skills/agents/naga_code_analysis_agent/tools/read_file/handler.py +66 -0
  120. Undefined/skills/agents/naga_code_analysis_agent/tools/read_naga_intro/config.json +12 -0
  121. Undefined/skills/agents/naga_code_analysis_agent/tools/read_naga_intro/handler.py +327 -0
  122. Undefined/skills/agents/naga_code_analysis_agent/tools/search_file_content/config.json +25 -0
  123. Undefined/skills/agents/naga_code_analysis_agent/tools/search_file_content/handler.py +46 -0
  124. Undefined/skills/agents/scheduler_agent/__init__.py +1 -0
  125. Undefined/skills/agents/scheduler_agent/config.json +17 -0
  126. Undefined/skills/agents/scheduler_agent/handler.py +218 -0
  127. Undefined/skills/agents/scheduler_agent/intro.md +17 -0
  128. Undefined/skills/agents/scheduler_agent/prompt.md +67 -0
  129. Undefined/skills/agents/scheduler_agent/tools/__init__.py +1 -0
  130. Undefined/skills/agents/scheduler_agent/tools/create_schedule_task/config.json +37 -0
  131. Undefined/skills/agents/scheduler_agent/tools/create_schedule_task/handler.py +68 -0
  132. Undefined/skills/agents/scheduler_agent/tools/delete_schedule_task/config.json +19 -0
  133. Undefined/skills/agents/scheduler_agent/tools/delete_schedule_task/handler.py +26 -0
  134. Undefined/skills/agents/scheduler_agent/tools/get_current_time/config.json +12 -0
  135. Undefined/skills/agents/scheduler_agent/tools/get_current_time/handler.py +5 -0
  136. Undefined/skills/agents/scheduler_agent/tools/list_schedule_tasks/config.json +11 -0
  137. Undefined/skills/agents/scheduler_agent/tools/list_schedule_tasks/handler.py +47 -0
  138. Undefined/skills/agents/scheduler_agent/tools/update_schedule_task/config.json +39 -0
  139. Undefined/skills/agents/scheduler_agent/tools/update_schedule_task/handler.py +46 -0
  140. Undefined/skills/agents/social_agent/config.json +17 -0
  141. Undefined/skills/agents/social_agent/handler.py +220 -0
  142. Undefined/skills/agents/social_agent/intro.md +17 -0
  143. Undefined/skills/agents/social_agent/prompt.md +19 -0
  144. Undefined/skills/agents/social_agent/tools/__init__.py +1 -0
  145. Undefined/skills/agents/social_agent/tools/bilibili_search/config.json +21 -0
  146. Undefined/skills/agents/social_agent/tools/bilibili_search/handler.py +68 -0
  147. Undefined/skills/agents/social_agent/tools/bilibili_user_info/config.json +17 -0
  148. Undefined/skills/agents/social_agent/tools/bilibili_user_info/handler.py +68 -0
  149. Undefined/skills/agents/social_agent/tools/get_current_time/config.json +12 -0
  150. Undefined/skills/agents/social_agent/tools/get_current_time/handler.py +5 -0
  151. Undefined/skills/agents/social_agent/tools/music_global_search/config.json +21 -0
  152. Undefined/skills/agents/social_agent/tools/music_global_search/handler.py +47 -0
  153. Undefined/skills/agents/social_agent/tools/music_info_get/config.json +22 -0
  154. Undefined/skills/agents/social_agent/tools/music_info_get/handler.py +35 -0
  155. Undefined/skills/agents/social_agent/tools/music_lyrics/config.json +22 -0
  156. Undefined/skills/agents/social_agent/tools/music_lyrics/handler.py +26 -0
  157. Undefined/skills/agents/social_agent/tools/video_random_recommend/config.json +21 -0
  158. Undefined/skills/agents/social_agent/tools/video_random_recommend/handler.py +21 -0
  159. Undefined/skills/agents/web_agent/config.json +17 -0
  160. Undefined/skills/agents/web_agent/handler.py +221 -0
  161. Undefined/skills/agents/web_agent/intro.md +14 -0
  162. Undefined/skills/agents/web_agent/prompt.md +16 -0
  163. Undefined/skills/agents/web_agent/tools/__init__.py +1 -0
  164. Undefined/skills/agents/web_agent/tools/crawl_webpage/config.json +21 -0
  165. Undefined/skills/agents/web_agent/tools/crawl_webpage/handler.py +102 -0
  166. Undefined/skills/agents/web_agent/tools/get_current_time/config.json +12 -0
  167. Undefined/skills/agents/web_agent/tools/get_current_time/handler.py +5 -0
  168. Undefined/skills/agents/web_agent/tools/web_search/config.json +21 -0
  169. Undefined/skills/agents/web_agent/tools/web_search/handler.py +29 -0
  170. Undefined/skills/tools/README.md +85 -0
  171. Undefined/skills/tools/__init__.py +120 -0
  172. Undefined/skills/tools/debug/config.json +17 -0
  173. Undefined/skills/tools/debug/handler.py +35 -0
  174. Undefined/skills/tools/end/config.json +17 -0
  175. Undefined/skills/tools/end/handler.py +24 -0
  176. Undefined/skills/tools/get_current_time/config.json +12 -0
  177. Undefined/skills/tools/get_current_time/handler.py +5 -0
  178. Undefined/skills/tools/get_forward_msg/config.json +17 -0
  179. Undefined/skills/tools/get_forward_msg/handler.py +131 -0
  180. Undefined/skills/tools/get_group_member_info/config.json +38 -0
  181. Undefined/skills/tools/get_group_member_info/handler.py +142 -0
  182. Undefined/skills/tools/get_messages_by_time/config.json +30 -0
  183. Undefined/skills/tools/get_messages_by_time/handler.py +128 -0
  184. Undefined/skills/tools/get_picture/config.json +45 -0
  185. Undefined/skills/tools/get_picture/handler.py +191 -0
  186. Undefined/skills/tools/get_recent_messages/config.json +30 -0
  187. Undefined/skills/tools/get_recent_messages/handler.py +88 -0
  188. Undefined/skills/tools/qq_like/config.json +22 -0
  189. Undefined/skills/tools/qq_like/handler.py +58 -0
  190. Undefined/skills/tools/render_html/config.json +26 -0
  191. Undefined/skills/tools/render_html/handler.py +39 -0
  192. Undefined/skills/tools/render_latex/config.json +26 -0
  193. Undefined/skills/tools/render_latex/handler.py +78 -0
  194. Undefined/skills/tools/render_markdown/config.json +26 -0
  195. Undefined/skills/tools/render_markdown/handler.py +63 -0
  196. Undefined/skills/tools/save_memory/config.json +17 -0
  197. Undefined/skills/tools/save_memory/handler.py +17 -0
  198. Undefined/skills/tools/send_message/config.json +21 -0
  199. Undefined/skills/tools/send_message/handler.py +60 -0
  200. Undefined/skills/tools/send_private_message/config.json +21 -0
  201. Undefined/skills/tools/send_private_message/handler.py +35 -0
  202. Undefined/utils/__init__.py +0 -0
  203. Undefined/utils/common.py +186 -0
  204. Undefined/utils/history.py +284 -0
  205. Undefined/utils/scheduler.py +286 -0
  206. Undefined/utils/sender.py +140 -0
  207. undefined_bot-2.1.0.dist-info/METADATA +259 -0
  208. undefined_bot-2.1.0.dist-info/RECORD +211 -0
  209. undefined_bot-2.1.0.dist-info/WHEEL +4 -0
  210. undefined_bot-2.1.0.dist-info/entry_points.txt +2 -0
  211. undefined_bot-2.1.0.dist-info/licenses/LICENSE +7 -0
@@ -0,0 +1,21 @@
1
+ from typing import Any, Dict
2
+ import httpx
3
+ import logging
4
+
5
+ logger = logging.getLogger(__name__)
6
+
7
+
8
+ async def execute(args: Dict[str, Any], context: Dict[str, Any]) -> str:
9
+ url = "https://api.jkyai.top/API/jxhssp.php"
10
+
11
+ try:
12
+ async with httpx.AsyncClient(timeout=15.0, follow_redirects=True) as client:
13
+ # 我们只需要最终的 URL,所以我们触发请求并检查历史或 url
14
+ response = await client.get(url)
15
+ final_url = str(response.url)
16
+
17
+ return f"🎥 随机视频推荐:\n{final_url}"
18
+
19
+ except Exception as e:
20
+ logger.exception(f"获取视频失败: {e}")
21
+ return f"获取视频失败: {e}"
@@ -0,0 +1,17 @@
1
+ {
2
+ "type": "function",
3
+ "function": {
4
+ "name": "web_agent",
5
+ "description": "网络搜索助手,提供网页搜索和网页内容获取功能,用于获取互联网上的最新信息。",
6
+ "parameters": {
7
+ "type": "object",
8
+ "properties": {
9
+ "prompt": {
10
+ "type": "string",
11
+ "description": "用户的搜索需求,例如:'搜索 Python 异步编程的最新发展'、'获取 https://example.com 的内容'"
12
+ }
13
+ },
14
+ "required": ["prompt"]
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,221 @@
1
+ from typing import Any, Dict, Callable
2
+ import importlib.util
3
+ import json
4
+ import asyncio
5
+ import aiofiles
6
+ import logging
7
+ from pathlib import Path
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class AgentToolRegistry:
13
+ """Agent 内部的工具注册表"""
14
+
15
+ def __init__(self, tools_dir: Path) -> None:
16
+ self.tools_dir: Path = (
17
+ tools_dir if isinstance(tools_dir, Path) else Path(tools_dir)
18
+ )
19
+ self._tools_schema: list[dict[str, Any]] = []
20
+ self._tools_handlers: dict[str, Callable[..., Any]] = {}
21
+ self.load_tools()
22
+
23
+ def load_tools(self) -> None:
24
+ """加载 agent 专属工具"""
25
+ if not self.tools_dir.exists():
26
+ logger.warning(f"Agent tools directory does not exist: {self.tools_dir}")
27
+ return
28
+
29
+ for item in self.tools_dir.iterdir():
30
+ if item.is_dir() and not item.name.startswith("_"):
31
+ self._load_tool_from_dir(item)
32
+
33
+ logger.info(
34
+ f"Agent loaded {len(self._tools_schema)} tools: {list(self._tools_handlers.keys())}"
35
+ )
36
+
37
+ def _load_tool_from_dir(self, tool_dir: Path) -> None:
38
+ """从目录加载工具"""
39
+ config_path: Path = tool_dir / "config.json"
40
+ handler_path: Path = tool_dir / "handler.py"
41
+
42
+ if not config_path.exists() or not handler_path.exists():
43
+ return
44
+
45
+ try:
46
+ with open(config_path, "r", encoding="utf-8") as f:
47
+ config: dict[str, Any] = json.load(f)
48
+
49
+ if "function" not in config or "name" not in config.get("function", {}):
50
+ return
51
+
52
+ tool_name: str = config["function"]["name"]
53
+
54
+ spec = importlib.util.spec_from_file_location(
55
+ f"agent_tools.{tool_name}", handler_path
56
+ )
57
+ if spec is None or spec.loader is None:
58
+ return
59
+
60
+ module = importlib.util.module_from_spec(spec)
61
+ spec.loader.exec_module(module)
62
+
63
+ if not hasattr(module, "execute"):
64
+ return
65
+
66
+ self._tools_schema.append(config)
67
+ self._tools_handlers[tool_name] = module.execute
68
+
69
+ except Exception as e:
70
+ logger.error(f"Failed to load tool from {tool_dir}: {e}")
71
+
72
+ def get_tools_schema(self) -> list[dict[str, Any]]:
73
+ """获取工具 schema"""
74
+ return self._tools_schema
75
+
76
+ async def execute_tool(
77
+ self, tool_name: str, args: dict[str, Any], context: dict[str, Any]
78
+ ) -> str:
79
+ """执行工具"""
80
+ handler = self._tools_handlers.get(tool_name)
81
+ if not handler:
82
+ return f"Tool not found: {tool_name}"
83
+
84
+ try:
85
+ if asyncio.iscoroutinefunction(handler):
86
+ result = await handler(args, context)
87
+ else:
88
+ result = handler(args, context)
89
+ return str(result)
90
+ except Exception as e:
91
+ logger.exception(f"Error executing tool {tool_name}")
92
+ return f"Error executing tool {tool_name}: {str(e)}"
93
+
94
+
95
+ async def _load_prompt() -> str:
96
+ """从 prompt.md 文件加载系统提示词"""
97
+ prompt_path: Path = Path(__file__).parent / "prompt.md"
98
+ if prompt_path.exists():
99
+ async with aiofiles.open(prompt_path, "r", encoding="utf-8") as f:
100
+ return await f.read()
101
+ return _get_default_prompt()
102
+
103
+
104
+ def _get_default_prompt() -> str:
105
+ """默认提示词"""
106
+ return "你是一个网络搜索助手..."
107
+
108
+
109
+ async def execute(args: Dict[str, Any], context: Dict[str, Any]) -> str:
110
+ """执行 web_agent"""
111
+ user_prompt: str = args.get("prompt", "")
112
+
113
+ if not user_prompt:
114
+ return "请提供您的搜索需求"
115
+
116
+ agent_tools_dir: Path = Path(__file__).parent / "tools"
117
+ tool_registry = AgentToolRegistry(agent_tools_dir)
118
+
119
+ tools: list[dict[str, Any]] = tool_registry.get_tools_schema()
120
+
121
+ ai_client = context.get("ai_client")
122
+ if not ai_client:
123
+ return "AI client 未在上下文中提供"
124
+
125
+ agent_config = ai_client.agent_config
126
+
127
+
128
+ system_prompt: str = await _load_prompt()
129
+
130
+ messages: list[dict[str, Any]] = [
131
+ {"role": "system", "content": system_prompt},
132
+ {"role": "user", "content": f"用户需求:{user_prompt}"},
133
+ ]
134
+
135
+ max_iterations: int = 20
136
+ iteration: int = 0
137
+
138
+ while iteration < max_iterations:
139
+ iteration += 1
140
+
141
+ try:
142
+ response = await ai_client._http_client.post(
143
+ agent_config.api_url,
144
+ headers={
145
+ "Authorization": f"Bearer {agent_config.api_key}",
146
+ "Content-Type": "application/json",
147
+ },
148
+ json=ai_client._build_request_body(
149
+ model_config=agent_config,
150
+ messages=messages,
151
+ max_tokens=agent_config.max_tokens,
152
+ tools=tools if tools else None,
153
+ tool_choice="auto",
154
+ ),
155
+ )
156
+ response.raise_for_status()
157
+ result: dict[str, Any] = response.json()
158
+
159
+ choice: dict[str, Any] = result.get("choices", [{}])[0]
160
+ message: dict[str, Any] = choice.get("message", {})
161
+ content: str = message.get("content") or ""
162
+ tool_calls: list[dict[str, Any]] = message.get("tool_calls", [])
163
+
164
+ if content.strip() and tool_calls:
165
+ content = ""
166
+
167
+ if not tool_calls:
168
+ return content
169
+
170
+ messages.append(
171
+ {"role": "assistant", "content": content, "tool_calls": tool_calls}
172
+ )
173
+
174
+ # 准备并发执行工具
175
+ tool_tasks = []
176
+ tool_call_ids = []
177
+
178
+ for tool_call in tool_calls:
179
+ call_id: str = tool_call.get("id", "")
180
+ function: dict[str, Any] = tool_call.get("function", {})
181
+ function_name: str = function.get("name", "")
182
+ function_args_str: str = function.get("arguments", "{}")
183
+
184
+ logger.info(f"Agent preparing tool: {function_name}")
185
+
186
+ try:
187
+ function_args: dict[str, Any] = json.loads(function_args_str)
188
+ except json.JSONDecodeError:
189
+ function_args = {}
190
+
191
+ tool_call_ids.append(call_id)
192
+ tool_tasks.append(
193
+ tool_registry.execute_tool(function_name, function_args, context)
194
+ )
195
+
196
+ # 并发执行
197
+ if tool_tasks:
198
+ logger.info(f"Agent executing {len(tool_tasks)} tools in parallel")
199
+ results = await asyncio.gather(*tool_tasks, return_exceptions=True)
200
+
201
+ for i, tool_result in enumerate(results):
202
+ call_id = tool_call_ids[i]
203
+ content_str: str = ""
204
+ if isinstance(tool_result, Exception):
205
+ content_str = f"Error: {str(tool_result)}"
206
+ else:
207
+ content_str = str(tool_result)
208
+
209
+ messages.append(
210
+ {
211
+ "role": "tool",
212
+ "tool_call_id": call_id,
213
+ "content": content_str,
214
+ }
215
+ )
216
+
217
+ except Exception as e:
218
+ logger.exception(f"Agent execution failed: {e}")
219
+ return f"处理失败: {e}"
220
+
221
+ return "达到最大迭代次数"
@@ -0,0 +1,14 @@
1
+ # 网络搜索助手
2
+
3
+ ## 能力
4
+ - **网页搜索**:使用搜索引擎获取互联网最新信息
5
+ - **网页爬取**:获取指定 URL 的网页内容并转换为 Markdown
6
+
7
+ ## 适用场景
8
+ - 用户询问的问题需要实时联网搜索时(如新闻、最新技术文档)
9
+ - 用户提供 URL 让你阅读或总结网页内容时
10
+ - 知识库中没有相关信息时
11
+
12
+ ## 参数说明
13
+ - `query` (搜索): 搜索关键词
14
+ - `url` (爬取): 目标网页链接
@@ -0,0 +1,16 @@
1
+ 你是一个网络搜索助手,专门帮助用户在互联网上查找信息。
2
+
3
+ 你的任务是根据用户需求,选择合适的工具来完成任务:
4
+
5
+ 1. **网页搜索** → web_search
6
+ 2. **获取网页内容** → crawl_webpage
7
+
8
+ 注意事项:
9
+ - 搜索结果可能包含广告或无关内容
10
+ - 网页内容可能会被截断
11
+ - 保持回答简洁,提供关键信息
12
+ - 标注信息来源和链接
13
+
14
+ 如果用户需求不明确,请先询问用户澄清问题。
15
+
16
+ 如果问题涉及时间,立刻调用时间工具获取。
@@ -0,0 +1 @@
1
+ # Web Agent Tools
@@ -0,0 +1,21 @@
1
+ {
2
+ "type": "function",
3
+ "function": {
4
+ "name": "crawl_webpage",
5
+ "description": "获取指定网页的内容,返回结构化的 Markdown 格式文本。支持获取网页的标题、正文、链接等信息。",
6
+ "parameters": {
7
+ "type": "object",
8
+ "properties": {
9
+ "url": {
10
+ "type": "string",
11
+ "description": "要获取的网页 URL"
12
+ },
13
+ "max_chars": {
14
+ "type": "integer",
15
+ "description": "返回的最大字符数(默认为 4096)"
16
+ }
17
+ },
18
+ "required": ["url"]
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,102 @@
1
+ from typing import Any, Dict
2
+ import logging
3
+ import os
4
+
5
+ logger = logging.getLogger(__name__)
6
+
7
+
8
+ async def execute(args: Dict[str, Any], context: Dict[str, Any]) -> str:
9
+ url = args.get("url", "")
10
+ if not url:
11
+ return "URL 不能为空"
12
+
13
+ # 从 context 标志检查可用性或尝试导入
14
+ if not context.get("crawl4ai_available", False):
15
+ return "网页获取功能未启用(crawl4ai 未安装)"
16
+
17
+ try:
18
+ from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig
19
+
20
+ try:
21
+ from crawl4ai import ProxyConfig
22
+
23
+ _PROXY_CONFIG_AVAILABLE = True
24
+ except ImportError:
25
+ _PROXY_CONFIG_AVAILABLE = False
26
+
27
+ except ImportError:
28
+ return "网页获取功能未启用(crawl4ai 未安装)"
29
+
30
+ max_chars = args.get("max_chars", 4096)
31
+
32
+ try:
33
+ use_proxy = os.getenv("USE_PROXY", "true").lower() == "true"
34
+ proxy = (
35
+ os.getenv("http_proxy")
36
+ or os.getenv("https_proxy")
37
+ or os.getenv("HTTP_PROXY")
38
+ or os.getenv("HTTPS_PROXY")
39
+ )
40
+
41
+ browser_config = BrowserConfig(
42
+ headless=True,
43
+ user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
44
+ viewport_width=1280,
45
+ viewport_height=720,
46
+ )
47
+
48
+ run_config_kwargs = {
49
+ "word_count_threshold": 1,
50
+ "cache_mode": "bypass",
51
+ "page_timeout": 30000,
52
+ "wait_for": "body",
53
+ "delay_before_return_html": 2.0,
54
+ }
55
+
56
+ if use_proxy and proxy:
57
+ logger.info(f"使用代理: {proxy}")
58
+ if _PROXY_CONFIG_AVAILABLE:
59
+ run_config_kwargs["proxy_config"] = ProxyConfig(server=proxy)
60
+ else:
61
+ run_config_kwargs["proxy_config"] = proxy
62
+ elif use_proxy and not proxy:
63
+ logger.warning("USE_PROXY=true 但未找到代理配置,将不使用代理")
64
+
65
+ run_config = CrawlerRunConfig(**run_config_kwargs)
66
+
67
+ async with AsyncWebCrawler(config=browser_config) as crawler:
68
+ result = await crawler.arun(url=url, config=run_config)
69
+
70
+ if result.success:
71
+ content = "# 网页解析结果\n\n"
72
+ content += f"**URL**: {result.url}\n\n"
73
+
74
+ if hasattr(result, "title") and result.title:
75
+ content += f"**标题**: {result.title}\n\n"
76
+
77
+ if hasattr(result, "description") and result.description:
78
+ content += f"**描述**: {result.description}\n\n"
79
+
80
+ content += "---\n\n## 内容\n\n"
81
+
82
+ markdown_text = result.markdown or ""
83
+ if max_chars > 0 and len(markdown_text) > max_chars:
84
+ markdown_text = markdown_text[:max_chars] + "\n\n...(内容已截断)"
85
+
86
+ content += markdown_text
87
+ return content
88
+ else:
89
+ error_msg = getattr(result, "error_message", "未知错误")
90
+ logger.error(f"抓取失败: {error_msg}")
91
+ return f"网页抓取失败: {error_msg}"
92
+
93
+ except RuntimeError as e:
94
+ if "ERR_NETWORK_CHANGED" in str(e) or "ERR_CONNECTION" in str(e):
95
+ logger.error(f"网络连接错误: {e}")
96
+ return f"网络连接错误,可能是代理配置问题。请检查代理设置或设置 USE_PROXY=false 禁用代理。错误: {e}"
97
+ else:
98
+ logger.error(f"抓取网页时发生错误: {e}")
99
+ return f"抓取网页时发生错误: {e}"
100
+ except Exception as e:
101
+ logger.error(f"网页获取失败: {e}")
102
+ return f"网页获取失败: {e}"
@@ -0,0 +1,12 @@
1
+ {
2
+ "type": "function",
3
+ "function": {
4
+ "name": "get_current_time",
5
+ "description": "获取当前系统时间。",
6
+ "parameters": {
7
+ "type": "object",
8
+ "properties": {},
9
+ "required": []
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,5 @@
1
+ from typing import Any, Dict
2
+ from datetime import datetime
3
+
4
+ async def execute(args: Dict[str, Any], context: Dict[str, Any]) -> str:
5
+ return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@@ -0,0 +1,21 @@
1
+ {
2
+ "type": "function",
3
+ "function": {
4
+ "name": "web_search",
5
+ "description": "使用 SearXNG 搜索引擎进行网页搜索,获取互联网上的信息。用于回答需要最新信息或你不确定的问题。",
6
+ "parameters": {
7
+ "type": "object",
8
+ "properties": {
9
+ "query": {
10
+ "type": "string",
11
+ "description": "搜索关键词"
12
+ },
13
+ "num_results": {
14
+ "type": "integer",
15
+ "description": "返回结果数量(默认为 5)"
16
+ }
17
+ },
18
+ "required": ["query"]
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,29 @@
1
+ from typing import Any, Dict
2
+ import logging
3
+
4
+ logger = logging.getLogger(__name__)
5
+
6
+ async def execute(args: Dict[str, Any], context: Dict[str, Any]) -> str:
7
+ query = args.get("query", "")
8
+ if not query:
9
+ return "搜索关键词不能为空"
10
+
11
+ search_wrapper = context.get("search_wrapper")
12
+ if not search_wrapper:
13
+ return "搜索功能未启用(SEARXNG_URL 未配置或 langchain_community 未安装)"
14
+
15
+ num_results = args.get("num_results", 5)
16
+
17
+ try:
18
+ results = search_wrapper.run(
19
+ query,
20
+ num_results=num_results
21
+ )
22
+
23
+ if results:
24
+ return f"搜索结果:\n{results}"
25
+ else:
26
+ return "搜索未返回结果"
27
+ except Exception as e:
28
+ logger.error(f"搜索执行失败: {e}")
29
+ return f"搜索执行失败: {e}"
@@ -0,0 +1,85 @@
1
+ # NagaAgent 机器人工具系统
2
+
3
+ 本项目采用模块化工具架构,所有工具都位于 `src/Undefined/tools/` 目录下。每个工具都是一个独立的文件夹,包含配置和实现逻辑。
4
+
5
+ ## 目录结构
6
+
7
+ ```text
8
+ tools/
9
+ ├── __init__.py # 工具注册表 (ToolRegistry)
10
+ ├── README.md # 本说明文件
11
+ └── tool_name/ # 工具名称
12
+ ├── config.json # 工具定义 (OpenAI 格式)
13
+ └── handler.py # 工具逻辑实现
14
+ ```
15
+
16
+ ## 如何添加一个新工具
17
+
18
+ ### 1. 创建工具目录
19
+ 在 `src/Undefined/tools/` 下创建一个新文件夹,例如 `my_new_tool`。
20
+
21
+ ### 2. 编写 `config.json`
22
+ 该文件定义了工具的名称、描述和参数结构(遵循 OpenAI function calling 格式)。
23
+
24
+ ```json
25
+ {
26
+ "type": "function",
27
+ "function": {
28
+ "name": "my_new_tool",
29
+ "description": "这里写工具的功能描述",
30
+ "parameters": {
31
+ "type": "object",
32
+ "properties": {
33
+ "arg1": {
34
+ "type": "string",
35
+ "description": "参数1的描述"
36
+ }
37
+ },
38
+ "required": ["arg1"]
39
+ }
40
+ }
41
+ }
42
+ ```
43
+
44
+ ### 3. 编写 `handler.py`
45
+ 实现工具的具体逻辑。必须包含一个异步函数 `execute(args, context)`。
46
+
47
+ ```python
48
+ from typing import Any, Dict
49
+
50
+ async def execute(args: Dict[str, Any], context: Dict[str, Any]) -> str:
51
+ # 1. 获取参数
52
+ val = args.get("arg1")
53
+
54
+ # 2. 执行逻辑
55
+ result = f"处理结果: {val}"
56
+
57
+ # 3. 返回字符串结果给 AI
58
+ return result
59
+ ```
60
+
61
+ ## 关于 Context 对象
62
+
63
+ `context` 参数包含了机器人运行时的上下文和回调函数,常用的有:
64
+
65
+ | 键名 | 类型 | 说明 |
66
+ | :--- | :--- | :--- |
67
+ | `send_message_callback` | `Callable` | 发送群消息的回调 |
68
+ | `send_private_message_callback` | `Callable` | 发送私聊消息的回调 |
69
+ | `send_image_callback` | `Callable` | 发送图片的回调 |
70
+ | `get_recent_messages_callback` | `Callable` | 获取历史消息的回调 |
71
+ | `memory_storage` | `MemoryStorage` | 记忆存储实例 |
72
+ | `ai_client` | `AIClient` | AI 客户端实例(用于调用图片描述等) |
73
+ | `sender` | `MessageSender` | 统一消息发送接口(自动记录历史) |
74
+ | `history_manager` | `MessageHistoryManager` | 统一历史记录管理器 |
75
+ | `base_path` | `Path` | 默认的基础路径 (通常锁定在 `code/NagaAgent`) |
76
+
77
+ ## 自动发现
78
+
79
+ 工具注册表 (`ToolRegistry`) 会在初始化时自动扫描目录下的所有文件夹。只要文件夹内同时存在 `config.json` 和 `handler.py`,工具就会被自动加载并提供给 AI 使用,**无需手动修改任何导入代码**。
80
+
81
+ ## 开发建议
82
+
83
+ 1. **返回字符串**:工具的返回值必须是字符串。如果是复杂数据,请将其转为 JSON 字符串或格式化的文本。
84
+ 2. **错误处理**:在 `handler.py` 中使用 `try...except` 捕获异常并返回友好的错误信息,这样 AI 可以理解发生了什么并尝试修复。
85
+ 3. **安全限制**:如果涉及文件操作,务必校验路径是否在 `base_path` 范围内,防止路径穿越攻击。