jarvis-ai-assistant 0.7.0__py3-none-any.whl → 0.7.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +243 -139
  3. jarvis/jarvis_agent/agent_manager.py +5 -10
  4. jarvis/jarvis_agent/builtin_input_handler.py +2 -6
  5. jarvis/jarvis_agent/config_editor.py +2 -7
  6. jarvis/jarvis_agent/event_bus.py +82 -12
  7. jarvis/jarvis_agent/file_context_handler.py +265 -15
  8. jarvis/jarvis_agent/file_methodology_manager.py +3 -4
  9. jarvis/jarvis_agent/jarvis.py +113 -98
  10. jarvis/jarvis_agent/language_extractors/__init__.py +57 -0
  11. jarvis/jarvis_agent/language_extractors/c_extractor.py +21 -0
  12. jarvis/jarvis_agent/language_extractors/cpp_extractor.py +21 -0
  13. jarvis/jarvis_agent/language_extractors/go_extractor.py +21 -0
  14. jarvis/jarvis_agent/language_extractors/java_extractor.py +84 -0
  15. jarvis/jarvis_agent/language_extractors/javascript_extractor.py +79 -0
  16. jarvis/jarvis_agent/language_extractors/python_extractor.py +21 -0
  17. jarvis/jarvis_agent/language_extractors/rust_extractor.py +21 -0
  18. jarvis/jarvis_agent/language_extractors/typescript_extractor.py +84 -0
  19. jarvis/jarvis_agent/language_support_info.py +486 -0
  20. jarvis/jarvis_agent/main.py +6 -12
  21. jarvis/jarvis_agent/memory_manager.py +7 -16
  22. jarvis/jarvis_agent/methodology_share_manager.py +10 -16
  23. jarvis/jarvis_agent/prompt_manager.py +1 -1
  24. jarvis/jarvis_agent/prompts.py +193 -171
  25. jarvis/jarvis_agent/protocols.py +8 -12
  26. jarvis/jarvis_agent/run_loop.py +77 -14
  27. jarvis/jarvis_agent/session_manager.py +2 -3
  28. jarvis/jarvis_agent/share_manager.py +12 -21
  29. jarvis/jarvis_agent/shell_input_handler.py +1 -2
  30. jarvis/jarvis_agent/task_analyzer.py +26 -4
  31. jarvis/jarvis_agent/task_manager.py +11 -27
  32. jarvis/jarvis_agent/tool_executor.py +2 -3
  33. jarvis/jarvis_agent/tool_share_manager.py +12 -24
  34. jarvis/jarvis_agent/web_server.py +55 -20
  35. jarvis/jarvis_c2rust/__init__.py +5 -5
  36. jarvis/jarvis_c2rust/cli.py +461 -499
  37. jarvis/jarvis_c2rust/collector.py +45 -53
  38. jarvis/jarvis_c2rust/constants.py +26 -0
  39. jarvis/jarvis_c2rust/library_replacer.py +264 -132
  40. jarvis/jarvis_c2rust/llm_module_agent.py +162 -190
  41. jarvis/jarvis_c2rust/loaders.py +207 -0
  42. jarvis/jarvis_c2rust/models.py +28 -0
  43. jarvis/jarvis_c2rust/optimizer.py +1592 -395
  44. jarvis/jarvis_c2rust/transpiler.py +1722 -1064
  45. jarvis/jarvis_c2rust/utils.py +385 -0
  46. jarvis/jarvis_code_agent/build_validation_config.py +2 -3
  47. jarvis/jarvis_code_agent/code_agent.py +394 -320
  48. jarvis/jarvis_code_agent/code_analyzer/__init__.py +3 -0
  49. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +4 -0
  50. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +17 -2
  51. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +3 -0
  52. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +36 -4
  53. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +9 -0
  54. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +9 -0
  55. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +12 -1
  56. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +22 -5
  57. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +57 -32
  58. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +62 -6
  59. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +8 -9
  60. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +290 -5
  61. jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -0
  62. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +21 -3
  63. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +72 -4
  64. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +35 -3
  65. jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +212 -0
  66. jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +254 -0
  67. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +52 -2
  68. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +73 -1
  69. jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
  70. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +306 -152
  71. jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
  72. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +193 -18
  73. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +18 -8
  74. jarvis/jarvis_code_agent/lint.py +258 -27
  75. jarvis/jarvis_code_agent/utils.py +0 -1
  76. jarvis/jarvis_code_analysis/code_review.py +19 -24
  77. jarvis/jarvis_data/config_schema.json +53 -26
  78. jarvis/jarvis_git_squash/main.py +4 -5
  79. jarvis/jarvis_git_utils/git_commiter.py +44 -49
  80. jarvis/jarvis_mcp/sse_mcp_client.py +20 -27
  81. jarvis/jarvis_mcp/stdio_mcp_client.py +11 -12
  82. jarvis/jarvis_mcp/streamable_mcp_client.py +15 -14
  83. jarvis/jarvis_memory_organizer/memory_organizer.py +55 -74
  84. jarvis/jarvis_methodology/main.py +32 -48
  85. jarvis/jarvis_multi_agent/__init__.py +79 -61
  86. jarvis/jarvis_multi_agent/main.py +3 -7
  87. jarvis/jarvis_platform/base.py +469 -199
  88. jarvis/jarvis_platform/human.py +7 -8
  89. jarvis/jarvis_platform/kimi.py +30 -36
  90. jarvis/jarvis_platform/openai.py +65 -27
  91. jarvis/jarvis_platform/registry.py +26 -10
  92. jarvis/jarvis_platform/tongyi.py +24 -25
  93. jarvis/jarvis_platform/yuanbao.py +31 -42
  94. jarvis/jarvis_platform_manager/main.py +66 -77
  95. jarvis/jarvis_platform_manager/service.py +8 -13
  96. jarvis/jarvis_rag/cli.py +49 -51
  97. jarvis/jarvis_rag/embedding_manager.py +13 -18
  98. jarvis/jarvis_rag/llm_interface.py +8 -9
  99. jarvis/jarvis_rag/query_rewriter.py +10 -21
  100. jarvis/jarvis_rag/rag_pipeline.py +24 -27
  101. jarvis/jarvis_rag/reranker.py +4 -5
  102. jarvis/jarvis_rag/retriever.py +28 -30
  103. jarvis/jarvis_sec/__init__.py +220 -3520
  104. jarvis/jarvis_sec/agents.py +143 -0
  105. jarvis/jarvis_sec/analysis.py +276 -0
  106. jarvis/jarvis_sec/cli.py +29 -6
  107. jarvis/jarvis_sec/clustering.py +1439 -0
  108. jarvis/jarvis_sec/file_manager.py +427 -0
  109. jarvis/jarvis_sec/parsers.py +73 -0
  110. jarvis/jarvis_sec/prompts.py +268 -0
  111. jarvis/jarvis_sec/report.py +83 -4
  112. jarvis/jarvis_sec/review.py +453 -0
  113. jarvis/jarvis_sec/utils.py +499 -0
  114. jarvis/jarvis_sec/verification.py +848 -0
  115. jarvis/jarvis_sec/workflow.py +7 -0
  116. jarvis/jarvis_smart_shell/main.py +38 -87
  117. jarvis/jarvis_stats/cli.py +1 -1
  118. jarvis/jarvis_stats/stats.py +7 -7
  119. jarvis/jarvis_stats/storage.py +15 -21
  120. jarvis/jarvis_tools/clear_memory.py +3 -20
  121. jarvis/jarvis_tools/cli/main.py +20 -23
  122. jarvis/jarvis_tools/edit_file.py +1066 -0
  123. jarvis/jarvis_tools/execute_script.py +42 -21
  124. jarvis/jarvis_tools/file_analyzer.py +6 -9
  125. jarvis/jarvis_tools/generate_new_tool.py +11 -20
  126. jarvis/jarvis_tools/lsp_client.py +1552 -0
  127. jarvis/jarvis_tools/methodology.py +2 -3
  128. jarvis/jarvis_tools/read_code.py +1525 -87
  129. jarvis/jarvis_tools/read_symbols.py +2 -3
  130. jarvis/jarvis_tools/read_webpage.py +7 -10
  131. jarvis/jarvis_tools/registry.py +370 -181
  132. jarvis/jarvis_tools/retrieve_memory.py +20 -19
  133. jarvis/jarvis_tools/rewrite_file.py +105 -0
  134. jarvis/jarvis_tools/save_memory.py +3 -15
  135. jarvis/jarvis_tools/search_web.py +3 -7
  136. jarvis/jarvis_tools/sub_agent.py +17 -6
  137. jarvis/jarvis_tools/sub_code_agent.py +14 -16
  138. jarvis/jarvis_tools/virtual_tty.py +54 -32
  139. jarvis/jarvis_utils/clipboard.py +7 -10
  140. jarvis/jarvis_utils/config.py +98 -63
  141. jarvis/jarvis_utils/embedding.py +5 -5
  142. jarvis/jarvis_utils/fzf.py +8 -8
  143. jarvis/jarvis_utils/git_utils.py +81 -67
  144. jarvis/jarvis_utils/input.py +24 -49
  145. jarvis/jarvis_utils/jsonnet_compat.py +465 -0
  146. jarvis/jarvis_utils/methodology.py +33 -35
  147. jarvis/jarvis_utils/utils.py +245 -202
  148. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.8.dist-info}/METADATA +205 -70
  149. jarvis_ai_assistant-0.7.8.dist-info/RECORD +218 -0
  150. jarvis/jarvis_agent/edit_file_handler.py +0 -584
  151. jarvis/jarvis_agent/rewrite_file_handler.py +0 -141
  152. jarvis/jarvis_agent/task_planner.py +0 -496
  153. jarvis/jarvis_platform/ai8.py +0 -332
  154. jarvis/jarvis_tools/ask_user.py +0 -54
  155. jarvis_ai_assistant-0.7.0.dist-info/RECORD +0 -192
  156. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.8.dist-info}/WHEEL +0 -0
  157. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.8.dist-info}/entry_points.txt +0 -0
  158. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.8.dist-info}/licenses/LICENSE +0 -0
  159. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.8.dist-info}/top_level.txt +0 -0
