jarvis-ai-assistant 0.2.8__py3-none-any.whl → 0.3.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.
Files changed (38) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +267 -240
  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 +133 -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 +32 -0
  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_tools/registry.py +32 -0
  25. jarvis/jarvis_tools/retrieve_memory.py +36 -8
  26. jarvis/jarvis_utils/clipboard.py +90 -0
  27. jarvis/jarvis_utils/config.py +54 -0
  28. jarvis/jarvis_utils/git_utils.py +17 -7
  29. jarvis/jarvis_utils/globals.py +18 -12
  30. jarvis/jarvis_utils/input.py +118 -16
  31. jarvis/jarvis_utils/methodology.py +48 -5
  32. jarvis/jarvis_utils/utils.py +169 -105
  33. {jarvis_ai_assistant-0.2.8.dist-info → jarvis_ai_assistant-0.3.0.dist-info}/METADATA +1 -1
  34. {jarvis_ai_assistant-0.2.8.dist-info → jarvis_ai_assistant-0.3.0.dist-info}/RECORD +38 -28
  35. {jarvis_ai_assistant-0.2.8.dist-info → jarvis_ai_assistant-0.3.0.dist-info}/WHEEL +0 -0
  36. {jarvis_ai_assistant-0.2.8.dist-info → jarvis_ai_assistant-0.3.0.dist-info}/entry_points.txt +0 -0
  37. {jarvis_ai_assistant-0.2.8.dist-info → jarvis_ai_assistant-0.3.0.dist-info}/licenses/LICENSE +0 -0
  38. {jarvis_ai_assistant-0.2.8.dist-info → jarvis_ai_assistant-0.3.0.dist-info}/top_level.txt +0 -0
jarvis/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """Jarvis AI Assistant"""
3
3
 
4
- __version__ = "0.2.8"
4
+ __version__ = "0.3.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,
@@ -43,7 +46,6 @@ from jarvis.jarvis_utils.globals import (
43
46
  set_interrupt,
44
47
  )
45
48
  from jarvis.jarvis_utils.input import get_multiline_input, user_confirm
46
- from jarvis.jarvis_utils.methodology import load_methodology, upload_methodology
47
49
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
48
50
  from jarvis.jarvis_utils.tag import ct, ot
49
51
 
@@ -115,7 +117,6 @@ class Agent:
115
117
  use_analysis: Optional[bool] = None,
116
118
  files: List[str] = [],
117
119
  ):
118
- self.files = files
119
120
  """初始化Jarvis Agent实例
120
121
 
121
122
  参数:
@@ -134,9 +135,48 @@ class Agent:
134
135
  use_methodology: 是否使用方法论
135
136
  use_analysis: 是否使用任务分析
136
137
  """
138
+ # 基础属性初始化
139
+ self.files = files
137
140
  self.name = make_agent_name(name)
138
141
  self.description = description
139
- # 初始化平台和模型
142
+ self.system_prompt = system_prompt
143
+ self.need_summary = need_summary
144
+ self.auto_complete = auto_complete
145
+ self.first = True
146
+ self.run_input_handlers_next_turn = False
147
+ self.user_data: Dict[str, Any] = {}
148
+ self.after_tool_call_cb: Optional[Callable[[Agent], None]] = None
149
+
150
+ # 初始化模型和会话
151
+ self._init_model(llm_type, model_group)
152
+ self._init_session()
153
+
154
+ # 初始化处理器
155
+ self._init_handlers(output_handler, input_handler, multiline_inputer, use_tools)
156
+
157
+ # 初始化配置
158
+ self._init_config(
159
+ use_methodology,
160
+ use_analysis,
161
+ execute_tool_confirm,
162
+ summary_prompt,
163
+ model_group,
164
+ )
165
+
166
+ # 初始化管理器
167
+ self.memory_manager = MemoryManager(self)
168
+ self.task_analyzer = TaskAnalyzer(self)
169
+ self.file_methodology_manager = FileMethodologyManager(self)
170
+
171
+ # 设置系统提示词
172
+ self._setup_system_prompt()
173
+
174
+ # 打印欢迎信息
175
+ welcome_message = f"{name} 初始化完成 - 使用 {self.model.name()} 模型" # type: ignore
176
+ PrettyOutput.print(welcome_message, OutputType.SYSTEM)
177
+
178
+ def _init_model(self, llm_type: str, model_group: Optional[str]):
179
+ """初始化模型平台"""
140
180
  if llm_type == "thinking":
