AgentOps for Java: the guard-then-evaluate half of the AgentOps lifecycle in one AgentOpsTask, built on guardflow-java and evalflow-java. The Java counterpart to the Rust agentflow umbrella crate / agentflow-go — but not a mechanical re-export of either.
Rust's agentflow bundles nine sibling crates behind one dependency via pub use. Java (like Go) has no equivalent of re-exporting another package's full API under a new name, and mechanically wrapping every exported type from eight independent libraries would be both unidiomatic and a maintenance trap. Java code composes small libraries by depending on them directly:
| Stage | Java library |
|---|---|
| Orchestrate | graphflow-stream-java |
| Guard | guardflow-java |
| Act-guard | actionguard-java |
| Validate | schemaflow-java |
| Evaluate | evalflow-java |
| Checkpoint | rollbackflow-java |
| Trace | traceflow-java |
| Cache | cacheflow-java |
| Remember | memoryflow-java |
Add whichever stages you need and nest their Handler decorators — the same way Servlet Filters compose:
Handler handler = TraceFlow.withTrace("call_llm",
CacheFlow.withCache(myLlmHandler, cache, keyFn, ttl),
tracer
);What agentflow-java actually provides is the one piece of the Rust crate that's real glue code, not re-export: AgentOpsTask.
Not published to Maven Central. Build the two dependencies locally, then this repo:
git clone https://github.com/thaicn1712/guardflow-java && (cd guardflow-java && mvn -q install -DskipTests)
git clone https://github.com/thaicn1712/evalflow-java && (cd evalflow-java && mvn -q install -DskipTests)<dependency>
<groupId>io.github.thaicn1712</groupId>
<artifactId>agentflow-java</artifactId>
<version>0.1.0</version>
</dependency>AgentOpsTask task = AgentOpsTask.wrap(myLlmHandler)
.withGuard(Guard.create().with(new MinLength(15)))
.withMaxAttempts(3)
.withMetric(new F1Score());
AgentOpsResult result = task.run(input, "the quick brown fox jumps over a lazy dog");
// result.output() -- the accepted (possibly retried) response
// result.scores().get("f1_score") -- an evalflow.MetricResultmyLlmHandler is any Handler — String run(String input) throws Exception.
If a Guard is set and never passes within maxAttempts, run does not throw — it falls back to the last (still-invalid) attempt, the same as the Rust and Go originals. This is easy to get wrong: check result.scores() yourself, or inspect result.output() against your own validity check, if you need to detect and act on "the guard never actually passed." Don't assume a normal return from run means the output was valid.
mvn -q compile exec:java -Dexec.mainClass=io.github.thaicn1712.agentflow.examples.FullLifecycleExampleMIT