Multi-Agent Systems (MAS)
Building multi-agent systems in Java: orchestrator-worker coordination, agent handoffs, communication protocols and conflict resolution — and the honest case for when one agent is better.
On this page
Multi-agent systems are where the field's ambition outruns its judgement most often. They are genuinely useful for a specific shape of problem — and a needless complication for most others. This tutorial covers how to build them well and, just as importantly, when not to.
Key Takeaways
- Use multiple agents only when sub-tasks need different prompts, tools or models.
- Coordinate through an orchestrator; prefer structured messages over free text.
- Handoffs pass control plus context to a better-suited agent.
- Surface conflicts between agents — do not average them away.
The honest starting point
A single agent with a well-chosen tool set beats a multi-agent system on cost, latency and debuggability for the large majority of problems. Before building one, ask: do my sub-tasks genuinely need different system prompts, tools or models? If not, one agent is the answer.
Multiple agents earn their keep when the jobs are truly different:
| Agent | Prompt shape | Tools | Model |
|---|---|---|---|
| Filings analyst | Careful, literal, citation-obsessed | Document search | Strong |
| News scanner | Fast, high-volume, sceptical | News search | Cheap |
| Calculator | Never estimates, always uses a tool | Deterministic maths | Cheap |
Those are three incompatible system prompts. Merging them produces one prompt that contradicts itself.
Orchestrator-worker coordination
The most robust MAS topology: an orchestrator decomposes, delegates, and combines. Workers do not talk to each other directly.
public Report handle(String task) {
List<SubTask> subs = orchestrator.decompose(task);
// Independent workers run concurrently — cheap on Java 21 virtual threads.
List<WorkerResult> results = subs.parallelStream()
.map(this::dispatchToWorker)
.toList();
// Reconciliation, not just concatenation — see below.
return orchestrator.reconcile(task, results);
}Handoffs
Sometimes control should pass to a specialist along with the context gathered so far:
public String handle(String request) {
TriageResult triage = triageAgent.assess(request);
// Hand off to the right specialist, carrying the context forward so the
// specialist does not start from scratch.
return switch (triage.category()) {
case TECHNICAL -> technicalAgent.handle(request, triage.context());
case BILLING -> billingAgent.handle(request, triage.context());
case ESCALATE -> humanQueue.enqueue(request, triage.context());
};
}Communication: prefer structured messages
Agents that pass free text to each other accumulate ambiguity. Structured results are more reliable:
// Poor: free text the next agent must re-interpret.
record LooseResult(String text) {}
// Better: typed fields the orchestrator can route on and reconcile.
record Finding(String claim, String source, Confidence confidence, List<String> caveats) {}Conflict resolution
When agents disagree, the disagreement is signal. Detect and surface it:
public Reconciliation reconcile(List<Finding> findings) {
List<Conflict> conflicts = ConflictDetector.detect(findings);
if (!conflicts.isEmpty()) {
// Do NOT average or silently pick one. Report both positions with their
// evidence so a human can adjudicate.
return Reconciliation.withConflicts(findings, conflicts);
}
return Reconciliation.agreed(findings);
}Cost and failure modes
A multi-agent system multiplies everything: more model calls (higher cost and latency), more failure points, and a harder debugging story because a wrong answer could come from any agent or the reconciliation. Budget and trace accordingly:
- Per-run budget across all agents, not per agent — see productionizing agentic systems.
- Trajectory logging for every agent's every step.
- Timeouts on each worker so one slow agent does not stall the whole task.
When one agent wins
Reach for a single agent when: the sub-tasks share a prompt and tool set, latency matters, cost matters, or you are early and learning what the problem even is. You can always split later; splitting prematurely commits you to the complexity before you know you need it.
Next
- Agent frameworks compared
- Productionizing agentic systems
- FinAgentic project — a full multi-agent build
Frequently Asked Questions
When should I use multiple agents instead of one?
What is agent handoff?
How do agents communicate in a multi-agent system?
What happens when two agents disagree?
Related tutorials
- Memory Systems for AgentsHow agent memory works beyond a chat window: working, episodic and semantic memory, vector-based recall, memory consolidation, and implementing persistent agent memory in Java.
- Agent Frameworks ComparedA practical comparison of agent frameworks for Java developers: LangChain4j, Spring AI, and how the Python ecosystem (LangGraph, CrewAI, AutoGen) compares — plus when to use no framework at all.
- Planning & Reasoning in AgentsHow agents plan and reason: task decomposition, hierarchical planning, chain-of-thought and tree-of-thought — with Java examples and honest guidance on when planning helps.
- Building Autonomous Coding AgentsDesign autonomous coding agents in Java: code generation with verification, review agents that bias for precision, refactoring and test-generation agents — with the guardrails they need.