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.
Files changed (159) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +243 -139
  3. jarvis/jarvis_agent/agent_manager.py +5 -10
  4. jarvis/jarvis_agent/builtin_input_handler.py +2 -6
  5. jarvis/jarvis_agent/config_editor.py +2 -7
  6. jarvis/jarvis_agent/event_bus.py +82 -12
  7. jarvis/jarvis_agent/file_context_handler.py +265 -15
  8. jarvis/jarvis_agent/file_methodology_manager.py +3 -4
  9. jarvis/jarvis_agent/jarvis.py +113 -98
  10. jarvis/jarvis_agent/language_extractors/__init__.py +57 -0
  11. jarvis/jarvis_agent/language_extractors/c_extractor.py +21 -0
  12. jarvis/jarvis_agent/language_extractors/cpp_extractor.py +21 -0
  13. jarvis/jarvis_agent/language_extractors/go_extractor.py +21 -0
  14. jarvis/jarvis_agent/language_extractors/java_extractor.py +84 -0
  15. jarvis/jarvis_agent/language_extractors/javascript_extractor.py +79 -0
  16. jarvis/jarvis_agent/language_extractors/python_extractor.py +21 -0
  17. jarvis/jarvis_agent/language_extractors/rust_extractor.py +21 -0
  18. jarvis/jarvis_agent/language_extractors/typescript_extractor.py +84 -0
  19. jarvis/jarvis_agent/language_support_info.py +486 -0
  20. jarvis/jarvis_agent/main.py +6 -12
  21. jarvis/jarvis_agent/memory_manager.py +7 -16
  22. jarvis/jarvis_agent/methodology_share_manager.py +10 -16
  23. jarvis/jarvis_agent/prompt_manager.py +1 -1
  24. jarvis/jarvis_agent/prompts.py +193 -171
  25. jarvis/jarvis_agent/protocols.py +8 -12
  26. jarvis/jarvis_agent/run_loop.py +77 -14
  27. jarvis/jarvis_agent/session_manager.py +2 -3
  28. jarvis/jarvis_agent/share_manager.py +12 -21
  29. jarvis/jarvis_agent/shell_input_handler.py +1 -2
  30. jarvis/jarvis_agent/task_analyzer.py +26 -4
  31. jarvis/jarvis_agent/task_manager.py +11 -27
  32. jarvis/jarvis_agent/tool_executor.py +2 -3
  33. jarvis/jarvis_agent/tool_share_manager.py +12 -24
  34. jarvis/jarvis_agent/web_server.py +55 -20
  35. jarvis/jarvis_c2rust/__init__.py +5 -5
  36. jarvis/jarvis_c2rust/cli.py +461 -499
  37. jarvis/jarvis_c2rust/collector.py +45 -53
  38. jarvis/jarvis_c2rust/constants.py +26 -0
  39. jarvis/jarvis_c2rust/library_replacer.py +264 -132
  40. jarvis/jarvis_c2rust/llm_module_agent.py +162 -190
  41. jarvis/jarvis_c2rust/loaders.py +207 -0
  42. jarvis/jarvis_c2rust/models.py +28 -0
  43. jarvis/jarvis_c2rust/optimizer.py +1592 -395
  44. jarvis/jarvis_c2rust/transpiler.py +1722 -1064
  45. jarvis/jarvis_c2rust/utils.py +385 -0
  46. jarvis/jarvis_code_agent/build_validation_config.py +2 -3
  47. jarvis/jarvis_code_agent/code_agent.py +394 -320
  48. jarvis/jarvis_code_agent/code_analyzer/__init__.py +3 -0
  49. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +4 -0
  50. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +17 -2
  51. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +3 -0
  52. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +36 -4
  53. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +9 -0
  54. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +9 -0
  55. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +12 -1
  56. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +22 -5
  57. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +57 -32
  58. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +62 -6
  59. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +8 -9
  60. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +290 -5
  61. jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -0
  62. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +21 -3
  63. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +72 -4
  64. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +35 -3
  65. jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +212 -0
  66. jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +254 -0
  67. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +52 -2
  68. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +73 -1
  69. jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
  70. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +306 -152
  71. jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
  72. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +193 -18
  73. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +18 -8
  74. jarvis/jarvis_code_agent/lint.py +258 -27
  75. jarvis/jarvis_code_agent/utils.py +0 -1
  76. jarvis/jarvis_code_analysis/code_review.py +19 -24
  77. jarvis/jarvis_data/config_schema.json +53 -26
  78. jarvis/jarvis_git_squash/main.py +4 -5
  79. jarvis/jarvis_git_utils/git_commiter.py +44 -49
  80. jarvis/jarvis_mcp/sse_mcp_client.py +20 -27
  81. jarvis/jarvis_mcp/stdio_mcp_client.py +11 -12
  82. jarvis/jarvis_mcp/streamable_mcp_client.py +15 -14
  83. jarvis/jarvis_memory_organizer/memory_organizer.py +55 -74
  84. jarvis/jarvis_methodology/main.py +32 -48
  85. jarvis/jarvis_multi_agent/__init__.py +79 -61
  86. jarvis/jarvis_multi_agent/main.py +3 -7
  87. jarvis/jarvis_platform/base.py +469 -199
  88. jarvis/jarvis_platform/human.py +7 -8
  89. jarvis/jarvis_platform/kimi.py +30 -36
  90. jarvis/jarvis_platform/openai.py +65 -27
  91. jarvis/jarvis_platform/registry.py +26 -10
  92. jarvis/jarvis_platform/tongyi.py +24 -25
  93. jarvis/jarvis_platform/yuanbao.py +31 -42
  94. jarvis/jarvis_platform_manager/main.py +66 -77
  95. jarvis/jarvis_platform_manager/service.py +8 -13
  96. jarvis/jarvis_rag/cli.py +49 -51
  97. jarvis/jarvis_rag/embedding_manager.py +13 -18
  98. jarvis/jarvis_rag/llm_interface.py +8 -9
  99. jarvis/jarvis_rag/query_rewriter.py +10 -21
  100. jarvis/jarvis_rag/rag_pipeline.py +24 -27
  101. jarvis/jarvis_rag/reranker.py +4 -5
  102. jarvis/jarvis_rag/retriever.py +28 -30
  103. jarvis/jarvis_sec/__init__.py +220 -3520
  104. jarvis/jarvis_sec/agents.py +143 -0
  105. jarvis/jarvis_sec/analysis.py +276 -0
  106. jarvis/jarvis_sec/cli.py +29 -6
  107. jarvis/jarvis_sec/clustering.py +1439 -0
  108. jarvis/jarvis_sec/file_manager.py +427 -0
  109. jarvis/jarvis_sec/parsers.py +73 -0
  110. jarvis/jarvis_sec/prompts.py +268 -0
  111. jarvis/jarvis_sec/report.py +83 -4
  112. jarvis/jarvis_sec/review.py +453 -0
  113. jarvis/jarvis_sec/utils.py +499 -0
  114. jarvis/jarvis_sec/verification.py +848 -0
  115. jarvis/jarvis_sec/workflow.py +7 -0
  116. jarvis/jarvis_smart_shell/main.py +38 -87
  117. jarvis/jarvis_stats/cli.py +1 -1
  118. jarvis/jarvis_stats/stats.py +7 -7
  119. jarvis/jarvis_stats/storage.py +15 -21
  120. jarvis/jarvis_tools/clear_memory.py +3 -20
  121. jarvis/jarvis_tools/cli/main.py +20 -23
  122. jarvis/jarvis_tools/edit_file.py +1066 -0
  123. jarvis/jarvis_tools/execute_script.py +42 -21
  124. jarvis/jarvis_tools/file_analyzer.py +6 -9
  125. jarvis/jarvis_tools/generate_new_tool.py +11 -20
  126. jarvis/jarvis_tools/lsp_client.py +1552 -0
  127. jarvis/jarvis_tools/methodology.py +2 -3
  128. jarvis/jarvis_tools/read_code.py +1525 -87
  129. jarvis/jarvis_tools/read_symbols.py +2 -3
  130. jarvis/jarvis_tools/read_webpage.py +7 -10
  131. jarvis/jarvis_tools/registry.py +370 -181
  132. jarvis/jarvis_tools/retrieve_memory.py +20 -19
  133. jarvis/jarvis_tools/rewrite_file.py +105 -0
  134. jarvis/jarvis_tools/save_memory.py +3 -15
  135. jarvis/jarvis_tools/search_web.py +3 -7
  136. jarvis/jarvis_tools/sub_agent.py +17 -6
  137. jarvis/jarvis_tools/sub_code_agent.py +14 -16
  138. jarvis/jarvis_tools/virtual_tty.py +54 -32
  139. jarvis/jarvis_utils/clipboard.py +7 -10
  140. jarvis/jarvis_utils/config.py +98 -63
  141. jarvis/jarvis_utils/embedding.py +5 -5
  142. jarvis/jarvis_utils/fzf.py +8 -8
  143. jarvis/jarvis_utils/git_utils.py +81 -67
  144. jarvis/jarvis_utils/input.py +24 -49
  145. jarvis/jarvis_utils/jsonnet_compat.py +465 -0
  146. jarvis/jarvis_utils/methodology.py +33 -35
  147. jarvis/jarvis_utils/utils.py +245 -202
  148. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/METADATA +205 -70
  149. jarvis_ai_assistant-0.7.6.dist-info/RECORD +218 -0
  150. jarvis/jarvis_agent/edit_file_handler.py +0 -584
  151. jarvis/jarvis_agent/rewrite_file_handler.py +0 -141
  152. jarvis/jarvis_agent/task_planner.py +0 -496
  153. jarvis/jarvis_platform/ai8.py +0 -332
  154. jarvis/jarvis_tools/ask_user.py +0 -54
  155. jarvis_ai_assistant-0.7.0.dist-info/RECORD +0 -192
  156. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/WHEEL +0 -0
  157. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/entry_points.txt +0 -0
  158. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/licenses/LICENSE +0 -0
  159. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/top_level.txt +0 -0
