jarvis-ai-assistant 0.1.222__py3-none-any.whl → 0.7.0__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 +1143 -245
- jarvis/jarvis_agent/agent_manager.py +97 -0
- jarvis/jarvis_agent/builtin_input_handler.py +12 -10
- jarvis/jarvis_agent/config_editor.py +57 -0
- jarvis/jarvis_agent/edit_file_handler.py +392 -99
- jarvis/jarvis_agent/event_bus.py +48 -0
- jarvis/jarvis_agent/events.py +157 -0
- jarvis/jarvis_agent/file_context_handler.py +79 -0
- jarvis/jarvis_agent/file_methodology_manager.py +117 -0
- jarvis/jarvis_agent/jarvis.py +1117 -147
- jarvis/jarvis_agent/main.py +78 -34
- jarvis/jarvis_agent/memory_manager.py +195 -0
- jarvis/jarvis_agent/methodology_share_manager.py +174 -0
- jarvis/jarvis_agent/prompt_manager.py +82 -0
- jarvis/jarvis_agent/prompts.py +46 -9
- jarvis/jarvis_agent/protocols.py +4 -1
- jarvis/jarvis_agent/rewrite_file_handler.py +141 -0
- jarvis/jarvis_agent/run_loop.py +146 -0
- jarvis/jarvis_agent/session_manager.py +9 -9
- jarvis/jarvis_agent/share_manager.py +228 -0
- jarvis/jarvis_agent/shell_input_handler.py +23 -3
- jarvis/jarvis_agent/stdio_redirect.py +295 -0
- jarvis/jarvis_agent/task_analyzer.py +212 -0
- jarvis/jarvis_agent/task_manager.py +154 -0
- jarvis/jarvis_agent/task_planner.py +496 -0
- jarvis/jarvis_agent/tool_executor.py +8 -4
- jarvis/jarvis_agent/tool_share_manager.py +139 -0
- jarvis/jarvis_agent/user_interaction.py +42 -0
- jarvis/jarvis_agent/utils.py +54 -0
- jarvis/jarvis_agent/web_bridge.py +189 -0
- jarvis/jarvis_agent/web_output_sink.py +53 -0
- jarvis/jarvis_agent/web_server.py +751 -0
- jarvis/jarvis_c2rust/__init__.py +26 -0
- jarvis/jarvis_c2rust/cli.py +613 -0
- jarvis/jarvis_c2rust/collector.py +258 -0
- jarvis/jarvis_c2rust/library_replacer.py +1122 -0
- jarvis/jarvis_c2rust/llm_module_agent.py +1300 -0
- jarvis/jarvis_c2rust/optimizer.py +960 -0
- jarvis/jarvis_c2rust/scanner.py +1681 -0
- jarvis/jarvis_c2rust/transpiler.py +2325 -0
- jarvis/jarvis_code_agent/build_validation_config.py +133 -0
- jarvis/jarvis_code_agent/code_agent.py +1605 -178
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +62 -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 +102 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +59 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +125 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +69 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +38 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +44 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +38 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +50 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +93 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +129 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +54 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +154 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator.py +43 -0
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +363 -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 +89 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +31 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +231 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +183 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +219 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +209 -0
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +451 -0
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +77 -0
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +48 -0
- jarvis/jarvis_code_agent/lint.py +275 -13
- jarvis/jarvis_code_agent/utils.py +142 -0
- jarvis/jarvis_code_analysis/checklists/loader.py +20 -6
- jarvis/jarvis_code_analysis/code_review.py +583 -548
- jarvis/jarvis_data/config_schema.json +339 -28
- jarvis/jarvis_git_squash/main.py +22 -13
- jarvis/jarvis_git_utils/git_commiter.py +171 -55
- jarvis/jarvis_mcp/sse_mcp_client.py +22 -15
- jarvis/jarvis_mcp/stdio_mcp_client.py +4 -4
- jarvis/jarvis_mcp/streamable_mcp_client.py +36 -16
- jarvis/jarvis_memory_organizer/memory_organizer.py +753 -0
- jarvis/jarvis_methodology/main.py +48 -63
- jarvis/jarvis_multi_agent/__init__.py +302 -43
- jarvis/jarvis_multi_agent/main.py +70 -24
- jarvis/jarvis_platform/ai8.py +40 -23
- jarvis/jarvis_platform/base.py +210 -49
- jarvis/jarvis_platform/human.py +11 -1
- jarvis/jarvis_platform/kimi.py +82 -76
- jarvis/jarvis_platform/openai.py +73 -1
- jarvis/jarvis_platform/registry.py +8 -15
- jarvis/jarvis_platform/tongyi.py +115 -101
- jarvis/jarvis_platform/yuanbao.py +89 -63
- jarvis/jarvis_platform_manager/main.py +194 -132
- jarvis/jarvis_platform_manager/service.py +122 -86
- jarvis/jarvis_rag/cli.py +156 -53
- jarvis/jarvis_rag/embedding_manager.py +155 -12
- jarvis/jarvis_rag/llm_interface.py +10 -13
- jarvis/jarvis_rag/query_rewriter.py +63 -12
- jarvis/jarvis_rag/rag_pipeline.py +222 -40
- jarvis/jarvis_rag/reranker.py +26 -3
- jarvis/jarvis_rag/retriever.py +270 -14
- jarvis/jarvis_sec/__init__.py +3605 -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 +116 -0
- jarvis/jarvis_sec/report.py +257 -0
- jarvis/jarvis_sec/status.py +264 -0
- jarvis/jarvis_sec/types.py +20 -0
- jarvis/jarvis_sec/workflow.py +219 -0
- jarvis/jarvis_smart_shell/main.py +405 -137
- jarvis/jarvis_stats/__init__.py +13 -0
- jarvis/jarvis_stats/cli.py +387 -0
- jarvis/jarvis_stats/stats.py +711 -0
- jarvis/jarvis_stats/storage.py +612 -0
- jarvis/jarvis_stats/visualizer.py +282 -0
- jarvis/jarvis_tools/ask_user.py +1 -0
- jarvis/jarvis_tools/base.py +18 -2
- jarvis/jarvis_tools/clear_memory.py +239 -0
- jarvis/jarvis_tools/cli/main.py +220 -144
- jarvis/jarvis_tools/execute_script.py +52 -12
- jarvis/jarvis_tools/file_analyzer.py +17 -12
- jarvis/jarvis_tools/generate_new_tool.py +46 -24
- jarvis/jarvis_tools/read_code.py +277 -18
- jarvis/jarvis_tools/read_symbols.py +141 -0
- jarvis/jarvis_tools/read_webpage.py +86 -13
- jarvis/jarvis_tools/registry.py +294 -90
- jarvis/jarvis_tools/retrieve_memory.py +227 -0
- jarvis/jarvis_tools/save_memory.py +194 -0
- jarvis/jarvis_tools/search_web.py +62 -28
- jarvis/jarvis_tools/sub_agent.py +205 -0
- jarvis/jarvis_tools/sub_code_agent.py +217 -0
- jarvis/jarvis_tools/virtual_tty.py +330 -62
- jarvis/jarvis_utils/builtin_replace_map.py +4 -5
- jarvis/jarvis_utils/clipboard.py +90 -0
- jarvis/jarvis_utils/config.py +607 -50
- jarvis/jarvis_utils/embedding.py +3 -0
- jarvis/jarvis_utils/fzf.py +57 -0
- jarvis/jarvis_utils/git_utils.py +251 -29
- jarvis/jarvis_utils/globals.py +174 -17
- jarvis/jarvis_utils/http.py +58 -79
- jarvis/jarvis_utils/input.py +899 -153
- jarvis/jarvis_utils/methodology.py +210 -83
- jarvis/jarvis_utils/output.py +220 -137
- jarvis/jarvis_utils/utils.py +1906 -135
- jarvis_ai_assistant-0.7.0.dist-info/METADATA +465 -0
- jarvis_ai_assistant-0.7.0.dist-info/RECORD +192 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/entry_points.txt +8 -2
- jarvis/jarvis_git_details/main.py +0 -265
- jarvis/jarvis_platform/oyi.py +0 -357
- jarvis/jarvis_tools/edit_file.py +0 -255
- jarvis/jarvis_tools/rewrite_file.py +0 -195
- jarvis_ai_assistant-0.1.222.dist-info/METADATA +0 -767
- jarvis_ai_assistant-0.1.222.dist-info/RECORD +0 -110
- /jarvis/{jarvis_git_details → jarvis_memory_organizer}/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/top_level.txt +0 -0
jarvis/jarvis_agent/main.py
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
import argparse
|
|
3
2
|
import os
|
|
3
|
+
from typing import Optional
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import typer
|
|
6
|
+
import yaml # type: ignore[import-untyped]
|
|
6
7
|
|
|
7
8
|
from jarvis.jarvis_agent import Agent
|
|
8
9
|
from jarvis.jarvis_utils.input import get_multiline_input
|
|
9
10
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
10
11
|
from jarvis.jarvis_utils.utils import init_env
|
|
12
|
+
from jarvis.jarvis_utils.config import set_config
|
|
13
|
+
|
|
14
|
+
app = typer.Typer(help="Jarvis AI 助手")
|
|
11
15
|
|
|
12
16
|
|
|
13
17
|
def load_config(config_path: str) -> dict:
|
|
@@ -20,7 +24,9 @@ def load_config(config_path: str) -> dict:
|
|
|
20
24
|
dict: 配置字典
|
|
21
25
|
"""
|
|
22
26
|
if not os.path.exists(config_path):
|
|
23
|
-
PrettyOutput.print(
|
|
27
|
+
PrettyOutput.print(
|
|
28
|
+
f"配置文件 {config_path} 不存在,使用默认配置", OutputType.WARNING
|
|
29
|
+
)
|
|
24
30
|
return {}
|
|
25
31
|
|
|
26
32
|
with open(config_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
@@ -32,63 +38,101 @@ def load_config(config_path: str) -> dict:
|
|
|
32
38
|
return {}
|
|
33
39
|
|
|
34
40
|
|
|
35
|
-
|
|
41
|
+
@app.command()
|
|
42
|
+
def cli(
|
|
43
|
+
config_file: Optional[str] = typer.Option(
|
|
44
|
+
None, "-f", "--config", help="代理配置文件路径"
|
|
45
|
+
),
|
|
46
|
+
agent_definition: Optional[str] = typer.Option(
|
|
47
|
+
None, "-c", "--agent-definition", help="代理定义文件路径"
|
|
48
|
+
),
|
|
49
|
+
task: Optional[str] = typer.Option(None, "-T", "--task", help="初始任务内容"),
|
|
50
|
+
|
|
51
|
+
model_group: Optional[str] = typer.Option(
|
|
52
|
+
None, "-g", "--llm-group", help="使用的模型组,覆盖配置文件中的设置"
|
|
53
|
+
),
|
|
54
|
+
non_interactive: bool = typer.Option(
|
|
55
|
+
False, "-n", "--non-interactive", help="启用非交互模式:用户无法与命令交互,脚本执行超时限制为5分钟"
|
|
56
|
+
),
|
|
57
|
+
):
|
|
36
58
|
"""Main entry point for Jarvis agent"""
|
|
37
|
-
#
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
# CLI 标志:非交互模式(不依赖配置文件)
|
|
60
|
+
if non_interactive:
|
|
61
|
+
try:
|
|
62
|
+
os.environ["JARVIS_NON_INTERACTIVE"] = "true"
|
|
63
|
+
except Exception:
|
|
64
|
+
pass
|
|
65
|
+
try:
|
|
66
|
+
set_config("JARVIS_NON_INTERACTIVE", True)
|
|
67
|
+
except Exception:
|
|
68
|
+
pass
|
|
69
|
+
# 非交互模式要求从命令行传入任务
|
|
70
|
+
if non_interactive and not (task and str(task).strip()):
|
|
71
|
+
PrettyOutput.print(
|
|
72
|
+
"非交互模式已启用:必须使用 --task 传入任务内容,因多行输入不可用。",
|
|
73
|
+
OutputType.ERROR,
|
|
74
|
+
)
|
|
75
|
+
raise typer.Exit(code=2)
|
|
76
|
+
# Initialize环境
|
|
77
|
+
init_env(
|
|
78
|
+
"欢迎使用 Jarvis AI 助手,您的智能助理已准备就绪!", config_file=config_file
|
|
52
79
|
)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
80
|
+
# 在初始化环境后同步 CLI 选项到全局配置,避免被 init_env 覆盖
|
|
81
|
+
try:
|
|
82
|
+
if model_group:
|
|
83
|
+
set_config("JARVIS_LLM_GROUP", str(model_group))
|
|
84
|
+
if non_interactive:
|
|
85
|
+
set_config("JARVIS_NON_INTERACTIVE", True)
|
|
86
|
+
except Exception:
|
|
87
|
+
# 静默忽略同步异常,不影响主流程
|
|
88
|
+
pass
|
|
57
89
|
|
|
58
90
|
# Load configuration
|
|
59
|
-
config = load_config(
|
|
91
|
+
config = load_config(agent_definition) if agent_definition else {}
|
|
60
92
|
|
|
61
93
|
# Override config with command-line arguments if provided
|
|
62
|
-
|
|
63
|
-
|
|
94
|
+
|
|
95
|
+
if model_group:
|
|
96
|
+
config["model_group"] = model_group
|
|
64
97
|
|
|
65
98
|
# Create and run agent
|
|
66
99
|
try:
|
|
67
100
|
agent = Agent(**config)
|
|
68
101
|
|
|
69
102
|
# Run agent with initial task if specified
|
|
70
|
-
if
|
|
71
|
-
PrettyOutput.print(f"执行初始任务: {
|
|
72
|
-
agent.run(
|
|
73
|
-
return
|
|
103
|
+
if task:
|
|
104
|
+
PrettyOutput.print(f"执行初始任务: {task}", OutputType.INFO)
|
|
105
|
+
agent.run(task)
|
|
106
|
+
return
|
|
74
107
|
|
|
75
108
|
try:
|
|
76
109
|
user_input = get_multiline_input("请输入你的任务(输入空行退出):")
|
|
77
110
|
if not user_input:
|
|
78
|
-
return
|
|
111
|
+
return
|
|
79
112
|
agent.set_addon_prompt(
|
|
80
113
|
"如果有必要,请先指定出行动计划,然后根据计划一步步执行,如果任务过于复杂,可以拆分子Agent进行执行,拆的子Agent需要掌握所有必要的任务信息,否则无法执行"
|
|
81
114
|
)
|
|
82
115
|
agent.run(user_input)
|
|
116
|
+
except KeyboardInterrupt:
|
|
117
|
+
# 用户主动取消输入,正常退出
|
|
118
|
+
return
|
|
119
|
+
except typer.Exit:
|
|
120
|
+
# 来自输入流程的正常退出
|
|
121
|
+
return
|
|
83
122
|
except Exception as e:
|
|
84
123
|
PrettyOutput.print(f"错误: {str(e)}", OutputType.ERROR)
|
|
85
124
|
|
|
125
|
+
except typer.Exit:
|
|
126
|
+
return
|
|
86
127
|
except Exception as e:
|
|
87
128
|
PrettyOutput.print(f"初始化错误: {str(e)}", OutputType.ERROR)
|
|
88
|
-
|
|
129
|
+
raise typer.Exit(code=1)
|
|
130
|
+
|
|
89
131
|
|
|
90
|
-
|
|
132
|
+
def main() -> None:
|
|
133
|
+
"""Application entry point."""
|
|
134
|
+
app()
|
|
91
135
|
|
|
92
136
|
|
|
93
137
|
if __name__ == "__main__":
|
|
94
|
-
|
|
138
|
+
main()
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
记忆管理器模块
|
|
4
|
+
负责处理Agent的记忆保存和检索功能
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from jarvis.jarvis_utils.globals import get_all_memory_tags
|
|
8
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
9
|
+
from jarvis.jarvis_agent.events import TASK_STARTED, BEFORE_HISTORY_CLEAR, TASK_COMPLETED
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MemoryManager:
|
|
13
|
+
"""记忆管理器,负责处理记忆相关的功能"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, agent):
|
|
16
|
+
"""
|
|
17
|
+
初始化记忆管理器
|
|
18
|
+
|
|
19
|
+
参数:
|
|
20
|
+
agent: Agent实例
|
|
21
|
+
"""
|
|
22
|
+
self.agent = agent
|
|
23
|
+
# 本轮任务是否已进行过记忆保存提示/处理的标记,用于事件去重
|
|
24
|
+
self._memory_prompted = False
|
|
25
|
+
# 订阅 Agent 事件(旁路集成,失败不影响主流程)
|
|
26
|
+
try:
|
|
27
|
+
self._subscribe_events()
|
|
28
|
+
except Exception:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
def prepare_memory_tags_prompt(self) -> str:
|
|
32
|
+
"""准备记忆标签提示"""
|
|
33
|
+
memory_tags = get_all_memory_tags()
|
|
34
|
+
memory_tags_prompt = ""
|
|
35
|
+
|
|
36
|
+
# 检查是否有save_memory工具
|
|
37
|
+
if self._has_save_memory_tool():
|
|
38
|
+
memory_tags_prompt = "\n\n💡 提示:在分析任务之前,建议使用 save_memory 工具将关键信息记录下来,便于后续检索和复用。"
|
|
39
|
+
|
|
40
|
+
# 构建记忆标签列表
|
|
41
|
+
if any(tags for tags in memory_tags.values()):
|
|
42
|
+
memory_tags_prompt += self._format_memory_tags(memory_tags)
|
|
43
|
+
|
|
44
|
+
return memory_tags_prompt
|
|
45
|
+
|
|
46
|
+
def _has_save_memory_tool(self) -> bool:
|
|
47
|
+
"""检查是否有save_memory工具"""
|
|
48
|
+
tool_registry = self.agent.get_tool_registry()
|
|
49
|
+
if tool_registry:
|
|
50
|
+
tool_names = [tool.name for tool in tool_registry.tools.values()]
|
|
51
|
+
return "save_memory" in tool_names
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
def _format_memory_tags(self, memory_tags: dict) -> str:
|
|
55
|
+
"""格式化记忆标签"""
|
|
56
|
+
prompt = (
|
|
57
|
+
"\n\n系统中存在以下记忆标签,你可以使用 retrieve_memory 工具检索相关记忆:"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
type_names = {
|
|
61
|
+
"short_term": "短期记忆",
|
|
62
|
+
"project_long_term": "项目长期记忆",
|
|
63
|
+
"global_long_term": "全局长期记忆",
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for memory_type, tags in memory_tags.items():
|
|
67
|
+
if tags:
|
|
68
|
+
type_name = type_names.get(memory_type, memory_type)
|
|
69
|
+
prompt += f"\n- {type_name}: {', '.join(tags)}"
|
|
70
|
+
|
|
71
|
+
return prompt
|
|
72
|
+
|
|
73
|
+
def prompt_memory_save(self):
|
|
74
|
+
"""让大模型自动判断并保存值得记忆的信息"""
|
|
75
|
+
# 检查是否有记忆相关工具
|
|
76
|
+
tool_registry = self.agent.get_tool_registry()
|
|
77
|
+
if not tool_registry:
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
tool_names = [tool.name for tool in tool_registry.tools.values()]
|
|
81
|
+
if "save_memory" not in tool_names:
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
# 构建提示词,让大模型自己判断并保存记忆
|
|
85
|
+
prompt = """请回顾本次任务的整个过程,判断是否有值得长期记忆或项目记忆的信息。
|
|
86
|
+
|
|
87
|
+
如果有以下类型的信息,请使用 save_memory 工具保存:
|
|
88
|
+
1. 解决问题的新方法或技巧(适合保存为 global_long_term)
|
|
89
|
+
2. 项目相关的重要发现或配置(适合保存为 project_long_term)
|
|
90
|
+
3. 用户的偏好或习惯(适合保存为 global_long_term)
|
|
91
|
+
4. 重要的技术知识或经验(适合保存为 global_long_term)
|
|
92
|
+
5. 项目特定的实现细节或约定(适合保存为 project_long_term)
|
|
93
|
+
|
|
94
|
+
请分析并保存有价值的信息,选择合适的记忆类型和标签。如果没有值得记忆的信息,请直接说明。"""
|
|
95
|
+
|
|
96
|
+
# 处理记忆保存
|
|
97
|
+
try:
|
|
98
|
+
# 清空本轮执行标记,便于准确判断是否调用了 save_memory
|
|
99
|
+
try:
|
|
100
|
+
self.agent.set_user_data("__last_executed_tool__", "")
|
|
101
|
+
self.agent.set_user_data("__executed_tools__", [])
|
|
102
|
+
except Exception:
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
response = self.agent.model.chat_until_success(prompt) # type: ignore
|
|
106
|
+
|
|
107
|
+
# 执行工具调用(如果有)
|
|
108
|
+
need_return, result = self.agent._call_tools(response)
|
|
109
|
+
|
|
110
|
+
# 根据实际执行的工具判断是否保存了记忆
|
|
111
|
+
saved = False
|
|
112
|
+
try:
|
|
113
|
+
last_tool = self.agent.get_user_data("__last_executed_tool__")
|
|
114
|
+
saved = last_tool == "save_memory"
|
|
115
|
+
except Exception:
|
|
116
|
+
saved = False
|
|
117
|
+
|
|
118
|
+
if saved:
|
|
119
|
+
PrettyOutput.print(
|
|
120
|
+
"已自动保存有价值的信息到记忆系统", OutputType.SUCCESS
|
|
121
|
+
)
|
|
122
|
+
else:
|
|
123
|
+
PrettyOutput.print("本次任务没有特别需要记忆的信息", OutputType.INFO)
|
|
124
|
+
|
|
125
|
+
except Exception as e:
|
|
126
|
+
PrettyOutput.print(f"记忆分析失败: {str(e)}", OutputType.ERROR)
|
|
127
|
+
finally:
|
|
128
|
+
# 设置记忆提示完成标记,避免事件触发造成重复处理
|
|
129
|
+
self._memory_prompted = True
|
|
130
|
+
try:
|
|
131
|
+
self.agent.set_user_data("__memory_save_prompted__", True)
|
|
132
|
+
except Exception:
|
|
133
|
+
pass
|
|
134
|
+
|
|
135
|
+
def add_memory_prompts_to_addon(self, addon_prompt: str, tool_registry) -> str:
|
|
136
|
+
"""在附加提示中添加记忆相关提示"""
|
|
137
|
+
memory_prompts = ""
|
|
138
|
+
|
|
139
|
+
if tool_registry:
|
|
140
|
+
tool_names = [tool.name for tool in tool_registry.tools.values()]
|
|
141
|
+
|
|
142
|
+
# 如果有save_memory工具,添加相关提示
|
|
143
|
+
if "save_memory" in tool_names:
|
|
144
|
+
memory_prompts += (
|
|
145
|
+
"\n - 如果有关键信息需要记忆,请调用save_memory工具进行记忆:"
|
|
146
|
+
)
|
|
147
|
+
memory_prompts += "\n * project_long_term: 保存与当前项目相关的长期信息(如:架构决策、关键配置、特定实现)"
|
|
148
|
+
memory_prompts += "\n * global_long_term: 保存通用的信息、用户喜好、知识、方法等(如:常用命令、个人偏好、解决方案)"
|
|
149
|
+
memory_prompts += "\n * short_term: 保存当前任务相关的临时信息(如:当前处理的文件、用户中间需求)"
|
|
150
|
+
|
|
151
|
+
# 如果有retrieve_memory工具,添加相关提示
|
|
152
|
+
if "retrieve_memory" in tool_names:
|
|
153
|
+
memory_prompts += "\n - 如果需要获取上下文或寻找解决方案,请调用retrieve_memory工具检索相关记忆"
|
|
154
|
+
|
|
155
|
+
return memory_prompts
|
|
156
|
+
|
|
157
|
+
# -----------------------
|
|
158
|
+
# 事件订阅与处理(旁路)
|
|
159
|
+
# -----------------------
|
|
160
|
+
def _subscribe_events(self) -> None:
|
|
161
|
+
bus = self.agent.get_event_bus() # type: ignore[attr-defined]
|
|
162
|
+
# 任务开始时重置去重标记
|
|
163
|
+
bus.subscribe(TASK_STARTED, self._on_task_started)
|
|
164
|
+
# 在清理历史前尝试保存记忆(若开启强制保存且尚未处理)
|
|
165
|
+
bus.subscribe(BEFORE_HISTORY_CLEAR, self._ensure_memory_prompt)
|
|
166
|
+
# 任务完成时作为兜底再尝试一次
|
|
167
|
+
bus.subscribe(TASK_COMPLETED, self._ensure_memory_prompt)
|
|
168
|
+
|
|
169
|
+
def _on_task_started(self, **payload) -> None:
|
|
170
|
+
self._memory_prompted = False
|
|
171
|
+
try:
|
|
172
|
+
self.agent.set_user_data("__memory_save_prompted__", False)
|
|
173
|
+
except Exception:
|
|
174
|
+
pass
|
|
175
|
+
|
|
176
|
+
def _ensure_memory_prompt(self, **payload) -> None:
|
|
177
|
+
# 仅在开启强制保存记忆时启用
|
|
178
|
+
if not getattr(self.agent, "force_save_memory", False):
|
|
179
|
+
return
|
|
180
|
+
# 避免在同一任务内重复提示/处理
|
|
181
|
+
if self._memory_prompted:
|
|
182
|
+
return
|
|
183
|
+
try:
|
|
184
|
+
already = bool(self.agent.get_user_data("__memory_save_prompted__"))
|
|
185
|
+
if already:
|
|
186
|
+
self._memory_prompted = True
|
|
187
|
+
return
|
|
188
|
+
except Exception:
|
|
189
|
+
pass
|
|
190
|
+
# 静默执行保存逻辑,失败不影响主流程
|
|
191
|
+
try:
|
|
192
|
+
self.prompt_memory_save()
|
|
193
|
+
except Exception:
|
|
194
|
+
# 忽略异常,保持主流程稳定
|
|
195
|
+
self._memory_prompted = True
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""方法论分享管理模块"""
|
|
3
|
+
import os
|
|
4
|
+
import glob
|
|
5
|
+
import json
|
|
6
|
+
import shutil
|
|
7
|
+
from typing import List, Dict, Any
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
|
|
11
|
+
from jarvis.jarvis_agent import OutputType, PrettyOutput, user_confirm
|
|
12
|
+
from jarvis.jarvis_agent.share_manager import ShareManager
|
|
13
|
+
from jarvis.jarvis_utils.config import (
|
|
14
|
+
get_central_methodology_repo,
|
|
15
|
+
get_methodology_dirs,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MethodologyShareManager(ShareManager):
|
|
20
|
+
"""方法论分享管理器"""
|
|
21
|
+
|
|
22
|
+
def __init__(self):
|
|
23
|
+
central_repo = get_central_methodology_repo()
|
|
24
|
+
if not central_repo:
|
|
25
|
+
PrettyOutput.print(
|
|
26
|
+
"错误:未配置中心方法论仓库(JARVIS_CENTRAL_METHODOLOGY_REPO)",
|
|
27
|
+
OutputType.ERROR,
|
|
28
|
+
)
|
|
29
|
+
PrettyOutput.print(
|
|
30
|
+
"请在配置文件中设置中心方法论仓库的Git地址", OutputType.INFO
|
|
31
|
+
)
|
|
32
|
+
raise typer.Exit(code=1)
|
|
33
|
+
|
|
34
|
+
super().__init__(central_repo, "central_methodology_repo")
|
|
35
|
+
|
|
36
|
+
def get_resource_type(self) -> str:
|
|
37
|
+
"""获取资源类型名称"""
|
|
38
|
+
return "方法论"
|
|
39
|
+
|
|
40
|
+
def format_resource_display(self, resource: Dict[str, Any]) -> str:
|
|
41
|
+
"""格式化资源显示"""
|
|
42
|
+
dir_name = os.path.basename(resource["directory"])
|
|
43
|
+
return f"{resource['problem_type']} (来自: {dir_name})"
|
|
44
|
+
|
|
45
|
+
def get_existing_resources(self) -> Dict[str, str]:
|
|
46
|
+
"""获取中心仓库中已有的方法论"""
|
|
47
|
+
existing_methodologies = {} # 存储 problem_type -> content 的映射
|
|
48
|
+
for filepath in glob.glob(os.path.join(self.repo_path, "*.json")):
|
|
49
|
+
try:
|
|
50
|
+
with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
|
|
51
|
+
methodology = json.load(f)
|
|
52
|
+
problem_type = methodology.get("problem_type", "")
|
|
53
|
+
content = methodology.get("content", "")
|
|
54
|
+
if problem_type and content:
|
|
55
|
+
existing_methodologies[problem_type] = content
|
|
56
|
+
except Exception:
|
|
57
|
+
pass
|
|
58
|
+
return existing_methodologies
|
|
59
|
+
|
|
60
|
+
def get_local_resources(self) -> List[Dict[str, Any]]:
|
|
61
|
+
"""获取本地方法论"""
|
|
62
|
+
# 获取中心仓库中已有的方法论
|
|
63
|
+
existing_methodologies = self.get_existing_resources()
|
|
64
|
+
|
|
65
|
+
# 获取所有方法论目录
|
|
66
|
+
from jarvis.jarvis_utils.methodology import _get_methodology_directory
|
|
67
|
+
|
|
68
|
+
methodology_dirs = [_get_methodology_directory()] + get_methodology_dirs()
|
|
69
|
+
|
|
70
|
+
# 收集所有方法论文件(排除中心仓库目录和已存在的方法论)
|
|
71
|
+
methodology_files = []
|
|
72
|
+
seen_problem_types = set() # 用于去重
|
|
73
|
+
|
|
74
|
+
for directory in set(methodology_dirs):
|
|
75
|
+
# 跳过中心仓库目录
|
|
76
|
+
if os.path.abspath(directory) == os.path.abspath(self.repo_path):
|
|
77
|
+
continue
|
|
78
|
+
|
|
79
|
+
if not os.path.isdir(directory):
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
for filepath in glob.glob(os.path.join(directory, "*.json")):
|
|
83
|
+
try:
|
|
84
|
+
with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
|
|
85
|
+
methodology = json.load(f)
|
|
86
|
+
problem_type = methodology.get("problem_type", "")
|
|
87
|
+
content = methodology.get("content", "")
|
|
88
|
+
|
|
89
|
+
# 基于内容判断是否已存在于中心仓库
|
|
90
|
+
is_duplicate = False
|
|
91
|
+
if problem_type in existing_methodologies:
|
|
92
|
+
# 如果problem_type相同,比较内容
|
|
93
|
+
if (
|
|
94
|
+
content.strip()
|
|
95
|
+
== existing_methodologies[problem_type].strip()
|
|
96
|
+
):
|
|
97
|
+
is_duplicate = True
|
|
98
|
+
|
|
99
|
+
# 排除已存在于中心仓库的方法论(基于内容),以及本地重复的方法论
|
|
100
|
+
if (
|
|
101
|
+
problem_type
|
|
102
|
+
and content
|
|
103
|
+
and not is_duplicate
|
|
104
|
+
and problem_type not in seen_problem_types
|
|
105
|
+
):
|
|
106
|
+
methodology_files.append(
|
|
107
|
+
{
|
|
108
|
+
"path": filepath,
|
|
109
|
+
"problem_type": problem_type,
|
|
110
|
+
"directory": directory,
|
|
111
|
+
"methodology": methodology,
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
seen_problem_types.add(problem_type)
|
|
115
|
+
except Exception:
|
|
116
|
+
pass
|
|
117
|
+
|
|
118
|
+
return methodology_files
|
|
119
|
+
|
|
120
|
+
def share_resources(self, resources: List[Dict[str, Any]]) -> List[str]:
|
|
121
|
+
"""分享方法论到中心仓库"""
|
|
122
|
+
# 确认操作
|
|
123
|
+
share_list = ["\n将要分享以下方法论到中心仓库:"]
|
|
124
|
+
for meth in resources:
|
|
125
|
+
share_list.append(f"- {meth['problem_type']}")
|
|
126
|
+
PrettyOutput.print("\n".join(share_list), OutputType.INFO)
|
|
127
|
+
|
|
128
|
+
if not user_confirm("确认分享这些方法论吗?"):
|
|
129
|
+
return []
|
|
130
|
+
|
|
131
|
+
# 复制选中的方法论到中心仓库
|
|
132
|
+
copied_list = []
|
|
133
|
+
for meth in resources:
|
|
134
|
+
src_file = meth["path"]
|
|
135
|
+
dst_file = os.path.join(self.repo_path, os.path.basename(src_file))
|
|
136
|
+
shutil.copy2(src_file, dst_file)
|
|
137
|
+
copied_list.append(f"已复制: {meth['problem_type']}")
|
|
138
|
+
|
|
139
|
+
return copied_list
|
|
140
|
+
|
|
141
|
+
def run(self) -> None:
|
|
142
|
+
"""执行方法论分享流程"""
|
|
143
|
+
try:
|
|
144
|
+
# 更新中心仓库
|
|
145
|
+
self.update_central_repo()
|
|
146
|
+
|
|
147
|
+
# 获取本地资源
|
|
148
|
+
local_resources = self.get_local_resources()
|
|
149
|
+
if not local_resources:
|
|
150
|
+
PrettyOutput.print(
|
|
151
|
+
"没有找到新的方法论文件(所有方法论可能已存在于中心仓库)",
|
|
152
|
+
OutputType.WARNING,
|
|
153
|
+
)
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
# 选择要分享的资源
|
|
157
|
+
selected_resources = self.select_resources(local_resources)
|
|
158
|
+
if not selected_resources:
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
# 分享资源
|
|
162
|
+
copied_list = self.share_resources(selected_resources)
|
|
163
|
+
if copied_list:
|
|
164
|
+
# 一次性显示所有复制结果
|
|
165
|
+
PrettyOutput.print("\n".join(copied_list), OutputType.SUCCESS)
|
|
166
|
+
|
|
167
|
+
# 提交并推送
|
|
168
|
+
self.commit_and_push(len(selected_resources))
|
|
169
|
+
|
|
170
|
+
PrettyOutput.print("\n方法论已成功分享到中心仓库!", OutputType.SUCCESS)
|
|
171
|
+
|
|
172
|
+
except Exception as e:
|
|
173
|
+
PrettyOutput.print(f"分享方法论时出错: {str(e)}", OutputType.ERROR)
|
|
174
|
+
raise typer.Exit(code=1)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
PromptManager: 统一管理 Agent 的系统提示词与附加提示词的构建逻辑。
|
|
4
|
+
|
|
5
|
+
设计目标(阶段一,最小变更):
|
|
6
|
+
- 提供独立的提示构建类,不改变现有行为
|
|
7
|
+
- 先行落地构建逻辑,后续在 Agent 中逐步委派使用
|
|
8
|
+
- 保持与现有工具/记忆系统兼容
|
|
9
|
+
"""
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
13
|
+
from jarvis.jarvis_utils.tag import ot
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
# 避免运行时循环依赖,仅用于类型标注
|
|
17
|
+
from . import Agent # noqa: F401
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PromptManager:
|
|
22
|
+
"""
|
|
23
|
+
提示管理器:负责构建系统提示与默认附加提示。
|
|
24
|
+
注意:该类不直接访问模型,只负责拼装字符串。
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, agent: "Agent"):
|
|
28
|
+
self.agent = agent
|
|
29
|
+
|
|
30
|
+
# ----------------------------
|
|
31
|
+
# 系统提示词构建
|
|
32
|
+
# ----------------------------
|
|
33
|
+
def build_system_prompt(self) -> str:
|
|
34
|
+
"""
|
|
35
|
+
构建系统提示词,复用现有的工具使用提示生成逻辑,保持行为一致。
|
|
36
|
+
"""
|
|
37
|
+
action_prompt = self.agent.get_tool_usage_prompt()
|
|
38
|
+
return f"""
|
|
39
|
+
{self.agent.system_prompt}
|
|
40
|
+
|
|
41
|
+
{action_prompt}
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
# ----------------------------
|
|
45
|
+
# 附加提示词构建
|
|
46
|
+
# ----------------------------
|
|
47
|
+
def build_default_addon_prompt(self, need_complete: bool) -> str:
|
|
48
|
+
"""
|
|
49
|
+
构建默认附加提示词(与 Agent.make_default_addon_prompt 行为保持一致)。
|
|
50
|
+
仅进行字符串拼装,不操作会话状态。
|
|
51
|
+
"""
|
|
52
|
+
# 结构化系统指令
|
|
53
|
+
action_handlers = ", ".join([handler.name() for handler in self.agent.output_handler])
|
|
54
|
+
|
|
55
|
+
# 任务完成提示
|
|
56
|
+
complete_prompt = (
|
|
57
|
+
f"- 输出{ot('!!!COMPLETE!!!')}"
|
|
58
|
+
if need_complete and self.agent.auto_complete
|
|
59
|
+
else ""
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# 工具与记忆相关提示
|
|
63
|
+
tool_registry = self.agent.get_tool_registry()
|
|
64
|
+
memory_prompts = self.agent.memory_manager.add_memory_prompts_to_addon(
|
|
65
|
+
"", tool_registry if isinstance(tool_registry, ToolRegistry) else None
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
addon_prompt = f"""
|
|
69
|
+
<system_prompt>
|
|
70
|
+
请判断是否已经完成任务,如果已经完成:
|
|
71
|
+
- 直接输出完成原因,不需要再有新的操作,不要输出{ot("TOOL_CALL")}标签
|
|
72
|
+
{complete_prompt}
|
|
73
|
+
如果没有完成,请进行下一步操作:
|
|
74
|
+
- 仅包含一个操作
|
|
75
|
+
- 如果信息不明确,请请求用户补充
|
|
76
|
+
- 如果执行过程中连续失败5次,请使用ask_user询问用户操作
|
|
77
|
+
- 操作列表:{action_handlers}{memory_prompts}
|
|
78
|
+
</system_prompt>
|
|
79
|
+
|
|
80
|
+
请继续。
|
|
81
|
+
"""
|
|
82
|
+
return addon_prompt
|