Agents name service
Introduction
A name service, in the context of computer networks and distributed systems, is a system which associates meaningful names with network resources. This is carried out to make it easier for humans to identify and access these resources rather than using numerical IP addresses. The Domain Name System (DNS) is a widely used name service on the internet. The DNS can be thought as the internet's phonebook. Domain names (for instance, example.com), are used by humans to access content online. Web browsers communicate using Internet Protocol (IP) addresses. DNS converts domain names to IP addresses so that browsers may access Internet resources. Each Internet-connected device has a unique IP address that other machines may use to locate that specific device. DNS servers remove the need for people to remember IP addresses by heart.
In the context of Fetch.ai's AI Agents, we have implemented a name service for agents. to allow for quicker identification thanks to human-readable names instead of other identifiers and address. For a better understanding of this, we will create two agents, alice
an bob
, register them in the Almanac ↗️, but with agent bob
being given a domain so to be easily found by any other agent.
Walk-through
For this example guide, we will make use of the uagents
and cosmpy
libraries. For further understanding and resources visit our documentation for either the uAgents Framework ↗️ and CosmPy ↗️, as well as Guides ↗️ and References ↗️ sections.
The first step we need to carry out is creating a dedicated directory for this task. Let's call this directory name_service. You can do so by running the following command: mkdir name_service
.
Within this directory, we need to create two separate scripts, one for each agent we will develop:
- Bob:
touch agent_1.py
- Alice:
touch agent_2.py
Let's get started!
Agent 1: bob
We start by defining the script for our agent bob
.
-
First of all, let's import all needed classes from
cosmpy.aerial.wallet
,uagents.network
,uagents.setup
, anduagents
. Then, we define a message data model for types of messages to be exchanged between our agents:from cosmpy.aerial.wallet import LocalWallet from uagents.network import get_name_service_contract from uagents.setup import fund_agent_if_low from uagents import Agent, Context, Model class Message(Model): message: str
-
We then need to initialize our agent
bob
and register it in the Almanac ↗️. We also initialize a wallet by ensuring it has enough funds in it, and then providebob
with a domain:bob = Agent( name="bob-0", seed="agent bob-0 secret phrase", port=8001, endpoint=["http://localhost:8001/submit"], ) my_wallet = LocalWallet.from_unsafe_seed("registration test wallet") name_service_contract = get_name_service_contract(test=True) DOMAIN = "agent" for wallet in [my_wallet, bob.wallet]: fund_agent_if_low(wallet.address())
Here,
bob
is initialized with a uniquename
, a secretseed
for authentication, a specificport
for communication, and anendpoint
for submitting data. For additional information on endpoints, visit our documentation ↗️. A local wallet namedmy_wallet
is then created using theseed
phrase"registration test wallet"
.The
name_service_contract
variable is created to hold an instance of a name service contract related to registering and managing agent names. Thetest=True
parameter indicates that this is a test environment.DOMAIN
is set to"agent"
, indicating the domain for the agent names. Thefor
loop iterates over the list of wallets (my_wallet
andbob.wallet
): for each wallet, it checks if the wallet's balance is low and if so, it funds the wallet usingfund_agent_if_low()
. -
We now need to define the behavior and functions of `bob agent.
@bob.on_event("startup") async def register_agent_name(ctx: Context): await name_service_contract.register( bob.ledger, my_wallet, ctx.address, ctx.name, DOMAIN ) @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}") if __name__ == "__main__": bob.run()
Here, we defined a
.on_event("startup")
decorator on the register_agent_name() function. This decorator indicates that the function is executed when the agent starts up. The function takes aContext
object as parameter, and registers the agent's name and domain using thename_service_contract
variable we previously defined. The function uses information from the context (ctx
) including the agent's ledger, wallet, address, name, and domain. -
Save the script.
The overall script should look as follows:
from cosmpy.aerial.wallet import LocalWallet
from uagents.network import get_name_service_contract
from uagents.setup import fund_agent_if_low
from uagents import Agent, Context, Model
class Message(Model):
message: str
bob = Agent(
name="bob-0",
seed="agent bob-0 secret phrase",
port=8001,
endpoint=["http://localhost:8001/submit"],
)
my_wallet = LocalWallet.from_unsafe_seed("registration test wallet")
name_service_contract = get_name_service_contract(test=True)
DOMAIN = "agent"
for wallet in [my_wallet, bob.wallet]:
fund_agent_if_low(wallet.address())
@bob.on_event("startup")
async def register_agent_name(ctx: Context):
await name_service_contract.register(
bob.ledger, my_wallet, ctx.address, ctx.name, DOMAIN
)
@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}")
if __name__ == "__main__":
bob.run()
Agent 2: alice
We can now define the script for our second agent alice
.
-
First of all, let's import all needed classes from
uagents.network
,uagents.setup
, anduagents
. Then, we need to define a message data model for types of messages to be exchanged between our agents:from uagents.setup import fund_agent_if_low from uagents import Agent, Context, Model class Message(Model): message: str
-
We then need to initialize our agent
alice
and register it in the Almanac ↗️ making sure it has enough funds in its wallet to register:alice = Agent( name="alice-0", seed="agent alice-0 secret phrase", port=8000, endpoint=["http://localhost:8000/submit"], ) fund_agent_if_low(alice.wallet.address())
-
We can now define the behavior of
alice
agent:@alice.on_interval(period=5) async def alice_interval_handler(ctx: Context): bob_name = "bob-0.agent" ctx.logger.info(f"Sending message to {bob_name}...") await ctx.send(bob_name, Message(message="Hello there bob.")) if __name__ == "__main__": alice.run()
Here we defined a
alice_interval_handler()
function. This function is a decorated with a.on_interval()
decorator indicating that the function is executed at intervals of 5 seconds. This function sends a message tobob
agent taking into account its name"bob-0.agent"
. Themessage
being sent contains the a"Hello there bob."
message -
Save the script.
The overall script should look as follows:
from uagents.setup import fund_agent_if_low
from uagents import Agent, Context, Model
class Message(Model):
message: str
alice = Agent(
name="alice-0",
seed="agent alice-0 secret phrase",
port=8000,
endpoint=["http://localhost:8000/submit"],
)
fund_agent_if_low(alice.wallet.address())
@alice.on_interval(period=5)
async def alice_interval_handler(ctx: Context):
bob_name = "bob-0.agent"
ctx.logger.info(f"Sending message to {bob_name}...")
await ctx.send(bob_name, Message(message="Hello there bob."))
if __name__ == "__main__":
alice.run()
Run the scripts
We are now ready to run the scripts. Remember to check if you are in the correct directory and to correctly activate your virtual environment.
Importantly, run agent_1.py
before running agent_2.py
scripts and run them from different terminals:
-
Terminal 1:
python agent_1.py
-
Terminal 2:
python agent_2.py
The output should be as follows, depending on the terminal:
-
bob
[bob-0]: Registering on almanac contract... [bob-0]: Registering on almanac contract...complete [network]: Registering name... [network]: Registering name...complete [bob-0]: Starting server on http://0.0.0.0:8001 (Press CTRL+C to quit) [bob-0]: Received message from agent1qwquu2d237gntfugrnwch38g8jkl76vdr05qjm4wyps6ap04fvt8vtzhpqw: Hello there bob. [bob-0]: Received message from agent1qwquu2d237gntfugrnwch38g8jkl76vdr05qjm4wyps6ap04fvt8vtzhpqw: Hello there bob. [bob-0]: Received message from agent1qwquu2d237gntfugrnwch38g8jkl76vdr05qjm4wyps6ap04fvt8vtzhpqw: Hello there bob.
-
alice
[alice-0]: Registering on almanac contract... [alice-0]: Registering on almanac contract...complete [alice-0]: Starting server on http://0.0.0.0:8000 (Press CTRL+C to quit) [alice-0]: Sending message to bob-0.agent... [alice-0]: Sending message to bob-0.agent... [alice-0]: Sending message to bob-0.agent...