Actions
How to think about what your matrix can do and how those capabilities map to real operations.
Actions are the verbs of your operating model. If the ontology teaches the AI what things exist and how they relate, actions teach it what can be done. Every action is a declared operation with an explicit I/O contract: a defined input schema, a defined output schema, and an execution mechanism that RARS handles transparently.
This contract is the same regardless of how the action executes. Whether it's a deterministic API call, a SPARQL query, or an agentic reasoning process, the contract defines what goes in and what must come out. The platform validates the output against the contract in every case. This is what makes actions composable and trustworthy: the caller doesn't need to know or care whether a human, an autonomous actor, or a direct service call produced the result.
Designing good actions is about choosing the right boundaries: what constitutes a single operation, what the caller needs to provide, what they get back, and how RARS should behave when things go wrong.
For the full action type reference, see rars-act:Actionrars-act:Action in the engine reference.
Actions as Methods on Concepts
If you read the domain modeling guide, you'll recognize the concept-oriented programming parallel. Actions are like methods: they can operate on a subject (like this in OOP), take parameters (the payload), and return a result.
tasks:ListTasks
a rars-act:Action ;
rars-act:isClassifiedAs rars-act:Query ;
rdfs:label "list tasks" ;
dct:description "List all tasks from the task management system." ;
rars-act:subjectScheme rars-ws:Workspace ;
rars-act:subjectRequired true ;
rars-act:payloadScheme tasks:ListTasksPayload ;
rars-act:resultScheme tasks:Task ;
rars-act:handler tasks:ListTasksIntegration .RARS sees the label, description, subject type, payload schema, and result type. That's enough to understand what the action does and how to invoke it. The handler (how it actually executes) is resolved by RARS.
Actions Without Subjects
The subject is not always required. This is where the OOP analogy intentionally breaks down. Many actions don't operate on an existing resource: they create new ones, perform system-wide operations, or interact with external services where no specific domain entity is the target.
tasks:CreateTask
a rars-act:Action ;
rars-act:isClassifiedAs rars-act:Mutation ;
rdfs:label "create task" ;
dct:description "Create a new task in the task management system." ;
rars-act:payloadScheme tasks:CreateTaskPayload ;
rars-act:resultScheme tasks:Task ;
rars-act:handler tasks:CreateTaskIntegration .No rars-act:subjectScheme, no rars-act:subjectRequired. The caller provides a payload and gets a result. This covers creation actions, search actions, import operations, and anything else where the action isn't scoped to a specific instance.
When you do use a subject, it enables dispatch and overloading. When you don't, the action is a standalone operation invoked directly by name. Both patterns are first-class. Choose based on whether the action conceptually operates on a specific resource or not.
Query vs Mutation
Every action is classified as either a Query or a Mutation. This isn't just a label. It changes how RARS treats the action operationally.
Queries are read-only and idempotent. The platform can safely cache their results, compose them in joins, and retry them on failure without side effects. Use Query when the action retrieves data without changing state.
Mutations modify state. They are never cached, cannot be used in cross-action joins, and RARS treats failed mutations differently from failed queries. Use Mutation when the action creates, updates, or deletes something.
Choosing correctly matters for the AI's reasoning. RARS knows it can freely call Query actions to gather information, but must be more deliberate about Mutations because they have real-world consequences.
Subject-Based Dispatch
The rars-act:subjectScheme property declares what type of resource the action operates on. When RARS invokes an action, RARS examines the subject's type and uses it to resolve the correct implementation.
This becomes powerful with overloading. You can define a single action interface and provide multiple implementations for different subject types. The platform uses semantic inference to select the most specific match at runtime.
Example: AI model provider dispatch
The platform uses this pattern for AI model integration. rars-ai:ChatComplete is a provider-agnostic action interface that takes any rars-ai:Model as its subject. Each provider matrix (Claude, OpenAI, etc.) installs an overload with a more specific subject type.
# The interface: provider-agnostic chat completion
rars-ai:ChatComplete
a rars-act:Action ;
rars-act:isClassifiedAs rars-act:Query ;
rars-act:subjectScheme rars-ai:Model ;
rars-act:payloadScheme rars-ai:ChatCompletePayload ;
rars-act:resultScheme rars-ai:TextCompletion .
# Claude's overload: handles any claude:Model subject
claude:ChatComplete
a rars-act:Action ;
rars-act:overloads rars-ai:ChatComplete ;
rars-act:subjectScheme claude:Model ;
rars-act:payloadScheme rars-ai:ChatCompletePayload ;
rars-act:resultScheme claude:TextCompletion ;
rars-act:handler claude:ChatCompleteIntegration .The model class hierarchy makes this work:
# Base class for all AI models
rars-ai:Model a owl:Class .
# Claude-specific subclass
claude:Model a owl:Class ;
rdfs:subClassOf rars-ai:Model .
# Specific models are named individuals
claude:Sonnet a claude:Model, owl:NamedIndividual ;
rars-ai:modelId "claude-sonnet-4-5-20250929" .
claude:Opus a claude:Model, owl:NamedIndividual ;
rars-ai:modelId "claude-opus-4-5-20250918" .When RARS invokes rars-ai:ChatComplete with claude:Sonnet as the subject, RARS sees that claude:Sonnet is a claude:Model, which is a subclass of rars-ai:Model. It resolves to claude:ChatComplete because that overload has the most specific matching subject scheme. RARS doesn't need to know about provider-specific implementations. It calls the generic interface, and RARS dispatches based on the model's type.
An OpenAI provider matrix would follow the same pattern: define openai:Model as a subclass of rars-ai:Model, register model individuals like openai:GPT4o, and install openai:ChatComplete as an overload. RARS's code never changes.
Example: Business domain overloading
The same pattern applies to your own domain. Consider a facilities management company that processes different types of work orders. The base matrix defines a generic ApproveWorkOrder action on WorkOrder. Specialized matrices override it for specific work order types that need different approval logic.
# Base matrix: generic work order approval
wo:ApproveWorkOrder
a rars-act:Action ;
rars-act:isClassifiedAs rars-act:Mutation ;
rars-act:subjectScheme wo:WorkOrder ;
rars-act:resultScheme wo:WorkOrder ;
rars-act:handler wo:DefaultApprovalIntegration .
# Electrical matrix: overloads for electrical work orders
# (requires permit validation before approval)
electrical:ApproveWorkOrder
a rars-act:Action ;
rars-act:overloads wo:ApproveWorkOrder ;
rars-act:subjectScheme electrical:ElectricalWorkOrder ;
rars-act:resultScheme wo:WorkOrder ;
rars-act:handler electrical:PermitCheckApprovalIntegration .Where electrical:ElectricalWorkOrder is a subclass of wo:WorkOrder:
electrical:ElectricalWorkOrder
a owl:Class ;
rdfs:subClassOf wo:WorkOrder ;
rdfs:label "electrical work order" ;
skos:definition "A work order for electrical maintenance or installation." .When RARS approves an electrical work order, RARS dispatches to the electrical-specific handler that validates permits. For any other work order type, the default handler runs. RARS just calls wo:ApproveWorkOrder on whatever work order it's dealing with.
This is the same pattern as method overriding in OOP, applied to business operations. An overload's subjectScheme must be a subclass of the parent action's subjectScheme to ensure type soundness. Any matrix can install overloads for any action interface, enabling third-party extensibility without modifying the original.
Handler Types
The handler determines how an action executes. You choose a handler type based on where the work happens.
ServiceIntegration: calls an external API. This is the most common handler for connecting to systems of record. It constructs HTTP requests from the invocation context using request templates and maps responses back to RDF using RML templates. See Connecting Systems of Record for the data product and service layer.
ScriptHandler: executes a SPARQL script that can orchestrate complex chains of other actions. A single script can invoke service integrations, other scripts, agentic actions, and human-in-the-loop steps as sub-actions, coordinating an entire end-to-end pipeline. For example, an ExecuteWorkOrder script handler might validate the work order, check permits, assign a technician, notify stakeholders, and update the CRM, each as a separate sub-action with its own handler type. Scripts are how you compose deterministic, multi-step workflows from smaller operations.
AgenticHandler: spawns a reasoning sub-agent to accomplish the action's goal. Use this specifically for non-deterministic actions where the path to the result requires AI reasoning, not just orchestration. The sub-agent can use other actions, query the graph, and reason across multiple steps to produce a result. See Contractual Agents below.
HumanInTheLoop: pauses execution and waits for a human to provide input or approval. Use this when automated resolution isn't appropriate and a decision requires human judgment.
The ability to blend these handler types within a single workflow is one of RARS's most powerful features. A script handler can orchestrate a pipeline where some steps are deterministic API calls, some require AI reasoning, and some need human approval, all within a single action with a single I/O contract. The deterministic steps execute predictably. The agentic steps bring flexibility where rigid logic would break down. The human steps inject judgment where automation isn't appropriate. The caller doesn't need to know which is which.
Example: Blended workflow for work order execution
This SPARQL script orchestrates an entire work order pipeline. It chains a deterministic service call, an agentic assessment, a human approval, and a final deterministic update into a single action.
# Activate the facilities matrix
PREFIX wo: <https://example.org/spec/facilities#>
# Construct the subgraph containing the information you need from the workflow
CONSTRUCT {
?workOrder wo:status ?woStatus ;
wo:priority ?priority ;
wo:approvedBy ?approver .
wo:dispatchRef ?dispatchRef .
}
WHERE {
# Step 1 (deterministic): fetch work order details from the backend
?workOrder wo:GetWorkOrder (
wo:workOrderId "123"
) .
# Step 2 (agentic): AI assesses risk based on work order details, site history, and compliance rules
?assessment wo:AssessRisk (?workOrder) .
?assessment wo:priority ?priority .
# Step 3 (human): manager reviews the assessment and approves
?approval wo:RequestApproval (
?workOrder
wo:assessment ?assessment
) .
?approval wo:approvedBy ?approver .
# Step 4 (deterministic): dispatch the approved work order
?dispatch wo:DispatchWorkOrder (
?workOrder
wo:approval ?approval
wo:priority ?priority
) .
?dispatch wo:dispatchRef ?dispatchRef .
?workOrder wo:status ?woStatus
}Each step is a separate action with its own handler type. GetWorkOrder and DispatchWorkOrder are service integrations that call the facilities backend. AssessRisk is an agentic handler that reasons over the work order details. RequestApproval is a human-in-the-loop action that pauses until a manager responds. The script threads results from one step into the next, and the entire pipeline executes behind a single ExecuteWorkOrder action with one I/O contract.
After this workflow is completed, all materialized information and its provenance are available in the internal context graph for you (and the internal agent) to evaluate and query at any time.
The I/O Contract
Every action defines an explicit contract: the payload schema (input) and the result schema (output). Together with SHACL shapes, these form a formal, validatable agreement about what goes into an action and what must come out.
Payloads are separate classes from your domain entities (see Modeling Your Domain on payload classes). A CreateTaskPayload has the fields needed to create a task, not every field a task can have. An UpdateTaskPayload might make all fields optional. Design payloads around what each specific operation requires from the caller.
Results declare the type of data the action produces. If ListTasks returns tasks:Task, any caller (human, agent, or another action) knows it can access task properties on the result. This enables composition: list tasks, filter by assignee, update the matching ones.
Design result types with intention. An action that returns a generic xsd:string gives the caller nothing to reason about. An action that returns a typed domain entity with a defined shape makes the output structured, queryable, and validatable.
The contract is enforced uniformly. Whether the action is a deterministic API call, a SPARQL query, or an autonomous actor that reasoned its way to a result, RARS validates the output against the same schema. This is what makes the system trustworthy at scale: the caller doesn't need to know how the result was produced, only that it conforms to the contract.
Contractual Agents
The I/O contract is especially powerful for agentic actions. When an AgenticHandler executes, a sub-agent reasons iteratively to accomplish the action's goal. It might call other actions, query the graph, synthesize information, and make decisions across multiple steps. But its output is still validated against the same result schema as a deterministic service call.
This means you can replace a deterministic action with an agentic one (or vice versa) without changing anything for the caller. A SummarizeReport action might start as a simple API call to an LLM endpoint. Later, you could swap its handler to an AgenticHandler that reads source data, cross-references multiple systems, and produces a richer summary. The contract stays the same. Callers are unaffected.
It also means agentic outputs are governed by the same SHACL validation as any other data in the system. If RARS produces a result that doesn't conform to the shape, the validation catches it. This closes the gap between deterministic and non-deterministic operations: both are held to the same standard.
Think about this when designing actions that might eventually be agentic. Define the result schema precisely. The tighter the contract, the more RARS can validate, and the more confidently the system can compose agentic results with everything else in the operating model.
Caching
Query actions are cached by default. The platform stores results and serves them on subsequent calls with the same inputs, avoiding redundant API calls.
Two things to configure:
Cache TTL (time-to-live): how long results stay valid. Set this based on how frequently the underlying data changes. Web search results might be valid for hours (rars-act:cacheTTL 21600). A list of active contexts might only be valid for seconds (rars-act:cacheTTL 30). Default is 300 seconds.
Cache key function: by default, RARS hashes the action URI, subject, and payload to generate a cache key. For some actions, you want smarter keys. A search action might normalize the query string (lowercase, collapse whitespace) so that "work order" and "Work Order" hit the same cache entry.
crawl:Search
rars-act:cacheTTL 21600 ;
rars-act:cacheKeyFunction [
rars-os:datatype xsd:string ;
rars-os:fromValue """
PREFIX crawl: <https://poliglot.io/rars/spec/crawl#>
PREFIX rars-act: <https://poliglot.io/rars/spec/actions#>
PREFIX rars-os: <https://poliglot.io/rars/spec/os#>
SELECT ?value WHERE {
?_process rars-os:parent ?invocation .
?invocation rars-act:payload ?p .
?p crawl:query ?query .
BIND(LCASE(REPLACE(STR(?query), "\\\\s+", " ")) AS ?value)
}
"""
] .Mutations are never cached, regardless of settings.
Action Healing
When a service integration fails, RARS can attempt self-repair instead of immediately propagating the error. This is called action healing.
When rars-act:healingEnabled true is set on a handler, a failed invocation triggers a diagnostic agent that receives the full failure context: the exception chain, the HTTP request that was made, the response body, the RML mapping definition, and the action's schemas. The healer diagnoses the root cause and attempts recovery.
Common scenarios where healing helps:
- Transient errors: the service returned a 503. The healer retries with backoff.
- Mapping mismatches: the API changed its response format. The healer inspects the actual response and adjusts the mapping.
- Authorization failures: a token expired or a permission is missing. The healer identifies the issue and can surface it for resolution.
If healing succeeds, the action completes normally from the caller's perspective. If it fails, the original error propagates. Healing doesn't introduce new failure modes.
Enable healing on ServiceIntegration handlers by default. It handles the most common transient failures without any additional design work. ScriptHandler actions rarely benefit from healing since SPARQL errors are usually logic bugs, not transient issues.
Request Templates and Response Mappings
Service integrations use request templates to construct HTTP requests and RML response templates to map responses back to RDF. These are the operational bridge between your actions and your services.
Request templates can use static values and dynamic SPARQL functions. Static values for things like HTTP method and fixed headers. Dynamic functions for things that depend on the invocation context: constructing paths from the subject's properties, building JSON bodies from the payload, resolving secrets for authentication headers.
Response templates use RML (RDF Mapping Language) to iterate over JSON/XML responses and create typed RDF resources. Each field in the response maps to a property on the result type.
See Request Mapping and Response Mapping for the full guides. The key design principle: RARS never sees the request/response wiring. It invokes actions by name and gets typed results back. The templates are the operational plumbing that RARS handles transparently.
Summary
- Actions are the verbs: they define what your operating model can do
- Query vs Mutation: determines caching, composability, and how the AI reasons about consequences
- Subjects are optional: use them when an action operates on a specific resource, omit them for creation, search, and system-wide operations
- Subjects enable dispatch: when present, RARS selects the right implementation based on the subject's type, enabling polymorphic action interfaces
- Overloading enables extensibility: define interfaces that third-party matrices can specialize with their own implementations
- The I/O contract is everything: define precise input and output schemas. The platform validates them uniformly, whether the action is deterministic or agentic.
- Contractual agents: agentic actions are held to the same schema validation as API calls. You can swap handler types without changing the contract.
- Design payloads intentionally: separate from domain entities, focused on what each operation needs
- Cache queries: set TTL based on data volatility, customize keys when input semantics matter
- Enable healing: self-repair for service integrations handles most transient failures automatically
- Actions cross boundaries: use them for external system calls and orchestration, not for querying data already in the graph