jarvis-ai-assistant 0.2.8__py3-none-any.whl → 0.3.1__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 (40) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +277 -242
  3. jarvis/jarvis_agent/agent_manager.py +85 -0
  4. jarvis/jarvis_agent/config_editor.py +53 -0
  5. jarvis/jarvis_agent/file_methodology_manager.py +105 -0
  6. jarvis/jarvis_agent/jarvis.py +30 -619
  7. jarvis/jarvis_agent/memory_manager.py +127 -0
  8. jarvis/jarvis_agent/methodology_share_manager.py +174 -0
  9. jarvis/jarvis_agent/prompts.py +18 -3
  10. jarvis/jarvis_agent/share_manager.py +176 -0
  11. jarvis/jarvis_agent/task_analyzer.py +126 -0
  12. jarvis/jarvis_agent/task_manager.py +111 -0
  13. jarvis/jarvis_agent/tool_share_manager.py +139 -0
  14. jarvis/jarvis_code_agent/code_agent.py +26 -20
  15. jarvis/jarvis_data/config_schema.json +37 -4
  16. jarvis/jarvis_platform/ai8.py +13 -1
  17. jarvis/jarvis_platform/base.py +20 -5
  18. jarvis/jarvis_platform/human.py +11 -1
  19. jarvis/jarvis_platform/kimi.py +10 -0
  20. jarvis/jarvis_platform/openai.py +20 -0
  21. jarvis/jarvis_platform/tongyi.py +14 -9
  22. jarvis/jarvis_platform/yuanbao.py +10 -0
  23. jarvis/jarvis_platform_manager/main.py +12 -12
  24. jarvis/jarvis_platform_manager/service.py +9 -4
  25. jarvis/jarvis_tools/registry.py +32 -0
  26. jarvis/jarvis_tools/retrieve_memory.py +36 -8
  27. jarvis/jarvis_tools/search_web.py +1 -1
  28. jarvis/jarvis_utils/clipboard.py +90 -0
  29. jarvis/jarvis_utils/config.py +64 -0
  30. jarvis/jarvis_utils/git_utils.py +17 -7
  31. jarvis/jarvis_utils/globals.py +18 -12
  32. jarvis/jarvis_utils/input.py +118 -16
  33. jarvis/jarvis_utils/methodology.py +48 -5
  34. jarvis/jarvis_utils/utils.py +169 -105
  35. {jarvis_ai_assistant-0.2.8.dist-info → jarvis_ai_assistant-0.3.1.dist-info}/METADATA +1 -1
  36. {jarvis_ai_assistant-0.2.8.dist-info → jarvis_ai_assistant-0.3.1.dist-info}/RECORD +40 -30
  37. {jarvis_ai_assistant-0.2.8.dist-info → jarvis_ai_assistant-0.3.1.dist-info}/WHEEL +0 -0
  38. {jarvis_ai_assistant-0.2.8.dist-info → jarvis_ai_assistant-0.3.1.dist-info}/entry_points.txt +0 -0
  39. {jarvis_ai_assistant-0.2.8.dist-info → jarvis_ai_assistant-0.3.1.dist-info}/licenses/LICENSE +0 -0
  40. {jarvis_ai_assistant-0.2.8.dist-info → jarvis_ai_assistant-0.3.1.dist-info}/top_level.txt +0 -0
@@ -13,6 +13,9 @@ from jarvis.jarvis_agent.prompt_builder import build_action_prompt
13
13
  from jarvis.jarvis_agent.protocols import OutputHandlerProtocol
14
14
  from jarvis.jarvis_agent.session_manager import SessionManager
15
15
  from jarvis.jarvis_agent.tool_executor import execute_tool_call
