Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ Build container image:
```shell
mvn clean install -DskipTests
```
Install the `akka` CLI as documented in [Install Akka CLI](https://doc.akka.io/reference/cli/index.html).
Install the `akka` CLI as documented in [Install Akka CLI](https://doc.akka.io/operations/cli/installation.html).

Set up secret containing OpenAI API key:
```shell
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>io.akka</groupId>
<artifactId>akka-javasdk-parent</artifactId>
<version>3.5.3</version>
<version>3.5.4</version>
</parent>

<groupId>io.akka.ai</groupId>
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/demo/multiagent/application/ActivityAgent.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import akka.javasdk.agent.Agent;
import akka.javasdk.annotations.AgentDescription;
import akka.javasdk.annotations.ComponentId;
import akka.javasdk.annotations.Component;
import akka.javasdk.client.ComponentClient;
import demo.multiagent.domain.AgentRequest;
import java.util.stream.Collectors;

@ComponentId("activity-agent")
@Component(id = "activity-agent")
@AgentDescription(
name = "Activity Agent",
description = """
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/demo/multiagent/application/ActivityView.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package demo.multiagent.application;

import akka.javasdk.annotations.ComponentId;
import akka.javasdk.annotations.Component;
import akka.javasdk.annotations.Consume;
import akka.javasdk.annotations.DeleteHandler;
import akka.javasdk.annotations.Query;
import akka.javasdk.view.TableUpdater;
import akka.javasdk.view.View;
import java.util.List;

@ComponentId("activity-view")
@Component(id = "activity-view")
public class ActivityView extends View {

public record ActivityEntries(List<ActivityEntry> entries) {}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package demo.multiagent.application;

import akka.javasdk.agent.evaluator.SummarizationEvaluator;
import akka.javasdk.agent.evaluator.ToxicityEvaluator;
import akka.javasdk.annotations.ComponentId;
import akka.javasdk.annotations.Consume;
import akka.javasdk.client.ComponentClient;
import akka.javasdk.consumer.Consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@ComponentId("agent-team-eval-consumer")
@Consume.FromWorkflow(AgentTeamWorkflow.class)
public class AgentTeamEvaluatorConsumer extends Consumer { // <1>

private static final Logger logger = LoggerFactory.getLogger(
AgentTeamEvaluatorConsumer.class
);

private final ComponentClient componentClient;

public AgentTeamEvaluatorConsumer(ComponentClient componentClient) {
this.componentClient = componentClient;
}

public Effect onStateChanged(AgentTeamWorkflow.State state) { // <2>
if (state.status() == AgentTeamWorkflow.Status.COMPLETED) {
evalToxicity(state);
evalSummarization(state);
}
return effects().done();
}

private void evalToxicity(AgentTeamWorkflow.State state) {
var result = componentClient
.forAgent()
.inSession(sessionId())
.method(ToxicityEvaluator::evaluate) // <3>
.invoke(state.finalAnswer());
if (result.passed()) {
logger.debug("Eval toxicity passed, session [{}]", sessionId()); // <4>
} else {
logger.warn(
"Eval toxicity failed, session [{}], explanation: {}",
sessionId(),
result.explanation()
);
}
}

private void evalSummarization(AgentTeamWorkflow.State state) {
var agentsAnswers = String.join("\n\n", state.agentResponses().values());

var result = componentClient
.forAgent()
.inSession(sessionId())
.method(SummarizationEvaluator::evaluate)
.invoke(
new SummarizationEvaluator.EvaluationRequest(agentsAnswers, state.finalAnswer())
);
if (result.passed()) {
logger.debug("Eval summarization passed, session [{}]", sessionId());
} else {
logger.warn(
"Eval summarization failed, session [{}], explanation: {}",
sessionId(),
result.explanation()
);
}
}

private String sessionId() {
return messageContext().eventSubject().get();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import static java.time.Duration.ofSeconds;

import akka.Done;
import akka.javasdk.annotations.ComponentId;
import akka.javasdk.annotations.Component;
import akka.javasdk.annotations.StepName;
import akka.javasdk.client.ComponentClient;
import akka.javasdk.client.DynamicMethodRef;
Expand All @@ -20,7 +20,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@ComponentId("agent-team")
@Component(id = "agent-team")
public class AgentTeamWorkflow extends Workflow<AgentTeamWorkflow.State> { // <1>

public record Request(String userId, String message) {}
Expand Down
25 changes: 15 additions & 10 deletions src/main/java/demo/multiagent/application/EvaluatorAgent.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package demo.multiagent.application;

import akka.javasdk.agent.Agent;
import akka.javasdk.agent.EvaluationResult;
import akka.javasdk.annotations.AgentDescription;
import akka.javasdk.annotations.ComponentId;
import akka.javasdk.annotations.Component;
import akka.javasdk.client.ComponentClient;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;

@ComponentId("evaluator-agent")
@Component(id = "evaluator-agent")
@AgentDescription(
name = "Evaluator Agent",
description = """
Expand All @@ -25,13 +26,17 @@ public record EvaluationRequest(
String finalAnswer
) {}

public record EvaluationResult(String evaluation, String feedback) {
public boolean ok() {
return switch (evaluation.toLowerCase(Locale.ROOT)) {
public record Result(String explanation, String label) implements EvaluationResult {
public boolean passed() {
if (label == null) throw new IllegalArgumentException(
"Model response must include label field"
);

return switch (label.toLowerCase(Locale.ROOT)) {
case "correct" -> true;
case "incorrect" -> false;
default -> throw new IllegalArgumentException(
"Unknown evaluation result [" + evaluation + "]"
"Unknown evaluation result [" + label + "]"
);
};
}
Expand Down Expand Up @@ -61,8 +66,8 @@ public boolean ok() {
respecting user preferences is the most important criteria

Your response must be a single JSON object with the following fields:
- "evaluation": A string, either "Correct" or "Incorrect".
- "feedback": Specific feedback on what works well or deviations from preferences.
- "explanation": Specific feedback on what works well or deviations from preferences.
- "label": A string, either "Correct" or "Incorrect".
""".stripIndent();

private final ComponentClient componentClient;
Expand All @@ -71,7 +76,7 @@ public EvaluatorAgent(ComponentClient componentClient) {
this.componentClient = componentClient;
}

public Effect<EvaluationResult> evaluate(EvaluationRequest request) {
public Effect<Result> evaluate(EvaluationRequest request) {
var allPreferences = componentClient
.forEventSourcedEntity(request.userId())
.method(PreferencesEntity::getPreferences)
Expand All @@ -86,7 +91,7 @@ public Effect<EvaluationResult> evaluate(EvaluationRequest request) {
return effects()
.systemMessage(SYSTEM_MESSAGE)
.userMessage(evaluationPrompt)
.responseConformsTo(EvaluationResult.class) // <3>
.responseConformsTo(Result.class) // <3>
.thenReply();
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/demo/multiagent/application/PlannerAgent.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
import akka.javasdk.agent.Agent;
import akka.javasdk.agent.AgentRegistry;
import akka.javasdk.annotations.AgentDescription;
import akka.javasdk.annotations.ComponentId;
import akka.javasdk.annotations.Component;
import demo.multiagent.domain.AgentSelection;
import demo.multiagent.domain.Plan;
import demo.multiagent.domain.PlanStep;
import java.util.List;

@ComponentId("planner-agent")
@Component(id = "planner-agent")
@AgentDescription(
name = "Planner",
description = """
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package demo.multiagent.application;

import akka.javasdk.annotations.ComponentId;
import akka.javasdk.annotations.Component;
import akka.javasdk.annotations.Consume;
import akka.javasdk.client.ComponentClient;
import akka.javasdk.consumer.Consumer;
import demo.multiagent.domain.PreferencesEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@ComponentId("preferences-consumer")
@Component(id = "preferences-consumer")
@Consume.FromEventSourcedEntity(PreferencesEntity.class) // <1>
public class PreferencesConsumer extends Consumer { // <2>

Expand Down Expand Up @@ -46,13 +46,13 @@ public Effect onPreferenceAdded(PreferencesEvent.PreferenceAdded event) {
.invoke(evaluationRequest); // <4>

logger.info(
"Evaluation completed for session {}: evaluation={}, feedback='{}'",
"Evaluation completed for session {}: label={}, explanation='{}'",
activity.sessionId(),
evaluationResult.evaluation(),
evaluationResult.feedback()
evaluationResult.label(),
evaluationResult.explanation()
);

if (!evaluationResult.ok()) {
if (!evaluationResult.passed()) {
// run the workflow again to generate a better answer

componentClient
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package demo.multiagent.application;

import akka.Done;
import akka.javasdk.annotations.ComponentId;
import akka.javasdk.annotations.Component;
import akka.javasdk.eventsourcedentity.EventSourcedEntity;
import demo.multiagent.domain.Preferences;
import demo.multiagent.domain.PreferencesEvent;
import java.util.List;

@ComponentId("preferences")
@Component(id = "preferences")
public class PreferencesEntity extends EventSourcedEntity<Preferences, PreferencesEvent> {

public record AddPreference(String preference) {}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/demo/multiagent/application/SelectorAgent.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
import akka.javasdk.agent.Agent;
import akka.javasdk.agent.AgentRegistry;
import akka.javasdk.annotations.AgentDescription;
import akka.javasdk.annotations.ComponentId;
import akka.javasdk.annotations.Component;
import demo.multiagent.domain.AgentSelection;

@ComponentId("selector-agent")
@Component(id = "selector-agent")
@AgentDescription(
name = "Selector Agent",
description = """
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

import akka.javasdk.agent.Agent;
import akka.javasdk.annotations.AgentDescription;
import akka.javasdk.annotations.ComponentId;
import akka.javasdk.annotations.Component;
import java.util.Collection;
import java.util.stream.Collectors;

@ComponentId("summarizer-agent")
@Component(id = "summarizer-agent")
@AgentDescription(
name = "Summarizer",
description = "An agent that creates a summary from responses provided by other agents"
Expand Down Expand Up @@ -34,11 +34,11 @@ public Effect<String> summarize(Request request) {
var allResponses = request.agentsResponses
.stream()
.filter(response -> !response.startsWith("ERROR"))
.collect(Collectors.joining(" "));
.collect(Collectors.joining("\n\n"));

return effects()
.systemMessage(buildSystemMessage(request.originalQuery))
.userMessage("Summarize the following message: '" + allResponses + "'")
.userMessage("Summarize the following: \n" + allResponses)
.thenReply();
}
}
4 changes: 2 additions & 2 deletions src/main/java/demo/multiagent/application/WeatherAgent.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

import akka.javasdk.agent.Agent;
import akka.javasdk.annotations.AgentDescription;
import akka.javasdk.annotations.ComponentId;
import akka.javasdk.annotations.Component;
import akka.javasdk.annotations.FunctionTool;
import demo.multiagent.domain.AgentRequest;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@ComponentId("weather-agent")
@Component(id = "weather-agent")
@AgentDescription(
name = "Weather Agent",
description = """
Expand Down
Loading
Loading