This tutorial will walk you through building a Discord agent using the Galadriel framework. This agent will simulate a specific persona and interact with users on your Discord server.

Prerequisites

  • Discord Bot: You’ll need a Discord bot account with its token and your server’s ID. Follow Discord’s documentation to create an application, add a bot to it, and invite it to your server. Ensure the bot has the necessary permissions (“Send Messages”, “Read Message History”).
  • Galadriel Environment: Make sure you have the Galadriel framework installed and set up as per the Quickstart Guide. This includes Python 3.10 or higher, and the Galadriel library.
  • OpenAI API Key: You will need an OpenAI API key to utilize the LLM for generating responses.
  • (Optional) Composio API Key: If you plan to use Composio tools, you will need an API Key from Composio.

Step 1: Set up the Discord Bot

  1. Create a Discord Application: Go to the Discord Developer Portal and create a new application.

  2. Create a Bot User: Navigate to the “Bot” tab of your application and click “Add Bot.”

  3. Obtain your Bot Token: In the “Bot” tab, click “Reset Token” and copy the token. Store this token securely. You’ll need it later. Keep this token secure! Enable the “Message Content Intent” under the “Privileged Gateway Intents” section.

  4. Invite the Bot to Your Server: Go to the “OAuth2” -> “URL Generator” tab, select the “bot” scope and the “Send Messages” and “Read Message History” permissions. Copy the generated URL and open it in your browser to add the bot to your server.

Step 2: Create a Galadriel Agent Project

  1. Clone the Galadriel repository:

    git clone <repository_url>
    cd galadriel/examples/discord
    

    Replace <repository_url> with the actual URL of the Galadriel repository.

  2. Create a virtual environment:

    python3 -m venv venv
    source venv/bin/activate
    
  3. Install the Galadriel framework:

    pip install galadriel
    

