Task Reflection Examples¶
Basic Reflection — Accumulating Improvements Across Runs¶
import net.agentensemble.Ensemble;
import net.agentensemble.Task;
import net.agentensemble.reflection.InMemoryReflectionStore;
import net.agentensemble.reflection.ReflectionStore;
// Keep the same store instance across runs to persist reflections
ReflectionStore store = new InMemoryReflectionStore();
Task researchTask = Task.builder()
.description("Research the top 5 AI trends of 2025")
.expectedOutput("A structured report with trend name, description, and impact assessment")
.reflect(true) // enable with default settings
.build();
Ensemble ensemble = Ensemble.builder()
.chatLanguageModel(model)
.task(researchTask)
.reflectionStore(store)
.build();
// Run 1: baseline execution, reflection stored afterward
EnsembleOutput run1 = ensemble.run();
System.out.println("Run 1 complete: " + run1.getRaw());
// Run 2: reflection notes injected into prompt
EnsembleOutput run2 = ensemble.run();
System.out.println("Run 2 complete: " + run2.getRaw());
// Run 3: further refined
EnsembleOutput run3 = ensemble.run();
System.out.println("Run 3 complete: " + run3.getRaw());
Using a Cheaper Model for Reflection¶
ChatModel primaryModel = OpenAiChatModel.builder()
.apiKey(apiKey)
.modelName("gpt-4o")
.build();
ChatModel reflectionModel = OpenAiChatModel.builder()
.apiKey(apiKey)
.modelName("gpt-4o-mini") // cheaper for meta-analysis
.build();
Task task = Task.builder()
.description("Write a detailed technical architecture document")
.expectedOutput("A structured architecture doc with sections: Overview, Components, Data Flow, Security")
.reflect(ReflectionConfig.builder()
.model(reflectionModel)
.build())
.build();
Ensemble.builder()
.chatLanguageModel(primaryModel)
.task(task)
.reflectionStore(new InMemoryReflectionStore())
.build()
.run();
Observing Reflection Events¶
ReflectionStore store = new InMemoryReflectionStore();
Ensemble.builder()
.chatLanguageModel(model)
.task(Task.builder()
.description("Summarise the quarterly earnings report")
.expectedOutput("A concise executive summary with key metrics")
.reflect(true)
.build())
.reflectionStore(store)
.onTaskReflected(event -> {
System.out.printf("[Reflection] Task: %s | Run: %d | First: %s%n",
event.taskDescription(),
event.reflection().runCount(),
event.isFirstReflection());
System.out.println(" Refined description: " + event.reflection().refinedDescription());
System.out.println(" Observations: " + event.reflection().observations());
})
.build()
.run();
Custom Reflection Strategy¶
// Domain-specific strategy that checks output against a known rubric
ReflectionStrategy rubricStrategy = input -> {
String output = input.taskOutput();
List<String> observations = new ArrayList<>();
List<String> suggestions = new ArrayList<>();
if (!output.contains("Executive Summary")) {
observations.add("Output was missing an Executive Summary section");
suggestions.add("Explicitly require an Executive Summary in the task description");
}
if (output.length() < 500) {
observations.add("Output was shorter than expected for a detailed report");
suggestions.add("Specify a minimum length or section count in the expected output");
}
String refinedDesc = input.task().getDescription()
+ " Include an Executive Summary section.";
return TaskReflection.ofFirstRun(
refinedDesc,
input.task().getExpectedOutput(),
observations,
suggestions
);
};
Task task = Task.builder()
.description("Produce a market analysis report")
.expectedOutput("A detailed market analysis")
.reflect(ReflectionConfig.builder()
.strategy(rubricStrategy)
.build())
.build();
Reflection with Multiple Tasks in a Pipeline¶
Only tasks with .reflect(true) will be reflected on. Tasks without it run normally:
Task fetchTask = Task.builder()
.description("Fetch the latest earnings data from the financial API")
.expectedOutput("JSON array of quarterly earnings records")
.handler(ctx -> ToolResult.success(apiClient.fetchEarnings()))
// No reflection -- this is a deterministic data fetch, no prompt to improve
.build();
Task analysisTask = Task.builder()
.description("Analyse the earnings data and identify key trends")
.expectedOutput("A structured analysis with trend identification and year-over-year comparison")
.context(List.of(fetchTask))
.reflect(true) // reflection only on the analytical step
.build();
ReflectionStore store = new InMemoryReflectionStore();
Ensemble.builder()
.chatLanguageModel(model)
.task(fetchTask)
.task(analysisTask)
.reflectionStore(store)
.build()
.run();
Inspecting a Stored Reflection¶
ReflectionStore store = new InMemoryReflectionStore();
Task task = Task.builder()
.description("Write a technical blog post about microservices")
.expectedOutput("A 600-800 word blog post with introduction, 3 main points, and conclusion")
.reflect(true)
.build();
Ensemble.builder()
.chatLanguageModel(model)
.task(task)
.reflectionStore(store)
.build()
.run();
// Inspect the stored reflection directly
String identity = TaskIdentity.of(task);
store.retrieve(identity).ifPresent(reflection -> {
System.out.println("Refined description: " + reflection.refinedDescription());
System.out.println("Refined expected output: " + reflection.refinedExpectedOutput());
System.out.println("Observations:");
reflection.observations().forEach(o -> System.out.println(" - " + o));
System.out.println("Suggestions:");
reflection.suggestions().forEach(s -> System.out.println(" - " + s));
System.out.println("Run count: " + reflection.runCount());
});