Counter AI Agent Demo
This demo shows how to run an AI agent implementing a counter.
It is a simple example that illustrates how state-machine replication is achieved across different agent instances through the consensus gadget, which is Tendermint, in this case. Note that, unlike the remaining demos, due to its simplicity, the business logic of this AI agent is not encoded as an FSM App. Rather, it is implemented as a simple skill (counter) containing an ABCIHandler. The skill does not contain proactive behaviours, which means that no client calls are made to the consensus gadget.
Architecture of the Demo
This demo is composed of:
- A set of four Tendermint nodes (
node0,node1,node2,node3). - A set of four AEAs (
abci0,abci1,abci2,abci3), in one-to-one connection with their corresponding Tendermint node.
Running the Demo
The steps below will guide you to download the counter AI agent configuration from the Service Registry, build and run a deployment that will run locally.
-
Ensure that your machine satisfies the framework requirements and that you have followed the setup instructions. As a result you should have a Pipenv workspace folder.
-
Use the CLI to download the
valory/counterAI agent.autonomy fetch valory/counter:0.1.0:bafybeicpyssnq37ukjjwzsmbg4bu5rcn3jnbueybktjbqvb7zn3r4ue6z4 --remote --service cd counter -
Inside the workspace folder, create a JSON file
keys.jsoncontaining the addresses and keys of the four agent instances that are part of this demo. Below you have a samplekeys.jsonfile that you can use for testing.Important
Use these keys for testing purposes only. Never use these keys in a production environment or for personal use.
[ { "address": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", "private_key": "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a" }, { "address": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", "private_key": "0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba" }, { "address": "0x976EA74026E726554dB657fA54763abd0C3a0aa9", "private_key": "0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e" }, { "address": "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955", "private_key": "0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356" } ] -
Build the required image
The command above generates the required images to run the AI agent.autonomy build-image -
Build a deployment setup for the demo AI agent:
autonomy deploy build keys.json -ltmThis will build the deployment setup required to run the AI agent locally.
Note
It is also possible to generate a deployment using a local AI agent configuration. See the Commands section for the complete details.
-
The build configuration will be located in
./abci_build_*. Run the deployment usingcd abci_build_* autonomy deploy runThis will deploy a local counter AI agent with four agent instances connected to four Tendermint nodes.
-
The logs of a single agent instance or Tendermint node can be inspected in another terminal with, e.g.,
wheredocker logs <container_id> --follow<container_id>refers to the Docker container ID for either an agent instance (abci0,abci1,abci2andabci3) or a Tendermint node (node0,node1,node2andnode3).
Interacting with the Demo
Recall that each agent instance has the skill counter, and the consensus gadget (Tendermint) manages the consensus protocol for incoming transactions. The counter skill implements the ABCICounterHandler which receives (and responds to) callbacks from the Tendermint network when certain events happen, in particular, when a client sends a transaction to the local blockchain managed by Tendermint.
Once the AI agent is up, you can interact with it.
The four Tendermint nodes, node0, node1, node2, and node3, are listening at ports 26657, 26667, 26677, and 26687, respectively.
To query the state of the AI agent from Tendermint node0, execute the following HTTP request:
curl http://localhost:26657/abci_query
What will happen behind the scenes is that the Tendermint node node0
will send a request_query ABCI request to the agent instance abci0 which will be handled by the ABCICounterHandler. The handler will reply
with a response_query ABCI response, containing the current state of the AI agent.
The response to this HTTP request above is:
{
"jsonrpc": "2.0",
"id": -1,
"result": {
"response": {
"code": 0,
"log": "value: 0",
"info": "",
"index": "0",
"key": null,
"value": "AAAAAA==",
"proofOps": {
"ops": []
},
"height": "0",
"codespace": ""
}
}
}
As you can see from the log field, the counter is initialized at 0.
value contains the base64 encoding of the bytes of the data,
representing the app state.
You can verify that running the same query against the other nodes will give you the same response, e.g.
curl http://localhost:26667/abci_query
Send a Transaction
To send a transaction and update the ABCI application state:
curl http://localhost:26657/broadcast_tx_commit\?tx\=0x01
where 0x01 is the new value for the distributed counter.
Once the request is received, the Tendermint node will
first check that the transaction is indeed valid against
the current state of the application by sending
a check_tx request to the ABCI application.
If so, the transaction will be added to the mempool
of pending transactions.
In our case, since the state before the transaction was 0x00,
and since 0x01 is a unitary increment of the counter,
the transaction is valid.
After the Tendermint network managed to reach a consensus, the command above receives this HTTP response:
{
"jsonrpc": "2.0",
"id": -1,
"result": {
"check_tx": {
"code": 0,
"data": null,
"log": "valid transaction.",
"info": "OK: the next count is a unitary increment.",
"gas_wanted": "0",
"gas_used": "0",
"events": [],
"codespace": ""
},
"deliver_tx": {
"code": 0,
"data": null,
"log": "",
"info": "",
"gas_wanted": "0",
"gas_used": "0",
"events": [],
"codespace": ""
},
"hash": "4BF5122F344554C53BDE2EBB8CD2B7E3D1600AD631C385A5D7CCE23C7785459A",
"height": "3"
}
}
The check_tx part is the response of the ABCI app when the
transaction was received and checked,
and deliver_tx is the response of the ABCI app after the transaction was applied
to the state.
The info field of the check_tx response:
OK: the next count is a unitary increment.
Note that we would have obtained the same result
by interacting with another available Tendermint node;
you can try by replacing the port 26657
with one of 26667, 26677 and 26687.
If, say, we had sent the transaction 0x02 instead of the
only legal one 0x01, we would have got the following response:
{
"jsonrpc": "2.0",
"id": -1,
"result": {
"check_tx": {
"code": 1,
"data": null,
"log": "invalid transaction.",
"info": "ERROR: the next count must be a unitary increment.",
"gas_wanted": "0",
"gas_used": "0",
"events": [],
"codespace": ""
},
"deliver_tx": {
"code": 0,
"data": null,
"log": "",
"info": "",
"gas_wanted": "0",
"gas_used": "0",
"events": [],
"codespace": ""
},
"hash": "DBC1B4C900FFE48D575B5DA5C638040125F65DB0FE3E24494B76EA986457D986",
"height": "0"
}
info field of the check_tx response:
ERROR: the next count must be a unitary increment.
Now, the query request
curl http://localhost:26667/abci_query
returns the updated counter value:
{
"jsonrpc": "2.0",
"id": -1,
"result": {
"response": {
"code": 0,
"log": "value: 1",
"info": "",
"index": "0",
"key": null,
"value": "AAAAAQ==",
"proofOps": {
"ops": []
},
"height": "0",
"codespace": ""
}
}
}
Interact through an AEA
In this section we will see an example of how to use an AEA to interact with the Tendermint network built above.
First, open a terminal to the root of this repository,
and fetch the counter_client agent:
autonomy fetch valory/counter_client:0.1.0:bafybeie2l7742uqghasr4wauppu64rddgdfodrsrlaq55scgl7u4ua226i --remote
This will copy the agent blueprint project in the counter_client directory.
Then, enter into the project, and generate a private key:
cd counter_client
autonomy generate-key ethereum
autonomy install
You can see the Tendermint node the skill is configured to interact with using the following command:
autonomy config get vendor.valory.skills.counter_client.models.params.args.tendermint_url
It will print localhost:26657, i.e. node0.
Finally, run the agent instance:
autonomy run
The agent instance periodically checks the current value of the counter;
this behaviour is implemented in the MonitorBehaviour of the
counter_client skill.
Moreover, it periodically sends a transaction to increment the
value of the counter; this behaviour is implemented in the
IncrementerBehaviour of the counter_client skill.
The output of the run should be something like:
_ _____ _
/ \ | ____| / \
/ _ \ | _| / _ \
/ ___ \ | |___ / ___ \
/_/ \_\|_____|/_/ \_\
v1.0.2
Starting AEA 'counter_client' in 'async' mode...
info: [counter_client] Start processing messages...
info: [counter_client] [2021-08-05T20:00:05.783576] Sending transaction with new count: 1
info: [counter_client] [2021-08-05T20:00:05.793780] The counter value is: 0
info: [counter_client] [2021-08-05T20:00:06.817543] The counter value is: 1
info: [counter_client] [2021-08-05T20:00:06.817852] Update current state: 1
info: [counter_client] [2021-08-05T20:00:07.816933] The counter value is: 1
info: [counter_client] [2021-08-05T20:00:08.784843] Sending transaction with new count: 2
info: [counter_client] [2021-08-05T20:00:08.838607] The counter value is: 1
info: [counter_client] [2021-08-05T20:00:09.822670] The counter value is: 2
info: [counter_client] [2021-08-05T20:00:09.823049] Update current state: 2
info: [counter_client] [2021-08-05T20:00:10.823978] The counter value is: 2
info: [counter_client] [2021-08-05T20:00:11.785852] Sending transaction with new count: 3
info: [counter_client] [2021-08-05T20:00:11.830393] The counter value is: 2
info: [counter_client] [2021-08-05T20:00:12.829347] The counter value is: 3
info: [counter_client] [2021-08-05T20:00:12.829746] Update current state: 3
info: [counter_client] [2021-08-05T20:00:13.826483] The counter value is: 3
info: [counter_client] [2021-08-05T20:00:14.787033] Sending transaction with new count: 4
info: [counter_client] [2021-08-05T20:00:14.838130] The counter value is: 3
info: [counter_client] [2021-08-05T20:00:15.826483] The counter value is: 4
info: [counter_client] [2021-08-05T20:00:15.826825] Update current state: 4
info: [counter_client] [2021-08-05T20:00:16.831485] The counter value is: 4
(...)