Skip to content

Commit

Permalink
content(docs): add uagents broadcast guide (#204)
Browse files Browse the repository at this point in the history
  • Loading branch information
FelixNicolaeBucsa authored Oct 18, 2023
1 parent b24e62b commit 762ba16
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 2 deletions.
9 changes: 9 additions & 0 deletions pages/guides.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,15 @@ import { GuideBox } from "../components/feature-guide-tabs";
}}
/>

<GuideBox
content={{
title: "AI Agents: broadcast 📡",
description:
"A guide showing how to use uAgents Framework to broadcast messages using protocols.",
path: "/guides/agents/broadcast",
}}
/>

</Grid3>

## Agentverse
Expand Down
3 changes: 2 additions & 1 deletion pages/guides/agents/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
"booking-demo": "How to book a table at a restaurant using agents",
"message-verification": "How to use agents to verify messages \uD83D\uDCEC\uD83D\uDD10",
"send-tokens": "How to use agents to send tokens \uD83D\uDCCA\uD83D\uDCB8",
"cleaning-demo": "How to use the agents to simulate a cleaning scenario"
"cleaning-demo": "How to use the agents to simulate a cleaning scenario ✨",
"broadcast": "AI Agents: broadcast \uD83D\uDCE1"
}
176 changes: 176 additions & 0 deletions pages/guides/agents/broadcast.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { Callout } from 'nextra/components'

# AI Agents: broadcast 📡

## Introduction

**Protocols** cover a very important role within the uAgents Framework. [Protocols ↗️](/references/uagents/uagents-protocols/agent-protocols) define the standards through which agents communication takes place. In this example, we will be showcasing a scenario where three agents, named `alice`, `bob`, and `charles`, use a custom protocol to communicate. In the example, Alice and Bob support the protocol, whereas Charles attempts to send broadcast messages to all agents using the protocol.

**Let's get started!**

## Walk-through

1. First of all, let's create a Python script for this task, and name it: `touch broadcast.py`

2. We then need to import the `Agent`, `Bureau`, `Context`, `Model`, and `Protocol` classes from the `uagents` library, and the `fund_agent_if_low` from `uagents.setup`. Then, let's create the 3 different agents using the class `Agent`. Each agent is initialized with a unique name and a seed phrase for wallet recovery. Additionally, if an agent's wallet balance is low, the `fund_agent_if_low()` function is called to add funds to their wallet:

```py copy
from uagents import Agent, Bureau, Context, Model, Protocol
from uagents.setup import fund_agent_if_low

alice = Agent(name="alice", seed="alice recovery phrase")
bob = Agent(name="bob", seed="bob recovery phrase")
charles = Agent(name="charles", seed="charles recovery phrase")

fund_agent_if_low(alice.wallet.address())
fund_agent_if_low(bob.wallet.address())
fund_agent_if_low(charles.wallet.address())
```

It is optional but useful to include a `seed` parameter when creating an agent to set fixed [addresses ↗️](/guides/agents/getting-uagent-address)️. Otherwise, random addresses will be generated every time you run the agent.

3. Let's then define two message data models to define the type of messages being handled and exchanged by the agents. We define a `BroadcastExampleRequest` and a `BroadcastExampleResponse` data models. Finally, create a `protocol` named `proto` with version `1.0`:

```py copy
class BroadcastExampleRequest(Model):
pass

class BroadcastExampleResponse(Model):
text: str

proto = Protocol(name="proto", version="1.0")
```

4. Let's now define a message handler function for incoming messages of type `BroadcastExampleRequest` in the protocol:

```py copy
@proto.on_message(model=BroadcastExampleRequest, replies=BroadcastExampleResponse)
async def handle_request(ctx: Context, sender: str, _msg: BroadcastExampleRequest):
await ctx.send(sender, BroadcastExampleResponse(text=f"Hello from {ctx.name}"))
```

Here we defined a `handle_request()` function which is used whenever a request is received. This sends a response back to the sender. This function is decorated with the `.on_message()` decorator indicating that this function is triggered whenever a message of type `BroadcastExampleRequest` is received. The function sends a response containing a greeting message with the name of the agent that sent the request in the first place.

5. Now, we need to include the `protocol` into the agents. Specifically, the protocol is included in both `alice` and `bob` agents. This means they will follow the rules defined in the protocol when communicating:

```py copy
alice.include(proto)
bob.include(proto)
```

<Callout type="info" emoji="ℹ️">
Note: after the first registration in the [Almanac ↗️](/references/contracts/uagents-almanac/almanac-overview) smart contract, it will take about 5 minutes before the agents can be found through the protocol.
</Callout>

6. It is now time to define the behavior and function of `charles` agent.

```py copy
@charles.on_interval(period=5.0)
async def say_hello(ctx: Context):
await ctx.experimental_broadcast(proto.digest, message=BroadcastExampleRequest())

@charles.on_message(model=BroadcastExampleResponse)
async def handle_response(ctx: Context, sender: str, msg: BroadcastExampleResponse):
ctx.logger.info(f"Received response from {sender}: {msg.text}")
```

In the first part, we use the `.on_interval()` decorator to define an interval behavior for this agent when the script is being run. In this case, the agent will execute the `say_hello()` function every 5 seconds. The `Context` object is a collection of data and functions related to the agent. Inside the `say_hello()` function, the agent uses the `ctx.experimental_broadcast()` method to send a broadcast message. The message is of type `BroadcastExampleRequest()` and it is being sent using the protocol's digest (`proto.digest`).

