jarvis-ai-assistant 0.1.5__py3-none-any.whl → 0.1.7__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.
Potentially problematic release.
This version of jarvis-ai-assistant might be problematic. Click here for more details.
- jarvis/__init__.py +1 -1
- jarvis/__pycache__/__init__.cpython-313.pyc +0 -0
- jarvis/__pycache__/agent.cpython-313.pyc +0 -0
- jarvis/__pycache__/main.cpython-313.pyc +0 -0
- jarvis/__pycache__/models.cpython-313.pyc +0 -0
- jarvis/__pycache__/utils.cpython-313.pyc +0 -0
- jarvis/__pycache__/zte_llm.cpython-313.pyc +0 -0
- jarvis/agent.py +235 -70
- jarvis/main.py +30 -14
- jarvis/models.py +72 -60
- jarvis/tools/__init__.py +1 -3
- jarvis/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/base.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/file_ops.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/search.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/shell.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/sub_agent.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/user_input.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/webpage.cpython-313.pyc +0 -0
- jarvis/tools/base.py +49 -128
- jarvis/tools/file_ops.py +1 -1
- jarvis/tools/search.py +77 -14
- jarvis/tools/shell.py +44 -49
- jarvis/tools/sub_agent.py +29 -25
- jarvis/tools/webpage.py +12 -26
- jarvis/utils.py +101 -65
- jarvis/zte_llm.py +28 -25
- {jarvis_ai_assistant-0.1.5.dist-info → jarvis_ai_assistant-0.1.7.dist-info}/METADATA +2 -1
- jarvis_ai_assistant-0.1.7.dist-info/RECORD +37 -0
- jarvis/.jarvis +0 -1
- jarvis/tools/python_script.py +0 -150
- jarvis_ai_assistant-0.1.5.dist-info/RECORD +0 -38
- {jarvis_ai_assistant-0.1.5.dist-info → jarvis_ai_assistant-0.1.7.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.5.dist-info → jarvis_ai_assistant-0.1.7.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.5.dist-info → jarvis_ai_assistant-0.1.7.dist-info}/top_level.txt +0 -0
jarvis/tools/__init__.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from .base import Tool, ToolRegistry
|
|
2
|
-
from .python_script import PythonScript
|
|
3
2
|
from .file_ops import FileOperationTool
|
|
4
3
|
from .search import SearchTool
|
|
5
4
|
from .shell import ShellTool
|
|
@@ -8,9 +7,8 @@ from .webpage import WebpageTool
|
|
|
8
7
|
__all__ = [
|
|
9
8
|
'Tool',
|
|
10
9
|
'ToolRegistry',
|
|
11
|
-
'PythonScript',
|
|
12
10
|
'FileOperationTool',
|
|
13
11
|
'SearchTool',
|
|
14
12
|
'ShellTool',
|
|
15
13
|
'WebpageTool',
|
|
16
|
-
]
|
|
14
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
jarvis/tools/base.py
CHANGED
|
@@ -13,12 +13,9 @@ class Tool:
|
|
|
13
13
|
def to_dict(self) -> Dict:
|
|
14
14
|
"""转换为Ollama工具格式"""
|
|
15
15
|
return {
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
"description": self.description,
|
|
20
|
-
"parameters": self.parameters
|
|
21
|
-
}
|
|
16
|
+
"name": self.name,
|
|
17
|
+
"description": self.description,
|
|
18
|
+
"parameters": json.dumps(self.parameters)
|
|
22
19
|
}
|
|
23
20
|
|
|
24
21
|
def execute(self, arguments: Dict) -> Dict[str, Any]:
|
|
@@ -35,17 +32,16 @@ class ToolRegistry:
|
|
|
35
32
|
"""注册所有默认工具"""
|
|
36
33
|
from .search import SearchTool
|
|
37
34
|
from .shell import ShellTool
|
|
38
|
-
from .python_script import PythonScriptTool
|
|
39
35
|
from .file_ops import FileOperationTool
|
|
40
36
|
from .webpage import WebpageTool
|
|
41
37
|
from .sub_agent import SubAgentTool
|
|
42
38
|
|
|
43
39
|
tools = [
|
|
44
|
-
SearchTool(),
|
|
40
|
+
SearchTool(self.model),
|
|
45
41
|
ShellTool(),
|
|
46
|
-
PythonScriptTool(),
|
|
47
42
|
FileOperationTool(),
|
|
48
43
|
WebpageTool(),
|
|
44
|
+
SubAgentTool(self.model)
|
|
49
45
|
]
|
|
50
46
|
|
|
51
47
|
for tool in tools:
|
|
@@ -56,14 +52,6 @@ class ToolRegistry:
|
|
|
56
52
|
func=tool.execute
|
|
57
53
|
)
|
|
58
54
|
|
|
59
|
-
sub_agent_tool = SubAgentTool(self.model)
|
|
60
|
-
self.register_tool(
|
|
61
|
-
name=sub_agent_tool.name,
|
|
62
|
-
description=sub_agent_tool.description,
|
|
63
|
-
parameters=sub_agent_tool.parameters,
|
|
64
|
-
func=sub_agent_tool.execute
|
|
65
|
-
)
|
|
66
|
-
|
|
67
55
|
def register_tool(self, name: str, description: str, parameters: Dict, func: Callable):
|
|
68
56
|
"""注册新工具"""
|
|
69
57
|
self.tools[name] = Tool(name, description, parameters, func)
|
|
@@ -84,115 +72,48 @@ class ToolRegistry:
|
|
|
84
72
|
return tool.execute(arguments)
|
|
85
73
|
|
|
86
74
|
def handle_tool_calls(self, tool_calls: List[Dict]) -> str:
|
|
87
|
-
"""
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
name = tool_call["function"]["name"]
|
|
91
|
-
args = tool_call["function"]["arguments"]
|
|
92
|
-
if isinstance(args, str):
|
|
93
|
-
try:
|
|
94
|
-
args = json.loads(args)
|
|
95
|
-
except json.JSONDecodeError:
|
|
96
|
-
return f"Invalid JSON in arguments for tool {name}"
|
|
97
|
-
|
|
98
|
-
PrettyOutput.print(f"Calling tool: {name}", OutputType.INFO)
|
|
99
|
-
if isinstance(args, dict):
|
|
100
|
-
for key, value in args.items():
|
|
101
|
-
PrettyOutput.print(f" - {key}: {value}", OutputType.INFO)
|
|
102
|
-
else:
|
|
103
|
-
PrettyOutput.print(f" Arguments: {args}", OutputType.INFO)
|
|
104
|
-
PrettyOutput.print("", OutputType.INFO)
|
|
75
|
+
"""处理工具调用,只处理第一个工具"""
|
|
76
|
+
if not tool_calls:
|
|
77
|
+
return ""
|
|
105
78
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
* Reviewing multiple code components
|
|
148
|
-
- Do NOT use for:
|
|
149
|
-
* Main task execution
|
|
150
|
-
* Single file operations
|
|
151
|
-
* Simple sequential steps
|
|
152
|
-
* Tasks requiring continuous context
|
|
153
|
-
|
|
154
|
-
3. Output Guidelines
|
|
155
|
-
- Focus on essential information
|
|
156
|
-
- Clear step-by-step explanations
|
|
157
|
-
- Summarize results concisely
|
|
158
|
-
|
|
159
|
-
Tool Call Format:
|
|
160
|
-
<tool_call>
|
|
161
|
-
{
|
|
162
|
-
"name": "tool_name",
|
|
163
|
-
"arguments": {
|
|
164
|
-
"param1": "value1"
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
</tool_call>
|
|
168
|
-
|
|
169
|
-
Example (Correct - Subtask Delegation):
|
|
170
|
-
Assistant: I'll create a sub-agent to handle the code analysis subtask.
|
|
171
|
-
<tool_call>
|
|
172
|
-
{
|
|
173
|
-
"name": "create_sub_agent",
|
|
174
|
-
"arguments": {
|
|
175
|
-
"name": "CodeAnalyzer",
|
|
176
|
-
"task": "Analyze the utils.py file structure and generate documentation",
|
|
177
|
-
"context": "This is part of the larger documentation task"
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
</tool_call>
|
|
181
|
-
|
|
182
|
-
Example (Incorrect - Main Task):
|
|
183
|
-
❌ Assistant: I'll create a sub-agent to handle the entire project analysis.
|
|
184
|
-
<tool_call>
|
|
185
|
-
{
|
|
186
|
-
"name": "create_sub_agent",
|
|
187
|
-
"arguments": {
|
|
188
|
-
"name": "ProjectAnalyzer",
|
|
189
|
-
"task": "Analyze and document the entire project"
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
</tool_call>
|
|
193
|
-
|
|
194
|
-
Remember:
|
|
195
|
-
1. ONE step at a time
|
|
196
|
-
2. Create sub-agents ONLY for subtasks
|
|
197
|
-
3. Explain before acting
|
|
198
|
-
4. Wait for results"""
|
|
79
|
+
# 只处理第一个工具调用
|
|
80
|
+
tool_call = tool_calls[0]
|
|
81
|
+
name = tool_call["name"]
|
|
82
|
+
args = tool_call["arguments"]
|
|
83
|
+
|
|
84
|
+
if isinstance(args, str):
|
|
85
|
+
try:
|
|
86
|
+
args = json.loads(args)
|
|
87
|
+
except json.JSONDecodeError:
|
|
88
|
+
PrettyOutput.print(f"工具参数格式无效: {name}", OutputType.ERROR)
|
|
89
|
+
return ""
|
|
90
|
+
|
|
91
|
+
# 显示工具调用信息
|
|
92
|
+
PrettyOutput.section(f"执行工具: {name}", OutputType.TOOL)
|
|
93
|
+
if isinstance(args, dict):
|
|
94
|
+
for key, value in args.items():
|
|
95
|
+
PrettyOutput.print(f"参数: {key} = {value}", OutputType.DEBUG)
|
|
96
|
+
else:
|
|
97
|
+
PrettyOutput.print(f"参数: {args}", OutputType.DEBUG)
|
|
98
|
+
|
|
99
|
+
# 执行工具调用
|
|
100
|
+
result = self.execute_tool(name, args)
|
|
101
|
+
|
|
102
|
+
# 处理结果
|
|
103
|
+
if result["success"]:
|
|
104
|
+
stdout = result["stdout"]
|
|
105
|
+
stderr = result.get("stderr", "")
|
|
106
|
+
output_parts = []
|
|
107
|
+
if stdout:
|
|
108
|
+
output_parts.append(f"输出:\n{stdout}")
|
|
109
|
+
if stderr:
|
|
110
|
+
output_parts.append(f"错误:\n{stderr}")
|
|
111
|
+
output = "\n\n".join(output_parts)
|
|
112
|
+
output = "没有输出和错误" if not output else output
|
|
113
|
+
PrettyOutput.section("执行成功", OutputType.SUCCESS)
|
|
114
|
+
else:
|
|
115
|
+
error_msg = result["error"]
|
|
116
|
+
output = f"执行失败: {error_msg}"
|
|
117
|
+
PrettyOutput.section("执行失败", OutputType.ERROR)
|
|
118
|
+
|
|
119
|
+
return output
|
jarvis/tools/file_ops.py
CHANGED
|
@@ -5,7 +5,7 @@ from ..utils import PrettyOutput, OutputType
|
|
|
5
5
|
|
|
6
6
|
class FileOperationTool:
|
|
7
7
|
name = "file_operation"
|
|
8
|
-
description = "
|
|
8
|
+
description = "文件操作 (read/write/append/exists)"
|
|
9
9
|
parameters = {
|
|
10
10
|
"type": "object",
|
|
11
11
|
"properties": {
|
jarvis/tools/search.py
CHANGED
|
@@ -1,43 +1,106 @@
|
|
|
1
|
-
from typing import Dict, Any
|
|
1
|
+
from typing import Dict, Any, List
|
|
2
2
|
from duckduckgo_search import DDGS
|
|
3
3
|
from ..utils import PrettyOutput, OutputType
|
|
4
|
+
from .webpage import WebpageTool
|
|
4
5
|
|
|
5
6
|
class SearchTool:
|
|
6
7
|
name = "search"
|
|
7
|
-
description = "
|
|
8
|
+
description = "使用DuckDuckGo搜索引擎搜索信息,并根据问题提取关键信息"
|
|
8
9
|
parameters = {
|
|
9
10
|
"type": "object",
|
|
10
11
|
"properties": {
|
|
11
12
|
"query": {
|
|
12
13
|
"type": "string",
|
|
13
|
-
"description": "
|
|
14
|
+
"description": "搜索关键词"
|
|
15
|
+
},
|
|
16
|
+
"question": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "需要回答的具体问题,用于从搜索结果中提取相关信息"
|
|
14
19
|
},
|
|
15
20
|
"max_results": {
|
|
16
21
|
"type": "integer",
|
|
17
|
-
"description": "
|
|
18
|
-
"default":
|
|
22
|
+
"description": "最大搜索结果数量",
|
|
23
|
+
"default": 3
|
|
19
24
|
}
|
|
20
25
|
},
|
|
21
|
-
"required": ["query"]
|
|
26
|
+
"required": ["query", "question"]
|
|
22
27
|
}
|
|
23
28
|
|
|
29
|
+
def __init__(self, model):
|
|
30
|
+
"""初始化搜索工具,需要传入语言模型用于信息提取"""
|
|
31
|
+
self.model = model
|
|
32
|
+
self.webpage_tool = WebpageTool()
|
|
33
|
+
|
|
34
|
+
def _extract_info(self, contents: List[str], question: str) -> str:
|
|
35
|
+
"""使用语言模型从网页内容中提取关键信息"""
|
|
36
|
+
prompt = {
|
|
37
|
+
"role": "user",
|
|
38
|
+
"content": f"""请根据以下搜索结果内容,回答问题:{question}
|
|
39
|
+
|
|
40
|
+
搜索结果内容:
|
|
41
|
+
{'-' * 40}
|
|
42
|
+
{''.join(contents)}
|
|
43
|
+
{'-' * 40}
|
|
44
|
+
|
|
45
|
+
请提供一个简洁、准确的答案,重点关注与问题直接相关的信息。如果搜索结果中没有相关信息,请明确说明。
|
|
46
|
+
回答时注意:
|
|
47
|
+
1. 保持客观性,只基于搜索结果提供信息
|
|
48
|
+
2. 如果不同来源有冲突,请指出差异
|
|
49
|
+
3. 适当引用信息来源
|
|
50
|
+
4. 如果信息不完整或不确定,请说明"""
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
response = self.model.chat([prompt])
|
|
55
|
+
return response
|
|
56
|
+
except Exception as e:
|
|
57
|
+
return f"信息提取失败: {str(e)}"
|
|
58
|
+
|
|
24
59
|
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
25
|
-
"""
|
|
60
|
+
"""执行搜索并提取信息"""
|
|
26
61
|
try:
|
|
27
|
-
|
|
28
|
-
|
|
62
|
+
query = args["query"]
|
|
63
|
+
question = args["question"]
|
|
64
|
+
max_results = args.get("max_results", 3)
|
|
65
|
+
|
|
66
|
+
# 打印搜索信息
|
|
67
|
+
PrettyOutput.print(f"搜索查询: {query}", OutputType.INFO)
|
|
68
|
+
PrettyOutput.print(f"相关问题: {question}", OutputType.INFO)
|
|
29
69
|
|
|
30
70
|
# 获取搜索结果
|
|
71
|
+
search_results = []
|
|
31
72
|
with DDGS() as ddgs:
|
|
32
|
-
results = ddgs.text(
|
|
33
|
-
keywords=
|
|
34
|
-
max_results=
|
|
35
|
-
)
|
|
73
|
+
results = list(ddgs.text(
|
|
74
|
+
keywords=query,
|
|
75
|
+
max_results=max_results
|
|
76
|
+
))
|
|
77
|
+
|
|
78
|
+
# 收集网页内容
|
|
79
|
+
contents = []
|
|
80
|
+
for i, result in enumerate(results, 1):
|
|
81
|
+
try:
|
|
82
|
+
PrettyOutput.print(f"正在读取第 {i}/{len(results)} 个结果... {result['title']} - {result['href']} - {result['body']}", OutputType.PROGRESS)
|
|
83
|
+
webpage_result = self.webpage_tool.execute({"url": result["href"]})
|
|
84
|
+
if webpage_result["success"]:
|
|
85
|
+
contents.append(f"\n来源 {i}:{result['href']}\n")
|
|
86
|
+
contents.append(webpage_result["stdout"])
|
|
87
|
+
except Exception as e:
|
|
88
|
+
PrettyOutput.print(f"读取结果 {i} 失败: {str(e)}", OutputType.WARNING)
|
|
89
|
+
continue
|
|
90
|
+
|
|
91
|
+
if not contents:
|
|
92
|
+
return {
|
|
93
|
+
"success": False,
|
|
94
|
+
"error": "未能获取任何有效的搜索结果"
|
|
95
|
+
}
|
|
36
96
|
|
|
97
|
+
# 提取信息
|
|
98
|
+
PrettyOutput.print("正在分析搜索结果...", OutputType.PROGRESS)
|
|
99
|
+
analysis = self._extract_info(contents, question)
|
|
37
100
|
|
|
38
101
|
return {
|
|
39
102
|
"success": True,
|
|
40
|
-
"stdout":
|
|
103
|
+
"stdout": f"搜索分析结果:\n\n{analysis}",
|
|
41
104
|
"stderr": ""
|
|
42
105
|
}
|
|
43
106
|
|
jarvis/tools/shell.py
CHANGED
|
@@ -1,80 +1,75 @@
|
|
|
1
1
|
from typing import Dict, Any
|
|
2
|
-
import
|
|
2
|
+
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
import shlex
|
|
5
|
+
from pathlib import Path
|
|
3
6
|
from ..utils import PrettyOutput, OutputType
|
|
4
7
|
|
|
5
8
|
class ShellTool:
|
|
6
9
|
name = "execute_shell"
|
|
7
|
-
description = """
|
|
8
|
-
|
|
9
|
-
1. Use grep/awk/sed to filter output when possible
|
|
10
|
-
2. Avoid listing all files/dirs unless specifically needed
|
|
11
|
-
3. Prefer -q/--quiet flags when status is all that's needed
|
|
12
|
-
4. Use head/tail to limit long outputs
|
|
13
|
-
5. Redirect stderr to /dev/null for noisy commands
|
|
14
|
-
|
|
15
|
-
Examples of optimized commands:
|
|
16
|
-
- 'ls -l file.txt' instead of 'ls -l'
|
|
17
|
-
- 'grep -c pattern file' instead of 'grep pattern file'
|
|
18
|
-
- 'ps aux | grep process | head -n 5' instead of 'ps aux'
|
|
19
|
-
- 'command 2>/dev/null' to suppress error messages
|
|
20
|
-
- 'df -h . ' instead of 'df -h'
|
|
21
|
-
"""
|
|
10
|
+
description = """执行shell命令并返回结果"""
|
|
11
|
+
|
|
22
12
|
parameters = {
|
|
23
13
|
"type": "object",
|
|
24
14
|
"properties": {
|
|
25
15
|
"command": {
|
|
26
16
|
"type": "string",
|
|
27
|
-
"description": "Shell command to execute (
|
|
28
|
-
},
|
|
29
|
-
"timeout": {
|
|
30
|
-
"type": "integer",
|
|
31
|
-
"description": "Command execution timeout in seconds",
|
|
32
|
-
"default": 30
|
|
17
|
+
"description": "Shell command to execute (filter output when possible)"
|
|
33
18
|
}
|
|
34
19
|
},
|
|
35
20
|
"required": ["command"]
|
|
36
21
|
}
|
|
37
22
|
|
|
23
|
+
def _escape_command(self, cmd: str) -> str:
|
|
24
|
+
"""转义命令中的特殊字符"""
|
|
25
|
+
# 将命令中的单引号替换为'"'"'
|
|
26
|
+
return cmd.replace("'", "'\"'\"'")
|
|
27
|
+
|
|
38
28
|
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
39
29
|
"""执行shell命令"""
|
|
40
30
|
try:
|
|
41
|
-
# 获取参数
|
|
42
31
|
command = args["command"]
|
|
43
|
-
timeout = args.get("timeout", 30)
|
|
44
32
|
|
|
45
|
-
#
|
|
46
|
-
|
|
47
|
-
command,
|
|
48
|
-
shell=True,
|
|
49
|
-
capture_output=True,
|
|
50
|
-
text=True,
|
|
51
|
-
timeout=timeout
|
|
52
|
-
)
|
|
33
|
+
# 生成临时文件名
|
|
34
|
+
output_file = os.path.join(tempfile.gettempdir(), f"jarvis_shell_{os.getpid()}.log")
|
|
53
35
|
|
|
54
|
-
#
|
|
55
|
-
|
|
36
|
+
# 转义命令中的特殊字符
|
|
37
|
+
escaped_command = self._escape_command(command)
|
|
38
|
+
|
|
39
|
+
# 修改命令以使用script
|
|
40
|
+
tee_command = f"script -q -c '{escaped_command}' {output_file}"
|
|
56
41
|
|
|
57
|
-
# 添加命令信息
|
|
58
42
|
PrettyOutput.print(f"执行命令: {command}", OutputType.INFO)
|
|
59
|
-
output.append(f"命令: {command}")
|
|
60
|
-
output.append("")
|
|
61
43
|
|
|
62
|
-
#
|
|
63
|
-
|
|
64
|
-
|
|
44
|
+
# 执行命令
|
|
45
|
+
return_code = os.system(tee_command)
|
|
46
|
+
|
|
47
|
+
# 读取输出文件
|
|
48
|
+
try:
|
|
49
|
+
with open(output_file, 'r', encoding='utf-8', errors='replace') as f:
|
|
50
|
+
output = f.read()
|
|
51
|
+
# 移除script命令添加的头尾
|
|
52
|
+
if output:
|
|
53
|
+
lines = output.splitlines()
|
|
54
|
+
if len(lines) > 2:
|
|
55
|
+
output = "\n".join(lines[1:-1])
|
|
56
|
+
except Exception as e:
|
|
57
|
+
output = f"读取输出文件失败: {str(e)}"
|
|
58
|
+
finally:
|
|
59
|
+
# 清理临时文件
|
|
60
|
+
Path(output_file).unlink(missing_ok=True)
|
|
65
61
|
|
|
66
62
|
return {
|
|
67
|
-
"success":
|
|
68
|
-
"stdout":
|
|
69
|
-
"stderr":
|
|
70
|
-
"return_code":
|
|
71
|
-
}
|
|
72
|
-
except subprocess.TimeoutExpired:
|
|
73
|
-
return {
|
|
74
|
-
"success": False,
|
|
75
|
-
"error": f"命令执行超时 (>{timeout}秒)"
|
|
63
|
+
"success": return_code == 0,
|
|
64
|
+
"stdout": output,
|
|
65
|
+
"stderr": "",
|
|
66
|
+
"return_code": return_code
|
|
76
67
|
}
|
|
68
|
+
|
|
77
69
|
except Exception as e:
|
|
70
|
+
# 确保清理临时文件
|
|
71
|
+
if 'output_file' in locals():
|
|
72
|
+
Path(output_file).unlink(missing_ok=True)
|
|
78
73
|
return {
|
|
79
74
|
"success": False,
|
|
80
75
|
"error": str(e)
|
jarvis/tools/sub_agent.py
CHANGED
|
@@ -6,37 +6,26 @@ from .base import ToolRegistry
|
|
|
6
6
|
|
|
7
7
|
class SubAgentTool:
|
|
8
8
|
name = "create_sub_agent"
|
|
9
|
-
description = """
|
|
10
|
-
|
|
11
|
-
Use this tool when:
|
|
12
|
-
1. A subtask can be executed independently
|
|
13
|
-
2. The task requires separate context management
|
|
14
|
-
3. To optimize token usage of the main agent
|
|
15
|
-
4. For parallel task processing
|
|
9
|
+
description = """创建一个子代理来处理独立任务。(重要:子代理启动时没有任何上下文!必须提供完整的步骤和上下文。)"""
|
|
16
10
|
|
|
17
|
-
The sub-agent will:
|
|
18
|
-
1. Inherit all tools from the parent agent
|
|
19
|
-
2. Maintain its own conversation history
|
|
20
|
-
3. Return a comprehensive task summary
|
|
21
|
-
"""
|
|
22
11
|
parameters = {
|
|
23
12
|
"type": "object",
|
|
24
13
|
"properties": {
|
|
25
14
|
"name": {
|
|
26
15
|
"type": "string",
|
|
27
|
-
"description": "
|
|
16
|
+
"description": "子代理名称(例如:'文件分析器')"
|
|
28
17
|
},
|
|
29
18
|
"task": {
|
|
30
19
|
"type": "string",
|
|
31
|
-
"description": "
|
|
20
|
+
"description": "需要明确步骤和目标的任务"
|
|
32
21
|
},
|
|
33
22
|
"context": {
|
|
34
23
|
"type": "string",
|
|
35
|
-
"description": "
|
|
24
|
+
"description": "必填:背景信息、执行步骤和预期结果",
|
|
36
25
|
"default": ""
|
|
37
26
|
}
|
|
38
27
|
},
|
|
39
|
-
"required": ["name", "task"]
|
|
28
|
+
"required": ["name", "task", "context"]
|
|
40
29
|
}
|
|
41
30
|
|
|
42
31
|
def __init__(self, model: BaseModel):
|
|
@@ -48,32 +37,47 @@ The sub-agent will:
|
|
|
48
37
|
try:
|
|
49
38
|
name = args["name"]
|
|
50
39
|
task = args["task"]
|
|
51
|
-
context = args.get("context"
|
|
40
|
+
context = args.get("context")
|
|
52
41
|
|
|
53
|
-
|
|
42
|
+
if not context:
|
|
43
|
+
return {
|
|
44
|
+
"success": False,
|
|
45
|
+
"error": "必须提供上下文信息,包括完整的背景和执行步骤。"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
PrettyOutput.print(f"正在创建子代理 '{name}'...", OutputType.INFO)
|
|
54
49
|
|
|
55
50
|
# Create a new tool registry for the sub-agent
|
|
56
51
|
tool_registry = ToolRegistry(self.model)
|
|
57
52
|
|
|
58
53
|
# Create the sub-agent with the specified name
|
|
59
|
-
sub_agent = Agent(self.model, tool_registry, name=name)
|
|
54
|
+
sub_agent = Agent(self.model, tool_registry, name=name, is_sub_agent=True)
|
|
60
55
|
|
|
61
|
-
# Prepare the task with context
|
|
62
|
-
full_task = f"
|
|
56
|
+
# Prepare the task with context
|
|
57
|
+
full_task = f"""背景和步骤:
|
|
58
|
+
{context}
|
|
59
|
+
|
|
60
|
+
主要任务:
|
|
61
|
+
{task}
|
|
62
|
+
|
|
63
|
+
要求:
|
|
64
|
+
1. 严格按照提供的步骤执行
|
|
65
|
+
2. 每完成一个步骤都要报告进度
|
|
66
|
+
3. 突出显示任何问题或不明确的点
|
|
67
|
+
4. 提供符合预期输出的详细结果"""
|
|
63
68
|
|
|
64
|
-
PrettyOutput.print(f"
|
|
69
|
+
PrettyOutput.print(f"子代理 '{name}' 正在执行任务...", OutputType.INFO)
|
|
65
70
|
|
|
66
71
|
# Execute the task and get the summary
|
|
67
72
|
summary = sub_agent.run(full_task)
|
|
68
|
-
|
|
69
73
|
return {
|
|
70
74
|
"success": True,
|
|
71
|
-
"stdout": f"
|
|
75
|
+
"stdout": f"子代理 '{name}' 已完成。\n\n结果:\n{summary}",
|
|
72
76
|
"stderr": ""
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
except Exception as e:
|
|
76
80
|
return {
|
|
77
81
|
"success": False,
|
|
78
|
-
"error": f"
|
|
82
|
+
"error": f"子代理执行失败:{str(e)}"
|
|
79
83
|
}
|