<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Christo’s Blog]]></title><description><![CDATA[AI engineering and consulting. The good, the bad and the occasionally very ugly.]]></description><link>https://blog.christoolivier.com</link><image><url>https://blog.christoolivier.com/img/substack.png</url><title>Christo’s Blog</title><link>https://blog.christoolivier.com</link></image><generator>Substack</generator><lastBuildDate>Wed, 15 Apr 2026 15:14:50 GMT</lastBuildDate><atom:link href="https://blog.christoolivier.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Christo Olivier]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[christoolivier@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[christoolivier@substack.com]]></itunes:email><itunes:name><![CDATA[Christo Olivier]]></itunes:name></itunes:owner><itunes:author><![CDATA[Christo Olivier]]></itunes:author><googleplay:owner><![CDATA[christoolivier@substack.com]]></googleplay:owner><googleplay:email><![CDATA[christoolivier@substack.com]]></googleplay:email><googleplay:author><![CDATA[Christo Olivier]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Bugs in Gemini Enterprise that will break your custom agents]]></title><description><![CDATA[It's not all moonlight and roses in the battle for Enterprise AI]]></description><link>https://blog.christoolivier.com/p/bugs-in-gemini-enterprise-that-will</link><guid isPermaLink="false">https://blog.christoolivier.com/p/bugs-in-gemini-enterprise-that-will</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Sun, 08 Feb 2026 18:30:31 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/xCV12jMQSqc" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There is a lot of excitement about Gemini Enterprise, especially if you run in GCP circles. The pitch is simple: Build with Google&#8217;s Agent Development Kit (ADK), deploy to Agent Engine, and expose it via Gemini Enterprise. It sounds like the perfect pipeline. It is a great vision! But before we get too excited, and assume this is ready for complex production workflows, let&#8217;s look at what you might encounter when you publish that agent to Agent Space and add it to Gemini Enterprise.</p><p>I&#8217;ve been spending time in the trenches connecting these pieces, and I&#8217;ve hit some bugs that aren&#8217;t just minor annoyances&#8212;they are real roadblocks in certain circumstances.</p><p>It is important to note that none of these bugs are caused by ADK or Agent Engine. They all seem to be introduced by Gemini Enterprise and how it currently interacts with custom agents.</p><p>I have recorded a video in which I walk through some of the issues I have encountered so far:</p><ul><li><p><strong>Sessions not working as expected:</strong> You&#8217;d expect a Session ID to persist throughout a chat so you can manage state or store files. In Gemini Enterprise against a custom agent? It resets on every single conversation turn.</p></li><li><p><strong>Issues rendering files to users:</strong> If your agent tries to render multiple unique PDFs to the user, Gemini Enterprise struggles. It ignores display names and, worse, if you try to preview different files, the UI frequently renders the exact same document for all of them. It&#8217;s a UI bug that makes document retrieval frustratingly unreliable.</p></li><li><p><strong>Not being able to access user uploaded files in their original format:</strong> This is the big one. When a user uploads a file (like a Word doc) to your agent, you probably want the raw binary to process yourself. Gemini Enterprise intercepts it, "helpfully" chunks it into text and images, and hands you the processed scraps.</p></li></ul><div id="youtube2-xCV12jMQSqc" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;xCV12jMQSqc&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/xCV12jMQSqc?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>The source code used in this video can be found at <a href="https://github.com/christo-olivier/gemini-enterprise-bugs">https://github.com/christo-olivier/gemini-enterprise-bugs</a></p><p></p>]]></content:encoded></item><item><title><![CDATA[Are we losing touch with reality?]]></title><description><![CDATA[A comment on Hacker News is making me question if the industry is losing its grip on reality, and what it means to say you are skilled in a profession]]></description><link>https://blog.christoolivier.com/p/are-we-losing-touch-with-reality</link><guid isPermaLink="false">https://blog.christoolivier.com/p/are-we-losing-touch-with-reality</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Tue, 06 Jan 2026 17:07:41 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/ad8a5d03-e702-4442-951a-f9e0768ea221_1200x655.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I was scrolling Hacker News over the weekend when a comment stopped me dead in my tracks. For a moment I stared at it in disbelief, not knowing if what I am reading is from someone who&#8217;s &#8220;trollcraft&#8221; is epic, or if it was sincere.</p><p>The comment was made on a post about how AI coding assistants are getting lots of people, who used to be engineers but have moved into other positions, back into coding.</p><p>As you can imagine the comment section was lively and full of opinions. Our industry can be labelled many things but lacking in opinions is not one of them.</p><p>The comment in questions was making the claim that, and I quote </p><blockquote><p>&#8220;If I tell a chef how to prepare a meal I want, and they prepare it, then I have cooked the meal&#8221;.</p></blockquote><p>I was simply dumbstruck reading that. We seem to have slowly been losing our grip on reality. </p><p>We all rely on technology to get our work done faster and more efficiently. Nothing wrong with that. But surely we must also realise that if you cannot do the work without AI then you don&#8217;t actually know how to do the work.</p><p>If we take that comment to its extreme then a non-technical manager, who has told an engineering team what to build, can consider themselves an engineer. The same as anyone that ordered a meal at McDonald&#8217;s would be able to call themself a chef or cook.</p><p>You might think I am being extreme here but I have seen this shift happen over the last two and a half years. True mastery and skill is being devalued at an alarming rate. Yes AI tooling is extremely useful in certain circumstances, but to wield it wisely you need the skill to know when it is busy messing up.</p><p>I don&#8217;t blame the commenter, we have been collectively bombarded with this messaging since AI coding tools have hit the industry. However, I cannot help but wonder if we are losing our grip on what skill and mastery mean? </p>]]></content:encoded></item><item><title><![CDATA[Cursor, we have a problem...]]></title><description><![CDATA[It's not me, it is you.]]></description><link>https://blog.christoolivier.com/p/cursor-we-have-a-problem</link><guid isPermaLink="false">https://blog.christoolivier.com/p/cursor-we-have-a-problem</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Thu, 22 May 2025 09:30:01 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/4b989dd6-d394-409f-bba3-e961920bd783_1200x600.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We need to have a conversation about Cursor. Yes the AI development environment has been all the rage. You see I have a problem with Cursor, in fact I have multiple problems with it.</p><p>To be fair to Cursor it is not the only culprit in this AI IDE <s>hype</s> arms race, but it and Windsurf are the prominent two that are forks of Visual Studio Code. Herein lies the first issue.</p><h1>Foundation built on a competitor&#8217;s code base</h1><p>Both Cursor and Windsurf are forks of Visual Studio Code. They are forks because VS Code extensions are limited in their ability to manipulate the user interface. Fair enough, that is a valid reason, but it is also a critical weakness.</p><p>VS Code at its core is open source but the version me and you download and install is a distribution built from that open source core. So what we normally download and install is NOT OPEN SOURCE. Yes I am yelling it so that it sinks in because somehow people are still surprised when I point this out.</p><p>There is VS Codium which is an open source distribution, but that is limited in the number of extensions that are available. Right here is where you should already start seeing the smoke from the dumpster fire that is &#8220;forking&#8221; VS Code.</p><p>The reason VS Code is so popular is due to all of the extensions that have been built for it. Many of those built by Microsoft, tons of them built by the community. It turns out that those core Microsoft extensions are only licensed to be used with VS Code. Microsoft recently stopped turning a blind eye to the shenanigans of those forks, and started<a href="https://github.com/getcursor/cursor/issues/2976"> blocking the installation</a> of their extensions in them.</p><p>Cursor had to scramble and start to implement open source versions of the extensions that got blocked, so for now things are kind of back to normal. This should however raise serious questions.</p><p>Do you think that any corporation would spend resources building something like VS Code simply out of the kindness of their hearts? Do you think that it is a good idea to build your entire business on the back of a code base controlled at its core by a competitor? Not only a competitor but a behemoth who has survived many industry transitions. (Metaphorically the equivalent of the crocodile having been around since the age of the dinosaurs)</p><p>The answer here should be a clear NO.</p><h1>A forced way of working</h1><p>This is the one that really annoys me. See if your AI coding tools force you into a specific IDE then I get uncomfortable. Not all professionals have the same workflow, not all work requires the same tools. Have we learnt nothing after all of these years?</p><p>I know some exceptional SREs that live and die on the hill that is Vim. I am in awe of their knowledge, skill, and the impact they have.</p><p>I know exceptional developers that use Jetbrains tooling because that works best for them. Likewise I know others who swear by VS Code, and others that use different tools like Zed.</p><p>The point is, people spend a lot of time finding the tooling that works best for their particular niche. It makes them more effective at what they do. The tool should fit around the person and the job.</p><p>Having your AI tooling tied into a specific IDE runs counter to this. I am sorry, but not everyone in the world is coding React and NextJS applications. (Something Cursor is exceptionally good at). It is great if Cursor works for you and your workflow. Please don&#8217;t force it on everyone.</p><p>Yet this is exactly what has started to happen. I have heard multiple first hand accounts of companies buying Cursor licenses for their teams and encouraging people to switch to it.</p><h1>The shift to coding agents</h1><p>While the AI IDE wars have been raging, the shift to coding agents has been slowly gathering pace.</p><p>Aider (a firm favourite of mine) and Claude Code, were the first prominent ones on the scene, and OpenAI has just released Codex.</p><p>These tools are command line tools that run outside of your IDE. This immediately makes them compatible with more people&#8217;s workflow.<br><br>It is refreshing to not have the tool dictate the IDE I should use. It also opens up ideas of using smaller scale agents in other places in your workflow. For example agents doing initial code review or suggesting performance improvements.</p><h1>My own experience so far</h1><p>My own journey with AI IDEs and tools has not been overnight. I have built two products from scratch with the help of them. Both products are non-trivial solutions, each having data pipelines, databases, backend APIs, UIs, and all the infrastructure to deploy and run them.</p><p>I started out 18 months ago and the journey has been:</p><ol><li><p>VS Code + GitHub Co-Pilot</p></li><li><p>VS Code + Cody (by Sourcegraph)</p></li><li><p>Cursor + Aider</p></li><li><p>VS Code + Augment Code</p></li></ol><p>During that time I always checked back on the tools I used previously to see if they have improved.</p><p>Through all of this I have always had the best experience with the tools that did not force me into a single IDE. Ones where I get to choose what works best for me.</p><p>If my own experience is anything to go by then choose the tools that allow you the freedom to work how you work best.</p><p>Being forced into a single tool is a step backwards for all of us. This is especially the case when that tool can do a Venture Capital rug pull on you at any time, or can themselves be clobbered by the giant on who&#8217;s back they have decided to build their fortune.</p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.christoolivier.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Christo&#8217;s Blog! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Calling Bullsh*t]]></title><description><![CDATA[Fleet admiral chaos reporting for duty]]></description><link>https://blog.christoolivier.com/p/calling-bullsht</link><guid isPermaLink="false">https://blog.christoolivier.com/p/calling-bullsht</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Wed, 23 Apr 2025 12:00:00 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/d1cae47c-76a6-477f-a04b-5e07c7963e0b_474x266.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>It has been almost a year since my last blog post. This terrible track record is on me, I really should commit to writing more. So, what on earth spurred me into action to write now? That would be the deep desire to jump on my soap box as if I am at <a href="https://en.wikipedia.org/wiki/Speakers%27_Corner">Speaker&#8217;s Corner</a>. </p><p>I have always enjoyed the writing of Steve Yegge. He is clearly a very experienced and accomplished engineer. His writing on the Sourcegraph blog has been great. But his post on 22 March 2025 titled <em><a href="https://sourcegraph.com/blog/revenge-of-the-junior-developer">Revenge of the junior developer</a></em> was the kick up the backside I needed to write again. I recommend you read it before you continue with this post because I will be &#8220;Don Quixote-ing&#8221; my way into some of the windmills I have picked out of that post.</p><p>Buckle up fellow nerds! This one is not for people with weak stomachs. There will be foul language&#8230; and I am possibly going to go full Karen&#8230; you have been warned!</p><p>(If you want to get into the full spirit of this post then may I suggest you pair it with <a href="https://www.youtube.com/watch?v=q31WY0Aobro">Anarchy in The UK by the Sex Pistols</a>)</p><h1>Opening credits</h1><p>For almost two years I have been building LLM based applications and solutions, or &#8220;AI&#8221; as most people now seem to call it. Not only have I been building solutions that use it heavily to solve problems, but I have also been using AI agents and IDEs for the development of these solutions.</p><p>Two years is sufficient time to get so close to the tech that what you see as normal is actually not the norm. </p><p>On top of that, if you did not know, I don&#8217;t live in Silicon Valley or in a Big Tech &#8220;scene&#8221;. I have been a tech consultant for a large part of my career and then went rogue and became a freelancer, before going mad and bootstrapping a startup. </p><p>So why am I telling you this? Because I want you to know I come from a world where your mouth needs to write cheques that your arse can cash. If you can&#8217;t deliver what you say you will deliver you won&#8217;t be in business for long. Also, you carry liability for the work you deliver. If you screw up on a project you can and will get sued for damages. We will get back to liability later, just let it sit there in the back of your mind as you keep reading.</p><h1>Commentary on the &#8220;Waves&#8221;</h1><p>The first thing that kicked me square in the &#8220;WTF?!!&#8221; was the &#8220;six waves&#8221; presented at the start of the post. Even though we are told that this is coming from &#8220;Exaggeration Central&#8221;, we are still introduced to the timeline of how chat-based vibe coding is going to be obsolete by Q3 of 2025. Yes, you read that correctly. Vibe coding is evolving, and the evolution is spectacular!</p><p>Below is <em>Figure 1: Overlapping waves of AI coding modalities,</em> taken from Steve&#8217;s blog post.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xLO1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F379a4ba0-573f-422e-b46d-d0f147f9ad9e_1600x990.avif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xLO1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F379a4ba0-573f-422e-b46d-d0f147f9ad9e_1600x990.avif 424w, https://substackcdn.com/image/fetch/$s_!xLO1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F379a4ba0-573f-422e-b46d-d0f147f9ad9e_1600x990.avif 848w, https://substackcdn.com/image/fetch/$s_!xLO1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F379a4ba0-573f-422e-b46d-d0f147f9ad9e_1600x990.avif 1272w, https://substackcdn.com/image/fetch/$s_!xLO1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F379a4ba0-573f-422e-b46d-d0f147f9ad9e_1600x990.avif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xLO1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F379a4ba0-573f-422e-b46d-d0f147f9ad9e_1600x990.avif" width="1456" height="901" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/379a4ba0-573f-422e-b46d-d0f147f9ad9e_1600x990.avif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:901,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;AI Coding Modalities Graph&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="AI Coding Modalities Graph" title="AI Coding Modalities Graph" srcset="https://substackcdn.com/image/fetch/$s_!xLO1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F379a4ba0-573f-422e-b46d-d0f147f9ad9e_1600x990.avif 424w, https://substackcdn.com/image/fetch/$s_!xLO1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F379a4ba0-573f-422e-b46d-d0f147f9ad9e_1600x990.avif 848w, https://substackcdn.com/image/fetch/$s_!xLO1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F379a4ba0-573f-422e-b46d-d0f147f9ad9e_1600x990.avif 1272w, https://substackcdn.com/image/fetch/$s_!xLO1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F379a4ba0-573f-422e-b46d-d0f147f9ad9e_1600x990.avif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The following is a direct quote from the post.</p><blockquote><p>The chart in Figure 1 depicts six overlapping waves of programming: traditional (2022), completions-based (2023), chat-based (2024), coding agents (2025 H1), agent clusters (2025 H2), and agent fleets (2026).</p></blockquote><p>Holy *&amp;^%&amp;^ batman! Forget that these tools need CONSTANT supervision to ensure they don&#8217;t go off the rails. Forget that a single chat-based LLM in &#8220;Agent Mode&#8221; can already generate more code than an experienced developer can review in a timely fashion. Just forget about all of that because what we have to look forward to is &#8220;agent clusters&#8221; in less than 6 months&#8217; time, and &#8220;agent fleets&#8221; in 2026. (On a side note, who has money on the job title Admiral of Architecture appearing on a job post soon?)</p><p>There is another industry where this sort of &#8220;like cures like&#8221; thinking is very prevalent. Yes people, I am going there! This sounds like the tech equivalent of homeopathy. Because if Agents cause issues then surely MORE agents would be the cure. </p><p>Now if you are thinking &#8220;surely Christo has gone mad, or he is now just a coding boomer coming to spoil our fun. This like cures like stuff is just an old man running at windmills&#8221;. Well may I present to you the solution proposed to keep those AI Coding Agents in line: (<em>Figure 2: FY26 Org Chart </em>from Steve&#8217;s blog post)</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!zcHI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9243af-4949-4aac-8b78-78a15f7a9fda_682x806.avif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!zcHI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9243af-4949-4aac-8b78-78a15f7a9fda_682x806.avif 424w, https://substackcdn.com/image/fetch/$s_!zcHI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9243af-4949-4aac-8b78-78a15f7a9fda_682x806.avif 848w, https://substackcdn.com/image/fetch/$s_!zcHI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9243af-4949-4aac-8b78-78a15f7a9fda_682x806.avif 1272w, https://substackcdn.com/image/fetch/$s_!zcHI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9243af-4949-4aac-8b78-78a15f7a9fda_682x806.avif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!zcHI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9243af-4949-4aac-8b78-78a15f7a9fda_682x806.avif" width="438" height="517.6363636363636" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9f9243af-4949-4aac-8b78-78a15f7a9fda_682x806.avif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:806,&quot;width&quot;:682,&quot;resizeWidth&quot;:438,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Org Chart in 2026 Influenced by AI&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Org Chart in 2026 Influenced by AI" title="Org Chart in 2026 Influenced by AI" srcset="https://substackcdn.com/image/fetch/$s_!zcHI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9243af-4949-4aac-8b78-78a15f7a9fda_682x806.avif 424w, https://substackcdn.com/image/fetch/$s_!zcHI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9243af-4949-4aac-8b78-78a15f7a9fda_682x806.avif 848w, https://substackcdn.com/image/fetch/$s_!zcHI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9243af-4949-4aac-8b78-78a15f7a9fda_682x806.avif 1272w, https://substackcdn.com/image/fetch/$s_!zcHI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9243af-4949-4aac-8b78-78a15f7a9fda_682x806.avif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I present to you AI Managers! Are you starting to see that homeopathy analogy come to life yet?</p><h1>Productivity gains</h1><p>Right, that was a lot of ranting and raving. I think you probably want a break from my soapbox antics, and I need to have another hot beverage (probably one with less caffeine).</p><p>Let&#8217;s slow down and now look at some of the productivity gains that people claim all of this produces.</p><p>The blog post in question states that each new modality in coding brings a 5X to 10X productivity boost. This is also the sort of figures you see thrown around online on LinkedIn and Twitter, sorry I mean X. </p><p>What no one is talking about are the details surrounding those claimed productivity gains. Think about it, is this how much faster you can generate code? Is this how much faster your team completes work? Which parts of the work are we talking about? All of it?!!</p><p>It matters knowing what the claims apply to. Most of your time as a software engineer is not spent writing code. Yes, that is correct, most of your time is spent gathering requirements, speaking to users, other teams, speaking to your own team, doing code reviews etc. It is not just sitting with your headphones on cranking out code.</p><p>Another thing that is very rarely mentioned when these claims are made is the type of environment they are realised in. Is this an indie dev working on their own? Is this a large development team working in a larger organisation? All of this matters. </p><p>There are clear signs that indie devs and small teams have a much bigger performance increase than teams in large organisations. You can already guess why that is right? That is correct, it is because small teams that can operate autonomously always move faster than large teams that need a lot of additional coordination to keep things on track and aligned to the overall goal.</p><p>The argument can be made that there are tons of productivity gains to be realised in larger teams by simply helping make these teams more autonomous without introducing tooling that can create an avalanche of code each day.</p><p>This brings me to my next point, what is the word on the street where people are currently using these tools?</p><h1>Where the tyre hits the road</h1><p>Let&#8217;s start by mentioning another blog post that made it to the font page of Hacker News. This one is called <em><a href="https://matthewsinclair.com/blog/0178-why-llm-powered-programming-is-more-mech-suit-than-artificial-human">Why LLM-Powered Programming is More Mech Suit Than Artificial Human</a></em> by Matthew Sinclair. It strongly aligns with my own experience using these tools daily.</p><p>Now Matthew looks to be a seasoned professional with bucket loads of experience through many different roles. What stood out to me was his mentioning of the vigilance required when working with a coding agent, a single coding agent not a cluster or fleet of them. He was specifically talking about Claude Code. </p><p>Here are some quotes from his blog post.</p><blockquote><p>With great power comes great responsibility. You must maintain constant awareness when working with AI coding tools&#8212;something I learned through several painful lessons.</p><p>Claude Code occasionally made bewildering decisions: changing framework code to make tests pass, commenting out whole sections of code and replacing them with hardcoded values to achieve a passing test rather than fixing the underlying problem, or introducing dependencies that weren&#8217;t necessary or appropriate.</p><p>&#8230;</p><p>Taking your eyes off the process, even briefly, can lead to trouble. In my case, the backend required three complete rewrites because I looked away at crucial junctures, allowing the AI to go down problematic implementation paths that weren&#8217;t apparent until much later.</p></blockquote><p>Let that sink in for a moment. Then read the next quote.</p><blockquote><p>For me, using Claude Code has been a learning exercise in itself. Progress often felt like two steps forward and three back, particularly in the early stages. Generating 20k+ lines of code became relatively straightforward on a daily basis, but knowing when to throw everything away and rebuild from scratch&#8212;that took 30 years of experience.</p></blockquote><p>20k+ lines of code generated daily.  Stew in that statement for a bit. Can you imagine having to review that amount of code daily?!</p><p>Can you ensure that the code will be of good quality, that the agents have not gone off the rails in a moment where you looked away and took a sip from your avolatte? If you say to me &#8220;yes&#8221; then I am going to call you a liar right here and now. It is simply not possible for a human to review and ensure code quality at that scale each and every day.</p><p>So obviously the only way to handle that volume of code is to have machines do that work. But if the machines writing the code can go off the rails, then surely the machines checking them can go off the rails too. See where I am going with this? DO YOU SEE WHERE I AM GOING WITH THIS?!!</p><p>Let me illustrate with a picture.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!S0cE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5937aeac-1634-4d7b-b850-8429d7434e71_474x266.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!S0cE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5937aeac-1634-4d7b-b850-8429d7434e71_474x266.jpeg 424w, https://substackcdn.com/image/fetch/$s_!S0cE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5937aeac-1634-4d7b-b850-8429d7434e71_474x266.jpeg 848w, https://substackcdn.com/image/fetch/$s_!S0cE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5937aeac-1634-4d7b-b850-8429d7434e71_474x266.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!S0cE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5937aeac-1634-4d7b-b850-8429d7434e71_474x266.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!S0cE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5937aeac-1634-4d7b-b850-8429d7434e71_474x266.jpeg" width="474" height="266" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5937aeac-1634-4d7b-b850-8429d7434e71_474x266.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:266,&quot;width&quot;:474,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Rocket powered skateboard&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Rocket powered skateboard" title="Rocket powered skateboard" srcset="https://substackcdn.com/image/fetch/$s_!S0cE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5937aeac-1634-4d7b-b850-8429d7434e71_474x266.jpeg 424w, https://substackcdn.com/image/fetch/$s_!S0cE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5937aeac-1634-4d7b-b850-8429d7434e71_474x266.jpeg 848w, https://substackcdn.com/image/fetch/$s_!S0cE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5937aeac-1634-4d7b-b850-8429d7434e71_474x266.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!S0cE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5937aeac-1634-4d7b-b850-8429d7434e71_474x266.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">AI Fleet Admiral</figcaption></figure></div><h1>The revenge of Liability</h1><p>Hello liability my old friend&#8230; you have come to talk to us again&#8230; because a vision is softly creeping&#8230;</p><p>It is time we speak the name of the one thing in GenAI no one wants to talk about. L-I-A-B-I-L-I-T-Y</p><p>Everyone wants to sell you an agent or a service where you hand over your money, but you keep all the risk and liability. Can you imagine that ever happening when you bring in a freelancer or a consulting firm? HELL NO! Consulting firms and freelancers cannot simply put a little sentence on a screen that says, &#8220;People make mistakes so please be aware of it and double check everything&#8221;.</p><p>No no no sir, we carry liability for our work. Don&#8217;t believe me? Think I just made that up? Let me tell you that I have been on projects where the consulting firm I was working for needed a project signed off by its parent company due to the project carrying <strong>unlimited risk. </strong>Yes, that means that if we did anything that caused damage to the client, they could take us to court for whatever amount of damages we caused. Literally they could sue us and the parent company for any amount even if it means the whole group of companies went bankrupt. (That type of project makes you think twice about spouting BS to a client)</p><p>As a freelancer I must have insurance as well for exactly this same reason. Clients demand it because otherwise they are left out of pocket when something bad happens and I am unable to pay the damages from my own assets. Oh, and as a startup we also need insurance with minimum amounts dictated by the clients that we do business with.</p><p>Why am I droning on about this? Because it is the answer to adopting the fantastical &#8220;Fleet of Agents&#8221; scenario. </p><p>If you are willing to accept liability for your Fleet of Agents&#8217; output, then I am willing to sign up to that service. Yes, you must accept the same liability a consulting firm or agency accepts when you engage them for work.</p><p>We already know that humans will have to trust the AI Managers that oversee the Coding Agents because no human will be able to review all the work produced by a single coding agent in full flow, never mind a Fleet of Agents. So, the seller of this service or tooling needs to be willing to accept liability. If they don&#8217;t, then you are clearly not confident in the output of your product.</p><p>If we need each human to check every line of code, then those fantastical 10X productivity gains are starting to look more and more out of reach won&#8217;t you say?</p><h1>What about the junior developers</h1><p>I cannot help but get one last jab in at the ludicrous idea that junior developers will do much better in this new world of agentic coding cluster armadas. Do you think, after everything that I have highlighted, that junior engineers will have the skill and intuition to deliver better outcomes in this new world?</p><p>If you do then please reach out, I have a supplement that works for all ailments and has no negative side effects. Big pharma does not want you to know about it&#8230;</p><p>What we need is even more rigorous training for those in our field that will oversee these tools. The amount of damage that can be done is orders of magnitude more with these tools than when you wrote code by hand. I am inclined to say that those productivity gains of 10X are more realistic as damage multiples in the hands of someone that does not know what they are doing.</p><p>Before you get on your high horse and come at me with your posse of agentic agents, I am not anti-junior. Relying only on the experienced developers is not an option because we all end up being a junior in some way shape or form at certain points when we tackle something new. So just because you are experienced in one domain does not mean you are not going to juggle chainsaws like a vibe ninja and lose your limbs when you tackle another domain.</p><p>We need to hammer home the fundamentals now more than ever! We need a new appreciation for knowing the fundamentals because only those that know them will be able to call bullshit on the &#8220;agent fleet&#8221; when it goes off the rails. Everyone else will be vibe coding their way into an episode of <a href="https://www.bbc.co.uk/news/stories-57520169">The Lazarus Heist</a>. (Go listen to the podcast, all seasons of it. It is amazing!!!)</p><h1>Wrapping up</h1><p>I have had a lot of fun just &#8220;vibe blogging&#8221; this post. No, I did not write a single piece of it with AI, I cranked up the tunes and simply wrote what was on my mind without much of a filter. </p><p>Where we are at today in tech feels a bit like one has stumbled into a trance party by accident. There are vibes all around. People making bold claims, while others are just chilling and embracing things one moment at a time.</p><p>I cannot help but get angry at some of the ridiculous claims being made. Especially because they are so far removed from my own experience over the last two year, and because I see the damage these claims are already causing.</p><p>We have not even touched on issues such as security, data sovereignty, financial feasibility or any of the many other things that factor into this discussion. I might write some more about that at some point.</p><p>What I do know is that we need to call bullshit on things loudly and more often than we currently do. This tooling is not magic and making exaggerated claims that read like fan fiction is helping no one in the real world.</p><p>I cannot predict the future, but neither can anyone else! What I can do is point out the flawed logic of some of what we are being bombarded with at the present time. </p><p>Things could change tomorrow, there could be a breakthrough that completely changes things. When that day comes, we will adapt. </p><p>Until then, I will catch you on the flip side.</p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Anatomy of an AI application]]></title><description><![CDATA[A high level overview of an application that uses LLMs]]></description><link>https://blog.christoolivier.com/p/anatomy-of-an-ai-application</link><guid isPermaLink="false">https://blog.christoolivier.com/p/anatomy-of-an-ai-application</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Fri, 28 Jun 2024 09:15:15 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!iS-k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c9b3480-b92d-4570-909f-e1350a999884_1128x1668.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I have been hard at work building a non trivial AI application. Looking back at the road so far, I thought it good to write up some of my experiences and the mental model I have developed.</p><h1>What type of AI application are we talking about?</h1><p>There are a ton of different applications for LLMs and generative AI inside software. Some of what I am going to describe will not generalise to all uses of LLMs.</p><p>The specific application I am talking about is one where the user interacts with the application through a chat interface as an assistant or partner in thought.</p><p>What I have been working on allows someone in a B2B sales team to do some of the following:</p><ul><li><p>Ask for up to date information about a deal they are working on.</p></li><li><p>Request a review of the deal and get insights of how well this deal is going.</p></li><li><p>Analyse the 10Q filings and quarterly earnings call transcripts of a company they are interested in and identify topics or information they are interested in.</p></li><li><p>Bring back data from web searches about target accounts into the conversation and combine it with internal data from CRM and other sources to ask questions against.</p></li><li><p>Ask questions about internal documentation to help them better prepare for upcoming meetings. For example, have the assistant pull out relevant information on how best to position the company&#8217;s product against that of a competitor who they know are also targeting this account.</p></li></ul><p>The application is built in such a way that new skills can be added over time.</p><p>This type of application is very similar to the interfaces of chat apps such as ChatGPT and Google&#8217;s Gemini. However we can also embed it into line of business applications, making it more integrated into the daily lives of our users.</p><h1>A mental model</h1><p>LLMs might look like magic at times yet other times like utter nonsense spouting wastes of time. In order to make them useful it helps to have a good mental model of how these applications function.</p><p>You see the LLM part of the application is just the piece that handles the text translation from and to the user. Underneath the surface a ton of things are happening to make the LLM look intelligent.</p><p>My mental model for these types of applications is that each conversation is an A4 page being filled with user input, data returned from tools, and the LLM&#8217;s response based on the conversation on that page so far. Let me try and illustrate this graphically.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iS-k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c9b3480-b92d-4570-909f-e1350a999884_1128x1668.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iS-k!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c9b3480-b92d-4570-909f-e1350a999884_1128x1668.png 424w, https://substackcdn.com/image/fetch/$s_!iS-k!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c9b3480-b92d-4570-909f-e1350a999884_1128x1668.png 848w, https://substackcdn.com/image/fetch/$s_!iS-k!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c9b3480-b92d-4570-909f-e1350a999884_1128x1668.png 1272w, https://substackcdn.com/image/fetch/$s_!iS-k!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c9b3480-b92d-4570-909f-e1350a999884_1128x1668.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iS-k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c9b3480-b92d-4570-909f-e1350a999884_1128x1668.png" width="442" height="653.5957446808511" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9c9b3480-b92d-4570-909f-e1350a999884_1128x1668.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1668,&quot;width&quot;:1128,&quot;resizeWidth&quot;:442,&quot;bytes&quot;:176507,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!iS-k!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c9b3480-b92d-4570-909f-e1350a999884_1128x1668.png 424w, https://substackcdn.com/image/fetch/$s_!iS-k!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c9b3480-b92d-4570-909f-e1350a999884_1128x1668.png 848w, https://substackcdn.com/image/fetch/$s_!iS-k!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c9b3480-b92d-4570-909f-e1350a999884_1128x1668.png 1272w, https://substackcdn.com/image/fetch/$s_!iS-k!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c9b3480-b92d-4570-909f-e1350a999884_1128x1668.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>You have probably heard the word &#8220;context&#8221; mentioned a lot in Gen AI conversations. This entire A4 page that we build up serves as the context that we feed to our LLM for it to then generate an answer.</p><p>Now depending on how sophisticated you get you can send pieces of the A4 page to different LLMs tasked with specific functions. In these cases the pieces of the A4 page you send to the LLMs will be the context. As you will learn, context (<a href="https://sourcegraph.com/blog/cheating-is-all-you-need">or "cheating" as Steve Yegge puts it</a>) is absolutely king.</p><p>When I started out I had the idea that I might need to train my own LLMs to perform certain tasks. You might have also heard others talk about fine tuning models to perform better at certain tasks. I will do my best to give some useful thoughts on this. However for a lot, and I mean the vast majority of what you might be doing, context is going to be your secret sauce.</p><h1>Context is everything</h1><p>The majority of AI applications, at least the kind I am describing here, will do some form of RAG. When people think about RAG they normally think about Vector databases. This is however only one form of RAG.</p><p>RAG (retrievement augmented generation) is just one part of building up the context that an LLM is operating within. The context of an LLM comprises everything that is fed into the LLM that it operates on to give a response. This includes:</p><ul><li><p>The prompt</p></li><li><p>Chat history (input from the user and response from LLM)</p></li><li><p>Data from function/tool calls</p></li><li><p>Data fetched from Vector stores</p></li><li><p>Data fetched from any source system</p></li></ul><p>The better the information in the context the better the response from the LLM.</p><p>Not all parts of the context are visible to the end user. You might hide some of it in the background and only inject it into the prompt that you send to the LLM. This normally gives rise to that magical feeling of &#8220;The LLM knows things!&#8221;. The application looks magical to the user because they cannot see the big chunk of information the LLM is using to answer their questions. Yet every follow up question gets answered accurately.&nbsp;</p><p>With the increase in input token size for most models it is becoming much easier to build out large amounts of context. Some of these models now have a 1 million token input size which is a vast amount of text. Just keep in mind that throwing everything at the context is not always the best strategy and can lead to performance issues in both duration of LLM calls and the output it generates.</p><p>You can get sophisticated with your context strategy where you cherry pick the pieces of chat history and information you pass to the LLM on each conversation turn. This can lead to very good results.</p><h1>Functions/Tools can be more than meets the eye</h1><p>Function calling with LLMs can be so much more than what you see in the normal documentation. The documentation typically highlights that you provide the LLM with a list of function definitions and the LLM comes back to you with which function to call and what arguments to pass to the function&#8217;s parameters.</p><p>This hides the fact that the functions can be the entry point to a much more complex process. For example you could create entire Python packages in which you use additional LLM calls to perform actions all hidden behind an entry point function.</p><p>You also do not need to have the LLM choose the arguments for the parameters. You could simply let it choose which function to call and pass through whatever you would like to the function. This can be useful by not having to over engineer your main LLM&#8217;s prompt to deal with things like entity extraction for specific function parameters.</p><p>An example of this is the Deal Review package we use in our application. The main LLM only decides that the user is looking for a deal review, then we pass the user input with the rest of the desired context to the entry point function of the Deal Review package. The package uses a dedicated LLM call to do entity extraction, looking for the name of a deal or a particular ID in the user input. It then makes the necessary calls out to databases to get the deal information and feeds that into another LLM call that is specifically designed to perform a deal review. Once all of this has completed the resulting text is passed back to the user. This process neatly contains the deal review functionality and allows us to separate out the prompts used. It also allows us to test individual pieces of the application separately making life much easier.</p><p>As you can see a function call does not need to be just a simple function call.</p><h1>Training your own LLM or Fine Tuning a model</h1><p>Inevitably there will be discussions about training your own LLM or fine tuning a model.</p><p>The reality is that training your own LLM is not a trivial undertaking and is prohibitively expensive for most people and companies. We are talking millions of dollars, not to mention the massive amounts of data you need to collect in order to have any chance at producing something good. Training of foundation model LLMs is currently only feasible for very large organisations.</p><p>A much more reasonable option is to take an LLM (typically a foundation model) and fine tune it for a specific use case that you might have. This requires you to have a couple of hundred or thousand data points (depending on what you are trying to do), and does not come at the same cost as training an LLM from scratch. The fine tuned model can then be used for that nice use case where a foundation model with good prompt and context is just not giving you what you are after.</p><p>Most of the time you will end up with a normal LLM foundation model and the correct context giving you good results. If that fails you will move to a fine tuned model.</p><h1>User experience</h1><p>As with all applications the user only has the frontend interaction by which to judge the application.</p><p>For AI applications additional thought needs to be put into the user experience. We have all become accustomed to the ChatGPT style interfaces where you are having conversation with what looks like a single agent. It serves as your single point of contact through which you access all the other services, agents and tools. This feels like sitting in a meeting room with a single expert who relays messages to others behind the scenes and then answers you..</p><p>We started out in our team with such an interface before one of our team members highlighted how unnatural this is in the real world. We then pivoted to a user interface where the user can directly access the different agents and tools we provide if they choose to. This is the equivalent of being able to address everyone in the meeting room and having a discussion about the information they provide.<br><br>As an example the user could ask the Deal Review agent for a review of a specific deal. This agent then pushes a detailed set of deal information and its summary into the chat context. The conversation can then continue and asking other agents questions would result in them having access to this context as well.</p><p>All of the above functionality is thanks to some very nice UX design in the front end. We use &#8220;<strong>@&#8221;</strong> to allow a user to address an agent. If the agent requires more input from the user, such as to make a choice between a set of values, the UI keeps focus on that agent in the front end until it has all the user input it needs. This makes it an easy and familiar experience since most social media and messaging applications work this way. We found that once users were shown this functionality it was extremely natural to them.</p><p>We have also embedded our application within some of the applications used by our users. This has made it easy for them to click on a &#8220;AI Chat&#8221; icon next to an account which would launch the chat interface with the account&#8217;s information preloaded into the context with an account review provided straight away. They can then continue the conversation from that point.</p><p>Most of what we have done on the UX side is to observe how our users work normally, then sprinkle our AI application into their normal workflows. This has made ordinary tools seem magical.</p><h1>Technical components at a high level</h1><p>The final thing I want to do is give a high level view of the technical components that make up the solution. We started out with a more complicated set of components and over time simplified it. This gave us the ability to focus more on the application&#8217;s functionality than keeping an eye on the infrastructure.</p><p>Below is a diagram of the main components we currently have.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!y4jS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdc6ad7-9b5e-48b2-8e94-8130db2b5c6f_1438x984.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!y4jS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdc6ad7-9b5e-48b2-8e94-8130db2b5c6f_1438x984.png 424w, https://substackcdn.com/image/fetch/$s_!y4jS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdc6ad7-9b5e-48b2-8e94-8130db2b5c6f_1438x984.png 848w, https://substackcdn.com/image/fetch/$s_!y4jS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdc6ad7-9b5e-48b2-8e94-8130db2b5c6f_1438x984.png 1272w, https://substackcdn.com/image/fetch/$s_!y4jS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdc6ad7-9b5e-48b2-8e94-8130db2b5c6f_1438x984.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!y4jS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdc6ad7-9b5e-48b2-8e94-8130db2b5c6f_1438x984.png" width="1438" height="984" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0cdc6ad7-9b5e-48b2-8e94-8130db2b5c6f_1438x984.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:984,&quot;width&quot;:1438,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:177689,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!y4jS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdc6ad7-9b5e-48b2-8e94-8130db2b5c6f_1438x984.png 424w, https://substackcdn.com/image/fetch/$s_!y4jS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdc6ad7-9b5e-48b2-8e94-8130db2b5c6f_1438x984.png 848w, https://substackcdn.com/image/fetch/$s_!y4jS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdc6ad7-9b5e-48b2-8e94-8130db2b5c6f_1438x984.png 1272w, https://substackcdn.com/image/fetch/$s_!y4jS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0cdc6ad7-9b5e-48b2-8e94-8130db2b5c6f_1438x984.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2>Front End</h2><p>Our front end is created using Svelte and makes the assistant available in two ways. One is in a line of business application that each team member logs into to see their metrics for the accounts they are working on. The other is a pure chat interface application akin to ChatGPT and Gemini.</p><p>Having this ability to bring the assistant into a line of business application that is used daily has been phenomenal. It allows us to sprinkle some &#8220;magic&#8221; into the application. Users find it intuitive to click on the assistant button next to things like deals, accounts, graphs etc. to launch a conversation that is pre-populated with the context to make the conversation instantly useful.</p><p>Having this great UX for our users is arguably the most important part of the entire application. Without it the tool would just be another tool, instead it now feels like an enhancement to their workflow.</p><h2>Backend</h2><p>The backend API is the heart of our application. It is created using FastAPI and contains all of the logic we use to make the assistant work for the user.</p><p>We currently expose a set of endpoints from this API. Some of the more interesting ones are:</p><ul><li><p>Chat - Used to interact with the agent and have the chat loop execute.</p></li><li><p>Memory - Used to get, set and delete key value pairs in the memory that is then fed into the context. We have the concept of both chat and user memory. Chat memory only exists for the specific chat while user memory is available across all chats for a user.</p></li><li><p>History - Used to get a list of previous chats for a specific user or get the full message history for a specific chat</p></li></ul><p>The tools available to the agent are also part of the backend API. This is currently a python package in the backend codebase but could in future be moved into a separate project of its own that is imported into the backend.</p><h2><strong>Persistent Storage</strong></h2><p>We use a combination of Firestore and Google Cloud Storage as our storage layer for the application. We decided on this combination because of the ever increasing input token size for models (Gemini Pro 1.5 Flash can take 1 million input tokens) and the Firestore 1MB size limit on documents. GCS is used for pieces of information that cloud go beyond the 1MB Firestore limit.</p><p>GCS might seem an odd choice but the read and write latency proved fast enough in our testing and it vastly simplified our stack.</p><h2>Sources</h2><p>Our sources of data range from 3rd party APIs and BigQuery to internal systems. Most of these sources are used by our tools that the agent has access to. Here is a brief overview of what we use:</p><ul><li><p>BigQuery (with BI Engine enabled) - Used as the source for some specific sets of data that is only available and curated in BigQuery.</p></li><li><p>SerpAPI - Used for web searches.</p></li><li><p>Vector store - Used to allow searching over all of the organisation&#8217;s internal PDFs and documents that serves as its knowledge base.</p></li></ul><h1>Final thoughts</h1><p>I hope this overview has given you some insight into an example of an AI application. It is important to note this is only one example built against specific use cases. Not all AI applications need to be a chat bot, in fact I see more applications for LLMs in workflows and inside regular applications than I do for the pure chat interface type.<br><br>While the tech is not perfect it is certainly useful. It is up to us to remove the hype and focus on applying it correctly. Once you cut through the noise you realise that it is just another tool in your toolbox, it does not replace the toolbox.</p>]]></content:encoded></item><item><title><![CDATA[LLMs and function/tool calling]]></title><description><![CDATA[What is it and how to achieve it with any LLM, not just those that support it in their API/SDK]]></description><link>https://blog.christoolivier.com/p/llms-and-functiontool-calling</link><guid isPermaLink="false">https://blog.christoolivier.com/p/llms-and-functiontool-calling</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Thu, 28 Mar 2024 09:07:19 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/05489428-5f41-4e94-8c47-c9e5f98a1a0c_1024x1024.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Function calling with LLMs cause a lot of confusion. The overhyped marketing combined with the YouTube tech-bros is not helping the situation.</p><p>What I am going to explore in this post is what it is, how it works, and how to achieve this with any LLM regardless of if its API or SDK supports it.</p><h1>What is function/tool calling with LLMs</h1><p>LLMs are next token predictors. That&#8217;s it. Nothing more, and nothing less. Based on the input (prompt) it will predict the next most likely token, then re-evaluate the original input + predicted token to predict the next token.</p><p>This loop runs until the LLM reaches its end where there are no more tokens to predict, or it has reached the number of output tokens it is allowed to produce.</p><p>Again, that&#8217;s it, nothing more and nothing less. LLMs DO NOT call functions. Regardless of what you have been told, they simply don&#8217;t. If your LLM API/SDK or model of choice calls functions for you then there is a layer of software wrapped around it that is taking care of this and invoking the function(s).</p><p>When you dig into the documentation for <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling">Google&#8217;s Vertex AI</a> and that of <a href="https://platform.openai.com/docs/guides/function-calling">OpenAI</a> this becomes clear. What is described in both is that the calling client (your software, ChatGPT, or whatever service you are playing with) sends to the LLM a list of the functions that are available, a description of each of them and a description of the parameters each function takes. The LLM will then return a response that states which function to call and which arguments to pass to the parameters of the function based on the prompt it received.</p><p>Once you receive this response from the LLM the calling client is responsible for executing the function and then returning the result back to the LLM as part of an updated prompt, now containing the output from the function. The LLM then returns a response by either specifying another function to call or returning a response that can be sent to the user by the calling client.</p><p>Here is the diagram from Google Cloud <a href="https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling#sequence_of_interactions">Vertex AI showing this flow</a>:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BkVs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5ff291d-c42f-424b-93bd-61db96e82c8c_516x566.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BkVs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5ff291d-c42f-424b-93bd-61db96e82c8c_516x566.png 424w, https://substackcdn.com/image/fetch/$s_!BkVs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5ff291d-c42f-424b-93bd-61db96e82c8c_516x566.png 848w, https://substackcdn.com/image/fetch/$s_!BkVs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5ff291d-c42f-424b-93bd-61db96e82c8c_516x566.png 1272w, https://substackcdn.com/image/fetch/$s_!BkVs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5ff291d-c42f-424b-93bd-61db96e82c8c_516x566.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BkVs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5ff291d-c42f-424b-93bd-61db96e82c8c_516x566.png" width="516" height="566" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d5ff291d-c42f-424b-93bd-61db96e82c8c_516x566.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:566,&quot;width&quot;:516,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Multi-turn Function Calling Interaction&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Multi-turn Function Calling Interaction" title="Multi-turn Function Calling Interaction" srcset="https://substackcdn.com/image/fetch/$s_!BkVs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5ff291d-c42f-424b-93bd-61db96e82c8c_516x566.png 424w, https://substackcdn.com/image/fetch/$s_!BkVs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5ff291d-c42f-424b-93bd-61db96e82c8c_516x566.png 848w, https://substackcdn.com/image/fetch/$s_!BkVs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5ff291d-c42f-424b-93bd-61db96e82c8c_516x566.png 1272w, https://substackcdn.com/image/fetch/$s_!BkVs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5ff291d-c42f-424b-93bd-61db96e82c8c_516x566.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>And here is an extract of the <a href="https://platform.openai.com/docs/guides/function-calling">OpenAI API documentation</a> describing exactly the same steps.</p><blockquote><p>The basic sequence of steps for function calling is as follows:</p><p>1. Call the model with the user query and a set of functions defined in the functions parameter.</p><p>2. The model can choose to call one or more functions; if so, the content will be a stringified JSON object adhering to your custom schema (note: the model may hallucinate parameters).</p><p>3. Parse the string into JSON in your code, and call your function with the provided arguments if they exist.</p><p>4. Call the model again by appending the function response as a new message, and let the model summarize the results back to the user.</p></blockquote><p>So now you know that there is no magic! This is good old fashioned software development with some LLM magic sprinkled in.</p><p><strong>NB!</strong> <strong>Remember that LLMs will hallucinate, and this also applies to the arguments it specifies for the parameters of the function call. Code accordingly!</strong></p><h1>How to use any LLM to call functions/tools</h1><p>So now that we know how it works, what is stopping us from using any other LLM that does not have a <code>function</code> or <code>tool</code> calling capability to perform the same action? The answer is nothing.</p><p>Almost all the SDKs and LLM APIs supporting this require you to send the definition of your functions/tools as a JSON Schema object or an OpenAPI Schema. So given that most of the LLMs are trained on loads of JSON we can take advantage of this by passing our list of tools to the LLM as a JSON object and specify a JSON Schema object that we would like to get back.</p><p>So, our plan is:</p><ol><li><p>Create a python dictionary that contains the name of the function and a description of it and its parameters.</p></li><li><p>Pass this to the LLM with a prompt instructing it to call functions when it requires to.</p></li><li><p>Receive the response and process it, calling any functions it specifies with the arguments it specifies for each parameter.</p></li><li><p>Return the result of a function call to the LLM as an updated prompt.</p></li><li><p>If not function call is received from the LLM but instead a message to pass to the user then we send this output to the user.</p></li><li><p>Profit! (ok not really, but you get the idea :D )</p></li></ol><p>I will be using <code>text-bison</code> on Google Cloud Vertex AI to try the above out. This is a TextGeneration model type that does not have function calling as a capability. I will also be using my <a href="https://github.com/christo-olivier/modelsmith">Modelsmith</a> python package to helps get a structured response from the LLM.</p><p>Lets look at an example. (The code for each screenshot can be found <a href="https://gist.github.com/christo-olivier/032b278401f1f29fd52d553d8eba1c9d">here on GitHub</a>)</p><h2>The functions</h2><p>We set up two functions for our LLM. These are very simple functions to help keep the example as short as possible. We include a print statement to show when the function is executed and what arguments were passed to it.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!n-Ns!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fe7e4ec-ee9b-44cd-8a46-e3f397e6ba61_1628x1252.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!n-Ns!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fe7e4ec-ee9b-44cd-8a46-e3f397e6ba61_1628x1252.png 424w, https://substackcdn.com/image/fetch/$s_!n-Ns!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fe7e4ec-ee9b-44cd-8a46-e3f397e6ba61_1628x1252.png 848w, https://substackcdn.com/image/fetch/$s_!n-Ns!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fe7e4ec-ee9b-44cd-8a46-e3f397e6ba61_1628x1252.png 1272w, https://substackcdn.com/image/fetch/$s_!n-Ns!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fe7e4ec-ee9b-44cd-8a46-e3f397e6ba61_1628x1252.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!n-Ns!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fe7e4ec-ee9b-44cd-8a46-e3f397e6ba61_1628x1252.png" width="1456" height="1120" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0fe7e4ec-ee9b-44cd-8a46-e3f397e6ba61_1628x1252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1120,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:285365,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!n-Ns!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fe7e4ec-ee9b-44cd-8a46-e3f397e6ba61_1628x1252.png 424w, https://substackcdn.com/image/fetch/$s_!n-Ns!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fe7e4ec-ee9b-44cd-8a46-e3f397e6ba61_1628x1252.png 848w, https://substackcdn.com/image/fetch/$s_!n-Ns!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fe7e4ec-ee9b-44cd-8a46-e3f397e6ba61_1628x1252.png 1272w, https://substackcdn.com/image/fetch/$s_!n-Ns!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fe7e4ec-ee9b-44cd-8a46-e3f397e6ba61_1628x1252.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Next up is doing some prep work to provide the LLM with a list of function it can call and creating a dictionary with the function name mapped to the function object. This dictionary is there to help us avoid a bunch of if statements, so is purely for convenience.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TVwd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540eb49-3eb8-4d03-bed5-a263dea20449_1206x972.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TVwd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540eb49-3eb8-4d03-bed5-a263dea20449_1206x972.png 424w, https://substackcdn.com/image/fetch/$s_!TVwd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540eb49-3eb8-4d03-bed5-a263dea20449_1206x972.png 848w, https://substackcdn.com/image/fetch/$s_!TVwd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540eb49-3eb8-4d03-bed5-a263dea20449_1206x972.png 1272w, https://substackcdn.com/image/fetch/$s_!TVwd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540eb49-3eb8-4d03-bed5-a263dea20449_1206x972.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TVwd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540eb49-3eb8-4d03-bed5-a263dea20449_1206x972.png" width="1206" height="972" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5540eb49-3eb8-4d03-bed5-a263dea20449_1206x972.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:972,&quot;width&quot;:1206,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:174228,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!TVwd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540eb49-3eb8-4d03-bed5-a263dea20449_1206x972.png 424w, https://substackcdn.com/image/fetch/$s_!TVwd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540eb49-3eb8-4d03-bed5-a263dea20449_1206x972.png 848w, https://substackcdn.com/image/fetch/$s_!TVwd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540eb49-3eb8-4d03-bed5-a263dea20449_1206x972.png 1272w, https://substackcdn.com/image/fetch/$s_!TVwd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5540eb49-3eb8-4d03-bed5-a263dea20449_1206x972.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I am using <code>inspect.getdoc</code> to get the docstring of the functions and add it to the description. For the parameters I use the Pydantic <code>TypeAdapter</code> class to get the JSON schema of the function&#8217;s parameters. This saves us having to build that out ourselves.</p><h2>The response object</h2><p>Now that we have our function related work out of the way it is time to turn our attention to the Pydantic model we want to get back from the LLM as a response. In this example it is a combination of either a <code>message</code> (if the LLM has enough information to answer the question) or a <code>function</code> (that needs to be called) and <code>arguments</code> (for us to use when calling the function).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ojJK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0fbae1-4c56-4986-b4b5-23e2857a0858_1628x1132.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ojJK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0fbae1-4c56-4986-b4b5-23e2857a0858_1628x1132.png 424w, https://substackcdn.com/image/fetch/$s_!ojJK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0fbae1-4c56-4986-b4b5-23e2857a0858_1628x1132.png 848w, https://substackcdn.com/image/fetch/$s_!ojJK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0fbae1-4c56-4986-b4b5-23e2857a0858_1628x1132.png 1272w, https://substackcdn.com/image/fetch/$s_!ojJK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0fbae1-4c56-4986-b4b5-23e2857a0858_1628x1132.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ojJK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0fbae1-4c56-4986-b4b5-23e2857a0858_1628x1132.png" width="1456" height="1012" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1d0fbae1-4c56-4986-b4b5-23e2857a0858_1628x1132.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1012,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:245932,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ojJK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0fbae1-4c56-4986-b4b5-23e2857a0858_1628x1132.png 424w, https://substackcdn.com/image/fetch/$s_!ojJK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0fbae1-4c56-4986-b4b5-23e2857a0858_1628x1132.png 848w, https://substackcdn.com/image/fetch/$s_!ojJK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0fbae1-4c56-4986-b4b5-23e2857a0858_1628x1132.png 1272w, https://substackcdn.com/image/fetch/$s_!ojJK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d0fbae1-4c56-4986-b4b5-23e2857a0858_1628x1132.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The descriptions on the Pydantic model&#8217;s properties are important as this is fed into the LLM&#8217;s prompt and helps give additional information of what is expected from the LLM.</p><h2>The prompt</h2><p>Moving on we look at the prompt that we will use. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!f6GS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff188f6b5-81cc-44ed-8e1a-d45ee20c6a2b_1692x1452.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!f6GS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff188f6b5-81cc-44ed-8e1a-d45ee20c6a2b_1692x1452.png 424w, https://substackcdn.com/image/fetch/$s_!f6GS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff188f6b5-81cc-44ed-8e1a-d45ee20c6a2b_1692x1452.png 848w, https://substackcdn.com/image/fetch/$s_!f6GS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff188f6b5-81cc-44ed-8e1a-d45ee20c6a2b_1692x1452.png 1272w, https://substackcdn.com/image/fetch/$s_!f6GS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff188f6b5-81cc-44ed-8e1a-d45ee20c6a2b_1692x1452.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!f6GS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff188f6b5-81cc-44ed-8e1a-d45ee20c6a2b_1692x1452.png" width="1456" height="1249" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f188f6b5-81cc-44ed-8e1a-d45ee20c6a2b_1692x1452.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1249,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:344510,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!f6GS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff188f6b5-81cc-44ed-8e1a-d45ee20c6a2b_1692x1452.png 424w, https://substackcdn.com/image/fetch/$s_!f6GS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff188f6b5-81cc-44ed-8e1a-d45ee20c6a2b_1692x1452.png 848w, https://substackcdn.com/image/fetch/$s_!f6GS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff188f6b5-81cc-44ed-8e1a-d45ee20c6a2b_1692x1452.png 1272w, https://substackcdn.com/image/fetch/$s_!f6GS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff188f6b5-81cc-44ed-8e1a-d45ee20c6a2b_1692x1452.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The prompt makes use of jinja templating to add the functions, response model, and finally the context to the prompt. </p><p>There is so much that can be said about prompts and <em>&#8220;prompt engineering&#8221;</em>, but I will leave that for another post. Suffice to say that it is less engineering and more magical trial and error.</p><h2>The application logic</h2><p>So, all that is left for us to do is put in some application logic to have the user ask a question and see if we get our functions executed and the question answered based on the data they return.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!cGjc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F293091d6-940d-4078-929f-66bcf103ac13_1582x2052.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!cGjc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F293091d6-940d-4078-929f-66bcf103ac13_1582x2052.png 424w, https://substackcdn.com/image/fetch/$s_!cGjc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F293091d6-940d-4078-929f-66bcf103ac13_1582x2052.png 848w, https://substackcdn.com/image/fetch/$s_!cGjc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F293091d6-940d-4078-929f-66bcf103ac13_1582x2052.png 1272w, https://substackcdn.com/image/fetch/$s_!cGjc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F293091d6-940d-4078-929f-66bcf103ac13_1582x2052.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!cGjc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F293091d6-940d-4078-929f-66bcf103ac13_1582x2052.png" width="1456" height="1889" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/293091d6-940d-4078-929f-66bcf103ac13_1582x2052.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1889,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:394737,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!cGjc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F293091d6-940d-4078-929f-66bcf103ac13_1582x2052.png 424w, https://substackcdn.com/image/fetch/$s_!cGjc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F293091d6-940d-4078-929f-66bcf103ac13_1582x2052.png 848w, https://substackcdn.com/image/fetch/$s_!cGjc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F293091d6-940d-4078-929f-66bcf103ac13_1582x2052.png 1272w, https://substackcdn.com/image/fetch/$s_!cGjc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F293091d6-940d-4078-929f-66bcf103ac13_1582x2052.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Let&#8217;s break the application logic down according to the main parts.</p><h4>Lines 1 - 5</h4><p>Using <a href="https://github.com/christo-olivier/modelsmith">Modelsmith</a>, we create our instance of <code>Forge</code> specifying the <code>text-bison</code> model and pass it the <code>LlmResponse</code> as our response model, and also passing in our custom prompt.</p><h4>Lines 7 - 10</h4><p>We create our <code>prompt_values</code> dictionary and pass in the functions available as JSON. The <code>context</code> is initialised to <code>None</code> as we will only use it to capture the output of a function call when that is required.</p><h4>Line 11 </h4><p>The user is asked for input which we will use as the input to the prompt sent to the LLM.</p><h4>Lines 14 to 44 </h4><p>This is the main body of our application loop.</p><p>First, we initialise our <code>counter</code> as we are only allowing two iterations of the logic. This is done to ensure that we do not end up in an infinite loop. Remember, the user&#8217;s input is sent as part of the prompt and as we have not built any safeguards into this example the wrong user input can cause the application to loop indefinitely. (Hello prompt injection!)</p><p>We check to see if we have received context, and we add it to the <code>prompt_values</code> if we did.</p><p>Next up we generate our response from the LLM, passing in all the arguments needed.</p><p>Based on the response from the LLM we go and retrieve the function to call, if one was specified. You can see the use of our <code>FUNCTION_MAP</code> constant in getting the function to call instead of using <code>if</code> statements. Once a function has been called the response from it is assigned to the <code>context</code> variable which will be added to the <code>prompt_values</code> in the next iteration of the loop and thus make it into the prompt going to the LLM.</p><p>If we have a message back from the LLM instead of a function call, then we print that out to the user and we break out of the loop.</p><p>Otherwise, we go around once again until we break out of the loop without returning a message from the LLM to the user. If this happens, we print a simple message to ask them to rephrase their question.</p><h2>The results</h2><p>Let&#8217;s take our toy example for a spin. First up we will ask it for the weather in Amsterdam.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-zgC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0367817-4b09-4a51-be2d-a6865566458b_566x60.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-zgC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0367817-4b09-4a51-be2d-a6865566458b_566x60.png 424w, https://substackcdn.com/image/fetch/$s_!-zgC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0367817-4b09-4a51-be2d-a6865566458b_566x60.png 848w, https://substackcdn.com/image/fetch/$s_!-zgC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0367817-4b09-4a51-be2d-a6865566458b_566x60.png 1272w, https://substackcdn.com/image/fetch/$s_!-zgC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0367817-4b09-4a51-be2d-a6865566458b_566x60.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-zgC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0367817-4b09-4a51-be2d-a6865566458b_566x60.png" width="566" height="60" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c0367817-4b09-4a51-be2d-a6865566458b_566x60.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:60,&quot;width&quot;:566,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:12739,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!-zgC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0367817-4b09-4a51-be2d-a6865566458b_566x60.png 424w, https://substackcdn.com/image/fetch/$s_!-zgC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0367817-4b09-4a51-be2d-a6865566458b_566x60.png 848w, https://substackcdn.com/image/fetch/$s_!-zgC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0367817-4b09-4a51-be2d-a6865566458b_566x60.png 1272w, https://substackcdn.com/image/fetch/$s_!-zgC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc0367817-4b09-4a51-be2d-a6865566458b_566x60.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Next up let&#8217;s ask for local news in Manchester.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aRiW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa66ebd9c-4123-48f7-931c-88c3e9cff5a6_660x60.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aRiW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa66ebd9c-4123-48f7-931c-88c3e9cff5a6_660x60.png 424w, https://substackcdn.com/image/fetch/$s_!aRiW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa66ebd9c-4123-48f7-931c-88c3e9cff5a6_660x60.png 848w, https://substackcdn.com/image/fetch/$s_!aRiW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa66ebd9c-4123-48f7-931c-88c3e9cff5a6_660x60.png 1272w, https://substackcdn.com/image/fetch/$s_!aRiW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa66ebd9c-4123-48f7-931c-88c3e9cff5a6_660x60.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aRiW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa66ebd9c-4123-48f7-931c-88c3e9cff5a6_660x60.png" width="660" height="60" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a66ebd9c-4123-48f7-931c-88c3e9cff5a6_660x60.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:60,&quot;width&quot;:660,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:15475,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!aRiW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa66ebd9c-4123-48f7-931c-88c3e9cff5a6_660x60.png 424w, https://substackcdn.com/image/fetch/$s_!aRiW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa66ebd9c-4123-48f7-931c-88c3e9cff5a6_660x60.png 848w, https://substackcdn.com/image/fetch/$s_!aRiW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa66ebd9c-4123-48f7-931c-88c3e9cff5a6_660x60.png 1272w, https://substackcdn.com/image/fetch/$s_!aRiW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa66ebd9c-4123-48f7-931c-88c3e9cff5a6_660x60.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>And there you have it. Function calling with a model that does not support it.</p><h1>Wrap-up and caveats</h1><p>We looked at function calling and realised it is simply an LLM telling us what function to call with which arguments for the function&#8217;s parameters. Hopefully demystifying function/tool calling in the process.</p><p>We then built a very basic example script to test out function calling with a model that does not have this capability in its SDK or API.</p><p>This was a very basic example to illustrate the concept. While it works it does not mean that it will always work reliably. This is the catch. When it comes to LLMs you are looking for reliability and predictability above all else. You need deterministic behaviour. Models that have function/tool calling as part of their SDK and API are typically trained to perform function calling, while other models are not. Hence, they will give better and more consistent results.</p><p>The usual caveats apply here, code defensively when it comes to LLMs. You are placing a non-deterministic element that makes up results inside your application logic and should make sure that it cannot cause problems.</p><p>I hope this has been a helpful example to show the concept. As always leave a comment or reach out to me if you would like to discuss this further.<br><br><em>If you missed it earlier, the code for the example script can be found here <a href="https://gist.github.com/christo-olivier/032b278401f1f29fd52d553d8eba1c9d">on GitHub</a>.</em></p>]]></content:encoded></item><item><title><![CDATA[Seamless settings management with Pydantic and Google Cloud Secret Manager]]></title><description><![CDATA[Pydantic's settings management capabilities make configuring your application easy across different environments, and you can easily extend it to new sources.]]></description><link>https://blog.christoolivier.com/p/seamless-settings-management-with</link><guid isPermaLink="false">https://blog.christoolivier.com/p/seamless-settings-management-with</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Tue, 05 Sep 2023 19:45:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/nSSoTRkEPLk" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There are a few tried and trusted ways to pass settings to your Python code. Environment variables are one of those, `.env` files are another and command line arguments are yet another.</p><p>Many developers create custom solutions to solve the challenge of settings management. However, the well-known Pydantic package provides a solid implementation for settings management that we can extend when needed.</p><p>Using Pydantic's settings management, you can have a standardised solution across all your projects with the added benefit of quickly extending it to sources not supported out of the box.</p><p>It is common to use Google Cloud Secret Manager when deploying solutions to Google Cloud. It is unfortunately not supported as standard in Pydantic's settings management tooling. To demonstrate how you can extend Pydantic's settings management to connect to Google Cloud Secret Manager, I have created a GitHub repository with an example and a YouTube video to provide a quick overview. I hope you find this as useful as I have.</p><p><strong>GitHub Repo:</strong> <a href="https://github.com/christo-olivier/pydantic-google-secrets">https://github.com/christo-olivier/pydantic-google-secrets</a></p><div id="youtube2-nSSoTRkEPLk" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;nSSoTRkEPLk&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/nSSoTRkEPLk?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[Deploying Streamlit applications to Google Cloud]]></title><description><![CDATA[A step-by-step guide on how to deploy and secure Streamlit applications in Google Cloud, ensuring only people in your organisation can access them]]></description><link>https://blog.christoolivier.com/p/deploying-streamlit-applications</link><guid isPermaLink="false">https://blog.christoolivier.com/p/deploying-streamlit-applications</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Tue, 29 Aug 2023 08:05:14 GMT</pubDate><enclosure url="https://substackcdn.com/image/youtube/w_728,c_limit/aJKkla4U47U" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There is no denying it, Streamlit is an extremely useful tool. It allows those of us without web development skills to put good looking data apps in the hands of our users. It can be a game changer for data teams, allowing them to create all manner of front-end applications to the data platform for their user community.</p><p>One thing that has always been a bit difficult to get a clear direction on is how to deploy Streamlit applications to your own cloud platform and secure them. Most of the content out there always deploy public facing apps. Most teams would like to limit these applications to only be accessible to users in their organisation.</p><p>I have put together a step-by-step guide on how to do just that on Google Cloud Platform. Get yourself a cup of coffee or tea, I hope you enjoy it.</p><div id="youtube2-aJKkla4U47U" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;aJKkla4U47U&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/aJKkla4U47U?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[Thoughts on Fivetran and similar tools]]></title><description><![CDATA[I saw this post on LinkedIn this morning. I could not help but have my thoughts run when I saw it. The meme has done the rounds in many forms, but what struck me in this incarnation was the text and comments around Fivetran and Airbyte. As you know, I have some experience with Airbyte's open-source version but have yet to use their cloud offering. I have written a few connectors for it and have Airbyte's open-source solution on multiple projects. I have also worked with Fivetran on a few projects. Naturally, I have some *opinions* on both.]]></description><link>https://blog.christoolivier.com/p/thoughts-on-fivetran-and-similar</link><guid isPermaLink="false">https://blog.christoolivier.com/p/thoughts-on-fivetran-and-similar</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Tue, 18 Apr 2023 10:40:28 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/002f5734-9169-4de4-9c2f-243ab7cb715b_869x530.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I saw this <a href="https://www.linkedin.com/posts/yelhassani_hottesttrends-dbt-redshift-activity-7053669646935158784-z9jn?utm_source=share&amp;utm_medium=member_desktop">post</a> on LinkedIn this morning. I could not help but have my thoughts run when I saw it. The meme has done the rounds in many forms, but what struck me in this incarnation was the text and comments around Fivetran and Airbyte.</p><p>As you know, I have some experience with Airbyte's open-source version but have yet to use their cloud offering. I have written a few connectors for it and have Airbyte's open-source solution on multiple projects. I have also worked with Fivetran on a few projects. Naturally, I have some <em>opinions</em> on both.</p><p>The author of the post correctly states that Airbyte has challenges. The project has many open issues, and most relate to the connectors provided. Now many of these connectors are open-source contributions, and hence there are some rough edges. It also makes it rather challenging to build connectors for many of the targeted SaaS tools, as you need access to them. Something you usually only get when you are paying for them. Few people can pay for them personally as they are tools made for business use.</p><p>I am by no means making excuses for Airbyte. I want to highlight why you will find open issues and encounter rough edges. What is good, though, is that when you run into these, you can look at the code and fix things, typically quite quickly. Again, this comes from my experience having done that for clients.</p><p>Now Fivetran is a whole other beast. It does a fantastic job of getting your data into your cloud data warehouse. But there are a few gotchas that you should be aware of.</p><ol><li><p>It will not load the raw data from the source for you. It will be loading data based on its own data model for that source. These models make sense as they are relational models for otherwise messy nested JSON from the source API. However, it means the data is processed before you receive it and in a structure that won't match the raw data. When you decide to one day move off of Fivetran you have a challenge. You don't have the original raw data, your existing pipelines are using the Fivetran relational model, and thus and thus you cannot simply swap Fivetran out with another tool. You will need to do a fair bit of work to replace it.</p></li><li><p>Fivetran sometimes does not provide all the raw data from a source's API. This has happened to my clients for sources like Shopify and Klaviyo. Sometimes this is a bug, which can take a lot of time to get fixed, and sometimes it is due to an API change stuck somewhere on the Fivetran team's backlog. </p></li><li><p>Security is another part of this that gives me pause. You effectively have to provide a Fivetran account access to your sources and cloud data warehouse for it to write data to it. This means your data flows through a 3rd party's systems whom you need to trust will not be doing nefarious things with that data. Call me paranoid, but there is a big difference between being able to look at the code being used and trusting that you will do the right thing as you specify in your EULA. (Just think about the recent issues with Tesla <a href="https://www.reuters.com/technology/tesla-workers-shared-sensitive-images-recorded-by-customer-cars-2023-04-06/">sharing sensitive images</a> recorded by customer cars)</p></li><li><p>Pricing for Fivetran can be somewhat obscure and work out quite expensive when you start to scale. I say pricing can be obscure because you are charged for the number of records you sync, but you can *negotiate* pricing in certain circumstances with them. </p></li></ol><p>My experience with Fivetran has made me very cautious of closed-source tools for data ingestion. You get an incredibly fast start when you use Fivetran or similar tools, but it comes at a cost. The cost of being in control of your Extract and Load part of your platform and being at the mercy of someone else's backlog, priorities, and pricing. </p><p>Does this only apply to Fivetran? No, it certainly does not; it applies to any tool you use to ingest data and includes things such as the SaaS version of Airbyte. </p><p>Be vigilant when making your tooling choices. Ensure you understand the price you will pay further down the line for the control you give up. The speed you gain at the start might be dwarfed by the effort needed to change later.</p>]]></content:encoded></item><item><title><![CDATA[Architecture of a B2B marketing insights platform]]></title><description><![CDATA[In my previous post, I introduced the SquareOne solution.]]></description><link>https://blog.christoolivier.com/p/architecture-of-a-b2b-marketing-insights</link><guid isPermaLink="false">https://blog.christoolivier.com/p/architecture-of-a-b2b-marketing-insights</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Wed, 29 Jun 2022 12:33:18 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!gKzb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F10d9cb69-dbb1-4e56-9980-18f2bb6dd050_961x489.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In my&nbsp;<a href="https://blog.christoolivier.com/p/stop-trying-to-adapt-to-marketing">previous post</a>, I introduced the SquareOne solution. This time round, I want to look at the high-level architecture that underpins it.</p><p>Below is a diagram showing the main components. One of the main goals was to keep the solution as cloud agnostic as possible. It is currently implemented on the Google Cloud Platform, but the GCP components can easily be changed for others on another platform.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gKzb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F10d9cb69-dbb1-4e56-9980-18f2bb6dd050_961x489.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gKzb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F10d9cb69-dbb1-4e56-9980-18f2bb6dd050_961x489.png 424w, https://substackcdn.com/image/fetch/$s_!gKzb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F10d9cb69-dbb1-4e56-9980-18f2bb6dd050_961x489.png 848w, https://substackcdn.com/image/fetch/$s_!gKzb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F10d9cb69-dbb1-4e56-9980-18f2bb6dd050_961x489.png 1272w, https://substackcdn.com/image/fetch/$s_!gKzb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F10d9cb69-dbb1-4e56-9980-18f2bb6dd050_961x489.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gKzb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F10d9cb69-dbb1-4e56-9980-18f2bb6dd050_961x489.png" width="961" height="489" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/10d9cb69-dbb1-4e56-9980-18f2bb6dd050_961x489.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:489,&quot;width&quot;:961,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:74082,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gKzb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F10d9cb69-dbb1-4e56-9980-18f2bb6dd050_961x489.png 424w, https://substackcdn.com/image/fetch/$s_!gKzb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F10d9cb69-dbb1-4e56-9980-18f2bb6dd050_961x489.png 848w, https://substackcdn.com/image/fetch/$s_!gKzb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F10d9cb69-dbb1-4e56-9980-18f2bb6dd050_961x489.png 1272w, https://substackcdn.com/image/fetch/$s_!gKzb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F10d9cb69-dbb1-4e56-9980-18f2bb6dd050_961x489.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The architecture is deceptively simple yet extremely powerful. It has a low cost of ownership and does not need a large engineering staff to operate and maintain.</p><p>Let's look at some of the chosen components.</p><h1>Data Extraction</h1><p>One of the most time-consuming parts of any data solution is extracting data from the various source systems that feed it. Marketing and Sales teams use many software products, most of which are SaaS solutions.</p><p>Instead of writing custom code to pull data from APIs, I chose to use Airbyte instead. More specifically, the open-source version of Airbyte. This version allows you to spin up Airbyte in your cloud environment, keeping all your data under your control. It is an essential consideration for many organisations with a strict security vetting process for new software, especially SaaS tools. There is also Airbyte Cloud if you prefer not to manage your own instance.</p><p>Airbyte provides many connectors to SaaS products and other data sources such as databases, Kafka, cloud storage etc. There is also an active open source community contributing to the connectors, with improvements and new connectors coming through almost daily. You can find the current list of supported connectors&nbsp;<a href="https://airbyte.com/connectors">here</a>.</p><p>Building your own connectors is also a straightforward process. You can use any language you want since connectors run as containers on either Docker or Kubernetes. I have created a few custom connectors so far and will be looking to contribute these back to the Airbyte community.</p><p>All data from source systems is landed in its raw format in cloud storage which provides a cost-effective historical store of data and does not tie the data into any particular tool for processing.</p><h1>Data Transformation</h1><p>The data stored in cloud storage is modelled and transformed into activities and attributes using DBT and Google BigQuery. BigQuery allows for serverless SQL against cloud storage, while DBT provides the data transformation, testing, and documentation framework.</p><p>I use a&nbsp;<a href="https://github.com/christo-olivier/dbt_script_materialization">custom DBT macro</a>&nbsp;that allows you to write SQL scripts or Data Definition Language queries instead of the regular SELECT statements DBT recommends. It is used only where necessary, such as where I need to define external tables against cloud storage or where complex SQL is required to generate an attribute.</p><p>I cannot overstate the value of DBT as it is an absolute game-changer for analytics engineering.</p><p>The solution uses the open-source version of DBT, which we run on our GKE cluster. As with Airbyte, a DBT Cloud option is available if you prefer not to manage it yourself.</p><p>BigQuery provides the processing power to turn large amounts of raw data into activities and attributes. The attribute calculations are typically complex analytics queries over large volumes of data. It is difficult to beat the flexibility and value of processing large amounts of structured and semi-structured data with BigQuery, especially for this use case.</p><p>Data products built from the activities and attributes can be kept in BigQuery or could be created and stored in a different tool depending on requirements.</p><h1>Orchestration</h1><p>For orchestration, I use Google Cloud Composer (Apache Airflow). Airflow provides a lot of operators out of the box that connects to all the components in the solution. With Cloud Composer being a Google-hosted version of Apache Airflow, you avoid having the overhead of managing your Airflow cluster.</p><p>It is important to note that Airflow is used purely as an orchestrator. No custom python tasks or applications run on Airflow itself. Custom applications or python scripts are packaged and launched on our GKE cluster.</p><p>Ensuring Airflow only performs orchestration makes testing custom scripts and applications easier. It also allows you to move to a different orchestration tool if required with minimal effort.</p><h1>Flexible Compute</h1><p>A Google Kubernetes Engine (GKE) Autopilot cluster is used for all custom applications and tasks. GKE Autopilot charges for the duration your containers run and scales up and down to meet demand automatically.</p><p>The flexibility and scale it provides and the cost savings of not being charged for idle resources help make the solution cost-effective. There is no "ops" or managing of the cluster; simply vast scalable compute resource you can tap into when needed.</p><h1>Other Components</h1><p>There are, of course, other components on the periphery not included in the diagram. These would be for managing Infrastructure as Code (IaC) and CI/CD.</p><p>For our Infrastructure as Code, we use the open-source version of Terraform for the same reasons mentioned earlier on other components.</p><p>For our CI/CD, we currently make use of Github Actions. The steps can be performed via any other CI/CD pipeline tool, so there is nothing that ties the solution to Github.</p><h1>Final Thoughts</h1><p>The architecture of the solution is straightforward by design. It did not start out like this. There was a period when additional components were used, such as Kafka, custom integration code, and apps running on Kubernetes.</p><p>Those components only added complexity and maintenance overhead while delivering no real additional business value for the problem being solved.</p><p>The guiding principle of "delivering maximum business value from development effort while keeping ownership cost as low as possible" helped guide the solution's architecture to what it is currently.</p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Stop trying to adapt to marketing data tools]]></title><description><![CDATA[How do we prove marketing's impact on sales? How does the buyer journey for manufacturing customers on the US east coast differ from those in Germany? What were the marketing and sales touch points before we won or lost an opportunity? Are we wasting advertising budget targeting accounts that are not in-market?]]></description><link>https://blog.christoolivier.com/p/stop-trying-to-adapt-to-marketing</link><guid isPermaLink="false">https://blog.christoolivier.com/p/stop-trying-to-adapt-to-marketing</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Tue, 21 Jun 2022 19:58:56 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!gV2v!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8e3e03b0-14d1-4be7-bc50-39a8cca4eca6_1280x720.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gV2v!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8e3e03b0-14d1-4be7-bc50-39a8cca4eca6_1280x720.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gV2v!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8e3e03b0-14d1-4be7-bc50-39a8cca4eca6_1280x720.jpeg 424w, https://substackcdn.com/image/fetch/$s_!gV2v!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8e3e03b0-14d1-4be7-bc50-39a8cca4eca6_1280x720.jpeg 848w, https://substackcdn.com/image/fetch/$s_!gV2v!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8e3e03b0-14d1-4be7-bc50-39a8cca4eca6_1280x720.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!gV2v!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8e3e03b0-14d1-4be7-bc50-39a8cca4eca6_1280x720.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gV2v!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8e3e03b0-14d1-4be7-bc50-39a8cca4eca6_1280x720.jpeg" width="728" height="409.5" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/8e3e03b0-14d1-4be7-bc50-39a8cca4eca6_1280x720.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:720,&quot;width&quot;:1280,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:171645,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!gV2v!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8e3e03b0-14d1-4be7-bc50-39a8cca4eca6_1280x720.jpeg 424w, https://substackcdn.com/image/fetch/$s_!gV2v!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8e3e03b0-14d1-4be7-bc50-39a8cca4eca6_1280x720.jpeg 848w, https://substackcdn.com/image/fetch/$s_!gV2v!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8e3e03b0-14d1-4be7-bc50-39a8cca4eca6_1280x720.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!gV2v!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F8e3e03b0-14d1-4be7-bc50-39a8cca4eca6_1280x720.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p>How do we prove marketing's impact on sales?</p></li><li><p>How does the buyer journey for manufacturing customers on the US east coast differ from those in Germany?</p></li><li><p>What were the marketing and sales touch points before we won or lost an opportunity?</p></li><li><p>Are we wasting advertising budget targeting accounts that are not in-market?</p></li></ul><p>To answer these day-to-day questions, Marketing Directors and CMOs of B2B companies know they need data, which needs to be sourced from all the different tools and platforms they use. So far, so good.</p><p>But once they try to get any insight out of that data, the problems usually start. Their "off-the-shelf" marketing data platform turns out to not be flexible enough for their customised systems and processes.</p><p>When it comes to data and insights in marketing, it is easy to be seduced by the promises of simple off-the-shelf solutions. However, each company is unique and requires a solution that adapts to them, not one that forces the company to change to fit the tool.</p><h1>The solution</h1><p>For the past 18 months, I have been hard at work building a solution that has helped a client answer the above questions and many more. I named it SquareOne because it is the foundation for marketing insights.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7HVC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F46625994-e1de-4aec-a585-b9980fd645f1_949x625.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7HVC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F46625994-e1de-4aec-a585-b9980fd645f1_949x625.png 424w, https://substackcdn.com/image/fetch/$s_!7HVC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F46625994-e1de-4aec-a585-b9980fd645f1_949x625.png 848w, https://substackcdn.com/image/fetch/$s_!7HVC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F46625994-e1de-4aec-a585-b9980fd645f1_949x625.png 1272w, https://substackcdn.com/image/fetch/$s_!7HVC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F46625994-e1de-4aec-a585-b9980fd645f1_949x625.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7HVC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F46625994-e1de-4aec-a585-b9980fd645f1_949x625.png" width="949" height="625" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/46625994-e1de-4aec-a585-b9980fd645f1_949x625.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:625,&quot;width&quot;:949,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:72891,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7HVC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F46625994-e1de-4aec-a585-b9980fd645f1_949x625.png 424w, https://substackcdn.com/image/fetch/$s_!7HVC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F46625994-e1de-4aec-a585-b9980fd645f1_949x625.png 848w, https://substackcdn.com/image/fetch/$s_!7HVC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F46625994-e1de-4aec-a585-b9980fd645f1_949x625.png 1272w, https://substackcdn.com/image/fetch/$s_!7HVC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F46625994-e1de-4aec-a585-b9980fd645f1_949x625.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The core concept is to take all the data from your tools and platforms and turn it into an activity stream. The activity stream is then processed into attributes for each account you target.</p><p>The following diagram shows this concept in its simplest form.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!E4rs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faa520514-6a98-44b3-9030-1a79efaffd39_888x480.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!E4rs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faa520514-6a98-44b3-9030-1a79efaffd39_888x480.png 424w, https://substackcdn.com/image/fetch/$s_!E4rs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faa520514-6a98-44b3-9030-1a79efaffd39_888x480.png 848w, https://substackcdn.com/image/fetch/$s_!E4rs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faa520514-6a98-44b3-9030-1a79efaffd39_888x480.png 1272w, https://substackcdn.com/image/fetch/$s_!E4rs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faa520514-6a98-44b3-9030-1a79efaffd39_888x480.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!E4rs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faa520514-6a98-44b3-9030-1a79efaffd39_888x480.png" width="888" height="480" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/aa520514-6a98-44b3-9030-1a79efaffd39_888x480.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:480,&quot;width&quot;:888,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:52023,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!E4rs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faa520514-6a98-44b3-9030-1a79efaffd39_888x480.png 424w, https://substackcdn.com/image/fetch/$s_!E4rs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faa520514-6a98-44b3-9030-1a79efaffd39_888x480.png 848w, https://substackcdn.com/image/fetch/$s_!E4rs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faa520514-6a98-44b3-9030-1a79efaffd39_888x480.png 1272w, https://substackcdn.com/image/fetch/$s_!E4rs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Faa520514-6a98-44b3-9030-1a79efaffd39_888x480.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>All activities share a standard set of properties, allowing them to be analysed together. They also have custom properties that enable deeper analysis of specific activity types.</p><p>Attributes are derived from the activity stream and are calculated daily. This starts from the chosen start date of the solution. Having attributes calculated for each day across history makes analysing attributes over time fast and easy.</p><p>While a straightforward concept, it becomes a powerful platform when combined with non-activity type data such as your customer accounts, opportunities, marketing campaigns etc.</p><p>SquareOne allows you to identify activities and attributes that serve your business better than any generic off-the-shelf solution. Once in place, you can build data products and enable automation of processes.</p><p>Below are some examples of what a recent client achieved with the platform.</p><h2>Buyer journey</h2><blockquote><h4><em>Identify which activities lead to success in the buyer journey.</em></h4></blockquote><p>Each of the activities taking place during the lifetime of an opportunity can be displayed in chronological order while highlighting stage changes as it moves through the marketing and sales funnel.</p><p>This data enables you to identify which activities lead to success or failure for specific industries and regions. You can determine a fingerprint for success, so to say.</p><h2>Multi-touch attribution model</h2><blockquote><h4><em>Customise the attribution model to your business needs</em></h4></blockquote><p>Moving on from the buyer journey analysis, you can construct a rich multi-touch attribution model. This model can be custom to your organisation and use concepts such as activity-specific half-lives and weightings to attribute a percentage of your opportunity's value to each of the activities that took place during the sales cycle.</p><p>For a client, the model was designed to assign a higher weighting to activities closer to a buyer journey stage change while giving others further from a stage change a lower weighting.</p><p>Using such a model shows the impact specific activities have had on sales in monetary terms. It also allows you to roll activities up to the team level to show the contribution of teams or departments to revenue.</p><p>Below is an example of what an analysis for an opportunity can look like.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AIER!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53ada64-74c3-43c3-b773-d38ee70d92da_965x585.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AIER!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53ada64-74c3-43c3-b773-d38ee70d92da_965x585.png 424w, https://substackcdn.com/image/fetch/$s_!AIER!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53ada64-74c3-43c3-b773-d38ee70d92da_965x585.png 848w, https://substackcdn.com/image/fetch/$s_!AIER!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53ada64-74c3-43c3-b773-d38ee70d92da_965x585.png 1272w, https://substackcdn.com/image/fetch/$s_!AIER!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53ada64-74c3-43c3-b773-d38ee70d92da_965x585.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AIER!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53ada64-74c3-43c3-b773-d38ee70d92da_965x585.png" width="965" height="585" data-attrs="{&quot;src&quot;:&quot;https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/e53ada64-74c3-43c3-b773-d38ee70d92da_965x585.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:585,&quot;width&quot;:965,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:68568,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!AIER!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53ada64-74c3-43c3-b773-d38ee70d92da_965x585.png 424w, https://substackcdn.com/image/fetch/$s_!AIER!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53ada64-74c3-43c3-b773-d38ee70d92da_965x585.png 848w, https://substackcdn.com/image/fetch/$s_!AIER!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53ada64-74c3-43c3-b773-d38ee70d92da_965x585.png 1272w, https://substackcdn.com/image/fetch/$s_!AIER!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2Fe53ada64-74c3-43c3-b773-d38ee70d92da_965x585.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Automate adding accounts to campaigns</h2><blockquote><h4><em>Go from running a limited number of campaigns simultaneously to an unlimited number.</em></h4></blockquote><p>Instead of performing manual tasks to pull a list of accounts you think would be a good fit for a marketing campaign, you can specify which attributes an account should have to be included in a campaign.</p><p>Having up-to-date attributes calculated for each target account from the combined systems used across marketing and sales allows you to automate campaign cohort selection.</p><p>A client used SquareOne to do this and went from running a dozen concurrent campaigns at any point to running 150 at the last count. The platform produced the cohorts of accounts that needed to be added and removed from each campaign daily.</p><p>This allowed the marketing team to get the best value for their marketing budget in the following ways:</p><ul><li><p>It avoided wasting money serving ads to accounts no longer in-market.</p></li><li><p>Accounts that were not part of the initial cohort but later showed strong signals of being in-market were not missed as they were added by the system.</p></li><li><p>Campaigns could be better targeted to specific groups of accounts due to a rich set of attributes. This avoided wasting resources on broad catch-all campaigns.</p></li></ul><h1>Stop trying to adapt to tools</h1><p><a href="https://www.christoolivier.com">Contact me</a> to discuss what a marketing data platform looks like that adapts to your business and delivers real value.</p><p><a href="https://www.christoolivier.com">www.christoolivier.com</a></p>]]></content:encoded></item><item><title><![CDATA[What's up with __init__.py and __main__.py ?]]></title><description><![CDATA[I have noticed that a lot of articles that show how to create a python application uses __init__.py in the root folder of the application. Some also use __main__.py in addition to the actual python script that is executed to run the application. Since python does not have any forced folder structures for applications it is easy for those new to Python to get really confused about it all.]]></description><link>https://blog.christoolivier.com/p/what-is-up-with-__init__-py-and-__main__-py</link><guid isPermaLink="false">https://blog.christoolivier.com/p/what-is-up-with-__init__-py-and-__main__-py</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Mon, 18 May 2020 10:33:35 GMT</pubDate><enclosure url="https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/a7b065d0-1829-418b-89cb-26eda13ffcb1_600x600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I have noticed that a lot of articles that show how to create a python application uses <code>__init__.py</code> in the root folder of the application. Some also use <code>__main__.py</code> in addition to the actual python script that is executed to run the application.</p><p>Since python does not have any forced folder structures for applications it is easy for those new to Python to get really confused about it all.</p><p>Let's dig into what these files do and how they should be used.</p><h1>Application folder structure</h1><p>A lot of the posts I have seen have been for applications in the Data Science space. These were python apps written to be executed from the command line without being installed as a package into your Python environment.</p><p>Thus, the majority of these applications would be executed from the command line as follows:</p><pre><code>$ python awesome.py
</code></pre><p>The most common folder structure for these applications would normally follow something that looks like this.</p><pre><code>|-- AwesomeApp
    |-- __init__.py  &lt;-- Why is this here?
    |-- __main__.py  &lt;-- What does this do?
    |-- awesome.py 
    |-- readme.md
    |-- requirements.txt
    |-- some_module.py
    |-- package
        |-- __init__.py
        |-- package_module.py