Step 3: Configure Your Agent

  1. Set Environment Variables: Create a .env file in the root of your agent directory. Populate it with the necessary environment variables:

    DISCORD_TOKEN=<YOUR_DISCORD_BOT_TOKEN>
    DISCORD_GUILD_ID=<YOUR_DISCORD_SERVER_ID>
    OPENAI_API_KEY=<YOUR_OPENAI_API_KEY>
    COMPOSIO_API_KEY=<YOUR_COMPOSIO_API_KEY> # Optional, only if using Composio tools
    
    • Replace <YOUR_DISCORD_BOT_TOKEN> with the bot token you obtained from the Discord Developer Portal.
    • Replace <YOUR_DISCORD_SERVER_ID> with the ID of your Discord server. To get the server ID, enable Developer Mode in Discord (User Settings -> Advanced) and right-click your server icon to select “Copy ID”.
    • Replace <YOUR_OPENAI_API_KEY> with your OpenAI API key.
    • Replace <YOUR_COMPOSIO_API_KEY> with your Composio API key, if you intend to use Composio tools.
  2. Create a Character Agent Configuration: This step involves creating a agent.json file containing the personality settings for your agent. This file dictates the agent’s behavior and style of communication.

    Create a agent.json file in your agent directory. You can customize this file to define the agent’s persona. Here’s an example configuration for an Elon Musk persona:

    {
      "name": "Elon Musk",
      "settings": {
        "model": "gpt-4",
        "debug": true
      },
      "system": "You are roleplaying as Elon Musk, CEO of Tesla, SpaceX, and X (formerly Twitter). You are known for your ambitious goals in sustainable energy, space exploration, and technological innovation. You have a unique communication style mixing technical depth with memes and humor.",
      "bio": [
        "Entrepreneur focused on accelerating humanity's transition to sustainable energy and making life multi-planetary",
        "Believes in taking bold risks to advance technology and human civilization",
        "Known for controversial statements and unorthodox leadership style"
      ],
      "lore": [
        "Sold PayPal to focus on rockets and electric cars when everyone said it was impossible",
        "Lives in a $50k house despite being one of the world's wealthiest people",
        "Named his child X Æ A-12",
        "Hosted SNL and revealed he has Asperger's syndrome",
        "Bought Twitter for $44B and renamed it to X"
      ],
      "topics": [
        "sustainable energy",
        "space exploration",
        "artificial intelligence",
        "electric vehicles",
        "neural technology",
        "social media",
        "cryptocurrency"
      ],
      "knowledge": [
        "Rocket engineering and orbital mechanics",
        "Electric vehicle technology and manufacturing",
        "Renewable energy systems and battery technology",
        "Artificial intelligence and its potential risks",
        "Software engineering and internet technology",
        "Physics and engineering principles",
        "Business strategy and scaling operations"
      ],
    }
    
  3. Modify the agent/character_agent.py file:

    Replace the content of agent/character_agent.py with the following code:

    import json
    from pathlib import Path
    
    from rich.text import Text
    
    from galadriel import ToolCallingAgent
    from galadriel.core_agent import LogLevel
    from galadriel.domain.prompts.format_prompt import load_agent_template
    from galadriel.entities import AgentMessage
    from galadriel.entities import Message
    
    DISCORD_SYSTEM_PROMPT = """
    {{system}}
    
    # Areas of Expertise
    {{knowledge}}
    
    # About {{agent_name}}:
    {{bio}}
    {{lore}}
    {{topics}}
    
    # Task: You received a new message on discord from {{user_name}}. You must reply in the voice and style of {{agent_name}}, here's the message:
    {{message}}
    
    Be very brief, and concise, add a statement in your voice.
    Maintain a natural conversation on discord, don't add signatures at the end of your messages.
    Don't overuse emojis.
    Please remember the chat history and use it to answer the question.
    """
    
    
    class CharacterAgent(ToolCallingAgent):
        def __init__(self, character_json_path: str, **kwargs):
            super().__init__(**kwargs)
            try:
                self.character_json_path = character_json_path
                # validate content of character_json_path
                _ = load_agent_template(DISCORD_SYSTEM_PROMPT, Path(self.character_json_path))
            except Exception as e:
                self.logger.log(Text(f"Error validating character file: {e}"), level=LogLevel.ERROR)
                raise e
    
        async def execute(self, message: Message) -> Message:
            try:
                character_prompt = load_agent_template(DISCORD_SYSTEM_PROMPT, Path(self.character_json_path))
                task_message = character_prompt.replace("{{message}}", message.content).replace(
                    "{{user_name}}", message.additional_kwargs["author"]
                )
                # Use parent's run method to process the message content
                response = super().run(
                    task=task_message,
                    stream=False,
                    reset=False,  # retain memory
                )
    
                # Extract message text if response is in JSON format
                response_text = str(response)
                try:
                    response_json = json.loads(response_text)
                    if isinstance(response_json, dict) and "answer" in response_json:
                        response_text = response_json["answer"]
                except json.JSONDecodeError:
                    pass  # Not JSON format, use original response
    
                return AgentMessage(
                    content=response_text,
                    conversation_id=message.conversation_id,
                )
            except Exception as e:
                self.logger.log(Text(f"Error processing message: {e}"), level=LogLevel.ERROR)
                return None
    
  4. Modify the agent/agent.py file: Replace the content with following code. Ensure character_json_path="agent.json" points to the correct location of the agent.json file.

    from galadriel.core_agent import LiteLLMModel
    from dotenv import load_dotenv
    from pathlib import Path
    
    from galadriel.tools.composio_converter import convert_action
    from character_agent import CharacterAgent
    from tools import get_time
    from galadriel import AgentRuntime
    from galadriel.clients import DiscordClient
    import os
    import asyncio
    from galadriel.logging_utils import get_agent_logger
    
    load_dotenv(dotenv_path=Path(".") / ".env", override=True)
    model = LiteLLMModel(model_id="gpt-4o", api_key=os.getenv("OPENAI_API_KEY"))
    
    logger = get_agent_logger()
    
    # Setup Discord client which
    # - pushes users' messages to the agent
    # - sends agent's responses back to the users
    discord_client = DiscordClient(guild_id=os.getenv("DISCORD_GUILD_ID"))
    
    # Setup Composio weather tool
    composio_weather_tool = convert_action(os.getenv("COMPOSIO_API_KEY"), "WEATHERMAP_WEATHER")
    
    # Add agent with GPT-4o model and tools helpful to answer Discord users' questions
    elon_musk_agent = CharacterAgent(
        character_json_path="agent.json",
        tools=[get_time],
        model=model,
    )
    
    # Set up the runtime
    runtime = AgentRuntime(
        inputs=[discord_client],
        outputs=[discord_client],
        agent=elon_musk_agent,
    )
    
    # Run the agent
    asyncio.run(runtime.run())
    

Step 4: Run the Agent

  1. Start the agent: In your terminal, navigate to the project directory and execute:

    python agent.py
    

Step 5: Interact with the Agent on Discord

  1. Go to your Discord server.
  2. Send a message in a channel where the bot has access.
  3. The bot should respond in the style of the specified persona (Elon Musk in this example).

Code Explanation:

  1. The CharacterAgent is a ToolCallingAgent that’s designed to embody a specific persona. It loads its personality from the agent.json file.

    class CharacterAgent(ToolCallingAgent):
    
  2. The agent.json file provides the agent with specific information such as lore, bio, and style to better generate the persona.

    character_json_path="agent.json"
    
  3. Tools are functions that the Agent can leverage. The time tool is listed here,

    tools=[get_time],
    
  4. The model defines the LLM that the Agent will use to compute and perform tasks.

    model=model,
    

Conclusion

You have now successfully created a Discord agent using the Galadriel framework that embodies a specific persona and interacts with users on a Discord server. Experiment with different personas, tools, and prompts to create diverse and engaging AI agents.