jarvis-ai-assistant 0.7.8__py3-none-any.whl → 1.0.2__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 +567 -222
- jarvis/jarvis_agent/agent_manager.py +19 -12
- jarvis/jarvis_agent/builtin_input_handler.py +79 -11
- jarvis/jarvis_agent/config_editor.py +7 -2
- jarvis/jarvis_agent/event_bus.py +24 -13
- jarvis/jarvis_agent/events.py +19 -1
- jarvis/jarvis_agent/file_context_handler.py +67 -64
- jarvis/jarvis_agent/file_methodology_manager.py +38 -24
- jarvis/jarvis_agent/jarvis.py +186 -114
- jarvis/jarvis_agent/language_extractors/__init__.py +8 -1
- jarvis/jarvis_agent/language_extractors/c_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/cpp_extractor.py +9 -4
- jarvis/jarvis_agent/language_extractors/go_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/java_extractor.py +27 -20
- jarvis/jarvis_agent/language_extractors/javascript_extractor.py +22 -17
- jarvis/jarvis_agent/language_extractors/python_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/rust_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/typescript_extractor.py +22 -17
- jarvis/jarvis_agent/language_support_info.py +250 -219
- jarvis/jarvis_agent/main.py +19 -23
- jarvis/jarvis_agent/memory_manager.py +9 -6
- jarvis/jarvis_agent/methodology_share_manager.py +21 -15
- jarvis/jarvis_agent/output_handler.py +4 -2
- jarvis/jarvis_agent/prompt_builder.py +7 -6
- jarvis/jarvis_agent/prompt_manager.py +113 -8
- jarvis/jarvis_agent/prompts.py +317 -85
- jarvis/jarvis_agent/protocols.py +5 -2
- jarvis/jarvis_agent/run_loop.py +192 -32
- jarvis/jarvis_agent/session_manager.py +7 -3
- jarvis/jarvis_agent/share_manager.py +23 -13
- jarvis/jarvis_agent/shell_input_handler.py +12 -8
- jarvis/jarvis_agent/stdio_redirect.py +25 -26
- jarvis/jarvis_agent/task_analyzer.py +29 -23
- jarvis/jarvis_agent/task_list.py +869 -0
- jarvis/jarvis_agent/task_manager.py +26 -23
- jarvis/jarvis_agent/tool_executor.py +6 -5
- jarvis/jarvis_agent/tool_share_manager.py +24 -14
- jarvis/jarvis_agent/user_interaction.py +3 -3
- jarvis/jarvis_agent/utils.py +9 -1
- jarvis/jarvis_agent/web_bridge.py +37 -17
- jarvis/jarvis_agent/web_output_sink.py +5 -2
- jarvis/jarvis_agent/web_server.py +165 -36
- jarvis/jarvis_c2rust/__init__.py +1 -1
- jarvis/jarvis_c2rust/cli.py +260 -141
- jarvis/jarvis_c2rust/collector.py +37 -18
- jarvis/jarvis_c2rust/constants.py +60 -0
- jarvis/jarvis_c2rust/library_replacer.py +242 -1010
- jarvis/jarvis_c2rust/library_replacer_checkpoint.py +133 -0
- jarvis/jarvis_c2rust/library_replacer_llm.py +287 -0
- jarvis/jarvis_c2rust/library_replacer_loader.py +191 -0
- jarvis/jarvis_c2rust/library_replacer_output.py +134 -0
- jarvis/jarvis_c2rust/library_replacer_prompts.py +124 -0
- jarvis/jarvis_c2rust/library_replacer_utils.py +188 -0
- jarvis/jarvis_c2rust/llm_module_agent.py +98 -1044
- jarvis/jarvis_c2rust/llm_module_agent_apply.py +170 -0
- jarvis/jarvis_c2rust/llm_module_agent_executor.py +288 -0
- jarvis/jarvis_c2rust/llm_module_agent_loader.py +170 -0
- jarvis/jarvis_c2rust/llm_module_agent_prompts.py +268 -0
- jarvis/jarvis_c2rust/llm_module_agent_types.py +57 -0
- jarvis/jarvis_c2rust/llm_module_agent_utils.py +150 -0
- jarvis/jarvis_c2rust/llm_module_agent_validator.py +119 -0
- jarvis/jarvis_c2rust/loaders.py +28 -10
- jarvis/jarvis_c2rust/models.py +5 -2
- jarvis/jarvis_c2rust/optimizer.py +192 -1974
- jarvis/jarvis_c2rust/optimizer_build_fix.py +286 -0
- jarvis/jarvis_c2rust/optimizer_clippy.py +766 -0
- jarvis/jarvis_c2rust/optimizer_config.py +49 -0
- jarvis/jarvis_c2rust/optimizer_docs.py +183 -0
- jarvis/jarvis_c2rust/optimizer_options.py +48 -0
- jarvis/jarvis_c2rust/optimizer_progress.py +469 -0
- jarvis/jarvis_c2rust/optimizer_report.py +52 -0
- jarvis/jarvis_c2rust/optimizer_unsafe.py +309 -0
- jarvis/jarvis_c2rust/optimizer_utils.py +469 -0
- jarvis/jarvis_c2rust/optimizer_visibility.py +185 -0
- jarvis/jarvis_c2rust/scanner.py +229 -166
- jarvis/jarvis_c2rust/transpiler.py +531 -2732
- jarvis/jarvis_c2rust/transpiler_agents.py +503 -0
- jarvis/jarvis_c2rust/transpiler_build.py +1294 -0
- jarvis/jarvis_c2rust/transpiler_codegen.py +204 -0
- jarvis/jarvis_c2rust/transpiler_compile.py +146 -0
- jarvis/jarvis_c2rust/transpiler_config.py +178 -0
- jarvis/jarvis_c2rust/transpiler_context.py +122 -0
- jarvis/jarvis_c2rust/transpiler_executor.py +516 -0
- jarvis/jarvis_c2rust/transpiler_generation.py +278 -0
- jarvis/jarvis_c2rust/transpiler_git.py +163 -0
- jarvis/jarvis_c2rust/transpiler_mod_utils.py +225 -0
- jarvis/jarvis_c2rust/transpiler_modules.py +336 -0
- jarvis/jarvis_c2rust/transpiler_planning.py +394 -0
- jarvis/jarvis_c2rust/transpiler_review.py +1196 -0
- jarvis/jarvis_c2rust/transpiler_symbols.py +176 -0
- jarvis/jarvis_c2rust/utils.py +269 -79
- jarvis/jarvis_code_agent/after_change.py +233 -0
- jarvis/jarvis_code_agent/build_validation_config.py +37 -30
- jarvis/jarvis_code_agent/builtin_rules.py +68 -0
- jarvis/jarvis_code_agent/code_agent.py +976 -1517
- jarvis/jarvis_code_agent/code_agent_build.py +227 -0
- jarvis/jarvis_code_agent/code_agent_diff.py +246 -0
- jarvis/jarvis_code_agent/code_agent_git.py +525 -0
- jarvis/jarvis_code_agent/code_agent_impact.py +177 -0
- jarvis/jarvis_code_agent/code_agent_lint.py +283 -0
- jarvis/jarvis_code_agent/code_agent_llm.py +159 -0
- jarvis/jarvis_code_agent/code_agent_postprocess.py +105 -0
- jarvis/jarvis_code_agent/code_agent_prompts.py +46 -0
- jarvis/jarvis_code_agent/code_agent_rules.py +305 -0
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +52 -48
- jarvis/jarvis_code_agent/code_analyzer/base_language.py +12 -10
- jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +12 -11
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +16 -12
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +26 -17
- jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +558 -104
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +27 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +22 -18
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +21 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +20 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +27 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +47 -23
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +71 -37
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +162 -35
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +111 -57
- jarvis/jarvis_code_agent/code_analyzer/build_validator.py +18 -12
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +185 -183
- jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +2 -1
- jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +24 -15
- jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +227 -141
- jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +321 -247
- jarvis/jarvis_code_agent/code_analyzer/language_registry.py +37 -29
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -13
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +15 -9
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +75 -45
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +87 -52
- jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +84 -51
- jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +94 -64
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +109 -71
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +97 -63
- jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +103 -69
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +271 -268
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +76 -64
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +92 -19
- jarvis/jarvis_code_agent/diff_visualizer.py +998 -0
- jarvis/jarvis_code_agent/lint.py +223 -524
- jarvis/jarvis_code_agent/rule_share_manager.py +158 -0
- jarvis/jarvis_code_agent/rules/clean_code.md +144 -0
- jarvis/jarvis_code_agent/rules/code_review.md +115 -0
- jarvis/jarvis_code_agent/rules/documentation.md +165 -0
- jarvis/jarvis_code_agent/rules/generate_rules.md +52 -0
- jarvis/jarvis_code_agent/rules/performance.md +158 -0
- jarvis/jarvis_code_agent/rules/refactoring.md +139 -0
- jarvis/jarvis_code_agent/rules/security.md +160 -0
- jarvis/jarvis_code_agent/rules/tdd.md +78 -0
- jarvis/jarvis_code_agent/test_rules/cpp_test.md +118 -0
- jarvis/jarvis_code_agent/test_rules/go_test.md +98 -0
- jarvis/jarvis_code_agent/test_rules/java_test.md +99 -0
- jarvis/jarvis_code_agent/test_rules/javascript_test.md +113 -0
- jarvis/jarvis_code_agent/test_rules/php_test.md +117 -0
- jarvis/jarvis_code_agent/test_rules/python_test.md +91 -0
- jarvis/jarvis_code_agent/test_rules/ruby_test.md +102 -0
- jarvis/jarvis_code_agent/test_rules/rust_test.md +86 -0
- jarvis/jarvis_code_agent/utils.py +36 -26
- jarvis/jarvis_code_analysis/checklists/loader.py +21 -21
- jarvis/jarvis_code_analysis/code_review.py +64 -33
- jarvis/jarvis_data/config_schema.json +285 -192
- jarvis/jarvis_git_squash/main.py +8 -6
- jarvis/jarvis_git_utils/git_commiter.py +53 -76
- jarvis/jarvis_mcp/__init__.py +5 -2
- jarvis/jarvis_mcp/sse_mcp_client.py +40 -30
- jarvis/jarvis_mcp/stdio_mcp_client.py +27 -19
- jarvis/jarvis_mcp/streamable_mcp_client.py +35 -26
- jarvis/jarvis_memory_organizer/memory_organizer.py +78 -55
- jarvis/jarvis_methodology/main.py +48 -39
- jarvis/jarvis_multi_agent/__init__.py +56 -23
- jarvis/jarvis_multi_agent/main.py +15 -18
- jarvis/jarvis_platform/base.py +179 -111
- jarvis/jarvis_platform/human.py +27 -16
- jarvis/jarvis_platform/kimi.py +52 -45
- jarvis/jarvis_platform/openai.py +101 -40
- jarvis/jarvis_platform/registry.py +51 -33
- jarvis/jarvis_platform/tongyi.py +68 -38
- jarvis/jarvis_platform/yuanbao.py +59 -43
- jarvis/jarvis_platform_manager/main.py +68 -76
- jarvis/jarvis_platform_manager/service.py +24 -14
- jarvis/jarvis_rag/README_CONFIG.md +314 -0
- jarvis/jarvis_rag/README_DYNAMIC_LOADING.md +311 -0
- jarvis/jarvis_rag/README_ONLINE_MODELS.md +230 -0
- jarvis/jarvis_rag/__init__.py +57 -4
- jarvis/jarvis_rag/cache.py +3 -1
- jarvis/jarvis_rag/cli.py +48 -68
- jarvis/jarvis_rag/embedding_interface.py +39 -0
- jarvis/jarvis_rag/embedding_manager.py +7 -230
- jarvis/jarvis_rag/embeddings/__init__.py +41 -0
- jarvis/jarvis_rag/embeddings/base.py +114 -0
- jarvis/jarvis_rag/embeddings/cohere.py +66 -0
- jarvis/jarvis_rag/embeddings/edgefn.py +117 -0
- jarvis/jarvis_rag/embeddings/local.py +260 -0
- jarvis/jarvis_rag/embeddings/openai.py +62 -0
- jarvis/jarvis_rag/embeddings/registry.py +293 -0
- jarvis/jarvis_rag/llm_interface.py +8 -6
- jarvis/jarvis_rag/query_rewriter.py +8 -9
- jarvis/jarvis_rag/rag_pipeline.py +61 -52
- jarvis/jarvis_rag/reranker.py +7 -75
- jarvis/jarvis_rag/reranker_interface.py +32 -0
- jarvis/jarvis_rag/rerankers/__init__.py +41 -0
- jarvis/jarvis_rag/rerankers/base.py +109 -0
- jarvis/jarvis_rag/rerankers/cohere.py +67 -0
- jarvis/jarvis_rag/rerankers/edgefn.py +140 -0
- jarvis/jarvis_rag/rerankers/jina.py +79 -0
- jarvis/jarvis_rag/rerankers/local.py +89 -0
- jarvis/jarvis_rag/rerankers/registry.py +293 -0
- jarvis/jarvis_rag/retriever.py +58 -43
- jarvis/jarvis_sec/__init__.py +66 -141
- jarvis/jarvis_sec/agents.py +21 -17
- jarvis/jarvis_sec/analysis.py +80 -33
- jarvis/jarvis_sec/checkers/__init__.py +7 -13
- jarvis/jarvis_sec/checkers/c_checker.py +356 -164
- jarvis/jarvis_sec/checkers/rust_checker.py +47 -29
- jarvis/jarvis_sec/cli.py +43 -21
- jarvis/jarvis_sec/clustering.py +430 -272
- jarvis/jarvis_sec/file_manager.py +99 -55
- jarvis/jarvis_sec/parsers.py +9 -6
- jarvis/jarvis_sec/prompts.py +4 -3
- jarvis/jarvis_sec/report.py +44 -22
- jarvis/jarvis_sec/review.py +180 -107
- jarvis/jarvis_sec/status.py +50 -41
- jarvis/jarvis_sec/types.py +3 -0
- jarvis/jarvis_sec/utils.py +160 -83
- jarvis/jarvis_sec/verification.py +411 -181
- jarvis/jarvis_sec/workflow.py +132 -21
- jarvis/jarvis_smart_shell/main.py +28 -41
- jarvis/jarvis_stats/cli.py +14 -12
- jarvis/jarvis_stats/stats.py +28 -19
- jarvis/jarvis_stats/storage.py +14 -8
- jarvis/jarvis_stats/visualizer.py +12 -7
- jarvis/jarvis_tools/base.py +5 -2
- jarvis/jarvis_tools/clear_memory.py +13 -9
- jarvis/jarvis_tools/cli/main.py +23 -18
- jarvis/jarvis_tools/edit_file.py +572 -873
- jarvis/jarvis_tools/execute_script.py +10 -7
- jarvis/jarvis_tools/file_analyzer.py +7 -8
- jarvis/jarvis_tools/meta_agent.py +287 -0
- jarvis/jarvis_tools/methodology.py +5 -3
- jarvis/jarvis_tools/read_code.py +305 -1438
- jarvis/jarvis_tools/read_symbols.py +50 -17
- jarvis/jarvis_tools/read_webpage.py +19 -18
- jarvis/jarvis_tools/registry.py +435 -156
- jarvis/jarvis_tools/retrieve_memory.py +16 -11
- jarvis/jarvis_tools/save_memory.py +8 -6
- jarvis/jarvis_tools/search_web.py +31 -31
- jarvis/jarvis_tools/sub_agent.py +32 -28
- jarvis/jarvis_tools/sub_code_agent.py +44 -60
- jarvis/jarvis_tools/task_list_manager.py +1811 -0
- jarvis/jarvis_tools/virtual_tty.py +29 -19
- jarvis/jarvis_utils/__init__.py +4 -0
- jarvis/jarvis_utils/builtin_replace_map.py +2 -1
- jarvis/jarvis_utils/clipboard.py +9 -8
- jarvis/jarvis_utils/collections.py +331 -0
- jarvis/jarvis_utils/config.py +699 -194
- jarvis/jarvis_utils/dialogue_recorder.py +294 -0
- jarvis/jarvis_utils/embedding.py +6 -3
- jarvis/jarvis_utils/file_processors.py +7 -1
- jarvis/jarvis_utils/fzf.py +9 -3
- jarvis/jarvis_utils/git_utils.py +71 -42
- jarvis/jarvis_utils/globals.py +116 -32
- jarvis/jarvis_utils/http.py +6 -2
- jarvis/jarvis_utils/input.py +318 -83
- jarvis/jarvis_utils/jsonnet_compat.py +119 -104
- jarvis/jarvis_utils/methodology.py +37 -28
- jarvis/jarvis_utils/output.py +201 -44
- jarvis/jarvis_utils/utils.py +986 -628
- {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/METADATA +49 -33
- jarvis_ai_assistant-1.0.2.dist-info/RECORD +304 -0
- jarvis/jarvis_code_agent/code_analyzer/structured_code.py +0 -556
- jarvis/jarvis_tools/generate_new_tool.py +0 -205
- jarvis/jarvis_tools/lsp_client.py +0 -1552
- jarvis/jarvis_tools/rewrite_file.py +0 -105
- jarvis_ai_assistant-0.7.8.dist-info/RECORD +0 -218
- {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/top_level.txt +0 -0
jarvis/jarvis_tools/registry.py
CHANGED
|
@@ -1,23 +1,36 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
from jarvis.jarvis_utils.jsonnet_compat import loads as json_loads
|
|
3
1
|
import json
|
|
4
2
|
import os
|
|
5
3
|
import re
|
|
6
4
|
import sys
|
|
7
5
|
import tempfile
|
|
8
6
|
from pathlib import Path
|
|
9
|
-
from typing import Any
|
|
7
|
+
from typing import Any
|
|
8
|
+
from typing import Callable
|
|
9
|
+
from typing import Dict
|
|
10
|
+
from typing import List
|
|
11
|
+
from typing import Optional
|
|
12
|
+
from typing import Protocol
|
|
13
|
+
from typing import Tuple
|
|
14
|
+
from typing import cast
|
|
10
15
|
|
|
11
|
-
import yaml
|
|
16
|
+
import yaml
|
|
12
17
|
|
|
13
18
|
from jarvis.jarvis_mcp import McpClient
|
|
14
19
|
from jarvis.jarvis_mcp.sse_mcp_client import SSEMcpClient
|
|
15
20
|
from jarvis.jarvis_mcp.stdio_mcp_client import StdioMcpClient
|
|
16
21
|
from jarvis.jarvis_mcp.streamable_mcp_client import StreamableMcpClient
|
|
17
22
|
from jarvis.jarvis_tools.base import Tool
|
|
18
|
-
from jarvis.jarvis_utils.config import get_data_dir
|
|
19
|
-
from jarvis.jarvis_utils.
|
|
20
|
-
from jarvis.jarvis_utils.
|
|
23
|
+
from jarvis.jarvis_utils.config import get_data_dir
|
|
24
|
+
from jarvis.jarvis_utils.config import get_tool_load_dirs
|
|
25
|
+
from jarvis.jarvis_utils.globals import get_global_model_group
|
|
26
|
+
|
|
27
|
+
# -*- coding: utf-8 -*-
|
|
28
|
+
from jarvis.jarvis_utils.jsonnet_compat import loads as json_loads
|
|
29
|
+
from jarvis.jarvis_utils.output import PrettyOutput
|
|
30
|
+
from jarvis.jarvis_utils.tag import ct
|
|
31
|
+
from jarvis.jarvis_utils.tag import ot
|
|
32
|
+
from jarvis.jarvis_utils.utils import daily_check_git_updates
|
|
33
|
+
from jarvis.jarvis_utils.utils import is_context_overflow
|
|
21
34
|
|
|
22
35
|
_multiline_example = """ {
|
|
23
36
|
"multiline_str": |||
|
|
@@ -39,8 +52,9 @@ _multiline_example = """ {
|
|
|
39
52
|
}"""
|
|
40
53
|
|
|
41
54
|
tool_call_help = f"""
|
|
42
|
-
|
|
43
|
-
|
|
55
|
+
## 工具调用指南(Markdown)
|
|
56
|
+
|
|
57
|
+
**工具调用格式(Jsonnet)**
|
|
44
58
|
{ot("TOOL_CALL")}
|
|
45
59
|
{{
|
|
46
60
|
"want": "想要从执行结果中获取到的信息",
|
|
@@ -52,7 +66,7 @@ tool_call_help = f"""
|
|
|
52
66
|
}}
|
|
53
67
|
{ct("TOOL_CALL")}
|
|
54
68
|
|
|
55
|
-
Jsonnet
|
|
69
|
+
**Jsonnet格式特性**
|
|
56
70
|
- 字符串引号:可使用双引号或单引号
|
|
57
71
|
- 多行字符串:推荐使用 ||| 或 ``` 分隔符包裹多行字符串,直接换行无需转义,支持保留缩进
|
|
58
72
|
示例:
|
|
@@ -60,15 +74,18 @@ Jsonnet格式特性:
|
|
|
60
74
|
- 尾随逗号:对象/数组最后一个元素后可添加逗号
|
|
61
75
|
- 注释:支持 // 单行或 /* */ 多行注释
|
|
62
76
|
|
|
63
|
-
|
|
77
|
+
**关键规则**
|
|
64
78
|
1. 每次只使用一个工具,等待结果后再进行下一步
|
|
65
79
|
2. {ot("TOOL_CALL")} 和 {ct("TOOL_CALL")} 必须出现在行首
|
|
66
80
|
3. 多行字符串参数推荐使用 ||| 或 ``` 分隔符包裹,直接换行无需转义,支持保留缩进
|
|
67
81
|
4. 等待执行结果,不要假设或创建虚假响应
|
|
68
82
|
5. 信息不足时询问用户,不要在没有完整信息的情况下继续
|
|
69
83
|
|
|
70
|
-
|
|
71
|
-
|
|
84
|
+
**常见错误**
|
|
85
|
+
- 同时调用多个工具
|
|
86
|
+
- 假设工具结果
|
|
87
|
+
- Jsonnet格式错误
|
|
88
|
+
- 缺少行首的开始/结束标签
|
|
72
89
|
"""
|
|
73
90
|
|
|
74
91
|
|
|
@@ -88,25 +105,22 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
88
105
|
|
|
89
106
|
def can_handle(self, response: str) -> bool:
|
|
90
107
|
# 仅当 {ot("TOOL_CALL")} 出现在行首时才认为可以处理(忽略大小写)
|
|
91
|
-
has_tool_call =
|
|
92
|
-
|
|
93
|
-
|
|
108
|
+
has_tool_call = (
|
|
109
|
+
re.search(rf"(?mi){re.escape(ot('TOOL_CALL'))}", response) is not None
|
|
110
|
+
)
|
|
94
111
|
return has_tool_call
|
|
95
112
|
|
|
96
113
|
def prompt(self) -> str:
|
|
97
114
|
"""加载工具"""
|
|
98
115
|
tools = self.get_all_tools()
|
|
99
116
|
if tools:
|
|
100
|
-
tools_prompt = "
|
|
101
|
-
tools_prompt += " <header>## 可用工具:</header>\n"
|
|
102
|
-
tools_prompt += " <tools_list>\n"
|
|
117
|
+
tools_prompt = "## 可用工具\n"
|
|
103
118
|
for tool in tools:
|
|
104
119
|
try:
|
|
105
|
-
tools_prompt += "
|
|
106
|
-
tools_prompt += f"
|
|
107
|
-
tools_prompt +=
|
|
108
|
-
tools_prompt += "
|
|
109
|
-
tools_prompt += " <json>|\n"
|
|
120
|
+
tools_prompt += f"- **名称**: {tool['name']}\n"
|
|
121
|
+
tools_prompt += f" - 描述: {tool['description']}\n"
|
|
122
|
+
tools_prompt += " - 参数:\n"
|
|
123
|
+
tools_prompt += "```json\n"
|
|
110
124
|
|
|
111
125
|
# 生成格式化的JSON参数
|
|
112
126
|
json_params = json.dumps(
|
|
@@ -118,18 +132,16 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
118
132
|
|
|
119
133
|
# 添加缩进并移除尾部空格
|
|
120
134
|
for line in json_params.split("\n"):
|
|
121
|
-
tools_prompt += f"
|
|
135
|
+
tools_prompt += f"{line.rstrip()}\n"
|
|
122
136
|
|
|
123
|
-
tools_prompt += "
|
|
124
|
-
tools_prompt += " </parameters>\n"
|
|
125
|
-
tools_prompt += " </tool>\n"
|
|
137
|
+
tools_prompt += "```\n"
|
|
126
138
|
|
|
127
139
|
except Exception as e:
|
|
128
|
-
|
|
140
|
+
PrettyOutput.auto_print(
|
|
141
|
+
f"❌ 工具 {tool['name']} 参数序列化失败: {str(e)}"
|
|
142
|
+
)
|
|
129
143
|
continue
|
|
130
144
|
|
|
131
|
-
tools_prompt += " </tools_list>\n"
|
|
132
|
-
tools_prompt += "</tools_section>\n"
|
|
133
145
|
tools_prompt += tool_call_help.rstrip() # 移除帮助文本尾部空格
|
|
134
146
|
return tools_prompt
|
|
135
147
|
return ""
|
|
@@ -137,13 +149,16 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
137
149
|
def handle(self, response: str, agent_: Any) -> Tuple[bool, Any]:
|
|
138
150
|
try:
|
|
139
151
|
# 传递agent给_extract_tool_calls,以便在解析失败时调用大模型修复
|
|
140
|
-
tool_call, err_msg, auto_completed = self._extract_tool_calls(
|
|
152
|
+
tool_call, err_msg, auto_completed = self._extract_tool_calls(
|
|
153
|
+
response, agent_
|
|
154
|
+
)
|
|
141
155
|
if err_msg:
|
|
142
156
|
# 只要工具解析错误,追加工具使用帮助信息(相当于一次 <ToolUsage>)
|
|
143
157
|
try:
|
|
144
158
|
from jarvis.jarvis_agent import Agent
|
|
145
|
-
|
|
146
|
-
|
|
159
|
+
|
|
160
|
+
agent_obj: Agent = agent_
|
|
161
|
+
tool_usage = agent_obj.get_tool_usage_prompt()
|
|
147
162
|
return False, f"{err_msg}\n\n{tool_usage}"
|
|
148
163
|
except Exception:
|
|
149
164
|
# 兼容处理:无法获取Agent或ToolUsage时,至少返回工具系统帮助信息
|
|
@@ -154,13 +169,13 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
154
169
|
result = f"检测到工具调用缺少结束标签,已自动补全{ct('TOOL_CALL')}。请确保后续工具调用包含完整的开始和结束标签。\n\n{result}"
|
|
155
170
|
return False, result
|
|
156
171
|
except Exception as e:
|
|
157
|
-
|
|
172
|
+
PrettyOutput.auto_print(f"❌ 工具调用处理失败: {str(e)}")
|
|
158
173
|
from jarvis.jarvis_agent import Agent
|
|
159
174
|
|
|
160
|
-
|
|
175
|
+
agent_final: Agent = agent_
|
|
161
176
|
return (
|
|
162
177
|
False,
|
|
163
|
-
f"工具调用处理失败: {str(e)}\n\n{
|
|
178
|
+
f"工具调用处理失败: {str(e)}\n\n{agent_final.get_tool_usage_prompt()}",
|
|
164
179
|
)
|
|
165
180
|
|
|
166
181
|
def __init__(self) -> None:
|
|
@@ -168,6 +183,8 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
168
183
|
self.tools: Dict[str, Tool] = {}
|
|
169
184
|
# 记录内置工具名称,用于区分内置工具和用户自定义工具
|
|
170
185
|
self._builtin_tool_names: set = set()
|
|
186
|
+
# 定义必选工具列表(这些工具将始终可用)
|
|
187
|
+
self._required_tools: List[str] = ["execute_script"]
|
|
171
188
|
# 加载内置工具和外部工具
|
|
172
189
|
self._load_builtin_tools()
|
|
173
190
|
self._load_external_tools()
|
|
@@ -177,9 +194,10 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
177
194
|
|
|
178
195
|
def _get_tool_stats(self) -> Dict[str, int]:
|
|
179
196
|
"""从数据目录获取工具调用统计"""
|
|
180
|
-
from jarvis.jarvis_stats.stats import StatsManager
|
|
181
197
|
from datetime import datetime
|
|
182
198
|
|
|
199
|
+
from jarvis.jarvis_stats.stats import StatsManager
|
|
200
|
+
|
|
183
201
|
# 获取所有工具的统计数据
|
|
184
202
|
tool_stats = {}
|
|
185
203
|
tools = self.get_all_tools()
|
|
@@ -221,7 +239,9 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
221
239
|
"""
|
|
222
240
|
missing_tools = [tool_name for tool_name in name if tool_name not in self.tools]
|
|
223
241
|
if missing_tools:
|
|
224
|
-
|
|
242
|
+
PrettyOutput.auto_print(
|
|
243
|
+
f"⚠️ 工具 {missing_tools} 不存在,可用的工具有: {', '.join(self.tools.keys())}"
|
|
244
|
+
)
|
|
225
245
|
self.tools = {
|
|
226
246
|
tool_name: self.tools[tool_name]
|
|
227
247
|
for tool_name in name
|
|
@@ -234,13 +254,29 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
234
254
|
参数:
|
|
235
255
|
names: 要移除的工具名称列表
|
|
236
256
|
"""
|
|
257
|
+
# 过滤掉必选工具,确保它们不会被移除
|
|
258
|
+
filtered_names = [name for name in names if name not in self._required_tools]
|
|
259
|
+
if filtered_names != names:
|
|
260
|
+
removed_required = [name for name in names if name in self._required_tools]
|
|
261
|
+
PrettyOutput.auto_print(
|
|
262
|
+
f"⚠️ 警告: 无法移除必选工具: {', '.join(removed_required)}"
|
|
263
|
+
)
|
|
237
264
|
self.tools = {
|
|
238
|
-
name: tool
|
|
265
|
+
name: tool
|
|
266
|
+
for name, tool in self.tools.items()
|
|
267
|
+
if name not in filtered_names
|
|
239
268
|
}
|
|
240
269
|
|
|
241
270
|
def _apply_tool_config_filter(self) -> None:
|
|
242
271
|
"""应用工具配置组的过滤规则"""
|
|
243
|
-
from jarvis.jarvis_utils.config import
|
|
272
|
+
from jarvis.jarvis_utils.config import get_tool_dont_use_list
|
|
273
|
+
from jarvis.jarvis_utils.config import get_tool_use_list
|
|
274
|
+
|
|
275
|
+
# 在过滤前保存必选工具的引用
|
|
276
|
+
required_tools_backup: Dict[str, Tool] = {}
|
|
277
|
+
for tool_name in self._required_tools:
|
|
278
|
+
if tool_name in self.tools:
|
|
279
|
+
required_tools_backup[tool_name] = self.tools[tool_name]
|
|
244
280
|
|
|
245
281
|
use_list = get_tool_use_list()
|
|
246
282
|
dont_use_list = get_tool_dont_use_list()
|
|
@@ -255,15 +291,27 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
255
291
|
else:
|
|
256
292
|
missing.append(tool_name)
|
|
257
293
|
if missing:
|
|
258
|
-
|
|
294
|
+
PrettyOutput.auto_print(
|
|
295
|
+
"⚠️ 警告: 配置的工具不存在: "
|
|
296
|
+
+ ", ".join(f"'{name}'" for name in missing)
|
|
297
|
+
)
|
|
259
298
|
self.tools = filtered_tools
|
|
260
299
|
|
|
261
|
-
# 如果配置了 dont_use
|
|
300
|
+
# 如果配置了 dont_use 列表,排除列表中的工具(但必选工具除外)
|
|
262
301
|
if dont_use_list:
|
|
263
302
|
for tool_name in dont_use_list:
|
|
264
|
-
if tool_name in self.tools:
|
|
303
|
+
if tool_name in self.tools and tool_name not in self._required_tools:
|
|
265
304
|
del self.tools[tool_name]
|
|
266
305
|
|
|
306
|
+
# 确保必选工具始终被包含(如果它们之前被加载过)
|
|
307
|
+
for tool_name in self._required_tools:
|
|
308
|
+
if tool_name in required_tools_backup:
|
|
309
|
+
self.tools[tool_name] = required_tools_backup[tool_name]
|
|
310
|
+
elif tool_name not in self.tools:
|
|
311
|
+
PrettyOutput.auto_print(
|
|
312
|
+
f"⚠️ 警告: 必选工具 '{tool_name}' 未加载,可能无法正常工作"
|
|
313
|
+
)
|
|
314
|
+
|
|
267
315
|
def _load_mcp_tools(self) -> None:
|
|
268
316
|
"""加载MCP工具,优先从配置获取,其次从目录扫描"""
|
|
269
317
|
from jarvis.jarvis_utils.config import get_mcp_config
|
|
@@ -281,7 +329,9 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
281
329
|
return
|
|
282
330
|
|
|
283
331
|
# 添加警告信息
|
|
284
|
-
|
|
332
|
+
PrettyOutput.auto_print(
|
|
333
|
+
"⚠️ 警告: 从文件目录加载MCP工具的方式将在未来版本中废弃,请尽快迁移到mcp配置方式"
|
|
334
|
+
)
|
|
285
335
|
|
|
286
336
|
# 遍历目录中的所有.yaml文件
|
|
287
337
|
error_lines = []
|
|
@@ -293,7 +343,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
293
343
|
except Exception as e:
|
|
294
344
|
error_lines.append(f"文件 {file_path} 加载失败: {str(e)}")
|
|
295
345
|
if error_lines:
|
|
296
|
-
|
|
346
|
+
PrettyOutput.auto_print("⚠️ " + "\n⚠️ ".join(error_lines))
|
|
297
347
|
|
|
298
348
|
def _load_builtin_tools(self) -> None:
|
|
299
349
|
"""从内置工具目录加载工具"""
|
|
@@ -306,7 +356,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
306
356
|
continue
|
|
307
357
|
|
|
308
358
|
self.register_tool_by_file(str(file_path))
|
|
309
|
-
|
|
359
|
+
|
|
310
360
|
# 记录当前已加载的工具名称为内置工具
|
|
311
361
|
self._builtin_tool_names = set(self.tools.keys())
|
|
312
362
|
|
|
@@ -335,10 +385,11 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
335
385
|
import subprocess
|
|
336
386
|
|
|
337
387
|
subprocess.run(
|
|
338
|
-
["git", "clone", central_repo, central_repo_path],
|
|
388
|
+
["git", "clone", central_repo, central_repo_path],
|
|
389
|
+
check=True,
|
|
339
390
|
)
|
|
340
391
|
except Exception as e:
|
|
341
|
-
|
|
392
|
+
PrettyOutput.auto_print(f"❌ 克隆中心工具仓库失败: {str(e)}")
|
|
342
393
|
|
|
343
394
|
# --- 全局每日更新检查 ---
|
|
344
395
|
daily_check_git_updates(tool_dirs, "tools")
|
|
@@ -367,12 +418,11 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
367
418
|
"""
|
|
368
419
|
try:
|
|
369
420
|
if "type" not in config:
|
|
370
|
-
|
|
421
|
+
PrettyOutput.auto_print(f"⚠️ 配置{config.get('name', '')}缺少type字段")
|
|
371
422
|
return False
|
|
372
423
|
|
|
373
424
|
# 检查enable标志
|
|
374
425
|
if not config.get("enable", True):
|
|
375
|
-
|
|
376
426
|
return False
|
|
377
427
|
|
|
378
428
|
name = config.get("name", "mcp")
|
|
@@ -423,18 +473,24 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
423
473
|
|
|
424
474
|
if config["type"] == "stdio":
|
|
425
475
|
if "command" not in config:
|
|
426
|
-
|
|
476
|
+
PrettyOutput.auto_print(
|
|
477
|
+
f"⚠️ 配置{config.get('name', '')}缺少command字段"
|
|
478
|
+
)
|
|
427
479
|
return False
|
|
428
480
|
elif config["type"] == "sse":
|
|
429
481
|
if "base_url" not in config:
|
|
430
|
-
|
|
482
|
+
PrettyOutput.auto_print(
|
|
483
|
+
f"⚠️ 配置{config.get('name', '')}缺少base_url字段"
|
|
484
|
+
)
|
|
431
485
|
return False
|
|
432
486
|
elif config["type"] == "streamable":
|
|
433
487
|
if "base_url" not in config:
|
|
434
|
-
|
|
488
|
+
PrettyOutput.auto_print(
|
|
489
|
+
f"⚠️ 配置{config.get('name', '')}缺少base_url字段"
|
|
490
|
+
)
|
|
435
491
|
return False
|
|
436
492
|
else:
|
|
437
|
-
|
|
493
|
+
PrettyOutput.auto_print(f"⚠️ 不支持的MCP客户端类型: {config['type']}")
|
|
438
494
|
return False
|
|
439
495
|
|
|
440
496
|
# 创建MCP客户端
|
|
@@ -451,7 +507,9 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
451
507
|
# 获取工具信息
|
|
452
508
|
tools = mcp_client.get_tool_list()
|
|
453
509
|
if not tools:
|
|
454
|
-
|
|
510
|
+
PrettyOutput.auto_print(
|
|
511
|
+
f"⚠️ 从配置{config.get('name', '')}获取工具列表失败"
|
|
512
|
+
)
|
|
455
513
|
return False
|
|
456
514
|
|
|
457
515
|
# 注册每个工具
|
|
@@ -489,7 +547,9 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
489
547
|
return True
|
|
490
548
|
|
|
491
549
|
except Exception as e:
|
|
492
|
-
|
|
550
|
+
PrettyOutput.auto_print(
|
|
551
|
+
f"⚠️ MCP配置{config.get('name', '')}加载失败: {str(e)}"
|
|
552
|
+
)
|
|
493
553
|
return False
|
|
494
554
|
|
|
495
555
|
def register_tool_by_file(self, file_path: str) -> bool:
|
|
@@ -504,7 +564,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
504
564
|
try:
|
|
505
565
|
p_file_path = Path(file_path).resolve() # 获取绝对路径
|
|
506
566
|
if not p_file_path.exists() or not p_file_path.is_file():
|
|
507
|
-
|
|
567
|
+
PrettyOutput.auto_print(f"❌ 文件不存在: {p_file_path}")
|
|
508
568
|
return False
|
|
509
569
|
|
|
510
570
|
# 临时将父目录添加到sys.path
|
|
@@ -559,37 +619,47 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
559
619
|
sys.path.remove(parent_dir)
|
|
560
620
|
|
|
561
621
|
except Exception as e:
|
|
562
|
-
|
|
622
|
+
PrettyOutput.auto_print(
|
|
623
|
+
f"❌ 从 {Path(file_path).name} 加载工具失败: {str(e)}"
|
|
624
|
+
)
|
|
563
625
|
return False
|
|
564
626
|
|
|
565
627
|
@staticmethod
|
|
566
628
|
def _has_tool_calls_block(content: str) -> bool:
|
|
567
629
|
"""从内容中提取工具调用块(仅匹配行首标签,忽略大小写)"""
|
|
568
|
-
pattern =
|
|
630
|
+
pattern = (
|
|
631
|
+
rf"(?msi){re.escape(ot('TOOL_CALL'))}(.*?)^{re.escape(ct('TOOL_CALL'))}"
|
|
632
|
+
)
|
|
569
633
|
return re.search(pattern, content) is not None
|
|
570
634
|
|
|
571
635
|
@staticmethod
|
|
572
636
|
def _get_long_response_hint(content: str) -> str:
|
|
573
637
|
"""生成长响应的提示信息
|
|
574
|
-
|
|
638
|
+
|
|
575
639
|
参数:
|
|
576
640
|
content: 响应内容
|
|
577
|
-
|
|
641
|
+
|
|
578
642
|
返回:
|
|
579
643
|
str: 如果响应较长,返回提示信息;否则返回空字符串
|
|
580
644
|
"""
|
|
581
645
|
if len(content) > 2048:
|
|
582
|
-
return
|
|
646
|
+
return (
|
|
647
|
+
"\n\n⚠️ 提示:响应内容较长(超过2048字符),可能是上下文溢出导致工具调用解析失败。"
|
|
648
|
+
"如果是修改文件(edit_file)操作,"
|
|
649
|
+
"建议分多次进行,每次处理文件的一部分。"
|
|
650
|
+
)
|
|
583
651
|
return ""
|
|
584
652
|
|
|
585
653
|
@staticmethod
|
|
586
|
-
def _extract_json_from_text(
|
|
654
|
+
def _extract_json_from_text(
|
|
655
|
+
text: str, start_pos: int = 0
|
|
656
|
+
) -> Tuple[Optional[str], int]:
|
|
587
657
|
"""从文本中提取完整的JSON对象(通过括号匹配)
|
|
588
|
-
|
|
658
|
+
|
|
589
659
|
参数:
|
|
590
660
|
text: 要提取的文本
|
|
591
661
|
start_pos: 开始搜索的位置
|
|
592
|
-
|
|
662
|
+
|
|
593
663
|
返回:
|
|
594
664
|
Tuple[Optional[str], int]:
|
|
595
665
|
- 第一个元素是提取的JSON字符串(如果找到),否则为None
|
|
@@ -597,94 +667,95 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
597
667
|
"""
|
|
598
668
|
# 跳过空白字符
|
|
599
669
|
pos = start_pos
|
|
600
|
-
while pos < len(text) and text[pos] in (
|
|
670
|
+
while pos < len(text) and text[pos] in (" ", "\t", "\n", "\r"):
|
|
601
671
|
pos += 1
|
|
602
|
-
|
|
672
|
+
|
|
603
673
|
if pos >= len(text):
|
|
604
674
|
return None, pos
|
|
605
|
-
|
|
675
|
+
|
|
606
676
|
# 检查是否以 { 开头
|
|
607
|
-
if text[pos] !=
|
|
677
|
+
if text[pos] != "{":
|
|
608
678
|
return None, pos
|
|
609
|
-
|
|
679
|
+
|
|
610
680
|
# 使用括号匹配找到完整的JSON对象
|
|
611
681
|
brace_count = 0
|
|
612
682
|
in_string = False
|
|
613
683
|
escape_next = False
|
|
614
684
|
string_char = None
|
|
615
|
-
|
|
685
|
+
|
|
616
686
|
json_start = pos
|
|
617
687
|
for i in range(pos, len(text)):
|
|
618
688
|
char = text[i]
|
|
619
|
-
|
|
689
|
+
|
|
620
690
|
if escape_next:
|
|
621
691
|
escape_next = False
|
|
622
692
|
continue
|
|
623
|
-
|
|
624
|
-
if char ==
|
|
693
|
+
|
|
694
|
+
if char == "\\":
|
|
625
695
|
escape_next = True
|
|
626
696
|
continue
|
|
627
|
-
|
|
697
|
+
|
|
628
698
|
if not in_string:
|
|
629
699
|
if char in ('"', "'"):
|
|
630
700
|
in_string = True
|
|
631
701
|
string_char = char
|
|
632
|
-
elif char ==
|
|
702
|
+
elif char == "{":
|
|
633
703
|
brace_count += 1
|
|
634
|
-
elif char ==
|
|
704
|
+
elif char == "}":
|
|
635
705
|
brace_count -= 1
|
|
636
706
|
if brace_count == 0:
|
|
637
707
|
# 找到完整的JSON对象
|
|
638
|
-
return text[json_start:i+1], i + 1
|
|
708
|
+
return text[json_start : i + 1], i + 1
|
|
639
709
|
else:
|
|
640
710
|
if char == string_char:
|
|
641
711
|
in_string = False
|
|
642
712
|
string_char = None
|
|
643
|
-
|
|
713
|
+
|
|
644
714
|
return None, len(text)
|
|
645
|
-
|
|
715
|
+
|
|
646
716
|
@staticmethod
|
|
647
717
|
def _clean_extra_markers(text: str) -> str:
|
|
648
718
|
"""清理文本中的额外标记(如 <|tool_call_end|> 等)
|
|
649
|
-
|
|
719
|
+
|
|
650
720
|
参数:
|
|
651
721
|
text: 要清理的文本
|
|
652
|
-
|
|
722
|
+
|
|
653
723
|
返回:
|
|
654
724
|
清理后的文本
|
|
655
725
|
"""
|
|
656
726
|
# 常见的额外标记模式
|
|
657
727
|
extra_markers = [
|
|
658
|
-
r
|
|
659
|
-
r
|
|
660
|
-
r
|
|
728
|
+
r"<\|tool_call_end\|>",
|
|
729
|
+
r"<\|tool_calls_section_end\|>",
|
|
730
|
+
r"<\|.*?\|>", # 匹配所有 <|...|> 格式的标记
|
|
661
731
|
]
|
|
662
|
-
|
|
732
|
+
|
|
663
733
|
cleaned = text
|
|
664
734
|
for pattern in extra_markers:
|
|
665
|
-
cleaned = re.sub(pattern,
|
|
666
|
-
|
|
735
|
+
cleaned = re.sub(pattern, "", cleaned, flags=re.IGNORECASE)
|
|
736
|
+
|
|
667
737
|
return cleaned.strip()
|
|
668
738
|
|
|
669
739
|
@staticmethod
|
|
670
740
|
def _try_llm_fix(content: str, agent: Any, error_msg: str) -> Optional[str]:
|
|
671
741
|
"""尝试使用大模型修复工具调用格式
|
|
672
|
-
|
|
742
|
+
|
|
673
743
|
参数:
|
|
674
744
|
content: 包含错误工具调用的内容
|
|
675
745
|
agent: Agent实例,用于调用大模型
|
|
676
746
|
error_msg: 错误消息
|
|
677
|
-
|
|
747
|
+
|
|
678
748
|
返回:
|
|
679
749
|
Optional[str]: 修复后的内容,如果修复失败则返回None
|
|
680
750
|
"""
|
|
681
751
|
try:
|
|
682
752
|
from jarvis.jarvis_agent import Agent
|
|
753
|
+
|
|
683
754
|
agent_instance: Agent = agent
|
|
684
|
-
|
|
755
|
+
|
|
685
756
|
# 获取工具使用说明
|
|
686
757
|
tool_usage = agent_instance.get_tool_usage_prompt()
|
|
687
|
-
|
|
758
|
+
|
|
688
759
|
# 构建修复提示
|
|
689
760
|
fix_prompt = f"""你之前的工具调用格式有误,请根据工具使用说明修复以下内容。
|
|
690
761
|
|
|
@@ -703,22 +774,75 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
703
774
|
3. 如果使用多行字符串,推荐使用 ||| 或 ``` 分隔符包裹
|
|
704
775
|
|
|
705
776
|
请直接返回修复后的完整工具调用内容,不要添加其他说明文字。"""
|
|
706
|
-
|
|
777
|
+
|
|
707
778
|
# 调用大模型修复
|
|
708
|
-
|
|
709
|
-
fixed_content = agent_instance.model.chat_until_success(fix_prompt)
|
|
710
|
-
|
|
779
|
+
PrettyOutput.auto_print("🤖 尝试使用大模型修复工具调用格式...")
|
|
780
|
+
fixed_content = agent_instance.model.chat_until_success(fix_prompt)
|
|
781
|
+
|
|
711
782
|
if fixed_content:
|
|
712
|
-
|
|
783
|
+
PrettyOutput.auto_print("✅ 大模型修复完成")
|
|
713
784
|
return fixed_content
|
|
714
785
|
else:
|
|
715
|
-
|
|
786
|
+
PrettyOutput.auto_print("❌ 大模型修复失败:返回内容为空")
|
|
716
787
|
return None
|
|
717
|
-
|
|
788
|
+
|
|
718
789
|
except Exception as e:
|
|
719
|
-
|
|
790
|
+
PrettyOutput.auto_print(f"❌ 大模型修复失败:{str(e)}")
|
|
720
791
|
return None
|
|
721
792
|
|
|
793
|
+
@staticmethod
|
|
794
|
+
def _check_and_handle_multiple_tool_calls(
|
|
795
|
+
content: str, blocks: List[str]
|
|
796
|
+
) -> Tuple[Optional[str], bool]:
|
|
797
|
+
"""检测并处理多个工具调用的情况
|
|
798
|
+
|
|
799
|
+
参数:
|
|
800
|
+
content: 包含工具调用的内容
|
|
801
|
+
blocks: 工具调用块列表
|
|
802
|
+
|
|
803
|
+
返回:
|
|
804
|
+
Tuple[Optional[str], bool]:
|
|
805
|
+
- 第一个元素:如果检测到多个工具调用,返回错误消息;否则返回None
|
|
806
|
+
- 第二个元素:是否检测到多个工具调用
|
|
807
|
+
"""
|
|
808
|
+
if len(blocks) <= 1:
|
|
809
|
+
return None, False
|
|
810
|
+
|
|
811
|
+
# 尝试解析每个块,收集所有成功解析的工具调用
|
|
812
|
+
parsed_tools = []
|
|
813
|
+
for item in blocks:
|
|
814
|
+
try:
|
|
815
|
+
cleaned_item = ToolRegistry._clean_extra_markers(item)
|
|
816
|
+
msg = json_loads(cleaned_item)
|
|
817
|
+
if "name" in msg and "arguments" in msg:
|
|
818
|
+
parsed_tools.append(msg)
|
|
819
|
+
except Exception:
|
|
820
|
+
# 如果某个块解析失败,可能是格式问题,继续检查其他块
|
|
821
|
+
pass
|
|
822
|
+
|
|
823
|
+
# 如果成功解析了多个工具调用,返回明确的错误信息
|
|
824
|
+
if len(parsed_tools) > 1:
|
|
825
|
+
tool_names = [
|
|
826
|
+
tool_call.get("name", "未知工具") for tool_call in parsed_tools
|
|
827
|
+
]
|
|
828
|
+
error_msg = f"""检测到多个工具调用(共 {len(parsed_tools)} 个),请一次只处理一个工具调用。
|
|
829
|
+
|
|
830
|
+
检测到的工具调用:
|
|
831
|
+
{chr(10).join(f" - {i + 1}. {name}" for i, name in enumerate(tool_names))}
|
|
832
|
+
|
|
833
|
+
失败原因:
|
|
834
|
+
系统要求每次只能执行一个工具调用,等待结果后再进行下一步操作。同时调用多个工具会导致:
|
|
835
|
+
1. 无法确定工具执行的顺序和依赖关系
|
|
836
|
+
2. 无法正确处理工具之间的交互
|
|
837
|
+
3. 可能导致资源竞争和状态不一致
|
|
838
|
+
|
|
839
|
+
请修改工具调用,确保每次只包含一个 {ot("TOOL_CALL")}...{ct("TOOL_CALL")} 块。
|
|
840
|
+
|
|
841
|
+
{tool_call_help}"""
|
|
842
|
+
return error_msg, True
|
|
843
|
+
|
|
844
|
+
return None, False
|
|
845
|
+
|
|
722
846
|
@staticmethod
|
|
723
847
|
def _extract_tool_calls(
|
|
724
848
|
content: str,
|
|
@@ -743,24 +867,45 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
743
867
|
close_tag = ct("TOOL_CALL")
|
|
744
868
|
# 使用正则表达式查找结束标签(忽略大小写),以获取实际位置和原始大小写
|
|
745
869
|
close_tag_pattern = re.escape(close_tag)
|
|
746
|
-
match = re.search(rf
|
|
870
|
+
match = re.search(rf"{close_tag_pattern}$", content.rstrip(), re.IGNORECASE)
|
|
747
871
|
if match:
|
|
748
872
|
pos = match.start()
|
|
749
873
|
if pos > 0 and content[pos - 1] not in ("\n", "\r"):
|
|
750
874
|
content = content[:pos] + "\n" + content[pos:]
|
|
751
875
|
|
|
752
876
|
# 首先尝试标准的提取方式(忽略大小写)
|
|
753
|
-
pattern =
|
|
877
|
+
pattern = (
|
|
878
|
+
rf"(?msi){re.escape(ot('TOOL_CALL'))}(.*?)^{re.escape(ct('TOOL_CALL'))}"
|
|
879
|
+
)
|
|
754
880
|
data = re.findall(pattern, content)
|
|
755
881
|
auto_completed = False
|
|
756
|
-
|
|
882
|
+
|
|
883
|
+
# 如果检测到多个工具调用块,先检查是否是多个独立的工具调用
|
|
884
|
+
if len(data) > 1:
|
|
885
|
+
(
|
|
886
|
+
error_msg,
|
|
887
|
+
has_multiple,
|
|
888
|
+
) = ToolRegistry._check_and_handle_multiple_tool_calls(content, data)
|
|
889
|
+
if has_multiple:
|
|
890
|
+
return (
|
|
891
|
+
cast(Dict[str, Dict[str, Any]], {}),
|
|
892
|
+
error_msg if error_msg else "",
|
|
893
|
+
False,
|
|
894
|
+
)
|
|
895
|
+
# 如果解析失败,可能是多个工具调用被当作一个 JSON 来解析了
|
|
896
|
+
# 继续执行后续的宽松提取逻辑
|
|
897
|
+
|
|
757
898
|
# 如果标准提取失败,尝试更宽松的提取方式
|
|
758
899
|
if not data:
|
|
759
900
|
# can_handle 确保 ot("TOOL_CALL") 在内容中(行首)。
|
|
760
901
|
# 如果数据为空,则表示行首的 ct("TOOL_CALL") 可能丢失。
|
|
761
|
-
has_open_at_bol =
|
|
762
|
-
|
|
763
|
-
|
|
902
|
+
has_open_at_bol = (
|
|
903
|
+
re.search(rf"(?mi){re.escape(ot('TOOL_CALL'))}", content) is not None
|
|
904
|
+
)
|
|
905
|
+
has_close_at_bol = (
|
|
906
|
+
re.search(rf"(?mi)^{re.escape(ct('TOOL_CALL'))}", content) is not None
|
|
907
|
+
)
|
|
908
|
+
|
|
764
909
|
if has_open_at_bol and not has_close_at_bol:
|
|
765
910
|
# 尝试通过附加结束标签来修复它(确保结束标签位于行首)
|
|
766
911
|
fixed_content = content.strip() + f"\n{ct('TOOL_CALL')}"
|
|
@@ -780,50 +925,104 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
780
925
|
# Even after fixing, it's not valid JSON, or user cancelled.
|
|
781
926
|
# Fall through to try more lenient extraction.
|
|
782
927
|
pass
|
|
783
|
-
|
|
928
|
+
|
|
784
929
|
# 如果仍然没有数据,尝试更宽松的提取:直接从开始标签后提取JSON
|
|
785
930
|
if not data:
|
|
931
|
+
# 先检查是否有多个工具调用块(可能被当作一个 JSON 来解析导致失败)
|
|
932
|
+
multiple_blocks = re.findall(
|
|
933
|
+
rf"(?msi){re.escape(ot('TOOL_CALL'))}(.*?){re.escape(ct('TOOL_CALL'))}",
|
|
934
|
+
content,
|
|
935
|
+
)
|
|
936
|
+
(
|
|
937
|
+
error_msg,
|
|
938
|
+
has_multiple,
|
|
939
|
+
) = ToolRegistry._check_and_handle_multiple_tool_calls(
|
|
940
|
+
content, multiple_blocks
|
|
941
|
+
)
|
|
942
|
+
if has_multiple:
|
|
943
|
+
return (
|
|
944
|
+
cast(Dict[str, Dict[str, Any]], {}),
|
|
945
|
+
error_msg if error_msg else "",
|
|
946
|
+
False,
|
|
947
|
+
)
|
|
948
|
+
|
|
786
949
|
# 找到开始标签的位置
|
|
787
|
-
open_tag_match = re.search(
|
|
950
|
+
open_tag_match = re.search(
|
|
951
|
+
rf"(?i){re.escape(ot('TOOL_CALL'))}", content
|
|
952
|
+
)
|
|
788
953
|
if open_tag_match:
|
|
789
954
|
# 从开始标签后提取JSON
|
|
790
955
|
start_pos = open_tag_match.end()
|
|
791
|
-
json_str, end_pos = ToolRegistry._extract_json_from_text(
|
|
792
|
-
|
|
956
|
+
json_str, end_pos = ToolRegistry._extract_json_from_text(
|
|
957
|
+
content, start_pos
|
|
958
|
+
)
|
|
959
|
+
|
|
793
960
|
if json_str:
|
|
794
961
|
# 清理JSON字符串中的额外标记
|
|
795
962
|
json_str = ToolRegistry._clean_extra_markers(json_str)
|
|
796
|
-
|
|
963
|
+
|
|
797
964
|
# 尝试解析JSON
|
|
798
965
|
try:
|
|
799
966
|
parsed = json_loads(json_str)
|
|
800
967
|
# 验证是否包含必要字段
|
|
801
|
-
if "name" in parsed and "arguments" in parsed
|
|
968
|
+
if "name" in parsed and "arguments" in parsed:
|
|
802
969
|
data = [json_str]
|
|
803
970
|
auto_completed = True
|
|
971
|
+
else:
|
|
972
|
+
# 记录缺少必要字段的错误
|
|
973
|
+
missing_fields = []
|
|
974
|
+
if "name" not in parsed:
|
|
975
|
+
missing_fields.append("name")
|
|
976
|
+
if "arguments" not in parsed:
|
|
977
|
+
missing_fields.append("arguments")
|
|
978
|
+
# 不立即返回错误,继续尝试其他方法,但记录信息用于后续错误提示
|
|
979
|
+
pass
|
|
804
980
|
except Exception:
|
|
805
|
-
# JSON
|
|
981
|
+
# JSON解析失败,记录错误信息用于后续错误提示
|
|
982
|
+
# 不立即返回错误,继续尝试其他方法(如大模型修复)
|
|
806
983
|
pass
|
|
807
|
-
|
|
984
|
+
else:
|
|
985
|
+
# JSON提取失败:没有找到有效的JSON对象
|
|
986
|
+
# 不立即返回错误,继续尝试其他方法(如大模型修复)
|
|
987
|
+
pass
|
|
988
|
+
|
|
808
989
|
# 如果仍然没有数据,尝试使用大模型修复
|
|
809
990
|
if not data:
|
|
810
991
|
long_hint = ToolRegistry._get_long_response_hint(content)
|
|
811
|
-
|
|
812
|
-
|
|
992
|
+
# 检查是否有开始和结束标签,生成更准确的错误消息
|
|
993
|
+
has_open = (
|
|
994
|
+
re.search(rf"(?i){re.escape(ot('TOOL_CALL'))}", content) is not None
|
|
995
|
+
)
|
|
996
|
+
has_close = (
|
|
997
|
+
re.search(rf"(?i){re.escape(ct('TOOL_CALL'))}", content) is not None
|
|
998
|
+
)
|
|
999
|
+
|
|
1000
|
+
if has_open and has_close:
|
|
1001
|
+
# 有开始和结束标签,但JSON解析失败
|
|
1002
|
+
error_msg = f"工具调用格式错误:检测到{ot('TOOL_CALL')}和{ct('TOOL_CALL')}标签,但JSON解析失败。请检查JSON格式是否正确,确保包含name和arguments字段。\n{tool_call_help}{long_hint}"
|
|
1003
|
+
elif has_open and not has_close:
|
|
1004
|
+
# 只有开始标签,没有结束标签
|
|
1005
|
+
error_msg = f"工具调用格式错误:检测到{ot('TOOL_CALL')}标签,但未找到{ct('TOOL_CALL')}标签。请确保工具调用包含完整的开始和结束标签。\n{tool_call_help}{long_hint}"
|
|
1006
|
+
else:
|
|
1007
|
+
# 其他情况
|
|
1008
|
+
error_msg = f"工具调用格式错误:无法解析工具调用内容。请检查工具调用格式。\n{tool_call_help}{long_hint}"
|
|
1009
|
+
|
|
813
1010
|
# 如果提供了agent且long_hint为空,尝试使用大模型修复
|
|
814
1011
|
if agent is not None and not long_hint:
|
|
815
|
-
|
|
816
|
-
|
|
1012
|
+
llm_fixed_content: Optional[str] = ToolRegistry._try_llm_fix(
|
|
1013
|
+
content, agent, error_msg
|
|
1014
|
+
)
|
|
1015
|
+
if llm_fixed_content is not None:
|
|
817
1016
|
# 递归调用自身,尝试解析修复后的内容
|
|
818
1017
|
return ToolRegistry._extract_tool_calls(fixed_content, None)
|
|
819
|
-
|
|
1018
|
+
|
|
820
1019
|
# 如果大模型修复失败或未提供agent或long_hint不为空,返回错误
|
|
821
1020
|
return (
|
|
822
1021
|
{},
|
|
823
1022
|
error_msg,
|
|
824
1023
|
False,
|
|
825
1024
|
)
|
|
826
|
-
|
|
1025
|
+
|
|
827
1026
|
ret = []
|
|
828
1027
|
for item in data:
|
|
829
1028
|
try:
|
|
@@ -831,20 +1030,46 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
831
1030
|
cleaned_item = ToolRegistry._clean_extra_markers(item)
|
|
832
1031
|
msg = json_loads(cleaned_item)
|
|
833
1032
|
except Exception as e:
|
|
1033
|
+
# 如果解析失败,先检查是否是因为有多个工具调用
|
|
1034
|
+
# 检查错误信息中是否包含 "expected a comma" 或类似的多对象错误
|
|
1035
|
+
error_str = str(e).lower()
|
|
1036
|
+
if "expected a comma" in error_str or "multiple" in error_str:
|
|
1037
|
+
# 尝试检测是否有多个工具调用块
|
|
1038
|
+
multiple_blocks = re.findall(
|
|
1039
|
+
rf"(?msi){re.escape(ot('TOOL_CALL'))}(.*?){re.escape(ct('TOOL_CALL'))}",
|
|
1040
|
+
content,
|
|
1041
|
+
)
|
|
1042
|
+
(
|
|
1043
|
+
error_msg,
|
|
1044
|
+
has_multiple,
|
|
1045
|
+
) = ToolRegistry._check_and_handle_multiple_tool_calls(
|
|
1046
|
+
content, multiple_blocks
|
|
1047
|
+
)
|
|
1048
|
+
if has_multiple:
|
|
1049
|
+
return (
|
|
1050
|
+
cast(Dict[str, Dict[str, Any]], {}),
|
|
1051
|
+
error_msg if error_msg else "",
|
|
1052
|
+
False,
|
|
1053
|
+
)
|
|
1054
|
+
|
|
834
1055
|
long_hint = ToolRegistry._get_long_response_hint(content)
|
|
835
1056
|
error_msg = f"""Jsonnet 解析失败:{e}
|
|
836
1057
|
|
|
837
1058
|
提示:Jsonnet支持双引号/单引号、尾随逗号、注释。多行字符串推荐使用 ||| 或 ``` 分隔符包裹,直接换行无需转义,支持保留缩进。
|
|
838
1059
|
|
|
839
1060
|
{tool_call_help}{long_hint}"""
|
|
840
|
-
|
|
1061
|
+
|
|
841
1062
|
# 如果提供了agent且long_hint为空,尝试使用大模型修复
|
|
842
1063
|
if agent is not None and not long_hint:
|
|
843
|
-
|
|
844
|
-
|
|
1064
|
+
retry_fixed_content: Optional[str] = ToolRegistry._try_llm_fix(
|
|
1065
|
+
content, agent, error_msg
|
|
1066
|
+
)
|
|
1067
|
+
if retry_fixed_content is not None:
|
|
845
1068
|
# 递归调用自身,尝试解析修复后的内容
|
|
846
|
-
return ToolRegistry._extract_tool_calls(
|
|
847
|
-
|
|
1069
|
+
return ToolRegistry._extract_tool_calls(
|
|
1070
|
+
retry_fixed_content, None
|
|
1071
|
+
)
|
|
1072
|
+
|
|
848
1073
|
# 如果大模型修复失败或未提供agent或long_hint不为空,返回错误
|
|
849
1074
|
return (
|
|
850
1075
|
{},
|
|
@@ -852,21 +1077,23 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
852
1077
|
False,
|
|
853
1078
|
)
|
|
854
1079
|
|
|
855
|
-
if "name" in msg and "arguments" in msg
|
|
1080
|
+
if "name" in msg and "arguments" in msg:
|
|
856
1081
|
ret.append(msg)
|
|
857
1082
|
else:
|
|
858
1083
|
long_hint = ToolRegistry._get_long_response_hint(content)
|
|
859
|
-
error_msg = f"""工具调用格式错误,请检查工具调用格式(缺少name、arguments
|
|
1084
|
+
error_msg = f"""工具调用格式错误,请检查工具调用格式(缺少name、arguments字段)。
|
|
860
1085
|
|
|
861
1086
|
{tool_call_help}{long_hint}"""
|
|
862
|
-
|
|
1087
|
+
|
|
863
1088
|
# 如果提供了agent且long_hint为空,尝试使用大模型修复
|
|
864
1089
|
if agent is not None and not long_hint:
|
|
865
|
-
|
|
866
|
-
|
|
1090
|
+
fixed_content_3: Optional[str] = ToolRegistry._try_llm_fix(
|
|
1091
|
+
content, agent, error_msg
|
|
1092
|
+
)
|
|
1093
|
+
if fixed_content_3 is not None:
|
|
867
1094
|
# 递归调用自身,尝试解析修复后的内容
|
|
868
|
-
return ToolRegistry._extract_tool_calls(
|
|
869
|
-
|
|
1095
|
+
return ToolRegistry._extract_tool_calls(fixed_content_3, None)
|
|
1096
|
+
|
|
870
1097
|
# 如果大模型修复失败或未提供agent或long_hint不为空,返回错误
|
|
871
1098
|
return (
|
|
872
1099
|
{},
|
|
@@ -894,7 +1121,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
894
1121
|
func: 工具执行函数
|
|
895
1122
|
"""
|
|
896
1123
|
if name in self.tools:
|
|
897
|
-
|
|
1124
|
+
PrettyOutput.auto_print(f"⚠️ 警告: 工具 '{name}' 已存在,将被覆盖")
|
|
898
1125
|
self.tools[name] = Tool(name, description, parameters, func, protocol_version)
|
|
899
1126
|
|
|
900
1127
|
def get_tool(self, name: str) -> Optional[Tool]:
|
|
@@ -956,7 +1183,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
956
1183
|
try:
|
|
957
1184
|
if getattr(tool, "protocol_version", "1.0") == "2.0":
|
|
958
1185
|
# v2.0: agent与参数分离传递
|
|
959
|
-
return tool.func(arguments, agent)
|
|
1186
|
+
return cast(Dict[str, Any], tool.func(arguments, agent))
|
|
960
1187
|
else:
|
|
961
1188
|
# v1.0: 兼容旧实现,将agent注入到arguments(如果提供)
|
|
962
1189
|
args_to_call = arguments.copy() if isinstance(arguments, dict) else {}
|
|
@@ -999,7 +1226,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
999
1226
|
"""
|
|
1000
1227
|
if len(output.splitlines()) > 60:
|
|
1001
1228
|
lines = output.splitlines()
|
|
1002
|
-
|
|
1229
|
+
PrettyOutput.auto_print("⚠️ 输出太长,截取前后30行")
|
|
1003
1230
|
return "\n".join(
|
|
1004
1231
|
lines[:30] + ["\n...内容太长,已截取前后30行...\n"] + lines[-30:]
|
|
1005
1232
|
)
|
|
@@ -1009,7 +1236,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
1009
1236
|
try:
|
|
1010
1237
|
name = tool_call["name"] # 确保name是str类型
|
|
1011
1238
|
args = tool_call["arguments"] # 原始参数(来自外部协议)
|
|
1012
|
-
want = tool_call
|
|
1239
|
+
want = tool_call.get("want", "")
|
|
1013
1240
|
|
|
1014
1241
|
from jarvis.jarvis_agent import Agent
|
|
1015
1242
|
|
|
@@ -1025,31 +1252,82 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
1025
1252
|
usage_prompt = agent_instance.get_tool_usage_prompt()
|
|
1026
1253
|
except Exception:
|
|
1027
1254
|
usage_prompt = tool_call_help
|
|
1028
|
-
|
|
1255
|
+
PrettyOutput.auto_print("❌ 工具参数格式无效")
|
|
1029
1256
|
return f"工具参数格式无效: {name}。arguments 应为可解析的 Jsonnet 或对象,请按工具调用格式提供。\n提示:对于多行字符串参数,推荐使用 ||| 或 ``` 分隔符包裹,直接换行无需转义,支持保留缩进。\n\n{usage_prompt}"
|
|
1030
1257
|
|
|
1031
|
-
|
|
1258
|
+
# 生成参数摘要,过滤敏感信息
|
|
1259
|
+
param_summary = ""
|
|
1260
|
+
if isinstance(args, dict) and args:
|
|
1261
|
+
# 敏感字段列表
|
|
1262
|
+
sensitive_keys = {
|
|
1263
|
+
"password",
|
|
1264
|
+
"token",
|
|
1265
|
+
"key",
|
|
1266
|
+
"secret",
|
|
1267
|
+
"auth",
|
|
1268
|
+
"credential",
|
|
1269
|
+
}
|
|
1270
|
+
summary_parts = []
|
|
1271
|
+
|
|
1272
|
+
for key, value in args.items():
|
|
1273
|
+
if key.lower() in sensitive_keys:
|
|
1274
|
+
summary_parts.append(f"{key}='***'")
|
|
1275
|
+
elif isinstance(value, (dict, list)):
|
|
1276
|
+
# 复杂类型简化为类型信息
|
|
1277
|
+
summary_parts.append(
|
|
1278
|
+
f"{key}={type(value).__name__}({len(value)} items)"
|
|
1279
|
+
)
|
|
1280
|
+
elif isinstance(value, str) and len(value) > 50:
|
|
1281
|
+
# 长字符串截断
|
|
1282
|
+
summary_parts.append(f"{key}='{value[:47]}...'")
|
|
1283
|
+
else:
|
|
1284
|
+
summary_parts.append(f"{key}={repr(value)}")
|
|
1285
|
+
|
|
1286
|
+
if summary_parts:
|
|
1287
|
+
# 将参数值中的换行符替换为空格,避免摘要中出现换行
|
|
1288
|
+
cleaned_parts = [
|
|
1289
|
+
part.replace("\n", " ").replace("\r", " ")
|
|
1290
|
+
for part in summary_parts
|
|
1291
|
+
]
|
|
1292
|
+
param_summary = " | ".join(cleaned_parts)
|
|
1293
|
+
|
|
1294
|
+
# 合并为一行输出:执行工具调用和参数摘要
|
|
1295
|
+
if param_summary:
|
|
1296
|
+
PrettyOutput.auto_print(f"🛠️ 执行工具调用 {name} [{param_summary}]")
|
|
1297
|
+
else:
|
|
1298
|
+
PrettyOutput.auto_print(f"🛠️ 执行工具调用 {name}")
|
|
1299
|
+
|
|
1032
1300
|
# 执行工具调用(根据工具实现的协议版本,由系统在内部决定agent的传递方式)
|
|
1033
1301
|
result = self.execute_tool(name, args, agent)
|
|
1034
|
-
|
|
1302
|
+
|
|
1035
1303
|
# 打印执行状态
|
|
1036
1304
|
if result.get("success", False):
|
|
1037
|
-
|
|
1305
|
+
PrettyOutput.auto_print(f"✅ 执行工具调用 {name} 成功")
|
|
1038
1306
|
else:
|
|
1039
|
-
|
|
1307
|
+
# 获取失败原因
|
|
1308
|
+
stderr = result.get("stderr", "")
|
|
1309
|
+
stdout = result.get("stdout", "")
|
|
1310
|
+
error_msg = stderr if stderr else (stdout if stdout else "未知错误")
|
|
1311
|
+
PrettyOutput.auto_print(f"❌ 执行工具调用 {name} 失败")
|
|
1312
|
+
PrettyOutput.auto_print(f" 失败原因: {error_msg}")
|
|
1040
1313
|
|
|
1041
1314
|
# 记录本轮实际执行的工具,供上层逻辑(如记忆保存判定)使用
|
|
1042
1315
|
try:
|
|
1043
1316
|
from jarvis.jarvis_agent import Agent # 延迟导入避免循环依赖
|
|
1317
|
+
|
|
1044
1318
|
agent_instance_for_record: Agent = agent_instance
|
|
1045
1319
|
# 记录最后一次执行的工具
|
|
1046
|
-
agent_instance_for_record.set_user_data("__last_executed_tool__", name)
|
|
1320
|
+
agent_instance_for_record.set_user_data("__last_executed_tool__", name)
|
|
1047
1321
|
# 记录本轮累计执行的工具列表
|
|
1048
|
-
executed_list = agent_instance_for_record.get_user_data(
|
|
1322
|
+
executed_list = agent_instance_for_record.get_user_data(
|
|
1323
|
+
"__executed_tools__"
|
|
1324
|
+
)
|
|
1049
1325
|
if not isinstance(executed_list, list):
|
|
1050
1326
|
executed_list = []
|
|
1051
1327
|
executed_list.append(name)
|
|
1052
|
-
agent_instance_for_record.set_user_data(
|
|
1328
|
+
agent_instance_for_record.set_user_data(
|
|
1329
|
+
"__executed_tools__", executed_list
|
|
1330
|
+
)
|
|
1053
1331
|
except Exception:
|
|
1054
1332
|
pass
|
|
1055
1333
|
|
|
@@ -1059,7 +1337,9 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
1059
1337
|
usage_prompt = agent_instance.get_tool_usage_prompt()
|
|
1060
1338
|
except Exception:
|
|
1061
1339
|
usage_prompt = tool_call_help
|
|
1062
|
-
err_output = self._format_tool_output(
|
|
1340
|
+
err_output = self._format_tool_output(
|
|
1341
|
+
result.get("stdout", ""), result.get("stderr", "")
|
|
1342
|
+
)
|
|
1063
1343
|
return f"{err_output}\n\n{usage_prompt}"
|
|
1064
1344
|
|
|
1065
1345
|
# 格式化输出
|
|
@@ -1068,11 +1348,9 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
1068
1348
|
)
|
|
1069
1349
|
|
|
1070
1350
|
# 检查内容是否过大
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
if agent_instance.model
|
|
1074
|
-
model_group = agent_instance.model.model_group
|
|
1075
|
-
platform = agent_instance.model
|
|
1351
|
+
# 使用全局模型组(不再从 agent 继承)
|
|
1352
|
+
model_group = get_global_model_group()
|
|
1353
|
+
platform = agent_instance.model if agent_instance.model else None
|
|
1076
1354
|
is_large_content = is_context_overflow(output, model_group, platform)
|
|
1077
1355
|
|
|
1078
1356
|
if is_large_content:
|
|
@@ -1106,7 +1384,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
1106
1384
|
</content>
|
|
1107
1385
|
|
|
1108
1386
|
上传的文件是以下工具执行结果:
|
|
1109
|
-
{json.dumps({"name":name, "arguments":args, "want":want}, ensure_ascii=False, indent=2)}
|
|
1387
|
+
{json.dumps({"name": name, "arguments": args, "want": want}, ensure_ascii=False, indent=2)}
|
|
1110
1388
|
|
|
1111
1389
|
请根据以上信息,继续完成任务。
|
|
1112
1390
|
"""
|
|
@@ -1126,17 +1404,18 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
|
1126
1404
|
# 尝试获取工具名称(如果已定义)
|
|
1127
1405
|
tool_name = ""
|
|
1128
1406
|
try:
|
|
1129
|
-
if
|
|
1407
|
+
if "name" in locals():
|
|
1130
1408
|
tool_name = name
|
|
1131
1409
|
except Exception:
|
|
1132
1410
|
pass
|
|
1133
1411
|
if tool_name:
|
|
1134
|
-
|
|
1412
|
+
PrettyOutput.auto_print(f"❌ 执行工具调用 {tool_name} 失败:{str(e)}")
|
|
1135
1413
|
else:
|
|
1136
|
-
|
|
1414
|
+
PrettyOutput.auto_print(f"❌ 工具调用失败:{str(e)}")
|
|
1137
1415
|
try:
|
|
1138
1416
|
from jarvis.jarvis_agent import Agent # 延迟导入避免循环依赖
|
|
1139
|
-
|
|
1417
|
+
|
|
1418
|
+
agent_instance_for_prompt: Agent = agent
|
|
1140
1419
|
usage_prompt = agent_instance_for_prompt.get_tool_usage_prompt()
|
|
1141
1420
|
except Exception:
|
|
1142
1421
|
usage_prompt = tool_call_help
|