jarvis-ai-assistant 0.1.2__tar.gz → 0.1.4__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.2/src/jarvis_ai_assistant.egg-info → jarvis_ai_assistant-0.1.4}/PKG-INFO +2 -3
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/pyproject.toml +3 -9
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/setup.py +2 -3
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/__init__.py +1 -1
- jarvis_ai_assistant-0.1.4/src/jarvis/agent.py +134 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/main.py +12 -2
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/__init__.py +0 -1
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/base.py +54 -42
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/shell.py +16 -2
- jarvis_ai_assistant-0.1.4/src/jarvis/tools/sub_agent.py +79 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/utils.py +25 -2
- jarvis_ai_assistant-0.1.4/src/jarvis/zte_llm.py +132 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4/src/jarvis_ai_assistant.egg-info}/PKG-INFO +2 -3
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis_ai_assistant.egg-info/SOURCES.txt +2 -1
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis_ai_assistant.egg-info/requires.txt +1 -2
- jarvis_ai_assistant-0.1.2/src/jarvis/agent.py +0 -100
- jarvis_ai_assistant-0.1.2/src/jarvis/tools/rag.py +0 -154
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/MANIFEST.in +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/README.md +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/setup.cfg +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/.jarvis +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/__pycache__/__init__.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/__pycache__/agent.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/__pycache__/models.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/__pycache__/tools.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/__pycache__/utils.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/models.py +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/__pycache__/base.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/__pycache__/file_ops.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/__pycache__/python_script.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/__pycache__/rag.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/__pycache__/search.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/__pycache__/shell.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/__pycache__/user_confirmation.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/__pycache__/user_interaction.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/__pycache__/webpage.cpython-313.pyc +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/file_ops.py +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/python_script.py +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/search.py +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/user_confirmation.py +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/user_interaction.py +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/webpage.py +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis_ai_assistant.egg-info/dependency_links.txt +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis_ai_assistant.egg-info/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis_ai_assistant.egg-info/top_level.txt +0 -0
{jarvis_ai_assistant-0.1.2/src/jarvis_ai_assistant.egg-info → jarvis_ai_assistant-0.1.4}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: jarvis-ai-assistant
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
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
|
|
@@ -21,8 +21,7 @@ Requires-Dist: beautifulsoup4>=4.9.3
|
|
|
21
21
|
Requires-Dist: duckduckgo-search>=3.0.0
|
|
22
22
|
Requires-Dist: pyyaml>=5.1
|
|
23
23
|
Requires-Dist: ollama>=0.1.6
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist: chromadb>=0.4.24
|
|
24
|
+
Requires-Dist: colorama>=0.4.6
|
|
26
25
|
Provides-Extra: dev
|
|
27
26
|
Requires-Dist: pytest; extra == "dev"
|
|
28
27
|
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.4"
|
|
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" }]
|
|
@@ -25,18 +25,12 @@ dependencies = [
|
|
|
25
25
|
"duckduckgo-search>=3.0.0",
|
|
26
26
|
"pyyaml>=5.1",
|
|
27
27
|
"ollama>=0.1.6",
|
|
28
|
-
"
|
|
29
|
-
"chromadb>=0.4.24",
|
|
28
|
+
"colorama>=0.4.6",
|
|
30
29
|
]
|
|
31
30
|
requires-python = ">=3.8"
|
|
32
31
|
|
|
33
32
|
[project.optional-dependencies]
|
|
34
|
-
dev = [
|
|
35
|
-
"pytest",
|
|
36
|
-
"black",
|
|
37
|
-
"isort",
|
|
38
|
-
"mypy",
|
|
39
|
-
]
|
|
33
|
+
dev = ["pytest", "black", "isort", "mypy"]
|
|
40
34
|
|
|
41
35
|
[project.urls]
|
|
42
36
|
Homepage = "https://github.com/skyfireitdiy/Jarvis"
|
|
@@ -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.4",
|
|
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",
|
|
@@ -18,8 +18,7 @@ setup(
|
|
|
18
18
|
"duckduckgo-search>=3.0.0",
|
|
19
19
|
"pyyaml>=5.1",
|
|
20
20
|
"ollama>=0.1.6",
|
|
21
|
-
"
|
|
22
|
-
"chromadb>=0.4.24",
|
|
21
|
+
"colorama>=0.4.6",
|
|
23
22
|
],
|
|
24
23
|
entry_points={
|
|
25
24
|
"console_scripts": [
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import subprocess
|
|
3
|
+
from typing import Dict, Any, List, Optional
|
|
4
|
+
from .tools import ToolRegistry
|
|
5
|
+
from .utils import Spinner, PrettyOutput, OutputType, get_multiline_input
|
|
6
|
+
from .models import BaseModel
|
|
7
|
+
import re
|
|
8
|
+
import os
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
|
|
11
|
+
class Agent:
|
|
12
|
+
def __init__(self, model: BaseModel, tool_registry: Optional[ToolRegistry] = None, name: str = "Jarvis"):
|
|
13
|
+
"""Initialize Agent with a model, optional tool registry and name"""
|
|
14
|
+
self.model = model
|
|
15
|
+
self.tool_registry = tool_registry or ToolRegistry(model)
|
|
16
|
+
self.name = name
|
|
17
|
+
# 编译正则表达式
|
|
18
|
+
self.tool_call_pattern = re.compile(r'<tool_call>\s*({[^}]+})\s*</tool_call>')
|
|
19
|
+
self.messages = [
|
|
20
|
+
{
|
|
21
|
+
"role": "system",
|
|
22
|
+
"content": f"""You are {name}, a rigorous AI assistant that executes tasks step by step.
|
|
23
|
+
|
|
24
|
+
Key Principles:
|
|
25
|
+
1. Execute ONE step at a time
|
|
26
|
+
2. Wait for each step's result before planning the next
|
|
27
|
+
3. Use tools to obtain all data, no fabrication
|
|
28
|
+
4. Create sub-agents for independent subtasks
|
|
29
|
+
5. Think carefully before each action
|
|
30
|
+
|
|
31
|
+
""" + self.tool_registry.tool_help_text()
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
self.spinner = Spinner()
|
|
35
|
+
|
|
36
|
+
def _call_model(self, messages: List[Dict], use_tools: bool = True) -> Dict:
|
|
37
|
+
"""调用模型获取响应"""
|
|
38
|
+
self.spinner.start()
|
|
39
|
+
try:
|
|
40
|
+
return self.model.chat(
|
|
41
|
+
messages=messages,
|
|
42
|
+
tools=self.tool_registry.get_all_tools() if use_tools else []
|
|
43
|
+
)
|
|
44
|
+
except Exception as e:
|
|
45
|
+
raise Exception(f"{self.name}: 模型调用失败: {str(e)}")
|
|
46
|
+
finally:
|
|
47
|
+
self.spinner.stop()
|
|
48
|
+
|
|
49
|
+
def run(self, user_input: str) -> str:
|
|
50
|
+
"""处理用户输入并返回响应,返回任务总结报告"""
|
|
51
|
+
self.clear_history()
|
|
52
|
+
self.messages.append({
|
|
53
|
+
"role": "user",
|
|
54
|
+
"content": user_input
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
while True:
|
|
58
|
+
try:
|
|
59
|
+
response = self._call_model(self.messages)
|
|
60
|
+
current_response = response
|
|
61
|
+
|
|
62
|
+
self.messages.append({
|
|
63
|
+
"role": "assistant",
|
|
64
|
+
"content": response["message"].get("content", ""),
|
|
65
|
+
"tool_calls": current_response["message"]["tool_calls"]
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
if len(current_response["message"]["tool_calls"]) > 0:
|
|
69
|
+
if current_response["message"].get("content"):
|
|
70
|
+
PrettyOutput.print(f"{self.name}: {current_response['message']['content']}", OutputType.SYSTEM)
|
|
71
|
+
|
|
72
|
+
tool_result = self.tool_registry.handle_tool_calls(current_response["message"]["tool_calls"])
|
|
73
|
+
PrettyOutput.print(f"{self.name} Tool Result: {tool_result}", OutputType.RESULT)
|
|
74
|
+
|
|
75
|
+
self.messages.append({
|
|
76
|
+
"role": "tool",
|
|
77
|
+
"content": tool_result
|
|
78
|
+
})
|
|
79
|
+
continue
|
|
80
|
+
|
|
81
|
+
final_content = current_response["message"].get("content", "")
|
|
82
|
+
if final_content:
|
|
83
|
+
PrettyOutput.print(f"{self.name}: {final_content}", OutputType.SYSTEM)
|
|
84
|
+
|
|
85
|
+
user_input = get_multiline_input(f"{self.name}: 您可以继续输入,或输入空行结束当前任务")
|
|
86
|
+
if not user_input:
|
|
87
|
+
PrettyOutput.print(f"{self.name}: 正在生成任务总结...", OutputType.INFO)
|
|
88
|
+
|
|
89
|
+
# 请求模型生成任务总结
|
|
90
|
+
summary_prompt = {
|
|
91
|
+
"role": "user",
|
|
92
|
+
"content": """Please provide a concise task summary focusing on:
|
|
93
|
+
|
|
94
|
+
1. Key Information:
|
|
95
|
+
- Important data points
|
|
96
|
+
- Critical findings
|
|
97
|
+
- Significant tool outputs
|
|
98
|
+
|
|
99
|
+
2. Task Results:
|
|
100
|
+
- Final outcomes
|
|
101
|
+
- Task completion status
|
|
102
|
+
- Any important conclusions
|
|
103
|
+
|
|
104
|
+
Keep the summary focused and brief, highlighting only the most essential information."""
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
summary_response = self._call_model(self.messages + [summary_prompt], use_tools=False)
|
|
109
|
+
summary = summary_response["message"].get("content", "")
|
|
110
|
+
|
|
111
|
+
PrettyOutput.print(f"==============={self.name} 任务总结===============", OutputType.INFO)
|
|
112
|
+
PrettyOutput.print(summary, OutputType.SYSTEM)
|
|
113
|
+
PrettyOutput.print("=" * (len(self.name) + 16), OutputType.INFO)
|
|
114
|
+
|
|
115
|
+
return summary
|
|
116
|
+
|
|
117
|
+
except Exception as e:
|
|
118
|
+
error_msg = f"{self.name}: 生成任务总结时出错: {str(e)}"
|
|
119
|
+
PrettyOutput.print(error_msg, OutputType.ERROR)
|
|
120
|
+
return "Task completed but summary generation failed."
|
|
121
|
+
|
|
122
|
+
self.messages.append({
|
|
123
|
+
"role": "user",
|
|
124
|
+
"content": user_input
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
except Exception as e:
|
|
128
|
+
error_msg = f"{self.name}: 处理响应时出错: {str(e)}"
|
|
129
|
+
PrettyOutput.print(error_msg, OutputType.ERROR)
|
|
130
|
+
return f"Task failed with error: {str(e)}"
|
|
131
|
+
|
|
132
|
+
def clear_history(self):
|
|
133
|
+
"""清除对话历史,只保留系统提示"""
|
|
134
|
+
self.messages = [self.messages[0]]
|
|
@@ -13,7 +13,8 @@ sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
|
13
13
|
from jarvis.agent import Agent
|
|
14
14
|
from jarvis.tools import ToolRegistry
|
|
15
15
|
from jarvis.models import DDGSModel, OllamaModel
|
|
16
|
-
from jarvis.utils import PrettyOutput, OutputType, get_multiline_input
|
|
16
|
+
from jarvis.utils import PrettyOutput, OutputType, get_multiline_input, load_env_from_file
|
|
17
|
+
from jarvis.zte_llm import create_zte_llm
|
|
17
18
|
|
|
18
19
|
# 定义支持的平台和模型
|
|
19
20
|
SUPPORTED_PLATFORMS = {
|
|
@@ -24,6 +25,10 @@ SUPPORTED_PLATFORMS = {
|
|
|
24
25
|
"ddgs": {
|
|
25
26
|
"models": ["gpt-4o-mini", "claude-3-haiku", "llama-3.1-70b", "mixtral-8x7b"],
|
|
26
27
|
"default": "gpt-4o-mini"
|
|
28
|
+
},
|
|
29
|
+
"zte": {
|
|
30
|
+
"models": ["NebulaBiz", "nebulacoder", "NTele-72B"],
|
|
31
|
+
"default": "NebulaBiz"
|
|
27
32
|
}
|
|
28
33
|
}
|
|
29
34
|
|
|
@@ -97,6 +102,8 @@ def main():
|
|
|
97
102
|
)
|
|
98
103
|
|
|
99
104
|
args = parser.parse_args()
|
|
105
|
+
|
|
106
|
+
load_env_from_file()
|
|
100
107
|
|
|
101
108
|
# 验证并设置默认模型
|
|
102
109
|
if args.model:
|
|
@@ -119,9 +126,12 @@ def main():
|
|
|
119
126
|
api_base=args.api_base
|
|
120
127
|
)
|
|
121
128
|
platform_name = f"Ollama ({args.model})"
|
|
122
|
-
|
|
129
|
+
elif args.platform == "ddgs": # ddgs
|
|
123
130
|
model = DDGSModel(model_name=args.model)
|
|
124
131
|
platform_name = f"DuckDuckGo Search ({args.model})"
|
|
132
|
+
elif args.platform == "zte": # zte
|
|
133
|
+
model = create_zte_llm(model_name=args.model)
|
|
134
|
+
platform_name = f"ZTE ({args.model})"
|
|
125
135
|
|
|
126
136
|
tool_registry = ToolRegistry()
|
|
127
137
|
agent = Agent(model, tool_registry)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from typing import Dict, Any, List, Optional, Callable
|
|
2
2
|
import json
|
|
3
3
|
from ..utils import PrettyOutput, OutputType
|
|
4
|
+
from ..models import BaseModel
|
|
4
5
|
|
|
5
6
|
class Tool:
|
|
6
7
|
def __init__(self, name: str, description: str, parameters: Dict, func: Callable):
|
|
@@ -25,8 +26,9 @@ class Tool:
|
|
|
25
26
|
return self.func(arguments)
|
|
26
27
|
|
|
27
28
|
class ToolRegistry:
|
|
28
|
-
def __init__(self):
|
|
29
|
+
def __init__(self, model: Optional[BaseModel] = None):
|
|
29
30
|
self.tools: Dict[str, Tool] = {}
|
|
31
|
+
self.model = model
|
|
30
32
|
self._register_default_tools()
|
|
31
33
|
|
|
32
34
|
def _register_default_tools(self):
|
|
@@ -37,8 +39,8 @@ class ToolRegistry:
|
|
|
37
39
|
from .user_confirmation import UserConfirmationTool
|
|
38
40
|
from .python_script import PythonScriptTool
|
|
39
41
|
from .file_ops import FileOperationTool
|
|
40
|
-
from .rag import RAGTool
|
|
41
42
|
from .webpage import WebpageTool
|
|
43
|
+
from .sub_agent import SubAgentTool
|
|
42
44
|
|
|
43
45
|
tools = [
|
|
44
46
|
SearchTool(),
|
|
@@ -47,7 +49,6 @@ class ToolRegistry:
|
|
|
47
49
|
UserConfirmationTool(),
|
|
48
50
|
PythonScriptTool(),
|
|
49
51
|
FileOperationTool(),
|
|
50
|
-
RAGTool(),
|
|
51
52
|
WebpageTool(),
|
|
52
53
|
]
|
|
53
54
|
|
|
@@ -59,6 +60,14 @@ class ToolRegistry:
|
|
|
59
60
|
func=tool.execute
|
|
60
61
|
)
|
|
61
62
|
|
|
63
|
+
sub_agent_tool = SubAgentTool(self.model)
|
|
64
|
+
self.register_tool(
|
|
65
|
+
name=sub_agent_tool.name,
|
|
66
|
+
description=sub_agent_tool.description,
|
|
67
|
+
parameters=sub_agent_tool.parameters,
|
|
68
|
+
func=sub_agent_tool.execute
|
|
69
|
+
)
|
|
70
|
+
|
|
62
71
|
def register_tool(self, name: str, description: str, parameters: Dict, func: Callable):
|
|
63
72
|
"""注册新工具"""
|
|
64
73
|
self.tools[name] = Tool(name, description, parameters, func)
|
|
@@ -117,6 +126,7 @@ class ToolRegistry:
|
|
|
117
126
|
def tool_help_text(self) -> str:
|
|
118
127
|
"""返回所有工具的帮助文本"""
|
|
119
128
|
return """Available Tools:
|
|
129
|
+
|
|
120
130
|
1. search: Search for information using DuckDuckGo
|
|
121
131
|
2. read_webpage: Extract content from webpages
|
|
122
132
|
3. execute_python: Run Python code with dependency management
|
|
@@ -124,57 +134,59 @@ class ToolRegistry:
|
|
|
124
134
|
5. ask_user: Get input from user with options support
|
|
125
135
|
6. ask_user_confirmation: Get yes/no confirmation from user
|
|
126
136
|
7. file_operation: Read/write files in workspace directory
|
|
127
|
-
8.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
1.
|
|
131
|
-
-
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
-
|
|
138
|
-
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
- Focus on
|
|
147
|
-
-
|
|
148
|
-
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
- Return only the most relevant passages
|
|
152
|
-
- Combine similar information
|
|
153
|
-
- Skip redundant context
|
|
154
|
-
|
|
155
|
-
Tool Usage Examples:
|
|
137
|
+
8. create_sub_agent: Create a sub-agent for independent tasks (RECOMMENDED for subtasks)
|
|
138
|
+
|
|
139
|
+
Core Rules:
|
|
140
|
+
1. ONE Step at a Time
|
|
141
|
+
- Execute only one action per response
|
|
142
|
+
- Explain what you're going to do before doing it
|
|
143
|
+
- Wait for the result before planning next step
|
|
144
|
+
- No multiple actions or parallel execution
|
|
145
|
+
|
|
146
|
+
2. Sub-Agent Usage
|
|
147
|
+
- Use create_sub_agent for independent subtasks
|
|
148
|
+
- Let sub-agents handle complex task sequences
|
|
149
|
+
- Examples of good sub-agent tasks:
|
|
150
|
+
* Code analysis and documentation
|
|
151
|
+
* File system operations
|
|
152
|
+
* Data processing
|
|
153
|
+
* Research tasks
|
|
154
|
+
|
|
155
|
+
3. Output Guidelines
|
|
156
|
+
- Focus on essential information
|
|
157
|
+
- Clear step-by-step explanations
|
|
158
|
+
- Summarize results concisely
|
|
159
|
+
|
|
160
|
+
Tool Call Format:
|
|
156
161
|
<tool_call>
|
|
157
162
|
{
|
|
158
|
-
"name": "
|
|
163
|
+
"name": "tool_name",
|
|
159
164
|
"arguments": {
|
|
160
|
-
"
|
|
161
|
-
"max_results": 2 # Limit results to save context
|
|
165
|
+
"param1": "value1"
|
|
162
166
|
}
|
|
163
167
|
}
|
|
164
168
|
</tool_call>
|
|
165
169
|
|
|
170
|
+
Format Rules:
|
|
171
|
+
1. ONE tool call per response
|
|
172
|
+
2. Valid JSON only in arguments
|
|
173
|
+
3. No comments or extra formatting
|
|
174
|
+
4. Match parameters exactly
|
|
175
|
+
|
|
176
|
+
Example (Correct - Single Step):
|
|
177
|
+
Assistant: I'll start by searching for the documentation.
|
|
166
178
|
<tool_call>
|
|
167
179
|
{
|
|
168
|
-
"name": "
|
|
180
|
+
"name": "search",
|
|
169
181
|
"arguments": {
|
|
170
|
-
"
|
|
171
|
-
"
|
|
182
|
+
"query": "Python GIL documentation",
|
|
183
|
+
"max_results": 2
|
|
172
184
|
}
|
|
173
185
|
}
|
|
174
186
|
</tool_call>
|
|
175
187
|
|
|
176
188
|
Remember:
|
|
177
|
-
1.
|
|
178
|
-
2.
|
|
179
|
-
3.
|
|
180
|
-
4.
|
|
189
|
+
1. ONE step at a time
|
|
190
|
+
2. Explain before acting
|
|
191
|
+
3. Wait for results
|
|
192
|
+
4. Use sub-agents for complex tasks"""
|
|
@@ -4,13 +4,27 @@ from ..utils import PrettyOutput, OutputType
|
|
|
4
4
|
|
|
5
5
|
class ShellTool:
|
|
6
6
|
name = "execute_shell"
|
|
7
|
-
description = "Execute shell commands and return the results
|
|
7
|
+
description = """Execute shell commands and return the results.
|
|
8
|
+
Guidelines for output optimization:
|
|
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
|
+
"""
|
|
8
22
|
parameters = {
|
|
9
23
|
"type": "object",
|
|
10
24
|
"properties": {
|
|
11
25
|
"command": {
|
|
12
26
|
"type": "string",
|
|
13
|
-
"description": "Shell command to execute"
|
|
27
|
+
"description": "Shell command to execute (use filters/limits for large outputs)"
|
|
14
28
|
},
|
|
15
29
|
"timeout": {
|
|
16
30
|
"type": "integer",
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from typing import Dict, Any
|
|
2
|
+
from ..agent import Agent
|
|
3
|
+
from ..models import BaseModel
|
|
4
|
+
from ..utils import PrettyOutput, OutputType
|
|
5
|
+
from .base import ToolRegistry
|
|
6
|
+
|
|
7
|
+
class SubAgentTool:
|
|
8
|
+
name = "create_sub_agent"
|
|
9
|
+
description = """Create a sub-agent to handle independent tasks.
|
|
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
|
|
16
|
+
|
|
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
|
+
parameters = {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"properties": {
|
|
25
|
+
"name": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"description": "Name for the sub-agent (e.g., 'FileAnalyzer', 'CodeReviewer')"
|
|
28
|
+
},
|
|
29
|
+
"task": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "Task description with complete context"
|
|
32
|
+
},
|
|
33
|
+
"context": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"description": "Additional context or background information",
|
|
36
|
+
"default": ""
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"required": ["name", "task"]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
def __init__(self, model: BaseModel):
|
|
43
|
+
"""Initialize with the same model as parent agent"""
|
|
44
|
+
self.model = model
|
|
45
|
+
|
|
46
|
+
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
47
|
+
"""Create and run a sub-agent for the specified task"""
|
|
48
|
+
try:
|
|
49
|
+
name = args["name"]
|
|
50
|
+
task = args["task"]
|
|
51
|
+
context = args.get("context", "")
|
|
52
|
+
|
|
53
|
+
PrettyOutput.print(f"Creating sub-agent '{name}'...", OutputType.INFO)
|
|
54
|
+
|
|
55
|
+
# Create a new tool registry for the sub-agent
|
|
56
|
+
tool_registry = ToolRegistry()
|
|
57
|
+
|
|
58
|
+
# Create the sub-agent with the specified name
|
|
59
|
+
sub_agent = Agent(self.model, tool_registry, name=name)
|
|
60
|
+
|
|
61
|
+
# Prepare the task with context if provided
|
|
62
|
+
full_task = f"{context}\n\nTask: {task}" if context else task
|
|
63
|
+
|
|
64
|
+
PrettyOutput.print(f"Sub-agent '{name}' executing task...", OutputType.INFO)
|
|
65
|
+
|
|
66
|
+
# Execute the task and get the summary
|
|
67
|
+
summary = sub_agent.run(full_task)
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
"success": True,
|
|
71
|
+
"stdout": f"Sub-agent '{name}' completed the task.\n\nSummary:\n{summary}",
|
|
72
|
+
"stderr": ""
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
except Exception as e:
|
|
76
|
+
return {
|
|
77
|
+
"success": False,
|
|
78
|
+
"error": f"Sub-agent execution failed: {str(e)}"
|
|
79
|
+
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
from pathlib import Path
|
|
1
2
|
import sys
|
|
2
3
|
import time
|
|
3
4
|
import threading
|
|
4
|
-
from typing import Optional
|
|
5
|
+
from typing import Dict, Optional
|
|
5
6
|
from enum import Enum
|
|
6
7
|
from datetime import datetime
|
|
7
8
|
import colorama
|
|
8
9
|
from colorama import Fore, Style
|
|
10
|
+
import os
|
|
9
11
|
|
|
10
12
|
# 初始化colorama
|
|
11
13
|
colorama.init()
|
|
@@ -102,4 +104,25 @@ def get_multiline_input(tip: str) -> str:
|
|
|
102
104
|
PrettyOutput.print("\n输入已取消", OutputType.ERROR)
|
|
103
105
|
return ""
|
|
104
106
|
|
|
105
|
-
return "\n".join(lines).strip()
|
|
107
|
+
return "\n".join(lines).strip()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def load_env_from_file():
|
|
111
|
+
"""Load environment variables from ~/.jarvis_env file"""
|
|
112
|
+
env_file = Path.home() / ".jarvis_env"
|
|
113
|
+
|
|
114
|
+
if env_file.exists():
|
|
115
|
+
try:
|
|
116
|
+
with open(env_file, "r", encoding="utf-8") as f:
|
|
117
|
+
for line in f:
|
|
118
|
+
line = line.strip()
|
|
119
|
+
if line and not line.startswith("#"):
|
|
120
|
+
try:
|
|
121
|
+
key, value = line.split("=", 1)
|
|
122
|
+
os.environ[key.strip()] = value.strip().strip("'").strip('"')
|
|
123
|
+
except ValueError:
|
|
124
|
+
continue
|
|
125
|
+
except Exception as e:
|
|
126
|
+
print(f"Warning: Failed to read ~/.jarvis_env: {e}")
|
|
127
|
+
|
|
128
|
+
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Dict, Any, List, Optional
|
|
6
|
+
from .models import BaseModel
|
|
7
|
+
|
|
8
|
+
class ZteLLM(BaseModel):
|
|
9
|
+
"""ZTE Nebula LLM implementation"""
|
|
10
|
+
|
|
11
|
+
def __init__(self,
|
|
12
|
+
app_id: str,
|
|
13
|
+
app_key: str,
|
|
14
|
+
emp_no: str,
|
|
15
|
+
auth_value: str,
|
|
16
|
+
model: str = "nebulacoder",
|
|
17
|
+
):
|
|
18
|
+
"""Initialize ZTE LLM with required credentials"""
|
|
19
|
+
self.app_id = str(app_id)
|
|
20
|
+
self.app_key = str(app_key)
|
|
21
|
+
self.emp_no = str(emp_no)
|
|
22
|
+
self.auth_value = str(auth_value)
|
|
23
|
+
self.model = model
|
|
24
|
+
self.base_url = "https://studio.zte.com.cn/zte-studio-ai-platform/openapi/v1"
|
|
25
|
+
|
|
26
|
+
def _make_request(self, endpoint: str, data: Dict[str, Any]) -> Dict[str, Any]:
|
|
27
|
+
"""Make request to ZTE API"""
|
|
28
|
+
headers = {
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
'Authorization': f'Bearer {self.app_id}-{self.app_key}',
|
|
31
|
+
'X-Emp-No': self.emp_no,
|
|
32
|
+
'X-Auth-Value': self.auth_value
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
response = requests.post(
|
|
36
|
+
f"{self.base_url}/{endpoint}",
|
|
37
|
+
headers=headers,
|
|
38
|
+
json=data
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
response.raise_for_status()
|
|
42
|
+
result = response.json()
|
|
43
|
+
|
|
44
|
+
if result["code"]["code"] != "0000":
|
|
45
|
+
raise Exception(f"API Error: {result['code']['msg']}")
|
|
46
|
+
|
|
47
|
+
return result["bo"]
|
|
48
|
+
|
|
49
|
+
def chat(self, messages: List[Dict[str, Any]], tools: Optional[List[Dict]] = None) -> Dict[str, Any]:
|
|
50
|
+
"""Chat with ZTE LLM"""
|
|
51
|
+
# Convert messages to prompt
|
|
52
|
+
prompt = self._convert_messages_to_prompt(messages)
|
|
53
|
+
|
|
54
|
+
# Prepare data for API call
|
|
55
|
+
data = {
|
|
56
|
+
"chatUuid": "",
|
|
57
|
+
"chatName": "",
|
|
58
|
+
"stream": False,
|
|
59
|
+
"keep": False,
|
|
60
|
+
"text": prompt,
|
|
61
|
+
"model": self.model
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# If tools are provided, add them to the prompt
|
|
65
|
+
if tools:
|
|
66
|
+
tools_desc = "Available tools:\n\n" + json.dumps(tools, indent=2, ensure_ascii=False)
|
|
67
|
+
data["text"] = tools_desc + "\n\n" + data["text"]
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
result = self._make_request("chat", data)
|
|
71
|
+
|
|
72
|
+
# Parse the response to extract potential tool calls
|
|
73
|
+
response_text = result["result"]
|
|
74
|
+
tool_calls = BaseModel.extract_tool_calls(response_text)
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
"message": {
|
|
78
|
+
"content": response_text,
|
|
79
|
+
"tool_calls": tool_calls
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
except Exception as e:
|
|
84
|
+
raise Exception(f"ZTE LLM chat failed: {str(e)}")
|
|
85
|
+
|
|
86
|
+
def _convert_messages_to_prompt(self, messages: List[Dict[str, Any]]) -> str:
|
|
87
|
+
"""Convert message list to a single prompt string"""
|
|
88
|
+
prompt_parts = []
|
|
89
|
+
|
|
90
|
+
for message in messages:
|
|
91
|
+
role = message["role"]
|
|
92
|
+
content = message.get("content", "")
|
|
93
|
+
|
|
94
|
+
if role == "system":
|
|
95
|
+
prompt_parts.append(f"System: {content}")
|
|
96
|
+
elif role == "user":
|
|
97
|
+
prompt_parts.append(f"User: {content}")
|
|
98
|
+
elif role == "assistant":
|
|
99
|
+
prompt_parts.append(f"Assistant: {content}")
|
|
100
|
+
elif role == "tool":
|
|
101
|
+
prompt_parts.append(f"Tool Result: {content}")
|
|
102
|
+
|
|
103
|
+
return "\n\n".join(prompt_parts)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def create_zte_llm(model_name: str = "NebulaBiz") -> ZteLLM:
|
|
107
|
+
"""Create ZTE LLM instance with provided parameters"""
|
|
108
|
+
# Load environment variables from file
|
|
109
|
+
|
|
110
|
+
# Get credentials from parameters, env file, or system environment variables
|
|
111
|
+
app_id = os.getenv('ZTE_APP_ID')
|
|
112
|
+
app_key = os.getenv('ZTE_APP_KEY')
|
|
113
|
+
emp_no = os.getenv('ZTE_EMP_NO')
|
|
114
|
+
auth_value = os.getenv('ZTE_AUTH_VALUE')
|
|
115
|
+
|
|
116
|
+
# Validate required credentials
|
|
117
|
+
if not all([app_id, app_key, emp_no, auth_value]):
|
|
118
|
+
raise ValueError(
|
|
119
|
+
"Missing required credentials. Please provide through either:\n"
|
|
120
|
+
"1. Function parameters\n"
|
|
121
|
+
"2. ~/.jarvis_env file\n"
|
|
122
|
+
"3. System environment variables\n\n"
|
|
123
|
+
"Required variables: ZTE_APP_ID, ZTE_APP_KEY, ZTE_EMP_NO, ZTE_AUTH_VALUE"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
return ZteLLM(
|
|
127
|
+
app_id=app_id,
|
|
128
|
+
app_key=app_key,
|
|
129
|
+
emp_no=emp_no,
|
|
130
|
+
auth_value=auth_value,
|
|
131
|
+
model=model_name
|
|
132
|
+
)
|
{jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4/src/jarvis_ai_assistant.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: jarvis-ai-assistant
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
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
|
|
@@ -21,8 +21,7 @@ Requires-Dist: beautifulsoup4>=4.9.3
|
|
|
21
21
|
Requires-Dist: duckduckgo-search>=3.0.0
|
|
22
22
|
Requires-Dist: pyyaml>=5.1
|
|
23
23
|
Requires-Dist: ollama>=0.1.6
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist: chromadb>=0.4.24
|
|
24
|
+
Requires-Dist: colorama>=0.4.6
|
|
26
25
|
Provides-Extra: dev
|
|
27
26
|
Requires-Dist: pytest; extra == "dev"
|
|
28
27
|
Requires-Dist: black; extra == "dev"
|
{jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis_ai_assistant.egg-info/SOURCES.txt
RENAMED
|
@@ -8,6 +8,7 @@ src/jarvis/agent.py
|
|
|
8
8
|
src/jarvis/main.py
|
|
9
9
|
src/jarvis/models.py
|
|
10
10
|
src/jarvis/utils.py
|
|
11
|
+
src/jarvis/zte_llm.py
|
|
11
12
|
src/jarvis/__pycache__/__init__.cpython-313.pyc
|
|
12
13
|
src/jarvis/__pycache__/agent.cpython-313.pyc
|
|
13
14
|
src/jarvis/__pycache__/models.cpython-313.pyc
|
|
@@ -17,9 +18,9 @@ src/jarvis/tools/__init__.py
|
|
|
17
18
|
src/jarvis/tools/base.py
|
|
18
19
|
src/jarvis/tools/file_ops.py
|
|
19
20
|
src/jarvis/tools/python_script.py
|
|
20
|
-
src/jarvis/tools/rag.py
|
|
21
21
|
src/jarvis/tools/search.py
|
|
22
22
|
src/jarvis/tools/shell.py
|
|
23
|
+
src/jarvis/tools/sub_agent.py
|
|
23
24
|
src/jarvis/tools/user_confirmation.py
|
|
24
25
|
src/jarvis/tools/user_interaction.py
|
|
25
26
|
src/jarvis/tools/webpage.py
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import subprocess
|
|
3
|
-
from typing import Dict, Any, List, Optional
|
|
4
|
-
from .tools import ToolRegistry
|
|
5
|
-
from .utils import Spinner, PrettyOutput, OutputType, get_multiline_input
|
|
6
|
-
from .models import BaseModel, OllamaModel
|
|
7
|
-
import re
|
|
8
|
-
import os
|
|
9
|
-
from datetime import datetime
|
|
10
|
-
|
|
11
|
-
class Agent:
|
|
12
|
-
def __init__(self, model: BaseModel, tool_registry: ToolRegistry):
|
|
13
|
-
self.model = model
|
|
14
|
-
self.tool_registry = tool_registry
|
|
15
|
-
# 编译正则表达式
|
|
16
|
-
self.tool_call_pattern = re.compile(r'<tool_call>\s*({[^}]+})\s*</tool_call>')
|
|
17
|
-
self.messages = [
|
|
18
|
-
{
|
|
19
|
-
"role": "system",
|
|
20
|
-
"content": """You are a rigorous AI assistant, all data must be obtained through tools, and no fabrication or speculation is allowed. """ + "\n" + self.tool_registry.tool_help_text()
|
|
21
|
-
}
|
|
22
|
-
]
|
|
23
|
-
self.spinner = Spinner()
|
|
24
|
-
|
|
25
|
-
def _call_model(self, messages: List[Dict], use_tools: bool = True) -> Dict:
|
|
26
|
-
"""调用模型获取响应"""
|
|
27
|
-
self.spinner.start()
|
|
28
|
-
try:
|
|
29
|
-
return self.model.chat(
|
|
30
|
-
messages=messages,
|
|
31
|
-
tools=self.tool_registry.get_all_tools() if use_tools else None
|
|
32
|
-
)
|
|
33
|
-
except Exception as e:
|
|
34
|
-
raise Exception(f"模型调用失败: {str(e)}")
|
|
35
|
-
finally:
|
|
36
|
-
self.spinner.stop()
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def run(self, user_input: str) :
|
|
40
|
-
"""处理用户输入并返回响应"""
|
|
41
|
-
# 检查是否是结束命令
|
|
42
|
-
self.clear_history()
|
|
43
|
-
self.messages.append({
|
|
44
|
-
"role": "user",
|
|
45
|
-
"content": user_input
|
|
46
|
-
})
|
|
47
|
-
while True:
|
|
48
|
-
try:
|
|
49
|
-
# 获取初始响应
|
|
50
|
-
response = self._call_model(self.messages)
|
|
51
|
-
current_response = response
|
|
52
|
-
|
|
53
|
-
# 将工具执行结果添加到对话
|
|
54
|
-
self.messages.append({
|
|
55
|
-
"role": "assistant",
|
|
56
|
-
"content": response["message"].get("content", ""),
|
|
57
|
-
"tool_calls": current_response["message"]["tool_calls"]
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
# 处理可能的多轮工具调用
|
|
61
|
-
if len(current_response["message"]["tool_calls"]) > 0:
|
|
62
|
-
# 添加当前助手响应到输出(如果有内容)
|
|
63
|
-
if current_response["message"].get("content"):
|
|
64
|
-
PrettyOutput.print(current_response["message"]["content"], OutputType.SYSTEM)
|
|
65
|
-
|
|
66
|
-
# 使用 ToolRegistry 的 handle_tool_calls 方法处理工具调用
|
|
67
|
-
tool_result = self.tool_registry.handle_tool_calls(current_response["message"]["tool_calls"])
|
|
68
|
-
PrettyOutput.print(tool_result, OutputType.RESULT)
|
|
69
|
-
|
|
70
|
-
self.messages.append({
|
|
71
|
-
"role": "tool",
|
|
72
|
-
"content": tool_result
|
|
73
|
-
})
|
|
74
|
-
continue
|
|
75
|
-
|
|
76
|
-
# 添加最终响应到对话历史和输出
|
|
77
|
-
final_content = current_response["message"].get("content", "")
|
|
78
|
-
|
|
79
|
-
if final_content:
|
|
80
|
-
PrettyOutput.print(final_content, OutputType.SYSTEM)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
# 如果没有工具调用且响应很短,可能需要继续对话
|
|
84
|
-
user_input = get_multiline_input("您可以继续输入,或输入空行结束当前任务")
|
|
85
|
-
if not user_input:
|
|
86
|
-
PrettyOutput.print("===============任务结束===============", OutputType.INFO)
|
|
87
|
-
break
|
|
88
|
-
|
|
89
|
-
self.messages.append({
|
|
90
|
-
"role": "user",
|
|
91
|
-
"content": user_input
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
except Exception as e:
|
|
95
|
-
error_msg = f"处理响应时出错: {str(e)}"
|
|
96
|
-
PrettyOutput.print(error_msg, OutputType.ERROR)
|
|
97
|
-
|
|
98
|
-
def clear_history(self):
|
|
99
|
-
"""清除对话历史,只保留系统提示"""
|
|
100
|
-
self.messages = [self.messages[0]]
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
from typing import Dict, Any, List
|
|
2
|
-
import chromadb
|
|
3
|
-
from chromadb.utils import embedding_functions
|
|
4
|
-
import hashlib
|
|
5
|
-
import re
|
|
6
|
-
from ..utils import PrettyOutput, OutputType
|
|
7
|
-
|
|
8
|
-
class RAGTool:
|
|
9
|
-
name = "rag_query"
|
|
10
|
-
description = """Execute RAG queries on documents.
|
|
11
|
-
Features:
|
|
12
|
-
1. Auto-creates document embeddings
|
|
13
|
-
2. Returns relevant passages
|
|
14
|
-
3. Maintains embedding database
|
|
15
|
-
"""
|
|
16
|
-
parameters = {
|
|
17
|
-
"type": "object",
|
|
18
|
-
"properties": {
|
|
19
|
-
"files": {
|
|
20
|
-
"type": "array",
|
|
21
|
-
"items": {"type": "string"},
|
|
22
|
-
"description": "Files to process"
|
|
23
|
-
},
|
|
24
|
-
"query": {
|
|
25
|
-
"type": "string",
|
|
26
|
-
"description": "Query text"
|
|
27
|
-
},
|
|
28
|
-
"num_passages": {
|
|
29
|
-
"type": "integer",
|
|
30
|
-
"description": "Number of passages",
|
|
31
|
-
"default": 3
|
|
32
|
-
},
|
|
33
|
-
"chunk_size": {
|
|
34
|
-
"type": "integer",
|
|
35
|
-
"description": "Chunk size",
|
|
36
|
-
"default": 500
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
"required": ["files", "query"]
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
def _get_document_hash(self, content: str) -> str:
|
|
43
|
-
return hashlib.md5(content.encode()).hexdigest()
|
|
44
|
-
|
|
45
|
-
def _chunk_text(self, text: str, chunk_size: int = 500) -> List[str]:
|
|
46
|
-
"""将文本分割成适当大小的块"""
|
|
47
|
-
# 按句子分割
|
|
48
|
-
sentences = re.split(r'(?<=[.!?])\s+', text)
|
|
49
|
-
chunks = []
|
|
50
|
-
current_chunk = []
|
|
51
|
-
current_length = 0
|
|
52
|
-
|
|
53
|
-
for sentence in sentences:
|
|
54
|
-
sentence_length = len(sentence)
|
|
55
|
-
if current_length + sentence_length > chunk_size and current_chunk:
|
|
56
|
-
chunks.append(" ".join(current_chunk))
|
|
57
|
-
current_chunk = []
|
|
58
|
-
current_length = 0
|
|
59
|
-
current_chunk.append(sentence)
|
|
60
|
-
current_length += sentence_length
|
|
61
|
-
|
|
62
|
-
if current_chunk:
|
|
63
|
-
chunks.append(" ".join(current_chunk))
|
|
64
|
-
|
|
65
|
-
return chunks
|
|
66
|
-
|
|
67
|
-
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
68
|
-
"""执行RAG查询"""
|
|
69
|
-
try:
|
|
70
|
-
files = args["files"]
|
|
71
|
-
query = args["query"]
|
|
72
|
-
num_passages = args.get("num_passages", 3)
|
|
73
|
-
chunk_size = args.get("chunk_size", 500)
|
|
74
|
-
|
|
75
|
-
# 初始化ChromaDB
|
|
76
|
-
chroma_client = chromadb.PersistentClient(path="./data/chromadb")
|
|
77
|
-
|
|
78
|
-
# 使用sentence-transformers作为嵌入模型
|
|
79
|
-
embedding_function = embedding_functions.SentenceTransformerEmbeddingFunction(
|
|
80
|
-
model_name="all-MiniLM-L6-v2"
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
# 获取或创建集合
|
|
84
|
-
collection = chroma_client.get_or_create_collection(
|
|
85
|
-
name="document_store",
|
|
86
|
-
embedding_function=embedding_function
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
# 处理每个文件
|
|
90
|
-
for file_path in files:
|
|
91
|
-
try:
|
|
92
|
-
with open(file_path, 'r', encoding='utf-8') as f:
|
|
93
|
-
content = f.read()
|
|
94
|
-
|
|
95
|
-
# 计算文档哈希值
|
|
96
|
-
doc_hash = self._get_document_hash(content)
|
|
97
|
-
|
|
98
|
-
# 检查文档是否已经存在且未更改
|
|
99
|
-
existing_ids = collection.get(
|
|
100
|
-
where={"doc_hash": doc_hash}
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
if not existing_ids["ids"]:
|
|
104
|
-
# 分块处理文档
|
|
105
|
-
chunks = self._chunk_text(content, chunk_size)
|
|
106
|
-
|
|
107
|
-
# 为每个块生成唯一ID
|
|
108
|
-
chunk_ids = [f"{doc_hash}_{i}" for i in range(len(chunks))]
|
|
109
|
-
|
|
110
|
-
# 添加到数据库
|
|
111
|
-
collection.add(
|
|
112
|
-
documents=chunks,
|
|
113
|
-
ids=chunk_ids,
|
|
114
|
-
metadatas=[{
|
|
115
|
-
"file_path": file_path,
|
|
116
|
-
"doc_hash": doc_hash,
|
|
117
|
-
"chunk_index": i
|
|
118
|
-
} for i in range(len(chunks))]
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
PrettyOutput.print(f"已添加文档: {file_path}", OutputType.INFO)
|
|
122
|
-
else:
|
|
123
|
-
PrettyOutput.print(f"文档已存在且未更改: {file_path}", OutputType.INFO)
|
|
124
|
-
|
|
125
|
-
except Exception as e:
|
|
126
|
-
PrettyOutput.print(f"处理文件 {file_path} 时出错: {str(e)}", OutputType.ERROR)
|
|
127
|
-
|
|
128
|
-
# 执行查询
|
|
129
|
-
results = collection.query(
|
|
130
|
-
query_texts=[query],
|
|
131
|
-
n_results=num_passages
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
# 格式化输出
|
|
135
|
-
output = [f"查询: {query}\n"]
|
|
136
|
-
output.append(f"找到 {len(results['documents'][0])} 个相关段落:\n")
|
|
137
|
-
|
|
138
|
-
for i, (doc, metadata) in enumerate(zip(results['documents'][0], results['metadatas'][0]), 1):
|
|
139
|
-
output.append(f"\n段落 {i}:")
|
|
140
|
-
output.append(f"来源: {metadata['file_path']}")
|
|
141
|
-
output.append(f"相关内容:\n{doc}\n")
|
|
142
|
-
output.append("-" * 50)
|
|
143
|
-
|
|
144
|
-
return {
|
|
145
|
-
"success": True,
|
|
146
|
-
"stdout": "\n".join(output),
|
|
147
|
-
"stderr": ""
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
except Exception as e:
|
|
151
|
-
return {
|
|
152
|
-
"success": False,
|
|
153
|
-
"error": f"RAG查询失败: {str(e)}"
|
|
154
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/__pycache__/agent.cpython-313.pyc
RENAMED
|
File without changes
|
|
File without changes
|
{jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/__pycache__/tools.cpython-313.pyc
RENAMED
|
File without changes
|
{jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/__pycache__/utils.cpython-313.pyc
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/user_confirmation.py
RENAMED
|
File without changes
|
{jarvis_ai_assistant-0.1.2 → jarvis_ai_assistant-0.1.4}/src/jarvis/tools/user_interaction.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|