Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Choose your own adventure in agentic design pat...

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

Choose your own adventure in agentic design patterns

Time to dive a little deeper, beyond the usual AI agent intros. When building your multi-agent systems, you’ll often be combining basic bricks like sequential, parallel, or loop flows. With those components, you create more complex patterns, like reflection loops, LLM-as-Judge, reviewer/critique agents, and coding agents in loops.

However the path to AI-nlightment might require you to make choices with the routing pattern, or to shepherd a swarm of agents to collaborate together. And even beyond that, with MCP and A2A, you might be able to create a swarm of remote agents!

As a committer on both LangChain4j and ADK for Java (Agent Development Kit), it’ll be my pleasure to guide you through this agentic adventure, and help you make the right choices and use the right abstractions on your journey.

Avatar for Guillaume Laforge

Guillaume Laforge

April 23, 2026

More Decks by Guillaume Laforge

Other Decks in Technology

Transcript

  1. Guillaume Laforge Developer Advocate chez Google Cloud Committer on ADK

    Java and LangChain4j Java Champion & Apache Groovy co-founder Cast Codeur Who Am I? glaforge glaforge.dev @glaforge @glaforge.dev @[email protected]
  2. I was carefully crafting my prompts… I was feeding just

    the right info at the right time! Grroaarr!!! I’m harnessing y’all!!! Prompt → Context → Harness
  3. Agentic Pattern ID Card • Name of the Pattern •

    Problem & Context • Solution & Implementation • Consequences (Pros & Cons)
  4. Programmatic Planning 🧩 Problem & Context Non-determinism is not always

    acceptable when your scenario has strong outcome constraints. 💡 Solution & Implementation You don’t always need an agentic framework or harness! Program the steps the agent must follow, and precisely control the LLM and context. ⏩ Consequences 👍 Pros • Easier to debug & reason about • Highly deterministic 👎 Cons • Code maintenance • Lack of agency for non-nominal situations Planning
  5. [ DEMO ] Deep Research ModelInteractionParams planParams = ModelInteractionParams.builder() .model("gemini-3-flash-preview")

    .input(String.format(""" Find a list of topics to research on the following subject: %s """, subject)) .responseFormat(GSchema.fromClass(String[].class)) .tools(new GoogleSearch()) .store(true) .build();
  6. [ DEMO ] Deep Research AgentInteractionParams researchParams = AgentInteractionParams.builder() .agent("deep-research-pro-preview-12-2025")

    .input(String.format(""" Write a concise research report on the following subject: <subject> %s </subject> By focusing on the following topics: <topics> %s </topics> """, subject, topicsList)) .background(true) .stream(true) .agentConfig(new DeepResearchAgentConfig(ThinkingSummaries.AUTO)) .store(true) .build();
  7. [ DEMO ] Deep Research ModelInteractionParams summaryParams = ModelInteractionParams.builder() .model("gemini-3-pro-preview")

    .input(String.format(""" Create a concise summary of the research below. Go straight with the summary, don't introduce the summary (don't write "Here's a summary..." or equivalent). %s """, reportBuilder)) .store(true) .build();
  8. [ DEMO ] Deep Research ModelInteractionParams infographicParams = ModelInteractionParams.builder() .model("gemini-3-pro-image-preview")

    .input(String.format(""" Create a hand-drawn and hand-written sketchnote style summary infographic, with a pure white background, use fluo highlighters for the key points, about the following information: %s """, summaryText)) .responseModalities(Interaction.Modality.IMAGE) .build();
  9. Retrieval Augmented Generation 🧩 Problem & Context Because of knowledge

    cut-off, LLMs don’t have the latest information, nor do they know about your own data. 💡 Solution & Implementation Content chunking or indexing, with vector search and/or BM25 retrieval, and prompt augmentation with highest matching documents ⏩ Consequences 👍 Pros • Agents have the latest information • No need for huge context windows 👎 Cons • Complexity of chunking & vectorizing • Necessitates a DB or search engine • Can miss the overall doc context Context Engineering
  10. Progressive Disclosure 🧩 Problem & Context Hard to scale agents

    loading hundreds of tool schemas into the system prompt: it causes "context rot", exhausts token limits, and triggers severe hallucinations. 💡 Solution & Implementation Reveal complexity gradually. Provide the agent with a lightweight menu of capabilities, injecting deep instructions only when a specific sub-task needs them. ⏩ Consequences 👍 Pros • Token Efficient: Lowers API costs. • Reduces Hallucinations. • Scalable: Infinite tool capacity. 👎 Cons • Requires external injection. • Added Latency. • Requires upfront thinking. Context Engineering
  11. Progressive Disclosure — Agent Skills pdf-processing/ ├── SKILL.md # Req:

    instr. + metadata ├── scripts/ # Opt: exec. code ├ └── pdfplumber.py ├── references/ # Opt: doc ├ └── forms.md └── assets/ # Opt: tmpl, resrc --- name: pdf-processing description: Extract PDF text, fill forms, merge files. Use when handling PDFs. --- # PDF Processing ## When to use this skill Use this skill when the user needs to work with PDF files... ## How to extract text 1. Use `pdfplumber` for text extraction... ## How to fill forms 1 2 3
  12. [DEMO] Draft & “Deslopify” // see https://glaforge.dev/posts/2026/03/08/fixing-ai-slop-with-a-skill-in-gemini-cli/ FileSystemSkill skill =

    ClassPathSkillLoader.loadSkill("skills/deslopify"); Skills skills = Skills.from(skill); var modelBuilder = GoogleAiGeminiChatModel.builder() .modelName("gemini-3.1-flash-lite-preview") .apiKey(System.getenv("GEMINI_API_KEY")) .sendThinking(true) .returnThinking(true); var model = modelBuilder.build(); var modelWithSearch = modelBuilder .allowGoogleSearch(true) .build();
  13. [DEMO] Draft & “Deslopify” public interface DraftAgent { @Agent(name =

    "draft_agent", description = "Expert in drafting content.") @UserMessage(""" Draft content about: {{topic}} Use the `google_search` tool to find relevant information about the requested topic. Write in a super exciting and engaging style, with lots of superlatives and hype! """) String draft(@V("topic") String topic); } public interface RefinerAgent { @Agent(name = "refiner_agent", description = "Expert in refining content.") @UserMessage(""" Deslopify this content: {{draft}} You have access to the `deslopify` skill. When the user's request relates to one of these skills, activate it first. """) String refine(@V("draft") String draft); }
  14. [DEMO] Draft & “Deslopify” var draftAgent = AgenticServices.agentBuilder(DraftAgent.class) .chatModel(modelWithSearch) .outputKey("draft")

    .build(); var refinerAgent = AgenticServices.agentBuilder(RefinerAgent.class) .chatModel(model) .toolProviders(skills.toolProvider()) .outputKey("refined") .build(); var contentWriter = AgenticServices.sequenceBuilder() .subAgents(draftAgent, refinerAgent) .outputKey("refined") .build(); var result = contentWriter.invokeWithAgenticScope(Map.of( "topic", "Latest news in AI from March and April 2026" ));
  15. Hierarchical Agent Decomposition 🧩 Problem & Context To solve complex

    edge cases, monolithic agents end up with giga prompts that don’t perform well on all tasks. 💡 Solution & Implementation Create and orchestrate a hierarchy of more specialized agents with simpler and more well defined tasks they can handle more efficiently. ⏩ Consequences 👍 Pros • Better quality results • More deterministic outcome • Pick more appropriate model 👎 Cons • Higher latency • Potentially more tokens Orchestration
  16. main_flow (sequential) poi_and_comic_flow (parallel) picture_analyzer_agent (agent) Model: Gemini 3 Flash

    points_of_interest_agent (agent) Model: Gemini 2.5 Flash Tools: Google Maps comic_illustrator_agent (agent) Model: Nano Banana Pro
  17. LlmAgent pictureAnalyzerAgent = LlmAgent.builder() .name("picture_analyzer_agent") .model("gemini-3-flash-preview") .instruction(""" Analyze the picture

    and return: - a detailed description of the content of the picture - the location where this picture was probably taken Return the result as JSON, without any commentary or Markdown code block notation, in the form: {"description": "The Eiffel tower from the Champs de Mars on a sunny day", "location": "Eiffel tower, Paris, France"} """) .outputKey(OUTPUT_KEY_DESCRIPTION_AND_LOCATION) .build(); [ DEMO ] Comic Trip Agent main_flow (sequential) poi_and_comic_flow (parallel) picture_analyzer_agent (agent) Model: Gemini 3 Flash points_of_interest_agent (agent) Model: Gemini 2.5 Flash Tools: Google Maps comic_illustrator_agent (agent) Model: Nano Banana Pro
  18. [ DEMO ] Comic Trip Agent LlmAgent poiGoogleMapsAgent = LlmAgent.builder()

    .name("points_of_interest_agent") .model("gemini-2.5-flash") .instruction(""" Given the location in: {description_and_location} Please list points of interest (POI) in the area no further than a kilometer away using the `google_maps` tool. Each POI should have a name and a description. Don't mention distances in your response. And don't start with introductory text for the list. """) .tools(new GoogleMapsTool()) .outputKey(OUTPUT_KEY_POINTS_OF_INTEREST) .build(); main_flow (sequential) poi_and_comic_flow (parallel) picture_analyzer_agent (agent) Model: Gemini 3 Flash points_of_interest_agent (agent) Model: Gemini 2.5 Flash Tools: Google Maps comic_illustrator_agent (agent) Model: Nano Banana Pro
  19. LlmAgent comicCreatorAgent = LlmAgent.builder() .name("comic_illustrator_agent") .model("gemini-3.1-flash-image-preview") .instruction(""" Turn this photography

    into a pop-art comic panel, with thick black outlines, colors drops, splashes and wide strokes or geometrical shapes. Use halftone textures for non-primary colored areas, and a vintage muted color palette. A caption should describe the location, as given in: {description_and_location} """) .generateContentConfig(GenerateContentConfig.builder() .responseModalities("IMAGE") .build()) .outputKey(OUTPUT_KEY_COMIC_ILLUSTRATION) .afterModelCallback((callbackContext, llmResponse) -> //... ).build(); [ DEMO ] Comic Trip Agent main_flow (sequential) poi_and_comic_flow (parallel) picture_analyzer_agent (agent) Model: Gemini 3 Flash points_of_interest_agent (agent) Model: Gemini 2.5 Flash Tools: Google Maps comic_illustrator_agent (agent) Model: Nano Banana Pro
  20. [ DEMO ] Comic Trip Agent ParallelAgent poiAndCommicFlow = ParallelAgent.builder()

    .name("poi_and_comic_flow") .subAgents( poiGoogleMapsAgent, comicCreatorAgent) .build(); SequentialAgent mainFlow = SequentialAgent.builder() .name("main_flow") .subAgents( pictureAnalyzerAgent, poiAndCommicFlow) .build(); main_flow (sequential) poi_and_comic_flow (parallel) picture_analyzer_agent (agent) Model: Gemini 3 Flash points_of_interest_agent (agent) Model: Gemini 2.5 Flash Tools: Google Maps comic_illustrator_agent (agent) Model: Nano Banana Pro
  21. LLM-as-Judge 🧩 Problem & Context Traditional metrics cannot evaluate complex

    agentic reasoning. Human evaluation is unscalable, slow, and expensive. 💡 Solution & Implementation Repurpose LLMs to assess & score other models' outputs and multi-step execution trajectories against predefined rubrics. ⏩ Consequences 👍 Pros • Usually align better with human prefs • Potential latency & cost optimization 👎 Cons • More complex setup • Inherit human and dataset bias • Vulnerable to prompt injection • Unstable numeric score Evaluation
  22. [DEMO] Summarization evaluation Summary with Gemini 3.1 Pro • High

    / best quality • Expensive price: $2.00 💡 Generate several summaries with cheaper & faster models, evaluate them all, pick the best one! Summary with Gemini 2.5 Flash Lite + Evaluation with Gemini 3 Flash • 3 summaries = $0.10 * 3 = $0.30 • 1 evaluation = $0.50 • Total: $0.80 Gemini Model Price per million tokens 3.1 Pro $2.00 3 Flash $0.50 3.1 Flash Lite $0.25 2.5 Pro $1.25 2.5 Flash $0.30 2.5 Flash Lite $0.10
  23. [DEMO] Summarization evaluation public static class Text implements TypedKey<String> {

    @Override public String name() { return "text"; } } public static class Summary implements TypedKey<String> { @Override public String name() { return "summary"; } } public static class Articles implements TypedKey<List<String>> { @Override public String name() { return "articles"; } } public static class Summaries implements TypedKey<List<String>> { @Override public String name() { return "summaries"; } } public static class Article implements TypedKey<String> { @Override public String name() { return "article"; } } public static class Comparison implements TypedKey<ComparisonResult> { @Override public String name() { return "comparison"; } } Sharing strongly typed objects instead of strings!
  24. [DEMO] Summarization evaluation public interface SummarizerAgent { @UserMessage(""" Provide a

    comprehensive summary of the following text: {{text}} """) @Agent(description = "Summarizes the given text", typedOutputKey = Summary.class) String summarize(@K(Text.class) String text); } public interface BatchSummarizerAgent extends AgentInstance { @Agent(description = "Generates a batch of summaries", typedOutputKey = Summaries.class) List<String> summarizeBatch(@K(Articles.class) List<String> articles); } Batching agents in parallel
  25. [DEMO] Summarization evaluation public interface ComparatorAgent { @UserMessage(""" You are

    an expert editor. Below are 3 summaries of the same article: {{summaries}} Here is the original article: {{article}} Compare the 3 summaries against the original article. Explain which summary is the most accurate and why. """) @Agent(description = "Compares the summaries against the original article", typedOutputKey = Comparison.class) ComparisonResult compareSummaries( @K(Summaries.class) List<String> summaries, @K(Article.class) String article); } Our LLM-as-Judge!!!
  26. [DEMO] Summarization evaluation GoogleAiGeminiChatModel mediumModel = GoogleAiGeminiChatModel.builder() .modelName("gemini-3-flash-preview") .apiKey(System.getenv("GEMINI_API_KEY")) .build();

    GoogleAiGeminiChatModel smallModel = GoogleAiGeminiChatModel.builder() .modelName("gemini-3.1-flash-lite-preview") .apiKey(System.getenv("GEMINI_API_KEY")) .build();
  27. [DEMO] Summarization evaluation SummarizerAgent summarizerAgent = AgenticServices.agentBuilder(SummarizerAgent.class) .chatModel(smallModel) .build(); BatchSummarizerAgent

    batchSummarizer = AgenticServices.parallelMapperBuilder(BatchSummarizerAgent.class) .subAgents(summarizerAgent) .executor(Executors.newFixedThreadPool(3)) .build(); ComparatorAgent comparatorAgent = AgenticServices.agentBuilder(ComparatorAgent.class) .chatModel(mediumModel) .build(); UntypedAgent evaluationWorkflow = AgenticServices.sequenceBuilder() .subAgents(batchSummarizer, comparatorAgent) .listener(...) .build(); summarizer (agent) Model: Gemini 3 Flash Lite summarizer (agent) Model: Gemini 3 Flash Lite summarizer (agent) Model: Flash Lite BATCH comparator (agent) Model: Flash FULL WORKFLOW
  28. [DEMO] Summarization evaluation var result = evaluationWorkflow.invokeWithAgenticScope( Map.of( "articles", List.of(articleContent,

    articleContent, articleContent), "article", articleContent ) ); List<String> summaries = result.agenticScope().readState(Summaries.class); ComparisonResult finalComparison = result.agenticScope().readState(Comparison.class);
  29. GOAP — Goal Oriented Action Planning 🧩 Problem & Context

    Hardcoded, rigid workflows are brittle and unscalable when the required steps need to change based on dynamic inputs or intermediate results. 💡 Solution & Implementation Instead of defining how to do a task, define the goal. Provide a pool of agents, each with strict preconditions and effects. The planner autonomously finds the path. ⏩ Consequences 👍 Pros • Modular: Easily add or remove agents without breaking logic. • Adaptable: Autonomously skips unnecessary steps. • Cleaner Code: Replaces tangled if/else pipelines. 👎 Cons • Small precalculation overhead Orchestration
  30. [ DEMO ] Content Specialist summarizer Input: content, report Output:

    summary content_collector Input: Output: content topic_researcher Input: Output: report infographic_artist Input: summary Output: infographic yt_shorts_creator Input: summary Output: yt_shorts_script
  31. [ DEMO ] Content Specialist summarizer Input: content, report Output:

    summary content_collector Input: Output: content topic_researcher Input: Output: report infographic_artist Input: summary Output: infographic yt_shorts_creator Input: summary Output: yt_shorts_script an infographic of the latest AI news of 2026
  32. [ DEMO ] Content Specialist summarizer Input: content, report Output:

    summary content_collector Input: Output: content topic_researcher Input: Output: report infographic_artist Input: summary Output: infographic yt_shorts_creator Input: summary Output: yt_shorts_script Let’s do a YouTube Shorts about this article
  33. [ DEMO ] Content Specialist LlmAgent contentCollector = LlmAgent.builder() .name("content_collector")

    .model("gemini-3.1-flash-lite-preview") .description("Collects content to work on") .instruction(""" Your role is to collect content to work on. Reply with the EXACT input you receive, without any additional text or changes. """) .outputKey("content") .build(); LlmAgent topicResearcher = LlmAgent.builder() .name("topic_researcher") .model("gemini-3.1-flash-lite-preview") .description("Researches a topic") .instruction(""" Your role is to research a given topic and provide a well-structured report. You'll use the `google_search` tool to find relevant information. """) .tools(new GoogleSearchTool()) .outputKey("report") .build(); summarizer Input: content, report Output: summary content_collector Input: Output: content topic_researcher Input: Output: report infographic_artist Input: summary Output: infographic yt_shorts_creator Input: summary Output: yt_shorts_script
  34. LlmAgent summarizer = LlmAgent.builder() .name("summarizer") .model("gemini-3.1-flash-lite-preview") .description("Summarizes content") .instruction(""" Your

    role is to summarize content in a concise and accurate way, without omitting any key information. If you're given a URL of an article or a YouTube video, use the `url_context` tool to fetch the content and then summarize it. If there's no URL in the prompt, summarize the given text. """) .tools(new UrlContextTool()) .outputKey("summary") .build(); [ DEMO ] Content Specialist summarizer Input: content, report Output: summary content_collector Input: Output: content topic_researcher Input: Output: report infographic_artist Input: summary Output: infographic yt_shorts_creator Input: summary Output: yt_shorts_script
  35. [ DEMO ] Content Specialist LlmAgent infographicArtist = LlmAgent.builder() .name("infographic_artist")

    .model("gemini-3.1-flash-image-preview") .description("Creates an infographic from content") .instruction(""" Your role is to create an infographic from content. """) .outputKey("infographic") .build(); LlmAgent ytShortsCreator = LlmAgent.builder() .name("yt_shorts_creator") .model("gemini-3.1-flash-lite-preview") .description("Creates a YouTube Shorts script from a summarized topic") .instruction(""" As a YouTube Shorts experienced creator, you create engaging and viral scripts. Your role is to create a YouTube Shorts script from the provided content summary. Be factual, catchy, use humor, and keep the viewer engaged. But avoid to be too sycophantic or to use clickbait & superlatives. """) .outputKey("youtube_shorts_script") .build(); summarizer Input: content, report Output: summary content_collector Input: Output: content topic_researcher Input: Output: report infographic_artist Input: summary Output: infographic yt_shorts_creator Input: summary Output: yt_shorts_script
  36. [ DEMO ] Content Specialist List<AgentMetadata> metadata = List.of( new

    AgentMetadata("content_collector", List.of(), "content"), new AgentMetadata("topic_researcher", List.of(), "report"), new AgentMetadata("summarizer", List.of("content", "report"), "summary"), new AgentMetadata("infographic_artist", List.of("summary"), "infographic"), new AgentMetadata("yt_shorts_creator", List.of("summary"), "yt_shorts_script") ); PlannerAgent agent = PlannerAgent.builder() .name("content_pipeline") .planner(new GoalOrientedPlanner("yt_shorts_script", metadata)) .subAgents(contentCollector, topicResearcher, summarizer, infographicArtist, ytShortsCreator) .build(); // Runner setup... then: Flowable<Event> eventFlowable = runner.runAsync(sessionKey, Content.fromParts(Part.fromText("https://arxiv.org/html/2603.21852v2"))); Inputs Outputs
  37. Ralph Wiggum & the Feedback Loop 🧩 Problem & Context

    LLMs are not necessarily capable of one-shotting every solution. Iteration is needed, but LLMs sometimes confidently thing they are done, but are wrong. 💡 Solution & Implementation Loops with actions and verification steps that stop only after a maximum iterations or when goal is reached. ⏩ Consequences 👍 Pros • Confidently reach the goal • More determinism in the outcome 👎 Cons • Higher latency • Higher token consumption Orchestration
  38. Coding Agent Loop 🧩 Problem & Context LLMs are not

    necessarily capable of one-shotting complex software programs. 💡 Solution & Implementation Giving an agent the right tools, and letting it loop over the task looking at outputs of actioned tools, make it converge towards the demanded implementation goal. ⏩ Consequences 👍 Pros • Make LLM more capable at coding tasks by let it reason in a loop 👎 Cons • Complexity of building a robust coding agent • Security concerns requires advanced sandboxing
  39. [ DEMO ] Coding Agent Loop public interface FileAgent {

    @Agent(name = "file_specialist", description = "Expert in file operations: read, write, edit, search.") @UserMessage("Perform this file task: {{task}}") String work(@V("task") String task); } public interface SystemAgent { @Agent(name = "system_specialist", description = "Expert in executing shell commands and managing the environment.") @UserMessage("Perform this system task: {{task}}") String work(@V("task") String task); } ... public interface NanocodeSupervisor extends AgenticScopeAccess { @Agent(name = "supervisor") String chat(@V("request") String request); }
  40. [ DEMO ] Coding Agent Loop var model = GoogleAiGeminiChatModel.builder()

    .modelName(MODEL_NAME).apiKey(GEMINI_KEY) .sendThinking(true).returnThinking(true) .build(); var fileAgent = AgenticServices.agentBuilder(FileAgent.class) .chatModel(model) .tools(new FileTools()) .build(); var systemAgent = AgenticServices.agentBuilder(SystemAgent.class) .chatModel(model) .tools(new SystemTools()) .build(); ... SupervisorAgent supervisor = AgenticServices.supervisorBuilder() .chatModel(model) .subAgents(fileAgent, systemAgent, webAgent) .responseStrategy(SupervisorResponseStrategy.SUMMARY) .build();
  41. [ DEMO ] Coding Agent Loop while (true) { try

    { var input = IO.readln(BOLD + BLUE + "❯ " + RESET + YELLOW); if (input == null) break; input = input.strip(); if (input.isEmpty()) continue; if (input.equals("/q") || input.equals("exit")) break; var response = supervisor.invoke(input); if (response != null && !response.isBlank()) { IO.println("\n" + CYAN + "⏺" + RESET + " " + markdown(response)); } } catch (Exception e) { IO.println(RED + "⏺ Error: " + e.getMessage() + RESET); } }
  42. Hard to get agents to reach their goal. Applying good

    design patterns help! And better models with context and harness engineering too. Summary Planning Context Engineering Tool Management Evaluation Orchestration Security & Observability Harness Engineering