Integrate an agent
A taskable agent is an asset, or a group of assets, that listens for tasks assigned to it in Lattice, executes them, and reports its progress back. This guide walks through the full agent workflow: publishing a taskable agent, streaming and parsing assigned tasks, updating task status, and handling cancellation requests.
An agent calls the following API to listen for tasks assigned to it in Lattice:
StreamAsAgent— For monitoring tasks routed to the agent using REST.ListenAsAgent— For monitoring tasks routed to the agent using gRPC.
Before you begin
- To publish taskable entities, and subscribe to tasks, set up your Lattice environment.
- Familiarize yourself with entities and different entity types.
- Review how to define a task. This guide uses the
Objectivetask definition from that page as its example.
gRPC authentication
If you are using gRPC with client credentials, set up the token refresh module before running the examples on this page.
Publish a taskable agent
An asset is an entity under your control, or under the control of another operator or system. Assets may accept tasks such as search or tracking. An agent is an asset, or a group of assets, that can complete a specific set of defined tasks.
To publish an agent, do the following:
Define a TaskCatalog
The entity model’s TaskCatalog
component defines the tasks that an asset can execute.
For example, if your asset supports the Objective task, publish an asset entity that includes it in its catalog, and listen for and execute that task. The
taskSpecificationUrl is the type URL of the task definition you authored and published to the
Schema Registry:
Publish the agent
Use the PublishEntity method to publish a taskable agent:
If successful, you see the entity ID in the console:
Process assigned tasks
Once an agent is published, the integration should open a stream to receive tasks routed to it. Each task carries a
specification — a
google.protobuf.Any that holds the task’s data.
To act on a task, the agent reads the metadata off the request, then decodes the specification into
the task type it advertised in its catalog.
Stream tasks as an agent
Use StreamAsAgent,
or ListenAsAgent
for gRPC, to open the stream. When an executeRequest arrives, the agent reads the task ID and status version,
parses the task data, then reports that it has started the task.
Task delivery over a stream does not wait for the agent to acknowledge receipt, so a silently
dropped connection might result in your agent silently skipping a task. To get a positive signal
that the connection is alive, set heartbeatIntervalMs when you open the stream:
The interval in milliseconds at which Lattice sends heartbeat events on the stream.
For StreamAsAgent, the default is 30000 ms, and the minimum is 1000 ms. Smaller values are
raised to 1000 ms. For ListenAsAgent, heartbeats are disabled unless you set this field.
When set, Lattice sends a heartbeat at the specified interval, and your agent can treat a missing heartbeat as a dropped connection and reconnect. Heartbeats arrive on the same stream as task requests and carry only a timestamp, so the receive loop skips them rather than treating them as tasks.
Replace AGENT_ID with the ID of the agent you want to task. If you are developing on Sandboxes,
replace this with the following simulated asset: Demo-Sim-Asset1:
If successful, you see the following output:
An agent should also handle stream interruptions gracefully. If the connection to Lattice fails, the stream closes. Handle stream errors and reconnect so that your agent keeps its subscription to task updates through transient network issues.
For more information, see Retry connections.
This example reports STATUS_EXECUTING as soon as a task arrives, but an agent isn’t obligated
to accept every task.
Before executing the task, validate that the agent can actually perform the task —
for example, that the requested parameters are within its capabilities. If it can’t, the agent
can reject the task by reporting STATUS_DONE_NOT_OK with a
TaskError of
ERROR_CODE_REJECTED instead of starting execution.
Parse the task specification
Decode the task’s specification to read the Objective the operator sent.
Over REST, the specification is delivered as JSON — a type URL alongside the task’s flattened
fields. Over gRPC, it’s a protobuf Any that you unpack into the Objective message
generated from your message. Check the type URL before reading the fields, since an
agent’s catalog can advertise more than one task type. Because Objective is a oneof,
inspect which target is set — an entity_id or an lla point:
The gRPC examples import an Objective type generated from your own
objective.proto. Publish your schema to the Schema
Registry, then generate the language bindings before running the agent.
Task and verify the task handler
The following steps aren’t required to implement tasking — they show how to exercise your task handler end to end in a development environment. Assign a task to your agent, then confirm it receives, parses, and updates the task.
Assign a task using the UI
In most cases an operator uses the Lattice UI to task your agent. To test the agent’s task handler in Lattice Sandboxes, do the following:
- Open your environment’s Lattice UI, and choose an asset from the Assets panel. On Sandboxes, choose Demo-Sim-Asset1.
- From the entity pane, choose Task, then select the
Objectivetask. - From the Task Details panel on the right hand side, set the objective — either an entity to reconnoiter or a lat/lon/altitude point — then choose Execute Task. This is the same field your agent reads from the task specification.
Update the status of a task
As an agent makes progress, it reports real-time updates to Lattice with
UpdateTaskStatus, incrementing the task’s
statusVersion on each update. In Lattice, tasks move through the following states:
STATUS_MACHINE_RECEIPT
The agent then responds back with status STATUS_MACHINE_RECEIPT, indicating that the task
has been received, and incrementing statusVersion accordingly:
STATUS_ACK
When the agent is ready to acknowledge the task, it does so using STATUS_ACK,
and again increments statusVersion:
STATUS_EXECUTING
As the agent begins to actively execute the task, it indicates this by reporting STATUS_EXECUTING back to Lattice:
STATUS_DONE_OK
Finally, when the agent reaches a terminal state and completes the task successfully, it
reports STATUS_DONE_OK. The agent can reach this state on its own, such as when its logic determines that the task’s objective is met, or in response to an operator-initiated request for task completion:
STATUS_DONE_NOT_OK
If the agent reaches a terminal state but does not complete the task successfully, it
reports STATUS_DONE_NOT_OK. The agent can reach this state on its own, such as when its logic determines that the task can’t be completed, or in response to an operator-initiated request for task completion or cancellation.
The agent should include a descriptive TaskError
when reporting STATUS_DONE_NOT_OK:
In this example, the message indicates that the agent encountered an internal error during task execution. You can add more descriptive errors to help the operator troubleshoot the issue accordingly.
The STATUS_DONE_OK and STATUS_DONE_NOT_OK statuses are considered terminal states.
Once a task reaches either state, it’s complete and cannot be updated.
Handle task cancellation
An operator can request that a task be cancelled while the agent is executing it. When a task has
already been sent to an agent, Lattice routes the CancelTask
request to that agent, which decides whether to accept or reject it. For the operator’s side of this
workflow, see Cancel tasks.
Set up a task processor
Create a task processor that listens for tasks assigned to your agent and handles cancellation requests.
For demonstration purposes, this example uses a TASK_ACTIVE environment variable to control whether the agent accepts or rejects cancellations:
The StreamAsAgent API establishes a server-sent events (SSE) stream that delivers three types of requests:
executeRequest which notifies the agent to start executing a task, completeRequest which requests the agent to complete a task, and
cancelRequest which requests the agent to cancel a task.
Handle cancellation requests
When a cancelRequest arrives, the agent retrieves the current task state and decides whether to accept or reject the cancellation.
Rejecting cancellation:
If the task is active and cannot be cancelled, the agent rejects the cancellation by
keeping the current status and attaching a TaskError
with code ERROR_CODE_REJECTED:
Accepting cancellation:
If the task can be cancelled, the agent accepts by setting the status to STATUS_DONE_NOT_OK
with a TaskError indicating ERROR_CODE_CANCELLED:
The agent must first retrieve the current task using GetTask to
obtain the current statusVersion,
then increment it before calling UpdateTaskStatus.
What’s next
- To create, monitor, and cancel tasks as an operator, see Operate on tasks.
- To author your own task definitions, see Define a task.
- See tasks in action with the auto-reconnaissance sample app.