Build and demo Java AI agent systems with langchain4j-agentic: workflow patterns, supervisor, custom Planner strategies (incl. the flagship typed-verdict / CriticResult-style critic pattern), plus MCP tools, A2A remote agents, build setup, and conference-demo storylines. Pinned to 1.15.0 / 1.15.0-beta25.
84
89%
Does it follow best practices?
Impact
100%
4.76xAverage score across 2 eval scenarios
Passed
No known issues
Progressive-reveal demo structure for a talk/workshop. The payoff is Act 3 — the typed-critic Custom Strategy (custom-strategy-critic-result.md, the "money slide").
One repo, four branches (act1…act4), each a delta on the previous.
Setup before the talk
make demo-reset to wipe generated files (notes/, *.html, out/) between rehearsals.maxSequentialToolsInvocations everywhere; keep a gpt-4o-mini fallback ready (gotchas.md).One annotation + one builder + Claude = a working tool-using agent. Use assets/App.java verbatim.
public interface Jclaw {
@UserMessage("You are jclaw. {{request}}")
@Agent(outputKey = "answer")
String act(@V("request") String request);
}
var jclaw = AgenticServices.agentBuilder(Jclaw.class)
.chatModel(claude)
.tools(new FileSystemTools())
.maxSequentialToolsInvocations(10)
.build();
System.out.println(jclaw.act("Summarize the README in this repo"));Beat: "No Python, no extra runtime. @Agent is @AiService with extras." Expect: "Is this just an AiService?" — yes, that's the point.
Add ShellTool (allowlisted to echo/ls/cat/wc for safety), WebFetchTool, and MessageWindowChatMemory.withMaxMessages(20). Show a two-turn conversation needing memory, a multi-tool task, and AgenticScope write-then-read.
Beat: "This is the shape of Claude Code — file ops + shell + web + memory — in Java, on the JVM you already run."
Make the topology visible and the control flow typed. Build Identify → Fix → Verify → Adjust where Verify returns CriticResult<AccountIssueSolution> and a custom strategy routes on it: successful==true → Finish (emit the typed solution), successful==false → Adjust with feedback → re-verify. Full code in custom-strategy-critic-result.md.
Live narration via an AgentListener:
→ identify / → fix / → verify (successful=false) / → adjust / → verify (successful=true) → doneThen the payoff:
HtmlReportGenerator.generateReport(monitor, Path.of("resolver-run.html"));Open it in the browser: topology + timeline + token counts, one line of code.
Why this beats a bare score loop: the critic returns a domain type (CriticResult<T>), routing reads result.successful() / result.feedback() / result.input() with compile-time safety, and Finish receives a fully typed AccountIssueSolution. This is the slide to dwell on.
Optional contrast: swap the custom strategy for supervisorBuilder() with responseStrategy(SUMMARY) — now an LLM picks the next agent. Showing deterministic Custom Strategy vs. non-deterministic supervisor in one demo is the whole "workflows ↔ pure agents" spectrum.
MCP (lower risk): point .toolProviders(...) at the filesystem MCP server (or, for a Confluent audience, the Confluent MCP server — "list my Kafka topics"). Same MCP ecosystem Claude Desktop uses, now from Java.
A2A (higher wow): a second Quarkus process exposes SummarizationAgent; jclaw calls it via a2aBuilder(...). Two JVMs, one calling the other. Wiring in mcp-and-a2a.md.
Closing beat: show the official tutorial URL and the in-tree SupervisorAgentIT.java so the audience can rebuild it.
FileSystemTools for Kafka tools; Act 3 problem becomes "diagnose a topic and propose a Flink SQL fix," verified by CriticResult<FlinkQuery>; Act 4 uses the Confluent MCP server.@Services with the Spring Boot 4 starter (in BOM 1.15.0).@Agent, save, rerun) is part of the show.Planner as the extension point.CriticResult verdict logic / wire an MCP server).supervisorContext.maxSequentialToolsInvocations — runaway loop = mortification.outputName from old examples — canonical is outputKey.npx MCP server with no warm-up — first call is slow enough to derail timing.successful==true/false edges firing off a real CriticResult<T> — the money slide, live.resolver-run.html — the most visually compelling thing the framework ships.MissingArgumentException, then add .errorHandler(ctx -> ErrorRecoveryResult.retry()) and a default-filling listener — same code, no crash.