Skip to content

Example: Research and Writer Pipeline

A classic two-agent sequential workflow where a researcher gathers information and a writer turns it into a blog post. This is the same pattern used by the included ResearchWriterExample application.


  1. Researcher receives a topic and produces a structured research summary
  2. Writer receives the topic and the researcher’s summary, and produces a polished blog post

import dev.langchain4j.model.openai.OpenAiChatModel;
import net.agentensemble.*;
import net.agentensemble.ensemble.EnsembleOutput;
import net.agentensemble.task.TaskOutput;
import net.agentensemble.workflow.Workflow;
import java.util.List;
import java.util.Map;
public class ResearchWriterExample {
public static void main(String[] args) {
String topic = args.length > 0 ? String.join(" ", args) : "AI agents in enterprise software";
var model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.build();
// 1. Define agents
var researcher = Agent.builder()
.role("Senior Research Analyst")
.goal("Find accurate, well-structured information on any given topic")
.background("You are a veteran researcher with expertise in technology and business. " +
"You write concise, factual summaries backed by evidence.")
.llm(model)
.build();
var writer = Agent.builder()
.role("Content Writer")
.goal("Write engaging, well-structured blog posts from research material")
.background("You are an experienced technology writer. You translate complex research " +
"into clear, engaging prose for a professional audience.")
.llm(model)
.responseFormat("Use markdown with clear headings (##) and bullet points where appropriate.")
.build();
// 2. Define tasks
var researchTask = Task.builder()
.description("Research the latest developments in {topic}. " +
"Cover current state, key players, recent breakthroughs, and near-term outlook.")
.expectedOutput("A 400-500 word structured summary with clear sections: " +
"Current State, Key Players, Recent Developments, and Outlook.")
.agent(researcher)
.build();
var writeTask = Task.builder()
.description("Write a professional blog post about {topic} based on the provided research.")
.expectedOutput("A 600-800 word blog post in markdown format. Include: an engaging title, " +
"an introduction, three main sections with subheadings, and a conclusion.")
.agent(writer)
.context(List.of(researchTask)) // writer receives the researcher's output
.build();
// 3. Build and run the ensemble
EnsembleOutput output = Ensemble.builder()
.agent(researcher)
.agent(writer)
.task(researchTask)
.task(writeTask)
.workflow(Workflow.SEQUENTIAL)
.build()
.run(Map.of("topic", topic));
// 4. Display results
System.out.println("=".repeat(60));
System.out.println("FINAL BLOG POST");
System.out.println("=".repeat(60));
System.out.println(output.getRaw());
System.out.println();
System.out.printf("Completed in %s | Total tool calls: %d%n",
output.getTotalDuration(), output.getTotalToolCalls());
}
}

Terminal window
git clone https://github.com/AgentEnsemble/agentensemble.git
cd agentensemble
export OPENAI_API_KEY=your-api-key
# Default topic (artificial intelligence agents in 2026)
./gradlew :agentensemble-examples:runResearchWriter
# Custom topic
./gradlew :agentensemble-examples:runResearchWriter --args="quantum computing applications in finance"

Give the researcher a web search tool to find current information:

var researcher = Agent.builder()
.role("Senior Research Analyst")
.goal("Find accurate, current information using web search")
.llm(model)
.tools(List.of(new WebSearchTool(searchClient)))
.maxIterations(10)
.build();

Add short-term memory so the writer automatically receives the researcher’s output without explicit context:

EnsembleOutput output = Ensemble.builder()
.agent(researcher)
.agent(writer)
.task(researchTask)
.task(writeTask) // no context() needed when using shortTerm memory
.memory(EnsembleMemory.builder().shortTerm(true).build())
.build()
.run(Map.of("topic", topic));

Add a third agent to review and polish the blog post:

var editor = Agent.builder()
.role("Senior Editor")
.goal("Polish writing for clarity, flow, and accuracy")
.llm(model)
.build();
var editTask = Task.builder()
.description("Review and polish the blog post about {topic}")
.expectedOutput("The polished blog post with any corrections and improvements applied")
.agent(editor)
.context(List.of(writeTask))
.build();
Ensemble.builder()
.agent(researcher).agent(writer).agent(editor)
.task(researchTask).task(writeTask).task(editTask)
.build()
.run(Map.of("topic", topic));