141
181
  platform_name = get_thinking_platform_name(model_group)
142
182
  model_name = get_thinking_model_name(model_group)
@@ -155,40 +195,52 @@ class Agent:
155
195
  self.model.set_model_name(model_name)
156
196
 
157
197
  self.model.set_model_group(model_group)
158
-
159
- self.user_data: Dict[str, Any] = {}
160
-
161
198
  self.model.set_suppress_output(False)
162
199
 
163
- # Initialize the session manager
164
- self.session = SessionManager(model=self.model, agent_name=self.name)
200
+ def _init_session(self):
201
+ """初始化会话管理器"""
202
+ self.session = SessionManager(model=self.model, agent_name=self.name) # type: ignore
165
203
 
204
+ def _init_handlers(
205
+ self,
206
+ output_handler: List[OutputHandlerProtocol],
207
+ input_handler: Optional[List[Callable[[str, Any], Tuple[str, bool]]]],
208
+ multiline_inputer: Optional[Callable[[str], str]],
209
+ use_tools: List[str],
210
+ ):
211
+ """初始化各种处理器"""
166
212
  from jarvis.jarvis_tools.registry import ToolRegistry
167
213
 
168
214
  self.output_handler = output_handler if output_handler else [ToolRegistry()]
169
215
  self.set_use_tools(use_tools)
170
216
 
217
+ self.input_handler = input_handler if input_handler is not None else []
218
+
171
219
  self.multiline_inputer = (
172
220
  multiline_inputer if multiline_inputer else get_multiline_input
173
221
  )
174
222
 
223
+ def _init_config(
224
+ self,
225
+ use_methodology: Optional[bool],
226
+ use_analysis: Optional[bool],
227
+ execute_tool_confirm: Optional[bool],
228
+ summary_prompt: Optional[str],
229
+ model_group: Optional[str],
230
+ ):
231
+ """初始化配置选项"""
175
232
  # 如果有上传文件,自动禁用方法论
176
233
  self.use_methodology = (
177
234
  False
178
- if files
235
+ if self.files
179
236
  else (
180
237
  use_methodology if use_methodology is not None else is_use_methodology()
181
238
  )
182
239
  )
240
+
183
241
  self.use_analysis = (
184
242
  use_analysis if use_analysis is not None else is_use_analysis()
185
243
  )
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
244
 
193
245
  self.execute_tool_confirm = (
194
246
  execute_tool_confirm
@@ -201,22 +253,17 @@ class Agent:
201
253
  )
202
254
 
203
255
  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
-
207
- PrettyOutput.print(welcome_message, OutputType.SYSTEM)
208
256
 
257
+ def _setup_system_prompt(self):
258
+ """设置系统提示词"""
209
259
  action_prompt = self.get_tool_usage_prompt()
