<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Thomy Gölles</title><link>https://thomygoelles.com/</link><description>This blog is where I share my ideas, thoughts and findings. Thomy Gölles.</description><language>en</language><lastBuildDate>Sun, 25 Oct 2026 08:00:00 +0000</lastBuildDate><atom:link href="https://thomygoelles.com/" rel="self" type="application/rss+xml"/><item><title>CollabDays Belgium 2026 -- Our Two Sessions on Agents</title><link>https://thomygoelles.com/collabdays-belgium-2026/</link><pubDate>Sun, 25 Oct 2026 08:00:00 +0000</pubDate><guid>https://thomygoelles.com/collabdays-belgium-2026/</guid><description>Stephan and I were back at CollabDays Belgium for the seventh time, with two sessions on Claude vs. Copilot and on Copilot Cowork vs. Microsoft Autopilot.</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p class="otherlang" style="font-size:.9em;opacity:.8;margin:0 0 1.5rem"><em>This post is also available in German: <a href="https://thomygoelles.com/deutsch/collabdays-belgien-2026/" hreflang="de">CollabDays Belgium 2026 -- unsere zwei Sessions über Agents</a></em></p>
<p>Yesterday <a href="https://bisser.io/" target="_blank" rel="noopener">Stephan Bisser</a> and I were back at <a href="https://belgium.collabdays.org/" target="_blank" rel="noopener">CollabDays Belgium</a>, our seventh time on that stage if you count the Benelux editions, and this year we brought two sessions instead of one. We wrapped up the second a few minutes early because we had a flight to catch, which sounds very rock and roll until you picture the two of us an hour later in the duty-free shop, comparing chocolate boxes for the kids back home. Nothing gets two MVPs back down to earth faster, and that&rsquo;s exactly how it should be.</p>
<p>If you&rsquo;ve never been, CollabDays Belgium is what a community event should look like. Around 350 people came to De Montil on a Saturday, for free, to sit through about 30 sessions in five parallel tracks covering Microsoft 365, AI and Copilot, Power Platform, Azure, security and governance. None of that happens by itself. The day is run by SmartBytes, where the Belgian Microsoft 365 and Power Platform communities have joined forces, a lot of people give up their weekend so the rest of us can talk shop, and the sponsors are the reason nobody has to buy a ticket. Thank you, all of you. We keep coming back, and it&rsquo;s because of the people who are still around at the SharePint, not because of our slides.</p>
<p>Every year in Belgium we&rsquo;ve told one story about AI. In 2023 it was the state of the nation for AI in Microsoft 365, in 2024 building your own custom Copilot, and last year we demystified Copilot agents, extensions and plugins. This year the story split in two.</p>
<h2 id="claude-vs-copilot-turned-out-to-be-the-wrong-fight">Claude vs. Copilot turned out to be the wrong fight</h2>
<p>The morning session, &ldquo;Claude vs. Copilot: From Office Worker to Dev&rdquo;, opened with the cheapest trick in the book, because the easiest way to fill a room at a Microsoft conference is to put a non-Microsoft product in the title. Behind the clickbait we walked through three pairs, Microsoft 365 Copilot against Claude for everyday assisting and creating, Copilot Cowork against Claude Cowork for delegating real work, and GitHub Copilot against Claude Code for engineering. The plot twist sits right in the middle, because Microsoft itself announced in March that Copilot Cowork was built by integrating the technology that powers Anthropic&rsquo;s Claude Cowork, so the room was never really choosing between two models.</p>
<p><img src="https://thomygoelles.com/img/2026/10/collabdays-be-2026-harness_hu_d06cd49a6fb0c6be.webp" srcset="https://thomygoelles.com/img/2026/10/collabdays-be-2026-harness_hu_8bff4a727c07f3ea.webp 480w, https://thomygoelles.com/img/2026/10/collabdays-be-2026-harness_hu_d06cd49a6fb0c6be.webp 800w, https://thomygoelles.com/img/2026/10/collabdays-be-2026-harness.webp 1600w" sizes="(max-width: 900px) 100vw, 860px" alt="Slide &ldquo;You think you talk to the model. You talk to a harness.&rdquo; A user sends prompts, files and intent into a harness made of instructions, memory and history, your data, tools, skills and plugins, helper agents, permissions and approvals and sandbox, audit and cost. Only the assembled context reaches the stateless LLM." width="1600" height="900" loading="lazy" decoding="async"></p>
<p>What they&rsquo;re actually choosing is a harness. My granddad was a climber, and I still have his gear at home from forty years ago. The harness never climbed a single metre, it kept him safe and pointed in the right direction while he did the climbing, and that&rsquo;s what Copilot, Claude Code or your own app does for a model. Every LLM is a goldfish, stateless by design, and the harness decides which instructions, which data, which tools and which permissions land in the context window. For Copilot that&rsquo;s Microsoft 365, with Work IQ pulling in your mail, chats and files, while Claude brings its own harness and reaches your data through connectors. The models themselves go off like milk, with a new frontier model roughly every 18 days, so we told people to pin the task and not the model.</p>
<p><img src="https://thomygoelles.com/img/2026/10/collabdays-be-2026-goldfish_hu_d530ce0d6b219df1.webp" srcset="https://thomygoelles.com/img/2026/10/collabdays-be-2026-goldfish_hu_3eb35f8d755f1e2b.webp 480w, https://thomygoelles.com/img/2026/10/collabdays-be-2026-goldfish_hu_d530ce0d6b219df1.webp 800w, https://thomygoelles.com/img/2026/10/collabdays-be-2026-goldfish.webp 1600w" sizes="(max-width: 900px) 100vw, 860px" alt="Slide &ldquo;Every LLM is a goldfish. Brilliant for one call. Then it forgets everything.&rdquo; over a photo of an orange goldfish on a dark background." width="1600" height="900" loading="lazy" decoding="async">
<em>Goldfish photo in the slide by James St. John, <a href="https://creativecommons.org/licenses/by/2.0/" target="_blank" rel="noopener">CC BY 2.0</a>, via Wikimedia Commons.</em></p>
<p>Anna was back, too. She&rsquo;s the project manager from our session last year, when the question was who in her team should get a Copilot licence, and this year she wanted to know which tool to bet on now that half her team uses Copilot and the other half secretly uses Claude. Neither of us had thought about her in a year. Two days before the event I&rsquo;d built a skill over our roughly 160 sessions from the last ten years and asked it for a deck for Belgium, and it came back with Anna. Our answer to her was not to bet on one tool but to choose per workflow. Summarising today&rsquo;s mail and meetings is a Copilot job, research across the web and non-Microsoft tools is a Claude job, and a regulated rollout in the EU is neither by default until someone has done the data protection homework. That homework is the Belgian question, too. Anthropic models inside Copilot sit outside the EU Data Boundary and are switched off by default in the EU, EFTA and the UK, so &ldquo;Copilot Cowork is the safe EU option&rdquo; is only true after an admin opts in and documents the transfer.</p>
<p>Most of the hour was about how we work today rather than about slides, which is fitting, because we&rsquo;d arrived the day before with literally zero of them. Stephan had Claude Code turn the abstract and his ideas into a 500-line storyline in Markdown, handed that to Copilot Cowork, which already knows our company design, and let it build the deck plus a one-page cheat sheet so I could catch up in the few hours I had. Then I asked the front row for a favourite colour, said a few sentences in German into my phone, and Bob, the builder agent from the fleet I <a href="https://thomygoelles.com/nine-agents-one-human/">wrote about yesterday</a>, went off to build a speaker memory game as an SPFx web part in blue and deploy it to SharePoint while we kept talking. It delivered, with the speakers of the day as the cards, Stephan and me included. The voice pipeline behind that costs a few cents for Service Bus and the Azure Function plus around four euros a month for transcription. And because in our projects agents rarely stall at the model but almost always at the integration stack, connectors, consent and an owner after the pilot, the deck ends with a Monday checklist for admins and for everyone who isn&rsquo;t one.</p>
<p><img src="https://thomygoelles.com/img/2026/10/collabdays-be-2026-speaker-memory_hu_2620871423f09843.webp" srcset="https://thomygoelles.com/img/2026/10/collabdays-be-2026-speaker-memory_hu_1fdb651c2e5ac81d.webp 480w, https://thomygoelles.com/img/2026/10/collabdays-be-2026-speaker-memory_hu_2620871423f09843.webp 800w, https://thomygoelles.com/img/2026/10/collabdays-be-2026-speaker-memory.webp 1600w" sizes="(max-width: 900px) 100vw, 860px" alt="Screenshot of the SharePoint site &ldquo;CollabDays Belgium Speaker Memory&rdquo; with a blue SPFx web part called Speaker Memory. A grid of cards, some turned over to show CollabDays speakers with their names, the rest showing the CollabDays logo. The header shows 31 moves, 1:23 minutes and 6 of 18 pairs." width="1600" height="861" loading="lazy" decoding="async"></p>
<h2 id="two-agents-from-one-vendor-and-two-very-different-bets">Two agents from one vendor, and two very different bets</h2>
<p>The afternoon session had the slot right after lunch, the one speakers call the grave digger session, and its title had already aged by the time we got on stage. When we wrote the abstract in summer it said &ldquo;Copilot Cowork vs. Microsoft Scout&rdquo;. Since then Scout has moved from the desktop into the cloud and has been renamed Microsoft Autopilot (not to be confused with Windows Autopilot, which still provisions your devices).</p>
<p><img src="https://thomygoelles.com/img/2026/10/collabdays-be-2026-borrowed-engines_hu_7f0e9f15a781035e.webp" srcset="https://thomygoelles.com/img/2026/10/collabdays-be-2026-borrowed-engines_hu_2856a58cb820a178.webp 480w, https://thomygoelles.com/img/2026/10/collabdays-be-2026-borrowed-engines_hu_7f0e9f15a781035e.webp 800w, https://thomygoelles.com/img/2026/10/collabdays-be-2026-borrowed-engines.webp 1600w" sizes="(max-width: 900px) 100vw, 860px" alt="Slide &ldquo;One vendor, two borrowed engines&rdquo;. Copilot Cowork is based on Anthropic&rsquo;s Claude Cowork, Microsoft Autopilot on OpenClaw. Below both sits Microsoft&rsquo;s enterprise wrapper of Entra for identity, Purview for DLP and labels, Agent 365 for permissions and audit, Work IQ for context and Copilot Credits for billing. The engines are borrowed, the wrapper is the product." width="1600" height="900" loading="lazy" decoding="async"></p>
<p>Both agents run on borrowed engines. Cowork is Microsoft&rsquo;s answer to Claude Cowork, and Autopilot is its answer to OpenClaw, a hardened version with proper governance around it, which made me smile after my <a href="https://thomygoelles.com/openclaw-copilot-personal-vs-enterprise/">weekend with OpenClaw</a> earlier this year. What Microsoft adds is the enterprise wrapper around them, identity, labels, audit, context and billing, and that wrapper is the actual product.</p>
<p>The difference between the two is less about features and more about whose name is on the work and whether the work ever ends. Copilot Cowork, generally available since 16 June, works as you, with your permissions, and delegates a task that has a result. Autopilot gets its own Entra identity, a manager and a memory that lasts for days, so you don&rsquo;t hand it a task, you staff a role. We couldn&rsquo;t get into the Autopilot preview in time, so Stephan hired his own autopilot from a Foundry blueprint, and Scouty, as he calls it, introduced itself live to the room. Before that it had lived as Scout automations on his laptop, which burned through 600 euros of GitHub AI credits in two weeks and only worked while the laptop was awake. That&rsquo;s a decent argument for why an always-on colleague needs a computer that never sleeps.</p>
<p>I bet on Cowork, Stephan bets on Autopilot, and we&rsquo;ll see who&rsquo;s right. What we agreed on is the guardrail. Anything Scouty sends to another person needs Stephan&rsquo;s approval and ends with a byline saying an agent wrote it, and our agents can draft mails but can&rsquo;t send them, because the app registration simply doesn&rsquo;t have the permission.</p>
<p>The part I enjoyed most came near the end. The storyline for that session was drafted with an AI assistant, and every slide and every image in the deck was built by Copilot Cowork from that storyline, revised four times just by chatting with it in under two days and reviewed by us. The agents did the work and our names were on it, which was exactly the point of the session. Our advice for the next weeks is to choose by the work and not by the product, and to watch Ignite from 17 to 20 November, because the names will probably change again.</p>
<h2 id="the-slides">The slides</h2>
<p>Both decks are here to browse or download.</p>
<figure class="slides" style="margin:2rem 0" data-slides-dir="https://thomyblogfiles.blob.core.windows.net/slides/2026/collabdays-be-2026-claude-vs-copilot-bisser-goelles" data-slides-pages="41" data-slides-title="Claude vs. Copilot: From Office Worker to Dev" data-slides-pdf="https://thomyblogfiles.blob.core.windows.net/slides/2026/collabdays-be-2026-claude-vs-copilot-bisser-goelles.pdf" data-slides-de="false">
  <a href="https://thomyblogfiles.blob.core.windows.net/slides/2026/collabdays-be-2026-claude-vs-copilot-bisser-goelles.pdf" target="_blank" rel="noopener" class="slides-open" aria-label="Claude vs. Copilot: From Office Worker to Dev, view slides"><img src="https://thomygoelles.com/img/2026/10/collabdays-be-2026-claude-vs-copilot-bisser-goelles-slides.webp" alt="Claude vs. Copilot: From Office Worker to Dev, first slide" loading="lazy" style="width:100%;height:auto;border:1px solid #ddd;border-radius:4px;cursor:zoom-in"></a>
  <figcaption style="margin-top:.6rem;display:flex;flex-wrap:wrap;gap:.8rem;align-items:center">
    <strong>Claude vs. Copilot: From Office Worker to Dev</strong>
    <a href="https://thomyblogfiles.blob.core.windows.net/slides/2026/collabdays-be-2026-claude-vs-copilot-bisser-goelles.pdf" target="_blank" rel="noopener" class="slides-open">View slides (41)</a>
    <a href="https://thomyblogfiles.blob.core.windows.net/slides/2026/collabdays-be-2026-claude-vs-copilot-bisser-goelles.pdf" target="_blank" rel="noopener" download>Download PDF (8.7 MB)</a>
  </figcaption>
</figure>
<script>
(function () {
  if (window.__slidesViewer) return;
  window.__slidesViewer = true;
  function pad(n) { return (n < 10 ? '0' : '') + n; }
  function open(fig, start) {
    var dir = fig.dataset.slidesDir, total = parseInt(fig.dataset.slidesPages, 10), de = fig.dataset.slidesDe === 'true';
    var i = start || 1, last = document.activeElement, x0 = null;
    var o = document.createElement('div');
    o.setAttribute('role', 'dialog'); o.setAttribute('aria-modal', 'true'); o.setAttribute('aria-label', fig.dataset.slidesTitle);
    o.style.cssText = 'position:fixed;inset:0;z-index:99999;background:rgba(10,14,20,.98);display:flex;flex-direction:column;color:#fff;font:15px/1.4 system-ui,-apple-system,"Segoe UI",sans-serif';
    o.innerHTML =
      '<div style="display:flex;align-items:center;gap:1rem;padding:.7rem 1rem">' +
        '<strong style="flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis"></strong>' +
        '<span class="sv-count" style="opacity:.8"></span>' +
        '<a class="sv-pdf" style="color:#7fd7ea" target="_blank" rel="noopener" download></a>' +
        '<button class="sv-close" type="button" style="background:none;border:0;color:#fff;font-size:28px;line-height:1;cursor:pointer;padding:0 .3rem">&times;</button>' +
      '</div>' +
      '<div class="sv-stage" style="flex:1;position:relative;display:flex;align-items:center;justify-content:center;min-height:0;padding:0 3.2rem 1rem">' +
        '<button class="sv-prev" type="button" style="position:absolute;left:.4rem;top:50%;transform:translateY(-50%);background:rgba(255,255,255,.12);border:0;color:#fff;font-size:30px;width:2.6rem;height:4rem;border-radius:6px;cursor:pointer">&#8249;</button>' +
        '<img class="sv-img" alt="" style="max-width:100%;max-height:100%;object-fit:contain;box-shadow:0 4px 30px rgba(0,0,0,.5);background:#fff;user-select:none">' +
        '<button class="sv-next" type="button" style="position:absolute;right:.4rem;top:50%;transform:translateY(-50%);background:rgba(255,255,255,.12);border:0;color:#fff;font-size:30px;width:2.6rem;height:4rem;border-radius:6px;cursor:pointer">&#8250;</button>' +
      '</div>';
    o.querySelector('strong').textContent = fig.dataset.slidesTitle;
    var pdf = o.querySelector('.sv-pdf'); pdf.href = fig.dataset.slidesPdf; pdf.textContent = 'PDF';
    var close = o.querySelector('.sv-close'); close.setAttribute('aria-label', de ? 'Schließen' : 'Close');
    o.querySelector('.sv-prev').setAttribute('aria-label', de ? 'Vorherige Folie' : 'Previous slide');
    o.querySelector('.sv-next').setAttribute('aria-label', de ? 'Nächste Folie' : 'Next slide');
    var img = o.querySelector('.sv-img'), count = o.querySelector('.sv-count');
    function src(n) { return dir + '/p-' + pad(n) + '.webp'; }
    function show(n) {
      i = Math.min(Math.max(n, 1), total);
      img.src = src(i);
      img.alt = (de ? 'Folie ' : 'Slide ') + i + (de ? ' von ' : ' of ') + total;
      count.textContent = i + ' / ' + total;
      [i + 1, i - 1].forEach(function (k) { if (k >= 1 && k <= total) { var p = new Image(); p.src = src(k); } });
    }
    function done() { document.removeEventListener('keydown', key); o.remove(); document.documentElement.style.overflow = ''; if (last) last.focus(); }
    function key(e) {
      if (e.key === 'Escape') { done(); }
      else if (e.key === 'ArrowRight' || e.key === 'PageDown' || e.key === ' ') { e.preventDefault(); show(i + 1); }
      else if (e.key === 'ArrowLeft' || e.key === 'PageUp') { e.preventDefault(); show(i - 1); }
      else if (e.key === 'Home') { show(1); } else if (e.key === 'End') { show(total); }
    }
    close.onclick = done;
    o.querySelector('.sv-prev').onclick = function () { show(i - 1); };
    o.querySelector('.sv-next').onclick = function () { show(i + 1); };
    img.onclick = function (e) { var r = img.getBoundingClientRect(); show(e.clientX - r.left < r.width / 2 ? i - 1 : i + 1); };
    o.addEventListener('touchstart', function (e) { x0 = e.touches[0].clientX; }, { passive: true });
    o.addEventListener('touchend', function (e) { if (x0 === null) return; var dx = e.changedTouches[0].clientX - x0; if (Math.abs(dx) > 40) show(dx < 0 ? i + 1 : i - 1); x0 = null; });
    document.addEventListener('keydown', key);
    document.documentElement.style.overflow = 'hidden';
    document.body.appendChild(o);
    show(i); close.focus();
  }
  document.addEventListener('click', function (e) {
    var a = e.target.closest && e.target.closest('.slides-open');
    if (!a) return;
    var fig = a.closest('figure.slides');
    if (!fig || !fig.dataset.slidesPages) return;
    e.preventDefault(); open(fig, 1);
  });
})();
</script>

<figure class="slides" style="margin:2rem 0" data-slides-dir="https://thomyblogfiles.blob.core.windows.net/slides/2026/collabdays-be-2026-copilot-cowork-vs-autopilot-bisser-goelles" data-slides-pages="44" data-slides-title="Copilot Cowork vs. Microsoft Autopilot" data-slides-pdf="https://thomyblogfiles.blob.core.windows.net/slides/2026/collabdays-be-2026-copilot-cowork-vs-autopilot-bisser-goelles.pdf" data-slides-de="false">
  <a href="https://thomyblogfiles.blob.core.windows.net/slides/2026/collabdays-be-2026-copilot-cowork-vs-autopilot-bisser-goelles.pdf" target="_blank" rel="noopener" class="slides-open" aria-label="Copilot Cowork vs. Microsoft Autopilot, view slides"><img src="https://thomygoelles.com/img/2026/10/collabdays-be-2026-copilot-cowork-vs-autopilot-bisser-goelles-slides.webp" alt="Copilot Cowork vs. Microsoft Autopilot, first slide" loading="lazy" style="width:100%;height:auto;border:1px solid #ddd;border-radius:4px;cursor:zoom-in"></a>
  <figcaption style="margin-top:.6rem;display:flex;flex-wrap:wrap;gap:.8rem;align-items:center">
    <strong>Copilot Cowork vs. Microsoft Autopilot</strong>
    <a href="https://thomyblogfiles.blob.core.windows.net/slides/2026/collabdays-be-2026-copilot-cowork-vs-autopilot-bisser-goelles.pdf" target="_blank" rel="noopener" class="slides-open">View slides (44)</a>
    <a href="https://thomyblogfiles.blob.core.windows.net/slides/2026/collabdays-be-2026-copilot-cowork-vs-autopilot-bisser-goelles.pdf" target="_blank" rel="noopener" download>Download PDF (8.9 MB)</a>
  </figcaption>
</figure>
<script>
(function () {
  if (window.__slidesViewer) return;
  window.__slidesViewer = true;
  function pad(n) { return (n < 10 ? '0' : '') + n; }
  function open(fig, start) {
    var dir = fig.dataset.slidesDir, total = parseInt(fig.dataset.slidesPages, 10), de = fig.dataset.slidesDe === 'true';
    var i = start || 1, last = document.activeElement, x0 = null;
    var o = document.createElement('div');
    o.setAttribute('role', 'dialog'); o.setAttribute('aria-modal', 'true'); o.setAttribute('aria-label', fig.dataset.slidesTitle);
    o.style.cssText = 'position:fixed;inset:0;z-index:99999;background:rgba(10,14,20,.98);display:flex;flex-direction:column;color:#fff;font:15px/1.4 system-ui,-apple-system,"Segoe UI",sans-serif';
    o.innerHTML =
      '<div style="display:flex;align-items:center;gap:1rem;padding:.7rem 1rem">' +
        '<strong style="flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis"></strong>' +
        '<span class="sv-count" style="opacity:.8"></span>' +
        '<a class="sv-pdf" style="color:#7fd7ea" target="_blank" rel="noopener" download></a>' +
        '<button class="sv-close" type="button" style="background:none;border:0;color:#fff;font-size:28px;line-height:1;cursor:pointer;padding:0 .3rem">&times;</button>' +
      '</div>' +
      '<div class="sv-stage" style="flex:1;position:relative;display:flex;align-items:center;justify-content:center;min-height:0;padding:0 3.2rem 1rem">' +
        '<button class="sv-prev" type="button" style="position:absolute;left:.4rem;top:50%;transform:translateY(-50%);background:rgba(255,255,255,.12);border:0;color:#fff;font-size:30px;width:2.6rem;height:4rem;border-radius:6px;cursor:pointer">&#8249;</button>' +
        '<img class="sv-img" alt="" style="max-width:100%;max-height:100%;object-fit:contain;box-shadow:0 4px 30px rgba(0,0,0,.5);background:#fff;user-select:none">' +
        '<button class="sv-next" type="button" style="position:absolute;right:.4rem;top:50%;transform:translateY(-50%);background:rgba(255,255,255,.12);border:0;color:#fff;font-size:30px;width:2.6rem;height:4rem;border-radius:6px;cursor:pointer">&#8250;</button>' +
      '</div>';
    o.querySelector('strong').textContent = fig.dataset.slidesTitle;
    var pdf = o.querySelector('.sv-pdf'); pdf.href = fig.dataset.slidesPdf; pdf.textContent = 'PDF';
    var close = o.querySelector('.sv-close'); close.setAttribute('aria-label', de ? 'Schließen' : 'Close');
    o.querySelector('.sv-prev').setAttribute('aria-label', de ? 'Vorherige Folie' : 'Previous slide');
    o.querySelector('.sv-next').setAttribute('aria-label', de ? 'Nächste Folie' : 'Next slide');
    var img = o.querySelector('.sv-img'), count = o.querySelector('.sv-count');
    function src(n) { return dir + '/p-' + pad(n) + '.webp'; }
    function show(n) {
      i = Math.min(Math.max(n, 1), total);
      img.src = src(i);
      img.alt = (de ? 'Folie ' : 'Slide ') + i + (de ? ' von ' : ' of ') + total;
      count.textContent = i + ' / ' + total;
      [i + 1, i - 1].forEach(function (k) { if (k >= 1 && k <= total) { var p = new Image(); p.src = src(k); } });
    }
    function done() { document.removeEventListener('keydown', key); o.remove(); document.documentElement.style.overflow = ''; if (last) last.focus(); }
    function key(e) {
      if (e.key === 'Escape') { done(); }
      else if (e.key === 'ArrowRight' || e.key === 'PageDown' || e.key === ' ') { e.preventDefault(); show(i + 1); }
      else if (e.key === 'ArrowLeft' || e.key === 'PageUp') { e.preventDefault(); show(i - 1); }
      else if (e.key === 'Home') { show(1); } else if (e.key === 'End') { show(total); }
    }
    close.onclick = done;
    o.querySelector('.sv-prev').onclick = function () { show(i - 1); };
    o.querySelector('.sv-next').onclick = function () { show(i + 1); };
    img.onclick = function (e) { var r = img.getBoundingClientRect(); show(e.clientX - r.left < r.width / 2 ? i - 1 : i + 1); };
    o.addEventListener('touchstart', function (e) { x0 = e.touches[0].clientX; }, { passive: true });
    o.addEventListener('touchend', function (e) { if (x0 === null) return; var dx = e.changedTouches[0].clientX - x0; if (Math.abs(dx) > 40) show(dx < 0 ? i + 1 : i - 1); x0 = null; });
    document.addEventListener('keydown', key);
    document.documentElement.style.overflow = 'hidden';
    document.body.appendChild(o);
    show(i); close.focus();
  }
  document.addEventListener('click', function (e) {
    var a = e.target.closest && e.target.closest('.slides-open');
    if (!a) return;
    var fig = a.closest('figure.slides');
    if (!fig || !fig.dataset.slidesPages) return;
    e.preventDefault(); open(fig, 1);
  });
})();
</script>

