DEV Community

Sujan Lamichhane
Sujan Lamichhane

Posted on

Building an AI Agent System with the ReACT Pattern in Java

From answering questions to solving problems — Phase 6 of the Jarvis AI Platform

After Phase 5, Jarvis could hear, speak, remember conversations, retrieve documents, and use tools. But every interaction was still limited to a single request and a single response.

You: "What's the weather in Kathmandu?"

Whisper
    ↓
AiOrchestrator
    ↓
WeatherTool
    ↓
Text-to-Speech

Jarvis:
"It is 22°C and clear."
Enter fullscreen mode Exit fullscreen mode

That works well for simple questions.

It completely breaks down when a task requires multiple decisions.


The Limitation of Single-Turn AI

Imagine asking:

Research the top 3 Java AI frameworks,
compare them,
and summarize the findings.
Enter fullscreen mode Exit fullscreen mode

A traditional chatbot usually replies:

I don't have enough information to research that.

The problem isn't intelligence.

The problem is planning.

To answer properly, the AI must:

  1. Search for Java AI frameworks
  2. Search for comparisons
  3. Gather information
  4. Analyze results
  5. Produce a summary

That requires multiple tool calls and reasoning between each one.

This is exactly what AI agents are designed to do.


What Is the ReACT Pattern?

ReACT stands for:

Reason + Act

Instead of generating one response, the AI repeatedly performs a reasoning loop.

THINK
↓
ACT
↓
OBSERVE
↓
THINK
↓
ACT
↓
OBSERVE
↓
FINAL ANSWER
Enter fullscreen mode Exit fullscreen mode

Example:

THOUGHT:
I should search for Java AI frameworks.

ACTION:
search

INPUT:
Java AI frameworks 2026

↓

OBSERVATION:
Spring AI
LangChain4j
Semantic Kernel

↓

THOUGHT:
Now I need comparison data.

↓

ACTION:
search

INPUT:
Spring AI vs LangChain4j

↓

FINAL ANSWER
Enter fullscreen mode Exit fullscreen mode

Instead of guessing everything up front, the AI gathers information step by step before producing the final response.


The Biggest Architectural Decision

The most important design decision of Phase 6 was not modifying the existing chat pipeline.

Instead of turning AiOrchestrator into a giant class responsible for both chat and agents, agents became a completely separate orchestration layer.

❌ Wrong

AiOrchestrator
    ↓
Single Chat
    ↓
Agent Logic
    ↓
Tool Logic
    ↓
Everything Mixed Together


✅ Correct

AgentController
        ↓
AgentOrchestrator
        ↓
AgentExecutor
        ↓
AgentPlanner
        ↓
ToolRegistry

AiOrchestrator
        ↑
Remains Completely Unchanged
Enter fullscreen mode Exit fullscreen mode

Everything built during Phases 1–5 continues working exactly as before.

Agents simply reuse the existing tools.


The Four-Layer Agent System

The final architecture looks like this.

AgentController
        ↓
AgentOrchestrator
        ↓
AgentExecutor
        ↓
AgentPlanner
        ↓
ToolRegistry
Enter fullscreen mode Exit fullscreen mode

Each component has a single responsibility.

  • AgentController exposes the REST API.
  • AgentOrchestrator manages the agent lifecycle.
  • AgentExecutor runs the ReACT loop.
  • AgentPlanner asks the AI what to do next.
  • ToolRegistry executes the selected tool.

Keeping these responsibilities isolated made the implementation significantly easier to maintain.


Teaching the AI to Think

The planner doesn't simply ask the AI for an answer.

Instead, it asks for structured output.

You are an AI agent.

Available tools:

- getWeather
- calculate
- search

For every step respond exactly as:

THOUGHT:
...

ACTION:
...

INPUT:
...
Enter fullscreen mode Exit fullscreen mode

When enough information has been gathered:

THOUGHT:
...

FINAL_ANSWER:
...
Enter fullscreen mode Exit fullscreen mode

This prompt acts as a contract between the model and the parser.


Parsing Structured Output Correctly

The first implementation used indexOf().

response.indexOf("ACTION:");
Enter fullscreen mode Exit fullscreen mode

That failed whenever the literal text ACTION: appeared inside user data.

The solution was precompiled regular expressions anchored to the beginning of each line.

private static final Pattern ACTION_PATTERN =
    Pattern.compile(
        "(?ms)^ACTION:\\s*(.*?)"
            + "(?=^(?:THOUGHT:|INPUT:|FINAL_ANSWER:)|\\z)");
Enter fullscreen mode Exit fullscreen mode

This guarantees that only real section headers are parsed.


The ReACT Execution Loop

The executor coordinates the complete lifecycle.

public Flux<AgentEvent> execute(
        Agent agent,
        UUID userId) {

    return Flux.create(sink ->
            runLoop(sink, agent, userId))
        .subscribeOn(Schedulers.boundedElastic())
        .timeout(TOTAL_TIMEOUT);
}
Enter fullscreen mode Exit fullscreen mode

A few design decisions are worth highlighting.

Flux.create()

Flux.generate() allows only one event per iteration.

Agents frequently emit multiple events:

  • THINK
  • ACT
  • OBSERVE

Flux.create() supports that naturally.

boundedElastic()

