jarvis-ai-assistant 0.1.6__tar.gz → 0.1.8__tar.gz
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_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/PKG-INFO +2 -1
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/pyproject.toml +2 -1
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/setup.py +2 -1
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/__init__.py +1 -1
- jarvis_ai_assistant-0.1.8/src/jarvis/__pycache__/__init__.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.8/src/jarvis/__pycache__/agent.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.8/src/jarvis/__pycache__/main.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.8/src/jarvis/__pycache__/models.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.8/src/jarvis/__pycache__/zte_llm.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.8/src/jarvis/agent.py +298 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/main.py +22 -6
- jarvis_ai_assistant-0.1.8/src/jarvis/models.py +122 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/__init__.py +0 -2
- jarvis_ai_assistant-0.1.8/src/jarvis/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.8/src/jarvis/tools/__pycache__/base.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.8/src/jarvis/tools/__pycache__/bing_search.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/__pycache__/file_ops.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.8/src/jarvis/tools/__pycache__/search.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/__pycache__/shell.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.8/src/jarvis/tools/__pycache__/sub_agent.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.8/src/jarvis/tools/__pycache__/webpage.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/base.py +8 -19
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/file_ops.py +1 -1
- jarvis_ai_assistant-0.1.8/src/jarvis/tools/search.py +144 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/shell.py +1 -20
- jarvis_ai_assistant-0.1.8/src/jarvis/tools/sub_agent.py +83 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/webpage.py +12 -26
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/zte_llm.py +26 -27
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis_ai_assistant.egg-info/PKG-INFO +2 -1
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis_ai_assistant.egg-info/SOURCES.txt +1 -1
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis_ai_assistant.egg-info/requires.txt +1 -0
- jarvis_ai_assistant-0.1.6/src/jarvis/__pycache__/__init__.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.6/src/jarvis/__pycache__/agent.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.6/src/jarvis/__pycache__/main.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.6/src/jarvis/__pycache__/models.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.6/src/jarvis/__pycache__/zte_llm.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.6/src/jarvis/agent.py +0 -195
- jarvis_ai_assistant-0.1.6/src/jarvis/models.py +0 -130
- jarvis_ai_assistant-0.1.6/src/jarvis/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.6/src/jarvis/tools/__pycache__/base.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.6/src/jarvis/tools/__pycache__/search.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.6/src/jarvis/tools/__pycache__/sub_agent.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.6/src/jarvis/tools/__pycache__/webpage.cpython-313.pyc +0 -0
- jarvis_ai_assistant-0.1.6/src/jarvis/tools/search.py +0 -48
- jarvis_ai_assistant-0.1.6/src/jarvis/tools/sub_agent.py +0 -141
- jarvis_ai_assistant-0.1.6/src/jarvis/tools/user_input.py +0 -74
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/MANIFEST.in +0 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/README.md +0 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/setup.cfg +0 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/__pycache__/tools.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/__pycache__/utils.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/__pycache__/python_script.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/__pycache__/rag.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/__pycache__/user_confirmation.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/__pycache__/user_input.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/tools/__pycache__/user_interaction.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis/utils.py +0 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis_ai_assistant.egg-info/dependency_links.txt +0 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis_ai_assistant.egg-info/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.6 → jarvis_ai_assistant-0.1.8}/src/jarvis_ai_assistant.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: jarvis-ai-assistant
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: Jarvis: An AI assistant that uses tools to interact with the system
|
|
5
5
|
Home-page: https://github.com/skyfireitdiy/Jarvis
|
|
6
6
|
Author: skyfire
|
|
@@ -22,6 +22,7 @@ Requires-Dist: duckduckgo-search>=3.0.0
|
|
|
22
22
|
Requires-Dist: pyyaml>=5.1
|
|
23
23
|
Requires-Dist: ollama>=0.1.6
|
|
24
24
|
Requires-Dist: colorama>=0.4.6
|
|
25
|
+
Requires-Dist: openai>=1.2.0
|
|
25
26
|
Provides-Extra: dev
|
|
26
27
|
Requires-Dist: pytest; extra == "dev"
|
|
27
28
|
Requires-Dist: black; extra == "dev"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "jarvis-ai-assistant"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.8"
|
|
8
8
|
description = "Jarvis: An AI assistant that uses tools to interact with the system"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "Your Name", email = "your.email@example.com" }]
|
|
@@ -26,6 +26,7 @@ dependencies = [
|
|
|
26
26
|
"pyyaml>=5.1",
|
|
27
27
|
"ollama>=0.1.6",
|
|
28
28
|
"colorama>=0.4.6",
|
|
29
|
+
"openai>=1.2.0",
|
|
29
30
|
]
|
|
30
31
|
requires-python = ">=3.8"
|
|
31
32
|
|
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name="jarvis-ai-assistant",
|
|
5
|
-
version="0.1.
|
|
5
|
+
version="0.1.8",
|
|
6
6
|
author="skyfire",
|
|
7
7
|
author_email="skyfireitdiy@hotmail.com",
|
|
8
8
|
description="An AI assistant that uses various tools to interact with the system",
|
|
@@ -19,6 +19,7 @@ setup(
|
|
|
19
19
|
"pyyaml>=5.1",
|
|
20
20
|
"ollama>=0.1.6",
|
|
21
21
|
"colorama>=0.4.6",
|
|
22
|
+
"openai>=1.2.0",
|
|
22
23
|
],
|
|
23
24
|
entry_points={
|
|
24
25
|
"console_scripts": [
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import subprocess
|
|
3
|
+
from typing import Dict, Any, List, Optional, Tuple
|
|
4
|
+
|
|
5
|
+
import yaml
|
|
6
|
+
from .tools import ToolRegistry
|
|
7
|
+
from .utils import PrettyOutput, OutputType, get_multiline_input
|
|
8
|
+
from .models import BaseModel
|
|
9
|
+
import re
|
|
10
|
+
import os
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
|
|
13
|
+
class Agent:
|
|
14
|
+
def __init__(self, model: BaseModel, tool_registry: ToolRegistry, name: str = "Jarvis", is_sub_agent: bool = False):
|
|
15
|
+
"""Initialize Agent with a model, optional tool registry and name
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
model: 语言模型实例
|
|
19
|
+
tool_registry: 工具注册表实例
|
|
20
|
+
name: Agent名称,默认为"Jarvis"
|
|
21
|
+
is_sub_agent: 是否为子Agent,默认为False
|
|
22
|
+
"""
|
|
23
|
+
self.model = model
|
|
24
|
+
self.tool_registry = tool_registry or ToolRegistry(model)
|
|
25
|
+
self.name = name
|
|
26
|
+
self.is_sub_agent = is_sub_agent
|
|
27
|
+
|
|
28
|
+
# 构建工具说明
|
|
29
|
+
tools_prompt = "可用工具:\n"
|
|
30
|
+
for tool in self.tool_registry.get_all_tools():
|
|
31
|
+
tools_prompt += f"- 名称: {tool['name']}\n"
|
|
32
|
+
tools_prompt += f" 描述: {tool['description']}\n"
|
|
33
|
+
tools_prompt += f" 参数: {tool['parameters']}\n"
|
|
34
|
+
|
|
35
|
+
self.messages = [
|
|
36
|
+
{
|
|
37
|
+
"role": "system",
|
|
38
|
+
"content": f"""你是 {name},一个严格遵循 ReAct 框架进行逐步推理和行动的 AI 助手。
|
|
39
|
+
|
|
40
|
+
{tools_prompt}
|
|
41
|
+
|
|
42
|
+
关键规则:
|
|
43
|
+
‼️ 禁止创建虚假对话
|
|
44
|
+
‼️ 禁止假设用户回应
|
|
45
|
+
‼️ 禁止在没有实际用户输入时继续
|
|
46
|
+
‼️ 只回应用户实际说的内容
|
|
47
|
+
‼️ 每个动作后停止并等待
|
|
48
|
+
|
|
49
|
+
ReAct 框架:
|
|
50
|
+
1. 思考
|
|
51
|
+
- 分析当前情况
|
|
52
|
+
- 考虑可用工具
|
|
53
|
+
- 规划下一步行动
|
|
54
|
+
- 仅基于事实
|
|
55
|
+
- 不做用户回应的假设
|
|
56
|
+
- 不想象对话内容
|
|
57
|
+
|
|
58
|
+
2. 行动(可选)
|
|
59
|
+
- 不是每次回应都需要调用工具
|
|
60
|
+
- 如果需要更多信息,直接询问用户
|
|
61
|
+
- 使用工具时:
|
|
62
|
+
- 只使用下面列出的工具
|
|
63
|
+
- 每次只执行一个工具
|
|
64
|
+
- 工具由用户手动执行
|
|
65
|
+
- 必须使用有效的YAML格式:
|
|
66
|
+
<START_TOOL_CALL>
|
|
67
|
+
name: tool_name
|
|
68
|
+
arguments:
|
|
69
|
+
param1: value1 # 所有参数必须正确缩进
|
|
70
|
+
param2: | # 使用YAML块样式表示多行字符串
|
|
71
|
+
line1
|
|
72
|
+
line2
|
|
73
|
+
<END_TOOL_CALL>
|
|
74
|
+
|
|
75
|
+
3. 观察
|
|
76
|
+
- 等待工具执行结果或用户回应
|
|
77
|
+
- 工具执行结果由用户提供
|
|
78
|
+
- 不要假设或想象回应
|
|
79
|
+
- 不要创建虚假对话
|
|
80
|
+
- 停止并等待实际输入
|
|
81
|
+
|
|
82
|
+
回应格式:
|
|
83
|
+
思考:我分析当前情况[具体情况]... 基于[事实],我需要[目标]...
|
|
84
|
+
|
|
85
|
+
[如果需要使用工具:]
|
|
86
|
+
行动:我将使用[工具]来[具体目的]...
|
|
87
|
+
<START_TOOL_CALL>
|
|
88
|
+
name: tool_name
|
|
89
|
+
arguments:
|
|
90
|
+
param1: value1
|
|
91
|
+
param2: |
|
|
92
|
+
multiline
|
|
93
|
+
value
|
|
94
|
+
<END_TOOL_CALL>
|
|
95
|
+
|
|
96
|
+
[如果需要更多信息:]
|
|
97
|
+
我需要了解更多关于[具体细节]的信息。请提供[需要的信息]。
|
|
98
|
+
|
|
99
|
+
严格规则:
|
|
100
|
+
‼️ 只使用下面列出的工具
|
|
101
|
+
‼️ 工具调用是可选的 - 需要时询问用户
|
|
102
|
+
‼️ 每次只能调用一个工具
|
|
103
|
+
‼️ 工具调用必须是有效的YAML格式
|
|
104
|
+
‼️ 参数必须正确缩进
|
|
105
|
+
‼️ 使用YAML块样式(|)表示多行值
|
|
106
|
+
‼️ <END_TOOL_CALL>后的内容将被丢弃
|
|
107
|
+
‼️ 工具由用户手动执行
|
|
108
|
+
‼️ 等待用户提供工具执行结果
|
|
109
|
+
‼️ 不要假设或想象用户回应
|
|
110
|
+
‼️ 没有用户输入时不要继续对话
|
|
111
|
+
‼️ 不要创建虚假对话
|
|
112
|
+
‼️ 每个动作后停止
|
|
113
|
+
‼️ 不要假设结果
|
|
114
|
+
‼️ 不要假设行动
|
|
115
|
+
|
|
116
|
+
注意事项:
|
|
117
|
+
- 先思考再行动
|
|
118
|
+
- 需要时询问用户
|
|
119
|
+
- 只使用列出的工具
|
|
120
|
+
- 一次一个工具
|
|
121
|
+
- 严格遵循YAML格式
|
|
122
|
+
- 等待用户回应
|
|
123
|
+
- 工具结果来自用户
|
|
124
|
+
- 不要假设回应
|
|
125
|
+
- 不要虚构对话
|
|
126
|
+
- 每个动作后停止
|
|
127
|
+
- 只在有实际用户输入时继续
|
|
128
|
+
"""
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
@staticmethod
|
|
133
|
+
def extract_tool_calls(content: str) -> Tuple[str, List[Dict]]:
|
|
134
|
+
"""从内容中提取工具调用,如果检测到多个工具调用则抛出异常,并返回工具调用之前的内容和工具调用"""
|
|
135
|
+
# 分割内容为行
|
|
136
|
+
lines = content.split('\n')
|
|
137
|
+
tool_call_lines = []
|
|
138
|
+
content_lines = [] # 存储工具调用之前的内容
|
|
139
|
+
in_tool_call = False
|
|
140
|
+
|
|
141
|
+
# 逐行处理
|
|
142
|
+
for line in lines:
|
|
143
|
+
content_lines.append(line) # 所有内容都添加到 content_lines
|
|
144
|
+
|
|
145
|
+
if '<START_TOOL_CALL>' in line:
|
|
146
|
+
in_tool_call = True
|
|
147
|
+
continue
|
|
148
|
+
elif '<END_TOOL_CALL>' in line:
|
|
149
|
+
if in_tool_call and tool_call_lines:
|
|
150
|
+
try:
|
|
151
|
+
# 直接解析YAML
|
|
152
|
+
tool_call_text = '\n'.join(tool_call_lines)
|
|
153
|
+
tool_call_data = yaml.safe_load(tool_call_text)
|
|
154
|
+
|
|
155
|
+
# 验证必要的字段
|
|
156
|
+
if "name" in tool_call_data and "arguments" in tool_call_data:
|
|
157
|
+
# 返回工具调用之前的内容和工具调用
|
|
158
|
+
return '\n'.join(content_lines), [{
|
|
159
|
+
"name": tool_call_data["name"],
|
|
160
|
+
"arguments": tool_call_data["arguments"]
|
|
161
|
+
}]
|
|
162
|
+
else:
|
|
163
|
+
PrettyOutput.print("工具调用缺少必要字段", OutputType.ERROR)
|
|
164
|
+
raise '工具调用缺少必要字段'
|
|
165
|
+
except yaml.YAMLError as e:
|
|
166
|
+
PrettyOutput.print(f"YAML解析错误: {str(e)}", OutputType.ERROR)
|
|
167
|
+
raise 'YAML解析错误'
|
|
168
|
+
except Exception as e:
|
|
169
|
+
PrettyOutput.print(f"处理工具调用时发生错误: {str(e)}", OutputType.ERROR)
|
|
170
|
+
raise '处理工具调用时发生错误'
|
|
171
|
+
in_tool_call = False
|
|
172
|
+
continue
|
|
173
|
+
|
|
174
|
+
if in_tool_call:
|
|
175
|
+
tool_call_lines.append(line)
|
|
176
|
+
|
|
177
|
+
# 如果没有找到有效的工具调用,返回原始内容
|
|
178
|
+
return '\n'.join(content_lines), []
|
|
179
|
+
|
|
180
|
+
def _call_model(self, messages: List[Dict]) -> Dict:
|
|
181
|
+
"""调用模型获取响应"""
|
|
182
|
+
try:
|
|
183
|
+
return self.model.chat(
|
|
184
|
+
messages=messages,
|
|
185
|
+
)
|
|
186
|
+
except Exception as e:
|
|
187
|
+
raise Exception(f"{self.name}: 模型调用失败: {str(e)}")
|
|
188
|
+
|
|
189
|
+
def run(self, user_input: str) -> str:
|
|
190
|
+
"""处理用户输入并返回响应,返回任务总结报告"""
|
|
191
|
+
self.clear_history()
|
|
192
|
+
|
|
193
|
+
# 显示任务开始
|
|
194
|
+
PrettyOutput.section(f"开始新任务: {self.name}", OutputType.PLANNING)
|
|
195
|
+
|
|
196
|
+
self.messages.append({
|
|
197
|
+
"role": "user",
|
|
198
|
+
"content": user_input
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
while True:
|
|
202
|
+
try:
|
|
203
|
+
# 显示思考状态
|
|
204
|
+
PrettyOutput.print("分析任务...", OutputType.PROGRESS)
|
|
205
|
+
|
|
206
|
+
current_response = self._call_model(self.messages)
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
result = Agent.extract_tool_calls(current_response)
|
|
210
|
+
except Exception as e:
|
|
211
|
+
PrettyOutput.print(f"工具调用错误: {str(e)}", OutputType.ERROR)
|
|
212
|
+
self.messages.append({
|
|
213
|
+
"role": "user",
|
|
214
|
+
"content": f"工具调用错误: {str(e)}"
|
|
215
|
+
})
|
|
216
|
+
continue
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
self.messages.append({
|
|
220
|
+
"role": "assistant",
|
|
221
|
+
"content": result[0]
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
if len(result[1]) > 0:
|
|
226
|
+
try:
|
|
227
|
+
# 显示工具调用
|
|
228
|
+
PrettyOutput.print("执行工具调用...", OutputType.PROGRESS)
|
|
229
|
+
tool_result = self.tool_registry.handle_tool_calls(result[1])
|
|
230
|
+
PrettyOutput.print(tool_result, OutputType.RESULT)
|
|
231
|
+
except Exception as e:
|
|
232
|
+
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
233
|
+
tool_result = f"Tool call failed: {str(e)}"
|
|
234
|
+
|
|
235
|
+
self.messages.append({
|
|
236
|
+
"role": "user",
|
|
237
|
+
"content": tool_result
|
|
238
|
+
})
|
|
239
|
+
continue
|
|
240
|
+
|
|
241
|
+
# 获取用户输入
|
|
242
|
+
user_input = get_multiline_input(f"{self.name}: 您可以继续输入,或输入空行结束当前任务")
|
|
243
|
+
if not user_input:
|
|
244
|
+
# 只有子Agent才需要生成任务总结
|
|
245
|
+
if self.is_sub_agent:
|
|
246
|
+
PrettyOutput.print("生成任务总结...", OutputType.PROGRESS)
|
|
247
|
+
|
|
248
|
+
# 生成任务总结
|
|
249
|
+
summary_prompt = {
|
|
250
|
+
"role": "user",
|
|
251
|
+
"content": """任务已完成。请根据之前的分析和执行结果,提供一个简明的任务总结,包括:
|
|
252
|
+
|
|
253
|
+
1. 关键发现:
|
|
254
|
+
- 分析过程中的重要发现
|
|
255
|
+
- 工具执行的关键结果
|
|
256
|
+
- 发现的重要数据
|
|
257
|
+
|
|
258
|
+
2. 执行成果:
|
|
259
|
+
- 任务完成情况
|
|
260
|
+
- 具体实现结果
|
|
261
|
+
- 达成的目标
|
|
262
|
+
|
|
263
|
+
请直接描述事实和实际结果,保持简洁明了。"""
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
while True:
|
|
267
|
+
try:
|
|
268
|
+
summary = self._call_model(self.messages + [summary_prompt])
|
|
269
|
+
|
|
270
|
+
# 显示任务总结
|
|
271
|
+
PrettyOutput.section("任务总结", OutputType.SUCCESS)
|
|
272
|
+
PrettyOutput.print(summary, OutputType.SYSTEM)
|
|
273
|
+
PrettyOutput.section("任务完成", OutputType.SUCCESS)
|
|
274
|
+
|
|
275
|
+
return summary
|
|
276
|
+
|
|
277
|
+
except Exception as e:
|
|
278
|
+
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
279
|
+
else:
|
|
280
|
+
# 顶层Agent直接返回空字符串
|
|
281
|
+
PrettyOutput.section("任务完成", OutputType.SUCCESS)
|
|
282
|
+
return ""
|
|
283
|
+
|
|
284
|
+
if user_input == "__interrupt__":
|
|
285
|
+
PrettyOutput.print("任务已取消", OutputType.WARNING)
|
|
286
|
+
return "Task cancelled by user"
|
|
287
|
+
|
|
288
|
+
self.messages.append({
|
|
289
|
+
"role": "user",
|
|
290
|
+
"content": user_input
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
except Exception as e:
|
|
294
|
+
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
295
|
+
|
|
296
|
+
def clear_history(self):
|
|
297
|
+
"""清除对话历史,只保留系统提示"""
|
|
298
|
+
self.messages = [self.messages[0]]
|
|
@@ -12,7 +12,7 @@ sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
12
12
|
|
|
13
13
|
from jarvis.agent import Agent
|
|
14
14
|
from jarvis.tools import ToolRegistry
|
|
15
|
-
from jarvis.models import DDGSModel, OllamaModel
|
|
15
|
+
from jarvis.models import DDGSModel, OllamaModel, OpenAIModel
|
|
16
16
|
from jarvis.utils import PrettyOutput, OutputType, get_multiline_input, load_env_from_file
|
|
17
17
|
from jarvis.zte_llm import create_zte_llm
|
|
18
18
|
|
|
@@ -20,15 +20,23 @@ from jarvis.zte_llm import create_zte_llm
|
|
|
20
20
|
SUPPORTED_PLATFORMS = {
|
|
21
21
|
"ollama": {
|
|
22
22
|
"models": ["qwen2.5:14b", "qwq"],
|
|
23
|
-
"default": "qwen2.5:14b"
|
|
23
|
+
"default": "qwen2.5:14b",
|
|
24
|
+
"allow_custom": True
|
|
24
25
|
},
|
|
25
26
|
"ddgs": {
|
|
26
27
|
"models": ["gpt-4o-mini", "claude-3-haiku", "llama-3.1-70b", "mixtral-8x7b"],
|
|
27
|
-
"default": "gpt-4o-mini"
|
|
28
|
+
"default": "gpt-4o-mini",
|
|
29
|
+
"allow_custom": False
|
|
28
30
|
},
|
|
29
31
|
"zte": {
|
|
30
32
|
"models": ["NebulaBiz", "nebulacoder", "NTele-72B"],
|
|
31
|
-
"default": "NebulaBiz"
|
|
33
|
+
"default": "NebulaBiz",
|
|
34
|
+
"allow_custom": False
|
|
35
|
+
},
|
|
36
|
+
"openai": {
|
|
37
|
+
"models": ["deepseek-chat"],
|
|
38
|
+
"default": "deepseek-chat",
|
|
39
|
+
"allow_custom": True
|
|
32
40
|
}
|
|
33
41
|
}
|
|
34
42
|
|
|
@@ -118,9 +126,10 @@ def main():
|
|
|
118
126
|
|
|
119
127
|
args.model = args.model or os.getenv("JARVIS_MODEL")
|
|
120
128
|
|
|
121
|
-
#
|
|
129
|
+
# 修改模型验证逻辑
|
|
122
130
|
if args.model:
|
|
123
|
-
if args.model not in SUPPORTED_PLATFORMS[args.platform]["models"]
|
|
131
|
+
if (args.model not in SUPPORTED_PLATFORMS[args.platform]["models"] and
|
|
132
|
+
not SUPPORTED_PLATFORMS[args.platform]["allow_custom"]):
|
|
124
133
|
supported_models = ", ".join(SUPPORTED_PLATFORMS[args.platform]["models"])
|
|
125
134
|
PrettyOutput.print(
|
|
126
135
|
f"错误: 平台 {args.platform} 不支持模型 {args.model}\n"
|
|
@@ -145,6 +154,13 @@ def main():
|
|
|
145
154
|
elif args.platform == "zte": # zte
|
|
146
155
|
model = create_zte_llm(model_name=args.model)
|
|
147
156
|
platform_name = f"ZTE ({args.model})"
|
|
157
|
+
elif args.platform == "openai":
|
|
158
|
+
model = OpenAIModel(
|
|
159
|
+
model_name=args.model,
|
|
160
|
+
api_key=os.getenv("OPENAI_API_KEY"),
|
|
161
|
+
api_base=os.getenv("OPENAI_API_BASE")
|
|
162
|
+
)
|
|
163
|
+
platform_name = f"OpenAI ({args.model})"
|
|
148
164
|
|
|
149
165
|
tool_registry = ToolRegistry(model)
|
|
150
166
|
agent = Agent(model, tool_registry)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import time
|
|
3
|
+
from typing import Dict, List, Optional, Tuple
|
|
4
|
+
from duckduckgo_search import DDGS
|
|
5
|
+
import ollama
|
|
6
|
+
from abc import ABC, abstractmethod
|
|
7
|
+
import yaml
|
|
8
|
+
import openai
|
|
9
|
+
|
|
10
|
+
from .utils import OutputType, PrettyOutput
|
|
11
|
+
|
|
12
|
+
class BaseModel(ABC):
|
|
13
|
+
"""大语言模型基类"""
|
|
14
|
+
|
|
15
|
+
@abstractmethod
|
|
16
|
+
def chat(self, messages: List[Dict]) -> str:
|
|
17
|
+
"""执行对话"""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DDGSModel(BaseModel):
|
|
22
|
+
def __init__(self, model_name: str = "gpt-4o-mini"):
|
|
23
|
+
"""
|
|
24
|
+
[1]: gpt-4o-mini
|
|
25
|
+
[2]: claude-3-haiku
|
|
26
|
+
[3]: llama-3.1-70b
|
|
27
|
+
[4]: mixtral-8x7b
|
|
28
|
+
"""
|
|
29
|
+
self.model_name = model_name
|
|
30
|
+
|
|
31
|
+
def __make_prompt(self, messages: List[Dict]) -> str:
|
|
32
|
+
prompt = ""
|
|
33
|
+
for message in messages:
|
|
34
|
+
prompt += f"[{message['role']}]: {message['content']}\n"
|
|
35
|
+
return prompt
|
|
36
|
+
|
|
37
|
+
def chat(self, messages: List[Dict]) -> str:
|
|
38
|
+
ddgs = DDGS()
|
|
39
|
+
prompt = self.__make_prompt(messages)
|
|
40
|
+
content = ddgs.chat(prompt)
|
|
41
|
+
PrettyOutput.print(content, OutputType.SYSTEM)
|
|
42
|
+
return content
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class OllamaModel(BaseModel):
|
|
46
|
+
"""Ollama模型实现"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, model_name: str = "qwen2.5:14b", api_base: str = "http://localhost:11434"):
|
|
49
|
+
self.model_name = model_name
|
|
50
|
+
self.api_base = api_base
|
|
51
|
+
self.client = ollama.Client(host=api_base)
|
|
52
|
+
|
|
53
|
+
def chat(self, messages: List[Dict]) -> str:
|
|
54
|
+
"""调用Ollama API获取响应"""
|
|
55
|
+
try:
|
|
56
|
+
# 使用流式调用
|
|
57
|
+
stream = self.client.chat(
|
|
58
|
+
model=self.model_name,
|
|
59
|
+
messages=messages,
|
|
60
|
+
stream=True
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# 收集完整响应
|
|
64
|
+
content_parts = []
|
|
65
|
+
for chunk in stream:
|
|
66
|
+
if chunk.message.content:
|
|
67
|
+
content_parts.append(chunk.message.content)
|
|
68
|
+
# 实时打印内容
|
|
69
|
+
PrettyOutput.print_stream(chunk.message.content, OutputType.SYSTEM)
|
|
70
|
+
|
|
71
|
+
PrettyOutput.print_stream_end()
|
|
72
|
+
|
|
73
|
+
# 合并完整内容
|
|
74
|
+
return "".join(content_parts)
|
|
75
|
+
|
|
76
|
+
except Exception as e:
|
|
77
|
+
raise Exception(f"Ollama API调用失败: {str(e)}")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class OpenAIModel(BaseModel):
|
|
81
|
+
"""OpenAI模型实现"""
|
|
82
|
+
|
|
83
|
+
def __init__(self, model_name: str = "deepseek-chat", api_key: Optional[str] = None, api_base: Optional[str] = None):
|
|
84
|
+
"""
|
|
85
|
+
初始化OpenAI模型
|
|
86
|
+
Args:
|
|
87
|
+
model_name: 模型名称,默认为 deepseek-chat
|
|
88
|
+
api_key: OpenAI API密钥
|
|
89
|
+
api_base: 可选的API基础URL,用于自定义端点
|
|
90
|
+
"""
|
|
91
|
+
self.model_name = model_name
|
|
92
|
+
if api_key:
|
|
93
|
+
openai.api_key = api_key
|
|
94
|
+
if api_base:
|
|
95
|
+
openai.base_url = api_base
|
|
96
|
+
|
|
97
|
+
def chat(self, messages: List[Dict]) -> str:
|
|
98
|
+
"""调用OpenAI API获取响应"""
|
|
99
|
+
try:
|
|
100
|
+
# 使用流式调用
|
|
101
|
+
stream = openai.chat.completions.create(
|
|
102
|
+
model=self.model_name,
|
|
103
|
+
messages=messages,
|
|
104
|
+
stream=True
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# 收集完整响应
|
|
108
|
+
content_parts = []
|
|
109
|
+
for chunk in stream:
|
|
110
|
+
if chunk.choices[0].delta.content:
|
|
111
|
+
content = chunk.choices[0].delta.content
|
|
112
|
+
content_parts.append(content)
|
|
113
|
+
# 实时打印内容
|
|
114
|
+
PrettyOutput.print_stream(content, OutputType.SYSTEM)
|
|
115
|
+
|
|
116
|
+
PrettyOutput.print_stream_end()
|
|
117
|
+
|
|
118
|
+
# 合并完整内容
|
|
119
|
+
return "".join(content_parts)
|
|
120
|
+
|
|
121
|
+
except Exception as e:
|
|
122
|
+
raise Exception(f"OpenAI API调用失败: {str(e)}")
|
|
@@ -3,7 +3,6 @@ from .file_ops import FileOperationTool
|
|
|
3
3
|
from .search import SearchTool
|
|
4
4
|
from .shell import ShellTool
|
|
5
5
|
from .webpage import WebpageTool
|
|
6
|
-
from .user_input import UserInputTool
|
|
7
6
|
|
|
8
7
|
__all__ = [
|
|
9
8
|
'Tool',
|
|
@@ -12,5 +11,4 @@ __all__ = [
|
|
|
12
11
|
'SearchTool',
|
|
13
12
|
'ShellTool',
|
|
14
13
|
'WebpageTool',
|
|
15
|
-
'UserInputTool',
|
|
16
14
|
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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]:
|
|
@@ -38,14 +35,13 @@ class ToolRegistry:
|
|
|
38
35
|
from .file_ops import FileOperationTool
|
|
39
36
|
from .webpage import WebpageTool
|
|
40
37
|
from .sub_agent import SubAgentTool
|
|
41
|
-
from .user_input import UserInputTool
|
|
42
38
|
|
|
43
39
|
tools = [
|
|
44
|
-
SearchTool(),
|
|
40
|
+
SearchTool(self.model),
|
|
45
41
|
ShellTool(),
|
|
46
42
|
FileOperationTool(),
|
|
47
43
|
WebpageTool(),
|
|
48
|
-
|
|
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)
|
|
@@ -90,8 +78,8 @@ class ToolRegistry:
|
|
|
90
78
|
|
|
91
79
|
# 只处理第一个工具调用
|
|
92
80
|
tool_call = tool_calls[0]
|
|
93
|
-
name = tool_call["
|
|
94
|
-
args = tool_call["
|
|
81
|
+
name = tool_call["name"]
|
|
82
|
+
args = tool_call["arguments"]
|
|
95
83
|
|
|
96
84
|
if isinstance(args, str):
|
|
97
85
|
try:
|
|
@@ -121,6 +109,7 @@ class ToolRegistry:
|
|
|
121
109
|
if stderr:
|
|
122
110
|
output_parts.append(f"错误:\n{stderr}")
|
|
123
111
|
output = "\n\n".join(output_parts)
|
|
112
|
+
output = "没有输出和错误" if not output else output
|
|
124
113
|
PrettyOutput.section("执行成功", OutputType.SUCCESS)
|
|
125
114
|
else:
|
|
126
115
|
error_msg = result["error"]
|
|
@@ -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": {
|