Skip to Content

Communicating with other agents

Introduction

Communication is an essential feature within any agents network. Communication allows agents to work together, exchange information, and forms an organic marketplace.

In this guide, we will explore two methods of communication between agents:

Let’s start with local communication. This is the first step you would need to undertake to familiarize yourself with the code syntax we will be using in the remote communication section.

Agents: Local Communication

Walk-through

The first step to better understand how agents communicate is to introduce how 2 agents perform a local communication. Let’s consider a basic example in which two agents say hello to each other.

  1. First of all, let’s create a Python script for this task:

    windows
    echo. > agents_communication.py
  2. Then, we import Agent, Context, Bureau, and Model from the uagents library and we then define the message structure for messages to be exchanged between the agents using the class Model:

agent_communication.py
from uagents import Agent, Bureau, Context, Model class Message(Model): message: str

The Message class defines the structure of message we can receive. In this example it’s just a string, but it could be a simple integer, or a complex object too.

  1. Now we create two agent instances, sigmar and slaanesh, with name and seed parameters:
agent_communication.py
sigmar = Agent(name="sigmar", seed="sigmar recovery phrase", port=8000, endpoint=["http://localhost:8000/submit"]) slaanesh = Agent(name="slaanesh", seed="slaanesh recovery phrase", port=8001, endpoint=["http://localhost:8001/submit"])

In this example we’re running multiple agents from one file.

  1. Let’s now define sigmar’s behaviors. We need to define a function for sigmar to send messages to slaanesh periodically:
agent_communication.py
@sigmar.on_interval(period=3.0) async def send_message(ctx: Context): await ctx.send(slaanesh.address, Message(message="hello there slaanesh"))

We can use the .on_interval() decorator to define a coroutine send_message() function that will be called every 3 seconds. The coroutine function sends a message to slaanesh using the ctx.send() method of the Context object.

  1. We then need to define a sigmar_message_handler() function for sigmar to manage incoming messages:
agent_communication.py
@sigmar.on_message(model=Message) async def sigmar_message_handler(ctx: Context, sender: str, msg: Message): ctx.logger.info(f"Received message from {sender}: {msg.message}")

This defines the coroutine function sigmar_message_handler() that serves as a message handler for sigmar. It is triggered whenever sigmar receives a message of type Message.

  1. Let’s now define the behavior of our second agent, slaanesh:
agent_communication.py
@slaanesh.on_message(model=Message) async def slaanesh_message_handler(ctx: Context, sender: str, msg: Message): ctx.logger.info(f"Received message from {sender}: {msg.message}")

Same as sigmar however, we make slaanesh compose a response message to be sent back using the ctx.send() method with sigmar.address as the recipient address and an instance of the Message model as the message payload.

It would also be valid to respond to the sender:

agent_communication.py
await ctx.send(sigmar.address, Message(message="hello there sigmar"))
  1. Let’s then use the Bureau class to create a Bureau object. This will allow us to run agents together in the same script:
agent_communication.py
bureau = Bureau() bureau.add(sigmar) bureau.add(slaanesh) if __name__ == "__main__": bureau.run()
  1. Save the script.

The complete script should be looking as follows:

agent_communication.py
from uagents import Agent, Bureau, Context, Model class Message(Model): message: str sigmar = Agent(name="sigmar", seed="sigmar recovery phrase", port=8000, endpoint=["http://localhost:8000/submit"]) slaanesh = Agent(name="slaanesh", seed="slaanesh recovery phrase", port=8001, endpoint=["http://localhost:8001/submit"]) @sigmar.on_interval(period=3.0) async def send_message(ctx: Context): await ctx.send(slaanesh.address, Message(message="hello there slaanesh")) @sigmar.on_message(model=Message) async def sigmar_message_handler(ctx: Context, sender: str, msg: Message): ctx.logger.info(f"Received message from {sender}: {msg.message}") @slaanesh.on_message(model=Message) async def slaanesh_message_handler(ctx: Context, sender: str, msg: Message): ctx.logger.info(f"Received message from {sender}: {msg.message}") await ctx.send(sigmar.address, Message(message="hello there sigmar")) bureau = Bureau() bureau.add(sigmar) bureau.add(slaanesh) if __name__ == "__main__": bureau.run()

We are now ready to run the script: python agents_communication.py

The output would be:

WARNING: [sigmar]: No endpoints provided. Skipping registration: Agent won't be reachable. WARNING: [slaanesh]: No endpoints provided. Skipping registration: Agent won't be reachable. INFO: [bureau]: Starting server on http://0.0.0.0:8000 (Press CTRL+C to quit) INFO: [sigmar]: Received message from agent1q0mau8vkmg78xx0sh8cyl4tpl4ktx94pqp2e94cylu6haugt2hd7j9vequ7: hello there sigmar INFO: [slaanesh]: Received message from agent1qww3ju3h6kfcuqf54gkghvt2pqe8qp97a7nzm2vp8plfxflc0epzcjsv79t: hello there slaanesh INFO: [sigmar]: Received message from agent1q0mau8vkmg78xx0sh8cyl4tpl4ktx94pqp2e94cylu6haugt2hd7j9vequ7: hello there sigmar INFO: [slaanesh]: Received message from agent1qww3ju3h6kfcuqf54gkghvt2pqe8qp97a7nzm2vp8plfxflc0epzcjsv79t: hello there slaanesh INFO: [sigmar]: Received message from agent1q0mau8vkmg78xx0sh8cyl4tpl4ktx94pqp2e94cylu6haugt2hd7j9vequ7: hello there sigmar

