mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-10-21 04:00:04 +08:00
♻️ 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:
co-authored by
webjoin111
pre-commit-ci[bot]
parent
80fc5b86a7
commit
0b32d69c9c
@@ -12,8 +12,8 @@ from .hitl import HITLController
|
||||
from .hooks import Hooks
|
||||
from .models import (
|
||||
AgentRunResult,
|
||||
AgentTask,
|
||||
StreamedRunResult,
|
||||
Task,
|
||||
)
|
||||
from .session import session_manager
|
||||
from .ui import UIController
|
||||
@@ -21,6 +21,7 @@ from .ui import UIController
|
||||
__all__ = [
|
||||
"GLOBAL_CAPABILITIES",
|
||||
"AgentRunResult",
|
||||
"AgentTask",
|
||||
"BlackboardManager",
|
||||
"CancellationToken",
|
||||
"HITLController",
|
||||
@@ -30,7 +31,6 @@ __all__ = [
|
||||
"NoneBotDeps",
|
||||
"RunContext",
|
||||
"StreamedRunResult",
|
||||
"Task",
|
||||
"UIController",
|
||||
"get_current_run_context",
|
||||
"register_global_capability",
|
||||
|
||||
@@ -383,6 +383,7 @@ def set_run_context(ctx: RunContext):
|
||||
|
||||
|
||||
def _is_run_context_type(annotation: Any) -> bool:
|
||||
"""判断类型标注是否为 RunContext 类型或其子类。"""
|
||||
if annotation is RunContext:
|
||||
return True
|
||||
origin = get_origin(annotation)
|
||||
|
||||
+305
-359
@@ -1,6 +1,6 @@
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Generic, Protocol, TypeVar
|
||||
from typing import Any, Generic, Protocol, TypeVar, overload
|
||||
|
||||
import anyio
|
||||
from nonebot.utils import is_coroutine_callable
|
||||
@@ -25,6 +25,7 @@ class HookTimeoutError(TimeoutError):
|
||||
"""当 Hook 函数执行超过配置的时间时抛出此异常。"""
|
||||
|
||||
def __init__(self, hook_name: str, func_name: str, timeout: float):
|
||||
"""初始化 HookTimeoutError 异常实例。"""
|
||||
self.hook_name = hook_name
|
||||
self.func_name = func_name
|
||||
self.timeout = timeout
|
||||
@@ -49,28 +50,38 @@ class _ToolHookEntry(_HookEntry[_FuncT]):
|
||||
|
||||
|
||||
class BeforeRunHookFunc(Protocol):
|
||||
"""运行前钩子:在 Agent 启动任何流转前触发。"""
|
||||
|
||||
def __call__(self, ctx: RunContext[Any], /) -> None | Awaitable[None]: ...
|
||||
|
||||
|
||||
class AfterRunHookFunc(Protocol):
|
||||
"""运行后钩子:在 Agent 获取最终结果后触发,可用于修改最终输出。"""
|
||||
|
||||
def __call__(
|
||||
self, ctx: RunContext[Any], /, *, result: AgentRunResult[Any]
|
||||
) -> AgentRunResult[Any] | Awaitable[AgentRunResult[Any]]: ...
|
||||
|
||||
|
||||
class WrapRunHookFunc(Protocol):
|
||||
"""包裹运行钩子:以洋葱模型接管整个 Agent 运行生命周期。"""
|
||||
|
||||
def __call__(
|
||||
self, ctx: RunContext[Any], /, *, handler: WrapRunHandler
|
||||
) -> AgentRunResult[Any] | Awaitable[AgentRunResult[Any]]: ...
|
||||
|
||||
|
||||
class OnRunErrorHookFunc(Protocol):
|
||||
"""运行异常钩子:捕获 Agent 级别的致命错误。"""
|
||||
|
||||
def __call__(
|
||||
self, ctx: RunContext[Any], /, *, error: BaseException
|
||||
) -> AgentRunResult[Any] | Awaitable[AgentRunResult[Any]]: ...
|
||||
|
||||
|
||||
class BeforeModelRequestHookFunc(Protocol):
|
||||
"""大模型请求前钩子:在发送网络请求前触发,可篡改 Messages 上下文。"""
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
ctx: RunContext[Any],
|
||||
@@ -83,6 +94,8 @@ class BeforeModelRequestHookFunc(Protocol):
|
||||
|
||||
|
||||
class AfterModelRequestHookFunc(Protocol):
|
||||
"""大模型请求后钩子:在接收到网络响应后触发,可验证或篡改原始 Response。"""
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
ctx: RunContext[Any],
|
||||
@@ -94,6 +107,8 @@ class AfterModelRequestHookFunc(Protocol):
|
||||
|
||||
|
||||
class WrapModelRequestHookFunc(Protocol):
|
||||
"""包裹大模型请求钩子:以洋葱模型接管 LLM 网络请求过程(可用于实现缓存)。"""
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
ctx: RunContext[Any],
|
||||
@@ -105,6 +120,8 @@ class WrapModelRequestHookFunc(Protocol):
|
||||
|
||||
|
||||
class OnModelRequestErrorHookFunc(Protocol):
|
||||
"""大模型请求异常钩子:捕获超时或网络等异常,可用于发起重试。"""
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
ctx: RunContext[Any],
|
||||
@@ -116,24 +133,32 @@ class OnModelRequestErrorHookFunc(Protocol):
|
||||
|
||||
|
||||
class PrepareToolsHookFunc(Protocol):
|
||||
"""准备工具集钩子:向大模型渲染 JSON Schema 前触发,可动态增删工具。"""
|
||||
|
||||
def __call__(
|
||||
self, ctx: RunContext[Any], tool_defs: list[Any], /
|
||||
) -> list[Any] | Awaitable[list[Any]]: ...
|
||||
|
||||
|
||||
class BeforeToolValidateHookFunc(Protocol):
|
||||
"""工具验证前钩子:在反序列化前触发,可篡改原始 JSON 字符串或字典。"""
|
||||
|
||||
def __call__(
|
||||
self, ctx: RunContext[Any], /, *, tool_name: str, args: str | dict[str, Any]
|
||||
) -> str | dict[str, Any] | Awaitable[str | dict[str, Any]]: ...
|
||||
|
||||
|
||||
class AfterToolValidateHookFunc(Protocol):
|
||||
"""工具验证后钩子:在 Schema 校验通过后触发,接收并可篡改强类型参数字典。"""
|
||||
|
||||
def __call__(
|
||||
self, ctx: RunContext[Any], /, *, tool_name: str, args: dict[str, Any]
|
||||
) -> dict[str, Any] | Awaitable[dict[str, Any]]: ...
|
||||
|
||||
|
||||
class WrapToolValidateHookFunc(Protocol):
|
||||
"""包裹工具验证钩子:以洋葱模型接管工具参数校验过程(可用于交互式拦截)。"""
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
ctx: RunContext[Any],
|
||||
@@ -146,6 +171,8 @@ class WrapToolValidateHookFunc(Protocol):
|
||||
|
||||
|
||||
class OnToolValidateErrorHookFunc(Protocol):
|
||||
"""工具验证异常钩子:捕获校验失败的异常,可将其转化为大模型自愈提示。"""
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
ctx: RunContext[Any],
|
||||
@@ -158,12 +185,16 @@ class OnToolValidateErrorHookFunc(Protocol):
|
||||
|
||||
|
||||
class BeforeToolExecuteHookFunc(Protocol):
|
||||
"""工具执行前钩子:在工具物理执行前触发,可用于最后的权限判定或参数篡改。"""
|
||||
|
||||
def __call__(
|
||||
self, ctx: RunContext[Any], /, *, tool_name: str, arguments: dict[str, Any]
|
||||
) -> dict[str, Any] | Awaitable[dict[str, Any]]: ...
|
||||
|
||||
|
||||
class AfterToolExecuteHookFunc(Protocol):
|
||||
"""工具执行后钩子:工具执行完毕后触发,可篡改最终发往大模型的返回结果。"""
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
ctx: RunContext[Any],
|
||||
@@ -176,6 +207,8 @@ class AfterToolExecuteHookFunc(Protocol):
|
||||
|
||||
|
||||
class WrapToolExecuteHookFunc(Protocol):
|
||||
"""包裹工具执行钩子:以洋葱模型接管特定工具的物理执行逻辑。"""
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
ctx: RunContext[Any],
|
||||
@@ -188,12 +221,15 @@ class WrapToolExecuteHookFunc(Protocol):
|
||||
|
||||
|
||||
class OnToolExecuteErrorHookFunc(Protocol):
|
||||
"""工具执行异常钩子:捕获工具执行时的崩溃异常,可用于兜底返回或自愈。"""
|
||||
|
||||
def __call__(
|
||||
self, ctx: RunContext[Any], /, *, tool_name: str, error: Exception
|
||||
) -> Any | Awaitable[Any]: ...
|
||||
|
||||
|
||||
async def _call_func(func: Callable[..., Any], *args: Any, **kwargs: Any) -> Any:
|
||||
"""统一执行同步或异步的可调用函数。"""
|
||||
if is_coroutine_callable(func):
|
||||
return await func(*args, **kwargs)
|
||||
return func(*args, **kwargs)
|
||||
@@ -276,224 +312,139 @@ def _tool_bare_or_parameterized(
|
||||
return decorator
|
||||
|
||||
|
||||
class BoundHookPoint(Generic[_FuncT]):
|
||||
"""绑定到具体实例的 Hook 注册点。"""
|
||||
|
||||
def __init__(self, key: str, registry_holder: Any):
|
||||
"""初始化绑定的 Hook 注册点。"""
|
||||
self.key = key
|
||||
self.registry_holder = registry_holder
|
||||
|
||||
@overload
|
||||
def __call__(self, func: _FuncT, /) -> _FuncT: ...
|
||||
|
||||
@overload
|
||||
def __call__(
|
||||
self, *, timeout: float | None = None
|
||||
) -> Callable[[_FuncT], _FuncT]: ...
|
||||
|
||||
def __call__(
|
||||
self, func: _FuncT | None = None, *, timeout: float | None = None
|
||||
) -> Any:
|
||||
"""支持装饰器语法注册钩子函数。"""
|
||||
return _bare_or_parameterized(
|
||||
self.registry_holder._r, self.key, func, timeout=timeout
|
||||
)
|
||||
|
||||
|
||||
class HookPoint(Generic[_FuncT]):
|
||||
"""描述符形式的 Hook 注册点。"""
|
||||
|
||||
def __init__(self, key: str):
|
||||
"""初始化 Hook 描述符。"""
|
||||
self.key = key
|
||||
|
||||
def __get__(self, instance: Any, owner: Any) -> BoundHookPoint[_FuncT]:
|
||||
"""通过描述符协议绑定 Hook 实例。"""
|
||||
return BoundHookPoint(self.key, instance)
|
||||
|
||||
|
||||
class BoundToolHookPoint(Generic[_FuncT]):
|
||||
"""绑定到具体实例的工具级别 Hook 注册点。"""
|
||||
|
||||
def __init__(self, key: str, registry_holder: Any):
|
||||
"""初始化绑定的工具 Hook 注册点。"""
|
||||
self.key = key
|
||||
self.registry_holder = registry_holder
|
||||
|
||||
@overload
|
||||
def __call__(self, func: _FuncT, /) -> _FuncT: ...
|
||||
|
||||
@overload
|
||||
def __call__(
|
||||
self, *, tools: list[str] | None = None, timeout: float | None = None
|
||||
) -> Callable[[_FuncT], _FuncT]: ...
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
func: _FuncT | None = None,
|
||||
*,
|
||||
tools: list[str] | None = None,
|
||||
timeout: float | None = None,
|
||||
) -> Any:
|
||||
"""支持装饰器语法注册工具钩子函数。"""
|
||||
return _tool_bare_or_parameterized(
|
||||
self.registry_holder._r, self.key, func, tools=tools, timeout=timeout
|
||||
)
|
||||
|
||||
|
||||
class ToolHookPoint(Generic[_FuncT]):
|
||||
"""描述符形式的工具级别 Hook 注册点。"""
|
||||
|
||||
def __init__(self, key: str):
|
||||
"""初始化工具 Hook 描述符。"""
|
||||
self.key = key
|
||||
|
||||
def __get__(self, instance: Any, owner: Any) -> BoundToolHookPoint[_FuncT]:
|
||||
"""通过描述符协议绑定工具 Hook 实例。"""
|
||||
return BoundToolHookPoint(self.key, instance)
|
||||
|
||||
|
||||
class _HookRegistration:
|
||||
"""
|
||||
Hooks 的装饰器命名空间。
|
||||
利用 @overload 提供完美的 IDE 强类型补全和文档提示。
|
||||
"""
|
||||
"""Hooks 装饰器注册辅助类,用于各阶段钩子的声明式注册"""
|
||||
|
||||
def __init__(self, hooks: "Hooks"):
|
||||
"""初始化钩子注册辅助实例。"""
|
||||
self._hooks = hooks
|
||||
|
||||
@property
|
||||
def _r(self) -> dict[str, list[_HookEntry[Any]]]:
|
||||
"""获取底层注册的钩子字典。"""
|
||||
return self._hooks._registry
|
||||
|
||||
from typing import overload
|
||||
before_run = HookPoint[BeforeRunHookFunc]("before_run")
|
||||
"""注册运行前钩子。在 Agent 启动任何流转前触发。"""
|
||||
after_run = HookPoint[AfterRunHookFunc]("after_run")
|
||||
"""注册运行后钩子。在 Agent 获取最终结果后触发,可修改结果。"""
|
||||
wrap_run = HookPoint[WrapRunHookFunc]("wrap_run")
|
||||
"""注册运行包裹钩子。以洋葱模型接管整个 Agent 运行过程。"""
|
||||
on_run_error = HookPoint[OnRunErrorHookFunc]("on_run_error")
|
||||
"""注册运行异常钩子。捕获 Agent 级别的致命错误。"""
|
||||
|
||||
@overload
|
||||
def before_run(self, func: BeforeRunHookFunc, /) -> BeforeRunHookFunc: ...
|
||||
@overload
|
||||
def before_run(
|
||||
self, *, timeout: float | None = None
|
||||
) -> Callable[[BeforeRunHookFunc], BeforeRunHookFunc]: ...
|
||||
def before_run(
|
||||
self, func: BeforeRunHookFunc | None = None, *, timeout: float | None = None
|
||||
) -> Any:
|
||||
"""注册运行前钩子。在 Agent 启动任何流转前触发。"""
|
||||
return _bare_or_parameterized(self._r, "before_run", func, timeout=timeout)
|
||||
before_model_request = HookPoint[BeforeModelRequestHookFunc]("before_model_request")
|
||||
"""注册大模型请求前钩子。可在此修改发送给 LLM 的 Messages 等上下文。"""
|
||||
after_model_request = HookPoint[AfterModelRequestHookFunc]("after_model_request")
|
||||
"""注册大模型请求后钩子。可在此验证或修改 LLM 的原始 Response。"""
|
||||
wrap_model_request = HookPoint[WrapModelRequestHookFunc]("wrap_model_request")
|
||||
"""注册大模型请求包裹钩子。以洋葱模型接管 LLM 的网络请求过程。"""
|
||||
on_model_request_error = HookPoint[OnModelRequestErrorHookFunc](
|
||||
"on_model_request_error"
|
||||
)
|
||||
"""注册大模型请求异常钩子。捕获超时或网络等异常。"""
|
||||
|
||||
@overload
|
||||
def after_run(self, func: AfterRunHookFunc, /) -> AfterRunHookFunc: ...
|
||||
@overload
|
||||
def after_run(
|
||||
self, *, timeout: float | None = None
|
||||
) -> Callable[[AfterRunHookFunc], AfterRunHookFunc]: ...
|
||||
def after_run(
|
||||
self, func: AfterRunHookFunc | None = None, *, timeout: float | None = None
|
||||
) -> Any:
|
||||
"""注册运行后钩子。在 Agent 获取最终结果后触发,可修改结果。"""
|
||||
return _bare_or_parameterized(self._r, "after_run", func, timeout=timeout)
|
||||
before_tool_validate = HookPoint[BeforeToolValidateHookFunc]("before_tool_validate")
|
||||
"""注册工具验证前钩子。在工具输入参数校验前触发,可篡改或校验参数。"""
|
||||
after_tool_validate = HookPoint[AfterToolValidateHookFunc]("after_tool_validate")
|
||||
"""注册工具验证后钩子。在工具输入参数校验成功后触发,可篡改最终参数。"""
|
||||
wrap_tool_validate = HookPoint[WrapToolValidateHookFunc]("wrap_tool_validate")
|
||||
"""注册工具验证包裹钩子。以洋葱模型接管工具参数校验过程。"""
|
||||
on_tool_validate_error = HookPoint[OnToolValidateErrorHookFunc](
|
||||
"on_tool_validate_error"
|
||||
)
|
||||
"""注册工具验证异常钩子。捕获并处理校验阶段抛出的异常。"""
|
||||
|
||||
@overload
|
||||
def wrap_run(self, func: WrapRunHookFunc, /) -> WrapRunHookFunc: ...
|
||||
@overload
|
||||
def wrap_run(
|
||||
self, *, timeout: float | None = None
|
||||
) -> Callable[[WrapRunHookFunc], WrapRunHookFunc]: ...
|
||||
def wrap_run(
|
||||
self, func: WrapRunHookFunc | None = None, *, timeout: float | None = None
|
||||
) -> Any:
|
||||
"""注册运行包裹钩子。以洋葱模型接管整个 Agent 运行过程。"""
|
||||
return _bare_or_parameterized(self._r, "wrap_run", func, timeout=timeout)
|
||||
|
||||
@overload
|
||||
def on_run_error(self, func: OnRunErrorHookFunc, /) -> OnRunErrorHookFunc: ...
|
||||
@overload
|
||||
def on_run_error(
|
||||
self, *, timeout: float | None = None
|
||||
) -> Callable[[OnRunErrorHookFunc], OnRunErrorHookFunc]: ...
|
||||
def on_run_error(
|
||||
self, func: OnRunErrorHookFunc | None = None, *, timeout: float | None = None
|
||||
) -> Any:
|
||||
"""注册运行异常钩子。捕获 Agent 级别的致命错误。"""
|
||||
return _bare_or_parameterized(self._r, "on_run_error", func, timeout=timeout)
|
||||
|
||||
@overload
|
||||
def before_model_request(
|
||||
self, func: BeforeModelRequestHookFunc, /
|
||||
) -> BeforeModelRequestHookFunc: ...
|
||||
@overload
|
||||
def before_model_request(
|
||||
self, *, timeout: float | None = None
|
||||
) -> Callable[[BeforeModelRequestHookFunc], BeforeModelRequestHookFunc]: ...
|
||||
def before_model_request(
|
||||
self,
|
||||
func: BeforeModelRequestHookFunc | None = None,
|
||||
*,
|
||||
timeout: float | None = None,
|
||||
) -> Any:
|
||||
"""注册大模型请求前钩子。可在此修改发送给 LLM 的 Messages 等上下文。"""
|
||||
return _bare_or_parameterized(
|
||||
self._r, "before_model_request", func, timeout=timeout
|
||||
)
|
||||
|
||||
@overload
|
||||
def after_model_request(
|
||||
self, func: AfterModelRequestHookFunc, /
|
||||
) -> AfterModelRequestHookFunc: ...
|
||||
@overload
|
||||
def after_model_request(
|
||||
self, *, timeout: float | None = None
|
||||
) -> Callable[[AfterModelRequestHookFunc], AfterModelRequestHookFunc]: ...
|
||||
def after_model_request(
|
||||
self,
|
||||
func: AfterModelRequestHookFunc | None = None,
|
||||
*,
|
||||
timeout: float | None = None,
|
||||
) -> Any:
|
||||
"""注册大模型请求后钩子。可在此验证或修改 LLM 的原始 Response。"""
|
||||
return _bare_or_parameterized(
|
||||
self._r, "after_model_request", func, timeout=timeout
|
||||
)
|
||||
|
||||
@overload
|
||||
def wrap_model_request(
|
||||
self, func: WrapModelRequestHookFunc, /
|
||||
) -> WrapModelRequestHookFunc: ...
|
||||
@overload
|
||||
def wrap_model_request(
|
||||
self, *, timeout: float | None = None
|
||||
) -> Callable[[WrapModelRequestHookFunc], WrapModelRequestHookFunc]: ...
|
||||
def wrap_model_request(
|
||||
self,
|
||||
func: WrapModelRequestHookFunc | None = None,
|
||||
*,
|
||||
timeout: float | None = None,
|
||||
) -> Any:
|
||||
"""注册大模型请求包裹钩子。以洋葱模型接管 LLM 的网络请求过程。"""
|
||||
return _bare_or_parameterized(
|
||||
self._r, "wrap_model_request", func, timeout=timeout
|
||||
)
|
||||
|
||||
@overload
|
||||
def on_model_request_error(
|
||||
self, func: OnModelRequestErrorHookFunc, /
|
||||
) -> OnModelRequestErrorHookFunc: ...
|
||||
@overload
|
||||
def on_model_request_error(
|
||||
self, *, timeout: float | None = None
|
||||
) -> Callable[[OnModelRequestErrorHookFunc], OnModelRequestErrorHookFunc]: ...
|
||||
def on_model_request_error(
|
||||
self,
|
||||
func: OnModelRequestErrorHookFunc | None = None,
|
||||
*,
|
||||
timeout: float | None = None,
|
||||
) -> Any:
|
||||
"""注册大模型请求异常钩子。捕获超时或网络等异常。"""
|
||||
return _bare_or_parameterized(
|
||||
self._r, "on_model_request_error", func, timeout=timeout
|
||||
)
|
||||
|
||||
@overload
|
||||
def before_tool_execute(
|
||||
self, func: BeforeToolExecuteHookFunc, /
|
||||
) -> BeforeToolExecuteHookFunc: ...
|
||||
@overload
|
||||
def before_tool_execute(
|
||||
self, *, tools: list[str] | None = None, timeout: float | None = None
|
||||
) -> Callable[[BeforeToolExecuteHookFunc], BeforeToolExecuteHookFunc]: ...
|
||||
def before_tool_execute(
|
||||
self,
|
||||
func: BeforeToolExecuteHookFunc | None = None,
|
||||
*,
|
||||
tools: list[str] | None = None,
|
||||
timeout: float | None = None,
|
||||
) -> Any:
|
||||
"""注册工具执行前钩子。可通过 tools 参数指定拦截特定工具,可篡改传入参数。"""
|
||||
return _tool_bare_or_parameterized(
|
||||
self._r, "before_tool_execute", func, tools=tools, timeout=timeout
|
||||
)
|
||||
|
||||
@overload
|
||||
def after_tool_execute(
|
||||
self, func: AfterToolExecuteHookFunc, /
|
||||
) -> AfterToolExecuteHookFunc: ...
|
||||
@overload
|
||||
def after_tool_execute(
|
||||
self, *, tools: list[str] | None = None, timeout: float | None = None
|
||||
) -> Callable[[AfterToolExecuteHookFunc], AfterToolExecuteHookFunc]: ...
|
||||
def after_tool_execute(
|
||||
self,
|
||||
func: AfterToolExecuteHookFunc | None = None,
|
||||
*,
|
||||
tools: list[str] | None = None,
|
||||
timeout: float | None = None,
|
||||
) -> Any:
|
||||
"""注册工具执行后钩子。可通过 tools 参数指定拦截特定工具,可篡改返回结果。"""
|
||||
return _tool_bare_or_parameterized(
|
||||
self._r, "after_tool_execute", func, tools=tools, timeout=timeout
|
||||
)
|
||||
|
||||
@overload
|
||||
def wrap_tool_execute(
|
||||
self, func: WrapToolExecuteHookFunc, /
|
||||
) -> WrapToolExecuteHookFunc: ...
|
||||
@overload
|
||||
def wrap_tool_execute(
|
||||
self, *, tools: list[str] | None = None, timeout: float | None = None
|
||||
) -> Callable[[WrapToolExecuteHookFunc], WrapToolExecuteHookFunc]: ...
|
||||
def wrap_tool_execute(
|
||||
self,
|
||||
func: WrapToolExecuteHookFunc | None = None,
|
||||
*,
|
||||
tools: list[str] | None = None,
|
||||
timeout: float | None = None,
|
||||
) -> Any:
|
||||
"""注册工具执行包裹钩子。以洋葱模型接管特定工具的执行逻辑。"""
|
||||
return _tool_bare_or_parameterized(
|
||||
self._r, "wrap_tool_execute", func, tools=tools, timeout=timeout
|
||||
)
|
||||
|
||||
@overload
|
||||
def on_tool_execute_error(
|
||||
self, func: OnToolExecuteErrorHookFunc, /
|
||||
) -> OnToolExecuteErrorHookFunc: ...
|
||||
@overload
|
||||
def on_tool_execute_error(
|
||||
self, *, tools: list[str] | None = None, timeout: float | None = None
|
||||
) -> Callable[[OnToolExecuteErrorHookFunc], OnToolExecuteErrorHookFunc]: ...
|
||||
def on_tool_execute_error(
|
||||
self,
|
||||
func: OnToolExecuteErrorHookFunc | None = None,
|
||||
*,
|
||||
tools: list[str] | None = None,
|
||||
timeout: float | None = None,
|
||||
) -> Any:
|
||||
"""注册工具执行异常钩子。捕获特定工具的崩溃异常,可用于自愈重试。"""
|
||||
return _tool_bare_or_parameterized(
|
||||
self._r, "on_tool_execute_error", func, tools=tools, timeout=timeout
|
||||
)
|
||||
before_tool_execute = ToolHookPoint[BeforeToolExecuteHookFunc](
|
||||
"before_tool_execute"
|
||||
)
|
||||
"""注册工具执行前钩子。可通过 tools 参数指定拦截特定工具,可篡改传入参数。"""
|
||||
after_tool_execute = ToolHookPoint[AfterToolExecuteHookFunc]("after_tool_execute")
|
||||
"""注册工具执行后钩子。可通过 tools 参数指定拦截特定工具,可篡改返回结果。"""
|
||||
wrap_tool_execute = ToolHookPoint[WrapToolExecuteHookFunc]("wrap_tool_execute")
|
||||
"""注册工具执行包裹钩子。以洋葱模型接管特定工具的执行逻辑。"""
|
||||
on_tool_execute_error = ToolHookPoint[OnToolExecuteErrorHookFunc](
|
||||
"on_tool_execute_error"
|
||||
)
|
||||
"""注册工具执行异常钩子。捕获特定工具的崩溃异常,可用于自愈重试。"""
|
||||
|
||||
|
||||
class Hooks(AbstractCapability):
|
||||
@@ -503,93 +454,111 @@ class Hooks(AbstractCapability):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""初始化 Hooks 拦截器实例。"""
|
||||
self._registry: dict[str, list[_HookEntry[Any]]] = {}
|
||||
self.on = _HookRegistration(self)
|
||||
|
||||
async def wrap_run(
|
||||
self, context: RunContext, handler: WrapRunHandler
|
||||
) -> AgentRunResult[Any]:
|
||||
"""派发:洋葱模型组装与全周期执行接管"""
|
||||
for entry in self._registry.get("before_run", []):
|
||||
await _call_entry(entry, "before_run", context)
|
||||
async def _dispatch_pipeline(
|
||||
self,
|
||||
hook_prefix: str,
|
||||
context: RunContext,
|
||||
handler: Callable,
|
||||
get_entries: Callable[[str], list[_HookEntry[Any]]],
|
||||
do_before: Callable[[_HookEntry[Any]], Awaitable[Any]],
|
||||
get_wrap_kwargs: Callable[[Callable], dict[str, Any]],
|
||||
invoke_chain: Callable[[Callable], Awaitable[Any]],
|
||||
do_error: Callable[[_HookEntry[Any], BaseException], Awaitable[Any]],
|
||||
do_after: Callable[[_HookEntry[Any], Any], Awaitable[Any]],
|
||||
) -> Any:
|
||||
"""核心泛型流水线引擎,按阶段分发并执行注册的钩子链。"""
|
||||
for entry in get_entries(f"before_{hook_prefix}"):
|
||||
await do_before(entry)
|
||||
|
||||
entries = self._registry.get("wrap_run", [])
|
||||
chain = handler
|
||||
if entries:
|
||||
for entry in reversed(entries):
|
||||
wrap_entries = get_entries(f"wrap_{hook_prefix}")
|
||||
if wrap_entries:
|
||||
for entry in reversed(wrap_entries):
|
||||
|
||||
def _wrap(
|
||||
e: _HookEntry[Any], h: Callable[..., Any]
|
||||
) -> Callable[..., Any]:
|
||||
async def _wrapped() -> Any:
|
||||
return await _call_entry(e, "wrap_run", context, h)
|
||||
def _wrap(e: _HookEntry[Any], h: Callable) -> Callable:
|
||||
async def _wrapped(*args, **kwargs) -> Any:
|
||||
return await _call_entry(
|
||||
e, f"wrap_{hook_prefix}", context, **get_wrap_kwargs(h)
|
||||
)
|
||||
|
||||
return _wrapped
|
||||
|
||||
chain = _wrap(entry, chain)
|
||||
|
||||
try:
|
||||
result = await chain()
|
||||
result = await invoke_chain(chain)
|
||||
except BaseException as error:
|
||||
for err_entry in reversed(self._registry.get("on_run_error", [])):
|
||||
err_entries = get_entries(f"on_{hook_prefix}_error")
|
||||
for err_entry in reversed(err_entries):
|
||||
try:
|
||||
return await _call_entry(err_entry, "on_run_error", context, error)
|
||||
return await do_error(err_entry, error)
|
||||
except BaseException as new_err:
|
||||
error = new_err
|
||||
raise error
|
||||
|
||||
for after_entry in reversed(self._registry.get("after_run", [])):
|
||||
result = await _call_entry(after_entry, "after_run", context, result)
|
||||
for after_entry in reversed(get_entries(f"after_{hook_prefix}")):
|
||||
res = await do_after(after_entry, result)
|
||||
if res is not None:
|
||||
result = res
|
||||
return result
|
||||
|
||||
async def wrap_run(
|
||||
self, context: RunContext, handler: WrapRunHandler
|
||||
) -> AgentRunResult[Any]:
|
||||
"""接管并包裹 Agent 运行生命周期的执行。"""
|
||||
return await self._dispatch_pipeline(
|
||||
"run",
|
||||
context,
|
||||
handler,
|
||||
get_entries=lambda p: self._registry.get(p, []),
|
||||
do_before=lambda e: _call_entry(e, "before_run", context),
|
||||
get_wrap_kwargs=lambda h: {"handler": h},
|
||||
invoke_chain=lambda c: c(),
|
||||
do_error=lambda e, err: _call_entry(e, "on_run_error", context, error=err),
|
||||
do_after=lambda e, res: _call_entry(e, "after_run", context, result=res),
|
||||
)
|
||||
|
||||
async def wrap_model_request(
|
||||
self,
|
||||
context: RunContext,
|
||||
llm_context: LLMContext[ChatRequest, ChatResponse],
|
||||
handler: WrapModelRequestHandler,
|
||||
) -> ChatResponse:
|
||||
"""派发:洋葱模型接管网络请求及前后生命周期"""
|
||||
for entry in self._registry.get("before_model_request", []):
|
||||
llm_context = await _call_entry(
|
||||
entry, "before_model_request", context, llm_context
|
||||
)
|
||||
"""接管并包裹 LLM 大模型请求的网络交互过程。"""
|
||||
|
||||
entries = self._registry.get("wrap_model_request", [])
|
||||
chain = handler
|
||||
if entries:
|
||||
for entry in reversed(entries):
|
||||
async def do_before(e):
|
||||
nonlocal llm_context
|
||||
res = await _call_entry(e, "before_model_request", context, llm_context)
|
||||
if res is not None:
|
||||
llm_context = res
|
||||
|
||||
def _make_chain(
|
||||
e: _HookEntry[Any], h: Callable[..., Any]
|
||||
) -> Callable[..., Any]:
|
||||
async def _wrapped(
|
||||
ctx_inner: LLMContext[ChatRequest, ChatResponse],
|
||||
) -> Any:
|
||||
return await _call_entry(
|
||||
e, "wrap_model_request", context, ctx_inner, h
|
||||
)
|
||||
|
||||
return _wrapped
|
||||
|
||||
chain = _make_chain(entry, chain)
|
||||
|
||||
try:
|
||||
response = await chain(llm_context)
|
||||
except Exception as error:
|
||||
for err_entry in reversed(self._registry.get("on_model_request_error", [])):
|
||||
try:
|
||||
return await _call_entry(
|
||||
err_entry, "on_model_request_error", context, llm_context, error
|
||||
)
|
||||
except Exception as new_err:
|
||||
error = new_err
|
||||
raise error
|
||||
|
||||
for after_entry in reversed(self._registry.get("after_model_request", [])):
|
||||
response = await _call_entry(
|
||||
after_entry, "after_model_request", context, llm_context, response
|
||||
)
|
||||
return response
|
||||
return await self._dispatch_pipeline(
|
||||
"model_request",
|
||||
context,
|
||||
handler,
|
||||
get_entries=lambda p: self._registry.get(p, []),
|
||||
do_before=do_before,
|
||||
get_wrap_kwargs=lambda h: {"request_context": llm_context, "handler": h},
|
||||
invoke_chain=lambda c: c(llm_context),
|
||||
do_error=lambda e, err: _call_entry(
|
||||
e,
|
||||
"on_model_request_error",
|
||||
context,
|
||||
request_context=llm_context,
|
||||
error=err,
|
||||
),
|
||||
do_after=lambda e, res: _call_entry(
|
||||
e,
|
||||
"after_model_request",
|
||||
context,
|
||||
request_context=llm_context,
|
||||
response=res,
|
||||
),
|
||||
)
|
||||
|
||||
async def wrap_tool_validate(
|
||||
self,
|
||||
@@ -598,51 +567,40 @@ class Hooks(AbstractCapability):
|
||||
args: str | dict[str, Any],
|
||||
handler: WrapToolValidateHandler,
|
||||
) -> dict[str, Any]:
|
||||
"""派发:洋葱模型接管工具参数校验及生命周期"""
|
||||
for entry in self._registry.get("before_tool_validate", []):
|
||||
args = await _call_entry(
|
||||
entry, "before_tool_validate", context, tool_name, args
|
||||
"""接管并包裹工具参数的校验过程。"""
|
||||
|
||||
async def do_before(e):
|
||||
nonlocal args
|
||||
res = await _call_entry(
|
||||
e, "before_tool_validate", context, tool_name=tool_name, args=args
|
||||
)
|
||||
if res is not None:
|
||||
args = res
|
||||
|
||||
entries = self._registry.get("wrap_tool_validate", [])
|
||||
chain = handler
|
||||
if entries:
|
||||
for entry in reversed(entries):
|
||||
|
||||
def _wrap(
|
||||
e: _HookEntry[Any], h: Callable[..., Any]
|
||||
) -> Callable[..., Any]:
|
||||
async def _wrapped(args_inner: str | dict[str, Any]) -> Any:
|
||||
return await _call_entry(
|
||||
e, "wrap_tool_validate", context, tool_name, args_inner, h
|
||||
)
|
||||
|
||||
return _wrapped
|
||||
|
||||
chain = _wrap(entry, chain)
|
||||
|
||||
try:
|
||||
validated_args = await chain(args)
|
||||
except Exception as error:
|
||||
for err_entry in reversed(self._registry.get("on_tool_validate_error", [])):
|
||||
try:
|
||||
return await _call_entry(
|
||||
err_entry,
|
||||
"on_tool_validate_error",
|
||||
context,
|
||||
tool_name,
|
||||
args,
|
||||
error,
|
||||
)
|
||||
except Exception as new_err:
|
||||
error = new_err
|
||||
raise error
|
||||
|
||||
for after_entry in reversed(self._registry.get("after_tool_validate", [])):
|
||||
validated_args = await _call_entry(
|
||||
after_entry, "after_tool_validate", context, tool_name, validated_args
|
||||
)
|
||||
return validated_args
|
||||
return await self._dispatch_pipeline(
|
||||
"tool_validate",
|
||||
context,
|
||||
handler,
|
||||
get_entries=lambda p: self._registry.get(p, []),
|
||||
do_before=do_before,
|
||||
get_wrap_kwargs=lambda h: {
|
||||
"tool_name": tool_name,
|
||||
"args": args,
|
||||
"handler": h,
|
||||
},
|
||||
invoke_chain=lambda c: c(args),
|
||||
do_error=lambda e, err: _call_entry(
|
||||
e,
|
||||
"on_tool_validate_error",
|
||||
context,
|
||||
tool_name=tool_name,
|
||||
args=args,
|
||||
error=err,
|
||||
),
|
||||
do_after=lambda e, res: _call_entry(
|
||||
e, "after_tool_validate", context, tool_name=tool_name, args=res
|
||||
),
|
||||
)
|
||||
|
||||
async def wrap_tool_execute(
|
||||
self,
|
||||
@@ -651,55 +609,43 @@ class Hooks(AbstractCapability):
|
||||
arguments: dict[str, Any],
|
||||
handler: WrapToolExecuteHandler,
|
||||
) -> Any:
|
||||
"""派发:洋葱模型接管工具执行(支持匹配名称)及生命周期"""
|
||||
for entry in _filter_tool_entries(
|
||||
self._registry.get("before_tool_execute", []), tool_name=tool_name
|
||||
):
|
||||
arguments = await _call_entry(
|
||||
entry, "before_tool_execute", context, tool_name, arguments
|
||||
)
|
||||
"""接管并包裹特定工具的物理执行逻辑。"""
|
||||
|
||||
entries = _filter_tool_entries(
|
||||
self._registry.get("wrap_tool_execute", []), tool_name=tool_name
|
||||
async def do_before(e):
|
||||
nonlocal arguments
|
||||
res = await _call_entry(
|
||||
e,
|
||||
"before_tool_execute",
|
||||
context,
|
||||
tool_name=tool_name,
|
||||
arguments=arguments,
|
||||
)
|
||||
if res is not None:
|
||||
arguments = res
|
||||
|
||||
return await self._dispatch_pipeline(
|
||||
"tool_execute",
|
||||
context,
|
||||
handler,
|
||||
get_entries=lambda p: _filter_tool_entries(
|
||||
self._registry.get(p, []), tool_name=tool_name
|
||||
),
|
||||
do_before=do_before,
|
||||
get_wrap_kwargs=lambda h: {
|
||||
"tool_name": tool_name,
|
||||
"arguments": arguments,
|
||||
"handler": h,
|
||||
},
|
||||
invoke_chain=lambda c: c(arguments),
|
||||
do_error=lambda e, err: _call_entry(
|
||||
e, "on_tool_execute_error", context, tool_name=tool_name, error=err
|
||||
),
|
||||
do_after=lambda e, res: _call_entry(
|
||||
e,
|
||||
"after_tool_execute",
|
||||
context,
|
||||
tool_name=tool_name,
|
||||
arguments=arguments,
|
||||
result=res,
|
||||
),
|
||||
)
|
||||
chain = handler
|
||||
if entries:
|
||||
for entry in reversed(entries):
|
||||
|
||||
def _wrap(
|
||||
e: _HookEntry[Any], h: Callable[..., Any]
|
||||
) -> Callable[..., Any]:
|
||||
async def _wrapped(args_inner: dict[str, Any]) -> Any:
|
||||
return await _call_entry(
|
||||
e, "wrap_tool_execute", context, tool_name, args_inner, h
|
||||
)
|
||||
|
||||
return _wrapped
|
||||
|
||||
chain = _wrap(entry, chain)
|
||||
|
||||
try:
|
||||
result = await chain(arguments)
|
||||
except Exception as error:
|
||||
for err_entry in reversed(
|
||||
_filter_tool_entries(
|
||||
self._registry.get("on_tool_execute_error", []), tool_name=tool_name
|
||||
)
|
||||
):
|
||||
try:
|
||||
return await _call_entry(
|
||||
err_entry, "on_tool_execute_error", context, tool_name, error
|
||||
)
|
||||
except Exception as new_err:
|
||||
error = new_err
|
||||
raise error
|
||||
|
||||
for after_entry in reversed(
|
||||
_filter_tool_entries(
|
||||
self._registry.get("after_tool_execute", []), tool_name=tool_name
|
||||
)
|
||||
):
|
||||
result = await _call_entry(
|
||||
after_entry, "after_tool_execute", context, tool_name, arguments, result
|
||||
)
|
||||
return result
|
||||
|
||||
@@ -232,7 +232,7 @@ class TaskResult(BaseModel):
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
|
||||
|
||||
class Task(BaseModel):
|
||||
class AgentTask(BaseModel):
|
||||
"""标准化数据契约(意图载体 Payload),定义大模型需要做什么及产出什么格式"""
|
||||
|
||||
id: str = Field(default_factory=lambda: __import__("uuid").uuid4().hex)
|
||||
@@ -263,7 +263,7 @@ class Task(BaseModel):
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _parse_and_set_guardrails(self) -> "Task":
|
||||
def _parse_and_set_guardrails(self) -> "AgentTask":
|
||||
from zhenxun.services.ai.guardrails import parse_guardrails
|
||||
|
||||
self._parsed_guardrails = parse_guardrails(self.guardrails)
|
||||
@@ -272,8 +272,8 @@ class Task(BaseModel):
|
||||
|
||||
__all__ = [
|
||||
"AgentRunResult",
|
||||
"AgentTask",
|
||||
"OutputDataT",
|
||||
"StreamedRunResult",
|
||||
"Task",
|
||||
"TaskResult",
|
||||
]
|
||||
|
||||
@@ -71,7 +71,9 @@ class LockContext(BaseModel):
|
||||
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
active_task: Any | None = None
|
||||
"""当前持锁运行的异步任务"""
|
||||
cancel_token: CancellationToken | None = None
|
||||
"""当前任务关联的取消令牌,以便由抢占者随时下发取消指令"""
|
||||
|
||||
|
||||
class AgentSessionManager:
|
||||
@@ -115,32 +117,38 @@ class AgentSessionManager:
|
||||
return count
|
||||
|
||||
def _get_lock(self, session_id: str) -> asyncio.Lock:
|
||||
"""获取或创建指定会话的内部同步锁"""
|
||||
if session_id not in self._locks:
|
||||
self._locks[session_id] = asyncio.Lock()
|
||||
return self._locks[session_id]
|
||||
|
||||
def get_exec_lock(self, session_id: str) -> asyncio.Lock:
|
||||
"""获取或创建指定会话的任务排队执行锁"""
|
||||
if session_id not in self._exec_locks:
|
||||
self._exec_locks[session_id] = asyncio.Lock()
|
||||
return self._exec_locks[session_id]
|
||||
|
||||
async def get_or_create(self, session_id: str) -> SessionInfo:
|
||||
"""获取或创建指定会话的信息,不存在则自动初始化"""
|
||||
async with self._get_lock(session_id):
|
||||
if session_id not in self._sessions:
|
||||
self._sessions[session_id] = SessionInfo(session_id=session_id)
|
||||
return self._sessions[session_id]
|
||||
|
||||
async def get(self, session_id: str) -> SessionInfo | None:
|
||||
"""获取指定会话的信息,不存在则返回 None"""
|
||||
async with self._get_lock(session_id):
|
||||
return self._sessions.get(session_id)
|
||||
|
||||
async def update_state(self, session_id: str, new_state: dict[str, Any]):
|
||||
"""用新字典更新指定会话的状态载荷"""
|
||||
async with self._get_lock(session_id):
|
||||
if session_id in self._sessions:
|
||||
self._sessions[session_id].state.update(new_state)
|
||||
self._sessions[session_id].updated_at = time.time()
|
||||
|
||||
async def delete(self, session_id: str):
|
||||
"""删除指定会话及其对应的长期内存上下文"""
|
||||
async with self._get_lock(session_id):
|
||||
self._sessions.pop(session_id, None)
|
||||
from zhenxun.services.ai.context.memory.manager import memory_manager
|
||||
|
||||
@@ -22,10 +22,12 @@ class TelemetrySubscriber:
|
||||
"""纯粹的数据观察者:默默记录时间戳与 Token 消耗"""
|
||||
|
||||
def __init__(self):
|
||||
"""初始化遥测数据观察者,准备记录统计摘要"""
|
||||
self.summary = AgentRunSummary()
|
||||
self._start_times: dict[str, float] = {}
|
||||
|
||||
def attach(self, bus: EventBus):
|
||||
"""注册监听的各种智能体生命周期和核心流程事件"""
|
||||
bus.subscribe(AgentRunStart, self.on_run_start)
|
||||
bus.subscribe(AgentRunEnd, self.on_run_end)
|
||||
bus.subscribe(LLMStartEvent, self.on_llm_start)
|
||||
@@ -34,19 +36,23 @@ class TelemetrySubscriber:
|
||||
bus.subscribe(ToolCallEndEvent, self.on_tool_end)
|
||||
|
||||
async def on_run_start(self, event: AgentRunStart):
|
||||
"""处理智能体运行开始事件,记录运行起始时间"""
|
||||
self._start_times["run"] = time.monotonic()
|
||||
logger.debug(f"🚀 [Telemetry] 智能体 {event.agent_name} 开始运行")
|
||||
|
||||
async def on_run_end(self, event: AgentRunEnd):
|
||||
"""处理智能体运行结束事件,统计总耗时并填充至结果"""
|
||||
dur = (time.monotonic() - self._start_times.get("run", time.monotonic())) * 1000
|
||||
self.summary.total_latency_ms = dur
|
||||
event.result.telemetry = self.summary
|
||||
logger.debug(f"🏁 [Telemetry] 智能体运行结束 (总耗时: {dur:.2f}ms)")
|
||||
|
||||
async def on_llm_start(self, event: LLMStartEvent):
|
||||
"""处理模型调用开始事件,记录单次大模型交互的起始时间"""
|
||||
self._start_times["llm"] = time.monotonic()
|
||||
|
||||
async def on_llm_end(self, event: LLMEndEvent):
|
||||
"""处理模型调用结束事件,累计大模型交互次数和延迟,并记录停止原因"""
|
||||
dur = (time.monotonic() - self._start_times.pop("llm", time.monotonic())) * 1000
|
||||
self.summary.chats.total += 1
|
||||
self.summary.chats.total_latency_ms += dur
|
||||
@@ -60,9 +66,11 @@ class TelemetrySubscriber:
|
||||
logger.debug(f"🧠 [Telemetry] 模型调用完成 (耗时: {dur:.2f}ms)")
|
||||
|
||||
async def on_tool_start(self, event: ToolCallStartEvent):
|
||||
"""处理工具调用开始事件,记录指定工具执行的起始时间"""
|
||||
self._start_times[f"tool_{event.tool_name}"] = time.monotonic()
|
||||
|
||||
async def on_tool_end(self, event: ToolCallEndEvent):
|
||||
"""处理工具调用结束事件,统计工具执行耗时,累计成功和失败次数"""
|
||||
dur = (
|
||||
time.monotonic()
|
||||
- self._start_times.pop(f"tool_{event.tool_name}", time.monotonic())
|
||||
@@ -93,6 +101,14 @@ class DefaultUISubscriber:
|
||||
def __init__(
|
||||
self, context: RunContext, reply_to: bool = False, verbose: bool = False
|
||||
):
|
||||
"""
|
||||
初始化默认的 UI 观察者,绑定运行上下文并获取 bot 与事件实例。
|
||||
|
||||
参数:
|
||||
context: 智能体单次运行的上下文对象,用于提取 bot、事件及依赖。
|
||||
reply_to: 在发送平台消息时是否对用户发起的消息进行回复(引用/艾特),默认 False。
|
||||
verbose: 是否开启冗长模式,若开启则会将工具流等过程事件也输出到平台,默认 False。
|
||||
""" # noqa: E501
|
||||
self.context = context
|
||||
self.reply_to = reply_to
|
||||
self.verbose = verbose
|
||||
@@ -100,10 +116,12 @@ class DefaultUISubscriber:
|
||||
self.event = context.get_event()
|
||||
|
||||
def attach(self, bus: EventBus):
|
||||
"""注册订阅相关的 UI 交互事件(如工具输出流、用户自定义事件等)"""
|
||||
bus.subscribe(ToolStreamChunkEvent, self.on_tool_stream)
|
||||
bus.subscribe(UserCustomEvent, self.on_custom_event)
|
||||
|
||||
async def _send_to_platform(self, display: Any):
|
||||
"""将通用的显示内容或富文本消息部件渲染并发送至具体的聊天平台"""
|
||||
if not self.bot or not self.event or not display:
|
||||
return
|
||||
|
||||
@@ -131,8 +149,10 @@ class DefaultUISubscriber:
|
||||
await self.bot.send(self.event, str(display))
|
||||
|
||||
async def on_tool_stream(self, event: ToolStreamChunkEvent):
|
||||
"""处理工具流式数据块事件,仅在 verbose 开启时发送给平台"""
|
||||
if self.verbose and event.content:
|
||||
await self._send_to_platform(event.content)
|
||||
|
||||
async def on_custom_event(self, event: UserCustomEvent):
|
||||
"""处理用户自定义事件,将展示内容发送至平台"""
|
||||
await self._send_to_platform(event.display)
|
||||
|
||||
Reference in New Issue
Block a user