jarvis-ai-assistant 0.3.30__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 +289 -87
- jarvis/jarvis_agent/agent_manager.py +17 -8
- jarvis/jarvis_agent/edit_file_handler.py +374 -86
- jarvis/jarvis_agent/event_bus.py +1 -1
- jarvis/jarvis_agent/file_context_handler.py +79 -0
- jarvis/jarvis_agent/jarvis.py +601 -43
- jarvis/jarvis_agent/main.py +32 -2
- jarvis/jarvis_agent/rewrite_file_handler.py +141 -0
- jarvis/jarvis_agent/run_loop.py +38 -5
- jarvis/jarvis_agent/share_manager.py +8 -1
- jarvis/jarvis_agent/stdio_redirect.py +295 -0
- jarvis/jarvis_agent/task_analyzer.py +5 -2
- jarvis/jarvis_agent/task_planner.py +496 -0
- 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 +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 +1171 -94
- 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 +270 -8
- jarvis/jarvis_code_agent/utils.py +142 -0
- jarvis/jarvis_code_analysis/code_review.py +483 -569
- jarvis/jarvis_data/config_schema.json +97 -8
- jarvis/jarvis_git_utils/git_commiter.py +38 -26
- jarvis/jarvis_mcp/sse_mcp_client.py +2 -2
- jarvis/jarvis_mcp/stdio_mcp_client.py +1 -1
- jarvis/jarvis_memory_organizer/memory_organizer.py +1 -1
- jarvis/jarvis_multi_agent/__init__.py +239 -25
- jarvis/jarvis_multi_agent/main.py +37 -1
- jarvis/jarvis_platform/base.py +103 -51
- jarvis/jarvis_platform/openai.py +26 -1
- jarvis/jarvis_platform/yuanbao.py +1 -1
- jarvis/jarvis_platform_manager/service.py +2 -2
- jarvis/jarvis_rag/cli.py +4 -4
- 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_stats/cli.py +1 -1
- jarvis/jarvis_stats/stats.py +1 -1
- jarvis/jarvis_stats/visualizer.py +1 -1
- jarvis/jarvis_tools/cli/main.py +1 -0
- jarvis/jarvis_tools/execute_script.py +46 -9
- jarvis/jarvis_tools/generate_new_tool.py +3 -1
- jarvis/jarvis_tools/read_code.py +275 -12
- jarvis/jarvis_tools/read_symbols.py +141 -0
- jarvis/jarvis_tools/read_webpage.py +5 -3
- jarvis/jarvis_tools/registry.py +73 -35
- jarvis/jarvis_tools/search_web.py +15 -11
- jarvis/jarvis_tools/sub_agent.py +24 -42
- jarvis/jarvis_tools/sub_code_agent.py +14 -13
- jarvis/jarvis_tools/virtual_tty.py +1 -1
- jarvis/jarvis_utils/config.py +187 -35
- jarvis/jarvis_utils/embedding.py +3 -0
- jarvis/jarvis_utils/git_utils.py +181 -6
- jarvis/jarvis_utils/globals.py +3 -3
- jarvis/jarvis_utils/http.py +1 -1
- jarvis/jarvis_utils/input.py +78 -2
- jarvis/jarvis_utils/methodology.py +25 -19
- jarvis/jarvis_utils/utils.py +644 -359
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/METADATA +85 -1
- jarvis_ai_assistant-0.7.0.dist-info/RECORD +192 -0
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/entry_points.txt +4 -0
- jarvis/jarvis_agent/config.py +0 -92
- jarvis/jarvis_tools/edit_file.py +0 -179
- jarvis/jarvis_tools/rewrite_file.py +0 -191
- jarvis_ai_assistant-0.3.30.dist-info/RECORD +0 -137
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/top_level.txt +0 -0
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
"JARVIS_MCP": {
|
|
8
8
|
"type": "array",
|
|
9
9
|
"description": "MCP工具配置列表",
|
|
10
|
+
"default": [],
|
|
10
11
|
"items": {
|
|
11
12
|
"type": "object",
|
|
12
13
|
"oneOf": [
|
|
@@ -182,11 +183,42 @@
|
|
|
182
183
|
"description": "执行工具前是否需要确认",
|
|
183
184
|
"default": false
|
|
184
185
|
},
|
|
186
|
+
"JARVIS_TOOL_FILTER_THRESHOLD": {
|
|
187
|
+
"type": "number",
|
|
188
|
+
"description": "AI工具筛选阈值:当可用工具数量超过此值时触发AI筛选",
|
|
189
|
+
"default": 30
|
|
190
|
+
},
|
|
191
|
+
"JARVIS_PLAN_ENABLED": {
|
|
192
|
+
"type": "boolean",
|
|
193
|
+
"description": "是否默认启用任务规划。当 Agent 初始化时 plan 参数未指定,将从此配置加载。默认 true。",
|
|
194
|
+
"default": true
|
|
195
|
+
},
|
|
196
|
+
"JARVIS_PLAN_MAX_DEPTH": {
|
|
197
|
+
"type": "number",
|
|
198
|
+
"description": "任务规划的最大层数。用于限制 plan 模式的递归拆分深度。仅在启用规划时生效(通过 CLI --plan/--no-plan 控制),默认 2。",
|
|
199
|
+
"default": 2
|
|
200
|
+
},
|
|
201
|
+
"JARVIS_SCRIPT_EXECUTION_TIMEOUT": {
|
|
202
|
+
"type": "number",
|
|
203
|
+
"description": "脚本执行的超时时间(秒),仅在非交互模式下生效。",
|
|
204
|
+
"default": 300
|
|
205
|
+
},
|
|
206
|
+
"JARVIS_AUTO_SUMMARY_ROUNDS": {
|
|
207
|
+
"type": "number",
|
|
208
|
+
"description": "基于对话轮次的自动总结阈值(达到该轮次后自动总结并清理历史)",
|
|
209
|
+
"default": 50
|
|
210
|
+
},
|
|
185
211
|
"JARVIS_CONFIRM_BEFORE_APPLY_PATCH": {
|
|
186
212
|
"type": "boolean",
|
|
187
213
|
"description": "应用补丁前是否需要确认",
|
|
188
214
|
"default": false
|
|
189
215
|
},
|
|
216
|
+
"JARVIS_PATCH_FORMAT": {
|
|
217
|
+
"type": "string",
|
|
218
|
+
"enum": ["all", "search", "search_range"],
|
|
219
|
+
"description": "补丁格式处理模式:all 同时支持 SEARCH 与 SEARCH_START/SEARCH_END;search 仅允许精确片段匹配;search_range 仅允许范围匹配。",
|
|
220
|
+
"default": "all"
|
|
221
|
+
},
|
|
190
222
|
"JARVIS_DATA_PATH": {
|
|
191
223
|
"type": "string",
|
|
192
224
|
"description": "Jarvis数据存储目录路径",
|
|
@@ -195,7 +227,7 @@
|
|
|
195
227
|
"JARVIS_PRETTY_OUTPUT": {
|
|
196
228
|
"type": "boolean",
|
|
197
229
|
"description": "是否启用美化输出",
|
|
198
|
-
"default":
|
|
230
|
+
"default": true
|
|
199
231
|
},
|
|
200
232
|
"JARVIS_USE_METHODOLOGY": {
|
|
201
233
|
"type": "boolean",
|
|
@@ -257,12 +289,12 @@
|
|
|
257
289
|
},
|
|
258
290
|
"JARVIS_CENTRAL_METHODOLOGY_REPO": {
|
|
259
291
|
"type": "string",
|
|
260
|
-
"description": "
|
|
292
|
+
"description": "中心方法论仓库路径或Git仓库地址。支持本地目录(含git子路径):若为本地目录将直接加入方法论加载路径;若为Git URL则会克隆到数据目录后加载。",
|
|
261
293
|
"default": ""
|
|
262
294
|
},
|
|
263
295
|
"JARVIS_CENTRAL_TOOL_REPO": {
|
|
264
296
|
"type": "string",
|
|
265
|
-
"description": "
|
|
297
|
+
"description": "中心工具仓库路径或Git仓库地址。支持本地目录(含git子路径):若为本地目录将直接加载其中的工具;若为Git URL则会克隆到数据目录并加载。",
|
|
266
298
|
"default": ""
|
|
267
299
|
},
|
|
268
300
|
"JARVIS_PRINT_PROMPT": {
|
|
@@ -270,31 +302,71 @@
|
|
|
270
302
|
"description": "是否打印提示",
|
|
271
303
|
"default": false
|
|
272
304
|
},
|
|
305
|
+
"JARVIS_PRINT_ERROR_TRACEBACK": {
|
|
306
|
+
"type": "boolean",
|
|
307
|
+
"description": "是否在错误输出时打印回溯调用链",
|
|
308
|
+
"default": false
|
|
309
|
+
},
|
|
273
310
|
"JARVIS_ENABLE_STATIC_ANALYSIS": {
|
|
274
311
|
"type": "boolean",
|
|
275
312
|
"description": "是否启用静态代码分析",
|
|
276
313
|
"default": true
|
|
277
314
|
},
|
|
315
|
+
"JARVIS_ENABLE_BUILD_VALIDATION": {
|
|
316
|
+
"type": "boolean",
|
|
317
|
+
"description": "是否启用构建验证。在代码编辑后自动验证代码能否成功编译/构建,确保编辑不会破坏项目构建。",
|
|
318
|
+
"default": true
|
|
319
|
+
},
|
|
320
|
+
"JARVIS_BUILD_VALIDATION_TIMEOUT": {
|
|
321
|
+
"type": "number",
|
|
322
|
+
"description": "构建验证的超时时间(秒)。当构建验证执行时间超过此值时,将终止验证并报告超时。",
|
|
323
|
+
"default": 30
|
|
324
|
+
},
|
|
325
|
+
"JARVIS_ENABLE_IMPACT_ANALYSIS": {
|
|
326
|
+
"type": "boolean",
|
|
327
|
+
"description": "是否启用编辑影响范围分析。分析代码编辑的影响范围,识别可能受影响的文件、函数、测试等,帮助评估编辑风险。",
|
|
328
|
+
"default": true
|
|
329
|
+
},
|
|
278
330
|
"JARVIS_FORCE_SAVE_MEMORY": {
|
|
279
331
|
"type": "boolean",
|
|
280
332
|
"description": "是否强制保存记忆",
|
|
281
|
-
"default":
|
|
333
|
+
"default": false
|
|
282
334
|
},
|
|
283
335
|
"JARVIS_ENABLE_GIT_JCA_SWITCH": {
|
|
284
336
|
"type": "boolean",
|
|
285
337
|
"description": "在初始化环境前检测Git仓库并提示可切换到代码开发模式(jca)",
|
|
286
|
-
"default":
|
|
338
|
+
"default": true
|
|
287
339
|
},
|
|
288
340
|
"JARVIS_ENABLE_STARTUP_CONFIG_SELECTOR": {
|
|
289
341
|
"type": "boolean",
|
|
290
342
|
"description": "在进入默认通用代理前,列出可用配置(agent/multi_agent/roles)供选择",
|
|
291
|
-
"default":
|
|
343
|
+
"default": true
|
|
292
344
|
},
|
|
293
345
|
"JARVIS_IMMEDIATE_ABORT": {
|
|
294
346
|
"type": "boolean",
|
|
295
347
|
"description": "是否启用立即中断:在对话迭代中检测到中断信号时立即返回",
|
|
296
348
|
"default": false
|
|
297
349
|
},
|
|
350
|
+
"JARVIS_SAVE_SESSION_HISTORY": {
|
|
351
|
+
"type": "boolean",
|
|
352
|
+
"description": "是否保存会话记录",
|
|
353
|
+
"default": false
|
|
354
|
+
},
|
|
355
|
+
"JARVIS_SKIP_PREDEFINED_TASKS": {
|
|
356
|
+
"type": "boolean",
|
|
357
|
+
"description": "是否跳过预定义任务加载(不读取 pre-command 列表)",
|
|
358
|
+
"default": false
|
|
359
|
+
},
|
|
360
|
+
"JARVIS_ADDON_PROMPT_THRESHOLD": {
|
|
361
|
+
"type": "number",
|
|
362
|
+
"description": "附加提示的触发阈值(字符数)。当消息长度超过此值时,会自动添加默认的附加提示",
|
|
363
|
+
"default": 1024
|
|
364
|
+
},
|
|
365
|
+
"JARVIS_ENABLE_INTENT_RECOGNITION": {
|
|
366
|
+
"type": "boolean",
|
|
367
|
+
"description": "是否启用意图识别功能。用于智能上下文推荐中的LLM意图提取和语义分析",
|
|
368
|
+
"default": true
|
|
369
|
+
},
|
|
298
370
|
"JARVIS_GIT_CHECK_MODE": {
|
|
299
371
|
"type": "string",
|
|
300
372
|
"enum": ["strict", "warn"],
|
|
@@ -341,7 +413,23 @@
|
|
|
341
413
|
"JARVIS_RAG_GROUPS": {
|
|
342
414
|
"type": "array",
|
|
343
415
|
"description": "预定义的RAG配置组",
|
|
344
|
-
"default": [
|
|
416
|
+
"default": [
|
|
417
|
+
{
|
|
418
|
+
"text": {
|
|
419
|
+
"embedding_model": "BAAI/bge-m3",
|
|
420
|
+
"rerank_model": "BAAI/bge-reranker-v2-m3",
|
|
421
|
+
"use_bm25": true,
|
|
422
|
+
"use_rerank": true
|
|
423
|
+
}
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
"code": {
|
|
427
|
+
"embedding_model": "Qodo/Qodo-Embed-1-1.5B",
|
|
428
|
+
"use_bm25": false,
|
|
429
|
+
"use_rerank": false
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
],
|
|
345
433
|
"items": {
|
|
346
434
|
"type": "object",
|
|
347
435
|
"additionalProperties": {
|
|
@@ -421,7 +509,8 @@
|
|
|
421
509
|
"required": [
|
|
422
510
|
"template"
|
|
423
511
|
]
|
|
424
|
-
}
|
|
512
|
+
},
|
|
513
|
+
"default": {}
|
|
425
514
|
},
|
|
426
515
|
"OPENAI_API_KEY": {
|
|
427
516
|
"type": "string",
|
|
@@ -177,30 +177,35 @@ commit信息
|
|
|
177
177
|
platform_name = None
|
|
178
178
|
model_name = None
|
|
179
179
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
and getattr(agent_from_args, "model", None)
|
|
184
|
-
):
|
|
185
|
-
try:
|
|
186
|
-
platform_name = agent_from_args.model.platform_name()
|
|
187
|
-
model_name = agent_from_args.model.name()
|
|
188
|
-
if not model_group and hasattr(
|
|
189
|
-
agent_from_args.model, "model_group"
|
|
190
|
-
):
|
|
191
|
-
model_group = agent_from_args.model.model_group
|
|
192
|
-
except Exception:
|
|
193
|
-
# 安全回退到后续逻辑
|
|
194
|
-
platform_name = None
|
|
195
|
-
model_name = None
|
|
196
|
-
|
|
197
|
-
# 如果未能从agent获取到,再根据 model_group 获取
|
|
198
|
-
if not platform_name:
|
|
180
|
+
# 优先根据 model_group 获取(确保配置一致性)
|
|
181
|
+
# 如果 model_group 存在,强制使用它来解析,避免使用 agent.model 中可能不一致的值
|
|
182
|
+
if model_group:
|
|
199
183
|
platform_name = get_normal_platform_name(model_group)
|
|
200
|
-
if not model_name:
|
|
201
184
|
model_name = get_normal_model_name(model_group)
|
|
202
|
-
|
|
203
|
-
|
|
185
|
+
else:
|
|
186
|
+
# 如果没有提供 model_group,尝试从传入的 agent 获取
|
|
187
|
+
if (
|
|
188
|
+
agent_from_args
|
|
189
|
+
and hasattr(agent_from_args, "model")
|
|
190
|
+
and getattr(agent_from_args, "model", None)
|
|
191
|
+
):
|
|
192
|
+
try:
|
|
193
|
+
platform_name = agent_from_args.model.platform_name()
|
|
194
|
+
model_name = agent_from_args.model.name()
|
|
195
|
+
if hasattr(agent_from_args.model, "model_group"):
|
|
196
|
+
model_group = agent_from_args.model.model_group
|
|
197
|
+
except Exception:
|
|
198
|
+
# 安全回退到后续逻辑
|
|
199
|
+
platform_name = None
|
|
200
|
+
model_name = None
|
|
201
|
+
|
|
202
|
+
# 如果仍未获取到,使用配置文件中的默认值(传入 None 会读取默认配置)
|
|
203
|
+
if not platform_name:
|
|
204
|
+
platform_name = get_normal_platform_name(None)
|
|
205
|
+
if not model_name:
|
|
206
|
+
model_name = get_normal_model_name(None)
|
|
207
|
+
|
|
208
|
+
# 最后的回退:尝试从全局 agent 获取(仅当仍未获取到时)
|
|
204
209
|
if not platform_name:
|
|
205
210
|
agent = get_agent(current_agent_name)
|
|
206
211
|
if (
|
|
@@ -208,10 +213,17 @@ commit信息
|
|
|
208
213
|
and hasattr(agent, "model")
|
|
209
214
|
and getattr(agent, "model", None)
|
|
210
215
|
):
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
model_group
|
|
216
|
+
try:
|
|
217
|
+
platform_name = agent.model.platform_name()
|
|
218
|
+
model_name = agent.model.name()
|
|
219
|
+
if not model_group and hasattr(agent.model, "model_group"):
|
|
220
|
+
model_group = agent.model.model_group
|
|
221
|
+
except Exception:
|
|
222
|
+
# 如果全局 agent 也无法获取,使用配置文件默认值
|
|
223
|
+
if not platform_name:
|
|
224
|
+
platform_name = get_normal_platform_name(None)
|
|
225
|
+
if not model_name:
|
|
226
|
+
model_name = get_normal_model_name(None)
|
|
215
227
|
|
|
216
228
|
# Create a new platform instance
|
|
217
229
|
if platform_name:
|
|
@@ -5,7 +5,7 @@ import time
|
|
|
5
5
|
from typing import Any, Callable, Dict, List, Optional
|
|
6
6
|
from urllib.parse import parse_qs, urlencode, urljoin
|
|
7
7
|
|
|
8
|
-
import requests
|
|
8
|
+
import requests # type: ignore[import-untyped]
|
|
9
9
|
|
|
10
10
|
from jarvis.jarvis_mcp import McpClient
|
|
11
11
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
@@ -568,7 +568,7 @@ class SSEMcpClient(McpClient):
|
|
|
568
568
|
if self.sse_response:
|
|
569
569
|
try:
|
|
570
570
|
self.sse_response.close()
|
|
571
|
-
except:
|
|
571
|
+
except Exception:
|
|
572
572
|
pass
|
|
573
573
|
|
|
574
574
|
# 关闭HTTP会话
|
|
@@ -7,17 +7,20 @@ import yaml
|
|
|
7
7
|
from jarvis.jarvis_agent import Agent
|
|
8
8
|
from jarvis.jarvis_agent.output_handler import OutputHandler
|
|
9
9
|
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
10
|
+
from jarvis.jarvis_agent.edit_file_handler import EditFileHandler
|
|
11
|
+
from jarvis.jarvis_agent.rewrite_file_handler import RewriteFileHandler
|
|
10
12
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
11
13
|
from jarvis.jarvis_utils.tag import ct, ot
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
class MultiAgent(OutputHandler):
|
|
15
|
-
def __init__(self, agents_config: List[Dict], main_agent_name: str):
|
|
17
|
+
def __init__(self, agents_config: List[Dict], main_agent_name: str, common_system_prompt: str = ""):
|
|
16
18
|
self.agents_config = agents_config
|
|
17
19
|
self.agents_config_map = {c["name"]: c for c in agents_config}
|
|
18
20
|
self.agents: Dict[str, Agent] = {}
|
|
19
21
|
self.main_agent_name = main_agent_name
|
|
20
22
|
self.original_question: Optional[str] = None
|
|
23
|
+
self.common_system_prompt: str = common_system_prompt
|
|
21
24
|
|
|
22
25
|
def prompt(self) -> str:
|
|
23
26
|
return f"""
|
|
@@ -66,18 +69,168 @@ content: |2
|
|
|
66
69
|
"""
|
|
67
70
|
|
|
68
71
|
def can_handle(self, response: str) -> bool:
|
|
69
|
-
|
|
72
|
+
# 只要检测到 SEND_MESSAGE 起始标签即认为可处理,
|
|
73
|
+
# 即便内容有误也由 handle 返回明确错误与修复指导
|
|
74
|
+
return ot("SEND_MESSAGE") in response
|
|
70
75
|
|
|
71
76
|
def handle(self, response: str, agent: Any) -> Tuple[bool, Any]:
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
"""
|
|
78
|
+
处理 SEND_MESSAGE。若存在格式/解析/字段/目标等问题,返回明确错误原因与修复指导。
|
|
79
|
+
"""
|
|
80
|
+
# 优先使用解析器获取“正确路径”结果
|
|
81
|
+
parsed = self._extract_send_msg(response)
|
|
82
|
+
if len(parsed) == 1:
|
|
83
|
+
msg = parsed[0]
|
|
84
|
+
# 字段校验
|
|
85
|
+
to_val = msg.get("to")
|
|
86
|
+
content_val = msg.get("content")
|
|
87
|
+
missing = []
|
|
88
|
+
if not to_val:
|
|
89
|
+
missing.append("to")
|
|
90
|
+
if content_val is None or (isinstance(content_val, str) and content_val.strip() == ""):
|
|
91
|
+
# 允许空格/空行被视为缺失
|
|
92
|
+
missing.append("content")
|
|
93
|
+
if missing:
|
|
94
|
+
guidance = (
|
|
95
|
+
"SEND_MESSAGE 字段缺失或为空:"
|
|
96
|
+
+ ", ".join(missing)
|
|
97
|
+
+ "\n修复建议:\n"
|
|
98
|
+
"- 必须包含 to 和 content 字段\n"
|
|
99
|
+
"- to: 目标智能体名称(字符串)\n"
|
|
100
|
+
"- content: 发送内容,建议使用多行块 |2 保持格式\n"
|
|
101
|
+
"示例:\n"
|
|
102
|
+
f"{ot('SEND_MESSAGE')}\n"
|
|
103
|
+
"to: 目标Agent名称\n"
|
|
104
|
+
"content: |2\n"
|
|
105
|
+
" 这里填写要发送的消息内容\n"
|
|
106
|
+
f"{ct('SEND_MESSAGE')}"
|
|
107
|
+
)
|
|
108
|
+
return False, guidance
|
|
109
|
+
# 类型校验
|
|
110
|
+
if not isinstance(to_val, str):
|
|
111
|
+
return False, "SEND_MESSAGE 字段类型错误:to 必须为字符串。修复建议:将 to 改为字符串,如 to: ChapterPolisher"
|
|
112
|
+
if not isinstance(content_val, str):
|
|
113
|
+
return False, "SEND_MESSAGE 字段类型错误:content 必须为字符串。修复建议:将 content 改为字符串或使用多行块 content: |2"
|
|
114
|
+
# 目标校验
|
|
115
|
+
if to_val not in self.agents_config_map:
|
|
116
|
+
available = ", ".join(self.agents_config_map.keys())
|
|
117
|
+
return (
|
|
118
|
+
False,
|
|
119
|
+
f"目标智能体不存在:'{to_val}' 不在可用列表中。\n"
|
|
120
|
+
f"可用智能体:[{available}]\n"
|
|
121
|
+
"修复建议:\n"
|
|
122
|
+
"- 将 to 修改为上述可用智能体之一\n"
|
|
123
|
+
"- 或检查配置中是否遗漏了该智能体的定义"
|
|
124
|
+
)
|
|
125
|
+
# 通过校验,交给上层发送
|
|
126
|
+
return True, {"to": to_val, "content": content_val}
|
|
127
|
+
elif len(parsed) > 1:
|
|
128
|
+
return (
|
|
129
|
+
False,
|
|
130
|
+
"检测到多个 SEND_MESSAGE 块。一次只能发送一个消息。\n修复建议:合并消息或分多轮发送,每轮仅保留一个 SEND_MESSAGE 块。"
|
|
131
|
+
)
|
|
132
|
+
# 未成功解析,进行诊断并返回可操作指导
|
|
133
|
+
try:
|
|
134
|
+
normalized = response.replace("\r\n", "\n").replace("\r", "\n")
|
|
135
|
+
except Exception:
|
|
136
|
+
normalized = response
|
|
137
|
+
ot_tag = ot("SEND_MESSAGE")
|
|
138
|
+
ct_tag = ct("SEND_MESSAGE")
|
|
139
|
+
has_open = ot_tag in normalized
|
|
140
|
+
has_close = ct_tag in normalized
|
|
141
|
+
if has_open and not has_close:
|
|
142
|
+
return (
|
|
143
|
+
False,
|
|
144
|
+
f"检测到 {ot_tag} 但缺少结束标签 {ct_tag}。\n"
|
|
145
|
+
"修复建议:在消息末尾补充结束标签,并确认标签各自独占一行。\n"
|
|
146
|
+
"示例:\n"
|
|
147
|
+
f"{ot_tag}\n"
|
|
148
|
+
"to: 目标Agent名称\n"
|
|
149
|
+
"content: |2\n"
|
|
150
|
+
" 这里填写要发送的消息内容\n"
|
|
151
|
+
f"{ct_tag}"
|
|
152
|
+
)
|
|
153
|
+
# 尝试提取原始块并指出 YAML 问题
|
|
154
|
+
import re as _re
|
|
155
|
+
pattern = _re.compile(
|
|
156
|
+
rf"{_re.escape(ot_tag)}[ \t]*\n(.*?)(?:\n)?[ \t]*{_re.escape(ct_tag)}",
|
|
157
|
+
_re.DOTALL,
|
|
158
|
+
)
|
|
159
|
+
blocks = pattern.findall(normalized)
|
|
160
|
+
if not blocks:
|
|
161
|
+
alt_pattern = _re.compile(
|
|
162
|
+
rf"{_re.escape(ot_tag)}[ \t]*(.*?)[ \t]*{_re.escape(ct_tag)}",
|
|
163
|
+
_re.DOTALL,
|
|
164
|
+
)
|
|
165
|
+
blocks = alt_pattern.findall(normalized)
|
|
166
|
+
if not blocks:
|
|
167
|
+
return (
|
|
168
|
+
False,
|
|
169
|
+
"SEND_MESSAGE 格式错误:未能识别完整的消息块。\n"
|
|
170
|
+
"修复建议:确保起止标签在单独行上,且中间内容为合法的 YAML,包含 to 与 content 字段。"
|
|
171
|
+
)
|
|
172
|
+
raw = blocks[0]
|
|
173
|
+
try:
|
|
174
|
+
msg_obj = yaml.safe_load(raw)
|
|
175
|
+
if not isinstance(msg_obj, dict):
|
|
176
|
+
return (
|
|
177
|
+
False,
|
|
178
|
+
"SEND_MESSAGE 内容必须为 YAML 对象(键值对)。\n"
|
|
179
|
+
"修复建议:使用 to 与 content 字段构成的对象。\n"
|
|
180
|
+
"示例:\n"
|
|
181
|
+
f"{ot('SEND_MESSAGE')}\n"
|
|
182
|
+
"to: 目标Agent名称\n"
|
|
183
|
+
"content: |2\n"
|
|
184
|
+
" 这里填写要发送的消息内容\n"
|
|
185
|
+
f"{ct('SEND_MESSAGE')}"
|
|
186
|
+
)
|
|
187
|
+
missing_keys = [k for k in ("to", "content") if k not in msg_obj]
|
|
188
|
+
if missing_keys:
|
|
189
|
+
return (
|
|
190
|
+
False,
|
|
191
|
+
"SEND_MESSAGE 缺少必要字段:" + ", ".join(missing_keys) + "\n"
|
|
192
|
+
"修复建议:补充缺失字段。\n"
|
|
193
|
+
"示例:\n"
|
|
194
|
+
f"{ot('SEND_MESSAGE')}\n"
|
|
195
|
+
"to: 目标Agent名称\n"
|
|
196
|
+
"content: |2\n"
|
|
197
|
+
" 这里填写要发送的消息内容\n"
|
|
198
|
+
f"{ct('SEND_MESSAGE')}"
|
|
199
|
+
)
|
|
200
|
+
# 针对值类型的提示(更细)
|
|
201
|
+
if not isinstance(msg_obj.get("to"), str):
|
|
202
|
+
return False, "SEND_MESSAGE 字段类型错误:to 必须为字符串。"
|
|
203
|
+
if not isinstance(msg_obj.get("content"), str):
|
|
204
|
+
return False, "SEND_MESSAGE 字段类型错误:content 必须为字符串,建议使用多行块 |2。"
|
|
205
|
+
# 若到此仍未返回,说明结构基本正确,但 _extract_send_msg 未命中,给出泛化建议
|
|
74
206
|
return (
|
|
75
207
|
False,
|
|
76
|
-
"
|
|
208
|
+
"SEND_MESSAGE 格式可能存在缩进或空白字符问题,导致未被系统识别。\n"
|
|
209
|
+
"修复建议:\n"
|
|
210
|
+
"- 确保起止标签各占一行\n"
|
|
211
|
+
"- 标签与内容之间保留换行\n"
|
|
212
|
+
"- 使用 content: |2 并保证 YAML 缩进一致\n"
|
|
213
|
+
"示例:\n"
|
|
214
|
+
f"{ot('SEND_MESSAGE')}\n"
|
|
215
|
+
"to: 目标Agent名称\n"
|
|
216
|
+
"content: |2\n"
|
|
217
|
+
" 这里填写要发送的消息内容\n"
|
|
218
|
+
f"{ct('SEND_MESSAGE')}"
|
|
219
|
+
)
|
|
220
|
+
except Exception as e:
|
|
221
|
+
return (
|
|
222
|
+
False,
|
|
223
|
+
f"SEND_MESSAGE YAML 解析失败:{str(e)}\n"
|
|
224
|
+
"修复建议:\n"
|
|
225
|
+
"- 检查冒号、缩进与引号是否正确\n"
|
|
226
|
+
"- 使用 content: |2 多行块以避免缩进歧义\n"
|
|
227
|
+
"示例:\n"
|
|
228
|
+
f"{ot('SEND_MESSAGE')}\n"
|
|
229
|
+
"to: 目标Agent名称\n"
|
|
230
|
+
"content: |2\n"
|
|
231
|
+
" 这里填写要发送的消息内容\n"
|
|
232
|
+
f"{ct('SEND_MESSAGE')}"
|
|
77
233
|
)
|
|
78
|
-
if len(send_messages) == 0:
|
|
79
|
-
return False, ""
|
|
80
|
-
return True, send_messages[0]
|
|
81
234
|
|
|
82
235
|
def name(self) -> str:
|
|
83
236
|
return "SEND_MESSAGE"
|
|
@@ -89,16 +242,38 @@ content: |2
|
|
|
89
242
|
Args:
|
|
90
243
|
content: The content containing send message
|
|
91
244
|
"""
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
245
|
+
# Normalize line endings to handle CRLF/CR cases to ensure robust matching
|
|
246
|
+
try:
|
|
247
|
+
normalized = content.replace("\r\n", "\n").replace("\r", "\n")
|
|
248
|
+
except Exception:
|
|
249
|
+
normalized = content
|
|
250
|
+
|
|
251
|
+
ot_tag = ot("SEND_MESSAGE")
|
|
252
|
+
ct_tag = ct("SEND_MESSAGE")
|
|
253
|
+
|
|
254
|
+
# Auto-append closing tag if missing
|
|
255
|
+
if ot_tag in normalized and ct_tag not in normalized:
|
|
256
|
+
normalized += "\n" + ct_tag
|
|
257
|
+
|
|
258
|
+
# Use robust regex with DOTALL; escape tags to avoid regex meta issues
|
|
259
|
+
pattern = re.compile(
|
|
260
|
+
rf"{re.escape(ot_tag)}[ \t]*\n(.*?)(?:\n)?[ \t]*{re.escape(ct_tag)}",
|
|
261
|
+
re.DOTALL,
|
|
96
262
|
)
|
|
263
|
+
data = pattern.findall(normalized)
|
|
264
|
+
# Fallback: handle cases without explicit newlines around closing tag
|
|
265
|
+
if not data:
|
|
266
|
+
alt_pattern = re.compile(
|
|
267
|
+
rf"{re.escape(ot_tag)}[ \t]*(.*?)[ \t]*{re.escape(ct_tag)}",
|
|
268
|
+
re.DOTALL,
|
|
269
|
+
)
|
|
270
|
+
data = alt_pattern.findall(normalized)
|
|
271
|
+
|
|
97
272
|
ret = []
|
|
98
273
|
for item in data:
|
|
99
274
|
try:
|
|
100
275
|
msg = yaml.safe_load(item)
|
|
101
|
-
if "to" in msg and "content" in msg:
|
|
276
|
+
if isinstance(msg, dict) and "to" in msg and "content" in msg:
|
|
102
277
|
ret.append(msg)
|
|
103
278
|
except Exception:
|
|
104
279
|
continue
|
|
@@ -112,6 +287,20 @@ content: |2
|
|
|
112
287
|
return None
|
|
113
288
|
|
|
114
289
|
config = self.agents_config_map[name].copy()
|
|
290
|
+
# 标记为多智能体运行,避免在非交互模式下自动开启 auto_complete
|
|
291
|
+
config.setdefault("in_multi_agent", True)
|
|
292
|
+
# 非主智能体统一禁用自动补全,防止多智能体并行时误触发自动交互
|
|
293
|
+
if name != self.main_agent_name:
|
|
294
|
+
config["auto_complete"] = False
|
|
295
|
+
|
|
296
|
+
# Prepend common system prompt if configured
|
|
297
|
+
common_sp = getattr(self, "common_system_prompt", "")
|
|
298
|
+
if common_sp:
|
|
299
|
+
existing_sp = config.get("system_prompt", "")
|
|
300
|
+
if existing_sp:
|
|
301
|
+
config["system_prompt"] = f"{common_sp}\n\n{existing_sp}"
|
|
302
|
+
else:
|
|
303
|
+
config["system_prompt"] = common_sp
|
|
115
304
|
|
|
116
305
|
if name != self.main_agent_name and self.original_question:
|
|
117
306
|
system_prompt = config.get("system_prompt", "")
|
|
@@ -119,18 +308,7 @@ content: |2
|
|
|
119
308
|
f"{system_prompt}\n\n# 原始问题\n{self.original_question}"
|
|
120
309
|
)
|
|
121
310
|
|
|
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)
|
|
311
|
+
agent = Agent(output_handler=[ToolRegistry(), EditFileHandler(), RewriteFileHandler(), self],**config)
|
|
134
312
|
self.agents[name] = agent
|
|
135
313
|
return agent
|
|
136
314
|
|
|
@@ -154,9 +332,35 @@ content: |2
|
|
|
154
332
|
PrettyOutput.print(f"未知消息类型: {type(msg)}", OutputType.WARNING)
|
|
155
333
|
break
|
|
156
334
|
|
|
335
|
+
# Generate a brief summary via direct model call to avoid run-loop recursion
|
|
336
|
+
# 如果在配置中显式设置了 summary_on_send=False,则不生成摘要
|
|
337
|
+
sender_config = self.agents_config_map.get(last_agent_name, {}) if hasattr(self, "agents_config_map") else {}
|
|
338
|
+
summary_on_send = sender_config.get("summary_on_send", True)
|
|
339
|
+
summary_text = ""
|
|
340
|
+
if summary_on_send:
|
|
341
|
+
try:
|
|
342
|
+
# 参照 Agent.generate_summary 的实现思路:基于当前 session.prompt 追加请求提示,直接调用底层模型
|
|
343
|
+
multi_agent_summary_prompt = """
|
|
344
|
+
请基于当前会话,为即将发送给其他智能体的协作交接写一段摘要,包含:
|
|
345
|
+
- 已完成的主要工作与产出
|
|
346
|
+
- 关键决策及其理由
|
|
347
|
+
- 已知的约束/风险/边界条件
|
|
348
|
+
- 未解决的问题与待澄清点
|
|
349
|
+
- 下一步建议与对目标智能体的具体请求
|
|
350
|
+
要求:
|
|
351
|
+
- 仅输出纯文本,不包含任何指令或工具调用
|
|
352
|
+
- 使用简洁的要点式表述
|
|
353
|
+
""".strip()
|
|
354
|
+
summary_any: Any = agent.model.chat_until_success( # type: ignore[attr-defined]
|
|
355
|
+
f"{agent.session.prompt}\n{multi_agent_summary_prompt}"
|
|
356
|
+
)
|
|
357
|
+
summary_text = summary_any.strip() if isinstance(summary_any, str) else ""
|
|
358
|
+
except Exception:
|
|
359
|
+
summary_text = ""
|
|
157
360
|
prompt = f"""
|
|
158
361
|
Please handle this message:
|
|
159
362
|
from: {last_agent_name}
|
|
363
|
+
summary_of_sender_work: {summary_text}
|
|
160
364
|
content: {msg['content']}
|
|
161
365
|
"""
|
|
162
366
|
to_agent_name = msg.get("to")
|
|
@@ -179,10 +383,20 @@ content: {msg['content']}
|
|
|
179
383
|
f"{last_agent_name} 正在向 {to_agent_name} 发送消息...", OutputType.INFO
|
|
180
384
|
)
|
|
181
385
|
|
|
386
|
+
# Keep a reference to the sender before switching to the receiver
|
|
387
|
+
sender_agent = agent
|
|
388
|
+
|
|
182
389
|
agent = self._get_agent(to_agent_name)
|
|
183
390
|
if not agent:
|
|
184
391
|
return f"智能体 {to_agent_name} 未找到"
|
|
185
392
|
|
|
393
|
+
# Check if the sending agent should be cleared
|
|
394
|
+
sender_config = self.agents_config_map.get(last_agent_name, {})
|
|
395
|
+
if sender_config.get("clear_after_send_message"):
|
|
396
|
+
if sender_agent:
|
|
397
|
+
PrettyOutput.print(f"清除智能体 {last_agent_name} 在发送消息后的历史记录...", OutputType.INFO)
|
|
398
|
+
sender_agent.clear_history()
|
|
399
|
+
|
|
186
400
|
last_agent_name = agent.name
|
|
187
401
|
msg = agent.run(prompt)
|
|
188
402
|
return ""
|