jarvis-ai-assistant 0.3.20__py3-none-any.whl → 0.3.21__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 (57) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +24 -3
  3. jarvis/jarvis_agent/config_editor.py +5 -1
  4. jarvis/jarvis_agent/edit_file_handler.py +15 -9
  5. jarvis/jarvis_agent/jarvis.py +99 -3
  6. jarvis/jarvis_agent/memory_manager.py +3 -3
  7. jarvis/jarvis_agent/share_manager.py +3 -1
  8. jarvis/jarvis_agent/task_analyzer.py +0 -1
  9. jarvis/jarvis_agent/task_manager.py +15 -5
  10. jarvis/jarvis_agent/tool_executor.py +2 -2
  11. jarvis/jarvis_code_agent/code_agent.py +39 -16
  12. jarvis/jarvis_git_utils/git_commiter.py +3 -6
  13. jarvis/jarvis_mcp/sse_mcp_client.py +9 -3
  14. jarvis/jarvis_mcp/streamable_mcp_client.py +15 -5
  15. jarvis/jarvis_memory_organizer/memory_organizer.py +1 -1
  16. jarvis/jarvis_methodology/main.py +4 -4
  17. jarvis/jarvis_multi_agent/__init__.py +3 -3
  18. jarvis/jarvis_platform/base.py +10 -5
  19. jarvis/jarvis_platform/kimi.py +18 -6
  20. jarvis/jarvis_platform/tongyi.py +18 -5
  21. jarvis/jarvis_platform/yuanbao.py +10 -3
  22. jarvis/jarvis_platform_manager/main.py +21 -7
  23. jarvis/jarvis_platform_manager/service.py +4 -3
  24. jarvis/jarvis_rag/cli.py +61 -22
  25. jarvis/jarvis_rag/embedding_manager.py +10 -3
  26. jarvis/jarvis_rag/llm_interface.py +4 -1
  27. jarvis/jarvis_rag/query_rewriter.py +3 -1
  28. jarvis/jarvis_rag/rag_pipeline.py +11 -3
  29. jarvis/jarvis_rag/retriever.py +151 -2
  30. jarvis/jarvis_smart_shell/main.py +59 -18
  31. jarvis/jarvis_stats/cli.py +11 -9
  32. jarvis/jarvis_stats/stats.py +14 -8
  33. jarvis/jarvis_stats/storage.py +23 -6
  34. jarvis/jarvis_tools/cli/main.py +63 -29
  35. jarvis/jarvis_tools/edit_file.py +3 -4
  36. jarvis/jarvis_tools/file_analyzer.py +0 -1
  37. jarvis/jarvis_tools/generate_new_tool.py +3 -3
  38. jarvis/jarvis_tools/read_code.py +0 -1
  39. jarvis/jarvis_tools/read_webpage.py +14 -4
  40. jarvis/jarvis_tools/registry.py +0 -3
  41. jarvis/jarvis_tools/retrieve_memory.py +0 -1
  42. jarvis/jarvis_tools/save_memory.py +0 -1
  43. jarvis/jarvis_tools/search_web.py +0 -2
  44. jarvis/jarvis_tools/sub_agent.py +197 -0
  45. jarvis/jarvis_tools/sub_code_agent.py +194 -0
  46. jarvis/jarvis_tools/virtual_tty.py +21 -13
  47. jarvis/jarvis_utils/config.py +35 -5
  48. jarvis/jarvis_utils/input.py +297 -56
  49. jarvis/jarvis_utils/methodology.py +3 -1
  50. jarvis/jarvis_utils/output.py +5 -2
  51. jarvis/jarvis_utils/utils.py +480 -170
  52. {jarvis_ai_assistant-0.3.20.dist-info → jarvis_ai_assistant-0.3.21.dist-info}/METADATA +10 -2
  53. {jarvis_ai_assistant-0.3.20.dist-info → jarvis_ai_assistant-0.3.21.dist-info}/RECORD +57 -55
  54. {jarvis_ai_assistant-0.3.20.dist-info → jarvis_ai_assistant-0.3.21.dist-info}/WHEEL +0 -0
  55. {jarvis_ai_assistant-0.3.20.dist-info → jarvis_ai_assistant-0.3.21.dist-info}/entry_points.txt +0 -0
  56. {jarvis_ai_assistant-0.3.20.dist-info → jarvis_ai_assistant-0.3.21.dist-info}/licenses/LICENSE +0 -0
  57. {jarvis_ai_assistant-0.3.20.dist-info → jarvis_ai_assistant-0.3.21.dist-info}/top_level.txt +0 -0