</code></pre><p>As you can see in the above folder structure it is the <code>__init__.py</code> and <code>__main__.py</code> that normally raised questions for me.</p><p>There are a whole bunch of ways you can structure your Python applications depending on their use and how you would like your users to install and run them.</p><p>Again, we are looking at the above structure as it is used often when <code>__init__.py</code> is at the root level and not because it is the correct folder structure for your project. The correct structure depends on what type of application your want to create and how you want to distribute it.</p><p>If you would like to read more about Python project structures, then have a look at these two links.</p><p><a href="https://docs.python-guide.org/writing/structure/">The Hitchhiker's Guide to Python - Structuring Your Project</a></p><p><a href="https://realpython.com/python-application-layouts/#application-with-internal-packages">Real Python - Python Application Layouts</a></p><h1>__init__.py</h1><p>So, let's get have a look at <code>__init__.py</code>. As per the Python documentation:</p><blockquote><p>The <code>__init__.py</code> files are required to make Python treat directories containing the file as packages. This prevents directories with a common name, such as string, unintentionally hiding valid modules that occur later on the module search path. In the simplest case, <code>__init__.py</code> can just be an empty file, but it can also execute initialization code for the package or set the <code>__all__</code> variable, described later.</p></blockquote><p>You can read the full detail here in the official documentation.<br><a href="https://docs.python.org/3/tutorial/modules.html#packages">Python Documentation - Packages</a></p><p>In our example project structure, the top level <code>__init__.py</code> file would only be valid if <strong>AwesomeApp</strong> was intended to be imported as a package by other applications. <em><strong>Since this is not the case it is unnecessary to have the top level <code>__init__.py</code> file there.</strong></em> It seems this is a mostly harmless habit some people develop. Try to avoid it if you can as it is not the correct use of <code>__init__.py</code>.</p><p>For some additional information around Python packages and the use of <code>__init__.py</code> have a look at the following two links.</p><p><a href="https://realpython.com/python-modules-packages/">Real Python - Python Modules and Packages</a></p><p><a href="https://docs.python-guide.org/writing/structure/#packages">The Hitchhiker's Guide to Python - Packages</a></p><h1>__main__.py</h1><p>Now let's move on to <code>__main__.py</code>. This file again has a specific purpose in the context of Python packages. When a package is installed in your Python environment and you use the <code>-m</code> argument of Python to call that package then Python will look for the <code>__main__.py</code> file and execute the code in it.</p><p>For example, if <strong>AwesomeApp</strong> was a package installed into our Python environment (which it is not, but go with me on this one), we could run the following on the command line which will then execute the code in <code>__main__.py</code>:</p><pre><code>$ python -m AwesomeApp
</code></pre><p>Since our current app is not designed to be installed as a Python package it also does not make a lot of sense to have <code>__main__.py</code> in the root level folder.</p><p>You can read a bit more about <code>__main__.py</code> from the following links.</p><p><a href="https://realpython.com/pypi-publish-python-package/#different-ways-of-calling-a-package">Real Python - How to Publish an Open-Source Python Package to PyPI</a></p><p><a href="https://docs.python.org/3/library/__main__.html?highlight=__main__%20py">Python Documentation - Top-level script environment</a></p><h1>Conclusion</h1><p>We have looked at the use of <code>__init__.py</code> and <code>__main__.py</code> in the context of a Python application that is not designed to be installed or used as a Python package but as a standalone application.</p><p>In this context the use of both <code>__init__.py</code> and <code>__main__.py</code> can cause some confusion as they are primarily used when creating Python packages.</p><p>The folder structure we looked at, which seems common in a lot of blog posts, is also not optimal for developing Python packages or applications that are to be distributed via PyPI or to be installed manually via setuptools etc.</p><p>Python packages and distributing applications are complex topics that we will look at in a future blog posts.</p>]]></content:encoded></item><item><title><![CDATA[Power BI plugin for Apache Airflow]]></title><description><![CDATA[Apache Airflow has been a core part of a few projects I have been involved in. One of the great things about it is how extensible it is. If there is a specific task you would like to perform, and it is not covered by the built-in or contributed functionality, you can easily create your own.]]></description><link>https://blog.christoolivier.com/p/power-bi-plugin-for-apache-airflow</link><guid isPermaLink="false">https://blog.christoolivier.com/p/power-bi-plugin-for-apache-airflow</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Tue, 09 Apr 2019 08:36:17 GMT</pubDate><enclosure url="https://bucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com/public/images/86f13c3c-3b48-4598-8beb-ad7fb51c1d66_474x474.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Apache Airflow has been a core part of a few projects I have been involved in. One of the great things about it is how extensible it is. If there is a specific task you would like to perform, and it is not covered by the built-in or contributed functionality, you can easily create your own.</p><p>The interest in Power BI has never been greater than it is today. It was thus no surprise that I would at some point run into a situation where I needed to orchestrate some actions against Power BI from an Airflow data pipeline. Currently no built-in functionality exist in Airflow to interact with Power BI.</p><p>In order to make life easier I have created a Power BI plugin for Apache Airflow. The code is available on Github <a href="https://github.com/christo-olivier/airflow_powerbi_plugin">here</a>. Installation instructions and further detail about the plugin can be found in the repository on Github.</p><p>Feedback, requests for additional functionality and pull requests are all welcome.</p>]]></content:encoded></item><item><title><![CDATA[Practical remote working: part 3 - business processes and culture]]></title><description><![CDATA[In the last two posts we tackled what I have always found the easy parts in being an effective remote worker.]]></description><link>https://blog.christoolivier.com/p/practical-remote-working-part-3-business-processes-and-culture</link><guid isPermaLink="false">https://blog.christoolivier.com/p/practical-remote-working-part-3-business-processes-and-culture</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Tue, 05 Jun 2018 11:52:49 GMT</pubDate><content:encoded><![CDATA[<p>In the last two posts we tackled what I have always found the easy parts in being an effective remote worker. In this post I am going to look at the difficult problems of business processes and culture. In order for organisations to embrace remote work and get all the benefits it provides you need to challenge some old school entrenched thinking and behaviour.</p><p>I want to explore the following topics in this post:</p><ul><li><p>Measuring productivity</p></li><li><p>Clear task definitions</p></li><li><p>Mentoring junior staff</p></li><li><p>Building a cohesive team</p></li><li><p>Isolation</p></li></ul><h1>Measuring productivity</h1><p> One of the first and most often asked questions is always about productivity and output. Questions such as "Will they be working their full X hours per day?" or "How do I know they are not slacking off and delivering less than what they could?" are common.</p><p>What most amuses me about these questions is that I have worked at plenty of organisation where the on-site staff were delivering far below what they should. However, since they were present for at least 8 hours per day this sub-par delivery was never really noticed or questioned. At some organisation there was so much "busy work" going on that people never truly realised that actual work was not taking place. Yet the organisation could not understand why their key projects were running behind schedule. Let that sink in for a moment.</p><p>It is really important to realise that just because someone is visibly sitting in their seat and looks busy does not mean they are actually moving you towards your goals. "Busy work" is the absolute enemy of productivity. It sucks the life out of a team and project. It is all of the tasks that are dogmatically followed but which does not move the work forward. I have seen it most in organisations that exhibit cargo cult behaviour with regards to methodologies and productivity.</p><p>So, in order to address the concerns about productivity of both on-site workers as well as remote workers we need to change the way we measure productivity. Repeat after me: "What gets measured gets managed". The simplest thing to do is to start tracking people's deliverables. This might seem like a very strange concept for some people, but many organisations have been doing this for a very long time. I personally believe that this is the reason why Kanban and Agile delivery approaches on IT projects work better than other methodologies.</p><p>In both of the above-mentioned delivery methodologies work is split into small tasks that are assigned to or picked up by employees. These tasks are estimated by the teams that need to deliver them. This leads to more realistic estimates against which people's delivery can be tracked and which will give insight into the true progress of work and the real productivity of your teams. This way of working is not only applicable to software development teams. If you think about it carefully you will realise that all work can be broken down into small manageable pieces which can be tracked. In fact, most people taking on any complex activity will end up breaking it down into its component tasks in order to deliver it.</p><p>The biggest challenge for an organisation stuck in the X hours per day mindset of productivity is to acknowledge that it does not accurately reflect a measure of productivity. Organisations need to change their view on productivity to be one in which we measure deliverables and the output that team members produce. Once you have done that you will also have no fear about whether your remote workers are productive because you will be accurately measuring their productivity.</p><h1>Clear task definitions</h1><p> Once you have made the change to start measuring productivity based on deliverables you will realise the importance of clear task definitions and good communication. If you start following a methodology like Agile or Kanban you will automatically be forced to break tasks into clearly defined units of work. I therefore recommend that you look into using such a delivery methodology to help you ensure tasks are clearly defined.</p><p>If you are not looking to use such a methodology or use your own hybrid approach, then ensure that tasks a clearly defined and contains a definition that states when a task is done. This "definition of done" is extremely important for clear task definitions since you otherwise would not know when to consider a task completed. I would venture to say that it is one of the most important things required to clearly define tasks. Knowing what your "definition of done" is indicates that you have thought through the task and understand it well enough to articulate the circumstances that will allow you to consider the task completed. A task without a clear "definition of done" is not a clearly defined task.</p><h1>Mentoring junior staff</h1><p> Remote work is not the best option for all situations or people. It is definitely a very difficult thing to do when you have junior team members and it is something I want to highlight here to ensure people are aware of it.</p><p>In my own experience most organisations try and provide some form of mentoring for junior staff to help them get productive as quickly as possible. This mentoring and day to day assistance does work much better when people are in the same location typically because you cannot always anticipate what challenges a junior member of staff will face. Juniors are also not always that confident in asking for help when they are struggling with something. They might feel that it will reflect badly on them. It is thus much easier when a colleague is able to periodically check in with them during the day to make sure they are not stuck without getting help.</p><p>While it is much easier to help junior members of staff when everyone is on-site it does not mean that there are no solutions for making it work remotely. One such solution is to schedule regular check in points throughout the day with your junior team members. Here they are able to ask questions related to their work and can get guidance. If you have fostered a good culture with open communication, then they should also become comfortable in getting in touch when they are unsure about any part of the work they need to do.</p><p>A second option is to ensure regular periods of on-site work where juniors and their senior team members are in the same location and work together. This can take the shape of doing X amount of days per week in the same location for a certain period of time until the junior team members are productive enough to transition to a larger portion of remote working.</p><p>Yet another approach, which has worked really well for me in the past when I was a junior, was to have a set period of time in the office with your senior team members at the start of your employment. Not all of them had to be there all of the time. It was simply important that there was one of them to help guide you through your tasks. After this period, you are assigned a mentor who would then work with you to ensure you progress your skills and always have a point of contact.</p><p>The approach I described last was used by the company I joined for my first job straight out of university. They were a niche consulting company specialising in demand and inventory management who sold a software solution as part of their offering. Due to their well thought out approach they were able to send me to a client who was 2,000 km away from the main office two months after me joining them. I was able to function as a delivery consultant on my own with this client for the duration of the 6-month project. I always felt like I had the support I needed when I required it. I was able to function effectively on this project due to their open and efficient communication, the extremely professional behaviour of my colleagues and their understanding of how to support people working remotely on client site. Some of my fondest memories of my career are still from my time working for this company.</p><h1>Building a cohesive team</h1><p> It is important to ensure that you build a cohesive team in your business. When your organisation has remote workers, it is important to foster a culture that ensure this includes those remote workers as well.</p><p>One of the most damaging things that can occur is for managers and workers that work in the office to create a culture of "Us vs Them". This can happen without it being a conscious decision. By not following some of the previous guidelines for effective communication, equipment and software, the perception can arise in the office that it is difficult to work with remote colleagues. This would of course be a false assumption because the challenges being experienced are being created by on-site employees and not the remote workers. If this is left unchallenged to run its course, then the end result will be this divisive culture resulting in the remote workers being seen as second-class employees. In the end the benefits of remote work will not be available to anyone.</p><p>In order to address the previously mentioned problem it is imperative that management enforce good policies that will ensure all parties involved follow the best practice recommendation to make communication as efficient as possible. It is always a big help when some of the management team take advantage of the remote work option and does that on a regular basis themselves. This will ensure that they have first-hand experience in how their company is doing in making remote work seamless.</p><h1>Isolation</h1><p> Isolation is always a big issue for remote workers. We as human beings are social creatures by nature. We all handle isolation in different ways and can cope with it to varying degrees. Some people prefer to be isolated while others cannot stand it at all. One challenges with remote work is to ensure that employees have the ability to be connected with the organisation as a whole to feel part of the team. This can take the form of annual company events that bring all employees together as well as smaller regional events that bring those working in the same region together. These events are a great way to allow people to feel part of the overall vision of the company they work for. In addition to these broader events it is also useful to encourage teams to have remote hangout sessions or coffee sessions informally during the week. This is a good way for remote teams to take a breather the way office bound employees in a team might take a coffee break together.</p><p>A second way of dealing with potential issues relating to isolation is to allow employees to choose the level of remote work that they are comfortable with. In an <a href="https://hbr.org/2014/01/to-raise-productivity-let-more-employees-work-from-home">article</a> written in the Harvard Business Review it was clear that not all employees would benefit from remote working and that allowing people to choose this option resulted in the best outcome.</p><h1>Conclusion</h1><p> Your company culture and the way you approach remote working within your company are both crucial for the success of any remote working program. Remote working does not work if an organisation is not committed to making the changes required to make it a success.</p><p>In this post we looked at some of the topics that I have come up against in my own personal experience. This only scratches the surface on the list of things one need to consider when wanting to ensure effective remote working. There are entire books written about the subject and many organisations who work entirely remote have made their experiences public. The first that comes to mind is <em><a href="https://scottberkun.com/yearwithoutpants/">The Year Without Pants</a> by Scott Berkun</em>, the second <em><a href="https://basecamp.com/books/remote">Remote: Office Not Required</a> by the founders of Basecamp</em>. Anyone who is looking to bring the benefit of remote working to their organisation will be well served to look at these resources.</p>]]></content:encoded></item><item><title><![CDATA[Practical remote working: part 2 - software]]></title><description><![CDATA[There are so many options for software when dealing with remote working that it can truly make your head spin.]]></description><link>https://blog.christoolivier.com/p/practical-remote-working-part-2-software</link><guid isPermaLink="false">https://blog.christoolivier.com/p/practical-remote-working-part-2-software</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Mon, 02 Apr 2018 14:50:53 GMT</pubDate><content:encoded><![CDATA[<p>There are so many options for software when dealing with remote working that it can truly make your head spin. In this section I want to look at the following categories of software:</p><ul><li><p>Video conferencing</p></li><li><p>Group chat</p></li><li><p>Task tracking and project management</p></li></ul><p>As always, the answer to the question of &#8220;what the best solution for X is?&#8221; will always depend on your company setup and existing software. I am aiming to provide my personal experience on what has worked well for me in the past and what has not. So, if you favourite piece of software is not in here then it is due to me not having personally used it yet.</p><h1>Video conferencing</h1><p>This is one of the most important pieces of software that you will use. Let me start off by saying that voice only calls are woefully inadequate for effective remote working. When working remotely you need to communicate as if you are sharing the same space with your colleagues or clients. Video calls are a much better solution and I will thus not make mention of the ability to dial into calls using a telephone in this section. Just have a listen to this endless loop of conference call <a href="https://conferencecall.biz/">audio</a> to realise how much time we waste by using voice only calls instead of speaking face to face over video conferencing.</p><p>It is important to evaluate your software based on the following factors:</p><ol><li><p>How easy is it to jump on a call?</p></li><li><p>Can people from outside the tool eco-system join calls?</p></li><li><p>Which devices are supported?</p></li><li><p>How well does the software handle low bandwidth situations?</p></li><li><p>How well does the software cope with multiple participants?</p></li></ol><h2>Appear.In</h2><p>One of my personal favourites for video calls is <a href="https://appear.in/">Appear.In</a>. Appear.In makes the process of jumping on a call an absolute breeze. It is web based and runs in your browser so no need to install anything on your computer. You set up a number of predefined rooms and everyone that needs to be on the call simply navigate to the URL. Your rooms are registered to your account and they persist. Having this simple persistent URL system ensures no one needs to hunt for dial in details or URLs that are ever changing.</p><p>External parties can join your calls just as easily since there is no software installation required. They simply navigate to the URL of your room and can join as a guest. There is no need for participants to register in order to use the service.</p><p>In addition to the browser support there are also apps for both iOS and Android which allows you to join from your mobile phone. These apps work very well in my experience and allows you to really join from absolutely anywhere that you have an internet connection.</p><p>So that leaves us with the question of how well this service handles low bandwidth situations? It is important to understand that there are two operating modes used by appear.in. The free version uses a purely peer to peer solution while the paid for version uses server infrastructure to route the calls. (for more information have a look <a href="https://appearin.helpscoutdocs.com/article/117-how-is-the-premium-version-different-from-the-free-version">here</a>) In my experience the paid for version handles low bandwidth situations well and would allow calls to be smooth in situations that the free version was not, especially with groups of 4 or more people. Group meetings on appear.in are supported for groups up to 12 people at the time of writing this post.</p><p>In my experience appear.in provides a friction-less experience for group video calls. It functions well in low bandwidth scenarios. It is well worth the $9.99 per month for the Pro version. The only drawback could be if you need meetings with more than 12 participants.</p><h2>Google Hangout &amp; Meet</h2><p>Google Hangouts is another great tool for video conferencing and provides both a free and paid for version. The free version is available for anyone with a Google account and is simply know as Hangouts, while the paid version comes bundled with G Suite and is known as <a href="https://gsuite.google.com/products/meet/">Meet</a>. There are a few differences between the two apps and you can have a look at the differences <a href="https://support.google.com/a/answer/7303775?hl=en">here</a>.</p><p>Both Hangouts and Meet make it easy to jump on a call. You can simply send a link for the call to your participants which allow them to join. Both tools do not require your clients to have a Google account to join. If your users want to use the apps for their specific platform then Meet will have the edge as it has better feature support for iOS than Hangouts.</p><p>Hangouts and Meet both support Android and iOS. Hangouts will run on Safari, Internet Explorer and Chrome and will require plugins on these to work. Meet will only run on the Chrome browser.</p><p>Both Hangouts and Meet handle low bandwidth connections well. I have found that Hangouts perform better on Chrome than it does on other browsers. It is thus worth making sure you are running Chrome, especially in a low bandwidth situation.</p><p>In my experience the software handles multiple participants really well. Depending on the version of G Suite that you have, Meet will support either 25 or 50 participants. Hangouts itself supports 25 participants.</p><h2>Skype for Business</h2><p>If you are using Office 365 for your business then you most likely have <a href="https://products.office.com/en-gb/skype-for-business/online-meetings">Skype for Business</a> (SfB). I have used SfB a lot in my career and it is a very capable tool. It combines instant messaging with video conferencing capabilities making it a real one stop communication platform.</p><p>SfB requires the user to install the app for their specific platform in order to get the most out of it. There is an option to use it in the browser that requires a plugin when you want to do video calls. There are apps for Windows, MacOS, iOS and Android and these apps all function well, with the exception of the MacOS app. The MacOS app is truly frustrating and feels like an afterthought. It can at times struggles with input lag when typing messages and becomes so sluggish that it is unusable. If you have any users in your organisation that use Apple hardware I would advise you to think carefully about using SfB.</p><p>It is fairly easy to join calls since you can either join them via a link or one of the apps.</p><p>SfB handles low bandwidth in an &#8220;ok&#8221; manner. From my experience it is not the best option for these scenarios. If you have low bandwidth issues it is better to look to tools such as Zoom, Google Hangouts or the paid version of Appear.In. On low bandwidth calls I have experienced a lot of video freezes and voice interruptions.</p><p>SfB does a good job of handling multiple participants and is a pleasant experience with larger groups. SfB can handle up to 250 participants depending on the version you are using.</p><h2>Zoom</h2><p><a href="https://www.zoom.us/">Zoom</a> is another full featured video conferencing solution which I have come to like. It also offers instant messaging capability alongside its video conferencing. It offers a really well-rounded set of capabilities.</p><p>Zoom allows users to join your calls by installing the required Zoom software on their machine. They do not need to have a Zoom accounts to join your call. Calls can be joined by either using the ID that is generated for each call or the personalised link of the host if they have created that for their account.</p><p>Zoom handles low bandwidth very well. From my own experience it was one of the best tools to use for multiple participants with varying connection speeds. If you have any of the normal plans for Zoom you will be able to have up to 25 people in a meeting. If you need more capacity, you can buy a large meeting plan which supports up to 200 participants. The user experience is good, even with large groups.</p><p>From my experience there is very little that Zoom cannot do and if you are looking for a good video conferencing solution then Zoom should be on your list to evaluate.</p><h1>Group chat</h1><p>Most organisations have had some form of instant messaging for a long time. In recent years an evolution of instant messaging has taken place which is aiming to bring the benefits of instant messaging, group messaging and file sharing all packaged up into one application. Like any tool available to us these applications can be tremendously beneficial when used correctly and totally overwhelming and ineffective when not. For this blog post I am not going to look at the best ways to use these applications but instead highlight two of the applications I have used successfully with clients in the past.</p><h2>Slack</h2><p>Slack is probably the best known of these types of application and is the one that has kick started the adoption of group chat in most companies today. Yes, there were many older applications that facilitated group chat in a similar manner in the past, but what makes Slack special is the API they expose for 3rd party plugins.</p><p>Slack allows you to create channels in which team members communicate with other users that are subscribed to the same channel. It also allows direct messaging between users and allows you to share. There is a myriad of plugins to integrate external services with Slack. It can thus do a tremendous number of things when it comes to communications.</p><p>The benefit of using software such as slack is in the ability to keep a complete history of a conversation or team discussion. This includes comments on topics and comments on shared documents. This allows you to curate knowledge in one platform that makes it more accessible than traditional instant messaging and email.</p><p>When you throw in the ability to connect external tools to your slack channels, for example for a development team the build process can be linked to a channel to receive notifications, you start to see the potential of these tools. It reduces the amount of email sent around and allows people to get instant notifications for those topics that they are involved in or responsible for.</p><p>In addition to the text-based messaging Slack also features the ability to do video calls and screen sharing natively, which is functionality added after its acquisition of Screenhero. If the native functionality is not to your liking you can integrate many of the applications mentioned in the earlier section on video conferencing with Slack.</p><p>Slack has both desktop, mobile and web clients which makes it easy for anyone to use on any device.</p><h2>Microsoft Teams</h2><p>When Microsoft released Teams it was widely seen as a clone of Slack. After having used it myself on a few projects I think it is better described as the natural convergence of SharePoint and Skype for Business. It is a true communications hub that integrates extremely well with the Office 365 suite of products.</p><p>It is very similar to Slack in that it provides channels for communication, however these channels are created inside containers called &#8220;teams&#8221;. Users are added to teams and can then see the channels inside those teams. It provides very good file sharing capabilities, wiki&#8217;s and both direct messaging as well as group conversations.</p><p>Teams feel to me like the grown-up version of Slack in which you have better control over how you share information and can achieve much more structure and rigour around such communication. It is a fantastic tool when working with remote teams exactly because it is capable of being a single point of contact and information dissemination within an organisation. Teams also provide the ability to use 3rd party plugins, called &#8220;apps&#8221;, to integrate other applications into the various channels.</p><p>Just like Slack, the Teams desktop clients provide real-time communications support (audio, video, and content sharing) for team meetings, group calling, and private one-on-one calls.</p><p>Microsoft Teams also has desktop, mobile and web clients available to allow easy adoption of this tool for everyone in your organisation.</p><h1>Task tracking and project management</h1><p>There are so many project management and task tracking tools out there that one could spend an eternity evaluating them all. Therefore, I am going to break with the theme of this post so far and not mention any specific pieces of software in this section. Instead I am going to provide some guidelines with which you can evaluate these tools to make sure you choose one that works for you and your team.</p><h2>No unnecessary complexity</h2><p>This should be your first area of focus. You need to ensure that any new member of a team is able to be productive with your chosen tool in as short a period of time as possible. If you provide them with a 30-minute guided walk through of the tool and they cannot immediately start using it afterwards then your tool is too complex. (I am not talking about power users or project managers here but instead about the average users that will be performing the tasks you are tracking.)</p><p>You might be laughing but I have had tools on project that required a full-time person just to keep it running and help the team out when needing to do certain tasks. There is absolutely no excuse for choosing overly complex tools given the amount of quality tools with well thought out UX we have available today.</p><h2>Overview at a glance</h2><p>Your tool needs to provide you with the ability to see how things are progressing at a glance. Remember we are not only talking about tracking software development tasks here. We are tracking everything that is happening in your project.</p><p>Having the ability to see how tasks depend on one another and see the overall progress of the project at a glance allows you to not lose focus of the overall goal while you are hacking away at the day to day detail. If you need to spend time compiling the progress manually then you need to look for a better solution.</p><h2>Adaptability</h2><p>Your tool needs to be able to adapt to the project methodology of your team and not force a particular way of working on you. This might sound strange to some people that have bought into a particular methodology. However, having the ability to adapt your software to best fit your specific needs is a tremendous help.</p><p>Not all teams will be software development teams. Tool rigidity starts to show up very quickly when you manage larger projects that include multiple disciplines across your business. As the adage goes &#8220;if your only tool is a hammer then every problem looks like a nail&#8221;. Don&#8217;t get caught out by trying to fit your process around a tool as it will blind you to more effective ways of solving challenges you encounter within your team and business processes.</p><h1>Wrapping up</h1><p>Hopefully this post serves as the starting point to get you thinking about using the correct software when remote work is involved. With so many products out there today, it is really difficult for anyone to prescribe what will work best for your own personal needs. The best is to test a few products based on your own requirements.</p><p>Always keep in mind that the software is simply a tool to help you perform certain functions better. They cannot make up for bad processes or bad planning. The best project management tools in the world will not help you and your team if you do not apply rigour and discipline in your work. Equally the best video conferencing software cannot help you communicate effectively if you do not make face to face calls your priority.</p><p>In the next post we will look at team culture and business processes in order to identify what changes you need to make in order to enable effective remote working in your organisation.</p>]]></content:encoded></item><item><title><![CDATA[Practical remote working: part 1 - hardware and environment]]></title><description><![CDATA[In this first post in this series we will look at the hardware you will need and also the physical environment that you should aim to create.]]></description><link>https://blog.christoolivier.com/p/practical-remote-working-part-1</link><guid isPermaLink="false">https://blog.christoolivier.com/p/practical-remote-working-part-1</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Mon, 08 Jan 2018 14:48:04 GMT</pubDate><content:encoded><![CDATA[<p>In this first post in this series we will look at the hardware you will need and also the physical environment that you should aim to create. Where applicable I will distinguish between advice for individual remote workers and those office workers that need to work with remote team members.</p><h1>Hardware</h1><p>So, let us start with an easy one that is very often overlooked, hardware. The reason I am saying this is often overlooked is because a lot of people seem to believe that just because your laptop has a microphone built into it you are ready to work remotely. This could not be further from the truth and is a recipe for a productivity disaster.</p><p>If on the other hand you are working from the office with team members that are working remotely then you too need to pay special attention to your hardware as well. You might think that having a call with a remote colleague or team over your laptop speakers is fine but in reality, it is a terrible idea. Not only are you annoying everyone else that is sitting around you but you are also bombarding those connecting remotely with a ton of office noise.</p><p>Here are some hardware and environment recommendations to ensure that you communicate efficiently whether you are sitting in the office or at a remote location.</p><h2>Headsets</h2><p>One of the first things you need to invest in is a good headset for making your calls with your team. Being unable to hear someone properly on the other end of the conversation is a sure-fire way to get everyone involved frustrated and ready to throw in the towel. Remember that your aim is for people to be able to hear you clearly when you speak. Your laptop microphone is generally not a good option as a lot of manufacturers use low quality microphones and the placement of the microphone is normally such that it will pick up loads of background noise.</p><p>Your headset does not need to cost a fortune, we are not looking for studio quality recorded sound here. We are simply looking for good clear sound that makes communicating easy. There are many options available out there and you need to try a few headsets to see which ones really work for you personally. I have found Logitech to make some good headsets at a very reasonable price. Jabra and Plantronics are two other brands that produce high quality products but always make sure to test things out. I have personally had a Plantronics headset that cost far more than my current Logitech one which delivered poorer quality audio during video calls.</p><p>Another option that can work depending on your work environment is a USB microphone and earbuds or headphones. If you are working from your home in a quiet environment then this is another route you could take. Make sure that you have headphones to avoid feedback from your laptop speakers. An entry level USB microphone will cost you at least the price of a headset and a good USB microphone will cost significantly more. Only look at this option if you are going to be using the microphone for other tasks such as recording training material or a podcast and have a quiet working environment.</p><h2>Webcam</h2><p>It is not sufficient to be heard clearly, you also need to make sure that your colleagues can see you clearly. Here again your laptop&#8217;s webcam might not be sufficient. Webcam quality on laptops can vary greatly so it is extremely important that you test it out to see how you are being received on the other end. If your webcam is low resolution or does not provide a wide enough angle to give a realistic image of your while communicating then invest in buying a good quality external webcam to use instead of the built in one.</p><p>Here again I have found Logitech to provide some good options that are affordable.</p><h2>Conference call facilities</h2><p>Conference call facilities are most applicable to team members that are working from the office or as a group from remote locations. Make sure that you have dedicated meeting rooms that have been set up with the right equipment to allow good web conferencing with team members. A bunch of people huddling around a laptop is not a good way to communicate.</p><p>Ensure you have the required screen, camera and voice equipment in the room. You need to make sure that those connecting remotely can see everyone in the room as well as hear everyone when they speak. Nothing is as annoying as having to ask people to repeat what they have just said because they were too far away from the speakerphone or because your speakerphone produces poor quality audio and everyone in the room cannot clearly hear those who joined remotely.</p><p>Also make sure you have a proper screen in your meeting rooms so that you can see those that have joined remotely. Setup your camera in your meeting room to be facing the attendees from the direction of the screen. This will ensure that when people talk to each other they will actually be looking right at each other. It sounds like a small thing however there are a ton of non-verbal communication that takes place during our interactions which are extremely important. If you are looking away from the camera when you speak to someone you are effectively diminishing the quality of your communication by not transmitting these non-verbal cues.</p><h2>Internet connection</h2><p>Next on our list is a big one, your internet connection speed. You could have all of the greatest hardware for doing video calls, but without a good internet connection you are going to struggle. Most people think that having a good download speed is all there is to it but unfortunately this is only half the story. While a good download speed is important for you to be receiving the video and audio from other, your upload speed is important to ensure that your video and audio is transmitted to them without any lag. Always look to have a good upload speed as well to make sure you can do your video calls with high quality.</p><p>Another thing to keep in mind with internet connections is with regards to the response time of your connection. This is how fast you get a response when you have sent a packet from your machine to the server. The only reason I bring this up is because sometimes 4G or mobile hotspots provide good upload and download speeds but provide a slow response time. This can cause lag and stuttering when you are doing something such as a video call even though you might have a fast upload and download speed. It can also occur when you are connected to the Wi-Fi in your office along with everyone else.</p><p>It is very difficult to recommend speeds for upload, download and response time. This is because difference video conferencing software have different requirements. The best recommendation is to always check the requirements of the software you will be using. I would go with their &#8220;recommended&#8221; requirements as the &#8220;minimal&#8221; requirements would not provide the best experience possible.</p><h1>Environment</h1><p>Having the right hardware is only one part of the solution. Another equally as important part is to have the correct environment. Environment will differ for you based on whether you are alone from a home office remotely or whether you are working from an office with remote team members. Let&#8217;s look at both of these two scenarios and the different challenges they present.</p><h2>Remote worker</h2><p>If you are planning on being a remote worker and the first thought in your mind is that of your comfortable couch in your living room then let me stop you right there. While the idea of working remotely from your couch might be enticing the reality will typically be that of lost productivity. Sitting at the dining table for days on end is also not a great idea and can similarly lead to productivity issues.</p><p>From personal experience the best option for working remotely from home is to have a home office or a room that can be dedicated to work for those days that you are working remotely. Having a dedicated space ensures that you can shut out your regular home life during your working hours and allows you to focus on the tasks at hand. It keeps distractions at bay and provides you with a quiet spot from which to do video calls with colleagues or clients.</p><p>Separating work and home life also helps prevent you from falling into the trap of working at all hours of the day. It is mentally much easier to keep work and personal times separate when you can physically close the door or leave the room in which you work. By keeping this separation, it is also easier to &#8220;switch&#8221; into &#8220;work mode&#8221; in the morning. This is especially important on days where you have to do tasks that you might find boring or routine. If you were in the office you would have no other choice but to complete these. If you are working from your sofa your TV might just become too big a temptation.</p><p>The golden rule for me has always been to create an environment that helps me focus by reducing as many distractions as possible. You should design a space in which you remove all of the distractions of the office while avoiding bringing any distractions of home into it.</p><h2>Office worker</h2><p>These days most offices are open plan and noisy and there are many studies that show it to have negative effects on productivity. If you will be working with remote colleagues it is important to ensure that you minimise the noise and chaos of the modern office. For example, having a video call over your laptop without a headset will annoy both your co-workers sitting next to you as well as the person on the other end of the call. Your co-workers will be forced to listen to your conversation while your remote colleague will be forced to listen to poor quality audio and background noise, making it more difficult to understand you. Always use a headset when you are doing calls with remote colleagues! If you find it hard to concentrate during your call due to office background noise then make sure to move to a quiet location where you yourself are less distracted.</p><p>Another scenario to avoid is that of everyone in the office huddling around a laptop when video calling with remote team members. This only leads to bad communication and a terrible experience for everyone involved. If you need to have multiple in-office people join a video conference call with remote co-workers then either use a properly set up conference room or have everyone dial in from their desks with headsets. Remember your aim here is to ensure that you have optimum communication for everyone involved. This cannot be achieved when people are huddling around a laptop at a desk or in a poorly set up meeting room.</p><p>I have often found that those working from an office do not realise the extent to which they can negatively affect productivity when dealing with remote colleagues by creating a poor environment for communication. It is important to ensure that everyone working from the office understand the important role they play in facilitating good communication.</p><h1>Wrapping up</h1><p>By getting the right hardware that works for your environment, and creating the most communication efficient and productive environment you will have laid the ground work for your remote working efforts.</p><p>In the next post, we will look at the different types of software that you need in order to make optimal use of your hardware and environment.</p>]]></content:encoded></item><item><title><![CDATA[A practical guide to effective remote working]]></title><description><![CDATA[Over the last few months I have seen a couple of remote working guides appear in my inbox sharing tips and best practice from across the industry to get the best out of remote work.]]></description><link>https://blog.christoolivier.com/p/a-practical-guide-to-effective-remote-working</link><guid isPermaLink="false">https://blog.christoolivier.com/p/a-practical-guide-to-effective-remote-working</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Mon, 08 Jan 2018 14:43:54 GMT</pubDate><content:encoded><![CDATA[<p>Over the last few months I have seen a couple of remote working guides appear in my inbox sharing tips and best practice from across the industry to get the best out of remote work. While I found a lot of these articles interesting and useful I could not help but notice that a lot of it is aimed at being content marketing and thus lack some practical advice for the day to day actions that can make or break remote working.</p><p>After many years working as a consultant and freelancer, having been a remote worker and having managed remote teams, I thought I would put down my own experiences and advice in the hope that it could benefit others. The idea is to provide practical advice not only for remote workers but also for office workers who interact with remote team members or clients.</p><p>Remote working has so many different aspects to it that trying to cover it in a single post would simply not work. I have thus decided to split this into a three-part series that will cover the following topics:</p><ul><li><p><a href="https://www.christoolivier.com/practical-remote-working-part-1/">Part 1 - Hardware and Environment</a></p></li><li><p><a href="https://www.christoolivier.com/practical-remote-working-part-2-software/">Part 2 - Software</a></p></li><li><p><a href="https://www.christoolivier.com/practical-remote-working-part-3-business-processes-and-culture/">Part 3 - Business Processes and Culture</a></p></li></ul><p>I will update the above list to link to the articles as I publish them. Feel free to get in touch with me on social media or keybase with any of your own experiences and feedback.</p>]]></content:encoded></item><item><title><![CDATA[SSIS frameworks and unnecessary complexity]]></title><description><![CDATA[The notification email arrives in my inbox carrying the unwelcome message that the SSIS job failed.]]></description><link>https://blog.christoolivier.com/p/ssis-frameworks-and-unnecessary-complexity</link><guid isPermaLink="false">https://blog.christoolivier.com/p/ssis-frameworks-and-unnecessary-complexity</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Thu, 22 Sep 2016 17:43:59 GMT</pubDate><content:encoded><![CDATA[<p>The notification email arrives in my inbox carrying the unwelcome message that the SSIS job failed. I instantly get a sinking feeling in my stomach, this is going to be an unpleasant morning. I open Management Studio and get to work looking at the SSIS Catalog execution reports to see if I can track down a meaningful error message. What should be a straight forward process of debugging the ETL turns instead into a SSIS debugging high wire act of querying obscure configuration tables, interrogating custom logging tables and setting up and executing parts of SSIS packages in Visual Studio.</p><p>SSIS frameworks, whilst very useful when implemented correctly, can be one of the greatest sources of unnecessary complexity when not designed with flexibility and modularity in mind. It seems that there is no limit to the appetite for complexity by developers and IT professionals. If you are considering creating a framework for your company, or are maintaining an existing one, then it will be well worth asking yourself the following questions on your road to framework nirvana.</p><h1>Are you hindering efficient debugging in Visual Studio?</h1><p>If the answer to this question is anything other than a confident &#8220;No" then you are on the road to frustration, agitation and lots of wasted time. We have come a long way since the dark days of DTS packages and the even darker days before that of BCP and TSQL scripts.</p><p>If you are unable to open your ETL solution in Visual Studio, set a few project and package parameters, run your solution and see the error displayed right there in your IDE then you have some soul searching to do. To better illustrate what I mean let's look at an example.</p><h2>The master conductor</h2><p>Since SQL Server 2012 we have had the ability to start SSIS packages via TSQL (without resorting to the dubious and horrible xp_cmdshell). Having this built in support for running SSIS packages is extremely useful but can lead to disastrous consequences when taken too far.</p><p>One example of this is when a framework uses a &#8220;master conductor&#8221; packages to start all the SSIS packages for a particular job via TSQL. Sometimes this package dynamically retrieves the list of packages to execute from a database table held in a &#8220;control&#8221; or &#8220;settings&#8221; database. Typically, these &#8220;control&#8221; or &#8220;settings&#8221; databases are a hangover from the SQL 2005 and 2008 era where configuration files were a pain and using such a database made life much easier. Today however this is not required anymore as the SSIS Catalog negates their use and makes life much simpler and explicit.</p><p>Now don&#8217;t confuse this &#8220;master conductor&#8221; package with the correct kind of &#8220;master&#8221; package in which you explicitly specify when and which packages to execute by using Execute Package tasks. I am specifically talking about a package that starts other SSIS packages via TSQL and which thus stops you from being able to debug your ETL in Visual Studio in a straight forward manner.</p><p>What ends up happening is a disconnect between your debug environment and the packages being executed. Your &#8220;master orchestrator&#8221; package will be running in Visual Studio but it will be starting SSIS packages that are deployed to the SSIS catalog and thus those packages won&#8217;t be opened in Visual Studio when they are executed.</p><p>Now I hear you saying &#8220;But you could still debug those individual SSIS packages on their own in Visual Studio!&#8221;. Yes, you are right, but if that master package runs complex logic to retrieve and set values in a &#8220;control / settings&#8221; database which in turn is passed to the packages it executes then you will spend a great deal of time setting up your environment to represent the state in which the error occurred. Simply put this is time wasted and needless frustration introduced not only into your development process but also your support process.</p><h1>Are you duplicating functionality that already exist?</h1><p>Think about this one for a moment, do you have any piece of functionality in your framework that currently exists out of the box in SSIS? If the answer is &#8220;Yes&#8221; then it is time to get to work to remove these components and use the out of the box functionality instead. Custom logging? Custom ways of configuring your SSIS packages from configuration databases? Custom error handling or restart ability functionality? Do any of these ring a bel?</p><p>A lot of this is again a hangover from the SQL 2005 / 2008 days but it is time to let go of it. We have SQL 2016 today and that is basically 3 versions since the SSIS catalog has been introduced. If you are still doing custom logging via some form of script tasks or custom components, then your efforts might be best spent developing a set of reports directly on the SSIS Catalog or alternatively extracting the data from the SSIS catalog and building your own little data mart from it.</p><p>While custom logging and custom configuration does not necessarily bring extra debugging frustration it is technical debt. For example, if you are using custom tasks to perform logging then you would need to update these tasks and recompile them before you could use them with a new version of SQL Server. Now this might not sound like that much of a problem but what happens if you use these components over a large set of projects and the time comes to upgrade to a new version of SQL Server? There will be no automatic SSIS package upgrade functionality for your custom components and it will add an extra set of headaches.</p><p>In the past I have been at a client where they had 50 instances of SQL Server running in the organisation and other another client where they had over 500 SSIS packages in their ETL solutions in the organisation. While these might sound like extreme examples large organisations do have a proverbial sprawl of ETL solutions and SQL Server instances. A small increase in technical debt per package would be amplified tremendously in such environments.</p><h1>Is your framework modular?</h1><p>Can you pick and choose the components from your framework that make sense for the ETL challenge you are trying to solve? Not all ETL challenges are the same and it is important that your framework does not force you to solve all challenges in exactly the same way.</p><p>Don&#8217;t get me wrong, I am not advocating a Wild West approach to ETL, not at all. What I am advocating is a modular framework that allows you to choose the different components of your framework that have been designed to solve your challenge in the simplest way. This means that your framework might have a specific approach to loading Flat Files and another approach to loading records from a source database and yet another for dealing with Excel files. Each of the individual SSIS packages should still conform to the standards set by your framework and template packages should be available to use as the starting point for development.</p><p>Monolithic approaches to implementing your ETL should be avoided at all cost. If the answer to the question &#8220;Why is it done this way?&#8221; is simply &#8220;Because that is the way the framework works.&#8221; Then alarm bells should start to ring.</p><h1>Is it easy to get up to speed with?</h1><p>If you have a new SSIS developer join your team can you get him up to speed with the framework in a day? If someone with good working knowledge of SSIS cannot be up and running with your framework in a day, then you have an over engineered framework. If it takes days or even weeks for someone to get comfortable with your framework, then your framework is simply too complicated.</p><p>Imagine how much more difficult it would be for someone junior to join your team and become productive. It can already be challenging for a junior developer new to SSIS to learn the ins and outs of SSIS itself. If you now increase the complexity with an overbearing framework you will not only take longer to get the developer productive, you might actually cause them to pick up bad habits in SSIS and thus stunt their growth.</p><h1>Summary</h1><p>The questions above are by no means exhaustive but they should provide guidance and will serve as an early warning system when you undertake the task of designing or maintaining your SSIS framework. They are not deep technical questions but instead serve to highlight some of the reasons why people build frameworks to begin with namely:</p><ul><li><p>To make debugging easier</p></li><li><p>To make support easier</p></li><li><p>To reduce duplicated effort</p></li><li><p>To apply a standard across all solutions</p></li><li><p>To increate modularity</p></li><li><p>To increase development speed</p></li><li><p>To decrease the barrier to entry</p></li></ul><p>If your framework is not helping you on your way to achieving these goals, then it is time to make the changes necessary to fix this.</p>]]></content:encoded></item><item><title><![CDATA[Creating a horizontal comparison bar chart using Tableau]]></title><description><![CDATA[I have put together a screencast with some step by step instructions on how to create a horizontal comparison bar chart in Tableau.]]></description><link>https://blog.christoolivier.com/p/creating-a-horizontal-comparison-bar-chart-using-tableau</link><guid isPermaLink="false">https://blog.christoolivier.com/p/creating-a-horizontal-comparison-bar-chart-using-tableau</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Wed, 02 Dec 2015 23:02:38 GMT</pubDate><content:encoded><![CDATA[<p>I have put together a screencast with some step by step instructions on how to create a horizontal comparison bar chart in Tableau. This can be problematic when done incorrectly leading to lots of time wasted trying to align two separate bar charts on a dashboard. With this method you will create a single chart making life much easier when creating your dashboards.</p>]]></content:encoded></item><item><title><![CDATA[Why Source to Target Mapping documents matter]]></title><description><![CDATA[We as developers really do not like writing documentation.]]></description><link>https://blog.christoolivier.com/p/why-source-to-target-mapping-documents-matter</link><guid isPermaLink="false">https://blog.christoolivier.com/p/why-source-to-target-mapping-documents-matter</guid><dc:creator><![CDATA[Christo Olivier]]></dc:creator><pubDate>Thu, 22 Oct 2015 16:05:14 GMT</pubDate><content:encoded><![CDATA[<p>We as developers really do not like writing documentation. I am quite sure that if given a choice some developers would rather walk over broken glass than sit down and create documentation for their solutions. Sure there are some exceptions to this, but in general writing documentation is not something that we enjoy nearly as much as designing and developing solutions.</p><p>Documentation is sometimes done &#8220;after the fact&#8221; as a final task that needs to be delivered. Sometimes &#8220;agile&#8221; is taken way too far and no real documentation exists at all. Although this might work in certain cases there are certain documents that truly should not be gone without and which should be created before development starts. In BI solution one such document is the humble Source to Target Mapping (S2T).</p><h1>What is a Source to Target Mapping document?</h1><p>A S2T document is simply a document that contains the mapping of source system fields to the fields of a target system. In a data warehouse this will typically be a mapping from either source files or database tables to the different tables in your target data warehouse / data mart.</p><p>Think of the S2T document as being the blueprint of your ETL solution. In addition to containing the mapping of fields from source to target the document also captures the following important information:</p><ul><li><p>Loading frequency for the mapping described by the document.</p></li><li><p>How source tables / files should be joined together in order to get the desired source dataset.</p></li><li><p>Data types of both the source as well as the target fields.</p></li><li><p>Any conversion logic that is applied to convert between data types.</p></li><li><p>Any business rules that need to be applied.</p></li><li><p>Any slowly changing dimension attributes and logic.</p></li></ul><p>The S2T document is the main input into your ETL development efforts.</p><h1>When should it be create?</h1><p>The normal BI Solution design process roughly follows the following very high level steps.</p><ol><li><p>Investigate business area(s) and their reporting requirements.</p></li><li><p>Investigate potential source systems.</p></li><li><p>Design a flexible dimensional model to address the current and potential future requirements.</p></li><li><p>Translate the dimensional model into a physical model.</p></li><li><p>Develop the ETL solution.</p></li><li><p>Develop the OLAP / Tabular solution.</p></li><li><p>Develop the reports.</p></li></ol><p>Our S2T documents should be created before we start with step 5. For some reason S2T documents are sometimes created while the ETL is being developed, this would be an example of &#8220;after the fact&#8221; documentation, mainly because in practice developers tend to first develop the ETL and then create the S2T documents. When done in this way S2T documents serve very little purpose other than to be system documentation for future maintenance. This approach also causes teams to forgo the main benefits of good S2T documentation.</p><h1>What are the benefits?</h1><p>Source to target mapping documents that are created during the design phase of the solution before embark on the actual ETL development have some of the following benefits.</p><ul><li><p>Ensure that the source data required to build the model exist in the way it is required for the designed solution.</p></li><li><p>Highlight any complexities in ETL processes before development start.</p></li><li><p>Ensures that the ETL development effort can be estimated more accurately. (Remember you cannot accurately estimate what you don&#8217;t understand or know.)</p></li><li><p>Removes the bottleneck of multiple ETL developers needing to engage with the analyst(s) or source system owners all at the same time to identify how to get the source data required.</p></li></ul><p>For projects with multiple ETL developers this last point is crucial. In most organisations the number of people that know a source system well enough to provide the information needed for a S2T mapping is very small. There is nothing that derails a project plan as badly as assuming that the ETL processes can be developed in parallel only to have all of your ETL developers struggle to get time with the one or two people that can give them the information they require.</p><h1>What should it look like?</h1><p>So now that you know why you should be creating S2T documents what should they look like? Well the honest answer is that it can take many different forms. Most of the time the documents end up being Excel spreadsheets which captures the information in an easy to read manner. There is no single correct way to do this, it is important to create a format that works for your team which contains all the necessary information listed earlier.</p><p>To get you started I have included an example which you can <a href="https://storage.googleapis.com/blog_content/source_to_target/S2T.xlsx">download</a>. This S2T document is an example of loading a fictitious Product dimension from the AdventureWorks OLTP database directly from the source system into the data warehouse / data mart. In the real world you would have multiple stages e.g. Source to Stage, Stage to Warehouse etc. each having their own Source to Target mapping.</p>]]></content:encoded></item></channel></rss>