article_icon
article

Microagents - Basic Agent to Agent Interaction

A guide helping you understand how microagents communicate with one another.

2023-05-120 min readjosh

In the previous tutorials we covered how to install the microagents package and how to create your first microagent. The next step now is adding a second microagent and making them communicate. Let's create a new Python script for this, and call it agent_communication.py.

code-icon
code-icon
touch agent_communication.py 

First of all, we need to write th code for our fist agent, alice, and then add the script for our second agent, bob. Once we created our microagents, we want them to exchange messages with one another. To do so, we need to import a new class, Model, which helps us to define generic messages.

We need to specify a custom data model for messages: a custom data model defines the fields and properties of an object, allowing the representation of structured data in a meaningful way.

code-icon
code-icon
from uagents import Agent, Bureau, Context, Model

class Message(Model):
    message: str

alice = Agent(name="alice")
bob = Agent(name="bob")

In our scenario, the custom data model Message is defined as a class with a single field text of type string. This means that any message that is sent using the Message data model will have a text field that contains a string value. By defining a custom data model for a message, we can ensure that the message's data is structured and can be easily parsed and manipulated by the receiving agent.

As it is possible to notice, we have also imported a Bureau class: this one allows us to create a collection of agents and run them together in the same script.

We want to create an interaction. To do so, we can define a function called send_message for alice to bob.

code-icon
code-icon
@alice.on_interval(period=3.0)
async def send_message(ctx: Context):
   await ctx.send(bob.address, Message(message="hello there bob"))
  • @alice.on_interval(period=3.0): this is a decorator that defines a periodic behavior for this agent. In this case, the alice agent will execute the send_message function every 3 seconds.

  • async def send_message(ctx: Context): this defines the behavior to be executed periodically by the agent. This function sends a message to bob every 3 seconds using the send method of the Context object.

  • await ctx.send(bob.address, Message(message=msg)): The function uses the send method of the Context object to send the message to bob's address. The send method takes two arguments: the recipient's address and the message to be sent. In this case, the recipient's address is retrieved from the bob.address attribute, and the message is created using the Message data model with the message string as the value for the message field.

Now, we define a message handler function, alice_message_handler, for alice to help handle all receiving messages.

code-icon
code-icon
@alice.on_message(model=Message)
async def alice_message_handler(ctx: Context, sender: str, msg: Message):
    ctx.logger.info(f"Received message from {sender}: {msg.message}")
  • @alice.on_message(model=Message): this decorator makes the function executable every time alice receives a message from bob matching the Message data model.

  • async def message_handler(ctx: Context, sender: str, msg: Message)::The function takes three arguments: ctx, sender, and msg:

    • The ctx argument is the context object for the agent.
    • The sender argument is a string that contains the address of the agent that has sent the message.
    • The msg argument is an instance of the Message data model that contains the data of the received message.
  • ctx.logger.info(f"Received message from {sender}: {msg.text}"): this method is log information about the received message: the sender's address and the message text using an f-string

We now need to define a message handler function, message_handler, for bob which will help handling all receiving messages from alice.

code-icon
code-icon
@bob.on_message(model=Message)
async def bob_message_handler(ctx: Context, sender: str, msg: Message):
    ctx.logger.info(f"Received message from {sender}: {msg.message}")
    await ctx.send(alice.address, Message(message="hello there alice"))
  • @bob.on_message(model=Message): this decorator makes the function executable every time bob rceives a message from alice matching the Message data model.

  • async def message_handler(ctx: Context, sender: str, msg: Message)::The function takes three arguments: ctx, sender, and msg:

    • The ctx argument is the context object for the agent.
    • The sender argument is a string that contains the address of the agent that has sent the message.
    • The msg argument is an instance of the Message data model that contains the data of the received message.
  • ctx.logger.info(f"Received message from {sender}: {msg.text}"): this method is log information about the received message: the sender's address and the message text using an f-string.

  • await ctx.send(bob.address, Message(message=msg)): The function uses the send method of the Context object to send the message to alice's address. The send method takes two arguments: the recipient's address and the message to be sent. In this case, the recipient's address is retrieved from the alice.address attribute, and the message is created using the Message data model with the message string as the value for the message field.

Finally, we need to add both agents to the Bureau in order to run them from the same script.

code-icon
code-icon
bureau = Bureau()
bureau.add(alice)
bureau.add(bob)

if __name__ == "__main__":
    bureau.run()

Your script should look as below:

code-icon
code-icon
from uagents import Agent, Bureau, Context, Model

class Message(Model):
    message: str

alice = Agent(name="alice")
bob = Agent(name="bob")

@alice.on_interval(period=3.0)
async def send_message(ctx: Context):
   await ctx.send(bob.address, Message(message="hello there bob"))

@alice.on_message(model=Message)
async def alice_message_handler(ctx: Context, sender: str, msg: Message):
    ctx.logger.info(f"Received message from {sender}: {msg.message}")
  
@bob.on_message(model=Message)
async def bob_message_handler(ctx: Context, sender: str, msg: Message):
    ctx.logger.info(f"Received message from {sender}: {msg.message}")
    await ctx.send(alice.address, Message(message="hello there alice"))

bureau = Bureau()
bureau.add(alice)
bureau.add(bob)

if __name__ == "__main__":
    bureau.run()

We are ready to run the script. Open your terminal, activate the virtual environment and make sure you are in the right directory for your microagents project.

code-icon
code-icon
python agents_communication.py

You should see the following output in your terminal printing every 2 seconds:

Here, Bob receives a message from Alice saying "hello there bob" every three seconds. Bob then handles the message and replies back to alice. Then, Alice receives a message from Bob saying "hello there Alice".


More from Fetch