jarvis-ai-assistant 0.3.30__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 +458 -152
- jarvis/jarvis_agent/agent_manager.py +17 -13
- 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 +329 -0
- jarvis/jarvis_agent/file_methodology_manager.py +3 -4
- jarvis/jarvis_agent/jarvis.py +628 -55
- 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 +34 -10
- 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 +105 -9
- jarvis/jarvis_agent/session_manager.py +2 -3
- jarvis/jarvis_agent/share_manager.py +20 -22
- jarvis/jarvis_agent/shell_input_handler.py +1 -2
- jarvis/jarvis_agent/stdio_redirect.py +295 -0
- jarvis/jarvis_agent/task_analyzer.py +31 -6
- 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/utils.py +5 -1
- jarvis/jarvis_agent/web_bridge.py +189 -0
- jarvis/jarvis_agent/web_output_sink.py +53 -0
- jarvis/jarvis_agent/web_server.py +786 -0
- jarvis/jarvis_c2rust/__init__.py +26 -0
- jarvis/jarvis_c2rust/cli.py +575 -0
- jarvis/jarvis_c2rust/collector.py +250 -0
- jarvis/jarvis_c2rust/constants.py +26 -0
- jarvis/jarvis_c2rust/library_replacer.py +1254 -0
- jarvis/jarvis_c2rust/llm_module_agent.py +1272 -0
- jarvis/jarvis_c2rust/loaders.py +207 -0
- jarvis/jarvis_c2rust/models.py +28 -0
- jarvis/jarvis_c2rust/optimizer.py +2157 -0
- jarvis/jarvis_c2rust/scanner.py +1681 -0
- jarvis/jarvis_c2rust/transpiler.py +2983 -0
- jarvis/jarvis_c2rust/utils.py +385 -0
- jarvis/jarvis_code_agent/build_validation_config.py +132 -0
- jarvis/jarvis_code_agent/code_agent.py +1371 -220
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +65 -0
- jarvis/jarvis_code_agent/code_analyzer/base_language.py +74 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +44 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +106 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +74 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +125 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +72 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +70 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +53 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +47 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +61 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +110 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +154 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +110 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +153 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator.py +43 -0
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +648 -0
- jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +18 -0
- jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +132 -0
- jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +330 -0
- jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +781 -0
- jarvis/jarvis_code_agent/code_analyzer/language_registry.py +185 -0
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +110 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +49 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +299 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +215 -0
- 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 +269 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +281 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +605 -0
- jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +252 -0
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +58 -0
- jarvis/jarvis_code_agent/lint.py +501 -8
- jarvis/jarvis_code_agent/utils.py +141 -0
- jarvis/jarvis_code_analysis/code_review.py +493 -584
- jarvis/jarvis_data/config_schema.json +128 -12
- jarvis/jarvis_git_squash/main.py +4 -5
- jarvis/jarvis_git_utils/git_commiter.py +82 -75
- jarvis/jarvis_mcp/sse_mcp_client.py +22 -29
- jarvis/jarvis_mcp/stdio_mcp_client.py +12 -13
- 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 +287 -55
- jarvis/jarvis_multi_agent/main.py +36 -4
- jarvis/jarvis_platform/base.py +524 -202
- jarvis/jarvis_platform/human.py +7 -8
- jarvis/jarvis_platform/kimi.py +30 -36
- jarvis/jarvis_platform/openai.py +88 -25
- jarvis/jarvis_platform/registry.py +26 -10
- jarvis/jarvis_platform/tongyi.py +24 -25
- jarvis/jarvis_platform/yuanbao.py +32 -43
- jarvis/jarvis_platform_manager/main.py +66 -77
- jarvis/jarvis_platform_manager/service.py +8 -13
- jarvis/jarvis_rag/cli.py +53 -55
- 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 +305 -0
- jarvis/jarvis_sec/agents.py +143 -0
- jarvis/jarvis_sec/analysis.py +276 -0
- jarvis/jarvis_sec/checkers/__init__.py +32 -0
- jarvis/jarvis_sec/checkers/c_checker.py +2680 -0
- jarvis/jarvis_sec/checkers/rust_checker.py +1108 -0
- jarvis/jarvis_sec/cli.py +139 -0
- 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 +336 -0
- jarvis/jarvis_sec/review.py +453 -0
- jarvis/jarvis_sec/status.py +264 -0
- jarvis/jarvis_sec/types.py +20 -0
- jarvis/jarvis_sec/utils.py +499 -0
- jarvis/jarvis_sec/verification.py +848 -0
- jarvis/jarvis_sec/workflow.py +226 -0
- jarvis/jarvis_smart_shell/main.py +38 -87
- jarvis/jarvis_stats/cli.py +2 -2
- jarvis/jarvis_stats/stats.py +8 -8
- jarvis/jarvis_stats/storage.py +15 -21
- jarvis/jarvis_stats/visualizer.py +1 -1
- jarvis/jarvis_tools/clear_memory.py +3 -20
- jarvis/jarvis_tools/cli/main.py +21 -23
- jarvis/jarvis_tools/edit_file.py +1019 -132
- jarvis/jarvis_tools/execute_script.py +83 -25
- jarvis/jarvis_tools/file_analyzer.py +6 -9
- jarvis/jarvis_tools/generate_new_tool.py +14 -21
- jarvis/jarvis_tools/lsp_client.py +1552 -0
- jarvis/jarvis_tools/methodology.py +2 -3
- jarvis/jarvis_tools/read_code.py +1736 -35
- jarvis/jarvis_tools/read_symbols.py +140 -0
- jarvis/jarvis_tools/read_webpage.py +12 -13
- jarvis/jarvis_tools/registry.py +427 -200
- jarvis/jarvis_tools/retrieve_memory.py +20 -19
- jarvis/jarvis_tools/rewrite_file.py +72 -158
- jarvis/jarvis_tools/save_memory.py +3 -15
- jarvis/jarvis_tools/search_web.py +18 -18
- jarvis/jarvis_tools/sub_agent.py +36 -43
- jarvis/jarvis_tools/sub_code_agent.py +25 -26
- jarvis/jarvis_tools/virtual_tty.py +55 -33
- jarvis/jarvis_utils/clipboard.py +7 -10
- jarvis/jarvis_utils/config.py +232 -45
- jarvis/jarvis_utils/embedding.py +8 -5
- jarvis/jarvis_utils/fzf.py +8 -8
- jarvis/jarvis_utils/git_utils.py +225 -36
- jarvis/jarvis_utils/globals.py +3 -3
- jarvis/jarvis_utils/http.py +1 -1
- jarvis/jarvis_utils/input.py +99 -48
- jarvis/jarvis_utils/jsonnet_compat.py +465 -0
- jarvis/jarvis_utils/methodology.py +52 -48
- jarvis/jarvis_utils/utils.py +819 -491
- jarvis_ai_assistant-0.7.6.dist-info/METADATA +600 -0
- jarvis_ai_assistant-0.7.6.dist-info/RECORD +218 -0
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/entry_points.txt +4 -0
- jarvis/jarvis_agent/config.py +0 -92
- jarvis/jarvis_agent/edit_file_handler.py +0 -296
- jarvis/jarvis_platform/ai8.py +0 -332
- jarvis/jarvis_tools/ask_user.py +0 -54
- jarvis_ai_assistant-0.3.30.dist-info/METADATA +0 -381
- jarvis_ai_assistant-0.3.30.dist-info/RECORD +0 -137
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/top_level.txt +0 -0
jarvis/jarvis_agent/__init__.py
CHANGED
|
@@ -7,7 +7,8 @@ import re
|
|
|
7
7
|
import sys
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from enum import Enum
|
|
10
|
-
from typing import Any, Callable, Dict, List, Optional,
|
|
10
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
|
11
|
+
|
|
11
12
|
|
|
12
13
|
# 第三方库导入
|
|
13
14
|
from rich.align import Align
|
|
@@ -28,12 +29,11 @@ from jarvis.jarvis_agent.file_methodology_manager import FileMethodologyManager
|
|
|
28
29
|
from jarvis.jarvis_agent.prompts import (
|
|
29
30
|
DEFAULT_SUMMARY_PROMPT,
|
|
30
31
|
SUMMARY_REQUEST_PROMPT,
|
|
31
|
-
TASK_ANALYSIS_PROMPT,
|
|
32
|
+
TASK_ANALYSIS_PROMPT, # noqa: F401
|
|
32
33
|
)
|
|
33
34
|
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
34
35
|
from jarvis.jarvis_agent.prompt_manager import PromptManager
|
|
35
36
|
from jarvis.jarvis_agent.event_bus import EventBus
|
|
36
|
-
from jarvis.jarvis_agent.config import AgentConfig
|
|
37
37
|
from jarvis.jarvis_agent.run_loop import AgentRunLoop
|
|
38
38
|
from jarvis.jarvis_agent.events import (
|
|
39
39
|
BEFORE_SUMMARY,
|
|
@@ -54,6 +54,9 @@ from jarvis.jarvis_agent.events import (
|
|
|
54
54
|
from jarvis.jarvis_agent.user_interaction import UserInteractionHandler
|
|
55
55
|
from jarvis.jarvis_agent.utils import join_prompts
|
|
56
56
|
from jarvis.jarvis_utils.methodology import _load_all_methodologies
|
|
57
|
+
from jarvis.jarvis_agent.shell_input_handler import shell_input_handler
|
|
58
|
+
from jarvis.jarvis_agent.file_context_handler import file_context_handler
|
|
59
|
+
from jarvis.jarvis_agent.builtin_input_handler import builtin_input_handler
|
|
57
60
|
|
|
58
61
|
# jarvis_platform 相关
|
|
59
62
|
from jarvis.jarvis_platform.base import BasePlatform
|
|
@@ -62,7 +65,6 @@ from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
|
62
65
|
# jarvis_utils 相关
|
|
63
66
|
from jarvis.jarvis_utils.config import (
|
|
64
67
|
get_data_dir,
|
|
65
|
-
get_max_token_count,
|
|
66
68
|
get_normal_model_name,
|
|
67
69
|
get_normal_platform_name,
|
|
68
70
|
is_execute_tool_confirm,
|
|
@@ -71,17 +73,19 @@ from jarvis.jarvis_utils.config import (
|
|
|
71
73
|
is_use_methodology,
|
|
72
74
|
get_tool_filter_threshold,
|
|
73
75
|
get_after_tool_call_cb_dirs,
|
|
76
|
+
get_addon_prompt_threshold,
|
|
77
|
+
is_enable_memory_organizer,
|
|
74
78
|
)
|
|
75
79
|
from jarvis.jarvis_utils.embedding import get_context_token_count
|
|
76
80
|
from jarvis.jarvis_utils.globals import (
|
|
77
81
|
delete_agent,
|
|
78
82
|
get_interrupt,
|
|
83
|
+
get_short_term_memories,
|
|
79
84
|
make_agent_name,
|
|
80
85
|
set_agent,
|
|
81
86
|
set_interrupt,
|
|
82
87
|
)
|
|
83
88
|
from jarvis.jarvis_utils.input import get_multiline_input, user_confirm
|
|
84
|
-
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
85
89
|
from jarvis.jarvis_utils.tag import ot
|
|
86
90
|
|
|
87
91
|
|
|
@@ -130,6 +134,10 @@ def show_agent_startup_stats(
|
|
|
130
134
|
if project_memory_dir.exists():
|
|
131
135
|
project_memory_count = len(list(project_memory_dir.glob("*.json")))
|
|
132
136
|
|
|
137
|
+
# 检查短期记忆
|
|
138
|
+
short_term_memories = get_short_term_memories()
|
|
139
|
+
short_term_memory_count = len(short_term_memories) if short_term_memories else 0
|
|
140
|
+
|
|
133
141
|
# 获取当前工作目录
|
|
134
142
|
current_dir = os.getcwd()
|
|
135
143
|
|
|
@@ -149,6 +157,12 @@ def show_agent_startup_stats(
|
|
|
149
157
|
f"📝 项目记忆: [bold magenta]{project_memory_count}[/bold magenta]"
|
|
150
158
|
)
|
|
151
159
|
|
|
160
|
+
# 如果有短期记忆,添加到统计信息中
|
|
161
|
+
if short_term_memory_count > 0:
|
|
162
|
+
stats_parts.append(
|
|
163
|
+
f"💭 短期记忆: [bold blue]{short_term_memory_count}[/bold blue]"
|
|
164
|
+
)
|
|
165
|
+
|
|
152
166
|
stats_text = Text.from_markup(" | ".join(stats_parts), justify="center")
|
|
153
167
|
|
|
154
168
|
# 创建包含欢迎信息和统计信息的面板内容
|
|
@@ -172,7 +186,7 @@ def show_agent_startup_stats(
|
|
|
172
186
|
console.print(Align.center(panel))
|
|
173
187
|
|
|
174
188
|
except Exception as e:
|
|
175
|
-
|
|
189
|
+
print(f"⚠️ 加载统计信息失败: {e}")
|
|
176
190
|
|
|
177
191
|
|
|
178
192
|
origin_agent_system_prompt = f"""
|
|
@@ -243,9 +257,34 @@ class Agent:
|
|
|
243
257
|
def clear_history(self):
|
|
244
258
|
"""
|
|
245
259
|
Clears the current conversation history by delegating to the session manager.
|
|
260
|
+
直接调用关键流程函数,事件总线仅用于非关键流程(如日志、监控等)。
|
|
246
261
|
"""
|
|
262
|
+
# 关键流程:直接调用 memory_manager 确保记忆提示
|
|
263
|
+
try:
|
|
264
|
+
self.memory_manager._ensure_memory_prompt(agent=self)
|
|
265
|
+
except Exception:
|
|
266
|
+
pass
|
|
267
|
+
|
|
268
|
+
# 非关键流程:广播清理历史前事件(用于日志、监控等)
|
|
269
|
+
try:
|
|
270
|
+
self.event_bus.emit(BEFORE_HISTORY_CLEAR, agent=self)
|
|
271
|
+
except Exception:
|
|
272
|
+
pass
|
|
273
|
+
|
|
274
|
+
# 清理会话历史并重置模型状态
|
|
247
275
|
self.session.clear_history()
|
|
248
|
-
#
|
|
276
|
+
# 重置 addon_prompt 跳过轮数计数器
|
|
277
|
+
self._addon_prompt_skip_rounds = 0
|
|
278
|
+
# 重置没有工具调用的计数器
|
|
279
|
+
self._no_tool_call_count = 0
|
|
280
|
+
|
|
281
|
+
# 重置后重新设置系统提示词,确保系统约束仍然生效
|
|
282
|
+
try:
|
|
283
|
+
self._setup_system_prompt()
|
|
284
|
+
except Exception:
|
|
285
|
+
pass
|
|
286
|
+
|
|
287
|
+
# 非关键流程:广播清理历史后的事件(用于日志、监控等)
|
|
249
288
|
try:
|
|
250
289
|
self.event_bus.emit(AFTER_HISTORY_CLEAR, agent=self)
|
|
251
290
|
except Exception:
|
|
@@ -264,6 +303,21 @@ class Agent:
|
|
|
264
303
|
"""获取工具使用提示"""
|
|
265
304
|
return build_action_prompt(self.output_handler) # type: ignore
|
|
266
305
|
|
|
306
|
+
def __new__(cls, *args, **kwargs):
|
|
307
|
+
if kwargs.get("agent_type") == "code":
|
|
308
|
+
try:
|
|
309
|
+
from jarvis.jarvis_code_agent.code_agent import CodeAgent
|
|
310
|
+
except ImportError as e:
|
|
311
|
+
raise RuntimeError(
|
|
312
|
+
"CodeAgent could not be imported. Please ensure jarvis_code_agent is installed correctly."
|
|
313
|
+
) from e
|
|
314
|
+
|
|
315
|
+
# 移除 agent_type 避免无限循环,并传递所有其他参数
|
|
316
|
+
kwargs.pop("agent_type", None)
|
|
317
|
+
return CodeAgent(**kwargs)
|
|
318
|
+
else:
|
|
319
|
+
return super().__new__(cls)
|
|
320
|
+
|
|
267
321
|
def __init__(
|
|
268
322
|
self,
|
|
269
323
|
system_prompt: str,
|
|
@@ -274,7 +328,6 @@ class Agent:
|
|
|
274
328
|
auto_complete: bool = False,
|
|
275
329
|
output_handler: Optional[List[OutputHandlerProtocol]] = None,
|
|
276
330
|
use_tools: Optional[List[str]] = None,
|
|
277
|
-
input_handler: Optional[List[Callable[[str, Any], Tuple[str, bool]]]] = None,
|
|
278
331
|
execute_tool_confirm: Optional[bool] = None,
|
|
279
332
|
need_summary: bool = True,
|
|
280
333
|
multiline_inputer: Optional[Callable[[str], str]] = None,
|
|
@@ -283,6 +336,10 @@ class Agent:
|
|
|
283
336
|
force_save_memory: Optional[bool] = None,
|
|
284
337
|
files: Optional[List[str]] = None,
|
|
285
338
|
confirm_callback: Optional[Callable[[str, bool], bool]] = None,
|
|
339
|
+
non_interactive: Optional[bool] = None,
|
|
340
|
+
in_multi_agent: Optional[bool] = None,
|
|
341
|
+
agent_type: str = "normal",
|
|
342
|
+
**kwargs,
|
|
286
343
|
):
|
|
287
344
|
"""初始化Jarvis Agent实例
|
|
288
345
|
|
|
@@ -293,8 +350,6 @@ class Agent:
|
|
|
293
350
|
|
|
294
351
|
summary_prompt: 任务总结提示模板
|
|
295
352
|
auto_complete: 是否自动完成任务
|
|
296
|
-
output_handler: 输出处理器列表
|
|
297
|
-
input_handler: 输入处理器列表
|
|
298
353
|
execute_tool_confirm: 执行工具前是否需要确认
|
|
299
354
|
need_summary: 是否需要生成总结
|
|
300
355
|
multiline_inputer: 多行输入处理器
|
|
@@ -302,21 +357,42 @@ class Agent:
|
|
|
302
357
|
use_analysis: 是否使用任务分析
|
|
303
358
|
force_save_memory: 是否强制保存记忆
|
|
304
359
|
confirm_callback: 用户确认回调函数,签名为 (tip: str, default: bool) -> bool;默认使用CLI的user_confirm
|
|
360
|
+
non_interactive: 是否以非交互模式运行(优先级最高,覆盖环境变量与配置)
|
|
305
361
|
"""
|
|
306
|
-
#
|
|
307
|
-
|
|
362
|
+
# 基础属性初始化(仅根据入参设置原始值;实际生效的默认回退在 _init_config 中统一解析)
|
|
363
|
+
# 标识与描述
|
|
308
364
|
self.name = make_agent_name(name)
|
|
309
365
|
self.description = description
|
|
310
366
|
self.system_prompt = system_prompt
|
|
311
|
-
|
|
312
|
-
self.auto_complete = auto_complete
|
|
367
|
+
# 行为控制开关(原始入参值)
|
|
368
|
+
self.auto_complete = bool(auto_complete)
|
|
369
|
+
self.need_summary = bool(need_summary)
|
|
370
|
+
self.use_methodology = use_methodology
|
|
371
|
+
self.use_analysis = use_analysis
|
|
372
|
+
self.execute_tool_confirm = execute_tool_confirm
|
|
373
|
+
self.summary_prompt = summary_prompt
|
|
374
|
+
self.force_save_memory = force_save_memory
|
|
375
|
+
# 资源与环境
|
|
376
|
+
self.model_group = model_group
|
|
377
|
+
self.files = files or []
|
|
378
|
+
self.use_tools = use_tools
|
|
379
|
+
self.non_interactive = non_interactive
|
|
380
|
+
# 多智能体运行标志:用于控制非交互模式下的自动完成行为
|
|
381
|
+
self.in_multi_agent = bool(in_multi_agent)
|
|
382
|
+
# 运行时状态
|
|
313
383
|
self.first = True
|
|
314
384
|
self.run_input_handlers_next_turn = False
|
|
315
385
|
self.user_data: Dict[str, Any] = {}
|
|
386
|
+
# 记录连续未添加 addon_prompt 的轮数
|
|
387
|
+
self._addon_prompt_skip_rounds: int = 0
|
|
388
|
+
# 记录连续没有工具调用的次数(用于非交互模式下的工具使用提示)
|
|
389
|
+
self._no_tool_call_count: int = 0
|
|
390
|
+
|
|
391
|
+
self._agent_type = "normal"
|
|
316
392
|
|
|
317
393
|
|
|
318
394
|
# 用户确认回调:默认使用 CLI 的 user_confirm,可由外部注入以支持 TUI/GUI
|
|
319
|
-
self.
|
|
395
|
+
self.confirm_callback: Callable[[str, bool], bool] = (
|
|
320
396
|
confirm_callback or user_confirm # type: ignore[assignment]
|
|
321
397
|
)
|
|
322
398
|
|
|
@@ -326,25 +402,62 @@ class Agent:
|
|
|
326
402
|
|
|
327
403
|
# 初始化处理器
|
|
328
404
|
self._init_handlers(
|
|
329
|
-
output_handler or [],
|
|
330
|
-
input_handler,
|
|
331
405
|
multiline_inputer,
|
|
406
|
+
output_handler,
|
|
332
407
|
use_tools or [],
|
|
333
408
|
)
|
|
334
409
|
# 初始化用户交互封装,保持向后兼容
|
|
335
|
-
self.user_interaction = UserInteractionHandler(self.multiline_inputer, self.
|
|
410
|
+
self.user_interaction = UserInteractionHandler(self.multiline_inputer, self.confirm_callback)
|
|
336
411
|
# 将确认函数指向封装后的 confirm,保持既有调用不变
|
|
337
|
-
self.
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
412
|
+
self.confirm_callback = self.user_interaction.confirm # type: ignore[assignment]
|
|
413
|
+
# 非交互模式参数支持:允许通过构造参数显式控制,便于其他Agent调用时设置
|
|
414
|
+
try:
|
|
415
|
+
# 优先使用构造参数,其次回退到环境变量
|
|
416
|
+
self.non_interactive = (
|
|
417
|
+
bool(non_interactive)
|
|
418
|
+
if non_interactive is not None
|
|
419
|
+
else str(os.environ.get("JARVIS_NON_INTERACTIVE", "")).lower() in ("1", "true", "yes")
|
|
420
|
+
)
|
|
421
|
+
# 如果构造参数显式提供,则同步到环境变量与全局配置,供下游组件读取
|
|
422
|
+
if non_interactive is not None:
|
|
423
|
+
os.environ["JARVIS_NON_INTERACTIVE"] = "true" if self.non_interactive else "false"
|
|
424
|
+
|
|
425
|
+
except Exception:
|
|
426
|
+
# 防御式回退
|
|
427
|
+
self.non_interactive = False
|
|
428
|
+
|
|
429
|
+
# 初始化配置(直接解析,不再依赖 _init_config)
|
|
430
|
+
try:
|
|
431
|
+
resolved_use_methodology = bool(use_methodology if use_methodology is not None else is_use_methodology())
|
|
432
|
+
except Exception:
|
|
433
|
+
resolved_use_methodology = bool(use_methodology) if use_methodology is not None else True
|
|
434
|
+
|
|
435
|
+
try:
|
|
436
|
+
resolved_use_analysis = bool(use_analysis if use_analysis is not None else is_use_analysis())
|
|
437
|
+
except Exception:
|
|
438
|
+
resolved_use_analysis = bool(use_analysis) if use_analysis is not None else True
|
|
439
|
+
|
|
440
|
+
try:
|
|
441
|
+
resolved_execute_tool_confirm = bool(execute_tool_confirm if execute_tool_confirm is not None else is_execute_tool_confirm())
|
|
442
|
+
except Exception:
|
|
443
|
+
resolved_execute_tool_confirm = bool(execute_tool_confirm) if execute_tool_confirm is not None else False
|
|
444
|
+
|
|
445
|
+
try:
|
|
446
|
+
resolved_force_save_memory = bool(force_save_memory if force_save_memory is not None else is_force_save_memory())
|
|
447
|
+
except Exception:
|
|
448
|
+
resolved_force_save_memory = bool(force_save_memory) if force_save_memory is not None else False
|
|
449
|
+
|
|
450
|
+
self.use_methodology = resolved_use_methodology
|
|
451
|
+
self.use_analysis = resolved_use_analysis
|
|
452
|
+
self.execute_tool_confirm = resolved_execute_tool_confirm
|
|
453
|
+
self.summary_prompt = (summary_prompt or DEFAULT_SUMMARY_PROMPT)
|
|
454
|
+
self.force_save_memory = resolved_force_save_memory
|
|
455
|
+
# 多智能体模式下,默认不自动完成(即使是非交互),仅在明确传入 auto_complete=True 时开启
|
|
456
|
+
if self.in_multi_agent:
|
|
457
|
+
self.auto_complete = bool(self.auto_complete)
|
|
458
|
+
else:
|
|
459
|
+
# 非交互模式下默认自动完成;否则保持传入的 auto_complete 值
|
|
460
|
+
self.auto_complete = bool(self.auto_complete or (self.non_interactive or False))
|
|
348
461
|
|
|
349
462
|
# 初始化事件总线需先于管理器,以便管理器在构造中安全订阅事件
|
|
350
463
|
self.event_bus = EventBus()
|
|
@@ -354,6 +467,14 @@ class Agent:
|
|
|
354
467
|
self.file_methodology_manager = FileMethodologyManager(self)
|
|
355
468
|
self.prompt_manager = PromptManager(self)
|
|
356
469
|
|
|
470
|
+
# 如果配置了强制保存记忆,确保 save_memory 工具可用
|
|
471
|
+
if self.force_save_memory:
|
|
472
|
+
self._ensure_save_memory_tool()
|
|
473
|
+
|
|
474
|
+
# 如果启用了分析,确保 methodology 工具可用
|
|
475
|
+
if self.use_analysis:
|
|
476
|
+
self._ensure_methodology_tool()
|
|
477
|
+
|
|
357
478
|
# 设置系统提示词
|
|
358
479
|
self._setup_system_prompt()
|
|
359
480
|
|
|
@@ -364,6 +485,7 @@ class Agent:
|
|
|
364
485
|
self.get_tool_registry(), # type: ignore
|
|
365
486
|
platform_name=self.model.platform_name(), # type: ignore
|
|
366
487
|
)
|
|
488
|
+
|
|
367
489
|
# 动态加载工具调用后回调
|
|
368
490
|
self._load_after_tool_callbacks()
|
|
369
491
|
|
|
@@ -374,9 +496,7 @@ class Agent:
|
|
|
374
496
|
|
|
375
497
|
maybe_model = PlatformRegistry().create_platform(platform_name)
|
|
376
498
|
if maybe_model is None:
|
|
377
|
-
|
|
378
|
-
f"平台 {platform_name} 不存在,将使用普通模型", OutputType.WARNING
|
|
379
|
-
)
|
|
499
|
+
print(f"⚠️ 平台 {platform_name} 不存在,将使用普通模型")
|
|
380
500
|
maybe_model = PlatformRegistry().get_normal_platform()
|
|
381
501
|
|
|
382
502
|
# 在此处收敛为非可选类型,确保后续赋值满足类型检查
|
|
@@ -394,67 +514,26 @@ class Agent:
|
|
|
394
514
|
|
|
395
515
|
def _init_handlers(
|
|
396
516
|
self,
|
|
397
|
-
output_handler: List[OutputHandlerProtocol],
|
|
398
|
-
input_handler: Optional[List[Callable[[str, Any], Tuple[str, bool]]]],
|
|
399
517
|
multiline_inputer: Optional[Callable[[str], str]],
|
|
518
|
+
output_handler: Optional[List[OutputHandlerProtocol]],
|
|
400
519
|
use_tools: List[str],
|
|
401
520
|
):
|
|
402
521
|
"""初始化各种处理器"""
|
|
403
|
-
|
|
522
|
+
default_handlers: List[Any] = [ToolRegistry()]
|
|
523
|
+
handlers = output_handler or default_handlers
|
|
524
|
+
self.output_handler = handlers
|
|
404
525
|
self.set_use_tools(use_tools)
|
|
405
|
-
self.input_handler =
|
|
526
|
+
self.input_handler = [
|
|
527
|
+
builtin_input_handler,
|
|
528
|
+
shell_input_handler,
|
|
529
|
+
file_context_handler,
|
|
530
|
+
]
|
|
406
531
|
self.multiline_inputer = multiline_inputer or get_multiline_input
|
|
407
532
|
|
|
408
|
-
def _init_config(
|
|
409
|
-
self,
|
|
410
|
-
use_methodology: Optional[bool],
|
|
411
|
-
use_analysis: Optional[bool],
|
|
412
|
-
execute_tool_confirm: Optional[bool],
|
|
413
|
-
summary_prompt: Optional[str],
|
|
414
|
-
model_group: Optional[str],
|
|
415
|
-
force_save_memory: Optional[bool],
|
|
416
|
-
):
|
|
417
|
-
"""初始化配置选项"""
|
|
418
|
-
# 使用集中配置解析,保持与原逻辑一致
|
|
419
|
-
cfg = AgentConfig(
|
|
420
|
-
system_prompt=self.system_prompt,
|
|
421
|
-
name=self.name,
|
|
422
|
-
description=self.description,
|
|
423
|
-
model_group=model_group,
|
|
424
|
-
auto_complete=self.auto_complete,
|
|
425
|
-
need_summary=self.need_summary,
|
|
426
|
-
summary_prompt=summary_prompt,
|
|
427
|
-
execute_tool_confirm=execute_tool_confirm,
|
|
428
|
-
use_methodology=use_methodology,
|
|
429
|
-
use_analysis=use_analysis,
|
|
430
|
-
force_save_memory=force_save_memory,
|
|
431
|
-
files=self.files,
|
|
432
|
-
max_token_count=None,
|
|
433
|
-
).resolve_defaults()
|
|
434
|
-
|
|
435
|
-
# 将解析结果回填到 Agent 实例属性,保持向后兼容
|
|
436
|
-
self.use_methodology = bool(cfg.use_methodology)
|
|
437
|
-
self.use_analysis = bool(cfg.use_analysis)
|
|
438
|
-
self.execute_tool_confirm = bool(cfg.execute_tool_confirm)
|
|
439
|
-
self.summary_prompt = cfg.summary_prompt or DEFAULT_SUMMARY_PROMPT
|
|
440
|
-
self.max_token_count = int(cfg.max_token_count or get_max_token_count(model_group))
|
|
441
|
-
self.force_save_memory = bool(cfg.force_save_memory)
|
|
442
|
-
|
|
443
|
-
# 聚合配置到 AgentConfig,作为后续单一事实来源(保持兼容,不改变既有属性使用)
|
|
444
|
-
self.config = cfg
|
|
445
|
-
|
|
446
533
|
def _setup_system_prompt(self):
|
|
447
534
|
"""设置系统提示词"""
|
|
448
535
|
try:
|
|
449
|
-
|
|
450
|
-
prompt_text = self.prompt_manager.build_system_prompt()
|
|
451
|
-
else:
|
|
452
|
-
action_prompt = self.get_tool_usage_prompt()
|
|
453
|
-
prompt_text = f"""
|
|
454
|
-
{self.system_prompt}
|
|
455
|
-
|
|
456
|
-
{action_prompt}
|
|
457
|
-
"""
|
|
536
|
+
prompt_text = self.prompt_manager.build_system_prompt()
|
|
458
537
|
self.model.set_system_prompt(prompt_text) # type: ignore
|
|
459
538
|
except Exception:
|
|
460
539
|
# 回退到原始行为,确保兼容性
|
|
@@ -475,6 +554,19 @@ class Agent:
|
|
|
475
554
|
"""Gets user data from the session."""
|
|
476
555
|
return self.session.get_user_data(key)
|
|
477
556
|
|
|
557
|
+
def get_remaining_token_count(self) -> int:
|
|
558
|
+
"""获取剩余可用的token数量
|
|
559
|
+
|
|
560
|
+
返回:
|
|
561
|
+
int: 剩余可用的token数量,如果无法获取则返回0
|
|
562
|
+
"""
|
|
563
|
+
if not self.model:
|
|
564
|
+
return 0
|
|
565
|
+
try:
|
|
566
|
+
return self.model.get_remaining_token_count()
|
|
567
|
+
except Exception:
|
|
568
|
+
return 0
|
|
569
|
+
|
|
478
570
|
def set_use_tools(self, use_tools):
|
|
479
571
|
"""设置要使用的工具列表"""
|
|
480
572
|
for handler in self.output_handler:
|
|
@@ -498,8 +590,10 @@ class Agent:
|
|
|
498
590
|
otherwise, fall back to calling with a single argument for compatibility.
|
|
499
591
|
"""
|
|
500
592
|
# 优先通过用户交互封装,便于未来替换 UI
|
|
501
|
-
|
|
593
|
+
try:
|
|
502
594
|
return self.user_interaction.multiline_input(tip, print_on_empty)
|
|
595
|
+
except Exception:
|
|
596
|
+
pass
|
|
503
597
|
try:
|
|
504
598
|
# Try to pass the keyword for enhanced input handler
|
|
505
599
|
return self.multiline_inputer(tip, print_on_empty=print_on_empty) # type: ignore
|
|
@@ -587,7 +681,7 @@ class Agent:
|
|
|
587
681
|
pass
|
|
588
682
|
|
|
589
683
|
except Exception as e:
|
|
590
|
-
|
|
684
|
+
print(f"⚠️ 从 {file_path} 加载回调失败: {e}")
|
|
591
685
|
finally:
|
|
592
686
|
if added_path:
|
|
593
687
|
try:
|
|
@@ -595,7 +689,7 @@ class Agent:
|
|
|
595
689
|
except ValueError:
|
|
596
690
|
pass
|
|
597
691
|
except Exception as e:
|
|
598
|
-
|
|
692
|
+
print(f"⚠️ 加载回调目录时发生错误: {e}")
|
|
599
693
|
|
|
600
694
|
def save_session(self) -> bool:
|
|
601
695
|
"""Saves the current session state by delegating to the session manager."""
|
|
@@ -615,6 +709,58 @@ class Agent:
|
|
|
615
709
|
return handler
|
|
616
710
|
return None
|
|
617
711
|
|
|
712
|
+
def _ensure_save_memory_tool(self) -> None:
|
|
713
|
+
"""如果配置了强制保存记忆,确保 save_memory 工具在 use_tools 列表中"""
|
|
714
|
+
try:
|
|
715
|
+
tool_registry = self.get_tool_registry()
|
|
716
|
+
if not tool_registry:
|
|
717
|
+
return
|
|
718
|
+
|
|
719
|
+
# 检查 save_memory 工具是否已注册(工具默认都会注册)
|
|
720
|
+
if not tool_registry.get_tool("save_memory"):
|
|
721
|
+
# 如果工具本身不存在,则无法使用,直接返回
|
|
722
|
+
return
|
|
723
|
+
|
|
724
|
+
# 检查 save_memory 是否在 use_tools 列表中
|
|
725
|
+
# 如果 use_tools 为 None,表示使用所有工具,无需添加
|
|
726
|
+
if self.use_tools is None:
|
|
727
|
+
return
|
|
728
|
+
|
|
729
|
+
# 如果 save_memory 不在 use_tools 列表中,则添加
|
|
730
|
+
if "save_memory" not in self.use_tools:
|
|
731
|
+
self.use_tools.append("save_memory")
|
|
732
|
+
# 更新工具注册表的工具列表
|
|
733
|
+
self.set_use_tools(self.use_tools)
|
|
734
|
+
except Exception:
|
|
735
|
+
# 忽略所有错误,不影响主流程
|
|
736
|
+
pass
|
|
737
|
+
|
|
738
|
+
def _ensure_methodology_tool(self) -> None:
|
|
739
|
+
"""如果启用了分析,确保 methodology 工具在 use_tools 列表中"""
|
|
740
|
+
try:
|
|
741
|
+
tool_registry = self.get_tool_registry()
|
|
742
|
+
if not tool_registry:
|
|
743
|
+
return
|
|
744
|
+
|
|
745
|
+
# 检查 methodology 工具是否已注册(工具默认都会注册)
|
|
746
|
+
if not tool_registry.get_tool("methodology"):
|
|
747
|
+
# 如果工具本身不存在,则无法使用,直接返回
|
|
748
|
+
return
|
|
749
|
+
|
|
750
|
+
# 检查 methodology 是否在 use_tools 列表中
|
|
751
|
+
# 如果 use_tools 为 None,表示使用所有工具,无需添加
|
|
752
|
+
if self.use_tools is None:
|
|
753
|
+
return
|
|
754
|
+
|
|
755
|
+
# 如果 methodology 不在 use_tools 列表中,则添加
|
|
756
|
+
if "methodology" not in self.use_tools:
|
|
757
|
+
self.use_tools.append("methodology")
|
|
758
|
+
# 更新工具注册表的工具列表
|
|
759
|
+
self.set_use_tools(self.use_tools)
|
|
760
|
+
except Exception:
|
|
761
|
+
# 忽略所有错误,不影响主流程
|
|
762
|
+
pass
|
|
763
|
+
|
|
618
764
|
def get_event_bus(self) -> EventBus:
|
|
619
765
|
"""获取事件总线实例"""
|
|
620
766
|
return self.event_bus
|
|
@@ -666,7 +812,13 @@ class Agent:
|
|
|
666
812
|
return message
|
|
667
813
|
|
|
668
814
|
def _add_addon_prompt(self, message: str, need_complete: bool) -> str:
|
|
669
|
-
"""添加附加提示到消息
|
|
815
|
+
"""添加附加提示到消息
|
|
816
|
+
|
|
817
|
+
规则:
|
|
818
|
+
1. 如果 session.addon_prompt 存在,优先使用它
|
|
819
|
+
2. 如果消息长度超过阈值,添加默认 addon_prompt
|
|
820
|
+
3. 如果连续10轮都没有添加过 addon_prompt,强制添加一次
|
|
821
|
+
"""
|
|
670
822
|
# 广播添加附加提示前事件(不影响主流程)
|
|
671
823
|
try:
|
|
672
824
|
self.event_bus.emit(
|
|
@@ -680,13 +832,32 @@ class Agent:
|
|
|
680
832
|
pass
|
|
681
833
|
|
|
682
834
|
addon_text = ""
|
|
835
|
+
should_add = False
|
|
836
|
+
|
|
683
837
|
if self.session.addon_prompt:
|
|
838
|
+
# 优先使用 session 中设置的 addon_prompt
|
|
684
839
|
addon_text = self.session.addon_prompt
|
|
685
840
|
message = join_prompts([message, addon_text])
|
|
686
841
|
self.session.addon_prompt = ""
|
|
842
|
+
should_add = True
|
|
687
843
|
else:
|
|
688
|
-
|
|
689
|
-
|
|
844
|
+
threshold = get_addon_prompt_threshold()
|
|
845
|
+
# 条件1:消息长度超过阈值
|
|
846
|
+
if len(message) > threshold:
|
|
847
|
+
addon_text = self.make_default_addon_prompt(need_complete)
|
|
848
|
+
message = join_prompts([message, addon_text])
|
|
849
|
+
should_add = True
|
|
850
|
+
# 条件2:连续10轮都没有添加过 addon_prompt,强制添加一次
|
|
851
|
+
elif self._addon_prompt_skip_rounds >= 10:
|
|
852
|
+
addon_text = self.make_default_addon_prompt(need_complete)
|
|
853
|
+
message = join_prompts([message, addon_text])
|
|
854
|
+
should_add = True
|
|
855
|
+
|
|
856
|
+
# 更新计数器:如果添加了 addon_prompt,重置计数器;否则递增
|
|
857
|
+
if should_add:
|
|
858
|
+
self._addon_prompt_skip_rounds = 0
|
|
859
|
+
else:
|
|
860
|
+
self._addon_prompt_skip_rounds += 1
|
|
690
861
|
|
|
691
862
|
# 广播添加附加提示后事件(不影响主流程)
|
|
692
863
|
try:
|
|
@@ -702,14 +873,9 @@ class Agent:
|
|
|
702
873
|
return message
|
|
703
874
|
|
|
704
875
|
def _manage_conversation_length(self, message: str) -> str:
|
|
705
|
-
"""
|
|
876
|
+
"""管理对话长度计数;摘要触发由剩余token数量在 AgentRunLoop 中统一处理(剩余token低于20%时触发)。"""
|
|
706
877
|
self.session.conversation_length += get_context_token_count(message)
|
|
707
878
|
|
|
708
|
-
if self.session.conversation_length > self.max_token_count:
|
|
709
|
-
summary = self._summarize_and_clear_history()
|
|
710
|
-
if summary:
|
|
711
|
-
message = join_prompts([summary, message])
|
|
712
|
-
self.session.conversation_length = get_context_token_count(message)
|
|
713
879
|
|
|
714
880
|
return message
|
|
715
881
|
|
|
@@ -729,7 +895,14 @@ class Agent:
|
|
|
729
895
|
pass
|
|
730
896
|
|
|
731
897
|
response = self.model.chat_until_success(message) # type: ignore
|
|
732
|
-
|
|
898
|
+
# 防御: 模型可能返回空响应(None或空字符串),统一为空字符串并告警
|
|
899
|
+
if not response:
|
|
900
|
+
try:
|
|
901
|
+
print("⚠️ 模型返回空响应,已使用空字符串回退。")
|
|
902
|
+
except Exception:
|
|
903
|
+
pass
|
|
904
|
+
response = ""
|
|
905
|
+
|
|
733
906
|
# 事件:模型调用后
|
|
734
907
|
try:
|
|
735
908
|
self.event_bus.emit(
|
|
@@ -745,9 +918,13 @@ class Agent:
|
|
|
745
918
|
|
|
746
919
|
return response
|
|
747
920
|
|
|
748
|
-
def generate_summary(self) -> str:
|
|
921
|
+
def generate_summary(self, for_token_limit: bool = False) -> str:
|
|
749
922
|
"""生成对话历史摘要
|
|
750
923
|
|
|
924
|
+
参数:
|
|
925
|
+
for_token_limit: 如果为True,表示由于token限制触发的summary,使用SUMMARY_REQUEST_PROMPT
|
|
926
|
+
如果为False,表示任务完成时的summary,使用用户传入的summary_prompt
|
|
927
|
+
|
|
751
928
|
返回:
|
|
752
929
|
str: 包含对话摘要的字符串
|
|
753
930
|
|
|
@@ -758,13 +935,31 @@ class Agent:
|
|
|
758
935
|
try:
|
|
759
936
|
if not self.model:
|
|
760
937
|
raise RuntimeError("Model not initialized")
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
938
|
+
|
|
939
|
+
print("🔍 开始生成对话历史摘要...")
|
|
940
|
+
|
|
941
|
+
if for_token_limit:
|
|
942
|
+
# token限制触发的summary:使用SUMMARY_REQUEST_PROMPT进行上下文压缩
|
|
943
|
+
prompt_to_use = self.session.prompt + "\n" + SUMMARY_REQUEST_PROMPT
|
|
944
|
+
else:
|
|
945
|
+
# 任务完成时的summary:使用用户传入的summary_prompt或DEFAULT_SUMMARY_PROMPT
|
|
946
|
+
safe_summary_prompt = self.summary_prompt or ""
|
|
947
|
+
if isinstance(safe_summary_prompt, str) and safe_summary_prompt.strip() != "":
|
|
948
|
+
prompt_to_use = safe_summary_prompt
|
|
949
|
+
else:
|
|
950
|
+
prompt_to_use = DEFAULT_SUMMARY_PROMPT
|
|
951
|
+
|
|
952
|
+
summary = self.model.chat_until_success(prompt_to_use) # type: ignore
|
|
953
|
+
# 防御: 可能返回空响应(None或空字符串),统一为空字符串并告警
|
|
954
|
+
if not summary:
|
|
955
|
+
try:
|
|
956
|
+
print("⚠️ 总结模型返回空响应,已使用空字符串回退。")
|
|
957
|
+
except Exception:
|
|
958
|
+
pass
|
|
959
|
+
summary = ""
|
|
765
960
|
return summary
|
|
766
961
|
except Exception:
|
|
767
|
-
|
|
962
|
+
print("❌ 总结对话历史失败")
|
|
768
963
|
return ""
|
|
769
964
|
|
|
770
965
|
def _summarize_and_clear_history(self) -> str:
|
|
@@ -784,11 +979,6 @@ class Agent:
|
|
|
784
979
|
注意:
|
|
785
980
|
当上下文长度超过最大值时使用
|
|
786
981
|
"""
|
|
787
|
-
# 在清理历史之前,提示用户保存重要记忆(事件驱动触发实际保存)
|
|
788
|
-
if self.force_save_memory:
|
|
789
|
-
PrettyOutput.print(
|
|
790
|
-
"对话历史即将被总结和清理,请先保存重要信息...", OutputType.INFO
|
|
791
|
-
)
|
|
792
982
|
|
|
793
983
|
if self._should_use_file_upload():
|
|
794
984
|
return self._handle_history_with_file_upload()
|
|
@@ -801,26 +991,39 @@ class Agent:
|
|
|
801
991
|
|
|
802
992
|
def _handle_history_with_summary(self) -> str:
|
|
803
993
|
"""使用摘要方式处理历史"""
|
|
804
|
-
summary
|
|
994
|
+
# token限制触发的summary,使用SUMMARY_REQUEST_PROMPT
|
|
995
|
+
summary = self.generate_summary(for_token_limit=True)
|
|
805
996
|
|
|
806
997
|
# 先获取格式化的摘要消息
|
|
807
998
|
formatted_summary = ""
|
|
808
999
|
if summary:
|
|
809
1000
|
formatted_summary = self._format_summary_message(summary)
|
|
810
1001
|
|
|
811
|
-
#
|
|
812
|
-
|
|
813
|
-
|
|
1002
|
+
# 关键流程:直接调用 memory_manager 确保记忆提示
|
|
1003
|
+
try:
|
|
1004
|
+
self.memory_manager._ensure_memory_prompt(agent=self)
|
|
1005
|
+
except Exception:
|
|
1006
|
+
pass
|
|
1007
|
+
|
|
1008
|
+
# 非关键流程:广播清理历史前事件(用于日志、监控等)
|
|
814
1009
|
try:
|
|
815
1010
|
self.event_bus.emit(BEFORE_HISTORY_CLEAR, agent=self)
|
|
816
1011
|
except Exception:
|
|
817
1012
|
pass
|
|
1013
|
+
|
|
1014
|
+
# 清理历史(但不清理prompt,因为prompt会在builtin_input_handler中设置)
|
|
1015
|
+
if self.model:
|
|
818
1016
|
self.model.reset()
|
|
819
1017
|
# 重置后重新设置系统提示词,确保系统约束仍然生效
|
|
820
1018
|
self._setup_system_prompt()
|
|
821
1019
|
# 重置会话
|
|
822
1020
|
self.session.clear_history()
|
|
823
|
-
#
|
|
1021
|
+
# 重置 addon_prompt 跳过轮数计数器
|
|
1022
|
+
self._addon_prompt_skip_rounds = 0
|
|
1023
|
+
# 重置没有工具调用的计数器
|
|
1024
|
+
self._no_tool_call_count = 0
|
|
1025
|
+
|
|
1026
|
+
# 非关键流程:广播清理历史后的事件(用于日志、监控等)
|
|
824
1027
|
try:
|
|
825
1028
|
self.event_bus.emit(AFTER_HISTORY_CLEAR, agent=self)
|
|
826
1029
|
except Exception:
|
|
@@ -830,13 +1033,25 @@ class Agent:
|
|
|
830
1033
|
|
|
831
1034
|
def _handle_history_with_file_upload(self) -> str:
|
|
832
1035
|
"""使用文件上传方式处理历史"""
|
|
833
|
-
#
|
|
1036
|
+
# 关键流程:直接调用 memory_manager 确保记忆提示
|
|
1037
|
+
try:
|
|
1038
|
+
self.memory_manager._ensure_memory_prompt(agent=self)
|
|
1039
|
+
except Exception:
|
|
1040
|
+
pass
|
|
1041
|
+
|
|
1042
|
+
# 非关键流程:广播清理历史前事件(用于日志、监控等)
|
|
834
1043
|
try:
|
|
835
1044
|
self.event_bus.emit(BEFORE_HISTORY_CLEAR, agent=self)
|
|
836
1045
|
except Exception:
|
|
837
1046
|
pass
|
|
1047
|
+
|
|
838
1048
|
result = self.file_methodology_manager.handle_history_with_file_upload()
|
|
839
|
-
#
|
|
1049
|
+
# 重置 addon_prompt 跳过轮数计数器
|
|
1050
|
+
self._addon_prompt_skip_rounds = 0
|
|
1051
|
+
# 重置没有工具调用的计数器
|
|
1052
|
+
self._no_tool_call_count = 0
|
|
1053
|
+
|
|
1054
|
+
# 非关键流程:广播清理历史后的事件(用于日志、监控等)
|
|
840
1055
|
try:
|
|
841
1056
|
self.event_bus.emit(AFTER_HISTORY_CLEAR, agent=self)
|
|
842
1057
|
except Exception:
|
|
@@ -876,19 +1091,37 @@ class Agent:
|
|
|
876
1091
|
# - TaskAnalyzer 通过订阅 before_summary/task_completed 事件执行分析与满意度收集
|
|
877
1092
|
# - MemoryManager 通过订阅 before_history_clear/task_completed 事件执行记忆保存(受 force_save_memory 控制)
|
|
878
1093
|
# 为减少耦合,这里不再直接调用上述组件,保持行为由事件触发
|
|
879
|
-
|
|
1094
|
+
# 仅在启用自动记忆整理时检查并整理记忆
|
|
1095
|
+
if is_enable_memory_organizer():
|
|
1096
|
+
self._check_and_organize_memory()
|
|
880
1097
|
|
|
881
1098
|
result = "任务完成"
|
|
882
1099
|
|
|
883
1100
|
if self.need_summary:
|
|
884
1101
|
|
|
885
|
-
|
|
886
|
-
|
|
1102
|
+
# 确保总结提示词非空:若为None或仅空白,则回退到默认提示词
|
|
1103
|
+
safe_summary_prompt = self.summary_prompt or ""
|
|
1104
|
+
if isinstance(safe_summary_prompt, str) and safe_summary_prompt.strip() == "":
|
|
1105
|
+
safe_summary_prompt = DEFAULT_SUMMARY_PROMPT
|
|
1106
|
+
# 注意:不要写回 session.prompt,避免回调修改/清空后导致使用空prompt
|
|
1107
|
+
|
|
1108
|
+
# 关键流程:直接调用 task_analyzer 执行任务分析
|
|
1109
|
+
try:
|
|
1110
|
+
self.task_analyzer._on_before_summary(
|
|
1111
|
+
agent=self,
|
|
1112
|
+
prompt=safe_summary_prompt,
|
|
1113
|
+
auto_completed=auto_completed,
|
|
1114
|
+
need_summary=self.need_summary,
|
|
1115
|
+
)
|
|
1116
|
+
except Exception:
|
|
1117
|
+
pass
|
|
1118
|
+
|
|
1119
|
+
# 非关键流程:广播将要生成总结事件(用于日志、监控等)
|
|
887
1120
|
try:
|
|
888
1121
|
self.event_bus.emit(
|
|
889
1122
|
BEFORE_SUMMARY,
|
|
890
1123
|
agent=self,
|
|
891
|
-
prompt=
|
|
1124
|
+
prompt=safe_summary_prompt,
|
|
892
1125
|
auto_completed=auto_completed,
|
|
893
1126
|
need_summary=self.need_summary,
|
|
894
1127
|
)
|
|
@@ -897,10 +1130,18 @@ class Agent:
|
|
|
897
1130
|
|
|
898
1131
|
if not self.model:
|
|
899
1132
|
raise RuntimeError("Model not initialized")
|
|
900
|
-
|
|
1133
|
+
# 直接使用本地变量,避免受事件回调影响
|
|
1134
|
+
ret = self.model.chat_until_success(safe_summary_prompt) # type: ignore
|
|
1135
|
+
# 防御: 总结阶段模型可能返回空响应(None或空字符串),统一为空字符串并告警
|
|
1136
|
+
if not ret:
|
|
1137
|
+
try:
|
|
1138
|
+
print("⚠️ 总结阶段模型返回空响应,已使用空字符串回退。")
|
|
1139
|
+
except Exception:
|
|
1140
|
+
pass
|
|
1141
|
+
ret = ""
|
|
901
1142
|
result = ret
|
|
902
1143
|
|
|
903
|
-
#
|
|
1144
|
+
# 非关键流程:广播完成总结事件(用于日志、监控等)
|
|
904
1145
|
try:
|
|
905
1146
|
self.event_bus.emit(
|
|
906
1147
|
AFTER_SUMMARY,
|
|
@@ -910,7 +1151,26 @@ class Agent:
|
|
|
910
1151
|
except Exception:
|
|
911
1152
|
pass
|
|
912
1153
|
|
|
913
|
-
|
|
1154
|
+
# 关键流程:直接调用 task_analyzer 和 memory_manager
|
|
1155
|
+
try:
|
|
1156
|
+
self.task_analyzer._on_task_completed(
|
|
1157
|
+
agent=self,
|
|
1158
|
+
auto_completed=auto_completed,
|
|
1159
|
+
need_summary=self.need_summary,
|
|
1160
|
+
)
|
|
1161
|
+
except Exception:
|
|
1162
|
+
pass
|
|
1163
|
+
|
|
1164
|
+
try:
|
|
1165
|
+
self.memory_manager._ensure_memory_prompt(
|
|
1166
|
+
agent=self,
|
|
1167
|
+
auto_completed=auto_completed,
|
|
1168
|
+
need_summary=self.need_summary,
|
|
1169
|
+
)
|
|
1170
|
+
except Exception:
|
|
1171
|
+
pass
|
|
1172
|
+
|
|
1173
|
+
# 非关键流程:广播任务完成事件(用于日志、监控等)
|
|
914
1174
|
try:
|
|
915
1175
|
self.event_bus.emit(
|
|
916
1176
|
TASK_COMPLETED,
|
|
@@ -931,10 +1191,12 @@ class Agent:
|
|
|
931
1191
|
|
|
932
1192
|
"""
|
|
933
1193
|
# 优先使用 PromptManager 以保持逻辑集中
|
|
934
|
-
|
|
1194
|
+
try:
|
|
935
1195
|
return self.prompt_manager.build_default_addon_prompt(need_complete)
|
|
1196
|
+
except Exception:
|
|
1197
|
+
pass
|
|
936
1198
|
|
|
937
|
-
#
|
|
1199
|
+
# 结构化系统指令(回退方案)
|
|
938
1200
|
action_handlers = ", ".join([handler.name() for handler in self.output_handler])
|
|
939
1201
|
|
|
940
1202
|
# 任务完成提示
|
|
@@ -957,10 +1219,11 @@ class Agent:
|
|
|
957
1219
|
{complete_prompt}
|
|
958
1220
|
如果没有完成,请进行下一步操作:
|
|
959
1221
|
- 仅包含一个操作
|
|
960
|
-
- 不要询问用户是否继续,直接继续执行直至完成
|
|
961
1222
|
- 如果信息不明确,请请求用户补充
|
|
962
|
-
- 如果执行过程中连续失败5
|
|
1223
|
+
- 如果执行过程中连续失败5次,请请求用户操作
|
|
963
1224
|
- 操作列表:{action_handlers}{memory_prompts}
|
|
1225
|
+
|
|
1226
|
+
注意:如果当前部分任务已完成,之前的上下文价值不大,可以输出<!!!SUMMARY!!!>标记来触发总结并清空历史,以便开始新的任务阶段。
|
|
964
1227
|
</system_prompt>
|
|
965
1228
|
|
|
966
1229
|
请继续。
|
|
@@ -986,7 +1249,19 @@ class Agent:
|
|
|
986
1249
|
self.session.prompt = f"{user_input}"
|
|
987
1250
|
try:
|
|
988
1251
|
set_agent(self.name, self)
|
|
989
|
-
|
|
1252
|
+
|
|
1253
|
+
# 关键流程:直接调用 memory_manager 重置任务状态
|
|
1254
|
+
try:
|
|
1255
|
+
self.memory_manager._on_task_started(
|
|
1256
|
+
agent=self,
|
|
1257
|
+
name=self.name,
|
|
1258
|
+
description=self.description,
|
|
1259
|
+
user_input=self.session.prompt,
|
|
1260
|
+
)
|
|
1261
|
+
except Exception:
|
|
1262
|
+
pass
|
|
1263
|
+
|
|
1264
|
+
# 非关键流程:广播任务开始事件(用于日志、监控等)
|
|
990
1265
|
try:
|
|
991
1266
|
self.event_bus.emit(
|
|
992
1267
|
TASK_STARTED,
|
|
@@ -999,7 +1274,7 @@ class Agent:
|
|
|
999
1274
|
pass
|
|
1000
1275
|
return self._main_loop()
|
|
1001
1276
|
except Exception as e:
|
|
1002
|
-
|
|
1277
|
+
print(f"❌ 任务失败: {str(e)}")
|
|
1003
1278
|
return f"Task failed: {str(e)}"
|
|
1004
1279
|
|
|
1005
1280
|
def _main_loop(self) -> Any:
|
|
@@ -1041,7 +1316,7 @@ class Agent:
|
|
|
1041
1316
|
return self._complete_task(auto_completed=False)
|
|
1042
1317
|
|
|
1043
1318
|
if any(handler.can_handle(current_response) for handler in self.output_handler):
|
|
1044
|
-
if self.
|
|
1319
|
+
if self.confirm_callback("检测到有工具调用,是否继续处理工具调用?", True):
|
|
1045
1320
|
self.session.prompt = join_prompts([
|
|
1046
1321
|
f"被用户中断,用户补充信息为:{user_input}",
|
|
1047
1322
|
"用户同意继续工具调用。"
|
|
@@ -1104,8 +1379,42 @@ class Agent:
|
|
|
1104
1379
|
temp_model.set_system_prompt(system_prompt)
|
|
1105
1380
|
return temp_model
|
|
1106
1381
|
|
|
1382
|
+
def _build_child_agent_params(self, name: str, description: str) -> Dict[str, Any]:
|
|
1383
|
+
"""构建子Agent参数,尽量继承父Agent配置,并确保子Agent非交互自动完成。"""
|
|
1384
|
+
use_tools_param: Optional[List[str]] = None
|
|
1385
|
+
try:
|
|
1386
|
+
tr = self.get_tool_registry()
|
|
1387
|
+
if isinstance(tr, ToolRegistry):
|
|
1388
|
+
selected_tools = tr.get_all_tools()
|
|
1389
|
+
use_tools_param = [t["name"] for t in selected_tools]
|
|
1390
|
+
except Exception:
|
|
1391
|
+
use_tools_param = None
|
|
1392
|
+
|
|
1393
|
+
return {
|
|
1394
|
+
"system_prompt": origin_agent_system_prompt,
|
|
1395
|
+
"name": name,
|
|
1396
|
+
"description": description,
|
|
1397
|
+
"model_group": self.model_group,
|
|
1398
|
+
"summary_prompt": self.summary_prompt,
|
|
1399
|
+
"auto_complete": True,
|
|
1400
|
+
"use_tools": use_tools_param,
|
|
1401
|
+
"execute_tool_confirm": self.execute_tool_confirm,
|
|
1402
|
+
"need_summary": self.need_summary,
|
|
1403
|
+
"multiline_inputer": self.multiline_inputer,
|
|
1404
|
+
"use_methodology": self.use_methodology,
|
|
1405
|
+
"use_analysis": self.use_analysis,
|
|
1406
|
+
"force_save_memory": self.force_save_memory,
|
|
1407
|
+
"files": self.files,
|
|
1408
|
+
"confirm_callback": self.confirm_callback,
|
|
1409
|
+
"non_interactive": True,
|
|
1410
|
+
"in_multi_agent": True,
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1107
1413
|
def _filter_tools_if_needed(self, task: str):
|
|
1108
|
-
"""如果工具数量超过阈值,使用大模型筛选相关工具
|
|
1414
|
+
"""如果工具数量超过阈值,使用大模型筛选相关工具
|
|
1415
|
+
|
|
1416
|
+
注意:仅筛选用户自定义工具,内置工具不参与筛选(始终保留)
|
|
1417
|
+
"""
|
|
1109
1418
|
tool_registry = self.get_tool_registry()
|
|
1110
1419
|
if not isinstance(tool_registry, ToolRegistry):
|
|
1111
1420
|
return
|
|
@@ -1115,10 +1424,16 @@ class Agent:
|
|
|
1115
1424
|
if len(all_tools) <= threshold:
|
|
1116
1425
|
return
|
|
1117
1426
|
|
|
1118
|
-
#
|
|
1427
|
+
# 获取用户自定义工具(非内置工具),仅对这些工具进行筛选
|
|
1428
|
+
custom_tools = tool_registry.get_custom_tools()
|
|
1429
|
+
if not custom_tools:
|
|
1430
|
+
# 没有用户自定义工具,无需筛选
|
|
1431
|
+
return
|
|
1432
|
+
|
|
1433
|
+
# 为工具选择构建提示(仅包含用户自定义工具)
|
|
1119
1434
|
tools_prompt_part = ""
|
|
1120
1435
|
tool_names = []
|
|
1121
|
-
for i, tool in enumerate(
|
|
1436
|
+
for i, tool in enumerate(custom_tools, 1):
|
|
1122
1437
|
tool_names.append(tool["name"])
|
|
1123
1438
|
tools_prompt_part += f"{i}. {tool['name']}: {tool['description']}\n"
|
|
1124
1439
|
|
|
@@ -1136,9 +1451,7 @@ class Agent:
|
|
|
1136
1451
|
请根据用户任务,从列表中选择最相关的工具。
|
|
1137
1452
|
请仅返回所选工具的编号,以逗号分隔。例如:1, 5, 12
|
|
1138
1453
|
"""
|
|
1139
|
-
|
|
1140
|
-
f"工具数量超过{threshold}个,正在使用AI筛选相关工具...", OutputType.INFO
|
|
1141
|
-
)
|
|
1454
|
+
print(f"ℹ️ 工具数量超过{threshold}个,正在使用AI筛选相关工具...")
|
|
1142
1455
|
# 广播工具筛选开始事件
|
|
1143
1456
|
try:
|
|
1144
1457
|
self.event_bus.emit(
|
|
@@ -1171,13 +1484,13 @@ class Agent:
|
|
|
1171
1484
|
if selected_tool_names:
|
|
1172
1485
|
# 移除重复项
|
|
1173
1486
|
selected_tool_names = sorted(list(set(selected_tool_names)))
|
|
1174
|
-
|
|
1487
|
+
# 合并内置工具名称和筛选出的用户自定义工具名称
|
|
1488
|
+
builtin_names = list(tool_registry._builtin_tool_names)
|
|
1489
|
+
final_tool_names = sorted(list(set(builtin_names + selected_tool_names)))
|
|
1490
|
+
tool_registry.use_tools(final_tool_names)
|
|
1175
1491
|
# 使用筛选后的工具列表重新设置系统提示
|
|
1176
1492
|
self._setup_system_prompt()
|
|
1177
|
-
|
|
1178
|
-
f"已筛选出 {len(selected_tool_names)} 个相关工具: {', '.join(selected_tool_names)}",
|
|
1179
|
-
OutputType.SUCCESS,
|
|
1180
|
-
)
|
|
1493
|
+
print(f"✅ 已筛选出 {len(selected_tool_names)} 个相关工具: {', '.join(selected_tool_names)}")
|
|
1181
1494
|
# 广播工具筛选事件
|
|
1182
1495
|
try:
|
|
1183
1496
|
self.event_bus.emit(
|
|
@@ -1191,9 +1504,7 @@ class Agent:
|
|
|
1191
1504
|
except Exception:
|
|
1192
1505
|
pass
|
|
1193
1506
|
else:
|
|
1194
|
-
|
|
1195
|
-
"AI 未能筛选出任何相关工具,将使用所有工具。", OutputType.WARNING
|
|
1196
|
-
)
|
|
1507
|
+
print("⚠️ AI 未能筛选出任何相关工具,将使用所有工具。")
|
|
1197
1508
|
# 广播工具筛选事件(无筛选结果)
|
|
1198
1509
|
try:
|
|
1199
1510
|
self.event_bus.emit(
|
|
@@ -1208,9 +1519,7 @@ class Agent:
|
|
|
1208
1519
|
pass
|
|
1209
1520
|
|
|
1210
1521
|
except Exception as e:
|
|
1211
|
-
|
|
1212
|
-
f"工具筛选失败: {e},将使用所有工具。", OutputType.ERROR
|
|
1213
|
-
)
|
|
1522
|
+
print(f"❌ 工具筛选失败: {e},将使用所有工具。")
|
|
1214
1523
|
|
|
1215
1524
|
def _check_and_organize_memory(self):
|
|
1216
1525
|
"""
|
|
@@ -1227,7 +1536,7 @@ class Agent:
|
|
|
1227
1536
|
"global",
|
|
1228
1537
|
)
|
|
1229
1538
|
except Exception as e:
|
|
1230
|
-
|
|
1539
|
+
print(f"⚠️ 检查记忆库时发生意外错误: {e}")
|
|
1231
1540
|
|
|
1232
1541
|
def _perform_memory_check(self, memory_type: str, base_path: Path, scope_name: str):
|
|
1233
1542
|
"""执行特定范围的记忆检查和整理"""
|
|
@@ -1268,11 +1577,8 @@ class Agent:
|
|
|
1268
1577
|
f"并且存在3个以上标签重叠的记忆。\n"
|
|
1269
1578
|
f"是否立即整理记忆库以优化性能和相关性?"
|
|
1270
1579
|
)
|
|
1271
|
-
if self.
|
|
1272
|
-
|
|
1273
|
-
f"正在开始整理 '{scope_name}' ({memory_type}) 记忆库...",
|
|
1274
|
-
OutputType.INFO,
|
|
1275
|
-
)
|
|
1580
|
+
if self.confirm_callback(prompt, False):
|
|
1581
|
+
print(f"ℹ️ 正在开始整理 '{scope_name}' ({memory_type}) 记忆库...")
|
|
1276
1582
|
organizer.organize_memories(memory_type, min_overlap=3)
|
|
1277
1583
|
else:
|
|
1278
|
-
|
|
1584
|
+
print(f"ℹ️ 已取消 '{scope_name}' 记忆库整理。")
|