@@ -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
- <introduction>
25
- # 🛠️ 工具使用系统
26
- 您正在使用一个需要精确格式和严格规则的工具执行系统。
27
- </introduction>
28
-
29
- <format>
30
- # 📋 工具调用格式
43
+ 工具调用格式(Jsonnet):
31
44
  {ot("TOOL_CALL")}
32
- want: 想要从执行结果中获取到的信息,如果工具输出内容过长,会根据此字段尝试提取有效信息
33
- name: 工具名称
34
-
35
- arguments:
36
- param1: 值1
37
- param2: 值2
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
- arguments:
83
- interpreter: bash
84
- script_content: |
85
- git status --porcelain
86
- {ct("TOOL_CALL")}
87
- </string_format>
88
-
89
- <best_practices>
90
- # 💡 最佳实践
91
- - 准备好后立即开始执行
92
- - 无需请求许可即可开始
93
- - 使用正确的字符串格式
94
- - 监控进度并调整
95
- - 遇到困难时请求帮助
96
- </best_practices>
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
- return re.search(rf'(?m){re.escape(ot("TOOL_CALL"))}', response) is not None
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 += " <yaml>|\n"
109
+ tools_prompt += " <json>|\n"
143
110
 
144
- # 生成格式化的YAML参数
145
- yaml_params = yaml.dump(
111
+ # 生成格式化的JSON参数
112
+ json_params = json.dumps(
146
113
  tool["parameters"],
147
- allow_unicode=True,
148
- indent=4,
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 yaml_params.split("\n"):
120
+ for line in json_params.split("\n"):
155
121
  tools_prompt += f" {line.rstrip()}\n"
156
122
 
157
- tools_prompt += " </yaml>\n"
123
+ tools_prompt += " </json>\n"
158
124
  tools_prompt += " </parameters>\n"
159
125
  tools_prompt += " </tool>\n"
160
126
 
161
- except yaml.YAMLError as e:
162
- PrettyOutput.print(
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
- tool_call, err_msg, auto_completed = self._extract_tool_calls(response)
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
- PrettyOutput.print(f"工具调用处理失败: {str(e)}", OutputType.ERROR)
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
- PrettyOutput.print(
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
- PrettyOutput.print(
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
- PrettyOutput.print(
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
- config = yaml.safe_load(open(file_path, "r", encoding="utf-8"))
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
- PrettyOutput.print("\n".join(error_lines), OutputType.WARNING)
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
- PrettyOutput.print(
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
- PrettyOutput.print(
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": yaml.safe_dump(ret, allow_unicode=True),
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
- PrettyOutput.print(
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
- PrettyOutput.print(
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
- PrettyOutput.print(
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
- PrettyOutput.print(
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
- PrettyOutput.print(
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
- PrettyOutput.print(
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
- PrettyOutput.print(f"文件不存在: {p_file_path}", OutputType.ERROR)
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
- PrettyOutput.print(
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'(?ms){re.escape(ot("TOOL_CALL"))}(.*?)^{re.escape(ct("TOOL_CALL"))}'
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
- if content.rstrip().endswith(ct("TOOL_CALL")):
652
- pos = content.rfind(ct("TOOL_CALL"))
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'(?ms){re.escape(ot("TOOL_CALL"))}(.*?)^{re.escape(ct("TOOL_CALL"))}'
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'(?m){re.escape(ot("TOOL_CALL"))}', content) is not None
664
- has_close_at_bol = re.search(rf'(?m)^{re.escape(ct("TOOL_CALL"))}', content) is not None
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
- # 再次提取,并检查YAML是否有效
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
- yaml.safe_load(temp_data[0]) # Check if valid YAML
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 (yaml.YAMLError, EOFError, KeyboardInterrupt):
684
- # Even after fixing, it's not valid YAML, or user cancelled.
685
- # Fall through to the original error.
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
- f"只有{ot('TOOL_CALL')}标签,未找到{ct('TOOL_CALL')}标签,调用格式错误,请检查工具调用格式。\n{tool_call_help}",
823
+ error_msg,
692
824
  False,
693
825
  )
826
+
694
827
  ret = []
695
828
  for item in data:
696
829
  try:
697
- msg = yaml.safe_load(item)
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
- f"""yaml 解析失败,请检查工具调用格式。
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
- f"""工具调用格式错误,请检查工具调用格式(缺少name、arguments、want字段)。
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
- PrettyOutput.print(
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 = json.loads(args)
853
- except json.JSONDecodeError:
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
- return f"工具参数格式无效: {name}。arguments 应为可解析的 JSON 或对象,请按工具调用格式提供。\n\n{usage_prompt}"
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
- is_large_content = is_context_overflow(output, model_group)
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
- {yaml.safe_dump({"name":name, "arguments":args, "want":want})}
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
- PrettyOutput.print(f"工具执行失败:{str(e)}", OutputType.ERROR)
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