diff --git a/astrbot/api/event/filter/__init__.py b/astrbot/api/event/filter/__init__.py index f5ab15ed0..650bce042 100644 --- a/astrbot/api/event/filter/__init__.py +++ b/astrbot/api/event/filter/__init__.py @@ -14,6 +14,8 @@ from astrbot.core.star.register import register_command_group as command_group from astrbot.core.star.register import register_custom_filter as custom_filter from astrbot.core.star.register import register_event_message_type as event_message_type from astrbot.core.star.register import register_llm_tool as llm_tool +from astrbot.core.star.register import register_on_agent_begin as on_agent_begin +from astrbot.core.star.register import register_on_agent_done as on_agent_done from astrbot.core.star.register import register_on_astrbot_loaded as on_astrbot_loaded from astrbot.core.star.register import ( register_on_decorating_result as on_decorating_result, @@ -51,6 +53,8 @@ __all__ = [ "custom_filter", "event_message_type", "llm_tool", + "on_agent_begin", + "on_agent_done", "on_astrbot_loaded", "on_decorating_result", "on_llm_request", diff --git a/astrbot/core/astr_agent_hooks.py b/astrbot/core/astr_agent_hooks.py index a67d7b49d..551df5989 100644 --- a/astrbot/core/astr_agent_hooks.py +++ b/astrbot/core/astr_agent_hooks.py @@ -12,6 +12,15 @@ from astrbot.core.star.star_handler import EventType class MainAgentHooks(BaseAgentRunHooks[AstrAgentContext]): + async def on_agent_begin( + self, run_context: ContextWrapper[AstrAgentContext] + ) -> None: + await call_event_hook( + run_context.context.event, + EventType.OnAgentBegin, + run_context, + ) + async def on_agent_done(self, run_context, llm_response) -> None: # 执行事件钩子 if llm_response and llm_response.reasoning_content: @@ -25,6 +34,12 @@ class MainAgentHooks(BaseAgentRunHooks[AstrAgentContext]): EventType.OnLLMResponseEvent, llm_response, ) + await call_event_hook( + run_context.context.event, + EventType.OnAgentDone, + run_context, + llm_response, + ) async def on_tool_start( self, diff --git a/astrbot/core/star/register/__init__.py b/astrbot/core/star/register/__init__.py index 5e99948cd..2363c722a 100644 --- a/astrbot/core/star/register/__init__.py +++ b/astrbot/core/star/register/__init__.py @@ -7,6 +7,8 @@ from .star_handler import ( register_custom_filter, register_event_message_type, register_llm_tool, + register_on_agent_begin, + register_on_agent_done, register_on_astrbot_loaded, register_on_decorating_result, register_on_llm_request, @@ -31,6 +33,8 @@ __all__ = [ "register_custom_filter", "register_event_message_type", "register_llm_tool", + "register_on_agent_begin", + "register_on_agent_done", "register_on_astrbot_loaded", "register_on_decorating_result", "register_on_llm_request", diff --git a/astrbot/core/star/register/star_handler.py b/astrbot/core/star/register/star_handler.py index 1385b5056..7b80505b9 100644 --- a/astrbot/core/star/register/star_handler.py +++ b/astrbot/core/star/register/star_handler.py @@ -460,6 +460,64 @@ def register_on_llm_response(**kwargs): return decorator +def register_on_agent_begin(**kwargs): + """当 Agent 开始运行时的事件 + + Examples: + ```py + from astrbot.core.agent.run_context import ContextWrapper + from astrbot.core.astr_agent_context import AstrAgentContext + + @on_agent_begin() + async def test( + self, + event: AstrMessageEvent, + run_context: ContextWrapper[AstrAgentContext], + ) -> None: + ... + ``` + + 请务必接收两个参数:event, run_context + + """ + + def decorator(awaitable): + _ = get_handler_or_create(awaitable, EventType.OnAgentBegin, **kwargs) + return awaitable + + return decorator + + +def register_on_agent_done(**kwargs): + """当 Agent 运行完成后的事件 + + Examples: + ```py + from astrbot.core.agent.run_context import ContextWrapper + from astrbot.core.astr_agent_context import AstrAgentContext + from astrbot.api.provider import LLMResponse + + @on_agent_done() + async def test( + self, + event: AstrMessageEvent, + run_context: ContextWrapper[AstrAgentContext], + response: LLMResponse, + ) -> None: + ... + ``` + + 请务必接收三个参数:event, run_context, response + + """ + + def decorator(awaitable): + _ = get_handler_or_create(awaitable, EventType.OnAgentDone, **kwargs) + return awaitable + + return decorator + + def register_on_using_llm_tool(**kwargs): """当调用函数工具前的事件。 会传入 tool 和 tool_args 参数。 diff --git a/astrbot/core/star/star_handler.py b/astrbot/core/star/star_handler.py index d28ac726a..55c089951 100644 --- a/astrbot/core/star/star_handler.py +++ b/astrbot/core/star/star_handler.py @@ -71,6 +71,22 @@ class StarHandlerRegistry(Generic[T]): plugins_name: list[str] | None = None, ) -> list[StarHandlerMetadata[Callable[..., Awaitable[Any]]]]: ... + @overload + def get_handlers_by_event_type( + self, + event_type: Literal[EventType.OnAgentBegin], + only_activated=True, + plugins_name: list[str] | None = None, + ) -> list[StarHandlerMetadata[Callable[..., Awaitable[Any]]]]: ... + + @overload + def get_handlers_by_event_type( + self, + event_type: Literal[EventType.OnAgentDone], + only_activated=True, + plugins_name: list[str] | None = None, + ) -> list[StarHandlerMetadata[Callable[..., Awaitable[Any]]]]: ... + @overload def get_handlers_by_event_type( self, @@ -213,6 +229,8 @@ class EventType(enum.Enum): OnWaitingLLMRequestEvent = enum.auto() # 等待调用 LLM(在获取锁之前,仅通知) OnLLMRequestEvent = enum.auto() # 收到 LLM 请求(可以是用户也可以是插件) OnLLMResponseEvent = enum.auto() # LLM 响应后 + OnAgentBegin = enum.auto() # Agent 开始运行 + OnAgentDone = enum.auto() # Agent 运行完成 OnDecoratingResultEvent = enum.auto() # 发送消息前 OnCallingFuncToolEvent = enum.auto() # 调用函数工具 OnUsingLLMToolEvent = enum.auto() # 使用 LLM 工具 diff --git a/astrbot/dashboard/routes/plugin.py b/astrbot/dashboard/routes/plugin.py index d151bbe6f..a387cb242 100644 --- a/astrbot/dashboard/routes/plugin.py +++ b/astrbot/dashboard/routes/plugin.py @@ -79,6 +79,8 @@ class PluginRoute(Route): EventType.AdapterMessageEvent: "平台消息下发时", EventType.OnLLMRequestEvent: "LLM 请求时", EventType.OnLLMResponseEvent: "LLM 响应后", + EventType.OnAgentBegin: "Agent 开始运行时", + EventType.OnAgentDone: "Agent 运行完成后", EventType.OnDecoratingResultEvent: "回复消息前", EventType.OnCallingFuncToolEvent: "函数工具", EventType.OnAfterMessageSentEvent: "发送消息后", diff --git a/docs/en/dev/star/guides/listen-message-event.md b/docs/en/dev/star/guides/listen-message-event.md index 705ca63f9..aa02d8400 100644 --- a/docs/en/dev/star/guides/listen-message-event.md +++ b/docs/en/dev/star/guides/listen-message-event.md @@ -289,6 +289,86 @@ async def on_llm_resp(self, event: AstrMessageEvent, resp: LLMResponse): # Note > You cannot use yield to send messages here. If you need to send, please use the `event.send()` method directly. +#### On Agent Begin + +When the Agent starts running, the `on_agent_begin` hook is triggered. + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.core.agent.run_context import ContextWrapper +from astrbot.core.astr_agent_context import AstrAgentContext + +@filter.on_agent_begin() +async def on_agent_begin(self, event: AstrMessageEvent, run_context: ContextWrapper[AstrAgentContext]): # Note there are three parameters + print("Agent started") +``` + +> You cannot use yield to send messages here. If you need to send, please use the `event.send()` method directly. + +#### Before LLM Tool Call + +When the Agent is about to call an LLM tool, the `on_using_llm_tool` hook is triggered. + +You can obtain the `FunctionTool` object and tool call arguments. + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.core.agent.tool import FunctionTool + +@filter.on_using_llm_tool() +async def on_using_llm_tool( + self, + event: AstrMessageEvent, + tool: FunctionTool, + tool_args: dict | None, +): + print(tool.name, tool_args) +``` + +> You cannot use yield to send messages here. If you need to send, please use the `event.send()` method directly. + +#### After LLM Tool Call + +After the LLM tool call completes, the `on_llm_tool_respond` hook is triggered. + +You can obtain the `FunctionTool` object, tool call arguments, and tool call result. + +```python +from mcp.types import CallToolResult + +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.core.agent.tool import FunctionTool + +@filter.on_llm_tool_respond() +async def on_llm_tool_respond( + self, + event: AstrMessageEvent, + tool: FunctionTool, + tool_args: dict | None, + tool_result: CallToolResult | None, +): + print(tool.name, tool_args, tool_result) +``` + +> You cannot use yield to send messages here. If you need to send, please use the `event.send()` method directly. + +#### On Agent Done + +After the Agent finishes running, the `on_agent_done` hook is triggered. This hook is triggered after `on_llm_response`. + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.provider import LLMResponse +from astrbot.core.agent.run_context import ContextWrapper +from astrbot.core.astr_agent_context import AstrAgentContext + +@filter.on_agent_done() +async def on_agent_done(self, event: AstrMessageEvent, run_context: ContextWrapper[AstrAgentContext], resp: LLMResponse): # Note there are four parameters + print(resp) +``` + +> You cannot use yield to send messages here. If you need to send, please use the `event.send()` method directly. + #### Before Sending Message Before sending a message, the `on_decorating_result` hook is triggered. diff --git a/docs/zh/dev/star/guides/listen-message-event.md b/docs/zh/dev/star/guides/listen-message-event.md index 8b720c9eb..762be035d 100644 --- a/docs/zh/dev/star/guides/listen-message-event.md +++ b/docs/zh/dev/star/guides/listen-message-event.md @@ -305,6 +305,86 @@ async def on_llm_resp(self, event: AstrMessageEvent, resp: LLMResponse): # 请 > 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 +#### Agent 开始运行时 + +在 Agent 开始运行时,会触发 `on_agent_begin` 钩子。 + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.core.agent.run_context import ContextWrapper +from astrbot.core.astr_agent_context import AstrAgentContext + +@filter.on_agent_begin() +async def on_agent_begin(self, event: AstrMessageEvent, run_context: ContextWrapper[AstrAgentContext]): + print("Agent 开始运行") +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +#### LLM 工具调用前 + +在 Agent 准备调用 LLM 工具时,会触发 `on_using_llm_tool` 钩子。 + +可以获取到 `FunctionTool` 对象和工具调用参数。 + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.core.agent.tool import FunctionTool + +@filter.on_using_llm_tool() +async def on_using_llm_tool( + self, + event: AstrMessageEvent, + tool: FunctionTool, + tool_args: dict | None, +): + print(tool.name, tool_args) +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +#### LLM 工具调用后 + +在 LLM 工具调用完成后,会触发 `on_llm_tool_respond` 钩子。 + +可以获取到 `FunctionTool` 对象、工具调用参数和工具调用结果。 + +```python +from mcp.types import CallToolResult + +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.core.agent.tool import FunctionTool + +@filter.on_llm_tool_respond() +async def on_llm_tool_respond( + self, + event: AstrMessageEvent, + tool: FunctionTool, + tool_args: dict | None, + tool_result: CallToolResult | None, +): + print(tool.name, tool_args, tool_result) +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +#### Agent 运行完成时 + +在 Agent 运行完成后,会触发 `on_agent_done` 钩子。这个钩子会在 `on_llm_response` 之后触发。本质上和 `on_llm_response` 一样。 + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.provider import LLMResponse +from astrbot.core.agent.run_context import ContextWrapper +from astrbot.core.astr_agent_context import AstrAgentContext + +@filter.on_agent_done() +async def on_agent_done(self, event: AstrMessageEvent, run_context: ContextWrapper[AstrAgentContext], resp: LLMResponse): + print(resp) +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + #### 发送消息前 在发送消息前,会触发 `on_decorating_result` 钩子。 diff --git a/docs/zh/dev/star/plugin.md b/docs/zh/dev/star/plugin.md index 318f0c4be..a286e9250 100644 --- a/docs/zh/dev/star/plugin.md +++ b/docs/zh/dev/star/plugin.md @@ -507,6 +507,88 @@ async def on_llm_resp(self, event: AstrMessageEvent, resp: LLMResponse): # 请 > 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 +##### Agent 开始运行时 + +在 Agent 开始运行时,会触发 `on_agent_begin` 钩子。 + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.core.agent.run_context import ContextWrapper +from astrbot.core.astr_agent_context import AstrAgentContext + +@filter.on_agent_begin() +async def on_agent_begin(self, event: AstrMessageEvent, run_context: ContextWrapper[AstrAgentContext]): # 请注意有三个参数 + print("Agent 开始运行") +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +##### LLM 工具调用前 + +在 Agent 准备调用 LLM 工具时,会触发 `on_using_llm_tool` 钩子。 + +可以获取到 `FunctionTool` 对象和工具调用参数。 + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.core.agent.tool import FunctionTool + +@filter.on_using_llm_tool() +async def on_using_llm_tool( + self, + event: AstrMessageEvent, + tool: FunctionTool, + tool_args: dict | None, +): + print(tool.name, tool_args) +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +##### LLM 工具调用后 + +在 LLM 工具调用完成后,会触发 `on_llm_tool_respond` 钩子。 + +可以获取到 `FunctionTool` 对象、工具调用参数和工具调用结果。 + +```python +from mcp.types import CallToolResult + +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.core.agent.tool import FunctionTool + +@filter.on_llm_tool_respond() +async def on_llm_tool_respond( + self, + event: AstrMessageEvent, + tool: FunctionTool, + tool_args: dict | None, + tool_result: CallToolResult | None, +): + print(tool.name, tool_args, tool_result) +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +##### Agent 运行完成时 + +在 Agent 运行完成后,会触发 `on_agent_done` 钩子。这个钩子会在 `on_llm_response` 之后触发。 + +可以获取到 `LLMResponse` 对象,可以对其进行修改。 + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.provider import LLMResponse +from astrbot.core.agent.run_context import ContextWrapper +from astrbot.core.astr_agent_context import AstrAgentContext + +@filter.on_agent_done() +async def on_agent_done(self, event: AstrMessageEvent, run_context: ContextWrapper[AstrAgentContext], resp: LLMResponse): # 请注意有四个参数 + print(resp) +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + ##### 发送消息前 在发送消息前,会触发 `on_decorating_result` 钩子。