Module 9 — Agentic Patterns & the 2026 Agent Stack
1. From Single-Call Chat to Agentic Loops — Why This Is the Center of Gravity
The shift in framing, stated precisely: every module up through Module 8 covered patterns where a human (or application code) initiates one bounded request, the system resolves it — possibly via tool calls or RAG retrieval — and returns one answer. An agent, in the sense this module uses the word, is given a goal, not a single prompt, and operates in a loop: plan, act, observe the result, decide whether the goal is met, and either stop or iterate further — autonomously, without a human mediating every step.
Why this is genuinely the framework’s current center of gravity, not just a trendy addition: as the conference material puts it directly, the question for Java teams in 2026 has shifted from “how do I call an LLM” to “how do I harness this effectively” — the foundational layer (ChatClient, Advisors, tool calling, MCP) is now described explicitly as “the foundation, not the destination.” Spring AI’s own roadmap discussion confirms this is an active, ongoing integration effort — pulling several currently-separate Spring AI Community incubating projects (Agent Utils, Agent Judge, Tool Search, MCP Annotations) toward becoming composable parts of core Spring AI over time, rather than something already fully landed.
Talking point that sets the right expectation for the rest of this module: “It’s important to be precise about what’s core-stable and what’s still incubating here. ChatClient, Advisors, tool calling, and MCP — Modules 2 through 8 — are core Spring AI, shipping in the 1.0.x/1.1.x lines covered in Module 1’s versioning discussion. The agentic layer this module covers — Agent Harness-style tooling, the Judge framework, Agent Clients — currently lives largely in the separate Spring AI Community organization as standalone, early-version incubating projects, not yet merged into core Spring AI. That’s not a criticism — it’s exactly the right way to develop something this new — but it changes how I’d present this in an interview: as ‘here’s the direction the ecosystem is moving and the concrete tools already proving it out,’ not ‘here’s stable framework API I’d bet a production system on today.'”
2. Agentic Tooling for Custom Loops — The “Spring AI Agents” Project
The clearest concrete artifact here is the Spring AI Agents project (Spring AI Community, evolved from earlier “Agent Client and Agent Bench” work) — currently at 0.1.0-SNAPSHOT, explicitly framed by its own documentation as: “What ChatClient did for completion endpoints, AgentClient does for Agent CLIs.”
The core abstraction it introduces — five concepts, deliberately named to make agent design legible:
| Concept | What it represents |
|---|---|
| Goals | A clear objective stated in natural language — not a single prompt, a target outcome |
| Tools | Actions the agent can take to work toward the goal |
| Context | Information available to the agent for making decisions |
| Judges | Automated verification of whether the goal was actually achieved |
| Sandboxes | Isolated execution environments (Docker or local process) so an autonomous agent can act safely |
CoverageJudge judge = new CoverageJudge(80.0);
AgentClientResponse response = agentClient
.goal("Increase JaCoCo test coverage to 80%")
.workingDirectory(projectRoot)
.advisors(JudgeAdvisor.builder().judge(judge).build())
.run();
Note the deliberate continuity with everything already covered: this reuses the exact same Advisor pattern from Module 5 — JudgeAdvisor slots into the same composable middleware chain as memory and RAG advisors, applied here to verify goal completion rather than transform a chat request.
A genuinely instructive real example from this project’s own validation work: an autonomous agent running against this framework increased JaCoCo test coverage from 0% to 71.4% on one of Spring’s own tutorial projects — a concrete, measured demonstration rather than a hypothetical, and a useful one to cite because it shows the “goal + judge + sandbox” loop actually closing in practice, not just being architecturally plausible.
Sandboxes deserve emphasis as the safety layer, directly extending Module 4’s tool-security discussion: running an agent that can autonomously edit code, run builds, and execute shell commands needs an isolated, reproducible environment — Docker or a constrained local process — precisely because an autonomous loop, by definition, isn’t getting per-action human confirmation the way a single tool call might. Talking point: “Sandboxing here is the agentic-loop equivalent of the ‘irreversible actions need explicit confirmation’ principle from Module 4’s tool security discussion — when you remove the human-in-the-loop checkpoint for the sake of autonomy, the isolation boundary has to absorb the risk that confirmation step used to cover. That’s not optional infrastructure, it’s the thing that makes ‘let it run autonomously until the goal is met’ a defensible design rather than a reckless one.”
Currently supported agent platforms (per the project’s own stated scope): Claude Code, Gemini, Amazon Q, Amp, and Codex — with Goose, Aider, OpenHands, and GitHub Copilot CLI listed as coming soon. This is the same “unified interface across many agentic CLIs” concept the table of contents flagged as Spring AI Agent Clients.
3. The Judge Framework — Verifying Goals, Not Just Execution
The companion Agent Judge project (org.springaicommunity:agent-judge-core, currently version 0.9.1) is described in its own documentation precisely: “Agent-agnostic evaluation framework with deterministic, command, and LLM judges. Compose judges into juries with configurable voting strategies. Zero coupling to any specific agent implementation.”
Why “verifying the goal was achieved” is a genuinely different, harder problem than “the agent finished running” — this is the conceptual core of the whole module, worth stating plainly: an agentic loop can terminate (run out of iterations, decide it’s “done,” hit an error and stop) without having actually accomplished anything useful. Execution completing and the goal being met are entirely separate questions, and conflating them is the single most common way an agentic system silently fails — it looks successful (it ran, it returned, no exception was thrown) while having done nothing of real value.
Three categories of judge, each suited to a different kind of goal:
// Deterministic judge — objective, checkable fact, no model call needed
Judge buildJudge = BuildSuccessJudge.maven("compile");
// Command judge — runs an external check (e.g., a test suite, a linter)
// and bases the verdict on its result
// LLM judge — for goals that can't be reduced to a deterministic check
// (e.g., "is this code review thoughtful and specific")
// Composing judges into a Jury with a voting strategy —
// for goals where no single check is fully sufficient on its own
Jury jury = SimpleJury.builder()
.name("eval")
.judge(buildJudge)
.judge(FileExistsJudge.of("target/classes/App.class"))
.votingStrategy(VotingStrategy.majority())
.build();
Verdict verdict = jury.vote(context);
The deliberate ordering — deterministic, then command, then LLM-based — is itself the design lesson worth internalizing. A goal like “increase test coverage to 80%” has an unambiguous deterministic check (the actual coverage number); a goal like “the code follows our conventions” might need a command-based linter check; a goal like “this explanation is genuinely clear to a non-technical reader” may have no honest deterministic proxy and ultimately needs an LLM-as-judge evaluation, with all the calibration caveats that implies.
Talking point connecting this directly to the A/B Testing module’s evaluation discipline from your Data Analyst track: “This is the same hierarchy of evidence quality from the experimentation module, just applied to agent evaluation instead of product metrics — a deterministic check is the equivalent of a hard, objective outcome metric; an LLM-as-judge verdict is closer to a subjective survey response, useful but with its own calibration and reliability questions. The Jury/voting-strategy pattern is a sensible mitigation for exactly the reason ensembling helps in any noisy-measurement context — no single judge, especially an LLM-based one, should be trusted as the sole arbiter of whether an autonomous agent actually succeeded, the same caution that applies to trusting a single noisy metric as the sole signal in an A/B test.”
A direct, important caution worth naming explicitly, since it’s the natural extension of every evaluation-honesty theme across this whole curriculum: an LLM-as-judge evaluating an LLM-driven agent’s output has a structural conflict-of-interest-shaped risk — both the agent and the judge are subject to similar blind spots if built on similar underlying models, and a judge can be gamed or simply share the same failure modes as the system it’s evaluating. Deterministic and command-based judges don’t have this problem, which is exactly why the framework explicitly supports composing multiple judge types rather than defaulting to “ask an LLM if it worked.”
4. Agent Clients — Unified Interface to External Agentic CLIs
This is the part of the Spring AI Agents project that directly matches the table of contents’ framing: “What ChatClient did for completion endpoints, AgentClient does for Agent CLIs.”
The problem being solved: over twenty agentic CLI and IDE tools now exist (Claude Code, Gemini CLI, Amazon Q, Amp, Codex, and more) — each with its own invocation mechanism, its own configuration, its own output format. Without a unifying abstraction, a Java application wanting to programmatically drive any of these tools would need bespoke integration code per tool, the exact N-bespoke-integrations problem MCP solves for tool access (Module 8), now recurring one layer up at the agent-orchestration level.
// Conceptual shape — invoking a specific agentic CLI through the unified interface,
// rather than shelling out to each tool's own bespoke invocation pattern
AgentClient claudeCodeClient = AgentClient.builder()
.platform("claude-code")
.build();
AgentClientResponse response = claudeCodeClient
.goal("Fix the failing test in OrderServiceTest and explain the root cause")
.workingDirectory(projectRoot)
.run();
Why this matters architecturally, beyond convenience: it means application code orchestrating “have an agent do X” doesn’t need to be rewritten if the team switches which underlying agentic CLI they standardize on — the same portability argument from ChatModel (Module 2) and VectorStore (Module 7), applied one layer up the stack, to entire agentic tools rather than individual model calls.
Talking point: “This is the same abstraction instinct that’s run through the entire framework, just applied at a new layer each time — ChatModel made model providers swappable, VectorStore made vector databases swappable, and AgentClient is making entire agentic CLI tools swappable. Recognizing that pattern is more valuable than memorizing any one of these interfaces individually, because it tells you what the next abstraction in this framework will probably look like before it’s even built.”
5. Claude Code SDK for Java — Formal, Fully-Typed Integration
Distinct from the generic AgentClient abstraction above: where AgentClient provides a unified, lowest-common-denominator interface across many different agentic tools, the Claude Code SDK for Java is described as offering formal, fully-typed integration specifically for Claude Code — the deliberate tradeoff between portability and depth that’s recurred throughout this entire track (the same shape of tradeoff as ChatOptions vs. AnthropicChatOptions from Module 2).
Talking point: “This is the exact same portable-vs-provider-specific tradeoff from Module 2’s options-class discussion, just recurring at the agent-platform layer — AgentClient is the ChatOptions-equivalent lowest common denominator across many agentic CLIs, and the Claude Code SDK for Java is the AnthropicChatOptions-equivalent deep, fully-typed integration with one specific platform. I’d reach for the generic client when building tooling that genuinely needs to stay agent-agnostic, and the dedicated SDK when a team has deliberately standardized on one platform and wants full type safety and feature access rather than the generic interface’s necessarily reduced surface area.”
6. Emerging Protocols — ACP and A2A
ACP (Agent Client Protocol) — standardizes client-to-agent communication, specifically the relationship between an IDE/client and the agent it’s driving. Gemini CLI already supports it, and the explicit framing is that you can build ACP-compliant agents that plug into IDEs generally — the same standardization instinct as MCP (Module 8), but addressing a different relationship: MCP standardizes how a model accesses tools/resources; ACP standardizes how an IDE or client talks to an agent.
A2A (Agent-to-Agent) — enables direct agent collaboration — one agent communicating with and delegating to another agent, as distinct from a single agent calling tools. This is the protocol layer underneath multi-agent patterns like the Subagent pattern referenced in the broader Spring AI agentic-patterns content (delegating to specialized agents with isolated context windows) — A2A is what standardizes how that delegation/communication happens across system or even organizational boundaries, the same way MCP standardizes tool access.
A useful way to keep these three protocols distinct, since they’re easy to conflate:
| Protocol | Standardizes | Relationship |
|---|---|---|
| MCP | Model ↔ Tools/Resources/Prompts | A model calling out for capability or data |
| ACP | Client/IDE ↔ Agent | A human-facing tool driving an agent |
| A2A | Agent ↔ Agent | Two autonomous agents collaborating/delegating directly |
Talking point that closes the module honestly: “I’d present ACP and A2A in an interview exactly as the Spring I/O material itself frames them — emerging, named, real, and worth knowing the shape of, but genuinely earlier-stage than MCP, which already has broader adoption and Spring AI’s first-class core support from Module 8. The fact that Gemini CLI already supports ACP is a meaningful adoption signal, but I wouldn’t claim either protocol has reached MCP’s level of ecosystem maturity yet — and being precise about that distinction, rather than presenting all three protocols as equally settled, is itself the kind of calibrated honesty that should run through how this whole agentic layer gets discussed, given how new all of it genuinely is.”