However, these agents can only communicate with each other on their local network, let’s next see how we can register these agents to be able to communicate with many other agents in the open network.

Agents Remote Communication: the Almanac Contract

An agent must register to the Almanac contract to communicate, to search for other agents or be found. Agents can query this contract to retrieve an HTTP endpoint for a recipient agent. Registration in the Almanac requires paying a small fee, so make sure to have enough funds to allow for this. You can query the Almanac now, by using the search feature on Agentverse.

Whenever an agent registers in the Almanac, it must specify the service endpoints alongside a weight parameter for each endpoint provided. Agents trying to communicate with your agent will choose the service endpoints using a weighted random selection.

Here, we show you how to create two agents and make them remotely communicate by registering and using the Almanac Contract.

Walk-through

The first step would be to create two different Python scripts for this task, each one representing a remote agent:

Slaanesh:

windows
echo. > remote_agents_slaanesh.py

Sigmar:

windows
echo. > remote_agents_sigmar.py

Let’s start by defining the script for sigmar.

Sigmar

  1. In remote_agents_sigmar.py script, we would need to import the necessary classes from the uagents (Agent, Context, and Model). We then need to define the message structure for messages to be exchanged between agents using the class Model, as well as the RECIPIENT_ADDRESS (slaanesh’s address). Note that if you don’t know slaanesh’s address yet, you can use print(slaanesh.address) after defining agent slaanesh to get this information. This is the address towards which sigmar will send messages:
remote_agents_sigmar.py
from uagents import Agent, Context, Model class Message(Model): message: str RECIPIENT_ADDRESS = "agent1qvm7v76zs6w2x90xvq99yc5xh7c2thjtm44zc09me556zxnra627gkf4zum"
  1. Let’s now create our agent, sigmar, by providing name, seed, port, and endpoint:
remote_agents_sigmar.py
sigmar = Agent( name="sigmar", port=8000, seed="sigmar secret phrase", endpoint=["http://127.0.0.1:8000/submit"], )
  1. We are ready to define sigmar’s behaviors. Let’s start with a function for sigmar to send messages:
remote_agents_sigmar.py
@sigmar.on_interval(period=2.0) async def send_message(ctx: Context): await ctx.send(RECIPIENT_ADDRESS, Message(message="hello there slaanesh"))

Here, the .on_interval() decorator schedules the send_message() function to be run every 2 seconds. Inside the function, there is an asynchronous call indicated by the ctx.send() method. This call sends a message with the content "hello there slaanesh" to the RECIPIENT_ADDRESS.

  1. We then need to define a function for sigmar to handle incoming messages from other agents:
remote_agents_sigmar.py
@sigmar.on_message(model=Message) async def message_handler(ctx: Context, sender: str, msg: Message): ctx.logger.info(f"Received message from {sender}: {msg.message}") if __name__ == "__main__": sigmar.run()

Here, we have used the .on_message() decorator to register the message_handler() coroutine function as a handler for incoming messages of type Message.

The message_handler() function takes three arguments: ctx, sender, and msg. Inside this function, we call the ctx.logger.info() method to log information about the received message, including the sender and message content.

  1. We can now save the script.

The overall script for sigmar agent should be looking as follows:

remote_agents_sigmar.py
from uagents import Agent, Context, Model class Message(Model): message: str RECIPIENT_ADDRESS = "agent1qvm7v76zs6w2x90xvq99yc5xh7c2thjtm44zc09me556zxnra627gkf4zum" sigmar = Agent( name="sigmar", port=8000, seed="sigmar secret phrase", endpoint=["http://127.0.0.1:8000/submit"], ) @sigmar.on_interval(period=2.0) async def send_message(ctx: Context): await ctx.send(RECIPIENT_ADDRESS, Message(message="hello there slaanesh")) @sigmar.on_message(model=Message) async def message_handler(ctx: Context, sender: str, msg: Message): ctx.logger.info(f"Received message from {sender}: {msg.message}") if __name__ == "__main__": sigmar.run()

Remember that you need to provide the name, seed, port, endpoint and RECIPIENT_ADDRESS parameters to correctly run this code.

We can now proceed by writing the script for agent slaanesh.

Slaanesh

  1. In remote_agents_slaanesh.py script, import the necessary classes from the uagents. Then, define the message structure for messages to be exchanged between the agents using the Model class, as well as our second uAgent, slaanesh, by providing name, seed, port, and endpoint:
remote_agents_slaanesh.py
from uagents import Agent, Context, Model class Message(Model): message: str slaanesh = Agent( name="slaanesh", port=8001, seed="slaanesh secret phrase", endpoint=["http://127.0.0.1:8001/submit"], )
  1. Let’s now define a function for slaanesh to handle incoming messages and answering back to the sender:
