Sagacity¶
Human oversight and audit for Spring AI agents. Pause before irreversible actions. Approve or reject. Unwind automatically. Every decision tamper-evident.
Your AI agents are making decisions that affect real people — approving transactions, sending emails, charging cards, updating records. Right now:
- nobody approved the irreversible action before it ran
- nothing undoes completed steps when something later fails
- there is no compliance-grade record of what happened, in what order, who approved what
- there is no answer when legal asks "can you prove your AI didn't act without authorisation?"
Sagacity is the governance layer that sits between your Spring AI agent and the actions it takes — inside your existing Spring Boot app, with no new infrastructure.
Verifiable workflows¶
Declare multi-step agent workflows with @Workflow, @Stage, @Gate, and @Check.
The runtime executes stages in order, chains outputs as inputs, and compensates completed
stages in reverse when anything fails. Topology is validated at startup — bad definitions
crash the app, not a production run.
Automatic compensation¶
Every @Stage or @Tool paired with @Compensable gets an automatic undo when the
workflow fails. Compensations run in reverse execution order, each outcome journaled.
A failing compensation is recorded and the run continues — partial cleanup beats none.
Human approval gates¶
Mark a stage with @Gate(approvalRequired = true) and the workflow pauses before it
executes. Resume via REST or programmatically. The approval is bound to the exact input
the approver saw — a re-planning agent cannot substitute a different payload.
Two minutes to understand the shape
Wrap individual tools, or declare a workflow¶
Option A — annotate individual tools. Sagacity intercepts every Spring AI tool call,
journals it, and compensates on failure. Drop @Compensable on any @Tool method.
@Tool(description = "Charge the customer")
@Compensable(by = "refundCharge")
public String chargeCard(String amount, String customerId) {
return payments.charge(customerId, amount); // returns "ch_1M2n3"
}
@Compensation
public void refundCharge(CompensationContext ctx) {
payments.refund(ctx.result()); // ctx.result() = "ch_1M2n3"
}
Option B — declare a verifiable workflow. Define the entire multi-step process as annotated stages. Stage outputs chain as inputs automatically. Gates, checks, and compensation are all first-class.
@Workflow("refund-approval")
@Component
public class RefundWorkflow {
@Stage(order = 1)
@Compensable(by = "cancelValidation") // undo if anything fails later
public String validateRefund(String orderId) {
return validation.check(orderId); // returns "val-8821"
}
@Stage(order = 2)
@Compensable(by = "reverseRefund")
public String issueRefund(String validationId) {
// 'validationId' injected automatically from stage 1's return value
return payments.refund(validationId); // returns "ref-4492"
}
@Stage(order = 3)
@Gate(approvalRequired = true, // workflow pauses here
reason = "Compliance must approve before customer is notified")
public String notifyCompliance(String refundId) {
// POST /sagacity/workflows/{runId}/gates/notifyCompliance/approve
return compliance.log(refundId);
}
@Stage(order = 4)
public void sendConfirmation(String complianceRef) {
email.send(complianceRef, "Your refund is confirmed");
}
@Compensation
public void cancelValidation(CompensationContext ctx) { validation.cancel(ctx.result()); }
@Compensation
public void reverseRefund(CompensationContext ctx) { payments.reverse(ctx.result()); }
}
// Run async — pauses at stage 3 until approved
WorkflowHandle handle = workflowRuntime.runAsync(refundWorkflow, "ORDER-88210");
workflowRuntime.approveGate(handle.runId(), "notifyCompliance");
handle.awaitCompletion(30, TimeUnit.MINUTES);
// Every stage journaled. Failure at any stage compensates in reverse.
The distinction that matters
How Sagacity relates to Temporal¶
Temporal is infrastructure. It solves durable execution — if your process crashes, your workflow replays from exactly where it stopped. It also handles distributed workers, cross-service orchestration, and horizontal scale. Temporal just raised $550M and ships a Spring AI integration (temporal-spring-ai) that makes model calls and tool executions durable activities. It is serious, production-grade infrastructure.
That is not the same problem Sagacity solves.
A workflow that resumes perfectly after a crash still leaves you with: - A charged card when the business logic says the order should be abandoned - An inventory reservation nobody will ever release - An email already sent to a customer about a transaction that failed
Crash recovery cannot undo a side effect. A refund is not a retry.
Sagacity answers a different question: when your agent succeeds technically but the business says "this should not have happened," what gets unwound, who approved it before it ran, and what is the tamper-evident record?
| Temporal | Sagacity | |
|---|---|---|
| Durable execution (survive process crash) | ✅ cluster-backed | ❌ v0.4 adds JDBC state, not the same |
| Distributed workers, horizontal scale | ✅ | ❌ single JVM |
| Spring AI native integration | ✅ temporal-spring-ai (Preview) |
✅ sagacity-spring-boot-starter |
| Undo side effects on business failure | ⚠️ possible via child workflow pattern | ✅ @Compensable — first-class, annotation-driven |
| Tamper-evident SHA-256 audit trail | ❌ event history is operational, not compliance-grade | ✅ append-only, hash-chained, verifiable |
| EU AI Act Article 12 compliance | ❌ | ✅ |
| Human approval gates before irreversible actions | ❌ | ✅ @Gate(approvalRequired=true) |
| New infrastructure to run | ✅ cluster or Temporal Cloud (~$200+/month) | ❌ library — add a dependency |
| Adopt without rewriting agent code | ❌ must model everything as Workflows + Activities | ✅ annotate existing Spring AI tools |
They are complementary. A production system could use both — Temporal for durability and scale, Sagacity for compensation semantics, approval gates, and the compliance audit trail. The things Temporal's event history records and the things Sagacity's hash-chained journal records serve different audiences: Temporal's history is for engineers debugging a stuck workflow; Sagacity's journal is for compliance officers proving what an AI agent did, in what order, and who approved it.
Install
Add the dependency¶
<!-- Core: compensation + audit trail -->
<dependency>
<groupId>io.github.sumitvairagar</groupId>
<artifactId>sagacity-spring-boot-starter</artifactId>
<version>0.3.0</version>
</dependency>
<!-- Optional: declarative workflow engine -->
<dependency>
<groupId>io.github.sumitvairagar</groupId>
<artifactId>sagacity-workflows</artifactId>
<version>0.3.0</version>
</dependency>
Requires Java 17+, Spring AI 2.0.0, Spring Boot 4.0.x.
Spring Boot 4 is required, not optional
Spring AI 2.0.0 compiles against Spring Framework 7. Spring Boot 3.5.x
resolves Spring Framework 6.2 and will downgrade spring-core underneath
Spring AI, failing at runtime with
NoClassDefFoundError: org/springframework/core/Nullness.
Before you rely on it
Status¶
0.3.0 ships the workflow engine. The compensation, approval, audit, and workflow paths
are covered by 202 tests across unit and integration suites, but the library has not
been battle-tested in production by anyone yet.
Read the threat model before relying on the audit trail for anything that matters. Workflow state is in-memory in v0.3 — runs are lost on JVM restart. JDBC-backed durable state is the v0.4 priority.
Known gaps: no streaming tool-call support, no LangChain4j adapter, no approval dashboard UI. See the roadmap.