16
+ from jarvis.jarvis_agent.memory_manager import MemoryManager
17
+ from jarvis.jarvis_agent.task_analyzer import TaskAnalyzer
18
+ from jarvis.jarvis_agent.file_methodology_manager import FileMethodologyManager
16
19
  from jarvis.jarvis_agent.prompts import (
17
20
  DEFAULT_SUMMARY_PROMPT,
18
21
  SUMMARY_REQUEST_PROMPT,
@@ -31,6 +34,7 @@ from jarvis.jarvis_utils.config import (
31
34
  get_thinking_model_name,
32
35
  get_thinking_platform_name,
33
36
  is_execute_tool_confirm,
37
+ is_force_save_memory,
34
38
  is_use_analysis,
35
39
  is_use_methodology,
36
40
  )
@@ -43,7 +47,6 @@ from jarvis.jarvis_utils.globals import (
43
47
  set_interrupt,
44
48
  )
45
49
  from jarvis.jarvis_utils.input import get_multiline_input, user_confirm
46
- from jarvis.jarvis_utils.methodology import load_methodology, upload_methodology
47
50
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
48
51
  from jarvis.jarvis_utils.tag import ct, ot
49
52
 
@@ -113,9 +116,9 @@ class Agent:
113
116
  multiline_inputer: Optional[Callable[[str], str]] = None,
114
117
  use_methodology: Optional[bool] = None,
115
118
  use_analysis: Optional[bool] = None,
119
+ force_save_memory: Optional[bool] = None,
116
120
  files: List[str] = [],
117
121
  ):
118
- self.files = files
119
122
  """初始化Jarvis Agent实例
120
123
 
121
124
  参数:
@@ -133,10 +136,51 @@ class Agent:
133
136
  multiline_inputer: 多行输入处理器
134
137
  use_methodology: 是否使用方法论
135
138
  use_analysis: 是否使用任务分析
139
+ force_save_memory: 是否强制保存记忆
136
140
  """
141
+ # 基础属性初始化
142
+ self.files = files
137
143
  self.name = make_agent_name(name)
138
144
  self.description = description
139
- # 初始化平台和模型
145
+ self.system_prompt = system_prompt
146
+ self.need_summary = need_summary
147
+ self.auto_complete = auto_complete
148
+ self.first = True
149
+ self.run_input_handlers_next_turn = False
150
+ self.user_data: Dict[str, Any] = {}
151
+ self.after_tool_call_cb: Optional[Callable[[Agent], None]] = None
152
+
153
+ # 初始化模型和会话
154
+ self._init_model(llm_type, model_group)
155
+ self._init_session()
156
+
157
+ # 初始化处理器
158
+ self._init_handlers(output_handler, input_handler, multiline_inputer, use_tools)
159
+
160
+ # 初始化配置
161
+ self._init_config(
162
+ use_methodology,
163
+ use_analysis,
164
+ execute_tool_confirm,
165
+ summary_prompt,
166
+ model_group,
167
+ force_save_memory,
168
+ )
169
+
170
+ # 初始化管理器
171
+ self.memory_manager = MemoryManager(self)
172
+ self.task_analyzer = TaskAnalyzer(self)
173
+ self.file_methodology_manager = FileMethodologyManager(self)
174
+
175
+ # 设置系统提示词
176
+ self._setup_system_prompt()
177
+
178
+ # 打印欢迎信息
179
+ welcome_message = f"{name} 初始化完成 - 使用 {self.model.name()} 模型" # type: ignore
180
+ PrettyOutput.print(welcome_message, OutputType.SYSTEM)
181
+
182
+ def _init_model(self, llm_type: str, model_group: Optional[str]):
183
+ """初始化模型平台"""
140
184
  if llm_type == "thinking":
141
185
  platform_name = get_thinking_platform_name(model_group)
142
186
  model_name = get_thinking_model_name(model_group)
@@ -146,49 +190,60 @@ class Agent:
146
190
 
147
191
  self.model = PlatformRegistry().create_platform(platform_name)
148
192
  if self.model is None:
149
- PrettyOutput.print(
150
- f"平台 {platform_name} 不存在,将使用普通模型", OutputType.WARNING
151
- )
193
+ PrettyOutput.print(f"平台 {platform_name} 不存在,将使用普通模型", OutputType.WARNING)
152
194
  self.model = PlatformRegistry().get_normal_platform()
153
195
 
154
196
  if model_name:
155
197
  self.model.set_model_name(model_name)
156
198
 
157
199
  self.model.set_model_group(model_group)
158
-
159
- self.user_data: Dict[str, Any] = {}
160
-
161
200
  self.model.set_suppress_output(False)
162
201
 
