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
|
@@ -20,7 +20,6 @@ from jarvis.jarvis_utils.methodology import (
|
|
|
20
20
|
_get_methodology_directory,
|
|
21
21
|
_load_all_methodologies,
|
|
22
22
|
)
|
|
23
|
-
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
24
23
|
|
|
25
24
|
app = typer.Typer(help="方法论管理工具")
|
|
26
25
|
|
|
@@ -55,12 +54,9 @@ def import_methodology(
|
|
|
55
54
|
indent=2,
|
|
56
55
|
)
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
)
|
|
62
|
-
except (json.JSONDecodeError, OSError) as e:
|
|
63
|
-
PrettyOutput.print(f"导入失败: {str(e)}", OutputType.ERROR)
|
|
57
|
+
print(f"✅ 成功导入 {len(import_data)} 个方法论(总计 {len(merged_data)} 个)")
|
|
58
|
+
except (ValueError, OSError) as e:
|
|
59
|
+
print(f"❌ 导入失败: {str(e)}")
|
|
64
60
|
raise typer.Exit(code=1)
|
|
65
61
|
|
|
66
62
|
|
|
@@ -73,12 +69,9 @@ def export_methodology(output_file: str = typer.Argument(..., help="导出文件
|
|
|
73
69
|
with open(output_file, "w", encoding="utf-8") as f:
|
|
74
70
|
json.dump(methodologies, f, ensure_ascii=False, indent=2)
|
|
75
71
|
|
|
76
|
-
|
|
77
|
-
f"成功导出 {len(methodologies)} 个方法论到 {output_file}",
|
|
78
|
-
OutputType.SUCCESS,
|
|
79
|
-
)
|
|
72
|
+
print(f"✅ 成功导出 {len(methodologies)} 个方法论到 {output_file}")
|
|
80
73
|
except (OSError, TypeError) as e:
|
|
81
|
-
|
|
74
|
+
print(f"❌ 导出失败: {str(e)}")
|
|
82
75
|
raise typer.Exit(code=1)
|
|
83
76
|
|
|
84
77
|
|
|
@@ -89,16 +82,17 @@ def list_methodologies():
|
|
|
89
82
|
methodologies = _load_all_methodologies()
|
|
90
83
|
|
|
91
84
|
if not methodologies:
|
|
92
|
-
|
|
85
|
+
print("ℹ️ 没有找到方法论")
|
|
93
86
|
return
|
|
94
87
|
|
|
95
88
|
# 先拼接再统一打印,避免在循环中逐条输出造成信息稀疏
|
|
96
89
|
lines = ["可用方法论:"]
|
|
97
90
|
for i, (problem_type, _) in enumerate(methodologies.items(), 1):
|
|
98
91
|
lines.append(f"{i}. {problem_type}")
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
92
|
+
joined_lines = '\n'.join(lines)
|
|
93
|
+
print(f"ℹ️ {joined_lines}")
|
|
94
|
+
except (OSError, ValueError) as e:
|
|
95
|
+
print(f"❌ 列出方法论失败: {str(e)}")
|
|
102
96
|
raise typer.Exit(code=1)
|
|
103
97
|
|
|
104
98
|
|
|
@@ -144,22 +138,20 @@ def extract_methodology(
|
|
|
144
138
|
"""
|
|
145
139
|
|
|
146
140
|
# 调用大模型平台提取方法论
|
|
147
|
-
|
|
141
|
+
print("ℹ️ 正在提取方法论...")
|
|
148
142
|
try:
|
|
149
143
|
response = platform.chat_until_success(prompt)
|
|
150
144
|
except Exception as e:
|
|
151
|
-
|
|
152
|
-
|
|
145
|
+
print("❌ 提取失败")
|
|
146
|
+
print(f"❌ 提取方法论失败: {str(e)}")
|
|
153
147
|
raise typer.Exit(code=1)
|
|
154
148
|
|
|
155
149
|
# 提取YAML部分
|
|
156
150
|
methodologies_start = response.find("<methodologies>") + len("<methodologies>")
|
|
157
151
|
methodologies_end = response.find("</methodologies>")
|
|
158
152
|
if methodologies_start == -1 or methodologies_end == -1:
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
"大模型未返回有效的<methodologies>格式", OutputType.ERROR
|
|
162
|
-
)
|
|
153
|
+
print("❌ 响应格式无效")
|
|
154
|
+
print("❌ 大模型未返回有效的<methodologies>格式")
|
|
163
155
|
raise typer.Exit(code=1)
|
|
164
156
|
|
|
165
157
|
yaml_content = response[methodologies_start:methodologies_end].strip()
|
|
@@ -170,14 +162,14 @@ def extract_methodology(
|
|
|
170
162
|
item["problem_type"]: item["content"] for item in data
|
|
171
163
|
}
|
|
172
164
|
except (yaml.YAMLError, KeyError, TypeError) as e:
|
|
173
|
-
|
|
174
|
-
|
|
165
|
+
print("❌ YAML解析失败")
|
|
166
|
+
print(f"❌ YAML解析错误: {str(e)}")
|
|
175
167
|
raise typer.Exit(code=1)
|
|
176
168
|
|
|
177
169
|
if not extracted_methodologies:
|
|
178
|
-
|
|
170
|
+
print("⚠️ 未提取到有效方法论")
|
|
179
171
|
return
|
|
180
|
-
|
|
172
|
+
print("✅ 提取到有效方法论")
|
|
181
173
|
|
|
182
174
|
# 加载现有方法论
|
|
183
175
|
existing_methodologies = _load_all_methodologies()
|
|
@@ -199,12 +191,9 @@ def extract_methodology(
|
|
|
199
191
|
indent=2,
|
|
200
192
|
)
|
|
201
193
|
|
|
202
|
-
|
|
203
|
-
f"成功从文件提取 {len(extracted_methodologies)} 个方法论(总计 {len(merged_data)} 个)",
|
|
204
|
-
OutputType.SUCCESS,
|
|
205
|
-
)
|
|
194
|
+
print(f"✅ 成功从文件提取 {len(extracted_methodologies)} 个方法论(总计 {len(merged_data)} 个)")
|
|
206
195
|
except Exception as e:
|
|
207
|
-
|
|
196
|
+
print(f"❌ 提取失败: {str(e)}")
|
|
208
197
|
raise typer.Exit(code=1)
|
|
209
198
|
|
|
210
199
|
|
|
@@ -247,22 +236,20 @@ def extract_methodology_from_url(
|
|
|
247
236
|
6. 内容字段使用|保留多行格式
|
|
248
237
|
"""
|
|
249
238
|
# 调用大模型平台提取方法论
|
|
250
|
-
|
|
239
|
+
print("ℹ️ 正在从URL提取方法论...")
|
|
251
240
|
try:
|
|
252
241
|
response = platform.chat_until_success(prompt)
|
|
253
242
|
except Exception as e:
|
|
254
|
-
|
|
255
|
-
|
|
243
|
+
print("❌ 提取失败")
|
|
244
|
+
print(f"❌ 提取方法论失败: {str(e)}")
|
|
256
245
|
raise typer.Exit(code=1)
|
|
257
246
|
|
|
258
247
|
# 提取YAML部分
|
|
259
248
|
methodologies_start = response.find("<methodologies>") + len("<methodologies>")
|
|
260
249
|
methodologies_end = response.find("</methodologies>")
|
|
261
250
|
if methodologies_start == -1 or methodologies_end == -1:
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
"大模型未返回有效的<methodologies>格式", OutputType.ERROR
|
|
265
|
-
)
|
|
251
|
+
print("❌ 响应格式无效")
|
|
252
|
+
print("❌ 大模型未返回有效的<methodologies>格式")
|
|
266
253
|
raise typer.Exit(code=1)
|
|
267
254
|
|
|
268
255
|
yaml_content = response[methodologies_start:methodologies_end].strip()
|
|
@@ -273,14 +260,14 @@ def extract_methodology_from_url(
|
|
|
273
260
|
item["problem_type"]: item["content"] for item in data
|
|
274
261
|
}
|
|
275
262
|
except (yaml.YAMLError, KeyError, TypeError) as e:
|
|
276
|
-
|
|
277
|
-
|
|
263
|
+
print("❌ YAML解析失败")
|
|
264
|
+
print(f"❌ YAML解析错误: {str(e)}")
|
|
278
265
|
raise typer.Exit(code=1)
|
|
279
266
|
|
|
280
267
|
if not extracted_methodologies:
|
|
281
|
-
|
|
268
|
+
print("⚠️ 未提取到有效方法论")
|
|
282
269
|
return
|
|
283
|
-
|
|
270
|
+
print("✅ 提取到有效方法论")
|
|
284
271
|
|
|
285
272
|
# 加载现有方法论
|
|
286
273
|
existing_methodologies = _load_all_methodologies()
|
|
@@ -302,12 +289,9 @@ def extract_methodology_from_url(
|
|
|
302
289
|
indent=2,
|
|
303
290
|
)
|
|
304
291
|
|
|
305
|
-
|
|
306
|
-
f"成功从URL提取 {len(extracted_methodologies)} 个方法论(总计 {len(merged_data)} 个)",
|
|
307
|
-
OutputType.SUCCESS,
|
|
308
|
-
)
|
|
292
|
+
print(f"✅ 成功从URL提取 {len(extracted_methodologies)} 个方法论(总计 {len(merged_data)} 个)")
|
|
309
293
|
except Exception as e:
|
|
310
|
-
|
|
294
|
+
print(f"❌ 从URL提取失败: {str(e)}")
|
|
311
295
|
raise typer.Exit(code=1)
|
|
312
296
|
|
|
313
297
|
|
|
@@ -1,25 +1,42 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
+
from jarvis.jarvis_utils.jsonnet_compat import loads as json_loads
|
|
2
3
|
import re
|
|
3
4
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
4
5
|
|
|
5
|
-
import yaml
|
|
6
|
-
|
|
7
6
|
from jarvis.jarvis_agent import Agent
|
|
8
7
|
from jarvis.jarvis_agent.output_handler import OutputHandler
|
|
9
8
|
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
10
|
-
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
11
9
|
from jarvis.jarvis_utils.tag import ct, ot
|
|
12
10
|
|
|
13
11
|
|
|
14
12
|
class MultiAgent(OutputHandler):
|
|
15
|
-
def __init__(self, agents_config: List[Dict], main_agent_name: str):
|
|
13
|
+
def __init__(self, agents_config: List[Dict], main_agent_name: str, common_system_prompt: str = ""):
|
|
16
14
|
self.agents_config = agents_config
|
|
17
15
|
self.agents_config_map = {c["name"]: c for c in agents_config}
|
|
18
16
|
self.agents: Dict[str, Agent] = {}
|
|
19
17
|
self.main_agent_name = main_agent_name
|
|
20
18
|
self.original_question: Optional[str] = None
|
|
19
|
+
self.common_system_prompt: str = common_system_prompt
|
|
21
20
|
|
|
22
21
|
def prompt(self) -> str:
|
|
22
|
+
_multiline_example_msg = """ {
|
|
23
|
+
"content": |||
|
|
24
|
+
第一行:直接换行,无需 \\n
|
|
25
|
+
第二行:包含"双引号",无需转义
|
|
26
|
+
第三行:包含'单引号',直接写
|
|
27
|
+
第四行:支持缩进保留
|
|
28
|
+
|||
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
或使用 ``` 代替 |||:
|
|
32
|
+
{
|
|
33
|
+
"content": ```
|
|
34
|
+
第一行:直接换行,无需 \\n
|
|
35
|
+
第二行:包含"双引号",无需转义
|
|
36
|
+
第三行:包含'单引号',直接写
|
|
37
|
+
第四行:支持缩进保留
|
|
38
|
+
```
|
|
39
|
+
}"""
|
|
23
40
|
return f"""
|
|
24
41
|
# 多智能体消息发送
|
|
25
42
|
|
|
@@ -30,22 +47,25 @@ class MultiAgent(OutputHandler):
|
|
|
30
47
|
- **明确性原则**:清晰表达意图、需求和期望结果
|
|
31
48
|
- **上下文保留**:在消息中包含足够的背景信息
|
|
32
49
|
|
|
33
|
-
###
|
|
50
|
+
### 消息格式标准(Jsonnet)
|
|
34
51
|
```
|
|
35
52
|
{ot("SEND_MESSAGE")}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
{{
|
|
54
|
+
"to": "智能体名称",
|
|
55
|
+
"content": |||
|
|
56
|
+
# 消息主题
|
|
57
|
+
## 背景信息
|
|
58
|
+
[提供必要的上下文和背景]
|
|
59
|
+
## 具体需求
|
|
60
|
+
[明确表达期望完成的任务]
|
|
61
|
+
## 相关资源
|
|
62
|
+
[列出相关文档、数据或工具]
|
|
63
|
+
## 期望结果
|
|
64
|
+
[描述期望的输出格式和内容]
|
|
65
|
+
## 下一步计划
|
|
66
|
+
[描述下一步的计划和行动]
|
|
67
|
+
|||
|
|
68
|
+
}}
|
|
49
69
|
{ct("SEND_MESSAGE")}
|
|
50
70
|
```
|
|
51
71
|
|
|
@@ -53,31 +73,186 @@ content: |2
|
|
|
53
73
|
|
|
54
74
|
```
|
|
55
75
|
{ot("SEND_MESSAGE")}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
76
|
+
{{
|
|
77
|
+
"to": "智能体名称",
|
|
78
|
+
"content": |||
|
|
79
|
+
# 消息主题
|
|
80
|
+
## 任务结果
|
|
81
|
+
[任务完成结果,用于反馈]
|
|
82
|
+
|||
|
|
83
|
+
}}
|
|
61
84
|
{ct("SEND_MESSAGE")}
|
|
62
85
|
```
|
|
63
86
|
|
|
87
|
+
**Jsonnet 格式说明**:
|
|
88
|
+
- 可以使用双引号 "..." 或单引号 '...' 包裹字符串
|
|
89
|
+
- **多行字符串推荐使用 ||| 或 ``` 分隔符包裹**,直接换行无需转义,支持保留缩进
|
|
90
|
+
示例:
|
|
91
|
+
{_multiline_example_msg}
|
|
92
|
+
- 支持尾随逗号
|
|
93
|
+
|
|
64
94
|
## 可用智能体资源
|
|
65
95
|
{chr(10).join([f"- **{c['name']}**: {c.get('description', '')}" for c in self.agents_config])}
|
|
66
96
|
"""
|
|
67
97
|
|
|
68
98
|
def can_handle(self, response: str) -> bool:
|
|
69
|
-
|
|
99
|
+
# 只要检测到 SEND_MESSAGE 起始标签即认为可处理,
|
|
100
|
+
# 即便内容有误也由 handle 返回明确错误与修复指导
|
|
101
|
+
return ot("SEND_MESSAGE") in response
|
|
70
102
|
|
|
71
103
|
def handle(self, response: str, agent: Any) -> Tuple[bool, Any]:
|
|
72
|
-
|
|
73
|
-
|
|
104
|
+
"""
|
|
105
|
+
处理 SEND_MESSAGE。若存在格式/解析/字段/目标等问题,返回明确错误原因与修复指导。
|
|
106
|
+
"""
|
|
107
|
+
# 优先使用解析器获取“正确路径”结果
|
|
108
|
+
parsed = self._extract_send_msg(response)
|
|
109
|
+
if len(parsed) == 1:
|
|
110
|
+
msg = parsed[0]
|
|
111
|
+
# 字段校验
|
|
112
|
+
to_val = msg.get("to")
|
|
113
|
+
content_val = msg.get("content")
|
|
114
|
+
missing = []
|
|
115
|
+
if not to_val:
|
|
116
|
+
missing.append("to")
|
|
117
|
+
if content_val is None or (isinstance(content_val, str) and content_val.strip() == ""):
|
|
118
|
+
# 允许空格/空行被视为缺失
|
|
119
|
+
missing.append("content")
|
|
120
|
+
if missing:
|
|
121
|
+
guidance = (
|
|
122
|
+
"SEND_MESSAGE 字段缺失或为空:"
|
|
123
|
+
+ ", ".join(missing)
|
|
124
|
+
+ "\n修复建议:\n"
|
|
125
|
+
"- 必须包含 to 和 content 字段\n"
|
|
126
|
+
"- to: 目标智能体名称(字符串)\n"
|
|
127
|
+
"- content: 发送内容(字符串)\n"
|
|
128
|
+
"示例:\n"
|
|
129
|
+
f"{ot('SEND_MESSAGE')}\n"
|
|
130
|
+
'{{\n "to": "目标Agent名称",\n "content": "这里填写要发送的消息内容"\n}}\n'
|
|
131
|
+
f"{ct('SEND_MESSAGE')}"
|
|
132
|
+
)
|
|
133
|
+
return False, guidance
|
|
134
|
+
# 类型校验
|
|
135
|
+
if not isinstance(to_val, str):
|
|
136
|
+
return False, "SEND_MESSAGE 字段类型错误:to 必须为字符串。修复建议:将 to 改为字符串,如 to: ChapterPolisher"
|
|
137
|
+
if not isinstance(content_val, str):
|
|
138
|
+
return False, "SEND_MESSAGE 字段类型错误:content 必须为字符串。修复建议:将 content 改为字符串"
|
|
139
|
+
# 目标校验
|
|
140
|
+
if to_val not in self.agents_config_map:
|
|
141
|
+
available = ", ".join(self.agents_config_map.keys())
|
|
142
|
+
return (
|
|
143
|
+
False,
|
|
144
|
+
f"目标智能体不存在:'{to_val}' 不在可用列表中。\n"
|
|
145
|
+
f"可用智能体:[{available}]\n"
|
|
146
|
+
"修复建议:\n"
|
|
147
|
+
"- 将 to 修改为上述可用智能体之一\n"
|
|
148
|
+
"- 或检查配置中是否遗漏了该智能体的定义"
|
|
149
|
+
)
|
|
150
|
+
# 通过校验,交给上层发送
|
|
151
|
+
return True, {"to": to_val, "content": content_val}
|
|
152
|
+
elif len(parsed) > 1:
|
|
153
|
+
return (
|
|
154
|
+
False,
|
|
155
|
+
"检测到多个 SEND_MESSAGE 块。一次只能发送一个消息。\n修复建议:合并消息或分多轮发送,每轮仅保留一个 SEND_MESSAGE 块。"
|
|
156
|
+
)
|
|
157
|
+
# 未成功解析,进行诊断并返回可操作指导
|
|
158
|
+
try:
|
|
159
|
+
normalized = response.replace("\r\n", "\n").replace("\r", "\n")
|
|
160
|
+
except Exception:
|
|
161
|
+
normalized = response
|
|
162
|
+
ot_tag = ot("SEND_MESSAGE")
|
|
163
|
+
ct_tag = ct("SEND_MESSAGE")
|
|
164
|
+
has_open = ot_tag in normalized
|
|
165
|
+
has_close = ct_tag in normalized
|
|
166
|
+
if has_open and not has_close:
|
|
167
|
+
return (
|
|
168
|
+
False,
|
|
169
|
+
f"检测到 {ot_tag} 但缺少结束标签 {ct_tag}。\n"
|
|
170
|
+
"修复建议:在消息末尾补充结束标签,并确认标签各自独占一行。\n"
|
|
171
|
+
"示例:\n"
|
|
172
|
+
f"{ot_tag}\n"
|
|
173
|
+
"to: 目标Agent名称\n"
|
|
174
|
+
"content: |2\n"
|
|
175
|
+
" 这里填写要发送的消息内容\n"
|
|
176
|
+
f"{ct_tag}"
|
|
177
|
+
)
|
|
178
|
+
# 尝试提取原始块并指出 JSON 问题
|
|
179
|
+
import re as _re
|
|
180
|
+
pattern = _re.compile(
|
|
181
|
+
rf"{_re.escape(ot_tag)}[ \t]*\n(.*?)(?:\n)?[ \t]*{_re.escape(ct_tag)}",
|
|
182
|
+
_re.DOTALL,
|
|
183
|
+
)
|
|
184
|
+
blocks = pattern.findall(normalized)
|
|
185
|
+
if not blocks:
|
|
186
|
+
alt_pattern = _re.compile(
|
|
187
|
+
rf"{_re.escape(ot_tag)}[ \t]*(.*?)[ \t]*{_re.escape(ct_tag)}",
|
|
188
|
+
_re.DOTALL,
|
|
189
|
+
)
|
|
190
|
+
blocks = alt_pattern.findall(normalized)
|
|
191
|
+
if not blocks:
|
|
192
|
+
return (
|
|
193
|
+
False,
|
|
194
|
+
"SEND_MESSAGE 格式错误:未能识别完整的消息块。\n"
|
|
195
|
+
"修复建议:确保起止标签在单独行上,且中间内容为合法的 JSON,包含 to 与 content 字段。"
|
|
196
|
+
)
|
|
197
|
+
raw = blocks[0]
|
|
198
|
+
try:
|
|
199
|
+
msg_obj = json_loads(raw)
|
|
200
|
+
if not isinstance(msg_obj, dict):
|
|
201
|
+
return (
|
|
202
|
+
False,
|
|
203
|
+
"SEND_MESSAGE 内容必须为 JSON 对象(键值对)。\n"
|
|
204
|
+
"修复建议:使用 to 与 content 字段构成的对象。\n"
|
|
205
|
+
"示例:\n"
|
|
206
|
+
f"{ot('SEND_MESSAGE')}\n"
|
|
207
|
+
'{{\n "to": "目标Agent名称",\n "content": "这里填写要发送的消息内容"\n}}\n'
|
|
208
|
+
f"{ct('SEND_MESSAGE')}"
|
|
209
|
+
)
|
|
210
|
+
missing_keys = [k for k in ("to", "content") if k not in msg_obj]
|
|
211
|
+
if missing_keys:
|
|
212
|
+
return (
|
|
213
|
+
False,
|
|
214
|
+
"SEND_MESSAGE 缺少必要字段:" + ", ".join(missing_keys) + "\n"
|
|
215
|
+
"修复建议:补充缺失字段。\n"
|
|
216
|
+
"示例:\n"
|
|
217
|
+
f"{ot('SEND_MESSAGE')}\n"
|
|
218
|
+
'{{\n "to": "目标Agent名称",\n "content": "这里填写要发送的消息内容"\n}}\n'
|
|
219
|
+
f"{ct('SEND_MESSAGE')}"
|
|
220
|
+
)
|
|
221
|
+
# 针对值类型的提示(更细)
|
|
222
|
+
if not isinstance(msg_obj.get("to"), str):
|
|
223
|
+
return False, "SEND_MESSAGE 字段类型错误:to 必须为字符串。"
|
|
224
|
+
if not isinstance(msg_obj.get("content"), str):
|
|
225
|
+
return False, "SEND_MESSAGE 字段类型错误:content 必须为字符串。"
|
|
226
|
+
# 若到此仍未返回,说明结构基本正确,但 _extract_send_msg 未命中,给出泛化建议
|
|
227
|
+
return (
|
|
228
|
+
False,
|
|
229
|
+
"SEND_MESSAGE 格式可能存在缩进或空白字符问题,导致未被系统识别。\n"
|
|
230
|
+
"修复建议:\n"
|
|
231
|
+
"- 确保起止标签各占一行\n"
|
|
232
|
+
"- 标签与内容之间保留换行\n"
|
|
233
|
+
"- 使用正确的 Jsonnet 格式\n"
|
|
234
|
+
"示例:\n"
|
|
235
|
+
f"{ot('SEND_MESSAGE')}\n"
|
|
236
|
+
'{{\n "to": "目标Agent名称",\n "content": "这里填写要发送的消息内容"\n}}\n'
|
|
237
|
+
f"{ct('SEND_MESSAGE')}"
|
|
238
|
+
)
|
|
239
|
+
except Exception as e:
|
|
74
240
|
return (
|
|
75
241
|
False,
|
|
76
|
-
"
|
|
242
|
+
f"SEND_MESSAGE Jsonnet 解析失败:{str(e)}\n"
|
|
243
|
+
"修复建议:\n"
|
|
244
|
+
"- 检查 Jsonnet 格式是否正确(引号、逗号、大括号)\n"
|
|
245
|
+
"- 可以使用双引号 \"...\" 或单引号 '...' 包裹字符串\n"
|
|
246
|
+
"- 对于多行内容,推荐使用 ||| 或 ``` 分隔符包裹,直接换行无需转义,支持保留缩进\n"
|
|
247
|
+
"示例:\n"
|
|
248
|
+
f"{ot('SEND_MESSAGE')}\n"
|
|
249
|
+
'{{\n "to": "目标Agent名称",\n "content": "这里填写要发送的消息内容"\n}}\n'
|
|
250
|
+
f"{ct('SEND_MESSAGE')}\n"
|
|
251
|
+
"或使用多行字符串(推荐使用 ||| 或 ``` 分隔符):\n"
|
|
252
|
+
f"{ot('SEND_MESSAGE')}\n"
|
|
253
|
+
'{{\n "to": "目标Agent名称",\n "content": |||\n多行消息内容\n可以包含换行\n包含"双引号"和\'单引号\'都无需转义\n更清晰易读\n |||\n}}\n'
|
|
254
|
+
f"{ct('SEND_MESSAGE')}"
|
|
77
255
|
)
|
|
78
|
-
if len(send_messages) == 0:
|
|
79
|
-
return False, ""
|
|
80
|
-
return True, send_messages[0]
|
|
81
256
|
|
|
82
257
|
def name(self) -> str:
|
|
83
258
|
return "SEND_MESSAGE"
|
|
@@ -89,16 +264,38 @@ content: |2
|
|
|
89
264
|
Args:
|
|
90
265
|
content: The content containing send message
|
|
91
266
|
"""
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
267
|
+
# Normalize line endings to handle CRLF/CR cases to ensure robust matching
|
|
268
|
+
try:
|
|
269
|
+
normalized = content.replace("\r\n", "\n").replace("\r", "\n")
|
|
270
|
+
except Exception:
|
|
271
|
+
normalized = content
|
|
272
|
+
|
|
273
|
+
ot_tag = ot("SEND_MESSAGE")
|
|
274
|
+
ct_tag = ct("SEND_MESSAGE")
|
|
275
|
+
|
|
276
|
+
# Auto-append closing tag if missing
|
|
277
|
+
if ot_tag in normalized and ct_tag not in normalized:
|
|
278
|
+
normalized += "\n" + ct_tag
|
|
279
|
+
|
|
280
|
+
# Use robust regex with DOTALL; escape tags to avoid regex meta issues
|
|
281
|
+
pattern = re.compile(
|
|
282
|
+
rf"{re.escape(ot_tag)}[ \t]*\n(.*?)(?:\n)?[ \t]*{re.escape(ct_tag)}",
|
|
283
|
+
re.DOTALL,
|
|
96
284
|
)
|
|
285
|
+
data = pattern.findall(normalized)
|
|
286
|
+
# Fallback: handle cases without explicit newlines around closing tag
|
|
287
|
+
if not data:
|
|
288
|
+
alt_pattern = re.compile(
|
|
289
|
+
rf"{re.escape(ot_tag)}[ \t]*(.*?)[ \t]*{re.escape(ct_tag)}",
|
|
290
|
+
re.DOTALL,
|
|
291
|
+
)
|
|
292
|
+
data = alt_pattern.findall(normalized)
|
|
293
|
+
|
|
97
294
|
ret = []
|
|
98
295
|
for item in data:
|
|
99
296
|
try:
|
|
100
|
-
msg =
|
|
101
|
-
if "to" in msg and "content" in msg:
|
|
297
|
+
msg = json_loads(item)
|
|
298
|
+
if isinstance(msg, dict) and "to" in msg and "content" in msg:
|
|
102
299
|
ret.append(msg)
|
|
103
300
|
except Exception:
|
|
104
301
|
continue
|
|
@@ -112,6 +309,20 @@ content: |2
|
|
|
112
309
|
return None
|
|
113
310
|
|
|
114
311
|
config = self.agents_config_map[name].copy()
|
|
312
|
+
# 标记为多智能体运行,避免在非交互模式下自动开启 auto_complete
|
|
313
|
+
config.setdefault("in_multi_agent", True)
|
|
314
|
+
# 非主智能体统一禁用自动补全,防止多智能体并行时误触发自动交互
|
|
315
|
+
if name != self.main_agent_name:
|
|
316
|
+
config["auto_complete"] = False
|
|
317
|
+
|
|
318
|
+
# Prepend common system prompt if configured
|
|
319
|
+
common_sp = getattr(self, "common_system_prompt", "")
|
|
320
|
+
if common_sp:
|
|
321
|
+
existing_sp = config.get("system_prompt", "")
|
|
322
|
+
if existing_sp:
|
|
323
|
+
config["system_prompt"] = f"{common_sp}\n\n{existing_sp}"
|
|
324
|
+
else:
|
|
325
|
+
config["system_prompt"] = common_sp
|
|
115
326
|
|
|
116
327
|
if name != self.main_agent_name and self.original_question:
|
|
117
328
|
system_prompt = config.get("system_prompt", "")
|
|
@@ -119,18 +330,7 @@ content: |2
|
|
|
119
330
|
f"{system_prompt}\n\n# 原始问题\n{self.original_question}"
|
|
120
331
|
)
|
|
121
332
|
|
|
122
|
-
|
|
123
|
-
if len(output_handler) == 0:
|
|
124
|
-
output_handler = [
|
|
125
|
-
ToolRegistry(),
|
|
126
|
-
self,
|
|
127
|
-
]
|
|
128
|
-
else:
|
|
129
|
-
if not any(isinstance(h, MultiAgent) for h in output_handler):
|
|
130
|
-
output_handler.append(self)
|
|
131
|
-
config["output_handler"] = output_handler
|
|
132
|
-
|
|
133
|
-
agent = Agent(**config)
|
|
333
|
+
agent = Agent(output_handler=[ToolRegistry(), self], **config)
|
|
134
334
|
self.agents[name] = agent
|
|
135
335
|
return agent
|
|
136
336
|
|
|
@@ -151,12 +351,38 @@ content: |2
|
|
|
151
351
|
|
|
152
352
|
if not isinstance(msg, Dict):
|
|
153
353
|
# Should not happen if agent.run() returns str or Dict
|
|
154
|
-
|
|
354
|
+
print(f"⚠️ 未知消息类型: {type(msg)}")
|
|
155
355
|
break
|
|
156
356
|
|
|
357
|
+
# Generate a brief summary via direct model call to avoid run-loop recursion
|
|
358
|
+
# 如果在配置中显式设置了 summary_on_send=False,则不生成摘要
|
|
359
|
+
sender_config = self.agents_config_map.get(last_agent_name, {})
|
|
360
|
+
summary_on_send = sender_config.get("summary_on_send", True)
|
|
361
|
+
summary_text = ""
|
|
362
|
+
if summary_on_send:
|
|
363
|
+
try:
|
|
364
|
+
# 参照 Agent.generate_summary 的实现思路:基于当前 session.prompt 追加请求提示,直接调用底层模型
|
|
365
|
+
multi_agent_summary_prompt = """
|
|
366
|
+
请基于当前会话,为即将发送给其他智能体的协作交接写一段摘要,包含:
|
|
367
|
+
- 已完成的主要工作与产出
|
|
368
|
+
- 关键决策及其理由
|
|
369
|
+
- 已知的约束/风险/边界条件
|
|
370
|
+
- 未解决的问题与待澄清点
|
|
371
|
+
- 下一步建议与对目标智能体的具体请求
|
|
372
|
+
要求:
|
|
373
|
+
- 仅输出纯文本,不包含任何指令或工具调用
|
|
374
|
+
- 使用简洁的要点式表述
|
|
375
|
+
""".strip()
|
|
376
|
+
summary_any: Any = agent.model.chat_until_success( # type: ignore[attr-defined]
|
|
377
|
+
f"{agent.session.prompt}\n{multi_agent_summary_prompt}"
|
|
378
|
+
)
|
|
379
|
+
summary_text = summary_any.strip() if isinstance(summary_any, str) else ""
|
|
380
|
+
except Exception:
|
|
381
|
+
summary_text = ""
|
|
157
382
|
prompt = f"""
|
|
158
383
|
Please handle this message:
|
|
159
384
|
from: {last_agent_name}
|
|
385
|
+
summary_of_sender_work: {summary_text}
|
|
160
386
|
content: {msg['content']}
|
|
161
387
|
"""
|
|
162
388
|
to_agent_name = msg.get("to")
|
|
@@ -164,9 +390,7 @@ content: {msg['content']}
|
|
|
164
390
|
return "消息中未指定 `to` 字段"
|
|
165
391
|
|
|
166
392
|
if to_agent_name not in self.agents_config_map:
|
|
167
|
-
|
|
168
|
-
f"未找到智能体 {to_agent_name},正在重试...", OutputType.WARNING
|
|
169
|
-
)
|
|
393
|
+
print(f"⚠️ 未找到智能体 {to_agent_name},正在重试...")
|
|
170
394
|
agent = self._get_agent(last_agent_name)
|
|
171
395
|
if not agent:
|
|
172
396
|
return f"智能体 {last_agent_name} 未找到"
|
|
@@ -175,14 +399,22 @@ content: {msg['content']}
|
|
|
175
399
|
)
|
|
176
400
|
continue
|
|
177
401
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
402
|
+
print(f"ℹ️ {last_agent_name} 正在向 {to_agent_name} 发送消息...")
|
|
403
|
+
|
|
404
|
+
# Keep a reference to the sender before switching to the receiver
|
|
405
|
+
sender_agent = agent
|
|
181
406
|
|
|
182
407
|
agent = self._get_agent(to_agent_name)
|
|
183
408
|
if not agent:
|
|
184
409
|
return f"智能体 {to_agent_name} 未找到"
|
|
185
410
|
|
|
411
|
+
# Check if the sending agent should be cleared
|
|
412
|
+
sender_config = self.agents_config_map.get(last_agent_name, {})
|
|
413
|
+
if sender_config.get("clear_after_send_message"):
|
|
414
|
+
if sender_agent:
|
|
415
|
+
print(f"ℹ️ 清除智能体 {last_agent_name} 在发送消息后的历史记录...")
|
|
416
|
+
sender_agent.clear_history()
|
|
417
|
+
|
|
186
418
|
last_agent_name = agent.name
|
|
187
419
|
msg = agent.run(prompt)
|
|
188
420
|
return ""
|