210
-
211
- self.model.set_system_prompt(
260
+ self.model.set_system_prompt( # type: ignore
212
261
  f"""
213
262
  {self.system_prompt}
214
263
 
215
264
  {action_prompt}
216
265
  """
217
266
  )
218
- self.first = True
219
- self.run_input_handlers_next_turn = False
220
267
 
221
268
  def set_user_data(self, key: str, value: Any):
222
269
  """Sets user data in the session."""
@@ -280,6 +327,7 @@ class Agent:
280
327
  参数:
281
328
  message: 输入给模型的消息
282
329
  need_complete: 是否需要完成任务标记
330
+ run_input_handlers: 是否运行输入处理器
283
331
 
284
332
  返回:
285
333
  str: 模型的响应
@@ -290,27 +338,63 @@ class Agent:
290
338
  3. 会自动添加附加提示
291
339
  4. 会检查并处理上下文长度限制
292
340
  """
341
+ # 处理输入
342
+ message = self._process_input(message, run_input_handlers)
343
+ if run_input_handlers and self._should_return_early(message):
344
+ return message
345
+
346
+ # 添加附加提示
347
+ message = self._add_addon_prompt(message, need_complete)
348
+
349
+ # 管理对话长度
350
+ message = self._manage_conversation_length(message)
351
+
352
+ # 调用模型
353
+ response = self._invoke_model(message)
354
+
355
+ return response
356
+
357
+ def _process_input(self, message: str, run_input_handlers: bool) -> str:
358
+ """处理输入消息"""
293
359
  if run_input_handlers:
294
360
  for handler in self.input_handler:
295
361
  message, need_return = handler(message, self)
296
362
  if need_return:
363
+ self._last_handler_returned = True
297
364
  return message
365
+ self._last_handler_returned = False
366
+ return message
367
+
368
+ def _should_return_early(self, message: str) -> bool:
369
+ """检查是否需要提前返回"""
370
+ return hasattr(self, "_last_handler_returned") and self._last_handler_returned
298
371
 
372
+ def _add_addon_prompt(self, message: str, need_complete: bool) -> str:
373
+ """添加附加提示到消息"""
299
374
  if self.session.addon_prompt:
300
375
  message += f"\n\n{self.session.addon_prompt}"
301
376
  self.session.addon_prompt = ""
302
377
  else:
303
378
  message += f"\n\n{self.make_default_addon_prompt(need_complete)}"
379
+ return message
304
380
 
305
- # 累加对话长度
381
+ def _manage_conversation_length(self, message: str) -> str:
382
+ """管理对话长度,必要时进行摘要"""
306
383
  self.session.conversation_length += get_context_token_count(message)
307
384
 
308
385
  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)
386
+ summary = self._summarize_and_clear_history()
387
+ if summary:
388
+ message = summary + "\n\n" + message
389
+ self.session.conversation_length = get_context_token_count(message)
311
390
 
391
+ return message
392
+
393
+ def _invoke_model(self, message: str) -> str:
394
+ """实际调用模型获取响应"""
312
395
  if not self.model:
313
396
  raise RuntimeError("Model not initialized")
397
+
314
398
  response = self.model.chat_until_success(message) # type: ignore
315
399
  self.session.conversation_length += get_context_token_count(response)
316
400
 
@@ -342,11 +426,12 @@ class Agent:
342
426
  """总结当前对话并清理历史记录
343
427
 
344
428
  该方法将:
345
- 1. 调用_generate_summary生成摘要
346
- 2. 清除对话历史
347
- 3. 保留系统消息
348
- 4. 添加摘要作为新上下文
349
- 5. 重置对话长度计数器
429
+ 1. 提示用户保存重要记忆
430
+ 2. 调用_generate_summary生成摘要
431
+ 3. 清除对话历史
432
+ 4. 保留系统消息
433
+ 5. 添加摘要作为新上下文
434
+ 6. 重置对话长度计数器
350
435
 
351
436
  返回:
352
437
  str: 包含对话摘要的字符串
@@ -354,25 +439,36 @@ class Agent:
354
439
  注意:
355
440
  当上下文长度超过最大值时使用
356
441
  """
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
442
+ # 在清理历史之前,提示用户保存重要记忆
443
+ print("📌 对话历史即将被总结和清理,请先保存重要信息...")
444
+ self.memory_manager.prompt_memory_save()
445
+
446
+ if self._should_use_file_upload():
447
+ return self._handle_history_with_file_upload()
448
+ else:
449
+ return self._handle_history_with_summary()
450
+
451
+ def _should_use_file_upload(self) -> bool:
452
+ """判断是否应该使用文件上传方式处理历史"""
453
+ return bool(self.model and self.model.support_upload_files())
366
454
 
367
- tmp_file = tempfile.NamedTemporaryFile(delete=False)
368
- tmp_file_name = tmp_file.name
369
- self.clear_history() # type: ignore
455
+ def _handle_history_with_summary(self) -> str:
456
+ """使用摘要方式处理历史"""
457
+ summary = self.generate_summary()
458
+ self.clear_history()
459
+
460
+ if not summary:
461
+ return ""
370
462
 
371
- if need_summary:
372
- if not summary:
373
- return ""
463
+ return self._format_summary_message(summary)
374
464
 
375
- return f"""
465
+ def _handle_history_with_file_upload(self) -> str:
466
+ """使用文件上传方式处理历史"""
467
+ return self.file_methodology_manager.handle_history_with_file_upload()
468
+
469
+ def _format_summary_message(self, summary: str) -> str:
470
+ """格式化摘要消息"""
471
+ return f"""
376
472
  以下是之前对话的关键信息总结:
377
473
 
378
474
  <content>
@@ -381,14 +477,6 @@ class Agent:
381
477
 
382
478
  请基于以上信息继续完成任务。请注意,这是之前对话的摘要,上下文长度已超过限制而被重置。请直接继续任务,无需重复已完成的步骤。如有需要,可以询问用户以获取更多信息。
383
479
  """
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
480
 
393
481
  def _call_tools(self, response: str) -> Tuple[bool, Any]:
394
482
  """
@@ -407,27 +495,19 @@ class Agent:
407
495
  2. 对于子Agent: 可能会生成总结(如果启用)
408
496
  3. 使用spinner显示生成状态
409
497
  """
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
- )
498
+ # 收集满意度反馈
499
+ satisfaction_feedback = self.task_analyzer.collect_satisfaction_feedback(
500
+ auto_completed
501
+ )
427
502
 