Then, we defined a `.on_message()` decorator which decorates `handle_response()` function. This function handles all incoming messages of type `BroadcastExampleResponse from other agents. When a response is received, it logs the information. Inside the `handle_response()` function, the agent logs an informational message using `ctx.logger.info()` method to print the sender and the content of the message. The message includes the sender's name and the text content of the response message.

7. We are now ready to set up a `Bureau` object for agents to be run together at the same time, and we add `alice`, `bob`, and `charles` to it using the `bureau.add()` method:

```py copy
bureau = Bureau(port=8000, endpoint="http://localhost:8000/submit")
bureau.add(alice)
bureau.add(bob)
bureau.add(charles)

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

The bureau is assigned to listen on `port=8000` and specifies an `endpoint` at `"http://localhost:8000/submit"` for submitting data.

8. Save the script.

The overall script should look as follows:

```py copy filename="broadcast.py"
from uagents import Agent, Bureau, Context, Model, Protocol
from uagents.setup import fund_agent_if_low

# create agents
# alice and bob will support the protocol
# charles will try to reach all agents supporting the protocol
alice = Agent(name="alice", seed="alice recovery phrase")
bob = Agent(name="bob", seed="bob recovery phrase")
charles = Agent(name="charles", seed="charles recovery phrase")

fund_agent_if_low(alice.wallet.address())
fund_agent_if_low(bob.wallet.address())
fund_agent_if_low(charles.wallet.address())


class BroadcastExampleRequest(Model):
pass

class BroadcastExampleResponse(Model):
text: str

# define protocol
proto = Protocol(name="proto", version="1.0")

@proto.on_message(model=BroadcastExampleRequest, replies=BroadcastExampleResponse)
async def handle_request(ctx: Context, sender: str, _msg: BroadcastExampleRequest):
await ctx.send(sender, BroadcastExampleResponse(text=f"Hello from {ctx.name}"))

# include protocol
# Note: after the first registration on the almanac smart contract, it will
# take about 5 minutes before the agents can be found through the protocol
alice.include(proto)
bob.include(proto)

# let charles send the message to all agents supporting the protocol
@charles.on_interval(period=5)
async def say_hello(ctx: Context):
await ctx.experimental_broadcast(proto.digest, message=BroadcastExampleRequest())

@charles.on_message(model=BroadcastExampleResponse)
async def handle_response(ctx: Context, sender: str, msg: BroadcastExampleResponse):
ctx.logger.info(f"Received response from {sender}: {msg.text}")

bureau = Bureau(port=8000, endpoint="http://localhost:8000/submit")
bureau.add(alice)
bureau.add(bob)
bureau.add(charles)

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

## Run the script

Make sure to have activated your virtual environment correctly.

Run the script: `python broadcast.py`

The output would be:

```
[setup]: Adding testnet funds to agent...
[setup]: Adding testnet funds to agent...complete
[alice]: Registering on almanac contract...
[alice]: Registering on almanac contract...complete
[ bob]: Registering on almanac contract...
[ bob]: Registering on almanac contract...complete
[charles]: Registering on almanac contract...
[charles]: Registering on almanac contract...complete
[bureau]: Starting server on http://0.0.0.0:8000 (Press CTRL+C to quit)
[charles]: Received response from agent1q0mau8vkmg78xx0sh8cyl4tpl4ktx94pqp2e94cylu6haugt2hd7j9vequ7: Hello from bob
[charles]: Received response from agent1qww3ju3h6kfcuqf54gkghvt2pqe8qp97a7nzm2vp8plfxflc0epzcjsv79t: Hello from alice
[charles]: Received response from agent1q0mau8vkmg78xx0sh8cyl4tpl4ktx94pqp2e94cylu6haugt2hd7j9vequ7: Hello from bob
[charles]: Received response from agent1qww3ju3h6kfcuqf54gkghvt2pqe8qp97a7nzm2vp8plfxflc0epzcjsv79t: Hello from alice
[charles]: Received response from agent1q0mau8vkmg78xx0sh8cyl4tpl4ktx94pqp2e94cylu6haugt2hd7j9vequ7: Hello from bob
[charles]: Received response from agent1qww3ju3h6kfcuqf54gkghvt2pqe8qp97a7nzm2vp8plfxflc0epzcjsv79t: Hello from alice
```
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,4 @@ Let's use a _simple restaurant table booking request_ as an example to better un
restaurant.include(book_proto)
```

For a better understanding of these concepts, consider having a look at the [Agents storage ↗️](/references/uagents/uagents-protocols/storage) and [Exchange protocol ↗️](/references/uagents/uagents-protocols/exchange-protocol) resources and consider going through the extensive [How to book a table at a restaurant using agents ↗️](/guides/agents/booking-demo) guide in the [AI Agents guides ↗️](/guides#ai-agents) section.
For a better understanding of these concepts, consider having a look at the [Agents storage ↗️](/references/uagents/uagents-protocols/storage) and [Exchange protocol ↗️](/references/uagents/uagents-protocols/exchange-protocol) resources and consider going through the extensive [How to book a table at a restaurant using agents ↗️](/guides/agents/booking-demo) guide in the [AI Agents guides ↗️](/guides#ai-agents) section. Also, checkout the [AI Agents: broadcast ↗️](/guides/agents/broadcast) for an additional implementation of protocols in agents communication.

0 comments on commit 762ba16

Please sign in to comment.