@@ -1,141 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- import os
3
- import re
4
- from typing import Any, List, Tuple
5
-
6
- from jarvis.jarvis_agent.output_handler import OutputHandler
7
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
8
- from jarvis.jarvis_utils.tag import ct, ot
9
-
10
-
11
- class RewriteFileHandler(OutputHandler):
12
- """
13
- 处理整文件重写指令的输出处理器。
14
-
15
- 指令格式:
16
- <REWRITE file=文件路径>
17
- 新的文件完整内容
18
- </REWRITE>
19
-
20
- 等价支持以下写法:
21
- <REWRITE file=文件路径>
22
- 新的文件完整内容
23
- </REWRITE>
24
-
25
- 说明:
26
- - 该处理器用于完全重写文件内容,适用于新增文件或大范围改写
27
- - 内部直接执行写入,提供失败回滚能力
28
- - 支持同一响应中包含多个 REWRITE/REWRITE 块
29
- """
30
-
31
- def __init__(self) -> None:
32
- # 允许 file 参数为单引号、双引号或无引号
33
- self.rewrite_pattern_file = re.compile(
34
- ot("REWRITE file=(?:'([^']+)'|\"([^\"]+)\"|([^>]+))")
35
- + r"\s*"
36
- + r"(.*?)"
37
- + r"\s*"
38
- + r"^"
39
- + ct("REWRITE"),
40
- re.DOTALL | re.MULTILINE,
41
- )
42
-
43
- def name(self) -> str:
44
- """获取处理器名称,用于操作列表展示"""
45
- return "REWRITE"
46
-
47
- def prompt(self) -> str:
48
- """返回用户提示,描述使用方法与格式"""
49
- return f"""文件重写指令格式:
50
- {ot("REWRITE file=文件路径")}
51
- 新的文件完整内容
52
- {ct("REWRITE")}
53
-
54
- 注意:
55
- - {ot("REWRITE")}、{ct("REWRITE")} 必须出现在行首,否则不生效(会被忽略)
56
- - 整文件重写会完全替换文件内容,如需局部修改请使用 PATCH 操作
57
- - 该操作由处理器直接执行,具备失败回滚能力"""
58
-
59
- def can_handle(self, response: str) -> bool:
60
- """判断响应中是否包含 REWRITE/REWRITE 指令"""
61
- return bool(self.rewrite_pattern_file.search(response))
62
-
63
- def handle(self, response: str, agent: Any) -> Tuple[bool, str]:
64
- """解析并执行整文件重写指令"""
65
- rewrites = self._parse_rewrites(response)
66
- if not rewrites:
67
- return False, "未找到有效的文件重写指令"
68
-
69
- # 记录 REWRITE 操作调用统计
70
- try:
71
- from jarvis.jarvis_stats.stats import StatsManager
72
-
73
- StatsManager.increment("rewrite_file", group="tool")
74
- except Exception:
75
- # 统计失败不影响主流程
76
- pass
77
-
78
- results: List[str] = []
79
-
80
- for file_path, content in rewrites:
81
- abs_path = os.path.abspath(file_path)
82
- original_content = None
83
- processed = False
84
- try:
85
- file_exists = os.path.exists(abs_path)
86
- if file_exists:
87
- with open(abs_path, "r", encoding="utf-8") as rf:
88
- original_content = rf.read()
89
- os.makedirs(os.path.dirname(abs_path), exist_ok=True)
90
- with open(abs_path, "w", encoding="utf-8") as wf:
91
- wf.write(content)
92
- processed = True
93
- results.append(f"✅ 文件 {abs_path} 重写成功")
94
- # 记录成功处理的文件(使用绝对路径)
95
- if agent:
96
- files = agent.get_user_data("files")
97
- if files:
98
- if abs_path not in files:
99
- files.append(abs_path)
100
- else:
101
- files = [abs_path]
102
- agent.set_user_data("files", files)
103
- except Exception as e:
104
- # 回滚已修改内容
105
- try:
106
- if processed:
107
- if original_content is None:
108
- if os.path.exists(abs_path):
109
- os.remove(abs_path)
110
- else:
111
- with open(abs_path, "w", encoding="utf-8") as wf:
112
- wf.write(original_content)
113
- except Exception:
114
- pass
115
- PrettyOutput.print(f"文件重写失败: {str(e)}", OutputType.ERROR)
116
- results.append(f"❌ 文件 {abs_path} 重写失败: {str(e)}")
117
-
118
- summary = "\n".join(results)
119
- # 按现有 EditFileHandler 约定,始终返回 (False, summary) 以继续主循环
120
- return False, summary
121
-
122
- def _parse_rewrites(self, response: str) -> List[Tuple[str, str]]:
123
- """
124
- 解析响应中的 REWRITE/REWRITE 指令块。
125
- 返回列表 [(file_path, content), ...],按在响应中的出现顺序排序
126
- """
127
- items: List[Tuple[str, str]] = []
128
- matches: List[Tuple[int, Any]] = []
129
- for m in self.rewrite_pattern_file.finditer(response):
130
- matches.append((m.start(), m))
131
-
132
- # 按出现顺序排序
133
- matches.sort(key=lambda x: x[0])
134
-
135
- for _, m in matches:
136
- file_path = m.group(1) or m.group(2) or m.group(3) or ""
137
- file_path = file_path.strip()
138
- content = m.group(4)
139
- if file_path:
140
- items.append((file_path, content))
141
- return items
@@ -1,496 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- TaskPlanner: 任务规划与子任务调度器
4
-
5
- 职责:
6
- - 判断是否需要拆分任务
7
- - 解析 <PLAN> YAML 列表
8
- - 为每个子任务创建子Agent并执行
9
- - 汇总所有子任务执行结果并写回父Agent上下文(包含 <PLAN>/<SUB_TASK_RESULTS>/<RESULT_SUMMARY>)
10
- """
11
-
12
- from typing import Any, List
13
- import re
14
-
15
- import yaml # type: ignore
16
-
17
- from jarvis.jarvis_agent.utils import join_prompts
18
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
19
-
20
-
21
- class TaskPlanner:
22
- """将 Agent 的任务规划逻辑封装为独立类,便于维护与复用。"""
23
-
24
- def __init__(self, agent: Any, plan_depth: int = 0, plan_max_depth: int = 2) -> None:
25
- """
26
- 参数:
27
- agent: 父Agent实例(须提供以下能力)
28
- - _create_temp_model(system_prompt: str) -> BasePlatform
29
- - _build_child_agent_params(name: str, description: str) -> dict
30
- - name, session, plan 等属性
31
- plan_depth: 当前规划深度(由外部在构造时传入)
32
- plan_max_depth: 规划最大深度(由外部在构造时传入)
33
- """
34
- self.agent = agent
35
- try:
36
- self.plan_depth = int(plan_depth)
37
- except Exception:
38
- self.plan_depth = 0
39
- try:
40
- self.plan_max_depth = int(plan_max_depth)
41
- except Exception:
42
- self.plan_max_depth = 2
43
-
44
- def _print_plan_status(
45
- self,
46
- subtasks: List[str],
47
- current_index: int,
48
- is_starting: bool = True,
49
- ) -> None:
50
- """
51
- 打印当前计划状态
52
-
53
- 参数:
54
- subtasks: 当前计划的所有子任务列表
55
- current_index: 当前任务索引(从1开始,0表示还未开始)
56
- is_starting: True表示任务开始,False表示任务完成
57
- """
58
- if not subtasks:
59
- return
60
-
61
- status_lines = ["📋 当前计划状态:"]
62
- status_lines.append("─" * 60)
63
-
64
- for idx, task in enumerate(subtasks, 1):
65
- if current_index == 0:
66
- # 全局视图:所有任务都是待执行
67
- status_lines.append(f"⏳ [{idx}] {task}")
68
- elif idx < current_index:
69
- # 已完成的任务
70
- status_lines.append(f"✅ [{idx}] {task}")
71
- elif idx == current_index:
72
- if is_starting:
73
- # 当前正在执行的任务
74
- status_lines.append(f"🔄 [{idx}] {task} ← 当前节点")
75
- else:
76
- # 刚完成的任务
77
- status_lines.append(f"✅ [{idx}] {task} ← 刚完成")
78
- else:
79
- # 待执行的任务
80
- status_lines.append(f"⏳ [{idx}] {task}")
81
-
82
- status_lines.append("─" * 60)
83
- if current_index == 0:
84
- status_lines.append(f"总任务数: {len(subtasks)},准备开始执行")
85
- elif is_starting:
86
- status_lines.append(f"进度: {current_index - 1}/{len(subtasks)} 已完成,正在执行第 {current_index} 个")
87
- else:
88
- status_lines.append(f"进度: {current_index}/{len(subtasks)} 已完成")
89
-
90
- PrettyOutput.print("\n".join(status_lines), OutputType.INFO)
91
-
92
- def _evaluate_plan_adjustment(
93
- self,
94
- task_text: str,
95
- original_plan: List[str],
96
- completed_tasks: List[str],
97
- completed_results: List[str],
98
- remaining_tasks: List[str],
99
- ) -> Any:
100
- """
101
- 评估计划是否需要调整
102
-
103
- 参数:
104
- task_text: 原始任务描述
105
- original_plan: 原始完整计划
106
- completed_tasks: 已完成的子任务列表
107
- completed_results: 已完成子任务的结果列表
108
- remaining_tasks: 剩余待执行的子任务列表
109
-
110
- 返回:
111
- dict: 包含 need_adjust 和 adjusted_plan 的字典,如果不需要调整则返回 None
112
- """
113
- try:
114
- evaluation_sys = (
115
- "你是一个任务计划评估助手。请根据已完成子任务的结果,评估剩余计划是否需要调整。\n"
116
- "当需要调整时,仅按以下结构输出:\n"
117
- "<PLAN_ADJUSTMENT>\n"
118
- "need_adjust: true\n"
119
- "reason: \"调整原因说明\"\n"
120
- "adjusted_plan:\n"
121
- " - 调整后的剩余子任务1\n"
122
- " - 调整后的剩余子任务2\n"
123
- "</PLAN_ADJUSTMENT>\n"
124
- "注意:adjusted_plan 必须是有效的 YAML 列表,仅包含字符串项;只能调整当前层级的剩余计划,不能修改已完成的子任务。\n"
125
- "当不需要调整时,仅输出:\n"
126
- "<PLAN_ADJUSTMENT>\n"
127
- "need_adjust: false\n"
128
- "</PLAN_ADJUSTMENT>\n"
129
- "禁止输出任何额外解释。"
130
- )
131
-
132
- completed_results_text = "\n".join(completed_results) if completed_results else "无"
133
- remaining_tasks_text = "\n".join(f"- {t}" for t in remaining_tasks) if remaining_tasks else "无"
134
-
135
- eval_prompt = (
136
- f"原始任务:\n{task_text}\n\n"
137
- f"原始完整计划:\n" + "\n".join(f"- {t}" for t in original_plan) + "\n\n"
138
- "已完成的子任务:\n" + "\n".join(f"- {t}" for t in completed_tasks) + "\n\n"
139
- f"已完成子任务的结果:\n{completed_results_text}\n\n"
140
- f"剩余待执行的子任务:\n{remaining_tasks_text}\n\n"
141
- "请评估剩余计划是否需要调整。如果需要调整,请提供调整后的剩余子任务列表(只能调整剩余部分,不能修改已完成的子任务)。"
142
- )
143
-
144
- # 直接使用agent的大模型接口(将系统提示词合并到prompt中)
145
- full_prompt = f"{evaluation_sys}\n\n{eval_prompt}"
146
- if hasattr(self.agent, "model") and hasattr(self.agent.model, "chat_until_success"):
147
- eval_resp = self.agent.model.chat_until_success(full_prompt) # type: ignore
148
- else:
149
- # 回退到临时模型
150
- temp_model = self.agent._create_temp_model(evaluation_sys)
151
- eval_resp = temp_model.chat_until_success(eval_prompt) # type: ignore
152
-
153
- if not eval_resp:
154
- return None
155
-
156
- text = str(eval_resp).strip()
157
- # 解析 <PLAN_ADJUSTMENT> 块
158
- m = re.search(
159
- r"<\s*PLAN_ADJUSTMENT\s*>\s*(.*?)\s*<\s*/\s*PLAN_ADJUSTMENT\s*>",
160
- text,
161
- re.IGNORECASE | re.DOTALL,
162
- )
163
- if m:
164
- block = m.group(1)
165
- try:
166
- data = yaml.safe_load(block)
167
- if isinstance(data, dict):
168
- need_adjust = data.get("need_adjust", False)
169
- if need_adjust:
170
- adjusted_plan = data.get("adjusted_plan", [])
171
- reason = data.get("reason", "")
172
- if adjusted_plan and isinstance(adjusted_plan, list):
173
- # 验证调整后的计划是有效的字符串列表
174
- valid_plan = []
175
- for item in adjusted_plan:
176
- if isinstance(item, str):
177
- s = item.strip()
178
- if s:
179
- valid_plan.append(s)
180
- if valid_plan:
181
- PrettyOutput.print(
182
- f"计划评估:需要调整。原因:{reason}",
183
- OutputType.INFO
184
- )
185
- return {
186
- "need_adjust": True,
187
- "reason": reason,
188
- "adjusted_plan": valid_plan,
189
- }
190
- else:
191
- return {"need_adjust": False}
192
- except Exception as e:
193
- PrettyOutput.print(
194
- f"解析计划调整结果失败: {e}", OutputType.WARNING
195
- )
196
- return None
197
- return None
198
- except Exception as e:
199
- PrettyOutput.print(f"评估计划调整失败: {e}", OutputType.WARNING)
200
- return None
201
-
202
- def maybe_plan_and_dispatch(self, task_text: str) -> None:
203
- """
204
- 当启用 agent.plan 时,调用临时模型评估是否需要拆分任务并执行子任务。
205
- - 若模型返回 <DONT_NEED/>,则直接返回不做任何修改;
206
- - 若返回 <SUB_TASK> 块,则解析每行以“- ”开头的子任务,逐个创建子Agent执行;
207
- - 将子任务与结果以结构化块写回到 agent.session.prompt,随后由主循环继续处理。
208
- """
209
- if not getattr(self.agent, "plan", False):
210
- return
211
-
212
- # 深度限制检查:当当前规划深度已达到或超过上限时,禁止继续规划
213
- try:
214
- current_depth = int(self.plan_depth)
215
- except Exception:
216
- current_depth = 0
217
- try:
218
- max_depth = int(self.plan_max_depth)
219
- except Exception:
220
- max_depth = 2
221
-
222
- if current_depth >= max_depth:
223
- PrettyOutput.print(
224
- f"已达到任务规划最大深度({max_depth}),本层不再进行规划。", OutputType.INFO
225
- )
226
- return
227
-
228
- try:
229
- PrettyOutput.print("任务规划启动,评估是否需要拆分...", OutputType.INFO)
230
- planning_sys = (
231
- "你是一个任务规划助手。请判断是否需要拆分任务。\n"
232
- "当需要拆分时,仅按以下结构输出:\n"
233
- "<PLAN>\n- 子任务1\n- 子任务2\n</PLAN>\n"
234
- "示例:\n"
235
- "<PLAN>\n- 分析当前任务,提取需要修改的文件列表\n- 修改配置默认值并更新相关 schema\n- 更新文档中对该默认值的描述\n</PLAN>\n"
236
- "注意:必须拆分为独立可完成的任务;不要将步骤拆分太细,一般不要超过4个步骤;子任务应具备明确的输入与可验证的输出;若超过4步将被判定为拆分失败并重试。\n"
237
- "要求:<PLAN> 内必须是有效 YAML 列表,仅包含字符串项;禁止输出任何额外解释。\n"
238
- "当不需要拆分时,仅输出:\n<DONT_NEED/>\n"
239
- "禁止输出任何额外解释。"
240
- )
241
- temp_model = self.agent._create_temp_model(planning_sys)
242
- plan_prompt = f"任务:\n{task_text}\n\n请严格按要求只输出结构化标签块。"
243
- plan_resp = temp_model.chat_until_success(plan_prompt) # type: ignore
244
- if not plan_resp:
245
- PrettyOutput.print("任务规划模型未返回有效响应。", OutputType.WARNING)
246
- return
247
- except Exception as e:
248
- # 规划失败不影响主流程
249
- PrettyOutput.print(f"任务规划失败: {e}", OutputType.ERROR)
250
- return
251
-
252
- text = str(plan_resp).strip()
253
- # 不需要拆分
254
- if re.search(r"<\s*DONT_NEED\s*/\s*>", text, re.IGNORECASE):
255
- PrettyOutput.print("任务规划完成:无需拆分。", OutputType.SUCCESS)
256
- return
257
-
258
- # 解析 <SUB_TASK> 块
259
- m = re.search(
260
- r"<\s*PLAN\s*>\s*(.*?)\s*<\s*/\s*PLAN\s*>",
261
- text,
262
- re.IGNORECASE | re.DOTALL,
263
- )
264
- subtasks: List[str] = []
265
- if m:
266
- block = m.group(1)
267
- try:
268
- data = yaml.safe_load(block)
269
- if isinstance(data, list):
270
- for item in data:
271
- if isinstance(item, str):
272
- s = item.strip()
273
- if s:
274
- subtasks.append(s)
275
- else:
276
- PrettyOutput.print("任务规划提示:无需拆分。", OutputType.INFO)
277
- except Exception:
278
- PrettyOutput.print("任务规划提示:无需拆分。", OutputType.INFO)
279
- else:
280
- PrettyOutput.print("任务规划提示:无需拆分。", OutputType.INFO)
281
-
282
- # 若子任务数量超过上限,则视为拆分失败并进行一次重试
283
- max_steps = 4
284
- if len(subtasks) > max_steps:
285
- PrettyOutput.print(
286
- f"任务拆分产生 {len(subtasks)} 个子任务,超过上限 {max_steps},视为拆分失败,正在重试一次...",
287
- OutputType.WARNING,
288
- )
289
- try:
290
- retry_prompt = (
291
- f"{plan_prompt}\n"
292
- "附加约束:子任务数量不要超过4个,务必合并可合并的步骤;保持每个子任务独立可完成且具有可验证的输出。"
293
- )
294
- plan_resp = temp_model.chat_until_success(retry_prompt) # type: ignore
295
- text = str(plan_resp).strip()
296
- m = re.search(
297
- r"<\s*PLAN\s*>\s*(.*?)\s*<\s*/\s*PLAN\s*>",
298
- text,
299
- re.IGNORECASE | re.DOTALL,
300
- )
301
- subtasks = []
302
- if m:
303
- block = m.group(1)
304
- try:
305
- data = yaml.safe_load(block)
306
- if isinstance(data, list):
307
- for item in data:
308
- if isinstance(item, str):
309
- s = item.strip()
310
- if s:
311
- subtasks.append(s)
312
- except Exception:
313
- pass
314
- except Exception as e:
315
- PrettyOutput.print(f"重试任务拆分失败: {e}", OutputType.ERROR)
316
-
317
- if len(subtasks) > max_steps:
318
- PrettyOutput.print(
319
- "重试后仍超过子任务上限,放弃拆分,交由主流程处理。",
320
- OutputType.WARNING,
321
- )
322
- return
323
-
324
- if not subtasks:
325
- # 无有效子任务,直接返回
326
- PrettyOutput.print("任务规划提示:无需拆分。", OutputType.INFO)
327
- return
328
-
329
- PrettyOutput.print(f"任务已拆分为 {len(subtasks)} 个子任务:", OutputType.SUCCESS)
330
- for i, st in enumerate(subtasks, 1):
331
- PrettyOutput.print(f" {i}. {st}", OutputType.INFO)
332
-
333
- # 保存初始计划,用于评估时的参考
334
- original_plan = subtasks.copy()
335
-
336
- # 打印全局视图(完整初始计划)
337
- PrettyOutput.print("\n" + "=" * 60, OutputType.INFO)
338
- PrettyOutput.print("📊 全局计划视图", OutputType.INFO)
339
- PrettyOutput.print("=" * 60, OutputType.INFO)
340
- self._print_plan_status(subtasks, 0, is_starting=True) # 0表示还未开始执行
341
- PrettyOutput.print("=" * 60 + "\n", OutputType.INFO)
342
-
343
- # 执行子任务
344
- executed_subtask_block_lines: List[str] = ["<PLAN>"]
345
- executed_subtask_block_lines += [f"- {t}" for t in subtasks]
346
- executed_subtask_block_lines.append("</PLAN>")
347
-
348
- results_lines: List[str] = []
349
- completed_count = 0 # 已完成的任务数量(用于编号)
350
- i = 0
351
- while i < len(subtasks):
352
- st = subtasks[i]
353
- completed_count += 1
354
- i += 1
355
- try:
356
- # 打印子任务开始时的计划状态
357
- self._print_plan_status(subtasks, completed_count, is_starting=True)
358
-
359
- # 使用已完成数量显示进度,更准确
360
- remaining_count = len(subtasks) - i + 1
361
- PrettyOutput.print(
362
- f"\n🚀 开始执行子任务 {completed_count} (剩余 {remaining_count} 个): {st}",
363
- OutputType.INFO
364
- )
365
- child_kwargs = self.agent._build_child_agent_params(
366
- name=f"{self.agent.name}-child-{completed_count}",
367
- description=f"子任务执行器: {st}",
368
- )
369
- # 使用父Agent的类创建子Agent,避免循环依赖
370
- child = self.agent.__class__(**child_kwargs)
371
- # 构造子任务执行提示,包含父任务与前置子任务结果,避免背景缺失
372
- subtask_block_text = "\n".join(executed_subtask_block_lines)
373
- if results_lines:
374
- prev_results_block = "<PREVIOUS_SUB_TASK_RESULTS>\n" + "\n".join(results_lines) + "\n</PREVIOUS_SUB_TASK_RESULTS>"
375
- else:
376
- prev_results_block = "<PREVIOUS_SUB_TASK_RESULTS />"
377
- child_prompt = join_prompts([
378
- f"原始任务:\n{task_text}",
379
- f"子任务规划:\n{subtask_block_text}",
380
- f"前置子任务执行结果:\n{prev_results_block}",
381
- f"当前子任务:{st}",
382
- "请基于原始任务背景与前置结果执行当前子任务,避免重复工作;如需依赖前置产物请直接复用;如需为后续子任务提供数据,请妥善保存(可使用工具保存文件或记忆)。"
383
- ])
384
- child_result = child.run(child_prompt)
385
- result_text = "" if child_result is None else str(child_result)
386
- # 防止极端长输出导致污染,这里不做截断,交由上层摘要策略控制
387
- results_lines.append(f"- 子任务{completed_count}: {st}\n 结果: {result_text}")
388
-
389
- # 打印子任务完成时的计划状态
390
- self._print_plan_status(subtasks, completed_count, is_starting=False)
391
-
392
- PrettyOutput.print(
393
- f"\n✅ 子任务 {completed_count} 执行完成 (剩余 {remaining_count - 1} 个)。",
394
- OutputType.SUCCESS
395
- )
396
-
397
- # 除了最后一步,每步完成后评估计划是否需要调整
398
- if i < len(subtasks):
399
- try:
400
- adjustment = self._evaluate_plan_adjustment(
401
- task_text=task_text,
402
- original_plan=original_plan,
403
- completed_tasks=subtasks[:i],
404
- completed_results=results_lines,
405
- remaining_tasks=subtasks[i:],
406
- )
407
- if adjustment and adjustment.get("need_adjust", False):
408
- adjusted_plan = adjustment.get("adjusted_plan", [])
409
- if adjusted_plan and isinstance(adjusted_plan, list):
410
- # 检查调整后的计划是否超过限制
411
- max_steps = 4
412
- total_after_adjust = i + len(adjusted_plan)
413
- if total_after_adjust > max_steps:
414
- PrettyOutput.print(
415
- f"调整后的计划包含 {total_after_adjust} 个子任务,超过上限 {max_steps},拒绝调整",
416
- OutputType.WARNING
417
- )
418
- else:
419
- # 更新后续子任务列表(保留已完成的部分)
420
- subtasks = subtasks[:i] + adjusted_plan
421
- # 更新已执行的子任务块
422
- executed_subtask_block_lines = ["<PLAN>"]
423
- executed_subtask_block_lines += [f"- {t}" for t in subtasks]
424
- executed_subtask_block_lines.append("</PLAN>")
425
- PrettyOutput.print(
426
- f"\n🔄 计划已调整,剩余 {len(adjusted_plan)} 个子任务:",
427
- OutputType.INFO
428
- )
429
- for j, adjusted_task in enumerate(adjusted_plan, 1):
430
- PrettyOutput.print(
431
- f" {j}. {adjusted_task}", OutputType.INFO
432
- )
433
- # 打印调整后的计划状态(当前任务已完成,下一个任务待执行)
434
- self._print_plan_status(subtasks, completed_count, is_starting=False)
435
- except Exception as e:
436
- # 评估失败不影响主流程
437
- PrettyOutput.print(
438
- f"计划评估失败: {e},继续执行原计划", OutputType.WARNING
439
- )
440
- except Exception as e:
441
- results_lines.append(f"- 子任务{completed_count}: {st}\n 结果: 执行失败,原因: {e}")
442
- PrettyOutput.print(
443
- f"子任务 {completed_count} 执行失败: {e}",
444
- OutputType.ERROR
445
- )
446
-
447
- subtask_block = "\n".join(executed_subtask_block_lines)
448
- results_block = "<SUB_TASK_RESULTS>\n" + "\n".join(results_lines) + "\n</SUB_TASK_RESULTS>"
449
-
450
- PrettyOutput.print("所有子任务执行完毕,正在整合结果...", OutputType.INFO)
451
- # 先对所有子任务结果进行简要自动汇总,便于父Agent继续整合
452
- summary_block = "<RESULT_SUMMARY>\n无摘要(将直接使用结果详情继续)\n</RESULT_SUMMARY>"
453
- try:
454
- summarizing_sys = (
455
- "你是一个任务结果整合助手。请根据提供的原始任务、子任务清单与子任务执行结果,"
456
- "生成简明扼要的汇总与关键结论,突出已完成项、遗留风险与下一步建议。"
457
- "严格仅输出以下结构:\n"
458
- "<RESULT_SUMMARY>\n"
459
- "…你的简要汇总…\n"
460
- "</RESULT_SUMMARY>\n"
461
- "不要输出其他任何解释。"
462
- )
463
- temp_model2 = self.agent._create_temp_model(summarizing_sys)
464
- sum_prompt = (
465
- f"原始任务:\n{task_text}\n\n"
466
- f"子任务规划:\n{subtask_block}\n\n"
467
- f"子任务执行结果:\n{results_block}\n\n"
468
- "请按要求仅输出汇总块。"
469
- )
470
- sum_resp = temp_model2.chat_until_success(sum_prompt) # type: ignore
471
- if isinstance(sum_resp, str) and sum_resp.strip():
472
- s = sum_resp.strip()
473
- if not re.search(r"<\s*RESULT_SUMMARY\s*>", s, re.IGNORECASE):
474
- s = f"<RESULT_SUMMARY>\n{s}\n</RESULT_SUMMARY>"
475
- summary_block = s
476
- except Exception:
477
- # 汇总失败不影响主流程,继续使用默认占位
478
- pass
479
-
480
- # 合并回父Agent的 prompt,父Agent将基于汇总与详情继续执行
481
- try:
482
- self.agent.session.prompt = join_prompts(
483
- [
484
- f"原始任务:\n{task_text}",
485
- f"子任务规划:\n{subtask_block}",
486
- f"子任务结果汇总:\n{summary_block}",
487
- f"子任务执行结果:\n{results_block}",
488
- "请基于上述子任务结果整合并完成最终输出。",
489
- ]
490
- )
491
- except Exception:
492
- # 回退拼接
493
- self.agent.session.prompt = (
494
- f"{task_text}\n\n{subtask_block}\n\n{summary_block}\n\n{results_block}\n\n"
495
- "请基于上述子任务结果整合并完成最终输出。"
496
- )