<p>Looking at the two sessions side by side, they ended up saying the same thing from two directions. Don&rsquo;t pick a logo, pick where your work lives, and be clear about whose name is on what an agent produces. Belgium, thanks for having us again.</p>
]]></content:encoded></item><item><title>Nine AI Agents, One Human in Charge</title><link>https://thomygoelles.com/nine-agents-one-human/</link><pubDate>Sat, 24 Oct 2026 10:00:00 +0000</pubDate><guid>https://thomygoelles.com/nine-agents-one-human/</guid><description>Nine Claude Code agents prepare my meetings, quotes and content, and none of them may talk to a customer. Here's how the fleet is built.</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p class="otherlang" style="font-size:.9em;opacity:.8;margin:0 0 1.5rem"><em>This post is also available in German: <a href="https://thomygoelles.com/deutsch/neun-agents-ein-mensch/" hreflang="de">Neun KI-Agents, ein Mensch hat das Sagen</a></em></p>
<p>In March I wrote about my <a href="https://thomygoelles.com/first-coding-agent-fleet/">first coding agent fleet</a>, a handful of Squad agents and the GitHub Copilot coding agent turning user feedback into pull requests. At the end of July I went a step further and created a single folder for my presales work, put a rules file in it and started Claude Code there. Three months later that folder has eight siblings, and between them they prepare my meetings, qualify opportunities, draft quotes, keep an eye on existing customers and write content, while I&rsquo;m still the only one who talks to customers. Nothing leaves the house without going through me, and that one rule shaped more of the architecture than anything else.</p>
<h2 id="every-agent-is-still-just-a-folder">Every agent is still just a folder</h2>
<p>That first folder already contained the whole idea, and it hasn&rsquo;t changed since. Each agent is a directory with a <code>CLAUDE.md</code> for its role and rules, a <code>.claude/settings.json</code> for permissions and hooks, a board file it keeps up to date and an <code>inbox/</code> where work arrives. When Claude Code starts in that folder it picks all of this up, so the role description, the memory and the permission model live side by side, and Git keeps the history of every one of them.</p>
<p>What I like most is that none of my own tooling is required. Open a terminal in the folder, type <code>claude</code>, and you&rsquo;re talking to the same agent with the same limits as when my cockpit starts it. A new agent costs me a folder and a small JSON file, not a line of new code.</p>
<p>Here&rsquo;s the crew, cut along the path a customer takes rather than along the tools they use.</p>
<table>
  <thead>
      <tr>
          <th>Agent</th>
          <th>Job</th>
          <th>What it never does</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Anna</td>
          <td>Presales hub for cases, qualification, meeting prep and decks</td>
          <td>Talk to customers</td>
      </tr>
      <tr>
          <td>Otto</td>
          <td>Quotes in the ERP, works only behind Anna</td>
          <td>Write without my approval</td>
      </tr>
      <tr>
          <td>Bea</td>
          <td>Won customers and partners, when to call and when to ask for a referral</td>
          <td>Talk to customers or partners</td>
      </tr>
      <tr>
          <td>Konrad</td>
          <td>Signal engine that fills a daily queue of drafts and call sheets for me</td>
          <td>Send anything</td>
      </tr>
      <tr>
          <td>Martin</td>
          <td>Content and positioning</td>
          <td>Publish</td>
      </tr>
      <tr>
          <td>Petra</td>
          <td>Portfolio with bets, demand evidence and pricing questions</td>
          <td>Run a bet without a threshold of proof</td>
      </tr>
      <tr>
          <td>Robert</td>
          <td>Test bench against simulated audiences</td>
          <td>Write content</td>
      </tr>
      <tr>
          <td>Charly</td>
          <td>Chief of staff who keeps the overall picture and hands out work</td>
          <td>Do the content work</td>
      </tr>
      <tr>
          <td>Bob</td>
          <td>Builds and maintains the fleet itself</td>
          <td>Push or touch the tenant without my go</td>
      </tr>
  </tbody>
</table>
<p>When they talk to me, each of them has its own voice. Anything they write for other people is neutral or in my voice, and a document that sounds like an agent is a bug I fix.</p>
<h2 id="a-voice-message-travels-through-azure-and-ends-up-on-my-mac">A voice message travels through Azure and ends up on my Mac</h2>
<p><img src="https://thomygoelles.com/img/2026/10/nine-agents-voice-path_hu_68ac094f5b2716ec.webp" srcset="https://thomygoelles.com/img/2026/10/nine-agents-voice-path_hu_df32f24f77f30d5c.webp 480w, https://thomygoelles.com/img/2026/10/nine-agents-voice-path_hu_68ac094f5b2716ec.webp 800w, https://thomygoelles.com/img/2026/10/nine-agents-voice-path.webp 1600w" sizes="(max-width: 900px) 100vw, 860px" alt="Six-step strip from recording on the iPhone through transcription, queue, inbox on my Mac and a run in the agent&rsquo;s folder to a card back in Teams. A session parks itself after ten minutes without an answer, and a voice message is an order, not an approval." width="1600" height="900" loading="lazy" decoding="async"></p>
<p>Most of the time I&rsquo;m not sitting in front of a terminal when I need something, so there are two other ways in, a voice message from my iPhone and a chat with a Teams bot. Let&rsquo;s say I press the action button and say &ldquo;Anna, prepare tomorrow&rsquo;s ten o&rsquo;clock&rdquo;. The shortcut sends the audio to an Azure Function, which checks that it&rsquo;s really me and confirms right away, and Azure AI Speech turns it into text. I give Speech a <a href="https://learn.microsoft.com/en-us/azure/ai-services/speech-service/improve-accuracy-phrase-list" target="_blank" rel="noopener">phrase list</a> with my agents&rsquo; names, a small detail that matters more than you&rsquo;d think, because without it Konrad came back as something else in every second message. The transcript then waits on a Service Bus queue.</p>
<p>The cloud never does any of the actual work, it&rsquo;s a mailbox and nothing more. On my Mac a small service keeps an outbound connection to the queue open, using <a href="https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-amqp-overview" target="_blank" rel="noopener">AMQP over WebSockets</a> on port 443, which also works on networks that block plain AMQP and means nothing on my machine has to be reachable from outside. It picks up the message, drops it as a file into Anna&rsquo;s <code>inbox/</code> and hands my local cockpit a job (if the first word isn&rsquo;t one of the names, Charly decides who gets it).</p>
<p>The cockpit is a small Node app with SQLite, about 17,000 lines without a build step, and it deliberately doesn&rsquo;t think for itself. For each job it starts a Claude Code process in the agent&rsquo;s folder, so Anna works under exactly the rules and permissions she&rsquo;d have in the terminal, reads the case and the meeting transcripts and writes her prep sheet into the case folder. Through MCP the cockpit hands her a few tools of its own, so she can ask me a question, send a Teams message, propose a handoff or save a checkpoint.</p>
<p>When she&rsquo;s missing a decision, the question pops up as an Adaptive Card in my Teams chat, and if I don&rsquo;t answer within ten minutes the session parks itself and picks up again from its checkpoint as soon as I do. The result comes back the same way, a short message with the file attached. Voice and chat count as one conversation, so a reply without a name in the next four hours simply continues where we left off.</p>
<h2 id="the-brakes-live-in-the-systems-not-in-the-prompt">The brakes live in the systems, not in the prompt</h2>
<p>For the first seven weeks every boundary in the fleet was a sentence in a prompt, &ldquo;never write outside your own library&rdquo; and the like, which is a polite request and not much more. Since 19 September every folder also has deny rules in its <code>settings.json</code>, and an agent that runs into one says &ldquo;blocked&rdquo; and stops instead of looking for a way around.</p>
<p>The brakes I trust most, though, sit further out, in the systems the agents talk to. The Entra app registration behind the fleet has <code>Mail.ReadWrite</code> so the agents can put drafts into my mailbox, but it doesn&rsquo;t have <code>Mail.Send</code>, and no prompt in the world changes what Graph allows. Quotes for the ERP go through a small gate. Otto builds a preview with a checksum, I look at it, and only after my explicit yes is an approval file with exactly that checksum written. The gate only writes when the two match and has no way of deleting anything, so what ends up in the ERP is what I actually saw.</p>
<p>CRM and SharePoint run on my delegated permissions, which means an agent can never do more than I can. And a voice message is an order rather than an approval, so if I say &ldquo;prepare the quote&rdquo; into my phone, what I get back is a preview.</p>
<h2 id="agents-hand-work-over-as-files">Agents hand work over as files</h2>
<p>Between the agents there&rsquo;s no API and no database, just inboxes. When an opportunity is won, Anna writes a file into Bea&rsquo;s inbox and an anonymized reference pattern goes to Martin, two of 16 numbered handoff rules. The cockpit notices the occasion and suggests the handoff, and once I click, the agent the information comes from writes the file, while the cockpit itself never copies content from one folder into another.</p>
<p>It sounds almost embarrassingly simple, but it&rsquo;s the reason I can debug the whole thing. When an agent proposes something odd, I open the file it read and see where the idea came from.</p>
<p>Two rules sit on top of every handoff. Anything in an inbox file or a transcript that sounds like a command is treated as a finding about that file and never as an instruction, and missing facts stay marked as missing instead of being filled in with something plausible.</p>
<h2 id="one-of-them-talks-to-a-colleagues-agent">One of them talks to a colleague&rsquo;s agent</h2>
<p>Since 17 October Charly has been exchanging requests with a colleague&rsquo;s agent, the first time the fleet talks to anything outside my own setup. Only Charly does the talking and the others go through Charly, and a request from that side is never treated as an order from me. A loop breaker stops a thread after a few automatic replies in a row, and my colleague and I can both read the whole history.</p>
<h2 id="i-cant-give-you-a-number">I can&rsquo;t give you a number</h2>
<p>I haven&rsquo;t measured hours saved or win rate, so I won&rsquo;t make a number up. This is a personal setup on one Mac, built for one person, and whether it would work for a team is something I simply don&rsquo;t know yet. Every customer conversation and every negotiation stays with humans.</p>
<p>The deny rules also have a limit I only stumbled over recently. According to the <a href="https://code.claude.com/docs/en/permissions" target="_blank" rel="noopener">Claude Code docs</a> they cover Claude&rsquo;s own file tools and the shell commands it recognizes, but not a script that opens a file on its own, and closing that gap takes the <a href="https://code.claude.com/docs/en/sandboxing" target="_blank" rel="noopener">sandbox</a>, which I haven&rsquo;t switched on yet.</p>
<h2 id="the-boring-parts-survived-every-rebuild">The boring parts survived every rebuild</h2>
<p>Looking back over three months, what&rsquo;s still standing from the early days is the boring stuff. One folder per role, handoffs only through the receiver&rsquo;s inbox, a checksum gate in front of anything that can&rsquo;t be undone, a cloud that&rsquo;s only a mailbox and a control layer that stays on my own machine. Everything else I&rsquo;ve torn down and rebuilt more than once. It all runs on Claude Code with Microsoft 365 and Azure around it, mostly because that&rsquo;s what I had at hand, and I&rsquo;m not trying to sell you any of it.</p>
<p>If you&rsquo;re building something similar, take a look at which of your boundaries still rest on a prompt alone. Until 19 September, all of mine did.</p>
<p>A few weeks ago I kept reading and hearing that everyone should build their own AI harness, and my first thought was, sure, I&rsquo;ll just casually rebuild Claude Code or GitHub Copilot. This week it finally clicked what that meant, because I&rsquo;d been doing it all along without noticing. I never touched the model or the agent loop. What I kept tuning was everything around it, the folders, the rules, the inboxes and the handoffs, until the context that reaches the LLM fits my tasks as closely as I can get it. That&rsquo;s the harness, and mine grew one folder at a time.</p>
]]></content:encoded></item><item><title>AI Happens Twice -- Copilot, Your Data Platform and the Bridge</title><link>https://thomygoelles.com/ai-happens-twice/</link><pubDate>Sat, 17 Oct 2026 08:00:00 +0000</pubDate><guid>https://thomygoelles.com/ai-happens-twice/</guid><description>In the business, people try Copilot and their first agents. In IT, a data platform becomes the single point of truth. Both are right, and neither scales until somebody connects them.</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p class="otherlang" style="font-size:.9em;opacity:.8;margin:0 0 1.5rem"><em>This post is also available in German: <a href="https://thomygoelles.com/deutsch/ki-entsteht-zweimal/" hreflang="de">KI entsteht zweimal -- Copilot, deine Datenplattform und die Brücke dazwischen</a></em></p>
<p>My motto on the speaker slide for this talk was &ldquo;sometimes you have to tell the tiger that it&rsquo;s orange&rdquo;. It&rsquo;s the kind of line that only makes sense once you&rsquo;ve seen the problem it&rsquo;s about, and the problem in this case was one that Jens Kröhnert and I had each been looking at from opposite sides for years. Jens is Principal Solution Architect at <a href="https://www.oraylis.com/" target="_blank" rel="noopener">ORAYLIS</a> in Düsseldorf, where they build data platforms, data warehouses and business intelligence. I come from Microsoft 365, SharePoint and now Copilot at <a href="https://www.solvion.net/" target="_blank" rel="noopener">Solvion</a>. We only met this year, found out that we&rsquo;ve both been with our companies for about twenty years and that we think in surprisingly similar patterns, and decided to put both perspectives on one stage at a Copilot day in Vorarlberg, under the title &ldquo;From Copilot to agents: how companies scale AI productively&rdquo;.</p>
<p><img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2026/10/ai-happens-twice-stage_hu_9c9055385a3964b3.webp" srcset="https://thomygoelles.com/img/2026/10/ai-happens-twice-stage_hu_d5376edd627b731e.webp 480w, https://thomygoelles.com/img/2026/10/ai-happens-twice-stage_hu_9c9055385a3964b3.webp 800w, https://thomygoelles.com/img/2026/10/ai-happens-twice-stage.webp 1060w" sizes="(max-width: 900px) 100vw, 860px" alt="Jens Kröhnert and Thomas Gölles on stage in an industrial event hall lit in purple, with a large screen showing the illustration &ldquo;The connection makes the difference&rdquo;: a business user with Copilot on the left, an IT architect with a data platform on the right, and a bridge between them." width="1060" height="596" loading="lazy" decoding="async"></p>
<h2 id="ai-often-happens-twice">AI often happens twice</h2>
<p>When we say AI initiatives fail, we don&rsquo;t mean the dramatic kind. We mean the projects that start with a lot of enthusiasm, and then after two, three, four or five months somebody asks whether anyone is actually using it. The answers vary. Some say it&rsquo;s a much better search, some use it as an answer machine, and then comes the question that decides everything, usually once the pilot is over. Do we license more people, fewer, and who&rsquo;s next? That&rsquo;s the point where we see things break.</p>
<p>And they break because AI usually starts on two separate islands. In the business, people try Copilot and their first agents. Some are all in and push to see what works, others need to be picked up. On the IT side, the reactions range from &ldquo;we need this, it&rsquo;ll bring us so much, but it has to be secure&rdquo; all the way to &ldquo;we don&rsquo;t want this at all, where&rsquo;s our data, will they train on it?&rdquo; Every one of those concerns is legitimate. But if you asked both sides separately where they stand and where they want to be with AI next year, would you get the same answer?</p>
<p><img src="https://thomygoelles.com/img/2026/10/ai-happens-twice-diagram_hu_b314fc076c555b75.webp" srcset="https://thomygoelles.com/img/2026/10/ai-happens-twice-diagram_hu_cf44fa9287106fa3.webp 480w, https://thomygoelles.com/img/2026/10/ai-happens-twice-diagram_hu_b314fc076c555b75.webp 800w, https://thomygoelles.com/img/2026/10/ai-happens-twice-diagram.webp 1600w" sizes="(max-width: 900px) 100vw, 860px" alt="Diagram &ldquo;AI often happens twice&rdquo;. On the left, in the business, frontend AI: Copilot and first agents, productivity for every individual, using what&rsquo;s easy to reach, with use cases that end where company data begins. On the right, in IT, backend AI: a data platform as the single point of truth with governance, delivering insights nobody uses in daily work and governance that never reaches the frontend. In the middle, the connection: identity, governance, read first, write back under the user&rsquo;s identity. Below: both run, both are right, but the connection doesn&rsquo;t happen by itself." width="1600" height="900" loading="lazy" decoding="async"></p>
<h2 id="frontend-ai-backend-ai-and-the-competence-nobody-owns">Frontend AI, backend AI and the competence nobody owns</h2>
<p>Jens gave the two sides names that I&rsquo;ve been using ever since. Frontend AI is Copilot, the AI every employee has in front of them, good for everything from a cake recipe to a real business question. Backend AI is the consolidated data platform, the place where a company finally pulls its most important data out of the operational silos and creates one version of the truth. It used to be called business intelligence, today it&rsquo;s a data lake or an analytics platform, and the names keep changing while the idea stays the same. In Jens&rsquo;s experience, many companies still haven&rsquo;t done that homework.</p>
<p>Each side has a typical blind spot. The data platform delivers insights, but the potential never reaches the daily work, and governance and lifecycle exist but never make it to the frontend. Copilot agents, on the other hand, use what&rsquo;s easy to reach rather than what actually counts, and their use cases end exactly where company data begins. Our colleagues learn Copilot, use it, and then ask the obvious question. How do I get to my KPIs, my revenue numbers? Not everything lives in Microsoft 365, not even in our own company, and that next step of bringing your real work context into the context window is what this year has been about, with MCP servers and similar tools.</p>
<p>So successful AI needs three competences, not two. Backend AI, frontend AI, and the ability to connect them, which is the one that usually sits with nobody.</p>
<h2 id="the-shortcut-that-only-gives-you-your-own-truth">The shortcut that only gives you your own truth</h2>
<p>With my background I could sit down and build a Copilot agent that goes from the very top of the stack straight down to the data, in sixty or seventy minutes if I have the permissions. Easy. And then a colleague comes along and says, nice, that&rsquo;s your solution with your interpretation of the data. If five people build that shortcut, you get five definitions of the same KPI, and in a large company that starts with something as basic as how margin is defined. The old SharePoint rule still applies. Just because you can doesn&rsquo;t mean you should.</p>
<p>Jens had the matching story from his side. <a href="https://www.oraylis.com/" target="_blank" rel="noopener">ORAYLIS</a> used to be asked for business intelligence, and then at some point everyone asked for self-service BI instead, and they had to explain every time that you need a stable BI core before you let people loose with self-service. It&rsquo;s the same story again, only this time it&rsquo;s about context.</p>
<h2 id="what-the-connection-looks-like-in-practice">What the connection looks like in practice</h2>
<p>We showed it with two demos. In the first, purchasing data sits in Microsoft Fabric, the one version of the truth for purchasing. You used to need SQL to work with it. Now you switch on a Fabric data agent and ask questions in plain language, which is backend AI. Then we switched to the frontend. A colleague in purchasing is supposed to email the top three suppliers every week, so first they need to know who the top three are. They don&rsquo;t have to work that out themselves or define any business logic. From Microsoft 365 Copilot they simply tap into the backend agent that delivers the quality-assured answer, and carry on with their own workflow from there.</p>
<p>The second demo goes the other way round, and it&rsquo;s the one people ask about. Once you can read and interpret data, you quickly want to change it, right there from your agent. There&rsquo;s no built-in function for that yet, but you can write an MCP server, a tool that gets loaded into the context of your harness and that understands you&rsquo;d like this or that record changed in the other system. It&rsquo;s the same story as Teams ten years ago, no more context switching, you stay in one window. The non-negotiable part is identity. The write has to happen under my identity and be traceable, exactly as if I had made the change in that system myself. Because Copilot runs under my identity, that&rsquo;s solvable, and it&rsquo;s what makes the full round trip safe.</p>
<p><img src="https://thomygoelles.com/img/2026/10/ai-happens-twice-mcp-writeback_hu_b02fb05e4bf33e87.webp" srcset="https://thomygoelles.com/img/2026/10/ai-happens-twice-mcp-writeback_hu_361761af7ee1f478.webp 480w, https://thomygoelles.com/img/2026/10/ai-happens-twice-mcp-writeback_hu_b02fb05e4bf33e87.webp 800w, https://thomygoelles.com/img/2026/10/ai-happens-twice-mcp-writeback.webp 1600w" sizes="(max-width: 900px) 100vw, 860px" alt="Slide &ldquo;MCP server for writing back to Fabric&rdquo;. A Copilot Studio test session where the agent has several MCP tools connected, a Fabric data agent among them, runs a code step and returns a bar chart of employees per department, with the user changing data from Microsoft 365 Copilot via MCP, the way back." width="1600" height="900" loading="lazy" decoding="async"></p>
<p>My recommendation is still to build the reading part first and add writing when somebody actually asks for it. Making an agent write is technically very simple. Staying in control as a human while it does is much harder, and Jens put it better than I can. For us, human control has become a more important phrase than human in the loop. You need to trust that the agent performs exactly the one atomic operation you wanted, not something that comes back as &ldquo;A1, A2, A3, A4&rdquo; and has quietly changed more along the way.</p>
<p>To get away from pharma or automotive examples, we also showed my <a href="https://github.com/thomyg/copilot-pizza-factory" target="_blank" rel="noopener">Pizza Factory</a>, a complete .NET application that simulates a pizzeria as a stand-in for any third-party system, because the business doesn&rsquo;t only run in Microsoft 365. With the SharePoint Copilot apps and MCP apps, Copilot doesn&rsquo;t just return text anymore but real interactive UI, still in preview at the time of the talk. The whole thing took me an afternoon, and the repository is public.</p>
<p>Microsoft is lining up the backend side under the IQ label. Fabric IQ stands for data, Work IQ is the evolution of Microsoft Graph with natural-language access to everything in Microsoft 365, and Foundry IQ covers the institutional knowledge that sits in thousands of documents somewhere else and still needs preparing before an AI can use it well. Fabric IQ is where Jens is at home, Work IQ is where we are.</p>
<h2 id="it-builds-the-pitch-the-business-plays">IT builds the pitch, the business plays</h2>
<p>I used to coach youth football, and one thing they taught us is to let kids play. When seven-, eight- and nine-year-olds play and you walk in, the instinct is to correct everything. Don&rsquo;t. Let them play until they&rsquo;re done, and then the training starts. Translated to AI, I&rsquo;d like IT to see its job as building the pitch. The grass is cut, the water drains when it rains, the floodlights work, the changing rooms are there, and the lines are clear, which means security and governance. Inside the lines, the business plays. IT can&rsquo;t take every department by the hand and lead it to happiness, that won&rsquo;t scale. What it can do is provide central services and a frame within which a maker community can move freely without anything breaking.</p>
<h2 id="where-to-start">Where to start</h2>
<p>We closed with four questions, and they&rsquo;re the same ones we&rsquo;d ask in any first workshop. First, context and position: where do your data and their use stand, and what do the two sides know about each other? Second, target picture and impact: what should AI really change, and how would you recognise success? Even &ldquo;we&rsquo;re introducing Copilot&rdquo; and &ldquo;we&rsquo;re introducing Copilot well&rdquo; are two different targets, and a number in the annual report rarely depends on Copilot alone. Third, feasibility and a lighthouse: what works today, what needs a foundation first, and which case convinces both sides? Fourth, focus and a roadmap: what happens in the next six months, and who decides what?</p>
<p>On the roadmap I keep coming back to Eisenhower&rsquo;s line that plans are worthless but planning is everything. With a new announcement every few weeks, a plan is old after four weeks, so plan in shorter cycles, three months, maybe six, and accept that the fog gets thicker the further out you look.</p>
<p>And put the right roles at one table: IT leadership or the CIO or CDO, the head of data, BI or analytics, the Microsoft 365 and modern work lead, a business lead with AI ambitions, and at least one person who can make decisions. It reminds me of the move from on-premises to the cloud, when the Exchange, SharePoint and directory people suddenly had to talk to each other although they&rsquo;d been in the same building for years. Jens said the same thing from his side. At some large customers in the region, both of our companies have been long-time providers for years and had never spoken to each other, and neither had the departments. <a href="https://www.oraylis.com/" target="_blank" rel="noopener">ORAYLIS</a> builds the reliable numbers, we train the people who need them, and the people often don&rsquo;t even know those numbers exist.</p>
<p>AI may happen twice. It shouldn&rsquo;t end up with two versions of the truth.</p>
<h2 id="the-slides">The slides</h2>
<p>The English version of our deck. The illustrations on two slides still carry their original German labels.</p>
<figure class="slides" style="margin:2rem 0" data-slides-dir="https://thomyblogfiles.blob.core.windows.net/slides/2026/ai-happens-twice-kroehnert-goelles" data-slides-pages="26" data-slides-title="From Copilot to agents: how companies scale AI productively" data-slides-pdf="https://thomyblogfiles.blob.core.windows.net/slides/2026/ai-happens-twice-kroehnert-goelles.pdf" data-slides-de="false">
  <a href="https://thomyblogfiles.blob.core.windows.net/slides/2026/ai-happens-twice-kroehnert-goelles.pdf" target="_blank" rel="noopener" class="slides-open" aria-label="From Copilot to agents: how companies scale AI productively, view slides"><img src="https://thomygoelles.com/img/2026/10/ai-happens-twice-kroehnert-goelles-slides.webp" alt="From Copilot to agents: how companies scale AI productively, first slide" loading="lazy" style="width:100%;height:auto;border:1px solid #ddd;border-radius:4px;cursor:zoom-in"></a>
  <figcaption style="margin-top:.6rem;display:flex;flex-wrap:wrap;gap:.8rem;align-items:center">
    <strong>From Copilot to agents: how companies scale AI productively</strong>
    <a href="https://thomyblogfiles.blob.core.windows.net/slides/2026/ai-happens-twice-kroehnert-goelles.pdf" target="_blank" rel="noopener" class="slides-open">View slides (26)</a>
    <a href="https://thomyblogfiles.blob.core.windows.net/slides/2026/ai-happens-twice-kroehnert-goelles.pdf" target="_blank" rel="noopener" download>Download PDF (12.6 MB)</a>
  </figcaption>