Planning calls, database writes, and tool execution are blocking operations.

Moving the entire loop onto boundedElastic() keeps the WebFlux event loop free.

Safety Limits

Every agent is protected by:

  • Maximum step count
  • Step timeout
  • Total execution timeout

Agents can never run forever.


Fixing the Step Index Bug

Initially each event incremented the step counter independently.

THINK → Step 0

ACT → Step 1

OBSERVE → Step 2
Enter fullscreen mode Exit fullscreen mode

Those three events actually belong to the same logical step.

The fix was simple.

Capture the current step once.

final int currentStep = stepIndex;

emitThink(currentStep);

emitAct(currentStep);

emitObserve(currentStep);

stepIndex++;
Enter fullscreen mode Exit fullscreen mode

Now every event generated during one reasoning cycle shares the same step number.


Exact Tool Matching

Originally tool dispatch used substring matching.

method.contains(toolName)
Enter fullscreen mode Exit fullscreen mode

This produced unexpected matches.

search
↓

webSearch

↓

searchDocuments
Enter fullscreen mode Exit fullscreen mode

The correct implementation performs exact matching.

method.equalsIgnoreCase(toolName.trim())
Enter fullscreen mode Exit fullscreen mode

Because the system prompt already specifies the exact method names, exact matching is both safer and simpler.


Streaming Agent Events

Agents execute for much longer than a normal chat response.

The browser shouldn't wait until everything finishes.

Instead, every reasoning step is streamed immediately.

event: think

event: act

event: observe

event: final

event: done
Enter fullscreen mode Exit fullscreen mode

Users can literally watch the AI think.


Handling Client Disconnects

One subtle problem appeared during testing.

If a browser tab closed, the agent continued executing in the background.

The fix required checking cancellation inside every loop iteration.

if (sink.isCancelled()) {
    return;
}
Enter fullscreen mode Exit fullscreen mode

One small check prevents wasted CPU time and unnecessary background work.


Agent State Machine

Agents move through a strict lifecycle.

PENDING
    ↓
RUNNING
    ↓
COMPLETED

or

FAILED

or

CANCELLED
Enter fullscreen mode Exit fullscreen mode

Invalid transitions are rejected directly by the domain model.

agent.withRunning();

agent.withCompleted();

agent.withFailed();
Enter fullscreen mode Exit fullscreen mode

The service layer doesn't enforce state rules.

The domain object does.


Compare-and-Set Updates

Concurrent updates introduced another challenge.

A completion event and an error event could arrive simultaneously.

Instead of overwriting each other, updates use compare-and-set semantics.

UPDATE agents

SET status = :newStatus

WHERE id = :id

AND status = :expectedStatus
Enter fullscreen mode Exit fullscreen mode

If another thread already changed the state, zero rows are updated.

No race conditions.

No silent overwrites.


REST API

The complete agent system exposes six endpoints.

POST   /api/v1/agents/stream
POST   /api/v1/agents
GET    /api/v1/agents
GET    /api/v1/agents/{id}
GET    /api/v1/agents/{id}/steps
DELETE /api/v1/agents/{id}
Enter fullscreen mode Exit fullscreen mode

The streaming endpoint returns live ReACT events while the asynchronous endpoint starts long-running agents without holding the HTTP request open.


A Complete Agent Execution

User

↓

"What is the weather in London
and Tokyo,
and what time is it there?"

↓

THINK

↓

Weather Tool

↓

Time Tool

↓

Weather Tool

↓

Time Tool

↓

FINAL ANSWER
Enter fullscreen mode Exit fullscreen mode

One request.

Multiple tools.

One coherent response.

No Python.

No LangChain.

Pure Java with Spring AI.


Lessons Learned

Structured prompts are contracts

The parser expects a specific format.

Any ambiguity breaks the workflow.

Prompt engineering matters just as much as parser implementation.


Graceful degradation wins

AI models occasionally produce malformed output.

Rather than failing, Jarvis treats unknown responses as a final answer and continues.


Domain models should enforce rules

The Agent object owns its lifecycle.

Impossible transitions become impossible states.


Compare-and-set prevents races

Multiple asynchronous events may update the same row.

Checking the expected state inside SQL eliminates lost updates.


Performance

Running on an Intel Core Ultra 7 with 16 GB RAM:

Operation Typical Time
Agent creation ~10 ms
AI planning 2–8 s
Tool execution 50–500 ms
Step persistence ~10 ms
Typical 3-step task 10–25 s

The AI planning phase dominates overall execution time.


What's Next

Phase 7 introduces the complete web interface.

It brings together everything built so far:

  • Real-time chat
  • Agent dashboard
  • Memory management
  • Document search
  • Voice interface
  • Settings management

The backend is complete.

The next challenge is building the frontend.


Contributing

Jarvis is open source under the Apache 2.0 License.

Current contributor-friendly issues include:

#84  CLI agent commands

#85  Agent REST API integration tests

#66  CLI tool commands

#34  CLI memory commands
Enter fullscreen mode Exit fullscreen mode

GitHub:

https://github.com/sujankim/jarvis-ai-platform
Enter fullscreen mode Exit fullscreen mode

Jarvis AI Platform Series


Your AI. Your Data. Your Machine.

Top comments (0)