jarvis-ai-assistant 0.2.7__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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +267 -240
- 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 +37 -398
- jarvis/jarvis_agent/memory_manager.py +133 -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 -0
- 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_tools/registry.py +79 -20
- jarvis/jarvis_tools/retrieve_memory.py +36 -8
- 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 +196 -106
- {jarvis_ai_assistant-0.2.7.dist-info → jarvis_ai_assistant-0.3.0.dist-info}/METADATA +1 -1
- {jarvis_ai_assistant-0.2.7.dist-info → jarvis_ai_assistant-0.3.0.dist-info}/RECORD +38 -28
- {jarvis_ai_assistant-0.2.7.dist-info → jarvis_ai_assistant-0.3.0.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.2.7.dist-info → jarvis_ai_assistant-0.3.0.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.2.7.dist-info → jarvis_ai_assistant-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.2.7.dist-info → jarvis_ai_assistant-0.3.0.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
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,
|
@@ -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
|
-
|
164
|
-
|
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
|
-
|
310
|
-
|
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.
|
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
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
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
|
-
|
368
|
-
|
369
|
-
|
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
|
-
|
372
|
-
if not summary:
|
373
|
-
return ""
|
463
|
+
return self._format_summary_message(summary)
|
374
464
|
|
375
|
-
|
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
|
-
|
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
|
-
)
|
498
|
+
# 收集满意度反馈
|
499
|
+
satisfaction_feedback = self.task_analyzer.collect_satisfaction_feedback(
|
500
|
+
auto_completed
|
501
|
+
)
|
427
502
|
|
428
503
|
if self.use_analysis:
|
429
|
-
self.
|
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
|
-
|
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工具"
|
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
|
-
|
530
|
-
|
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
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
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
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
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
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
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}"
|