Hook

Hooks provide extension points to monitor and modify agent behavior at specific execution stages.

Hook Overview

AgentScope Java uses a unified event model where all hooks implement the onEvent(HookEvent) method:

  • Event-based: All agent activities generate events

  • Type-safe: Pattern matching on event types

  • Priority-ordered: Hooks execute by priority (lower value = higher priority)

  • Modifiable: Some events allow modification of execution context

Supported Events

Event Type

Timing

Modifiable

Description

PreCallEvent

Before agent call

Before agent starts processing (notification-only)

PostCallEvent

After agent call

After agent completes response (can modify final message)

PreReasoningEvent

Before reasoning

Before LLM reasoning (can modify input messages)

PostReasoningEvent

After reasoning

After LLM reasoning (can modify reasoning result)

ReasoningChunkEvent

During reasoning stream

Each chunk of streaming reasoning (notification-only)

PreActingEvent

Before tool execution

Before tool execution (can modify tool parameters)

PostActingEvent

After tool execution

After tool execution (can modify tool result)

ActingChunkEvent

During tool stream

Tool execution progress chunks (notification-only)

ErrorEvent

On error

When errors occur (notification-only)

Creating Hooks

Basic Hook

import io.agentscope.core.hook.Hook;
import io.agentscope.core.hook.HookEvent;
import io.agentscope.core.hook.PreCallEvent;
import io.agentscope.core.hook.PostCallEvent;
import reactor.core.publisher.Mono;

public class LoggingHook implements Hook {

    @Override
    public <T extends HookEvent> Mono<T> onEvent(T event) {
        
        if (event instanceof PreCallEvent) {
            System.out.println("Agent starting: " + event.getAgent().getName());
            return Mono.just(event);
        }
        
        if (event instanceof PostCallEvent) {
            System.out.println("Agent finished: " + event.getAgent().getName());
            return Mono.just(event);
        }
        
        return Mono.just(event);
    }
}

Hook with Priority

public class HighPriorityHook implements Hook {

    @Override
    public int priority() {
        return 10;  // Lower number = higher priority (default is 100)
    }

    @Override
    public <T extends HookEvent> Mono<T> onEvent(T event) {
        // This hook executes before hooks with priority > 10
        return Mono.just(event);
    }
}

Modifying Events

Some events allow modification:

import io.agentscope.core.hook.Hook;
import io.agentscope.core.hook.HookEvent;
import io.agentscope.core.hook.PreReasoningEvent;
import io.agentscope.core.message.Msg;
import io.agentscope.core.message.MsgRole;
import io.agentscope.core.message.TextBlock;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;

public class PromptEnhancingHook implements Hook {

    @Override
    public <T extends HookEvent> Mono<T> onEvent(T event) {
        if (event instanceof PreReasoningEvent e) {
            List<Msg> messages = new ArrayList<>(e.getInputMessages());
            messages.add(0, Msg.builder()
                    .role(MsgRole.SYSTEM)
                    .content(List.of(TextBlock.builder().text("Think step by step.").build()))
                    .build());
            e.setInputMessages(messages);
            return Mono.just(event);
        }
        return Mono.just(event);
    }
}

Configure Hooks in Agent

Register hooks when building an agent:

import io.agentscope.core.ReActAgent;
import java.util.List;

ReActAgent agent = ReActAgent.builder()
        .name("Assistant")
        .model(model)
        .toolkit(toolkit)
        .hooks(List.of(
                new LoggingHook(),
                new HighPriorityHook(),
                new PromptEnhancingHook()
        ))
        .build();

Hooks are immutable after agent construction.

Hook Examples

Monitoring Tool Execution

Track tool calls:

import io.agentscope.core.hook.Hook;
import io.agentscope.core.hook.HookEvent;
import io.agentscope.core.hook.PostActingEvent;
import io.agentscope.core.hook.PreActingEvent;
import io.agentscope.core.message.TextBlock;
import reactor.core.publisher.Mono;

public class ToolMonitorHook implements Hook {

    @Override
    public <T extends HookEvent> Mono<T> onEvent(T event) {
        
        if (event instanceof PreActingEvent e) {
            System.out.println("Calling tool: " + e.getToolUse().getName());
            System.out.println("Arguments: " + e.getToolUse().getInput());
            return Mono.just(event);
        }

        if (event instanceof PostActingEvent e) {
            String resultText = e.getToolResult().getOutput().stream()
                    .filter(block -> block instanceof TextBlock)
                    .map(block -> ((TextBlock) block).getText())
                    .findFirst()
                    .orElse("");
            System.out.println("Tool result: " + resultText);
            return Mono.just(event);
        }

        return Mono.just(event);
    }
}

Monitoring Errors

Monitor and handle errors:

import io.agentscope.core.hook.ErrorEvent;
import io.agentscope.core.hook.Hook;
import io.agentscope.core.hook.HookEvent;
import reactor.core.publisher.Mono;

public class ErrorHandlingHook implements Hook {

    @Override
    public <T extends HookEvent> Mono<T> onEvent(T event) {

        if (event instanceof ErrorEvent e) {
            System.err.println("Error in agent: " + e.getAgent().getName());
            System.err.println("Error message: " + e.getError().getMessage());
            return Mono.just(event);
        }

        return Mono.just(event);
    }
}

Complete Example

See the complete Hook example:

  • examples/src/main/java/io/agentscope/examples/HookExample.java

Run the example:

cd examples
mvn exec:java -Dexec.mainClass="io.agentscope.examples.HookExample"