jarvis-ai-assistant 0.1.32__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 (55) hide show
  1. jarvis/__init__.py +3 -0
  2. jarvis/__pycache__/__init__.cpython-313.pyc +0 -0
  3. jarvis/__pycache__/agent.cpython-313.pyc +0 -0
  4. jarvis/__pycache__/main.cpython-313.pyc +0 -0
  5. jarvis/__pycache__/models.cpython-313.pyc +0 -0
  6. jarvis/__pycache__/tools.cpython-313.pyc +0 -0
  7. jarvis/__pycache__/utils.cpython-313.pyc +0 -0
  8. jarvis/__pycache__/zte_llm.cpython-313.pyc +0 -0
  9. jarvis/agent.py +289 -0
  10. jarvis/main.py +148 -0
  11. jarvis/models/__init__.py +3 -0
  12. jarvis/models/__pycache__/__init__.cpython-313.pyc +0 -0
  13. jarvis/models/__pycache__/base.cpython-313.pyc +0 -0
  14. jarvis/models/__pycache__/kimi.cpython-313.pyc +0 -0
  15. jarvis/models/__pycache__/openai.cpython-313.pyc +0 -0
  16. jarvis/models/__pycache__/oyi.cpython-313.pyc +0 -0
  17. jarvis/models/__pycache__/registry.cpython-313.pyc +0 -0
  18. jarvis/models/base.py +39 -0
  19. jarvis/models/kimi.py +389 -0
  20. jarvis/models/openai.py +96 -0
  21. jarvis/models/oyi.py +271 -0
  22. jarvis/models/registry.py +199 -0
  23. jarvis/tools/__init__.py +5 -0
  24. jarvis/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  25. jarvis/tools/__pycache__/base.cpython-313.pyc +0 -0
  26. jarvis/tools/__pycache__/bing_search.cpython-313.pyc +0 -0
  27. jarvis/tools/__pycache__/calculator.cpython-313.pyc +0 -0
  28. jarvis/tools/__pycache__/calculator_tool.cpython-313.pyc +0 -0
  29. jarvis/tools/__pycache__/file_ops.cpython-313.pyc +0 -0
  30. jarvis/tools/__pycache__/generator.cpython-313.pyc +0 -0
  31. jarvis/tools/__pycache__/methodology.cpython-313.pyc +0 -0
  32. jarvis/tools/__pycache__/python_script.cpython-313.pyc +0 -0
  33. jarvis/tools/__pycache__/rag.cpython-313.pyc +0 -0
  34. jarvis/tools/__pycache__/registry.cpython-313.pyc +0 -0
  35. jarvis/tools/__pycache__/search.cpython-313.pyc +0 -0
  36. jarvis/tools/__pycache__/shell.cpython-313.pyc +0 -0
  37. jarvis/tools/__pycache__/sub_agent.cpython-313.pyc +0 -0
  38. jarvis/tools/__pycache__/user_confirmation.cpython-313.pyc +0 -0
  39. jarvis/tools/__pycache__/user_input.cpython-313.pyc +0 -0
  40. jarvis/tools/__pycache__/user_interaction.cpython-313.pyc +0 -0
  41. jarvis/tools/__pycache__/webpage.cpython-313.pyc +0 -0
  42. jarvis/tools/base.py +23 -0
  43. jarvis/tools/file_ops.py +110 -0
  44. jarvis/tools/generator.py +172 -0
  45. jarvis/tools/methodology.py +145 -0
  46. jarvis/tools/registry.py +183 -0
  47. jarvis/tools/shell.py +78 -0
  48. jarvis/tools/sub_agent.py +82 -0
  49. jarvis/utils.py +202 -0
  50. jarvis_ai_assistant-0.1.32.dist-info/LICENSE +21 -0
  51. jarvis_ai_assistant-0.1.32.dist-info/METADATA +345 -0
  52. jarvis_ai_assistant-0.1.32.dist-info/RECORD +55 -0
  53. jarvis_ai_assistant-0.1.32.dist-info/WHEEL +5 -0
  54. jarvis_ai_assistant-0.1.32.dist-info/entry_points.txt +2 -0
  55. jarvis_ai_assistant-0.1.32.dist-info/top_level.txt +1 -0
