jarvis-ai-assistant 0.7.0__py3-none-any.whl → 0.7.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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +243 -139
- jarvis/jarvis_agent/agent_manager.py +5 -10
- jarvis/jarvis_agent/builtin_input_handler.py +2 -6
- jarvis/jarvis_agent/config_editor.py +2 -7
- jarvis/jarvis_agent/event_bus.py +82 -12
- jarvis/jarvis_agent/file_context_handler.py +265 -15
- jarvis/jarvis_agent/file_methodology_manager.py +3 -4
- jarvis/jarvis_agent/jarvis.py +113 -98
- jarvis/jarvis_agent/language_extractors/__init__.py +57 -0
- jarvis/jarvis_agent/language_extractors/c_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/cpp_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/go_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/java_extractor.py +84 -0
- jarvis/jarvis_agent/language_extractors/javascript_extractor.py +79 -0
- jarvis/jarvis_agent/language_extractors/python_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/rust_extractor.py +21 -0
- jarvis/jarvis_agent/language_extractors/typescript_extractor.py +84 -0
- jarvis/jarvis_agent/language_support_info.py +486 -0
- jarvis/jarvis_agent/main.py +6 -12
- jarvis/jarvis_agent/memory_manager.py +7 -16
- jarvis/jarvis_agent/methodology_share_manager.py +10 -16
- jarvis/jarvis_agent/prompt_manager.py +1 -1
- jarvis/jarvis_agent/prompts.py +193 -171
- jarvis/jarvis_agent/protocols.py +8 -12
- jarvis/jarvis_agent/run_loop.py +77 -14
- jarvis/jarvis_agent/session_manager.py +2 -3
- jarvis/jarvis_agent/share_manager.py +12 -21
- jarvis/jarvis_agent/shell_input_handler.py +1 -2
- jarvis/jarvis_agent/task_analyzer.py +26 -4
- jarvis/jarvis_agent/task_manager.py +11 -27
- jarvis/jarvis_agent/tool_executor.py +2 -3
- jarvis/jarvis_agent/tool_share_manager.py +12 -24
- jarvis/jarvis_agent/web_server.py +55 -20
- jarvis/jarvis_c2rust/__init__.py +5 -5
- jarvis/jarvis_c2rust/cli.py +461 -499
- jarvis/jarvis_c2rust/collector.py +45 -53
- jarvis/jarvis_c2rust/constants.py +26 -0
- jarvis/jarvis_c2rust/library_replacer.py +264 -132
- jarvis/jarvis_c2rust/llm_module_agent.py +162 -190
- jarvis/jarvis_c2rust/loaders.py +207 -0
- jarvis/jarvis_c2rust/models.py +28 -0
- jarvis/jarvis_c2rust/optimizer.py +1592 -395
- jarvis/jarvis_c2rust/transpiler.py +1722 -1064
- jarvis/jarvis_c2rust/utils.py +385 -0
- jarvis/jarvis_code_agent/build_validation_config.py +2 -3
- jarvis/jarvis_code_agent/code_agent.py +394 -320
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +3 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +4 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +17 -2
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +3 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +36 -4
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +9 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +9 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +12 -1
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +22 -5
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +57 -32
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +62 -6
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +8 -9
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +290 -5
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +21 -3
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +72 -4
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +35 -3
- jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +212 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +254 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +52 -2
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +73 -1
- jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +306 -152
- jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +193 -18
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +18 -8
- jarvis/jarvis_code_agent/lint.py +258 -27
- jarvis/jarvis_code_agent/utils.py +0 -1
- jarvis/jarvis_code_analysis/code_review.py +19 -24
- jarvis/jarvis_data/config_schema.json +53 -26
- jarvis/jarvis_git_squash/main.py +4 -5
- jarvis/jarvis_git_utils/git_commiter.py +44 -49
- jarvis/jarvis_mcp/sse_mcp_client.py +20 -27
- jarvis/jarvis_mcp/stdio_mcp_client.py +11 -12
- jarvis/jarvis_mcp/streamable_mcp_client.py +15 -14
- jarvis/jarvis_memory_organizer/memory_organizer.py +55 -74
- jarvis/jarvis_methodology/main.py +32 -48
- jarvis/jarvis_multi_agent/__init__.py +79 -61
- jarvis/jarvis_multi_agent/main.py +3 -7
- jarvis/jarvis_platform/base.py +469 -199
- jarvis/jarvis_platform/human.py +7 -8
- jarvis/jarvis_platform/kimi.py +30 -36
- jarvis/jarvis_platform/openai.py +65 -27
- jarvis/jarvis_platform/registry.py +26 -10
- jarvis/jarvis_platform/tongyi.py +24 -25
- jarvis/jarvis_platform/yuanbao.py +31 -42
- jarvis/jarvis_platform_manager/main.py +66 -77
- jarvis/jarvis_platform_manager/service.py +8 -13
- jarvis/jarvis_rag/cli.py +49 -51
- jarvis/jarvis_rag/embedding_manager.py +13 -18
- jarvis/jarvis_rag/llm_interface.py +8 -9
- jarvis/jarvis_rag/query_rewriter.py +10 -21
- jarvis/jarvis_rag/rag_pipeline.py +24 -27
- jarvis/jarvis_rag/reranker.py +4 -5
- jarvis/jarvis_rag/retriever.py +28 -30
- jarvis/jarvis_sec/__init__.py +220 -3520
- jarvis/jarvis_sec/agents.py +143 -0
- jarvis/jarvis_sec/analysis.py +276 -0
- jarvis/jarvis_sec/cli.py +29 -6
- jarvis/jarvis_sec/clustering.py +1439 -0
- jarvis/jarvis_sec/file_manager.py +427 -0
- jarvis/jarvis_sec/parsers.py +73 -0
- jarvis/jarvis_sec/prompts.py +268 -0
- jarvis/jarvis_sec/report.py +83 -4
- jarvis/jarvis_sec/review.py +453 -0
- jarvis/jarvis_sec/utils.py +499 -0
- jarvis/jarvis_sec/verification.py +848 -0
- jarvis/jarvis_sec/workflow.py +7 -0
- jarvis/jarvis_smart_shell/main.py +38 -87
- jarvis/jarvis_stats/cli.py +1 -1
- jarvis/jarvis_stats/stats.py +7 -7
- jarvis/jarvis_stats/storage.py +15 -21
- jarvis/jarvis_tools/clear_memory.py +3 -20
- jarvis/jarvis_tools/cli/main.py +20 -23
- jarvis/jarvis_tools/edit_file.py +1066 -0
- jarvis/jarvis_tools/execute_script.py +42 -21
- jarvis/jarvis_tools/file_analyzer.py +6 -9
- jarvis/jarvis_tools/generate_new_tool.py +11 -20
- jarvis/jarvis_tools/lsp_client.py +1552 -0
- jarvis/jarvis_tools/methodology.py +2 -3
- jarvis/jarvis_tools/read_code.py +1525 -87
- jarvis/jarvis_tools/read_symbols.py +2 -3
- jarvis/jarvis_tools/read_webpage.py +7 -10
- jarvis/jarvis_tools/registry.py +370 -181
- jarvis/jarvis_tools/retrieve_memory.py +20 -19
- jarvis/jarvis_tools/rewrite_file.py +105 -0
- jarvis/jarvis_tools/save_memory.py +3 -15
- jarvis/jarvis_tools/search_web.py +3 -7
- jarvis/jarvis_tools/sub_agent.py +17 -6
- jarvis/jarvis_tools/sub_code_agent.py +14 -16
- jarvis/jarvis_tools/virtual_tty.py +54 -32
- jarvis/jarvis_utils/clipboard.py +7 -10
- jarvis/jarvis_utils/config.py +98 -63
- jarvis/jarvis_utils/embedding.py +5 -5
- jarvis/jarvis_utils/fzf.py +8 -8
- jarvis/jarvis_utils/git_utils.py +81 -67
- jarvis/jarvis_utils/input.py +24 -49
- jarvis/jarvis_utils/jsonnet_compat.py +465 -0
- jarvis/jarvis_utils/methodology.py +33 -35
- jarvis/jarvis_utils/utils.py +245 -202
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/METADATA +205 -70
- jarvis_ai_assistant-0.7.6.dist-info/RECORD +218 -0
- jarvis/jarvis_agent/edit_file_handler.py +0 -584
- jarvis/jarvis_agent/rewrite_file_handler.py +0 -141
- jarvis/jarvis_agent/task_planner.py +0 -496
- jarvis/jarvis_platform/ai8.py +0 -332
- jarvis/jarvis_tools/ask_user.py +0 -54
- jarvis_ai_assistant-0.7.0.dist-info/RECORD +0 -192
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/top_level.txt +0 -0
jarvis/jarvis_tools/registry.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
+
from jarvis.jarvis_utils.jsonnet_compat import loads as json_loads
|
|
2
3
|
import json
|
|
3
4
|
import os
|
|
4
5
|
import re
|
|
@@ -15,95 +16,58 @@ from jarvis.jarvis_mcp.stdio_mcp_client import StdioMcpClient
|
|
|
15
16
|
from jarvis.jarvis_mcp.streamable_mcp_client import StreamableMcpClient
|
|
16
17
|
from jarvis.jarvis_tools.base import Tool
|
|
17
18
|
from jarvis.jarvis_utils.config import get_data_dir, get_tool_load_dirs
|
|
18
|
-
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
19
19
|
from jarvis.jarvis_utils.tag import ct, ot
|
|
20
20
|
from jarvis.jarvis_utils.utils import is_context_overflow, daily_check_git_updates
|
|
21
21
|
|
|
22
|
+
_multiline_example = """ {
|
|
23
|
+
"multiline_str": |||
|
|
24
|
+
第一行:直接换行,无需 \\n
|
|
25
|
+
第二行:包含"双引号",无需转义
|
|
26
|
+
第三行:包含'单引号',直接写
|
|
27
|
+
第四行:支持缩进保留
|
|
28
|
+
|||
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
或使用 ``` 代替 |||:
|
|
32
|
+
{
|
|
33
|
+
"multiline_str": ```
|
|
34
|
+
第一行:直接换行,无需 \\n
|
|
35
|
+
第二行:包含"双引号",无需转义
|
|
36
|
+
第三行:包含'单引号',直接写
|
|
37
|
+
第四行:支持缩进保留
|
|
38
|
+
```
|
|
39
|
+
}"""
|
|
40
|
+
|
|
22
41
|
tool_call_help = f"""
|
|
23
42
|
<tool_system_guide>
|
|
24
|
-
|
|
25
|
-
# 🛠️ 工具使用系统
|
|
26
|
-
您正在使用一个需要精确格式和严格规则的工具执行系统。
|
|
27
|
-
</introduction>
|
|
28
|
-
|
|
29
|
-
<format>
|
|
30
|
-
# 📋 工具调用格式
|
|
43
|
+
工具调用格式(Jsonnet):
|
|
31
44
|
{ot("TOOL_CALL")}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
arguments:
|
|
36
|
-
|
|
37
|
-
|
|
45
|
+
{{
|
|
46
|
+
"want": "想要从执行结果中获取到的信息",
|
|
47
|
+
"name": "工具名称",
|
|
48
|
+
"arguments": {{
|
|
49
|
+
"param1": "值1",
|
|
50
|
+
"param2": "值2"
|
|
51
|
+
}}
|
|
52
|
+
}}
|
|
38
53
|
{ct("TOOL_CALL")}
|
|
39
|
-
</format>
|
|
40
|
-
|
|
41
|
-
<rules>
|
|
42
|
-
# ❗ 关键规则
|
|
43
|
-
<rule>
|
|
44
|
-
### 1. 每次只使用一个工具
|
|
45
|
-
- 一次只执行一个工具
|
|
46
|
-
- 等待结果后再进行下一步
|
|
47
|
-
</rule>
|
|
48
|
-
|
|
49
|
-
<rule>
|
|
50
|
-
### 2. 严格遵守格式
|
|
51
|
-
- 完全按照上述格式
|
|
52
|
-
- 使用正确的YAML格式,2个空格作为缩进
|
|
53
|
-
- 包含所有必需参数
|
|
54
|
-
- {ot("TOOL_CALL")} 和 {ct("TOOL_CALL")} 必须出现在行首
|
|
55
|
-
</rule>
|
|
56
|
-
|
|
57
|
-
<rule>
|
|
58
|
-
### 3. 结果处理
|
|
59
|
-
- 等待执行结果
|
|
60
|
-
- 不要假设结果
|
|
61
|
-
- 不要创建虚假响应
|
|
62
|
-
- 不要想象对话
|
|
63
|
-
</rule>
|
|
64
|
-
|
|
65
|
-
<rule>
|
|
66
|
-
### 4. 信息管理
|
|
67
|
-
- 如果信息不足,询问用户
|
|
68
|
-
- 跳过不必要的步骤
|
|
69
|
-
- 如果卡住,请求指导
|
|
70
|
-
- 不要在没有完整信息的情况下继续
|
|
71
|
-
</rule>
|
|
72
|
-
</rules>
|
|
73
|
-
|
|
74
|
-
<string_format>
|
|
75
|
-
# 📝 字符串参数格式
|
|
76
|
-
使用 |2 语法表示字符串参数,防止多行字符串行首空格引起歧义。
|
|
77
|
-
|
|
78
|
-
{ot("TOOL_CALL")}
|
|
79
|
-
want: 当前的git状态,期望获取xxx的提交记录
|
|
80
|
-
name: execute_script
|
|
81
54
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
{
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
<common_errors>
|
|
99
|
-
# ⚠️ 常见错误
|
|
100
|
-
- 同时调用多个工具
|
|
101
|
-
- 假设工具结果
|
|
102
|
-
- 创建虚构对话
|
|
103
|
-
- 在没有所需信息的情况下继续
|
|
104
|
-
- yaml 格式错误
|
|
105
|
-
- {ot("TOOL_CALL")} 和 {ct("TOOL_CALL")} 没有出现在行首
|
|
106
|
-
</common_errors>
|
|
55
|
+
Jsonnet格式特性:
|
|
56
|
+
- 字符串引号:可使用双引号或单引号
|
|
57
|
+
- 多行字符串:推荐使用 ||| 或 ``` 分隔符包裹多行字符串,直接换行无需转义,支持保留缩进
|
|
58
|
+
示例:
|
|
59
|
+
{_multiline_example}
|
|
60
|
+
- 尾随逗号:对象/数组最后一个元素后可添加逗号
|
|
61
|
+
- 注释:支持 // 单行或 /* */ 多行注释
|
|
62
|
+
|
|
63
|
+
关键规则:
|
|
64
|
+
1. 每次只使用一个工具,等待结果后再进行下一步
|
|
65
|
+
2. {ot("TOOL_CALL")} 和 {ct("TOOL_CALL")} 必须出现在行首
|
|
66
|
+
3. 多行字符串参数推荐使用 ||| 或 ``` 分隔符包裹,直接换行无需转义,支持保留缩进
|
|
67
|
+
4. 等待执行结果,不要假设或创建虚假响应
|
|
68
|
+
5. 信息不足时询问用户,不要在没有完整信息的情况下继续
|
|
69
|
+
|
|
70
|
+
常见错误:同时调用多个工具、假设工具结果、Jsonnet格式错误、标签未出现在行首
|
|
107
71
|
</tool_system_guide>
|
|
108
72
|
"""
|
|
109
73
|
|
|
@@ -123,8 +87,11 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
123
87
|
return "TOOL_CALL"
|
|
124
88
|
|
|
125
89
|
def can_handle(self, response: str) -> bool:
|
|
126
|
-
# 仅当 {ot("TOOL_CALL")}
|
|
127
|
-
|
|
90
|
+
# 仅当 {ot("TOOL_CALL")} 出现在行首时才认为可以处理(忽略大小写)
|
|
91
|
+
has_tool_call = re.search(rf'(?mi){re.escape(ot("TOOL_CALL"))}', response) is not None
|
|
92
|
+
if has_tool_call:
|
|
93
|
+
print("🛠️ 检测到工具调用") # 增加工具emoji
|
|
94
|
+
return has_tool_call
|
|
128
95
|
|
|
129
96
|
def prompt(self) -> str:
|
|
130
97
|
"""加载工具"""
|
|
@@ -139,30 +106,26 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
139
106
|
tools_prompt += f" <name>名称: {tool['name']}</name>\n"
|
|
140
107
|
tools_prompt += f" <description>描述: {tool['description']}</description>\n"
|
|
141
108
|
tools_prompt += " <parameters>\n"
|
|
142
|
-
tools_prompt += " <
|
|
109
|
+
tools_prompt += " <json>|\n"
|
|
143
110
|
|
|
144
|
-
# 生成格式化的
|
|
145
|
-
|
|
111
|
+
# 生成格式化的JSON参数
|
|
112
|
+
json_params = json.dumps(
|
|
146
113
|
tool["parameters"],
|
|
147
|
-
|
|
148
|
-
indent=
|
|
114
|
+
ensure_ascii=False,
|
|
115
|
+
indent=2,
|
|
149
116
|
sort_keys=False,
|
|
150
|
-
width=120, # 增加行宽限制
|
|
151
117
|
)
|
|
152
118
|
|
|
153
119
|
# 添加缩进并移除尾部空格
|
|
154
|
-
for line in
|
|
120
|
+
for line in json_params.split("\n"):
|
|
155
121
|
tools_prompt += f" {line.rstrip()}\n"
|
|
156
122
|
|
|
157
|
-
tools_prompt += " </
|
|
123
|
+
tools_prompt += " </json>\n"
|
|
158
124
|
tools_prompt += " </parameters>\n"
|
|
159
125
|
tools_prompt += " </tool>\n"
|
|
160
126
|
|
|
161
|
-
except
|
|
162
|
-
|
|
163
|
-
f"工具 {tool['name']} 参数序列化失败: {str(e)}",
|
|
164
|
-
OutputType.ERROR,
|
|
165
|
-
)
|
|
127
|
+
except Exception as e:
|
|
128
|
+
print(f"❌ 工具 {tool['name']} 参数序列化失败: {str(e)}")
|
|
166
129
|
continue
|
|
167
130
|
|
|
168
131
|
tools_prompt += " </tools_list>\n"
|
|
@@ -173,7 +136,8 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
173
136
|
|
|
174
137
|
def handle(self, response: str, agent_: Any) -> Tuple[bool, Any]:
|
|
175
138
|
try:
|
|
176
|
-
|
|
139
|
+
# 传递agent给_extract_tool_calls,以便在解析失败时调用大模型修复
|
|
140
|
+
tool_call, err_msg, auto_completed = self._extract_tool_calls(response, agent_)
|
|
177
141
|
if err_msg:
|
|
178
142
|
# 只要工具解析错误,追加工具使用帮助信息(相当于一次 <ToolUsage>)
|
|
179
143
|
try:
|
|
@@ -190,7 +154,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
190
154
|
result = f"检测到工具调用缺少结束标签,已自动补全{ct('TOOL_CALL')}。请确保后续工具调用包含完整的开始和结束标签。\n\n{result}"
|
|
191
155
|
return False, result
|
|
192
156
|
except Exception as e:
|
|
193
|
-
|
|
157
|
+
print(f"❌ 工具调用处理失败: {str(e)}")
|
|
194
158
|
from jarvis.jarvis_agent import Agent
|
|
195
159
|
|
|
196
160
|
agent: Agent = agent_
|
|
@@ -202,6 +166,8 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
202
166
|
def __init__(self) -> None:
|
|
203
167
|
"""初始化工具注册表"""
|
|
204
168
|
self.tools: Dict[str, Tool] = {}
|
|
169
|
+
# 记录内置工具名称,用于区分内置工具和用户自定义工具
|
|
170
|
+
self._builtin_tool_names: set = set()
|
|
205
171
|
# 加载内置工具和外部工具
|
|
206
172
|
self._load_builtin_tools()
|
|
207
173
|
self._load_external_tools()
|
|
@@ -255,10 +221,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
255
221
|
"""
|
|
256
222
|
missing_tools = [tool_name for tool_name in name if tool_name not in self.tools]
|
|
257
223
|
if missing_tools:
|
|
258
|
-
|
|
259
|
-
f"工具 {missing_tools} 不存在,可用的工具有: {', '.join(self.tools.keys())}",
|
|
260
|
-
OutputType.WARNING,
|
|
261
|
-
)
|
|
224
|
+
print(f"⚠️ 工具 {missing_tools} 不存在,可用的工具有: {', '.join(self.tools.keys())}")
|
|
262
225
|
self.tools = {
|
|
263
226
|
tool_name: self.tools[tool_name]
|
|
264
227
|
for tool_name in name
|
|
@@ -292,10 +255,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
292
255
|
else:
|
|
293
256
|
missing.append(tool_name)
|
|
294
257
|
if missing:
|
|
295
|
-
|
|
296
|
-
"警告: 配置的工具不存在: " + ", ".join(f"'{name}'" for name in missing),
|
|
297
|
-
OutputType.WARNING,
|
|
298
|
-
)
|
|
258
|
+
print("⚠️ 警告: 配置的工具不存在: " + ", ".join(f"'{name}'" for name in missing))
|
|
299
259
|
self.tools = filtered_tools
|
|
300
260
|
|
|
301
261
|
# 如果配置了 dont_use 列表,排除列表中的工具
|
|
@@ -321,21 +281,19 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
321
281
|
return
|
|
322
282
|
|
|
323
283
|
# 添加警告信息
|
|
324
|
-
|
|
325
|
-
"警告: 从文件目录加载MCP工具的方式将在未来版本中废弃,请尽快迁移到JARVIS_MCP配置方式",
|
|
326
|
-
OutputType.WARNING,
|
|
327
|
-
)
|
|
284
|
+
print("⚠️ 警告: 从文件目录加载MCP工具的方式将在未来版本中废弃,请尽快迁移到JARVIS_MCP配置方式")
|
|
328
285
|
|
|
329
286
|
# 遍历目录中的所有.yaml文件
|
|
330
287
|
error_lines = []
|
|
331
288
|
for file_path in mcp_tools_dir.glob("*.yaml"):
|
|
332
289
|
try:
|
|
333
|
-
|
|
290
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
291
|
+
config = yaml.safe_load(f)
|
|
334
292
|
self.register_mcp_tool_by_config(config)
|
|
335
293
|
except Exception as e:
|
|
336
294
|
error_lines.append(f"文件 {file_path} 加载失败: {str(e)}")
|
|
337
295
|
if error_lines:
|
|
338
|
-
|
|
296
|
+
print("⚠️ " + "\n⚠️ ".join(error_lines))
|
|
339
297
|
|
|
340
298
|
def _load_builtin_tools(self) -> None:
|
|
341
299
|
"""从内置工具目录加载工具"""
|
|
@@ -348,6 +306,9 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
348
306
|
continue
|
|
349
307
|
|
|
350
308
|
self.register_tool_by_file(str(file_path))
|
|
309
|
+
|
|
310
|
+
# 记录当前已加载的工具名称为内置工具
|
|
311
|
+
self._builtin_tool_names = set(self.tools.keys())
|
|
351
312
|
|
|
352
313
|
def _load_external_tools(self) -> None:
|
|
353
314
|
"""从jarvis_data/tools和配置的目录加载外部工具"""
|
|
@@ -377,9 +338,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
377
338
|
["git", "clone", central_repo, central_repo_path], check=True
|
|
378
339
|
)
|
|
379
340
|
except Exception as e:
|
|
380
|
-
|
|
381
|
-
f"克隆中心工具仓库失败: {str(e)}", OutputType.ERROR
|
|
382
|
-
)
|
|
341
|
+
print(f"❌ 克隆中心工具仓库失败: {str(e)}")
|
|
383
342
|
|
|
384
343
|
# --- 全局每日更新检查 ---
|
|
385
344
|
daily_check_git_updates(tool_dirs, "tools")
|
|
@@ -408,9 +367,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
408
367
|
"""
|
|
409
368
|
try:
|
|
410
369
|
if "type" not in config:
|
|
411
|
-
|
|
412
|
-
f"配置{config.get('name', '')}缺少type字段", OutputType.WARNING
|
|
413
|
-
)
|
|
370
|
+
print(f"⚠️ 配置{config.get('name', '')}缺少type字段")
|
|
414
371
|
return False
|
|
415
372
|
|
|
416
373
|
# 检查enable标志
|
|
@@ -430,7 +387,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
430
387
|
|
|
431
388
|
return {
|
|
432
389
|
"success": True,
|
|
433
|
-
"stdout":
|
|
390
|
+
"stdout": json.dumps(ret, ensure_ascii=False, indent=2),
|
|
434
391
|
"stderr": "",
|
|
435
392
|
}
|
|
436
393
|
|
|
@@ -466,29 +423,18 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
466
423
|
|
|
467
424
|
if config["type"] == "stdio":
|
|
468
425
|
if "command" not in config:
|
|
469
|
-
|
|
470
|
-
f"配置{config.get('name', '')}缺少command字段",
|
|
471
|
-
OutputType.WARNING,
|
|
472
|
-
)
|
|
426
|
+
print(f"⚠️ 配置{config.get('name', '')}缺少command字段")
|
|
473
427
|
return False
|
|
474
428
|
elif config["type"] == "sse":
|
|
475
429
|
if "base_url" not in config:
|
|
476
|
-
|
|
477
|
-
f"配置{config.get('name', '')}缺少base_url字段",
|
|
478
|
-
OutputType.WARNING,
|
|
479
|
-
)
|
|
430
|
+
print(f"⚠️ 配置{config.get('name', '')}缺少base_url字段")
|
|
480
431
|
return False
|
|
481
432
|
elif config["type"] == "streamable":
|
|
482
433
|
if "base_url" not in config:
|
|
483
|
-
|
|
484
|
-
f"配置{config.get('name', '')}缺少base_url字段",
|
|
485
|
-
OutputType.WARNING,
|
|
486
|
-
)
|
|
434
|
+
print(f"⚠️ 配置{config.get('name', '')}缺少base_url字段")
|
|
487
435
|
return False
|
|
488
436
|
else:
|
|
489
|
-
|
|
490
|
-
f"不支持的MCP客户端类型: {config['type']}", OutputType.WARNING
|
|
491
|
-
)
|
|
437
|
+
print(f"⚠️ 不支持的MCP客户端类型: {config['type']}")
|
|
492
438
|
return False
|
|
493
439
|
|
|
494
440
|
# 创建MCP客户端
|
|
@@ -505,10 +451,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
505
451
|
# 获取工具信息
|
|
506
452
|
tools = mcp_client.get_tool_list()
|
|
507
453
|
if not tools:
|
|
508
|
-
|
|
509
|
-
f"从配置{config.get('name', '')}获取工具列表失败",
|
|
510
|
-
OutputType.WARNING,
|
|
511
|
-
)
|
|
454
|
+
print(f"⚠️ 从配置{config.get('name', '')}获取工具列表失败")
|
|
512
455
|
return False
|
|
513
456
|
|
|
514
457
|
# 注册每个工具
|
|
@@ -546,9 +489,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
546
489
|
return True
|
|
547
490
|
|
|
548
491
|
except Exception as e:
|
|
549
|
-
|
|
550
|
-
f"MCP配置{config.get('name', '')}加载失败: {str(e)}", OutputType.WARNING
|
|
551
|
-
)
|
|
492
|
+
print(f"⚠️ MCP配置{config.get('name', '')}加载失败: {str(e)}")
|
|
552
493
|
return False
|
|
553
494
|
|
|
554
495
|
def register_tool_by_file(self, file_path: str) -> bool:
|
|
@@ -563,7 +504,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
563
504
|
try:
|
|
564
505
|
p_file_path = Path(file_path).resolve() # 获取绝对路径
|
|
565
506
|
if not p_file_path.exists() or not p_file_path.is_file():
|
|
566
|
-
|
|
507
|
+
print(f"❌ 文件不存在: {p_file_path}")
|
|
567
508
|
return False
|
|
568
509
|
|
|
569
510
|
# 临时将父目录添加到sys.path
|
|
@@ -618,25 +559,176 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
618
559
|
sys.path.remove(parent_dir)
|
|
619
560
|
|
|
620
561
|
except Exception as e:
|
|
621
|
-
|
|
622
|
-
f"从 {Path(file_path).name} 加载工具失败: {str(e)}", OutputType.ERROR
|
|
623
|
-
)
|
|
562
|
+
print(f"❌ 从 {Path(file_path).name} 加载工具失败: {str(e)}")
|
|
624
563
|
return False
|
|
625
564
|
|
|
626
565
|
@staticmethod
|
|
627
566
|
def _has_tool_calls_block(content: str) -> bool:
|
|
628
|
-
"""
|
|
629
|
-
pattern = rf'(?
|
|
567
|
+
"""从内容中提取工具调用块(仅匹配行首标签,忽略大小写)"""
|
|
568
|
+
pattern = rf'(?msi){re.escape(ot("TOOL_CALL"))}(.*?)^{re.escape(ct("TOOL_CALL"))}'
|
|
630
569
|
return re.search(pattern, content) is not None
|
|
631
570
|
|
|
571
|
+
@staticmethod
|
|
572
|
+
def _get_long_response_hint(content: str) -> str:
|
|
573
|
+
"""生成长响应的提示信息
|
|
574
|
+
|
|
575
|
+
参数:
|
|
576
|
+
content: 响应内容
|
|
577
|
+
|
|
578
|
+
返回:
|
|
579
|
+
str: 如果响应较长,返回提示信息;否则返回空字符串
|
|
580
|
+
"""
|
|
581
|
+
if len(content) > 2048:
|
|
582
|
+
return "\n\n⚠️ 提示:响应内容较长(超过2048字符),可能是上下文溢出导致工具调用解析失败。如果是修改文件(edit_file)或重写文件(rewrite_file)操作,建议分多次进行,每次处理文件的一部分。"
|
|
583
|
+
return ""
|
|
584
|
+
|
|
585
|
+
@staticmethod
|
|
586
|
+
def _extract_json_from_text(text: str, start_pos: int = 0) -> Tuple[Optional[str], int]:
|
|
587
|
+
"""从文本中提取完整的JSON对象(通过括号匹配)
|
|
588
|
+
|
|
589
|
+
参数:
|
|
590
|
+
text: 要提取的文本
|
|
591
|
+
start_pos: 开始搜索的位置
|
|
592
|
+
|
|
593
|
+
返回:
|
|
594
|
+
Tuple[Optional[str], int]:
|
|
595
|
+
- 第一个元素是提取的JSON字符串(如果找到),否则为None
|
|
596
|
+
- 第二个元素是JSON结束后的位置
|
|
597
|
+
"""
|
|
598
|
+
# 跳过空白字符
|
|
599
|
+
pos = start_pos
|
|
600
|
+
while pos < len(text) and text[pos] in (' ', '\t', '\n', '\r'):
|
|
601
|
+
pos += 1
|
|
602
|
+
|
|
603
|
+
if pos >= len(text):
|
|
604
|
+
return None, pos
|
|
605
|
+
|
|
606
|
+
# 检查是否以 { 开头
|
|
607
|
+
if text[pos] != '{':
|
|
608
|
+
return None, pos
|
|
609
|
+
|
|
610
|
+
# 使用括号匹配找到完整的JSON对象
|
|
611
|
+
brace_count = 0
|
|
612
|
+
in_string = False
|
|
613
|
+
escape_next = False
|
|
614
|
+
string_char = None
|
|
615
|
+
|
|
616
|
+
json_start = pos
|
|
617
|
+
for i in range(pos, len(text)):
|
|
618
|
+
char = text[i]
|
|
619
|
+
|
|
620
|
+
if escape_next:
|
|
621
|
+
escape_next = False
|
|
622
|
+
continue
|
|
623
|
+
|
|
624
|
+
if char == '\\':
|
|
625
|
+
escape_next = True
|
|
626
|
+
continue
|
|
627
|
+
|
|
628
|
+
if not in_string:
|
|
629
|
+
if char in ('"', "'"):
|
|
630
|
+
in_string = True
|
|
631
|
+
string_char = char
|
|
632
|
+
elif char == '{':
|
|
633
|
+
brace_count += 1
|
|
634
|
+
elif char == '}':
|
|
635
|
+
brace_count -= 1
|
|
636
|
+
if brace_count == 0:
|
|
637
|
+
# 找到完整的JSON对象
|
|
638
|
+
return text[json_start:i+1], i + 1
|
|
639
|
+
else:
|
|
640
|
+
if char == string_char:
|
|
641
|
+
in_string = False
|
|
642
|
+
string_char = None
|
|
643
|
+
|
|
644
|
+
return None, len(text)
|
|
645
|
+
|
|
646
|
+
@staticmethod
|
|
647
|
+
def _clean_extra_markers(text: str) -> str:
|
|
648
|
+
"""清理文本中的额外标记(如 <|tool_call_end|> 等)
|
|
649
|
+
|
|
650
|
+
参数:
|
|
651
|
+
text: 要清理的文本
|
|
652
|
+
|
|
653
|
+
返回:
|
|
654
|
+
清理后的文本
|
|
655
|
+
"""
|
|
656
|
+
# 常见的额外标记模式
|
|
657
|
+
extra_markers = [
|
|
658
|
+
r'<\|tool_call_end\|>',
|
|
659
|
+
r'<\|tool_calls_section_end\|>',
|
|
660
|
+
r'<\|.*?\|>', # 匹配所有 <|...|> 格式的标记
|
|
661
|
+
]
|
|
662
|
+
|
|
663
|
+
cleaned = text
|
|
664
|
+
for pattern in extra_markers:
|
|
665
|
+
cleaned = re.sub(pattern, '', cleaned, flags=re.IGNORECASE)
|
|
666
|
+
|
|
667
|
+
return cleaned.strip()
|
|
668
|
+
|
|
669
|
+
@staticmethod
|
|
670
|
+
def _try_llm_fix(content: str, agent: Any, error_msg: str) -> Optional[str]:
|
|
671
|
+
"""尝试使用大模型修复工具调用格式
|
|
672
|
+
|
|
673
|
+
参数:
|
|
674
|
+
content: 包含错误工具调用的内容
|
|
675
|
+
agent: Agent实例,用于调用大模型
|
|
676
|
+
error_msg: 错误消息
|
|
677
|
+
|
|
678
|
+
返回:
|
|
679
|
+
Optional[str]: 修复后的内容,如果修复失败则返回None
|
|
680
|
+
"""
|
|
681
|
+
try:
|
|
682
|
+
from jarvis.jarvis_agent import Agent
|
|
683
|
+
agent_instance: Agent = agent
|
|
684
|
+
|
|
685
|
+
# 获取工具使用说明
|
|
686
|
+
tool_usage = agent_instance.get_tool_usage_prompt()
|
|
687
|
+
|
|
688
|
+
# 构建修复提示
|
|
689
|
+
fix_prompt = f"""你之前的工具调用格式有误,请根据工具使用说明修复以下内容。
|
|
690
|
+
|
|
691
|
+
**错误信息:**
|
|
692
|
+
{error_msg}
|
|
693
|
+
|
|
694
|
+
**工具使用说明:**
|
|
695
|
+
{tool_usage}
|
|
696
|
+
|
|
697
|
+
**错误的工具调用内容:**
|
|
698
|
+
{content}
|
|
699
|
+
|
|
700
|
+
请修复上述工具调用内容,确保:
|
|
701
|
+
1. 包含完整的 {ot("TOOL_CALL")} 和 {ct("TOOL_CALL")} 标签
|
|
702
|
+
2. JSON格式正确,包含 name、arguments、want 三个字段
|
|
703
|
+
3. 如果使用多行字符串,推荐使用 ||| 或 ``` 分隔符包裹
|
|
704
|
+
|
|
705
|
+
请直接返回修复后的完整工具调用内容,不要添加其他说明文字。"""
|
|
706
|
+
|
|
707
|
+
# 调用大模型修复
|
|
708
|
+
print("🤖 尝试使用大模型修复工具调用格式...")
|
|
709
|
+
fixed_content = agent_instance.model.chat_until_success(fix_prompt) # type: ignore
|
|
710
|
+
|
|
711
|
+
if fixed_content:
|
|
712
|
+
print("✅ 大模型修复完成")
|
|
713
|
+
return fixed_content
|
|
714
|
+
else:
|
|
715
|
+
print("❌ 大模型修复失败:返回内容为空")
|
|
716
|
+
return None
|
|
717
|
+
|
|
718
|
+
except Exception as e:
|
|
719
|
+
print(f"❌ 大模型修复失败:{str(e)}")
|
|
720
|
+
return None
|
|
721
|
+
|
|
632
722
|
@staticmethod
|
|
633
723
|
def _extract_tool_calls(
|
|
634
724
|
content: str,
|
|
725
|
+
agent: Optional[Any] = None,
|
|
635
726
|
) -> Tuple[Dict[str, Dict[str, Any]], str, bool]:
|
|
636
727
|
"""从内容中提取工具调用。
|
|
637
728
|
|
|
638
729
|
参数:
|
|
639
730
|
content: 包含工具调用的内容
|
|
731
|
+
agent: 可选的Agent实例,用于在解析失败时调用大模型修复
|
|
640
732
|
|
|
641
733
|
返回:
|
|
642
734
|
Tuple[Dict[str, Dict[str, Any]], str, bool]:
|
|
@@ -647,26 +739,33 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
647
739
|
异常:
|
|
648
740
|
Exception: 如果工具调用缺少必要字段
|
|
649
741
|
"""
|
|
650
|
-
# 如果</TOOL_CALL
|
|
651
|
-
|
|
652
|
-
|
|
742
|
+
# 如果</TOOL_CALL>出现在响应的末尾,但是前面没有换行符,自动插入一个换行符进行修复(忽略大小写)
|
|
743
|
+
close_tag = ct("TOOL_CALL")
|
|
744
|
+
# 使用正则表达式查找结束标签(忽略大小写),以获取实际位置和原始大小写
|
|
745
|
+
close_tag_pattern = re.escape(close_tag)
|
|
746
|
+
match = re.search(rf'{close_tag_pattern}$', content.rstrip(), re.IGNORECASE)
|
|
747
|
+
if match:
|
|
748
|
+
pos = match.start()
|
|
653
749
|
if pos > 0 and content[pos - 1] not in ("\n", "\r"):
|
|
654
750
|
content = content[:pos] + "\n" + content[pos:]
|
|
655
751
|
|
|
656
|
-
#
|
|
657
|
-
pattern = rf'(?
|
|
752
|
+
# 首先尝试标准的提取方式(忽略大小写)
|
|
753
|
+
pattern = rf'(?msi){re.escape(ot("TOOL_CALL"))}(.*?)^{re.escape(ct("TOOL_CALL"))}'
|
|
658
754
|
data = re.findall(pattern, content)
|
|
659
755
|
auto_completed = False
|
|
756
|
+
|
|
757
|
+
# 如果标准提取失败,尝试更宽松的提取方式
|
|
660
758
|
if not data:
|
|
661
759
|
# can_handle 确保 ot("TOOL_CALL") 在内容中(行首)。
|
|
662
760
|
# 如果数据为空,则表示行首的 ct("TOOL_CALL") 可能丢失。
|
|
663
|
-
has_open_at_bol = re.search(rf'(?
|
|
664
|
-
has_close_at_bol = re.search(rf'(?
|
|
761
|
+
has_open_at_bol = re.search(rf'(?mi){re.escape(ot("TOOL_CALL"))}', content) is not None
|
|
762
|
+
has_close_at_bol = re.search(rf'(?mi)^{re.escape(ct("TOOL_CALL"))}', content) is not None
|
|
763
|
+
|
|
665
764
|
if has_open_at_bol and not has_close_at_bol:
|
|
666
765
|
# 尝试通过附加结束标签来修复它(确保结束标签位于行首)
|
|
667
766
|
fixed_content = content.strip() + f"\n{ct('TOOL_CALL')}"
|
|
668
767
|
|
|
669
|
-
# 再次提取,并检查
|
|
768
|
+
# 再次提取,并检查JSON是否有效
|
|
670
769
|
temp_data = re.findall(
|
|
671
770
|
pattern,
|
|
672
771
|
fixed_content,
|
|
@@ -674,45 +773,104 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
674
773
|
|
|
675
774
|
if temp_data:
|
|
676
775
|
try:
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
# Ask user for confirmation
|
|
680
|
-
|
|
776
|
+
json_loads(temp_data[0]) # Check if valid JSON
|
|
681
777
|
data = temp_data
|
|
682
778
|
auto_completed = True
|
|
683
|
-
except (
|
|
684
|
-
# Even after fixing, it's not valid
|
|
685
|
-
# Fall through to
|
|
779
|
+
except (Exception, EOFError, KeyboardInterrupt):
|
|
780
|
+
# Even after fixing, it's not valid JSON, or user cancelled.
|
|
781
|
+
# Fall through to try more lenient extraction.
|
|
686
782
|
pass
|
|
687
|
-
|
|
783
|
+
|
|
784
|
+
# 如果仍然没有数据,尝试更宽松的提取:直接从开始标签后提取JSON
|
|
785
|
+
if not data:
|
|
786
|
+
# 找到开始标签的位置
|
|
787
|
+
open_tag_match = re.search(rf'(?i){re.escape(ot("TOOL_CALL"))}', content)
|
|
788
|
+
if open_tag_match:
|
|
789
|
+
# 从开始标签后提取JSON
|
|
790
|
+
start_pos = open_tag_match.end()
|
|
791
|
+
json_str, end_pos = ToolRegistry._extract_json_from_text(content, start_pos)
|
|
792
|
+
|
|
793
|
+
if json_str:
|
|
794
|
+
# 清理JSON字符串中的额外标记
|
|
795
|
+
json_str = ToolRegistry._clean_extra_markers(json_str)
|
|
796
|
+
|
|
797
|
+
# 尝试解析JSON
|
|
798
|
+
try:
|
|
799
|
+
parsed = json_loads(json_str)
|
|
800
|
+
# 验证是否包含必要字段
|
|
801
|
+
if "name" in parsed and "arguments" in parsed and "want" in parsed:
|
|
802
|
+
data = [json_str]
|
|
803
|
+
auto_completed = True
|
|
804
|
+
except Exception:
|
|
805
|
+
# JSON解析失败,继续尝试其他方法
|
|
806
|
+
pass
|
|
807
|
+
|
|
808
|
+
# 如果仍然没有数据,尝试使用大模型修复
|
|
688
809
|
if not data:
|
|
810
|
+
long_hint = ToolRegistry._get_long_response_hint(content)
|
|
811
|
+
error_msg = f"只有{ot('TOOL_CALL')}标签,未找到{ct('TOOL_CALL')}标签,调用格式错误,请检查工具调用格式。\n{tool_call_help}{long_hint}"
|
|
812
|
+
|
|
813
|
+
# 如果提供了agent且long_hint为空,尝试使用大模型修复
|
|
814
|
+
if agent is not None and not long_hint:
|
|
815
|
+
fixed_content = ToolRegistry._try_llm_fix(content, agent, error_msg)
|
|
816
|
+
if fixed_content:
|
|
817
|
+
# 递归调用自身,尝试解析修复后的内容
|
|
818
|
+
return ToolRegistry._extract_tool_calls(fixed_content, None)
|
|
819
|
+
|
|
820
|
+
# 如果大模型修复失败或未提供agent或long_hint不为空,返回错误
|
|
689
821
|
return (
|
|
690
822
|
{},
|
|
691
|
-
|
|
823
|
+
error_msg,
|
|
692
824
|
False,
|
|
693
825
|
)
|
|
826
|
+
|
|
694
827
|
ret = []
|
|
695
828
|
for item in data:
|
|
696
829
|
try:
|
|
697
|
-
|
|
830
|
+
# 清理可能存在的额外标记
|
|
831
|
+
cleaned_item = ToolRegistry._clean_extra_markers(item)
|
|
832
|
+
msg = json_loads(cleaned_item)
|
|
698
833
|
except Exception as e:
|
|
834
|
+
long_hint = ToolRegistry._get_long_response_hint(content)
|
|
835
|
+
error_msg = f"""Jsonnet 解析失败:{e}
|
|
836
|
+
|
|
837
|
+
提示:Jsonnet支持双引号/单引号、尾随逗号、注释。多行字符串推荐使用 ||| 或 ``` 分隔符包裹,直接换行无需转义,支持保留缩进。
|
|
838
|
+
|
|
839
|
+
{tool_call_help}{long_hint}"""
|
|
840
|
+
|
|
841
|
+
# 如果提供了agent且long_hint为空,尝试使用大模型修复
|
|
842
|
+
if agent is not None and not long_hint:
|
|
843
|
+
fixed_content = ToolRegistry._try_llm_fix(content, agent, error_msg)
|
|
844
|
+
if fixed_content:
|
|
845
|
+
# 递归调用自身,尝试解析修复后的内容
|
|
846
|
+
return ToolRegistry._extract_tool_calls(fixed_content, None)
|
|
847
|
+
|
|
848
|
+
# 如果大模型修复失败或未提供agent或long_hint不为空,返回错误
|
|
699
849
|
return (
|
|
700
850
|
{},
|
|
701
|
-
|
|
702
|
-
{e}
|
|
703
|
-
|
|
704
|
-
{tool_call_help}""",
|
|
851
|
+
error_msg,
|
|
705
852
|
False,
|
|
706
853
|
)
|
|
707
854
|
|
|
708
855
|
if "name" in msg and "arguments" in msg and "want" in msg:
|
|
709
856
|
ret.append(msg)
|
|
710
857
|
else:
|
|
858
|
+
long_hint = ToolRegistry._get_long_response_hint(content)
|
|
859
|
+
error_msg = f"""工具调用格式错误,请检查工具调用格式(缺少name、arguments、want字段)。
|
|
860
|
+
|
|
861
|
+
{tool_call_help}{long_hint}"""
|
|
862
|
+
|
|
863
|
+
# 如果提供了agent且long_hint为空,尝试使用大模型修复
|
|
864
|
+
if agent is not None and not long_hint:
|
|
865
|
+
fixed_content = ToolRegistry._try_llm_fix(content, agent, error_msg)
|
|
866
|
+
if fixed_content:
|
|
867
|
+
# 递归调用自身,尝试解析修复后的内容
|
|
868
|
+
return ToolRegistry._extract_tool_calls(fixed_content, None)
|
|
869
|
+
|
|
870
|
+
# 如果大模型修复失败或未提供agent或long_hint不为空,返回错误
|
|
711
871
|
return (
|
|
712
872
|
{},
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
{tool_call_help}""",
|
|
873
|
+
error_msg,
|
|
716
874
|
False,
|
|
717
875
|
)
|
|
718
876
|
if len(ret) > 1:
|
|
@@ -736,9 +894,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
736
894
|
func: 工具执行函数
|
|
737
895
|
"""
|
|
738
896
|
if name in self.tools:
|
|
739
|
-
|
|
740
|
-
f"警告: 工具 '{name}' 已存在,将被覆盖", OutputType.WARNING
|
|
741
|
-
)
|
|
897
|
+
print(f"⚠️ 警告: 工具 '{name}' 已存在,将被覆盖")
|
|
742
898
|
self.tools[name] = Tool(name, description, parameters, func, protocol_version)
|
|
743
899
|
|
|
744
900
|
def get_tool(self, name: str) -> Optional[Tool]:
|
|
@@ -760,6 +916,18 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
760
916
|
"""
|
|
761
917
|
return [tool.to_dict() for tool in self.tools.values()]
|
|
762
918
|
|
|
919
|
+
def get_custom_tools(self) -> List[Dict[str, Any]]:
|
|
920
|
+
"""获取用户自定义工具(非内置工具)
|
|
921
|
+
|
|
922
|
+
返回:
|
|
923
|
+
List[Dict[str, Any]]: 包含用户自定义工具信息的列表
|
|
924
|
+
"""
|
|
925
|
+
return [
|
|
926
|
+
tool.to_dict()
|
|
927
|
+
for tool in self.tools.values()
|
|
928
|
+
if tool.name not in self._builtin_tool_names
|
|
929
|
+
]
|
|
930
|
+
|
|
763
931
|
def execute_tool(
|
|
764
932
|
self, name: str, arguments: Dict[str, Any], agent: Optional[Any] = None
|
|
765
933
|
) -> Dict[str, Any]:
|
|
@@ -831,6 +999,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
831
999
|
"""
|
|
832
1000
|
if len(output.splitlines()) > 60:
|
|
833
1001
|
lines = output.splitlines()
|
|
1002
|
+
print("⚠️ 输出太长,截取前后30行")
|
|
834
1003
|
return "\n".join(
|
|
835
1004
|
lines[:30] + ["\n...内容太长,已截取前后30行...\n"] + lines[-30:]
|
|
836
1005
|
)
|
|
@@ -849,17 +1018,25 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
849
1018
|
# 如果args是字符串,尝试解析为JSON
|
|
850
1019
|
if isinstance(args, str):
|
|
851
1020
|
try:
|
|
852
|
-
args =
|
|
853
|
-
except
|
|
1021
|
+
args = json_loads(args)
|
|
1022
|
+
except Exception:
|
|
854
1023
|
# 返回错误并附带完整的工具使用提示,指导下一次正确调用
|
|
855
1024
|
try:
|
|
856
1025
|
usage_prompt = agent_instance.get_tool_usage_prompt()
|
|
857
1026
|
except Exception:
|
|
858
1027
|
usage_prompt = tool_call_help
|
|
859
|
-
|
|
1028
|
+
print("❌ 工具参数格式无效")
|
|
1029
|
+
return f"工具参数格式无效: {name}。arguments 应为可解析的 Jsonnet 或对象,请按工具调用格式提供。\n提示:对于多行字符串参数,推荐使用 ||| 或 ``` 分隔符包裹,直接换行无需转义,支持保留缩进。\n\n{usage_prompt}"
|
|
860
1030
|
|
|
1031
|
+
print(f"🛠️ 执行工具调用 {name}")
|
|
861
1032
|
# 执行工具调用(根据工具实现的协议版本,由系统在内部决定agent的传递方式)
|
|
862
1033
|
result = self.execute_tool(name, args, agent)
|
|
1034
|
+
|
|
1035
|
+
# 打印执行状态
|
|
1036
|
+
if result.get("success", False):
|
|
1037
|
+
print(f"✅ 执行工具调用 {name} 成功")
|
|
1038
|
+
else:
|
|
1039
|
+
print(f"❌ 执行工具调用 {name} 失败")
|
|
863
1040
|
|
|
864
1041
|
# 记录本轮实际执行的工具,供上层逻辑(如记忆保存判定)使用
|
|
865
1042
|
try:
|
|
@@ -892,9 +1069,11 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
892
1069
|
|
|
893
1070
|
# 检查内容是否过大
|
|
894
1071
|
model_group = None
|
|
1072
|
+
platform = None
|
|
895
1073
|
if agent_instance.model:
|
|
896
1074
|
model_group = agent_instance.model.model_group
|
|
897
|
-
|
|
1075
|
+
platform = agent_instance.model
|
|
1076
|
+
is_large_content = is_context_overflow(output, model_group, platform)
|
|
898
1077
|
|
|
899
1078
|
if is_large_content:
|
|
900
1079
|
# 创建临时文件
|
|
@@ -927,7 +1106,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
927
1106
|
</content>
|
|
928
1107
|
|
|
929
1108
|
上传的文件是以下工具执行结果:
|
|
930
|
-
{
|
|
1109
|
+
{json.dumps({"name":name, "arguments":args, "want":want}, ensure_ascii=False, indent=2)}
|
|
931
1110
|
|
|
932
1111
|
请根据以上信息,继续完成任务。
|
|
933
1112
|
"""
|
|
@@ -944,7 +1123,17 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
944
1123
|
return output
|
|
945
1124
|
|
|
946
1125
|
except Exception as e:
|
|
947
|
-
|
|
1126
|
+
# 尝试获取工具名称(如果已定义)
|
|
1127
|
+
tool_name = ""
|
|
1128
|
+
try:
|
|
1129
|
+
if 'name' in locals():
|
|
1130
|
+
tool_name = name
|
|
1131
|
+
except Exception:
|
|
1132
|
+
pass
|
|
1133
|
+
if tool_name:
|
|
1134
|
+
print(f"❌ 执行工具调用 {tool_name} 失败:{str(e)}")
|
|
1135
|
+
else:
|
|
1136
|
+
print(f"❌ 工具调用失败:{str(e)}")
|
|
948
1137
|
try:
|
|
949
1138
|
from jarvis.jarvis_agent import Agent # 延迟导入避免循环依赖
|
|
950
1139
|
agent_instance_for_prompt: Agent = agent # type: ignore
|