428
503
  if self.use_analysis:
429
- self._analysis_task(satisfaction_feedback)
504
+ self.task_analyzer.analysis_task(satisfaction_feedback)
505
+ else:
506
+ # 如果没有开启分析,也提示用户是否有值得记忆的信息
507
+ self.memory_manager.prompt_memory_save()
508
+
430
509
  if self.need_summary:
510
+
431
511
  print("📄 正在生成总结...")
432
512
  self.session.prompt = self.summary_prompt
433
513
  if not self.model:
@@ -438,23 +518,6 @@ class Agent:
438
518
 
439
519
  return "任务完成"
440
520
 
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
521
  def make_default_addon_prompt(self, need_complete: bool) -> str:
459
522
  """生成附加提示。
460
523
 
@@ -473,21 +536,10 @@ class Agent:
473
536
  )
474
537
 
475
538
  # 检查工具列表并添加记忆工具相关提示
476
- memory_prompts = ""
477
539
  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工具"
540
+ memory_prompts = self.memory_manager.add_memory_prompts_to_addon(
541
+ "", tool_registry
542
+ )
491
543
 
492
544
  addon_prompt = f"""
493
545
  <system_prompt>
@@ -521,155 +573,130 @@ class Agent:
521
573
  3. 包含错误处理和恢复逻辑
522
574
  4. 自动加载相关方法论(如果是首次运行)