@@ -0,0 +1,172 @@
1
+ from typing import Dict, Any
2
+ from pathlib import Path
3
+ from jarvis.models.registry import ModelRegistry
4
+ from jarvis.tools.registry import ToolRegistry
5
+ from jarvis.utils import OutputType, PrettyOutput
6
+
7
+ class ToolGeneratorTool:
8
+ name = "generate_tool"
9
+ description = "生成新的工具代码并自动注册到Jarvis,自动扩充Jarvis的能力"
10
+ parameters = {
11
+ "type": "object",
12
+ "properties": {
13
+ "tool_name": {
14
+ "type": "string",
15
+ "description": "工具的名称(snake_case格式)"
16
+ },
17
+ "class_name": {
18
+ "type": "string",
19
+ "description": "工具类的名称(PascalCase格式)"
20
+ },
21
+ "description": {
22
+ "type": "string",
23
+ "description": "工具的功能描述"
24
+ },
25
+ "parameters": {
26
+ "type": "object",
27
+ "description": "工具参数的JSON Schema定义"
28
+ }
29
+ },
30
+ "required": ["tool_name", "class_name", "description", "parameters"]
31
+ }
32
+
33
+ def __init__(self):
34
+ """初始化工具生成器
35
+ """
36
+ # 设置工具目录
37
+ self.tools_dir = Path.home() / '.jarvis_tools'
38
+
39
+ # 确保工具目录存在
40
+ self.tools_dir.mkdir(parents=True, exist_ok=True)
41
+
42
+ def _generate_tool_code(self, tool_name: str, class_name: str, description: str, parameters: Dict) -> str:
43
+ """使用大模型生成工具代码"""
44
+ model = ModelRegistry.get_global_model()
45
+
46
+ prompt = f"""请生成一个Python工具类的代码,要求如下,除了代码,不要输出任何内容:
47
+
48
+ 1. 类名: {class_name}
49
+ 2. 工具名称: {tool_name}
50
+ 3. 功能描述: {description}
51
+ 4. 参数定义: {parameters}
52
+
53
+ 严格按照以下格式生成代码(各函数的参数和返回值一定要与示例一致):
54
+
55
+ ```python
56
+ from typing import Dict, Any, Protocol, Optional
57
+ from jarvis.utils import OutputType, PrettyOutput
58
+ from jarvis.models.registry import ModelRegistry
59
+
60
+ class ExampleTool:
61
+ name = "example_tool"
62
+ description = "示例工具"
63
+ parameters = {{
64
+ "type": "object",
65
+ "properties": {{
66
+ "param1": {{"type": "string"}}
67
+ }},
68
+ "required": ["param1"]
69
+ }}
70
+
71
+ def __init__(self):
72
+ self.model = ModelRegistry.get_global_model()
73
+
74
+ def execute(self, args: Dict) -> Dict[str, Any]:
75
+ try:
76
+ # 验证参数示例
77
+ if "param1" not in args:
78
+ return {{"success": False, "error": "缺少必需参数: param1"}}
79
+
80
+ # 记录操作示例
81
+ PrettyOutput.print(f"处理参数: {{args['param1']}}", OutputType.INFO)
82
+
83
+ # 使用大模型示例
84
+ response = self.model.chat("prompt")
85
+
86
+ # 实现具体功能
87
+ result = "处理结果"
88
+
89
+ return {{
90
+ "success": True,
91
+ "stdout": result,
92
+ "stderr": ""
93
+ }}
94
+ except Exception as e:
95
+ PrettyOutput.print(str(e), OutputType.ERROR)
96
+ return {{
97
+ "success": False,
98
+ "error": str(e)
99
+ }}
100
+ ```"""
101
+
102
+ # 调用模型生成代码
103
+ response = model.chat(prompt)
104
+ model.delete_chat()
105
+
106
+ # 提取代码块
107
+ code_start = response.find("```python")
108
+ code_end = response.find("```", code_start + 9)
109
+
110
+ if code_start == -1 or code_end == -1:
111
+ # 如果没有找到代码块标记,假设整个响应都是代码
112
+ return response
113
+
114
+ # 提取代码块内容(去掉```python和```标记)
115
+ code = response[code_start + 9:code_end].strip()
116
+ return code
117
+
118
+ def execute(self, args: Dict) -> Dict[str, Any]:
119
+ """生成工具代码"""
120
+ try:
121
+ tool_name = args["tool_name"]
122
+ class_name = args["class_name"]
123
+ description = args["description"]
124
+ parameters = args["parameters"]
125
+
126
+ PrettyOutput.print(f"开始生成工具: {tool_name}", OutputType.INFO)
127
+
128
+ # 生成工具代码
129
+ tool_code = self._generate_tool_code(
130
+ tool_name,
131
+ class_name,
132
+ description,
133
+ parameters
134
+ )
135
+
136
+ # 获取工具文件路径
137
+ tool_file = self.tools_dir / f"{tool_name}.py"
138
+
139
+ # 写入工具文件
140
+ with open(tool_file, "w", encoding="utf-8") as f:
141
+ f.write(tool_code)
142
+
143
+ # 创建或更新 __init__.py
144
+ init_file = self.tools_dir / "__init__.py"
145
+ if not init_file.exists():
146
+ with open(init_file, "w", encoding="utf-8") as f:
147
+ f.write("# Jarvis Tools\n")
148
+
149
+ # 注册工具
150
+ success = ToolRegistry.get_global_tool_registry().register_tool_by_file(tool_file)
151
+ if not success:
152
+ return {
153
+ "success": False,
154
+ "error": "工具生成成功但注册失败"
155
+ }
156
+
157
+ return {
158
+ "success": True,
159
+ "stdout": f"工具已生成并注册到Jarvis\n"
160
+ f"工具目录: {self.tools_dir}\n"
161
+ f"工具名称: {tool_name}\n"
162
+ f"工具描述: {description}\n"
163
+ f"工具参数: {parameters}",
164
+ "stderr": ""
165
+ }
166
+
167
+ except Exception as e:
168
+ PrettyOutput.print(str(e), OutputType.ERROR)
169
+ return {
170
+ "success": False,
171
+ "error": f"生成工具失败: {str(e)}"
172
+ }
@@ -0,0 +1,145 @@
1
+ import os
2
+ import yaml
3
+ from typing import Dict, Optional, Any
4
+ from jarvis.utils import OutputType, PrettyOutput
5
+
6
+
7
+ class MethodologyTool:
8
+ """经验管理工具"""
9
+
10
+ name = "methodology"
11
+ description = "管理问题处理方法论,支持添加、更新、删除操作"
12
+ parameters = {
13
+ "type": "object",
14
+ "properties": {
15
+ "operation": {
16
+ "type": "string",
17
+ "description": "操作类型 (delete/update/add)",
18
+ "enum": ["delete", "update", "add"]
19
+ },
20
+ "problem_type": {
21
+ "type": "string",
22
+ "description": "问题类型,例如:code_review, bug_fix 等"
23
+ },
24
+ "content": {
25
+ "type": "string",
26
+ "description": "方法论内容 (update/add 时必需)",
27
+ "optional": True
28
+ }
29
+ },
30
+ "required": ["operation", "problem_type"]
31
+ }
32
+
33
+ def __init__(self):
34
+ """初始化经验管理工具"""
35
+ self.methodology_file = os.path.expanduser("~/.jarvis_methodology")
36
+ self._ensure_file_exists()
37
+
38
+ def _ensure_file_exists(self):
39
+ """确保方法论文件存在"""
40
+ if not os.path.exists(self.methodology_file):
41
+ try:
42
+ with open(self.methodology_file, 'w', encoding='utf-8') as f:
43
+ yaml.safe_dump({}, f, allow_unicode=True)
44
+ except Exception as e:
45
+ PrettyOutput.print(f"创建方法论文件失败: {str(e)}", OutputType.ERROR)
46
+
47
+ def _load_methodologies(self) -> Dict:
48
+ """加载所有方法论"""
49
+ try:
50
+ with open(self.methodology_file, 'r', encoding='utf-8') as f:
51
+ return yaml.safe_load(f) or {}
52
+ except Exception as e:
53
+ PrettyOutput.print(f"加载方法论失败: {str(e)}", OutputType.ERROR)
54
+ return {}
55
+
56
+ def _save_methodologies(self, methodologies: Dict):
57
+ """保存所有方法论"""
58
+ try:
59
+ with open(self.methodology_file, 'w', encoding='utf-8') as f:
60
+ yaml.safe_dump(methodologies, f, allow_unicode=True)
61
+ except Exception as e:
62
+ PrettyOutput.print(f"保存方法论失败: {str(e)}", OutputType.ERROR)
63
+
64
+ def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
65
+ """执行方法论管理操作
66
+
67
+ Args:
68
+ args: 包含操作参数的字典
69
+ - operation: 操作类型 (delete/update/add)
70
+ - problem_type: 问题类型
71
+ - content: 方法论内容 (update/add 时必需)
72
+
73
+ Returns:
74
+ Dict[str, Any]: 包含执行结果的字典
75
+ """
76
+ operation = args.get("operation")
77
+ problem_type = args.get("problem_type")
78
+ content = args.get("content")
79
+
80
+ if not operation or not problem_type:
81
+ return {
82
+ "success": False,
83
+ "error": "缺少必要参数: operation 和 problem_type"
84
+ }
85
+
86
+ methodologies = self._load_methodologies()
87
+
88
+ try:
89
+ if operation == "delete":
90
+ if problem_type in methodologies:
91
+ del methodologies[problem_type]
92
+ self._save_methodologies(methodologies)
93
+ return {
94
+ "success": True,
95
+ "stdout": f"已删除问题类型 '{problem_type}' 的方法论"
96
+ }
97
+ else:
98
+ return {
99
+ "success": False,
100
+ "error": f"未找到问题类型 '{problem_type}' 的方法论"
101
+ }
102
+
103
+ elif operation in ["update", "add"]:
104
+ if not content:
105
+ return {
106
+ "success": False,
107
+ "error": f"{operation} 操作需要提供方法论内容"
108
+ }
109
+
110
+ if operation == "update" and problem_type not in methodologies:
111
+ return {
112
+ "success": False,
113
+ "error": f"未找到问题类型 '{problem_type}' 的方法论,无法更新"
114
+ }
115
+
116
+ methodologies[problem_type] = content
117
+ self._save_methodologies(methodologies)
118
+ return {
119
+ "success": True,
120
+ "stdout": f"已{'更新' if operation == 'update' else '添加'}问题类型 '{problem_type}' 的方法论"
121
+ }
122
+
123
+ else:
124
+ return {
125
+ "success": False,
126
+ "error": f"不支持的操作类型: {operation}"
127
+ }
128
+
129
+ except Exception as e:
130
+ return {
131
+ "success": False,
132
+ "error": f"执行失败: {str(e)}"
133
+ }
134
+
135
+ def get_methodology(self, problem_type: str) -> Optional[str]:
136
+ """获取指定问题类型的方法论
137
+
138
+ Args:
139
+ problem_type: 问题类型
140
+
141
+ Returns:
142
+ Optional[str]: 方法论内容,如果不存在则返回 None
143
+ """
144
+ methodologies = self._load_methodologies()
145
+ return methodologies.get(problem_type)
@@ -0,0 +1,183 @@
1
+
2
+ import importlib
3
+ import json
4
+ from pathlib import Path
5
+ import sys
6
+ from typing import Any, Callable, Dict, List, Optional
7
+
8
+ from jarvis.models.registry import ModelRegistry
9
+ from jarvis.tools.base import Tool
10
+ from jarvis.utils import OutputType, PrettyOutput
11
+
12
+
13
+ class ToolRegistry:
14
+ global_tool_registry = None # type: ignore
15
+ def __init__(self):
16
+ """初始化工具注册器
17
+ """
18
+ self.tools: Dict[str, Tool] = {}
19
+ # 加载内置工具和外部工具
20
+ self._load_builtin_tools()
21
+ self._load_external_tools()
22
+
23
+ @staticmethod
24
+ def get_global_tool_registry():
25
+ """获取全局工具注册器"""
26
+ if ToolRegistry.global_tool_registry is None:
27
+ ToolRegistry.global_tool_registry = ToolRegistry()
28
+ return ToolRegistry.global_tool_registry
29
+
30
+ def _load_builtin_tools(self):
31
+ """从内置tools目录加载工具"""
32
+ tools_dir = Path(__file__).parent
33
+
34
+ # 遍历目录下的所有.py文件
35
+ for file_path in tools_dir.glob("*.py"):
36
+ # 跳过基础文件和__init__.py
37
+ if file_path.name in ["base.py", "__init__.py", "registry.py"]:
38
+ continue
39
+
40
+ self.register_tool_by_file(file_path)
41
+
42
+ def _load_external_tools(self):
43
+ """从~/.jarvis_tools加载外部工具"""
44
+ external_tools_dir = Path.home() / '.jarvis_tools'
45
+ if not external_tools_dir.exists():
46
+ return
47
+
48
+ # 遍历目录下的所有.py文件
49
+ for file_path in external_tools_dir.glob("*.py"):
50
+ # 跳过__init__.py
51
+ if file_path.name == "__init__.py":
52
+ continue
53
+
54
+ self.register_tool_by_file(file_path)
55
+
56
+ def register_tool_by_file(self, file_path: str):
57
+ """从指定文件加载并注册工具
58
+
59
+ Args:
60
+ file_path: 工具文件的路径
61
+
62
+ Returns:
63
+ bool: 是否成功加载工具
64
+ """
65
+ try:
66
+ file_path = Path(file_path).resolve() # 获取绝对路径
67
+ if not file_path.exists() or not file_path.is_file():
68
+ PrettyOutput.print(f"文件不存在: {file_path}", OutputType.ERROR)
69
+ return False
70
+
71
+ # 动态导入模块
72
+ module_name = file_path.stem
73
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
74
+ if not spec or not spec.loader:
75
+ PrettyOutput.print(f"无法加载模块: {file_path}", OutputType.ERROR)
76
+ return False
77
+
78
+ module = importlib.util.module_from_spec(spec)
79
+ sys.modules[module_name] = module # 添加到 sys.modules 以支持相对导入
80
+ spec.loader.exec_module(module)
81
+
82
+ # 查找模块中的工具类
83
+ tool_found = False
84
+ for item_name in dir(module):
85
+ item = getattr(module, item_name)
86
+ # 检查是否是类,并且有必要的属性
87
+ if (isinstance(item, type) and
88
+ hasattr(item, 'name') and
89
+ hasattr(item, 'description') and
90
+ hasattr(item, 'parameters')):
91
+
92
+ # 实例化工具类,传入模型和输出处理器
93
+ tool_instance = item()
94
+
95
+ # 注册工具
96
+ self.register_tool(
97
+ name=tool_instance.name,
98
+ description=tool_instance.description,
99
+ parameters=tool_instance.parameters,
100
+ func=tool_instance.execute
101
+ )
102
+ PrettyOutput.print(f"已加载工具: {tool_instance.name}: {tool_instance.description}", OutputType.INFO)
103
+ tool_found = True
104
+
105
+ if not tool_found:
106
+ PrettyOutput.print(f"文件中未找到有效的工具类: {file_path}", OutputType.WARNING)
107
+ return False
108
+
109
+ return True
110
+
111
+ except Exception as e:
112
+ PrettyOutput.print(f"加载工具失败 {file_path.name}: {str(e)}", OutputType.ERROR)
113
+ return False
114
+
115
+ def register_tool(self, name: str, description: str, parameters: Dict, func: Callable):
116
+ """注册新工具"""
117
+ self.tools[name] = Tool(name, description, parameters, func)
118
+
119
+ def get_tool(self, name: str) -> Optional[Tool]:
120
+ """获取工具"""
121
+ return self.tools.get(name)
122
+
123
+ def get_all_tools(self) -> List[Dict]:
124
+ """获取所有工具的Ollama格式定义"""
125
+ return [tool.to_dict() for tool in self.tools.values()]
126
+
127
+ def execute_tool(self, name: str, arguments: Dict) -> Dict[str, Any]:
128
+ """执行指定工具"""
129
+ tool = self.get_tool(name)
130
+ if tool is None:
131
+ return {"success": False, "error": f"Tool {name} does not exist"}
132
+ return tool.execute(arguments)
133
+
134
+ def handle_tool_calls(self, tool_calls: List[Dict]) -> str:
135
+ """处理工具调用,只处理第一个工具"""
136
+ try:
137
+ if not tool_calls:
138
+ return ""
139
+
140
+ # 只处理第一个工具调用
141
+ tool_call = tool_calls[0]
142
+ name = tool_call["name"]
143
+ args = tool_call["arguments"]
144
+
145
+ if isinstance(args, str):
146
+ try:
147
+ args = json.loads(args)
148
+ except json.JSONDecodeError:
149
+ PrettyOutput.print(f"工具参数格式无效: {name}", OutputType.ERROR)
150
+ return ""
151
+
152
+ # 显示工具调用信息
153
+ PrettyOutput.section(f"执行工具: {name}", OutputType.TOOL)
154
+ if isinstance(args, dict):
155
+ for key, value in args.items():
156
+ PrettyOutput.print(f"参数: {key} = {value}", OutputType.DEBUG)
157
+ else:
158
+ PrettyOutput.print(f"参数: {args}", OutputType.DEBUG)
159
+
160
+ # 执行工具调用
161
+ result = self.execute_tool(name, args)
162
+
163
+ # 处理结果
164
+ if result["success"]:
165
+ stdout = result["stdout"]
166
+ stderr = result.get("stderr", "")
167
+ output_parts = []
168
+ if stdout:
169
+ output_parts.append(f"输出:\n{stdout}")
170
+ if stderr:
171
+ output_parts.append(f"错误:\n{stderr}")
172
+ output = "\n\n".join(output_parts)
173
+ output = "没有输出和错误" if not output else output
174
+ PrettyOutput.section("执行成功", OutputType.SUCCESS)
175
+ else:
176
+ error_msg = result["error"]
177
+ output = f"执行失败: {error_msg}"
178
+ PrettyOutput.section("执行失败", OutputType.ERROR)
179
+
180
+ return output
181
+ except Exception as e:
182
+ PrettyOutput.print(f"执行工具失败: {str(e)}", OutputType.ERROR)
183
+ return f"Tool call failed: {str(e)}"
jarvis/tools/shell.py ADDED
@@ -0,0 +1,78 @@
1
+ from typing import Dict, Any
2
+ import os
3
+ import tempfile
4
+ from pathlib import Path
5
+
6
+ from jarvis.utils import OutputType, PrettyOutput
7
+
8
+
9
+ class ShellTool:
10
+ name = "execute_shell"
11
+ description = """执行shell命令并返回结果"""
12
+
13
+ parameters = {
14
+ "type": "object",
15
+ "properties": {
16
+ "command": {
17
+ "type": "string",
18
+ "description": "Shell command to execute"
19
+ }
20
+ },
21
+ "required": ["command"]
22
+ }
23
+
24
+
25
+ def _escape_command(self, cmd: str) -> str:
26
+ """转义命令中的特殊字符"""
27
+ return cmd.replace("'", "'\"'\"'")
28
+
29
+ def execute(self, args: Dict) -> Dict[str, Any]:
30
+ """执行shell命令"""
31
+ try:
32
+ command = args["command"]
33
+
34
+ # 生成临时文件名
35
+ output_file = os.path.join(tempfile.gettempdir(), f"jarvis_shell_{os.getpid()}.log")
36
+
37
+ # 转义命令中的特殊字符
38
+ escaped_command = self._escape_command(command)
39
+
40
+ # 修改命令以使用script
41
+ tee_command = f"script -q -c '{escaped_command}' {output_file}"
42
+
43
+ PrettyOutput.print(f"执行命令: {command}", OutputType.INFO)
44
+
45
+ # 执行命令
46
+ return_code = os.system(tee_command)
47
+
48
+ # 读取输出文件
49
+ try:
50
+ with open(output_file, 'r', encoding='utf-8', errors='replace') as f:
51
+ output = f.read()
52
+ # 移除script命令添加的头尾
53
+ if output:
54
+ lines = output.splitlines()
55
+ if len(lines) > 2:
56
+ output = "\n".join(lines[1:-1])
57
+ except Exception as e:
58
+ output = f"读取输出文件失败: {str(e)}"
59
+ finally:
60
+ # 清理临时文件
61
+ Path(output_file).unlink(missing_ok=True)
62
+
63
+ return {
64
+ "success": return_code == 0,
65
+ "stdout": output,
66
+ "stderr": "",
67
+ "return_code": return_code
68
+ }
69
+
70
+ except Exception as e:
71
+ # 确保清理临时文件
72
+ if 'output_file' in locals():
73
+ Path(output_file).unlink(missing_ok=True)
74
+ PrettyOutput.print(str(e), OutputType.ERROR)
75
+ return {
76
+ "success": False,
77
+ "error": str(e)
78
+ }
@@ -0,0 +1,82 @@
1
+ from typing import Dict, Any
2
+
3
+
4
+ from jarvis.agent import Agent
5
+ from jarvis.utils import OutputType, PrettyOutput
6
+
7
+
8
+ class SubAgentTool:
9
+ name = "create_sub_agent"
10
+ description = "创建一个子代理来处理特定任务,子代理会生成任务总结报告"
11
+ parameters = {
12
+ "type": "object",
13
+ "properties": {
14
+ "agent_name": {
15
+ "type": "string",
16
+ "description": "子代理的名称"
17
+ },
18
+ "task": {
19
+ "type": "string",
20
+ "description": "需要完成的具体任务"
21
+ },
22
+ "context": {
23
+ "type": "string",
24
+ "description": "任务相关的上下文信息",
25
+ "default": ""
26
+ },
27
+ "goal": {
28
+ "type": "string",
29
+ "description": "任务的完成目标",
30
+ "default": ""
31
+ },
32
+ "files": {
33
+ "type": "array",
34
+ "items": {"type": "string"},
35
+ "description": "相关文件路径列表,用于文件问答和处理",
36
+ "default": []
37
+ }
38
+ },
39
+ "required": ["agent_name", "task", "context", "goal"]
40
+ }
41
+
42
+
43
+ def execute(self, args: Dict) -> Dict[str, Any]:
44
+ """创建并运行子代理"""
45
+ try:
46
+ agent_name = args["agent_name"]
47
+ task = args["task"]
48
+ context = args.get("context", "")
49
+ goal = args.get("goal", "")
50
+ files = args.get("files", [])
51
+
52
+ PrettyOutput.print(f"创建子代理: {agent_name}", OutputType.INFO)
53
+
54
+ # 构建任务描述
55
+ task_description = task
56
+ if context:
57
+ task_description = f"上下文信息:\n{context}\n\n任务:\n{task}"
58
+ if goal:
59
+ task_description += f"\n\n完成目标:\n{goal}"
60
+
61
+ # 创建子代理
62
+ sub_agent = Agent(
63
+ name=agent_name,
64
+ is_sub_agent=True
65
+ )
66
+
67
+ # 运行子代理,传入文件列表
68
+ PrettyOutput.print("子代理开始执行任务...", OutputType.INFO)
69
+ result = sub_agent.run(task_description, file_list=files)
70
+
71
+ return {
72
+ "success": True,
73
+ "stdout": f"子代理任务完成\n\n{result}",
74
+ "stderr": ""
75
+ }
76
+
77
+ except Exception as e:
78
+ PrettyOutput.print(str(e), OutputType.ERROR)
79
+ return {
80
+ "success": False,
81
+ "error": f"子代理执行失败: {str(e)}"
82
+ }