Blog

Demo: Build and deploy a multi-agent system with Akka - Complete

Written by Tyler Jewell | Jul 10, 2025 7:00:00 AM

In this demo, we build, run and evaluate an agentic system with Akka and Claude Code. It contains agents, memory, endpoints, orchestration, and streaming. The system is deployed as a 3 node cluster for resilience, and then deployed into GCP and AWS for multi-region failover and disaster recovery.

Generate agent

Use akka-context folder that contains Akka SDK documentation and check Agent 
docs.
Create an Akka SDK agent named `GreetingAgent` that assists users in learning 
greetings in different languages.
- **Input:** A string question.
- **Output:** Returns an `Effect<String>`.
- method with name `ask`
- Do not perform any environment variables check.
- Do not add JavaDoc annotations
- place it into com.example.application package
- system message should be a static variable
- **Guidelines for system prompt:**
  - The user must provide a language.
  - Always append the language used in parentheses in English (e.g., "Bonjour 
  (French)").
  - At the end of each response, append a list of previous greetings used in 
  the current session.

Test agent

Use akka-context folder that contains Akka SDK documentation and check Agent
integration test docs.
Develop only one integration test for `GreetingAgent` in the `com.example` 
package, named `IntegrationTest`.
- Include a `TestModelProvider` to mock the model with a fixed response.
- Ensure all necessary classes are properly imported.

Add endpoint

Use akka-context folder that contains Akka SDK documentation and check Endpoint 
docs and how to call an Agent.
Implement an HTTP endpoint `GreetingAgentEndpoint` with the following 
specifications:
- **Base path:** `/chat`
- **Endpoint:** POST `/ask`
- **Request:** Accepts a JSON record `QueryRequest` with fields `String userId` 
and `String question`. 
- Put the QueryRequest in the GreetingAgentEndpoint class
- The agent should use `userId` as the sessionId and `question` as the 
user message.
- add acl for all access

Add MCP server

Use akka-context folder that contains Akka SDK documentation and check 
MCPEndpoint docs
Create an MCP server endpoint named `UserNameMcpEndpoint` for retrieving 
user's name based on `userId`.
- Place the implementation in the `com.example.api` package.
- Provide a mock implementation that returns a random name for each query.
- add all needed annotations (class and method)
- use MCP Tool annotations with all required parameters including the 
McpEndpoint annotation

Add MCP tool to an agent

Use akka-context folder that contains Akka SDK documentation and check how 
to configure MCP tools for an Agent
Update the `GreetingAgent` to display the user's name during the first 
interaction, for example: "Hello <user name>!".
- Configure GreetingAgent to use `UserNameMcpEndpoint` as an MCP tool. No need 
to use AllowedToolNames. Use the service name specified by the `artifactId` 
in `pom.xml`.
- set `userId` from context().sessionId().
- Add the `userId` to the user message in the following format: 
"userId:<userID>;question:
- update system prompt accordingly 

Add streaming

Use akka-context folder that contains Akka SDK documentation and check Agent 
and Endpoint streaming support.
Replace the `GreetingAgent`'s `greet` method with a streaming response.
- Update the `GreetingAgentEndpoint`'s `ask` endpoint to use the streaming 
response from `GreetingAgent` and wrap it with Server-Sent Events (SSE), 
returning an `akka.http.javadsl.model.HttpResponse`.
- Remove all non-streaming methods from both `GreetingAgent` and 
`GreetingAgentEndpoint`.
- Update the integration test to use the streaming agent. Do not modify the 
`TestModelProvider`.
**Example usage in the integration test:**
var tokenStream = componentClient
    .forAgent()
    .inSession(sessionId)
    .tokenStream(HelloWorldAgent::streamGreet)
    .source(userGreeting);

// Run the stream and collect the tokens into a List
List<String> tokens = tokenStream
    .toMat(Sink.seq(), Keep.right())
    .run(testKit.getMaterializer())
    .toCompletableFuture()
    .get();