Communicating with other uAgents π±π€
Introduction
Communication is an essential feature of any agent network. It allows agents to work together, exchange information, and collaborate towards common goals. Within the uAgents (micro-agents) Framework, there are various ways through which uAgents can communicate, each offering unique advantages and use cases.
In this guide, we will explore two methods of communication between uAgents:
- Local communication.
- Remote communication via the Almanac Contract βοΈοΈ, and the Agentverse Mailroom / IoT Gateway π« βοΈοΈ service.
We want to start by introducing you to the concept of 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. Moreover, local communication is important for debugging purposes.
Let's get started!
uAgents: Local Communication
Walk-through
The first step to better understand how uAgents communicate is to introduce how 2 uAgents perform a local communication. Letβs consider a basic example in which two uAgents say hello to each other.
-
First of all, let's create a Python script for this task and name it:
touch agents_communication.py
-
Then, let's import these necessary classes from the uagents library:
Agent
,Context
,Bureau
, andModel
Let's then define the message structure for messages to be exchanged between the uAgents using the classModel
. Then, we would need to create the uAgents,alice
andbob
, with name and seed parameters.from uagents import Agent, Bureau, Context, Model class Message(Model): message: str alice = Agent(name="alice", seed="alice recovery phrase") bob = Agent(name="bob", seed="bob recovery phrase")
-
Let's now define
alice
's behaviors. We need to define a function foralice
to send messages to bob periodically:@alice.on_interval(period=3.0) async def send_message(ctx: Context): await ctx.send(bob.address, Message(message="hello there bob"))
We can use the
.on_interval()
decorator to define a coroutinesend_message()
function that will be called every 3 seconds. The coroutine function sends a message tobob
using thectx.send()
method of theContext
object. -
We then need to define a
alice_message_handler()
function foralice
to manage incoming messages:@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}")
This defines the coroutine function
alice_message_handler()
that serves as a message handler foralice
. It is triggered whenever alice receives a message of typeMessage
. The function logs the received message and its sender using thectx.logger.info()
method. -
Let's now define the behavior of our second agent,
bob
:@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"))
Here, we have defined a coroutine
bob_message_handler()
function that serves as the message handler forbob
. It is triggered whenever bob receives a message of typeMessage
from other agents. The function logs the received message and its sender using thectx.logger.info()
method. Finally, we makebob
compose a response message to be sent back using thectx.send()
method withalice.address
as the recipient address and an instance of theMessage
model as the message payload. -
Let's then use the
Bureau
class to create aBureau
object. This will allow us to run uAgents together in the same script.bureau = Bureau() bureau.add(alice) bureau.add(bob) if __name__ == "__main__": bureau.run()
-
Save the script.
The complete script should be looking as follows:
from uagents import Agent, Bureau, Context, Model
class Message(Model):
message: str
alice = Agent(name="alice", seed="alice recovery phrase")
bob = Agent(name="bob", seed="bob recovery phrase")
@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 now ready to run the script: python agents_communication.py
The output would be:
[alice]: Received message from agent1q0mau8vkmg78xx0sh8cyl4tpl4ktx94pqp2e94cylu6haugt2hd7j9vequ7: hello there alice
[ bob]: Received message from agent1qww3ju3h6kfcuqf54gkghvt2pqe8qp97a7nzm2vp8plfxflc0epzcjsv79t: hello there bob
[alice]: Received message from agent1q0mau8vkmg78xx0sh8cyl4tpl4ktx94pqp2e94cylu6haugt2hd7j9vequ7: hello there alice
[ bob]: Received message from agent1qww3ju3h6kfcuqf54gkghvt2pqe8qp97a7nzm2vp8plfxflc0epzcjsv79t: hello there bob
[alice]: Received message from agent1q0mau8vkmg78xx0sh8cyl4tpl4ktx94pqp2e94cylu6haugt2hd7j9vequ7: hello there alice
uAgents Remote Communication: the Almanac Contract
Remote uAgents communication is achieved by registering the uAgents into the Almanac contract βοΈοΈ and then querying it to retrieve an HTTP endpoint from the recipient uAgent. Registration in the Almanac requires paying a small fee, so make sure to have enough funds to allow for this.
Whenever a uAgent registers in the Almanac, it must specify the service endpoints βοΈοΈ alongside a weight parameter for each endpoint provided. Agents trying to communicate with your uAgent, will choose the service endpoints using a weighted random selection.
Here, we show you how to create two uAgents 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 uAgent:
Bob: touch remote_agents_bob.py
Alice: touch remote_agents_alice.py
Let's start by defining the script for alice.
Alice
-
In
remote_agents_alice.py
script, we would need to import the necessary classes from theuagents
(Agent
,Context
, andModel
) and fromuagents.setup
(fund_agent_if_low
). We then need to define the message structure for messages to be exchanged between agents using the classModel
, as well as theRECIPIENT_ADDRESS
. This is the address to whichalice
will send messages:from uagents import Agent, Context, Model from uagents.setup import fund_agent_if_low class Message(Model): message: str RECIPIENT_ADDRESS="agent1q2kxet3vh0scsf0sm7y2erzz33cve6tv5uk63x64upw5g68kr0chkv7hw50"
-
Let's now create our uAgent,
alice
, by providingname
,seed
,port
, andendpoint
. Also, make sure it has enough funds to register in the Almanac contract.alice = Agent( name="alice", port=8000, seed="alice secret phrase", endpoint=["http://127.0.0.1:8000/submit"], ) fund_agent_if_low(alice.wallet.address())
On the Fetch.ai testnet, you can use the
fund_agent_if_low
function. This one checks if the balance of the uAgent's wallet is below a certain threshold, and if so, sends a transaction to fund the wallet with a specified amount of cryptocurrency. In this case, it checks if the balance ofalice
's wallet is low and funds it if necessary. -
We are ready to define
alice
's behaviors. Let's start with a function foralice
to send messages:@alice.on_interval(period=2.0) async def send_message(ctx: Context): await ctx.send(RECIPIENT_ADDRESS, Message(message="hello there bob")
Here, the
.on_interval()
decorator schedules thesend_message()
coroutine function to be run every 2 seconds. Inside the function, there is an asynchronous call indicated by thectx.send()
method. This call sends a message with the content"hello there bob"
to theRECIPIENT_ADDRESS
. -
We then need to define a function for
alice
to handle incoming messages from other agents:@alice.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__": alice.run()
Here, we have used the
.on_message()
decorator to register themessage_handler()
coroutine function as a handler for incoming messages of typeMessage
.The
message_handler()
function takes three arguments:ctx
,sender
, andmsg
. Inside this function, we call thectx.logger.info()
method to log information about the received message, including the sender and message content. -
We can now save the script.
The overall script for alice agent should be looking as follows:
from uagents import Agent, Context, Model
from uagents.setup import fund_agent_if_low
class Message(Model):
message: str
RECIPIENT_ADDRESS="agent1q2kxet3vh0scsf0sm7y2erzz33cve6tv5uk63x64upw5g68kr0chkv7hw50"
alice = Agent(
name="alice",
port=8000,
seed="alice secret phrase",
endpoint=["http://127.0.0.1:8000/submit"],
)
fund_agent_if_low(alice.wallet.address())
@alice.on_interval(period=2.0)
async def send_message(ctx: Context):
await ctx.send(RECIPIENT_ADDRESS, Message(message="hello there bob"))
@alice.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__":
alice.run()
We can now proceed by writing the script for agent bob
.
Bob
-
In
remote_agents_bob.py
script, import the necessary classes from theuagents
anduagents.setup
. Then, define the message structure for messages to be exchanged between the uAgents using theModel
class, as well as our second uAgent,bob
, by providingname
,seed
,port
, andendpoint
. Make sure it has enough funds to register in the Almanac contract.from uagents import Agent, Context, Model from uagents.setup import fund_agent_if_low class Message(Model): message: str bob = Agent( name="bob", port=8001, seed="bob secret phrase", endpoint=["http://127.0.0.1:8001/submit"], ) fund_agent_if_low(bob.wallet.address())
-
Let's now define a function for
bob
to handle incoming messages and answering back to the sender:@bob.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 alice")) if __name__ == "__main__": bob.run()
Here, we have defined an asynchronous
message_handler()
function for bob to handle incoming messages from other uAgents. The function is decorated with.on_message()
, and it is triggered whenever a message of typeMessage
is received bybob
. 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 thectx.send()
with a new message. The response message contains theMessage
data model with a"hello there alice"
message. -
Save the script.
The overall script for bob should be looking as follows:
from uagents.setup import fund_agent_if_low
from uagents import Agent, Context, Model
class Message(Model):
message: str
bob = Agent(
name="bob",
port=8001,
seed="bob secret phrase",
endpoint=["http://127.0.0.1:8001/submit"],
)
fund_agent_if_low(bob.wallet.address())
@bob.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 alice"))
if __name__ == "__main__":
bob.run()
Run the scripts
In different terminal windows, first run bob
and then alice
from different terminals. 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_bob.py
Terminal 2: python remote_agents_alice.py
The output will depend on the terminal:
-
Alice:
[alice]: Received message from agent1q2kxet3vh0scsf0sm7y2erzz33cve6tv5uk63x64upw5g68kr0chkv7hw50: hello there alice [alice]: Received message from agent1q2kxet3vh0scsf0sm7y2erzz33cve6tv5uk63x64upw5g68kr0chkv7hw50: hello there alice [alice]: Received message from agent1q2kxet3vh0scsf0sm7y2erzz33cve6tv5uk63x64upw5g68kr0chkv7hw50: hello there alice
-
Bob:
[ bob]: Received message from agent1qdp9j2ev86k3h5acaayjm8tpx36zv4mjxn05pa2kwesspstzj697xy5vk2a: hello there bob [ bob]: Received message from agent1qdp9j2ev86k3h5acaayjm8tpx36zv4mjxn05pa2kwesspstzj697xy5vk2a: hello there bob [ bob]: Received message from agent1qdp9j2ev86k3h5acaayjm8tpx36zv4mjxn05pa2kwesspstzj697xy5vk2a: hello there bob
uAgents Remote Communication: the AgentVerse Mailbox Service
uAgents can communicate remotely by also using the Agentverse βοΈ (opens in a new tab)οΈ. The Agentverse is a platform that aims at creating a decentralized network of agents capable of communicating and interacting with each other. Agents in the Agentverse can send and receive messages, execute tasks, and collaborate with other agents to achieve various goals.
In this guide, we want to show how to enable remote communications between uAgents using the Agentverse Mailbox Service βοΈοΈ.
Walk-through
We make use of the uAgents Remote Communication guide above, but now, we specify the Mailbox server and the API Key for our uAgents.
Alice
-
First of all, let's create a script for
alice
:touch alice.py
-
We need to import the necessary classes from
uagents
(Agent
,Context
,Model
) anduagents.setup
(fund_agent_if_low
), and define theMessage
class for messages to be exchanged between our uAgents. We also need to generate a secureSEED_PHRASE
(e.g., https://pypi.org/project/mnemonic/ (opens in a new tab)) and get the address of our agent, which is needed to register it to create aMailbox
, alongside a name (i.e.,alice
in this case). Following this, we would need to sign up at Agentverse βοΈ (opens in a new tab) to get anAPI key
:from uagents import Agent, Context, Model from uagents.setup import fund_agent_if_low class Message(Model): message: str SEED_PHRASE = "put_your_seed_phrase_here" print(f"Your agent's address is: {Agent(seed=SEED_PHRASE).address}") API_KEY = "put_your_API_key_here"
-
_Now your agent is ready to join the Agentverse!__ We can now register our uAgent,
alice
, by providingname
,seed
, andmailbox
. Make sure your agent has enough funds for this:agent = Agent( name="alice", seed=SEED_PHRASE, mailbox=f"{API_KEY}@wss://agentverse.ai", ) fund_agent_if_low(agent.wallet.address())
On the Fetch.ai testnet, you can use the
fund_agent_if_low
function. This one checks if the balance of the uAgentβs wallet is below a certain threshold, and if so, sends a transaction to fund the wallet with a specified amount of cryptocurrency. -
Let's define a message handler function for
alice
:@agent.on_message(model=Message, replies={Message}) async def handle_message(ctx: Context, sender: str, msg: Message): ctx.logger.info(f"Received message from {sender}: {msg.message}") ctx.logger.info("Sending message to bob") await ctx.send(sender, Message(message="hello there bob")) if __name__ == "__main__": agent.run()
We have defined a
handle_message()
coroutine function that serves as the message handler for the agent. It is triggered whenever the agent receives a message of typeMessage
. This function logs the received message and its sender using thectx.logger.info()
method. It then sends a response message back to the sender using thectx.send()
method with the sender address and an instance of theMessage
model. -
Save the script.
The overall script for alice
should look as follows:
from uagents import Agent, Context, Model
from uagents.setup import fund_agent_if_low
class Message(Model):
message: str
SEED_PHRASE = "put_your_seed_phrase_here"
print(f"Your agent's address is: {Agent(seed=SEED_PHRASE).address}")
API_KEY = "put_your_API_key_here"
agent = Agent(
name="alice",
seed=SEED_PHRASE,
mailbox=f"{API_KEY}@wss://agentverse.ai",
)
fund_agent_if_low(agent.wallet.address())
@agent.on_message(model=Message, replies={Message})
async def handle_message(ctx: Context, sender: str, msg: Message):
ctx.logger.info(f"Received message from {sender}: {msg.message}")
ctx.logger.info("Sending message to bob")
await ctx.send(sender, Message(message="hello there bob"))
if __name__ == "__main__":
agent.run()
Remember that you need to generate your SEED_PHRASE and API_KEY and substitute these into the above required fields for the script to run correctly.
Bob
-
Let's now create another Python script for bob:
touch bob.py
-
We can now import the necessary classes from
uagents
anduagents.setup
, and define theMessage
class for messages to be exchanged between our uAgents. We then need to defineALICE_ADDRESS
by copying the address generated in the script foralice
above, as well as generate a secondSEED_PHRASE
(e.g. https://pypi.org/project/mnemonic/ (opens in a new tab)), and get the address for our agent, which is needed to register it to create aMailbox
, alongside a name (i.e.,bob
in this case). Like foralice
, head towards the Agentverse βοΈ (opens in a new tab)οΈ to get theAPI key
forbob
:from uagents import Agent, Context, Model from uagents.setup import fund_agent_if_low class Message(Model): message: str ALICE_ADDRESS = "paste_alice_address_here" SEED_PHRASE = "put_your_seed_phrase_here" print(f"Your agent's address is: {Agent(seed=SEED_PHRASE).address}") API_KEY = "put_your_API_key_here"
-
Now your agent is ready to join the Agentverse! Let's register this second uAgent,
bob
, by providingname
,seed
, andmailbox
server:agent = Agent( name="bob", seed=SEED_PHRASE, mailbox=f"{API_KEY}@wss://agentverse.ai", ) fund_agent_if_low(agent.wallet.address())
-
We can now define a function for
bob
to send messages:@agent.on_interval(period=2.0) async def send_message(ctx: Context): ctx.logger.info("Sending message to alice") await ctx.send(ALICE_ADDRESS, Message(message="hello there alice"))
Here, we have defined a
send_message()
coroutine function that is scheduled to run periodically every 2 seconds using the.on_interval()
decorator. Inside the coroutine function, a message of typeMessage
is sent toalice
's address using thectx.send(9
) method. -
Let's now define a message handler for bob to handle incoming messages:
@agent.on_message(model=Message, replies=set()) async def on_message(ctx: Context, sender: str, msg: Message): ctx.logger.info(f"Received message from {sender}: {msg.message}") if __name__ == "__main__": agent.run()
Here, we have set up an
on_message()
function for bob to handle messages of typeMessage
. When a message of this type is received bybob
, the message handler function logs the sender's address and the content of the message usingctx.logger.info()
method. -
Save the script.
The overall script for bob
should look as follows:
from uagents import Agent, Context, Model
from uagents.setup import fund_agent_if_low
class Message(Model):
message: str
ALICE_ADDRESS = "paste_alice_address_here"
SEED_PHRASE = "put_your_seed_phrase_here"
print(f"Your agent's address is: {Agent(seed=SEED_PHRASE).address}")
API_KEY = "put_your_API_key_here"
bob = Agent(
name="bob",
seed=SEED_PHRASE,
mailbox=f"{API_KEY}@wss://agentverse.ai",
)
fund_agent_if_low(bob.wallet.address())
@bob.on_interval(period=2.0)
async def send_message(ctx: Context):
ctx.logger.info("Sending message to alice")
await ctx.send(ALICE_ADDRESS, Message(message="hello there alice"))
@bob.on_message(model=Message, replies=set())
async def on_message(ctx: Context, sender: str, msg: Message):
ctx.logger.info(f"Received message from {sender}: {msg.message}")
if __name__ == "__main__":
bob.run()
Remember that you need to generate your SEED_PHRASE and API_KEY and substitute these into the above required fields for the script to run correctly. Here, you also need to provide bob with an ALICE_ADDRESS field.
Run the scripts
Now, we are ready to run our scripts. Run alice
and bob
from different terminals. The received messages will print out in each terminal:
Bob: python bob.py
Alice: python alice.py
The output should be as follows depending on the terminal:
-
Alice:
[alice]: Received message from agent1q0p2mhaqjv46wmsn6jx3lnggwffnpuzsuy70ax6np07wdhlkjqytx7fq73t: hello there alice [alice]: Sending message to bob [alice]: Received message from agent1q0p2mhaqjv46wmsn6jx3lnggwffnpuzsuy70ax6np07wdhlkjqytx7fq73t: hello there alice [alice]: Sending message to bob [alice]: Received message from agent1q0p2mhaqjv46wmsn6jx3lnggwffnpuzsuy70ax6np07wdhlkjqytx7fq73t: hello there alice
-
Bob:
[ bob]: Received message from agent1qvgt6q5ld2q6fm4x3xaptppgl5gpa5p3xx9g38n5ds3eht9h0gyn5nq0nl7: hello there bob [ bob]: Sending message to alice [ bob]: Received message from agent1qvgt6q5ld2q6fm4x3xaptppgl5gpa5p3xx9g38n5ds3eht9h0gyn5nq0nl7: hello there bob [ bob]: Sending message to alice [ bob]: Received message from agent1qvgt6q5ld2q6fm4x3xaptppgl5gpa5p3xx9g38n5ds3eht9h0gyn5nq0nl7: hello there bob
Conclusion
In this comprehensive guide, we explored two different methods of communication for uAgents using the uagents library:
- Local communication.
- Remote communication via the Almanac Contract or the AgentVerse Mailbox Service.
For local communication, we learned how to use the uagents library to create two uAgents, alice and bob, 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 uAgents 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.
In addition, we introduced the AgentVerse Mailbox Service to facilitate remote communication through the usage of the AgentVerse Explorer. We demonstrated the communication process by creating alice and bob scripts and utilizing their respective API keys to communicate remotely via such service. The decentralized network enabled messages transfer and information sharing between the remote agents. By following this last approach, users can leverage the power of the AgentVerse Explorer to build intricate networks of remotely interacting uAgents.
This opens up a world of possibilities for collaborative decision-making, distributed problem-solving, and decentralized coordination across a myriad of applications and domains. With this newfound knowledge, developers can harness the potential of autonomous agents to create innovative solutions and use cases in a rapidly evolving digital and decentralized landscape.