523
575
  """
524
-
525
576
  self.session.prompt = f"{user_input}"
526
577
  try:
527
578
  set_agent(self.name, self)
579
+ return self._main_loop()
580
+ except Exception as e:
581
+ PrettyOutput.print(f"任务失败: {str(e)}", OutputType.ERROR)
582
+ return f"Task failed: {str(e)}"
528
583
 
529
- run_input_handlers = True
530
- while True:
584
+ def _main_loop(self) -> Any:
585
+ """主运行循环"""
586
+ run_input_handlers = True
587
+
588
+ while True:
589
+ try:
590
+ # 更新输入处理器标志
531
591
  if self.run_input_handlers_next_turn:
532
592
  run_input_handlers = True
533
593
  self.run_input_handlers_next_turn = False
534
594
 
595
+ # 首次运行初始化
535
596
  if self.first:
536
597
  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
-
606
- except Exception as e:
607
- PrettyOutput.print(f"任务失败: {str(e)}", OutputType.ERROR)
608
- return f"Task failed: {str(e)}"
609
598
 
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}"
599
+ # 调用模型获取响应
600
+ current_response = self._call_model(
601
+ self.session.prompt, True, run_input_handlers
602
+ )
603
+ self.session.prompt = ""
604
+ run_input_handlers = False
605
+
606
+ # 处理中断
607
+ interrupt_result = self._handle_run_interrupt(current_response)
608
+ if interrupt_result:
609
+ if isinstance(interrupt_result, tuple):
610
+ run_input_handlers, should_continue = interrupt_result
611
+ if should_continue:
612
+ continue
655
613
  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上传的文件包含历史对话信息,可以从中获取一些经验信息。"
614
+ return interrupt_result
615
+
616
+ # 处理工具调用
617
+ need_return, self.session.prompt = self._call_tools(current_response)
618
+ if need_return:
619
+ return self.session.prompt
620
+
621
+ # 执行回调
622
+ if self.after_tool_call_cb:
623
+ self.after_tool_call_cb(self)
624
+
625
+ # 检查是否需要继续
626
+ if self.session.prompt or self.session.addon_prompt:
627
+ continue
628
+
629
+ # 检查自动完成
630
+ if self.auto_complete and ot("!!!COMPLETE!!!") in current_response:
631
+ return self._complete_task(auto_completed=True)
632
+
633
+ # 获取下一步用户输入
634
+ next_action = self._get_next_user_action()
635
+ if next_action == "continue":
636
+ run_input_handlers = True
637
+ continue
638
+ elif next_action == "complete":
639
+ return self._complete_task(auto_completed=False)
640
+
641
+ except Exception as e:
642
+ PrettyOutput.print(f"任务失败: {str(e)}", OutputType.ERROR)
643
+ return f"Task failed: {str(e)}"
644
+
645
+ def _handle_run_interrupt(
646
+ self, current_response: str
647
+ ) -> Optional[Union[Any, Tuple[bool, bool]]]:
648
+ """处理运行中的中断
649
+
650
+ 返回:
651
+ None: 无中断,继续执行
652
+ Any: 需要返回的结果
653
+ Tuple[bool, bool]: (run_input_handlers, should_continue)
654
+ """
655
+ if not get_interrupt():
656
+ return None
657
+
658
+ set_interrupt(False)
659
+ user_input = self.multiline_inputer(f"模型交互期间被中断,请输入用户干预信息:")
660
+
661
+ if not user_input:
662
+ # 用户输入为空,完成任务
663
+ return self._complete_task(auto_completed=False)
664
+
665
+ if any(handler.can_handle(current_response) for handler in self.output_handler):
666
+ if user_confirm("检测到有工具调用,是否继续处理工具调用?", True):
667
+ self.session.prompt = f"被用户中断,用户补充信息为:{user_input}\n\n用户同意继续工具调用。"
668
+ return None # 继续执行工具调用
669
+ else:
670
+ self.session.prompt = f"被用户中断,用户补充信息为:{user_input}\n\n检测到有工具调用,但被用户拒绝执行。请根据用户的补充信息重新考虑下一步操作。"
671
+ return (True, True) # run_input_handlers=True, should_continue=True
664
672
  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
-
673
+ self.session.prompt = f"被用户中断,用户补充信息为:{user_input}"
674
+ return (True, True) # run_input_handlers=True, should_continue=True
675
+
676
+ def _get_next_user_action(self) -> str:
677
+ """获取用户下一步操作
678
+
679
+ 返回:
680
+ str: "continue" 或 "complete"
681
+ """
682
+ user_input = self.multiline_inputer(
683
+ f"{self.name}: 请输入,或输入空行来结束当前任务:"
684
+ )
685
+
686
+ if user_input:
687
+ self.session.prompt = user_input
688
+ return "continue"
689
+ else:
690
+ return "complete"
691
+
692
+ def _first_run(self):
693
+ """首次运行初始化"""
694
+ # 准备记忆标签提示
695
+ memory_tags_prompt = self.memory_manager.prepare_memory_tags_prompt()
696
+
697
+ # 处理文件上传和方法论加载
698
+ self.file_methodology_manager.handle_files_and_methodology()
699
+
673
700
  # 添加记忆标签提示
674
701
  if memory_tags_prompt:
675
702
  self.session.prompt = f"{self.session.prompt}{memory_tags_prompt}"