jarvis-ai-assistant 0.3.28__py3-none-any.whl → 0.3.29__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 (36) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +60 -25
  3. jarvis/jarvis_agent/event_bus.py +2 -2
  4. jarvis/jarvis_agent/events.py +157 -0
  5. jarvis/jarvis_agent/file_methodology_manager.py +17 -4
  6. jarvis/jarvis_agent/jarvis.py +3 -3
  7. jarvis/jarvis_agent/memory_manager.py +4 -3
  8. jarvis/jarvis_agent/prompts.py +2 -2
  9. jarvis/jarvis_agent/protocols.py +4 -1
  10. jarvis/jarvis_agent/run_loop.py +9 -24
  11. jarvis/jarvis_agent/shell_input_handler.py +6 -1
  12. jarvis/jarvis_agent/task_analyzer.py +18 -13
  13. jarvis/jarvis_agent/task_manager.py +6 -4
  14. jarvis/jarvis_agent/utils.py +50 -0
  15. jarvis/jarvis_mcp/sse_mcp_client.py +1 -1
  16. jarvis/jarvis_memory_organizer/memory_organizer.py +4 -4
  17. jarvis/jarvis_platform/kimi.py +1 -1
  18. jarvis/jarvis_platform/tongyi.py +1 -1
  19. jarvis/jarvis_platform/yuanbao.py +1 -1
  20. jarvis/jarvis_rag/retriever.py +3 -3
  21. jarvis/jarvis_stats/stats.py +2 -2
  22. jarvis/jarvis_stats/storage.py +3 -3
  23. jarvis/jarvis_tools/edit_file.py +3 -3
  24. jarvis/jarvis_tools/execute_script.py +2 -2
  25. jarvis/jarvis_tools/sub_agent.py +2 -2
  26. jarvis/jarvis_tools/sub_code_agent.py +2 -2
  27. jarvis/jarvis_utils/config.py +3 -3
  28. jarvis/jarvis_utils/fzf.py +4 -3
  29. jarvis/jarvis_utils/input.py +5 -5
  30. jarvis/jarvis_utils/utils.py +96 -9
  31. {jarvis_ai_assistant-0.3.28.dist-info → jarvis_ai_assistant-0.3.29.dist-info}/METADATA +1 -1
  32. {jarvis_ai_assistant-0.3.28.dist-info → jarvis_ai_assistant-0.3.29.dist-info}/RECORD +36 -34
  33. {jarvis_ai_assistant-0.3.28.dist-info → jarvis_ai_assistant-0.3.29.dist-info}/WHEEL +0 -0
  34. {jarvis_ai_assistant-0.3.28.dist-info → jarvis_ai_assistant-0.3.29.dist-info}/entry_points.txt +0 -0
  35. {jarvis_ai_assistant-0.3.28.dist-info → jarvis_ai_assistant-0.3.29.dist-info}/licenses/LICENSE +0 -0
  36. {jarvis_ai_assistant-0.3.28.dist-info → jarvis_ai_assistant-0.3.29.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.3.28"
4
+ __version__ = "0.3.29"
@@ -34,7 +34,23 @@ from jarvis.jarvis_agent.prompt_manager import PromptManager
34
34
  from jarvis.jarvis_agent.event_bus import EventBus
35
35
  from jarvis.jarvis_agent.config import AgentConfig
36
36
  from jarvis.jarvis_agent.run_loop import AgentRunLoop
37
+ from jarvis.jarvis_agent.events import (
38
+ BEFORE_SUMMARY,
39
+ AFTER_SUMMARY,
40
+ TASK_COMPLETED,
41
+ TASK_STARTED,
42
+ BEFORE_ADDON_PROMPT,
43
+ AFTER_ADDON_PROMPT,
44
+ BEFORE_HISTORY_CLEAR,
45
+ AFTER_HISTORY_CLEAR,
46
+ BEFORE_MODEL_CALL,
47
+ AFTER_MODEL_CALL,
48
+ INTERRUPT_TRIGGERED,
49
+ BEFORE_TOOL_FILTER,
50
+ TOOL_FILTERED,
51
+ )
37
52
  from jarvis.jarvis_agent.user_interaction import UserInteractionHandler
53
+ from jarvis.jarvis_agent.utils import join_prompts
38
54
  from jarvis.jarvis_utils.methodology import _load_all_methodologies
39
55
 
40
56
  # jarvis_platform 相关
@@ -212,6 +228,15 @@ class LoopAction(Enum):
212
228
 
213
229
 
214
230
  class Agent:
231
+ # Attribute type annotations to satisfy static type checkers
232
+ event_bus: EventBus
233
+ memory_manager: MemoryManager
234
+ task_analyzer: TaskAnalyzer
235
+ file_methodology_manager: FileMethodologyManager
236
+ prompt_manager: PromptManager
237
+ model: BasePlatform
238
+ session: SessionManager
239
+
215
240
  def clear_history(self):
216
241
  """
217
242
  Clears the current conversation history by delegating to the session manager.
@@ -219,7 +244,7 @@ class Agent:
219
244
  self.session.clear_history()
220
245
  # 广播清理历史后的事件
221
246
  try:
222
- self.event_bus.emit("after_history_clear", agent=self)
247
+ self.event_bus.emit(AFTER_HISTORY_CLEAR, agent=self)
223
248
  except Exception:
224
249
  pass
225
250
 
@@ -342,12 +367,15 @@ class Agent:
342
367
  platform_name = get_normal_platform_name(model_group)
343
368
  model_name = get_normal_model_name(model_group)
344
369
 
345
- self.model = PlatformRegistry().create_platform(platform_name)
346
- if self.model is None:
370
+ maybe_model = PlatformRegistry().create_platform(platform_name)
371
+ if maybe_model is None:
347
372
  PrettyOutput.print(
348
373
  f"平台 {platform_name} 不存在,将使用普通模型", OutputType.WARNING
349
374
  )
350
- self.model = PlatformRegistry().get_normal_platform()
375
+ maybe_model = PlatformRegistry().get_normal_platform()
376
+
377
+ # 在此处收敛为非可选类型,确保后续赋值满足类型检查
378
+ self.model = maybe_model
351
379
 
352
380
  if model_name:
353
381
  self.model.set_model_name(model_name)
@@ -555,7 +583,7 @@ class Agent:
555
583
  # 广播添加附加提示前事件(不影响主流程)
556
584
  try:
557
585
  self.event_bus.emit(
558
- "before_addon_prompt",
586
+ BEFORE_ADDON_PROMPT,
559
587
  agent=self,
560
588
  need_complete=need_complete,
561
589
  current_message=message,
@@ -567,16 +595,16 @@ class Agent:
567
595
  addon_text = ""
568
596
  if self.session.addon_prompt:
569
597
  addon_text = self.session.addon_prompt
570
- message += f"\n\n{addon_text}"
598
+ message = join_prompts([message, addon_text])
571
599
  self.session.addon_prompt = ""
572
600
  else:
573
601
  addon_text = self.make_default_addon_prompt(need_complete)
574
- message += f"\n\n{addon_text}"
602
+ message = join_prompts([message, addon_text])
575
603
 
576
604
  # 广播添加附加提示后事件(不影响主流程)
577
605
  try:
578
606
  self.event_bus.emit(
579
- "after_addon_prompt",
607
+ AFTER_ADDON_PROMPT,
580
608
  agent=self,
581
609
  need_complete=need_complete,
582
610
  addon_text=addon_text,
@@ -593,7 +621,7 @@ class Agent:
593
621
  if self.session.conversation_length > self.max_token_count:
594
622
  summary = self._summarize_and_clear_history()
595
623
  if summary:
596
- message = summary + "\n\n" + message
624
+ message = join_prompts([summary, message])
597
625
  self.session.conversation_length = get_context_token_count(message)
598
626
 
599
627
  return message
@@ -606,7 +634,7 @@ class Agent:
606
634
  # 事件:模型调用前
607
635
  try:
608
636
  self.event_bus.emit(
609
- "before_model_call",
637
+ BEFORE_MODEL_CALL,
610
638
  agent=self,
611
639
  message=message,
612
640
  )
@@ -618,7 +646,7 @@ class Agent:
618
646
  # 事件:模型调用后
619
647
  try:
620
648
  self.event_bus.emit(
621
- "after_model_call",
649
+ AFTER_MODEL_CALL,
622
650
  agent=self,
623
651
  message=message,
624
652
  response=response,
@@ -697,7 +725,7 @@ class Agent:
697
725
  if self.model:
698
726
  # 广播清理历史前事件
699
727
  try:
700
- self.event_bus.emit("before_history_clear", agent=self)
728
+ self.event_bus.emit(BEFORE_HISTORY_CLEAR, agent=self)
701
729
  except Exception:
702
730
  pass
703
731
  self.model.reset()
@@ -707,7 +735,7 @@ class Agent:
707
735
  self.session.clear_history()
708
736
  # 广播清理历史后的事件
709
737
  try:
710
- self.event_bus.emit("after_history_clear", agent=self)
738
+ self.event_bus.emit(AFTER_HISTORY_CLEAR, agent=self)
711
739
  except Exception:
712
740
  pass
713
741
 
@@ -717,13 +745,13 @@ class Agent:
717
745
  """使用文件上传方式处理历史"""
718
746
  # 广播清理历史前事件
719
747
  try:
720
- self.event_bus.emit("before_history_clear", agent=self)
748
+ self.event_bus.emit(BEFORE_HISTORY_CLEAR, agent=self)
721
749
  except Exception:
722
750
  pass
723
751
  result = self.file_methodology_manager.handle_history_with_file_upload()
724
752
  # 广播清理历史后的事件
725
753
  try:
726
- self.event_bus.emit("after_history_clear", agent=self)
754
+ self.event_bus.emit(AFTER_HISTORY_CLEAR, agent=self)
727
755
  except Exception:
728
756
  pass
729
757
  return result
@@ -771,7 +799,7 @@ class Agent:
771
799
  # 广播将要生成总结事件
772
800
  try:
773
801
  self.event_bus.emit(
774
- "before_summary",
802
+ BEFORE_SUMMARY,
775
803
  agent=self,
776
804
  prompt=self.session.prompt,
777
805
  auto_completed=auto_completed,
@@ -788,7 +816,7 @@ class Agent:
788
816
  # 广播完成总结事件
789
817
  try:
790
818
  self.event_bus.emit(
791
- "after_summary",
819
+ AFTER_SUMMARY,
792
820
  agent=self,
793
821
  summary=result,
794
822
  )
@@ -798,7 +826,7 @@ class Agent:
798
826
  # 广播任务完成事件(不影响主流程)
799
827
  try:
800
828
  self.event_bus.emit(
801
- "task_completed",
829
+ TASK_COMPLETED,
802
830
  agent=self,
803
831
  auto_completed=auto_completed,
804
832
  need_summary=self.need_summary,
@@ -842,6 +870,7 @@ class Agent:
842
870
  {complete_prompt}
843
871
  如果没有完成,请进行下一步操作:
844
872
  - 仅包含一个操作
873
+ - 不要询问用户是否继续,直接继续执行直至完成
845
874
  - 如果信息不明确,请请求用户补充
846
875
  - 如果执行过程中连续失败5次,请使用ask_user询问用户操作
847
876
  - 操作列表:{action_handlers}{memory_prompts}
@@ -873,7 +902,7 @@ class Agent:
873
902
  # 广播任务开始事件(不影响主流程)
874
903
  try:
875
904
  self.event_bus.emit(
876
- "task_started",
905
+ TASK_STARTED,
877
906
  agent=self,
878
907
  name=self.name,
879
908
  description=self.description,
@@ -910,7 +939,7 @@ class Agent:
910
939
  # 广播中断事件(包含用户输入,可能为空字符串)
911
940
  try:
912
941
  self.event_bus.emit(
913
- "interrupt_triggered",
942
+ INTERRUPT_TRIGGERED,
914
943
  agent=self,
915
944
  current_response=current_response,
916
945
  user_input=user_input,
@@ -926,10 +955,16 @@ class Agent:
926
955
 
927
956
  if any(handler.can_handle(current_response) for handler in self.output_handler):
928
957
  if self.user_confirm("检测到有工具调用,是否继续处理工具调用?", True):
929
- self.session.prompt = f"被用户中断,用户补充信息为:{user_input}\n\n用户同意继续工具调用。"
958
+ self.session.prompt = join_prompts([
959
+ f"被用户中断,用户补充信息为:{user_input}",
960
+ "用户同意继续工具调用。"
961
+ ])
930
962
  return None # 继续执行工具调用
931
963
  else:
932
- self.session.prompt = f"被用户中断,用户补充信息为:{user_input}\n\n检测到有工具调用,但被用户拒绝执行。请根据用户的补充信息重新考虑下一步操作。"
964
+ self.session.prompt = join_prompts([
965
+ f"被用户中断,用户补充信息为:{user_input}",
966
+ "检测到有工具调用,但被用户拒绝执行。请根据用户的补充信息重新考虑下一步操作。"
967
+ ])
933
968
  return LoopAction.SKIP_TURN # 请求主循环 continue
934
969
  else:
935
970
  self.session.prompt = f"被用户中断,用户补充信息为:{user_input}"
@@ -1020,7 +1055,7 @@ class Agent:
1020
1055
  # 广播工具筛选开始事件
1021
1056
  try:
1022
1057
  self.event_bus.emit(
1023
- "before_tool_filter",
1058
+ BEFORE_TOOL_FILTER,
1024
1059
  agent=self,
1025
1060
  task=task,
1026
1061
  total_tools=len(all_tools),
@@ -1059,7 +1094,7 @@ class Agent:
1059
1094
  # 广播工具筛选事件
1060
1095
  try:
1061
1096
  self.event_bus.emit(
1062
- "tool_filtered",
1097
+ TOOL_FILTERED,
1063
1098
  agent=self,
1064
1099
  task=task,
1065
1100
  selected_tools=selected_tool_names,
@@ -1075,7 +1110,7 @@ class Agent:
1075
1110
  # 广播工具筛选事件(无筛选结果)
1076
1111
  try:
1077
1112
  self.event_bus.emit(
1078
- "tool_filtered",
1113
+ TOOL_FILTERED,
1079
1114
  agent=self,
1080
1115
  task=task,
1081
1116
  selected_tools=[],
@@ -8,7 +8,7 @@
8
8
  - 不引入额外依赖,便于在 Agent 中渐进集成
9
9
  """
10
10
  from collections import defaultdict
11
- from typing import Callable, DefaultDict, Dict, List
11
+ from typing import Callable, DefaultDict, Dict, List, Any
12
12
 
13
13
 
14
14
 
@@ -36,7 +36,7 @@ class EventBus:
36
36
  except ValueError:
37
37
  pass
38
38
 
39
- def emit(self, event: str, **payload: Dict) -> None:
39
+ def emit(self, event: str, **payload: Any) -> None:
40
40
  """
41
41
  广播事件。回调中的异常将被捕获并忽略,以保证主流程稳定。
42
42
  """
@@ -0,0 +1,157 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ 事件主题与负载类型定义(jarvis_agent.events)
4
+
5
+ 目的:
6
+ - 统一事件名称,避免在代码各处硬编码字符串导致的漂移
7
+ - 提供事件负载的类型提示,便于静态检查与后续文档化
8
+ - 本文件仅提供常量与类型定义,不改变现有行为
9
+ """
10
+ from typing import Any, TypedDict, List
11
+
12
+ # 事件主题常量
13
+ BEFORE_TOOL_CALL = "before_tool_call"
14
+ AFTER_TOOL_CALL = "after_tool_call"
15
+
16
+ # 会话与任务生命周期相关
17
+ TASK_STARTED = "task_started"
18
+ TASK_COMPLETED = "task_completed"
19
+
20
+ # 总结阶段
21
+ BEFORE_SUMMARY = "before_summary"
22
+ AFTER_SUMMARY = "after_summary"
23
+
24
+ # 附加提示
25
+ BEFORE_ADDON_PROMPT = "before_addon_prompt"
26
+ AFTER_ADDON_PROMPT = "after_addon_prompt"
27
+
28
+ # 历史清理
29
+ BEFORE_HISTORY_CLEAR = "before_history_clear"
30
+ AFTER_HISTORY_CLEAR = "after_history_clear"
31
+
32
+ # 模型调用
33
+ BEFORE_MODEL_CALL = "before_model_call"
34
+ AFTER_MODEL_CALL = "after_model_call"
35
+
36
+ # 其他
37
+ INTERRUPT_TRIGGERED = "interrupt_triggered"
38
+ BEFORE_TOOL_FILTER = "before_tool_filter"
39
+ TOOL_FILTERED = "tool_filtered"
40
+
41
+
42
+ # 事件负载类型(仅用于类型提示)
43
+ class BeforeToolCallEvent(TypedDict, total=False):
44
+ agent: Any
45
+ current_response: str
46
+
47
+ class AfterToolCallEvent(TypedDict, total=False):
48
+ agent: Any
49
+ current_response: str
50
+ need_return: bool
51
+ tool_prompt: str
52
+
53
+ # 任务生命周期
54
+ class TaskStartedEvent(TypedDict, total=False):
55
+ agent: Any
56
+ name: str
57
+ description: str
58
+ user_input: str
59
+
60
+ class TaskCompletedEvent(TypedDict, total=False):
61
+ agent: Any
62
+ auto_completed: bool
63
+ need_summary: bool
64
+
65
+ # 总结阶段
66
+ class BeforeSummaryEvent(TypedDict, total=False):
67
+ agent: Any
68
+ prompt: str
69
+ auto_completed: bool
70
+ need_summary: bool
71
+
72
+ class AfterSummaryEvent(TypedDict, total=False):
73
+ agent: Any
74
+ summary: str
75
+
76
+ # 附加提示
77
+ class BeforeAddonPromptEvent(TypedDict, total=False):
78
+ agent: Any
79
+ need_complete: bool
80
+ current_message: str
81
+ has_session_addon: bool
82
+
83
+ class AfterAddonPromptEvent(TypedDict, total=False):
84
+ agent: Any
85
+ need_complete: bool
86
+ addon_text: str
87
+ final_message: str
88
+
89
+ # 历史清理
90
+ class BeforeHistoryClearEvent(TypedDict, total=False):
91
+ agent: Any
92
+
93
+ class AfterHistoryClearEvent(TypedDict, total=False):
94
+ agent: Any
95
+
96
+ # 模型调用
97
+ class BeforeModelCallEvent(TypedDict, total=False):
98
+ agent: Any
99
+ message: str
100
+
101
+ class AfterModelCallEvent(TypedDict, total=False):
102
+ agent: Any
103
+ message: str
104
+ response: str
105
+
106
+ # 中断
107
+ class InterruptTriggeredEvent(TypedDict, total=False):
108
+ agent: Any
109
+ current_response: str
110
+ user_input: str
111
+
112
+ # 工具筛选
113
+ class BeforeToolFilterEvent(TypedDict, total=False):
114
+ agent: Any
115
+ task: str
116
+ total_tools: int
117
+ threshold: int
118
+
119
+ class ToolFilteredEvent(TypedDict, total=False):
120
+ agent: Any
121
+ task: str
122
+ selected_tools: List[str]
123
+ total_tools: int
124
+ threshold: int
125
+
126
+ __all__ = [
127
+ "BEFORE_TOOL_CALL",
128
+ "AFTER_TOOL_CALL",
129
+ "TASK_STARTED",
130
+ "TASK_COMPLETED",
131
+ "BEFORE_SUMMARY",
132
+ "AFTER_SUMMARY",
133
+ "BEFORE_ADDON_PROMPT",
134
+ "AFTER_ADDON_PROMPT",
135
+ "BEFORE_HISTORY_CLEAR",
136
+ "AFTER_HISTORY_CLEAR",
137
+ "BEFORE_MODEL_CALL",
138
+ "AFTER_MODEL_CALL",
139
+ "INTERRUPT_TRIGGERED",
140
+ "BEFORE_TOOL_FILTER",
141
+ "TOOL_FILTERED",
142
+ "BeforeToolCallEvent",
143
+ "AfterToolCallEvent",
144
+ "TaskStartedEvent",
145
+ "TaskCompletedEvent",
146
+ "BeforeSummaryEvent",
147
+ "AfterSummaryEvent",
148
+ "BeforeAddonPromptEvent",
149
+ "AfterAddonPromptEvent",
150
+ "BeforeHistoryClearEvent",
151
+ "AfterHistoryClearEvent",
152
+ "BeforeModelCallEvent",
153
+ "AfterModelCallEvent",
154
+ "InterruptTriggeredEvent",
155
+ "BeforeToolFilterEvent",
156
+ "ToolFilteredEvent",
157
+ ]
@@ -8,6 +8,7 @@ import tempfile
8
8
 
9
9
  from jarvis.jarvis_utils.methodology import load_methodology, upload_methodology
10
10
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
11
+ from jarvis.jarvis_agent.utils import join_prompts
11
12
 
12
13
 
13
14
  class FileMethodologyManager:
@@ -47,16 +48,25 @@ class FileMethodologyManager:
47
48
  # 上传成功
48
49
 
49
50
  if self.agent.files:
50
- self.agent.session.prompt = f"{self.agent.session.prompt}\n\n上传的文件包含历史对话信息和方法论文件,可以从中获取一些经验信息。"
51
+ self.agent.session.prompt = join_prompts([
52
+ self.agent.session.prompt,
53
+ "上传的文件包含历史对话信息和方法论文件,可以从中获取一些经验信息。"
54
+ ])
51
55
  else:
52
- self.agent.session.prompt = f"{self.agent.session.prompt}\n\n上传的文件包含历史对话信息,可以从中获取一些经验信息。"
56
+ self.agent.session.prompt = join_prompts([
57
+ self.agent.session.prompt,
58
+ "上传的文件包含历史对话信息,可以从中获取一些经验信息。"
59
+ ])
53
60
 
54
61
  def _handle_files_upload(self):
55
62
  """处理普通文件上传"""
56
63
  if not self.agent.model.upload_files(self.agent.files): # type: ignore
57
64
  PrettyOutput.print("文件上传失败,将忽略文件列表", OutputType.WARNING)
58
65
  else:
59
- self.agent.session.prompt = f"{self.agent.session.prompt}\n\n上传的文件包含历史对话信息,可以从中获取一些经验信息。"
66
+ self.agent.session.prompt = join_prompts([
67
+ self.agent.session.prompt,
68
+ "上传的文件包含历史对话信息,可以从中获取一些经验信息。"
69
+ ])
60
70
 
61
71
  def _handle_local_mode(self):
62
72
  """处理本地模式(不支持文件上传)"""
@@ -80,7 +90,10 @@ class FileMethodologyManager:
80
90
  platform_name=self.agent.model.platform_name(),
81
91
  model_name=self.agent.model.name(),
82
92
  )
83
- self.agent.session.prompt = f"{self.agent.session.prompt}\n\n以下是历史类似问题的执行经验,可参考:\n{methodology}"
93
+ self.agent.session.prompt = join_prompts([
94
+ self.agent.session.prompt,
95
+ f"以下是历史类似问题的执行经验,可参考:\n{methodology}"
96
+ ])
84
97
 
85
98
  def handle_history_with_file_upload(self) -> str:
86
99
  """使用文件上传方式处理历史"""
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """Jarvis AI 助手主入口模块"""
3
- from typing import Optional
3
+ from typing import Optional, List
4
4
 
5
5
  import typer
6
6
 
@@ -391,7 +391,7 @@ def handle_builtin_config_selector(
391
391
  if category == "roles":
392
392
  count = opt.get("roles_count")
393
393
  details_val = opt.get("details", "")
394
- parts: list[str] = []
394
+ parts: List[str] = []
395
395
  if isinstance(count, int) and count > 0:
396
396
  parts.append(f"{count} 个角色")
397
397
  if isinstance(details_val, str) and details_val:
@@ -442,7 +442,7 @@ def handle_builtin_config_selector(
442
442
  if choice_index != -1:
443
443
  try:
444
444
  sel = options[choice_index]
445
- args: list[str] = []
445
+ args: List[str] = []
446
446
 
447
447
  if sel["category"] == "agent":
448
448
  # jarvis-agent 支持 -f/--config(全局配置)与 -c/--agent-definition
@@ -6,6 +6,7 @@
6
6
 
7
7
  from jarvis.jarvis_utils.globals import get_all_memory_tags
8
8
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
9
+ from jarvis.jarvis_agent.events import TASK_STARTED, BEFORE_HISTORY_CLEAR, TASK_COMPLETED
9
10
 
10
11
 
11
12
  class MemoryManager:
@@ -159,11 +160,11 @@ class MemoryManager:
159
160
  def _subscribe_events(self) -> None:
160
161
  bus = self.agent.get_event_bus() # type: ignore[attr-defined]
161
162
  # 任务开始时重置去重标记
162
- bus.subscribe("task_started", self._on_task_started)
163
+ bus.subscribe(TASK_STARTED, self._on_task_started)
163
164
  # 在清理历史前尝试保存记忆(若开启强制保存且尚未处理)
164
- bus.subscribe("before_history_clear", self._ensure_memory_prompt)
165
+ bus.subscribe(BEFORE_HISTORY_CLEAR, self._ensure_memory_prompt)
165
166
  # 任务完成时作为兜底再尝试一次
166
- bus.subscribe("task_completed", self._ensure_memory_prompt)
167
+ bus.subscribe(TASK_COMPLETED, self._ensure_memory_prompt)
167
168
 
168
169
  def _on_task_started(self, **payload) -> None:
169
170
  self._memory_prompted = False
@@ -81,7 +81,7 @@ TASK_ANALYSIS_PROMPT = f"""<task_analysis>
81
81
  2. 方法论应该具备足够的通用性,可应用于同类问题
82
82
  3. 特别注意用户在执行过程中提供的修正、反馈和改进建议
83
83
  4. 如果用户明确指出了某个解决步骤的优化方向,这应该被纳入方法论
84
- 5. 方法论要严格按照实际的执行流程来总结,不要遗漏或增加任何步骤
84
+ 5. 方法论应面向未来复用,总结“下次遇到同类问题应该如何处理”的通用流程与检查清单,避免局限于本次执行细节
85
85
  </evaluation_criteria>
86
86
  <tool_requirements>
87
87
  工具代码要求:
@@ -157,7 +157,7 @@ TASK_ANALYSIS_PROMPT = f"""<task_analysis>
157
157
  <methodology_requirements>
158
158
  方法论格式要求:
159
159
  1. 问题重述: 简明扼要的问题归纳,不含特定细节
160
- 2. 最优解决方案: 经过用户验证的、最终有效的解决方案(将每个步骤要使用的工具也列举出来)
160
+ 2. 可复用解决流程: 面向“下次遇到同类问题”的步骤化方案(列出每步可用的工具),避免与本次特定上下文绑定
161
161
  3. 注意事项: 执行中可能遇到的常见问题和注意点,尤其是用户指出的问题
162
162
  4. 可选步骤: 对于有多种解决路径的问题,标注出可选步骤和适用场景
163
163
  </methodology_requirements>
@@ -1,7 +1,8 @@
1
1
  # -*- coding: utf-8 -*-
2
- from typing import Any, Protocol, Tuple
2
+ from typing import Any, Protocol, Tuple, runtime_checkable
3
3
 
4
4
 
5
+ @runtime_checkable
5
6
  class OutputHandlerProtocol(Protocol):
6
7
  """
7
8
  Defines the interface for an output handler, which is responsible for
@@ -28,3 +29,5 @@ class OutputHandlerProtocol(Protocol):
28
29
  A tuple containing a boolean (whether to return) and the result.
29
30
  """
30
31
  ...
32
+
33
+ __all__ = ["OutputHandlerProtocol"]
@@ -11,7 +11,8 @@ from enum import Enum
11
11
  from typing import Any, TYPE_CHECKING
12
12
 
13
13
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
14
- from jarvis.jarvis_utils.tag import ot
14
+ from jarvis.jarvis_agent.events import BEFORE_TOOL_CALL, AFTER_TOOL_CALL
15
+ from jarvis.jarvis_agent.utils import join_prompts, is_auto_complete, normalize_next_action
15
16
 
16
17
  if TYPE_CHECKING:
17
18
  # 仅用于类型标注,避免运行时循环依赖
@@ -63,7 +64,7 @@ class AgentRunLoop:
63
64
  # 广播工具调用前事件(不影响主流程)
64
65
  try:
65
66
  ag.event_bus.emit(
66
- "before_tool_call",
67
+ BEFORE_TOOL_CALL,
67
68
  agent=ag,
68
69
  current_response=current_response,
69
70
  )
@@ -72,12 +73,7 @@ class AgentRunLoop:
72
73
  need_return, tool_prompt = ag._call_tools(current_response)
73
74
 
74
75
  # 将上一个提示和工具提示安全地拼接起来
75
- prompt_parts = []
76
- if ag.session.prompt:
77
- prompt_parts.append(ag.session.prompt)
78
- if tool_prompt:
79
- prompt_parts.append(tool_prompt)
80
- ag.session.prompt = "\n\n".join(prompt_parts)
76
+ ag.session.prompt = join_prompts([ag.session.prompt, tool_prompt])
81
77
 
82
78
  if need_return:
83
79
  return ag.session.prompt
@@ -88,7 +84,7 @@ class AgentRunLoop:
88
84
  # 广播工具调用后的事件(不影响主流程)
89
85
  try:
90
86
  ag.event_bus.emit(
91
- "after_tool_call",
87
+ AFTER_TOOL_CALL,
92
88
  agent=ag,
93
89
  current_response=current_response,
94
90
  need_return=need_return,
@@ -102,27 +98,16 @@ class AgentRunLoop:
102
98
  continue
103
99
 
104
100
  # 检查自动完成
105
- if ag.auto_complete and ot("!!!COMPLETE!!!") in current_response:
101
+ if ag.auto_complete and is_auto_complete(current_response):
106
102
  return ag._complete_task(auto_completed=True)
107
103
 
108
104
  # 获取下一步用户输入
109
105
  next_action = ag._get_next_user_action()
110
- if (
111
- next_action == "continue"
112
- or (
113
- isinstance(next_action, Enum)
114
- and getattr(next_action, "value", None) == "continue"
115
- )
116
- ):
106
+ action = normalize_next_action(next_action)
107
+ if action == "continue":
117
108
  run_input_handlers = True
118
109
  continue
119
- elif (
120
- next_action == "complete"
121
- or (
122
- isinstance(next_action, Enum)
123
- and getattr(next_action, "value", None) == "complete"
124
- )
125
- ):
110
+ elif action == "complete":
126
111
  return ag._complete_task(auto_completed=False)
127
112
 
128
113
  except Exception as e:
@@ -3,6 +3,7 @@ from typing import Any, Tuple
3
3
 
4
4
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
5
5
  from jarvis.jarvis_utils.input import user_confirm
6
+ from jarvis.jarvis_agent.utils import join_prompts
6
7
 
7
8
 
8
9
  def shell_input_handler(user_input: str, agent: Any) -> Tuple[str, bool]:
@@ -41,7 +42,11 @@ def shell_input_handler(user_input: str, agent: Any) -> Tuple[str, bool]:
41
42
  )
42
43
  if user_confirm("是否将执行结果反馈给Agent?", default=True):
43
44
  return (
44
- f"{user_input}\n\n用户执行以下脚本:\n{script}\n\n执行结果:\n{output}",
45
+ join_prompts([
46
+ user_input,
47
+ f"用户执行以下脚本:\n{script}",
48
+ f"执行结果:\n{output}",
49
+ ]),
45
50
  False,
46
51
  )
47
52
  return "", True