OpenRP Docs
Behavior Engine

Getting started

Create your first behavior graph to automate character responses.

The Behavior Editor is a visual node editor where you design the brain of your character. Behaviors are powerful pipelines that define exactly what characters do when triggered by certain events, like responding to a chat message.

Every world in OpenRP comes with a default behavior that allows your character to respond to user chat messages. However, you can extend the existing system with custom behaviors, such as a specialized memory system, character or world states (e.g. relationship levels), or even make characters output images.

In this guide, we will walk you through the basics of the Behavior Editor and build a minimum working behavior that responds to a user message.

Creating a simple behavior

Behaviors can be created on the behavior tab in your world. You may also import an existing behavior here by copying that behavior with the export button. Create a new behavior and click on it to open the Behavior Editor.

Note

Worlds do not contain any behavior when first created; OpenRP Chat uses a default chat behavior in this case.

The Behavior Editor has two modes, a build mode for making changes to the behavior, and a debug mode for testing your behavior. You can toggle between them using the toggle at the top of the editor.

A behavior consists of nodes and edges. Nodes are the building blocks and each is a concrete action, such as generating an LLM response or sending a chat message. Edges define which nodes are executed next.

Let's build a simple behavior that triggers when a user sends a message, passes that message to an AI model, and posts the generated response back to the chat.

Step 1: Create the event node

Every behavior must start with an event node. This node is the starting point of the execution and defines when the behavior is triggered.

Important

A behavior can only have one event node.

  1. Open the node palette and drag the Chat Message node (under Events) onto the canvas.
  2. Select the node and update the Node ID to myEventInput in the inspector on the right and click Save.

Step 2: Fetch the Message Content

The Chat Message event node gives us the ID of the message that triggered the event, and we can use this ID to fetch the content of that message using the Get Chat Message node.

  1. Drag the Get Chat Message node (under Storage) onto the canvas.

    Note

    Not to be confused with the Get Chat Messages node, which fetches a list of messages instead of a single one.

  2. Connect the output (next) of myEventInput to the input (previous) of the new getChatMessage node.
  3. Select the node and configure it in the inspector:
    • Message ID: set to myEventInput.messageId.

This tells the node to read the messageId from the output of the myEventInput node, which is our Chat Message event node from step 1.

Step 3: Generate the AI response

Now that we have the message content, we can use it to generate an AI response.

  1. Drag the Get Default Model node (under AI) onto the canvas.
  2. Drag the LLM node (under AI) onto the canvas.
  3. Connect the output of getChatMessage to the input of getDefaultModel, then connect the output of getDefaultModel to the input of llm.
  4. Configure llm in the inspector:
    • System prompt: set to User: {{getChatMessage.content}}.

      Note

      This field accepts a Template String, which can use outputs from previous nodes to dynamically construct text. In this case, it will become User: Hello world! if the user sent a message saying "Hello world!".

    • Model ID: set to getDefaultModel.id.

Step 4: Send the response

Finally, let's send the response back to the chat. But first, we need to determine who to send it as. To do this, we need to know who is in the chat.

  1. Drag the Get Chat node (under Storage) onto the canvas and connect it to the output of llm.
  2. Configure getChat in the inspector:
    • Chat ID: set to myEventInput.chatId. Our event also sends in the id of the chat that triggered the event.
    • Expand Relations: set to participants. This means that we want to read who is in this chat. getChat will only return the participants of this chat if this is requested.
  3. Drag the Filter List node (under Utilities) onto the canvas and connect it to the output of getChat.
  4. Configure filterList in the inspector:
    • List: set to getChat.participants.data.
    • Item Condition: set to item.userId === null. This filters out user participants (who will always have a user ID), leaving only AI characters in the list.

      Note

      item is a special name that refers to each element in the list. Since we passed in a list of participants, item.userId refers to the userId field of each participant.

  5. Drag the Insert Chat Message node (under Storage) onto the canvas and connect it to the output of filterList.
  6. Configure insertChatMessage in the inspector:
    • Chat ID: set to myEventInput.chatId.
    • Content: set to {{llm.outputText}}.
    • Chat Participant ID: set to filter.list[0].id. This will get the first participant in the list of AI characters. In the case of a direct message chat, this will always be the other character in the chat.

      Challenge

      When there are multiple characters in the chat, can you think of a way using the nodes we've learned so far to decide which character should respond to the message?

Make sure to save the graph by clicking on the Save button in the top bar of the Behavior Editor.

Test your behavior

Let's see the behavior in action.

  1. Pick a character you want to use the new behavior on in your world. Edit that character and under Character behavior, enable Behavior Engine and add the behavior you just created.
  2. Start a new chat with this character in your world.
  3. Send a message.
  4. View the Message Metadata for your sent message by hovering or long pressing on your message and select the View Metadata option. You should see a link to the behavior execution in the dialog that opens.
  5. Click on it to open the Behavior Editor. The link will open the behavior execution that was triggered by your message.

Debug mode

In debug mode, you can see the execution status for the nodes we configured. You can also see the actual inputs and outputs for each node. These are very helpful in debugging your behavior if something didn't go as expected.

If you have followed the guide perfectly, you should see all nodes completed successfully. If a node failed, you can click on it to see any error messages and confirm its run-time input.

Note

When a behavior runs, it resolves all the Expressions and Template Strings you entered in the inspector to their actual values. For example, {{myEventInput.chatId}} in insertChatMessage node becomes the actual id of your chat, and User: {{getChatMessage.content}} in llm node becomes User: Hello world! if you sent "Hello world!".

Navigate back to your chat and you should see the character responding to your message.

Testing in the editor

To make testing faster, you can directly test in the Behavior Editor.

  1. Select myEventInput node, and you will see a Manual Test section in the inspector. Fill it with a chat ID and message ID:

    • To find your chat ID, go back to your chat and copy the last segment of the URL, after /chats/. For example: 019ee8d0-ee75-770f-867d-370299c1b900.
    • To find your message ID, view your message metadata again and copy the ID. It should look similar to the chat ID above.
    • You can delete the modelSettings as this is not used in our behavior.

    Your Manual Test input should look something like this:

    {
      "chatId": "019ee8d0-ee75-770f-867d-370299c1b900",
      "messageId": "019ee8d4-4392-72d9-8b86-19b1e5619833"
    }
  2. Trigger the behavior with the Run Trigger Test button.

  3. Behavior Editor will enter debug mode, and you can view the execution results in real time.

Behaviors triggered this way behaves exactly the same as behaviors triggered by actual events.

Common issues

Invalid input format

This error means the node is expecting an input of a certain type or value but got something else. For example, it received -100 for the "number of messages to load", or an empty string "" where it expected an ID.

Usually this means you should double check what you input into that field. Are you adding "{{}}" around your input when it is not a Template String? Are you giving it a number when it expects a list?

If you are using an Expression, double check the output of the node where your expression is coming from. The Resolved Input section on the node shows you exactly what each referenced node output looks like, so you can see if they are what you expect them to be. If they are not, check the nodes that produced that output.

On this page