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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +277 -242
- jarvis/jarvis_agent/agent_manager.py +85 -0
- jarvis/jarvis_agent/config_editor.py +53 -0
- jarvis/jarvis_agent/file_methodology_manager.py +105 -0
- jarvis/jarvis_agent/jarvis.py +30 -619
- jarvis/jarvis_agent/memory_manager.py +127 -0
- jarvis/jarvis_agent/methodology_share_manager.py +174 -0
- jarvis/jarvis_agent/prompts.py +18 -3
- jarvis/jarvis_agent/share_manager.py +176 -0
- jarvis/jarvis_agent/task_analyzer.py +126 -0
- jarvis/jarvis_agent/task_manager.py +111 -0
- jarvis/jarvis_agent/tool_share_manager.py +139 -0
- jarvis/jarvis_code_agent/code_agent.py +26 -20
- jarvis/jarvis_data/config_schema.json +37 -4
- jarvis/jarvis_platform/ai8.py +13 -1
- jarvis/jarvis_platform/base.py +20 -5
- jarvis/jarvis_platform/human.py +11 -1
- jarvis/jarvis_platform/kimi.py +10 -0
- jarvis/jarvis_platform/openai.py +20 -0
- jarvis/jarvis_platform/tongyi.py +14 -9
- jarvis/jarvis_platform/yuanbao.py +10 -0
- jarvis/jarvis_platform_manager/main.py +12 -12
- jarvis/jarvis_platform_manager/service.py +9 -4
- jarvis/jarvis_tools/registry.py +32 -0
- jarvis/jarvis_tools/retrieve_memory.py +36 -8
- jarvis/jarvis_tools/search_web.py +1 -1
- jarvis/jarvis_utils/clipboard.py +90 -0
- jarvis/jarvis_utils/config.py +64 -0
- jarvis/jarvis_utils/git_utils.py +17 -7
- jarvis/jarvis_utils/globals.py +18 -12
- jarvis/jarvis_utils/input.py +118 -16
- jarvis/jarvis_utils/methodology.py +48 -5
- jarvis/jarvis_utils/utils.py +169 -105
- {jarvis_ai_assistant-0.2.8.dist-info → jarvis_ai_assistant-0.3.1.dist-info}/METADATA +1 -1
- {jarvis_ai_assistant-0.2.8.dist-info → jarvis_ai_assistant-0.3.1.dist-info}/RECORD +40 -30
- {jarvis_ai_assistant-0.2.8.dist-info → jarvis_ai_assistant-0.3.1.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.2.8.dist-info → jarvis_ai_assistant-0.3.1.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.2.8.dist-info → jarvis_ai_assistant-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.2.8.dist-info → jarvis_ai_assistant-0.3.1.dist-info}/top_level.txt +0 -0
jarvis/jarvis_agent/__init__.py
CHANGED
@@ -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
|
-
|
164
|
-
|
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
|
-
|
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
|
-
|
310
|
-
|
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.
|
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
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
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
|
-
|
368
|
-
|
369
|
-
|
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
|
-
|
372
|
-
if not summary:
|
373
|
-
return ""
|
473
|
+
return self._format_summary_message(summary)
|
374
474
|
|
375
|
-
|
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
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
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.
|
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
|
-
|
479
|
-
|
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
|
-
|
530
|
-
|
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
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
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
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
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
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
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}"
|