163
- # Initialize the session manager
164
- self.session = SessionManager(model=self.model, agent_name=self.name)
202
+ def _init_session(self):
203
+ """初始化会话管理器"""
204
+ self.session = SessionManager(model=self.model, agent_name=self.name) # type: ignore
165
205
 
206
+ def _init_handlers(
207
+ self,
208
+ output_handler: List[OutputHandlerProtocol],
209
+ input_handler: Optional[List[Callable[[str, Any], Tuple[str, bool]]]],
210
+ multiline_inputer: Optional[Callable[[str], str]],
211
+ use_tools: List[str],
212
+ ):
213
+ """初始化各种处理器"""
166
214
  from jarvis.jarvis_tools.registry import ToolRegistry
167
215
 
168
216
  self.output_handler = output_handler if output_handler else [ToolRegistry()]
169
217
  self.set_use_tools(use_tools)
170
218
 
219
+ self.input_handler = input_handler if input_handler is not None else []
220
+
171
221
  self.multiline_inputer = (
172
222
  multiline_inputer if multiline_inputer else get_multiline_input
173
223
  )
174
224
 
225
+ def _init_config(
226
+ self,
227
+ use_methodology: Optional[bool],
228
+ use_analysis: Optional[bool],
229
+ execute_tool_confirm: Optional[bool],
230
+ summary_prompt: Optional[str],
231
+ model_group: Optional[str],
232
+ force_save_memory: Optional[bool],
233
+ ):
234
+ """初始化配置选项"""
175
235
  # 如果有上传文件,自动禁用方法论
176
236
  self.use_methodology = (
177
237
  False
178
- if files
238
+ if self.files
179
239
  else (
180
240
  use_methodology if use_methodology is not None else is_use_methodology()
181
241
  )
182
242
  )
243
+
183
244
  self.use_analysis = (
184
245
  use_analysis if use_analysis is not None else is_use_analysis()
185
246
  )
186
- self.system_prompt = system_prompt
187
- self.input_handler = input_handler if input_handler is not None else []
188
- self.need_summary = need_summary
189
- # Load configuration from environment variables
190
-
191
- self.after_tool_call_cb: Optional[Callable[[Agent], None]] = None
192
247
 
193
248
  self.execute_tool_confirm = (
194
249
  execute_tool_confirm
@@ -201,22 +256,23 @@ class Agent:
201
256
  )
202
257
 
203
258
  self.max_token_count = get_max_token_count(model_group)
204
- self.auto_complete = auto_complete
205
- welcome_message = f"{name} 初始化完成 - 使用 {self.model.name()} 模型"
206
259
 
207
- PrettyOutput.print(welcome_message, OutputType.SYSTEM)
260
+ self.force_save_memory = (
261
+ force_save_memory
262
+ if force_save_memory is not None
263
+ else is_force_save_memory()
264
+ )
208
265
 
266
+ def _setup_system_prompt(self):
267
+ """设置系统提示词"""
209
268
  action_prompt = self.get_tool_usage_prompt()
