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

Add the entity for preferences

Add a new file PreferencesEntity.java to src/main/java/com/example/application/

PreferencesEntity.java
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/

Preferences.java
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/

PreferencesEvent.java
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.

ActivityAgent.java
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.

ActivityEndpoint.java
@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:

ActivityEndpoint.java
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