@@ -23,12 +23,20 @@ def list_tools(
23
23
 
24
24
  if as_json:
25
25
  if detailed:
26
- PrettyOutput.print(json.dumps(tools, indent=2, ensure_ascii=False), OutputType.CODE, lang="json")
26
+ PrettyOutput.print(
27
+ json.dumps(tools, indent=2, ensure_ascii=False),
28
+ OutputType.CODE,
29
+ lang="json",
30
+ )
27
31
  else:
28
32
  simple_tools = [
29
33
  {"name": t["name"], "description": t["description"]} for t in tools
30
34
  ]
31
- PrettyOutput.print(json.dumps(simple_tools, indent=2, ensure_ascii=False), OutputType.CODE, lang="json")
35
+ PrettyOutput.print(
36
+ json.dumps(simple_tools, indent=2, ensure_ascii=False),
37
+ OutputType.CODE,
38
+ lang="json",
39
+ )
32
40
  else:
33
41
  PrettyOutput.section("可用工具列表", OutputType.SYSTEM)
34
42
  for tool in tools:
@@ -37,18 +45,27 @@ def list_tools(
37
45
  if detailed:
38
46
  PrettyOutput.print(" 参数:", OutputType.INFO)
39
47
  import json as _json # local import to ensure available
40
- PrettyOutput.print(_json.dumps(tool["parameters"], ensure_ascii=False, indent=2), OutputType.CODE, lang="json")
48
+
49
+ PrettyOutput.print(
50
+ _json.dumps(tool["parameters"], ensure_ascii=False, indent=2),
51
+ OutputType.CODE,
52
+ lang="json",
53
+ )
41
54
 
42
55
 
43
56
  @app.command("stat")
44
57
  def stat_tools(
45
58
  as_json: bool = typer.Option(False, "--json", help="以JSON格式输出"),
46
- last_days: Optional[int] = typer.Option(None, "--days", help="显示最近N天的统计(默认显示所有历史数据)"),
47
- format: str = typer.Option("table", "--format", help="显示格式: table, chart, summary")
59
+ last_days: Optional[int] = typer.Option(
60
+ None, "--days", help="显示最近N天的统计(默认显示所有历史数据)"
61
+ ),
62
+ format: str = typer.Option(
63
+ "table", "--format", help="显示格式: table, chart, summary"
64
+ ),
48
65
  ):
49
66
  """显示工具调用统计信息"""
50
67
  from jarvis.jarvis_stats.stats import StatsManager
51
-
68
+
52
69
  if format == "table":
53
70
  registry = ToolRegistry()
54
71
  stats = registry._get_tool_stats()
@@ -64,13 +81,24 @@ def stat_tools(
64
81
  table_data.sort(key=lambda x: x[1], reverse=True)
65
82
 
66
83
  if as_json:
67
- PrettyOutput.print(json.dumps(dict(table_data), indent=2), OutputType.CODE, lang="json")
84
+ PrettyOutput.print(
85
+ json.dumps(dict(table_data), indent=2), OutputType.CODE, lang="json"
86
+ )
68
87
  else:
69
88
  time_desc = f"最近{last_days}天" if last_days else "所有历史"
70
89
  PrettyOutput.section(f"工具调用统计 ({time_desc})", OutputType.SYSTEM)
71
90
  if table_data:
72
- PrettyOutput.print(tabulate(table_data, headers=["工具名称", "调用次数"], tablefmt="grid"), OutputType.CODE, lang="text")
73
- PrettyOutput.print(f"\n总计: {len(table_data)} 个工具被使用,共 {sum(x[1] for x in table_data)} 次调用", OutputType.INFO)
91
+ PrettyOutput.print(
92
+ tabulate(
93
+ table_data, headers=["工具名称", "调用次数"], tablefmt="grid"
94
+ ),
95
+ OutputType.CODE,
96
+ lang="text",
97
+ )
98
+ PrettyOutput.print(
99
+ f"\n总计: {len(table_data)} 个工具被使用,共 {sum(x[1] for x in table_data)} 次调用",
100
+ OutputType.INFO,
101
+ )
74
102
  else:
75
103
  PrettyOutput.print("暂无工具调用记录", OutputType.INFO)
76
104
  else:
@@ -79,51 +107,59 @@ def stat_tools(
79
107
  # 显示所有标记为 tool 组的指标
80
108
  metrics = StatsManager.list_metrics()
81
109
  tool_metrics = []
82
-
110
+
83
111
  for metric in metrics:
84
112
  # 检查是否是工具组的指标
85
113
  if last_days:
86
114
  stats_data = StatsManager.get_stats(
87
- metric_name=metric,
88
- last_days=last_days,
89
- tags={"group": "tool"}
115
+ metric_name=metric, last_days=last_days, tags={"group": "tool"}
90
116
  )
91
117
  else:
92
118
  # 获取所有历史数据
93
119
  from datetime import datetime
120
+
94
121
  stats_data = StatsManager.get_stats(
95
122
  metric_name=metric,
96
123
  start_time=datetime(2000, 1, 1),
97
124
  end_time=datetime.now(),
98
- tags={"group": "tool"}
125
+ tags={"group": "tool"},
99
126
  )
100
127
  if stats_data and stats_data.get("records"):
101
128
  tool_metrics.append(metric)
102
-
129
+
103
130
  if tool_metrics:
104
131
  for metric in tool_metrics:
105
132
  if format == "chart":
106
133
  if last_days:
107
- StatsManager.plot(metric, last_days=last_days, tags={"group": "tool"})
134
+ StatsManager.plot(
135
+ metric, last_days=last_days, tags={"group": "tool"}
136
+ )
108
137
  else:
109
138
  from datetime import datetime
139
+
110
140
  StatsManager.plot(
111
- metric,
112
- start_time=datetime(2000, 1, 1),
113
- end_time=datetime.now(),
114
- tags={"group": "tool"}
141
+ metric,
142
+ start_time=datetime(2000, 1, 1),
143
+ end_time=datetime.now(),
144
+ tags={"group": "tool"},
115
145
  )
116
146
  elif format == "summary":
117
147
  if last_days:
118
- StatsManager.show(metric, last_days=last_days, format="summary", tags={"group": "tool"})
148
+ StatsManager.show(
149
+ metric,
150
+ last_days=last_days,
151
+ format="summary",
152
+ tags={"group": "tool"},
153
+ )
119
154
  else:
120
155
  from datetime import datetime
156
+
121
157
  StatsManager.show(
122
- metric,
123
- start_time=datetime(2000, 1, 1),
124
- end_time=datetime.now(),
125
- format="summary",
126
- tags={"group": "tool"}
158
+ metric,
159
+ start_time=datetime(2000, 1, 1),
160
+ end_time=datetime.now(),
161
+ format="summary",
162
+ tags={"group": "tool"},
127
163
  )
128
164
  else:
129
165
  PrettyOutput.print("暂无工具调用记录", OutputType.INFO)
@@ -159,9 +195,7 @@ def call_tool(
159
195
  with open(args_file, "r", encoding="utf-8") as f:
160
196
  tool_args = json.load(f)
161
197
  except (json.JSONDecodeError, FileNotFoundError) as e:
162
- PrettyOutput.print(
163
- f"错误: 无法从文件加载参数: {str(e)}", OutputType.ERROR
164
- )
198
+ PrettyOutput.print(f"错误: 无法从文件加载参数: {str(e)}", OutputType.ERROR)
165
199
  raise typer.Exit(code=1)
166
200
 
167
201
  required_params = tool_obj.parameters.get("required", [])
@@ -148,12 +148,13 @@ class FileSearchReplaceTool:
148
148
  content = f.read()
149
149
  original_content = content
150
150
 
151
-
152
151
  success, temp_content = EditFileHandler._fast_edit(
153
152
  file_path, changes
154
153
  )
155
154
  if not success:
156
- PrettyOutput.print(f"文件 {file_path} 处理失败", OutputType.ERROR)
155
+ PrettyOutput.print(
156
+ f"文件 {file_path} 处理失败", OutputType.ERROR
157
+ )
157
158
  file_results.append(
158
159
  {
159
160
  "file": file_path,
@@ -164,8 +165,6 @@ class FileSearchReplaceTool:
164
165
  )
165
166
  continue
166
167
 
167
-
168
-
169
168
  # 只有当所有替换操作都成功时,才写回文件
170
169
  if success and (
171
170
  temp_content != original_content or not file_exists
@@ -105,7 +105,6 @@ class FileAnalyzerTool:
105
105
 
106
106
  analysis_result = platform.chat_until_success(analysis_request)
107
107
 
108
-
109
108
  # 清理会话
110
109
  platform.delete_chat()
111
110
 
@@ -80,7 +80,9 @@ class generate_new_tool:
80
80
  # 验证工具代码中的名称是否与tool_name一致
81
81
  import re
82
82
 
83
- match = re.search(r"^\s*name\s*=\s*[\"'](.+?)[\"']", tool_code, re.MULTILINE)
83
+ match = re.search(
84
+ r"^\s*name\s*=\s*[\"'](.+?)[\"']", tool_code, re.MULTILINE
85
+ )
84
86
  if not match:
85
87
  return {
86
88
  "success": False,
@@ -146,8 +148,6 @@ class generate_new_tool:
146
148
  )
147
149
  success_message += f"\n注册到当前会话失败,可能需要重新启动Jarvis"
148
150
 
149
-
150
-
151
151
  # 检查并安装缺失的依赖
152
152
  try:
153
153
  required_packages = set()
@@ -115,7 +115,6 @@ class ReadCodeTool:
115
115
  f"{numbered_content}\n\n"
116
116
  )
117
117
 
118
-
119
118
  if agent:
120
119
  files = agent.get_user_data("files")
121
120
  if files:
@@ -81,15 +81,25 @@ class WebpageTool:
81
81
  resp = http_get(url, timeout=10.0, allow_redirects=True)
82
82
  content_md = md(resp.text, strip=["script", "style"])
83
83
  except requests.exceptions.HTTPError as e:
84
- PrettyOutput.print(f"⚠️ HTTP错误 {e.response.status_code} 访问 {url}", OutputType.WARNING)
85
- return {"success": False, "stdout": "", "stderr": f"HTTP错误:{e.response.status_code}"}
84
+ PrettyOutput.print(
85
+ f"⚠️ HTTP错误 {e.response.status_code} 访问 {url}",
86
+ OutputType.WARNING,
87
+ )
88
+ return {
89
+ "success": False,
90
+ "stdout": "",
91
+ "stderr": f"HTTP错误:{e.response.status_code}",
92
+ }
86
93
  except requests.exceptions.RequestException as e:
87
94
  PrettyOutput.print(f"⚠️ 请求错误: {e}", OutputType.WARNING)
88
95
  return {"success": False, "stdout": "", "stderr": f"请求错误:{e}"}
89
96
 
90
97
  if not content_md or not content_md.strip():
91
- return {"success": False, "stdout": "", "stderr": "无法从网页抓取有效内容。"}
92
-
98
+ return {
99
+ "success": False,
100
+ "stdout": "",
101
+ "stderr": "无法从网页抓取有效内容。",
102
+ }
93
103
 
94
104
  summary_prompt = f"""以下是网页 {url} 的内容(已转换为Markdown):
95
105
  ----------------
@@ -288,7 +288,6 @@ class ToolRegistry(OutputHandlerProtocol):
288
288
  if tool_name in self.tools:
289
289
  del self.tools[tool_name]
290
290
 
291
-
292
291
  def _load_mcp_tools(self) -> None:
293
292
  """加载MCP工具,优先从配置获取,其次从目录扫描"""
294
293
  from jarvis.jarvis_utils.config import get_mcp_config
@@ -351,7 +350,6 @@ class ToolRegistry(OutputHandlerProtocol):
351
350
  try:
352
351
  import subprocess
353
352
 
354
-
355
353
  subprocess.run(
356
354
  ["git", "clone", central_repo, central_repo_path], check=True
357
355
  )
@@ -649,7 +647,6 @@ class ToolRegistry(OutputHandlerProtocol):
649
647
 
650
648
  # Ask user for confirmation
651
649
 
652
-
653
650
  data = temp_data
654
651
  except (yaml.YAMLError, EOFError, KeyboardInterrupt):
655
652
  # Even after fixing, it's not valid YAML, or user cancelled.
@@ -166,7 +166,6 @@ class RetrieveMemoryTool:
166
166
 
167
167
  # 打印结果摘要
168
168
 
169
-
170
169
  if tags:
171
170
  pass
172
171
 
@@ -174,7 +174,6 @@ class SaveMemoryTool:
174
174
 
175
175
  # 生成总结报告
176
176
 
177
-
178
177
  # 构建返回结果
179
178
  output = {
180
179
  "total": len(memories),
@@ -81,8 +81,6 @@ class SearchWebTool:
81
81
 
82
82
  url_list_str = "\n".join(f" - {u}" for u in visited_urls)
83
83
 
84
-
85
-
86
84
  summary_prompt = f"请为查询“{query}”总结以下内容:\n\n{full_content}"
87
85
 
88
86
  if not agent.model:
@@ -0,0 +1,197 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ sub_agent 工具
4
+ 将子任务交给通用 Agent 执行,并返回执行结果。
5
+
6
+ 约定:
7
+ - 仅接收一个参数:task
8
+ - 不依赖父 Agent,所有配置使用系统默认与全局变量
9
+ - 子Agent必须自动完成(auto_complete=True)且需要summary(need_summary=True)
10
+ """
11
+ from typing import Any, Dict, Optional
12
+ import json
13
+
14
+ from jarvis.jarvis_agent import Agent, origin_agent_system_prompt
15
+ from jarvis.jarvis_utils.globals import delete_agent
16
+
17
+
18
+ class SubAgentTool:
19
+ """
20
+ 临时创建一个通用 Agent 执行子任务,执行完立即清理。
21
+ - 不注册至全局
22
+ - 使用系统默认/全局配置
23
+ - 启用自动完成与总结
24
+ """
25
+
26
+ # 必须与文件名一致,供 ToolRegistry 自动注册
27
+ name = "sub_agent"
28
+ description = "将子任务交给通用 Agent 执行,并返回执行结果(使用系统默认配置,自动完成并生成总结)。"
29
+ parameters = {
30
+ "type": "object",
31
+ "properties": {
32
+ "task": {
33
+ "type": "string",
34
+ "description": "要执行的子任务内容(必填)",
35
+ },
36
+ "background": {
37
+ "type": "string",
38
+ "description": "任务背景与已知信息(可选,将与任务一并提供给子Agent)",
39
+ },
40
+ },
41
+ "required": ["task"],
42
+ }
43
+
44
+ def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
45
+ """
46
+ 执行子任务并返回结果。
47
+ 返回:
48
+ - success: 是否成功
49
+ - stdout: 子 Agent 返回的结果(字符串或JSON字符串)
50
+ - stderr: 错误信息(如有)
51
+ """
52
+ try:
53
+ task: str = str(args.get("task", "")).strip()
54
+ if not task:
55
+ return {
56
+ "success": False,
57
+ "stdout": "",
58
+ "stderr": "task 不能为空",
59
+ }
60
+
61
+ # 读取背景信息并组合任务
62
+ background: str = str(args.get("background", "")).strip()
63
+ enhanced_task = (
64
+ f"背景信息:\n{background}\n\n任务:\n{task}" if background else task
65
+ )
66
+
67
+ # 读取背景信息并组合任务
68
+ background: str = str(args.get("background", "")).strip()
69
+ enhanced_task = (
70
+ f"背景信息:\n{background}\n\n任务:\n{task}" if background else task
71
+ )
72
+
73
+ # 继承父Agent的运行参数(用于覆盖默认值);若无父Agent则使用默认/全局配置
74
+ parent_agent = args.get("agent")
75
+ # 如未注入父Agent,尝试从全局获取当前或任一已注册Agent
76
+ if parent_agent is None:
77
+ try:
78
+ from jarvis.jarvis_utils import globals as G # 延迟导入避免循环
79
+
80
+ curr = getattr(G, "current_agent_name", "")
81
+ if curr:
82
+ parent_agent = getattr(G, "global_agents", {}).get(curr)
83
+ if parent_agent is None and getattr(G, "global_agents", {}):
84
+ try:
85
+ parent_agent = next(iter(G.global_agents.values()))
86
+ except Exception:
87
+ parent_agent = None
88
+ except Exception:
89
+ parent_agent = None
90
+ # 默认/全局
91
+ system_prompt = origin_agent_system_prompt
92
+ need_summary = True
93
+ auto_complete = True
94
+
95
+ # 可继承参数
96
+ model_group = None
97
+ summary_prompt = None
98
+ execute_tool_confirm = None
99
+ use_methodology = None
100
+ use_analysis = None
101
+ force_save_memory = None
102
+ use_tools: list[str] = []
103
+
104
+ try:
105
+ if parent_agent is not None:
106
+ # 继承模型组
107
+ if getattr(parent_agent, "model", None):
108
+ model_group = getattr(parent_agent.model, "model_group", None)
109
+ # 继承开关类参数
110
+ summary_prompt = getattr(parent_agent, "summary_prompt", None)
111
+ execute_tool_confirm = getattr(
112
+ parent_agent, "execute_tool_confirm", None
113
+ )
114
+ use_methodology = getattr(parent_agent, "use_methodology", None)
115
+ use_analysis = getattr(parent_agent, "use_analysis", None)
116
+ force_save_memory = getattr(parent_agent, "force_save_memory", None)
117
+ # 继承工具使用集(名称列表)
118
+ parent_registry = parent_agent.get_tool_registry()
119
+ if parent_registry:
120
+ for t in parent_registry.get_all_tools():
121
+ if isinstance(t, dict) and t.get("name"):
122
+ use_tools.append(str(t["name"]))
123
+ except Exception:
124
+ # 忽略继承失败,退回默认配置
125
+ pass
126
+
127
+ # 为避免交互阻塞:提供自动确认与空输入处理器
128
+ def _auto_confirm(tip: str, default: bool = True) -> bool:
129
+ return default
130
+
131
+ # 创建子Agent(其余配置使用默认/全局)
132
+ agent = Agent(
133
+ system_prompt=system_prompt,
134
+ name="SubAgent",
135
+ description="Temporary sub agent for executing a subtask",
136
+ llm_type="normal", # 使用默认模型类型
137
+ model_group=model_group, # 继承父Agent模型组(如可用)
138
+ summary_prompt=summary_prompt, # 继承父Agent总结提示词(如可用)
139
+ auto_complete=auto_complete,
140
+ output_handler=None, # 默认 ToolRegistry
141
+ use_tools=None, # 初始不限定,稍后同步父Agent工具集
142
+ input_handler=None, # 允许使用系统默认输入链
143
+ execute_tool_confirm=execute_tool_confirm, # 继承父Agent(如可用)
144
+ need_summary=need_summary,
145
+ multiline_inputer=None, # 使用系统默认输入器(允许用户输入)
146
+ use_methodology=use_methodology, # 继承父Agent(如可用)
147
+ use_analysis=use_analysis, # 继承父Agent(如可用)
148
+ force_save_memory=force_save_memory, # 继承父Agent(如可用)
149
+ files=None,
150
+ confirm_callback=_auto_confirm, # 自动确认
151
+ )
152
+
153
+ # 同步父Agent的模型名称与工具使用集(若可用)
154
+ try:
155
+ if (
156
+ parent_agent is not None
157
+ and getattr(parent_agent, "model", None)
158
+ and getattr(agent, "model", None)
159
+ ):
160
+ try:
161
+ model_name = parent_agent.model.name() # type: ignore[attr-defined]
162
+ if model_name:
163
+ agent.model.set_model_name(model_name) # type: ignore[attr-defined]
164
+ except Exception:
165
+ pass
166
+ if use_tools:
167
+ agent.set_use_tools(use_tools)
168
+ except Exception:
169
+ pass
170
+
171
+ # 执行任务
172
+ result = agent.run(enhanced_task)
173
+
174
+ # 主动清理,避免污染父 Agent 的全局状态
175
+ try:
176
+ delete_agent(agent.name)
177
+ except Exception:
178
+ pass
179
+
180
+ # 规范化输出
181
+ if isinstance(result, (dict, list)):
182
+ stdout = json.dumps(result, ensure_ascii=False, indent=2)
183
+ else:
184
+ stdout = str(result) if result is not None else "任务执行完成"
185
+
186
+ return {
187
+ "success": True,
188
+ "stdout": stdout,
189
+ "stderr": "",
190
+ }
191
+
192
+ except Exception as e:
193
+ return {
194
+ "success": False,
195
+ "stdout": "",
196
+ "stderr": f"执行子任务失败: {str(e)}",
197
+ }