210
-
211
- self.model.set_system_prompt(
269
+ self.model.set_system_prompt( # type: ignore
212
270
  f"""
213
271
  {self.system_prompt}
214
272
 
215
273
  {action_prompt}
216
274
  """
217
275
  )
218
- self.first = True
219
- self.run_input_handlers_next_turn = False
220
276
 
221
277
  def set_user_data(self, key: str, value: Any):
222
278
  """Sets user data in the session."""
@@ -280,6 +336,7 @@ class Agent:
280
336
  参数:
281
337
  message: 输入给模型的消息
282
338
  need_complete: 是否需要完成任务标记
339
+ run_input_handlers: 是否运行输入处理器
283
340
 
284
341
  返回:
285
342
  str: 模型的响应
@@ -290,27 +347,63 @@ class Agent:
290
347
  3. 会自动添加附加提示
291
348
  4. 会检查并处理上下文长度限制
292
349
  """
350
+ # 处理输入
351
+ message = self._process_input(message, run_input_handlers)
352
+ if run_input_handlers and self._should_return_early(message):
353
+ return message
354
+
355
+ # 添加附加提示
356
+ message = self._add_addon_prompt(message, need_complete)
357
+
358
+ # 管理对话长度
359
+ message = self._manage_conversation_length(message)
360
+
361
+ # 调用模型
362
+ response = self._invoke_model(message)
363
+
364
+ return response
365
+
366
+ def _process_input(self, message: str, run_input_handlers: bool) -> str:
367
+ """处理输入消息"""
293
368
  if run_input_handlers:
294
369
  for handler in self.input_handler:
295
370
  message, need_return = handler(message, self)
296
371
  if need_return:
372
+ self._last_handler_returned = True
297
373
  return message
374
+ self._last_handler_returned = False
375
+ return message
376
+
377
+ def _should_return_early(self, message: str) -> bool:
378
+ """检查是否需要提前返回"""
379
+ return hasattr(self, "_last_handler_returned") and self._last_handler_returned
298
380
 
381
+ def _add_addon_prompt(self, message: str, need_complete: bool) -> str:
382
+ """添加附加提示到消息"""
299
383
  if self.session.addon_prompt:
300
384
  message += f"\n\n{self.session.addon_prompt}"
301
385
  self.session.addon_prompt = ""
302
386
  else:
303
387
  message += f"\n\n{self.make_default_addon_prompt(need_complete)}"
388
+ return message
304
389
 
305
- # 累加对话长度
390
+ def _manage_conversation_length(self, message: str) -> str:
391
+ """管理对话长度,必要时进行摘要"""
306
392
  self.session.conversation_length += get_context_token_count(message)
307
393
 
308
394
  if self.session.conversation_length > self.max_token_count:
309
- message = self._summarize_and_clear_history() + "\n\n" + message
310
- self.session.conversation_length += get_context_token_count(message)
395
+ summary = self._summarize_and_clear_history()
396
+ if summary:
397
+ message = summary + "\n\n" + message
398
+ self.session.conversation_length = get_context_token_count(message)
311
399
 
400
+ return message
401
+
402
+ def _invoke_model(self, message: str) -> str:
403
+ """实际调用模型获取响应"""
312
404
  if not self.model:
313
405
  raise RuntimeError("Model not initialized")
406
+
314
407
  response = self.model.chat_until_success(message) # type: ignore
315
408
  self.session.conversation_length += get_context_token_count(response)
316
409
 
@@ -342,11 +435,12 @@ class Agent:
342
435
  """总结当前对话并清理历史记录
343
436
 
344
437
  该方法将:
345
- 1. 调用_generate_summary生成摘要
346
- 2. 清除对话历史
347
- 3. 保留系统消息
348
- 4. 添加摘要作为新上下文
349
- 5. 重置对话长度计数器
438
+ 1. 提示用户保存重要记忆
439
+ 2. 调用_generate_summary生成摘要
440
+ 3. 清除对话历史
441
+ 4. 保留系统消息
442
+ 5. 添加摘要作为新上下文
443
+ 6. 重置对话长度计数器
350
444
 
351
445
  返回:
352
446
  str: 包含对话摘要的字符串
@@ -354,25 +448,37 @@ class Agent:
354
448
  注意:
355
449
  当上下文长度超过最大值时使用
356
450
  """
357
- need_summary = True
358
- tmp_file_name = ""
359
- try:
360
- if self.model and self.model.support_upload_files():
361
- need_summary = False
362
- if need_summary:
363
- summary = self.generate_summary()
364
- else:
365
- import tempfile
451
+ # 在清理历史之前,提示用户保存重要记忆
452
+ if self.force_save_memory:
453
+ print("📌 对话历史即将被总结和清理,请先保存重要信息...")
454
+ self.memory_manager.prompt_memory_save()
455
+
456
+ if self._should_use_file_upload():
457
+ return self._handle_history_with_file_upload()
458
+ else:
459
+ return self._handle_history_with_summary()
366
460
 
367
- tmp_file = tempfile.NamedTemporaryFile(delete=False)
368
- tmp_file_name = tmp_file.name
369
- self.clear_history() # type: ignore
461
+ def _should_use_file_upload(self) -> bool:
462
+ """判断是否应该使用文件上传方式处理历史"""
463
+ return bool(self.model and self.model.support_upload_files())
464
+
465
+ def _handle_history_with_summary(self) -> str:
466
+ """使用摘要方式处理历史"""
467
+ summary = self.generate_summary()
468
+ self.clear_history()
469
+
470
+ if not summary:
471
+ return ""
370
472
 
371
- if need_summary:
372
- if not summary:
373
- return ""
473
+ return self._format_summary_message(summary)
374
474
 
375
- return f"""
475
+ def _handle_history_with_file_upload(self) -> str:
476
+ """使用文件上传方式处理历史"""
477
+ return self.file_methodology_manager.handle_history_with_file_upload()
478
+
479
+ def _format_summary_message(self, summary: str) -> str:
480
+ """格式化摘要消息"""
481
+ return f"""
376
482
  以下是之前对话的关键信息总结:
377
483
 
378
484
  <content>
@@ -381,14 +487,6 @@ class Agent:
381
487
 
382
488
  请基于以上信息继续完成任务。请注意,这是之前对话的摘要,上下文长度已超过限制而被重置。请直接继续任务,无需重复已完成的步骤。如有需要,可以询问用户以获取更多信息。
383
489
  """
384
- else:
385
- if self.model and self.model.upload_files([tmp_file_name]):
386
- return "上传的文件是历史对话信息,请基于历史对话信息继续完成任务。"
387
- else:
388
- return ""
389
- finally:
390
- if tmp_file_name:
391
- os.remove(tmp_file_name)
392
490
 
393
491
  def _call_tools(self, response: str) -> Tuple[bool, Any]:
394
492
  """
@@ -407,26 +505,18 @@ class Agent:
407
505
  2. 对于子Agent: 可能会生成总结(如果启用)
408
506
  3. 使用spinner显示生成状态
409
507
  """
410
- satisfaction_feedback = ""
411
-
412
- if not auto_completed and self.use_analysis:
413
- if user_confirm("您对本次任务的完成是否满意?", True):
414
- satisfaction_feedback = "\n\n用户对本次任务的完成表示满意。"
415
- else:
416
- feedback = self.multiline_inputer(
417
- "请提供您的反馈意见(可留空直接回车):"
418
- )
419
- if feedback:
420
- satisfaction_feedback = (
421
- f"\n\n用户对本次任务的完成不满意,反馈意见如下:\n{feedback}"
422
- )
423
- else:
424
- satisfaction_feedback = (
425
- "\n\n用户对本次任务的完成不满意,未提供具体反馈意见。"
426
- )
508
+ # 收集满意度反馈
509
+ satisfaction_feedback = self.task_analyzer.collect_satisfaction_feedback(
510
+ auto_completed
511
+ )
427
512
 
428
513
  if self.use_analysis:
429
- self._analysis_task(satisfaction_feedback)
514
+ self.task_analyzer.analysis_task(satisfaction_feedback)
515
+ else:
516
+ # 如果没有开启分析,也提示用户是否有值得记忆的信息
517
+ if self.force_save_memory:
518
+ self.memory_manager.prompt_memory_save()
519
+
430
520
  if self.need_summary:
431
521
  print("📄 正在生成总结...")
432
522
  self.session.prompt = self.summary_prompt
@@ -438,23 +528,6 @@ class Agent:
438
528
 
439
529
  return "任务完成"
440
530
 
441
- def _analysis_task(self, satisfaction_feedback: str = ""):
442
- print("🔍 正在分析任务...")
443
- try:
444
- # 让模型判断是否需要生成方法论
445
- analysis_prompt = TASK_ANALYSIS_PROMPT
446
- if satisfaction_feedback:
447
- analysis_prompt += satisfaction_feedback
448
-
449
- self.session.prompt = analysis_prompt
450
- if not self.model:
451
- raise RuntimeError("Model not initialized")
452
- response = self.model.chat_until_success(self.session.prompt) # type: ignore
453
- self._call_tools(response)
454
- print("✅ 分析完成")
455
- except Exception as e:
456
- print("❌ 分析失败")
457
-
458
531
  def make_default_addon_prompt(self, need_complete: bool) -> str:
459
532
  """生成附加提示。
460
533
 
@@ -473,21 +546,10 @@ class Agent:
473
546
  )
474
547
 
475
548
  # 检查工具列表并添加记忆工具相关提示
476
- memory_prompts = ""
477
549
  tool_registry = self.get_tool_registry()
478
- if tool_registry:
479
- tool_names = [tool.name for tool in tool_registry.tools.values()]
480
-
481
- # 如果有save_memory工具,添加相关提示
482
- if "save_memory" in tool_names:
483
- memory_prompts += "\n - 如果有关键信息需要记忆,请调用save_memory工具进行记忆:"
484
- memory_prompts += "\n * project_long_term: 保存与当前项目相关的长期信息"
485
- memory_prompts += "\n * global_long_term: 保存通用的信息、用户喜好、知识、方法等"
486
- memory_prompts += "\n * short_term: 保存当前任务相关的临时信息"
487
-
488
- # 如果有retrieve_memory工具,添加相关提示
489
- if "retrieve_memory" in tool_names:
490
- memory_prompts += "\n - 如果需要检索相关记忆信息,请调用retrieve_memory工具"
550
+ memory_prompts = self.memory_manager.add_memory_prompts_to_addon(
551
+ "", tool_registry
552
+ )
491
553
 
492
554
  addon_prompt = f"""
493
555
  <system_prompt>
@@ -521,155 +583,128 @@ class Agent:
521
583
  3. 包含错误处理和恢复逻辑
522
584
  4. 自动加载相关方法论(如果是首次运行)
523
585
  """
524
-
525
586
  self.session.prompt = f"{user_input}"
526
587
  try:
527
588
  set_agent(self.name, self)
589
+ return self._main_loop()
590
+ except Exception as e:
591
+ PrettyOutput.print(f"任务失败: {str(e)}", OutputType.ERROR)
592
+ return f"Task failed: {str(e)}"
593
+
594
+ def _main_loop(self) -> Any:
595
+ """主运行循环"""
596
+ run_input_handlers = True
528
597
 
529
- run_input_handlers = True
530
- while True:
598
+ while True:
599
+ try:
600
+ # 更新输入处理器标志
531
601
  if self.run_input_handlers_next_turn:
532
602
  run_input_handlers = True
533
603
  self.run_input_handlers_next_turn = False
534
604
 
605
+ # 首次运行初始化
535
606
  if self.first:
536
607
  self._first_run()
537
- try:
538
- current_response = self._call_model(
539
- self.session.prompt, True, run_input_handlers
540
- )
541
- self.session.prompt = ""
542
- run_input_handlers = False
543
-
544
- if get_interrupt():
545
- set_interrupt(False)
546
- user_input = self.multiline_inputer(
547
- f"模型交互期间被中断,请输入用户干预信息:"
548
- )
549
- if user_input:
550
- run_input_handlers = True
551
- # 如果有工具调用且用户确认继续,则继续执行工具调用
552
- if any(
553
- handler.can_handle(current_response)
554
- for handler in self.output_handler
555
- ):
556
- if user_confirm(
557
- "检测到有工具调用,是否继续处理工具调用?", True
558
- ):
559
- # 先添加用户干预信息到session
560
- self.session.prompt = f"被用户中断,用户补充信息为:{user_input}\n\n用户同意继续工具调用。"
561
- # 继续执行下面的工具调用逻辑,不要continue跳过
562
- else:
563
- # 用户选择不继续处理工具调用
564
- self.session.prompt = f"被用户中断,用户补充信息为:{user_input}\n\n检测到有工具调用,但被用户拒绝执行。请根据用户的补充信息重新考虑下一步操作。"
565
- continue
566
- else:
567
- # 没有检测到工具调用
568
- self.session.prompt = (
569
- f"被用户中断,用户补充信息为:{user_input}"
570
- )
571
- continue
572
-
573
- need_return, self.session.prompt = self._call_tools(
574
- current_response
575
- )
576
-
577
- if need_return:
578
- return self.session.prompt
579
-
580
- if self.after_tool_call_cb:
581
- self.after_tool_call_cb(self)
582
-
583
- if self.session.prompt or self.session.addon_prompt:
584
- continue
585
-
586
- if self.auto_complete and ot("!!!COMPLETE!!!") in current_response:
587
- return self._complete_task(auto_completed=True)
588
-
589
- # 获取用户输入
590
- user_input = self.multiline_inputer(
591
- f"{self.name}: 请输入,或输入空行来结束当前任务:"
592
- )
593
-
594
- if user_input:
595
- self.session.prompt = user_input
596
- run_input_handlers = True
597
- continue
598
-
599
- if not user_input:
600
- return self._complete_task(auto_completed=False)
601
-
602
- except Exception as e:
603
- PrettyOutput.print(f"任务失败: {str(e)}", OutputType.ERROR)
604
- return f"Task failed: {str(e)}"
605
608
 
606
- except Exception as e:
607
- PrettyOutput.print(f"任务失败: {str(e)}", OutputType.ERROR)
608
- return f"Task failed: {str(e)}"
609
-
610
- def _first_run(self):
611
- # 获取所有记忆标签并添加到提示中
612
- from jarvis.jarvis_utils.globals import get_all_memory_tags
613
-
614
- memory_tags = get_all_memory_tags()
615
- memory_tags_prompt = ""
616
-
617
- # 检查是否有save_memory工具
618
- tool_registry = self.get_tool_registry()
619
- has_save_memory = False
620
- if tool_registry:
621
- tool_names = [tool.name for tool in tool_registry.tools.values()]
622
- has_save_memory = "save_memory" in tool_names
623
-
624
- # 如果有save_memory工具,添加记录关键信息的提示
625
- if has_save_memory:
626
- memory_tags_prompt = "\n\n💡 提示:在分析任务之前,建议使用 save_memory 工具将关键信息记录下来,便于后续检索和复用。"
627
-
628
- if any(tags for tags in memory_tags.values()):
629
- memory_tags_prompt += "\n\n系统中存在以下记忆标签,你可以使用 retrieve_memory 工具检索相关记忆:"
630
- for memory_type, tags in memory_tags.items():
631
- if tags:
632
- type_name = {
633
- "short_term": "短期记忆",
634
- "project_long_term": "项目长期记忆",
635
- "global_long_term": "全局长期记忆"
636
- }.get(memory_type, memory_type)
637
- memory_tags_prompt += f"\n- {type_name}: {', '.join(tags)}"
638
-
639
- # 如果有上传文件,先上传文件
640
- if self.model and self.model.support_upload_files():
641
- if self.use_methodology:
642
- if not upload_methodology(self.model, other_files=self.files):
643
- if self.files:
644
- PrettyOutput.print(
645
- "文件上传失败,将忽略文件列表", OutputType.WARNING
646
- )
647
- # 上传失败则回退到本地加载
648
- msg = self.session.prompt
649
- for handler in self.input_handler:
650
- msg, _ = handler(msg, self)
651
- self.session.prompt = f"{self.session.prompt}\n\n以下是历史类似问题的执行经验,可参考:\n{load_methodology(msg, self.get_tool_registry())}{memory_tags_prompt}"
652
- else:
653
- if self.files:
654
- self.session.prompt = f"{self.session.prompt}\n\n上传的文件包含历史对话信息和方法论文件,可以从中获取一些经验信息。{memory_tags_prompt}"
609
+ # 调用模型获取响应
610
+ current_response = self._call_model(
611
+ self.session.prompt, True, run_input_handlers
612
+ )
613
+ self.session.prompt = ""
614
+ run_input_handlers = False
615
+
616
+ # 处理中断
617
+ interrupt_result = self._handle_run_interrupt(current_response)
618
+ if interrupt_result:
619
+ if isinstance(interrupt_result, tuple):
620
+ run_input_handlers, should_continue = interrupt_result
621
+ if should_continue:
622
+ continue
655
623
  else:
656
- self.session.prompt = f"{self.session.prompt}\n\n上传的文件包含历史对话信息,可以从中获取一些经验信息。{memory_tags_prompt}"
657
- elif self.files:
658
- if not self.model.upload_files(self.files):
659
- PrettyOutput.print(
660
- "文件上传失败,将忽略文件列表", OutputType.WARNING
661
- )
662
- else:
663
- self.session.prompt = f"{self.session.prompt}\n\n上传的文件包含历史对话信息,可以从中获取一些经验信息。"
624
+ return interrupt_result
625
+
626
+ # 处理工具调用
627
+ need_return, self.session.prompt = self._call_tools(current_response)
628
+ if need_return:
629
+ return self.session.prompt
630
+
631
+ # 执行回调
632
+ if self.after_tool_call_cb:
633
+ self.after_tool_call_cb(self)
634
+
635
+ # 检查是否需要继续
636
+ if self.session.prompt or self.session.addon_prompt:
637
+ continue
638
+
639
+ # 检查自动完成
640
+ if self.auto_complete and ot("!!!COMPLETE!!!") in current_response:
641
+ return self._complete_task(auto_completed=True)
642
+
643
+ # 获取下一步用户输入
644
+ next_action = self._get_next_user_action()
645
+ if next_action == "continue":
646
+ run_input_handlers = True
647
+ continue
648
+ elif next_action == "complete":
649
+ return self._complete_task(auto_completed=False)
650
+
651
+ except Exception as e:
652
+ PrettyOutput.print(f"任务失败: {str(e)}", OutputType.ERROR)
653
+ return f"Task failed: {str(e)}"
654
+
655
+ def _handle_run_interrupt(
656
+ self, current_response: str
657
+ ) -> Optional[Union[Any, Tuple[bool, bool]]]:
658
+ """处理运行中的中断
659
+
660
+ 返回:
661
+ None: 无中断,继续执行
662
+ Any: 需要返回的结果
663
+ Tuple[bool, bool]: (run_input_handlers, should_continue)
664
+ """
665
+ if not get_interrupt():
666
+ return None
667
+
668
+ set_interrupt(False)
669
+ user_input = self.multiline_inputer(f"模型交互期间被中断,请输入用户干预信息:")
670
+
671
+ if not user_input:
672
+ # 用户输入为空,完成任务
673
+ return self._complete_task(auto_completed=False)
674
+
675
+ if any(handler.can_handle(current_response) for handler in self.output_handler):
676
+ if user_confirm("检测到有工具调用,是否继续处理工具调用?", True):
677
+ self.session.prompt = f"被用户中断,用户补充信息为:{user_input}\n\n用户同意继续工具调用。"
678
+ return None # 继续执行工具调用
679
+ else:
680
+ self.session.prompt = f"被用户中断,用户补充信息为:{user_input}\n\n检测到有工具调用,但被用户拒绝执行。请根据用户的补充信息重新考虑下一步操作。"
681
+ return (True, True) # run_input_handlers=True, should_continue=True
664
682
  else:
665
- if self.files:
666
- PrettyOutput.print("不支持上传文件,将忽略文件列表", OutputType.WARNING)
667
- if self.use_methodology:
668
- msg = self.session.prompt
669
- for handler in self.input_handler:
670
- msg, _ = handler(msg, self)
671
- self.session.prompt = f"{self.session.prompt}\n\n以下是历史类似问题的执行经验,可参考:\n{load_methodology(msg, self.get_tool_registry())}"
672
-
683
+ self.session.prompt = f"被用户中断,用户补充信息为:{user_input}"
684
+ return (True, True) # run_input_handlers=True, should_continue=True
685
+
686
+ def _get_next_user_action(self) -> str:
687
+ """获取用户下一步操作
688
+
689
+ 返回:
690
+ str: "continue" 或 "complete"
691
+ """
692
+ user_input = self.multiline_inputer(f"{self.name}: 请输入,或输入空行来结束当前任务:")
693
+
694
+ if user_input:
695
+ self.session.prompt = user_input
696
+ return "continue"
697
+ else:
698
+ return "complete"
699
+
700
+ def _first_run(self):
701
+ """首次运行初始化"""
702
+ # 准备记忆标签提示
703
+ memory_tags_prompt = self.memory_manager.prepare_memory_tags_prompt()
704
+
705
+ # 处理文件上传和方法论加载
706
+ self.file_methodology_manager.handle_files_and_methodology()
707
+
673
708
  # 添加记忆标签提示
674
709
  if memory_tags_prompt:
675
710
  self.session.prompt = f"{self.session.prompt}{memory_tags_prompt}"