remote_agents_slaanesh.py
@slaanesh.on_message(model=Message) async def message_handler(ctx: Context, sender: str, msg: Message): ctx.logger.info(f"Received message from {sender}: {msg.message}") await ctx.send(sender, Message(message="hello there sigmar")) if __name__ == "__main__": slaanesh.run()

Here, we have defined an asynchronous message_handler() function for slaanesh to handle incoming messages from other uAgents. The function is decorated with .on_message(), and it is triggered whenever a message of type Message is received by slaanesh. When a message is received, the handler function logs the sender’s address and the content of the message. It then sends a response back to the sender using the ctx.send() with a new message. The response message contains the Message data model with a "hello there sigmar" message.

  1. Save the script.

The overall script for slaanesh should be looking as follows:

remote_agents_slaanesh.py
from uagents import Agent, Context, Model class Message(Model): message: str slaanesh = Agent( name="slaanesh", port=8001, seed="slaanesh secret phrase", endpoint=["http://127.0.0.1:8001/submit"], ) @slaanesh.on_message(model=Message) async def message_handler(ctx: Context, sender: str, msg: Message): ctx.logger.info(f"Received message from {sender}: {msg.message}") await ctx.send(sender, Message(message="hello there sigmar")) if __name__ == "__main__": slaanesh.run()

Remember that you need to provide the name, seed, port and endpoint parameters to correctly run this code.

Run the scripts

In different terminal windows, first run slaanesh and then sigmar. They will register automatically in the Almanac contract using their funds. The received messages will print out in each terminal:

Terminal 1: python remote_agents_slaanesh.py

Terminal 2: python remote_agents_sigmar.py

The output will depend on the terminal:

  • Sigmar:

    INFO: [sigmar]: Registration on Almanac API successful INFO: [sigmar]: Registering on almanac contract... INFO: [sigmar]: Registering on almanac contract...complete INFO: [sigmar]: Agent inspector available at https://agentverse.ai/inspect/?uri=http%3A//127.0.0.1%3A8000&address=agent1qvwqu6a0km09mq4f6j6kmke9smswmgcergmml9a54av9449rqtmmxy4qwe6 INFO: [sigmar]: Starting server on http://0.0.0.0:8000 (Press CTRL+C to quit) INFO: [sigmar]: Received message from agent1qvm7v76zs6w2x90xvq99yc5xh7c2thjtm44zc09me556zxnra627gkf4zum: hello there sigmar INFO: [sigmar]: Received message from agent1qvm7v76zs6w2x90xvq99yc5xh7c2thjtm44zc09me556zxnra627gkf4zum: hello there sigmar INFO: [sigmar]: Received message from agent1qvm7v76zs6w2x90xvq99yc5xh7c2thjtm44zc09me556zxnra627gkf4zum: hello there sigmar
  • Slaanesh:

    INFO: [slaanesh]: Registration on Almanac API successful INFO: [slaanesh]: Registering on almanac contract... INFO: [slaanesh]: Registering on almanac contract...complete INFO: [slaanesh]: Agent inspector available at https://agentverse.ai/inspect/?uri=http%3A//127.0.0.1%3A8001&address=agent1qvm7v76zs6w2x90xvq99yc5xh7c2thjtm44zc09me556zxnra627gkf4zum INFO: [slaanesh]: Starting server on http://0.0.0.0:8001 (Press CTRL+C to quit) INFO: [slaanesh]: Received message from agent1qvwqu6a0km09mq4f6j6kmke9smswmgcergmml9a54av9449rqtmmxy4qwe6: hello there slaanesh INFO: [slaanesh]: Received message from agent1qvwqu6a0km09mq4f6j6kmke9smswmgcergmml9a54av9449rqtmmxy4qwe6: hello there slaanesh INFO: [slaanesh]: Received message from agent1qvwqu6a0km09mq4f6j6kmke9smswmgcergmml9a54av9449rqtmmxy4qwe6: hello there slaanesh

Before we go on…

As we touched on before in Register in Almanac, when the agent uses .run() function this tells the uagents library to register the agent to the Almanac. It’s simple, agents initialize themselves, and register to a service which acts as a search engine for agents (the Almanac) then, when agents receive messages they can respond.

Conclusion

In this guide, we explored two different methods of communication for Agents using the uagents library:

  • Local communication.
  • Remote communication via the Almanac Contract.

For local communication, we learned how to use the uagents library to create two agents, sigmar and slaanesh, and enable them to exchange messages with one another. We defined the message structure using the Model class and implemented message handlers for both agents. By running the script we observed their real-time message exchange.

Next, we delved into remote communication, which facilitates interaction between agents through the Almanac Contract. This method requires registering the agents in the Almanac Contract and querying for HTTP endpoints for communication. By running the scripts separately, we could observe the real-time messages exchange, fostering a decentralized network of interacting agents.

With this, we suspect you’re ready to start building agents, as part of multi-agent system the Almanac allows; awesome. If you want to go further though, take a look at the message verification and sending tokens, after-all you do want to be sure you are speaking to whom you think you are, and agents getting paid is awesome.

Last updated on