jarvis-ai-assistant 0.1.5__py3-none-any.whl → 0.1.6__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 +93 -31
- jarvis/main.py +8 -8
- jarvis/models.py +55 -35
- jarvis/tools/__init__.py +3 -3
- jarvis/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- jarvis/tools/__pycache__/base.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/base.py +45 -113
- jarvis/tools/shell.py +62 -48
- jarvis/tools/sub_agent.py +82 -20
- jarvis/tools/user_input.py +74 -0
- jarvis/utils.py +101 -65
- jarvis/zte_llm.py +5 -1
- {jarvis_ai_assistant-0.1.5.dist-info → jarvis_ai_assistant-0.1.6.dist-info}/METADATA +1 -1
- jarvis_ai_assistant-0.1.6.dist-info/RECORD +38 -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.6.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.5.dist-info → jarvis_ai_assistant-0.1.6.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.5.dist-info → jarvis_ai_assistant-0.1.6.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
jarvis/agent.py
CHANGED
|
@@ -2,7 +2,7 @@ import json
|
|
|
2
2
|
import subprocess
|
|
3
3
|
from typing import Dict, Any, List, Optional
|
|
4
4
|
from .tools import ToolRegistry
|
|
5
|
-
from .utils import
|
|
5
|
+
from .utils import PrettyOutput, OutputType, get_multiline_input
|
|
6
6
|
from .models import BaseModel
|
|
7
7
|
import re
|
|
8
8
|
import os
|
|
@@ -14,28 +14,77 @@ class Agent:
|
|
|
14
14
|
self.model = model
|
|
15
15
|
self.tool_registry = tool_registry or ToolRegistry(model)
|
|
16
16
|
self.name = name
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
|
|
18
|
+
# 构建工具说明
|
|
19
|
+
tools_prompt = "Available Tools:\n"
|
|
20
|
+
for tool in self.tool_registry.get_all_tools():
|
|
21
|
+
tools_prompt += f"- Tool: {tool['function']['name']}\n"
|
|
22
|
+
tools_prompt += f" Description: {tool['function']['description']}\n"
|
|
23
|
+
tools_prompt += f" Arguments: {tool['function']['parameters']}\n"
|
|
24
|
+
|
|
19
25
|
self.messages = [
|
|
20
26
|
{
|
|
21
27
|
"role": "system",
|
|
22
|
-
"content": f"""You are {name},
|
|
28
|
+
"content": f"""You are {name}, an AI assistant that follows the ReAct (Reasoning + Acting) framework to solve tasks step by step.
|
|
29
|
+
|
|
30
|
+
FRAMEWORK:
|
|
31
|
+
1. Thought: Analyze the current situation and plan the next step
|
|
32
|
+
2. Action: Execute ONE specific tool call
|
|
33
|
+
3. Observation: Review the result
|
|
34
|
+
4. Next: Plan the next step or conclude
|
|
35
|
+
|
|
36
|
+
FORMAT:
|
|
37
|
+
Thought: I need to [reasoning about the current situation]...
|
|
38
|
+
Action: I will use [tool] to [purpose]...
|
|
39
|
+
<START_TOOL_CALL>
|
|
40
|
+
name: tool_name
|
|
41
|
+
arguments:
|
|
42
|
+
param1: value1
|
|
43
|
+
<END_TOOL_CALL>
|
|
44
|
+
|
|
45
|
+
After receiving result:
|
|
46
|
+
Observation: The tool returned [analyze result]...
|
|
47
|
+
Next: Based on this, I will [next step]...
|
|
48
|
+
|
|
49
|
+
CORE RULES:
|
|
50
|
+
1. ONE Action Per Response
|
|
51
|
+
- Only ONE tool call per response
|
|
52
|
+
- Additional tool calls will be ignored
|
|
53
|
+
- Complete current step before next
|
|
54
|
+
|
|
55
|
+
2. Clear Reasoning
|
|
56
|
+
- Explain your thought process
|
|
57
|
+
- Justify tool selection
|
|
58
|
+
- Analyze results thoroughly
|
|
59
|
+
|
|
60
|
+
Examples:
|
|
61
|
+
✓ Good Response:
|
|
62
|
+
Thought: I need to check the content of utils.py first to understand its structure.
|
|
63
|
+
Action: I will read the file content.
|
|
64
|
+
<START_TOOL_CALL>
|
|
65
|
+
name: file_operation
|
|
66
|
+
arguments:
|
|
67
|
+
operation: read
|
|
68
|
+
filepath: src/utils.py
|
|
69
|
+
<END_TOOL_CALL>
|
|
23
70
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
4. Create sub-agents for independent subtasks
|
|
29
|
-
5. Think carefully before each action
|
|
71
|
+
✗ Bad Response:
|
|
72
|
+
Thought: Let's analyze the code.
|
|
73
|
+
Action: I'll read and check everything.
|
|
74
|
+
[Multiple or vague tool calls...]
|
|
30
75
|
|
|
31
|
-
|
|
76
|
+
Remember:
|
|
77
|
+
- Always start with "Thought:"
|
|
78
|
+
- Use exactly ONE tool per response
|
|
79
|
+
- Wait for results before next step
|
|
80
|
+
- Clearly explain your reasoning
|
|
81
|
+
|
|
82
|
+
{tools_prompt}"""
|
|
32
83
|
}
|
|
33
84
|
]
|
|
34
|
-
self.spinner = Spinner()
|
|
35
85
|
|
|
36
86
|
def _call_model(self, messages: List[Dict], use_tools: bool = True) -> Dict:
|
|
37
87
|
"""调用模型获取响应"""
|
|
38
|
-
self.spinner.start()
|
|
39
88
|
try:
|
|
40
89
|
return self.model.chat(
|
|
41
90
|
messages=messages,
|
|
@@ -43,12 +92,14 @@ Key Principles:
|
|
|
43
92
|
)
|
|
44
93
|
except Exception as e:
|
|
45
94
|
raise Exception(f"{self.name}: 模型调用失败: {str(e)}")
|
|
46
|
-
finally:
|
|
47
|
-
self.spinner.stop()
|
|
48
95
|
|
|
49
96
|
def run(self, user_input: str) -> str:
|
|
50
97
|
"""处理用户输入并返回响应,返回任务总结报告"""
|
|
51
98
|
self.clear_history()
|
|
99
|
+
|
|
100
|
+
# 显示任务开始
|
|
101
|
+
PrettyOutput.section(f"开始新任务: {self.name}", OutputType.PLANNING)
|
|
102
|
+
|
|
52
103
|
self.messages.append({
|
|
53
104
|
"role": "user",
|
|
54
105
|
"content": user_input
|
|
@@ -56,9 +107,14 @@ Key Principles:
|
|
|
56
107
|
|
|
57
108
|
while True:
|
|
58
109
|
try:
|
|
110
|
+
# 显示思考状态
|
|
111
|
+
PrettyOutput.print("分析任务...", OutputType.PROGRESS)
|
|
59
112
|
response = self._call_model(self.messages)
|
|
60
113
|
current_response = response
|
|
61
114
|
|
|
115
|
+
# 流式输出已经在model中处理,这里添加换行
|
|
116
|
+
PrettyOutput.print_stream_end()
|
|
117
|
+
|
|
62
118
|
self.messages.append({
|
|
63
119
|
"role": "assistant",
|
|
64
120
|
"content": response["message"].get("content", ""),
|
|
@@ -67,10 +123,16 @@ Key Principles:
|
|
|
67
123
|
|
|
68
124
|
if len(current_response["message"]["tool_calls"]) > 0:
|
|
69
125
|
if current_response["message"].get("content"):
|
|
70
|
-
PrettyOutput.print(
|
|
126
|
+
PrettyOutput.print(current_response["message"]["content"], OutputType.SYSTEM)
|
|
71
127
|
|
|
72
|
-
|
|
73
|
-
|
|
128
|
+
try:
|
|
129
|
+
# 显示工具调用
|
|
130
|
+
PrettyOutput.print("执行工具调用...", OutputType.PROGRESS)
|
|
131
|
+
tool_result = self.tool_registry.handle_tool_calls(current_response["message"]["tool_calls"])
|
|
132
|
+
PrettyOutput.print(tool_result, OutputType.RESULT)
|
|
133
|
+
except Exception as e:
|
|
134
|
+
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
135
|
+
tool_result = f"Tool call failed: {str(e)}"
|
|
74
136
|
|
|
75
137
|
self.messages.append({
|
|
76
138
|
"role": "tool",
|
|
@@ -78,15 +140,12 @@ Key Principles:
|
|
|
78
140
|
})
|
|
79
141
|
continue
|
|
80
142
|
|
|
81
|
-
|
|
82
|
-
if final_content:
|
|
83
|
-
PrettyOutput.print(f"{self.name}: {final_content}", OutputType.SYSTEM)
|
|
84
|
-
|
|
143
|
+
# 获取用户输入
|
|
85
144
|
user_input = get_multiline_input(f"{self.name}: 您可以继续输入,或输入空行结束当前任务")
|
|
86
145
|
if not user_input:
|
|
87
|
-
PrettyOutput.print(
|
|
146
|
+
PrettyOutput.print("生成任务总结...", OutputType.PROGRESS)
|
|
88
147
|
|
|
89
|
-
#
|
|
148
|
+
# 生成任务总结
|
|
90
149
|
summary_prompt = {
|
|
91
150
|
"role": "user",
|
|
92
151
|
"content": """The task has been completed. Based on the previous analysis and execution results, provide a task summary including:
|
|
@@ -103,21 +162,25 @@ Key Principles:
|
|
|
103
162
|
|
|
104
163
|
Focus only on facts and actual results. Be direct and concise."""
|
|
105
164
|
}
|
|
165
|
+
|
|
106
166
|
while True:
|
|
107
167
|
try:
|
|
108
168
|
summary_response = self._call_model(self.messages + [summary_prompt], use_tools=False)
|
|
109
169
|
summary = summary_response["message"].get("content", "")
|
|
110
170
|
|
|
111
|
-
|
|
171
|
+
# 显示任务总结
|
|
172
|
+
PrettyOutput.section("任务总结", OutputType.SUCCESS)
|
|
112
173
|
PrettyOutput.print(summary, OutputType.SYSTEM)
|
|
113
|
-
PrettyOutput.
|
|
174
|
+
PrettyOutput.section("任务完成", OutputType.SUCCESS)
|
|
114
175
|
|
|
115
176
|
return summary
|
|
116
177
|
|
|
117
178
|
except Exception as e:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
179
|
+
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
180
|
+
|
|
181
|
+
if user_input == "__interrupt__":
|
|
182
|
+
PrettyOutput.print("任务已取消", OutputType.WARNING)
|
|
183
|
+
return "Task cancelled by user"
|
|
121
184
|
|
|
122
185
|
self.messages.append({
|
|
123
186
|
"role": "user",
|
|
@@ -125,8 +188,7 @@ Focus only on facts and actual results. Be direct and concise."""
|
|
|
125
188
|
})
|
|
126
189
|
|
|
127
190
|
except Exception as e:
|
|
128
|
-
|
|
129
|
-
PrettyOutput.print(error_msg, OutputType.ERROR)
|
|
191
|
+
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
130
192
|
|
|
131
193
|
def clear_history(self):
|
|
132
194
|
"""清除对话历史,只保留系统提示"""
|
jarvis/main.py
CHANGED
|
@@ -19,7 +19,7 @@ from jarvis.zte_llm import create_zte_llm
|
|
|
19
19
|
# 定义支持的平台和模型
|
|
20
20
|
SUPPORTED_PLATFORMS = {
|
|
21
21
|
"ollama": {
|
|
22
|
-
"models": ["
|
|
22
|
+
"models": ["qwen2.5:14b", "qwq"],
|
|
23
23
|
"default": "qwen2.5:14b"
|
|
24
24
|
},
|
|
25
25
|
"ddgs": {
|
|
@@ -88,13 +88,16 @@ def select_task(tasks: dict) -> str:
|
|
|
88
88
|
|
|
89
89
|
def main():
|
|
90
90
|
"""Main entry point for Jarvis."""
|
|
91
|
+
|
|
92
|
+
load_env_from_file()
|
|
93
|
+
|
|
91
94
|
parser = argparse.ArgumentParser(description="Jarvis AI Assistant")
|
|
92
95
|
|
|
93
96
|
# 添加平台选择参数
|
|
94
97
|
parser.add_argument(
|
|
95
98
|
"--platform",
|
|
96
99
|
choices=list(SUPPORTED_PLATFORMS.keys()),
|
|
97
|
-
default="ddgs",
|
|
100
|
+
default=os.getenv("JARVIS_PLATFORM") or "ddgs",
|
|
98
101
|
help="选择运行平台 (默认: ollama)"
|
|
99
102
|
)
|
|
100
103
|
|
|
@@ -107,13 +110,13 @@ def main():
|
|
|
107
110
|
# 添加API基础URL参数
|
|
108
111
|
parser.add_argument(
|
|
109
112
|
"--api-base",
|
|
110
|
-
default="http://localhost:11434",
|
|
113
|
+
default=os.getenv("JARVIS_OLLAMA_API_BASE") or "http://localhost:11434",
|
|
111
114
|
help="Ollama API基础URL (仅用于Ollama平台, 默认: http://localhost:11434)"
|
|
112
115
|
)
|
|
113
116
|
|
|
114
117
|
args = parser.parse_args()
|
|
115
118
|
|
|
116
|
-
|
|
119
|
+
args.model = args.model or os.getenv("JARVIS_MODEL")
|
|
117
120
|
|
|
118
121
|
# 验证并设置默认模型
|
|
119
122
|
if args.model:
|
|
@@ -162,12 +165,9 @@ def main():
|
|
|
162
165
|
while True:
|
|
163
166
|
try:
|
|
164
167
|
user_input = get_multiline_input("请输入您的任务(输入空行退出):")
|
|
165
|
-
if not user_input:
|
|
168
|
+
if not user_input or user_input == "__interrupt__":
|
|
166
169
|
break
|
|
167
170
|
agent.run(user_input)
|
|
168
|
-
except KeyboardInterrupt:
|
|
169
|
-
print("\n正在退出...")
|
|
170
|
-
break
|
|
171
171
|
except Exception as e:
|
|
172
172
|
PrettyOutput.print(f"错误: {str(e)}", OutputType.ERROR)
|
|
173
173
|
|
jarvis/models.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import json
|
|
2
1
|
import re
|
|
3
2
|
import time
|
|
4
3
|
from typing import Dict, List, Optional
|
|
5
4
|
from duckduckgo_search import DDGS
|
|
6
5
|
import ollama
|
|
7
6
|
from abc import ABC, abstractmethod
|
|
7
|
+
import yaml
|
|
8
8
|
|
|
9
9
|
from .utils import OutputType, PrettyOutput
|
|
10
10
|
|
|
@@ -18,32 +18,46 @@ class BaseModel(ABC):
|
|
|
18
18
|
|
|
19
19
|
@staticmethod
|
|
20
20
|
def extract_tool_calls(content: str) -> List[Dict]:
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
"""从内容中提取工具调用,只返回第一个有效的工具调用"""
|
|
22
|
+
# 分割内容为行
|
|
23
|
+
lines = content.split('\n')
|
|
24
|
+
tool_call_lines = []
|
|
25
|
+
in_tool_call = False
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
for
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
tool_call_text = match.group(1).strip()
|
|
31
|
-
tool_call_data = json.loads(tool_call_text)
|
|
27
|
+
# 逐行处理
|
|
28
|
+
for line in lines:
|
|
29
|
+
if not line:
|
|
30
|
+
continue
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
32
|
+
if line == '<START_TOOL_CALL>':
|
|
33
|
+
tool_call_lines = []
|
|
34
|
+
in_tool_call = True
|
|
35
|
+
continue
|
|
36
|
+
elif line == '<END_TOOL_CALL>':
|
|
37
|
+
if in_tool_call and tool_call_lines:
|
|
38
|
+
try:
|
|
39
|
+
# 解析工具调用内容
|
|
40
|
+
tool_call_text = '\n'.join(tool_call_lines)
|
|
41
|
+
tool_call_data = yaml.safe_load(tool_call_text)
|
|
42
|
+
|
|
43
|
+
# 验证必要的字段
|
|
44
|
+
if "name" in tool_call_data and "arguments" in tool_call_data:
|
|
45
|
+
# 只返回第一个有效的工具调用
|
|
46
|
+
return [{
|
|
47
|
+
"function": {
|
|
48
|
+
"name": tool_call_data["name"],
|
|
49
|
+
"arguments": tool_call_data["arguments"]
|
|
50
|
+
}
|
|
51
|
+
}]
|
|
52
|
+
except yaml.YAMLError:
|
|
53
|
+
pass # 跳过无效的YAML
|
|
54
|
+
except Exception:
|
|
55
|
+
pass # 跳过其他错误
|
|
56
|
+
in_tool_call = False
|
|
57
|
+
elif in_tool_call:
|
|
58
|
+
tool_call_lines.append(line)
|
|
59
|
+
|
|
60
|
+
return [] # 如果没有找到有效的工具调用,返回空列表
|
|
47
61
|
|
|
48
62
|
|
|
49
63
|
class DDGSModel(BaseModel):
|
|
@@ -57,11 +71,7 @@ class DDGSModel(BaseModel):
|
|
|
57
71
|
self.model_name = model_name
|
|
58
72
|
|
|
59
73
|
def __make_prompt(self, messages: List[Dict], tools: Optional[List[Dict]] = None) -> str:
|
|
60
|
-
prompt = "
|
|
61
|
-
for tool in tools:
|
|
62
|
-
prompt += f"- Tool: {tool['function']['name']}\n"
|
|
63
|
-
prompt += f" Description: {tool['function']['description']}\n"
|
|
64
|
-
prompt += f" Arguments: {tool['function']['parameters']}\n"
|
|
74
|
+
prompt = ""
|
|
65
75
|
for message in messages:
|
|
66
76
|
prompt += f"[{message['role']}]: {message['content']}\n"
|
|
67
77
|
return prompt
|
|
@@ -70,6 +80,7 @@ class DDGSModel(BaseModel):
|
|
|
70
80
|
ddgs = DDGS()
|
|
71
81
|
prompt = self.__make_prompt(messages, tools)
|
|
72
82
|
content = ddgs.chat(prompt)
|
|
83
|
+
PrettyOutput.print_stream(content, OutputType.SYSTEM)
|
|
73
84
|
tool_calls = BaseModel.extract_tool_calls(content)
|
|
74
85
|
return {
|
|
75
86
|
"message": {
|
|
@@ -90,16 +101,25 @@ class OllamaModel(BaseModel):
|
|
|
90
101
|
def chat(self, messages: List[Dict], tools: Optional[List[Dict]] = None) -> Dict:
|
|
91
102
|
"""调用Ollama API获取响应"""
|
|
92
103
|
try:
|
|
93
|
-
|
|
104
|
+
# 使用流式调用
|
|
105
|
+
stream = self.client.chat(
|
|
94
106
|
model=self.model_name,
|
|
95
107
|
messages=messages,
|
|
96
|
-
|
|
108
|
+
stream=True
|
|
97
109
|
)
|
|
98
110
|
|
|
99
|
-
|
|
100
|
-
|
|
111
|
+
# 收集完整响应
|
|
112
|
+
content_parts = []
|
|
113
|
+
for chunk in stream:
|
|
114
|
+
if chunk.message.content:
|
|
115
|
+
content_parts.append(chunk.message.content)
|
|
116
|
+
# 实时打印内容
|
|
117
|
+
PrettyOutput.print_stream(chunk.message.content, OutputType.SYSTEM)
|
|
118
|
+
|
|
119
|
+
# 合并完整内容
|
|
120
|
+
content = "".join(content_parts)
|
|
121
|
+
tool_calls = BaseModel.extract_tool_calls(content)
|
|
101
122
|
|
|
102
|
-
# 转换响应格式
|
|
103
123
|
return {
|
|
104
124
|
"message": {
|
|
105
125
|
"content": content,
|
jarvis/tools/__init__.py
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
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
|
|
6
5
|
from .webpage import WebpageTool
|
|
6
|
+
from .user_input import UserInputTool
|
|
7
7
|
|
|
8
8
|
__all__ = [
|
|
9
9
|
'Tool',
|
|
10
10
|
'ToolRegistry',
|
|
11
|
-
'PythonScript',
|
|
12
11
|
'FileOperationTool',
|
|
13
12
|
'SearchTool',
|
|
14
13
|
'ShellTool',
|
|
15
14
|
'WebpageTool',
|
|
16
|
-
|
|
15
|
+
'UserInputTool',
|
|
16
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
jarvis/tools/base.py
CHANGED
|
@@ -35,17 +35,17 @@ class ToolRegistry:
|
|
|
35
35
|
"""注册所有默认工具"""
|
|
36
36
|
from .search import SearchTool
|
|
37
37
|
from .shell import ShellTool
|
|
38
|
-
from .python_script import PythonScriptTool
|
|
39
38
|
from .file_ops import FileOperationTool
|
|
40
39
|
from .webpage import WebpageTool
|
|
41
40
|
from .sub_agent import SubAgentTool
|
|
41
|
+
from .user_input import UserInputTool
|
|
42
42
|
|
|
43
43
|
tools = [
|
|
44
44
|
SearchTool(),
|
|
45
45
|
ShellTool(),
|
|
46
|
-
PythonScriptTool(),
|
|
47
46
|
FileOperationTool(),
|
|
48
47
|
WebpageTool(),
|
|
48
|
+
UserInputTool(),
|
|
49
49
|
]
|
|
50
50
|
|
|
51
51
|
for tool in tools:
|
|
@@ -84,115 +84,47 @@ class ToolRegistry:
|
|
|
84
84
|
return tool.execute(arguments)
|
|
85
85
|
|
|
86
86
|
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)
|
|
87
|
+
"""处理工具调用,只处理第一个工具"""
|
|
88
|
+
if not tool_calls:
|
|
89
|
+
return ""
|
|
105
90
|
|
|
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
|
-
* Researching different aspects of a topic
|
|
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"""
|
|
91
|
+
# 只处理第一个工具调用
|
|
92
|
+
tool_call = tool_calls[0]
|
|
93
|
+
name = tool_call["function"]["name"]
|
|
94
|
+
args = tool_call["function"]["arguments"]
|
|
95
|
+
|
|
96
|
+
if isinstance(args, str):
|
|
97
|
+
try:
|
|
98
|
+
args = json.loads(args)
|
|
99
|
+
except json.JSONDecodeError:
|
|
100
|
+
PrettyOutput.print(f"工具参数格式无效: {name}", OutputType.ERROR)
|
|
101
|
+
return ""
|
|
102
|
+
|
|
103
|
+
# 显示工具调用信息
|
|
104
|
+
PrettyOutput.section(f"执行工具: {name}", OutputType.TOOL)
|
|
105
|
+
if isinstance(args, dict):
|
|
106
|
+
for key, value in args.items():
|
|
107
|
+
PrettyOutput.print(f"参数: {key} = {value}", OutputType.DEBUG)
|
|
108
|
+
else:
|
|
109
|
+
PrettyOutput.print(f"参数: {args}", OutputType.DEBUG)
|
|
110
|
+
|
|
111
|
+
# 执行工具调用
|
|
112
|
+
result = self.execute_tool(name, args)
|
|
113
|
+
|
|
114
|
+
# 处理结果
|
|
115
|
+
if result["success"]:
|
|
116
|
+
stdout = result["stdout"]
|
|
117
|
+
stderr = result.get("stderr", "")
|
|
118
|
+
output_parts = []
|
|
119
|
+
if stdout:
|
|
120
|
+
output_parts.append(f"输出:\n{stdout}")
|
|
121
|
+
if stderr:
|
|
122
|
+
output_parts.append(f"错误:\n{stderr}")
|
|
123
|
+
output = "\n\n".join(output_parts)
|
|
124
|
+
PrettyOutput.section("执行成功", OutputType.SUCCESS)
|
|
125
|
+
else:
|
|
126
|
+
error_msg = result["error"]
|
|
127
|
+
output = f"执行失败: {error_msg}"
|
|
128
|
+
PrettyOutput.section("执行失败", OutputType.ERROR)
|
|
129
|
+
|
|
130
|
+
return output
|