User preferences
New to Akka? Start here: Use the Build your first agent guide to get a simple agentic service running locally and interact with it. |
Overview
To make the activity suggestions more personalized, we will add user preferences that the ActivityAgent
will use.
In this part of the guide you will:
-
Create an entity for the preferences
-
Use the preferences from the agent
-
Include a user id in the endpoint
Prerequisites
-
Java 21, we recommend Eclipse Adoptium
-
Apache Maven version 3.9 or later
Add the entity for preferences
Add a new file PreferencesEntity.java
to src/main/java/com/example/application/
import akka.Done;
import akka.javasdk.annotations.ComponentId;
import akka.javasdk.eventsourcedentity.EventSourcedEntity;
import java.util.List;
@ComponentId("preferences") (2)
public class PreferencesEntity extends EventSourcedEntity<Preferences, PreferencesEvent> { (1)
public record AddPreference(String preference) {}
@Override
public Preferences emptyState() {
return new Preferences(List.of());
}
public Effect<Done> addPreference(AddPreference command) { (3)
return effects()
.persist(new PreferencesEvent.PreferenceAdded(command.preference()))
.thenReply(__ -> Done.done());
}
public Effect<Preferences> getPreferences() { (4)
return effects().reply(currentState());
}
@Override
public Preferences applyEvent(PreferencesEvent event) { (5)
return switch (event) {
case PreferencesEvent.PreferenceAdded evt -> currentState()
.addPreference(evt.preference());
};
}
}
1 | Extend EventSourcedEntity , with the type of state this entity represents, and the interface for the events it persists. |
2 | Annotate the class so Akka can identify it as an event-sourced entity. |
3 | Define the command handler method to add a preference text. |
4 | Define another command handler to retrieve all preferences. |
5 | Updates of the Preferences state is performed from the persisted events. |
You also need the Preferences
and the PreferencesEvent
records.
Add a new file Preferences.java
to src/main/java/com/example/domain/
import java.util.ArrayList;
import java.util.List;
public record Preferences(List<String> entries) {
public Preferences addPreference(String preference) {
var newEntries = new ArrayList<>(entries);
newEntries.add(preference);
return new Preferences(newEntries);
}
}
Here we use plain text for the preferences, but it could be more structured information.
Add a new file PreferencesEvent.java
to src/main/java/com/example/domain/
import akka.javasdk.annotations.TypeName;
public sealed interface PreferencesEvent {
@TypeName("preference-added")
record PreferenceAdded(String preference) implements PreferencesEvent {}
}
Use from the agent
To use the preferences in the ActivityAgent
we need to inject the component client and retrieve the preferences for a given user id.
import akka.javasdk.agent.Agent;
import akka.javasdk.annotations.ComponentId;
import akka.javasdk.client.ComponentClient;
import java.util.stream.Collectors;
@ComponentId("activity-agent")
public class ActivityAgent extends Agent {
public record Request(String userId, String message) {}
private static final String SYSTEM_MESSAGE =
"""
You are an activity agent. Your job is to suggest activities in the
real world. Like for example, a team building activity, sports, an
indoor or outdoor game, board games, a city trip, etc.
""".stripIndent();
private final ComponentClient componentClient;
public ActivityAgent(ComponentClient componentClient) { (1)
this.componentClient = componentClient;
}
public Effect<String> query(Request request) { (2)
var allPreferences = componentClient
.forEventSourcedEntity(request.userId())
.method(PreferencesEntity::getPreferences)
.invoke(); (3)
String userMessage;
if (allPreferences.entries().isEmpty()) {
userMessage = request.message();
} else {
userMessage = request.message() +
"\nPreferences:\n" +
allPreferences.entries().stream().collect(Collectors.joining("\n", "- ", ""));
}
return effects()
.systemMessage(SYSTEM_MESSAGE)
.userMessage(userMessage) (4)
.thenReply();
}
}
1 | Inject ComponentClient . |
2 | Include user id in the request to the agent. |
3 | Retrieve the preferences for the given user id. |
4 | In addition to the original message, include the preferences in the user message to the LLM. |
User id in endpoint
We need to add the user id to the HTTP request.
@Post("/activities/{userId}")
public String suggestActivities(String userId, Request request) { (1)
var sessionId = UUID.randomUUID().toString();
return componentClient
.forAgent()
.inSession(sessionId)
.method(ActivityAgent::query)
.invoke(new ActivityAgent.Request(userId, request.message())); (2)
}
1 | Add userId as a path parameter. |
2 | Call the agent with the new ActivityAgent.Request record that includes the userId . |
Update preferences from endpoint
To update the preferences, we add another method to the endpoint:
public record AddPreference(String preference) {}
@Post("/preferences/{userId}")
public HttpResponse addPreference(String userId, AddPreference request) { (1)
componentClient
.forEventSourcedEntity(userId)
.method(PreferencesEntity::addPreference)
.invoke(new PreferencesEntity.AddPreference(request.preference())); (2)
return HttpResponses.created();
}
1 | Add a method to add a preference. |
2 | Call the PreferenceEntity for the given user id. |
Use the imports:
import akka.http.javadsl.model.HttpResponse;
import akka.javasdk.http.HttpResponses;
Running the service
Stop the service with ctrl-c
if it’s still running.
Start your service locally:
mvn compile exec:java
Pick a user id, here alice
, and add some preferences:
curl -i localhost:9000/preferences/alice \
--header "Content-Type: application/json" \
-XPOST \
--data '{
"preference": "I like outdoor activities."
}'
curl -i localhost:9000/preferences/alice \
--header "Content-Type: application/json" \
-XPOST \
--data '{
"preference": "I dislike museums."
}'
Ask for activities.
curl -i -XPOST --location "http://localhost:9000/activities/alice" \
--header "Content-Type: application/json" \
--data '{"message": "I am in Madrid. What should I do?"}'
Does it take your preferences into account for the suggestions?
Next steps
-
Activities may depend on the weather forecast. Continue with Weather agent that will make use of agent function tools.
-
Learn more about the
EventSourceEntity
component.