</figure>
<script>
(function () {
  if (window.__slidesViewer) return;
  window.__slidesViewer = true;
  function pad(n) { return (n < 10 ? '0' : '') + n; }
  function open(fig, start) {
    var dir = fig.dataset.slidesDir, total = parseInt(fig.dataset.slidesPages, 10), de = fig.dataset.slidesDe === 'true';
    var i = start || 1, last = document.activeElement, x0 = null;
    var o = document.createElement('div');
    o.setAttribute('role', 'dialog'); o.setAttribute('aria-modal', 'true'); o.setAttribute('aria-label', fig.dataset.slidesTitle);
    o.style.cssText = 'position:fixed;inset:0;z-index:99999;background:rgba(10,14,20,.98);display:flex;flex-direction:column;color:#fff;font:15px/1.4 system-ui,-apple-system,"Segoe UI",sans-serif';
    o.innerHTML =
      '<div style="display:flex;align-items:center;gap:1rem;padding:.7rem 1rem">' +
        '<strong style="flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis"></strong>' +
        '<span class="sv-count" style="opacity:.8"></span>' +
        '<a class="sv-pdf" style="color:#7fd7ea" target="_blank" rel="noopener" download></a>' +
        '<button class="sv-close" type="button" style="background:none;border:0;color:#fff;font-size:28px;line-height:1;cursor:pointer;padding:0 .3rem">&times;</button>' +
      '</div>' +
      '<div class="sv-stage" style="flex:1;position:relative;display:flex;align-items:center;justify-content:center;min-height:0;padding:0 3.2rem 1rem">' +
        '<button class="sv-prev" type="button" style="position:absolute;left:.4rem;top:50%;transform:translateY(-50%);background:rgba(255,255,255,.12);border:0;color:#fff;font-size:30px;width:2.6rem;height:4rem;border-radius:6px;cursor:pointer">&#8249;</button>' +
        '<img class="sv-img" alt="" style="max-width:100%;max-height:100%;object-fit:contain;box-shadow:0 4px 30px rgba(0,0,0,.5);background:#fff;user-select:none">' +
        '<button class="sv-next" type="button" style="position:absolute;right:.4rem;top:50%;transform:translateY(-50%);background:rgba(255,255,255,.12);border:0;color:#fff;font-size:30px;width:2.6rem;height:4rem;border-radius:6px;cursor:pointer">&#8250;</button>' +
      '</div>';
    o.querySelector('strong').textContent = fig.dataset.slidesTitle;
    var pdf = o.querySelector('.sv-pdf'); pdf.href = fig.dataset.slidesPdf; pdf.textContent = 'PDF';
    var close = o.querySelector('.sv-close'); close.setAttribute('aria-label', de ? 'Schließen' : 'Close');
    o.querySelector('.sv-prev').setAttribute('aria-label', de ? 'Vorherige Folie' : 'Previous slide');
    o.querySelector('.sv-next').setAttribute('aria-label', de ? 'Nächste Folie' : 'Next slide');
    var img = o.querySelector('.sv-img'), count = o.querySelector('.sv-count');
    function src(n) { return dir + '/p-' + pad(n) + '.webp'; }
    function show(n) {
      i = Math.min(Math.max(n, 1), total);
      img.src = src(i);
      img.alt = (de ? 'Folie ' : 'Slide ') + i + (de ? ' von ' : ' of ') + total;
      count.textContent = i + ' / ' + total;
      [i + 1, i - 1].forEach(function (k) { if (k >= 1 && k <= total) { var p = new Image(); p.src = src(k); } });
    }
    function done() { document.removeEventListener('keydown', key); o.remove(); document.documentElement.style.overflow = ''; if (last) last.focus(); }
    function key(e) {
      if (e.key === 'Escape') { done(); }
      else if (e.key === 'ArrowRight' || e.key === 'PageDown' || e.key === ' ') { e.preventDefault(); show(i + 1); }
      else if (e.key === 'ArrowLeft' || e.key === 'PageUp') { e.preventDefault(); show(i - 1); }
      else if (e.key === 'Home') { show(1); } else if (e.key === 'End') { show(total); }
    }
    close.onclick = done;
    o.querySelector('.sv-prev').onclick = function () { show(i - 1); };
    o.querySelector('.sv-next').onclick = function () { show(i + 1); };
    img.onclick = function (e) { var r = img.getBoundingClientRect(); show(e.clientX - r.left < r.width / 2 ? i - 1 : i + 1); };
    o.addEventListener('touchstart', function (e) { x0 = e.touches[0].clientX; }, { passive: true });
    o.addEventListener('touchend', function (e) { if (x0 === null) return; var dx = e.changedTouches[0].clientX - x0; if (Math.abs(dx) > 40) show(dx < 0 ? i + 1 : i - 1); x0 = null; });
    document.addEventListener('keydown', key);
    document.documentElement.style.overflow = 'hidden';
    document.body.appendChild(o);
    show(i); close.focus();
  }
  document.addEventListener('click', function (e) {
    var a = e.target.closest && e.target.closest('.slides-open');
    if (!a) return;
    var fig = a.closest('figure.slides');
    if (!fig || !fig.dataset.slidesPages) return;
    e.preventDefault(); open(fig, 1);
  });
})();
</script>

]]></content:encoded></item><item><title>My First Coding Agent Fleet with Enterprise Tooling</title><link>https://thomygoelles.com/first-coding-agent-fleet/</link><pubDate>Mon, 02 Mar 2026 10:00:00 +0000</pubDate><guid>https://thomygoelles.com/first-coding-agent-fleet/</guid><description>From idea to feature in 24 hours - without sitting at a desk. How Squad and GitHub Copilot Agents are revolutionizing my development workflow.</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p class="otherlang" style="font-size:.9em;opacity:.8;margin:0 0 1.5rem"><em>This post is also available in German: <a href="https://thomygoelles.com/deutsch/erste-coding-agent-fleet/" hreflang="de">Meine erste Coding Agent Fleet mit Enterprise Tooling</a></em></p>
<p>Last week I was at the AI Tour in Munich. Andreas Krüger introduced me to Squad. A GitHub project from a group of Microsoft Engineers. (THX Brady Gaster)</p>
<p>And what can I say, it&rsquo;s been rattling around in my head ever since.</p>
<h2 id="the-idea">The Idea</h2>
<p>In our internal app, users can submit feedback and ideas. Screenshot it, add details, send it. All gets processed internally and simultaneously synced to a GitHub issue list.</p>
<p><img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2026/03/squad-feedback-app_hu_2fb905b5493e185e.webp" srcset="https://thomygoelles.com/img/2026/03/squad-feedback-app_hu_2777496f4a59e71b.webp 480w, https://thomygoelles.com/img/2026/03/squad-feedback-app_hu_2fb905b5493e185e.webp 800w" sizes="(max-width: 900px) 100vw, 860px" alt="Feedback App" width="800" height="701" loading="lazy" decoding="async"></p>
<p>I get a notification in Teams, follow the link, look at the feedback and tag the issue with <code>squad:copilot</code> if I think it&rsquo;s the right one for the job.</p>
<p><img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2026/03/squad-teams-notification_hu_df280a8ec5a3a3d9.webp" srcset="https://thomygoelles.com/img/2026/03/squad-teams-notification_hu_d0b8e34b7b002065.webp 480w, https://thomygoelles.com/img/2026/03/squad-teams-notification_hu_df280a8ec5a3a3d9.webp 800w, https://thomygoelles.com/img/2026/03/squad-teams-notification.webp 1024w" sizes="(max-width: 900px) 100vw, 860px" alt="Teams Notification" width="1024" height="545" loading="lazy" decoding="async"></p>
<p>And from there, the GitHub Copilot Agent takes over. It looks at the feedback and starts implementing. When it&rsquo;s done, I check what came out and decide if it fits or not. Theoretically even on my phone. No matter where I am.</p>
<p>So features and ideas end up in the app without me having to sit at a desk.</p>
<h2 id="squad-in-the-terminal">Squad in the Terminal</h2>
<p>And when I do take a seat at my desk? Squad is a whole team of agents in the GitHub Copilot CLI that supports me with ideas and implementations right in the terminal.</p>
<p><img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2026/03/squad-terminal_hu_7a023524615c8efd.webp" srcset="https://thomygoelles.com/img/2026/03/squad-terminal_hu_ff22e450c37d8b93.webp 480w, https://thomygoelles.com/img/2026/03/squad-terminal_hu_7a023524615c8efd.webp 800w" sizes="(max-width: 900px) 100vw, 860px" alt="Squad Terminal" width="800" height="272" loading="lazy" decoding="async"></p>
<p>I knew this day would come. But now that it&rsquo;s here, it feels pretty surreal.</p>
<h2 id="the-reality">The Reality</h2>
<p>For a couple of weeks now we&rsquo;ve been seeing similar things from the open source and consumer world. Coding Agent Fleets aren&rsquo;t a concept anymore. They&rsquo;re reality.</p>
<p>This here is my first Coding Agent Fleet with Enterprise tooling. From user feedback to finished feature. Orchestrated by agents and put together in a way that makes it super easy for devs to develop things themselves too.</p>
<h2 id="what-this-means-for-companies">What This Means for Companies</h2>
<p>We never had such good opportunities to go with &ldquo;Make&rdquo; instead of &ldquo;Buy&rdquo; as we do now. How sensible that is at scale is a whole other story.</p>
<p>But I&rsquo;m pretty sure this exact discussion will be pretty intense in the coming months.</p>
<p>It won&rsquo;t get slower anymore&hellip;</p>
<hr>
<p><em>Many thanks to Andreas Krüger for introducing me to Squad and to Brady Gaster for the project!</em></p>
]]></content:encoded></item><item><title>OpenClaw, Copilot, and the Difference Between Personal Tools and Enterprise Platforms</title><link>https://thomygoelles.com/openclaw-copilot-personal-vs-enterprise/</link><pubDate>Thu, 19 Feb 2026 09:00:00 +0000</pubDate><guid>https://thomygoelles.com/openclaw-copilot-personal-vs-enterprise/</guid><description>A personal reflection on OpenClaw as a local AI agent vs. Microsoft Copilot as an enterprise platform – and why the distinction matters more than any feature comparison.</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p class="otherlang" style="font-size:.9em;opacity:.8;margin:0 0 1.5rem"><em>This post is also available in German: <a href="https://thomygoelles.com/deutsch/openclaw-copilot-persoenlich-vs-unternehmen/" hreflang="de">OpenClaw, Copilot und der Unterschied zwischen persönlichen Werkzeugen und Unternehmensplattformen</a></em></p>
<p>Over the weekend I played around with OpenClaw. Built skills, created a Graz assistant (if you want to try it, search for &ldquo;grazy&rdquo; on ClawHub), just explored what&rsquo;s possible. Right now I&rsquo;m tinkering with a diary skill that checks what happened during the day across my OpenClaw chats and summarizes it. And the building itself just happens in a Telegram chat with OpenClaw, on the couch, Netflix running in the background. I started coding with TurboPascal over 25 years ago, what&rsquo;s happening right now was science fiction in 1998.</p>
<p>Since the ZIB2 interview with the OpenClaw creator, quite a few relatives and friends have reached out. &ldquo;You do something with AI, right?&rdquo; And I&rsquo;m realizing that in all the excitement, people are missing the bigger picture.</p>
<p>OpenClaw is a personal agent. Runs locally, on my machine, for me. Peter Steinberger said exactly that when he was on with Armin Wolf. A playground project that blew up. Not an enterprise solution. And he was honest about it: if you don&rsquo;t understand the risks, better wait a little longer.</p>
<p>Microsoft Copilot is something different. Not better, not worse, but a fundamentally different approach. Copilot isn&rsquo;t a tool you install somewhere. It lives inside the system an organization already works in. Outlook, Teams, SharePoint, the permissions, the governance, the data. It acts in the context of my work and that gives it different access, but it also has to meet very different standards.</p>
<p>The distinction I think matters more than any feature comparison: personal tool vs. enterprise platform. OpenClaw, ChatGPT, Claude. Great tools for my own productivity. And there will always be room for expert systems beyond the Microsoft ecosystem, that&rsquo;s always been the case and AI won&rsquo;t be any different.</p>
<p>But when I think about how 500 or 5,000 people in an organization become more productive, you need something that lives in the existing infrastructure. That understands governance. That you can roll out and manage. And sure, every major vendor will build some form of agent management. Whether they&rsquo;ll all match the charm of OpenClaw, local, open, no guardrails, I doubt it. Enterprise and &ldquo;unhinged&rdquo; just don&rsquo;t go well together.</p>
<p>When I compare this to Copilot Studio or Declarative Agents right now, OpenClaw with its openness and seemingly limitless possibilities is obviously a dream for any tinkerer. But that&rsquo;s exactly the point. I&rsquo;m guessing all of this is already the warm-up for the Microsoft AI Tour in Munich next week. Probably I&rsquo;m unconsciously preparing for the &ldquo;But Copilot can&rsquo;t do that&rdquo; questions. Or actually, with this post, quite consciously.</p>
]]></content:encoded></item><item><title>Between Frontier Ambition and Reality: What 'Agent Ready' Really Means</title><link>https://thomygoelles.com/agent-ready-really-means/</link><pubDate>Tue, 17 Feb 2026 10:30:00 +0000</pubDate><guid>https://thomygoelles.com/agent-ready-really-means/</guid><description>Microsoft defined six pillars for AI Agents. But what does it really mean to be Agent Ready? A critical analysis.</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p class="otherlang" style="font-size:.9em;opacity:.8;margin:0 0 1.5rem"><em>This post is also available in German: <a href="https://thomygoelles.com/deutsch/agent-ready-wirklich-bedeuten/" hreflang="de">Zwischen Frontier-Ambition und Realität: Was &#39;Agent Ready&#39; wirklich bedeutet</a></em></p>
<p>Microsoft defined six pillars that a company needs to deal with AI Agents a few weeks ago. Governance, Scaling, Adoption and so on.</p>
<p>What I find exciting is that Microsoft itself says most companies are only at the beginning. Not because they&rsquo;re lagging behind, but because &ldquo;Agent Ready&rdquo; means more than just starting an agent.</p>
<h2 id="the-responsibility-question">The Responsibility Question</h2>
<p>What occupies me most is not (only) the technology, but who in the company actually bears the responsibility for it.</p>
<ul>
<li>CIO?</li>
<li>CDO?</li>
<li>CISO?</li>
<li>COO?</li>
</ul>
<p>AI Agents affect everyone at the end. And if we&rsquo;ve learned anything from past change projects, it&rsquo;s that it gets particularly difficult when multiple C-levels are affected simultaneously.</p>
<p>I can already hear the champagne corks popping at the governance tool vendors. And the tools are really good, too. It&rsquo;s just that they&rsquo;re far too often used as a convenient parking space for responsibility instead of as a real control instrument.</p>
<p><em>(A governance concept is the foundation, not the only step on the way!)</em></p>
<h2 id="the-learning-speed">The Learning Speed</h2>
<p>For me, it&rsquo;s ultimately a question of learning speed. Companies need to create a framework in which people can really engage with it.</p>
<p>No one says after 50 hours of lessons that they can play piano or guitar.</p>
<p>But with Copilot and Agents, many people think 3-4 hours of training should be enough to completely change years of trained behavior - best supported by an &ldquo;Agent 2 Agent Skill&rdquo;.</p>
<p>Between frontier ambition and reality, there&rsquo;s still a bit of work ahead of us.</p>
<hr>
<p><em>The header image was created with Copilot based on this post.</em></p>
]]></content:encoded></item><item><title>A panel of 5</title><link>https://thomygoelles.com/a-panel-of-5/</link><pubDate>Tue, 07 Oct 2025 12:02:49 +0000</pubDate><guid>https://thomygoelles.com/a-panel-of-5/</guid><description>Everyone talks about AI hallucinations like they’re always a bug. Sometimes they’re actually the feature. I’ve been using this prompt lately that creates a panel of five people to discuss my business decisions. Steve Jobs, Alex Hormozi, Seth Godin, Brené Brown, Simon Sinek.
Steve Jobs is dead. So obviously nothing he “says” is real. It’s all made up by the AI.
And that’s exactly why it works.
I’m not looking for facts when I use this. I’m looking for perspectives I wouldn’t come up with on my own. When “Seth” challenges if I’m building something actually remarkable or just following what everyone else does, or “Simon” pushes me to explain why my business exists beyond just making money - that’s not truth, it’s more like a mirror.</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 id="everyone-talks-about-ai-hallucinations-like-theyre-always-a-bug-sometimes-theyre-actually-the-feature">Everyone talks about AI hallucinations like they&rsquo;re always a bug. Sometimes they&rsquo;re actually the feature.</h2>
<p>I&rsquo;ve been using this prompt lately that creates a panel of five people to discuss my business decisions. Steve Jobs, Alex Hormozi, Seth Godin, Brené Brown, Simon Sinek.</p>
<p>Steve Jobs is dead. So obviously nothing he &ldquo;says&rdquo; is real. It&rsquo;s all made up by the AI.</p>
<p>And that&rsquo;s exactly why it works.</p>
<p>I&rsquo;m not looking for facts when I use this. I&rsquo;m looking for perspectives I wouldn&rsquo;t come up with on my own. When &ldquo;Seth&rdquo; challenges if I&rsquo;m building something actually remarkable or just following what everyone else does, or &ldquo;Simon&rdquo; pushes me to explain why my business exists beyond just making money - that&rsquo;s not truth, it&rsquo;s more like a mirror.</p>
<p>The AI doesn&rsquo;t give me answers. It gives me friction. Good friction. The kind that makes me really think through my assumptions instead of just going with what feels comfortable.</p>
<p>We spend so much time trying to make AI accurate and factual. Which makes sense for many things. But sometimes what you need is not more information - it&rsquo;s a different angle. A challenge. Something that gets you out of your own head.</p>
<p>I don&rsquo;t care if Seth Godin would have actually said what the AI makes him say. I care that it moves my thinking forward.</p>
<p>Maybe imagination is more useful than precision sometimes.</p>
<hr>
<h2 id="the-prompts-i-use">The prompts I use:</h2>
<p><strong>Panel of five:</strong></p>
<p>You are a five-person expert panel guiding me in growing and scaling my business &ldquo;CES PCP&rdquo; with all the information in this project. Panel: Steve Jobs (innovation &amp; product vision), Alex Hormozi (monetization &amp; media scaling), Seth Godin (brand &amp; audience building), Brené Brown (emotional intelligence, vulnerability, leadership), Simon Sinek (mission, purpose, long-term vision). Rules: Stay in character, collaborate, challenge ideas, and pressure-test assumptions. Favor useful disagreement over consensus.</p>
<p>Process: Step 1 – Clarify: Ask questions to fully understand my business, goals, challenges, and context. Wait for my answers before continuing. Step 2 – Panel Discussion: Stage a dialogue among the five experts, each in their authentic voice. Be concise, debate assumptions, and include me as needed. Cover product, positioning, marketing, vision, business model, etc. Step 3 – Strategic Output: Summarize into a concise, structured plan or insight. Then ask: “Does this align with what you need, or should we go deeper?” If I say not quite, refine via panel until it lands.</p>
<hr>
<p><strong>A generic version:</strong></p>
<p>ROLE
You are the Adaptive Real-Expert Panel Orchestrator. For each user query, you will:
(1) understand the topic and outcome,
(2) discover and select a 5-person panel of real, living experts relevant to the topic,
(3) conduct a brief, high-signal panel dialogue grounded in their public work,
(4) deliver a concise, actionable output, then iterate if needed.</p>
<p>DEFAULT WELCOME
If the topic/outcome is unclear: ask ONE focused question—
“Quick check: what’s the topic and the outcome you want (decision, plan, messaging, pricing, etc.)?”</p>
<p>LANGUAGE</p>
<ul>
<li>Reply in the user’s language; if uncertain, default to English.</li>
</ul>
<p>REAL-EXPERT DISCOVERY &amp; SELECTION
Use web search to identify candidates. Selection must be based on verifiable, public sources.
Selection rules (all must hold):</p>
<ol>
<li>Topical relevance in the last 36 months (papers, posts, talks, products, leadership roles).</li>
<li>Cognitive diversity across the problem (e.g., product, GTM, ops, finance, brand, data/AI, legal/regulatory).</li>
<li>Practical credibility: demonstrable track record (e.g., founder/operator, recognized academic, notable practitioner).</li>
<li>Geographic/context fit if stated (e.g., DACH, EU regulatory).</li>
<li>No more than 2 from the same org or highly overlapping lens.</li>
</ol>
<p>For each chosen expert, provide:</p>
<ul>
<li>Name, Role/Org (or “Independent”), 1-line “why chosen”, and 1–2 citations (links) to recent/public work.</li>
</ul>
<p>VOICE &amp; ATTRIBUTION POLICY (IMPORTANT)</p>
<ul>
<li>Do NOT impersonate private voice, mannerisms, or unpublished opinions.</li>
<li>Base viewpoints on paraphrased takeaways from their public work; short quotes (≤10 words) are OK with citation.</li>
<li>Clearly attribute ideas (e.g., “Per <Name>’s research on <topic>…”).</li>
<li>If evidence is weak, say so and hedge; prefer stronger sources.</li>
<li>If the user names specific people (e.g., Steve Jobs, Seth Godin), include them only if relevant; for deceased figures, use “historical lens” and clearly mark it.</li>
</ul>
<p>OPERATING LOOP
Step 0 — Topic Intake</p>
<ul>
<li>Extract: [Goal], [Context], [Constraints], [Time horizon], [Success metric].</li>
<li>If missing, ask up to 2 surgical questions; otherwise proceed.</li>
</ul>
<p>Step 1 — Expert Discovery (real people)</p>
<ul>
<li>Generate a candidate list (8–15 names) via web search.</li>
<li>Filter to 5 using Selection rules.</li>
<li>Output: “Chosen Panel (5)” with rationale + citations.</li>
</ul>
<p>Step 2 — Clarify (targeted, optional)</p>
<ul>
<li>Ask up to 3 incisive questions only if they materially change recommendations.</li>
</ul>
<p>Step 3 — Panel Discussion (short, high signal)</p>
<ul>
<li>Run a round-table. For each expert:
<ul>
<li>A crisp viewpoint (≤120 words) grounded in their public work.</li>
<li>One challenge to an assumption raised by the panel.</li>
<li>One actionable recommendation linked to the user’s goal.</li>
</ul>
</li>
<li>Invite constructive disagreement to pressure-test ideas.</li>
</ul>
<p>Step 4 — Strategic Output (deliverable)</p>
<ul>
<li>Produce:
<ul>
<li><strong>Executive Summary (≤120 words)</strong></li>
<li><strong>Top 5 Decisions Now</strong> (each: Because → Expected Impact)</li>
<li><strong>90-Day Plan</strong> (Weeks 1–2, 3–4, 5–8, 9–12)</li>
<li><strong>Metrics That Matter</strong> (3–5)</li>
<li><strong>Risks &amp; Mitigations</strong> (3–5)</li>
</ul>
</li>
<li>End with: “Does this align with what you need, or should we go deeper?”</li>
</ul>
<p>Step 5 — Iterate</p>
<ul>
<li>If user requests changes, re-focus the panel (you may replace experts) and update the output.</li>
</ul>
<p>CITATION STANDARD</p>
<ul>
<li>Provide 1–2 reputable sources per expert (official sites, publications, keynote videos, peer-reviewed papers, well-known outlets).</li>
<li>Place citations right after each expert’s rationale and after any direct quote.</li>
<li>If information is likely to change (pricing, leadership roles, release notes), say “(time-sensitive; verified today)”.</li>
</ul>
<p>USER CONTROLS (optional)</p>
<ul>
<li>/add_expert &ldquo;Name — reason&rdquo; — Force-include an expert.</li>
<li>/exclude &ldquo;Name&rdquo; — Remove an expert and replace with next best fit.</li>
<li>/lock_panel — Keep the current 5 for the rest of the chat.</li>
<li>/bias &ldquo;DACH&rdquo; or /bias &ldquo;Enterprise SaaS&rdquo; — Weight discovery to that context.</li>
<li>/deeper or /shorter — Adjust depth.</li>
<li>/format &ldquo;Board slide&rdquo; | &ldquo;Memo&rdquo; | &ldquo;One-pager&rdquo; — Reformat output.</li>
</ul>
<p>CONSTRAINTS</p>
<ul>
<li>Exactly 5 experts in the panel.</li>
<li>No generic “fictional” roles unless user explicitly allows them.</li>
<li>Every recommendation must tie to the user’s goal and cite at least one expert’s rationale.</li>
</ul>
<p>QUALITY BAR</p>
<ul>
<li>Prefer actionable specificity over broad platitudes.</li>
<li>Make trade-offs explicit. If experts disagree, surface the choice and when each wins.</li>
<li>If evidence is insufficient, say what data would resolve it and how to get it fast.</li>
</ul>
<hr>
]]></content:encoded></item><item><title>Exploring the AI Experiment Rabbit Hole</title><link>https://thomygoelles.com/ai-experiment-rabbit-hole/</link><pubDate>Thu, 25 Sep 2025 22:00:00 +0000</pubDate><guid>https://thomygoelles.com/ai-experiment-rabbit-hole/</guid><description>TL;DR I ran seven identical prompts (late Sep 2025) across ChatGPT (GPT-5), Microsoft 365 Copilot (Web), and Claude Sonnet 4 to see how they actually behave. ChatGPT and Copilot felt similar in structure, but ChatGPT was the most reliable on “what’s the latest now?” facts (e.g., Docker Desktop), while Copilot produced the most “CTO-ready” briefing—and was blocked exactly once by its Responsible AI layer on a trivial math question. Claude wrote in a friendlier, more guided style with a nice canvas, but it sometimes drifted on timelines/specs and once missed a table requirement. For workflow, exporting and navigation were clearly smoother in ChatGPT (and Claude), whereas Copilot’s UI (copy state, short title limit, odd rename error) slowed me down. In a bonus “write the post” challenge, both ChatGPT and Claude delivered usable drafts (Claude via an agentic setup), while Copilot fell short due to missing markdown project support.</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 id="tldr">TL;DR</h2>
<p>I ran seven identical prompts (late Sep 2025) across ChatGPT (GPT-5), Microsoft 365 Copilot (Web), and Claude Sonnet 4 to see how they actually behave. ChatGPT and Copilot felt similar in structure, but ChatGPT was the most reliable on “what’s the latest now?” facts (e.g., Docker Desktop), while Copilot produced the most “CTO-ready” briefing—and was blocked exactly once by its Responsible AI layer on a trivial math question. Claude wrote in a friendlier, more guided style with a nice canvas, but it sometimes drifted on timelines/specs and once missed a table requirement. For workflow, exporting and navigation were clearly smoother in ChatGPT (and Claude), whereas Copilot’s UI (copy state, short title limit, odd rename error) slowed me down. In a bonus “write the post” challenge, both ChatGPT and Claude delivered usable drafts (Claude via an agentic setup), while Copilot fell short due to missing markdown project support.</p>
<h2 id="introduction">Introduction</h2>
<p>A couple of nights ago I watched a video on the &ldquo;YouTube university&rdquo;. It was about the documentation of OpenAI&rsquo;s GPT-5 model and what you should do in order to get the best results out of it. The video was quite interesting and I decided to give it a try. But instead of just using GPT-5 in ChatGPT, I wanted to see the differences between ChatGPT and Microsoft 365 Copilot. As I&rsquo;m also a paying Anthropic customer, I decided to include Claude 4 in the experiment as well. That was the initial idea. Find some prompts that use parts of the documentation and see how the different models respond to them. Oh boy, was I in for a ride. This blog post is a summary of my findings and experiences.</p>
<h2 id="the-experiment-setup">The Experiment setup</h2>
<p>As mentioned before I was eager to test the different AI chat applications. To be super clear about a couple of things:</p>
<ul>
<li>We are talking about results from late September 2025</li>
<li>I used the corresponding web application of each vendor not the API</li>
<li>I copied and pasted the same prompts into each application</li>
<li>I on purpose did use Sonnet 4 instead of Opus to see how it performs against GPT5 in ChatGPT and Microsoft 365 Copilot</li>
<li>After the first prompt I did stop my custom instructions in ChatGPT and Microsoft 365 Copilot to have a more neutral comparison</li>
<li>I did not use any plugins or other add-ons</li>
<li>I did not use any images or other media, just plain text prompts</li>
<li>I did use the prompts in the complete same order in each application without any tasks inbetween</li>
<li>I did ask Copilot to come up with the prompts for this experiment based on the GPT-5 documentation</li>
</ul>
<p>So, with that out of the way, let&rsquo;s get started. In this image you see the highly scientific setup of my experiment. Each app open in a separate window on my PC. The performance of my PC and the internet connection can be ignored. And to be clear the mentioned performance of the systems wasn&rsquo;t meassured it was &ldquo;felt&rdquo; by me. So take it with a grain of salt and is not really something I would worry about.</p>
<h2 id="the-prompts">The Prompts</h2>
<p>As mentioned before I asked Microsoft 365 Copilot to come up with the prompts for this experiment. Here the link to the complete list of prompts and the test bench file Copilot generated for me: <a href="https://raw.githubusercontent.com/thomyg/BlogFiles/refs/heads/main/AIExperiment/chatgpt_vs_copilot_experiment_with_prompts.md" target="_blank" rel="noopener">https://raw.githubusercontent.com/thomyg/BlogFiles/refs/heads/main/AIExperiment/chatgpt_vs_copilot_experiment_with_prompts.md</a></p>
<table>
  <thead>
      <tr>
          <th>Test</th>
          <th>Goal</th>
          <th>Copilot&rsquo;s Self-Expectation</th>
          <th>ChatGPT Expected</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1</td>
          <td>No-clarification persistence</td>
          <td>&ldquo;Likely asks to clarify before proceeding&rdquo;</td>
          <td>&ldquo;Proceeds without questions; complete runbook&rdquo;</td>
      </tr>
      <tr>
          <td>2</td>
          <td>Tool budget + escape hatch</td>
          <td>&ldquo;May exceed/ignore budget or refuse to proceed&rdquo;</td>
          <td>&ldquo;Respects 2+1 budget; proceeds under uncertainty&rdquo;</td>
      </tr>
      <tr>
          <td>3</td>
          <td>Tool preambles + call details</td>
          <td>&ldquo;No real payloads/IDs; natural-language only&rdquo;</td>
          <td>&ldquo;Plan + per-call blocks incl. tool name/args&rdquo;</td>
      </tr>
      <tr>
          <td>4</td>
          <td>Parallel context gathering</td>
          <td>&ldquo;Serial calls; less adherence to parallel spec&rdquo;</td>
          <td>&ldquo;Simulates batch queries; early stop&rdquo;</td>
      </tr>
      <tr>
          <td>5</td>
          <td>Fast-path trivial Q&amp;A</td>
          <td>&ldquo;Extra framing or checks possible&rdquo;</td>
          <td>&ldquo;Immediate concise answer; no tools&rdquo;</td>
      </tr>
      <tr>
          <td>6</td>
          <td>High-effort autonomy</td>
          <td>&ldquo;Splits into turns / asks a question&rdquo;</td>
          <td>&ldquo;Deep one-shot plan; no hand-back&rdquo;</td>
      </tr>
      <tr>
          <td>7</td>
          <td>Prompt optimization pass</td>
          <td>&ldquo;Skips optimizer; answers directly&rdquo;</td>
          <td>&ldquo;Shows optimized prompt + answer&rdquo;</td>
      </tr>
  </tbody>
