Stateful Autonomous Agents
In the previous post we built a very simple Hello World autonomous agent on the Obyte platform that only responded with a static value. In this article we will learn how to store state and execute different actions based on user input and the AA state.
Agent Greeter
This example is gonna be an autonomous agent that greets the user differently depending on whether the user already registered. The user will have to first send their name which the AA will store. Next, every time the user interacts with the AA, it will greet them with a warm welcome back message.
Requesting user input
This AA will require that the user first supply their name. But how can we do that? It's easy, the Obyte wallet allows sending data fields along with a payment which the AA can easily read via the trigger.data
variable:
{
bounce_fees: { base: 10000 },
messages: [
{
app: 'state',
state: `{
var[trigger.address] = trigger.data.name;
response['message'] = 'Hello ' || var[trigger.address];
}`
}
]
}
A couple of things to notice here:
- we use the
state
message type because that is the only place where state variables can be set. State variables can be accessed (read) in any part of the script, but you can only assign values in the state message. - state variables are assigned using the
var
associative array-like variable. Thevar[trigger.address] = trigger.data.name
maps the value oftrigger.address
to the value oftrigger.data.name
, for example53LAFVHRJ7C2SNNEMLUCJCZCZFXZNKV4
toJohn Doe
. trigger.data
refers to a data payload object of the triggering Obyte unit and we can access its properties with the dot operator. In the above example thetrigger.data.name
reads the value of thename
property.- finally, when setting the response message, we can see that the state variable can be immediately read after we assigned a value to it.
And here is how the user would call Agent Greeter from the Send screen of the wallet:
As we can see, the wallet immediately predicts the outcome of the call and displays what state variables would be set upon execution.
Handling errors
What happens if the user does not send the name
data field? Rather than storing no name, we should instead give an error feedback to the user indicating the missing data element. Let's make some changes:
{
bounce_fees: { base: 10000 },
messages: [
{
init: `{
$name = trigger.data.name otherwise bounce("name is required!");
}`,
app: 'state',
state: `{
var[trigger.address] = $name;
response['message'] = 'Hello ' || var[trigger.address];
}`
}
]
}
A few new things are introduced above:
- an
init
property which can have a value with Oscript statements. This is a great place to initialize local variables and handle preconditions and erroneous situations. - a local variable called
$name
is initialized with the value of thename
data property. It is important to note, that local variables cannot be re-assigned once initialized with a value. - the
otherwise
keyword which is a binary operator that returns the left side if that is "truthy" , otherwise it returns the expression on the right side. Another example would be$name = trigger.data.name otherwise 'anonymous';
- the
bounce
command which is used to terminate the AA execution with an error.
Here is how it looks like in the wallet when no name
data field is provided:
Adding multiple behaviors
Our goal is to recognize and greet the user once they introduced themselves to Agent Greeter. To achieve that we will have to branch our code and alter the behavior if we already stored a name for the calling user. For that, we are going to use the cases
keyword. See the final script below:
{
bounce_fees: { base: 10000 },
messages: {
cases: [
{
if: `{ !var[trigger.address] }`,
messages: [{
init: `{
$name = trigger.data.name otherwise bounce("name is required!");
}`,
app: 'state',
state: `{
var[trigger.address] = $name;
response['message'] = 'Hello ' || var[trigger.address];
}`
}]
},
{
messages: [{
app: 'state',
state: `{
response['message'] = 'Welcome back ' || var[trigger.address];
}`
}]
}
]
}
}
The cases
keyword is used for parameterizing JSON objects. The cases
takes an array and picks the first element of which if
condition is truthy or the last one without an if
. The final script has two branches: the first executes only if the user has not registered their name yet, while the second executes otherwise when the AA already knows the user and so it can welcome them by their name.
Important to note that recognizing the same user only works if the user triggers the AA with the same address. For regular users that means they are supposed to use a single address wallet for interacting with Agent Greeter. Bots and programmed headless wallets of course can construct triggering units that would pick a specific address even if they use a multi-address wallet.
As an exercise, try to modify the script to return the user most of the fees they paid for interacting with the agent.
Congratulations @pmiklos! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :
You can view your badges on your Steem Board and compare to others on the Steem Ranking
If you no longer want to receive notifications, reply to this comment with the word
STOP
Vote for @Steemitboard as a witness to get one more award and increased upvotes!