mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-10-10 22:30:02 +08:00
♻️ refactor(core): 重构 AI 编排框架与记忆及 RAG 子系统 (#2149)
* ♻️ refactor(core): 重构 AI 编排框架与记忆及 RAG 子系统 - 【重构】重构 `BaseRunnable` 并引入统一的 `RunIntent` 意图载体,规范 Agent、Team 和 Workflow 的执行流 - 【解耦】将中期记忆槽和长期向量记忆从 `MemoryConfig` 中解耦,转为独立的能力组件与工具箱进行管理 - 【记忆】移除 `MemoryReader` 和 `MemoryWriter`,统一封装为 `SessionMemoryContext` 会话记忆门面 - 【RAG】重构检索器与存储后端接口,统一采用 `QueryRequest` 进行多维度联合检索,并引入 `InMemoryScorer` 提升打分性能 - 【事件】优化 `EventBus` 异步事件分发机制,引入队列机制确保事件按序处理,避免并发竞态问题 - 【依赖注入】移除 `memory` 注入项,优化 `DependencyInjector` 的签名解析缓存以提升性能 * 🚨 auto fix by pre-commit hooks --------- Co-authored-by: webjoin111 <455457521@qq.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
webjoin111
pre-commit-ci[bot]
parent
922d092650
commit
52f7dbdedf
@@ -7,7 +7,7 @@ from pydantic import BaseModel, Field
|
||||
|
||||
from zhenxun.services.ai.core.exceptions import AbortException, ControlFlowExit
|
||||
from zhenxun.services.ai.core.stream_events import ToolStreamChunkEvent
|
||||
from zhenxun.services.ai.flow.base import BaseRunnable
|
||||
from zhenxun.services.ai.flow.core.base import BaseRunnable
|
||||
from zhenxun.services.ai.run import RunContext
|
||||
from zhenxun.services.ai.tools.core.tool import BaseTool
|
||||
from zhenxun.services.ai.tools.models import ToolResult
|
||||
@@ -49,7 +49,9 @@ class DelegateTool(BaseTool):
|
||||
"""
|
||||
resolved_name = name or getattr(runnable, "name", "SubRunnable")
|
||||
resolved_desc = description or getattr(
|
||||
runnable, "description", f"将子任务委派给 {resolved_name} 执行"
|
||||
runnable,
|
||||
"profile_summary",
|
||||
getattr(runnable, "description", f"将子任务委派给 {resolved_name} 执行"),
|
||||
)
|
||||
final_name = (
|
||||
f"delegate_to_{resolved_name}"
|
||||
|
||||
@@ -450,7 +450,7 @@ def bind_matcher(
|
||||
"""
|
||||
|
||||
if require_prefix:
|
||||
ns = infer_plugin_namespace(default="global")
|
||||
ns = infer_plugin_namespace()
|
||||
if ns and ns not in ("global", "unknown"):
|
||||
if not name.startswith(f"{ns}_"):
|
||||
name = f"{ns}_{name}"
|
||||
|
||||
@@ -218,7 +218,7 @@ def tool(
|
||||
if require_prefix:
|
||||
from zhenxun.utils.utils import infer_plugin_namespace
|
||||
|
||||
ns = infer_plugin_namespace(default="global")
|
||||
ns = infer_plugin_namespace()
|
||||
if ns and ns not in ("global", "unknown"):
|
||||
if not tool_name.startswith(f"{ns}_"):
|
||||
tool_name = f"{ns}_{tool_name}"
|
||||
|
||||
@@ -217,6 +217,14 @@ class BaseToolkit:
|
||||
)
|
||||
return f"<{tag_name}>\n{text}\n</{tag_name}>"
|
||||
|
||||
def clone_with(self, **kwargs: Any) -> "BaseToolkit":
|
||||
"""克隆当前工具箱原型,并透明注入新的运行时属性。"""
|
||||
new_tk = copy.copy(self)
|
||||
for k, v in kwargs.items():
|
||||
setattr(new_tk, k, v)
|
||||
new_tk._cached_tools = None
|
||||
return new_tk
|
||||
|
||||
def prefixed(self, prefix: str) -> "BaseToolkit":
|
||||
"""克隆工具箱并为其中所有工具追加统一的前缀"""
|
||||
new_tk = copy.copy(self)
|
||||
|
||||
@@ -365,7 +365,7 @@ class ToolProviderManager:
|
||||
|
||||
def register_tool(self, tool: ToolExecutable):
|
||||
"""注册由 @tool 生成的单一工具"""
|
||||
ns = infer_plugin_namespace(default="global")
|
||||
ns = infer_plugin_namespace()
|
||||
self.local_provider.register_tool(tool, ns)
|
||||
tags = getattr(getattr(tool, "settings", None), "tags", [])
|
||||
tag_str = f" | Tags: {tags}" if tags else ""
|
||||
@@ -376,7 +376,7 @@ class ToolProviderManager:
|
||||
"""
|
||||
注册一个完整的 Toolkit 实例,使其可通过智能字符串路由(Tag或Name)被动态发现。
|
||||
"""
|
||||
ns = infer_plugin_namespace(default="global")
|
||||
ns = infer_plugin_namespace()
|
||||
self.local_provider.register_toolkit(toolkit, ns)
|
||||
tk_name = getattr(toolkit, "__class__", type).__name__
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
import fnmatch
|
||||
from typing import TYPE_CHECKING, Any, Literal
|
||||
from typing import Any, Literal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
@@ -13,9 +13,6 @@ from zhenxun.services.ai.run.context import RunContext
|
||||
from zhenxun.services.ai.utils.logger import log_tool as logger
|
||||
from zhenxun.utils.pydantic_compat import model_dump, model_validate
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from zhenxun.services.ai.tools.core.tool import BaseTool
|
||||
|
||||
|
||||
class DirectivePayload(BaseModel):
|
||||
"""工具执行产生的副作用控制流指令载荷"""
|
||||
@@ -272,7 +269,7 @@ class Query(BaseModel):
|
||||
metadata_filter: dict[str, Any] | None = Field(default=None)
|
||||
"""如果提供,则工具的 metadata 必须包含这里列出的所有键值对。"""
|
||||
|
||||
def match(self, tool: "BaseTool") -> bool:
|
||||
def match(self, tool: Any) -> bool:
|
||||
"""判断某个工具或工具箱是否符合当前 Query 的筛选条件"""
|
||||
|
||||
def _match_pattern(val: str, pattern: str | list[str]) -> bool:
|
||||
|
||||
@@ -2,15 +2,17 @@ from typing import Any, Literal, Optional
|
||||
|
||||
from pydantic import Field, create_model
|
||||
|
||||
from zhenxun.services.ai.context.memory.manager import memory_manager
|
||||
from zhenxun.services.ai.context.memory.models import MemoryConfig
|
||||
from zhenxun.services.ai.context.memory.storage.backends import MemoryScope
|
||||
from zhenxun.services.ai.context.memory.types import SessionMetadata
|
||||
from zhenxun.services.ai.context.rag.engine import ScopedRAGClient
|
||||
from zhenxun.services.ai.run.context import RunContext
|
||||
from zhenxun.services.ai.tools.core.decorators import tool
|
||||
from zhenxun.services.ai.tools.core.tool import BaseTool
|
||||
from zhenxun.services.ai.tools.core.toolkit import BaseToolkit
|
||||
from zhenxun.services.ai.tools.models import ToolOptions, ToolResult
|
||||
from zhenxun.services.ai.utils.logger import log_tool as logger
|
||||
from zhenxun.services.ai.utils.runtime import ContextUtils
|
||||
from zhenxun.services.ai.utils.scope import ScopeBuilder
|
||||
|
||||
|
||||
class MemoryManagementToolkit(BaseToolkit):
|
||||
@@ -26,23 +28,45 @@ class MemoryManagementToolkit(BaseToolkit):
|
||||
|
||||
shared_options = ToolOptions(silent=True)
|
||||
|
||||
default_instructions = """\
|
||||
## 🧠 长期记忆管理系统 (Long-Term Memory)
|
||||
该系统是你的「无限档案馆」。⚠️ **注意:系统默认不会主动向你提供所有历史信息,你必须通过主动搜索来回忆。**
|
||||
_INTRO_TEXT = (
|
||||
"## 🧠 长期记忆管理系统 (Long-Term Memory)\n"
|
||||
"该系统是你的「无限档案馆」。系统默认不会主动向你提供所有历史信息,你必须通过主动搜索来回忆。\n\n"
|
||||
"### 📝 职责说明\n"
|
||||
)
|
||||
_READ_GUIDE = (
|
||||
"- **寻找历史线索**:当遇到未知情况,或用户提及过去的事情、"
|
||||
"特定设定时,必须主动检索历史库(使用 `search_memory`)。\n"
|
||||
)
|
||||
_WRITE_GUIDE = (
|
||||
"- **记录离散事实与经验**:当需要记录某个独立事件、历史经验、"
|
||||
"问题解决方案或具体事实时(使用 `save_memory`)。\n"
|
||||
"- **隐式记录**:当接收到值得记忆的重要信息时,请静默记录。"
|
||||
"除非用户主动提问,否则无需向用户显式汇报'我已记住'。\n"
|
||||
"- **按需更新**:如果发现某项历史记录已过时或状态发生扭转,"
|
||||
"请先检索出它的 ID,再修改(`update_memory`)或废弃(`delete_memory`)。\n"
|
||||
"- **精准提炼**:保存记忆时请提炼核心价值,避免保存无意义的闲聊。\n"
|
||||
)
|
||||
|
||||
### 📝 何时使用长期记忆?
|
||||
- **记录离散事实与经验**:当需要记录某个独立事件、历史经验、问题解决方案或具体事实时(使用 `save_memory`)。
|
||||
- **寻找历史线索**:当遇到未知情况,或用户提及过去的事情、特定设定时,必须主动检索历史库(使用 `search_memory`)。
|
||||
default_instructions = _INTRO_TEXT + _READ_GUIDE + _WRITE_GUIDE
|
||||
|
||||
### ⚙️ 操作规范
|
||||
1. **隐式记录**:当接收到值得记忆的重要信息时,请静默记录。除非用户主动提问,否则无需向用户显式汇报"我已记住"。
|
||||
2. **按需更新**:如果发现某项历史记录已过时或状态发生扭转,请先检索出它的 ID,再进行修改(使用 `update_memory`)或废弃(使用 `delete_memory`)。
|
||||
3. **精准提炼**:保存记忆时请提炼核心价值,避免保存无意义的闲聊。\
|
||||
""" # noqa: E501
|
||||
@classmethod
|
||||
def read_only(cls, **kwargs) -> "MemoryManagementToolkit":
|
||||
"""[工厂方法] 创建一个只读模式的长期记忆工具箱。"""
|
||||
kwargs["include"] = ["search_memory"]
|
||||
kwargs.setdefault("instructions", cls._INTRO_TEXT + cls._READ_GUIDE)
|
||||
return cls(**kwargs)
|
||||
|
||||
@classmethod
|
||||
def write_only(cls, **kwargs) -> "MemoryManagementToolkit":
|
||||
"""[工厂方法] 创建一个仅写入模式的长期记忆工具箱。"""
|
||||
kwargs["exclude"] = ["search_memory"]
|
||||
kwargs.setdefault("instructions", cls._INTRO_TEXT + cls._WRITE_GUIDE)
|
||||
return cls(**kwargs)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
memory_config: MemoryConfig | None = None,
|
||||
rag_client: ScopedRAGClient | None = None,
|
||||
scopes: dict[str, ScopeBuilder] | None = None,
|
||||
namespace: str | None = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
@@ -50,85 +74,43 @@ class MemoryManagementToolkit(BaseToolkit):
|
||||
初始化主动记忆管理工具箱。
|
||||
|
||||
参数:
|
||||
memory_config: 记忆系统的全局配置对象,为空则使用全局默认。
|
||||
rag_client: 底层 RAG 检索引擎客户端实例。
|
||||
scopes: 作用域构建器映射字典,用于动态限定存储的分区。
|
||||
namespace: 当前隔离环境的命名空间。
|
||||
kwargs: 其他透传给 BaseToolkit 的参数。
|
||||
"""
|
||||
super().__init__(**kwargs)
|
||||
self.memory_config = memory_config
|
||||
self.rag_client = rag_client
|
||||
from zhenxun.services.ai.context.memory.types import Isolation
|
||||
|
||||
self.scopes = scopes or {"私有": Isolation.AGENT_USER()}
|
||||
self._namespace = namespace
|
||||
|
||||
def _get_runtime_meta_and_scope(
|
||||
self, context: RunContext, scope_name: str | None = None
|
||||
) -> tuple[Any, SessionMetadata]:
|
||||
"""动态获取当前运行时的数据库实例与会话元信息,实现无状态化"""
|
||||
ns = self._namespace or getattr(context.session, "namespace", "global")
|
||||
scope = memory_manager.get_long_term_memory(self.memory_config, namespace=ns)
|
||||
scope = MemoryScope(rag_client=self.rag_client) if self.rag_client else None
|
||||
|
||||
scope_builder = None
|
||||
if (
|
||||
self.memory_config
|
||||
and self.memory_config.long_term
|
||||
and self.memory_config.long_term.scopes
|
||||
):
|
||||
scopes_dict = self.memory_config.long_term.scopes
|
||||
if not scope_name:
|
||||
scope_builder = next(iter(scopes_dict.values()))
|
||||
else:
|
||||
scope_builder = scopes_dict.get(scope_name)
|
||||
|
||||
if not scope_builder:
|
||||
scope_builder = getattr(self.memory_config, "base_isolation", None)
|
||||
if not scope_builder:
|
||||
from zhenxun.services.ai.context.memory.types import Isolation
|
||||
|
||||
scope_builder = Isolation.AGENT_USER()
|
||||
|
||||
selector = scope_builder.resolve(
|
||||
deps=context.deps,
|
||||
prefix="",
|
||||
default_namespace=ns,
|
||||
default_agent=context.run.agent_name,
|
||||
scope_builder = (
|
||||
self.scopes.get(scope_name)
|
||||
if scope_name
|
||||
else next(iter(self.scopes.values()), None)
|
||||
)
|
||||
parts = selector.get_scope_parts()
|
||||
all_scopes = {"/"}
|
||||
current_path = ""
|
||||
for part in parts:
|
||||
current_path += f"/{part}"
|
||||
all_scopes.add(current_path)
|
||||
accessible_scopes = list(all_scopes)
|
||||
accessible_scopes.sort(key=lambda x: len(x.split("/")))
|
||||
|
||||
scope_name_mapping = {}
|
||||
if (
|
||||
self.memory_config
|
||||
and self.memory_config.long_term
|
||||
and self.memory_config.long_term.scopes
|
||||
):
|
||||
for name, builder in self.memory_config.long_term.scopes.items():
|
||||
sel = builder.resolve(
|
||||
deps=context.deps,
|
||||
prefix="",
|
||||
default_namespace=ns,
|
||||
default_agent=context.run.agent_name,
|
||||
)
|
||||
scope_name_mapping[sel.scope_prefix] = name
|
||||
|
||||
session_meta = SessionMetadata(
|
||||
session_id=context.session_id or "default_session",
|
||||
selector=selector,
|
||||
scope_prefix=selector.scope_prefix,
|
||||
accessible_scopes=accessible_scopes,
|
||||
scope_name_mapping=scope_name_mapping,
|
||||
session_meta = ContextUtils.build_session_meta(
|
||||
context=context,
|
||||
target_builder=scope_builder,
|
||||
extra_scopes=self.scopes,
|
||||
custom_namespace=self._namespace,
|
||||
)
|
||||
return scope, session_meta
|
||||
|
||||
async def get_tools(self, context: RunContext | None = None) -> dict[str, BaseTool]:
|
||||
tools = await super().get_tools(context)
|
||||
if not self.memory_config or not self.memory_config.long_term.enable:
|
||||
if not getattr(self, "rag_client", None):
|
||||
return tools
|
||||
|
||||
scopes_dict = self.memory_config.long_term.scopes
|
||||
scopes_dict = getattr(self, "scopes", {})
|
||||
if not scopes_dict:
|
||||
return tools
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ from typing import Any, Literal
|
||||
from pydantic import Field, create_model
|
||||
|
||||
from zhenxun.services.ai.context.memory.manager import memory_manager
|
||||
from zhenxun.services.ai.context.memory.models import MemoryConfig
|
||||
from zhenxun.services.ai.context.memory.types import (
|
||||
MemorySlot,
|
||||
SessionMetadata,
|
||||
@@ -16,6 +15,7 @@ from zhenxun.services.ai.tools.core.decorators import tool
|
||||
from zhenxun.services.ai.tools.core.tool import BaseTool
|
||||
from zhenxun.services.ai.tools.core.toolkit import BaseToolkit
|
||||
from zhenxun.services.ai.tools.models import ToolOptions, ToolResult
|
||||
from zhenxun.services.ai.utils.runtime import ContextUtils
|
||||
|
||||
_SLOT_LOCKS: dict[str, asyncio.Lock] = {}
|
||||
_GLOBAL_LOCK = asyncio.Lock()
|
||||
@@ -45,119 +45,78 @@ class MemorySlotToolkit(BaseToolkit):
|
||||
|
||||
shared_options = ToolOptions(silent=True)
|
||||
|
||||
default_instructions = """\
|
||||
## 📋 状态与规则面板 (Memory Slots / 中期记忆)
|
||||
该系统是你的「桌面便利贴」或「共享黑板」,用于保存你当前需要随时查阅的核心状态与全局规范。
|
||||
_INTRO_TEXT = (
|
||||
"## 📋 状态与规则面板 (Memory Slots / 中期记忆)\n"
|
||||
"该系统是你的「桌面便利贴」或「共享黑板」,用于保存你当前需要随时查阅的核心状态与全局规范。\n\n"
|
||||
"### 💡 核心机制\n"
|
||||
"- 记忆槽内容会在每次对话时**直接注入提示词中**,你无需搜索即可看见。\n"
|
||||
"- 槽位容量极其有限,仅用于维持最新的运行状态。\n\n"
|
||||
"### 📝 职责说明\n"
|
||||
)
|
||||
_READ_GUIDE = (
|
||||
"- **探索可用面板**:接手新任务时,可使用 `list_slots` "
|
||||
"宏观查看当前存在哪些面板。\n"
|
||||
)
|
||||
_WRITE_GUIDE = (
|
||||
"- **维护规范与进度**:如设定'沟通口吻'等规范(`update_slot`),"
|
||||
"或记录'待办清单'(`append_slot`)。\n"
|
||||
"- **保持精简**:内容过长时,主动将其归档到长期记忆,"
|
||||
"再重新提炼或调用 `delete_slot` 删除。\n"
|
||||
)
|
||||
|
||||
### 💡 核心机制
|
||||
- 被保存在记忆槽中的内容(如果已置顶),会在每次对话时**直接注入到你的上下文提示词中**,你无需任何搜索即可看见。
|
||||
- 槽位容量极其有限,仅用于维持当前最新的运行状态。
|
||||
default_instructions = _INTRO_TEXT + _READ_GUIDE + _WRITE_GUIDE
|
||||
|
||||
### 📝 何时使用记忆槽?
|
||||
- **维护全局规则**:例如设定"用户整体偏好"、"沟通口吻"、"全局指导原则"等需要时刻遵守的规范(使用 `update_slot`)。
|
||||
- **追踪当前进度**:例如记录"待办事项清单"、"当前任务进度"、"上下文摘要"(使用 `append_slot` 列表或 `update_slot` 覆盖)。
|
||||
@classmethod
|
||||
def read_only(cls, **kwargs) -> "MemorySlotToolkit":
|
||||
"""[工厂方法] 创建一个只读模式的记忆槽工具箱。"""
|
||||
kwargs["include"] = ["list_slots", "read_slot"]
|
||||
kwargs.setdefault("instructions", cls._INTRO_TEXT + cls._READ_GUIDE)
|
||||
return cls(**kwargs)
|
||||
|
||||
### ⚙️ 操作规范
|
||||
1. **探索可用面板**:接手新任务时,可使用 `list_slots` 宏观查看当前存在哪些状态面板。
|
||||
2. **保持精简**:槽位有严格的字符数限制。当内容过长时,请主动将其归档到长期记忆后,重新提炼并覆盖槽位,或直接调用 `delete_slot` 删除不再需要的槽位。\
|
||||
""" # noqa: E501
|
||||
@classmethod
|
||||
def write_only(cls, **kwargs) -> "MemorySlotToolkit":
|
||||
"""[工厂方法] 创建一个仅写入模式的记忆槽工具箱。"""
|
||||
kwargs["exclude"] = ["list_slots", "read_slot"]
|
||||
kwargs.setdefault("instructions", cls._INTRO_TEXT + cls._WRITE_GUIDE)
|
||||
return cls(**kwargs)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
memory_config: MemoryConfig | None = None,
|
||||
scopes: dict[str, Any] | None = None,
|
||||
backend: Any = None,
|
||||
namespace: str | None = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
"""
|
||||
初始化中期记忆槽工具箱。
|
||||
|
||||
参数:
|
||||
memory_config: 记忆系统的全局配置对象,为空则使用全局默认。
|
||||
namespace: 当前隔离环境的命名空间。
|
||||
kwargs: 其他透传给 BaseToolkit 的参数。
|
||||
"""
|
||||
super().__init__(**kwargs)
|
||||
self.memory_config = memory_config
|
||||
self.scopes = scopes or {}
|
||||
self.backend = backend
|
||||
self._namespace = namespace
|
||||
|
||||
def _get_runtime_meta_and_ctx(
|
||||
self, context: RunContext, scope_name: str | None = None
|
||||
) -> tuple[Any, SessionMetadata]:
|
||||
ns = self._namespace or getattr(context.session, "namespace", "global")
|
||||
slot_ctx = memory_manager.get_slot_context(self.memory_config, namespace=ns)
|
||||
slot_ctx = self.backend or memory_manager.get_backend("slots", namespace=ns)
|
||||
|
||||
scope_builder = None
|
||||
if (
|
||||
self.memory_config
|
||||
and self.memory_config.slots
|
||||
and self.memory_config.slots.scopes
|
||||
):
|
||||
scopes_dict = self.memory_config.slots.scopes
|
||||
if not scope_name:
|
||||
scope_builder = next(iter(scopes_dict.values()))
|
||||
else:
|
||||
scope_builder = scopes_dict.get(scope_name)
|
||||
|
||||
if not scope_builder:
|
||||
scope_builder = getattr(self.memory_config, "base_isolation", None)
|
||||
if not scope_builder:
|
||||
from zhenxun.services.ai.context.memory.types import Isolation
|
||||
|
||||
scope_builder = Isolation.AGENT_USER()
|
||||
|
||||
selector = scope_builder.resolve(
|
||||
deps=context.deps,
|
||||
prefix="",
|
||||
default_namespace=ns,
|
||||
default_agent=context.run.agent_name,
|
||||
scope_builder = (
|
||||
self.scopes.get(scope_name)
|
||||
if scope_name
|
||||
else next(iter(self.scopes.values()), None)
|
||||
)
|
||||
parts = selector.get_scope_parts()
|
||||
all_scopes = {"/"}
|
||||
current_path = ""
|
||||
for part in parts:
|
||||
current_path += f"/{part}"
|
||||
all_scopes.add(current_path)
|
||||
accessible_scopes = list(all_scopes)
|
||||
accessible_scopes.sort(key=lambda x: len(x.split("/")))
|
||||
|
||||
scope_name_mapping = {}
|
||||
if (
|
||||
self.memory_config
|
||||
and self.memory_config.slots
|
||||
and self.memory_config.slots.scopes
|
||||
):
|
||||
for name, builder in self.memory_config.slots.scopes.items():
|
||||
sel = builder.resolve(
|
||||
deps=context.deps,
|
||||
prefix="",
|
||||
default_namespace=ns,
|
||||
default_agent=context.run.agent_name,
|
||||
)
|
||||
scope_name_mapping[sel.scope_prefix] = name
|
||||
|
||||
session_meta = SessionMetadata(
|
||||
session_id=context.session_id or "default_session",
|
||||
selector=selector,
|
||||
scope_prefix=selector.scope_prefix,
|
||||
accessible_scopes=accessible_scopes,
|
||||
scope_name_mapping=scope_name_mapping,
|
||||
session_meta = ContextUtils.build_session_meta(
|
||||
context=context,
|
||||
target_builder=scope_builder,
|
||||
extra_scopes=self.scopes,
|
||||
custom_namespace=self._namespace,
|
||||
)
|
||||
return slot_ctx, session_meta
|
||||
|
||||
async def get_tools(self, context: RunContext | None = None) -> dict[str, BaseTool]:
|
||||
tools = await super().get_tools(context)
|
||||
|
||||
if (
|
||||
not self.memory_config
|
||||
or not self.memory_config.slots
|
||||
or not self.memory_config.slots.enable
|
||||
):
|
||||
if not self.scopes:
|
||||
return tools
|
||||
|
||||
scopes_dict = self.memory_config.slots.scopes
|
||||
if not scopes_dict:
|
||||
return tools
|
||||
scope_keys = tuple(scopes_dict.keys())
|
||||
scope_keys = tuple(self.scopes.keys())
|
||||
|
||||
if len(scope_keys) > 1:
|
||||
ScopeType = Literal[scope_keys]
|
||||
@@ -237,12 +196,7 @@ class MemorySlotToolkit(BaseToolkit):
|
||||
res = ["已创建的记忆槽列表:"]
|
||||
|
||||
show_scope = False
|
||||
if (
|
||||
self.memory_config
|
||||
and self.memory_config.slots
|
||||
and self.memory_config.slots.scopes
|
||||
and len(self.memory_config.slots.scopes) > 1
|
||||
):
|
||||
if len(self.scopes) > 1:
|
||||
show_scope = True
|
||||
|
||||
for s in slots:
|
||||
|
||||
@@ -295,6 +295,9 @@ class SkillMetaToolkit(BaseToolkit, SkillSandboxExecutionMixin):
|
||||
if sandbox is None and context is not None:
|
||||
sandbox = Inject._providers["sandbox"]["global"](context)
|
||||
|
||||
if sandbox is None:
|
||||
return ToolResult(output="❌ 缺少沙箱环境或执行上下文").as_error()
|
||||
|
||||
executor = await sandbox.get_or_create_session(session_id, blueprint=bp)
|
||||
fs_executor = cast(SupportsFileSystem, executor)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user