feat(aibox): 调度器任务间上下文隔离
- 每个定时任务创建独立DeepSeekChat实例,拥有独立messages/工具注册表/消息锁 - 任务与WebSocket主对话互不干扰,消除并发冲突与上下文污染根因 - 共享技能管理器懒加载一次供所有任务复用,避免重复扫描技能 - DeepSeekChat支持传入skill_manager复用,任务实例history_manager=None不污染历史
This commit is contained in:
1 parent
fa9679b7a6
commit
69a8eee192
1 file changed
+45
-22
+45
-22
@@ -2835,8 +2835,37 @@ class TaskScheduler:
|
|||||||
self.execution_history = []
|
self.execution_history = []
|
||||||
self.max_history = 100
|
self.max_history = 100
|
||||||
|
|
||||||
|
# 共享技能管理器(懒加载一次,供所有任务实例复用,避免重复扫描技能)
|
||||||
|
self._shared_skill_manager = None
|
||||||
|
|
||||||
self.logger.info(i18n.get('scheduler_started'))
|
self.logger.info(i18n.get('scheduler_started'))
|
||||||
|
|
||||||
|
def _get_shared_skill_manager(self):
|
||||||
|
"""获取共享技能管理器(懒加载,全局只创建一次)"""
|
||||||
|
if self._shared_skill_manager is None:
|
||||||
|
self._shared_skill_manager = SkillManager(self.logger)
|
||||||
|
return self._shared_skill_manager
|
||||||
|
|
||||||
|
def _create_task_ai(self):
|
||||||
|
"""为单个定时任务创建独立的AI实例,实现任务间上下文隔离。
|
||||||
|
|
||||||
|
每个任务拥有独立的 messages / 工具注册表 / 消息锁,互不干扰,
|
||||||
|
也不影响 WebSocket 主对话。共享只读资源(记忆库、备忘录、提示词、
|
||||||
|
技能管理器)复用,避免重复加载开销。
|
||||||
|
"""
|
||||||
|
return DeepSeekChat(
|
||||||
|
api_key=None,
|
||||||
|
max_history=constants.DEFAULT_MAX_HISTORY,
|
||||||
|
debug_level=self.logger.level,
|
||||||
|
exit_callback=None, # 任务执行不触发全局对话结束
|
||||||
|
history_manager=None, # 不加载历史对话上下文,任务隔离
|
||||||
|
memory_db=self.ai_manager.memory_db if self.ai_manager else None,
|
||||||
|
memo_db=self.ai_manager.memo_db if self.ai_manager else None,
|
||||||
|
command_executor=self.ai_manager.command_executor if self.ai_manager else None,
|
||||||
|
prompt_manager=self.ai_manager.prompt_manager if self.ai_manager else None,
|
||||||
|
skill_manager=self._get_shared_skill_manager()
|
||||||
|
)
|
||||||
|
|
||||||
def set_websocket_handler(self, handler):
|
def set_websocket_handler(self, handler):
|
||||||
"""设置WebSocket处理器,用于广播任务执行状态"""
|
"""设置WebSocket处理器,用于广播任务执行状态"""
|
||||||
self.ws_handler = handler
|
self.ws_handler = handler
|
||||||
@@ -2994,8 +3023,8 @@ class TaskScheduler:
|
|||||||
|
|
||||||
result = "任务已触发AI对话"
|
result = "任务已触发AI对话"
|
||||||
|
|
||||||
# 如果有AI管理器,触发AI对话
|
# 为当前任务创建独立的AI实例,实现任务间上下文隔离(不干扰主对话,也不受其他任务影响)
|
||||||
if self.ai_manager and self.ai_manager.ai:
|
ai = self._create_task_ai()
|
||||||
try:
|
try:
|
||||||
# 获取当前事件循环
|
# 获取当前事件循环
|
||||||
loop = asyncio.get_running_loop()
|
loop = asyncio.get_running_loop()
|
||||||
@@ -3027,29 +3056,13 @@ class TaskScheduler:
|
|||||||
loop
|
loop
|
||||||
)
|
)
|
||||||
|
|
||||||
# 修改:确保AI处于新对话状态,不加载历史上下文
|
|
||||||
if self.ai_manager.ai:
|
|
||||||
ai = self.ai_manager.ai
|
|
||||||
# 先取消任何正在进行中的对话(防止WebSocket线程与调度器线程并发修改messages)
|
|
||||||
if ai.is_busy():
|
|
||||||
self.logger.info("AI正在处理中,先取消当前对话...")
|
|
||||||
ai.cancel()
|
|
||||||
# 短暂等待让正在进行的请求退出并释放锁(使用asyncio.sleep避免阻塞事件循环)
|
|
||||||
for _ in range(20):
|
|
||||||
await asyncio.sleep(0.1)
|
|
||||||
if not ai.is_busy():
|
|
||||||
break
|
|
||||||
# 重置AI对话状态,但保留系统提示词(在锁保护下执行)
|
|
||||||
ai.reset_conversation()
|
|
||||||
self.logger.info("AI对话状态已重置,准备执行任务")
|
|
||||||
|
|
||||||
# 创建一个 Future 来等待 AI 完成
|
# 创建一个 Future 来等待 AI 完成
|
||||||
ai_future = loop.create_future()
|
ai_future = loop.create_future()
|
||||||
|
|
||||||
def run_ai():
|
def run_ai():
|
||||||
try:
|
try:
|
||||||
# 在单独的线程中运行 AI
|
# 在单独的线程中运行 AI(独立实例,天然隔离,无并发冲突)
|
||||||
response = self.ai_manager.ai.think_and_respond(user_input, output_callback)
|
response = ai.think_and_respond(user_input, output_callback)
|
||||||
# 将结果设置到 Future 中
|
# 将结果设置到 Future 中
|
||||||
loop.call_soon_threadsafe(ai_future.set_result, response)
|
loop.call_soon_threadsafe(ai_future.set_result, response)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -3089,6 +3102,13 @@ class TaskScheduler:
|
|||||||
})
|
})
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
finally:
|
||||||
|
# 任务执行完毕,释放独立实例占用的资源
|
||||||
|
try:
|
||||||
|
if ai and hasattr(ai, 'cancel'):
|
||||||
|
ai.cancel()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
# 获取任务的最新状态(可选,用于日志)
|
# 获取任务的最新状态(可选,用于日志)
|
||||||
updated_memo = memo_db.get_memo(memo['id'])
|
updated_memo = memo_db.get_memo(memo['id'])
|
||||||
@@ -6686,7 +6706,7 @@ class DeepSeekChat:
|
|||||||
system_prompt: str = None, debug_level: int = None, exit_callback: Callable = None,
|
system_prompt: str = None, debug_level: int = None, exit_callback: Callable = None,
|
||||||
history_manager: ConversationHistory = None, memory_db: MemoryDatabase = None,
|
history_manager: ConversationHistory = None, memory_db: MemoryDatabase = None,
|
||||||
memo_db: MemoDatabase = None, command_executor: CommandExecutor = None,
|
memo_db: MemoDatabase = None, command_executor: CommandExecutor = None,
|
||||||
prompt_manager: PromptManager = None):
|
prompt_manager: PromptManager = None, skill_manager: SkillManager = None):
|
||||||
self.config = ConfigManager()
|
self.config = ConfigManager()
|
||||||
self.logger = Logger("DeepSeek", debug_level if debug_level is not None else Logger("").level)
|
self.logger = Logger("DeepSeek", debug_level if debug_level is not None else Logger("").level)
|
||||||
self.debug_level = self.logger.level
|
self.debug_level = self.logger.level
|
||||||
@@ -6701,6 +6721,8 @@ class DeepSeekChat:
|
|||||||
self.history_loaded = False
|
self.history_loaded = False
|
||||||
self.conversation_active = True
|
self.conversation_active = True
|
||||||
self.messages = []
|
self.messages = []
|
||||||
|
# 共享技能管理器:传入时复用(避免每个任务重复扫描技能),否则按需创建
|
||||||
|
self._shared_skill_manager = skill_manager
|
||||||
|
|
||||||
# 添加对话压缩器
|
# 添加对话压缩器
|
||||||
self.compressor = ConversationCompressor(self, self.logger)
|
self.compressor = ConversationCompressor(self, self.logger)
|
||||||
@@ -6826,7 +6848,8 @@ class DeepSeekChat:
|
|||||||
self.tool_registry.register_many(builtin_tools, is_builtin=True)
|
self.tool_registry.register_many(builtin_tools, is_builtin=True)
|
||||||
|
|
||||||
# 创建技能管理器:启动时对每个技能执行 --help,收集第一行简介
|
# 创建技能管理器:启动时对每个技能执行 --help,收集第一行简介
|
||||||
skill_manager = SkillManager(self.logger)
|
# 若传入共享 skill_manager 则复用,避免每个任务/实例重复扫描技能
|
||||||
|
skill_manager = self._shared_skill_manager or SkillManager(self.logger)
|
||||||
self.tool_registry.skill_manager = skill_manager
|
self.tool_registry.skill_manager = skill_manager
|
||||||
|
|
||||||
# 注册扩展技能搜索工具(单一工具,替代逐个注册 ExtensionTool)
|
# 注册扩展技能搜索工具(单一工具,替代逐个注册 ExtensionTool)
|
||||||
|
|||||||
Reference in new issue
Block a user