<?xml version="1.0" encoding="utf-8"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><language>en</language><title>Blog posts by Andre Gabriel Coetzee</title> <link>https://world.optimizely.com/blogs/andre-gabriel-coetzee/</link><description></description><ttl>60</ttl><generator>Optimizely World</generator><item> <title>Optimizely Opal: How to Build Effective Workflow Agents</title>            <link>https://world.optimizely.com/blogs/andre-gabriel-coetzee/dates/2026/5/my-optimizely-opal-workflow-agent-experiment-what-worked-what-didnt/</link>            <description>&lt;div&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;If you&#39;re building workflow agents in Optimizely Opal, this post covers how specialized agents pass context to each other, why keeping agents small and focused matters, and a useful trick for handling situations where you&#39;re not sure how to proceed.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;I recently decided to put Optimizely Opal through its paces. The experiment: build a simple mock API, feed external data into Opal, and see if a workflow agent could take it from there and autonomously publish content to the CMS, with no human intervention required.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;The workflow agent I built pulls data from an external mock API on a schedule, analyzes it, checks it against existing content in the CMS, makes a judgement call on whether a new article is warranted, and if it is, writes and publishes it to the CMS, all without any human intervention.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;It is made up of 5 specialized agents, each with a single responsibility, and a condition that branches the workflow based on whether a new article is warranted.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;My example consists of the following:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;An agent that retrieves route information from the mock AviationStack API&lt;/li&gt;
&lt;li&gt;An agent that compares the route info with articles published in the CMS, and makes a judgement call on whether the info from the API call warrants a new article in the CMS&lt;/li&gt;
&lt;li&gt;An agent that writes an article based on the info received from the external mock AviationStack API&lt;/li&gt;
&lt;li&gt;An agent that publishes the new article to the CMS&lt;/li&gt;
&lt;li&gt;An agent that gracefully ends the workflow if no new article is needed&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;br /&gt;&lt;img src=&quot;https://codaio.imgix.net/docs/-XvL0D-nmX/blobs/bl-etsUzv2Dz7/3408a8416b38371b4a0a1011e247e5b4e5e77bb263977247b0959774ff8866ac828fd6d9e6d45d4a68071da6794cd9b31e81b41c9d373cd0f98c28b0fa85bc306cb1f0ba3f63e13021abea570172c64fdace84bb6a029614852e4ee55aec1581d21f86d7?fit=max&amp;amp;fm=webp&amp;amp;lossless=true&quot; alt=&quot;image.png&quot; /&gt;&lt;br /&gt;&lt;em&gt;&lt;br /&gt;The full workflow agent. A scheduler triggers the process, data flows through each specialized agent in sequence, and a condition branches the workflow: TRUE continues to writing and publishing, FALSE ends the workflow cleanly&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;If you want to build a similar workflow agent yourself and apply some of the tips here in practice, here&amp;rsquo;s what you need:&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style=&quot;font-size: 14pt;&quot;&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;Optimizely Opal&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;font-size: 14pt;&quot;&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;Access to Optimizely CMS&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;font-size: 14pt;&quot;&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;A mock API with a Custom Tool configured in Opal&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;font-size: 14pt;&quot;&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;Basic familiarity with creating specialized agents in Opal&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-size: 36pt;&quot;&gt;Here&#39;s what I learned&lt;/span&gt;&lt;span style=&quot;font-size: 36pt; color: rgb(149, 165, 166);&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/h1&gt;
&lt;hr /&gt;
&lt;h1 style=&quot;text-align: left;&quot;&gt;There&amp;rsquo;s no prompting between specialized agents&lt;/h1&gt;
&lt;div&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;Each agent passes its output directly to the next and runs completely on its own, with no human in the loop. This keeps the workflow consistent and predictable, but it also means you need to be deliberate about what each agent outputs, because the next agent depends on it entirely.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;&lt;br /&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;Agent 1 output:&lt;/span&gt;&lt;strong&gt;&lt;br /&gt;&lt;/strong&gt;&lt;br /&gt;&lt;img src=&quot;https://codaio.imgix.net/docs/-XvL0D-nmX/blobs/bl-x86SnAX_nl/04321c3dacc79ff08a0e50ab4ef5d1395bce005a2833c489d7a4b07620421b8a25558afd065d4f84eb5a8f142735d8c7a16f055f22ffdbba483ce02c24b572933eea4059a10bcbd7fc2bacf09d32a9b319a21a261d2e93613658e2309e64058eb5ad7f3d?fit=max&amp;amp;fm=webp&amp;amp;lossless=true&quot; alt=&quot;image.png&quot; /&gt;&lt;/div&gt;
&lt;div&gt;&lt;em&gt;&lt;br /&gt;The first specialized agent outputs a simple JSON object containing the route information retrieved from the mock AviationStack API&lt;/em&gt;&lt;/div&gt;
&lt;p&gt;&lt;br /&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;Agent 2 Input:&lt;/span&gt;&lt;strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;https://codaio.imgix.net/docs/-XvL0D-nmX/blobs/bl-E_3xcxTabY/912d743ab0f4926d01b83641f9962891a9e74717061f1b1627c8f4f13f56130f0d16de0c633d3ad468347b2e40fea7cc98acf787fd96287fe03251229eaec0c03447a6715e5b27610f0a668a3b0460a039269b4d430926f2532d7e4412ac13e3519eb0eb?fit=max&amp;amp;fm=webp&amp;amp;lossless=true&quot; alt=&quot;image.png&quot; /&gt;&lt;br /&gt;&lt;/strong&gt;&lt;br /&gt;&lt;img src=&quot;https://codaio.imgix.net/docs/-XvL0D-nmX/blobs/bl-QB8U70NVT3/7b84ae2291e4ee54ee6e79af84649770c8f4e0ad6abf05b347e3298ddf6f1c04513c49a97a83df7bf2fd0511b0752f875c57abdafbc7c009a2d1f7342a09b7e88afe36eac29b803c33d28cf09c3b1f31fa9c0de26b2024b9a27bcf879ca1971af303a6cb?fit=max&amp;amp;fm=webp&amp;amp;lossless=true&quot; alt=&quot;image.png&quot; /&gt;&lt;/p&gt;
&lt;div&gt;&lt;em&gt;That same &quot;routes&quot; object becomes the input for the next agent, defined as a required variable and referenced directly in the prompt template. This is what &quot;output becomes input&quot; looks like in practice.&lt;/em&gt;&lt;/div&gt;
&lt;hr /&gt;
&lt;h1&gt;Create as many specialized agents as you need&lt;/h1&gt;
&lt;div&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;Don&#39;t hesitate to create as many agents as your workflow needs, and be as granular as necessary. Each agent should do one thing well, not many things adequately.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;Developers will recognize this as the Single Responsibility Principle (SRP), and it applies just as well here. For those less familiar with the term, the idea is simple: don&#39;t create one agent that retrieves external data, analyzes it, writes an article, and publishes it. That&#39;s too much for one agent to handle reliably, and you&#39;ll end up with bloated instructions. Split those responsibilities across separate agents instead, and let them pass context to each other.&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;&lt;img src=&quot;https://codaio.imgix.net/docs/-XvL0D-nmX/blobs/bl-sMM-Tmyes9/61ea231a49ab6ce082d59c3280cebe49ccf630168f5711d6ac95fd1bf44ae642972dce83161d529b5fba3bffcc92e05f253c88f6455a1b8956db6c2f0a7e6515195ad3317906a8ac44f162bd09dad40ac87b8d156b245522a56d496a965b52e663bfb3e9?fit=max&amp;amp;fm=webp&amp;amp;lossless=true&quot; alt=&quot;image.png&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;&lt;em&gt;A single agent handling too many responsibilities. This is harder to debug, harder to maintain, and more likely to produce inconsistent results.&lt;/em&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h1&gt;&lt;img src=&quot;https://codaio.imgix.net/docs/-XvL0D-nmX/blobs/bl-gjt814Dg6l/3b1f8e8e38428374d003026b0770899e52979739d84faf1f7befc8627b0c86aafee258fd6aa56c0aed5fd7fb1e290551d996993d788d68c2960f742fce7bc24298bbda5ddd838b27ca48309889ac291c38cf1085afa53fd8139594e9415051527e0eadfa?fit=max&amp;amp;fm=webp&amp;amp;lossless=true&quot; alt=&quot;image.png&quot; /&gt;&lt;/h1&gt;
&lt;div&gt;&lt;em&gt;The same workflow split across three focused agents, each with a single responsibility, and each passing its output to the next.&lt;/em&gt;&lt;/div&gt;
&lt;hr /&gt;
&lt;h1&gt;If you run into a gray area and you&#39;re not sure how to handle it, create a specialized agent&lt;/h1&gt;
&lt;div&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;This is simpler than it sounds. When you hit a question like &quot;How do I get it to write an article?&quot;, you create a specialized agent for it. &quot;How do I get it to publish to the CMS?&quot;, you also create a specialized agent for it. Most problems in a workflow agent have the same answer: a focused, purpose-built specialized agent.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;A good example from my own workflow: when the condition evaluates to false, meaning the external data doesn&#39;t warrant a new article, I needed the workflow to simply stop cleanly. There&#39;s no built-in &quot;do nothing&quot; option, but the answer was straightforward: create an &quot;End Workflow&quot; agent whose only job is to acknowledge the decision and exit gracefully.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src=&quot;https://codaio.imgix.net/docs/-XvL0D-nmX/blobs/bl-2tv3E7USqI/7715b92663891aeb140f10a67422095138d453cdaa125065284a4f1ba266274aa0a691cb8a78ffa01e8fa74f202faea083499b389d56a38ad325e7d628c2d3fc61dcd1254f253d452d8fb0f0d48db3d644f2eadc9b3eff5646f43bf733d3203320c4ecbe?fit=max&amp;amp;fm=webp&amp;amp;lossless=true&quot; alt=&quot;image.png&quot; /&gt;&lt;/div&gt;
&lt;div&gt;&lt;em&gt;&lt;br /&gt;The &quot;End Workflow&quot; agent keeps it simple by design. No tools, no content creation, no external calls. Its only job is to end the workflow cleanly when no action is needed, which is exactly the kind of focused single-responsibility agent your workflow will thank you for later.&lt;/em&gt;&lt;/div&gt;
&lt;hr /&gt;
&lt;h1&gt;Don&#39;t assume an agent has context from earlier in the workflow unless you explicitly pass it&lt;/h1&gt;
&lt;div&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;This is an important one. Just because multiple specialized agents live inside the same workflow agent doesn&#39;t mean they automatically share context. If Agent 4 needs data that Agent 1 retrieved, you need to explicitly pass it through each agent&#39;s inputs and outputs along the way, it won&#39;t be there by default.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;In my workflow, by the time the article-writing agent needs to do its job, it has no automatic awareness of the route data retrieved at the start. If I want it to have that context, I need to explicitly include it in the outputs of the agents before it, and specify it as an input to the agents that follow.&lt;/span&gt;&lt;/div&gt;
&lt;h1&gt;&lt;img src=&quot;https://codaio.imgix.net/docs/-XvL0D-nmX/blobs/bl-arF8aKa8Nu/28333b43056d181ee30677a8a0c36cbc391b8cdd2353caa86ebbcdc85376aab333272045c1b19ab42d79436a11f4d4b7e0a2daf625a1b2fd25f3c49b22df4af72eee1c908366c15327e2f333003cd3800e1aabad69f9f8dbc01cb8fa3eb4c1ab1722a169?fit=max&amp;amp;fm=webp&amp;amp;lossless=true&quot; alt=&quot;image.png&quot; /&gt;&lt;/h1&gt;
&lt;div&gt;&lt;em&gt;The output schema of my &quot;Gap Analyzer&quot; agent explicitly carries the &quot;routes&quot; object forward, alongside its own &quot;reason&quot; and &quot;publishDecision&quot; fields. Without &quot;routes&quot; being defined here as an output, the article-writing agent downstream would have no access to the original API data, even though it was retrieved earlier in the same workflow.&lt;br /&gt;&lt;/em&gt;&lt;hr /&gt;&lt;span style=&quot;font-size: 14pt;&quot;&gt;That&#39;s about it for now! Hope this helps someone else out. If you&#39;ve built something similar or have any questions or suggestions, I&#39;d love to hear about it in the comments. Thanks for reading!&lt;/span&gt;&lt;/div&gt;
&lt;h1&gt;&lt;br /&gt;&lt;br /&gt;&lt;/h1&gt;</description>            <guid>https://world.optimizely.com/blogs/andre-gabriel-coetzee/dates/2026/5/my-optimizely-opal-workflow-agent-experiment-what-worked-what-didnt/</guid>            <pubDate>Wed, 20 May 2026 09:04:10 GMT</pubDate>           <category>Blog post</category></item></channel>
</rss>