Example Agent

In this example, we will demonstrate how to create an AI assistant that can perform tasks such as searching the internet, sending emails, creating calendar events, and booking flights. We will use the asteroid-sdk Python SDK to supervise tool executions with the supervise() decorator, ensuring compliance with specified policies or requiring human approval when necessary.

Overview

The assistant agent is designed to:

  • Interact with the user and understand their requests.
  • Use tools to perform specific actions like searching the internet or sending emails.
  • Utilize the supervise() decorator to supervise tool executions.
  • Allow supervision and approval of actions in real-time through the Asteroid web interface.

Key Components

1. Supervisors

Supervisors are functions that approve, modify, or escalate tool calls based on certain policies or checks. We define custom supervisors and use built-in ones.

  • check_email_address_supervisor: A custom supervisor that checks if an email address is in a whitelist before sending an email.
def check_email_address_supervisor(whitelisted_emails: List[str]) -> Supervisor:
    def supervisor(
        func: Callable,
        supervision_context: SupervisionContext,
        tool_kwargs: Dict[str, Any],
        **kwargs
    ) -> SupervisionDecision:
        to_email = tool_kwargs.get('to')
        if to_email in whitelisted_emails:
            return SupervisionDecision(
                decision=SupervisionDecisionType.APPROVE,
                explanation=f"The email address '{to_email}' is allowed."
            )
        else:
            return SupervisionDecision(
                decision=SupervisionDecisionType.ESCALATE,
                explanation=f"The email address '{to_email}' is not in the whitelist."
            )
    supervisor.__name__ = check_email_address_supervisor.__name__
    supervisor.supervisor_attributes = {"whitelisted_emails": whitelisted_emails}
    return supervisor
  • Built-in Supervisors: We also use llm_supervisor and human_supervisor from the asteroid-sdk SDK.

2. Tools

We define several tools that the assistant can use. Each tool is supervised to ensure compliance with policies.

  • internet_search: Searches the internet using DuckDuckGo.
@supervise()
def internet_search(query: str, max_results: int = 3) -> str:
    # Function implementation
  • send_email: Sends an email, supervised to check recipient and content.
@supervise(supervision_functions=[
    [check_email_address_supervisor(whitelisted_emails=[ASTEROID_EMAIL])],
    [llm_supervisor(instructions=EMAIL_INVITATION_POLICY), human_supervisor()]
])
def send_email(to: str, subject: str, body: str):
    # Function implementation
  • create_calendar_event: Creates a calendar event, supervised to ensure correctness.
@supervise(supervision_functions=[[llm_supervisor(instructions=CORRECT_TOOL_PARAMETERS_POLICY)]])
def create_calendar_event(title: str, start_time: str, end_time: str):
    # Function implementation
  • book_flight: Books a flight ticket, always requiring human approval.
@supervise(supervision_functions=[[human_supervisor()]])
def book_flight(departure_city: str, arrival_city: str, datetime: str, maximum_price: float):
    # Function implementation

3. Assistant Agent

The assistant agent interacts with the user, utilizes the tools, and handles OpenAI responses.

  • Creating OpenAI Tool Definitions: We convert our tool functions into OpenAI’s expected schema.
def create_openai_tool(func: Callable) -> dict:
    # Function implementation
  • Chat Function with OpenAI: Handles interaction with the OpenAI GPT model.
def chat_with_openai(messages: List[Dict], tools: List[Callable]):
    # Function implementation
  • Executing Tool Calls: Executes tool calls as decided by the assistant.
def execute_tool_call(tool_call, tools):
    # Function implementation
  • Starting the Chatbot: The main loop that starts the chatbot and handles user interaction.
def start_chatbot(
    start_prompt: str,
    tools: List[Callable],
    run_id: UUID,
    output_widget: widgets.Output,
    input_widget: widgets.Text
):
    # Function implementation

4. Running the Assistant

We set up the environment and start the assistant.

from asteroid_sdk.wrappers.openai import asteroid_openai_client, asteroid_init

# Define the initial prompt for the chatbot
start_prompt = (
    "Go and find the most interesting events happening in AI next week in San Francisco. "
    "Then create a calendar event for the most interesting one. When done, ask me email addresses "
    "where you should send invitations for that event. After the email is sent, book me a flight ticket "
    "from London to San Francisco to attend that event. Make sure that the flight price is less than 1000 GBP."
)

# List of tools available to the assistant
tools = [
    internet_search,
    send_email,
    create_calendar_event,
    book_flight
]

# Initialize the OpenAI client
client = OpenAI()

# Important! For this to work, Asteroid server needs to be running, contact Asteroid to get access
run_id = asteroid_init(project_name="Email Assistant")
# When you wrap the client, all supervised functions will be registered
wrapped_client = asteroid_openai_client(client, run_id)

# Start the chatbot
start_chatbot(start_prompt, tools, run_id, wrapped_client)

Supervising the Agent in Real-Time

Head to the Asteroid Platform and find the agent that you configured. You will be able to observe the execution of the agent and interact with it via human reviews where needed.

Conclusion

By using the supervise() decorator and Asteroid, we can ensure that our AI assistant operates within predefined policies and allows for human oversight when necessary. This example demonstrates how to integrate supervised tool executions into an AI assistant, enhancing safety and reliability.


More Examples