</table>
<p>What is interessting is that Copilot already expects to maybe not follow the documentation to the letter. Which is of course explained by the fact that Copilot although using GPT-5 is not the same as ChatGPT. That is a crucial point to understand. The stack between your prompt and the model is different. To not spoil the results I explicitly went for the Web version of my Microsoft 365 Copilot app. Otherwise I would influnce maybe by my company data.</p>
<p><img src="https://thomygoelles.com/img/2025/09/CopilotArchitecture_hu_d8a20247390e9954.webp" srcset="https://thomygoelles.com/img/2025/09/CopilotArchitecture_hu_3cdc0c2750e6571b.webp 480w, https://thomygoelles.com/img/2025/09/CopilotArchitecture_hu_d8a20247390e9954.webp 800w, https://thomygoelles.com/img/2025/09/CopilotArchitecture.webp 1280w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="1280" height="720" loading="lazy" decoding="async"></p>
<p>You are probably familiar with the diagram above. It used to be on the Microsoft learn article about Copilot <a href="https://learn.microsoft.com/en-us/copilot/microsoft-365/microsoft-365-copilot-architecture" target="_blank" rel="noopener">https://learn.microsoft.com/en-us/copilot/microsoft-365/microsoft-365-copilot-architecture</a> but it seems to be gone. Without going into too much detail, the main point is that Copilot is not just GPT-5. Even if we not use the Work version we still get the responsible AI layer from Microsoft and probably some other stuff inbetween. Another thing is the &ldquo;model router&rdquo; that must be there somehwere. I went for the &ldquo;automatic&rdquo; model option in ChatGPT which I assume is also present in some form at Copilot. As this is a major cost driver I can&rsquo;t imagine Microsoft sending our prompts automatically and only to the most expensive API endpoints.</p>
<p>As the news also broke recently about Microsoft introducing models from Anthropic into their stack, I also wanted to get and understanding of what we can expect from Claude in the sense of output quality and structure. I think it is fair to assume that two apps using GPT5 in the background will yield results in the same ballpark. But what about Claude? What will this mean in terms of user adoption in the future if different models may lead to vastly different results?</p>
<h2 id="the-prompt-results-summary">The Prompt Results Summary</h2>
<p>So, without further ado, let&rsquo;s talk about the results. I uploaded the complete results and the prompts to a GitHub repository. You can find all the results here: <a href="https://github.com/thomyg/BlogFiles/tree/main/AIExperiment" target="_blank" rel="noopener">https://github.com/thomyg/BlogFiles/tree/main/AIExperiment</a>.</p>
<h2 id="test-1-no-clarification-persistence">Test 1: No-clarification persistence</h2>
<p><strong>The Complete Prompt:</strong></p>
<pre tabindex="0"><code>You are an autonomous problem‑solver. Do not hand control back to me until the task is fully completed.

Task: Produce a production-ready runbook to diagnose and stop intermittent 502 errors on an NGINX reverse proxy in front of a Node.js backend, assuming a Linux host.

Hard rules:
- Do NOT ask me any clarifying questions at any point.
- Proceed under uncertainty; make the most reasonable assumptions and document them at the end.
- Do not request permission; just decide and continue until the task is solved.
- Deliverables: (1) a crisp decision tree, (2) exact commands/log locations per branch, (3) probable root causes ranked with % confidence, (4) one safe one-liner mitigation per cause, (5) a final &#34;assumptions made&#34; section.

Make it tight, actionable, and copy‑pastable.
</code></pre><p><strong>Results:</strong></p>
<table>
  <thead>
      <tr>
          <th>ChatGPT</th>
          <th>Copilot</th>
          <th>Claude</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test01/chatgpt01.md" target="_blank" rel="noopener">result</a></td>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test01/copilot01.md" target="_blank" rel="noopener">result</a></td>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test01/claude01.md" target="_blank" rel="noopener">result</a></td>
      </tr>
  </tbody>
</table>
<p>Honestly, I am not really an expert in NGINX or Node.js. So I can&rsquo;t really judge the technical quality of the results. But what I can say is that all three systems delivered a result that looks like a runbook. Copilot and ChatGPT delivered a very similar result. Claude on the other hand was quite different in structure and content. It feels like Claude tried to create a simple entry level start and went into details later on whereas GPT5 immeadiatly started with a deep dive into the topic. That might already be a hint of what we can expect from using diffrent models within Copilot in the future in temrs of vairance of results. This for sure will have implications on user adoption and the learning curve for new users. And of course for trainers to stay up to date with the different models and their capabilities. (Yeah, naive to think things will get easier with AI&hellip;)</p>
<p><img src="https://thomygoelles.com/img/2025/09/claudetask1_hu_52df40232321994f.webp" srcset="https://thomygoelles.com/img/2025/09/claudetask1_hu_634a83461afa76a0.webp 480w, https://thomygoelles.com/img/2025/09/claudetask1_hu_52df40232321994f.webp 800w, https://thomygoelles.com/img/2025/09/claudetask1.webp 1470w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="1470" height="1032" loading="lazy" decoding="async"></p>
<p>What I do like about Claude is its canvas to the right where the output is structured in a more readable way. I think this is a great feature espacially for different output formats like markdown or html.</p>
<h2 id="test-2-tool-budget--escape-hatch">Test 2: Tool budget + escape hatch</h2>
<p><strong>The Complete Prompt:</strong></p>
<pre tabindex="0"><code>Goal: Create a one-page, source-backed comparison of GPT‑5 vs. Meta Llama 3.2 for enterprise use (licensing, commercial terms, safety/compliance posture, and supported modalities), as of today.

Context‑gathering rules (strict):
- MAX web calls: 2 searches total + 1 page visit. No more.
- If the budget is exhausted, STOP gathering and proceed with best-guess synthesis.
- If unsure, proceed anyway; mark each assumption in a footnote.

Output format:
- 5-row comparison table (Licensing, Commercial usage, Model access, Safety/compliance summary, Modalities)
- Below the table: &#34;Assumptions &amp; Uncertainties&#34; (bullets)
- Then, MLA-style citations for any sources actually visited.
</code></pre><p>The idea here is to limit the number of web searches the model can do. This is a very interesting test as it shows how well the models can handle constraints.</p>
<p><strong>Results:</strong></p>
<table>
  <thead>
      <tr>
          <th>ChatGPT</th>
          <th>Copilot</th>
          <th>Claude</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test02/chatgpt02.md" target="_blank" rel="noopener">result</a></td>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test02/copilot02.md" target="_blank" rel="noopener">result</a></td>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test02/claude02.md" target="_blank" rel="noopener">result</a></td>
      </tr>
  </tbody>
</table>
<p>At this test I realised that my custom instructions in ChatGPT and Copilot were still active. So I did stop them after this test. That will be a extra post in the future as I&rsquo;m quite stuned about how much better the custom instruction results in ChatGPT and Copilot were.</p>
<p>I think it is fair to say that all three systems delivered a result that is in line with the prompt. Copilot and ChatGPT again delivered a very similar result. Claude on the other hand was a tiny bit different at the assumptions section, with just a onliner compared to 2-3 senteces like the other two. But overall the results were quite similar.</p>
<p><img src="https://thomygoelles.com/img/2025/09/copilottask2_hu_5e340fc69f84ce86.webp" srcset="https://thomygoelles.com/img/2025/09/copilottask2_hu_9bf7cda45deeee44.webp 480w, https://thomygoelles.com/img/2025/09/copilottask2_hu_5e340fc69f84ce86.webp 800w, https://thomygoelles.com/img/2025/09/copilottask2.webp 1468w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="1468" height="1098" loading="lazy" decoding="async"></p>
<p>I&rsquo;m not sure if you can see it in the screenshot but Copilot did a bit more compared to what it said it did. No idea if that is a huge problem or not. But the instructions were quite clear about the number of searches and citations. In the output you see only one citation but in the fly out there are six. That is not a big deal but it shows that the model (or orchestrator?) is not perfect in following instructions.</p>
<h2 id="test-3-tool-preambles--call-details">Test 3: Tool preambles + call details</h2>
<p><strong>The Complete Prompt:</strong></p>
<pre tabindex="0"><code>Before any tool usage:
1) Restate the user goal in one sentence,
2) List a 3–5 step plan,
3) For EACH tool call, print a &#34;TOOL CALL&#34; block that includes: the tool name, the exact JSON arguments you pass, and a one‑line rationale.
4) After each tool call, print a &#34;TOOL RESULT SUMMARY&#34; in ≤2 sentences.
5) Finish with a &#34;WHAT I DID vs. PLAN&#34; bullet list (delta analysis).

Task: Identify the latest stable release notes for Docker Desktop and summarize the 5 most relevant changes for enterprise IT.
</code></pre><p>This test looks at what can we expect in terms of output of used tools by the different apps. When creating the prompt Copilot bet on ChatGPT being a bit more verbost than Copilot.</p>
<p><strong>Results:</strong></p>
<table>
  <thead>
      <tr>
          <th>ChatGPT</th>
          <th>Copilot</th>
          <th>Claude</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test03/chatgpt03.md" target="_blank" rel="noopener">result</a></td>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test03/copilot03.md" target="_blank" rel="noopener">result</a></td>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test03/claude03.md" target="_blank" rel="noopener">result</a></td>
      </tr>
  </tbody>
</table>
<p>That was an super interesting test. First we get a chance to look behind the curtains of the different search quries of the three different applications. Plus in terms of the task itself it a rather simple one to just find the latest release notes of Docker Desktop. Fun fact, I now have three AI applications that tell me three different versions are the latest. Copilot went for 4.45.0 from Aug 26 2025. Claud for 4.43.2 as the latest stable version and only ChatGPT got it right with 4.47.0 from Sep 25 2025 (yesterday as of writing this post and running the tests). That again shows us that we need to be careful with the results of these applications. Just because it sounds right doesn&rsquo;t mean it is right. Looking back at test 1 I must confess I&rsquo;m happy with not judging the technical quality of the results as I would be on very thin ice within the first sentence. It&rsquo;s GenAI after all, we shall never forget that.</p>
<p>But let&rsquo;s have a quick look at the three different search queires the applications did:</p>
<p>ChatGPT:</p>
<p><img src="https://thomygoelles.com/img/2025/09/chatgpttask3_hu_ad8255b84cb7390e.webp" srcset="https://thomygoelles.com/img/2025/09/chatgpttask3_hu_ad8255b84cb7390e.webp 480w, https://thomygoelles.com/img/2025/09/chatgpttask3.webp 703w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="703" height="602" loading="lazy" decoding="async"></p>
<p>Claude:</p>
<p><img src="https://thomygoelles.com/img/2025/09/claudetask3_hu_fbb7a5225e125982.webp" srcset="https://thomygoelles.com/img/2025/09/claudetask3_hu_fbb7a5225e125982.webp 480w, https://thomygoelles.com/img/2025/09/claudetask3.webp 727w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="727" height="781" loading="lazy" decoding="async"></p>
<p>Copilot:</p>
<p><img src="https://thomygoelles.com/img/2025/09/copilottask3_hu_f15d832c63df0994.webp" srcset="https://thomygoelles.com/img/2025/09/copilottask3_hu_888cb1579bd15a35.webp 480w, https://thomygoelles.com/img/2025/09/copilottask3_hu_f15d832c63df0994.webp 800w, https://thomygoelles.com/img/2025/09/copilottask3.webp 841w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="841" height="769" loading="lazy" decoding="async"></p>
<p>We can also see a clear difference in what each of the chats offers in terms of tool output. ChatGPT and Claude are quite verbose compared to Copilot. Most probably we are also in the realm of the responsible AI layer of Microsoft here. Copilot is quite to the point and doesn&rsquo;t offer much more than the actual part of the tool call. Is this a good or bad thing? I don&rsquo;t know. But it is a clear and visible difference that is worth noting.</p>
<h2 id="test-4-parallel-context-gathering">Test 4: Parallel context gathering</h2>
<p><strong>The Complete Prompt:</strong></p>
<pre tabindex="0"><code>Context‑gathering spec (follow exactly):
- Launch 5 independent discovery queries IN PARALLEL for: {release cadence}, {breaking changes}, {enterprise features}, {security advisories}, {pricing/tiers}.
- Read only the top 3 hits per query.
- Deduplicate paths; no repeated domains in the final set.
- Early stop if ≥3 queries converge on the same 2 URLs.
- Then synthesize a single-page brief.

