AJAN Actions - aantakli/AJAN-service GitHub Wiki
In AJAN, performing an action follows several steps. First, for an action to be context-sensitive, it must be defined in an SBT with an Action Node (see chapert chapter SPARQL BT Leaf Nodes). As described, an SBT Action Node is used to read the agent knowledge at runtime and generate the input for the action, the so-called Action Input. However, the execution of the actual action is based on the so-called action description, which is referenced in the respective SBT action node and applied to the Action Input.
In the following, the actual workflow of the execution of an action is roughly described and in the Action Description chapter, the most important aspects and how these are to be defined in AJAN are dealt with in more detail.
Action Execution Workflow
-
As described in the introduction, the action execution starts with reading the agent knowledge via an SBT Action Node. Here, a SPARQL 1.1 CONSTRUCT query is used to generate the so-called Action Input, an RDF data set.
-
The Action Input is then applied with the Action Definition linked in the SBT Action Node. The Action Definition describes how an action changes the agent domain and how an action is finally to be executed. Thus, the Action Definition is just a kind of manual to transform the domain with an internal or external service.
-
In order to use the actual service to be executed, a command must be sent to it for which the mentioned Action Input is used. So that a service accepts this, the Action Input is evaluated by AJAN on the basis of the Action Definition (Consumable) before the execution. This is done with a predefined SPARQL 1.1 ASK query.
-
If the Action Input contains the required information, the Payload for the action command/request is generated from it in the next step. By default, AJAN considers HTTP/RDF services. The command is therefore transmitted via HTTP as an RDF graph. In the default case, which is described in more detail later in this documentation, the Payload is generated via a SPARQL 1.1 CONSTRUCT query from the Action Input. However, there is also the possibility to integrate actions via so-called AJAN plugins, e.g. to use pure TCP sockets for communication.
-
Finally, for an HTTP/RDF action, an HTTP request is sent to the service with based on the Action Definition to use. If the response of the service or the result of the action must be waited for a longer time, AJAN generates a response URI on which the response is listened for.
-
If a service has successfully or unsuccessfully processed the action request, it sends the result, the so-called Action Result back to AJAN. In the synchronous case DIRECTLY after receipt and processing of the request and in the asynchronous case after an undefined time. In the latter case, the result is sent to the request URI provided by AJAN.
-
The Action Result is then evaluated based on the Action Definition (Poducible) (#7a) and stored in the Agent Knowledge (#7b).
Action Definition
As described in chapter SPARQL BT Leaf Nodes, AJAN actions are 'external' HTTP-based services or 'internal' plugin-based methods that can be executed by an AJAN agent via an SBT action node. Such a node uses an Action Definition to communicate with such a method. An Action Definition specifies not only how a client can communicate with such a method, but also how an action or method transforms the agent's domain and what input data is required to execute such a method. The input for action execution is defined by the bt:inputs
field of the SBT node and evaluated based on the selected (bt:definition
) Action Definition. Action Definitions are stored in the services repository (e.g. http://localhost:8090/rdf4j/repositories/services) of an AJAN instance and can be added dynamically during agent execution. A use case for this could be an agent querying an external HTTP service via HTTP/GET to obtain its Action Definition in order to subsequently use that service.
The general parts of an Action Definition are:
- Type
- Communication
- Transformation
- Binding (only relevant for HTTP based Service Actions)
General RDF Structure of an Action Definition in [Turtle/RDF] (comments strat with: #)
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix actn: <http://www.ajan.de/actn#> .
:ExampleWalkAction
# Type -------------------------------
rdf:type actn:ServiceAction ;
rdfs:label "ExampleWalkAction";
# Communication ----------------------
actn:communication actn:Asynchronous ;
# Transformation ---------------------
actn:consumes :ExampleConsumible ;
actn:produces :ExampleProducible ;
actn:variables ( #... );
# Binding ----------------------------
actn:runBinding :RunExampleWalkAction ;
actn:abortBinding :AbortExampleWalkAction .
Type
The type (rdf:type
) indicates how an action or method is integrated in AJAN. As mentioned earlier, there are two such types in AJAN, HTTP service based (actn:ServiceAction
) or AJAN plugin based (actn:PluginAction
):
rdf:type actn:ServiceAction ;
HTTP service is the default in AJAN, which means that AJAN generally assumes that an action/method is implemented as an HTTP/RDF service and accordingly communication takes place via HTTP requests.
rdf:type actn:PluginAction ;
To make it possible to integrate actions into AJAN in a different form, either to use a different middleware or to offer AJAN internal methods as actions, AJAN offers a plugin system. In that case, the binding part of a Action Description is implemented in the related plugin and not part of the Action Description itself.
Communication
The communication predicate (actn:communication
) indicates if an action or method responds synchronously (actn:Synchronous
) or asynchronously (actn:Asynchronous
):
actn:communication actn:Synchronous ;
In the first case, this means that the agent can expect a final response from an action immediately after it has been executed.
actn:communication actn:Asynchronous ;
In the second case, the agent may have to wait for an undefined time until an action is completed and thus its result is received. If the service to be executed is an HTTP/RDF service, a request URI (actn:asyncRequestURI
) is automatically added to the Action Input, which AJAN listens for, to which the service can send the respective result after successful or unsuccessful action execution.
An example could be a navigation action of an avatar in a simulation environment. This action is subject to geometric constraints, so the duration of this action is variable and the action itself is asynchronous. Asynchronous actions can be aborted and the respective SBT action node has a RUNNING state which is not the case for synchronous actions.
Transformation
To describe how an action changes the agent domain and which input parameters are required by the action and what the agent can expect for an Action Result, an Action Definition includes a so-called transformation part. This consists of three parts: Consumable; Producible; and Variables.
actn:consumes [
a actn:Consumable ;
actn:sparql """
PREFIX test: <http://test/>
PREFIX actn: <http://www.ajan.de/actn#>
ASK
WHERE {
?avatar a test:Avatar .
?avatar test:position ?current .
?current a test:Position .
?target a test:Position .
?c actn:asyncRequestURI ?requestURI .
}"""^^xsd:string ;
] ;
A Consumable resource (actn:Consumable
) is a SPARQL 1.1 ASK query that specifies which input parameters an action service expects to be executed. In addition to the input parameters, preconditions can also be defined in the query. The Consumable is not only used to describe the expected parameters, the corresponding ASK query (actn:sparql
) is also applied to the Action Input to check if the required data is actually available before the action is executed. If the evaluation fails, the result of the ASK Query is 'false', the corresponding SBT Action Node returns FAILED as status. If the action is asynchronous, a request URI is expected in the Consumable. In this case, however, this is automatically added to the Action Input by AJAN.
actn:produces [
a actn:Producible ;
actn:sparql """
PREFIX test: <http://test/>
PREFIX actn: <http://www.ajan.de/actn#>
PREFIX dct: <http://purl.org/dc/terms/>
ASK
WHERE {
?avatar test:position ?target .
FILTER NOT EXISTS {
?avatar test:position ?current .
FILTER (?target != ?current)
}
OPTIONAL {
?response rdf:type actn:FAULT .
?response dct:description ?message .
}
}"""^^xsd:string ;
] ;
As with a Consumable, a Producible resource (actn:Producible
) is defined using a SPARQL 1.1 ASK query. In contrast, however, the input conditions or preconditions are not described with this. Instead, the service output or the action effects are described with such a query. Also this ASK query is used to evaluate an RDF dataset in AJAN. However, in this case it is the result of an executed action, the so-called Action Result. If the result of the Producible query is 'false', the corresponding SBT action node fails.
actn:variables (
[ a actn:ActionVariable;
sp:varName "avatar" ]
[ a actn:ActionVariable;
sp:varName "current" ]
[ a actn:ActionVariable;
sp:varName "target" ]
);
Action variables (actn:ActionVariable
) are used in an Action Description to bind the same SPARQL variables used in a Consumable and in a Producible. This clearly defines which information in an Action Input matches which information in an Action Result or which describe the same instance. For example, in the above example, the variable names avatar, current and target are used in the Consumable and in the Producable. By listing these variable names as Action Variable, it is determined that the resources behind them are the same instances in both queries.
This mechanism describes clearly how an action changes such an instance. AJAN takes advantage of this fact and updates or changes the respective data in the agent knowledge after successful evaluation of the Action Result.
Binding
The binding is only needed if the action is available as HTTP/RDF service, so it is from rdf:type
actn:ServiceAction
. In case of a plugin based action (actn:PluginAction
) this field is not needed, because the way of communicating with a method or how it is executed is implemented in the corresponding plugin. In the case of a Service Action, the binding specification is used to define how the HTTP request is to be executed, i.e. which HTTP method and which HTTP headers are to be used, how the Payload is defined and to which URL the request is to be sent. If the Service Action communication is asynchronous (actn:Asynchronous
), not only is a binding needed to execute the action (actn:runBinding
), but another is needed to be able to abort a running action (actn:abortBinding
):
actn:runBinding :RunExampleWalkAction ;
actn:abortBinding :AbortExampleWalkAction .
The definition of a binding for execution and for aborting an action are identical, which is why only a run binding is discussed below. A binding is of type actn:Binding
and because it describes an HTTP request at the end also of type http-core:Request
. In general, vocabularies from the HTTP context are used to describe a binding. These include:
- @prefix http-core: http://www.w3.org/2006/http#
- @prefix http-headers: http://www.w3.org/2008/http-headers#
- @prefix http-methods: http://www.w3.org/2008/http-methods#
In addition to the HTTP version (http-core:httpVersion
) to be used, the HTTP method (http-core:mthd
, using the HTTP methods vocabulary http://www.w3.org/2008/http-methods#) and the request URI (http-core:requestURI
) with the xsd type xsd:anyURI
must also be specified:
:RunExampleWalkAction
rdf:type actn:Binding ;
rdf:type http-core:Request ;
http-core:httpVersion "1.1" ;
http-core:mthd http-methods:POST ;
http-core:requestURI "http://localhost:8092/walk"^^xsd:anyURI ;
If certain headers are to be included in an HTTP request, they are inserted in a binding definition via the http-core:headers
list. To set headers, a header resource of type http-core:Header
must contain the header name and associated value with the http-core
and http-headers
vocabularies as shown below.
http-core:headers ( [
a http-core:Header ;
http-core:hdrName http-headers:content-type ;
http-core:fieldValue "text/turtle"
][
a http-core:Header ;
http-core:hdrName http-headers:accept ;
http-core:fieldValue "text/turtle"
]
);
The contents of a request for action execution or action cancellation are specified via the http-core:body
field or via an actn:Payload
resource. The actual content is the result of a SPARQL 1.1 CONSTRUCT query (actn:sparql
). The defined query is applied at runtime, after a successful evaluation (through the Consumable definition), to the Action Input (RDF dataset created by the executed SBT Action Node). If it is an asynchronous action, an endpoint is dynamically created in AJAN which is listened to in order to receive the action result. In order for the external service to be able to send information back to AJAN, a request URI (actn:asyncRequestURI
), the generated endpoint, is attached to the Action Input in the asynchronous case, which is taken into account in the Payload generation.
http-core:body [
a actn:Payload ;
actn:sparql """
PREFIX test: <http://test/>
PREFIX some: <http://some/>
PREFIX actn: <http://www.ajan.de/actn#>
CONSTRUCT {
some:Request some:avatar ?avatar .
some:Request some:target ?target .
?c actn:asyncRequestURI ?requestURI .
}
WHERE {
?avatar a test:Avatar .
?avatar test:position ?current .
?current a test:Position .
?target a test:Position .
?c actn:asyncRequestURI ?requestURI .
FILTER (?target != ?current)
}"""^^xsd:string ;
] .
Overall Example in [Turtle/RDF] (comments strat with: #)
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix dct: <http://purl.org/dc/terms/> .
@prefix http-core: <http://www.w3.org/2006/http#> .
@prefix http-headers: <http://www.w3.org/2008/http-headers#> .
@prefix http-methods: <http://www.w3.org/2008/http-methods#> .
@prefix actn: <http://www.ajan.de/actn#> .
:ExampleWalkAction
rdf:type actn:ServiceAction ;
actn:communication actn:Asynchronous ;
rdfs:label "ExampleWalkAction";
actn:variables (
[ a actn:ActionVariable;
sp:varName "avatar" ]
[ a actn:ActionVariable;
sp:varName "current" ]
[ a actn:ActionVariable;
sp:varName "target" ]
);
actn:consumes [
a actn:Consumable ;
actn:sparql """
PREFIX test: <http://test/>
PREFIX actn: <http://www.ajan.de/actn#>
ASK
WHERE {
?avatar a test:Avatar .
?avatar test:position ?current .
?current a test:Position .
?target a test:Position .
?c actn:asyncRequestURI ?requestURI .
}"""^^xsd:string ;
] ;
actn:produces [
a actn:Producible ;
actn:sparql """
PREFIX test: <http://test/>
PREFIX actn: <http://www.ajan.de/actn#>
PREFIX dct: <http://purl.org/dc/terms/>
ASK
WHERE {
?avatar test:position ?target .
FILTER NOT EXISTS {
?avatar test:position ?current .
FILTER (?target != ?current)
}
OPTIONAL {
?response rdf:type actn:FAULT .
?response dct:description ?message .
}
}"""^^xsd:string ;
] ;
actn:runBinding :RunExampleWalkAction ;
actn:abortBinding :AbortExampleWalkAction .
# Bindings ----------------------------------------
:RunExampleWalkAction
rdf:type actn:Binding ;
rdf:type http-core:Request ;
http-core:httpVersion "1.1" ;
http-core:mthd http-methods:POST ;
http-core:requestURI "http://localhost:8092/walk"^^xsd:anyURI ;
http-core:headers ( [
a http-core:Header ;
http-core:hdrName http-headers:content-type ;
http-core:fieldValue "text/turtle"
][
a http-core:Header ;
http-core:hdrName http-headers:accept ;
http-core:fieldValue "text/turtle"
]
);
http-core:body [
a actn:Payload ;
actn:sparql """
PREFIX test: <http://test/>
PREFIX some: <http://some/>
PREFIX actn: <http://www.ajan.de/actn#>
CONSTRUCT {
some:Request some:avatar ?avatar .
some:Request some:target ?target .
?c actn:asyncRequestURI ?requestURI .
}
WHERE {
?avatar a test:Avatar .
?avatar test:position ?current .
?current a test:Position .
?target a test:Position .
?c actn:asyncRequestURI ?requestURI .
FILTER (?target != ?current)
}"""^^xsd:string ;
] .
:AbortExampleWalkAction
rdf:type actn:Binding ;
rdf:type http-core:Request ;
http-core:httpVersion "1.1" ;
http-core:mthd http-methods:POST ;
http-core:requestURI "http://localhost:8092/walk/abort"^^xsd:anyURI ;
http-core:headers ( [
a http-core:Header ;
http-core:hdrName http-headers:content-type ;
http-core:fieldValue "text/turtle"
][
a http-core:Header ;
http-core:hdrName http-headers:accept ;
http-core:fieldValue "text/turtle"
]
);
http-core:body [
a actn:Payload ;
actn:sparql """
PREFIX test: <http://test/>
CONSTRUCT {
?avatar a test:Avatar .
}
WHERE {
?avatar a test:Avatar .
}"""^^xsd:string ;
] .