This sample demonstrates how to create a custom human-in-the-loop form that allows users to review and confirm AI-generated data before adding it to a web map.

This app uses SkillAgent to create a custom agent for reporting road maintenance issues in a city. The skill instructs the agent to extract the report attributes and call a FunctionTool. The tool’s human-in-the-loop middleware resolves the address, previews the location, and interrupts execution. The arcgisInterrupt event displays the custom review form.

reportTool.middlewares = [
createHumanInTheLoopToolMiddleware({
outputKey: "approval",
interrupt: async ({ input }) => {
// the input variable includes the data extracted by the agent from the user's input
category.value = input.category;
severity.value = input.severity;
notes.value = input.notes;
address.value = pendingReport.address;
// zooms to the location on the map for preview
await previewLocation(pendingReport);
// Return the interrupt object to display the human-in-the-loop form with final submission options
return {
kind: "singleSelection",
message: "Review and edit the maintenance report before confirming submission.",
metadata: ["Yes", "No"],
};
},
}),
];

When the agent hits the interrupt, the human-in-the-loop middleware pauses execution. The application must listen for the arcgisInterrupt event to display the custom review form.

assistant.addEventListener("arcgisInterrupt", (event) => {
// Display the custom review form
document.getElementById("review-form").hidden = false;
});

The form occupies the assistant’s interrupt slot, replacing the default confirmation interface with editable category, severity, notes, and address fields.

<arcgis-assistant reference-element="#maintenance-map" always-use-agent-id="maintenance">
<template id="agent-template">
<arcgis-assistant-agent></arcgis-assistant-agent>
</template>
<form id="review-form" slot="interrupt" hidden>
<!-- Editable report fields and confirmation buttons -->
</form>
</arcgis-assistant>

When the user submits the form, the application captures the reviewed report attributes and calls assistant.submitInterrupt("Yes") to resume the tool’s execution.

form.addEventListener("submit", (event) => {
event.preventDefault();
const reviewedReport = {
location: pendingReport.location.clone(),
attributes: {
category: category.value,
severity: severity.value,
notes: notes.value.trim(),
address: pendingReport.address,
CreationDate: Date.now(),
},
};
assistant.submitInterrupt("Yes");
});

The tool executes its logic after the human-in-the-loop review. Once the user confirms the report, the tool uses the reviewed attributes to create or update the feature in the maintenance layer.

reportTool.execute = async ({ approval }) => {
const result = await layer.applyEdits({
addFeatures: [new Graphic({ geometry: reviewedReport.location, attributes: reviewedReport.attributes })],
});
const added = result.addFeatureResults[0];
layer.refresh();
return JSON.stringify({
message: "Saved the user-reviewed report. These values may differ from the original extraction.",
objectId: added.objectId,
...reviewedReport.attributes,
});
};
2DAssistantai-componentsarcgis-assistantarcgis-assistant-agentarcgis-maparcgis-searcharcgis-zoomcalcite-componentscalcite-inputcalcite-selectcalcite-text-areaeditinghuman-in-the-loopSkillAgent5.2