Lifecycle Hooks

Using lifecycle hooks to monitor and debug agent execution.

Using Lifecycle Hooks to Monitor Agent Execution

Lifecycle hooks allow you to plug in your custom logging or processing steps upon key lifecycle events during the agent.run() method, such as function calls and remote service interactions.

This feature is particularly useful when you want to display the details of these events in a human-readable format to your end users in your web application, or to your agent developers if you offers a platform for your customers to build and observe agents.

Python

lifecycle_hook.py

import json

from rich.console import Console

from oci.addons.adk import Agent, AgentClient
from oci.addons.adk.tool.prebuilt import CalculatorToolkit
from oci.addons.adk.run.types import RequiredAction, PerformedAction

# A callback function that is called when a required action is fulfilled
def handle_fulfilled_required_action(required_action: RequiredAction, performed_action: PerformedAction):
    # Implement your custom logging or processing here
    # Access the required action and performed action objects to get the details
    pass

# A callback function that is called when a remote service is invoked
def handle_invoked_remote_service(chat_request, chat_response):
    # Implement your custom logging or processing here
    # Access the chat request and response objects to get the details
    pass

def main():

    client = AgentClient(
        auth_type="api_key",
        profile="DEFAULT",
        region="us-chicago-1"
    )

    agent = Agent(
        client=client,
        agent_endpoint_id="ocid1.genaiagentendpoint...",
        instructions="You're a helpful assistant that can perform calculations.",
        tools=[CalculatorToolkit()]
    )

    agent.setup()

    input = "What's the square root of 475695037565?"
    response = agent.run(
        input,
        max_steps=6,
        on_fulfilled_required_action=handle_fulfilled_required_action,
        on_invoked_remote_service=handle_invoked_remote_service,
    )

    response.pretty_print()

if __name__ == "__main__":
    main()

Java

LifecycleHookAgent.java


package demos;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.oracle.bmc.ConfigFileReader;
import com.oracle.bmc.auth.BasicAuthenticationDetailsProvider;
import com.oracle.bmc.auth.SessionTokenAuthenticationDetailsProvider;
import com.oracle.bmc.generativeaiagentruntime.model.FunctionCallingPerformedAction;
import com.oracle.bmc.generativeaiagentruntime.model.FunctionCallingRequiredAction;
import com.oracle.bmc.generativeaiagentruntime.model.PerformedAction;
import com.oracle.bmc.generativeaiagentruntime.model.RequiredAction;
import com.oracle.bmc.generativeaiagentruntime.responses.ChatResponse;
import com.oracle.bmc.adk.agent.Agent;
import com.oracle.bmc.adk.agent.RunOptions;
import com.oracle.bmc.adk.client.AgentClient;
import com.oracle.bmc.adk.tools.prebuilt.CalculatorToolkit;
import com.oracle.bmc.adk.run.RunResponse;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class LifecycleHookAgent {
  private static final ObjectMapper objectMapper = new ObjectMapper();

  public static void main(String[] args) throws Exception {
    final String AGENT_ENDPOINT_ID =
        "ocid1.genaiagentendpoint.oc1.us-chicago-1...";

    final String configLocation = "~/.oci/config";
    final String configProfile = "DEFAULT";
    BasicAuthenticationDetailsProvider authProvider =
            new SessionTokenAuthenticationDetailsProvider(
                    ConfigFileReader.parse(configLocation, configProfile));

    AgentClient agentClient = AgentClient.builder()
        .authProvider(authProvider)
        .region("us-chicago-1")
        .build();

    // Instantiate the local agent object (with the client, instructions, and tools to be
    // registered)
    Agent agent = Agent.builder()
            .client(agentClient)
            .agentEndpointId(WRITER_AGENT_ENDPOINT_ID)
            .instructions("You are a helpful assistant that can perform calculations.")
            .tools(List.of(new CalculatorToolkit()))
            .build();

    // Set up the agent (configures the instructions and tools in the remote agent resource)
    agent.setup();

    // Use the agent to process the end user request (automatically handles function calling)
    final String input = "What is the square root of 16?";
    final Integer maxStep = 6;
    final RunOptions runOptions = RunOptions.builder()
            .maxSteps(maxStep)
            .onFulfilledRequiredAction(LifecycleHookAgent::handleFulfilledRequiredAction)
            .onInvokedRemoteService(LifecycleHookAgent::handleInvokedRemoteService)
            .build();

    RunResponse response = agent.run(input, runOptions);

    response.prettyPrint();
  }

  public static void handleFulfilledRequiredAction(
      RequiredAction requiredAction, PerformedAction performedAction) {
    String toolCallName =
        ((FunctionCallingRequiredAction) requiredAction).getFunctionCall().getName();
    String toolCallArgs =
        ((FunctionCallingRequiredAction) requiredAction).getFunctionCall().getArguments();
    String toolCallOutput =
        ((FunctionCallingPerformedAction) performedAction).getFunctionCallOutput();

    // Format JSON strings with proper indentation
    String formattedArgs = formatJson(toolCallArgs);
    String formattedOutput = formatJson(toolCallOutput);

    String baseMessage = "Executed client-side function tool: **" + toolCallName + "**";
    String details =
        "Function Tool call arguments:\n```json\n"
            + formattedArgs
            + "\n```\nFunction Tool call output:\n```json\n"
            + formattedOutput
            + "\n```";

    System.out.println(baseMessage);
    System.out.println(details);
  }

  public static void handleInvokedRemoteService(
      Map<String, Object> chatRequest, ChatResponse chatResponse) {
    String label = "Invoked Agent Service API: ";
    if (chatResponse.getChatResult().getRequiredActions() != null) {
      List<RequiredAction> actions = chatResponse.getChatResult().getRequiredActions();
      label += " received " + actions.size() + " action(s) for client to take";
    } else {
      label += " received agent text message";
    }

    String content =
        "Agent Service call request:\n```json\n"
            + formatJson(chatRequest)
            + "\n```\nAgent Service call response:\n```json\n"
            + formatJson(chatResponse)
            + "\n```";

    System.out.println(label);
    System.out.println(content);
  }

  private static String formatJson(Object obj) {
    try {
      return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
    } catch (JsonProcessingException e) {
      return obj.toString();
    }
  }
}

Currently the ADK supports two lifecycle hooks:

  • on_fulfilled_required_action: This hook is called after a required action is fulfilled, such as a function tool is invoked.
  • on_invoked_remote_service: This hook is called when the remote agent service API is invoked.