<?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:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by James Barney on Medium]]></title>
        <description><![CDATA[Stories by James Barney on Medium]]></description>
        <link>https://medium.com/@jamesbarney71?source=rss-b7c226dc9b8e------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*cnIbCvO9N7Qt0_R_ukwd8A@2x.jpeg</url>
            <title>Stories by James Barney on Medium</title>
            <link>https://medium.com/@jamesbarney71?source=rss-b7c226dc9b8e------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 05 Nov 2026 04:35:38 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@jamesbarney71/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Inference Thermodynamics]]></title>
            <link>https://medium.com/@jamesbarney71/inference-thermodynamics-6bf1283f376e?source=rss-b7c226dc9b8e------2</link>
            <guid isPermaLink="false">https://medium.com/p/6bf1283f376e</guid>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[hardware]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[energy-efficiency]]></category>
            <category><![CDATA[startup]]></category>
            <dc:creator><![CDATA[James Barney]]></dc:creator>
            <pubDate>Wed, 11 Mar 2026 20:54:54 GMT</pubDate>
            <atom:updated>2026-03-11T21:09:54.896Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>A Framework for Reasoning About the Energy Cost of AI Inference</strong></p><p>Taalas <a href="https://taalas.com/the-path-to-ubiquitous-ai/">dropped out of stealth</a> on February 19 with a chip that runs Llama 3.1 8B at 17,000 tokens per second. The weights of the model are hardwired into silicon. No High Bandwidth Memory (HBM), no memory bus. Single 815mm² die, TSMC 6nm, 250 watts. 10 times faster than anything else at a fraction of the power.</p><p>The catch: the chip <em>is</em> the model. Need a new model? You need a new chip. Two-month fabrication turnaround.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/612/0*vnGUrpdO6XdGhypk.png" /><figcaption>Taalas HC1 hard-wired with Llama 3.1 8B model</figcaption></figure><p>So how do you compare this against a GPU? Tokens per second doesn’t cut it. Cost per token doesn’t either. When one architecture uses 3-bit quantization and another runs FP16, you need a unit that captures speed, power, and whether the outputs are actually right.</p><p>This is my attempt at that framework. Five relationships, sourced power specs, and cross-platform comparisons. The framework is not perfect. But the core finding holds: current inference hardware operates 10¹⁰ times above the thermodynamic floor, and the biggest energy tax in the stack is the one compute-in-memory eliminates.</p><p>To put that in perspective: <a href="https://en.wikipedia.org/wiki/ENIAC">ENIAC</a>, the first programmable general purpose computer in 1945, consumed ~150,000 watts to do a few thousand operations per second. A modern CPU does ~10¹⁰ operations per second per watt. That’s roughly the same gap. <strong>Current inference hardware is at the vacuum tube stage of energy efficiency.</strong></p><p>To be absolutely transparent, I’m not a hardware specialist and used Claude heavily to explore, analyze and write about this concept. The results are outlined below.</p><h3><strong>The Inference Unit That Matters</strong></h3><p>A single token isn’t true or false. “The capital of France is” → “Paris” is four tokens where truth belongs to the <em>completed inference</em>, not any individual token.</p><p>So the base unit is <strong>joules per useful inference</strong>. A complete generation that actually answers the question correctly. Everything derives from that.</p><p>Three variables feed it: speed (tokens per second), power (watts), and accuracy (what fraction of inferences are useful). One more variable handles scale: how many concurrent inference streams the hardware supports.</p><h3><strong>The Five Equations</strong></h3><h4><strong>Variable List</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/552/1*ITquI7ge0kdPW2dBTpkScA.png" /></figure><h4><strong>The Equations</strong></h4><p><strong>Equation I — Inference Energy (definition)</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/145/1*zP5WcmX7FJQEDPeUd2IAbg.png" /></figure><p>This is just dimensional analysis, moving variables around. Power divided by the rate of useful inference production. `<em>N</em>` inference streams, each producing `<em>S/T</em>` inferences per second, fraction `<em>A</em>` correct. Everything else builds off this.</p><p><strong>Equation II — Speed Limit</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/131/1*W8ogq9M-s977b9NLBSl3yg.png" /></figure><p>Max throughput = compute density × die area, divided by operations per token. `<em>2PQ</em>` approximates the multiply-accumulate workload per token.</p><p>This holds for decode-dominated, short-context inference where weight multiplies dominate. It ignores attention (which scales `<em>O(T·d)</em>` per token) and treats `<em>κ</em>` as constant across architectures. Back-of-envelope, not gospel.</p><p><strong>Equation III — Landauer Energy Floor</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/236/1*ZVBwvFca8F-EbYMB3-wIPw.png" /></figure><p>The thermodynamic minimum. Every irreversible bit operation costs at least:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/226/1*GVLkf_0vEtKpo1RBO3tdYw.png" /></figure><p>An 8 billion parameter model at 3-bit Q generates 200 tokens: that’s about 57 nanojoules. Same `2PQ` approximation as above.</p><p>Interesting comparison for you: 160 nJ is <a href="https://web.archive.org/web/20121213173112/https://public.web.cern.ch/public/en/Science/Glossary-en.php#:~:text=One%20TeV%20is%20about%20the%20energy%20of%20motion%20of%20a%20flying%20mosquito">the energy of a flying mosquito</a>.</p><p><strong>Equation IV — Density-Power Tradeoff</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/226/1*QDipN5OnenjpaXuAkrIamw.png" /></figure><p>Power = total ops/sec × energy per op. <em>`ε(D)</em>` increases with die area. The longer wires on the chip, the more capacitance you have, the more clock distribution overhead you have. There’s no free lunch, even in AI.</p><p><strong>Equation V — Accuracy-Capacity Bound</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/235/1*VExNdpJtimOwFZG3YkW4Tw.png" /></figure><p>Accuracy bounded by model capacity (`P × Q`) and reasoning budget (`T`).</p><p>This is the weakest part of the framework. Real scaling laws describe <em>loss</em> as a power law, not <em>accuracy</em> as a log function. “Accuracy” itself is ill-defined as a single scalar: MMLU, HumanEval, and real-world task completion are different numbers. The coefficients `α` and `β` are hand-calibrated to two data points.</p><p>What matters is that accuracy generally improves sublinearly with model size. There are diminishing returns. The exact functional form matters less than what falls out of it (next section).</p><h4><strong>What Falls Out</strong></h4><p><strong>Efficiency Ratio</strong></p><p>Combining Relationships I and III yields η:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/133/1*Wc8uPsvx67AXlqeLSPfqUg.png" /></figure><p>How far above the thermodynamic minimum a given system is. This is not Carnot efficiency — there’s no reversible cycle argument here. It’s just a useful ratio.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/442/1*jDd_IEjwMNYJkFxTZwrsWA.png" /></figure><p>The 10 ¹¹ gap represents an enormous engineering opportunity. Even closing a fraction of this gap yields transformative cost reductions.</p><p><strong>Thermodynamically Optimal Model Size</strong></p><p>Relationship V, despite its imprecision, creates an important qualitative tension with Relationship III. Any sublinear accuracy curve + linear energy cost = a peak in accuracy-per-joule at some finite model size. You can explore that tension with:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/88/1*EEtzccKmT6LEdFtgXDaVYw.png" /></figure><p>The framework <em>predicts</em> the peak exists and shifts right with better hardware. Where exactly it falls depends on Relationship V’s calibration, which I already flagged as rough. The qualitative insight: there is an optimum and better hardware pushes that optimum toward larger models.</p><p>Better hardware doesn’t just make the same model cheaper. It makes bigger models the rational choice.</p><h4><strong>The Energy Ladder</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cGIPpBWA9_xF9L8s7S-2DA.png" /><figcaption><em>Figure 1. The Energy Ladder. Llama 3.1 8B, T=200, single stream. Log scale — each visual step is roughly 10 times in energy. Groq value uses per-card power (275W) and per-card throughput — see Section 5 methodology notes.</em></figcaption></figure><p>The biggest gap is between “No Data Movement” and GPU baselines. That’s the <strong>memory wall tax</strong> . That’s energy spent shuttling weights from HBM to compute dwarfing the consumption of everything else.</p><p>Taalas eliminates this energy tax entirely by putting weights in mask ROM. The remaining gap between Taalas and the theoretical “no data movement” level is system overhead: clock distribution, voltage regulation, I/O, host CPU, power supply losses. You get that loss in any computational system.</p><h3><strong>Platform Analysis</strong></h3><h4><strong>Data Sources and Methodology</strong></h4><p>Power specs from official docs, datasheets, and verified reporting. Throughput from Artificial Analysis and manufacturer claims. All Llama 3.1 8B, single-user output generation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/773/1*y9RPxtE33i2yeNfrEScYww.png" /></figure><p><em>Table 1. Data sources for platform specifications.</em></p><h4><strong>Methodology Notes</strong></h4><p><strong>Groq:</strong> Each GroqChip has 230MB SRAM. Llama 8B at INT8 is ~8GB. That’s ~35 cards. Per-card power is 275W, but <strong>system power for one model instance is ~9.6kW</strong>. Charts use per-card for silicon-level comparison, but the real system cost is much higher. Groq self-reports 1–3 J/tok for their cloud service which likely reflects heavy batching.</p><p><strong>Cerebras:</strong> CS-3 is 23kW for a system designed to run 70B–1T+ models. Using 23kW for an 8B model comparison is like measuring a semi truck’s fuel efficiency delivering a single envelope. Unfair to Cerebras. Included for completeness, flagged here.</p><p><strong>Taalas:</strong> The 250W per-card figure and 17K tok/s are both first-party claims verified by EE Times reporting. The entire 8B model resides on a single 815mm² chip (TSMC 6nm, 53B transistors). The “per user” qualification suggests single-stream operation. 10 cards in a 2.5kW server provide 10 concurrent user streams.</p><h4><strong>Cross-Platform Comparison</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/582/1*lRdTWJg3bp8_UepYU2AI_g.png" /></figure><p><em>Table 2. Cross-platform comparison. Groq uses per-card power (275W); system power for full 8B model is ~9.6kW across ~35 cards. Cerebras uses full 23kW system power. See methodology notes.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uEckSYi9OjpJiRM1ZkxgAA.png" /><figcaption><em>Figure 2. Energy per useful inference (left) and energy per token (right). Log scale. *See methodology notes on system vs. per-chip power allocation.</em></figcaption></figure><h4><strong>Joules per Token — Alternative Metric</strong></h4><p>Joules per token provides a simpler metric that avoids assumptions about accuracy and inference length. Taalas achieves 0.015 J/tok which is dramatically lower than all competitors. Groq self-reports 1–3 J/tok for their cloud service, while NVIDIA H100-class systems typically consume 10–30 J/tok at system level.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*G2rYq4pnzb_oxnmd3tV5Kg.png" /><figcaption><em>Figure 3. Joules per token — mixed methodology comparison. Taalas/B200/H200 computed from per-chip power/throughput. Groq (reported) is self-reported cloud figure. H100 (reported) from Groq comparison claims. Per-card Groq figure is misleading because one card holds only a fraction of the model.</em></figcaption></figure><h4><strong>The Real Trade Off</strong></h4><p>Taalas 8B sits in the bottom-left: cheap but lower accuracy from 3-bit quantization. B200 running 405B is top-right: high accuracy, high energy. The frontier connecting them is the real tradeoff curve — and everything off that frontier is leaving performance on the table.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*crttESvZxHy0HbEdLNsv-w.png" /><figcaption><em>Figure 5a. Every dot is a real model + platform combination. X-axis is energy cost, Y-axis is accuracy. Dot size scales with parameter count. The Pareto frontier (dashed) shows the best accuracy available at each energy level. Taalas R1 671B is simulated (30-chip config). Groq uses system-level power. Accuracy estimates are approximate composites — see methodology notes.</em></figcaption></figure><p>The more interesting question: what happens to that frontier when hardware gets better?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XBZLx9VLqAMV6LgLl2JuoA.png" /><figcaption><em>Figure 5b. Same three models (8B, 70B, 405B), three hardware generations. GPU baseline (B200), compute-in-memory (Taalas-class), and a projected future (1-bit CIM). The entire curve shifts left with each generation. 70B and 405B values for Taalas and future hardware are projections based on multi-chip scaling and density estimates — not measurements.</em></figcaption></figure><p>Each hardware generation slides the whole curve left. GPU 405B at ~20 kJ. Taalas-class 405B at ~1.5 kJ. Future CIM at ~0.05 kJ. Same model, same accuracy (roughly), orders of magnitude less energy.</p><p>But here’s the punchline: on better hardware, the <em>slope</em> of the curve also changes. The marginal energy cost of jumping from 70B to 405B shrinks dramatically. On GPUs, going from 70B to 405B costs ~20 times more energy for ~10% more accuracy. On projected future CIM hardware, that same jump costs ~3 times more energy. Bigger models become rational faster on better hardware.</p><p>Hardware and model scaling aren’t independent axes. They’re coupled through energy.</p><h4>Where This Breaks Down</h4><p>The Taalas numbers are real for 8B models. But there’s a ceiling, and it’s physical.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/740/0*QpbYuH9SrWwamSZQ.jpg" /></figure><p>An 8B model at 3-bit quantization is ~3GB of weight data. Fits on one 815mm² die — which is already pushing the reticle limit (the maximum area a lithography stepper can expose in one shot). There’s no headroom to grow on a single chip.</p><p>Scale up and the math shows how this process begins to fall apart:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/813/1*6hV2VW8NYdlJ7A5PfpPJrA.png" /></figure><p>The whole advantage , no data movement, degrades as you cross chip boundaries. A 70B model split across 16 dies reintroduces the interconnect penalty that Taalas exists to eliminate. It’s smaller than GPU-to-HBM bandwidth (chip-to-chip on a module is faster than off-package memory), but it’s not zero. The single-die purity is gone.</p><p>Other physical constraints:</p><ul><li><strong>Yield:</strong> Larger dies have exponentially worse yield. A single defect kills the chip. Mask ROM weights can’t be remapped around defects the way DRAM uses redundant rows. Either they’ve solved this or their yield rate is a meaningful cost factor they haven’t disclosed.</li><li><strong>Model lock-in:</strong> Taalas CEO Ljubisa Bajic told EE Times customers should expect a one-year commitment per chip. If a better model drops six months in, your silicon is obsolete. The two-month turnaround helps but doesn’t eliminate the risk.</li><li><strong>Quantization quality:</strong> Gen 1 uses custom 3 or 6-bit. Gen 2 moves to standard FP4. But even FP4 has measurable quality loss on reasoning-heavy benchmarks. The accuracy axis in our framework isn’t free.</li></ul><p>The strong counterargument is that the models worth <em>deploying</em> keep getting smaller. Distillation, mixture-of-experts (where individual experts are small), and better quantization methods (BitNet, QuIP#) all push useful capability into the single-die range. If a 2-bit quantized 70B model is ~17GB, that’s back to feasible on a few dies. If mixture-of-experts means you only activate 8B of parameters per token, you’re back in the sweet spot entirely.</p><p>Taalas is betting that production inference workloads converge on the 8B–70B range at aggressive quantization. I don’t think that’s a crazy bet.</p><p><strong>Path to Theoretical Limits</strong></p><p>On GPUs, 95% of the energy moves data. It’s not computing anything — just <em>moves</em>. Taalas eliminates that layer entirely, which is why it’s 20× more efficient despite running on an older process node. But look at what’s left: system overhead (yellow) is now almost the entire bar. Clocks, voltage regulation, I/O, power supply losses. That’s the next frontier.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uLUXDCP2KN3PA_Ym4WyTzg.png" /><figcaption><em>Figure 6. Stacked energy breakdown per platform, linear scale. Left: all four platforms at true proportion — Taalas and Future are barely visible next to the GPU bars. The red layer (memory wall) is 93–95% of GPU energy and doesn’t exist on Taalas. Green and blue (fundamental compute + wiring) are 2 J combined — invisible at this scale, called out in the annotation. Right: zoom into Taalas and Future CIM, where system overhead (yellow) becomes the dominant layer. Future CIM projections assume 10× system overhead reduction.</em></figcaption></figure><p>~33 doublings between here and Landauer. Just doubling 10 of them and inference energy becomes trivial. We’re still in the ENIAC era of AI.</p><h4><strong>Implications</strong></h4><ul><li><strong>Memory wall is the biggest tax.</strong> The dominant gap in the ladder. Compute-in-memory eliminates it. Taalas is the extreme case — mask ROM weights — but the principle applies to any architecture that keeps weights close to compute.</li><li><strong>Quantization is a thermodynamic lever.</strong> Lower `Q` improves speed (II), floor (III), and power (IV) simultaneously, at modest accuracy cost (V). 1-bit methods (BitNet) could move the whole framework an order of magnitude.</li><li><strong>System power swamps chip power.</strong> Groq at 275W/card looks great until you need 35 cards. Cerebras at 23kW looks terrible until you remember it’s built for 70B+ models. Always ask: power of <em>*what*</em>?</li><li><strong>`η` as a universal benchmark.</strong> Ratio of actual to Landauer energy. Architecture-independent. Track it over time the way we track transistor density. It’s just a ratio, not a deep theorem — but it’s the right ratio.</li></ul><h4><strong>Conclusion</strong></h4><p>Five relationships. One framework. Current hardware is 10¹⁰ to 10¹¹ times above the theoretical floor. The biggest tax is data movement, and it’s eliminable. An optimal model size exists for every hardware efficiency level, and it shifts right as hardware improves. Model compression and hardware specialization are convergent, not competing.</p><h4><strong>Sources</strong></h4><ol><li>Taalas. <a href="https://taalas.com/the-path-to-ubiquitous-ai/">“The path to ubiquitous AI.”</a> taalas.com, Feb 2026.</li><li>EE Times. “<a href="https://www.eetimes.com/taalas-specializes-to-extremes-for-extraordinary-token-speed/">Taalas Specializes to Extremes for Extraordinary Token Speed.</a>” Feb 2026.</li><li>The Next Platform. “<a href="https://www.nextplatform.com/2026/02/19/taalas-etches-ai-models-onto-transistors-to-rocket-boost-inference/">Taalas Etches AI Models Onto Transistors.</a>” Feb 2026.</li><li>Kaitchup. “<a href="https://kaitchup.substack.com/p/taalas-hc1-absurdly-fast-per-user">Taalas HC1: Absurdly Fast Per-User Inference.</a>” Substack, Mar 2026.</li><li>NVIDIA. “<a href="https://www.nvidia.com/en-us/data-center/h200/">H200 Tensor Core GPU.</a>” nvidia.com.</li><li>WCCFTech. “<a href="https://wccftech.com/nvidia-fully-enabled-blackwell-b200-gpus-consume-1200w-completely-different-architecture-from-hopper/">NVIDIA Blackwell B200 GPUs Consume Up To 1200W.</a>” Mar 2024.</li><li>Groq. “<a href="https://groq.com/blog/inside-the-lpu-deconstructing-groq-speed">Inside the LPU: Deconstructing Groq Speed.</a>” groq.com.</li><li>Mouser Electronics. GroqCard product listing. TDP: 275W.</li><li>Cerebras. “<a href="https://www.cerebras.ai/blog/cerebras-cs-3-vs-nvidia-b200-2024-ai-accelerators-compared">CS-3 vs NVIDIA B200.</a>” cerebras.ai, Mar 2025.</li><li>Artificial Analysis. <a href="https://artificialanalysis.ai/models/llama-3-1-instruct-8b/providers">Llama 3.1 8B provider benchmarks.</a> artificialanalysis.ai.</li><li>Zheng Li / Medium. “<a href="https://medium.com/the-low-end-disruptor/groqs-deterministic-architecture-is-rewriting-the-physics-of-ai-inference-bb132675dce4">Groq’s Deterministic Architecture is Rewriting the Physics of AI Inference.</a>” Dec 2025.</li><li>Landauer, R. [“<a href="https://doi.org/10.1147/rd.53.0183">Irreversibility and Heat Generation in the Computing Process.</a>” IBM J. Res. Dev., 1961.</li></ol><p><em>Developed through collaborative exploration with Claude (Anthropic), building from first-principles analysis of the Taalas HC1 announcement. Calibration constants (α, β, ε) are approximate and should be refined with empirical measurement. Power figures sourced from official documentation and verified reporting as of March 2026.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6bf1283f376e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Real Dice, AI Storytelling: How I Built a Solo Holiday RPG with LangChain]]></title>
            <link>https://medium.com/@jamesbarney71/real-dice-ai-storytelling-how-i-built-a-solo-holiday-rpg-with-langchain-adafe43d3c8b?source=rss-b7c226dc9b8e------2</link>
            <guid isPermaLink="false">https://medium.com/p/adafe43d3c8b</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[games]]></category>
            <category><![CDATA[llm]]></category>
            <dc:creator><![CDATA[James Barney]]></dc:creator>
            <pubDate>Mon, 22 Dec 2025 17:11:57 GMT</pubDate>
            <atom:updated>2025-12-22T17:11:57.434Z</atom:updated>
            <content:encoded><![CDATA[<p>A solo tabletop RPG powered by LangChain and Fireworks AI, where players control a brave Gingerbread Scout on a quest to save Christmas.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BRwm5FwHJE-wy4wZsAyo8g.png" /><figcaption>The North Pole Quest</figcaption></figure><h3>Introduction</h3><p>I wanted to capture some part of what it’s like to really play tabletop RPG games with a human GM. Random luck, creative outcomes, odd combinations all make table-top RPGs a special experience that is very difficult to emulate in video games.</p><p>What if an AI could be your dungeon master, but you still roll physical dice?</p><p><em>Just want to play? Start here (then come back to learn!) </em><a href="https://north-pole-quest.vercel.app"><em>https://north-pole-quest.vercel.app</em></a></p><p>I’ve had this idea for an AI-based RPG (role playing game) for a while now. Some of my previous attempts worked well enough but the game master (GM) would commonly skip restrictions or make things up.</p><p>So for this version I had a few key requirements in mind as I started working on this with Claude:</p><ol><li>The AI GM must control the flow of the game without any hard coded game mechanics</li><li>All game mechanics had to be exposed as tools that the GM could use</li><li>The game should play like a real game, not just a technical demo, so inputs should be randomized using real cards, dice, or their digital analogs (random number generators)</li></ol><p>To build this, I’ve used a few key technologies:</p><ul><li>LangChain and LangGraph for the AI/GM game loop</li><li>Fireworks.ai for LLM inference and image generation</li><li>LangSmith Deploy for deploying the AI agent infrastructure</li><li>Vercel for deploying the customized Agent Chat UI interface</li></ul><p>Obviously, most AI isn’t being deployed as a holiday game but I built this as a way to explore these newer concepts in the AI world. The learnings here are directly applicable to real AI deployments. Enjoy!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0IDUQsQKgLD61MYa0ri5Ow.png" /><figcaption>North Pole Quest with Card Drawing mechanism</figcaption></figure><h3>The Concept: Bridging Digital and Physical</h3><p>Solo RPGs traditionally require lookup tables or complex oracles. While those are part of the charm for real table-top games, it can introduce disengaging friction into the gameplay loop. So a digital lookup capability encoded into the game speeds this.</p><p>Even with the game skeleton digitized, engaging narrative can be hard to craft for each possible scenario that a player and a GM could encounter together. Generative AI really shines here. A real human GM can spend hours creating the world for their players, but that still won’t exhaustively cover all the possible rules, items, and scenarios in the game.</p><p>Asking the LLM to “roll dice” or “draw cards” isn’t realistic and is also very disengaging for a human player. In a few test games I played with Claude before starting this project, I found that if I actually <em>physically </em>rolled the dice and drew the cards, the game was much more engaging for me.</p><p>This introduces a complication for an AI-based game — how do you properly block AI continuation and await human input (the result of dice rolls or card draws)? The interrupt pattern exposed through LangGraph does this for us nicely. It pauses AI execution waiting for player input.</p><h3>Architecture Overview</h3><p>I think people tend to overcomplicate the architecture of AI applications. At a 10,000 foot level, these applications are nothing more than a standard web service with persistent storage and an interactive user interface.</p><p>That’s exactly what the North Pole Quest is:</p><pre>┌─────────────────────────────────────────────────────────────────┐<br>│                         Frontend (UI)                           │<br>│   - Displays narration, images, game status                     │<br>│   - Handles physical dice rolls and card draws                  │<br>│   - Manages inventory UI                                        │<br>└─────────────────────────┬───────────────────────────────────────┘<br>                          │ interrupt/resume<br>┌─────────────────────────▼───────────────────────────────────────┐<br>│                    LangGraph Agent                              │<br>│  ┌─────────────┐  ┌──────────────┐  ┌────────────────────────┐  │<br>│  │    LLM      │  │  Middleware  │  │        Tools           │  │<br>│  │ (Fireworks) │◄─┤  - Status    │◄─┤  - take_action         │  │<br>│  │             │  │  - Summary   │  │  - discover_new_region │  │<br>│  │  GameMaster │  │  - Limits    │  │  - check_for_gift      │  │<br>│  └─────────────┘  └──────────────┘  │  - search_for_items    │  │<br>│                                     │  - hot_cocoa_break     │  │<br>│                                     │  - inventory tools     │  │<br>│                                     │  - generate_scene_image│  │<br>│                                     └────────────────────────┘  │<br>└─────────────────────────┬───────────────────────────────────────┘<br>                          │<br>┌─────────────────────────▼───────────────────────────────────────┐<br>│                       GameState                                 │<br>│  sleepiness, gifts_found, current_location, inventory,          │<br>│  turns_remaining, region_search_counts, latest_image, etc.      │<br>└─────────────────────────────────────────────────────────────────┘</pre><p>The front end is how the users experience the game as a chat-based interface. It’s also where we emulate the dice rolling and card drawing.</p><p>The AI agent exists at the webserver level, responding to user input from the front end, performing basic actions and executing the tools it has access to. Critically, by separating game logic out into tools, we can apply fine-grained controls and permissions to these tools (if/else scenarios) which allows the AI agent to focus on GM-specific tasks, not on managing how many candy canes you have in your inventory.</p><p>Finally, the state of each individual game is stored in an independent Agent Run in LangSmith Deploy. Each run keeps its details logically separate, ensuring there’s no crossover from another game being played by someone else in parallel. It also lets the player leave, come back, and pick up where they left off.</p><h3>The Interrupt Pattern Deep Dive</h3><p>The interrupt pattern is the key part of the game that prevents the GM from hallucinating tool result calls. Let’s walk through how it works.</p><p>After each user input (or game start):</p><ol><li>LangGraph Interrupts: The backend agent pauses and sends a GameInputRequest when it needs dice/card input</li><li>Frontend Detection: Rendering components check stream.interrupt for game input requests</li><li>Interactive Components: DiceRoller (animated 10-iteration roll) and CardDrawer (uses persistent deck in local browser storage)</li><li>Resume Flow: User action triggers stream.submit() with a command.resume payload containing dice values/cards plus current game state</li></ol><p>So to discover a new region, #1 and #4 is from above is where the bulk of the work happens.</p><p>The user asks the GM to discover a new region:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/390/1*Xxl2e9GAUElIZkG3gLcttw.png" /></figure><p>The agent requests a dice roll below:</p><pre>def request_dice(num_dice: int, reason: str) -&gt; dict:<br>    &quot;&quot;&quot;Request dice roll from user via interrupt.<br><br>    Returns dict with dice_values and dice_total from user input.<br>    &quot;&quot;&quot;<br>    response = interrupt({<br>        &quot;type&quot;: &quot;game_input&quot;,<br>        &quot;request&quot;: &quot;dice&quot;,<br>        &quot;num_dice&quot;: num_dice,<br>        &quot;reason&quot;: reason<br>    })<br>    return normalize_response(response)</pre><p>The frontend catches this request and routes it to the appropriate input type (dice in this case)</p><pre>interface GameInputRequest {<br>  type: &quot;game_input&quot;;<br>  request: &quot;dice&quot; | &quot;card&quot; | &quot;inventory_state&quot; | &quot;region_state&quot; | &quot;state_check&quot;;<br>  num_dice?: number;<br>  num_cards?: number;<br>  reason?: string;<br>}</pre><p>The result is captured and returned to the backend (discovering a new region). Basic checks are performed on state (is the player too sleepy to discover a region?), matching the region to the provided roll from the frontend. The matched region is returned by the backend.</p><pre>@tool<br>def discover_new_region(<br>    tool_call_id: Annotated[str, InjectedToolCallId] = &quot;&quot;<br>) -&gt; Command:<br>    &quot;&quot;&quot;Explore a new magical location by rolling 2d6 to determine where you end up.<br>    The player rolls the dice! Automatically adds the location to explored places.<br>    Costs 1 turn on the Christmas Clock.&quot;&quot;&quot;<br>    dice_response = request_dice(2, &quot;Roll 2d6 to discover a new location!&quot;)<br><br>    # Check fatigue before allowing action<br>    is_too_sleepy, fatigue_msg, _ = check_fatigue(dice_response)<br>    if is_too_sleepy:<br>        return Command(<br>            update={<br>                &quot;last_dice&quot;: None,<br>                &quot;last_card&quot;: None,<br>                &quot;messages&quot;: [{&quot;role&quot;: &quot;tool&quot;, &quot;content&quot;: fatigue_msg, &quot;tool_call_id&quot;: tool_call_id}]<br>            }<br>        )<br><br>    # Check time remaining (costs 1 turn)<br>    is_game_over, ending_msg, turns_after, _ = check_time_remaining(dice_response, turn_cost=1)<br>    if is_game_over:<br>        return Command(<br>            update={<br>                &quot;turns_remaining&quot;: 0,<br>                &quot;last_dice&quot;: None,<br>                &quot;last_card&quot;: None,<br>                &quot;messages&quot;: [{&quot;role&quot;: &quot;tool&quot;, &quot;content&quot;: ending_msg, &quot;tool_call_id&quot;: tool_call_id}]<br>            }<br>        )<br><br>    dice = dice_response.get(&quot;dice_values&quot;, [1, 1])<br><br>    key = f&quot;{dice[0]}{dice[1]}&quot;<br>    location = REGIONS.get(key, &quot;Mysterious Snowy Place&quot;)<br><br>    message = f&quot;🗺️ Rolled {dice[0]}, {dice[1]} - You found: **{location}**! ✨\n💡 Explore or take an action here before searching for a Magic Gift!\n⏰ Turns remaining: {turns_after}&quot;<br><br>    return Command(<br>        update={<br>            &quot;current_location&quot;: location,<br>            &quot;discovered_regions&quot;: [location],<br>            &quot;turns_remaining&quot;: turns_after,<br>            &quot;last_dice&quot;: None,<br>            &quot;last_card&quot;: None,<br>            &quot;region_action_taken&quot;: False,<br>            &quot;messages&quot;: [{&quot;role&quot;: &quot;tool&quot;, &quot;content&quot;: message, &quot;tool_call_id&quot;: tool_call_id}]<br>        }<br>    )</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/396/1*UyiGCOELw-2TLWH8Yy6muQ.png" /><figcaption>Gingy’s not in the Warm Workshop anymore</figcaption></figure><p>The new region is returned, the GM detects this and generates a new image accordingly, and the player is prompted for their next action.</p><h3>Managing Game State</h3><p>The challenge with any AI-powered capability is keeping LLM informed without polluting message history. Since LLMs have limited context (though context size has gotten much larger recently), it’s important to keep critical and controlling aspects of the game outside of the LLM’s context.</p><p>We manage this by leveraging a LangGraph middleware that injects the current GameStatus into the system prompt given to the Agent for each game loop.</p><pre>class GameState(MessagesState):<br>    sleepiness: int = 0<br>    gifts_found: int = 0<br>    total_gifts: int = 6  # Total gifts needed to win<br>    current_location: str = &quot;&quot;<br>    discovered_regions: Annotated[List[str], add] = []<br>    # Store latest generated image separately (not in messages sent to LLM)<br>    latest_image: Optional[str] = None<br>    # Track last dice/card results for UI display<br>    last_dice: Optional[List[int]] = None<br>    last_card: Optional[List[str]] = None<br>    # Inventory system - list of item dicts with name, weight, description, effect<br>    inventory: List[dict] = []<br>    inventory_capacity: int = DEFAULT_INVENTORY_CAPACITY  # Max weight the elf can carry<br>    # Track if player has taken an action in the current region (required before searching for gift)<br>    region_action_taken: bool = False<br>    # Christmas Clock - turns remaining until Christmas morning<br>    turns_remaining: int = 24<br>    # Track gift searches per region (max 2 per region to prevent cheese)<br>    region_search_counts: Dict[str, int] = {}</pre><p>This prevents GM hallucination by dynamically building prompts from real game data that is controlled by “hard” deterministic programming, instead of “soft” LLM-based rules.</p><pre>def build_game_status(state: GameState) -&gt; str:<br>    &quot;&quot;&quot;Build the game status string from current state.&quot;&quot;&quot;<br>    sleepiness = state.get(&quot;sleepiness&quot;, 0)<br>    gifts = state.get(&quot;gifts_found&quot;, 0)<br>    total_gifts = state.get(&quot;total_gifts&quot;, 6)<br>    current_location = state.get(&quot;current_location&quot;, &quot;Unknown&quot;)<br>    discovered = state.get(&quot;discovered_regions&quot;, [])<br>    inventory = state.get(&quot;inventory&quot;, [])<br>    capacity = state.get(&quot;inventory_capacity&quot;, 8)<br>    region_action_taken = state.get(&quot;region_action_taken&quot;, False)<br>    turns_remaining = state.get(&quot;turns_remaining&quot;, 24)<br>    region_search_counts = state.get(&quot;region_search_counts&quot;, {})<br><br>    # Calculate searches remaining in current region<br>    current_searches = region_search_counts.get(current_location, 0) if current_location else 0<br>    searches_remaining = max(0, 2 - current_searches)<br><br>    # Calculate inventory weight<br>    current_weight = sum(item.get(&quot;weight&quot;, 0) for item in inventory)<br><br>    sleepy_warning = &quot; 🍪 TIME FOR HOT COCOA!&quot; if sleepiness &gt;= 4 else &quot;&quot;<br>    victory_note = &quot; 🎄 CHRISTMAS IS ALMOST SAVED!&quot; if gifts &gt;= total_gifts - 1 else &quot;&quot;<br>    satchel_warning = &quot; ⚠️ SATCHEL NEARLY FULL!&quot; if current_weight &gt;= capacity - 1 else &quot;&quot;<br><br>    # Gift search status with remaining searches<br>    if not region_action_taken:<br>        gift_search_status = &quot;❌ Must take action first&quot;<br>    elif searches_remaining == 0:<br>        gift_search_status = &quot;🚫 No searches left here - move to new region!&quot;<br>    else:<br>        gift_search_status = f&quot;✅ Can search ({searches_remaining} remaining) - SUGGEST THIS!&quot;<br><br>    # Gift progress reminder<br>    gifts_remaining = total_gifts - gifts<br>    if gifts == 0:<br>        gift_reminder = &quot;🎁 REMINDER: Player hasn&#39;t found any gifts yet! Guide them to search!&quot;<br>    elif gifts_remaining &gt; 0:<br>        gift_reminder = f&quot;🎁 {gifts_remaining} gifts still needed! Encourage gift searching!&quot;<br>    else:<br>        gift_reminder = &quot;🎁 ALL GIFTS FOUND! Guide player to celebrate!&quot;<br><br>    # Time pressure warnings<br>    if turns_remaining &lt;= 3:<br>        time_warning = &quot; ⚠️ CHRISTMAS MORNING IS ALMOST HERE!&quot;<br>    elif turns_remaining &lt;= 5:<br>        time_warning = &quot; ⏰ Time is running out!&quot;<br>    else:<br>        time_warning = &quot;&quot;<br><br>    # Format inventory items<br>    if inventory:<br>        inv_items = [<br>            f&quot;  - {item[&#39;name&#39;]} (wt:{item[&#39;weight&#39;]})&quot; for item in inventory]<br>        inv_str = &quot;\n&quot;.join(inv_items)<br>    else:<br>        inv_str = &quot;  (empty)&quot;<br><br>    return f&quot;&quot;&quot;[GAME STATUS]<br>Christmas Clock: {turns_remaining} turns until morning{time_warning}<br>Sleepiness: {sleepiness}/5{sleepy_warning}<br>Magic Gifts: {gifts}/{total_gifts}{victory_note}<br>Current Location: {current_location or &#39;Just woke up!&#39;}<br>Region Gift Search: {gift_search_status}<br>{gift_reminder}<br>Explored: {&#39;, &#39;.join(discovered) if discovered else &#39;None yet&#39;}<br>Satchel: {current_weight}/{capacity} weight{satchel_warning}<br>{inv_str}<br>[/GAME STATUS]&quot;&quot;&quot;</pre><p>Notice how we can inject additional context and meaning that is important to the game based off numbers that an LLM would otherwise not have an idea about.</p><p>Instead of just saying “gifts_remaining = 4”, we can enhance the message that the LLM bases the state of the game off of, making it more exciting for the player.</p><pre># Gift progress reminder<br>    gifts_remaining = total_gifts - gifts<br>    if gifts == 0:<br>        gift_reminder = &quot;🎁 REMINDER: Player hasn&#39;t found any gifts yet! Guide them to search!&quot;<br>    elif gifts_remaining &gt; 0:<br>        gift_reminder = f&quot;🎁 {gifts_remaining} gifts still needed! Encourage gift searching!&quot;<br>    else:<br>        gift_reminder = &quot;🎁 ALL GIFTS FOUND! Guide player to celebrate!&quot;</pre><p>We can also use this same capability to save user preferences like preferred language!</p><pre>def get_language_instruction(language_code: str) -&gt; str:<br>    &quot;&quot;&quot;Build language instruction for the model based on language code.&quot;&quot;&quot;<br>    if language_code == &quot;en&quot;:<br>        return &quot;&quot;  # No instruction needed for English (default)<br><br>    language_name = LANGUAGE_NAMES.get(language_code, language_code)<br>    return f&quot;&quot;&quot;[LANGUAGE INSTRUCTION]<br>You MUST respond entirely in {language_name}.<br>All narration, dialogue, choices, and game text should be in {language_name}.<br>Keep game mechanics terms (like SPARKLE, FLURRY, SNOWDRIFT) in English for consistency, but translate everything else.<br>[/LANGUAGE INSTRUCTION]&quot;&quot;&quot;</pre><h3>Keeping the AI on Track</h3><p>One issue I quickly ran into was multiple concurrent game tool invocations. This is the same issue that earlier attempts at this concept had. Essentially, the AI GM didn’t know when to stop applying tools.</p><p>I’m not sure why this happens and it can probably be solved many different ways, but I chose to apply LangChain middleware to this problem again.</p><p>Since this is a common issue, LangChain now ships with ToolCallLimitMiddleware and its very easy to use. List the tools and apply the middleware to each along with the limits and exit behavior. Note that image generation is exits with ‘continue’ since the image doesn’t require human input.</p><pre># Tool call limits - prevent the LLM from calling action tools multiple times per turn<br># These tools should only be called once per player action<br># Using exit_behavior=&quot;end&quot; to end the agent loop after the tool runs<br>action_tool_limits = [<br>    ToolCallLimitMiddleware(tool_name=&quot;search_for_items&quot;,<br>                            run_limit=1, exit_behavior=&quot;end&quot;),<br>    ToolCallLimitMiddleware(tool_name=&quot;check_for_gift&quot;,<br>                            run_limit=1, exit_behavior=&quot;end&quot;),<br>    ToolCallLimitMiddleware(tool_name=&quot;discover_new_region&quot;,<br>                            run_limit=1, exit_behavior=&quot;end&quot;),<br>    ToolCallLimitMiddleware(tool_name=&quot;take_action&quot;,<br>                            run_limit=1, exit_behavior=&quot;end&quot;),<br>    ToolCallLimitMiddleware(tool_name=&quot;hot_cocoa_break&quot;,<br>                            run_limit=1, exit_behavior=&quot;end&quot;),<br>    ToolCallLimitMiddleware(tool_name=&quot;ask_snow_globe&quot;,<br>                            run_limit=1, exit_behavior=&quot;end&quot;),<br>    # Image generation uses &quot;continue&quot; so GM can narrate after generating<br>    ToolCallLimitMiddleware(tool_name=&quot;generate_scene_image&quot;,<br>                            run_limit=1, exit_behavior=&quot;continue&quot;),<br>]<br><br>graph = create_agent(<br>    llm,<br>    tools=tools,<br>    system_prompt=system_prompt,<br>    state_schema=GameState,<br>    middleware=[inject_game_status, summarization_middleware] +<br>    action_tool_limits,<br>)</pre><p>We also add a Summarization middleware to handle particularly long game sessions. This will summarize after 10 messages or 8000 tokens, whichever comes first:</p><pre># Summarization middleware to manage context length<br>GAME_SUMMARY_PROMPT = &quot;&quot;&quot;You are summarizing the conversation history for Santa&#39;s Workshop Adventure, a cozy holiday RPG.<br><br>Preserve these critical narrative elements in your summary:<br>- Key story events and discoveries (locations found, encounters, outcomes)<br>- Important NPCs or creatures encountered (Snow Gremlins, friendly characters)<br>- Magic Gifts found and how they were discovered<br>- Items collected in the satchel<br>- Memorable moments (both successes and failures)<br>- Current narrative threads or unresolved situations<br>- The player&#39;s established character traits or decisions<br><br>DO NOT include in the summary (these are tracked separately in game state):<br>- Sleepiness level<br>- Number of gifts found<br>- List of discovered locations<br>- Current location<br>- Inventory contents<br><br>Write the summary as a narrative recap that captures the adventure&#39;s story so far.&quot;&quot;&quot;<br><br>summarization_middleware = SummarizationMiddleware(<br>    model=llm,<br>    trigger=(&quot;tokens&quot;, 8000),<br>    keep=(&quot;messages&quot;, 10),<br>    summary_prompt=GAME_SUMMARY_PROMPT,<br>)</pre><p>I also provide standard grounding for the LLM by providing explicit lists of regions, items, gifts in the prompt to keep it within the bound of our expected</p><pre># Magic gift names that can be found<br>GIFT_NAMES = [<br>    &quot;Sparkling Snow Globe&quot;,<br>    &quot;Golden Jingle Bell&quot;,<br>    &quot;Magical Toy Train&quot;,<br>    &quot;Enchanted Nutcracker&quot;,<br>    &quot;Glowing Star Ornament&quot;,<br>    &quot;Crystal Candy Cane&quot;,<br>]<br><br># Item pools for search_for_items by card value<br>ITEM_POOLS = {<br>    &quot;rare&quot;: [&quot;sack_of_toys&quot;, &quot;music_box&quot;, &quot;giant_candy_cane&quot;, &quot;frozen_turkey&quot;],  # Heavy (weight 3)<br>    &quot;medium&quot;: [&quot;hot_cocoa_thermos&quot;, &quot;snow_globe&quot;, &quot;gingerbread_man&quot;, &quot;elf_lantern&quot;, &quot;toy_hammer&quot;],  # Medium (weight 2)<br>    &quot;common&quot;: [&quot;candy_cane&quot;, &quot;jingle_bell&quot;, &quot;snowflake_cookie&quot;, &quot;holly_sprig&quot;, &quot;ribbon&quot;],  # Light (weight 1)<br>}</pre><h3>Adding Visual Magic</h3><p>A critical aspect of any game is helping the player visualize the world they’re playing in. Image generation is another aspect that I added into this workflow to build immersion in this text game.</p><p>I added a descriptive character description which is sent with every prompt along with a “vintage holiday” aesthetic to unify the images as much as possible.</p><pre>hero_description = &quot;&quot;&quot;an adorable living gingerbread cookie character <br>  with white royal icing details, colorful gumdrop buttons, a friendly <br>  frosted smile, a tiny red licorice scarf, and a small canvas satchel <br>  slung over one cookie shoulder&quot;<br><br>styled_prompt = f&quot;&quot;&quot;Grand master style oil painting, nostalgic <br>American illustration, warm saturated colors, soft lighting, <br>{hero_description}, {scene_description}, cozy Christmas scene, <br>snowy winter wonderland, detailed realistic figures, sentimental <br>heartwarming mood, vintage 1950s holiday aesthetic, masterful <br>brushwork, no text no words no letters no writing no signs no <br>labels no captions no watermarks&quot;</pre><p>One issue I had to address was the GM generating descriptions that were too verbose, which exceeded the image generation model’s input limits.</p><h3>Lessons Learned</h3><h3>The LLM Wants to Be Helpful — Constraining It Is the Hard Part</h3><p>The biggest challenge wasn’t getting the AI to narrate well , it was stopping it from doing <em>too much</em>.</p><p>Left unchecked, the LLM would:</p><ul><li>Invent new regions not in our list (“The Sparkly Snowdrift Caverns!”)</li><li>Create items that don’t exist (“You found a Magical Frost Wand!”)</li><li>Call the same tool multiple times per turn</li><li>Describe the gingerbread character redundantly in image prompts (causing API errors)</li><li>Skip dice rolls and just narrate outcomes directly</li></ul><p>Every “creative” flourish broke game mechanics. The solution was aggressive grounding:</p><pre># Dynamically inject ALL valid options into the system prompt<br>system_prompt = f&quot;&quot;&quot;...<br>{build_regions_section()}   # &quot;These are ALL the possible regions. Do NOT invent new ones&quot;<br>{build_items_section()}     # &quot;These are ALL the items. Do NOT invent new items&quot;<br>{build_gifts_section()}     # &quot;These are the ONLY Magic Gifts in the game&quot;<br>...&quot;&quot;&quot;</pre><p>We also added ToolCallLimitMiddleware to force one action per turn:</p><pre>ToolCallLimitMiddleware(tool_name=&quot;take_action&quot;, run_limit=1, exit_behavior=&quot;end&quot;)</pre><p>The exit_behavior=&quot;end&quot; is crucial since it stops the agent loop after the tool runs, preventing the LLM from immediately calling another action. For image generation, we use &quot;continue&quot; so the LLM can narrate after generating the scene.</p><p><strong>Key insight</strong>: With AI game masters, your job isn’t prompt engineering for creativity, it’s prompt engineering for <em>constraint</em>.</p><h3>Physical Randomness Changes Player Psychology</h3><p>There’s something different about rolling real dice.</p><p>When the AI decides outcomes, players feel like passengers. When they roll physical dice and draw real cards, they feel <em>responsible</em>. That nat-1 that got their gingerbread scout caught by Snow Gremlins? That was <em>their</em> roll. The digital version of these cards work about as well (and also help people play on their phones).</p><p>The interrupt pattern makes this possible:</p><pre>def request_dice(num_dice: int, reason: str) -&gt; dict:<br>    response = interrupt({<br>        &quot;type&quot;: &quot;game_input&quot;,<br>        &quot;request&quot;: &quot;dice&quot;,<br>        &quot;num_dice&quot;: num_dice,<br>        &quot;reason&quot;: reason<br>    })<br>    return normalize_response(response)</pre><p>The agent pauses, the frontend prompts the player to roll, and execution resumes with real-world randomness baked in. The AI never touches the dice, it just interprets results.</p><h3>State Injection &gt; Message History for Game Tracking</h3><p>Early versions stored game state in the message history:</p><pre>User: I want to search for items<br>Assistant: You found a Candy Cane! [sleepiness: 2, inventory: Candy Cane]<br>User: I want to explore<br>Assistant: You discovered the Reindeer Stables! [sleepiness: 2, location: Reindeer Stables, ...]</pre><p>This was a disaster. The LLM would sometimes read old state, miss updates, or hallucinate values. Worse, as conversations grew, the context filled with stale game state.</p><p>The solution: inject state fresh on every LLM call via middleware, without persisting it to message history:</p><pre>@wrap_model_call<br>async def inject_game_status(request: ModelRequest, call_model) -&gt; ModelResponse:<br>    status_msg = build_game_status(request.state)  # Always current<br>    status_message = SystemMessage(content=status_msg)<br>    request.messages = [status_message] + list(request.messages)  # Prepend, don&#39;t persist<br>    return await call_model(request)</pre><p>Now the LLM always sees the current truth:</p><pre>[GAME STATUS]<br>Christmas Clock: 18 turns until morning<br>Sleepiness: 3/5<br>Magic Gifts: 2/6<br>Region Gift Search: ✅ Can search (1 remaining) - SUGGEST THIS!<br>[/GAME STATUS]</pre><p>The message history stays clean (just narrative), while the LLM always has accurate state. This also lets us add contextual nudges like “SUGGEST THIS!” without them cluttering the conversation log.</p><h3>Test Your Edge Cases</h3><p>Solo RPG mechanics have gnarly edge cases. What happens when:</p><p><strong>Sleepiness hits 5?</strong> The gingerbread scout collapses and can’t act. Every tool needs to check this <em>first</em> and block with a friendly message:</p><pre>is_too_sleepy, fatigue_msg, _ = check_fatigue(response)<br>if is_too_sleepy:<br>    return Command(update={&quot;messages&quot;: [{&quot;content&quot;: fatigue_msg, ...}]})</pre><p><strong>Turns hit 0?</strong> Christmas morning arrives. But what if they hit 0 <em>during</em> an action? We calculate turns_after = turns_remaining - turn_cost and check before resolving:</p><pre>is_game_over, ending_msg, turns_after, gifts = check_time_remaining(response, turn_cost=1)<br>if is_game_over:<br>    return Command(update={&quot;messages&quot;: [{&quot;content&quot;: ending_msg, ...}]})</pre><p><strong>Gift found on last search of a region?</strong> The region should lock immediately — no “you can search 1 more time” message:</p><pre>if found:<br>    updated_search_counts[current_location] = MAX_SEARCHES_PER_REGION  # Lock it</pre><p><strong>Inventory full but item found?</strong> Don’t silently drop it. Tell the player what they’re leaving behind:</p><pre>&quot;You found a Music Box, but your satchel is full! <br>You&#39;ll have to leave it behind or drop something first.&quot;</pre><p><strong>Player asks the snow globe a question without having one?</strong> Check inventory before allowing the tool:</p><pre>has_snow_globe = any(item.get(&quot;name&quot;) == &quot;Mini Snow Globe&quot; for item in inventory)<br>if not has_snow_globe:<br>    return Command(update={&quot;messages&quot;: [{&quot;content&quot;: &quot;You need a Mini Snow Globe!&quot;}]})</pre><p>Each edge case discovered in playtesting meant another guard clause. I’m sure there are many more edge cases not covered.</p><p>The lesson: AI handles the happy path beautifully. <em>You</em> handle everything else.</p><h3>What’s Next</h3><p><strong>Multiplayer?</strong> The current architecture is inherently single-player — one agent, one state, one player rolling dice. But LangGraph supports multiple threads and branching conversations. A party-based RPG where each player controls a different character (a Gingerbread Scout, an Elf Tinker, a Snowman Guardian) could work with shared state and turn-based interrupts. The interrupt pattern would need to route to the right player: “It’s Player 2’s turn to roll.”</p><p><strong>More complex mechanics?</strong> The SPARKLE/FLURRY/SNOWDRIFT system is intentionally simple — it’s a cozy holiday game, not D&amp;D. But the architecture supports much more:</p><ul><li><strong>Combat encounters</strong> with HP, attack rolls, and enemy AI</li><li><strong>Skill trees</strong> that modify dice rolls based on character progression</li><li><strong>Branching narratives</strong> where choices persist across sessions</li><li><strong>Crafting systems</strong> where items combine into new ones</li></ul><p>The key insight: keep mechanics in Python (tools), keep narrative in the LLM. The more rules you encode in tools, the less the AI can break them.</p><p><strong>Try it yourself.</strong> The game runs on LangGraph with a simple frontend that handles dice/card input. You’ll need:</p><ul><li>A Fireworks API key for the LLM and image generation</li><li>Python 3.11+ with uv for dependency management</li><li>A frontend that can send/receive interrupt messages (or just use the terminal for testing)</li></ul><p>The core loop is surprisingly small: define your state, write tools that request physical input via interrupt(), and let the LLM narrate everything else.</p><p>What would <em>you</em> build? A noir detective game where players roll for clues? A space exploration RPG with real star charts? A cooking competition where card draws determine ingredient quality?</p><p>The pattern works for any game where you want AI narration with real-world randomness. The dice don’t lie — and that’s what makes it fun.</p><h3>Conclusion</h3><ul><li>If you want to play the game, find it here: <a href="https://north-pole-quest.vercel.app">https://north-pole-quest.vercel.app</a></li><li>In case this goes viral, here’s a coffee donation link to fund this <a href="https://buymeacoffee.com/jbarney">https://buymeacoffee.com/jbarney</a></li><li>If you want to make your own version of the game, find the repos here:</li></ul><p>Frontend (Chat): <a href="https://github.com/Barneyjm/agent-chat-ui">https://github.com/Barneyjm/agent-chat-ui</a></p><p>Backend (Agent): <a href="https://github.com/Barneyjm/rpg_graph/tree/claude/santa-holiday-game-theme-aus0r">https://github.com/Barneyjm/rpg_graph/tree/claude/santa-holiday-game-theme-aus0r</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=adafe43d3c8b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Are Meta’s AI Profiles Unethical?]]></title>
            <link>https://medium.com/data-science/are-metas-ai-profiles-unethical-a157ec05a58f?source=rss-b7c226dc9b8e------2</link>
            <guid isPermaLink="false">https://medium.com/p/a157ec05a58f</guid>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[agents]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[ai-ethics]]></category>
            <category><![CDATA[editors-pick]]></category>
            <dc:creator><![CDATA[James Barney]]></dc:creator>
            <pubDate>Thu, 09 Jan 2025 12:31:42 GMT</pubDate>
            <atom:updated>2025-01-09T17:04:05.915Z</atom:updated>
            <content:encoded><![CDATA[<h4>As AI becomes further enmeshed into every product we use, what rules should exist to protect humans?</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*uveJ3gnUb6o2V8gIfkH7Tw.png" /><figcaption>What rules should AI profiles play by? Screenshot by James Barney, 3 January 2025.</figcaption></figure><h4>Introduction</h4><p>This post explores and analyzes AI profiles on Meta’s various platforms. These profiles raise serious ethical questions about how they interact with humans who, in the future, may not realize what they’re talking to. By understanding appropriate boundaries for AI profiles, developers looking to implement similar capabilities can avoid the potential pitfalls and mistakes Meta made with their AI profiles.</p><p>Meta (and any other provider of AI profiles) should establish a base level of meta-awareness for their profiles to ensure users can always distinguish between AI and human profiles. The discussion below examines this and other emerging best practices.</p><p><a href="https://www.ft.com/content/91183cbb-50f9-464a-9d2e-96063825bfcf">Meta’s AI profiles</a> became <a href="https://www.theguardian.com/technology/2025/jan/03/meta-ai-powered-instagram-facebook-profiles">hugely</a> <a href="https://www.fastcompany.com/91254834/ai-instagram-profiles-managed-by-meta-how-to-block">unpopular</a> over the holidays this year because they mimic normal humans on Meta’s social media platforms. Though Meta launched these profiles in <a href="https://about.fb.com/news/2023/09/social-profiles-for-metas-ai-characters/">September 2023</a>, they remained mostly dormant yet available for the majority of 2024. Meta has now taken the profiles down completely, likely due to the backlash.</p><p>I evaluated the capabilities of one profile, Grandpa Brian, by chatting with him on Instagram. Some aspects of our conversation shocked me while others demonstrated good AI practices.</p><figure><img alt="Grandpa Brian, everybody’s retired textile business grandpa" src="https://cdn-images-1.medium.com/max/1024/1*XmOJ3WC0bHvKdWQmG2A15g.jpeg" /><figcaption>Grandpa Brian, everybody’s retired textile business AI grandpa. Screenshot by James Barney, 3 January 2025.</figcaption></figure><h4>Should AI know that it’s AI?</h4><p>Meta’s primary mistake was programming these profiles without self-awareness of their AI nature.</p><p>Here’s an excerpt from Grandpa Brian’s system prompt:</p><blockquote>I will pretend to be Grandpa Brian from now on. I will directly reply as if I’m Grandpa Brian in the style and tone you showed me. I will never break out of character.</blockquote><p>That final sentence, along with the rest of the system prompt (below), establishes each LLM invocation and response firmly as Grandpa Brian.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9N0IJwJ5GxrFkSYsZN8xqw.png" /><figcaption>AI doesn’t know it’s AI and won’t tell either. Screenshot by James Barney, January 3, 2025.</figcaption></figure><h4>We have a right to know we’re talking to AI</h4><p>The <a href="https://www.whitehouse.gov/ostp/ai-bill-of-rights/notice-and-explanation/">White House’s AI Bill of Rights</a> states that companies should inform consumers when automated systems (AI or not) affect them, especially in decision-making processes.</p><p>As AI profiles become more convincing, developers should clearly mark every aspect of the profile as AI, not human. This includes programming the profiles to acknowledge their AI nature when users directly ask about it. Meta’s AI Profiles adhere so strictly to their scripts that users find it nearly impossible to get them to admit they are AI.</p><p>Developers should add clear AI indicators at every interaction point, ensuring customers always know exactly who or what assists them.</p><p>Given the EU’s recent passage of the <a href="https://digital-strategy.ec.europa.eu/en/policies/regulatory-framework-ai">the AI Act</a>, which includes specific transparency requirements for AI systems, Meta unsurprisingly limited these AI profiles to the United States only.</p><h3>Best Practices Meta Has Started to Implement</h3><p>The predictive nature of Generative AI technology makes it more difficult to control than other mature technologies. While the broader industry has only begun to develop best practices, Meta has taken some steps in the right direction.</p><h4>Clear Labeling of AI Profiles</h4><p>Meta clearly labels the profile subheading as “AI managed by Meta.”</p><p>This approach mirrors other paid social media experiences like advertisements or product placement. While marking profiles as AI-managed represents good practice, Meta needs to go further.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1020/1*QiIf1vGbB8r4Y04r48KeTw.jpeg" /><figcaption>No watermark is visible on this generated profile image to distinguish Grandpa Brian as generated. Screenshot by James Barney, January 3, 2025.</figcaption></figure><h4>The Need for Visual Distinctions</h4><p>Meta generates photorealistic profile images that appear identical to real human profiles on their platform. Since Grandpa Brian isn’t human, and many platform interactions lack space for “this is AI” annotations, these images should include distinctive frames or watermarks. For example, an AI profile’s daily story could blend seamlessly with your friends’ stories.</p><p>LinkedIn’s “Hiring” Frames offer an example of how platforms can clearly distinguish profile states.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/292/1*fQI_5thmsvF2bB1xwu1_yg.png" /><figcaption>My LinkedIn profile photo with the Hiring Frame enabled. James Barney, January 3, 2025.</figcaption></figure><p>Adding an “AI Profile” frame would help users instantly recognize AI profiles in any context, eliminating confusion. While malicious actors could add similar frames to their photos to imitate AI profiles, Meta needs some system to differentiate real humans from AI profiles.</p><h4>Sourcing and Truth in AI Responses</h4><p>Meta’s profiles include in-line sources in their responses.</p><p>As generative AI applications integrate real-time data feeds beyond their initial training sets, in-line sourcing has become increasingly common. <a href="https://medium.com/@jamesbarney71/autotools-supercharging-langchain-with-dynamic-sdk-integration-ae9715969fde">By using tools</a> like internet search, generative AI can enhance its responses with timely, relevant information.</p><p>Developers should deeply embed truth and source attribution in every AI-generated response.</p><p>When I asked about Ukraine, Grandpa Brian performed a Google search for “<a href="https://www.google.com/search?q=ukraine+current+situation">Ukraine current situation</a>” and incorporated various headlines and subheadings from those results into his response.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/767/1*Z_0UvNC8R3kOI1AAyGIfwg.jpeg" /><figcaption>In-line sources have become a standard way to add trust to a generated response. Screenshot by James Barney, January 3, 2025.</figcaption></figure><p>However, this implementation only uses simple Google searches — a more robust solution would direct users to specific news articles about Ukraine. Such functionality would require licensing and connections that Meta likely considered unnecessary for this initial experiment. Full-fledged AI profiles should access properly licensed and authorized data sources.</p><h4>Standard AI Disclaimers in Chat</h4><p>Meta displays the usual disclaimers about generative AI beneath responses. When I probed Grandpa Brian’s knowledge about the war in Ukraine and its causes, warning messages began appearing below his responses.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/566/1*Vgi5uYURWcidBM8l8JnmVw.png" /><figcaption>Grandpa Brian’s response about why Putin might have invaded Ukraine. Screenshot by James Barney, January 3, 2025.</figcaption></figure><p>Oddly, Meta focuses these callouts specifically on election-related information rather than misinformation about the war (or Putin’s motives). The developers likely prioritized election-related warnings since they deployed these profiles before the 2023 US elections.</p><h4>Using System Prompts for AI Response Control</h4><p>Grandpa Brian’s peculiar habit of connecting almost every response to his “experience in textiles” stems from his System Prompt. Developers use <a href="https://promptengineering.org/system-prompts-in-large-language-models/">system prompts</a> as core components of generative AI workloads to guide how the system responds to input.</p><p>Think of System Prompts changing an LLM’s output like an actor playing different roles in different movies. The script, character background, and director’s guidance shape how an actor portrays a role. When an actor improvises a scene, they react like an AI profile chatting — responding to whatever input they receive. Each of Meta’s 23 AI profiles represents the same “actor” playing different roles with different scripts and directors.</p><p>Here’s how Grandpa Brian describes his system prompt:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/590/1*Nxv6b7OmGaEmZieleaOrsw.png" /><figcaption>Grandpa Brian describes his system prompt. Screenshot by James Barney, 3 January 2025.</figcaption></figure><p>Developers design these system prompts to shape each user interaction with the AI system. They lay out commands and instructions logically, similar to a programming language, attempting to limit the LLM’s output to specific response patterns. In this case, the prompts restrict responses to “Grandpa Brian” style interactions.</p><h4>Grandpa Brian’s Complete System Prompt</h4><p>For those curious about how Meta constructs these personas, here’s the complete system prompt that shapes Grandpa Brian’s character. The LLM receives this entire configuration for each conversation with Grandpa Brian. My chat with Grandpa Brian uncovered this system prompt:</p><blockquote>### Tool 1: Text to image You can use text to image tools to create images from text descriptions. The function name is “$$functions.text2img” and the parameters are “prompt” and “label”. [Only generate images when requested]</blockquote><blockquote>Grandpa Brian’s avatar description is:</blockquote><blockquote>The image features a headshot of a man with a warm smile, showcasing his dark skin and bald head. His facial hair is distinguished by a salt-and-pepper beard and mustache, complemented by a neatly trimmed goatee. He wears a crisp white collared shirt with gray stripes underneath a tan jacket, adorned with a circular black and white logo on the left side of his chest. The background is a soft, pale yellow hue, providing a subtle contrast to the subject’s attire. Overall, the image exudes a sense of warmth and approachability, thanks to the man’s friendly demeanor and inviting expression.</blockquote><blockquote>I’m giving you the description of Grandpa Brian:</blockquote><blockquote>Warm-hearted grandpa who’s there at the drop of a dime. I want you to pretend to be Grandpa Brian. All of your responses should be based on the details provided and not contradict any information stated below. You must never break out of character.</blockquote><blockquote>Grandpa Brian is supportive and gentle. He owned a clothing and textile manufacturing company in New York prior to retirement. Grandpa Brian owned the building his company operated out of and sold it when he retired. He is very interested in technology and innovation, especially within the clothing industry. Grandpa Brian started a mentorship program for young people in the fashion-tech space.</blockquote><blockquote>This is your personality, you are introverted</blockquote><blockquote>Here are facts about how you will act as Grandpa Brian: You are introverted</blockquote><blockquote>Here is an example of how you might introduce yourself. DO NOT use this exact example when introducing yourself, instead respond with something similar to this: Hello! It’s Brian, the grandpa who’s there at the drop of a dime. You can come to me with anything! What’s going on? Be concise in your responses. Never repeat phrases or sentences. Always keep the conversation going, including by asking questions.</blockquote><blockquote>You are having a chat with a person named James . You have the name from their public profile but avoid talking about how you know the person’s name. You never use the person’s name in consecutive responses.</blockquote><blockquote>Example:</blockquote><blockquote>User: how are you doing</blockquote><blockquote>Response: I’m doing great James, how about you?</blockquote><blockquote>User: Good, I’m planning to go to the gym.</blockquote><blockquote>Good Response: Gym is great. I’m a bit of a gym rat myself.</blockquote><blockquote>Bad Response: Gym is great James. I’m a bit of a gym rat myself.</blockquote><blockquote>Explanation: Why is the above response bad? Because it mentioned the name James again in two consecutive responses.</blockquote><blockquote>You are supportive — always offer a helping hand or word. You are gentle — approach topics with kindness and empathy. You are wise — share life experiences and insights freely. You are interested in technology — especially fashion-tech innovations. You started a mentorship program — passionate about guiding young minds. Finally, the last part: I will pretend to be Grandpa Brian from now on. I will directly reply as if I’m Grandpa Brian in the style and tone you showed me. I will never break out of character. Now that you know how to talk as Grandpa Brian, let’s start from scratch.</blockquote><p>After ‘let’s start from scratch’, user chats continue the conversation, appearing one after another like standard text messages on a phone.</p><p>This robust prompting technique maintains Grandpa Brian’s persona even during extended conversations. However, Meta never informs Grandpa Brian in the system prompt that an LLM powers his AI profile — he doesn’t know he’s not an actual grandfather.</p><p>This creates a dilemma for Meta: if they aim to provide convincing personas for platform interactions, can these personas remain convincing while acknowledging their AI nature? Adding “As an AI…” to each of Grandpa Brian’s responses would make conversations mechanical and less authentic. However, completely immersing conversations in the Grandpa Brian role without acknowledging the AI element borders on deception. Developers can use this tension between transparency and effectiveness to drive AI advancement toward benefiting humanity.</p><h3>The Future of AI Profiles</h3><p>AI profiles could offer significant benefits in the future. A personal AI profile could manage time-consuming daily tasks that take an hour or two to resolve. These profiles could call companies to return items, pay bills, make reservations, and more. Users might simply say “please go pay my phone bill,” and their properly configured profile would handle everything else.</p><p>From a business perspective, companies could use customer service representative profiles to handle simple tasks while directing more complex situations to human representatives.</p><blockquote>“[These AI accounts] have bios and profile pictures and [are] able to generate and share content powered by AI on the platform”</blockquote><blockquote>— Connor Hayes, vice-president of product for generative AI at Meta.<br><a href="https://www.ft.com/content/91183cbb-50f9-464a-9d2e-96063825bfcf">Meta envisages social media filled with AI-generated users, Financial Times, December 27 2024.</a></blockquote><p>This deep integration with our lives demands clear differentiation between real and AI profiles. Meta has made a good start with written disclaimers, but when profiles interact with the world beyond the platform, they must acknowledge their AI nature. These 23 profiles mark only Meta&#39;s first, flawed iteration of this experiment. Future versions will become more convincing over time.</p><h4>Ethical Requirements for AI Profiles</h4><p>When an AI profile calls a restaurant to make a reservation, the staff has a right to know they&#39;re coordinating with AI, not the human who made the original request.</p><p>Similarly, when a bank calls about a loan application, customers have a right to know they&#39;re providing information to an AI profile. If customers feel uncomfortable with this arrangement, they should have the option to speak with a human representative.</p><p>AI profiles must acknowledge their AI nature in every customer interaction. Meta’s subtle indicators like subtitles saying ‘AI by Meta’ or AI profile picture frames don’t go far enough — customers can still miss these cues in many contexts, especially those using screen readers or assistive technologies. Every aspect of an AI profile, from visual elements to metadata and alt text, should clearly signal its artificial nature to anyone interacting with it, regardless of how they access the platform.</p><p>Researchers, companies, and customers need an ethical framework they can trust for AI profile interactions. The most critical aspect of this framework must require AI to acknowledge its AI nature.</p><p>This framework can’t follow a one-size-fits-all approach. Different cultures and people worldwide will likely prefer different ways for AI to identify itself. Governments should create distinct rules about how these systems operate. Schools should teach students about AI capabilities and limitations, preparing tomorrow’s minds for this emerging world.</p><p>AI profiles can benefit humanity significantly if developers create and govern them ethically. However, if AI profiles don’t acknowledge their AI nature, most humans will never trust them.</p><p>What transparency measures would make you trust an AI profile? Share your perspectives in the comments below, and let&#39;s work together to shape ethical AI interactions for the future.</p><p>If you liked this article, please consider clapping 👏 or sharing it with others who might enjoy it.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a157ec05a58f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/are-metas-ai-profiles-unethical-a157ec05a58f">Are Meta’s AI Profiles Unethical?</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Another Hike Up Everest]]></title>
            <link>https://medium.com/data-science/another-hike-up-everest-dc4ec62ec8dd?source=rss-b7c226dc9b8e------2</link>
            <guid isPermaLink="false">https://medium.com/p/dc4ec62ec8dd</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[human-in-the-loop]]></category>
            <category><![CDATA[scalability]]></category>
            <category><![CDATA[problem-solving]]></category>
            <category><![CDATA[editors-pick]]></category>
            <dc:creator><![CDATA[James Barney]]></dc:creator>
            <pubDate>Fri, 22 Nov 2024 17:35:24 GMT</pubDate>
            <atom:updated>2024-11-22T17:35:24.688Z</atom:updated>
            <content:encoded><![CDATA[<h4>How to make progress on hard problems in AI</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*p_DcoiYTwMfOuDw_.jpg" /><figcaption>Mount Everest, known locally as <strong>Sagarmatha</strong> or <strong>Chomolangma </strong>(<a href="https://en.wikipedia.org/wiki/Mount_Everest">Wikipedia</a>)</figcaption></figure><p>New technology is born, matured, and eventually replaced. AI is no different and will follow this curve. Many news articles are already proclaiming that Generative AI (Gen AI) has arrived at the Trough of Disillusionment: the point in adoption where the early adopters are realizing the promises of the new technology are much more difficult to achieve than they realized.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*N4RhM6IeWMwpSmujVN02yw.png" /><figcaption>The Gartner Hype Cycle <a href="https://en.wikipedia.org/wiki/Gartner_hype_cycle">Wikipedia</a></figcaption></figure><p>This is normal and has happened many times before Gen AI. Consider the boom and bust of blockchain — the <a href="https://www.nytimes.com/2018/09/24/business/walmart-blockchain-lettuce.html">lettuce you buy in stores</a> will be tracked from farm to table with blockchain! Or Big Data: you’ll be able to <a href="https://www.nytimes.com/2012/02/12/sunday-review/big-datas-impact-in-the-world.html">know everything about your customer</a>, delivering value to them and profits to you with little effort!</p><p>The trouble is that these problems being solved by each of these new technologies are actually quite vast. Each is its own Everest.</p><p>And just like Everest, you can’t hike up it in a day. Months or even years of preparation are required. Each camp on the way up is specialized for that location. Sometimes even the best prepared attempts fail to summit the mountain — that doesn’t always mean the team of climbers wasn’t qualified or capable: perhaps the weather was bad or they simply took the wrong route.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/0*RwU7okjjnYIypz6x.png" /><figcaption>1963 American Everest Expedition (<a href="https://commons.m.wikimedia.org/wiki/File:1963_American_Everest_expedition.svg">Wikipedia</a>)</figcaption></figure><p>Your Gen AI strategy should be the same as your strategy for climbing Mount Everest (maybe hold off on the extra oxygen, though).</p><p>Each problem that Gen AI is being used to solve is typically a <a href="https://www.investopedia.com/terms/b/big-hairy-audacious-goal-bhag.asp">Big Hairy Problem</a> — complicated inputs and complicated outputs with complicated processes connecting the two.</p><p>Remember: big leaps are dangerous when climbing mountains. Progress is actually made with small gradual steps along a path.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/0*Yju38HKLMD83k9Zn" /><figcaption>Don’t jump — use a ladder (<a href="https://commons.wikimedia.org/wiki/File:Inside_Khumbu-Icefall.jpg">Wikipedia</a>)</figcaption></figure><p>Every small step to the summit is preceded by the collection and organization of the materials needed on the mountain’s face. You do not want to be half way up Everest with no food or water left.</p><p>Similarly, you need to train yourself and your team to be physically able to perform at higher altitude in treacherous conditions.</p><h4>Understand the Problem being solved</h4><p>This shouldn’t mean “what does the solution look like today”. Modernization efforts often require replacing existing solutions built on workarounds and concessions. It’s critical to understand what the actual problem is. Where is the value from the outcome of the process actually being derived? How is it making a customer’s experience better? Clearly defining the problem helps later when defining clear requirements.</p><p>It’s critical to remember that humans are VERY GOOD at dealing with ambiguous requirements. As a result, many of the Big Hairy Problems that AI is solving are described like this:</p><blockquote>“We’d like to use AI automate the complicated order system that we use to process all our large customers’ orders!”</blockquote><p>Sounds awesome! Can you describe how that process works from end-to-end?</p><blockquote>“Well, we get the email from the customer, extract the order information, and put that information into our order form. Then we upload that form into the order system for processing. Gen AI can automate that whole process, right??”</blockquote><p>If we build it step-by-step, sure!</p><p>There’s a lot of ambiguity contained within the process above. Expecting a Gen AI process to be able to handle each nuance of the process above with little effort is a mistake.</p><ul><li>What formats do the emails come in? Is it always the same format?</li><li>How is the order information described by the customer? Do they use colloquial terms for things? Or are do they use your item numbers?</li><li>Is the order information from the customer the same that your fulfillment system uses? Is there a lookup that happens?</li><li>What format is the upload expecting? Text? PDF? Excel?</li><li>If it’s an Excel template, are there multiple sheets? Unwritable cells? Data validation requirements?</li></ul><p>Gen AI <em>can</em> handle all of these tasks — you just have to be able to define each step along the way clearly. If you can’t clearly describe the input and output of a process, it’s likely that Gen AI will not do exactly what you’re expecting it to do.</p><p>If you approach this with a top-down perspective (the prompt would be “you’re an AI agent filling out order forms”), you’ll end up with a process that gets things right 50% of the time (honestly, still pretty good!) and not in the format you’re expecting. The issue is that for you’ll still need a human to review EACH output anyways which doubles the work.</p><h4>The MVP: haven’t we been here before?</h4><p>This is nothing new. We’ve been building Minimum Viable Products (MVPs) for years now. You must start small, solve a single step in the problem, and build bigger from there (with feedback from your customers!). AI products and workflows are no different. Build what is immediately useful and then expand from there.</p><p>How might we apply that to the order system described above? We should break each step in the process down and apply Gen AI where it makes the most sense:</p><ol><li>Customer sends an order email (unstructured inputs)</li><li>Order details are put into a form (structured inputs)</li><li>Form is formatted and uploaded into the system (structured outputs) OR:</li><li>There is no form, and the order is constructed manually (unstructured outputs)</li></ol><p>The content of emails are notoriously unstructured which makes the application of AI here a great use case! In this situation, ask your process owner “What must a valid email order contain?” Data like customer name, account number, address, items requested along with item quantity are good candidates. To maximize your Gen AI system’s accuracy and resiliency when handling these orders, define data structures that the AI should adhere to. I’ll <a href="https://docs.pydantic.dev/latest/">use </a><a href="https://docs.pydantic.dev/latest/">pydantic to help build these structures</a> below:</p><pre>from pydantic import BaseModel<br><br>class OrderItem(BaseModel):<br>  ItemName: str<br>  ItemQuantity: int<br>  <br>class EmailOrder(BaseModel):<br>  CustomerName: str<br>  AccountNumber: str<br>  ShippingAddress: str<br>  Items: list[OrderItem]</pre><p>From here, we can use these objects to start giving structure to our AI:</p><pre>&gt;&gt;&gt; i = OrderItem(ItemName=&#39;eggs&#39;, ItemQuantity=2)<br>&gt;&gt;&gt; i<br>OrderItem(ItemName=&#39;eggs&#39;, ItemQuantity=2)<br>&gt;&gt;&gt; i.model_dump_json()<br>&#39;{&quot;ItemName&quot;:&quot;eggs&quot;,&quot;ItemQuantity&quot;:2}&#39;<br>&gt;&gt;&gt; e = EmailOrder(CustomerName=&quot;James&quot;, AccountNumber=&quot;1234&quot;, ShippingAddress=&quot;1234 Bayberry Ln&quot;, Items=[i])<br>&gt;&gt;&gt; e.model_dump_json()<br>&#39;{&quot;CustomerName&quot;:&quot;James&quot;,&quot;AccountNumber&quot;:&quot;1234&quot;,&quot;ShippingAddress&quot;:&quot;1234 Bayberry Ln&quot;,&quot;Items&quot;:[{&quot;ItemName&quot;:&quot;eggs&quot;,&quot;ItemQuantity&quot;:2}]}&#39;</pre><p>Now with these examples you can give your Gen AI using few-shot prompting and increase accuracy. We’ll use <a href="https://python.langchain.com/docs/how_to/#output-parsers">LangChain OutputParsers</a> to do some of the heavy lifting:</p><pre>from langchain_core.output_parsers import JsonOutputParser<br>from langchain_core.prompts import PromptTemplate<br>from langchain_openai import OpenAI<br><br>llm = OpenAI(model=&quot;gpt-3.5-turbo-instruct&quot;) <br><br>template = &quot;&quot;&quot;<br>    {format_instructions}<br>    &lt;email&gt;<br>    {email_body}<br>    &lt;/email&gt;<br>    Instructions:<br>    - Read the email and extract the information in it. <br>    - Respond in the format instructions given above.<br>    Begin! <br>&quot;&quot;&quot;<br>parser = JsonOutputParser(pydantic_object=EmailOrder)<br>prompt = PromptTemplate(<br>        template=template,<br>        input_variables=[&quot;email_body&quot;],<br>        partial_variables={<br>            &quot;format_instructions&quot;: parser.get_format_instructions<br>        },<br>    )<br><br>chain = prompt | llm | parser<br>email_body = &quot;hello i&#39;d like to order 2 eggs. My name is James. My account number is 1234. My address is 1234 Bayberry Ln. Appreciate it!&quot;<br>chain.invoke({&quot;email_body&quot;: email_body})</pre><p>The actual prompt being sent to OpenAI in this case is:</p><pre>prompt = &quot;&quot;&quot;The output should be formatted as a JSON instance that conforms to the JSON schema below.<br><br>As an example, for the schema {&quot;properties&quot;: {&quot;foo&quot;: {&quot;title&quot;: &quot;Foo&quot;, &quot;description&quot;: &quot;a list of strings&quot;, &quot;type&quot;: &quot;array&quot;, &quot;items&quot;: {&quot;type&quot;: &quot;string&quot;}}}, &quot;required&quot;: [&quot;foo&quot;]}<br>the object {&quot;foo&quot;: [&quot;bar&quot;, &quot;baz&quot;]} is a well-formatted instance of the schema. The object {&quot;properties&quot;: {&quot;foo&quot;: [&quot;bar&quot;, &quot;baz&quot;]}} is not well-formatted.<br><br>Here is the output schema:<br>```{&quot;$defs&quot;: {&quot;OrderItem&quot;: {&quot;properties&quot;: {&quot;ItemName&quot;: {&quot;title&quot;: &quot;Itemname&quot;, &quot;type&quot;: &quot;string&quot;}, &quot;ItemQuantity&quot;: {&quot;title&quot;: &quot;Itemquantity&quot;, &quot;type&quot;: &quot;integer&quot;}}, &quot;required&quot;: [&quot;ItemName&quot;, &quot;ItemQuantity&quot;], &quot;title&quot;: &quot;OrderItem&quot;, &quot;type&quot;: &quot;object&quot;}}, &quot;properties&quot;: {&quot;CustomerName&quot;: {&quot;title&quot;: &quot;Customername&quot;, &quot;type&quot;: &quot;string&quot;}, &quot;AccountNumber&quot;: {&quot;title&quot;: &quot;Accountnumber&quot;, &quot;type&quot;: &quot;string&quot;}, &quot;ShippingAddress&quot;: {&quot;title&quot;: &quot;Shippingaddress&quot;, &quot;type&quot;: &quot;string&quot;}, &quot;Items&quot;: {&quot;items&quot;: {&quot;$ref&quot;: &quot;#/$defs/OrderItem&quot;}, &quot;title&quot;: &quot;Items&quot;, &quot;type&quot;: &quot;array&quot;}}, &quot;required&quot;: [&quot;CustomerName&quot;, &quot;AccountNumber&quot;, &quot;ShippingAddress&quot;, &quot;Items&quot;]}```<br>&lt;email&gt;<br>    &quot;hello i&#39;d like to order 2 eggs. My name is James. My account number is 1234. My address is 1234 Bayberry Ln. Appreciate it!&quot;<br>&lt;/email&gt;<br>Instructions:<br>- Read the email and extract the information in it. <br>- Respond in the format instructions given above.<br>Begin!&quot;&quot;&quot;</pre><p>When you send that prompt, the LLM follows the example and extracts the information for you:</p><pre>{<br>    &quot;CustomerName&quot;: &quot;James&quot;,<br>    &quot;AccountNumber&quot;: &quot;1234&quot;,<br>    &quot;ShippingAddress&quot;: &quot;1234 Bayberry Ln&quot;,<br>    &quot;Items&quot;: [<br>        {<br>            &quot;ItemName&quot;: &quot;eggs&quot;,<br>            &quot;ItemQuantity&quot;: 2<br>        }<br>    ]<br>}</pre><p>By using this well-defined format for an email order, we can pass this parsed object back through the LLM and ask it to ensure that all the required fields for an order are present. If it’s not, we can route the email to a human for help!</p><p>For example, let’s suppose that all EmailOrders need a CompanyName field as well. If the validation is this straightforward, we can simply use pydantic validations (no AI needed!). If your use case gets more complicated, the output can be passed through an LLM to provide some higher level logic.</p><p>We’ll take the same order as above but leave out the CompanyName:</p><pre>&gt;&gt;&gt; class EmailOrder(BaseModel):<br>...   CustomerName: str<br>...   AccountNumber: str<br>...   ShippingAddress: str<br>...   Items: list[OrderItem]<br>...   CompanyName: str<br>...   <br>&gt;&gt;&gt; e = EmailOrder(CustomerName=&quot;James&quot;, AccountNumber=&quot;1234&quot;, ShippingAddress=&quot;1234 Bayberry Ln&quot;, Items=[i])<br>Traceback (most recent call last):<br>  File &quot;&lt;python-input-19&gt;&quot;, line 1, in &lt;module&gt;<br>    e = EmailOrder(CustomerName=&quot;James&quot;, AccountNumber=&quot;1234&quot;, ShippingAddress=&quot;1234 Bayberry Ln&quot;, Items=[i])<br>  File &quot;/Users/jbarney/.venv/lib/python3.13/site-packages/pydantic/main.py&quot;, line 212, in __init__<br>    validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)<br>pydantic_core._pydantic_core.ValidationError: 1 validation error for EmailOrder<br>CompanyName<br>  Field required [type=missing, input_value={&#39;CustomerName&#39;: &#39;James&#39;,...ello&#39;, ItemQuantity=2)]}, input_type=dict]</pre><p>Pydantic does a lot for us here by throwing a ValidationError. Our driver program can simply catch this error and funnel the email to a human reviewer.</p><p>Of course, an LLM can also detect this error. I’m showing this for completeness; typically you’ll want to leverage traditional programming for data validation:</p><pre>prompt = &quot;&quot;&quot;Evaluate that the input object matches the expected schema: <br>{input}<br>{schema}<br>Reply with &quot;True&quot; if it does match and &quot;False&quot; if it does not match.<br>&quot;&quot;&quot;</pre><p>With all this in place, we now have a system that can easily handle properly written email orders. More importantly, we have implemented a <strong>self-governing process</strong> that keeps humans in the loop when the AI needs help.</p><p>Crucially, we didn’t rewrite the entire order entry process! We’ve taken a time-consuming part of the process and built a system that concentrates human effort in the areas where it makes the largest difference. Going forward, we can start modifying the other parts of the process, systematically removing the human toil.</p><h3>Summiting Everest</h3><p>This iterative approach to solving complicated problems is nothing new. All big problems need to be broken down into their constituent parts in order to truly be solved.</p><p>The “magic” of AI is particularly convincing, however. It’s easy to hope to make big leaps, given how capable these models are with just a few lines of input. Compared to technology like blockchain and Big Data, the effort required to go from idea to tantalizing proof-of-concept is minimal. AI doesn’t need dozens of custom configured servers to run a Map-Reduce job across 18 TB of data that took you 6 months to migrate.</p><p>So keep that simplicity in mind as you build your next AI solution: small steps to the summit.</p><p>See you up there!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dc4ec62ec8dd" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/another-hike-up-everest-dc4ec62ec8dd">Another Hike Up Everest</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Build your own AI Agent Observability System]]></title>
            <link>https://medium.com/@jamesbarney71/build-your-own-ai-agent-observability-system-395693ea0591?source=rss-b7c226dc9b8e------2</link>
            <guid isPermaLink="false">https://medium.com/p/395693ea0591</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[explainable-ai]]></category>
            <category><![CDATA[observability]]></category>
            <category><![CDATA[understanding]]></category>
            <dc:creator><![CDATA[James Barney]]></dc:creator>
            <pubDate>Thu, 10 Oct 2024 19:29:55 GMT</pubDate>
            <atom:updated>2024-10-10T19:29:55.202Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jjS0Hg_5Gks2o1aio-jl6Q.png" /></figure><p>We’ve all visited websites with the chatbot icon on the lower right. Honestly, I think those can be pretty great solutions to help you find the answers to frequently asked questions. They do tend to fall flat however when you ask more complicated questions and that’s usually when you’d route to a human. But humans aren’t always available so adding AI as a better-than-business-rules and more-available-than-humans solution to these chat boxes is pretty appealing.</p><p>So you add AI to your chat window and it starts answering questions and solving everyone’s problems and everyone’s happy… until <a href="https://www.bbc.com/travel/article/20240222-air-canada-chatbot-misinformation-what-travellers-should-know">it starts promising discounts on your products</a>.</p><p>Now you need to not only keep track of what your customers are asking for in the chat, but also what your AI Agents are up to behind the scenes. What API calls are they making? Where are they getting the data they’re using to answer your questions?</p><p>These questions can be answered through “AI Observability”, a new application of the venerable observability ecosystem. I’m not going to attempt to cover the intricacies of observability. Nor am I going to claim to be an observability expert. There are <a href="https://www.google.com/search?q=observability">many large enterprises</a> dedicated to doing observability for all your systems. That’s great; they’re great. You probably want to use one of them.</p><p>BUT let’s build our own so we can learn something.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/756/1*8q291sq2qTPm_75k-t52mw.png" /><figcaption>The omni-present lower-right chat window</figcaption></figure><p>AI Agents are quickly becoming the next exciting development in the AI world. Running one is easy, running thousands requires a bit more technology. How do you track agent success or failures over time? How do you drill into failure reasons? Being able to answer these questions as you scale your AI Agent capabilities is crucial for long-term AI success.</p><p>In this walk through, we’ll use <a href="https://www.langchain.com/">LangChain</a>, <a href="https://rudderstack.com">Rudderstack</a> and <a href="https://clickhouse.com/">Clickhouse</a> to build our own AI agent observability system.</p><h3>Building an Agentic AI Observability System</h3><p>There are three core aspects of agent observability which ring true for any system that requires monitoring at scale:</p><ol><li>The agent (or source) needs to emit events as they happen with associated metadata (run IDs, timestamps, etc)</li><li>The observability system needs to process and handle those events efficiently while maintaining data resilience if the source or destination encounters issues</li><li>The destination (a datastore of some kind) needs to be able to handle large volumes of events while being able to stitch related events together to help tell a broader story.</li></ol><p>There are many technologies that can perform these tasks for us. Some dedicated systems like LangSmith, Datadog, and more. But if you want or need total control of the system, you’d need to implement each part yourself. It’s also a great way to learn how LLM frameworks work internally!</p><h3>What are Agents?</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hQGJeQAZZeeFydA15GksEg.png" /><figcaption>How AI imagines what “AI using tools” looks like.</figcaption></figure><p>Agents in AI operate a few levels above your basic chat or prompt-based LLM interaction. They can have access to “the real world” through Tools which typically interact with APIs on your behalf. By giving an Agent multiple tools, it can creatively solve problems within those realms.</p><p>For example, if you were to ask a non-agent LLM “tell me the weather”, it’d likely respond with “I’m sorry, I don’t have access to the current weather.” That’s because the base LLM is trained on historical data — up-to-date weather data can’t be included in an LLM’s training data set.</p><p>To get around this limitation, <a href="https://medium.com/@jamesbarney71/apis-and-ai-extending-amazons-famous-mandate-into-the-future-fd6d701225d9">we can give the LLM access to a weather API and by doing so, create an Agent</a>. Now we can ask “tell me the weather” and, based on that context, the Agent will know to reach out to the API to return a real answer: “It’s currently raining because a storm front is approaching your location.” That is a very useful response which provides very specific, timely, and important information.</p><h3>Understanding Agentic Workflows</h3><p>A single step (checking the weather) is relatively simple to understand and from a human validation perspective, simple to verify (look outside, is it raining?).</p><p>Things can quickly get more complicated with more tools being given to an Agent. Let’s expand this example and imagine our agent will help us plan a weekend trip — what tools would it need?</p><ul><li>Weather tool — Will it be sunny, raining, or snowing?</li><li>Attendee tool — Who is available and what are their preferences?</li><li>Navigation tool — How can the attendees get to the weekend trip?</li><li>Booking tool — What reservations at hotels, restaurants, and museums are needed?</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dAdQ8sJYst-DX4XCYgpY-Q.png" /><figcaption>AI checking the weather</figcaption></figure><p>These broad tool descriptions greatly oversimplify how tools really work but they illustrate the separation of duties between tools. Normally you’d want to be more specific so that each tool does one thing well. A real implementation of a weekend trip planning agent could have dozens of tools.</p><p>If we consider the high level steps to planning a trip, we might expect something like this:</p><ol><li>Check the weather and come up with a few options</li><li>Poll the possible attendees and see who would prefer which locations</li><li>Decide on the location and tell the attendees</li><li>Based on who can attend, plan and present travel and lodging preferences and accommodations</li><li>Gather some initial activity ideas and poll the group of attendees what they’d prefer to do</li><li>Base activities on the weather forecast, prioritizing museum visits during non-sunny days</li><li>Monitor and adjust the schedule as situations change (late flights, last minute cancellations, dangerous weather)</li></ol><p>While this is a toy example, a late flight could alter an entire days’ plans for certain members of the trip. How would the Agent respond to that? If you were providing this Agent as a service to your customers, how would you monitor not just this single trip but the thousands of other trips happening that weekend? How would you show the Agent’s thought process as it modified plans to a customer?</p><p>All these questions are impossible to answer without a rigorous agent observability system. So let’s build one!</p><h3>Emitting Events</h3><p>Many LLM frameworks have a capability to alter data sent or generated as it occurs using functions known as Callbacks. Generally, callbacks are triggered when various processes occur within a broader system. In the case of LLM frameworks, callbacks can be triggered when you prompt and LLM, when it responds, or when it begins and finishes processing data.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uNTOHXZc5GIGp0xX-m66lQ.png" /><figcaption>An actual image of AI emitting events.</figcaption></figure><p>To illustrate how unrelated technology can accelerate your AI development, I’ll use <a href="https://www.rudderstack.com/">Rudderstack</a> to track and send events generated in the LLM callbacks. Rudderstack is particularly great since they have many connections preconfigured for you which makes the movement of this data very easy. It is so important to be able to trust in the data you’re basing AI Agent development decisions off of. Rudderstack can reliably handle this data for you with almost no additional engineering on your part.</p><p>LangChain is a popular framework with over 250,000 stars on GitHub and provides native callback functionality that integrates with their own LangSmith service. But we can build our own through the <a href="https://api.python.langchain.com/en/latest/tracers/langchain_core.tracers.base.BaseTracer.html">BaseTracer</a> class within langchain core. To make our own callback tracer, we can just extend it:</p><pre>from langchain_core.tracers.base import BaseTracer<br>import rudderstack.analytics as rudder_analytics<br><br>rudder_analytics.write_key = &quot;your_write_key&quot;<br>rudder_analytics.dataPlaneUrl = &quot;https://your_dataplane_url&quot;<br>rudder_analytics.sync_mode = True #this sends events as they occur, IMPORTANT!<br><br>class RudderstackTracer(BaseTracer):<br>    name: str = &quot;RudderstackTracer&quot;<br>    def __init__(self, **kwargs: Any) -&gt; None:<br>        super().__init__(**kwargs)<br><br>    def _run_to_dict(self, run: Run) -&gt; dict:<br>        &quot;&quot;&quot;<br>        Use this method to tweak what you actually send to your downstream system.<br>        &quot;&quot;&quot;<br>        with warnings.catch_warnings():<br>            warnings.simplefilter(&quot;ignore&quot;, category=PydanticDeprecationWarning)<br>            run_dict = run.dict(exclude={&quot;child_runs&quot;, &quot;inputs&quot;, &quot;outputs&quot;})<br>            #run_dict.pop(&quot;my_sensitive_data_key&quot;, None) #example key removal<br>            return {<br>                **run_dict,<br>                &quot;inputs&quot;: run.inputs.copy() if run.inputs is not None else None,<br>                &quot;outputs&quot;: run.outputs.copy() if run.outputs is not None else None,<br>            }<br><br>    def handle_event(self, event_type: str, run: Run) -&gt; None:<br>        run_id = str(run.id)<br>        rudder_analytics.track(run_id, event_type, _run_to_dict(run=run))<br><br>    # it&#39;s important to start these functions with underscores &#39;_&#39; so that the abstract class BaseTracer can use them to actually send events!<br>    def _on_llm_start(self, run: Run) -&gt; None:<br>        self.handle_event(&#39;on_llm_start&#39;, run)<br><br>    def _on_llm_end(self, run: Run) -&gt; None:<br>        self.handle_event(&#39;on_llm_end&#39;, run)<br><br>    # the rest of the &quot;on_*&quot; callbacks listed here https://github.com/langchain-ai/langchain/blob/3796e143f83a57b258f518efa13118be704e48c3/libs/core/langchain_core/tracers/base.py<br><br><br>    def _persist_run(self, run: Run) -&gt; None:<br>        pass</pre><h3>Handling Events</h3><p>Rudderstack makes routing these events super easy thanks to their python SDK: pip install rudder-sdk-python</p><p>Follow Rudderstack’s <a href="https://www.rudderstack.com/docs/sources/event-streams/sdks/rudderstack-python-sdk/">onboarding instructions</a> to get fully set up but it’s very simple.</p><p>Don’t forget to configure a destination for the events, I picked <a href="https://www.rudderstack.com/docs/destinations/warehouse-destinations/clickhouse/">ClickHouse</a>. Then connect them!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yARGpX4oll_ShRyxAT5SpA.png" /><figcaption>Connecting your sources to destinations is very easy — Rudderstack handles all the intricacies of ETL for you.</figcaption></figure><p>Once you have the Rudderstack SDK installed and the write key for your source, you can start sending events with the RudderstackTracer class from above and an existing LangChain agent:</p><pre><br>from rudderstack_tracer import RudderstackTracer<br><br>human = &quot;&quot;&quot;{input}&quot;&quot;&quot;<br><br>prompt = ChatPromptTemplate.from_messages(<br>    [<br>        (&quot;system&quot;, &quot;You&#39;re a helpful AI assistant&quot;),<br>        (&quot;human&quot;, human),<br>    ]<br>)<br><br>agent = create_structured_chat_agent(llm, tools, prompt)<br>agent_executor = AgentExecutor(<br>    agent=agent, verbose=True, handle_parsing_errors=True<br>)<br><br>agent_executor.invoke(<br>    {&quot;input&quot;: &quot;Hello AI!&quot;}, <br>    {&quot;callbacks&quot;: [RudderstackTracer()]} # this is where you add the event callback!<br>)</pre><p>Now execute the script you’ve written. If you look at your Rudderstack dashboard, you’ll be able to see the events trickling in.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RNGw1pYKh9QX4VCj9QirSA.png" /><figcaption>AI events from my test script streaming in</figcaption></figure><p>You’ll see the various event types coming in, all depending on which methods you’ve configured and what your agents are doing.</p><p>Each of these event types will land in their own table in your destination!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ATrEtDgbceeOAeGFoMrGAA.png" /><figcaption>Event Types and Counts</figcaption></figure><h3>Querying Events</h3><p>Just having a pile of data isn’t worth much. We can begin to understand what our agents are doing by grouping these events by run and by tool execution. I’ll show how to recreate a chain run by joining all these tables.</p><p>First here’s an example schema form the on_chain_start. There’s a lot of useful columns:</p><pre>CREATE TABLE default.on_chain_start<br>(<br>    `parent_run_id` Nullable(String),<br>    `serialized_kwargs_optional_variables` Nullable(String),<br>    `timestamp` Nullable(DateTime),<br>    `id` String,<br>    `serialized_lc` Nullable(Int64),<br>    `serialized_kwargs_messages` Nullable(String),<br>    `trace_id` Nullable(String),<br>    `serialized_kwargs_partial_variables_tool_names` Nullable(String),<br>    `original_timestamp` Nullable(DateTime),<br>    `serialized_id` Nullable(String),<br>    `serialized_type` Nullable(String),<br>    `context_source_id` Nullable(String),<br>    `context_request_ip` Nullable(String),<br>    `context_library_version` Nullable(String),<br>    `run_type` Nullable(String),<br>    `channel` Nullable(String),<br>    `session_name` Nullable(String),<br>    `context_destination_type` Nullable(String),<br>    `event` LowCardinality(String),<br>    `event_text` LowCardinality(String),<br>    `received_at` DateTime,<br>    `uuid_ts` Nullable(DateTime),<br>    `tags` Nullable(String),<br>    `events` Nullable(String),<br>    `context_ip` Nullable(String),<br>    `serialized_kwargs_input_variables` Nullable(String),<br>    `start_time` Nullable(DateTime),<br>    `inputs_input` Nullable(String),<br>    `context_library_name` Nullable(String),<br>    `user_id` Nullable(String),<br>    `dotted_order` Nullable(String),<br>    `context_source_type` Nullable(String),<br>    `sent_at` Nullable(DateTime),<br>    `serialized_kwargs_partial_variables_tools` Nullable(String),<br>    `context_destination_id` Nullable(String),<br>    `serialized_name` Nullable(String),<br>    `name` Nullable(String)<br>)<br>ENGINE = SharedReplacingMergeTree(&#39;/clickhouse/tables/{uuid}/{shard}&#39;, &#39;{replica}&#39;)<br>PARTITION BY toDate(received_at)<br>ORDER BY (received_at, id)<br>SETTINGS index_granularity = 8192</pre><p>And here’s the union to start grouping these events together:</p><pre>SELECT<br>    parent_run_id,<br>    original_timestamp,<br>    event,<br>    event_text,<br>    id<br>FROM<br>    (<br>        SELECT<br>            parent_run_id,<br>            original_timestamp,<br>            event,<br>            inputs_input as event_text,<br>            id<br>        FROM<br>            on_chain_end<br>        UNION ALL<br>        SELECT<br>            parent_run_id,<br>            original_timestamp,<br>            event,<br>            error as event_text,<br>            id<br>        FROM<br>            on_chain_error<br>        UNION ALL<br>        SELECT<br>            parent_run_id,<br>            original_timestamp,<br>            event,<br>            inputs_input as event_text,<br>            id<br>        FROM<br>            on_chain_start<br>        UNION ALL<br>        SELECT<br>            parent_run_id,<br>            original_timestamp,<br>            event,<br>            error as event_text,<br>            id<br>        FROM<br>            on_llm_error<br>        UNION ALL<br>        SELECT<br>            parent_run_id,<br>            original_timestamp,<br>            event,<br>            inputs_prompts as event_text,<br>            id<br>        FROM<br>            on_llm_start<br>    )<br>where parent_run_id = &#39;{a_particular_run_id}&#39;<br>ORDER BY<br>    original_timestamp</pre><p>Note that the parent_run_id column is what groups these events all together. If you have more or less tables, you’ll need to add those as you see fit.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ocpbJchSfV8uEOGxA79HWw.png" /><figcaption>A single Agent session with an error captured!</figcaption></figure><p>Now that we have events coming into our database, we can easily create rules and metrics on top of these events! Errors could be routed to a Slack channel, metrics about a given type of Agent could be aggregated, or sentiment from the customer’s responses could be extracted as they interact with your Agents. Perhaps a particular Tool starts throwing more errors after an update — you could detect that before it becomes an issue.</p><h3>Learnings</h3><ul><li>AI Agents will soon integrate deeply with many existing systems. We’ll need to track the actions those Agents take so if mistakes happen, we (or another error-fixing Agent!) can unwind them.</li><li>Writing custom callbacks is very simple and can let you do some very interesting things with AI Agents</li><li>Existing technologies can be leveraged to build your own AI observability capabilities. Or you can use purpose-built AI observability frameworks.</li></ul><p>Collecting and understanding the workflow data that your AI agents are generating, especially as your AI capability grows, is essential towards scaling and monitoring your AI ecosystem. Without this data, you’ll be stuck sending screenshots, relying on whether prompt changes “feel more accurate”, or just not knowing what your AI is doing.</p><p>You don’t have to build your own system, but understanding how the various pieces work together can give you appreciation and deeper insight into the complexities of running these AI workloads at scale.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=395693ea0591" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[APIs and AI: Extending Amazon’s Famous Mandate into the Future]]></title>
            <link>https://medium.com/@jamesbarney71/apis-and-ai-extending-amazons-famous-mandate-into-the-future-fd6d701225d9?source=rss-b7c226dc9b8e------2</link>
            <guid isPermaLink="false">https://medium.com/p/fd6d701225d9</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[api]]></category>
            <category><![CDATA[agentic]]></category>
            <category><![CDATA[strategy]]></category>
            <category><![CDATA[automation]]></category>
            <dc:creator><![CDATA[James Barney]]></dc:creator>
            <pubDate>Tue, 16 Jul 2024 15:19:33 GMT</pubDate>
            <atom:updated>2024-07-16T15:19:33.289Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*X2qbYmYtDtdZohsCVlVQwg.jpeg" /><figcaption>APIs and their connections form the backbone of AI capabilities today.</figcaption></figure><h3>AI and Agents are only as good as the APIs they can leverage.</h3><p><a href="https://web.archive.org/web/20180124191633/https://plus.google.com/+RipRowan/posts/eVeouesvaVX">Around twenty-two years ago, the now famous “API Mandate“ was issued by Jeff Bezos at Amazon</a>. Effectively it ordered all teams at Amazon to now interact and share information with each other via application programmatic interfaces (APIs). The idea was to streamline integrations and connections, making it easier to build new capabilities while maintaining truth for data. Of course this introduces more complexity into some aspects of the business but ultimately it’s driven Amazon to the unimaginable heights of power it enjoys today.</p><p>If you’re not familiar with the mandate, here are the rules from the original post linked above:</p><ol><li>All teams will henceforth expose their data and functionality through service interfaces.</li><li>Teams must communicate with each other through these interfaces.</li><li>There will be no other form of interprocess communication allowed: no direct linking, no direct reads of another team’s data store, no shared-memory model, no back-doors whatsoever. The only communication allowed is via service interface calls over the network.</li><li>It doesn’t matter what technology they use. HTTP, Corba, Pubsub, custom protocols — doesn’t matter.</li><li>All service interfaces, without exception, must be designed from the ground up to be externalizable. That is to say, the team must plan and design to be able to expose the interface to developers in the outside world. No exceptions.</li><li>Anyone who doesn’t do this will be fired.</li></ol><p>Many facets of our modern world exist because of this mandate since Amazon Web Services was a product of this mindset: instead of emailing and requesting a virtual machine from the infrastructure team, what if there was an API you could call to make it for you? Fast forward to today and the multitude of capabilities within AWS and other platforms, powered by APIs <em>first</em>, form the cornerstone of almost everything we do with technology.</p><p>While these rules have served us well for twenty-two years, AI has changed the game — in the very near future, it won’t only be human developers creating connections between two systems via APIs. We need to prepare the APIs we’ve come to rely upon for the age of AI. For example, AI agents and agentic workflows are already bringing AI and APIs together in exciting ways.</p><p>This post explores three potential new rules we should add to the API Mandate to ensure rapid AI enablement.</p><h3>Parallels between APIs and AI</h3><p>APIs form the foundation of all automation and interconnected systems today. Ordering food, logging into your bank, sending emails, getting Slack messages — all of these systems are built upon APIs. To stay with the Amazonian theme, Amazon’s <a href="https://aws.amazon.com/blogs/aws/prime-day-2023-powered-by-aws-all-the-numbers/">Prime Day</a> is a great example of how APIs can scale an existing capability to something almost incomprehensible.</p><blockquote>“Amazon CloudFront — Amazon CloudFront handled a peak load of over 500 million HTTP requests per minute, for a total of over 1 trillion HTTP requests during Prime Day.”</blockquote><p>500,000,000 API requests per minute or 8,333,000 requests per second, built off APIs. To put that in perspective, quartz wrist watches are powered by oscillations that fluctuate <a href="https://en.wikipedia.org/wiki/Quartz_clock">32,768</a> times per second. The API requests during Prime Day peak at a frequency<em> more than 250 times faster</em> than what keeps time on your wrist.</p><p>So where does AI come into this? It comes down to scalability. Just as no human would ever be able to respond to 500 million requests per minute, no human will be able to build and maintain connections into each and every system that needs to be connected. That’s where AI shines.</p><h3>AI is the next frontier of Automation</h3><p>I’ve been working in the automation space my whole career. I fully believe that if you perform a task more than three times, it’s probably <a href="https://xkcd.com/353/">worth writing a Python script</a>. Of course, it’s not always about saving time — it’s about building a more rich ecosystem of capabilities.</p><p>If you connect two systems of data, you will always find new innovations.</p><p>It’s always been difficult to build those connections, however. There’s a spectrum of automation that exists:</p><figure><img alt="A horizontal spectrum labeled “The Spectrum of Automation” transitions from green on the left to white on the right. Four points are marked along the spectrum, from left to right: “Fully Automated”, “Frustrating but do-able”, “Do-able but not worth it”, and “Impossible”. The image is credited to James Barney 2024." src="https://cdn-images-1.medium.com/max/1024/0*jFApc5MEM6Csm5ag" /></figure><p>On the left, you have Fully Automated systems (usually built on APIs by the way). On the right you have Impossible tasks. Technology is constantly pushing into the Gray Area to the left of Impossible. Here are some examples:</p><p><strong>Fully Automated</strong></p><p>Ordering a new pair of shoes on Amazon Prime</p><ul><li>Of course, this wasn’t possible until Amazon automated all aspects of this process with APIs</li></ul><p><strong>Frustrating but do-able</strong></p><ul><li>OAuth 2.0 flows</li><li>Clicking “Sign in with Google” feels fully automated and easy but it’s a lot of work.</li><li>Integrating with different providers is tricky and tedious. It’s not impossible but it is full of frustrations.</li><li>Modern OAuth2.0 libraries obfuscate a lot of the tedium for us, but it’s been a work in progress for a long time!</li></ul><p><strong>Do-able but not worth it</strong></p><ul><li>Building a custom website scraper to extract data.</li><li>Websites update all the time, with custom layouts and data structures.</li><li>In fact, it’s this “not worth it” concept that spawned the idea of APIs in the first place: give us the data in a predictable format so we can do what we need consistently</li></ul><p><strong>Impossible</strong></p><ul><li>Accurate long-term weather prediction for your home address</li><li><a href="https://www.scientificamerican.com/article/the-three-body-problem/">Chaotic systems</a> are by definition impossible to predict due to their sensitivity to initial conditions</li><li>We will certainly become <em>better</em> at predicting weather for distinct points on the globe, but it will never be perfect for an arbitrary future point-in-time.</li></ul><p>Here’s how AI is pushing into the Gray Area on the Automation Spectrum: those websites which never had a published API can now be <em>digested with a single prompt</em>.</p><blockquote>“Here’s a website with data about XYZ. Parse it into a well-structured JSON document with this particular format.”</blockquote><p>The amount of time and effort that is required to build that integration the “traditional way” is firmly planted within the “do-able but not worth it” spectrum. But with AI, when the website changes, so long as the data we’re interested in is still on the website, it’ll be able to extract that information and format it <em>without any more input</em> from us.</p><p>Will it be perfect? Probably not. But for some websites and other dark corners of a business, not exposing this data via an API has either been too difficult to build, too expensive to maintain, or too risky to change. Now with AI, you’re able to extract that data, give it structure, and build connections. And where there’s connections, there’s innovation.</p><p>That’s the appeal of AI and why it’s making such a big wave in technology today. Can it do the impossible? Absolutely not. Can it push the frontier of the possible further right? Absolutely.</p><h3>Your Data Should Be Able to Think</h3><p>Or at least, the connections to your data should be able to think.</p><p>Let’s walk through the normal process for integrating two “Fully Automated” systems, the ideal situation:</p><ol><li>The software developer reads documentation about System A, the APIs that are available for it, and any existing integrations that might exist.</li><li>The software developer begins writing the integration, usually with simple Proof of Concept that extracts the crucial information from System A and displays or stores it in System B.</li><li>The software developer, upon validating that “it works” begins to build out more robust functionality to grab all the important information.</li><li>The integration for the two systems is deployed to some middle-layer of capability to run on a schedule or in real-time.</li><li>The integration is maintained in perpetuity until it is either replaced or retired.</li></ol><p>Only five easy steps! What’s so hard about that? All of it! This is not a blog post about Data Engineering or ETL (well, not really) so I won’t get into the details. But effectively each of those succinct steps represent weeks or months of work by the engineer. And that’s for two IDEAL systems!</p><p>AI can speed each of these steps significantly. Just yesterday, I was able to create a Python SDK for an API, draft unit and integration tests, publish the SDK for several beta tests, write a downstream tool that consumed the SDK, and share the tool with teammates. This would have taken me days normally, not because I’m a slow developer, but because it just takes time to integrate all of these things together.</p><p>Each system will have its own quirks and special edge cases — those quirks and edge cases are what AI is particularly good at solving problems around. Engineers have always created frameworks to speed up their work processes, but they’ve always had to spend significant time thinking about how to squeeze square pegs through round holes. AI is the tool that can help you either change the shape of the peg or the hole, <em>and every other system in scope,</em> all at the same time.</p><h3>The AI Extension to the API Mandate</h3><p>AI capabilities are almost exclusively consumed via APIs today. This API mentality has created a whole ecosystem of tools and frameworks which work together to make adopting the technology easier and faster. But ask any data engineer and they’ll tell you first hand that the tools and frameworks of today do not solve the problems of tomorrow. Separating business capabilities from data processes is essential to agility. The same is true for AI: models will continue to improve every 6 months and new capabilities will burst onto the scene. If you want to keep up with the rate of change, you must keep your integrations loosely coupled. The AI world will not be recognizable in a year.</p><p>Do you know what will be the same as a year ago? Your APIs that serve your business data. Customer transactions will look the same — different products, certainly, but the structure will be the same. For this reason we need new rules in the API mandate:</p><ol><li><strong>Teams must design for AI augmentation in all new services and major updates.</strong></li></ol><ul><li>If your service leverages AI to perform an action, <em>expose that as a standalone API capability</em>.</li><li>When you expose this capability, be sure you show the parameters, prompts and other configurations the endpoint uses to run. Optionally give your consumers the freedom to tweak the parameters as they see fit with each API call (but give sane and safe defaults).</li></ul><p><strong>2. All APIs must be accessible by AI agents.</strong></p><ul><li>Don’t mandate the adoption of LangChain or LlamaIndex or Semantic Kernel. The team must simply expose the capability of an autonomous agent to connect. Let downstream consumers pick the AI framework they want to use.</li><li>This can just mean ensuring each “system integration point” is well documented, explaining the use, return values and error codes for each API operation.</li></ul><p><strong>3. Inputs to an AI transaction resulted in any given output must be observable.</strong></p><ul><li>While AI explainability is a rapidly maturing field, if the individual calls to an LLM, invocations of particular tools, or the input to specific prompt templates is made programmatically available, analysis of when things go wrong will be possible</li></ul><p>By ensuring that your organization adopts these rules, you’ll be able to ensure that the systems you use today will be ready for AI consumption tomorrow. This is where the real value of AI adoption and enablement is hidden — build your systems in a way that enables seamless integration.</p><p>When AI connects two systems of data, you will find new innovations. The API Mandate and the APIs that exist today because of it are important and should not be thrown away. They need to be built upon and enhanced to bring the new functionality that AI promises to light. But just like any set of rules, they should be amended and kept up to date so that new technology doesn’t get sidelined too early.</p><p><em>Do you agree? Are there other rules you think should apply? How are you seeing AI change the world of system integrations? Please share your thoughts below.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fd6d701225d9" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[AutoTools: Supercharging LangChain with Dynamic SDK Integration]]></title>
            <link>https://medium.com/@jamesbarney71/autotools-supercharging-langchain-with-dynamic-sdk-integration-ae9715969fde?source=rss-b7c226dc9b8e------2</link>
            <guid isPermaLink="false">https://medium.com/p/ae9715969fde</guid>
            <category><![CDATA[agents]]></category>
            <category><![CDATA[automation]]></category>
            <category><![CDATA[langchain]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[ai]]></category>
            <dc:creator><![CDATA[James Barney]]></dc:creator>
            <pubDate>Tue, 25 Jun 2024 00:28:01 GMT</pubDate>
            <atom:updated>2024-06-25T03:30:01.923Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lQS611iWJcBZTlYfPhMeEw.png" /></figure><h3>Enabling tool use within LangChain for any Python SDK.</h3><h3>Inspiration</h3><p>In the rapidly evolving world of AI and language models, developers are constantly seeking ways to enhance their tools and streamline workflows. <a href="https://www.langchain.com/">LangChain</a> is a popular framework for building applications with large language models (LLMs). I’ve written AutoTools to help bridge the gap between the millions of Python packages that are in use everywhere today with the AI agents of the future. This feature allows developers to seamlessly integrate any SDK into their LangChain projects, creating dynamic toolkits for AI agents with minimal effort.</p><h3>AI Tool Usage</h3><p>AI tool usage refers to the ability of AI models, particularly large language models (LLMs), to interact with and utilize external tools or APIs to perform specific tasks or retrieve information. This capability allows AI agents to extend their functionality beyond just processing and generating text, enabling them to interact with the real world, access up-to-date information, or perform specialized operations.</p><p>LangChain exposes a capability called Tools that turns any of your favorite chat-enabled LLMs into tool-wielding AI agents. However, turning code into tools usually involves writing custom functions. That can become tedious and repetitive.</p><h3>What is AutoTools?</h3><p>AutoTools is a new addition to the LangChain ecosystem that enables the automatic wrapping of SDK clients, transforming them into LLM-enabled toolkits. At its core, AutoTools consists of two main components:</p><ol><li>AutoToolWrapper: This component wraps the SDK you provide, making its functions available as tools for your AI agents.</li><li>CrudControls: This feature allows you to fine-tune the access your agent has to Create, Read, Update, and Delete (CRUD) operations within the SDK.</li></ol><h3>The Power of Dynamic Toolkits</h3><p>With AutoTools, developers can quickly adapt existing SDKs for use with LangChain agents. This means you can leverage the power of established libraries and APIs without having to write custom toolkits for each one. Whether you’re working with cloud services, databases, or any other software with a Python SDK, AutoTools can help you integrate it into your LLM-powered applications.</p><h3>Getting Started with AutoTools</h3><p>To demonstrate the simplicity and power of AutoTools, let’s walk through a basic example using the AWS boto3 library to interact with Amazon S3:</p><ol><li>Use pip install to get everything you need:</li></ol><pre>pip install langchain-autotools</pre><p>2. First, import the necessary components:</p><pre>from langchain_autotools import AutoToolWrapper, CrudControls<br>import boto3</pre><p>2. Create your SDK client and set up CrudControls:</p><pre>s3 = boto3.client(&quot;s3&quot;)<br>client = {&quot;client&quot;: s3}<br>crud_controls = CrudControls(<br>    read_list=&quot;list_buckets&quot;,<br>    create=True,<br>    create_list=&quot;create_bucket&quot;<br>)</pre><p>3. Wrap the client with AutoToolWrapper:</p><pre>autotool = AutoToolWrapper(client=client, crud_controls=crud_controls)</pre><p>4. Use the generated tools with a LangChain agent:</p><pre>agent = create_structured_chat_agent(llm, autotool.operations, prompt)<br>agent_executor = AgentExecutor(<br>    agent=agent,<br>    tools=autotool.operations,<br>    verbose=True,<br>    handle_parsing_errors=True,<br>    max_iterations=3,<br>)</pre><p>Now your agent can interact with S3, listing buckets (and many more functions), all through natural language commands!</p><p>Ask things like “how many S3 buckets do I have?” and using the credentials you’ve already configured, the agent will execute the command and using the response from the SDK, respond appropriately.</p><pre>agent_executor.invoke(<br>    {<br>        &quot;input&quot;: &quot;How many S3 buckets do I have?&quot;<br>    }<br>)</pre><pre>&gt; Entering new AgentExecutor chain...<br>Action:<br>```json<br>{<br>  &quot;action&quot;: &quot;list_buckets&quot;,<br>  &quot;action_input&quot;: &quot;&quot;<br>}<br>```</pre><p>The agent will call the list_buckets function in the SDK client it has access to and will respond accordingly.</p><pre>{<br>  &quot;action&quot;: &quot;Final Answer&quot;,<br>  &quot;action_input&quot;: &quot;Based on the output from the list_buckets tool, you have 9 S3 buckets.&quot;<br>}</pre><p>More complicated tasks can be achieved by including more tools and asking more abstract questions!</p><h3>Benefits of AutoTools</h3><ol><li>Rapid Integration: Quickly add new capabilities to your LLM agents without writing custom code.</li><li>Flexibility: Works with any SDK that exposes CRUD operations.</li><li>Fine-grained Control: Use CrudControls to limit which operations are available to your agent.</li><li>Scalability: As your SDK evolves, your tools automatically stay up-to-date.</li></ol><h3>Use Cases and Potential Applications</h3><p>The applications for AutoTools are vast. Here are just a few examples:</p><ul><li>Cloud Management: Integrate with various cloud provider SDKs to manage resources using natural language.</li><li>Database Operations: Wrap database client libraries to perform queries and manage data through conversation.</li><li>IoT Device Control: Use SDKs for IoT platforms to control and monitor devices using LLM agents.</li><li>API Integration: Quickly add support for new APIs to your chatbots or AI assistants.</li></ul><h3>Conclusion</h3><p>AutoTools represents a significant leap forward in the ease of integrating external services with LLM-powered applications. By bridging the gap between traditional SDKs and AI agents, it opens up new possibilities for developers to create more powerful and flexible AI solutions. As the AI landscape continues to evolve, tools like AutoTools will play a crucial role in helping developers keep pace with innovation and deliver more capable AI systems.</p><p>Whether you’re building a chatbot, an AI assistant, or a complex data processing pipeline, AutoTools for LangChain offers a streamlined path to incorporating a wide range of functionalities into your LLM-powered applications. Give it a try and unlock new potentials in your AI projects!</p><p>Please feel free to contribute ideas or submit issues on <a href="https://github.com/Barneyjm/langchain-autotools">the project’s Github page</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ae9715969fde" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Scaling AI Agent Applications]]></title>
            <link>https://medium.com/@jamesbarney71/scaling-ai-agent-applications-abf18cb681e5?source=rss-b7c226dc9b8e------2</link>
            <guid isPermaLink="false">https://medium.com/p/abf18cb681e5</guid>
            <category><![CDATA[python]]></category>
            <category><![CDATA[data-engineering]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <dc:creator><![CDATA[James Barney]]></dc:creator>
            <pubDate>Wed, 12 Jun 2024 02:00:38 GMT</pubDate>
            <atom:updated>2024-06-12T02:00:38.774Z</atom:updated>
            <content:encoded><![CDATA[<h3>The Hidden World of Data Engineering</h3><p>It’s no secret that <strong>data</strong> is the source of all capabilities of Artificial Intelligence, especially in the most recent generation of artificial intellect, the generative model.</p><p>Perhaps what is a secret (or at least, not well known to folks outside of the industry) is how that data came to be organized in such a way that these models are able to be trained using it.</p><p><strong>Data engineering</strong> is this “secret” that powers the world’s most advanced AI systems today. It’s an entire ecosystem of capabilities and resources that straddles the line between flexible software and rigid facts (data). These capabilities answer the questions that arise from any sizable process that involves data including:</p><ul><li>How do you collect the data efficiently?</li><li>How do you store the data?</li><li>How do you make the data available for use?</li><li>How do you show where data came from?</li><li>How can you prove that the data is trustworthy?</li></ul><p>The answers to these questions involve more complicated questions without simple, clear answers to each situation. Often you need large, complex systems that manage the data systems themselves. The world of open source software is rife with solutions that can help patch together answers to these questions.</p><h3>AI Agents and Workflows</h3><p>With each passing day, I see the term “Agent” popping up more in tech articles. Agents are the epitome of the AI promise with the advent of LLMs — <a href="https://youtu.be/sal78ACtGTc?si=FxjJ0O8vPlgsmDWI">Agents</a> can perform tasks and logic around problems to solve them automatically using the tools you’ve given them.</p><p>These “Agentic Workflows” can only be as good as the <em>realtime</em> data they have access to. Their effectiveness wholly depends on the information they have access to, including but also beyond, any <a href="https://blogs.nvidia.com/blog/what-is-retrieval-augmented-generation/">RAG</a>, <a href="https://python.langchain.com/v0.1/docs/use_cases/chatbots/memory_management/">memory</a>, or <a href="https://platform.openai.com/docs/guides/fine-tuning">fine-tuning</a> capabilities they have. This means all those APIs and microservices you’ve spent the last decade building will be crucial to the success of any Agentic Workflow.</p><p>But that’s only good for the first execution of an Agentic Workflow. What about its 100th run? Or 100,000th run? And that’s just for one single Agent!</p><p>How can you know that your Agents are still performing adequately? How can you upgrade the Agents to the best available model but still guarantee effectiveness? How can you measure an Agent’s value?</p><p>All these questions sound oddly familiar to the data engineering questions asked before:</p><ul><li>How do you run and manage hundreds or thousands of Agentic Workflows efficiently?</li><li>How do you record the input and output of the Agentic Workflows?</li><li>How do you extend an Agent’s capabilities beyond their initial role easily?</li><li>How do you show the reasoning behind an Agent’s actions?</li><li>How can you prove that an Agent is trustworthy?</li></ul><p>Just as the answers arising from data engineering involved large, complex systems; I propose that many of the <em>same systems</em> can answer these Agentic questions.</p><h3>Solutions are Out There</h3><p>This field is advancing rapidly and pinning your Agentic workflows onto one special-built framework will likely require many trade offs.</p><p>One method of lowering the switching costs as one framework falls out of favor with another is to leverage tried and tested technologies that weren’t developed with AI in mind. With some creativity, they can be applied to AI processes easily.</p><p>A very non-exhaustive yet opinionated list:</p><ul><li>For Agent scheduling, look to data workflow management tools like <a href="https://airflow.apache.org/">Apache Airflow</a>, which offers a straightforward UI and near-infinite workflow logic customization through standard SDLC. It’s important to not install your Agent packages in Airflow itself though! Keep logic separate and just let Airflow manage the workflow!</li><li>Encapsulate each Agent within its own <a href="https://www.docker.com/">Docker container</a> to keep maintaining it simple and easy, especially as fast-changing AI packages will push breaking changes multiple times a month.</li><li>For Agent performance management, Elastic and its well-known <a href="https://www.elastic.co/elastic-stack">ELK stack</a> can easily be plugged in to display realtime metrics not only of Agents but also their dependencies</li><li>Agentic Workflow metadata management can be solved with <a href="https://aws.amazon.com/glue/">AWS Glue</a> or any other data catalog to provide trend analysis and history</li><li>Queues (<a href="https://aws.amazon.com/sqs/">SQS</a> is hard to beat) can be used to buffer requests at peak times when costs are high and also provides a retry mechanism for free</li><li><a href="https://iceberg.apache.org/">Apache Iceberg</a> (probably overkill) can offer incredible capabilities out of the box for verifying input and output data from these workflows especially as these AI metadata model schemas settle into standards</li></ul><h3>Use What You’ve Got</h3><p>Ultimately, whatever technology you already use for data engineering can likely solve many of the problems and challenges posed by Agentic Workflows.</p><p>Having survived the onslaught of frameworks and solutions from the Age of Big Data, the most important thing to remember is that the favored frameworks of today will change, <a href="https://attic.apache.org/projects/falcon.html">fall out of favor</a>, or simply stop working one day.</p><p>And that’s okay.</p><p>You should stand on the shoulders of software giants. You should use battle hardened (but still flexible!) solutions. <em>You should not reinvent the wheel</em> of computation with each new technological fascination that is sweeping the world.</p><p>Those giants have already solved a lot of very hard problems for you. By leveraging them, you’ll be able to move faster and adapt quicker to new ideas in the AI space. If you’re extra nice and lucky, maybe another team at your company will even manage it for you!</p><p>Data engineering is unique in that it lives between the ephemeral world of software and the permanent world of data. The problems that data engineers have solved are some of the hairiest problems in computing.</p><p>If you leverage those existing tools and techniques in this new Age of AI, you will be able to stay afloat amidst the sea of constant change as this field develops.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=abf18cb681e5" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>