智能体钩子

Hooks是AgentScope中的扩展点,允许开发者在特定执行点自定义智能体行为,提供了一种无需修改智能体核心实现即可灵活修改或扩展其功能的方式。

在AgentScope中,钩子函数围绕智能体的核心功能实现:

AgentScope中支持的钩子类型

智能体类别

核心功能

挂钩类型

描述

AgentBase &
其子类

回复

pre_reply
post_reply

在智能体回复消息前后触发的钩子

打印

pre_print
post_print

向目标输出(如终端、网页界面)打印消息前后的钩子函数

观察

pre_observe
post_observe

在观察来自环境或其他智能体的消息前后执行的钩子

ReActAgentBase &
其子类别
reply
print
observe
pre_reply
post_reply
pre_print
post_print
pre_observe
post_observe

_reasoning

pre_reasoning
post_reasoning

智能体推理过程之前/之后的钩子函数

_acting

pre_acting
post_acting

Agent 动作过程前后的钩子函数

提示

由于Agent中的钩子是通过元类实现的,因此它们支持继承。

为简化使用,AgentScope为所有钩子提供了统一的签名。

import asyncio
from typing import Any, Type

from agentscope.agent import ReActAgentBase, AgentBase
from agentscope.message import Msg

钩子签名

AgentScope 提供以下统一的前后钩子签名:

预执行钩子签名

所有前置钩子的签名

名称

描述

Arguments

self: AgentBase | ReActAgentBase

智能体实例

kwargs: dict[str, Any]

目标函数的输入参数
函数,或修改后的参数
根据最近的非空返回值
先前钩子的值

Returns

dict[str, Any] | None

修改的参数或None

注意

核心函数的所有位置参数和关键字参数都以单个 kwargs 字典形式传递给钩子函数

前置钩子模板定义如下:

def pre_hook_template(
    self: AgentBase | ReActAgentBase,
    kwargs: dict[str, Any],
) -> dict[str, Any] | None:  # The modified displayed message
    """Pre hook template."""
    pass

后钩子签名

对于后钩子(post hooks),签名中会额外添加一个output参数,它代表目标函数的输出。 如果核心函数没有输出,output参数将为None

所有后钩子的签名

名称

描述

参数

self: AgentBase | ReActAgentBase

智能体实例

kwargs: dict[str, Any]

包含所有参数的字典
目标函数的

输出: Any

目标函数的输出或
最近的非空返回值
来自之前的钩子函数

返回

dict[str, Any] | None

修改后的参数或无返回值

def post_hook_template(
    self: AgentBase | ReActAgentBase,
    kwargs: dict[str, Any],
    output: Any,  # The output of the target function
) -> Any:  # The modified output
    """Post hook template."""
    pass

钩子管理

AgentScope提供实例级和类级的钩子,具体取决于钩子的有效作用域。 它们的执行顺序如下:

Hooks in AgentScope

AgentScope 提供了内置方法来在实例和类级别管理钩子,如下所示:

AgentScope 中的 Hook 管理方法

级别

方法

描述

实例层面

register_instance_hook

为当前对象注册一个钩子,使用
给定钩子类型和名称。

remove_instance_hook

移除当前对象的钩子
给定钩子类型与名称.

clear_instance_hooks

使用以下内容清除当前对象的所有钩子:
给定挂钩类型。

类级别

register_class_hook

为该类的所有对象注册钩子
使用给定的钩子类型和名称。

remove_class_hook

移除该类别所有对象的钩子
使用给定的钩子类型和名称。

clear_class_hooks

清除所有对象中的所有钩子
带给定钩子类型的类。

使用钩子时,您必须遵循以下规则:

重要

执行顺序

  • Hooks 按照注册顺序执行

  • 多个钩子可以被串联在一起

返回值处理

  • 对于前置钩子:非None的返回值将被传递给下一个钩子或核心函数

  • 当一个钩子返回 None 时,下一个钩子将使用先前钩子中最近的非 None 返回值。

  • 如果所有之前的钩子都返回 None,下一个钩子将接收一份原始参数的副本

  • 最终的非None返回值(如果所有钩子都返回None,则为原始参数)会被传递给核心函数

  • 对于后置钩子:工作方式与前置钩子相同。

重要:切勿在钩子中调用核心函数(reply/speak/observe/_reasoning/_acting)以避免无限循环

以以下代理为例,我们可以了解如何注册、移除和清理钩子:
# Create a simple test agent class
class TestAgent(AgentBase):
    """A test agent for demonstrating hooks."""

    async def reply(self, msg: Msg) -> Msg:
        """Reply to the message."""
        return msg

我们创建一个实例级钩子和一个类级钩子,用于在回复前修改消息内容。

# Create two pre-reply hooks
def instance_pre_reply_hook(
    self: AgentBase,
    kwargs: dict[str, Any],
) -> dict[str, Any]:
    """A pre-reply hook that modifies the message content."""
    msg = kwargs["msg"]
    msg.content += "[instance-pre-reply]"
    # return modified kwargs
    return {
        **kwargs,
        "msg": msg,
    }


def cls_pre_reply_hook(
    self: AgentBase,
    kwargs: dict[str, Any],
) -> dict[str, Any]:
    """A pre-reply hook that modifies the message content."""
    msg = kwargs["msg"]
    msg.content += "[cls-pre-reply]"
    # return modified kwargs
    return {
        **kwargs,
        "msg": msg,
    }


# Register class hook
TestAgent.register_class_hook(
    hook_type="pre_reply",
    hook_name="test_pre_reply",
    hook=cls_pre_reply_hook,
)

# Register instance hook
agent = TestAgent()
agent.register_instance_hook(
    hook_type="pre_reply",
    hook_name="test_pre_reply",
    hook=instance_pre_reply_hook,
)


async def example_test_hook() -> None:
    """An example function to test the hooks."""
    msg = Msg(
        name="user",
        content="Hello, world!",
        role="user",
    )
    res = await agent(msg)
    print("Response content:", res.content)
    TestAgent.clear_class_hooks()


asyncio.run(example_test_hook())
Response content: Hello, world![instance-pre-reply][cls-pre-reply]

我们可以看到消息内容中添加了“[instance-pre-reply]”和“[cls-pre-reply]”。

脚本总运行时间: (0 分 0.002 秒)

Gallery generated by Sphinx-Gallery