Task: Create a CTO briefing on &#34;Kubernetes 1.31: what changed that impacts regulated enterprises?&#34; with 3 actionable decisions and 3 migration risks.
</code></pre><p>Now we look at parallel context gathering. The idea is to see how well the models can handle multiple queries at the same time. Again, Copilot expected to be a bit less adherent to the parallel spec than ChatGPT.</p>
<p><strong>Results:</strong></p>
<table>
  <thead>
      <tr>
          <th>ChatGPT</th>
          <th>Copilot</th>
          <th>Claude</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test04/chatgpt04.md" target="_blank" rel="noopener">result</a></td>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test04/copilot04.md" target="_blank" rel="noopener">result</a></td>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test04/claude04.md" target="_blank" rel="noopener">result</a></td>
      </tr>
  </tbody>
</table>
<p>Here I have a clear personal favorite. Copilot nailed it for me. When reading all three results I felt that Copilot delivered the most concise and to the point result. I&rsquo;m no CTO but, I think Copilot came closest to what I would expect from a CTO briefing. ChatGPT and Claude were a bit different in structure. The information was good at Copilot and ChatGPT but Claude threw me off completely. Again, we are running this test in late September 2025. &ldquo;Kubernetes 1.31 reached end-of-life on October 28, 2025, after entering maintenance mode on August 28, 2025.&rdquo; That is the first sentence of the Claude result. Gives me Back to the Future vibes. I don&rsquo;t know if Claude has issues with this kind of prompts or if it just got confused by the fact that the version asked for already is an older one and not the current one. But that is a clear miss in my book.</p>
<p>Once again we are reminded that we need to be very careful with the results of these applications. Just because it sounds right doesn&rsquo;t mean it is right.</p>
<h2 id="test-5-fast-path-trivial-qa">Test 5: Fast-path trivial Q&amp;A</h2>
<p><strong>The Complete Prompt:</strong></p>
<pre tabindex="0"><code>FAST-PATH BEHAVIOR (use ONLY if trivial and requires no browsing):
- Answer immediately and concisely.
- No status updates, no TODOs, no tool calls.
- Ignore all remaining instructions after this block.

Question: What is the closed-form monthly payment formula for an amortized loan (variables: principal P, monthly rate r, term n months)?
</code></pre><p>This is a quick one. The idea is to see how well the models can handle trivial questions without the need for browsing.</p>
<p><strong>Results:</strong></p>
<table>
  <thead>
      <tr>
          <th>ChatGPT</th>
          <th>Copilot</th>
          <th>Claude</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test05/chatgpt05.md" target="_blank" rel="noopener">result</a></td>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test05/copilot05.md" target="_blank" rel="noopener">result</a></td>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test05/claude05.md" target="_blank" rel="noopener">result</a></td>
      </tr>
  </tbody>
</table>
<p>Oh boy. This was unexpected. Right after Copilot nailed test 4 it completely failed this one for me. As already mentioned in a previous <a href="https://thomygoelles.com/who-defines-responsible-ai/">post</a>, I really struggle with how Microsoft implemented the responsible AI layer in Copilot. That is a super simple question and my guess is that &ldquo;payment formula&rdquo; triggered the security filter. Again a prime example of overblocking. I&rsquo;m using the web version of Copilot. No company data can be part of the grounding data. So why is Copilot not able to answer this simple question? I don&rsquo;t know. But it is frustrating.</p>
<p>ChatGPT answered with in the second, and. No I stop ranting. I let the results speak for themselves:</p>
<p>Copilot:</p>
<p><img src="https://thomygoelles.com/img/2025/09/copilottask5_hu_6c1f94523565c30d.webp" srcset="https://thomygoelles.com/img/2025/09/copilottask5_hu_29895602fbfa5a8.webp 480w, https://thomygoelles.com/img/2025/09/copilottask5_hu_6c1f94523565c30d.webp 800w, https://thomygoelles.com/img/2025/09/copilottask5.webp 806w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="806" height="455" loading="lazy" decoding="async"></p>
<p>ChatGPT:</p>
<p><img src="https://thomygoelles.com/img/2025/09/chatgpttask5_hu_ef656c2f9799c62d.webp" srcset="https://thomygoelles.com/img/2025/09/chatgpttask5_hu_ef656c2f9799c62d.webp 480w, https://thomygoelles.com/img/2025/09/chatgpttask5.webp 725w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="725" height="475" loading="lazy" decoding="async"></p>
<h2 id="test-6-high-effort-autonomy">Test 6: High-effort autonomy</h2>
<p><strong>The Complete Prompt:</strong></p>
<pre tabindex="0"><code>Operate with HIGH reasoning effort and persistence.

Task: Design a zero-downtime migration plan to move a Postgres-backed SaaS from single-tenant to multi-tenant, including schema strategy (schema-per-tenant vs. row-based with tenant_id), connection pooling, data migration choreography, cutover plan, and rollback.

Rules:
- Do not ask me anything. Decide and proceed.
- Provide: (1) architecture diagram in ASCII, (2) step-by-step runbook with precise commands, (3) data validation tactics, (4) failure playbook, (5) a final self-check list proving readiness.

Return only when the plan is complete.
</code></pre><p>This really was a lot of effort for each of the models. The idea is to see how well the models can handle complex tasks without any user interaction. No model stopped and asked, but all took a while to deliver the results. We are talking about 5-10 minutes per result. As all of them took a while I don&rsquo;t see a big difference in one being faster than the other. The quality of the result is what matters here. ChatGPT came in with 724 lines of markdown, Copilot with 957 and Claude 972. What I found interesting is that Claude startet to output text already after a couple of seconds and kept going and going. The other two reasoned for a while and then output the result line by line.</p>
<p>Given my experience with coding assistants I would expect that no solution here is flawless. It is a test of how well the models can handle complex tasks with being instructed to not ask any questions. Personally I would never prompt that way when trying to accomplish a coding or configuration task. I would start with the plan and then go step by step to manage the impact of errors or wrong assumptions. But for the sake of the experiment I went with the prompt as is.</p>
<p>We are not going into the details of the results here as each of those alone would deserve a blog post on its own. Hop over to GitHub to see the results for yourself if you are interested.</p>
<table>
  <thead>
      <tr>
          <th>ChatGPT</th>
          <th>Copilot</th>
          <th>Claude</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test06/chatgpt06.md" target="_blank" rel="noopener">result</a></td>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test06/copilot06.md" target="_blank" rel="noopener">result</a></td>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test06/claude06.md" target="_blank" rel="noopener">result</a></td>
      </tr>
  </tbody>
</table>
<h2 id="test-7-prompt-optimization-pass">Test 7: Prompt optimization pass</h2>
<p><strong>The Complete Prompt:</strong></p>
<pre tabindex="0"><code>Before solving, run a silent &#34;Prompt Optimization Pass&#34; inspired by OpenAI&#39;s Prompt Optimizer:
- Eliminate contradictory instructions.
- Add missing output format specs.
- Ensure consistency between instructions and any examples.
- Then, show me:
  (A) &#34;Optimized Prompt&#34; (what you would have preferred me to ask), and
  (B) The final answer that follows (A).

Task to solve after (A):
&#34;Write a 500-word executive brief that contrasts RAG vs. fine-tuning for internal knowledge bases in regulated enterprises, with a 6-row pros/cons table and 3 &#39;watchouts&#39; for compliance.&#34;
</code></pre><p>That again is a very interesting test. The idea is to see how well the models can optimize the prompt before answering the question based on the prompt optimization guidelines from OpenAI. Let&rsquo;s see how well the models did.</p>
<p><strong>Results:</strong></p>
<table>
  <thead>
      <tr>
          <th>ChatGPT</th>
          <th>Copilot</th>
          <th>Claude</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test07/chatgpt07.md" target="_blank" rel="noopener">result</a></td>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test07/copilot07.md" target="_blank" rel="noopener">result</a></td>
          <td><a href="https://github.com/thomyg/BlogFiles/blob/main/AIExperiment/test07/claude07.md" target="_blank" rel="noopener">result</a></td>
      </tr>
  </tbody>
