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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +60 -25
- jarvis/jarvis_agent/event_bus.py +2 -2
- jarvis/jarvis_agent/events.py +157 -0
- jarvis/jarvis_agent/file_methodology_manager.py +17 -4
- jarvis/jarvis_agent/jarvis.py +3 -3
- jarvis/jarvis_agent/memory_manager.py +4 -3
- jarvis/jarvis_agent/prompts.py +2 -2
- jarvis/jarvis_agent/protocols.py +4 -1
- jarvis/jarvis_agent/run_loop.py +9 -24
- jarvis/jarvis_agent/shell_input_handler.py +6 -1
- jarvis/jarvis_agent/task_analyzer.py +18 -13
- jarvis/jarvis_agent/task_manager.py +6 -4
- jarvis/jarvis_agent/utils.py +50 -0
- jarvis/jarvis_mcp/sse_mcp_client.py +1 -1
- jarvis/jarvis_memory_organizer/memory_organizer.py +4 -4
- jarvis/jarvis_platform/kimi.py +1 -1
- jarvis/jarvis_platform/tongyi.py +1 -1
- jarvis/jarvis_platform/yuanbao.py +1 -1
- jarvis/jarvis_rag/retriever.py +3 -3
- jarvis/jarvis_stats/stats.py +2 -2
- jarvis/jarvis_stats/storage.py +3 -3
- jarvis/jarvis_tools/edit_file.py +3 -3
- jarvis/jarvis_tools/execute_script.py +2 -2
- jarvis/jarvis_tools/sub_agent.py +2 -2
- jarvis/jarvis_tools/sub_code_agent.py +2 -2
- jarvis/jarvis_utils/config.py +3 -3
- jarvis/jarvis_utils/fzf.py +4 -3
- jarvis/jarvis_utils/input.py +5 -5
- jarvis/jarvis_utils/utils.py +96 -9
- {jarvis_ai_assistant-0.3.28.dist-info → jarvis_ai_assistant-0.3.29.dist-info}/METADATA +1 -1
- {jarvis_ai_assistant-0.3.28.dist-info → jarvis_ai_assistant-0.3.29.dist-info}/RECORD +36 -34
- {jarvis_ai_assistant-0.3.28.dist-info → jarvis_ai_assistant-0.3.29.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.3.28.dist-info → jarvis_ai_assistant-0.3.29.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.3.28.dist-info → jarvis_ai_assistant-0.3.29.dist-info}/licenses/LICENSE +0 -0
- {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
jarvis/jarvis_agent/__init__.py
CHANGED
@@ -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(
|
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
|
-
|
346
|
-
if
|
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
|
-
|
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
|
-
|
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
|
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
|
602
|
+
message = join_prompts([message, addon_text])
|
575
603
|
|
576
604
|
# 广播添加附加提示后事件(不影响主流程)
|
577
605
|
try:
|
578
606
|
self.event_bus.emit(
|
579
|
-
|
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
|
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
|
-
|
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
|
-
|
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(
|
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(
|
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(
|
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(
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 =
|
958
|
+
self.session.prompt = join_prompts([
|
959
|
+
f"被用户中断,用户补充信息为:{user_input}",
|
960
|
+
"用户同意继续工具调用。"
|
961
|
+
])
|
930
962
|
return None # 继续执行工具调用
|
931
963
|
else:
|
932
|
-
self.session.prompt =
|
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
|
-
|
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
|
-
|
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
|
-
|
1113
|
+
TOOL_FILTERED,
|
1079
1114
|
agent=self,
|
1080
1115
|
task=task,
|
1081
1116
|
selected_tools=[],
|
jarvis/jarvis_agent/event_bus.py
CHANGED
@@ -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:
|
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 =
|
51
|
+
self.agent.session.prompt = join_prompts([
|
52
|
+
self.agent.session.prompt,
|
53
|
+
"上传的文件包含历史对话信息和方法论文件,可以从中获取一些经验信息。"
|
54
|
+
])
|
51
55
|
else:
|
52
|
-
self.agent.session.prompt =
|
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 =
|
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 =
|
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
|
"""使用文件上传方式处理历史"""
|
jarvis/jarvis_agent/jarvis.py
CHANGED
@@ -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:
|
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:
|
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(
|
163
|
+
bus.subscribe(TASK_STARTED, self._on_task_started)
|
163
164
|
# 在清理历史前尝试保存记忆(若开启强制保存且尚未处理)
|
164
|
-
bus.subscribe(
|
165
|
+
bus.subscribe(BEFORE_HISTORY_CLEAR, self._ensure_memory_prompt)
|
165
166
|
# 任务完成时作为兜底再尝试一次
|
166
|
-
bus.subscribe(
|
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
|
jarvis/jarvis_agent/prompts.py
CHANGED
@@ -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>
|
jarvis/jarvis_agent/protocols.py
CHANGED
@@ -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"]
|
jarvis/jarvis_agent/run_loop.py
CHANGED
@@ -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.
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
111
|
-
|
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
|
-
|
45
|
+
join_prompts([
|
46
|
+
user_input,
|
47
|
+
f"用户执行以下脚本:\n{script}",
|
48
|
+
f"执行结果:\n{output}",
|
49
|
+
]),
|
45
50
|
False,
|
46
51
|
)
|
47
52
|
return "", True
|