♻️ refactor(tools): 重构工具装饰器系统并优化沙箱与文档注释 (#2147)

* ♻️ refactor(ai): 重构 AI 服务模块并完善文档注释

- 【重构】统一清理并优化所有 AI 服务模块文件的导入语句,将其移至文件顶部
- 【重构】重构 `hooks.py` 中的 `Hooks` 派发逻辑,使用通用管道函数消除重复代码,并引入 `HookPoint` 描述符
- 【重构】重构工具装饰器实现,新增 `toolkit` 类装饰器,优化 `BaseToolkit` 配置合并与前缀处理
- 【功能】Docker 沙箱容器创建时支持自动注入系统代理环境变量并配置 `ExtraHosts`
- 【功能】Jupyter 服务启动前自动清理旧进程并初始化临时目录权限
- 【修复】优化 Pydantic 结构化输出校验失败时的错误信息提取,提供更详细的字段级错误反馈
- 【修复】在 `api.py` 中避免将 `ModelRetry` 和 `ControlFlowExit` 异常错误地包装为 `LLMException`
- 【文档】为 AI 服务、沙箱、工具链、工作流等核心模块补充完整的 Docstring 和类型注释

* 📝 docs(ai): 补全核心模块文档注释并清理冗余代码

- 补全 `run/context`、`run/hooks` 和 `tools/engine/registry` 中类与方法的中文文档注释
- 清理 `tools/providers/builtin/sandbox` 中未使用的 `PythonPluginProtocol` 协议及相关导入
- 规范化部分代码的格式与尾随逗号

* ♻️ refactor!(flow): 重构 Task 为 AgentTask 并优化工作流元数据定义

- 【Breaking Change】将 `Task` 重命名为 `AgentTask` 以避免命名冲突
- 更新 Agent、Team、Workflow 等模块中的类型声明与相关逻辑
- 引入 `AutoNodeMeta` 强类型元数据,替换工作流装饰器中的裸字典定义
- 将 `StepMeta`、`ConditionMeta` 和 `RouterMeta` 统一移动至 `types.py`
- 优化 `RunnableNode` 对上游 `AgentTask` 的处理与拼接逻辑
- 调整团队协作策略中 `FinishAction` 的返回值为完整结果对象

* ♻️ refactor(workflow): 移除人工确认机制并重构错误策略

- 移除工作流节点的人工确认(HITL)与挂起继续机制
- 删除 `auto` 自动化工作流及相关装饰器文件
- 将错误处理策略类从 `types.py` 拆分并移动到新文件 `policies.py`
- 优化节点执行失败时的异常信息格式化输出
- 移除 `WorkflowRunResult` 和 `StepOutput` 中与挂起相关的状态字段

* 🚨 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:
Rumio
2026-07-05 11:47:21 +08:00
committed by GitHub
co-authored by webjoin111 pre-commit-ci[bot]
parent 80fc5b86a7
commit 0b32d69c9c
61 changed files with 1659 additions and 1511 deletions
+2 -1
View File
@@ -1,4 +1,4 @@
from .decorators import Rules, tool
from .decorators import Rules, tool, toolkit
from .schema import (
FieldPermission,
RequireAdminLevel,
@@ -20,4 +20,5 @@ __all__ = [
"RequireSuperUser",
"Rules",
"tool",
"toolkit",
]
+9 -15
View File
@@ -1,16 +1,24 @@
import asyncio
from collections.abc import Callable
import json
from typing import Any
from aiocache import SimpleMemoryCache
from zhenxun.configs.config import Config
from zhenxun.services.ai.capabilities import (
AbstractCapability,
WrapToolExecuteHandler,
WrapToolValidateHandler,
)
from zhenxun.services.ai.run import RunContext
from zhenxun.services.ai.core.exceptions import (
NeedsInputException,
ToolFatalError,
ToolRetryError,
)
from zhenxun.services.ai.run.context import RunContext
from zhenxun.services.ai.run.di import DependencyInjector
from zhenxun.services.ai.run.hitl import HITLController
from zhenxun.services.ai.tools.models import ToolResult
from zhenxun.services.ai.utils import PermissionUtils
from zhenxun.services.log import logger
@@ -99,8 +107,6 @@ class ApprovalCapability(AbstractCapability):
if tool is None:
return await handler(arguments)
import json
args_str = json.dumps(arguments, ensure_ascii=False, indent=2)
confirm_msg = f"即将在本地执行高危工具 [{tool_name}]\n参数:\n{args_str}"
@@ -110,8 +116,6 @@ class ApprovalCapability(AbstractCapability):
await hitl_lock.acquire()
try:
from zhenxun.services.ai.run.hitl import HITLController
hitl = HITLController(context)
await hitl.ask_confirm(f"⚠️ **安全交互审批**\n\n{confirm_msg}", timeout=60.0)
logger.info(f"🛡️ [HITL] 工具 {tool_name} 审批通过。")
@@ -275,8 +279,6 @@ class ConfigDependencyCapability(AbstractCapability):
async def prepare_tools(
self, context: RunContext, tool_defs: list[Any]
) -> list[Any]:
from zhenxun.configs.config import Config
if Config.get_config(self.module, self.key) == self.expected_value:
return tool_defs
return []
@@ -292,12 +294,6 @@ class InteractiveCapability(AbstractCapability):
args: str | dict[str, Any],
handler: WrapToolValidateHandler,
) -> dict[str, Any]:
from zhenxun.services.ai.core.exceptions import (
NeedsInputException,
ToolFatalError,
ToolRetryError,
)
tool = context.call.current_tool
current_kwargs = dict(args) if isinstance(args, dict) else args
@@ -322,8 +318,6 @@ class InteractiveCapability(AbstractCapability):
"请发送文本补充,或回复“取消”中止。"
)
try:
from zhenxun.services.ai.run.hitl import HITLController
hitl = HITLController(context)
user_input = await hitl.ask_text(prompt_msg, timeout=60.0)
except ToolFatalError:
+140 -162
View File
@@ -4,53 +4,99 @@ from collections.abc import Callable
from typing import Any
from zhenxun.services.ai.capabilities import AbstractCapability
from zhenxun.services.ai.run import RunContext
from zhenxun.services.ai.run.context import RunContext
from zhenxun.services.ai.tools.core.capabilities import (
AdminLevelCapability,
ApprovalCapability,
CacheCapability,
FallbackCapability,
GroupOnlyCapability,
InteractiveCapability,
LifecycleCapability,
SuperuserCapability,
)
from zhenxun.services.ai.tools.models import ToolOptions
from zhenxun.utils.pydantic_compat import model_copy
def _update_settings(func: Callable, **kwargs) -> Callable:
"""叠加协议底层:创建或更新函数的 __tool_settings__"""
if not hasattr(func, "__tool_settings__"):
setattr(func, "__tool_settings__", ToolOptions())
settings: ToolOptions = getattr(func, "__tool_settings__")
for k, v in kwargs.items():
if k == "capabilities":
settings.capabilities.extend(v)
elif k == "metadata":
settings.metadata.update(v)
def toolkit(
rules: list[ToolOptions] | ToolOptions | None = None,
prefix: str = "",
instructions: str | None = None,
):
"""
类级别的工具箱装饰器,用于向内部所有 @tool 统一下发配置规则、名称前缀以及工具箱级系统提示词说明。
参数:
rules: 声明式规则集合或单个规则,应用于工具箱内所有工具。
prefix: 工具箱内所有工具的名称前缀,通常以下划线结尾。
instructions: 工具箱级别的系统提示词补充说明,大模型可见。
返回:
Callable: 装饰器函数,接收一个类并返回该类。
""" # noqa: E501
def decorator(cls):
merged_options = ToolOptions()
if rules:
rule_list = rules if isinstance(rules, list) else [rules]
for r in rule_list:
if isinstance(r, ToolOptions):
merged_options = merged_options.merge(r)
from zhenxun.services.ai.tools.models import ToolkitConfig
base_config = getattr(cls, "_default_config", None) or ToolkitConfig()
new_config = model_copy(base_config, deep=True)
if prefix:
new_config.prefix = prefix
if new_config.shared_options:
new_config.shared_options = new_config.shared_options.merge(merged_options)
else:
setattr(settings, k, v)
return func
new_config.shared_options = merged_options
cls._default_config = new_config
if instructions is not None:
cls.default_instructions = instructions
return cls
return decorator
def require_sandbox(
python_packages: list[str] | None = None,
node_packages: list[str] | None = None,
system_packages: list[str] | None = None,
):
) -> ToolOptions:
"""显式声明该工具所需安装的沙箱环境依赖"""
def decorator(func):
setattr(
func,
"__sandbox_requirements__",
{
"python": python_packages or [],
"node": node_packages or [],
"system": system_packages or [],
},
)
return func
return decorator
return ToolOptions(
sandbox_requirements={
"python": python_packages or [],
"node": node_packages or [],
"system": system_packages or [],
}
)
class ToolkitMethodDescriptor:
"""用于 Toolkit 类方法的描述符,确保在实例化时绑定正确的 self 并生成 FunctionTool"""
def __init__(
self, func: Callable, name: str, description: str, settings: ToolOptions
self, func: Callable, name: str, description: str | None, settings: ToolOptions
):
"""
初始化 Toolkit 类方法描述符。
参数:
func: 底层的 Python 可执行函数对象。
name: 大模型识别与调用的工具名。
description: 大模型阅读的工具功能描述说明。
settings: 工具的声明式高阶配置项 (ToolOptions)。
"""
self.func = func
self.name = name
self.description = description
@@ -64,7 +110,6 @@ class ToolkitMethodDescriptor:
import types
from zhenxun.services.ai.tools.core.tool import FunctionTool
from zhenxun.utils.pydantic_compat import model_copy
bound_func = types.MethodType(self.func, instance)
return FunctionTool(
@@ -82,6 +127,7 @@ class ToolkitMethodDescriptor:
def tool(
name: str | None = None,
description: str | None = None,
rules: list[ToolOptions] | ToolOptions | None = None,
settings: ToolOptions | None = None,
tags: list[str] | None = None,
auto_register: bool = False,
@@ -91,23 +137,26 @@ def tool(
将普通函数或类方法注册为 LLM 工具的统一装饰器大一统。
参数:
name: 工具的名称(英文字母及下划线)。
大模型将看到此名称。如果为空则默认使用函数名。
description: 工具描述。
大模型将基于此决定何时、如何使用该工具。如果为空,将读取函数 docstring。
settings: 工具的高阶配置对象 (ToolOptions)。
用于控制极速缓存、人工审批拦截、静默执行等扩展能力。
tags: 工具的标签列表。
用于被 Agent 的智能字符串路由识别并进行能力注入。
auto_register: 是否自动注册到当前命名空间的工具注册表。
默认为 False,开发者需要显式传递实例。若为 True 方可通过字符串调用。
require_prefix: 是否自动添加插件命名空间前缀(仅对游离函数生效)。默认为 False。
name: 工具的名称(英文字母及下划线),大模型将看到此名称,如果为空则默认使用函数名。
description: 工具描述,大模型将基于此决定何时、如何使用该工具,如果为空,将读取函数 docstring。
rules: 声明式规则集合预设列表,用于接收通过 Rules.xxx() 生成的策略载荷并组合。
settings: 工具的高阶配置对象 (ToolOptions),用于控制极速缓存、人工审批拦截、静默执行等扩展能力。
tags: 工具的标签列表,用于被 Agent 的智能字符串路由识别并进行能力注入。
auto_register: 是否自动注册到当前命名空间的工具注册表,默认为 False,若为 True 方可通过字符串调用。
require_prefix: 是否自动添加插件命名空间前缀(仅对游离函数生效),默认为 False。
返回:
Callable | FunctionTool: 包装后的函数或方法。
"""
""" # noqa: E501
def decorator(func: Callable):
base_settings = settings or ToolOptions()
if rules:
rule_list = rules if isinstance(rules, list) else [rules]
for r in rule_list:
if isinstance(r, ToolOptions):
base_settings = base_settings.merge(r)
if tags:
base_settings.tags = list(set(base_settings.tags + tags))
existing_settings = getattr(func, "__tool_settings__", None)
@@ -116,10 +165,13 @@ def tool(
reqs = getattr(func, "__sandbox_requirements__", None)
if reqs:
base_settings.sandbox_requirements = reqs
if not base_settings.sandbox_requirements:
base_settings.sandbox_requirements = reqs
else:
base_settings.sandbox_requirements.update(reqs)
tool_name = name or func.__name__
tool_desc = description or func.__doc__ or "未提供描述"
tool_desc = description
import inspect
@@ -172,30 +224,19 @@ def tool(
return decorator
def with_cache(ttl: int = 3600, cache_function: Callable | None = None):
def with_cache(ttl: int = 3600, cache_function: Callable | None = None) -> ToolOptions:
"""开启极速缓存,阻止参数相同的重复请求发往底层。"""
from zhenxun.services.ai.tools.core.capabilities import CacheCapability
def decorator(func: Callable):
return _update_settings(
func,
capabilities=[CacheCapability(ttl=ttl, cache_function=cache_function)],
)
return decorator
return ToolOptions(
capabilities=[CacheCapability(ttl=ttl, cache_function=cache_function)]
)
def silent():
def silent() -> ToolOptions:
"""静默执行。工具执行过程与结果不会作为界面流渲染给用户,仅作大模型内部参考。"""
def decorator(func: Callable):
return _update_settings(func, silent=True)
return decorator
return ToolOptions(silent=True)
def direct_reply():
def direct_reply() -> ToolOptions:
"""直出模式。工具执行完毕后强制中断大模型的思考循环,将工具输出作为最终回答返回。"""
class DirectReplyCapability(AbstractCapability):
@@ -209,139 +250,70 @@ def direct_reply():
return EndRunResult(output=result.output)
return EndRunResult(output=result)
def decorator(func: Callable):
return _update_settings(func, capabilities=[DirectReplyCapability()])
return decorator
return ToolOptions(capabilities=[DirectReplyCapability()])
def interactive():
def interactive() -> ToolOptions:
"""开启交互式参数补全。如果参数缺失或校验失败,
会主动通过 Bot 向用户提问要求补全。"""
from zhenxun.services.ai.tools.core.capabilities import InteractiveCapability
def decorator(func: Callable):
return _update_settings(func, capabilities=[InteractiveCapability()])
return decorator
return ToolOptions(capabilities=[InteractiveCapability()])
def require_approval():
def require_approval() -> ToolOptions:
"""高危操作标记。调用前将拦截并发送至群组要求超管人工审核。"""
from zhenxun.services.ai.tools.core.capabilities import ApprovalCapability
def decorator(func: Callable):
return _update_settings(func, capabilities=[ApprovalCapability()])
return decorator
return ToolOptions(capabilities=[ApprovalCapability()])
def fallback(tool_name: str):
def fallback(tool_name: str) -> ToolOptions:
"""降级备用工具。主工具执行失败时自动路由至备用工具。"""
from zhenxun.services.ai.tools.core.capabilities import FallbackCapability
def decorator(func: Callable):
return _update_settings(
func,
capabilities=[FallbackCapability(fallback_tool_name=tool_name)],
)
return decorator
return ToolOptions(capabilities=[FallbackCapability(fallback_tool_name=tool_name)])
def before_execute(hook_func: Callable):
def before_execute(hook_func: Callable) -> ToolOptions:
"""生命周期拦截:在工具即将执行前触发,可在此篡改全局状态或通过依赖注入访问当前参数。"""
from zhenxun.services.ai.tools.core.capabilities import LifecycleCapability
def decorator(func: Callable):
return _update_settings(
func, capabilities=[LifecycleCapability(before_execute=hook_func)]
)
return decorator
return ToolOptions(capabilities=[LifecycleCapability(before_execute=hook_func)])
def after_execute(hook_func: Callable):
def after_execute(hook_func: Callable) -> ToolOptions:
"""生命周期拦截:在工具执行完毕后触发,可在此修改将发往大模型的返回结果。"""
from zhenxun.services.ai.tools.core.capabilities import LifecycleCapability
def decorator(func: Callable):
return _update_settings(
func, capabilities=[LifecycleCapability(after_execute=hook_func)]
)
return decorator
return ToolOptions(capabilities=[LifecycleCapability(after_execute=hook_func)])
def validate_args(hook_func: Callable):
def validate_args(hook_func: Callable) -> ToolOptions:
"""生命周期拦截:在工具反序列化校验前触发。如果校验失败直接抛出异常,将触发大模型重试机制。"""
from zhenxun.services.ai.tools.core.capabilities import LifecycleCapability
def decorator(func: Callable):
return _update_settings(
func, capabilities=[LifecycleCapability(validate_args=hook_func)]
)
return decorator
return ToolOptions(capabilities=[LifecycleCapability(validate_args=hook_func)])
def prepare_tool(hook_func: Callable):
def prepare_tool(hook_func: Callable) -> ToolOptions:
"""生命周期拦截:在向大模型渲染 JSON Schema 前触发,
可在此动态修改该工具的定义或返回 None 对大模型隐藏。"""
from zhenxun.services.ai.tools.core.capabilities import LifecycleCapability
def decorator(func: Callable):
return _update_settings(
func, capabilities=[LifecycleCapability(prepare_tool=hook_func)]
)
return decorator
return ToolOptions(capabilities=[LifecycleCapability(prepare_tool=hook_func)])
def require_superuser():
def require_superuser() -> ToolOptions:
"""仅限超级管理员使用。若调用者无权限,该工具将直接对大模型隐藏。"""
from zhenxun.services.ai.tools.core.capabilities import SuperuserCapability
def decorator(func: Callable):
return _update_settings(func, capabilities=[SuperuserCapability()])
return decorator
return ToolOptions(capabilities=[SuperuserCapability()])
def require_admin_level(min_level: int = 1):
def require_admin_level(min_level: int = 1) -> ToolOptions:
"""仅限满足指定群聊权限等级的用户使用。"""
from zhenxun.services.ai.tools.core.capabilities import AdminLevelCapability
def decorator(func: Callable):
return _update_settings(
func,
capabilities=[AdminLevelCapability(min_level)],
metadata={"admin_level": min_level},
)
return decorator
return ToolOptions(
capabilities=[AdminLevelCapability(min_level)],
metadata={"admin_level": min_level},
)
def require_group():
def require_group() -> ToolOptions:
"""限制该工具仅能在群聊环境中被大模型调用。"""
from zhenxun.services.ai.tools.core.capabilities import GroupOnlyCapability
def decorator(func: Callable):
return _update_settings(func, capabilities=[GroupOnlyCapability()])
return decorator
return ToolOptions(capabilities=[GroupOnlyCapability()])
def require_minimum_gold(amount: int):
def require_minimum_gold(amount: int) -> ToolOptions:
"""需满足一定的金币余额才可调用,同时执行后会自动扣除该金币。"""
def decorator(func: Callable):
return _update_settings(func, metadata={"cost_gold": amount})
return decorator
return ToolOptions(metadata={"cost_gold": amount})
def require_session_state(key: str, expected_value: Any = None):
def require_session_state(key: str, expected_value: Any = None) -> ToolOptions:
"""需要当前执行上下文中包含指定状态变量,否则隐藏工具"""
class StateDependencyCapability(AbstractCapability):
@@ -356,10 +328,7 @@ def require_session_state(key: str, expected_value: Any = None):
return tool_defs
return []
def decorator(func: Callable):
return _update_settings(func, capabilities=[StateDependencyCapability()])
return decorator
return ToolOptions(capabilities=[StateDependencyCapability()])
class Rules:
@@ -368,6 +337,15 @@ class Rules:
用于控制工具的极速缓存、沙箱要求、前端静默以及人机交互审批等。
"""
@staticmethod
def combine(*rules: ToolOptions) -> ToolOptions:
"""打包组合多个规则预设载荷"""
base = ToolOptions()
for r in rules:
if isinstance(r, ToolOptions):
base = base.merge(r)
return base
sandbox = staticmethod(require_sandbox)
"""环境:声明执行该工具所需的物理沙箱及 pip/npm 包依赖"""
+6 -15
View File
@@ -1,4 +1,5 @@
from collections.abc import Callable
import copy
import hashlib
import inspect
import json
@@ -6,18 +7,22 @@ from typing import Any, Literal
from pydantic import BaseModel, ValidationError
from zhenxun.services.ai.capabilities import CombinedCapability
from zhenxun.services.ai.core.exceptions import (
ControlFlowExit,
NeedsInputException,
ToolFatalError,
ToolRetryError,
)
from zhenxun.services.ai.core.models import ToolDefinition
from zhenxun.services.ai.run import RunContext
from zhenxun.services.ai.run.context import RunContext
from zhenxun.services.ai.tools.core.capabilities import InteractiveCapability
from zhenxun.services.ai.tools.models import (
ResolvedToolPayload,
ToolOptions,
ToolResult,
)
from zhenxun.services.ai.utils.utils import wrap_to_async
from zhenxun.services.log import logger
from zhenxun.utils.pydantic_compat import model_dump, model_json_schema, model_validate
@@ -112,8 +117,6 @@ class BaseTool:
def clone_with_options(self, override: Any) -> "BaseTool":
"""创建当前工具的浅拷贝,并覆盖 ToolOptions 和基本属性"""
import copy
new_tool = copy.copy(self)
if hasattr(override, "to_tool_options"):
@@ -154,8 +157,6 @@ class BaseTool:
)
schema_hint = build_schema_hint(validation_model)
from zhenxun.services.ai.tools.core.capabilities import InteractiveCapability
if any(
isinstance(c, InteractiveCapability) for c in self.settings.capabilities
):
@@ -225,10 +226,6 @@ class BaseTool:
)
if context and self.settings.capabilities:
from zhenxun.services.ai.capabilities import (
CombinedCapability,
)
combined_cap = CombinedCapability(self.settings.capabilities)
defs = await combined_cap.prepare_tools(context, [tool_def])
if not defs:
@@ -239,8 +236,6 @@ class BaseTool:
async def resolve(self, context: RunContext | None = None) -> ResolvedToolPayload:
"""实现 ToolResolvable 协议,将自身解析为标准 Payload"""
import copy
definition = await self.get_definition(context)
if definition is None:
return ResolvedToolPayload()
@@ -288,8 +283,6 @@ class BaseTool:
try:
return await self._core_execution(context_to_pass, **kwargs)
except Exception as e:
from zhenxun.services.ai.core.exceptions import ControlFlowExit
if not isinstance(e, ControlFlowExit):
logger.error(f"工具 {self.name} 执行抛出异常,将交由底层引擎处理: {e}")
raise
@@ -381,8 +374,6 @@ class FunctionTool(BaseTool):
super().__init__(name=name, description=description, settings=settings)
self._original_func = func
from zhenxun.services.ai.utils.utils import wrap_to_async
self.__name__ = self.name
self._func = wrap_to_async(func)
self._schema_built = False
+24 -10
View File
@@ -1,9 +1,10 @@
from collections.abc import Callable
import copy
import inspect
from typing import Any, ClassVar
from zhenxun.services.ai.core.protocols.tool import ToolExecutable
from zhenxun.services.ai.run import RunContext
from zhenxun.services.ai.run.context import RunContext
from zhenxun.services.ai.run.di import DependencyInjector
from zhenxun.services.ai.tools.models import (
ResolvedToolPayload,
@@ -51,19 +52,27 @@ class BaseToolkit:
tools: 除了带有 @tool 装饰器的方法外,要额外动态注入的独立工具列表。
instructions: 当前工具箱实例的系统提示词补充说明。
"""
base_config = getattr(self.__class__, "_default_config", ToolkitConfig())
if config is not None:
merged_dict = {
**model_dump(self._default_config, exclude_unset=True),
**model_dump(base_config, exclude_unset=True),
**model_dump(config, exclude_unset=True),
}
self.config = ToolkitConfig(**merged_dict)
else:
self.config = ToolkitConfig(
prefix=prefix,
include=include,
exclude=exclude,
shared_options=shared_options,
)
overrides = {}
if prefix is not None:
overrides["prefix"] = prefix
if include is not None:
overrides["include"] = include
if exclude is not None:
overrides["exclude"] = exclude
if shared_options is not None:
overrides["shared_options"] = shared_options
merged_dict = {**model_dump(base_config, exclude_unset=True), **overrides}
self.config = ToolkitConfig(**merged_dict)
if self.config.prefix is None:
self.config.prefix = ""
@@ -101,6 +110,7 @@ class BaseToolkit:
def _apply_global_settings(
self, original_name: str, settings: ToolOptions
) -> ToolOptions:
"""合并工具箱全局配置到单个工具的配置项中"""
if self.config.shared_options:
settings = self.config.shared_options.merge(settings)
@@ -143,16 +153,18 @@ class BaseToolkit:
)
async def get_tools(self, context: RunContext | None = None) -> dict[str, BaseTool]:
"""获取该工具箱中所有合法注册的工具实例映射表"""
if self._cached_tools is not None:
return self._cached_tools
tools_dict: dict[str, BaseTool] = {}
for t in self._injected_tools:
t.parent_toolkit = self
if self.config.prefix and not t.name.startswith(self.config.prefix):
t.name = f"{self.config.prefix}{t.name}"
t.settings = self._apply_global_settings(t.name, t.settings)
tools_dict[t.name] = t
import inspect
for name, member in inspect.getmembers(self.__class__):
if hasattr(member, "__toolkit_tool__"):
bound_tool = getattr(self, name)
@@ -201,6 +213,7 @@ class BaseToolkit:
return f"<{tag_name}>\n{text}\n</{tag_name}>"
def prefixed(self, prefix: str) -> "BaseToolkit":
"""克隆工具箱并为其中所有工具追加统一的前缀"""
new_tk = copy.copy(self)
new_tk.config = model_copy(self.config, deep=True)
current_prefix = new_tk.config.prefix or ""
@@ -209,6 +222,7 @@ class BaseToolkit:
return new_tk
def filtered(self, filter_func: Callable[[BaseTool], bool]) -> "BaseToolkit":
"""克隆工具箱并通过自定义过滤器筛选其中的工具"""
new_tk = copy.copy(self)
new_tk.config = model_copy(self.config, deep=True)
old_filter = self._instance_filter