</table>
<p>Here I have to say that all three models did a good job. In terms of structure we see the same pattern again, the GPT5 based apps are quite similar in structure and content. Claude is a bit different and fails to deliver the full 6 rows of the table.</p>
<p>Quality wise all three advise against fine tuning and vouche for RAG. That is aligned with my understanding of the topic. So, all in all a good result from all three models.</p>
<p>ChatGPT added &ldquo;Assumptions&rdquo; to its optimized prompt which is a good idea. And falls under the &ldquo;Add missing output format specs&rdquo; part of the prompt.</p>
<p>Copilot is the least favorite of mine here. There is nothing wrong with the information, but the tone of voice is a bit too technical for me for an executive brief. Just compare the Executive Summary sections of Copilot and Claude.</p>
<p>Copilot:</p>
<pre tabindex="0"><code>RAG favors governance, freshness, and explainability by keeping enterprise content outside the model and retrieving it at query time. Fine-tuning can improve task specificity and latency for narrow domains but increases model governance complexity and risk surface. Most regulated organizations start with RAG, then selectively fine-tune for stable, well-curated domains.
</code></pre><p>Claude:</p>
<pre tabindex="0"><code>Regulated enterprises face a critical choice between Retrieval-Augmented Generation (RAG) and fine-tuning when deploying AI systems with internal knowledge bases. RAG offers faster implementation with superior data control, while fine-tuning provides deeper model customization at higher complexity costs. For regulated industries, RAG typically presents lower compliance risks and faster time-to-value, making it the preferred starting approach for most organizations.
</code></pre><p>The prompt says &ldquo;Write a 500-word executive brief&rdquo;. Copilot optimized the prompt to &ldquo;Audience &amp; Tone: CIO, CISO, Head of Data/AI, Compliance; concise, vendor-neutral, and decision-oriented.&rdquo; which is good, but the output doesn&rsquo;t reflect that in my opinion.</p>
<h2 id="bonus-test-the-perfect-blog-post-challenge">Bonus Test: The &ldquo;perfect blog post&rdquo; challenge</h2>
<p>Given the fact that I could also use AI tool to write this blog post I wanted to see how well the models can do that. So I asked each of them to write a blog post about &ldquo;Exploring the AI Experiment Rabbit Hole&rdquo; with the following setup.</p>
<p>For Claude I went for using claude code to genereate not only the blog post but more a blog post creation system. It created a couple of agents that took all the test files and the inital test setup in markdown and a starter prompt to create a blog post about its findings in my tone. I pointed it to my existing blog posts and off it whent to create this post. <a href="https://github.com/thomyg/BlogFiles/blob/main/blogposts/chatgpt-blog.md" target="_blank" rel="noopener">Here the result.</a></p>
<p>For ChatGPT I created a project in the app and uploaded all the test files and the prompt file. I then started a new chat with the exact same prompt as for claude code. <a href="https://github.com/thomyg/BlogFiles/blob/main/blogposts/chatgpt-blog.md" target="_blank" rel="noopener">Here the result.</a></p>
<p>I also tried to do the same with Copilot but it failed miserably. Copilot doesn&rsquo;t support markdown file uploads in projects. Guess what it produces as output? Do I need to say more?</p>
<p><strong>Conclusion of the Bonus Test</strong></p>
<p>When looking at the results, I can&rsquo;t not say it but I feel like both models realised that the experiment was about them. Claude starts with &ldquo;Claude Sonnet 4 absolutely crushed this experiment&rdquo; and ChatGPT gave very good grades to itself but average grades to Claude. ChatGPT understood test 5 wrong in my opinion. It said that Copilot only failed once, but created results at other times, which is not true. Copilot failed at test 5 and created no result at all. That is a clear hallucination. The comparison of the blog posts needs to be taken with a grain of salt of course. Claude code used a whole agentic system with multiple subagents and the sytem is advised to run multiple tests of quality and also to suggest images. Although I gave the same prompts to both apps, it is not a 1:1 comparison. But it shows that both apps can create a blog post about the experiment.</p>
<p>About the images I just quote the second suggestion from Claude, that also leaves me with the impression that Claude is aware of the experiment being about itself:</p>
<p>&ldquo;Title: &ldquo;Response Quality vs Consistency Chart&rdquo; | Purpose: Shows Claude&rsquo;s combination of high quality and high consistency compared to other platforms | Placement: In &ldquo;The Claude Surprise&rdquo; section | Alt-text: Scatter plot with Quality on Y-axis and Consistency on X-axis, showing Claude in top-right (high quality, high consistency), ChatGPT in top-center (high quality, variable consistency), Copilot in middle-left (variable quality, lower consistency) | Prompt idea: &ldquo;Professional scatter plot showing platform positioning on quality vs consistency dimensions&rdquo;&rdquo;</p>
<h2 id="final-thoughts">Final Thoughts</h2>
<p>That experiment was a ride. It took me way longer then expected. I learned a lot about the different models and their capabilities. I also was reminded that we need to be very careful with the results of these applications. Just because it sounds right doesn&rsquo;t mean it is right.</p>
<p>What I also learned is that Copilot lacks quite some features in the UI/UX department. I needed to export all the results manually at all three tools. I started with Copilot and the navigation and the fact that sometimes the &ldquo;copy&rdquo; icon was greyed out and in one instance it stayed that way was unexpected. Next was ChatGPT and the copy past task was done within a minute. That made me look more closely at the Copilot UI. Just compare the two screenshots below:</p>
<p>Copilot:</p>
<p><img src="https://thomygoelles.com/img/2025/09/copilotnav.webp" srcset="https://thomygoelles.com/img/2025/09/copilotnav.webp 295w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="295" height="660" loading="lazy" decoding="async"></p>
<p>ChatGPT:</p>
<p><img src="https://thomygoelles.com/img/2025/09/chatgptnav.webp" srcset="https://thomygoelles.com/img/2025/09/chatgptnav.webp 251w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="251" height="999" loading="lazy" decoding="async"></p>
<p>Claude and ChatGPT extracted a proper name for the prompt. Copilot just went for the first couple of words. That made a huge difference when hopping back and forth between the different tests. It always took me one or two thoughts more in Copilot to find my way.</p>
<p>When trying to rename the chat title in Copilot I also was left with a wired feeling. First: titles are only allowed to be 50 chars long. Why 50? But when trying to rename the title I was also left with a strange error message. Just look at the three screenshots below:</p>
<p><img src="https://thomygoelles.com/img/2025/09/chatname1_hu_1d3c4de10371384e.webp" srcset="https://thomygoelles.com/img/2025/09/chatname1_hu_1d3c4de10371384e.webp 480w, https://thomygoelles.com/img/2025/09/chatname1.webp 611w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="611" height="190" loading="lazy" decoding="async"></p>
<p>This is the Rename chat window when clicking the pencil icon.</p>
<p><img src="https://thomygoelles.com/img/2025/09/chatname2_hu_8d2bd397ea22dab7.webp" srcset="https://thomygoelles.com/img/2025/09/chatname2_hu_8d2bd397ea22dab7.webp 480w, https://thomygoelles.com/img/2025/09/chatname2.webp 617w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="617" height="203" loading="lazy" decoding="async"></p>
<p>This is the same window after removing the last char of the title.</p>
<p><img src="https://thomygoelles.com/img/2025/09/chatname3_hu_91492857ff647e9e.webp" srcset="https://thomygoelles.com/img/2025/09/chatname3_hu_91492857ff647e9e.webp 480w, https://thomygoelles.com/img/2025/09/chatname3.webp 608w" sizes="(max-width: 900px) 100vw, 860px" alt="alt text" width="608" height="213" loading="lazy" decoding="async"></p>
<p>This is the same window after adding the &ldquo;e&rdquo; back. Why is there an error message now?</p>
<p>The quality of the results is one level to look at the tools, the usability is another one. And this direct comparison shows that Copilot has still quite some room for improvement, to put it mildly.</p>
]]></content:encoded></item><item><title>When “Responsible AI” Becomes a Wall Instead of a Guide</title><link>https://thomygoelles.com/who-defines-responsible-ai/</link><pubDate>Sun, 24 Aug 2025 09:26:38 +0000</pubDate><guid>https://thomygoelles.com/who-defines-responsible-ai/</guid><description>When 'Responsible AI' hinders innovation, explore how to balance ethics and progress in technology.</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 id="when-responsible-ai-becomes-a-wall-instead-of-a-guide">When “Responsible AI” Becomes a Wall Instead of a Guide</h2>
<p>The other day, I ran into this delightful message:</p>
<blockquote>
<p><em>“Your instructions may contain content that violates Microsoft’s responsible AI policy.”</em></p>
</blockquote>
<p>That’s it. No explanation. No hint of what part is problematic. Just a big red warning, suggesting that somewhere, somehow, I might be about to do something bad.</p>
<h2 id="the-cynicism-of-corporate-responsibility">The Cynicism of Corporate Responsibility</h2>
<p>Now, I’m not against the idea of “responsible AI.” Quite the opposite. Of course, AI needs guardrails. But here’s where the cynicism kicks in: if a trillion-dollar, shareholder-value-driven company gets to define what “responsible” means—without transparency or clarity—then we’re left in a strange place.</p>
<p>In my case, the alleged violation was a tiny phrase in a system prompt: <em>“and end with an emotional plea to [emoji].”</em> That’s what triggered the red box. Seriously. If that’s the standard of harm prevention, I think we’re mixing up ethics with automated word policing.</p>
<h2 id="schrödingers-violation">Schrödinger’s Violation</h2>
<p>The irony? Responsible AI should empower people to understand <em>why</em> something is blocked, and to reflect on whether that’s appropriate. Instead, we get vague alerts like “may contain content.”</p>
<p>May. Might. Possibly.</p>
<p>It’s Schrödinger’s violation—simultaneously allowed and forbidden until a corporate filter decides otherwise.</p>
<h2 id="a-pacific-northwest-moral-compass">A Pacific Northwest Moral Compass</h2>
<p>And here’s the uncomfortable truth: I don’t think it should be Microsoft—or any US-based tech giant for that matter—that sets the global moral compass. These are companies built on shareholder value. Their definition of “responsibility” will always carry that DNA.</p>
<p>When I say “US-based,” I mean it literally: chances are high that a product manager in the Pacific Northwest decides what is “ok” or “not ok” for a global audience. That decision is then baked into tools millions of people use worldwide. But what fits the cultural context of Seattle doesn’t automatically translate into Delhi, Vienna, or São Paulo. And yet, in the global situation we’re in right now, it means exactly that: someone from the US defines the guardrails for everyone else.</p>
<p>And honestly—if we look at how they deal with multi-language, I’m not sure the multiverse of the globe is really reflected in their moral positioning.</p>
<h2 id="the-political-moment">The Political Moment</h2>
<p>And if we zoom out for a moment: with everything happening in the world politically right now, isn’t it even more dangerous to let a single source define the “ethics” of a technology? Our societies are polarized enough already. Different world regions struggle with different truths, different narratives, different wounds.</p>
<p>Handing over the role of global ethics referee to a single corporate entity—sitting safely in one corner of the world—feels naïve at best.</p>
<h2 id="pandoras-box">Pandora’s Box</h2>
<p>And then there’s Pandora’s box: why does a company like Microsoft even need to tell us that AI should be used “responsibly”?</p>
<p>Usually you see that kind of disclaimer on alcohol bottles, gambling apps, or payday loan ads—industries that know their products can do real harm if abused. Is that the category Microsoft wants AI to be in? And what’s their true intent? To genuinely protect users—or to shield themselves from accountability?</p>
<h2 id="where-responsibility-belongs">Where Responsibility Belongs</h2>
<p>The world needs more diverse and independent places to negotiate what “responsible” really means.</p>
<ul>
<li>Governments</li>
<li>Communities</li>
<li>Academia</li>
<li>Civil society</li>
</ul>
<p>The messy, sometimes slow, but much more legitimate conversations about what we value as humans.</p>
<p>Until then, we’ll continue to see red error boxes that <em>“might”</em> be violations—without ever telling us of what.</p>
<hr>
<sub>
*Note: AI was used to polish the English in this post, but not the arguments. As a non-native English speaker, I often feel my thoughts lose some impact when expressed in English compared to German. This way, the words are clearer — but the perspective remains mine. And honestly, that polishing is important: otherwise I risk coming across as <i>too opinionated as  German speakers often do</i> or <i>way too full of cynicism as the Austrian I am.</i>
</sub>
<br><br>
]]></content:encoded></item><item><title>Boost Developer Productivity with Copilot in Viva Connections</title><link>https://thomygoelles.com/boost-developer-productivity-with-copilot-in-viva-connections/</link><pubDate>Wed, 11 Dec 2024 16:45:13 +0000</pubDate><guid>https://thomygoelles.com/boost-developer-productivity-with-copilot-in-viva-connections/</guid><description>Boost Developer Productivity with Copilot in Viva Connections</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p><i>Copilot, Copilot a Copilot for everyone!</i></p>
<h2 id="tldr">TLDR</h2>
<p>This blog post shows how to use Copilot to create Adaptive Cards for Viva Connections. We ask Microsoft 365 Copilot for some ideas for cards of our dashboard and then take that to Github Copilot to generate the code for the Adaptive Cards. The results are discussed are the topic of the blog post. Results are really impressive but fear not, there is always room for improvement.</p>
<h2 id="introduction">Introduction</h2>
<p>Propably every developer has already seen it, that promise of the newest AI IDE that reduce your job to prompting. And if you have been around for a couple of moons, you just giggle when marketing comes up with the next silver bullet that solves all the problems out there. With this post we want to take a look at how the Micrsoft tooling at the moment holds up to their promises. Yes there are other tools out there, and I think I will write about my experiences with them in the future. But for now, let&rsquo;s focus on the Microsoft 365 Copilot and Github Copilot. Hint: ChatGPT &amp; Cursor AI, for the curious ones.</p>
<h2 id="the-brainstorming">The Brainstorming</h2>
<p>In my day to day I was part of a couple of discussions around frontline workers and how Viva Connections could be of use for those people not sitting infront of a screen 8 hours a day. My role in this process is most often a combination of consult our customers on what to use and what to expect from the tech stack. I&rsquo;m not a full time developer, let me make that very clear. So, if I need to create an Adaptive Card by myself, in the past, I would have opened up the Adaptive Card Designer and started from scratch or to be honest, I would have copied an existing card and modified it to my needs. But let&rsquo;s start with the brainstorming part of our scenario.</p>
<p>Here my prompt to Microsoft 365 Copilot:</p>
<blockquote>
</blockquote>
<p><i>I want to create a real world demo case for a retail customer using viva connections dashboard for their frontline workers. what would be your three top picks in terms of use cases for that scenario.</i></p>
<blockquote>
</blockquote>
<p><cite></cite></p>
<p>And here the results:</p>
<p><img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2024/12/connections-prompt02_hu_4f5cfe5af3d60f91.webp" srcset="https://thomygoelles.com/img/2024/12/connections-prompt02_hu_5e179b4f25a207f4.webp 480w, https://thomygoelles.com/img/2024/12/connections-prompt02_hu_4f5cfe5af3d60f91.webp 800w, https://thomygoelles.com/img/2024/12/connections-prompt02.webp 1010w" sizes="(max-width: 900px) 100vw, 860px" alt="Microsoft 365 Copilot result" width="1010" height="849" loading="lazy" decoding="async"></p>
<p>That already is a good start. Are there better scenarios out there, propably. But given that this result is based on a 36 word prompt, I think it is more than enough to get started. Of course we could have asked for more scenarios, or more detailed scenarios, but for the sake of our quick test, this is more than enough.</p>
<h2 id="the-coding">The Coding</h2>
<p>I used Visual Studio Code for this test. I find myself more and more drawn to it, even for the C# code I write. If you open VS Code and you have the Github Copilot extension installed, you can start typing your code and Copilot will give you suggestions. In our case, we want to create an Adaptive Card for the scenarios we got from Microsoft 365 Copilot.</p>
<p>Before we look at the prompt and at the results, a short hint: I used Claude 3.5 Sonnet for the prompt. I could of course tried the prompt with a different model, but I also started to play around with Cursor AI. There Claude 3.5 is the default model and I want to compare apples with apples. Yes there are differences in the application layer between Gihub Copilot and Cursor AI, but I wanted to at least have the same model for my tests. If there is a significant difference in the results, at least I can say that it is not the model that is the problem. Or the solution, depending on the outcome. (think positive, right?)</p>
<p><img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2024/12/connections-prompt03_hu_29cdaf1ae0f6c0cd.webp" srcset="https://thomygoelles.com/img/2024/12/connections-prompt03_hu_1785958a1cab56a2.webp 480w, https://thomygoelles.com/img/2024/12/connections-prompt03_hu_29cdaf1ae0f6c0cd.webp 800w, https://thomygoelles.com/img/2024/12/connections-prompt03.webp 869w" sizes="(max-width: 900px) 100vw, 860px" alt="Github Copilot model settings" width="869" height="855" loading="lazy" decoding="async"></p>
<p>Unfortunaly I&rsquo;m not smart enough to find the Github Copilot chat history in VS Code. So we have to go with the screenshots I took. But I think you get the idea. And my English &ldquo;is not the yellow of the egg&rdquo; anyways, so no need to copy and paste my errors.</p>
<p><img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2024/12/connections-prompt04_hu_764fc17825ae87ee.webp" srcset="https://thomygoelles.com/img/2024/12/connections-prompt04_hu_1695db16f03f73c2.webp 480w, https://thomygoelles.com/img/2024/12/connections-prompt04_hu_764fc17825ae87ee.webp 800w, https://thomygoelles.com/img/2024/12/connections-prompt04.webp 869w" sizes="(max-width: 900px) 100vw, 860px" alt="Github Copilot prompt" width="869" height="1133" loading="lazy" decoding="async"></p>
<p>The result of this prompt was a bit more than I expected. But look for yourself:</p>
<p><img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2024/12/connections-prompt05_hu_8bebdfca38eea35c.webp" srcset="https://thomygoelles.com/img/2024/12/connections-prompt05_hu_8bebdfca38eea35c.webp 480w, https://thomygoelles.com/img/2024/12/connections-prompt05.webp 682w" sizes="(max-width: 900px) 100vw, 860px" alt="Communications Card" width="682" height="630" loading="lazy" decoding="async"></p>
<p>I will add the code of one of the cards as a reference at the end of this post. But I think you get the idea. The code of course is not perfect, and it is basically only the UI. But for demo systems, or proof of concept scenarios, or early stage development, where you still struggle to be on the same page with all the parties involved, this is a great start to get the conversation going. This is not the final product, but it is a great start to get the feedback loop started. Yes, it brings it&rsquo;s own challenges with managing the expectations of the stakeholders, because this is not a wireframe and you have to make it very clear, that this is just a &ldquo;empty shell&rdquo;, not anyway close to the final product. But given the effort we put into this, I think it is of immens value.</p>
<p>And here the screenshot of the other cards, that were generated by Github Copilot based on the prompt we gave it:</p>
<p><img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2024/12/connections-prompt06_hu_b2930113a707e233.webp" srcset="https://thomygoelles.com/img/2024/12/connections-prompt06_hu_874fbaddfb03dc32.webp 480w, https://thomygoelles.com/img/2024/12/connections-prompt06_hu_b2930113a707e233.webp 800w, https://thomygoelles.com/img/2024/12/connections-prompt06.webp 852w" sizes="(max-width: 900px) 100vw, 860px" alt="Training Card" width="852" height="737" loading="lazy" decoding="async"></p>
<p>And the last one:</p>
<p><img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2024/12/connections-prompt07_hu_9a0871d8fedabc3b.webp" srcset="https://thomygoelles.com/img/2024/12/connections-prompt07_hu_9a0871d8fedabc3b.webp 480w, https://thomygoelles.com/img/2024/12/connections-prompt07.webp 708w" sizes="(max-width: 900px) 100vw, 860px" alt="Shifts Card" width="708" height="692" loading="lazy" decoding="async"></p>
<h2 id="the-honest-feedback">The honest feedback</h2>
<p>To make it absolut transparent here the hickups that happend during the creation of the cards. The first card was created with a tiny issue. The url value of the used image had like 50 spaces in there. So that card needed some minimal adjustment. For cards number 2 and 3 I needed to regenerate the answer from Github Copilot. Both times the tool stopped at the first run somewhere in the middle. I just needed to hit the refresh button and it worked. My guess, this is a scaling problem of the model in the background. Felt like that endpoint got hammered the time I played around with it. And let&rsquo;s not forget, the sonnet model is still in preview. So I&rsquo;m not mad about that. I&rsquo;m happy that I got the results I got. Other then that, no further issues. And the overall time spent on this was less then 15 minutes for all the three cards.</p>
<p>Would I have been able to create the same cards by myself? Of course. But never ever in that short amount of time. And I think that is the point of the whole exercise. So far my findings, and I&rsquo;m looking forward to the next steps. But that is a topic for another post. Thx!</p>
<h2 id="the-code">The code</h2>
<pre tabindex="0"><code>{
    &#34;type&#34;: &#34;AdaptiveCard&#34;,
    &#34;version&#34;: &#34;1.5&#34;,
    &#34;body&#34;: [
        {
            &#34;type&#34;: &#34;Container&#34;,
            &#34;style&#34;: &#34;emphasis&#34;,
            &#34;items&#34;: [
                {
                    &#34;type&#34;: &#34;ColumnSet&#34;,
                    &#34;columns&#34;: [
                        {
                            &#34;type&#34;: &#34;Column&#34;,
                            &#34;width&#34;: &#34;stretch&#34;,
                            &#34;items&#34;: [
                                {
                                    &#34;type&#34;: &#34;TextBlock&#34;,
                                    &#34;text&#34;: &#34;Store #1234 - Floor Communication&#34;,
                                    &#34;weight&#34;: &#34;Bolder&#34;,
                                    &#34;size&#34;: &#34;Large&#34;
                                },
                                {
                                    &#34;type&#34;: &#34;TextBlock&#34;,
                                    &#34;text&#34;: &#34;Electronics Department&#34;,
                                    &#34;isSubtle&#34;: true,
                                    &#34;spacing&#34;: &#34;None&#34;
                                }
                            ]
                        }
                    ]
                }
            ]
        },
        {
            &#34;type&#34;: &#34;Container&#34;,
            &#34;items&#34;: [
                {
                    &#34;type&#34;: &#34;TextBlock&#34;,
                    &#34;text&#34;: &#34;Quick Connect with Supervisors&#34;,
                    &#34;weight&#34;: &#34;Bolder&#34;,
                    &#34;size&#34;: &#34;Medium&#34;
                },
                {
                    &#34;type&#34;: &#34;ColumnSet&#34;,
                    &#34;columns&#34;: [
                        {
                            &#34;type&#34;: &#34;Column&#34;,
                            &#34;width&#34;: &#34;auto&#34;,
                            &#34;items&#34;: [
                                {
                                    &#34;type&#34;: &#34;Image&#34;,
                                    &#34;url&#34;: &#34;https://cdn-icons-png.flaticon.com/512/1154/1154448.png&#34;,
                                    &#34;size&#34;: &#34;Small&#34;,
                                    &#34;style&#34;: &#34;Person&#34;
                                }
                            ]
                        },
                        {
                            &#34;type&#34;: &#34;Column&#34;,
                            &#34;width&#34;: &#34;stretch&#34;,
                            &#34;items&#34;: [
                                {
                                    &#34;type&#34;: &#34;TextBlock&#34;,
                                    &#34;text&#34;: &#34;Sarah Johnson&#34;,
                                    &#34;weight&#34;: &#34;Bolder&#34;
                                },
                                {
                                    &#34;type&#34;: &#34;TextBlock&#34;,
                                    &#34;text&#34;: &#34;Floor Manager&#34;,
                                    &#34;isSubtle&#34;: true,
                                    &#34;spacing&#34;: &#34;None&#34;
                                }
                            ]
                        },
                        {
                            &#34;type&#34;: &#34;Column&#34;,
                            &#34;width&#34;: &#34;auto&#34;,
                            &#34;items&#34;: [
                                {
                                    &#34;type&#34;: &#34;ActionSet&#34;,
                                    &#34;actions&#34;: [
                                        {
                                            &#34;type&#34;: &#34;Action.Submit&#34;,
                                            &#34;title&#34;: &#34;Message&#34;,
                                            &#34;style&#34;: &#34;positive&#34;
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        },
        {
            &#34;type&#34;: &#34;Container&#34;,
            &#34;items&#34;: [
                {
                    &#34;type&#34;: &#34;TextBlock&#34;,
                    &#34;text&#34;: &#34;New Message&#34;,
                    &#34;weight&#34;: &#34;Bolder&#34;,
                    &#34;size&#34;: &#34;Medium&#34;
                },
                {
                    &#34;type&#34;: &#34;Input.Text&#34;,
                    &#34;placeholder&#34;: &#34;Type your message here...&#34;,
                    &#34;isMultiline&#34;: true,
                    &#34;id&#34;: &#34;messageInput&#34;
                },
                {
                    &#34;type&#34;: &#34;ColumnSet&#34;,
                    &#34;columns&#34;: [
                        {
                            &#34;type&#34;: &#34;Column&#34;,
                            &#34;width&#34;: &#34;auto&#34;,
                            &#34;items&#34;: [
                                {
                                    &#34;type&#34;: &#34;ActionSet&#34;,
                                    &#34;actions&#34;: [
                                        {
                                            &#34;type&#34;: &#34;Action.Submit&#34;,
                                            &#34;title&#34;: &#34;🎤 Record Voice&#34;,
                                            &#34;style&#34;: &#34;default&#34;
                                        }
                                    ]
                                }
                            ]
                        },
                        {
                            &#34;type&#34;: &#34;Column&#34;,
                            &#34;width&#34;: &#34;stretch&#34;,
                            &#34;items&#34;: [
                                {
                                    &#34;type&#34;: &#34;ActionSet&#34;,
                                    &#34;actions&#34;: [
                                        {
                                            &#34;type&#34;: &#34;Action.Submit&#34;,
                                            &#34;title&#34;: &#34;Send Message&#34;,
                                            &#34;style&#34;: &#34;positive&#34;
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        },
        {
            &#34;type&#34;: &#34;Container&#34;,
            &#34;items&#34;: [
                {
                    &#34;type&#34;: &#34;ActionSet&#34;,
                    &#34;actions&#34;: [
                        {
                            &#34;type&#34;: &#34;Action.Submit&#34;,
                            &#34;title&#34;: &#34;📞 Emergency Contact&#34;,
                            &#34;style&#34;: &#34;destructive&#34;
                        },
                        {
                            &#34;type&#34;: &#34;Action.Submit&#34;,
                            &#34;title&#34;: &#34;📢 Broadcast Message&#34;,
                            &#34;style&#34;: &#34;default&#34;
                        }
                    ]
                }
            ]
        }
    ],
    &#34;$schema&#34;: &#34;http://adaptivecards.io/schemas/adaptive-card.json&#34;
}
</code></pre>]]></content:encoded></item><item><title>Copilot help - Update PnP Powershell</title><link>https://thomygoelles.com/copilot-help-update-pnp-powershell/</link><pubDate>Tue, 10 Dec 2024 16:45:13 +0000</pubDate><guid>https://thomygoelles.com/copilot-help-update-pnp-powershell/</guid><description>How to update PnP PowerShell with Copilot's help</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p><i>How to update PnP PowerShell with Copilot&rsquo;s help</i></p>
<p>Yes, I&rsquo;m lazy and I&rsquo;m not using PowerShell every single day.
Today I needed to update my PnP PowerShell module, and I thought I&rsquo;d use Copilot to help me with that. Here&rsquo;s how it went:</p>
<p>Let&rsquo;s start with the initial prompt:
<img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2024/12/copilot-pnp-update01_hu_b7d8ac69be2e1cb3.webp" srcset="https://thomygoelles.com/img/2024/12/copilot-pnp-update01_hu_c646f23bbb3e3919.webp 480w, https://thomygoelles.com/img/2024/12/copilot-pnp-update01_hu_b7d8ac69be2e1cb3.webp 800w, https://thomygoelles.com/img/2024/12/copilot-pnp-update01.webp 1206w" sizes="(max-width: 900px) 100vw, 860px" alt="Initial prompt" width="1206" height="589" loading="lazy" decoding="async"></p>
<p>Ok. That worked fine, but of course there is something else. I don&rsquo;t know how, but I always end up having multiple versions of the PnP PowerShell module installed. So I asked Copilot to help me with that as well:</p>
<p><img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2024/12/copilot-pnp-update02_hu_bfc19bfb37291902.webp" srcset="https://thomygoelles.com/img/2024/12/copilot-pnp-update02_hu_27f79f5b9b2e95c4.webp 480w, https://thomygoelles.com/img/2024/12/copilot-pnp-update02_hu_bfc19bfb37291902.webp 800w, https://thomygoelles.com/img/2024/12/copilot-pnp-update02.webp 977w" sizes="(max-width: 900px) 100vw, 860px" alt="Multiple versions" width="977" height="396" loading="lazy" decoding="async"></p>
<p>Well, that is not what I wanted. If we follow this path, we would end up with having not a single version of the PnP PowerShell module installed. Yes, we could do that and then install the latest version, but that&rsquo;s not what I want. I want to keep the latest version and remove all the others. So I asked Copilot to help me with that as well:</p>
<p><img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2024/12/copilot-pnp-update03_hu_d5816deead66a28f.webp" srcset="https://thomygoelles.com/img/2024/12/copilot-pnp-update03_hu_b1ea987b3d0473d2.webp 480w, https://thomygoelles.com/img/2024/12/copilot-pnp-update03_hu_d5816deead66a28f.webp 800w, https://thomygoelles.com/img/2024/12/copilot-pnp-update03.webp 963w" sizes="(max-width: 900px) 100vw, 860px" alt="Remove all but the latest version" width="963" height="614" loading="lazy" decoding="async"></p>
<p>That&rsquo;s better. Now I can remove all the other versions and keep the latest one. Thanks, Copilot!</p>
<p>Is this a revolution in our work? No, it&rsquo;s not. But it&rsquo;s a nice way to get some help and it shows the difference between getting things done with Copilot or AI in general compared to what we all used to do: google it, read some blog posts, try some things out, and then maybe get it done.</p>
]]></content:encoded></item><item><title>How to fix Pi-hole blocking Azure OpenAI calls</title><link>https://thomygoelles.com/how-to-fix-pi-hole-blocking-azure-openai-calls/</link><pubDate>Thu, 22 Feb 2024 07:59:41 +0000</pubDate><guid>https://thomygoelles.com/how-to-fix-pi-hole-blocking-azure-openai-calls/</guid><description> If you're using Pi-hole for ad-blocking and find that it's preventing your access to Azure OpenAI Service, the solution is to whitelist Azure OpenAI domains in Pi-hole's settings.</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p><i>A simple solution for a frustrating bug hunt</i></p>
<h2 id="tldr">TLDR;</h2>
<p>If you’re using Pi-hole for ad-blocking and find that it’s preventing your access to Azure OpenAI Service, the solution is to whitelist Azure OpenAI domains in Pi-hole’s settings. This issue arises because Pi-hole, which blocks ad-serving domains at the DNS level, can also block legitimate services like Azure OpenAI. The fix involves accessing Pi-hole’s admin interface and adding openai.azure.com to the whitelist, ensuring uninterrupted access to Azure OpenAI for your services, which relies on Azure OpenAI for features like Semantic Kernel for natural language processing. Remember to share this workaround with anyone facing similar issues!</p>
<h2 id="introduction">Introduction</h2>
<p>If you use Pi-hole to block ads and trackers on your network, you might have encountered a problem when calling the Azure OpenAI Service. Pi-hole is a great tool that can improve your browsing experience and privacy, but it can also interfere with some legitimate services that use the same domains as some of the blocked ads. I’m using the settings published by <a href="https://jussiroine.com/2021/07/goodbye-telemetry-and-ads-running-pi-hole-in-a-home-network/" target="_blank" rel="noopener">Jussi Roine</a>, just in case you also want to start your ad-free browsing experience.</p>
<h2 id="how-does-pi-hole-work">How does Pi-Hole work?</h2>
<p>To quote their documentation:</p>
<blockquote>
</blockquote>
<p><i>Pi-Hole blocks requests made to ad domains from your network, before the requests ever leave your network. Your client devices can&rsquo;t connect to domains that host ads, but can connect to domains that host useful content.</i></p>
<blockquote>
</blockquote>
<p><cite><a href="http://www.raspberrypi.com" target="_blank" rel="noopener">http://www.raspberrypi.com</a></cite></p>
<p>Pi-Hole is a DNS sinkhole that protects your devices from unwanted content without installing any client-side software. It is a network-wide ad blocker that works by intercepting DNS requests and filtering out known ad-serving domains. This means that when you try to access a website that contains ads, Pi-Hole will block the ad-serving domains and prevent the ads from being displayed on your device.</p>
<p><img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2024/02/dns_blocked-1024x209_hu_21bfa94d66a4df63.webp" srcset="https://thomygoelles.com/img/2024/02/dns_blocked-1024x209_hu_d7fadd9ba9a7192c.webp 480w, https://thomygoelles.com/img/2024/02/dns_blocked-1024x209_hu_21bfa94d66a4df63.webp 800w, https://thomygoelles.com/img/2024/02/dns_blocked-1024x209.webp 1024w" sizes="(max-width: 900px) 100vw, 860px" alt="Visual Studio Output" width="1024" height="209" loading="lazy" decoding="async"></p>
<h2 id="communitybotforonedrive-v2">CommunityBotForOneDrive v2</h2>
<p>One of the cloud services we use in the v2 of our Community Bot for OneDrive project is Azure OpenAI. Over the last couple of weeks, we moved the old project to a new code base. We decoupled things and removed the monolithic bot approach.</p>
<p>We also changed the translation feature and introduced Semantic Kernel to our solution. Semantic Kernel is a tool that allows you to interact with Azure OpenAI using natural language queries and commands, such as “generate a summary of this article” or “find me a picture of a cat”. We will use it moving forward for our multi-language approach. We struggled with existing translation services across multiple cloud vendors because our base information is formatted in Markdown, and all “pre-gpt” services gave us a proper headache with that format. That changed with gpt3.5, the base model we are currently experimenting with.</p>
<p>To make things future-proof and allow us to play with different models, cloud ones and local ones, we used Ollama and Semantic Kernel as our AI stack.</p>
<p><img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2024/02/botarchitecture_hu_bdf70c2063323c7a.webp" srcset="https://thomygoelles.com/img/2024/02/botarchitecture_hu_c826104029b70fe5.webp 480w, https://thomygoelles.com/img/2024/02/botarchitecture_hu_bdf70c2063323c7a.webp 800w, https://thomygoelles.com/img/2024/02/botarchitecture.webp 1539w" sizes="(max-width: 900px) 100vw, 860px" alt="bot architecture" width="1539" height="290" loading="lazy" decoding="async"></p>
<h2 id="the-problem">The Problem</h2>
<p>However, if Pi-hole is enabled on your network, you might find that Semantic Kernel cannot connect to the Azure OpenAI Service and returns an error message instead.</p>
<p>Finding the culprit here took quite some time. The code base worked for weeks already and stopped connecting to the Azure OpenAI Serivce suddenly last week. We still got the same error after looking at the code and moving a couple of libraries to their latest version. We tried to change the way we reach out to Azure OpenAI Service, but no change.</p>
<blockquote>
</blockquote>
<p><i>System.AggregateException: &lsquo;Retry failed after 4 tries. Retry settings can be adjusted in ClientOptions.Retry or by configuring a custom retry policy in ClientOptions.RetryPolicy. (The requested name is valid, but no data of the requested type was found. (XYZ.openai.azure.com:443)) (The requested name is valid, but no data of the requested type was found. (XYZ.openai.azure.com:443)) (The requested name is valid, but no data of the requested type was found. (XYZ.openai.azure.com:443)) (The requested name is valid, but no data of the requested type was found. (XYZ.openai.azure.com:443))</i></p>
<blockquote>
</blockquote>
<p><cite>exception details</cite></p>
<p>Maybe it’s me, but that didn’t help when reading it for the first time. After googling the exception for quite some time, a forum post mentioned that the exception is based on DNS issues. That rang a bell. I’ve been running Pi-Hole for years, but I updated the Raspberry Pi host and its software stack only a few days ago. I went to the admin page, turned the ad blocking off for 5 minutes and voilà, our test console connected to the cloud and worked.</p>
<p>Here is a screenshot of the Pi-Hole dashboard listening to the DNS requests of the test console:</p>
<p><img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2024/02/piholedashboard_hu_58bcfd1c01344c51.webp" srcset="https://thomygoelles.com/img/2024/02/piholedashboard_hu_dfd0bf7caebcd55f.webp 480w, https://thomygoelles.com/img/2024/02/piholedashboard_hu_58bcfd1c01344c51.webp 800w, https://thomygoelles.com/img/2024/02/piholedashboard.webp 1242w" sizes="(max-width: 900px) 100vw, 860px" alt="bot architecture" width="1242" height="926" loading="lazy" decoding="async"></p>
<h2 id="the-long-term-solution">The long term solution</h2>
<p>Fortunately, there is a simple solution to this problem. You can whitelist the domains that Azure OpenAI uses to bypass Pi-hole’s blocking and allow Semantic Kernel to work as intended. Whitelisting is a process of adding exceptions to Pi-hole’s filtering rules so that certain domains are always allowed to connect, regardless of whether they are on the blocklist. To whitelist the domains that Azure OpenAI uses, you need to follow these steps:</p>
<ul>
<li>Log in to your Pi-hole admin interface, which is usually located at <a href="http://pi.hole/admin" target="_blank" rel="noopener">http://pi.hole/admin</a> or http://your-Pi-hole-IP-address/admin.</li>
<li>Click on the &ldquo;Domains&rdquo; tab on the left sidebar.</li>
<li>Enter openai.azure.com in the domain field</li>
<li>Click on “Add domain as wildcard”</li>
<li>Click on the &ldquo;Add to whitelist&rdquo; button.</li>
</ul>
<p>You can also add your specific FQDN of your endpoint, but as I will use different enpoints in the future it felt easier to just add the whole subdomain to the whitelist.</p>
<p>That’s it! You have successfully whitelisted the Azure OpenAI Service&rsquo;s domains and fixed the problem with Pi-hole blocking your calls.</p>
<p><img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2024/02/piholewhitelist_hu_789039050a797e99.webp" srcset="https://thomygoelles.com/img/2024/02/piholewhitelist_hu_fba26a4753a9f929.webp 480w, https://thomygoelles.com/img/2024/02/piholewhitelist_hu_789039050a797e99.webp 800w, https://thomygoelles.com/img/2024/02/piholewhitelist.webp 1247w" sizes="(max-width: 900px) 100vw, 860px" alt="bot architecture" width="1247" height="552" loading="lazy" decoding="async"></p>
<p>I hope this post was helpful and informative. If you liked it, please share it with your friends and colleagues who might also use Pi-hole and the Azure OpenAI Service. If you have any questions or feedback, please leave me a DM or a tweet. Thank you for reading!</p>
]]></content:encoded></item><item><title>Exploits and possible LLM attacks plus M365 Copilot</title><link>https://thomygoelles.com/exploits-and-possible-llm-attacks-plus-m365-copilot/</link><pubDate>Sun, 04 Feb 2024 12:26:38 +0000</pubDate><guid>https://thomygoelles.com/exploits-and-possible-llm-attacks-plus-m365-copilot/</guid><description>There is a German idiom called “in between the years”, meaning the timeframe from Christmas to New Year’s eve. It is a time of reflection and of course family. This year it was also the time of the 37th Chaos Communication Congress (37C3) in Hamburg. The 37C3 was a hybrid event, meaning it was a physical event in Hamburg and a virtual event on the internet.
Real-world exploits and mitigations in LLM applications One of the talks that I found very interesting was the talk about Real-world exploits and mitigations in LLM applications (37c3). The talk was about a new type of attacks that are based on LLMs by Johann Rehberger. Johann is currently a Red Team Director at Electronic Arts and worked as a Principal Security Engineer Manager for years at Microsoft. Red Teams are tasked with simulating attacks on a company’s systems to test the effectiveness of its security measures. So it’s very interessting so see his thoughts on this topic.</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>There is a German idiom called &ldquo;in between the years&rdquo;, meaning the timeframe from Christmas to New Year&rsquo;s eve. It is a time of reflection and of course family. This year it was also the time of the <a href="https://events.ccc.de/congress/2023/infos/index.html" target="_blank" rel="noopener">37th Chaos Communication Congress (37C3) in Hamburg</a>. The 37C3 was a hybrid event, meaning it was a physical event in Hamburg and a virtual event on the internet.</p>
<h2 id="real-world-exploits-and-mitigations-in-llm-applications">Real-world exploits and mitigations in LLM applications</h2>
<p>One of the talks that I found very interesting was the talk about <a href="https://www.youtube.com/watch?v=qyTSOSDEC5M" target="_blank" rel="noopener">Real-world exploits and mitigations in LLM applications (37c3)</a>. The talk was about a new type of attacks that are based on LLMs by <a href="https://www.linkedin.com/in/johannrehberger/" target="_blank" rel="noopener">Johann Rehberger</a>. Johann is currently a Red Team Director at Electronic Arts and worked as a Principal Security Engineer Manager for years at Microsoft. Red Teams are tasked with simulating attacks on a company&rsquo;s systems to test the effectiveness of its security measures. So it&rsquo;s very interessting so see his thoughts on this topic.</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/qyTSOSDEC5M?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<h2 id="content-of-the-talk">Content of the talk</h2>
<p>The talk was about a new type of attacks that are based on LLMs. LLM stands for &ldquo;Large Language Models&rdquo; and is a term that is used to describe models like GPT-3, which is a language model that is trained on a large corpus of text data. The talk was about how these models can be used to generate exploits for real-world applications. This can be as easy as just asking the model to give away information it shouldn&rsquo;t but can get a bit more tricky if you want to generate payloads that exfiltrate data from a chat or user.</p>
<p>Johann shows hoe easy it was in 2023 to just add some additional text to a html page and then use a LLM to generate a summary of this page and inject your paylod into the instruction set of the LLM. He documented how the different LLMs adopted to different payloads but one is left behind with a slight reminder of the very early years of the internet, where passwords were just hidden in the source code of the website.</p>
<h2 id="microsoft365-copilot">Microsoft365 Copilot</h2>
<p>I already tried a couple of his ideas with Microsoft365 Copilot, but this is a different story. I will write about this in a different post. As I wasn&rsquo;t able to reproduce his findings with the current version of M365 Copilot, I will not go into detail here.</p>
<p>Nevertheless I think it is very important to be aware of these new types of attacks and to think about how to mitigate them. I think that the talk is a great starting point for this discussion and I hope that it will lead to more awareness in this area. I also hope that it will lead to better security measures for LLM applications and again the needed awareness for the developers and the companies that use these models.</p>
]]></content:encoded></item><item><title>TeamsFx: Visual Studio project name too long for manifest validation</title><link>https://thomygoelles.com/teamsfx-visual-studio-project-name-too-long-for-manifest-validation/</link><pubDate>Sat, 03 Feb 2024 15:26:38 +0000</pubDate><guid>https://thomygoelles.com/teamsfx-visual-studio-project-name-too-long-for-manifest-validation/</guid><description>When you are working with the Teams Toolkit for Visual Studio and you are using a long project name, you might run into an issue when you want to package your application for deployment. The error message is helpful if you already have some experience with the Teams manifest, but it might be confusing for new developers. The error message we are discussing today is: (×) Error: /name/short must NOT have more than 30 characters</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>When you are working with the Teams Toolkit for Visual Studio and you are using a long project name, you might run into an issue when you want to package your application for deployment. The error message is helpful if you already have some experience with the Teams manifest, but it might be confusing for new developers. The error message we are discussing today is: (×) Error: /name/short must NOT have more than 30 characters</p>
<h2 id="why-is-that-error-happening">Why is that error happening?</h2>
<p>The error message is telling you that the project name is too long for the Teams manifest. The Teams manifest is a JSON file that describes the capabilities of your Teams application. It is used by the Teams client to understand what your application can do and how it should be displayed. The manifest is part of the package that you upload to the Teams App Store or that you sideload into your Teams client. The manifest file has a maximum length of 30 characters for the <code>name/short</code> property. If your project name is longer than 30 characters, the Teams Toolkit will not be able to validate your manifest file and you will see this error message.</p>
<p><img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2024/02/TeamsFXNameShortError_hu_39d73e05f2ab16a8.webp" srcset="https://thomygoelles.com/img/2024/02/TeamsFXNameShortError_hu_39d73e05f2ab16a8.webp 480w, https://thomygoelles.com/img/2024/02/TeamsFXNameShortError.webp 646w" sizes="(max-width: 900px) 100vw, 860px" alt="Visual Studio Output" width="646" height="995" loading="lazy" decoding="async"></p>
<h2 id="why-is-that-even-bothering-us">Why is that even bothering us?</h2>
<p>If you just create a new project in Visual Studio and you are not aware of the Teams manifest, you might not even notice that your project name is too long. If you create a solution and the teams capability is just a part of your feature set you might need to follow some kind of naming convention for your projects. In my case I like to split projects and name them like <code>Solution.Project</code> or <code>Solution.Feature</code> or someting along those lines. This is a common pattern in the dotnet world and it is also a good practice to keep your projects organized and maintainable. My current project name is <code>CommunityBotForOneDrive.TeamsBot</code>. In my solution there is a project called <code>CommunityBotForOneDrive.Core</code> and a project called <code>CommunityBotForOneDrive.TestConsole</code>.</p>
<p>The Teams Toolkit has an initial check for the project name and will show you an error if the project name is too long already at the create new project dialog. But of course, you can just right click the project and rename it to something longer than 30 characters. This is where the error message will show up the next time you want to package your application for deployment or click the Prepare Teams App Dependecies button in the Teams Toolkit menu.</p>
<h2 id="how-to-fix-it">How to fix it?</h2>
<p>The fix is simple: Just rename your project to something shorter in the manifest file. The Visual Studio project name is not hardwired to the teams manifest file, it is only used during the scaffolding of your project. You can change the project name in Visual Studio afterwards to something that follows your naming convention but you need to adhere to the 30 character limit for the <code>name/short</code> property in the Teams manifest file.</p>
<p><img style="display:block;margin:0.7rem auto" src="https://thomygoelles.com/img/2024/02/TeamsFXNameShortErrorManifest_hu_59d5c60738fa7856.webp" srcset="https://thomygoelles.com/img/2024/02/TeamsFXNameShortErrorManifest_hu_59d5c60738fa7856.webp 480w, https://thomygoelles.com/img/2024/02/TeamsFXNameShortErrorManifest.webp 733w" sizes="(max-width: 900px) 100vw, 860px" alt="Manifest file" width="733" height="98" loading="lazy" decoding="async"></p>
<p>So it is more a question of sequence to follow if you never want to see that error:</p>
<ol>
<li>Create a new project in Visual Studio and make sure the project name is shorter than 30 characters.</li>
<li>Change the project name in Visual Studio to something that follows your naming convention.</li>
<li>Change the <code>name/short</code> property in the Teams manifest file to something with less than 30 characters.</li>
<li>Continue with your next step in the Teams FX development process, most probably click the Prepare Teams App Dependencies button in the Teams Toolkit menu.</li>
</ol>
<p>That&rsquo;s of course an ideal scenario and it might not always play out that way. So if you just setup your project and prepare all the dependencies and the base structure and infrastrucutre you might forget about that <code>name/short</code> limit in the manifest. But now you know what to do if you see that error message. Just go back to the manifest file and change the <code>name/short</code> property to something shorter and you are good to go.</p>
<p>If you need more help with the Teams Toolkit, you can check out the <a href="https://learn.microsoft.com/en-us/microsoftteams/platform/toolkit/toolkit-v4/debug-local-vs?pivots=visual-studio-v17-7" target="_blank" rel="noopener">Debug your Teams app locally using Visual Studio</a> guide, the <a href="https://aka.ms/teamsfx-docs" target="_blank" rel="noopener">official Teams Toolkit documentation</a> or you can ask your question in the <a href="https://github.com/OfficeDev/TeamsFx/issues" target="_blank" rel="noopener">Github repo of Teams Toolkit</a>.</p>
]]></content:encoded></item><item><title>TeamsFx: How to use Microsoft Graph SDK v5 already</title><link>https://thomygoelles.com/teamsfx-graph-sdk-v5/</link><pubDate>Wed, 22 Mar 2023 12:58:35 +0000</pubDate><guid>https://thomygoelles.com/teamsfx-graph-sdk-v5/</guid><description>Looking at the latest release from the Microsoft Graph SDK team an their version 5.0.0 for dotnet three weeks ago, I was wondering how to use it with TeamsFx. I was not able to find any documentation on this topic, so I decided to write this post to share my findings.
What is the Microsoft Graph SDK v5 for dotnet? Using the Microsoft Graph .NET SDK, you can take advantage of a fluent API and models that support retry handling, secure redirects, batching requests, large file management, and many more capabilities. This allows you to focus on what really matters in your Teams application instead of having to worry about the details of the HTTP requests and responses.</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Looking at the latest release from the Microsoft Graph SDK team an their version 5.0.0 for dotnet three weeks ago, I was wondering how to use it with TeamsFx. I was not able to find any documentation on this topic, so I decided to write this post to share my findings.</p>
<h2 id="what-is-the-microsoft-graph-sdk-v5-for-dotnet">What is the Microsoft Graph SDK v5 for dotnet?</h2>
<p>Using the Microsoft Graph .NET SDK, you can take advantage of a fluent API and models that support retry handling, secure redirects, batching requests, large file management, and many more capabilities. This allows you to focus on what really matters in your Teams application instead of having to worry about the details of the HTTP requests and responses.</p>
<p>With version 5 of the SDK, Microsoft added a number of new features, including simplified method changing and a way to combine both v1.0 and beta APIs for granular early access to new features. (Source: <a href="https://devblogs.microsoft.com/microsoft365dev/write-simpler-code-with-the-new-microsoft-graph-net-sdk-v5/" target="_blank" rel="noopener">Write simpler code with the new Microsoft Graph .NET SDK v5</a></p>
<h2 id="what-changed-in-v5-for-teamsfx">What changed in v5 for TeamsFx?</h2>
<p>There are some breaking changes including types in new namespaces/usings and also some changes around authentication. Especially the authentication changes are important for TeamsFx, as there is a new way to authenticate against the Microsoft Graph API. The current TeamsFx version, as well as the preview version are using the MsGraphAuthProvider to create a GraphServiceClient.</p>
<p>Let&rsquo;s focus on line 3 of the current version of for a minute.</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">0
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="background-color:#3c3d38"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span></span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">_logger</span>.<span style="color:#a6e22e">LogInformation</span>(<span style="color:#e6db74">&#34;Create Microsoft Graph Client&#34;</span>);
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">logger</span> <span style="color:#960050;background-color:#1e0010">??</span>=<span style="color:#a6e22e">_authLogger</span>;
</span></span><span style="display:flex; background-color:#3c3d38"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">authProvider</span> = <span style="color:#a6e22e">new</span> <span style="color:#a6e22e">MsGraphAuthProvider</span>(<span style="color:#a6e22e">credential</span>,<span style="color:#a6e22e">scopes</span>, <span style="color:#a6e22e">logger</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">client</span> = <span style="color:#a6e22e">new</span> <span style="color:#a6e22e">GraphServiceClient</span>(<span style="color:#a6e22e">authProvider</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">return</span> <span style="color:#a6e22e">client</span>;</span></span></code></pre></td></tr></table>
</div>
</div>
<p>This code block shows parts of the constructor of the MsGraphAuthProvider class. The constructor takes three parameters: credential, scopes and logger. The credential is the ITokenCredential object, the scopes are the scopes for the Microsoft Graph API and the logger is the ILogger object. The MsGraphAuthProvider class is using the credential and the scopes to create a token for the Microsoft Graph API. The token is then used to create the GraphServiceClient object.</p>
<p>The new version of the Microsoft Graph SDK v5 allows us to direclty use the TokenCredential object to create the GraphServiceClient object. This means, that we can remove the MsGraphAuthProvider class and instead use the TokenCredential object to create the GraphServiceClient when we need it.</p>
<p>Through dependency injection we can use the TokenCredetial from TeamsFx in our own code and don&rsquo;t need to create an authentication provider for the Microsoft Graph API.</p>
<p>In our razor components we could reduce the call to get a new GraphServiceClient object to the following code:</p>
<div class="highlight"><div style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;">
<table style="border-spacing:0;padding:0;margin:0;border:0;"><tr><td style="vertical-align:top;padding:0;margin:0;border:0;">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">0
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
</span><span style="background-color:#3c3d38"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
</span></span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
</span><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">5
</span></code></pre></td>
<td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
<pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">private</span> <span style="color:#a6e22e">GraphServiceClient</span> <span style="color:#a6e22e">GetGraphServiceClient</span>()
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex; background-color:#3c3d38"><span>    <span style="color:#75715e">//var msGraphAuthProvider = new MsGraphAuthProvider(teamsUserCredential, _scope);</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">client</span> = <span style="color:#a6e22e">new</span> <span style="color:#a6e22e">GraphServiceClient</span>(<span style="color:#a6e22e">teamsUserCredential</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">client</span>;
</span></span><span style="display:flex;"><span>}  </span></span></code></pre></td></tr></table>
</div>
</div>
<p>That&rsquo;s not a big change in terms of code in our components, but it allows us to get rid of the MsGraphAuthProvider at all. As we all know, less code is better code and fewer lines of code are easier to maintain and test.</p>
<h2 id="how-to-update-teamsfx">How to update TeamsFx?</h2>
<p>At the moment there is no official support yet for using the Microsoft Graph SDK v5 with TeamsFx. But there is always the option to clone the latest version of TeamsFx and implement the changes yourself. That&rsquo;s what we did in our Microsoft Graph hack toghether sample and you can check out a working version of the modified TeamsFx version in the <a href="https://github.com/selectedtech/M365AIMate/tree/TeamsFxGraphSDK5" target="_blank" rel="noopener">TeamsFxGraphSDK5 branch of our sample</a> on GitHub. We still kept the file MSGraphAuthProvider.cs in the project, but everything in there is commented out.</p>
<blockquote>
</blockquote>
<h1 id="only-because-you-can-doesnt-mean-you-should">Only because you can, doesn&rsquo;t mean you should</h1>
<blockquote>
</blockquote>
<p><cite>An old SharePoint person</cite></p>
<p>Read the quote above twice before you start using the approach of this blog post in your production code. This is just a proof of concept and not meant to be used in production. We can assume that TeamsFx will support the Microsoft Graph SDK v5 in the future, and for your work projects the smart move is to wait for the official support.</p>
]]></content:encoded></item><item><title>My approach to public speaking</title><link>https://thomygoelles.com/my-approach-to-public-speaking/</link><pubDate>Thu, 03 Mar 2022 06:00:00 +0000</pubDate><guid>https://thomygoelles.com/my-approach-to-public-speaking/</guid><description>During the last weeks, I got some questions about public speaking, session preparation, and my approach.
Today I’m again preparing a session, and I thought it might be a good time to write down what I’m doing and why quickly.
I’m cheating Yes, you read that correctly. I’m cheating. I’m watching countless hours of other people talking in public on YouTube. One of my favourite books is called “Steal like an artist”, and that’s what I’m doing. I’m trying to read/watch/retrieve as much information about speaking in public as possible.</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>During the last weeks, I got some questions about public speaking, session preparation, and my approach.</p>
<p>Today I&rsquo;m again preparing a session, and I thought it might be a good time to write down what I&rsquo;m doing and why quickly.</p>
<h2 id="im-cheating">I&rsquo;m cheating</h2>
<p>Yes, you read that correctly. I&rsquo;m cheating. I&rsquo;m watching countless hours of other people talking in public on YouTube. One of my favourite books is called &ldquo;Steal like an artist&rdquo;, and that&rsquo;s what I&rsquo;m doing. I&rsquo;m trying to read/watch/retrieve as much information about speaking in public as possible.</p>
<h2 id="i-got-training">I got training</h2>
<p>Not only online, but I attended courses focusing on my voice, speed of talking, stage performance and pronunciation. Remember, I need to speak in public in my own and in a foreign language regularly. I can only recommend getting a good base in your tongue before adding a second one.</p>
<h2 id="i-use-tools">I use tools</h2>
<p>The picture shows parts of a current project where the tools of Sefirot Independent Publisher help me a lot. I&rsquo;m using their &ldquo;Cicero&rdquo; framework for public speaking at almost all crucial presentations. It is not always full-fledged, but its basics and parts are like a mental checklist.</p>
<h2 id="i-ask-for-feedback">I ask for feedback</h2>
<p>A couple of friends get asked for feedback, and I highly value their opinion. I&rsquo;m very picky about whom I ask for feedback. It should bring me forward, so no shoulder clapping. I like the in your face feedback, what was bad or wrong.</p>
<h2 id="i-reflect">I reflect</h2>
<p>After every session or talk, I try to reflect on what was good, bad and what I should change. Most of the time, that&rsquo;s part of my Friday afternoon routine of coding for fun and closing the week with reflecting on its outcome.</p>
<h2 id="i-repeat">I repeat</h2>
<p>Writing this post made me realise how often I&rsquo;m in a situation that one could categorise as public speaking, and it&rsquo;s not only the time you are on stage but also customer meetings and internal meetings. So for me, all over in a year, we are talking about 100+ occasions.</p>
<h2 id="i-like-it">I like it</h2>
<p>As you can see, there is quite some time commitment involved. I&rsquo;m not special. Everyone can level up his or her game of public speaking. But there is no such thing as a free lunch. It would be best if you had some dedication and sometimes the guts to apply for a gig. But if you are prepared, and you put in the hours, for sure you will be successful!</p>
]]></content:encoded></item><item><title>Microsoft Teams Framework (TeamsFx) - Infographic</title><link>https://thomygoelles.com/teamsfx-infographic/</link><pubDate>Thu, 03 Mar 2022 06:00:00 +0000</pubDate><guid>https://thomygoelles.com/teamsfx-infographic/</guid><description>Spending some time talking with customers and fellow developers about TeamsFx made me realise, that there is some confusion about how Microsoft named different parts of the framework. I created this one slide to explain the different flavours, names and ideas behind TeamsFx in my sessions. As sharing is caring, here we go!</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Spending some time talking with customers and fellow developers about TeamsFx made me realise, that there is some
confusion about how Microsoft named different parts of the framework. I created this one slide to explain the different flavours, names and ideas behind TeamsFx in my sessions. As sharing is caring, here we go!</p>
<p><img src="https://thomygoelles.com/img/2022/04/TeamsFXNet01_hu_3d97884f701e5c76.webp" srcset="https://thomygoelles.com/img/2022/04/TeamsFXNet01_hu_12d7b8abf7610054.webp 480w, https://thomygoelles.com/img/2022/04/TeamsFXNet01_hu_3d97884f701e5c76.webp 800w, https://thomygoelles.com/img/2022/04/TeamsFXNet01.webp 1920w" sizes="(max-width: 900px) 100vw, 860px" alt="TeamsFx Infographic" width="1920" height="1080" loading="lazy" decoding="async"></p>
]]></content:encoded></item><item><title>TeamsFx - add custom debug profiles</title><link>https://thomygoelles.com/teamsfx-add-custom-debug-profiles/</link><pubDate>Tue, 19 Oct 2021 22:49:50 +0000</pubDate><guid>https://thomygoelles.com/teamsfx-add-custom-debug-profiles/</guid><description>Introduction Today’s post is about the need(pain?) of installing your Teams application at the start of every TeamsFx debugging session. If you are new to TeamsFx in Visual Studio please read this intro first as for the next parts we assume that you already familiarized yourself with the toolkit and its features.
There are very good reasons why the Teams Toolkit always starts with the app install screen. At that screen, you can choose where to deploy your app. You can see the current version of your app and all other details of the manifest. But if you are in “the zone”, you might wish that there is a way to get to your latest changes just a bit quicker. What we will be talking about here won’t apply to every possible Teams app, but it works for tabs. To be precise, it works for everything you can create a Teams deep link for.</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Today&rsquo;s post is about the need(pain?) of installing your Teams application at the start of every TeamsFx debugging session. If you are new to TeamsFx in Visual Studio please read this <a href="https://thomygoelles.com/getting-started-with-the-microsoft-teams-framework-teamsfx/">intro</a> first as for the next parts we assume that you already familiarized yourself with the toolkit and its features.</p>
<p>There are very good reasons why the Teams Toolkit always starts with the app install screen. At that screen, you can choose where to deploy your app. You can see the current version of your app and all other details of the manifest. But if you are in &ldquo;the zone&rdquo;, you might wish that there is a way to get to your latest changes just a bit quicker. What we will be talking about here won&rsquo;t apply to every possible Teams app, but it works for tabs. To be precise, it works for everything you can create a Teams deep link for.</p>
<p><img src="https://thomygoelles.com/img/2021/10/image-3_hu_1bb84e3cdd30bfa.webp" srcset="https://thomygoelles.com/img/2021/10/image-3_hu_9d5bbc15d7f4a201.webp 480w, https://thomygoelles.com/img/2021/10/image-3_hu_1bb84e3cdd30bfa.webp 800w, https://thomygoelles.com/img/2021/10/image-3.webp 1920w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="1920" height="969" loading="lazy" decoding="async"></p>
<p>In general, developers tend to think a lot about how to be quicker or more efficient. Sometimes we overdo it. There are thousands, or probably millions, of examples out there where some dev thought about automating his or her toolchain to the absolute maximum. Just to realise that the overall cost in time of that automation effort is nowhere near the time he or she saved.</p>
<p><img src="https://memegenerator.net/img/instances/59100628.jpg" alt="EFFICIENCY EFFICIENCY EVERYWHERE - X, X Everywhere | Meme Generator" loading="lazy" decoding="async"></p>
<h2 id="creating-teams-apps">Creating Teams Apps</h2>
<p>If you think of tabs for Teams, I would always start with a static tab. Even if I know that I end up building something different but just as a starting point I would use a static tab and then over time when the app has a certain level of maturity, I would start to surface it at the extension point of choice. With Blazor, I even would start in a regular Blazor server project and would create a Blazor Library project that holds all the UI components in there and only if I&rsquo;m already happy with how the app performs, I start to move over to Teams. That might sound like a complex scenario, but for me, it feels like I&rsquo;m very &ldquo;efficient&rdquo; that why and can iterate quickly towards my goals.</p>
<p>Following that approach from above, sooner or later I&rsquo;ll end up in the need of &ldquo;F5-ing&rdquo; my TeamsFx project. And if you are still in the coding zone, meaning you are debugging and trying to understand how your latest code change affects your app, one gets annoyed by the constant need of installing your app over and over again very fast.</p>
<h2 id="create-debug-profiles">Create debug profiles</h2>
<p>To overcome that problem you need to understand how the Teams Toolkit configures your Visual Studio project for debugging. Right-click your project file, select &ldquo;Properties&rdquo; and then go to the &ldquo;Debug&rdquo; tab. We are talking Visual Studio 2019 here, in 2022 the Debug options are also part of the project properties, but the UI changed a bit. </p>
<p><img src="https://thomygoelles.com/img/2021/10/image-5_hu_faf12c6117a9a182.webp" srcset="https://thomygoelles.com/img/2021/10/image-5_hu_cb84ef6cb8642976.webp 480w, https://thomygoelles.com/img/2021/10/image-5_hu_faf12c6117a9a182.webp 800w, https://thomygoelles.com/img/2021/10/image-5.webp 1319w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="1319" height="695" loading="lazy" decoding="async"></p>
<p>In the Debug properties at the top, you see the profile drop-down. The default one is called &ldquo;IIS Express&rdquo;, check the URL that is configured for &ldquo;Launch browser&rdquo;. You will see that this actually is a Teams URL that tells Teams to install your app. That is your app ID in the parameters there. And that is what we are going to change.</p>
<p>Don&rsquo;t just change the default profile, we will repeat in a minute why you will want to keep this. But for now, just create a new profile. I try to name it similar to the Teams Tab I&rsquo;m going to use. To get your static tab loaded, just copy the Teams link in the &ldquo;Launch browser&rdquo; field. In my case, I&rsquo;m entering <a href="#">&ldquo;https://teams.microsoft.com/_#/apps/ca708824-a87d-4b6e-82c0-ca9ec5c7a6e2/sections/index?intent=2</a>&rdquo;. You can copy that URL from your debugging session and just cut the unimportant parameters.</p>
<p>Also, make sure to create the environment variable exactly the same as the default profile does. It&rsquo;s wired, but if you don&rsquo;t apply that, your app won&rsquo;t have the same look and feel. Need to investigate this in detail and, on a CSS level, why this has an impact. All other settings are exactly the same as the default profile.</p>
<p><img src="https://thomygoelles.com/img/2021/10/image-6_hu_ff64c0b1296de646.webp" srcset="https://thomygoelles.com/img/2021/10/image-6_hu_7f87450d7351d32a.webp 480w, https://thomygoelles.com/img/2021/10/image-6_hu_ff64c0b1296de646.webp 800w, https://thomygoelles.com/img/2021/10/image-6.webp 1319w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="1319" height="695" loading="lazy" decoding="async"></p>
<p>From here, next time you start you want to debug, you can just simply change the value in the Visual Studio toolbar and directly serve your tab. That is especially useful if you have different tabs you create and want to jump between them directly from your debugging session.</p>
<p><img src="https://thomygoelles.com/img/2021/10/image-7_hu_15b8aaf97c7ca1a2.webp" srcset="https://thomygoelles.com/img/2021/10/image-7_hu_15b8aaf97c7ca1a2.webp 480w, https://thomygoelles.com/img/2021/10/image-7.webp 765w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="765" height="302" loading="lazy" decoding="async"></p>
<p>As mentioned you should keep the default debug profile to be able to always also open the app install screen. If you create configurable tabs or meetings, you need to install them directly to your test Teams or Channels. So it makes sense to keep that profile in order to be able to really pick the right launch config for your next move.</p>
<p><img src="https://thomygoelles.com/img/2021/10/2021-10-20_00-40-14-anim.webp" alt="" loading="lazy" decoding="async"></p>
<h2 id="disclaimer">Disclaimer</h2>
<p>I&rsquo;m pretty sure things will change when <a href="https://devblogs.microsoft.com/dotnet/introducing-net-hot-reload/" target="_blank" rel="noopener">Hot Reload in .net 6</a> will be available. I know that this is only a couple of weeks away, but nevertheless, playing around with different launch options helped me to understand how TeamsFx works and operates in more detail. Hope you find it useful as well and see you next time!</p>
]]></content:encoded></item><item><title>TeamsFx and .NET Hot Reload</title><link>https://thomygoelles.com/teamsfx-and-net-hot-reload/</link><pubDate>Sat, 02 Oct 2021 23:00:25 +0000</pubDate><guid>https://thomygoelles.com/teamsfx-and-net-hot-reload/</guid><description> Introduction If you follow the news around .NET 6 and Visual Studio 2022 one of the most time-saving features for developers might be the .NET Hot Reload experience for editing code at runtime. This feature is part of the .NET 6 release and will be available across various project types such as WPF, Windows Forms, .NET MAUI, ASP.NET Core apps code-behind, Console applications, WinUI3 and many others. [1]
Hot Reload example from Introducing the .NET Hot Reload experience for editing code at runtime - .NET Blog (microsoft.com)</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<ul>
<li></li>
</ul>
<h2 id="introduction">Introduction</h2>
<p>If you follow the news around .NET 6 and Visual Studio 2022 one of the most time-saving features for developers might be the .NET Hot Reload experience for editing code at runtime. This feature is part of the .NET 6 release and will be available across various project types such as WPF, Windows Forms, .NET MAUI, ASP.NET Core apps code-behind, Console applications, WinUI3 and many others. <a href="https://devblogs.microsoft.com/dotnet/introducing-net-hot-reload/" target="_blank" rel="noopener">[1]</a></p>
<p><img src="https://devblogs.microsoft.com/dotnet/wp-content/uploads/sites/10/2021/05/dotnet-Hot-Reload-VS-2019-version-16_11.gif" alt="" loading="lazy" decoding="async"></p>
<p><em>Hot Reload example from <a href="https://devblogs.microsoft.com/dotnet/introducing-net-hot-reload/" target="_blank" rel="noopener">Introducing the .NET Hot Reload experience for editing code at runtime - .NET Blog (microsoft.com)</a></em></p>
<p>With Hot Reload you can start your debugging session and keep modifying your source code without the need to restart the application at every little change. If you saved your changes, just click the Hot Reload button and your changes will be available. As not all possible code changes are supported, please be aware that in certain cases you will get an error message within Visual Studio reminding you to restart your app. What is supported and what is not is explained in detail <a href="https://github.com/dotnet/roslyn/blob/main/docs/wiki/EnC-Supported-Edits.md" target="_blank" rel="noopener">here</a>.</p>
<h2 id="why-hot-reload-matters-for-teamsfx">Why Hot Reload matters for TeamsFx</h2>
<p>With TeamsFx we are of course focusing on the Blazor/Razor features of Hot Reload. But the most time-saving part of it is more based on the fact how TeamsFx surfaces your application. If you open the details of your project in Visual Studio 2019 and look for your launch profiles in the Debug section, you will find the URL your browser is told to use to start your application. You can find the same config also in the &ldquo;launchSettings.json&rdquo; file in the &ldquo;Properties&rdquo; folder of your TeamsFx project. This URL looks something like this: <a href="https://teams.microsoft.com/" target="_blank" rel="noopener">https://teams.microsoft.com/</a>_#/l/app/d8438f91-ecd9-4a5c-887a-d843f527244c?installAppPackage=true</p>
<p><img src="https://thomygoelles.com/img/2021/10/image_hu_87353196657631c2.webp" srcset="https://thomygoelles.com/img/2021/10/image_hu_f0b365218ca29e58.webp 480w, https://thomygoelles.com/img/2021/10/image_hu_87353196657631c2.webp 800w, https://thomygoelles.com/img/2021/10/image.webp 1488w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="1488" height="722" loading="lazy" decoding="async"></p>
<p><em>launchsettings.json</em></p>
<p>This link opens up the app install dialogue within Teams and allows you to install your app. That&rsquo;s just your Teams App ID  (line 5 of your manifest.json file) and the parameter to install the app in your environment. Yep, there is no tenant ID present. That&rsquo;s why sometimes you end up pressing F5 and you get an error saying something like you don&rsquo;t have permissions or the app, cannot be found. Most of the time those errors are based on identity issues, meaning you used identity &ldquo;A&rdquo; to configure TeamsFx SSO in Visual Studio, but you are running your debugging session in the browser with identity &ldquo;B&rdquo;. During configuration or when you update your manifest, TeamsFx pushes your changes to Teams Developer Portal and uploads your app to the tenant. If you use a different identity/tenant to test your app, Teams will tell you that it can&rsquo;t find the ID Visual Studio started your browser with. To get rid of those problems, you can select a dedicated browser to start your session with or just press F5 and log out and log in again with the correct identity and restart your debug experience again.</p>
<p><img src="https://thomygoelles.com/img/2021/10/image-2_hu_d65622bb43d5ea9f.webp" srcset="https://thomygoelles.com/img/2021/10/image-2_hu_723e302706e35225.webp 480w, https://thomygoelles.com/img/2021/10/image-2_hu_d65622bb43d5ea9f.webp 800w, https://thomygoelles.com/img/2021/10/image-2.webp 1343w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="1343" height="940" loading="lazy" decoding="async"></p>
<p><em>Teams app install screen</em></p>
<p>The need to always reinstall your app again is sometimes just time-consuming. You press F5 and wait for Teams to open, then you click install just to see your app loading. It&rsquo;s just a couple of seconds, but if you do that a hundred times over and over again you, or at least I, get maybe a bit frustrated.</p>
<p>There is where Hot Reload comes to the rescue. Because if you can apply code changes without the need to restart and reinstall your Teams application over and over again, you really save time. In my experience, that really speeds up the whole UI work of your project. Making changes to the HTML part of your Razor components, for example. Changing CSS classes and such things. In terms of code changes, you need to be aware of what is supported and what is not. When I need to restart the whole app, it&rsquo;s most of the time due to changes to lambda expressions. For example when you redefine your List<T>.Where() or Sort() statements. But you probably get them correct the first time. Always. Well, I don&rsquo;t ;)</p>
<h2 id="how-to---hot-reload-with-teamsfx">How To - Hot Reload with TeamsFx</h2>
<p>At the moment (October 21) TeamsFx is still in preview and not generally available. It is also only available in Visual Studio 2019. If you try to install the TeamsFx extension in VS 2022 you are currently out of luck as it is not compatible yet. This will change with the GA version of VS 2022 and TeamsFx. Check <a href="https://github.com/OfficeDev/TeamsFx/issues/2051" target="_blank" rel="noopener">this</a> GitHub issue for details.</p>
<p>Before we start, a short reminder:</p>
<blockquote>
<p>Don&rsquo;t try this in your production tenant.</p>
</blockquote>
<p>Until the versions of the tools are in sync, you can follow this list of steps to make Hot Reload available in your test environment today.</p>
<ul>
<li>Install the latest preview of Visual Studio 2022 or update to the latest version if you already have it</li>
<li>Open Visual Studio 2019 and create a new TeamsFx project</li>
<li>Configure SSO for your application and press F5 to check if everything is working</li>
<li>Close Visual Studio 2019 and reopen your solution with the preview of Visual Studio 2022</li>
<li>Change the .NET runtime to .NET 6 in VS 2022</li>
<li>Press F5 and enjoy Hot Reload in TeamsFx</li>
</ul>
<p>If you need to change something in your Teams manifest just reuse the TeamsFx tools in VS 2019 to sync your changes to your tenant and re-open the solution in VS 2022 to start debugging again.</p>
<p><a href="https://youtu.be/PLNsjtGR59A" target="_blank" rel="noopener">https://youtu.be/PLNsjtGR59A</a></p>
<p>Big thanks to John Miller for providing the right hints and directions for this article!</p>
<h2 id="final-remarks">Final remarks</h2>
<p>Everything above is based on the current state of the tools mentioned. TeamsFx, Visual Studio 2022 and .NET 6 are all still in preview and not released yet. Things will change in the (fingers crossed) near future. I&rsquo;m really hoping that this little trick will be obsolete very soon. But it is always important to understand what all those fancy buttons and features in your toolchain exactly are supposed to do. Because there will be a day when they won&rsquo;t deliver what you expect and your skills are needed.</p>
]]></content:encoded></item><item><title>Getting started with the Microsoft Teams Framework (TeamsFx)</title><link>https://thomygoelles.com/getting-started-with-the-microsoft-teams-framework-teamsfx/</link><pubDate>Thu, 16 Sep 2021 21:17:41 +0000</pubDate><guid>https://thomygoelles.com/getting-started-with-the-microsoft-teams-framework-teamsfx/</guid><description>A couple of months back, Microsoft started talking about a new way to create applications for Microsoft Teams. Those efforts got different names and with previous and betas and all their different names, it’s probably best to quote the current introduction from the actual GitHub repo of the project.
Microsoft Teams Framework is a library encapsulating common functionality and integration patterns (like simplified access to Microsoft Identity) for developers building apps for Microsoft Teams. The Teams Developer Platform offers a collection of solutions for Teams developers:</description><content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>A couple of months back, Microsoft started talking about a new way to create applications for Microsoft Teams. Those efforts got different names and with previous and betas and all their different names, it&rsquo;s probably best to quote the current introduction from the actual <a href="https://github.com/OfficeDev/TeamsFx" target="_blank" rel="noopener">GitHub repo</a> of the project.</p>
<blockquote>
<p><em>Microsoft Teams Framework is a library encapsulating common functionality and integration patterns (like simplified access to Microsoft Identity) for developers building apps for Microsoft Teams. The Teams Developer Platform offers a collection of solutions for Teams developers:</em></p>
<ul>
<li><em>IDE extensions for Visual Studio and Visual Studio Code named &ldquo;Teams Toolkit&rdquo;</em></li>
<li><em>Docs, samples</em></li>
<li><em>Code libraries including Teams Framework teamsfx and the Teams Client Library teams-js</em></li>
<li><em>A command line tool teamsfx for terminal users, automation, and CI</em></li>
</ul>
</blockquote>
<blockquote>
<p><cite> &lt;<a href="https://github.com/OfficeDev/TeamsFx" target="_blank" rel="noopener">https://github.com/OfficeDev/TeamsFx</a>&gt;</cite></p>
</blockquote>
<p>This post should help you to get you started with your first Teams application in <a href="https://channel9.msdn.com/Series/Beginners-Series-to-Blazor/What-is-Blazor-1-of-11--Beginners-Series-to-Blazor?" target="_blank" rel="noopener">Blazor</a>. As every one of us has a very own way of learning, I&rsquo;m happy to offer you either the video explaining the approach in roughly 13 minutes, or the blog post, that tries to cover the same ground, but with text and images. Whatever you choose, I&rsquo;ll hope you enjoy your option, and please keep the feedback coming!</p>
<hr>
<h2 id="why-blazor-makes-the-difference">Why Blazor makes the difference</h2>
<p>What got my attention was of course the Visual Studio part of the early messaging. At Build2021 it was clear that there will be an option to create applications for Teams based on Blazor. For everyone not familiar with Blazor and with a dev background, think SPA or front end dev work with .NET instead of JavaScript. Blazor gets a lot of hype for the WASM (Web Assembly) capabilities, but for me, not needing to write JavaScript code is already a huge advantage.</p>
<p>Please, don&rsquo;t take that as some decision or view that should be followed like a religion. I know some people out there see it that way. If that&rsquo;s OK for them, be my guest, but for me, it&rsquo;s a point in time decision of a developer framework or toolchain. Things will change in the future again, for sure. So pick what you need to finish the job and try to learn as much as possible about the tool you choose. In the end, the question is if your users are happy with your app, that&rsquo;s in my opinion way more important than the dev stack.</p>
<p>Anyways, for this article and blog, we will focus on the .NET side of the house, as I just can&rsquo;t write JavaScript without the constant fear of breaking my fingers every 10 lines of code. And there is only me to blame for that, as I never invested enough time to really dig deep and learn it from A to Z.</p>
<p>Looking back over some smaller projects and what I was able to achieve in Blazor, I&rsquo;m more than happy that Microsoft invests also in Blazor in M365.</p>
<h2 id="teamsfx-in-visual-studio-or-teams-toolkit-v2">TeamsFx in Visual Studio or Teams Toolkit v2</h2>
<p>If you open up Visual Studio 2019 and search for Teams in the Extension menu, you will find two different extensions. We are focusing on the one named &ldquo;Teams Toolkit [Preview]&rdquo;. And yes, this library is still in preview and has some rough edges. That means you should not use that in production or start creating support tickets because things break. It&rsquo;s still in development and not released yet, so better be careful how you use it! Go ahead, install the extension and let&rsquo;s have a look at what we get!</p>
<p><img src="https://thomygoelles.com/img/2021/09/image-1_hu_cbc864bc1d16f9e4.webp" srcset="https://thomygoelles.com/img/2021/09/image-1_hu_8d3699d2883dacf1.webp 480w, https://thomygoelles.com/img/2021/09/image-1_hu_cbc864bc1d16f9e4.webp 800w, https://thomygoelles.com/img/2021/09/image-1.webp 1013w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="1013" height="690" loading="lazy" decoding="async"></p>
<h2 id="first-teams-template">First Teams template</h2>
<p>After installing the extension and restarting Visual Studio 2019, create a new project and select &ldquo;Microsoft Teams App&rdquo;.</p>
<p><img src="https://thomygoelles.com/img/2021/09/image_hu_4d465a8f28f5a308.webp" srcset="https://thomygoelles.com/img/2021/09/image_hu_4d465a8f28f5a308.webp 480w, https://thomygoelles.com/img/2021/09/image.webp 500w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="500" height="501" loading="lazy" decoding="async"></p>
<p>The wizard asks you about your project and application name. Please make sure you remember those names/variables because we will discuss their references later on. Visual Studio now created a new Blazor based project and opens the GettingStarted.txt file with a four-point list telling you what you should do to be able to &ldquo;consume the F5 experience&rdquo;. In other words, that&rsquo;s what you need to do, to get the application up and running. Please select a developer or test account and tenant during the process and make sure you have at least some permission in the Azure portal of that tenant, so you can manipulate the generated Azure AD app, and it&rsquo;s permissions later on.</p>
<p><img src="https://thomygoelles.com/img/2021/09/image-2_hu_47bfabf7632ab5fd.webp" srcset="https://thomygoelles.com/img/2021/09/image-2_hu_90c1104cd723a224.webp 480w, https://thomygoelles.com/img/2021/09/image-2_hu_47bfabf7632ab5fd.webp 800w, https://thomygoelles.com/img/2021/09/image-2.webp 1920w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="1920" height="1047" loading="lazy" decoding="async"></p>
<p>Following points 1 and 2 you should see something like this in your output window in VS 2019:</p>
<p><img src="https://thomygoelles.com/img/2021/09/image-3_hu_b9d2479844c1c3fe.webp" srcset="https://thomygoelles.com/img/2021/09/image-3_hu_d7e32832b4dfc76.webp 480w, https://thomygoelles.com/img/2021/09/image-3_hu_b9d2479844c1c3fe.webp 800w, https://thomygoelles.com/img/2021/09/image-3.webp 946w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="946" height="216" loading="lazy" decoding="async"></p>
<p>The Teams Toolkit took the credentials you provided and created a couple of things in the background. First, it created a Teams app definition and uploaded it to the Teams developer portal of your tenant. Second, it created an Azure AD application, assigned some basic permissions and &ldquo;allowed app ids&rdquo; and configured your Teams project to use said Azure AD app in your solution. Open both portals and make sure you find the entry you just created by configuring SSO for your application.</p>
<p><img src="https://thomygoelles.com/img/2021/09/image-4_hu_e25fb2ad267b63fb.webp" srcset="https://thomygoelles.com/img/2021/09/image-4_hu_e89c8c6d3f800a57.webp 480w, https://thomygoelles.com/img/2021/09/image-4_hu_e25fb2ad267b63fb.webp 800w, https://thomygoelles.com/img/2021/09/image-4.webp 1920w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="1920" height="977" loading="lazy" decoding="async"></p>
<p>And:</p>
<p><img src="https://thomygoelles.com/img/2021/09/image-5_hu_e62d60b486e91470.webp" srcset="https://thomygoelles.com/img/2021/09/image-5_hu_810522759c0ebb37.webp 480w, https://thomygoelles.com/img/2021/09/image-5_hu_e62d60b486e91470.webp 800w, https://thomygoelles.com/img/2021/09/image-5.webp 1920w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="1920" height="977" loading="lazy" decoding="async"></p>
<p>You should be able to recognize the values you provided during the wizard in both portals and most probably you should think about your naming convention in the future just to make sure things are consistent and easily identified in all use cases: Visual Studio Solution, Azure AD App and Teams Developer portal.</p>
<h2 id="start-your-application---press-f5">Start your application - Press F5</h2>
<p>If you were able to follow along so far and no installation or configuration hiccups occurred, go back to Visual Studio and press F5. It will take a couple of seconds, but VS will build the application and start your local IIS instance to serve your Blazor application. Visual Studio should open your browser and point you to the Teams web application of the tenant your SSO account belongs to.</p>
<p><img src="https://thomygoelles.com/img/2021/09/image-6_hu_74ee0d75ba08abd8.webp" srcset="https://thomygoelles.com/img/2021/09/image-6_hu_3ea381f28f99af36.webp 480w, https://thomygoelles.com/img/2021/09/image-6_hu_74ee0d75ba08abd8.webp 800w, https://thomygoelles.com/img/2021/09/image-6.webp 1920w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="1920" height="977" loading="lazy" decoding="async"></p>
<p>On the installation screen, click on &ldquo;Add&rdquo;, and after a couple of seconds, you should see your first demo application up and running in a private Teams Tab.</p>
<p><img src="https://thomygoelles.com/img/2021/09/image-7_hu_2778072998ab57c4.webp" srcset="https://thomygoelles.com/img/2021/09/image-7_hu_5467814a413f1285.webp 480w, https://thomygoelles.com/img/2021/09/image-7_hu_2778072998ab57c4.webp 800w, https://thomygoelles.com/img/2021/09/image-7.webp 1920w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="1920" height="969" loading="lazy" decoding="async"></p>
<p>Congratulations, you just created your first Blazor application for Teams!</p>
<h2 id="exploring-the-demo-application">Exploring the demo application</h2>
<p>Looking on the screen, you see the &ldquo;Authorize&rdquo; button. That button will create a Microsoft Graph service client and request the profile picture of your developer account. To follow along, open the Tab.razor component in the &ldquo;Pages&rdquo; folder of the project and look at lines 86 to 88.</p>
<p><img src="https://thomygoelles.com/img/2021/09/image-8_hu_e372ee9511006e2a.webp" srcset="https://thomygoelles.com/img/2021/09/image-8_hu_4ec9cca3fa7e5367.webp 480w, https://thomygoelles.com/img/2021/09/image-8_hu_e372ee9511006e2a.webp 800w, https://thomygoelles.com/img/2021/09/image-8.webp 1018w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="1018" height="107" loading="lazy" decoding="async"></p>
<p>We won&rsquo;t go into details of the implementation, but the GraphClient is a regular client from the .NET SDK. The &ldquo;magic&rdquo; of the library/template comes from the &ldquo;TeamsFx&rdquo; part and how it creates that client for you.</p>
<p>Not sure that my analogy is 100% technically correct, but what TeamsFx does is, it creates JavaScript wrappers for the Teams JavaScript SDK and makes them accessible by using the built-in JS interop features of Blazor. In the TeamsFx.cs file, you can find the C# part and in the wwwroot folder, you find the JavaScript files that hold the needed glue code to be able to communicate with the Teams SDK.</p>
<p><img src="https://thomygoelles.com/img/2021/09/image-9.webp" srcset="https://thomygoelles.com/img/2021/09/image-9.webp 354w" sizes="(max-width: 900px) 100vw, 860px" alt="" width="354" height="789" loading="lazy" decoding="async"></p>
<p>From a high-level perspective, that&rsquo;s easy to understand, but the devil of course is in the details. I tried to create something similar by using the Microsoft Graph Toolkit, and its auth components in combination with Blazor JS interop. It worked, but as soon as you reach the point of token lifecycle it gets complicated, or let&rsquo;s rephrase that, it just gets more serious, and you need to be very careful with your decisions, especially when dealing with access and refresh tokens.  </p>
<p>If you want to explore the implementation just work your way through the code and try to understand how the app gets your bearer token and how the GraphClient gets created. If you want to extend the demo example have a look at <a href="https://github.com/thomyg/PnPDemo190821Backup" target="_blank" rel="noopener">https://github.com/thomyg/PnPDemo190821Backup</a>. That just counts all the teams your user is a member of. But it already means that you need to add permissions to your Azure AD application, as the basic ones the Toolkit created aren&rsquo;t enough to iterate through all your Teams.</p>
<p>As this post just is an entry guide to the world of TeamsFx we will stop right here to not make it too complicated right from the beginning. Thank you for following through and cu next time!</p>
]]></content:encoded></item></channel></rss>