<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-7022755517551007355</atom:id><lastBuildDate>Tue, 28 Apr 2026 23:35:51 +0000</lastBuildDate><category>Delphi</category><category>News</category><category>TCanvas</category><category>Open Source</category><category>Java</category><category>Maths</category><category>Delphi 2010</category><category>Pragmatic Programming</category><category>Tools</category><category>VLO Framework</category><category>Components</category><category>Computational geometry</category><category>Python</category><category>English</category><category>Extreme Programming</category><category>Delphi XE</category><category>AGILE</category><category>Videos</category><category>Internet</category><category>Design patterns</category><category>Utilities</category><category>Artificial Intelligence</category><category>IDE Delphi</category><category>Physics</category><category>Eclipse</category><category>Operating System</category><category>.NET</category><category>Vijeo Citect</category><category>Productivity</category><category>Windows</category><category>openAI</category><category>Encryption Algorithms</category><category>P-Zaggy</category><category>Robotics</category><category>API Windows</category><category>Force-Directed Graph</category><category>Image processing</category><category>Indy</category><category>TED</category><category>Configuration library</category><category>Data Bases</category><category>Subversion</category><category>2D</category><category>Aspect oriented programming</category><category>COM Objects</category><category>Industrial Automation</category><category>Machine Learning</category><category>MySQL</category><category>RTTI</category><category>Raspberry Pi</category><category>Regular Expressions</category><category>Scada</category><category>Security</category><category>Debug</category><category>Education</category><category>Excel</category><category>JBoss</category><category>SQL Server</category><category>Software configuration</category><category>communications</category><category>github</category><category>google guice</category><category>llm</category><category>neuroplasticity</category><category>.net core 3.0</category><category>3D</category><category>AI Agents</category><category>ASP.NET</category><category>Android</category><category>Chaining Method</category><category>Continuous Integration</category><category>Cryptography</category><category>Dependency Injection</category><category>Google</category><category>Google Chrome</category><category>HTML</category><category>JDBC</category><category>JSON</category><category>JavaScript</category><category>LangChain</category><category>Lazarus</category><category>Linux</category><category>Microsoft</category><category>Molecules</category><category>OpenSSL</category><category>Parse.com</category><category>Persistence</category><category>RAG</category><category>REST</category><category>Services</category><category>Ubuntu</category><category>nodejs</category><category>ruby</category><category>wetware</category><category>API</category><category>Advanced Encryption Standard</category><category>Atom table</category><category>Code Coverage</category><category>Delphi10.2Tokyo</category><category>DevExpress</category><category>Extensions</category><category>Free Pascal</category><category>IT</category><category>Icons</category><category>Industrial Control</category><category>Kernel</category><category>MVC</category><category>Messaging</category><category>Ollama</category><category>Routes</category><category>Scripts</category><category>Scrum</category><category>TDD</category><category>TeamCity</category><category>Virtual machines</category><category>Windows Mobile</category><category>XML</category><category>csv</category><category>games</category><category>portfolio-optimization</category><category>ADO</category><category>AI</category><category>BaaS</category><category>Blogger</category><category>ChatBot</category><category>Cloud</category><category>Code Quality</category><category>Code Reviews</category><category>ComfyUI</category><category>Compilers</category><category>Compliance Automation</category><category>Console</category><category>Convex Hull</category><category>DLL</category><category>DUnit</category><category>Delphi XE6</category><category>DelphiBerlin</category><category>Devops</category><category>Dijkstra</category><category>Docker</category><category>Exceptions</category><category>FIT</category><category>Facebook</category><category>Finance</category><category>Financial Regulation</category><category>Firebase</category><category>Generics</category><category>Graph Visualization</category><category>Hacking</category><category>Hibernate</category><category>Investment</category><category>Kubernetes</category><category>LINQ</category><category>LangGraph</category><category>LoRA</category><category>Local LLM</category><category>Mock</category><category>NDepend</category><category>Notification</category><category>OLE</category><category>Object oriented programming</category><category>Pair Programming</category><category>Parsing</category><category>Photography</category><category>Prim Algorithm</category><category>Profiling</category><category>RPi3</category><category>RSA</category><category>Redes</category><category>Refactoring</category><category>Ribbon Controls</category><category>SQL</category><category>SSL</category><category>Science</category><category>Sharing</category><category>Skills</category><category>TStringGrid</category><category>UOC</category><category>Unicode</category><category>Unit Testing</category><category>University</category><category>VSCode</category><category>WMI</category><category>Web API</category><category>WorkFlow</category><category>XML-RPC</category><category>ZeosDBO</category><category>actions</category><category>bot</category><category>codex</category><category>deployment</category><category>efficient-frontier</category><category>flickr</category><category>gnuplot</category><category>monte-carlo</category><category>neuroscience</category><category>redmine</category><category>riskoptima</category><category>telegram</category><category>.net 5</category><category>.net 9</category><category>.net core 2.1</category><category>.net core 3.1</category><category>AG2</category><category>AI Chatbot</category><category>AI Coding Agents</category><category>AI workflow</category><category>AIAgents</category><category>AICodingAssistant</category><category>AJAX</category><category>ASCII</category><category>ASP.NET Core</category><category>AST Evaluation</category><category>Abstract Factory</category><category>Access</category><category>ActiveX</category><category>Agent Orchestration</category><category>Agentic AI</category><category>AgenticAI</category><category>Alpha-beta pruning</category><category>Analytics</category><category>AspectJ</category><category>Augmented Reality</category><category>AutoGen</category><category>BackEnd</category><category>Backup</category><category>Base64</category><category>C#</category><category>C++</category><category>CLI</category><category>CNN</category><category>CUP</category><category>Calculator Tool</category><category>CatvsDog</category><category>Certification</category><category>Chainlit</category><category>Claude Code</category><category>Clipboard</category><category>Cloud Computing</category><category>CodeLlama7B</category><category>Comparison</category><category>Compression</category><category>Concordion</category><category>ContinueExtension</category><category>CrewAI</category><category>CruiseControl</category><category>Currency</category><category>DBase</category><category>DLTK</category><category>Data Analysis</category><category>Data Recovery</category><category>Data Science</category><category>Data Visualization</category><category>Death March</category><category>Debian</category><category>Deep Learning</category><category>Delegates</category><category>Delphi XE5</category><category>Delphi XE7</category><category>Delphi10Seattle</category><category>DelphiFMX</category><category>DelphiTokyo</category><category>Deterministic AI</category><category>Developer Tooling</category><category>DeveloperProductivity</category><category>Devices</category><category>Diaspora</category><category>Digital Identity</category><category>Direct2D</category><category>EADS</category><category>ERP</category><category>ESMA</category><category>Entrepreneur</category><category>Exif</category><category>Files</category><category>Flash</category><category>FontAwesome</category><category>Forecasting</category><category>Fractals</category><category>Fuzzy</category><category>GEO</category><category>GPT-4o</category><category>GPT‑2</category><category>Game</category><category>Geek</category><category>Generative AI</category><category>GenerativeAI</category><category>Genetic Algorithms</category><category>Google Maps</category><category>Google Noop</category><category>Google SketchUp</category><category>Google insight</category><category>Google wave</category><category>Gource</category><category>GraphBasedWorkflows</category><category>HTTPS</category><category>Hardware</category><category>HuggingFace</category><category>IDE</category><category>IPTC</category><category>ISA</category><category>InceptionResnetV2</category><category>InnoSetup</category><category>Interface oriented programming</category><category>J2EE</category><category>JEDI</category><category>JUnit</category><category>Jenkins</category><category>Jflex</category><category>Jobs</category><category>Kanban</category><category>Keras</category><category>KeyStrokes</category><category>Kinvey</category><category>LLM inference</category><category>LLM integration</category><category>LLM tool calling</category><category>LSTM</category><category>LocalLLM</category><category>MCP Client</category><category>MCP Server</category><category>MVC5</category><category>Matlab</category><category>Matplotlib</category><category>MiFID II</category><category>MiFID II/III Updates</category><category>MiFIR</category><category>Modbus</category><category>Multi-Agent AI (CrewAI)</category><category>NAS</category><category>NUnit</category><category>Near Real-Time Reporting</category><category>NetStat</category><category>OSGi</category><category>Observability</category><category>Offline AI</category><category>OfflineCopilot</category><category>OpenAI GPT-4o</category><category>PCB</category><category>PDF</category><category>PDFSearchTool</category><category>PFOF Ban &amp; Consolidated Tape</category><category>PHP</category><category>PPL</category><category>PaaS</category><category>Pandas Agent</category><category>Pathfinding</category><category>Peaberry</category><category>Penjili</category><category>Perl</category><category>Powershell</category><category>Prezi</category><category>PrivacyFirstAI</category><category>Private AI</category><category>Processing</category><category>Prompt</category><category>Prompt Engineering</category><category>PushNotifications</category><category>Pydantic</category><category>Python CLI</category><category>QR2</category><category>R2build</category><category>RPC</category><category>RSS</category><category>ReAct</category><category>RegTech</category><category>Registro Windows</category><category>ReportBuilder</category><category>Restful</category><category>Retrieval Augmented Generation</category><category>SDK</category><category>SDXL</category><category>SMTP</category><category>SOAP</category><category>SP500</category><category>Safe Evaluation</category><category>Sequence Diagrams</category><category>Servlet</category><category>SignalR</category><category>Simuladores</category><category>Speech SDK</category><category>StableDiffusion</category><category>Steve Jobs</category><category>Storage</category><category>Streaming</category><category>Streaming Outputs</category><category>Subclipse</category><category>Syntax Highlighting</category><category>TBookMark</category><category>TCustomAttributes</category><category>TFN</category><category>TKinter</category><category>Task</category><category>Task-Centric AI</category><category>TensorFlow</category><category>Thiessen</category><category>Tool Calling</category><category>ToolsAndOrchestration</category><category>Trac</category><category>Tron</category><category>UML</category><category>UTF-8</category><category>VBA</category><category>VCL</category><category>VIX</category><category>VNC</category><category>VS Code</category><category>Vanguard</category><category>Visprint</category><category>Visual Studio 2019</category><category>Voronoi</category><category>WSDL</category><category>Web Service</category><category>WinRM</category><category>Windows10</category><category>Windows11Dev</category><category>Winsock</category><category>YouTube summarization</category><category>Zaluum</category><category>ai‑workflow</category><category>algebra</category><category>analysis</category><category>analysis suite</category><category>arcade</category><category>architecture</category><category>art</category><category>asset allocation</category><category>automation</category><category>budgeting</category><category>bugs</category><category>charts</category><category>chunked summarization</category><category>classification</category><category>cloning studio</category><category>code rush</category><category>command</category><category>compliance</category><category>computing</category><category>continuous batching</category><category>copyright</category><category>correlation</category><category>csharp</category><category>dev‑environment</category><category>diagram</category><category>diff</category><category>divergenge</category><category>dotCover</category><category>dynamic portfolio</category><category>dynamic quantization</category><category>eReader</category><category>ebook</category><category>email signature</category><category>exporter</category><category>fictional personas</category><category>film</category><category>fireworks</category><category>fixed chain</category><category>fun</category><category>function calling</category><category>fundamental rights</category><category>funds</category><category>gemma 4</category><category>git</category><category>greeks</category><category>group chat</category><category>heroku</category><category>index</category><category>inheritance</category><category>instruments</category><category>investment advisor</category><category>investment strategies</category><category>jqplot</category><category>large files</category><category>latency reduction</category><category>layer</category><category>library</category><category>local voice</category><category>logger</category><category>lua</category><category>macroeconomics</category><category>map-reduce</category><category>market analysis</category><category>matrix</category><category>mean-variance</category><category>memory reduction</category><category>mobile</category><category>mobile device</category><category>mouse</category><category>multi-agent</category><category>network</category><category>ocr</category><category>openclaw</category><category>options</category><category>orchestrator-worker pattern</category><category>overlap</category><category>owasp</category><category>parallel</category><category>password</category><category>performance</category><category>performance optimization</category><category>portfolio</category><category>private repository</category><category>protobuf</category><category>pygame</category><category>quant developer</category><category>quantization</category><category>qwen3</category><category>recommendation</category><category>recursive agent</category><category>reference library</category><category>reflection pattern</category><category>remoting</category><category>repositories</category><category>responsive</category><category>risk grades</category><category>risk tolerance</category><category>scripting</category><category>self-hosting</category><category>serilog</category><category>simulation</category><category>simulator</category><category>social networks</category><category>spectre.console</category><category>straddle</category><category>supercomputer</category><category>tesseract</category><category>testing</category><category>threading</category><category>throughput improvement</category><category>tutor</category><category>tutorial</category><category>twitter</category><category>vibe coding</category><category>visualization</category><category>vol</category><category>web-scraping</category><category>weight‑only quantization</category><category>whatsapp</category><category>windows service</category><category>wsl2</category><category>youtube-transcript-api</category><category>yt-dlp</category><title>Random thoughts on coding and technology</title><description>I’m Jordi Corbilla, a Senior Full-Stack &amp;amp; Quantitative Engineer working across trading, compliance, risk, and AI systems. This blog is where I write about practical engineering: Python, .NET, quantitative tooling, local/hybrid LLM workflows, and the systems thinking behind real-world software.</description><link>https://thundaxsoftware.blogspot.com/</link><managingEditor>noreply@blogger.com (Jordi Corbilla)</managingEditor><generator>Blogger</generator><openSearch:totalResults>552</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-2220041510035337352</guid><pubDate>Sun, 19 Apr 2026 06:33:00 +0000</pubDate><atom:updated>2026-04-19T06:50:24.674+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">cloning studio</category><category domain="http://www.blogger.com/atom/ns#">local voice</category><category domain="http://www.blogger.com/atom/ns#">Python</category><category domain="http://www.blogger.com/atom/ns#">qwen3</category><title>Building a Local Voice Cloning Studio That Actually Runs on My Machine</title><description>&lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEg4rV9Sh3XmT5Cvdese1wY7GATOy0mXbzga6pP7WIXMfOrCILNT5jUWw1neCTsTqzpm_aGMJaOLX991wla6VmZtRu3UOcl_ri2h-7blx245iQxymuazIIpQARp0ymLuAytf5jUoCnlKw-kJ6gTxvLhUX-dfwdcozVJDW8YC6QosFyhd9DZczeKTniQNcNQ&quot; style=&quot;clear: left; display: inline; float: left; font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1024&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEg4rV9Sh3XmT5Cvdese1wY7GATOy0mXbzga6pP7WIXMfOrCILNT5jUWw1neCTsTqzpm_aGMJaOLX991wla6VmZtRu3UOcl_ri2h-7blx245iQxymuazIIpQARp0ymLuAytf5jUoCnlKw-kJ6gTxvLhUX-dfwdcozVJDW8YC6QosFyhd9DZczeKTniQNcNQ=w320-h320&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;2&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;I wanted a local voice cloning workflow that felt like a real product, not a notebook demo.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;4&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The goal was simple: record or upload a short reference clip, create a reusable voice profile, type arbitrary text, synthesize speech locally, and keep every file on my own machine. No accounts, no cloud voice API, no remote inference.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;6&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The result is&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;&lt;span style=&quot;color: #7f6000;&quot;&gt;&lt;a href=&quot;https://github.com/JordiCorbilla/local-voice-studio&quot;&gt;local-voice-studio&lt;/a&gt;&lt;/span&gt;&lt;/code&gt;: a React and FastAPI application with local profile management, reference audio handling, generation history, diagnostics, and pluggable local TTS engines.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;8&quot; dir=&quot;auto&quot; id=&quot;the-product-shape&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;The Product Shape&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;10&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The app is built around a few core workflows.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;12&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;You can create multiple voice profiles, each with notes, language preference, tags, saved reference clips, a primary clip, and synthesis defaults. Reference clips can come from browser microphone recording or from uploaded audio files. The backend normalizes every clip to a model-friendly WAV file, stores metadata in SQLite, and keeps the original upload on disk.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;14&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The generation page is a focused workspace: choose a profile, enter text, optionally provide delivery instructions, tweak advanced settings, generate audio, and then preview or download the WAV result. Every generation is stored in history with status, parameters, input text, output path, and errors if something fails.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;16&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The settings page shows the local runtime state: active engine, model name, GPU detection, FFmpeg availability, directories in use, and whether local transcription is available.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;18&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Everything lives under the local&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;&lt;span style=&quot;color: #7f6000;&quot;&gt;data/&lt;/span&gt;&lt;/code&gt;&amp;nbsp;directory.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;18&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiKONn2eGMehvA4CkbVPbvEOvvfKcN93yvObODdRKzcrpvL8A_W--Z131OaXLBYqDWST37GlAht5YfiW3W6pS8ctqcS8IVO_el22OlnMZxSphewtZCPDHyjN87AyOM_c1CGdxWOrtOTbb5f645WjFOYO00HRb6EAXk-ctFwtFdV7U5YXoLj-Bu2SoThTjk&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;815&quot; data-original-width=&quot;1000&quot; height=&quot;523&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiKONn2eGMehvA4CkbVPbvEOvvfKcN93yvObODdRKzcrpvL8A_W--Z131OaXLBYqDWST37GlAht5YfiW3W6pS8ctqcS8IVO_el22OlnMZxSphewtZCPDHyjN87AyOM_c1CGdxWOrtOTbb5f645WjFOYO00HRb6EAXk-ctFwtFdV7U5YXoLj-Bu2SoThTjk=w640-h523&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjICaGAWghimtwrtdRuyjd4UVAvzCbja-zGou9nfEKQb-lBjrZ7NNBY-3hGr47qnd0crjm36xklVhClP2OsoHxfx5qjT9ygBko09jSjWXOiFD6tpei-Im3un6iNwZQiAc0f5HAeYOcvEPfsQkC1ORFT3hSY02UFcuy5sIfRFeJGvJZzl6TLO4plAOw8P5Y&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;818&quot; data-original-width=&quot;1003&quot; height=&quot;522&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjICaGAWghimtwrtdRuyjd4UVAvzCbja-zGou9nfEKQb-lBjrZ7NNBY-3hGr47qnd0crjm36xklVhClP2OsoHxfx5qjT9ygBko09jSjWXOiFD6tpei-Im3un6iNwZQiAc0f5HAeYOcvEPfsQkC1ORFT3hSY02UFcuy5sIfRFeJGvJZzl6TLO4plAOw8P5Y=w640-h522&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;20&quot; dir=&quot;auto&quot; id=&quot;architecture&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Architecture&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;22&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The backend is FastAPI with a deliberately boring service/repository split.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;24&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Routes handle HTTP only. Services coordinate profile management, audio processing, generation jobs, runtime diagnostics, and TTS orchestration. Repositories isolate SQLite persistence. Audio utilities handle normalization and metadata probing through FFmpeg and FFprobe.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;26&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The TTS runtime sits behind a&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;TtsEngine&lt;/code&gt;&amp;nbsp;interface. The generation service does not care whether the active engine is XTTS or Qwen3. It prepares a profile, builds a synthesis payload, writes the output to&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;data/generated&lt;/code&gt;, and records status updates in SQLite.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;28&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Synthesis runs as an in-process background job. That is enough for a local desktop-style app and avoids adding Redis, Celery, or other distributed infrastructure too early.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;30&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The frontend is React, TypeScript, Vite, React Router, and TanStack Query. The UI is desktop-first, dark, and intentionally simple: left navigation, profile cards, reference clip panels, a generation workspace, history, and diagnostics.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;32&quot; dir=&quot;auto&quot; id=&quot;the-xtts-lesson&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;The XTTS Lesson&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;34&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The first implementation used Coqui XTTS v2.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;36&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;XTTS was a useful baseline because it can do zero-shot voice cloning from reference audio and it is relatively well-known in local TTS experiments. The app normalized clips, cached conditioning artifacts when possible, and used the selected primary clip as the conditioning source.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;38&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;But in practice, the output did not sound close enough. The voice often drifted into a generic speaker. In one test, the result sounded like a random English man rather than the target voice.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;40&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That was an important product lesson: a working local model integration is not the same thing as a good cloning experience.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;42&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The app was technically generating speech, but the speaker identity was not good enough.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;44&quot; dir=&quot;auto&quot; id=&quot;why-transcript-aware-cloning-helped&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Why Transcript-Aware Cloning Helped&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;46&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The breakthrough was moving from audio-only cloning to transcript-aware cloning.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;48&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Qwen3-TTS expects the reference audio and the exact text spoken in that reference audio. That small workflow change matters. Instead of asking the model to infer everything from the waveform alone, the app gives it aligned acoustic and linguistic context.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;50&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;So the profile workflow changed:&lt;/p&gt;&lt;ol class=&quot;code-line&quot; data-line=&quot;52&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;52&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Record or upload a clean reference clip.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;53&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Mark the best clip as primary.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;54&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Save the exact transcript of that clip.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;55&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Generate using Qwen3-TTS.&lt;/li&gt;&lt;/ol&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;57&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The app now stores&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;reference_text&lt;/code&gt;&amp;nbsp;on each clip. The primary clip transcript becomes part of the conditioning fingerprint, so changing the transcript invalidates old cached prompts. There is also an optional local transcription path using a Whisper-style local ASR dependency, but manual transcript entry is always available and often better if the user knows exactly what they said.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;59&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This made the system feel much closer to the Voicebox-style workflow that worked well in practice.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;61&quot; dir=&quot;auto&quot; id=&quot;ux-details-that-matter&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;UX Details That Matter&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;63&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Local AI workflows often have long, silent waits. That is bad UX.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;65&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The app now shows loading feedback where it matters:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;67&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;67&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;Transcribe locally&lt;/code&gt;&amp;nbsp;button shows a spinner while transcription is running.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;68&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The transcription button is disabled while the action is in progress, preventing accidental duplicate requests.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;69&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;Generate audio&lt;/code&gt;&amp;nbsp;button shows a spinner while the job is starting or running.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;70&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The generate page warns if the selected primary clip is missing its transcript.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;72&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;These are small details, but they make the app feel more trustworthy. When local models take time to load, users need visible progress.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;74&quot; dir=&quot;auto&quot; id=&quot;running-it-locally&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Running It Locally&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;76&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The recommended path is Qwen3-TTS.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;78&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;From the repository root:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; color: #d4d4d4; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; white-space: pre-wrap;&quot;&gt;&lt;code class=&quot;code-line language-powershell&quot; data-line=&quot;80&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;powershell &lt;span class=&quot;hljs-literal&quot; style=&quot;color: #569cd6;&quot;&gt;-ExecutionPolicy&lt;/span&gt; Bypass &lt;span class=&quot;hljs-operator&quot;&gt;-File&lt;/span&gt; .\scripts\setup.ps1
&amp;amp; .\.venv\Scripts\python.exe &lt;span class=&quot;hljs-literal&quot; style=&quot;color: #569cd6;&quot;&gt;-m&lt;/span&gt; pip install &lt;span class=&quot;hljs-literal&quot; style=&quot;color: #569cd6;&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;hljs-string&quot; style=&quot;color: #d69d85;&quot;&gt;&quot;.\backend[qwen,transcription]&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;85&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Start the backend:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; color: #d4d4d4; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; white-space: pre-wrap;&quot;&gt;&lt;code class=&quot;code-line language-powershell&quot; data-line=&quot;87&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;&lt;span class=&quot;hljs-variable&quot; style=&quot;color: #bd63c5;&quot;&gt;$env:LVS_TTS_ENGINE&lt;/span&gt; = &lt;span class=&quot;hljs-string&quot; style=&quot;color: #d69d85;&quot;&gt;&quot;qwen3&quot;&lt;/span&gt;
powershell &lt;span class=&quot;hljs-literal&quot; style=&quot;color: #569cd6;&quot;&gt;-ExecutionPolicy&lt;/span&gt; Bypass &lt;span class=&quot;hljs-operator&quot;&gt;-File&lt;/span&gt; .\scripts\dev&lt;span class=&quot;hljs-literal&quot; style=&quot;color: #569cd6;&quot;&gt;-backend&lt;/span&gt;.ps1
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;92&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Start the frontend:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; color: #d4d4d4; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; white-space: pre-wrap;&quot;&gt;&lt;code class=&quot;code-line language-powershell&quot; data-line=&quot;94&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;powershell &lt;span class=&quot;hljs-literal&quot; style=&quot;color: #569cd6;&quot;&gt;-ExecutionPolicy&lt;/span&gt; Bypass &lt;span class=&quot;hljs-operator&quot;&gt;-File&lt;/span&gt; .\scripts\dev&lt;span class=&quot;hljs-literal&quot; style=&quot;color: #569cd6;&quot;&gt;-frontend&lt;/span&gt;.ps1
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;98&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Then open:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; color: #d4d4d4; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; white-space: pre-wrap;&quot;&gt;&lt;code class=&quot;code-line language-text&quot; data-line=&quot;100&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;http://localhost:5173
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;104&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If another local app is already using port&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;8000&lt;/code&gt;, run the backend on&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;8010&lt;/code&gt;&amp;nbsp;and point the frontend proxy at it:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; color: #d4d4d4; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; white-space: pre-wrap;&quot;&gt;&lt;code class=&quot;code-line language-powershell&quot; data-line=&quot;106&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;&lt;span class=&quot;hljs-variable&quot; style=&quot;color: #bd63c5;&quot;&gt;$env:LVS_TTS_ENGINE&lt;/span&gt; = &lt;span class=&quot;hljs-string&quot; style=&quot;color: #d69d85;&quot;&gt;&quot;qwen3&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-variable&quot; style=&quot;color: #bd63c5;&quot;&gt;$env:LVS_BACKEND_PORT&lt;/span&gt; = &lt;span class=&quot;hljs-string&quot; style=&quot;color: #d69d85;&quot;&gt;&quot;8010&quot;&lt;/span&gt;
powershell &lt;span class=&quot;hljs-literal&quot; style=&quot;color: #569cd6;&quot;&gt;-ExecutionPolicy&lt;/span&gt; Bypass &lt;span class=&quot;hljs-operator&quot;&gt;-File&lt;/span&gt; .\scripts\dev&lt;span class=&quot;hljs-literal&quot; style=&quot;color: #569cd6;&quot;&gt;-backend&lt;/span&gt;.ps1
&lt;/code&gt;&lt;/pre&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; color: #d4d4d4; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; white-space: pre-wrap;&quot;&gt;&lt;code class=&quot;code-line language-powershell&quot; data-line=&quot;112&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;&lt;span class=&quot;hljs-variable&quot; style=&quot;color: #bd63c5;&quot;&gt;$env:VITE_API_TARGET&lt;/span&gt; = &lt;span class=&quot;hljs-string&quot; style=&quot;color: #d69d85;&quot;&gt;&quot;http://127.0.0.1:8010&quot;&lt;/span&gt;
powershell &lt;span class=&quot;hljs-literal&quot; style=&quot;color: #569cd6;&quot;&gt;-ExecutionPolicy&lt;/span&gt; Bypass &lt;span class=&quot;hljs-operator&quot;&gt;-File&lt;/span&gt; .\scripts\dev&lt;span class=&quot;hljs-literal&quot; style=&quot;color: #569cd6;&quot;&gt;-frontend&lt;/span&gt;.ps1
&lt;/code&gt;&lt;/pre&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;117&quot; dir=&quot;auto&quot; id=&quot;what-i-would-improve-next&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;What I Would Improve Next&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;119&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The next improvements are mostly product polish and runtime hardening.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;121&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The app should make the active engine more visible in the main UI. Profiles should get a clear &quot;Qwen-ready&quot; badge when their primary clip has a transcript. The transcription workflow could show progress beyond a spinner, especially on first model load. The settings page could provide stronger guidance when the environment is missing Qwen, Whisper, CUDA, FFmpeg, or model weights.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;123&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Long-form generation also needs more work. The app currently supports normal generation jobs, but paragraph-aware chunking, concatenation, and better history grouping would make it more useful for narration.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;125&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Finally, Qwen and XTTS have different Python/runtime expectations. A future version could support separate engine environments or a small process boundary so users do not have to fit every model into one Python virtual environment.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;127&quot; dir=&quot;auto&quot; id=&quot;takeaway&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Takeaway&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;129&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The biggest lesson was that local-first voice cloning is not just about wiring up a model.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;131&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The workflow matters.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;133&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;For this project, XTTS proved the architecture and local storage model. Qwen3-TTS made the voice identity closer by adding the missing reference transcript. The resulting app is still early, but it now has the shape of a real local voice studio: profiles, clips, transcripts, generation history, runtime diagnostics, and no cloud dependency.&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2026/04/building-local-voice-cloning-studio.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEg4rV9Sh3XmT5Cvdese1wY7GATOy0mXbzga6pP7WIXMfOrCILNT5jUWw1neCTsTqzpm_aGMJaOLX991wla6VmZtRu3UOcl_ri2h-7blx245iQxymuazIIpQARp0ymLuAytf5jUoCnlKw-kJ6gTxvLhUX-dfwdcozVJDW8YC6QosFyhd9DZczeKTniQNcNQ=s72-w320-h320-c" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-5149034108934542377</guid><pubDate>Mon, 13 Apr 2026 20:13:00 +0000</pubDate><atom:updated>2026-04-13T20:13:54.082+00:00</atom:updated><title>Beyond Prompts: Building a Codex Playbook for Real Application Development</title><description>&lt;p&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAUiFHHutCTN7kbhV5NxEhy5RFrz2jPL7g6veYGIeZYMYmmAuJFD5CN2JDSPWTJoFEqw7AtEl2Dz0NcfPdLbDWs8WUqg8wDyhbOXG0GlswtXRqbovFPIP7NtpJ2-Hm_AciRP7hosEvG5tm-J0b_VDi-XQhoIMzfmW7OzA7dHUu3peBWYFlepLPms-LbZY/s1536/ChatGPT%20Image%20Apr%2013,%202026,%2009_11_54%20PM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1536&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAUiFHHutCTN7kbhV5NxEhy5RFrz2jPL7g6veYGIeZYMYmmAuJFD5CN2JDSPWTJoFEqw7AtEl2Dz0NcfPdLbDWs8WUqg8wDyhbOXG0GlswtXRqbovFPIP7NtpJ2-Hm_AciRP7hosEvG5tm-J0b_VDi-XQhoIMzfmW7OzA7dHUu3peBWYFlepLPms-LbZY/s320/ChatGPT%20Image%20Apr%2013,%202026,%2009_11_54%20PM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;One-off prompting is weak engineering infrastructure.&lt;p&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;4&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;It can be useful for getting unstuck, drafting code, or exploring ideas, but it does not produce repeatable engineering behavior on its own. The same prompt that works acceptably on one backend feature often degrades into generic advice or inconsistent implementation on the next one. That happens because a prompt is usually trying to do too many jobs at once: repository policy, architectural philosophy, feature workflow, review heuristics, and implementation detail.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;6&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This repository was built to separate those concerns and make them durable.&amp;nbsp;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;6&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;a href=&quot;https://github.com/JordiCorbilla/codex-engineering-playbook&quot;&gt;JordiCorbilla/codex-engineering-playbook&lt;/a&gt;&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;8&quot; dir=&quot;auto&quot; id=&quot;the-core-problem&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;The Core Problem&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;10&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Most teams experimenting with coding agents start with giant instruction blobs. They pour style preferences, architecture opinions, code review heuristics, framework conventions, and edge-case warnings into a single file or prompt. That usually fails in predictable ways:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;12&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;12&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;durable guidance gets buried under temporary workflow detail&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;13&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;the instructions become too large to scan and too vague to route reliably&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;14&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;project structure becomes secondary to the agent&#39;s last prompt&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;15&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;implementation becomes inconsistent across languages and application layers&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;17&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The problem is not that the model needs more words. The problem is that the repository needs a better operating model.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;19&quot; dir=&quot;auto&quot; id=&quot;what-this-repository-builds-instead&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;What This Repository Builds Instead&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;21&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This playbook uses four layers:&lt;/p&gt;&lt;ol class=&quot;code-line&quot; data-line=&quot;23&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;23&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;AGENTS.md&lt;/code&gt;&amp;nbsp;for durable repository-wide rules&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;24&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;.agents/skills&lt;/code&gt;&amp;nbsp;for reusable workflows&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;25&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;templates&lt;/code&gt;&amp;nbsp;and&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;scripts&lt;/code&gt;&amp;nbsp;for deterministic implementation support&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;26&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;examples&lt;/code&gt;&amp;nbsp;for concrete, inspectable reference projects&lt;/li&gt;&lt;/ol&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;28&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That split is the point of the repository.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;30&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The top-level&amp;nbsp;&lt;a data-href=&quot;../AGENTS.md&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/AGENTS.md&quot; style=&quot;text-decoration-line: none;&quot;&gt;AGENTS.md&lt;/a&gt;&amp;nbsp;stays small. It says what the repository is for, when to prefer skills, what &quot;done&quot; means, and what kinds of validation are expected. It does not try to teach every implementation pattern directly. That restraint is important. Durable instructions should be hard to invalidate.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;32&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The detailed workflow logic moves into focused skills such as:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;34&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;34&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;a data-href=&quot;../.agents/skills/csharp-backend-feature/SKILL.md&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/.agents/skills/csharp-backend-feature/SKILL.md&quot; style=&quot;text-decoration-line: none;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;csharp-backend-feature&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;35&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;a data-href=&quot;../.agents/skills/python-backend-review/SKILL.md&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/.agents/skills/python-backend-review/SKILL.md&quot; style=&quot;text-decoration-line: none;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;python-backend-review&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;36&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;a data-href=&quot;../.agents/skills/react-typescript-feature/SKILL.md&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/.agents/skills/react-typescript-feature/SKILL.md&quot; style=&quot;text-decoration-line: none;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;react-typescript-feature&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;37&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;a data-href=&quot;../.agents/skills/api-contract-review/SKILL.md&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/.agents/skills/api-contract-review/SKILL.md&quot; style=&quot;text-decoration-line: none;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;api-contract-review&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;38&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;a data-href=&quot;../.agents/skills/layered-architecture-recon/SKILL.md&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/.agents/skills/layered-architecture-recon/SKILL.md&quot; style=&quot;text-decoration-line: none;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;layered-architecture-recon&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;40&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This is better than a giant style blob because routing matters. Reviewing a Python backend diff is not the same task as implementing a React feature or mapping an unfamiliar layered architecture. Putting those workflows into separate skills makes the instructions sharper and more reusable.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;42&quot; dir=&quot;auto&quot; id=&quot;why-durable-repo-instructions-matter&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Why Durable Repo Instructions Matter&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;44&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Agent behavior improves when the repository teaches stable defaults instead of relying on the operator to restate them every time.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;46&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;In this repository, the durable defaults are simple:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;48&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;48&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;inspect existing structure first&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;49&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;keep boundaries clear&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;50&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;put logic in the right layer&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;51&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;validate the touched area before calling the work done&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;52&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;explain trade-offs when architecture changes&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;54&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Those rules are broad enough to matter across C#, Python, and React, but narrow enough to remain useful. They establish an operating model without pretending the languages are interchangeable.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;56&quot; dir=&quot;auto&quot; id=&quot;why-skills-beat-giant-prompt-files&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Why Skills Beat Giant Prompt Files&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;58&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;A single giant prompt tends to flatten all engineering work into &quot;follow best practices.&quot; That is almost always too soft. Real engineering work has modes, and the quality bar changes with the mode.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;60&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Feature work needs placement discipline. Review work needs risk detection. Contract review needs attention to caller-visible behavior. Architecture reconnaissance needs an accurate map, not rewrite advice.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;62&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That is why the skills in this repository are intentionally separate:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;64&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;64&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;csharp-backend-feature&lt;/code&gt;&amp;nbsp;cares about thin controllers, service-led behavior, async correctness, DTO boundaries, and targeted tests.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;65&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;csharp-backend-review&lt;/code&gt;&amp;nbsp;cares about fat controllers, sync-over-async mistakes, leaky entity boundaries, and weak exception flows.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;66&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;python-backend-feature&lt;/code&gt;&amp;nbsp;emphasizes typed boundaries, thin request handling, explicit services, and restrained abstraction.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;67&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;frontend-ux-polish&lt;/code&gt;&amp;nbsp;is about hierarchy, spacing, labels, state clarity, and affordances, not broad frontend architecture.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;69&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Separating those workflows does two things. It makes routing more reliable, and it avoids making every task carry the weight of every other task&#39;s instructions.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;71&quot; dir=&quot;auto&quot; id=&quot;the-c-guidance&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;The C# Guidance&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;73&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The C# portion of the playbook favors a layered ASP.NET Core style, but it is not blindly enterprise. The guidance in&amp;nbsp;&lt;a data-href=&quot;../conventions/csharp.md&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/conventions/csharp.md&quot; style=&quot;text-decoration-line: none;&quot;&gt;conventions/csharp.md&lt;/a&gt;&amp;nbsp;pushes toward controllers, services, and repositories when those boundaries provide real value. It also says something many style guides avoid saying clearly: repository abstractions can become ceremony.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;75&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That trade-off matters. In the&amp;nbsp;&lt;a data-href=&quot;../examples/csharp-api&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/examples/csharp-api&quot; style=&quot;text-decoration-line: none;&quot;&gt;C# example app&lt;/a&gt;, the repository boundary exists because it helps keep persistence concerns separate and makes the service easy to test. But it stays intentionally small and in-memory. The goal is to demonstrate responsibility boundaries and async flow, not to perform ORM theater.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;77&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The example includes:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;79&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;79&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;&lt;a data-href=&quot;../examples/csharp-api/src/CodexEngineeringPlaybook.CSharpApi/Controllers/OrdersController.cs&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/examples/csharp-api/src/CodexEngineeringPlaybook.CSharpApi/Controllers/OrdersController.cs&quot; style=&quot;text-decoration-line: none;&quot;&gt;OrdersController.cs&lt;/a&gt;&amp;nbsp;for HTTP concerns&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;80&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;&lt;a data-href=&quot;../examples/csharp-api/src/CodexEngineeringPlaybook.CSharpApi/Services/OrderService.cs&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/examples/csharp-api/src/CodexEngineeringPlaybook.CSharpApi/Services/OrderService.cs&quot; style=&quot;text-decoration-line: none;&quot;&gt;OrderService.cs&lt;/a&gt;&amp;nbsp;for validation, orchestration, and business flow&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;81&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;&lt;a data-href=&quot;../examples/csharp-api/src/CodexEngineeringPlaybook.CSharpApi/Repositories/InMemoryOrderRepository.cs&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/examples/csharp-api/src/CodexEngineeringPlaybook.CSharpApi/Repositories/InMemoryOrderRepository.cs&quot; style=&quot;text-decoration-line: none;&quot;&gt;InMemoryOrderRepository.cs&lt;/a&gt;&amp;nbsp;for isolated storage behavior&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;82&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;&lt;a data-href=&quot;../examples/csharp-api/src/CodexEngineeringPlaybook.CSharpApi/Middleware/ExceptionMappingMiddleware.cs&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/examples/csharp-api/src/CodexEngineeringPlaybook.CSharpApi/Middleware/ExceptionMappingMiddleware.cs&quot; style=&quot;text-decoration-line: none;&quot;&gt;ExceptionMappingMiddleware.cs&lt;/a&gt;&amp;nbsp;for centralized error translation&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;84&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That is a good example of the repository&#39;s overall stance: use structure to clarify responsibilities, not to multiply files for their own sake.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;86&quot; dir=&quot;auto&quot; id=&quot;the-python-guidance&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;The Python Guidance&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;88&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Python code often swings between two bad extremes when teams talk about architecture. One extreme is route handlers full of business logic and IO. The other is an imported enterprise pattern that creates classes, abstract base types, and indirection that the codebase has not earned.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;90&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The Python guidance in&amp;nbsp;&lt;a data-href=&quot;../conventions/python.md&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/conventions/python.md&quot; style=&quot;text-decoration-line: none;&quot;&gt;conventions/python.md&lt;/a&gt;&amp;nbsp;tries to avoid both.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;92&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;It emphasizes:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;94&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;94&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;thin request handling&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;95&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;typed boundaries&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;96&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;explicit validation&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;97&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;clear service or module ownership&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;98&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;simplicity over cleverness&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;100&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;In the&amp;nbsp;&lt;a data-href=&quot;../examples/python-api&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/examples/python-api&quot; style=&quot;text-decoration-line: none;&quot;&gt;Python example app&lt;/a&gt;, the route module translates HTTP concerns, the service owns the business path, and the data-access layer stores records in memory. The example is intentionally small, but the boundaries are inspectable:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;102&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;102&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;&lt;a data-href=&quot;../examples/python-api/app/routes/orders.py&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/examples/python-api/app/routes/orders.py&quot; style=&quot;text-decoration-line: none;&quot;&gt;orders.py&lt;/a&gt;&amp;nbsp;keeps request handling thin&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;103&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;&lt;a data-href=&quot;../examples/python-api/app/services/orders.py&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/examples/python-api/app/services/orders.py&quot; style=&quot;text-decoration-line: none;&quot;&gt;orders.py&lt;/a&gt;&amp;nbsp;owns the business behavior&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;104&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;&lt;a data-href=&quot;../examples/python-api/app/data/orders.py&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/examples/python-api/app/data/orders.py&quot; style=&quot;text-decoration-line: none;&quot;&gt;orders.py&lt;/a&gt;&amp;nbsp;isolates storage concerns&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;105&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;&lt;a data-href=&quot;../examples/python-api/app/schemas/orders.py&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/examples/python-api/app/schemas/orders.py&quot; style=&quot;text-decoration-line: none;&quot;&gt;orders.py&lt;/a&gt;&amp;nbsp;defines typed request and response models&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;107&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That structure is useful because it makes future changes cheaper. A new validation rule or storage implementation has an obvious home.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;109&quot; dir=&quot;auto&quot; id=&quot;the-react--typescript-guidance&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;The React + TypeScript Guidance&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;111&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Frontend guidance is often too aesthetic or too abstract. This playbook is trying to be engineering guidance, so the React conventions focus on code structure, state handling, and contract discipline.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;113&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The priorities in&amp;nbsp;&lt;a data-href=&quot;../conventions/react-typescript.md&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/conventions/react-typescript.md&quot; style=&quot;text-decoration-line: none;&quot;&gt;conventions/react-typescript.md&lt;/a&gt;&amp;nbsp;are:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;115&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;115&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;keep components focused&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;116&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;separate API access from rendering&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;117&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;make loading, error, and empty states explicit&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;118&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;type props and async data honestly&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;119&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;keep hooks disciplined&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;120&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;avoid business logic hidden in JSX&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;122&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The&amp;nbsp;&lt;a data-href=&quot;../examples/react-app&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/examples/react-app&quot; style=&quot;text-decoration-line: none;&quot;&gt;React example app&lt;/a&gt;&amp;nbsp;is not a design system demo. It is a small application slice showing the separation between transport, state, and presentation:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;124&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;124&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;&lt;a data-href=&quot;../examples/react-app/src/api/orders.ts&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/examples/react-app/src/api/orders.ts&quot; style=&quot;text-decoration-line: none;&quot;&gt;orders.ts&lt;/a&gt;&amp;nbsp;owns data access behavior&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;125&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;&lt;a data-href=&quot;../examples/react-app/src/hooks/useOrders.ts&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/examples/react-app/src/hooks/useOrders.ts&quot; style=&quot;text-decoration-line: none;&quot;&gt;useOrders.ts&lt;/a&gt;&amp;nbsp;models async state transitions&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;126&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;&lt;a data-href=&quot;../examples/react-app/src/components/OrderSummaryList.tsx&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/examples/react-app/src/components/OrderSummaryList.tsx&quot; style=&quot;text-decoration-line: none;&quot;&gt;OrderSummaryList.tsx&lt;/a&gt;&amp;nbsp;renders typed data&lt;/span&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;127&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;&lt;a data-href=&quot;../examples/react-app/src/App.tsx&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/examples/react-app/src/App.tsx&quot; style=&quot;text-decoration-line: none;&quot;&gt;App.tsx&lt;/a&gt;&amp;nbsp;composes the UI and makes loading, empty, and error states visible&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;129&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That is deliberate. Many generated React examples hide the interesting part of the problem inside a single file. This one does not.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;131&quot; dir=&quot;auto&quot; id=&quot;why-the-examples-matter&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Why The Examples Matter&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;133&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Example apps are not filler in a repository like this. They are operational anchors.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;135&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Without examples, convention documents drift toward abstract advice. Skills become more likely to overfit to generic framework knowledge. Templates become harder to evaluate because there is no concrete reference point for what &quot;good&quot; looks like in the repository.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;137&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The examples in this playbook make the guidance testable. They show the relationship between the documents and actual code. They also give Codex stable insertion points when exploring an unfamiliar but similarly structured codebase.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;139&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The practical run instructions live in the repository&amp;nbsp;&lt;a data-href=&quot;../README.md&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/README.md&quot; style=&quot;text-decoration-line: none;&quot;&gt;README&lt;/a&gt;. That split is intentional: the blog explains the design, while the README acts as the operational starting point.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;141&quot; dir=&quot;auto&quot; id=&quot;scripts-and-templates-as-engineering-tools&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Scripts And Templates As Engineering Tools&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;143&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Another failure mode in agent-oriented repositories is over-explaining deterministic work in prose. If a C# controller shape is predictable, a template is better than another paragraph. If you need to discover likely insertion points or summarize changed files, a small script is usually better than asking the model to infer everything from scratch every time.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;145&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That is why this repository includes:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;147&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;147&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;a data-href=&quot;../templates/csharp&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/templates/csharp&quot; style=&quot;text-decoration-line: none;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;templates/csharp&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;148&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;a data-href=&quot;../templates/python&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/templates/python&quot; style=&quot;text-decoration-line: none;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;templates/python&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;149&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;a data-href=&quot;../templates/react&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/templates/react&quot; style=&quot;text-decoration-line: none;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;templates/react&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;150&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;a data-href=&quot;../scripts/scan_repo.py&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/scripts/scan_repo.py&quot; style=&quot;text-decoration-line: none;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;scripts/scan_repo.py&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;151&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;a data-href=&quot;../scripts/find_feature_insertion_points.py&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/scripts/find_feature_insertion_points.py&quot; style=&quot;text-decoration-line: none;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;scripts/find_feature_insertion_points.py&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;152&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;a data-href=&quot;../scripts/discover_tests.py&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/scripts/discover_tests.py&quot; style=&quot;text-decoration-line: none;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;scripts/discover_tests.py&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;153&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;a data-href=&quot;../scripts/summarize_changed_files.py&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/scripts/summarize_changed_files.py&quot; style=&quot;text-decoration-line: none;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;scripts/summarize_changed_files.py&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;154&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;a data-href=&quot;../scripts/validate_playbook.py&quot; href=&quot;https://file+.vscode-resource.vscode-cdn.net/c%3A/repo/codex-engineering-playbook/scripts/validate_playbook.py&quot; style=&quot;text-decoration-line: none;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;scripts/validate_playbook.py&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;156&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;These are small on purpose. They exist to support repeatability, not to introduce another platform inside the repository.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;158&quot; dir=&quot;auto&quot; id=&quot;strengths&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Strengths&lt;/h2&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;160&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;160&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The repository is structured for reuse, not just demonstration.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;161&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The top-level instructions are durable enough to survive ordinary growth.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;162&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The skills are narrow enough to route reliably.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;163&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The examples make the guidance concrete instead of rhetorical.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;164&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The validation bar is lightweight but real.&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;166&quot; dir=&quot;auto&quot; id=&quot;limitations&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Limitations&lt;/h2&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;168&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;168&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The playbook is intentionally opinionated, which means it will not fit every team culture.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;169&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The examples are small; they demonstrate boundaries, not the full complexity of production systems.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;170&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The repository does not solve framework-specific edge cases beyond the patterns it chooses to illustrate.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;171&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Skills can become stale if a team&#39;s real architecture evolves and the playbook does not.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;173&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;There is also a rigidity trade-off. If a team treats the playbook as law rather than guidance, it can become a local maximum. Not every codebase needs controller/service/repository layering. Not every React feature needs another hook. Not every Python service needs another module boundary. Good playbooks create defaults, not dogma.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;175&quot; dir=&quot;auto&quot; id=&quot;where-this-helps-most&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Where This Helps Most&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;177&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This approach helps most when:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;179&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;179&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;a team wants more repeatable coding-agent behavior&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;180&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;the codebase already values clear boundaries&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;181&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;multiple languages need a shared architectural vocabulary&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;182&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;implementation and review workflows should be routable and explicit&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;184&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;It helps less when:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;186&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;186&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;the codebase is intentionally experimental&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;187&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;architecture changes weekly&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;188&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;framework-specific constraints dominate general engineering structure&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;189&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;the team wants maximal freedom and minimal convention&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;191&quot; dir=&quot;auto&quot; id=&quot;pros-and-cons&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Pros And Cons&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;193&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Pros:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;195&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;195&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;better repeatability than one-off prompts&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;196&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;clearer separation between policy, workflow, and implementation detail&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;197&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;easier extension through focused skills&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;198&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;stronger grounding through examples and validation&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;200&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Cons:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;202&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;202&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;more repository structure to maintain&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;203&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;more upfront design work than dropping a prompt file into a repo&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;204&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;some duplication between conventions, skills, and examples is intentional and needs upkeep&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;205&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;can become too rigid if teams stop exercising judgment&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;207&quot; dir=&quot;auto&quot; id=&quot;final-view&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Final View&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;209&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The point of this repository is not to prove that Codex can follow instructions. The point is to make those instructions worth following.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;211&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That requires durable repository guidance, focused workflow skills, deterministic helpers, and concrete examples. It also requires saying no to the usual temptation to dump everything into a single mega-prompt and hope the agent will sort it out.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;213&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That temptation is understandable. It is also a weak substitute for engineering structure.&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2026/04/beyond-prompts-building-codex-playbook.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAUiFHHutCTN7kbhV5NxEhy5RFrz2jPL7g6veYGIeZYMYmmAuJFD5CN2JDSPWTJoFEqw7AtEl2Dz0NcfPdLbDWs8WUqg8wDyhbOXG0GlswtXRqbovFPIP7NtpJ2-Hm_AciRP7hosEvG5tm-J0b_VDi-XQhoIMzfmW7OzA7dHUu3peBWYFlepLPms-LbZY/s72-c/ChatGPT%20Image%20Apr%2013,%202026,%2009_11_54%20PM.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-1668364096065516364</guid><pubDate>Sun, 12 Apr 2026 18:27:00 +0000</pubDate><atom:updated>2026-04-12T18:27:03.863+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">codex</category><category domain="http://www.blogger.com/atom/ns#">layer</category><category domain="http://www.blogger.com/atom/ns#">openAI</category><category domain="http://www.blogger.com/atom/ns#">Skills</category><title>Skills for Codex: The missing layer between prompts and reliable workflows</title><description>&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgU3qPmBKKpcVqtbbLGQfIJ7pDA1SupuZcJHIdA0tenFtSAFWJaHK1jQSBYh9i_-GKSBWNhfJuA0QVgxIfwe3HxYcnMxJy4h1WyPgyzFnjYa8DxQaGw3zgRq12HjD2q3FLWrzxGgHJbg9hDIo3j0AMv_OMjPHEgD9i7KLIk793NtcEx37dscUOdQ3x84Aw/s1536/ChatGPT%20Image%20Apr%2012,%202026,%2007_23_49%20PM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1536&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgU3qPmBKKpcVqtbbLGQfIJ7pDA1SupuZcJHIdA0tenFtSAFWJaHK1jQSBYh9i_-GKSBWNhfJuA0QVgxIfwe3HxYcnMxJy4h1WyPgyzFnjYa8DxQaGw3zgRq12HjD2q3FLWrzxGgHJbg9hDIo3j0AMv_OMjPHEgD9i7KLIk793NtcEx37dscUOdQ3x84Aw/s320/ChatGPT%20Image%20Apr%2012,%202026,%2007_23_49%20PM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif;&quot;&gt;The real unlock for coding agents is packaging workflows into&amp;nbsp;&lt;/span&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;AGENTS.md&lt;/code&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif;&quot;&gt;, skills, and scripts&lt;/span&gt;&lt;/b&gt;&lt;p&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;4&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Most teams experimenting with coding agents are still operating at the wrong layer.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;6&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;They keep trying to get reliability by writing better prompts. That helps a little. It does not solve the real problem.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;8&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The hard part is not telling the model to &quot;be careful&quot; or &quot;think step by step.&quot; The hard part is turning repeated engineering workflows into artifacts the agent can reuse: repo instructions, narrowly scoped skills, and deterministic helper scripts.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;10&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That is what I built in this demo repository.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;12&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The repo is small on purpose. It contains a tiny legacy-style Python service, four concrete Codex skills, a short&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;AGENTS.md&lt;/code&gt;, several Python scripts, and lightweight tests. The point is not to show off a toy prompt library. The point is to show what a serious workflow layer looks like when you want an agent to behave more like an engineer and less like an improv partner.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;14&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;One-line takeaway:&amp;nbsp;&lt;strong&gt;prompting is too ephemeral for repeated engineering work.&lt;/strong&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;16&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Another one:&amp;nbsp;&lt;strong&gt;if a workflow matters more than once, it should probably exist as a repo artifact.&lt;/strong&gt;&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;18&quot; dir=&quot;auto&quot; id=&quot;why-prompting-alone-breaks-down&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Why prompting alone breaks down&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;20&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;One-off prompting degrades for boring reasons:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;22&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;22&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The same task gets described slightly differently every time.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;23&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The agent re-derives the workflow from scratch.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;24&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Deterministic steps get handled as prose instead of code.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;25&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The model mixes operational guidance with local reasoning and drops part of it under pressure.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;27&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Senior engineers already know this pattern from human systems. If a workflow matters, we do not rely on memory alone. We add runbooks, scripts, templates, checks, and conventions.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;29&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Coding agents need the same thing.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;31&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&quot;Please inspect the code carefully before changing anything&quot; is not a reliable mechanism. It is a wish. A skill with explicit triggers, steps, outputs, and boundaries is a mechanism.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;33&quot; dir=&quot;auto&quot; id=&quot;the-missing-layer-agentsmd--skills--scripts&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;The missing layer:&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;AGENTS.md&lt;/code&gt;&amp;nbsp;+ skills + scripts&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;35&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The repo uses three layers, each doing a different job.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;37&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;AGENTS.md&lt;/code&gt;&amp;nbsp;is the repo-level contract. It tells Codex how work should be done here:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;38&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;38&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;prefer skills for recurring workflows&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;39&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;use scripts for deterministic tasks&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;40&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;validate changes before calling them done&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;41&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;plan first when risk or ambiguity is non-trivial&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;43&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That file is intentionally short. Repo instructions are more useful when they set constraints and decision rules, not when they try to micromanage every possible action.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;45&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The skills are the workflow layer. Each one has a sharp name, obvious routing description, trigger conditions, workflow steps, expected outputs, and boundaries. That matters because routing quality is part of the problem. If skill names are vague, the agent will miss them or apply them at the wrong time.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;47&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The scripts are there for the parts that should not depend on language generation at all. Parsing test failures, scanning a repo for hotspots, and extracting risk signals from a diff are all better handled with code than with a paragraph of instructions.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;49&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;One-line takeaway:&amp;nbsp;&lt;strong&gt;natural language is a poor substitute for a parser.&lt;/strong&gt;&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;51&quot; dir=&quot;auto&quot; id=&quot;the-example-repo&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;The example repo&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;53&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The demo repo has a compact but realistic structure:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;55&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;55&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;skills/&lt;/code&gt;&amp;nbsp;contains four engineering workflows&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;56&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;scripts/&lt;/code&gt;&amp;nbsp;contains Python helpers for deterministic scanning and summarization&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;57&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;demo/legacy_service/&lt;/code&gt;&amp;nbsp;contains a deliberately awkward Python service&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;58&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;demo/artifacts/&lt;/code&gt;&amp;nbsp;contains failing test output and a risky sample diff&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;59&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;tests/&lt;/code&gt;&amp;nbsp;validates the scripts&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;61&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The service is small, but it has the right kind of mess:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;63&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;63&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;module-level in-memory storage&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;64&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;a module-level cache&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;65&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;configuration read from environment at import time&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;66&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;time-sensitive pricing logic&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;67&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;hidden coupling between service, storage, and cache code&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;69&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That is enough to make the skills meaningful. The repo is not pretending that agents only work on greenfield, well-factored code.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;71&quot; dir=&quot;auto&quot; id=&quot;how-to-get-the-repo-and-run-it&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;How to get the repo and run it&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;73&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The repository is here:&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;75&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;https://github.com/JordiCorbilla/engineering-skills-for-codex&lt;/code&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;77&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If you want the full example locally, clone it and run the lightweight validation:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-bash&quot; data-line=&quot;79&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;git &lt;span class=&quot;hljs-built_in&quot;&gt;clone&lt;/span&gt; https://github.com/JordiCorbilla/engineering-skills-for-codex.git
&lt;span class=&quot;hljs-built_in&quot;&gt;cd&lt;/span&gt; engineering-skills-for-codex
python -m unittest discover -s tests -p &lt;span class=&quot;hljs-string&quot;&gt;&quot;test_*.py&quot;&lt;/span&gt; -v
python -m unittest discover -s demo/tests -p &lt;span class=&quot;hljs-string&quot;&gt;&quot;test_*.py&quot;&lt;/span&gt; -v
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;86&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;make&lt;/code&gt;&amp;nbsp;is available in your environment, the equivalent entry points are simpler:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-bash&quot; data-line=&quot;88&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;make &lt;span class=&quot;hljs-built_in&quot;&gt;test&lt;/span&gt;
make validate
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;93&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The skills themselves live under&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;skills/&lt;/code&gt;. In practice, you use them by opening the repo in Codex and invoking the workflow by name when the task matches. For example:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;95&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;95&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;ask Codex to use&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;investigate-before-patching&lt;/code&gt;&amp;nbsp;before editing&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;demo/legacy_service/services/order_service.py&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;96&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;ask Codex to use&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;summarize-test-failures&lt;/code&gt;&amp;nbsp;against&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;demo/artifacts/pytest_failure_output.txt&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;97&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;ask Codex to use&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;legacy-codebase-recon&lt;/code&gt;&amp;nbsp;on&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;demo/legacy_service&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;98&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;ask Codex to use&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;review-change-safely&lt;/code&gt;&amp;nbsp;on&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;demo/artifacts/sample_change.diff&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;100&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The deterministic parts can also be run directly without the skill wrapper:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-bash&quot; data-line=&quot;102&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;python scripts/inspect_patch_context.py --root demo --target demo/legacy_service/services/order_service.py
python scripts/summarize_test_failures.py --input demo/artifacts/pytest_failure_output.txt
python scripts/legacy_recon.py --root demo/legacy_service
python scripts/diff_review_summary.py --input demo/artifacts/sample_change.diff
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;109&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If you want to reuse the skills in another repository, the practical starting point is to copy the relevant folder from&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;skills/&lt;/code&gt;, bring over any supporting script from&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;scripts/&lt;/code&gt;, and adapt&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;AGENTS.md&lt;/code&gt;&amp;nbsp;so the repo-level instructions match the codebase you actually have. Treat these skills as templates for repeatable workflows, not drop-in magic.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;111&quot; dir=&quot;auto&quot; id=&quot;the-skills&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;The skills&lt;/h2&gt;&lt;h3 class=&quot;code-line&quot; data-line=&quot;113&quot; dir=&quot;auto&quot; id=&quot;investigate-before-patching&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 1.25em; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;investigate-before-patching&lt;/code&gt;&lt;/h3&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;115&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This skill exists to stop speculative editing.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;117&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Before changing code, the workflow inspects the target module, local dependencies, related tests, and obvious coverage gaps. The helper script,&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;scripts/inspect_patch_context.py&lt;/code&gt;, gives a deterministic first pass over imports, sibling modules, and nearby tests.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;119&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That sounds basic. It is also exactly the sort of discipline agents tend to skip when left alone. They jump to the patch because producing a patch is easier than earning one.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;121&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The important design choice here is scope. The skill does not promise a full call graph. It explicitly says the scan is a starting point and that critical modules still need to be read.&lt;/p&gt;&lt;h3 class=&quot;code-line&quot; data-line=&quot;123&quot; dir=&quot;auto&quot; id=&quot;summarize-test-failures&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 1.25em; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;summarize-test-failures&lt;/code&gt;&lt;/h3&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;125&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This skill handles a very common failure mode in agent-driven work: drowning in raw test output.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;127&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The script ingests a test log, extracts parseable failures, clusters them by likely cause, and produces a debugging brief. In the demo, the sample pytest log gets reduced to three concrete buckets:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;129&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;129&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;stale state or cache invalidation&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;130&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;pricing rule regression&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;131&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;configuration drift&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;133&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That is already more useful than dumping forty lines of traceback back at the user.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;135&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The limit is important though: this is a triage tool, not a diagnosis engine. If the log is messy or incomplete, the summary should stay humble.&lt;/p&gt;&lt;h3 class=&quot;code-line&quot; data-line=&quot;137&quot; dir=&quot;auto&quot; id=&quot;legacy-codebase-recon&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 1.25em; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;legacy-codebase-recon&lt;/code&gt;&lt;/h3&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;139&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This is the first-pass orientation skill for inherited systems.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;141&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The script looks for entry points and scores modules for hotspots using simple heuristics: mutable globals, environment coupling, cache usage, time-sensitive behavior, and general structural weight. On the demo service, it correctly flags the order service and repository layer as the riskiest modules.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;143&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This is exactly where skills can help: not by pretending to understand a legacy system instantly, but by providing a disciplined first pass that makes the next manual inspection smarter.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;145&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The failure mode is also obvious. Heuristics are directional. A hotspot score is not architecture truth.&lt;/p&gt;&lt;h3 class=&quot;code-line&quot; data-line=&quot;147&quot; dir=&quot;auto&quot; id=&quot;review-change-safely&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 1.25em; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;review-change-safely&lt;/code&gt;&lt;/h3&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;149&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This skill is designed for reviewing diffs with an emphasis on unintended side effects.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;151&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The demo diff changes a function signature, alters the returned payload shape, and replaces a guarded key lookup with a required-key lookup. The script surfaces all three and also notes that no tests moved with the behavior change.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;153&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That is a useful baseline because many agent reviews default to a glorified summary. Serious review work needs findings, not narration.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;155&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The skill therefore forces a review shape:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;156&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;156&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;findings first&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;157&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;severity and confidence&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;158&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;file references&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;159&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;residual risk&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;161&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That structure matters. It is much easier to trust and act on than a free-form paragraph about what changed.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;163&quot; dir=&quot;auto&quot; id=&quot;why-scripts-matter&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Why scripts matter&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;165&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The scripts are not there because Python is exciting. They are there because some tasks are simply better when the machine is forced to be literal.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;167&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Three examples:&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;169&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;First, parsing failure lines. Asking a model to &quot;read this pytest output and summarize it&quot; works until the output is long, repetitive, or messy. A parser is cheap and stable.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;171&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Second, scanning code for risk signals. Telling the model to &quot;look for tight coupling and hidden state&quot; is too vague on its own. A script can make that scan explicit and repeatable, even if the results are only heuristic.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;173&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Third, diff review. Function signature changes, payload key changes, and test coverage gaps are not subtle concepts. They should be machine-detectable.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;175&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The right split is simple:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;176&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;176&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;use scripts for deterministic extraction&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;177&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;use skills for workflow logic&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;178&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;use the model for judgment, prioritization, and synthesis&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;180&quot; dir=&quot;auto&quot; id=&quot;what-worked-well&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;What worked well&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;182&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The most useful part of the repo is the layering.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;184&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;AGENTS.md&lt;/code&gt;&amp;nbsp;gives the repo a stable operating model. The skills give recurring tasks structure and routing clarity. The scripts prevent a class of drift where every run invents a slightly different method. The tests keep the helpers from silently rotting.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;186&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The second useful part is restraint. The skills are short. They are not trying to encode every engineering principle. They are only trying to make a few repeated workflows reliable enough to be worth using.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;188&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;One-line takeaway:&amp;nbsp;&lt;strong&gt;a narrow skill you can trust is worth more than a broad skill you cannot.&lt;/strong&gt;&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;190&quot; dir=&quot;auto&quot; id=&quot;what-still-does-not-work-well&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;What still does not work well&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;192&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Skills do not remove the need for real engineering judgment.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;194&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;They do not understand runtime behavior. They do not replace reading the code. They do not magically fix weak tests, misleading names, or missing context from the user. They also do not help much when the task is genuinely novel and there is no repeated workflow to package.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;196&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;There is also a maintenance cost. Once you create a skill, you own it. If the repo changes and the skill instructions or script behavior drift, you have created a false sense of reliability.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;198&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This is why I would be cautious about building fifty skills too early. Most teams should start with a handful of painful, recurring workflows and make those solid.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;200&quot; dir=&quot;auto&quot; id=&quot;when-i-would-use-this-in-production&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;When I would use this in production&lt;/h2&gt;&lt;p class=&quot;code-line code-active-line&quot; data-line=&quot;202&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;I would use this pattern in production when:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;204&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;204&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;the team is repeatedly using agents on the same codebase&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;205&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;the work includes bug triage, reviews, legacy recon, or change planning&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;206&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;there is enough repetition to justify a workflow artifact&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;207&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;the team cares more about consistency than about model theatrics&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;209&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;I would not bother in a repo where agent use is rare, the workflows are highly bespoke, or the codebase is changing so quickly that skill maintenance would dominate the value.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;211&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The threshold is not &quot;do we use AI?&quot; The threshold is &quot;do we keep repeating the same engineering moves?&quot;&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;213&quot; dir=&quot;auto&quot; id=&quot;closing-thoughts&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Closing thoughts&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;215&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The claim here is modest.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;217&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Coding agents do not become reliable because we found the perfect prompt. They become more useful when we stop forcing the model to reinvent stable workflows on every task.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;219&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That means packaging the workflow.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;221&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Write the repo contract in&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;AGENTS.md&lt;/code&gt;. Turn repeated tasks into skills with real boundaries. Move deterministic steps into scripts. Test those scripts. Then let the model operate inside that frame.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;223&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That is less glamorous than prompt alchemy. It is also much closer to how engineering systems usually become dependable.&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2026/04/skills-for-codex-missing-layer-between.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgU3qPmBKKpcVqtbbLGQfIJ7pDA1SupuZcJHIdA0tenFtSAFWJaHK1jQSBYh9i_-GKSBWNhfJuA0QVgxIfwe3HxYcnMxJy4h1WyPgyzFnjYa8DxQaGw3zgRq12HjD2q3FLWrzxGgHJbg9hDIo3j0AMv_OMjPHEgD9i7KLIk793NtcEx37dscUOdQ3x84Aw/s72-c/ChatGPT%20Image%20Apr%2012,%202026,%2007_23_49%20PM.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-4287333928539196813</guid><pubDate>Wed, 08 Apr 2026 15:34:00 +0000</pubDate><atom:updated>2026-04-08T16:24:22.906+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">analysis</category><category domain="http://www.blogger.com/atom/ns#">correlation</category><category domain="http://www.blogger.com/atom/ns#">funds</category><category domain="http://www.blogger.com/atom/ns#">ISA</category><category domain="http://www.blogger.com/atom/ns#">overlap</category><category domain="http://www.blogger.com/atom/ns#">Vanguard</category><title>Building Fund Overlap Lab: From Surface Labels to Real Exposure</title><description>&lt;p&gt;&lt;span face=&quot;-apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif&quot; style=&quot;font-size: 14px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfpNBdLfrDBwJXjPDzuPwIJO95u87e6xBjljAmfbTeBI5Iv0nddYH4bK77Wm-Gh75bJkvRl3s7w-fNAKdBt4AtRi7K3d_pW9_rQB_LOXUT9VzXQqwtnUgKgeSEJJyuVpWmlsmZD7HCTnOva73Lu1OorvGuO_mEaZ1uBOguxBhKtqVFYEgu8Vj7ok8MmVk/s1536/ChatGPT%20Image%20Apr%208,%202026,%2004_31_56%20PM.png&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1536&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfpNBdLfrDBwJXjPDzuPwIJO95u87e6xBjljAmfbTeBI5Iv0nddYH4bK77Wm-Gh75bJkvRl3s7w-fNAKdBt4AtRi7K3d_pW9_rQB_LOXUT9VzXQqwtnUgKgeSEJJyuVpWmlsmZD7HCTnOva73Lu1OorvGuO_mEaZ1uBOguxBhKtqVFYEgu8Vj7ok8MmVk/s320/ChatGPT%20Image%20Apr%208,%202026,%2004_31_56%20PM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;If you have ever compared two multi-asset funds and thought, &quot;These look different, but are they actually different?&quot; this project is for that exact problem.&lt;p&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;4&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Fund Overlap Lab is a Python tool for &lt;b&gt;Vanguard UK funds&lt;/b&gt; that retrieves holdings, normalizes names, and calculates overlap. It includes a CLI for fast checks and a Streamlit app for interactive analysis.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;4&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;a href=&quot;https://github.com/JordiCorbilla/fund-overlap-lab&quot;&gt;JordiCorbilla/fund-overlap-lab: fund-overlap-lab&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;4&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;h2 class=&quot;code-line code-active-line&quot; data-line=&quot;6&quot; dir=&quot;auto&quot; id=&quot;why-it-exists&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Why It Exists&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;8&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Wrapper funds such as &lt;a href=&quot;https://www.vanguardinvestor.co.uk/what-we-offer/all-products&quot;&gt;LifeStrategy&lt;/a&gt; and Target Retirement can look distinct at the top level while sharing large portions of the same underlying exposures.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;10&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The project aims to make that visible by combining:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;12&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;12&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;look-through holdings&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;13&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;transparent overlap math&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;14&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;risk and cost context&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;15&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;practical UI workflows for real portfolio conversations&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;span face=&quot;-apple-system, BlinkMacSystemFont, Segoe WPC, Segoe UI, system-ui, Ubuntu, Droid Sans, sans-serif&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span face=&quot;-apple-system, BlinkMacSystemFont, Segoe WPC, Segoe UI, system-ui, Ubuntu, Droid Sans, sans-serif&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjLVa1iABN846Z7ZQZAp0avPjC7aco-0Px6x_KP9DHvtpWffaWIcZgBZMv7W0xmZXhueSOTt_HG4mQrtUbT3ChOAae0tEGEECfW18EIv4rSdSaBzZxD5k6F60NDjvrrJVOdLHC4VhvPME4yGm7bg-Yhrc7IF8hakWkPutDRRRNThPAythrk6ht-gGDurpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;1472&quot; data-original-width=&quot;959&quot; height=&quot;640&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjLVa1iABN846Z7ZQZAp0avPjC7aco-0Px6x_KP9DHvtpWffaWIcZgBZMv7W0xmZXhueSOTt_HG4mQrtUbT3ChOAae0tEGEECfW18EIv4rSdSaBzZxD5k6F60NDjvrrJVOdLHC4VhvPME4yGm7bg-Yhrc7IF8hakWkPutDRRRNThPAythrk6ht-gGDurpg=w416-h640&quot; width=&quot;416&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;17&quot; dir=&quot;auto&quot; id=&quot;core-overlap-model&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Core Overlap Model&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;19&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;For two funds, overlap is calculated as:&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;21&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;sum(min(weight_a_i, weight_b_i))&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;23&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;where each i is a normalized underlying holding.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;25&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The app then reports:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;27&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;27&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;overlap percentage&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;28&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;shared and distinct holdings&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;29&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;only-in-A and only-in-B tables&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;30&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;bucket-level aggregation&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;span face=&quot;-apple-system, BlinkMacSystemFont, Segoe WPC, Segoe UI, system-ui, Ubuntu, Droid Sans, sans-serif&quot;&gt;&lt;span style=&quot;font-size: 14px;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjW84xNrTrdn6PwRi4qENB-igpDEBW6XLUG3a14jpU2wTsG5if-7WMOiBBrCf2jzuDeMThPX3-KQnU7hI3hkrTTG__A4R-fl5icWnQ-E36DYBlZtASjxzKFOtgezRxdvkroJlllyqUuwEoAK0CGeUYn12wKuA3Bp2zoAFxLE_RskoBjPh1Apxl-cY8VQBU&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;1232&quot; data-original-width=&quot;912&quot; height=&quot;640&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEjW84xNrTrdn6PwRi4qENB-igpDEBW6XLUG3a14jpU2wTsG5if-7WMOiBBrCf2jzuDeMThPX3-KQnU7hI3hkrTTG__A4R-fl5icWnQ-E36DYBlZtASjxzKFOtgezRxdvkroJlllyqUuwEoAK0CGeUYn12wKuA3Bp2zoAFxLE_RskoBjPh1Apxl-cY8VQBU=w475-h640&quot; width=&quot;475&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhv5GFGczcfJkVMxRVaebapKBbfbvC3T4FDg8M05LvzL-bvEYizs7SokWRaY0gSXfb_fszj87TREGiaVwAI8l-NQJZzdxHwRaR9-Q9dq1-aLWp-Ls0UYnFlkIc7pY4QE7JLvZEHDK6FugoNzdZIZge8q1LeCato1_w292XsSXTlxvkEP-PJoaLNaKUaPgk&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;958&quot; data-original-width=&quot;905&quot; height=&quot;640&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhv5GFGczcfJkVMxRVaebapKBbfbvC3T4FDg8M05LvzL-bvEYizs7SokWRaY0gSXfb_fszj87TREGiaVwAI8l-NQJZzdxHwRaR9-Q9dq1-aLWp-Ls0UYnFlkIc7pY4QE7JLvZEHDK6FugoNzdZIZge8q1LeCato1_w292XsSXTlxvkEP-PJoaLNaKUaPgk=w605-h640&quot; width=&quot;605&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;32&quot; dir=&quot;auto&quot; id=&quot;data-pipeline-what-changed-and-why&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Data Pipeline: What Changed and Why&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;34&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Early versions parsed portfolio HTML tables. That broke when pages shifted to JS-driven rendering. The current pipeline is built to survive those changes.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;36&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Current strategy:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;38&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;38&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;primary source: GraphQL endpoint for detailed holdings&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;39&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;fallback source: HTML table/text extraction where possible&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;40&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;resilience fallback: API asset-allocation level extraction&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;41&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;dynamic product lookup from Vanguard product catalog&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;43&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This layered approach keeps the tool operational when upstream page structures move.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;45&quot; dir=&quot;auto&quot; id=&quot;how-data-access-works-technical&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;How Data Access Works&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;47&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Under the hood, the provider follows a staged resolution and retrieval flow:&lt;/p&gt;&lt;ol class=&quot;code-line&quot; data-line=&quot;49&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;49&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Product resolution&lt;/li&gt;&lt;/ol&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;51&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;51&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;the app accepts multiple identifiers (fund code, SEDOL, slug)&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;52&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;it queries Vanguard UK product metadata and resolves those inputs to canonical product fields (including portId)&lt;/li&gt;&lt;/ul&gt;&lt;ol class=&quot;code-line&quot; data-line=&quot;54&quot; dir=&quot;auto&quot; start=&quot;2&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;54&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Primary holdings retrieval (GraphQL)&lt;/li&gt;&lt;/ol&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;56&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;56&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;with the resolved product, it calls Vanguard UK GraphQL endpoints to fetch holdings details&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;57&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;this path returns richer data than static page scraping, including constituent-level rows where available&lt;/li&gt;&lt;/ul&gt;&lt;ol class=&quot;code-line&quot; data-line=&quot;59&quot; dir=&quot;auto&quot; start=&quot;3&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;59&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Recursive expansion (optional)&lt;/li&gt;&lt;/ol&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;61&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;61&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;in Ultimate Look-Through mode, holdings that are themselves funds or ETFs can be expanded recursively&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;62&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;each child weight is multiplied through the parent path weight&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;63&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;recursion is bounded by max depth and protected against cycles&lt;/li&gt;&lt;/ul&gt;&lt;ol class=&quot;code-line&quot; data-line=&quot;65&quot; dir=&quot;auto&quot; start=&quot;4&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;65&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Fallback retrieval&lt;/li&gt;&lt;/ol&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;67&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;67&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;if detailed holdings cannot be retrieved, the provider falls back to HTML extraction and then allocation-level API data&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;68&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;this keeps the app functional even when one upstream path changes&lt;/li&gt;&lt;/ul&gt;&lt;ol class=&quot;code-line&quot; data-line=&quot;70&quot; dir=&quot;auto&quot; start=&quot;5&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;70&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Normalization and comparison preparation&lt;/li&gt;&lt;/ol&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;72&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;72&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;holding names are normalized for robust joins&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;73&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;duplicate rows are consolidated&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;74&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;percentage fields are standardized before overlap calculations&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;76&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This design keeps the user workflow simple while handling real-world upstream variability in a resilient way.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;78&quot; dir=&quot;auto&quot; id=&quot;ultimate-look-through-mode-recursive&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Ultimate Look-Through Mode (Recursive)&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;80&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;A major upgrade is recursive decomposition of fund-of-funds structures.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;82&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;You can now choose between:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;84&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;84&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Direct Holdings mode: one layer of holdings&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;85&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Ultimate Look-Through mode: recursively expands eligible underlying funds and ETFs&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;87&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Both Two-Fund Compare and Portfolio Analysis now include:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;89&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;89&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;an Ultimate Look-Through toggle&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;90&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;a max depth control to limit recursion&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;91&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;cycle protection so recursive expansion does not loop&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;93&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This matters for mixed wrappers where one level of holdings is still another basket.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;95&quot; dir=&quot;auto&quot; id=&quot;streamlit-ux-improvements&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Example Questions It Answers Quickly&lt;/h2&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;126&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;126&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;How much overlap exists between a LifeStrategy fund and a Target Retirement fund right now?&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;127&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Is a portfolio diversified across managers and wrappers, or concentrated in repeated underlyings?&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;128&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Do risk and OCF differences align with actual exposure differences?&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;129&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Does recursive look-through materially change the diversification story?&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;131&quot; dir=&quot;auto&quot; id=&quot;project-structure&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Project Structure&lt;/h2&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;133&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;133&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;fund_overlap_lab/providers.py: data access, product resolution, holdings retrieval&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;134&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;fund_overlap_lab/compare.py: overlap and portfolio analytics&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;135&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;fund_overlap_lab/models.py: data models&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;136&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;fund_overlap_lab/buckets.py: coarse asset bucketing&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;137&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;fund_overlap_lab/cli.py: command-line workflows&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;138&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;app.py: Streamlit experience&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;140&quot; dir=&quot;auto&quot; id=&quot;run-it&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Run It&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;142&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Install:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-bash&quot; data-line=&quot;144&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;150&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;CLI compare:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-bash&quot; data-line=&quot;152&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;python -m fund_overlap_lab.cli compare VGL100A VAR45GA
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;156&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Streamlit app:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-bash&quot; data-line=&quot;158&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;streamlit run app.py
&lt;/code&gt;&lt;/pre&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;162&quot; dir=&quot;auto&quot; id=&quot;what-is-next&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Final Thought&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;174&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Fund Overlap Lab is intentionally practical. It is built to help answer a real question before making allocation changes:&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;176&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;b&gt;&quot;Am I truly diversifying, or buying the same exposure through different wrappers?&quot;&lt;/b&gt;&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2026/04/building-fund-overlap-lab-from-surface.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfpNBdLfrDBwJXjPDzuPwIJO95u87e6xBjljAmfbTeBI5Iv0nddYH4bK77Wm-Gh75bJkvRl3s7w-fNAKdBt4AtRi7K3d_pW9_rQB_LOXUT9VzXQqwtnUgKgeSEJJyuVpWmlsmZD7HCTnOva73Lu1OorvGuO_mEaZ1uBOguxBhKtqVFYEgu8Vj7ok8MmVk/s72-c/ChatGPT%20Image%20Apr%208,%202026,%2004_31_56%20PM.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-3652035870410943406</guid><pubDate>Tue, 07 Apr 2026 09:43:00 +0000</pubDate><atom:updated>2026-04-07T09:43:39.530+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">instruments</category><category domain="http://www.blogger.com/atom/ns#">library</category><category domain="http://www.blogger.com/atom/ns#">quant developer</category><category domain="http://www.blogger.com/atom/ns#">reference library</category><title>Building a Quant Developer Reference Library</title><description>&lt;p&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbOhavBnCgK2-8XdJdGE0nYsZp8vtXe3usDHnH0MNWZLJO5pPRrjEyVw2nO-Dw7oVyTazsxY9D3NjuukB_QzbfovdZ0Wqwu7i1y0p3DB-H1vCpzvryf7bHXQJiWbI1-cHxQ1G5NbN-or6mfai3KgtGbk6SHZlAFnPnJcdH6c50sWCt_PqbfTFYo7zxwQE/s1536/ChatGPT%20Image%20Apr%207,%202026,%2010_38_15%20AM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1536&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbOhavBnCgK2-8XdJdGE0nYsZp8vtXe3usDHnH0MNWZLJO5pPRrjEyVw2nO-Dw7oVyTazsxY9D3NjuukB_QzbfovdZ0Wqwu7i1y0p3DB-H1vCpzvryf7bHXQJiWbI1-cHxQ1G5NbN-or6mfai3KgtGbk6SHZlAFnPnJcdH6c50sWCt_PqbfTFYo7zxwQE/s320/ChatGPT%20Image%20Apr%207,%202026,%2010_38_15%20AM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;For a long time, I wanted a single place that explained quantitative finance the way quant developers actually need it explained.&lt;p&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;4&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Not as a pure math textbook. Not as a collection of interview flashcards. Not as a random pile of disconnected notes.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;8&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;I wanted something practical. Something that connects pricing theory to implementation. Something that helps you move from &quot;I know the formula&quot; to &quot;I can build this correctly, test it, and trust it in production.&quot;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;10&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;So I started building the&amp;nbsp;&lt;strong&gt;Quantitative Developer Reference Library&lt;/strong&gt;.&amp;nbsp;&lt;a href=&quot;https://github.com/JordiCorbilla/Quantitative-Developer-Reference-Library&quot;&gt;JordiCorbilla/Quantitative-Developer-Reference-Library: Quantitative-Developer-Reference-Library&lt;/a&gt;&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;12&quot; dir=&quot;auto&quot; id=&quot;why-this-exists&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Why This Exists&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;14&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;A lot of great quantitative finance material already exists. The problem is not the lack of information. The problem is fragmentation.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;16&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If you are a quant developer, the knowledge you need is usually scattered across:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;17&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;17&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;textbooks that go deep on theory but say little about implementation,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;18&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;code samples that skip market conventions and edge cases,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;19&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;desk knowledge that lives in someone&#39;s head,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;20&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;internal wikis that are useful but never portable,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;21&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;and years of trial and error that are expensive to repeat.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;23&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That leaves a gap between understanding the model and building the system.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;25&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;In practice, that gap is where most of the pain lives:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;26&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;26&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;curves built with the wrong conventions,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;27&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;volatility surfaces that look smooth but produce nonsense risk,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;28&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;pricing engines that work for the happy path but fail on real trades,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;29&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;PnL explain that does not reconcile,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;30&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;market data that is technically available but not analytically usable,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;31&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;and analytics platforms that become hard to trust as they grow.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;33&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This repository is meant to close that gap.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;35&quot; dir=&quot;auto&quot; id=&quot;what-the-library-tries-to-do&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;What The Library Tries To Do&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;37&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The goal is simple:&lt;/p&gt;&lt;blockquote class=&quot;code-line&quot; data-line=&quot;39&quot; dir=&quot;auto&quot; style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(34, 34, 34); border-color: rgba(0, 122, 204, 0.5); border-left-style: solid; border-left-width: 5px; border-radius: 2px; font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin: 0px; padding: 0px 16px 0px 10px; position: relative;&quot;&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;39&quot; dir=&quot;auto&quot; style=&quot;margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;span style=&quot;color: white;&quot;&gt;Build a practitioner-focused reference for quant developers that connects products, pricing, risk, market data, numerical methods, and production architecture.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;41&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This is not meant to be read cover to cover like a course.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;43&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;It is designed to work as:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;44&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;44&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;a reference you can return to when building something real,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;45&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;a bridge between theory and implementation,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;46&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;a map of how the major pieces fit together,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;47&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;and a foundation that can keep growing over time.&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;49&quot; dir=&quot;auto&quot; id=&quot;what-is-in-the-repo&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;What Is In The Repo&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;51&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The library is organized to cover both the instrument side and the engineering side.&lt;/p&gt;&lt;h3 class=&quot;code-line&quot; data-line=&quot;53&quot; dir=&quot;auto&quot; id=&quot;core-product-chapters&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 1.25em; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; position: relative;&quot;&gt;Core product chapters&lt;/h3&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;54&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;54&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;options,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;55&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;futures and forwards,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;56&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;equities,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;57&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;FX,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;58&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;fixed income,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;59&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;interest rates,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;60&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;credit,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;61&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;commodities,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;62&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;and cross-asset topics.&lt;/li&gt;&lt;/ul&gt;&lt;h3 class=&quot;code-line&quot; data-line=&quot;64&quot; dir=&quot;auto&quot; id=&quot;quant-engineering-chapters&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 1.25em; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; position: relative;&quot;&gt;Quant engineering chapters&lt;/h3&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;65&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;65&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;market data,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;66&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;pricing architecture,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;67&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;risk and PnL,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;68&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;testing and validation,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;69&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;performance and production.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;71&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;There is also a numerical methods chapter because, in practice, almost every serious pricing problem ends up there eventually.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;73&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The idea is that a quant developer should not need to mentally separate:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;74&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;74&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&quot;the finance part,&quot;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;75&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&quot;the code part,&quot;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;76&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;and &quot;the production part.&quot;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;78&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Those are usually the same problem, just seen from different angles.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;80&quot; dir=&quot;auto&quot; id=&quot;the-point-of-view&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;The Point Of View&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;82&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This repo is opinionated.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;84&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;It assumes that quantitative correctness is not just about deriving the right formula. It is also about:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;85&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;85&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;using the right conventions,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;86&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;consuming the right market data,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;87&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;choosing the right numerical method,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;88&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;exposing the right risk,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;89&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;and building the system so the result is reproducible and explainable.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;91&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That is why the library focuses on questions like:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;92&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;92&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;What does the desk actually quote?&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;93&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;What data do you really need?&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;94&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;What assumptions are hidden in this model?&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;95&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;What tends to break in production?&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;96&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;How should this be tested?&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;97&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;How do you know the output is sane?&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;99&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Those questions matter just as much as the equation itself.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;101&quot; dir=&quot;auto&quot; id=&quot;who-it-is-for&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Who It Is For&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;103&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;I built this primarily for:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;104&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;104&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;quant developers,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;105&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;engineering-minded quants,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;106&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;developers moving into front-office or analytics roles,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;107&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;and practitioners who want a more implementation-grounded way to study the field.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;109&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If you are preparing for interviews, building pricing libraries, working on market data or risk systems, or trying to connect multiple years of learning into one coherent framework, this should be useful.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;111&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;It should also be useful to people already in the field who want a clean reference instead of searching across old notes and half-remembered documents.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;113&quot; dir=&quot;auto&quot; id=&quot;what-makes-it-different&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;What Makes It Different&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;115&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;There are plenty of resources that explain option pricing. There are plenty of resources that explain fixed income. There are plenty of resources that explain numerical methods.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;119&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;What is rarer is a resource that keeps all of the following in the same frame:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;120&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;120&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;product intuition,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;121&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;pricing formulas,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;122&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;market conventions,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;123&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;implementation shape,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;124&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;validation logic,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;125&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;and production pitfalls.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;127&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That is the standard I want this repository to meet.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;129&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;For example, an options chapter should not stop at Black-Scholes. It should also talk about:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;130&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;130&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;implied volatility surfaces,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;131&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;no-arbitrage checks,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;132&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;surface risk conventions,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;133&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;American exercise intuition,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;134&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;implementation pitfalls,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;135&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;and the difference between a formula that works on paper and an analytics workflow that works on a desk.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;137&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The same standard applies across rates, fixed income, credit, market data, risk, and architecture.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;139&quot; dir=&quot;auto&quot; id=&quot;why-i-am-sharing-it-early&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Why I Am Sharing It Early&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;141&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;I do not want this to become a private note system that only becomes visible once it is &quot;finished.&quot;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;143&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;A library like this gets better in the open.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;145&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;It improves when people use it, challenge it, extend it, and point out where the real complexity lives. Quant development is too broad, too practical, and too experience-driven for one person to pretend they can write the final word on it alone.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;147&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;So I am sharing it as a growing public reference.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;149&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The current version already has the foundation:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;150&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;150&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;a consistent structure,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;151&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;broad chapter coverage,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;152&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;deeper treatment in options, fixed income, rates, and numerical methods,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;153&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;and dedicated chapters for the engineering side of quant work.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;155&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;From here, the plan is to keep making it more useful.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;157&quot; dir=&quot;auto&quot; id=&quot;where-i-want-to-take-it&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Where I Want To Take It&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;159&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Over time, I want this repository to become a serious long-term resource for the quant developer community.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;161&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That means:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;162&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;162&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;deeper product coverage,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;163&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;better worked examples,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;164&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;more implementation patterns,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;165&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;more sanity-check frameworks,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;166&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;stronger cross-links between topics,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;167&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;and eventually a body of material that is genuinely useful both for study and for real-world system design.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;169&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Some of the areas I especially want to deepen:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;170&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;170&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;volatility modelling,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;171&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;multi-curve rates,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;172&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;fixed-income relative value and spread analytics,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;173&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;calibration workflows,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;174&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;pricing architecture patterns,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;175&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;risk explain,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;176&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;and the operational side of production quant systems.&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;178&quot; dir=&quot;auto&quot; id=&quot;if-you-work-in-this-space&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;If You Work In This Space&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;180&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If you are a quant developer, quant, trader, or engineer working near pricing and risk systems, I would love for you to take a look.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;182&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Use it. Challenge it. Steal from it. Improve it.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;187&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If a section is missing something important, I want to know. If a convention should be clarified, I want to know. If there is a subtle implementation trap that every real system hits, that is exactly the kind of knowledge worth capturing here.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;191&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The best technical references are not just correct. They are useful.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;193&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That is the bar.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;195&quot; dir=&quot;auto&quot; id=&quot;repository&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Repository&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;197&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If this sounds useful, you can explore the repo here:&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;199&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;strong&gt;Quantitative Developer Reference Library&lt;/strong&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;201&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Start with:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;202&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;202&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;README.md&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;203&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;00-overview.md&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;204&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;01-options.md&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;205&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;05-fixed-income.md&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;206&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;06-interest-rates.md&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;207&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;10-numerical-methods.md&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;209&quot; dir=&quot;auto&quot; id=&quot;closing-thought&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Closing Thought&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;211&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Quant finance has no shortage of complexity. What it often lacks is structure.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;214&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The point of this library is to create more structure:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;215&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;215&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;structure around the products,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;216&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;structure around the implementation,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;217&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;structure around the failure modes,&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;218&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;and structure around the knowledge itself.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;220&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If it saves someone time, helps someone build something better, or gives the community a shared reference that is more practical than what already exists, then it is worth doing.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;222&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That is the project.&lt;/p&gt;&lt;hr class=&quot;code-line&quot; data-line=&quot;224&quot; dir=&quot;auto&quot; style=&quot;border-bottom-color: rgba(255, 255, 255, 0.18); border-bottom-style: solid; border-image: initial; border-left: 0px rgba(255, 255, 255, 0.18); border-right: 0px rgba(255, 255, 255, 0.18); border-top: 0px rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; height: 1px; position: relative;&quot; /&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;226&quot; dir=&quot;auto&quot; id=&quot;short-version-for-social-posts&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Short Version For Social Posts&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;228&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;I built a&amp;nbsp;&lt;strong&gt;Quantitative Developer Reference Library&lt;/strong&gt;: a practical, growing repo for quant developers that connects products, pricing, risk, market data, numerical methods, and production architecture.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;230&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;It is designed to bridge the gap between &quot;I know the formula&quot; and &quot;I can build this properly.&quot;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;232&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Current coverage includes options, futures, equities, FX, fixed income, rates, credit, commodities, cross-asset topics, plus market data, pricing architecture, risk/PnL, testing, and production engineering.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;234&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If you work in quant dev, pricing, risk, or analytics engineering, I would love feedback.&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2026/04/building-quant-developer-reference.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbOhavBnCgK2-8XdJdGE0nYsZp8vtXe3usDHnH0MNWZLJO5pPRrjEyVw2nO-Dw7oVyTazsxY9D3NjuukB_QzbfovdZ0Wqwu7i1y0p3DB-H1vCpzvryf7bHXQJiWbI1-cHxQ1G5NbN-or6mfai3KgtGbk6SHZlAFnPnJcdH6c50sWCt_PqbfTFYo7zxwQE/s72-c/ChatGPT%20Image%20Apr%207,%202026,%2010_38_15%20AM.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-4080647754098520486</guid><pubDate>Tue, 07 Apr 2026 07:36:00 +0000</pubDate><atom:updated>2026-04-07T07:36:36.241+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">fireworks</category><category domain="http://www.blogger.com/atom/ns#">fun</category><category domain="http://www.blogger.com/atom/ns#">Python</category><category domain="http://www.blogger.com/atom/ns#">simulation</category><title>Building a CLI Firework Simulation in Python, Just for Fun</title><description>&lt;p&gt;&lt;span style=&quot;font-family: &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, sans-serif; font-size: 13px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiG3RcTc2sCT69obRO63RZIOjLgep_9EsFbq4rbf55mmPALg-km4A1R1UlKYGXtzqJzxIRpEWyVsgT4vYC97EneLjI0h9loB0DmZL6ThafWAYKE1W4cw7tTvfV4UNnWHpvq3yqNJbOjoJxCf015afngaPI67b6ZIJCbnyaTShnqNc2IQEmj6ilAw7Yea68&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;408&quot; data-original-width=&quot;871&quot; height=&quot;188&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiG3RcTc2sCT69obRO63RZIOjLgep_9EsFbq4rbf55mmPALg-km4A1R1UlKYGXtzqJzxIRpEWyVsgT4vYC97EneLjI0h9loB0DmZL6ThafWAYKE1W4cw7tTvfV4UNnWHpvq3yqNJbOjoJxCf015afngaPI67b6ZIJCbnyaTShnqNc2IQEmj6ilAw7Yea68=w400-h188&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Every now and then it is worth building something with no real business value at all.&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/JordiCorbilla/firework-simulation&quot;&gt;JordiCorbilla/firework-simulation: firework-simulation&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;text-size-chat leading-relaxed extension:leading-normal my-2&quot; style=&quot;--tw-leading: 1.5; border: 0px solid; box-sizing: border-box; font-family: &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, sans-serif; font-size: 13px; line-height: 1.5; margin-block: 8px; margin: 0px; padding: 0px;&quot;&gt;This terminal firework simulation was one of those projects.&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyuRxe_KM9Q-dKlxe-3-Bvug79jjPpXkz95iCGtaaob6X0LhWeBmxs9j1CILUTrTe5SgWR93QP5_w55_m67PbsUItg4A8jEeSWEehotHPhypi1K7xRnH5obeqiKH-ZwvjFlk6zDlgAo3W-C3DMO8ckCFnetVL4v5M9kl0g8HOUDu8R3wmx183LCfCE1RM/s957/fireworks.gif&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;685&quot; data-original-width=&quot;957&quot; height=&quot;458&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyuRxe_KM9Q-dKlxe-3-Bvug79jjPpXkz95iCGtaaob6X0LhWeBmxs9j1CILUTrTe5SgWR93QP5_w55_m67PbsUItg4A8jEeSWEehotHPhypi1K7xRnH5obeqiKH-ZwvjFlk6zDlgAo3W-C3DMO8ckCFnetVL4v5M9kl0g8HOUDu8R3wmx183LCfCE1RM/w640-h458/fireworks.gif&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p class=&quot;text-size-chat leading-relaxed extension:leading-normal my-2&quot; style=&quot;--tw-leading: 1.5; border: 0px solid; box-sizing: border-box; font-family: &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, sans-serif; font-size: 13px; line-height: 1.5; margin-block: 8px; margin: 0px; padding: 0px;&quot;&gt;It was not meant to be a serious graphics engine, a physics experiment, or a polished game. It was just a fun idea: could a terminal window, a handful of ASCII characters, and some ANSI colors feel enough like fireworks to make me smile? The answer turned out to be yes.&lt;/p&gt;&lt;p class=&quot;text-size-chat leading-relaxed extension:leading-normal my-2&quot; style=&quot;--tw-leading: 1.5; border: 0px solid; box-sizing: border-box; font-family: &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, sans-serif; font-size: 13px; line-height: 1.5; margin-block: 8px; margin: 0px; padding: 0px;&quot;&gt;The basic idea is simple. A rocket launches from the bottom of the terminal, rises with velocity and gravity, bursts near its apex, and then scatters particles across the screen. Those particles fade out over time, slow down slightly, and disappear. Render that fast enough in a loop and you get something that feels lively, colourful, and surprisingly expressive for such a low-tech medium.&lt;/p&gt;&lt;p class=&quot;text-size-chat leading-relaxed extension:leading-normal my-2&quot; style=&quot;--tw-leading: 1.5; border: 0px solid; box-sizing: border-box; font-family: &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, sans-serif; font-size: 13px; line-height: 1.5; margin-block: 8px; margin: 0px; padding: 0px;&quot;&gt;What makes a project like this enjoyable is that the constraints are part of the charm. A terminal is a terrible place for realistic animation. Resolution is coarse, circles are approximate, motion is chunky, and different terminals behave differently. But that is exactly why the project works best as a stylized effect rather than a realistic one. Bright colours, simple trails, exaggerated bursts, and particles fading from&amp;nbsp;&lt;span class=&quot;_inlineMarkdown_3nxm6_25 inline-markdown text-size-chat-sm font-mono blend bg-token-text-code-block-background rounded-sm px-1.5 py-0.5 leading-none extension:bg-token-foreground/10 electron:bg-token-list-hover-background/60&quot; style=&quot;--tw-leading: 1 !important; background-color: oklab(0.845175 0.0000383258 0.0000169277 / 0.1); border-radius: 7.5px; border: 0px solid; box-sizing: border-box; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; line-height: 1 !important; margin: 0px; padding-block: 2px; padding-inline: 6px; padding: 0px;&quot;&gt;@&lt;/span&gt;&amp;nbsp;to&amp;nbsp;&lt;span class=&quot;_inlineMarkdown_3nxm6_25 inline-markdown text-size-chat-sm font-mono blend bg-token-text-code-block-background rounded-sm px-1.5 py-0.5 leading-none extension:bg-token-foreground/10 electron:bg-token-list-hover-background/60&quot; style=&quot;--tw-leading: 1 !important; background-color: oklab(0.845175 0.0000383258 0.0000169277 / 0.1); border-radius: 7.5px; border: 0px solid; box-sizing: border-box; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; line-height: 1 !important; margin: 0px; padding-block: 2px; padding-inline: 6px; padding: 0px;&quot;&gt;o&lt;/span&gt;&amp;nbsp;to&amp;nbsp;&lt;span class=&quot;_inlineMarkdown_3nxm6_25 inline-markdown text-size-chat-sm font-mono blend bg-token-text-code-block-background rounded-sm px-1.5 py-0.5 leading-none extension:bg-token-foreground/10 electron:bg-token-list-hover-background/60&quot; style=&quot;--tw-leading: 1 !important; background-color: oklab(0.845175 0.0000383258 0.0000169277 / 0.1); border-radius: 7.5px; border: 0px solid; box-sizing: border-box; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; line-height: 1 !important; margin: 0px; padding-block: 2px; padding-inline: 6px; padding: 0px;&quot;&gt;*&lt;/span&gt;&amp;nbsp;to&amp;nbsp;&lt;span class=&quot;_inlineMarkdown_3nxm6_25 inline-markdown text-size-chat-sm font-mono blend bg-token-text-code-block-background rounded-sm px-1.5 py-0.5 leading-none extension:bg-token-foreground/10 electron:bg-token-list-hover-background/60&quot; style=&quot;--tw-leading: 1 !important; background-color: oklab(0.845175 0.0000383258 0.0000169277 / 0.1); border-radius: 7.5px; border: 0px solid; box-sizing: border-box; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; line-height: 1 !important; margin: 0px; padding-block: 2px; padding-inline: 6px; padding: 0px;&quot;&gt;.&lt;/span&gt;&amp;nbsp;do more for the effect than realism ever could.&lt;/p&gt;&lt;p class=&quot;text-size-chat leading-relaxed extension:leading-normal my-2&quot; style=&quot;--tw-leading: 1.5; border: 0px solid; box-sizing: border-box; font-family: &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, sans-serif; font-size: 13px; line-height: 1.5; margin-block: 8px; margin: 0px; padding: 0px;&quot;&gt;The implementation uses plain Python and ANSI escape codes, which keeps it lightweight and easy to run. There is a render loop, a small particle system, a few burst patterns, and some keyboard controls for launching rockets, triggering bursts, toggling finale mode, pausing, and quitting. Nothing too deep. Just enough structure to make the show feel varied.&lt;/p&gt;&lt;p class=&quot;text-size-chat leading-relaxed extension:leading-normal my-2&quot; style=&quot;--tw-leading: 1.5; border: 0px solid; box-sizing: border-box; font-family: &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, sans-serif; font-size: 13px; line-height: 1.5; margin-block: 8px; margin: 0px; padding: 0px;&quot;&gt;I also leaned into the playful side of it. There are different shell patterns like chrysanthemum, ring, willow, palm, and crackle. Rockets leave trails. The background has stars. There is an automatic finale mode that ramps up the pace. None of this was necessary. That was the point.&lt;/p&gt;&lt;p class=&quot;text-size-chat leading-relaxed extension:leading-normal my-2&quot; style=&quot;--tw-leading: 1.5; border: 0px solid; box-sizing: border-box; font-family: &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, sans-serif; font-size: 13px; line-height: 1.5; margin-block: 8px; margin: 0px; padding: 0px;&quot;&gt;Projects like this are a good reminder that programming does not always need to be optimized around utility. Sometimes it is enough to build something because it is amusing, visually satisfying, or slightly ridiculous. A CLI firework show definitely qualifies.&lt;/p&gt;&lt;p class=&quot;text-size-chat leading-relaxed extension:leading-normal my-2&quot; style=&quot;--tw-leading: 1.5; border: 0px solid; box-sizing: border-box; font-family: &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, sans-serif; font-size: 13px; line-height: 1.5; margin-block: 8px; margin: 0px; padding: 0px;&quot;&gt;In the end, this was just for fun, and that is exactly why it was worth making.&lt;/p&gt;&lt;p class=&quot;text-size-chat leading-relaxed extension:leading-normal my-2&quot; style=&quot;--tw-leading: 1.5; border: 0px solid; box-sizing: border-box; font-family: &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, sans-serif; font-size: 13px; line-height: 1.5; margin-block: 8px; margin: 0px; padding: 0px;&quot;&gt;If you want to try it yourself, run:&lt;/p&gt;&lt;div class=&quot;bg-token-text-code-block-background border-token-input-background relative overflow-clip rounded-lg border contain-inline-size dark my-2&quot; data-theme=&quot;dark&quot; style=&quot;--tw-contain-size: inline-size; background-color: rgba(10, 10, 10, 0.4); border-radius: 12.5px; border: 1px solid rgb(60, 60, 60); box-sizing: border-box; contain: inline-size; corner-shape: superellipse(1.5); font-family: &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, sans-serif; font-size: 13px; margin-block: 8px; margin: 0px; overflow: clip; padding: 0px; position: relative;&quot;&gt;&lt;div class=&quot;text-size-chat overflow-y-auto p-2&quot; dir=&quot;ltr&quot; style=&quot;border: 0px solid; box-sizing: border-box; margin: 0px; overflow-y: auto; padding: 8px; scrollbar-color: rgba(121, 121, 121, 0.4) rgba(0, 0, 0, 0);&quot;&gt;&lt;code class=&quot;whitespace-pre!&quot; style=&quot;background-color: unset !important; border-radius: 0px !important; border: 0px solid; box-sizing: border-box; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-feature-settings: normal; font-size: 1em; font-variation-settings: normal; margin: 0px; padding: 0px !important; white-space: pre !important;&quot;&gt;python -m fireworks
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;p class=&quot;text-size-chat leading-relaxed extension:leading-normal my-2&quot; style=&quot;--tw-leading: 1.5; border: 0px solid; box-sizing: border-box; font-family: &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, sans-serif; font-size: 13px; line-height: 1.5; margin-block: 8px; margin: 0px; padding: 0px;&quot;&gt;And if you want to interact with it:&lt;/p&gt;&lt;ul class=&quot;text-size-chat leading-relaxed extension:leading-normal mt-0 mb-4  list-disc pl-4&quot; style=&quot;--tw-leading: 1.5; border: 0px solid; box-sizing: border-box; font-family: &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, sans-serif; font-size: 13px; line-height: 1.5; list-style-image: initial; list-style-position: initial; margin: 0px 0px 16px; padding: 0px 0px 0px 16px;&quot;&gt;&lt;li class=&quot;text-size-chat leading-relaxed extension:leading-normal mb-1.5&quot; style=&quot;--tw-leading: 1.5; border: 0px solid; box-sizing: border-box; line-height: 1.5; margin: 0px 0px 6px; padding: 0px;&quot;&gt;&lt;span class=&quot;_inlineMarkdown_3nxm6_25 inline-markdown text-size-chat-sm font-mono blend bg-token-text-code-block-background rounded-sm px-1.5 py-0.5 leading-none extension:bg-token-foreground/10 electron:bg-token-list-hover-background/60&quot; style=&quot;--tw-leading: 1 !important; background-color: oklab(0.845175 0.0000383258 0.0000169277 / 0.1); border-radius: 7.5px; border: 0px solid; box-sizing: border-box; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; line-height: 1 !important; margin: 0px; padding-block: 2px; padding-inline: 6px; padding: 0px;&quot;&gt;space&lt;/span&gt;&amp;nbsp;launches a rocket&lt;/li&gt;&lt;li class=&quot;text-size-chat leading-relaxed extension:leading-normal mb-1.5&quot; style=&quot;--tw-leading: 1.5; border: 0px solid; box-sizing: border-box; line-height: 1.5; margin: 0px 0px 6px; padding: 0px;&quot;&gt;&lt;span class=&quot;_inlineMarkdown_3nxm6_25 inline-markdown text-size-chat-sm font-mono blend bg-token-text-code-block-background rounded-sm px-1.5 py-0.5 leading-none extension:bg-token-foreground/10 electron:bg-token-list-hover-background/60&quot; style=&quot;--tw-leading: 1 !important; background-color: oklab(0.845175 0.0000383258 0.0000169277 / 0.1); border-radius: 7.5px; border: 0px solid; box-sizing: border-box; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; line-height: 1 !important; margin: 0px; padding-block: 2px; padding-inline: 6px; padding: 0px;&quot;&gt;r&lt;/span&gt;&amp;nbsp;triggers a random burst&lt;/li&gt;&lt;li class=&quot;text-size-chat leading-relaxed extension:leading-normal mb-1.5&quot; style=&quot;--tw-leading: 1.5; border: 0px solid; box-sizing: border-box; line-height: 1.5; margin: 0px 0px 6px; padding: 0px;&quot;&gt;&lt;span class=&quot;_inlineMarkdown_3nxm6_25 inline-markdown text-size-chat-sm font-mono blend bg-token-text-code-block-background rounded-sm px-1.5 py-0.5 leading-none extension:bg-token-foreground/10 electron:bg-token-list-hover-background/60&quot; style=&quot;--tw-leading: 1 !important; background-color: oklab(0.845175 0.0000383258 0.0000169277 / 0.1); border-radius: 7.5px; border: 0px solid; box-sizing: border-box; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; line-height: 1 !important; margin: 0px; padding-block: 2px; padding-inline: 6px; padding: 0px;&quot;&gt;f&lt;/span&gt;&amp;nbsp;toggles finale mode&lt;/li&gt;&lt;li class=&quot;text-size-chat leading-relaxed extension:leading-normal mb-1.5&quot; style=&quot;--tw-leading: 1.5; border: 0px solid; box-sizing: border-box; line-height: 1.5; margin: 0px 0px 6px; padding: 0px;&quot;&gt;&lt;span class=&quot;_inlineMarkdown_3nxm6_25 inline-markdown text-size-chat-sm font-mono blend bg-token-text-code-block-background rounded-sm px-1.5 py-0.5 leading-none extension:bg-token-foreground/10 electron:bg-token-list-hover-background/60&quot; style=&quot;--tw-leading: 1 !important; background-color: oklab(0.845175 0.0000383258 0.0000169277 / 0.1); border-radius: 7.5px; border: 0px solid; box-sizing: border-box; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; line-height: 1 !important; margin: 0px; padding-block: 2px; padding-inline: 6px; padding: 0px;&quot;&gt;p&lt;/span&gt;&amp;nbsp;pauses&lt;/li&gt;&lt;li class=&quot;text-size-chat leading-relaxed extension:leading-normal mb-1.5&quot; style=&quot;--tw-leading: 1.5; border: 0px solid; box-sizing: border-box; line-height: 1.5; margin: 0px 0px 6px; padding: 0px;&quot;&gt;&lt;span class=&quot;_inlineMarkdown_3nxm6_25 inline-markdown text-size-chat-sm font-mono blend bg-token-text-code-block-background rounded-sm px-1.5 py-0.5 leading-none extension:bg-token-foreground/10 electron:bg-token-list-hover-background/60&quot; style=&quot;--tw-leading: 1 !important; background-color: oklab(0.845175 0.0000383258 0.0000169277 / 0.1); border-radius: 7.5px; border: 0px solid; box-sizing: border-box; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; line-height: 1 !important; margin: 0px; padding-block: 2px; padding-inline: 6px; padding: 0px;&quot;&gt;q&lt;/span&gt;&amp;nbsp;quits&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;text-size-chat leading-relaxed extension:leading-normal my-2&quot; style=&quot;--tw-leading: 1.5; border: 0px solid; box-sizing: border-box; font-family: &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, sans-serif; font-size: 13px; line-height: 1.5; margin-block: 8px; margin: 0px; padding: 0px;&quot;&gt;It is a small project, but it captures something I like about programming: sometimes a silly idea is reason enough.&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2026/04/building-cli-firework-simulation-in.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEiG3RcTc2sCT69obRO63RZIOjLgep_9EsFbq4rbf55mmPALg-km4A1R1UlKYGXtzqJzxIRpEWyVsgT4vYC97EneLjI0h9loB0DmZL6ThafWAYKE1W4cw7tTvfV4UNnWHpvq3yqNJbOjoJxCf015afngaPI67b6ZIJCbnyaTShnqNc2IQEmj6ilAw7Yea68=s72-w400-h188-c" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-5933550275974143162</guid><pubDate>Tue, 07 Apr 2026 07:25:00 +0000</pubDate><atom:updated>2026-04-07T07:25:01.653+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">gemma 4</category><category domain="http://www.blogger.com/atom/ns#">Local LLM</category><category domain="http://www.blogger.com/atom/ns#">mobile device</category><title>Running Gemma 4 Locally on Android: Surprisingly Good</title><description>&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJ9p5zMtIkYPtQvrHr9uN631YM4acYRXJQhEattigQMynJtERXnItMH969TEa2SDpdxCOBDAFOsq_BXI6UqoKtmAZTCSzeRkP5gSNTX2bxu7i4VimncFVh-6kG_F7RrxAHaocrueORRwNPofHqhVcbqG3UYpWzE_yX_PB4rk3Jwyi9rQaBEjEiV6_tvW8/s1536/ChatGPT%20Image%20Apr%207,%202026,%2008_18_31%20AM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1536&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJ9p5zMtIkYPtQvrHr9uN631YM4acYRXJQhEattigQMynJtERXnItMH969TEa2SDpdxCOBDAFOsq_BXI6UqoKtmAZTCSzeRkP5gSNTX2bxu7i4VimncFVh-6kG_F7RrxAHaocrueORRwNPofHqhVcbqG3UYpWzE_yX_PB4rk3Jwyi9rQaBEjEiV6_tvW8/s320/ChatGPT%20Image%20Apr%207,%202026,%2008_18_31%20AM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;I’ve been experimenting with on-device LLMs for a while—mostly via Ollama, OpenClaw, and various local inference stacks—but this is the first time I’ve seen something that feels &lt;em&gt;actually usable&lt;/em&gt; on a phone without compromise.&lt;p&gt;&lt;/p&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;I just tested &lt;strong&gt;Gemma 4 (E2B-it variant)&lt;/strong&gt; directly on Android, and the experience is… unexpectedly solid.&lt;/p&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;div contenteditable=&quot;false&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;TL;DR&lt;/h2&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Area&lt;/th&gt;&lt;th&gt;Verdict&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Setup&lt;/td&gt;&lt;td&gt;Extremely simple (native integration)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Performance&lt;/td&gt;&lt;td&gt;Fast enough to feel interactive&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Quality&lt;/td&gt;&lt;td&gt;Strong for a ~2.5GB model&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;UX&lt;/td&gt;&lt;td&gt;Clean, minimal, no friction&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Practicality&lt;/td&gt;&lt;td&gt;Finally viable for real usage&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div contenteditable=&quot;false&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;What This Is&lt;/h2&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;This is Google’s &lt;strong&gt;Gemma 4 model running fully on-device&lt;/strong&gt;, exposed through an Android-native interface using LiteRT-LM.&lt;/p&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;Key characteristics:&lt;/p&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;&lt;strong&gt;~2.5GB footprint&lt;/strong&gt; (E2B-it variant)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Runs entirely locally&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Supports multimodal input&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;~32K context window&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;No cloud dependency&lt;/li&gt;&lt;li&gt;No latency spikes from network&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;This matters more than it sounds—because most “local LLM” setups are either:&lt;/p&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;Too heavy (desktop GPU required), or&lt;/li&gt;&lt;li&gt;Too slow (toy-level mobile inference)&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;This sits right in the middle: &lt;em&gt;practical local intelligence&lt;/em&gt;.&lt;/p&gt;&lt;div contenteditable=&quot;false&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;First Impressions&lt;/h2&gt;&lt;h3&gt;1. Setup Experience&lt;/h3&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;This is where it stands out immediately.&lt;/p&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;No:&lt;/p&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;Docker&lt;/li&gt;&lt;li&gt;Python envs&lt;/li&gt;&lt;li&gt;CUDA nonsense&lt;/li&gt;&lt;li&gt;CLI gymnastics&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;Just:&lt;/p&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;Open app&lt;/li&gt;&lt;li&gt;Tap download&lt;/li&gt;&lt;li&gt;Done&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEg0PBu5xp4yYMry9TYl9AMhLJXrjg_QwdUWnIl6Rzwyj_6AXFgBjtZsxQ-pV9RsKEjTSFpsrscf1vx6O8u-CRL-HJ-b4xjcFt8vkgZbjrGYor9DXmF3C-MvIFzxbXWyFYXG6OpyM0OriKo1kt_9Ia7zLcIESb0ykODco0Hllcce8EyFcFneofQO3NtnBVY&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;2048&quot; data-original-width=&quot;921&quot; height=&quot;640&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEg0PBu5xp4yYMry9TYl9AMhLJXrjg_QwdUWnIl6Rzwyj_6AXFgBjtZsxQ-pV9RsKEjTSFpsrscf1vx6O8u-CRL-HJ-b4xjcFt8vkgZbjrGYor9DXmF3C-MvIFzxbXWyFYXG6OpyM0OriKo1kt_9Ia7zLcIESb0ykODco0Hllcce8EyFcFneofQO3NtnBVY=w288-h640&quot; width=&quot;288&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;Compared to typical local setups (Ollama, llama.cpp, etc.), this is &lt;strong&gt;orders of magnitude simpler&lt;/strong&gt;.&lt;/p&gt;&lt;div contenteditable=&quot;false&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h3&gt;2. Performance&lt;/h3&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;This is the surprising part.&lt;/p&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;Responses are &lt;strong&gt;fast enough to feel conversational&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;No obvious stalling or token starvation&lt;/li&gt;&lt;li&gt;Latency feels closer to edge inference than “local hack”&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;This suggests:&lt;/p&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;Aggressive quantization&lt;/li&gt;&lt;li&gt;Optimized runtime (LiteRT-LM is doing heavy lifting here)&lt;/li&gt;&lt;li&gt;Likely hardware acceleration (NNAPI / GPU paths)&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;It’s not desktop-level—but it’s &lt;em&gt;good enough to actually use&lt;/em&gt;.&lt;/p&gt;&lt;div contenteditable=&quot;false&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h3&gt;3. Model Quality&lt;/h3&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;For a &lt;strong&gt;2.5GB model&lt;/strong&gt;, the quality is impressive:&lt;/p&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;Coherent reasoning&lt;/li&gt;&lt;li&gt;Good instruction following&lt;/li&gt;&lt;li&gt;Decent structure in responses&lt;/li&gt;&lt;li&gt;No obvious collapse under moderate prompts&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;Where it likely struggles (as expected):&lt;/p&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;Deep multi-step reasoning&lt;/li&gt;&lt;li&gt;Heavy coding tasks&lt;/li&gt;&lt;li&gt;Long-chain logical consistency&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;But for:&lt;/p&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;Notes&lt;/li&gt;&lt;li&gt;Quick analysis&lt;/li&gt;&lt;li&gt;Idea generation&lt;/li&gt;&lt;li&gt;Lightweight coding help&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;…it’s absolutely viable.&lt;/p&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEitSfbVA8TGJCjaJ99Oh4RulVwoJifKGq8q83iOtN0uW4HUqZxD57t9J931GviUYtpVuv14IreHkXKtzOHtBtKtAXg9KCa1VMK3YZ6RlYfhZ8V7l2sF0E3yjCWu0zLx8Bq4Vd6zPBYh3fgOWt4JaMSK2s2-Msw3SminhGsLPW8UPHgYXoJCsivovtshD2I&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;2048&quot; data-original-width=&quot;921&quot; height=&quot;640&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEitSfbVA8TGJCjaJ99Oh4RulVwoJifKGq8q83iOtN0uW4HUqZxD57t9J931GviUYtpVuv14IreHkXKtzOHtBtKtAXg9KCa1VMK3YZ6RlYfhZ8V7l2sF0E3yjCWu0zLx8Bq4Vd6zPBYh3fgOWt4JaMSK2s2-Msw3SminhGsLPW8UPHgYXoJCsivovtshD2I=w288-h640&quot; width=&quot;288&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;div contenteditable=&quot;false&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;Why This Matters (Strategically)&lt;/h2&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;This is bigger than just “cool mobile AI”.&lt;/p&gt;&lt;h3&gt;1. True Edge AI Is Finally Here&lt;/h3&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;We’re crossing a threshold:&lt;/p&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Before&lt;/td&gt;&lt;td&gt;Now&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cloud-only intelligence&lt;/td&gt;&lt;td&gt;Local-first viable&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Privacy tradeoffs&lt;/td&gt;&lt;td&gt;Fully private inference&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Latency issues&lt;/td&gt;&lt;td&gt;Instant response&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;API costs&lt;/td&gt;&lt;td&gt;Zero marginal cost&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;This changes:&lt;/p&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;Enterprise workflows&lt;/li&gt;&lt;li&gt;Personal productivity&lt;/li&gt;&lt;li&gt;Privacy models&lt;/li&gt;&lt;/ul&gt;&lt;div contenteditable=&quot;false&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h3&gt;2. Cost Model Disruption&lt;/h3&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;If you can run:&lt;/p&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;A &lt;strong&gt;good-enough model locally&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;With zero infra cost&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;Then:&lt;/p&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;Not every task needs GPT-5 / Claude Opus&lt;/li&gt;&lt;li&gt;You offload 70–80% of interactions locally&lt;/li&gt;&lt;li&gt;Cloud becomes &lt;em&gt;premium tier&lt;/em&gt;, not default&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;This is exactly the direction your current stack (OpenClaw + Ollama) is already heading—this just compresses it into mobile.&lt;/p&gt;&lt;div contenteditable=&quot;false&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h3&gt;3. UX Is the Real Breakthrough&lt;/h3&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;The real innovation here is not the model—it’s the &lt;strong&gt;delivery&lt;/strong&gt;.&lt;/p&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;Compare:&lt;/p&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Stack&lt;/td&gt;&lt;td&gt;Friction&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Ollama + CLI&lt;/td&gt;&lt;td&gt;Medium&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;OpenClaw + agents&lt;/td&gt;&lt;td&gt;High (powerful but complex)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Android Gemma app&lt;/td&gt;&lt;td&gt;Near zero&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;Who wins?&lt;br /&gt;The one users can install in 30 seconds.&lt;/p&gt;&lt;div contenteditable=&quot;false&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;Where It Fits in a Serious Stack&lt;/h2&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;Given your current setup (agentic + local infra), this opens interesting architecture options:&lt;/p&gt;&lt;h3&gt;Hybrid Model Strategy&lt;/h3&gt;&lt;ul data-spread=&quot;true&quot;&gt;&lt;li&gt;&lt;strong&gt;Mobile (Gemma 4)&lt;/strong&gt;&lt;br /&gt;→ Quick queries, notes, offline usage&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Local Desktop (Ollama / OpenClaw)&lt;/strong&gt;&lt;br /&gt;→ Agent workflows, automation, coding&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Cloud (Claude / GPT)&lt;/strong&gt;&lt;br /&gt;→ Heavy reasoning, critical tasks&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;This becomes a &lt;strong&gt;tiered inference system&lt;/strong&gt;:&lt;/p&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;Cheap → Fast → Local&lt;/li&gt;&lt;li&gt;Expensive → Smart → Cloud&lt;/li&gt;&lt;/ul&gt;&lt;div contenteditable=&quot;false&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;Limitations (Be Realistic)&lt;/h2&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;This is not magic.&lt;/p&gt;&lt;h3&gt;Constraints:&lt;/h3&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;Memory-bound → limited reasoning depth&lt;/li&gt;&lt;li&gt;Smaller parameter count → weaker abstraction&lt;/li&gt;&lt;li&gt;Likely struggles with:&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;complex code generation&lt;/li&gt;&lt;li&gt;financial modelling&lt;/li&gt;&lt;li&gt;deep system design&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Hidden Tradeoffs:&lt;/h3&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;Quantization artifacts&lt;/li&gt;&lt;li&gt;Potential hallucination under pressure&lt;/li&gt;&lt;li&gt;Performance tied to device hardware&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;Still—none of these are deal-breakers for its target use.&lt;/p&gt;&lt;div contenteditable=&quot;false&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;Opinionated Take&lt;/h2&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;This is the first time I’d say:&lt;/p&gt;&lt;blockquote&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;&lt;strong&gt;Local LLMs on mobile are no longer a gimmick.&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;We’re not at parity with cloud models—but we don’t need to be.&lt;/p&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;We just need:&lt;/p&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;70% of capability&lt;/li&gt;&lt;li&gt;0% latency&lt;/li&gt;&lt;li&gt;0% cost&lt;/li&gt;&lt;li&gt;100% privacy&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;And this hits that balance.&lt;/p&gt;&lt;div contenteditable=&quot;false&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;What I’d Do Next (If You Want to Push This Further)&lt;/h2&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;Given your background, this is where it gets interesting:&lt;/p&gt;&lt;h3&gt;1. Build a Mobile → Agent Bridge&lt;/h3&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;Phone handles prompts locally&lt;/li&gt;&lt;li&gt;Escalates complex tasks to your OpenClaw backend&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;2. Local RAG on Mobile&lt;/h3&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;Index notes / PDFs on-device&lt;/li&gt;&lt;li&gt;Use Gemma as query layer&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;3. Trading / Quant Use Case (Lightweight)&lt;/h3&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;Quick portfolio queries&lt;/li&gt;&lt;li&gt;Market summaries (cached locally)&lt;/li&gt;&lt;li&gt;Decision journaling&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;4. Telegram Bot + Mobile LLM Hybrid&lt;/h3&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;Local inference first&lt;/li&gt;&lt;li&gt;Cloud fallback only when needed&lt;/li&gt;&lt;/ul&gt;&lt;div contenteditable=&quot;false&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;Final Verdict&lt;/h2&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Dimension&lt;/td&gt;&lt;td&gt;Score&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Innovation&lt;/td&gt;&lt;td&gt;8/10&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Practicality&lt;/td&gt;&lt;td&gt;9/10&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Performance&lt;/td&gt;&lt;td&gt;7.5/10&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;UX&lt;/td&gt;&lt;td&gt;9.5/10&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Strategic impact&lt;/td&gt;&lt;td&gt;9/10&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;&lt;strong&gt;This is the direction everything is going.&lt;/strong&gt;&lt;/p&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;Not bigger models.&lt;br /&gt;Not more GPUs.&lt;/p&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;&lt;strong&gt;Smarter distribution of intelligence across edge + cloud.&lt;/strong&gt;&lt;/p&gt;&lt;div contenteditable=&quot;false&quot;&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;Closing Thought&lt;/h2&gt;&lt;p class=&quot;isSelectedEnd&quot;&gt;If this trajectory continues:&lt;/p&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;Your phone becomes your &lt;strong&gt;primary AI interface&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Your laptop becomes your &lt;strong&gt;agent orchestration layer&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;The cloud becomes &lt;strong&gt;optional, not required&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;That’s a very different world than where we were even 12 months ago.&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2026/04/running-gemma-4-locally-on-android.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJ9p5zMtIkYPtQvrHr9uN631YM4acYRXJQhEattigQMynJtERXnItMH969TEa2SDpdxCOBDAFOsq_BXI6UqoKtmAZTCSzeRkP5gSNTX2bxu7i4VimncFVh-6kG_F7RrxAHaocrueORRwNPofHqhVcbqG3UYpWzE_yX_PB4rk3Jwyi9rQaBEjEiV6_tvW8/s72-c/ChatGPT%20Image%20Apr%207,%202026,%2008_18_31%20AM.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-4634772013518407785</guid><pubDate>Sun, 05 Apr 2026 13:31:00 +0000</pubDate><atom:updated>2026-04-05T15:44:00.433+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">MCP Client</category><category domain="http://www.blogger.com/atom/ns#">MCP Server</category><category domain="http://www.blogger.com/atom/ns#">openAI</category><category domain="http://www.blogger.com/atom/ns#">Python</category><title>Build a Simple MCP Agent with OpenAI: Describe and OCR Local Images</title><description>&lt;p&gt;&lt;span face=&quot;-apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif&quot; style=&quot;font-size: 14px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGEOsahzZJdHXGtOmONmo7SBtOeLHZ068Ok4p1r_XBHBlRg1YFIce7DyfY88vbIO9799DOaK7XB68AaxMbTH8qzvnXawUvOHwBZPXTmbZQT5Pm93yopDQoJ5B8iZY3i-o6pByjS8uYCvoWZSt8g0IrRW4P3Gw7bgYSOoHDwUWWv5Q4Ra0kRjwQb7NSlsE/s1536/ChatGPT%20Image%20Apr%205,%202026,%2002_29_25%20PM.png&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1536&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGEOsahzZJdHXGtOmONmo7SBtOeLHZ068Ok4p1r_XBHBlRg1YFIce7DyfY88vbIO9799DOaK7XB68AaxMbTH8qzvnXawUvOHwBZPXTmbZQT5Pm93yopDQoJ5B8iZY3i-o6pByjS8uYCvoWZSt8g0IrRW4P3Gw7bgYSOoHDwUWWv5Q4Ra0kRjwQb7NSlsE/s320/ChatGPT%20Image%20Apr%205,%202026,%2002_29_25%20PM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;If you want a practical MCP example that is easy to run and easy to explain, image analysis is a great fit.&lt;p&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;4&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;4&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Source code:&amp;nbsp;&lt;a href=&quot;https://github.com/JordiCorbilla/mcp-image-analysis-agent&quot;&gt;JordiCorbilla/mcp-image-analysis-agent: mcp-image-analysis-agent&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;4&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;4&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;4&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;4&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;In this walkthrough, we will build a minimal Model Context Protocol (MCP) setup:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;6&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;6&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;An MCP server that exposes tools to list images, download files, and analyse local images&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;7&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;An MCP client that uses OpenAI tool-calling to decide when to call that tool&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;8&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;A CLI flow that returns both:&lt;ul class=&quot;code-line&quot; data-line=&quot;9&quot; dir=&quot;auto&quot; style=&quot;margin-bottom: 0px; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;9&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;image description&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;10&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;extracted text (OCR)&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;12&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This example is intentionally small so you can publish it, teach it, and extend it quickly.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;14&quot; dir=&quot;auto&quot; id=&quot;what-you-will-build&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;What You Will Build&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;16&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;You will run two Python programs:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;18&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;18&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;server.py&lt;/code&gt;: MCP server with practical tools for image workflows&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;19&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;client.py&lt;/code&gt;: OpenAI-powered agentic client that connects to the server and can invoke MCP tools&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;21&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The interesting part is the split of responsibilities:&lt;/p&gt;&lt;ol class=&quot;code-line&quot; data-line=&quot;23&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;23&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The client handles conversation, model calls, and tool orchestration.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;24&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The server handles local file access safely and image analysis logic.&lt;/li&gt;&lt;/ol&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;26&quot; dir=&quot;auto&quot; id=&quot;why-this-is-a-good-mcp-example&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Why This Is a Good MCP Example&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;28&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;MCP is most useful when a model needs controlled access to local capabilities. In this project:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;30&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;30&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The model cannot directly read files.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;31&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The model must call a tool exposed by your MCP server.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;32&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The server validates paths and performs the action.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;34&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That makes this architecture both practical and safer than giving direct unrestricted local access.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;36&quot; dir=&quot;auto&quot; id=&quot;prerequisites&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Prerequisites&lt;/h2&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;38&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;38&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Python 3.10+&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;39&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;OpenAI API key&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;40&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;A local image file in the lab folder (for example&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;sample.png&lt;/code&gt;)&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;42&quot; dir=&quot;auto&quot; id=&quot;project-files&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Project Files&lt;/h2&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;44&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;44&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;client.py&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;45&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;server.py&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;46&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;requirements.txt&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;48&quot; dir=&quot;auto&quot; id=&quot;install-dependencies&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Install Dependencies&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;50&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;From the lab folder:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-powershell&quot; data-line=&quot;52&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;python &lt;span class=&quot;hljs-literal&quot;&gt;-m&lt;/span&gt; pip install &lt;span class=&quot;hljs-literal&quot;&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;56&quot; dir=&quot;auto&quot; id=&quot;set-environment-variables&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Set Environment Variables&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;58&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Use a&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;.env&lt;/code&gt;&amp;nbsp;file in the same folder:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-env&quot; data-line=&quot;60&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;OPENAI_API_KEY=sk-your-key-here
OPENAI_MODEL=gpt-4.1-mini
# Optional override for server-side vision model
OPENAI_VISION_MODEL=gpt-4.1-mini
&lt;/code&gt;&lt;/pre&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;67&quot; dir=&quot;auto&quot; id=&quot;run-the-demo&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Run the Demo&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;69&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Start the client and point it to the server:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-powershell&quot; data-line=&quot;71&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;python .\client.py .\server.py
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;75&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;From the menu:&lt;/p&gt;&lt;ol class=&quot;code-line&quot; data-line=&quot;77&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;77&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Choose&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;Analyse Local Image (Description + OCR)&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;78&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Enter an image path relative to the project folder&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;79&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Optionally add a focus instruction (for example:&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;extract receipt totals&lt;/code&gt;)&lt;/li&gt;&lt;/ol&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;81&quot; dir=&quot;auto&quot; id=&quot;how-the-agentic-loop-works&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;How the Agentic Loop Works&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;83&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The client does this repeatedly:&lt;/p&gt;&lt;ol class=&quot;code-line&quot; data-line=&quot;85&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;85&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Sends user query + available MCP tools to OpenAI.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;86&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;If model requests tool calls, client executes them through MCP.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;87&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Tool results are appended back into the conversation.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;88&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Model returns final answer.&lt;/li&gt;&lt;/ol&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;90&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That means the model decides&amp;nbsp;&lt;em&gt;when&lt;/em&gt;&amp;nbsp;to use tools, while your server decides&amp;nbsp;&lt;em&gt;how&lt;/em&gt;&amp;nbsp;tools operate.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;92&quot; dir=&quot;auto&quot; id=&quot;core-mcp-tools&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Core MCP Tools&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;94&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The server exposes these core tools:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;96&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;96&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;list_images_in_current_folder()&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;97&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;download_file_from_url(url, output_path=&quot;&quot;)&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;98&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;analyze_local_image(file_path, focus=&quot;&quot;)&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 class=&quot;code-line&quot; data-line=&quot;100&quot; dir=&quot;auto&quot; id=&quot;1-list-images-in-the-current-folder&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 1.25em; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; position: relative;&quot;&gt;1) List Images in the Current Folder&lt;/h3&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;102&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Use this when you want the agent to discover available images before analysis.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;104&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Example request:&lt;/p&gt;&lt;blockquote class=&quot;code-line&quot; data-line=&quot;106&quot; dir=&quot;auto&quot; style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(34, 34, 34); border-color: rgba(0, 122, 204, 0.5); border-left-style: solid; border-left-width: 5px; border-radius: 2px; font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin: 0px; padding: 0px 16px 0px 10px; position: relative;&quot;&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;106&quot; dir=&quot;auto&quot; style=&quot;margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;span style=&quot;color: white;&quot;&gt;List images available in this folder.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;108&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Returned data includes:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;110&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;110&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;count&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;111&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;images[]&lt;/code&gt;&amp;nbsp;with name, relative path, size, and modified timestamp&lt;/li&gt;&lt;/ul&gt;&lt;h3 class=&quot;code-line&quot; data-line=&quot;113&quot; dir=&quot;auto&quot; id=&quot;2-download-a-file-from-a-url&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 1.25em; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; position: relative;&quot;&gt;2) Download a File from a URL&lt;/h3&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;115&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Use this to bring an image into your project folder before analysis.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;117&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Example request:&lt;/p&gt;&lt;blockquote class=&quot;code-line&quot; data-line=&quot;119&quot; dir=&quot;auto&quot; style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(34, 34, 34); border-color: rgba(0, 122, 204, 0.5); border-left-style: solid; border-left-width: 5px; border-radius: 2px; font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin: 0px; padding: 0px 16px 0px 10px; position: relative;&quot;&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;119&quot; dir=&quot;auto&quot; style=&quot;margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;span style=&quot;color: white;&quot;&gt;Download&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;https://example.com/photo.jpg&lt;/code&gt;&amp;nbsp;to&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;downloads/photo.jpg&lt;/code&gt;.&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;121&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The tool restricts downloads to&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;http&lt;/code&gt;/&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;https&lt;/code&gt;&amp;nbsp;and saves only inside your project directory.&lt;/p&gt;&lt;h3 class=&quot;code-line&quot; data-line=&quot;123&quot; dir=&quot;auto&quot; id=&quot;3-analyze-a-local-image&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 1.25em; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; position: relative;&quot;&gt;3) Analyse a Local Image&lt;/h3&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;125&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This performs description + OCR in one pass.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;127&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Behaviour:&lt;/p&gt;&lt;ol class=&quot;code-line&quot; data-line=&quot;129&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;129&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Validates the path stays inside the project directory.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;130&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Checks that the file exists and is a supported image format.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;131&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Converts image bytes to a data URL.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;132&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Sends image + instruction to OpenAI Vision.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;133&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Returns structured output:&lt;ul class=&quot;code-line&quot; data-line=&quot;134&quot; dir=&quot;auto&quot; style=&quot;margin-bottom: 0px; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;134&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;description&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;135&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;extracted_text&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;136&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;notable_details&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;138&quot; dir=&quot;auto&quot; id=&quot;example-prompt&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Example Prompt&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;140&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;User asks:&lt;/p&gt;&lt;blockquote class=&quot;code-line&quot; data-line=&quot;142&quot; dir=&quot;auto&quot; style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(34, 34, 34); border-color: rgba(0, 122, 204, 0.5); border-left-style: solid; border-left-width: 5px; border-radius: 2px; font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin: 0px; padding: 0px 16px 0px 10px; position: relative;&quot;&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;142&quot; dir=&quot;auto&quot; style=&quot;margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;span style=&quot;color: white;&quot;&gt;Analyze image&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;sample.png&lt;/code&gt;. Use MCP tools. Return a short description, extracted text, and two useful&lt;/span&gt; follow-up actions.&lt;/p&gt;&lt;/blockquote&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;144&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;You can also run a&lt;br /&gt; two-step flow:&lt;/p&gt;&lt;ol class=&quot;code-line&quot; data-line=&quot;146&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;146&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Ask the agent to list images with&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;list_images_in_current_folder&lt;/code&gt;.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;147&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Pick one and ask the agent to analyse it with&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;analyze_local_image&lt;/code&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;149&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Possible response:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;151&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;151&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Description: A photographed whiteboard with sprint tasks and dates.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;152&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Extracted text: &quot;Sprint 12, Demo Friday, Fix auth timeout&quot;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;153&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Follow-up actions:&lt;ul class=&quot;code-line&quot; data-line=&quot;154&quot; dir=&quot;auto&quot; style=&quot;margin-bottom: 0px; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;154&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Convert items into a task checklist.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;155&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Create calendar reminders for deadline lines.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;157&quot; dir=&quot;auto&quot; id=&quot;error-handling-included&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Error Handling Included&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;159&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This sample handles common failures cleanly:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;161&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;161&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Missing&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;OPENAI_API_KEY&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;162&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Invalid image path&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;163&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Path traversal attempts outside project folder&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;164&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Unsupported image extension&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;165&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Non-JSON model output fallback&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;167&quot; dir=&quot;auto&quot; id=&quot;security-notes&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Security Notes&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;169&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Even in a demo, do not skip path validation.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;171&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The server uses a safe resolver that enforces all tool file access under the project root. This prevents prompts from tricking your tool into reading arbitrary files outside the workspace.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;173&quot; dir=&quot;auto&quot; id=&quot;cost-and-model-tips&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Cost and Model Tips&lt;/h2&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;175&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;175&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Start with&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;gpt-4.1-mini&lt;/code&gt;&amp;nbsp;for lower cost.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;176&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;For higher OCR fidelity, test a stronger model in&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;OPENAI_VISION_MODEL&lt;/code&gt;.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;177&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Keep outputs concise to reduce tokens.&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;179&quot; dir=&quot;auto&quot; id=&quot;troubleshooting&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Troubleshooting&lt;/h2&gt;&lt;h3 class=&quot;code-line&quot; data-line=&quot;181&quot; dir=&quot;auto&quot; id=&quot;openai_api_key-is-not-set&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 1.25em; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;OPENAI_API_KEY is not set&lt;/code&gt;&lt;/h3&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;183&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Add the key to&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;.env&lt;/code&gt;&amp;nbsp;or set it in your shell, then restart.&lt;/p&gt;&lt;h3 class=&quot;code-line&quot; data-line=&quot;185&quot; dir=&quot;auto&quot; id=&quot;image-file-not-found&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 1.25em; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;Image file not found&lt;/code&gt;&lt;/h3&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;187&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Use a path relative to the project folder where you run the client.&lt;/p&gt;&lt;h3 class=&quot;code-line&quot; data-line=&quot;189&quot; dir=&quot;auto&quot; id=&quot;unsupported-image-extension&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 1.25em; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;Unsupported image extension&lt;/code&gt;&lt;/h3&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;191&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Use one of:&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;.png&lt;/code&gt;,&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;.jpg&lt;/code&gt;,&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;.jpeg&lt;/code&gt;,&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;.webp&lt;/code&gt;,&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;.gif&lt;/code&gt;,&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;.bmp&lt;/code&gt;,&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;.tif&lt;/code&gt;,&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;.tiff&lt;/code&gt;.&lt;/p&gt;&lt;h3 class=&quot;code-line&quot; data-line=&quot;193&quot; dir=&quot;auto&quot; id=&quot;no-ocr-text-returned&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 1.25em; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; position: relative;&quot;&gt;No OCR text returned&lt;/h3&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;195&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Some images contain no readable text or low-quality text. Try a clearer image or add focus guidance.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;197&quot; dir=&quot;auto&quot; id=&quot;extension-ideas&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Extension Ideas&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;199&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Once this baseline works, you can add:&lt;/p&gt;&lt;ol class=&quot;code-line&quot; data-line=&quot;201&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;201&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Batch image processing folder mode.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;202&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Structured OCR outputs (fields for receipts/invoices/forms).&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;203&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;A second tool to summarize OCR into action items.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;204&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Markdown report generation from analysis results.&lt;/li&gt;&lt;/ol&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;206&quot; dir=&quot;auto&quot; id=&quot;final-thoughts&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Final Thoughts&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;208&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This project is a compact, publishable MCP example that demonstrates real agent behaviour:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;210&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;210&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;OpenAI handles reasoning and tool decisions.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;211&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;MCP exposes local capabilities safely.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;212&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;You get immediately useful output from local images.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;214&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If you are teaching MCP, this is a strong first demo before moving on to multi-tool, multi-agent workflows.&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2026/04/build-simple-mcp-agent-with-openai.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGEOsahzZJdHXGtOmONmo7SBtOeLHZ068Ok4p1r_XBHBlRg1YFIce7DyfY88vbIO9799DOaK7XB68AaxMbTH8qzvnXawUvOHwBZPXTmbZQT5Pm93yopDQoJ5B8iZY3i-o6pByjS8uYCvoWZSt8g0IrRW4P3Gw7bgYSOoHDwUWWv5Q4Ra0kRjwQb7NSlsE/s72-c/ChatGPT%20Image%20Apr%205,%202026,%2002_29_25%20PM.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-5715822006633499128</guid><pubDate>Sun, 29 Mar 2026 15:48:00 +0000</pubDate><atom:updated>2026-03-29T15:48:08.367+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">csv</category><category domain="http://www.blogger.com/atom/ns#">large files</category><category domain="http://www.blogger.com/atom/ns#">Productivity</category><category domain="http://www.blogger.com/atom/ns#">Python</category><title>Rowlens: finding one exact CSV row in a 17 GB file without blowing up memory</title><description>&lt;p&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh65WHkeBnAjL5weuaCEMa6-79CTBp-mtp460lrmlox5wACR4yRgotXMsSr6HZg_0ygZV3Bge4m-2g6V2PpWw-4eFQ1faVecOEINkSInifyqmelIQgNnqB2v9MdEhkdkTG0YJxHK7VX_lcN0M2O4LEP860qESO1QsWqzTAzZdgGiBjDkF3Iva2wM72CeX4/s1536/ChatGPT%20Image%20Mar%2029,%202026,%2004_47_38%20PM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1536&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh65WHkeBnAjL5weuaCEMa6-79CTBp-mtp460lrmlox5wACR4yRgotXMsSr6HZg_0ygZV3Bge4m-2g6V2PpWw-4eFQ1faVecOEINkSInifyqmelIQgNnqB2v9MdEhkdkTG0YJxHK7VX_lcN0M2O4LEP860qESO1QsWqzTAzZdgGiBjDkF3Iva2wM72CeX4/s320/ChatGPT%20Image%20Mar%2029,%202026,%2004_47_38%20PM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;Most CSV tooling assumes the file is still small enough to load, sort, or inspect interactively. That breaks down fast once the file is measured in gigabytes.&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;rowlens&lt;/code&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px;&quot;&gt;was built for the opposite case: a CLI you can point at a very large CSV when you already know the clues you are looking for and you want the exact matching rows back in a readable format.&lt;/span&gt;&lt;p&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;4&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The design constraint was simple: never read the full file into memory.&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;&lt;a href=&quot;https://github.com/JordiCorbilla/rowlens&quot;&gt;rowlens&lt;/a&gt;&lt;/code&gt;&amp;nbsp;opens the CSV as a stream, reads the header once, and then processes each record one at a time with Python&#39;s standard&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;csv&lt;/code&gt;&amp;nbsp;reader. That keeps memory usage effectively flat even when the input file is 17 GB or more. The tool does not build an index, cache rows, or attempt an in-memory dataframe workflow. It just walks the file once and stops early if you set&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;--max-results&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;6&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The matching model is intentionally narrow for version 1.0. Repeated&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;--keyword&lt;/code&gt;&amp;nbsp;arguments are treated as exact cell-value matches. Repeated&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;--filter&lt;/code&gt;&amp;nbsp;arguments are treated as substring checks. A row is returned only when it satisfies every supplied condition. That gives a useful two-stage search pattern in practice: use&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;--keyword&lt;/code&gt;&amp;nbsp;for the hard identifier, then&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;--filter&lt;/code&gt;&amp;nbsp;to narrow the surrounding context.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;8&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Example:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-bash&quot; data-line=&quot;10&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;rowlens --file &lt;span class=&quot;hljs-string&quot;&gt;&quot;huge.csv&quot;&lt;/span&gt; --keyword &lt;span class=&quot;hljs-string&quot;&gt;&quot;1213131&quot;&lt;/span&gt; --filter &lt;span class=&quot;hljs-string&quot;&gt;&quot;AAA&quot;&lt;/span&gt; --output &lt;span class=&quot;hljs-string&quot;&gt;&quot;results.txt&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;14&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If a row matches, the output is not dumped as raw CSV. Instead,&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;rowlens&lt;/code&gt;&amp;nbsp;renders a bordered CLI report with summary metadata at the top and then a per-match table that lists each column name beside its value. That matters more than it sounds. When you are debugging a production extract or validating a record in an enormous export, the important part is not just finding the row. The important part is understanding the row immediately.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;16&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That output style was inspired by terminal-first tooling such as&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;csv-stream-diff&lt;/code&gt;: strong borders, obvious sections, and tabular structure that still works in plain text when redirected to a file. The same rendered report can be written with&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;--output&lt;/code&gt;, which makes it easy to attach to a ticket, share in chat, or keep as a trace artifact from an investigation.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;18&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Under the hood, Python was a good fit here because the problem is mostly streaming IO plus deterministic row checks. The package is structured with Poetry from the start so it can be shipped cleanly to PyPI. The CLI entry point lives behind the&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;rowlens&lt;/code&gt;&amp;nbsp;console script, dependencies are minimal, and the test suite covers the main behaviors: combined keyword and filter matching, case-insensitive search, support for extra cells beyond the header, and output-file generation.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;20&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Version 1.0 is deliberately focused. It solves one job well: find the exact row you care about in a file too large for the usual tools, then present it cleanly. Future iterations could add column-scoped matching, JSON output, compressed input support, or richer summary stats. But the first release already hits the core operational need: streaming search for massive CSVs with output that humans can read immediately.&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2026/03/rowlens-finding-one-exact-csv-row-in-17.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh65WHkeBnAjL5weuaCEMa6-79CTBp-mtp460lrmlox5wACR4yRgotXMsSr6HZg_0ygZV3Bge4m-2g6V2PpWw-4eFQ1faVecOEINkSInifyqmelIQgNnqB2v9MdEhkdkTG0YJxHK7VX_lcN0M2O4LEP860qESO1QsWqzTAzZdgGiBjDkF3Iva2wM72CeX4/s72-c/ChatGPT%20Image%20Mar%2029,%202026,%2004_47_38%20PM.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-3076618669400551702</guid><pubDate>Sat, 28 Mar 2026 22:00:00 +0000</pubDate><atom:updated>2026-03-28T22:00:09.637+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">csv</category><category domain="http://www.blogger.com/atom/ns#">exporter</category><category domain="http://www.blogger.com/atom/ns#">Python</category><category domain="http://www.blogger.com/atom/ns#">SQL Server</category><title>Building sqlcsv-exporter: a Python CLI to Stream SQL Server Data into CSV</title><description>&lt;p&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlUQQvA389uHODF4CmfDMdiRASbUCsVe8lLxJIbpR5KZENbaZWk2geoH9uDaR9UMAPv18gPioTmBfBIfOh1ITv0rTYFzIlobRdOdBS87T2MT5p_GgdxwMeV5Qc6QMYnklHDvntq_LNwZN2r2kIFoNsCrkOtJ9fZwb9m-dmh0YsKzsho61dLGdKdTk9DSc/s1536/ChatGPT%20Image%20Mar%2028,%202026,%2009_58_18%20PM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1536&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlUQQvA389uHODF4CmfDMdiRASbUCsVe8lLxJIbpR5KZENbaZWk2geoH9uDaR9UMAPv18gPioTmBfBIfOh1ITv0rTYFzIlobRdOdBS87T2MT5p_GgdxwMeV5Qc6QMYnklHDvntq_LNwZN2r2kIFoNsCrkOtJ9fZwb9m-dmh0YsKzsho61dLGdKdTk9DSc/s320/ChatGPT%20Image%20Mar%2028,%202026,%2009_58_18%20PM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;I recently built a small Python package called&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;&lt;a href=&quot;https://github.com/JordiCorbilla/sqlcsv-exporter&quot;&gt;sqlcsv-exporter&lt;/a&gt;&lt;/code&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px;&quot;&gt;to solve a very practical problem: export the results of a SQL Server query into a CSV file, from the command line, without dragging an entire dataset into memory.&lt;/span&gt;&lt;p&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;4&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The tool is packaged as a Poetry project, designed to be publishable to PyPI, and focused on a workflow that is common in reporting and data operations teams:&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;4&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;6&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;6&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;store the SQL in a&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;.sql&lt;/code&gt;&amp;nbsp;file&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;7&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;pass connection details at runtime&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;8&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;stream rows in chunks&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;9&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;write the CSV incrementally&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;10&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;show visible progress while the job runs&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;12&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This post walks through why I built it, how it works, and what I learned while testing it against a local SQL Server instance.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;14&quot; dir=&quot;auto&quot; id=&quot;the-problem&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;The Problem&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;16&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The starting point was familiar: a one-off Python script that connected to SQL Server, ran a query, and wrote a CSV. That works for quick experiments, but it gets messy fast.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;18&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Typical issues show up immediately:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;20&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;20&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;query text is embedded inside Python instead of living in a&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;.sql&lt;/code&gt;&amp;nbsp;file&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;21&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;authentication logic is mixed into export logic&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;22&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;the script is hard to reuse across environments&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;23&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;large result sets become memory-heavy&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;24&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;the UX is poor when a query runs for several seconds or several minutes&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;26&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The goal was to replace that with a proper CLI package that feels like a real tool rather than a disposable script.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;28&quot; dir=&quot;auto&quot; id=&quot;the-goal&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;The Goal&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;30&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;I wanted a command like this:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-powershell&quot; data-line=&quot;32&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;sqlcsv&lt;span class=&quot;hljs-literal&quot;&gt;-exporter&lt;/span&gt; `
  &lt;span class=&quot;hljs-literal&quot;&gt;--sql&lt;/span&gt; .\queries\report.sql `
  &lt;span class=&quot;hljs-literal&quot;&gt;--output&lt;/span&gt; .\exports\report.csv `
  &lt;span class=&quot;hljs-literal&quot;&gt;--server&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;DESKTOP-TTUSQLJ\SQLEXPRESS&quot;&lt;/span&gt; `
  &lt;span class=&quot;hljs-literal&quot;&gt;--database&lt;/span&gt; QuantDevTest
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;40&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The requirements were straightforward:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;42&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;42&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;read SQL from file&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;43&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;connect to SQL Server with trusted auth or SQL auth&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;44&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;stream the result set with&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;fetchmany()&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;45&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;write CSV rows incrementally&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;46&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;avoid pandas for the write path&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;47&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;provide useful progress output&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;48&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;keep the package testable and publishable&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;50&quot; dir=&quot;auto&quot; id=&quot;the-package-structure&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;The Package Structure&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;52&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;I split the project into small modules so each concern is isolated:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;54&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;54&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;cli.py&lt;/code&gt;: argument parsing and process exit behavior&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;55&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;config.py&lt;/code&gt;: config model and validation&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;56&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;connection.py&lt;/code&gt;: ODBC connection string and connection creation&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;57&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;sql_rewriter.py&lt;/code&gt;: SQL file loading and optional variable rewriting&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;58&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;exporter.py&lt;/code&gt;: query execution, chunked fetch, CSV writing, and progress rendering&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;60&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That separation matters because it makes the code easier to test. The CSV writer can be tested without a database. The SQL rewriting logic can be tested with simple strings. The export workflow can be tested with mocked connections and cursors.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;62&quot; dir=&quot;auto&quot; id=&quot;why-streaming-matters&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Why Streaming Matters&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;64&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The core design choice was to stream results instead of loading everything into memory.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;66&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The exporter:&lt;/p&gt;&lt;ol class=&quot;code-line&quot; data-line=&quot;68&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;68&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;opens the SQL file&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;69&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;optionally rewrites a declared date variable such as&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;@InAsOfDate&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;70&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;executes the query with&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;pyodbc&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;71&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;fetches rows in chunks&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;72&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;writes each chunk directly to a CSV file&lt;/li&gt;&lt;/ol&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;74&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That keeps the memory profile stable even when the result set gets large.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;76&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Instead of doing something like this:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-python&quot; data-line=&quot;78&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;rows = cursor.fetchall()
writer.writerows(rows)
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;83&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;the tool does this conceptually:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-python&quot; data-line=&quot;85&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;True&lt;/span&gt;:
    rows = cursor.fetchmany(chunk_size)
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;not&lt;/span&gt; rows:
        &lt;span class=&quot;hljs-keyword&quot;&gt;break&lt;/span&gt;
    writer.writerows(rows)
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;93&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That small change is the difference between a toy exporter and something you can trust on real datasets.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;95&quot; dir=&quot;auto&quot; id=&quot;a-better-cli-experience&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;A Better CLI Experience&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;97&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Command-line tools that do database work have one recurring UX problem: silence. If a query takes ten seconds, users start wondering whether the command is stuck, blocked, or dead.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;99&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;So the exporter prints:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;101&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;101&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;a short run summary before execution&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;102&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;detected column count after the query starts returning metadata&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;103&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;a live progress display while rows are written&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;104&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;a final completion summary with rows, columns, file size, and elapsed time&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;106&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;I used&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;rich&lt;/code&gt;&amp;nbsp;for the terminal output instead of hand-rolled&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;print()&lt;/code&gt;&amp;nbsp;calls. It gives the tool a more deliberate interface without turning it into a full TUI.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;108&quot; dir=&quot;auto&quot; id=&quot;handling-sql-as-a-file&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Handling SQL as a File&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;110&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;One design decision I liked immediately was keeping SQL in a separate file. That brings a few benefits:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;112&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;112&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;the query is easier to inspect and review&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;113&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;analysts or DBAs can edit SQL without touching Python code&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;114&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;the same CLI can be reused for many exports&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;115&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;long or multi-step SQL scripts remain readable&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;117&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;For example, this simple test query lives in its own file:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-sql&quot; data-line=&quot;119&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;SELECT&lt;/span&gt; TOP (&lt;span class=&quot;hljs-number&quot;&gt;1000&lt;/span&gt;)
    [Id],
    [Stock],
    [LowPrice],
    [MaxPrice],
    [AvgPrice]
&lt;span class=&quot;hljs-keyword&quot;&gt;FROM&lt;/span&gt; [QuantDevTest].[dbo].[StockMetrics];
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;129&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That file can be passed directly to the CLI without changing the Python package at all.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;131&quot; dir=&quot;auto&quot; id=&quot;optional-sql-rewriting&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Optional SQL Rewriting&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;133&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Some teams keep parameter values in SQL scripts using a declared variable like:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-sql&quot; data-line=&quot;135&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;DECLARE&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;@InAsOfDate&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;DATE&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&#39;2026-03-01&#39;&lt;/span&gt;;
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;139&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;To support that style, the tool includes a lightweight SQL rewriting step. If the variable exists, the CLI can replace its value based on&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;--date&lt;/code&gt;. If the variable does not exist, the SQL runs unchanged.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;141&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This keeps the interface flexible:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;143&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;143&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;SQL files can remain mostly static&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;144&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;operators can adjust date-driven exports without manually editing the file&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;146&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;It is intentionally narrow in scope. It is not trying to become a generic SQL templating engine.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;148&quot; dir=&quot;auto&quot; id=&quot;a-real-world-bug-caught-by-live-testing&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;A Real-World Bug Caught by Live Testing&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;150&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The first end-to-end run against my local SQL Server instance did not succeed.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;152&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The CLI connected successfully, but then failed with:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-text&quot; data-line=&quot;154&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;AttributeError: &#39;pyodbc.Cursor&#39; object has no attribute &#39;timeout&#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;158&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That was useful. The mocked tests passed, but the live run exposed a compatibility assumption in the code. Some&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;pyodbc&lt;/code&gt;&amp;nbsp;environments expose timeout behavior differently.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;160&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The fix was simple:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;162&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;162&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;try setting the timeout on the cursor if that attribute exists&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;163&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;otherwise fall back to the connection object&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;165&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That is exactly why live testing matters. A tool can look clean in unit tests and still fail in a real environment for reasons that only surface with the actual driver and actual database connection.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;167&quot; dir=&quot;auto&quot; id=&quot;stress-testing-with-a-slower-query&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Stress Testing with a Slower Query&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;169&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;A fast export is not a great demo for progress reporting, so I added a dedicated slow test query.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;171&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;It:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;173&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;173&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;copies base rows into a temp table&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;174&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;creates two multiplier temp tables&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;175&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;cross joins them to expand the result set&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;176&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;adds a short&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;WAITFOR DELAY&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;178&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That makes it easy to simulate a longer-running export without needing a large production-sized table.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;180&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Conceptually, the query looks like this:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-sql&quot; data-line=&quot;182&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;SELECT&lt;/span&gt; TOP (&lt;span class=&quot;hljs-number&quot;&gt;1000&lt;/span&gt;) ... &lt;span class=&quot;hljs-keyword&quot;&gt;INTO&lt;/span&gt; #BaseStockMetrics ...
&lt;span class=&quot;hljs-keyword&quot;&gt;SELECT&lt;/span&gt; TOP (&lt;span class=&quot;hljs-number&quot;&gt;200&lt;/span&gt;) ... &lt;span class=&quot;hljs-keyword&quot;&gt;INTO&lt;/span&gt; #MultiplierA ...
&lt;span class=&quot;hljs-keyword&quot;&gt;SELECT&lt;/span&gt; TOP (&lt;span class=&quot;hljs-number&quot;&gt;200&lt;/span&gt;) ... &lt;span class=&quot;hljs-keyword&quot;&gt;INTO&lt;/span&gt; #MultiplierB ...

WAITFOR DELAY &lt;span class=&quot;hljs-string&quot;&gt;&#39;00:00:03&#39;&lt;/span&gt;;

&lt;span class=&quot;hljs-keyword&quot;&gt;SELECT&lt;/span&gt; ...
&lt;span class=&quot;hljs-keyword&quot;&gt;FROM&lt;/span&gt; #BaseStockMetrics &lt;span class=&quot;hljs-keyword&quot;&gt;AS&lt;/span&gt; b
&lt;span class=&quot;hljs-keyword&quot;&gt;CROSS&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;JOIN&lt;/span&gt; #MultiplierA &lt;span class=&quot;hljs-keyword&quot;&gt;AS&lt;/span&gt; a
&lt;span class=&quot;hljs-keyword&quot;&gt;CROSS&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;JOIN&lt;/span&gt; #MultiplierB &lt;span class=&quot;hljs-keyword&quot;&gt;AS&lt;/span&gt; c;
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;195&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;With a small base table, this is enough to create a large CSV and make the progress output visible. With a large base table, it can scale very aggressively, so it is the kind of test query you use carefully.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;197&quot; dir=&quot;auto&quot; id=&quot;testing-strategy&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Testing Strategy&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;199&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The package includes pytest coverage around the parts that are most likely to break:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;201&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;201&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;config validation&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;202&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;date resolution&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;203&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;SQL file loading&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;204&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;SQL variable rewriting&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;205&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;connection string generation&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;206&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;CSV writing&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;207&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;mocked end-to-end export behavior&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;209&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That balance is important. Not every code path needs a deep integration test, but the components that define correctness should have coverage.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;211&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The live SQL Server run complements those tests by exercising:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;213&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;213&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;the actual ODBC driver&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;214&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;the actual SQL Server instance&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;215&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;actual filesystem writes&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;216&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;real CLI behavior&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;218&quot; dir=&quot;auto&quot; id=&quot;packaging-for-pypi&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Packaging for PyPI&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;220&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The project is set up as a Poetry package and versioned as&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;1.0.1&lt;/code&gt;.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;222&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Build artifacts are created with:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-powershell&quot; data-line=&quot;224&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;poetry build
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;228&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That produces:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;230&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;230&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;a source distribution&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;231&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;a wheel&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;233&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Those are the exact artifacts needed for publishing to PyPI.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;235&quot; dir=&quot;auto&quot; id=&quot;example-usage&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Example Usage&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;237&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Trusted connection:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-powershell&quot; data-line=&quot;239&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;poetry run sqlcsv&lt;span class=&quot;hljs-literal&quot;&gt;-exporter&lt;/span&gt; `
  &lt;span class=&quot;hljs-literal&quot;&gt;--sql&lt;/span&gt; .\queries\stock_metrics_top_1000.sql `
  &lt;span class=&quot;hljs-literal&quot;&gt;--output&lt;/span&gt; .\exports\stock_metrics_top_1000.csv `
  &lt;span class=&quot;hljs-literal&quot;&gt;--server&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;DESKTOP-TTUSQLJ\SQLEXPRESS&quot;&lt;/span&gt; `
  &lt;span class=&quot;hljs-literal&quot;&gt;--database&lt;/span&gt; QuantDevTest
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;247&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;SQL authentication:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-powershell&quot; data-line=&quot;249&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;poetry run sqlcsv&lt;span class=&quot;hljs-literal&quot;&gt;-exporter&lt;/span&gt; `
  &lt;span class=&quot;hljs-literal&quot;&gt;--sql&lt;/span&gt; .\queries\stock_metrics_top_1000.sql `
  &lt;span class=&quot;hljs-literal&quot;&gt;--output&lt;/span&gt; .\exports\stock_metrics_top_1000.csv `
  &lt;span class=&quot;hljs-literal&quot;&gt;--server&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;my-server&quot;&lt;/span&gt; `
  &lt;span class=&quot;hljs-literal&quot;&gt;--database&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;Reporting&quot;&lt;/span&gt; `
  &lt;span class=&quot;hljs-literal&quot;&gt;--sql-auth&lt;/span&gt; `
  &lt;span class=&quot;hljs-literal&quot;&gt;--username&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;report_user&quot;&lt;/span&gt; `
  &lt;span class=&quot;hljs-literal&quot;&gt;--password&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;secret&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;260&quot; dir=&quot;auto&quot; id=&quot;final-thoughts&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Final Thoughts&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;262&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This is not a huge project, but it is a useful example of turning a script into a tool.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;264&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The interesting part was not the CSV writing itself. It was the engineering around it:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;266&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;266&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;separating concerns into modules&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;267&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;choosing streaming over eager loading&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;268&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;designing a usable CLI&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;269&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;writing tests around the core behavior&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;270&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;validating the tool against a real SQL Server environment&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;272&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That is usually the difference between “something that works on my machine” and “something I would actually hand to another team.”&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;274&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If I extend it further, the next areas I would look at are:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;276&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;276&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;environment-variable support for connection defaults&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;277&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;optional gzip output&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;278&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;row count estimates before execution when possible&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;279&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;better handling for very wide result sets&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;280&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;structured logging for scheduled runs&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;282&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;For now,&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;sqlcsv-exporter&lt;/code&gt;&amp;nbsp;does the job it was meant to do: run a SQL Server query from a file and stream the results into CSV in a way that is practical, testable, and ready to package.&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2026/03/building-sqlcsv-exporter-python-cli-to.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlUQQvA389uHODF4CmfDMdiRASbUCsVe8lLxJIbpR5KZENbaZWk2geoH9uDaR9UMAPv18gPioTmBfBIfOh1ITv0rTYFzIlobRdOdBS87T2MT5p_GgdxwMeV5Qc6QMYnklHDvntq_LNwZN2r2kIFoNsCrkOtJ9fZwb9m-dmh0YsKzsho61dLGdKdTk9DSc/s72-c/ChatGPT%20Image%20Mar%2028,%202026,%2009_58_18%20PM.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-6634847053541739349</guid><pubDate>Sun, 22 Mar 2026 20:30:25 +0000</pubDate><atom:updated>2026-03-27T18:53:25.924+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">csv</category><category domain="http://www.blogger.com/atom/ns#">diff</category><category domain="http://www.blogger.com/atom/ns#">Python</category><title>Building csv-stream-diff: A Fast, Streaming CSV Comparison Tool for Very Large Files</title><description>&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiZghSoBVzhRlA-BOo-Az9CEfg3bybMp6CB9LbAXCfabBM8IFlfB8ZHm__cRmPlhubeqk_eLVf4cdJir9-nZCZOEJI6_NIsFuBOBS5bCSNo3CiiNZMmvxa4UJHVczhsCFRMnN6mPvM0ONpsUMn8IhECvZPGrUXKWBBU7kK2j2oAqNEter_j4jlHAsr65qM&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1536&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiZghSoBVzhRlA-BOo-Az9CEfg3bybMp6CB9LbAXCfabBM8IFlfB8ZHm__cRmPlhubeqk_eLVf4cdJir9-nZCZOEJI6_NIsFuBOBS5bCSNo3CiiNZMmvxa4UJHVczhsCFRMnN6mPvM0ONpsUMn8IhECvZPGrUXKWBBU7kK2j2oAqNEter_j4jlHAsr65qM&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;span face=&quot;-apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif&quot; style=&quot;font-size: 14px;&quot;&gt;Comparing two CSV files sounds simple until the files are no longer small.&lt;/span&gt;&lt;p&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;4&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Once the datasets move into the millions of rows, the usual approaches start to fall apart. Loading both files into memory is expensive. Spreadsheet tools stop being useful. Even many ad hoc scripts become slow, fragile, or impossible to run reliably in production-like environments.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;6&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That is the problem I wanted to solve with&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;csv-stream-diff&lt;/code&gt;.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;8&quot; dir=&quot;auto&quot; id=&quot;the-problem&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;The Problem&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;10&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;In real systems, CSV comparison is rarely just &quot;diff these two files.&quot;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;12&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Usually the job looks more like this:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;14&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;14&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The files are large enough that a full in-memory load is risky or impossible&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;15&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The key columns on the left and right files do not use the same names&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;16&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The comparison should only consider a selected subset of columns&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;17&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Duplicate keys may exist and need to be reported clearly&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;18&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Sometimes a full comparison is required, but sometimes a statistically useful sample is enough&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;19&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;The output needs to be machine-readable so it can feed downstream validation or remediation workflows&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;21&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;I wanted a tool that could handle that cleanly, with minimal dependencies, and still be easy to package and run anywhere.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;23&quot; dir=&quot;auto&quot; id=&quot;what-csv-stream-diff-does&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;What&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;csv-stream-diff&lt;/code&gt;&amp;nbsp;Does&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;25&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;&lt;a href=&quot;https://github.com/JordiCorbilla/csv-stream-diff&quot;&gt;csv-stream-diff&lt;/a&gt;&lt;/code&gt;&amp;nbsp;is a Python CLI tool for comparing very large CSV files using:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;27&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;27&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;streaming reads&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;28&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;hash-based partitioning&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;29&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;multiprocessing&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;30&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;YAML-driven configuration&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;32&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;It produces structured output files for:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;34&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;34&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;rows only on the left&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;35&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;rows only on the right&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;36&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;rows with cell-level differences&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;37&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;duplicate keys&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;38&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;summary metadata&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;40&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;It is designed to be practical rather than clever.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;42&quot; dir=&quot;auto&quot; id=&quot;the-core-design&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;The Core Design&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;44&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The main design constraint was memory.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;46&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If a tool tries to build a single giant in-memory index for both files, it will eventually hit a limit. So instead of comparing the full files at once,&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;csv-stream-diff&lt;/code&gt;&amp;nbsp;uses a two-phase approach.&lt;/p&gt;&lt;h3 class=&quot;code-line&quot; data-line=&quot;48&quot; dir=&quot;auto&quot; id=&quot;1-partition-both-files-into-hashed-buckets&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 1.25em; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; position: relative;&quot;&gt;1. Partition both files into hashed buckets&lt;/h3&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;50&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Each row key is normalized and hashed into a bucket. The left and right files are streamed row by row and written into matching bucket files on disk.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;52&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That matters because rows with the same normalized key always land in the same bucket. Once that is true, each bucket can be compared independently.&lt;/p&gt;&lt;h3 class=&quot;code-line&quot; data-line=&quot;54&quot; dir=&quot;auto&quot; id=&quot;2-compare-buckets-in-parallel&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 1.25em; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; position: relative;&quot;&gt;2. Compare buckets in parallel&lt;/h3&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;56&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;After partitioning, the tool compares bucket pairs using multiple worker processes. Each worker only needs to index one bucket of the left file at a time, not the entire dataset.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;58&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This keeps memory bounded while still taking advantage of all available CPU cores.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;60&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The result is a design that scales much better for heavy loads than a naïve single-process implementation.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;62&quot; dir=&quot;auto&quot; id=&quot;why-yaml-configuration&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Why YAML Configuration&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;64&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;I did not want the CLI to become a wall of flags.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;66&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The comparison usually needs several pieces of information:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;68&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;68&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;the left and right file paths&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;69&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;the left and right key columns&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;70&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;the left and right comparison columns&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;71&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;CSV dialect options&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;72&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;output paths&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;73&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;sampling settings&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;74&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;performance settings&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;76&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That is much easier to manage in a YAML file than on the command line.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;78&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The CLI still supports overrides, but the YAML file is the primary contract. That makes runs reproducible and easier to version alongside data validation jobs.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;80&quot; dir=&quot;auto&quot; id=&quot;exact-sampling-for-large-validation-runs&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Exact Sampling for Large Validation Runs&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;82&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Sometimes you do not want to compare every row.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;84&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;For example, if the source files contain tens of millions of records, you may want to run a fast validation pass against an exact random sample of keys before committing to a full comparison.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;86&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;csv-stream-diff&lt;/code&gt;&amp;nbsp;supports that:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;88&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;88&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;sampling.size: 0&lt;/code&gt;&amp;nbsp;means compare everything&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;89&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;sampling.size &amp;gt; 0&lt;/code&gt;&amp;nbsp;means compare an exact random sample of left-side unique keys&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;90&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;sampling.seed&lt;/code&gt;&amp;nbsp;makes the sample reproducible&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;92&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This gives you a useful middle ground between tiny spot checks and full heavy-load comparisons.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;94&quot; dir=&quot;auto&quot; id=&quot;handling-duplicate-keys&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Handling Duplicate Keys&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;96&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Duplicate keys are one of the most annoying edge cases in file comparison work.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;98&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If a key appears multiple times, the comparison becomes ambiguous. Instead of failing silently or hiding the problem, the tool reports duplicate keys explicitly and continues using the first occurrence for the main comparison.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;100&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That behaviour is deliberate:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;102&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;102&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;you get a warning&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;103&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;you get a separate duplicate-key artifact&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;104&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;you still get a usable comparison result&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;106&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This makes the tool better suited for messy real-world data.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;108&quot; dir=&quot;auto&quot; id=&quot;keeping-dependencies-small&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Keeping Dependencies Small&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;110&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;I wanted the runtime dependency footprint to stay minimal.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;112&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The tool is built mostly with the Python standard library. The only runtime dependency is&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;PyYAML&lt;/code&gt;, which is used for configuration loading.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;114&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That keeps installation simple and reduces operational friction when the tool needs to run in different environments.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;116&quot; dir=&quot;auto&quot; id=&quot;outputs-that-are-actually-useful&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Outputs That Are Actually Useful&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;118&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;One important goal was to avoid producing a human-only report.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;120&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The tool writes separate output files for each class of result, which makes it easier to automate downstream processing:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;122&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;122&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;only_in_left.csv&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;123&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;only_in_right.csv&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;124&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;differences.csv&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;125&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;duplicate_keys.csv&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;126&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;summary.json&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;128&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;differences.csv&lt;/code&gt;&amp;nbsp;file is especially useful because it reports cell-level differences with both the left and right column names and values.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;130&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That means you can do more than say &quot;this row changed.&quot; You can say exactly how it changed.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;132&quot; dir=&quot;auto&quot; id=&quot;testing-the-tool-properly&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Testing the Tool Properly&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;134&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;I also wanted the project to be easy to validate.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;136&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;So the repository includes:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;138&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;138&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;unit tests with&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;pytest&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;139&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;BDD-style acceptance tests with&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;behave&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;140&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;a fixture generator that creates two baseline-identical CSV files and then introduces controlled differences&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;142&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The generator makes it easy to create realistic comparison scenarios involving:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;144&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;144&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;changed values&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;145&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;left-only rows&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;146&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;right-only rows&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;147&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;duplicate keys&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;149&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That is useful both for development and for demonstrating the tool to others.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;151&quot; dir=&quot;auto&quot; id=&quot;a-few-practical-lessons&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;A Few Practical Lessons&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;153&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Building this reinforced a few engineering lessons:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;155&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;155&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;For large-file tooling, streaming and partitioning beat clever in-memory shortcuts&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;156&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Exact sampling is worth implementing properly because it gives a fast validation mode without becoming a toy feature&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;157&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Duplicate handling should be explicit, not implicit&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;158&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Machine-readable outputs matter as much as console output&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;159&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Minimal dependencies make utility tools easier to adopt&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;161&quot; dir=&quot;auto&quot; id=&quot;example-usage&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Example Usage&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;163&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;With a config file in place, the tool is intentionally simple to run:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-bash&quot; data-line=&quot;165&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;python -m csvstreamdiff.cli --config config.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;169&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;You can also override selected settings from the CLI:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-bash&quot; data-line=&quot;171&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;python -m csvstreamdiff.cli --config config.yaml --sample-size 100000 --workers 8
&lt;/code&gt;&lt;/pre&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;175&quot; dir=&quot;auto&quot; id=&quot;why-i-built-it&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Why I Built It&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;177&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This project came from a practical need: compare large CSV datasets reliably, with clear outputs, and without depending on heavy frameworks or fragile one-off scripts.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;179&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The result is a tool that is meant to be packaged, published, and reused anywhere.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;181&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That was the bar from the start.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;181&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEi-UWpU84tI9yoDSNNHULk29CaZ6E8EdS3qNp69LsLP0PNVQqk0Xd9tXOSENa5zJ8zbndRX98VyK_xh2PacjfBLRtva8Z0C-k3qguzi9cP8FeEmRrFM5RRaKQwc2eEMe4kDZGaDOPpJVWr7UR9VOlCFdDi8lKdJSkdsIq4sYJWQJa-5XGLyoKd03azh5t0&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;510&quot; data-original-width=&quot;1136&quot; height=&quot;288&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEi-UWpU84tI9yoDSNNHULk29CaZ6E8EdS3qNp69LsLP0PNVQqk0Xd9tXOSENa5zJ8zbndRX98VyK_xh2PacjfBLRtva8Z0C-k3qguzi9cP8FeEmRrFM5RRaKQwc2eEMe4kDZGaDOPpJVWr7UR9VOlCFdDi8lKdJSkdsIq4sYJWQJa-5XGLyoKd03azh5t0=w640-h288&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;h2 class=&quot;code-line code-active-line&quot; data-line=&quot;183&quot; dir=&quot;auto&quot; id=&quot;closing&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Closing&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;185&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If you work with large exports, migration validation, reconciliation jobs, or data quality checks, CSV comparison becomes infrastructure very quickly.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;187&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;csv-stream-diff&lt;/code&gt;&amp;nbsp;is my attempt to make that infrastructure solid:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;189&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;189&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;reproducible&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;190&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;scalable&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;191&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;explicit&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;192&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;easy to automate&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;194&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If you want to explore the project, the repository includes the CLI, example configuration, test generator, and packaging setup needed to build and publish it.&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2026/03/building-csv-stream-diff-fast-streaming.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEiZghSoBVzhRlA-BOo-Az9CEfg3bybMp6CB9LbAXCfabBM8IFlfB8ZHm__cRmPlhubeqk_eLVf4cdJir9-nZCZOEJI6_NIsFuBOBS5bCSNo3CiiNZMmvxa4UJHVczhsCFRMnN6mPvM0ONpsUMn8IhECvZPGrUXKWBBU7kK2j2oAqNEter_j4jlHAsr65qM=s72-c" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-1971339674031673041</guid><pubDate>Sun, 15 Mar 2026 12:44:00 +0000</pubDate><atom:updated>2026-03-15T21:31:04.725+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">github</category><category domain="http://www.blogger.com/atom/ns#">Ollama</category><category domain="http://www.blogger.com/atom/ns#">openclaw</category><category domain="http://www.blogger.com/atom/ns#">telegram</category><title>Coding from Telegram: my OpenClaw + Ollama setup</title><description>&lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiASW05dU0Rk56B7PU3aTBSHNzeF_BDwT7a157Ke7U7oPwvjslYTkGV7FuYBTL7u9V-hMgvB5GTRERIvB9YZnDG0kTW0oBW7b-HrDtTJVcb1qIKHqWjYpeoVIRd5g_0i9S2xhprxKO-g5KN4xQX7Rkjv5DQGMLVA4KLcZ5L0X2mWwhviS-TkNLY9dIa1k/s1536/ChatGPT%20Image%20Mar%2015,%202026,%2012_08_56%20PM.png&quot; style=&quot;clear: left; display: inline; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1536&quot; data-original-width=&quot;1024&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiASW05dU0Rk56B7PU3aTBSHNzeF_BDwT7a157Ke7U7oPwvjslYTkGV7FuYBTL7u9V-hMgvB5GTRERIvB9YZnDG0kTW0oBW7b-HrDtTJVcb1qIKHqWjYpeoVIRd5g_0i9S2xhprxKO-g5KN4xQX7Rkjv5DQGMLVA4KLcZ5L0X2mWwhviS-TkNLY9dIa1k/s320/ChatGPT%20Image%20Mar%2015,%202026,%2012_08_56%20PM.png&quot; width=&quot;213&quot; /&gt;&lt;/a&gt;&lt;/p&gt;&lt;div style=&quot;text-align: left;&quot;&gt;I finally got a setup working that feels a bit like the future.&lt;/div&gt;&lt;p data-end=&quot;471&quot; data-start=&quot;446&quot;&gt;I launched OpenClaw with:&lt;/p&gt;&lt;pre class=&quot;overflow-visible! px-0!&quot; data-end=&quot;531&quot; data-start=&quot;473&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-[calc(var(--sticky-padding-top)+9*var(--spacing))]&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-bash&quot;&gt;&lt;i&gt;ollama launch openclaw --model kimi-k2.5:cloud&lt;/i&gt;
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;636&quot; data-start=&quot;533&quot;&gt;and connected it to &lt;b&gt;Telegram&lt;/b&gt;, which means I can now talk to my coding assistant directly from my phone.&lt;/p&gt;&lt;p data-end=&quot;749&quot; data-start=&quot;638&quot;&gt;What makes this especially satisfying is that the whole thing is running on a very inexpensive mini PC at home:&lt;/p&gt;&lt;p data-end=&quot;895&quot; data-start=&quot;751&quot;&gt;&lt;strong data-end=&quot;895&quot; data-start=&quot;751&quot;&gt;Mini PC, 12GB RAM + 128GB ROM, Intel Celeron J4125 (up to 2.7 GHz), Windows 10 Pro, dual Wi-Fi 2.4/5G, Bluetooth 4.2, 4K HD, 2 HDMI + 1 VGA.&lt;/strong&gt;&lt;/p&gt;&lt;p data-end=&quot;1039&quot; data-start=&quot;897&quot;&gt;Nothing exotic. Nothing expensive. Just a small, affordable box quietly sitting there, acting as the bridge between my workspace and my phone.&lt;/p&gt;&lt;p data-end=&quot;1211&quot; data-start=&quot;1041&quot;&gt;That is part of the appeal. This setup feels surprisingly capable for something that costs very little and, beyond the hardware I already own, is effectively free to run.&lt;/p&gt;&lt;p data-end=&quot;1589&quot; data-start=&quot;1213&quot;&gt;The interesting part is not just that it chats back. It has access to my workspace, can help create projects, work with files, and act more like a real development companion than a simple question-answer bot. That changes the experience completely. It stops being something I occasionally open in a browser and starts feeling more like an always-available engineering copilot.&lt;/p&gt;&lt;p data-end=&quot;1656&quot; data-start=&quot;1591&quot;&gt;What I like most is the shift in how and where coding can happen.&lt;/p&gt;&lt;p data-end=&quot;1982&quot; data-start=&quot;1658&quot;&gt;I can be away from my desk, out doing groceries, commuting, or just walking around, and still make progress. I can send a quick message to scaffold an idea, create a project structure, inspect something in the workspace, or prepare the next task before I am back in front of the machine. It turns dead time into useful time.&lt;/p&gt;&lt;p data-end=&quot;2055&quot; data-start=&quot;1984&quot;&gt;That is the part that feels powerful: development becomes more ambient.&lt;/p&gt;&lt;p data-end=&quot;2385&quot; data-start=&quot;2057&quot;&gt;Instead of waiting for the perfect focused block at a desk, I can stay connected to the flow of a project throughout the day. A thought appears, I message it. A small task comes to mind, I delegate it. A project idea starts forming, I capture it immediately. By the time I sit down again, some of the groundwork is already done.&lt;/p&gt;&lt;p data-end=&quot;2728&quot; data-start=&quot;2387&quot;&gt;There is also something elegant about the stack itself. Ollama provides a clean way to launch the model workflow, OpenClaw gives the agent a useful operating surface, and Telegram becomes the lightweight interface that makes it all feel natural. No heavy setup on the phone, no friction, no need to open a laptop just to move a task forward.&lt;/p&gt;&lt;p data-end=&quot;3042&quot; data-start=&quot;2730&quot;&gt;The other thing I genuinely like is how accessible it is. You do not need a powerful workstation, a costly hosted service, or a complex mobile setup. A cheap mini PC, a Telegram account, and the right tooling are enough to build something that feels surprisingly close to a personal remote engineering assistant.&lt;/p&gt;&lt;p data-end=&quot;3255&quot; data-start=&quot;3044&quot;&gt;It is still early, of course, and this kind of workflow needs trust, guardrails, and careful handling when it comes to file access and automation. But even in its current form, it already feels genuinely useful.&lt;/p&gt;&lt;p data-end=&quot;3402&quot; data-start=&quot;3257&quot;&gt;A coding assistant in Telegram sounds like a gimmick until it starts helping you build real things while you are standing in a supermarket queue.&lt;/p&gt;&lt;p data-end=&quot;3492&quot; data-start=&quot;3404&quot;&gt;That is when it stops sounding like a demo and starts feeling like a new way of working.&lt;/p&gt;&lt;p&gt;

















&lt;br /&gt;&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2026/03/coding-from-telegram-my-openclaw-ollama.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiASW05dU0Rk56B7PU3aTBSHNzeF_BDwT7a157Ke7U7oPwvjslYTkGV7FuYBTL7u9V-hMgvB5GTRERIvB9YZnDG0kTW0oBW7b-HrDtTJVcb1qIKHqWjYpeoVIRd5g_0i9S2xhprxKO-g5KN4xQX7Rkjv5DQGMLVA4KLcZ5L0X2mWwhviS-TkNLY9dIa1k/s72-c/ChatGPT%20Image%20Mar%2015,%202026,%2012_08_56%20PM.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-3790779292704483243</guid><pubDate>Sun, 08 Mar 2026 15:39:00 +0000</pubDate><atom:updated>2026-03-09T19:37:46.205+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">arcade</category><category domain="http://www.blogger.com/atom/ns#">pygame</category><category domain="http://www.blogger.com/atom/ns#">Python</category><title>Tina the Destroyer: Turning Family Photos Into a Playable Arcade Game</title><description>&lt;p&gt;&lt;span face=&quot;-apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif&quot; style=&quot;font-size: 14px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiWxpR8M85-Z7rR_RINd8sg8PG-BcvWSRqCeey3NJQ4haZhbjBKrQgezS7wJwXdi1sdsxXkSpOGc0nWBVcUjMXCZYKgUFvrvv08TlfLVLBk2QbFAVGcJDVjeREXoY_5y5DfsxHkNMmyMrBEYHNDHh-3l2U39bMpuvP60474TeAHq-yf2-NNSnh2TDdombc&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1536&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiWxpR8M85-Z7rR_RINd8sg8PG-BcvWSRqCeey3NJQ4haZhbjBKrQgezS7wJwXdi1sdsxXkSpOGc0nWBVcUjMXCZYKgUFvrvv08TlfLVLBk2QbFAVGcJDVjeREXoY_5y5DfsxHkNMmyMrBEYHNDHh-3l2U39bMpuvP60474TeAHq-yf2-NNSnh2TDdombc&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;I built a small local arcade game called&lt;span face=&quot;-apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif&quot; style=&quot;font-size: 14px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;strong style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px;&quot;&gt;Tina the Destroyer&lt;/strong&gt;&lt;span face=&quot;-apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif&quot; style=&quot;font-size: 14px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span face=&quot;-apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif&quot; style=&quot;font-size: 14px;&quot;&gt;with Python and Pygame.&lt;/span&gt;&lt;p&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;4&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The idea was simple and personal: I took pictures of my daughter, converted them into game-ready pixel sprites, and built a one-screen game around that character. The result is a fast arcade loop where Tina smashes falling rats before they hit the ground.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;6&quot; dir=&quot;auto&quot; id=&quot;the-core-idea&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;&lt;br /&gt;&lt;/h2&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;6&quot; dir=&quot;auto&quot; id=&quot;the-core-idea&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;The Core Idea&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;8&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;I wanted a game that was:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;10&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;10&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Fast to play&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;11&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Easy to understand&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;12&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Personal and fun&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;14&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;So I designed a tight gameplay loop:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;16&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;16&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Move left and right&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;17&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Press&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;Space&lt;/code&gt;&amp;nbsp;to perform a ground smash&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;18&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Squash rats for points&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;19&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Lose a life when a rat reaches the bottom&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;20&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Game over after 3 missed rats&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;22&quot; dir=&quot;auto&quot; id=&quot;from-photos-to-sprites&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;From Photos to Sprites&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;24&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The visual pipeline started with real photos. I cleaned and adapted the character images to work as sprite sheets and exported key poses:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;26&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;26&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;player_idle.png&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;27&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;player_smash.png&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;29&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;I also prepared enemy and effect assets:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;31&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;31&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;rat.png&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;32&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;rat_squashed.png&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;33&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;impact.png&lt;/code&gt;&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;34&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;title.png&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;36&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;For audio, I added smash, miss, and looped music effects, with fallback loading so the game still runs if a file is missing.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;38&quot; dir=&quot;auto&quot; id=&quot;building-the-game&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Building the Game&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;40&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The game is organized into small modules:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;42&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;42&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;main.py&lt;/code&gt;&amp;nbsp;entrypoint&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;43&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;game.py&lt;/code&gt;&amp;nbsp;state machine and game loop&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;44&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;entities.py&lt;/code&gt;&amp;nbsp;player, rats, and impact effects&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;45&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;settings.py&lt;/code&gt;&amp;nbsp;tuning values&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;46&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;utils.py&lt;/code&gt;&amp;nbsp;asset loading and fallbacks&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;48&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Key features I focused on:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;50&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;50&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Clean state flow (&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;TITLE&lt;/code&gt;,&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;PLAYING&lt;/code&gt;,&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;GAME_OVER&lt;/code&gt;,&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;QUITTING&lt;/code&gt;)&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;51&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Responsive movement and smash timing&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;52&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Difficulty scaling over time&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;53&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Score/lives HUD&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;54&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Best-score persistence to JSON&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;55&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Graceful fallback behavior for missing assets&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;57&quot; dir=&quot;auto&quot; id=&quot;vibe-coding-the-project&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Vibe Coding the Project&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;59&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;I vibe coded this with&amp;nbsp;&lt;strong&gt;gpt-5-3.codex&lt;/strong&gt;, iterating quickly on gameplay feel, tuning values, and production details (asset fallbacks, restart flow, audio handling, and UI polish).&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;59&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Source code:&amp;nbsp;&lt;a href=&quot;https://github.com/JordiCorbilla/tina-the-destroyer&quot;&gt;JordiCorbilla/tina-the-destroyer: tina-the-destroyer&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;61&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;That pairing made it easy to keep momentum: prototype fast, test, adjust, repeat.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;63&quot; dir=&quot;auto&quot; id=&quot;why-i-loved-this-build&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Why I Loved This Build&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;65&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This project sits at the intersection of family creativity and engineering. It started as a fun idea with photos and ended up as a fully playable local arcade game I can run anytime with:&lt;/p&gt;&lt;pre style=&quot;background-color: rgba(10, 10, 10, 0.4); border-color: rgb(48, 48, 49); border-image: none 100% / 1 / 0 stretch; border-radius: 3px; border-style: solid; border-width: 1px; font-size: 14px; margin-top: 0px; overflow: auto; padding: 16px; text-wrap-mode: wrap;&quot;&gt;&lt;code class=&quot;code-line language-bash&quot; data-line=&quot;67&quot; dir=&quot;auto&quot; style=&quot;background: none; border-radius: 4px; display: inline-block; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 0px; position: relative; tab-size: 4;&quot;&gt;pip install pygame
python main.py
&lt;/code&gt;&lt;/pre&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;72&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Small scope, real personality, and a complete game loop. Exactly the kind of build I enjoy.&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2026/03/tina-destroyer-turning-family-photos.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEiWxpR8M85-Z7rR_RINd8sg8PG-BcvWSRqCeey3NJQ4haZhbjBKrQgezS7wJwXdi1sdsxXkSpOGc0nWBVcUjMXCZYKgUFvrvv08TlfLVLBk2QbFAVGcJDVjeREXoY_5y5DfsxHkNMmyMrBEYHNDHh-3l2U39bMpuvP60474TeAHq-yf2-NNSnh2TDdombc=s72-c" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>London, UK</georss:featurename><georss:point>51.5072178 -0.1275862</georss:point><georss:box>23.196983963821154 -35.2838362 79.817451636178845 35.0286638</georss:box></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-6132393536646830344</guid><pubDate>Sun, 15 Feb 2026 21:09:00 +0000</pubDate><atom:updated>2026-03-15T21:35:19.084+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">.net 9</category><category domain="http://www.blogger.com/atom/ns#">CLI</category><category domain="http://www.blogger.com/atom/ns#">spectre.console</category><category domain="http://www.blogger.com/atom/ns#">vibe coding</category><title>Building a Portfolio CLI in 2026 (and Why CLIs Feel Cool Again)</title><description>&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px;&quot;&gt;Over a single weekend, I decided to build a portfolio tracker that lives entirely in the terminal. Not a web app. Not a spreadsheet. A real, interactive CLI with panels, charts, and daily tracking.&lt;/span&gt;&lt;/div&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;2&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgasgqBfoOzXHRnLxyRi7lG_Mo-SBXednHuueBekIGEqwKjbAHk5eGauvXYbwv6QU47HatMtlit6cz-2qACJUL4bKPoiT2Ilih2su0WeLWUux8yCTb_xKAALbPwAQt0ehRJ9KnPDWJ-m-uw8Lkh7UoLa8diDQp2rzJdazRzwRU_xe1STcJuRleY6sXGm8w&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;968&quot; data-original-width=&quot;1896&quot; height=&quot;326&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgasgqBfoOzXHRnLxyRi7lG_Mo-SBXednHuueBekIGEqwKjbAHk5eGauvXYbwv6QU47HatMtlit6cz-2qACJUL4bKPoiT2Ilih2su0WeLWUux8yCTb_xKAALbPwAQt0ehRJ9KnPDWJ-m-uw8Lkh7UoLa8diDQp2rzJdazRzwRU_xe1STcJuRleY6sXGm8w=w640-h326&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;I also wanted it to fit how I actually work: Excel as the source of truth, but with a console experience that makes daily updates fast and visual.&lt;p&gt;&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;6&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This was vibe-coded with Chat GPT-5-2-Codex over a weekend, which made it surprisingly fun to iterate on UI tweaks, edge cases, and the overall feel.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;8&quot; dir=&quot;auto&quot; id=&quot;why-a-cli&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Why a CLI?&lt;/h2&gt;&lt;p class=&quot;code-line code-active-line&quot; data-line=&quot;9&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;CLIs are back. Not because they&#39;re nostalgic, but because they&#39;re fast, distraction-free, and feel like tools instead of products. A good CLI lets you focus on the data and decisions rather than the UI chrome.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;11&quot; dir=&quot;auto&quot; id=&quot;what-i-built&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;What I Built&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;12&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The result is&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;&lt;a href=&quot;https://github.com/JordiCorbilla/my-portfolio-cli&quot;&gt;my-portfolio-cli&lt;/a&gt;&lt;/code&gt;, an interactive portfolio dashboard that:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;13&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;13&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Reads data from an Excel workbook.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;14&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Renders a rich TUI with Spectre.Console.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;15&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Shows daily PnL, MTD performance, and FY summaries.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;16&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Displays a simple PnL bar chart right in the terminal.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;17&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Lets you add a new daily snapshot instantly.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;18&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Creates new monthly sheets when you need them.&lt;/li&gt;&lt;/ul&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;20&quot; dir=&quot;auto&quot; id=&quot;the-flow&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;The Flow&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;21&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;Start it and it opens directly in interactive mode. Use arrow keys to move across days and months. Press&amp;nbsp;&lt;code style=&quot;background-color: rgba(255, 255, 255, 0.1); border-radius: 4px; font-family: Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 1em; line-height: 1.357em; padding: 1px 3px;&quot;&gt;A&lt;/code&gt;&amp;nbsp;to add a new entry. If the month doesn&#39;t exist yet, it creates it automatically.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;23&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If you&#39;re starting from scratch (no workbook or no month sheets), the CLI prompts you to create the first month, add accounts, and enter initial values.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;25&quot; dir=&quot;auto&quot; id=&quot;design-notes&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;Design Notes&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;26&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;The key was making it feel like a real console app:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;27&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;27&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Clean layout with strong visual hierarchy.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;28&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Color-coded gains and losses.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;29&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;A compact FY summary that&#39;s readable at a glance.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;30&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;A minimal bar chart for quick trend cues.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;32&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;It isn&#39;t about being fancy. It&#39;s about being useful and enjoyable to use every day.&lt;/p&gt;&lt;h2 class=&quot;code-line&quot; data-line=&quot;34&quot; dir=&quot;auto&quot; id=&quot;whats-next&quot; style=&quot;border-bottom: 1px solid rgba(255, 255, 255, 0.18); border-left-color: rgba(255, 255, 255, 0.18); border-right-color: rgba(255, 255, 255, 0.18); border-top-color: rgba(255, 255, 255, 0.18); font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em; position: relative;&quot;&gt;What&#39;s Next&lt;/h2&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;35&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;This project is going online soon, which means:&lt;/p&gt;&lt;ul class=&quot;code-line&quot; data-line=&quot;36&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 0.7em; margin-top: 0px; position: relative;&quot;&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;36&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;A demo workbook and screenshots.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;37&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;A short walkthrough.&lt;/li&gt;&lt;li class=&quot;code-line&quot; data-line=&quot;38&quot; dir=&quot;auto&quot; style=&quot;position: relative;&quot;&gt;Possibly a minimal installer or single-file publish.&lt;/li&gt;&lt;/ul&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;40&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;For now, it&#39;s a tight little tool that does exactly what I need.&lt;/p&gt;&lt;p class=&quot;code-line&quot; data-line=&quot;42&quot; dir=&quot;auto&quot; style=&quot;font-family: -apple-system, BlinkMacSystemFont, &amp;quot;Segoe WPC&amp;quot;, &amp;quot;Segoe UI&amp;quot;, system-ui, Ubuntu, &amp;quot;Droid Sans&amp;quot;, sans-serif; font-size: 14px; margin-bottom: 16px; margin-top: 0px; position: relative;&quot;&gt;If you want to build your own CLI tool, I highly recommend it. The feedback loop is short, and it&#39;s easy to keep refining the experience until it feels right.&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2026/02/building-portfolio-cli-in-2026-and-why.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEgasgqBfoOzXHRnLxyRi7lG_Mo-SBXednHuueBekIGEqwKjbAHk5eGauvXYbwv6QU47HatMtlit6cz-2qACJUL4bKPoiT2Ilih2su0WeLWUux8yCTb_xKAALbPwAQt0ehRJ9KnPDWJ-m-uw8Lkh7UoLa8diDQp2rzJdazRzwRU_xe1STcJuRleY6sXGm8w=s72-w640-h326-c" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-7888794576508862265</guid><pubDate>Sun, 25 Jan 2026 17:37:00 +0000</pubDate><atom:updated>2026-01-25T17:37:53.845+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI Coding Agents</category><category domain="http://www.blogger.com/atom/ns#">Claude Code</category><category domain="http://www.blogger.com/atom/ns#">Developer Tooling</category><category domain="http://www.blogger.com/atom/ns#">Local LLM</category><category domain="http://www.blogger.com/atom/ns#">Offline AI</category><category domain="http://www.blogger.com/atom/ns#">Ollama</category><category domain="http://www.blogger.com/atom/ns#">Private AI</category><category domain="http://www.blogger.com/atom/ns#">VS Code</category><title>Running Claude Code Fully Locally with Ollama (Yes, It’s Possible — With Caveats)</title><description>&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9WeixbvB2oF-WOzD69_uFgmOsJ0xiUFPaV5rjs39NGGc1-0axBdzGYBQBpMNL3Nh4SXifIJOZCaYeja16D9YLWvRXk2xRfrtx0FNWIIZCx9dMgZatSXY7BiFPUz0z9gZpI-YhgNfgLbjqisB6y6ZUW1qnNjyywe5KZTvYWjzFPFKEE-xK5td5rAaYh-Q/s1536/ChatGPT%20Image%20Jan%2025,%202026,%2005_20_49%20PM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1536&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9WeixbvB2oF-WOzD69_uFgmOsJ0xiUFPaV5rjs39NGGc1-0axBdzGYBQBpMNL3Nh4SXifIJOZCaYeja16D9YLWvRXk2xRfrtx0FNWIIZCx9dMgZatSXY7BiFPUz0z9gZpI-YhgNfgLbjqisB6y6ZUW1qnNjyywe5KZTvYWjzFPFKEE-xK5td5rAaYh-Q/s320/ChatGPT%20Image%20Jan%2025,%202026,%2005_20_49%20PM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;Claude Code is designed as a &lt;strong data-end=&quot;448&quot; data-start=&quot;406&quot;&gt;cloud-first, tool-centric coding agent&lt;/strong&gt; tightly coupled to Anthropic’s infrastructure. Out of the box, it &lt;strong data-end=&quot;548&quot; data-start=&quot;515&quot;&gt;does not support local models&lt;/strong&gt;.&lt;p&gt;&lt;/p&gt;
&lt;p data-end=&quot;582&quot; data-start=&quot;551&quot;&gt;However, with a combination of:&lt;/p&gt;
&lt;ul data-end=&quot;672&quot; data-start=&quot;583&quot;&gt;
&lt;li data-end=&quot;606&quot; data-start=&quot;583&quot;&gt;
&lt;p data-end=&quot;606&quot; data-start=&quot;585&quot;&gt;endpoint redirection,&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;638&quot; data-start=&quot;607&quot;&gt;
&lt;p data-end=&quot;638&quot; data-start=&quot;609&quot;&gt;model aliasing inside Ollama,&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;672&quot; data-start=&quot;639&quot;&gt;
&lt;p data-end=&quot;672&quot; data-start=&quot;641&quot;&gt;and a tool-capable local model,&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;791&quot; data-start=&quot;674&quot;&gt;you &lt;em data-end=&quot;683&quot; data-start=&quot;678&quot;&gt;can&lt;/em&gt; run Claude Code &lt;strong data-end=&quot;720&quot; data-start=&quot;700&quot;&gt;entirely locally&lt;/strong&gt; against Ollama — including &lt;strong data-end=&quot;790&quot; data-start=&quot;748&quot;&gt;file edits, refactors, and shell tools&lt;/strong&gt;.&lt;/p&gt;
&lt;p data-end=&quot;918&quot; data-start=&quot;793&quot;&gt;This article documents the &lt;strong data-end=&quot;845&quot; data-start=&quot;820&quot;&gt;exact path that works&lt;/strong&gt;, the &lt;strong data-end=&quot;881&quot; data-start=&quot;851&quot;&gt;failure modes you will hit&lt;/strong&gt;, and the &lt;strong data-end=&quot;917&quot; data-start=&quot;891&quot;&gt;engineering trade-offs&lt;/strong&gt;.&lt;/p&gt;&lt;h2 data-end=&quot;966&quot; data-start=&quot;925&quot;&gt;Why Claude Code Is Hard to Run Locally&lt;/h2&gt;&lt;p data-end=&quot;1011&quot; data-start=&quot;968&quot;&gt;Claude Code makes three strong assumptions:&lt;/p&gt;&lt;ol data-end=&quot;1146&quot; data-start=&quot;1013&quot;&gt;
&lt;li data-end=&quot;1064&quot; data-start=&quot;1013&quot;&gt;
&lt;p data-end=&quot;1064&quot; data-start=&quot;1016&quot;&gt;&lt;strong data-end=&quot;1064&quot; data-start=&quot;1016&quot;&gt;Anthropic authentication is always available&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1113&quot; data-start=&quot;1065&quot;&gt;
&lt;p data-end=&quot;1113&quot; data-start=&quot;1068&quot;&gt;&lt;strong data-end=&quot;1093&quot; data-start=&quot;1068&quot;&gt;Model names are fixed&lt;/strong&gt; (&lt;code data-end=&quot;1112&quot; data-start=&quot;1095&quot;&gt;claude-sonnet-*&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1146&quot; data-start=&quot;1114&quot;&gt;
&lt;p data-end=&quot;1146&quot; data-start=&quot;1117&quot;&gt;&lt;strong data-end=&quot;1146&quot; data-start=&quot;1117&quot;&gt;Tool calling is mandatory&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p data-end=&quot;1221&quot; data-start=&quot;1148&quot;&gt;Most “local Claude” guides fail because they only solve &lt;strong data-end=&quot;1211&quot; data-start=&quot;1204&quot;&gt;one&lt;/strong&gt; of these.&lt;/p&gt;&lt;p data-end=&quot;1264&quot; data-start=&quot;1223&quot;&gt;To succeed, you must solve &lt;strong data-end=&quot;1263&quot; data-start=&quot;1250&quot;&gt;all three&lt;/strong&gt;.&lt;/p&gt;&lt;hr data-end=&quot;1269&quot; data-start=&quot;1266&quot; /&gt;&lt;h2 data-end=&quot;1306&quot; data-start=&quot;1271&quot;&gt;Architecture That Actually Works&lt;/h2&gt;&lt;pre class=&quot;overflow-visible! px-0!&quot; data-end=&quot;1539&quot; data-start=&quot;1308&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre!&quot;&gt;VS &lt;span class=&quot;hljs-selector-tag&quot;&gt;Code&lt;/span&gt;
 └─ Claude &lt;span class=&quot;hljs-selector-tag&quot;&gt;Code&lt;/span&gt; extension
     ├─ hardcoded model name (claude-sonnet-*)
     ├─ mandatory tools
     └─ Anthropic-style requests
           ↓
      Ollama (localhost)
           ↓
   Tool-capable local model (Qwen)
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;1555&quot; data-start=&quot;1541&quot;&gt;Key insight:&lt;/p&gt;&lt;p data-end=&quot;918&quot; data-start=&quot;793&quot;&gt;








&lt;/p&gt;&lt;blockquote data-end=&quot;1633&quot; data-start=&quot;1556&quot;&gt;
&lt;p data-end=&quot;1633&quot; data-start=&quot;1558&quot;&gt;You don’t change Claude Code — you &lt;strong data-end=&quot;1632&quot; data-start=&quot;1593&quot;&gt;adapt Ollama to look like Anthropic&lt;/strong&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 data-end=&quot;1681&quot; data-start=&quot;1640&quot;&gt;Step 1: Redirect Claude Code to Ollama&lt;/h2&gt;&lt;p data-end=&quot;1710&quot; data-start=&quot;1683&quot;&gt;In VS Code &lt;code data-end=&quot;1709&quot; data-start=&quot;1694&quot;&gt;Command palette -&amp;gt; Preferences: Open User Settings (JSON)&lt;/code&gt;:&lt;/p&gt;&lt;pre class=&quot;overflow-visible! px-0!&quot; data-end=&quot;1974&quot; data-start=&quot;1712&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-json&quot;&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;claudeCode.environmentVariables&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;ANTHROPIC_BASE_URL&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;http://127.0.0.1:11434&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;ANTHROPIC_AUTH_TOKEN&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;value&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;ollama&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;claudeCode.disableLoginPrompt&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;2038&quot; data-start=&quot;1976&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgJBceCONKj-YQIyfSWW2Soc2jhAUO3v7HNCC868hhWA6Wc3baPSU9y8WNMtK456kkMIp8gUXzJ5ME87WBujBtdUt2mMqVcixMr8GWdteFPR2cEjSTvgruZxRl2rdsIoGxuI7Vhqf_NYezIidHLdslbGtlmVW_Re2U9Bh7y3V3GErCQaf9CchF8jOMbSsE&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;326&quot; data-original-width=&quot;666&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgJBceCONKj-YQIyfSWW2Soc2jhAUO3v7HNCC868hhWA6Wc3baPSU9y8WNMtK456kkMIp8gUXzJ5ME87WBujBtdUt2mMqVcixMr8GWdteFPR2cEjSTvgruZxRl2rdsIoGxuI7Vhqf_NYezIidHLdslbGtlmVW_Re2U9Bh7y3V3GErCQaf9CchF8jOMbSsE=s16000&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p data-end=&quot;2038&quot; data-start=&quot;1976&quot;&gt;This bypasses cloud calls and sends &lt;strong data-end=&quot;2037&quot; data-start=&quot;2012&quot;&gt;all traffic to Ollama&lt;/strong&gt;.&lt;/p&gt;&lt;p data-end=&quot;2054&quot; data-start=&quot;2040&quot;&gt;At this point:&lt;/p&gt;&lt;ul data-end=&quot;2124&quot; data-start=&quot;2055&quot;&gt;
&lt;li data-end=&quot;2065&quot; data-start=&quot;2055&quot;&gt;
&lt;p data-end=&quot;2065&quot; data-start=&quot;2057&quot;&gt;UI works&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2081&quot; data-start=&quot;2066&quot;&gt;
&lt;p data-end=&quot;2081&quot; data-start=&quot;2068&quot;&gt;Requests flow&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2124&quot; data-start=&quot;2082&quot;&gt;
&lt;p data-end=&quot;2124&quot; data-start=&quot;2084&quot;&gt;But you will get &lt;strong data-end=&quot;2124&quot; data-start=&quot;2101&quot;&gt;404 model not found&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;p data-end=&quot;918&quot; data-start=&quot;793&quot;&gt;





&lt;/p&gt;&lt;p data-end=&quot;2143&quot; data-start=&quot;2126&quot;&gt;That is expected.&lt;/p&gt;&lt;h2 data-end=&quot;2194&quot; data-start=&quot;2150&quot;&gt;Step 2: Understand the Model Name Problem&lt;/h2&gt;&lt;p data-end=&quot;2242&quot; data-start=&quot;2196&quot;&gt;Claude Code &lt;strong data-end=&quot;2218&quot; data-start=&quot;2208&quot;&gt;always&lt;/strong&gt; sends model names like:&lt;/p&gt;&lt;pre class=&quot;overflow-visible! px-0!&quot; data-end=&quot;2278&quot; data-start=&quot;2244&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre!&quot;&gt;claude-sonnet-4-5-20250929
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;2317&quot; data-start=&quot;2280&quot;&gt;Ollama does &lt;strong data-end=&quot;2299&quot; data-start=&quot;2292&quot;&gt;not&lt;/strong&gt; have such models.&lt;/p&gt;&lt;p data-end=&quot;2396&quot; data-start=&quot;2319&quot;&gt;You cannot configure Claude Code to use &lt;code data-end=&quot;2366&quot; data-start=&quot;2359&quot;&gt;gemma&lt;/code&gt;, &lt;code data-end=&quot;2375&quot; data-start=&quot;2368&quot;&gt;llama&lt;/code&gt;, or &lt;code data-end=&quot;2386&quot; data-start=&quot;2380&quot;&gt;qwen&lt;/code&gt; directly.&lt;/p&gt;&lt;h3 data-end=&quot;2426&quot; data-start=&quot;2398&quot;&gt;Solution: Model aliasing&lt;/h3&gt;&lt;p data-end=&quot;2510&quot; data-start=&quot;2428&quot;&gt;Ollama lets you create &lt;strong data-end=&quot;2474&quot; data-start=&quot;2451&quot;&gt;lightweight aliases&lt;/strong&gt; that map one model name to another.&lt;/p&gt;&lt;hr data-end=&quot;2515&quot; data-start=&quot;2512&quot; /&gt;&lt;h2 data-end=&quot;2555&quot; data-start=&quot;2517&quot;&gt;Step 3: Why Gemma Fails (Important)&lt;/h2&gt;&lt;p data-end=&quot;2624&quot; data-start=&quot;2557&quot;&gt;Gemma models (including &lt;code data-end=&quot;2590&quot; data-start=&quot;2581&quot;&gt;gemma3n&lt;/code&gt;) &lt;strong data-end=&quot;2623&quot; data-start=&quot;2592&quot;&gt;do not support tool calling&lt;/strong&gt;.&lt;/p&gt;&lt;p data-end=&quot;2670&quot; data-start=&quot;2626&quot;&gt;Claude Code &lt;em data-end=&quot;2646&quot; data-start=&quot;2638&quot;&gt;always&lt;/em&gt; sends a &lt;code data-end=&quot;2662&quot; data-start=&quot;2655&quot;&gt;tools&lt;/code&gt; schema.&lt;/p&gt;&lt;p data-end=&quot;2688&quot; data-start=&quot;2672&quot;&gt;Resulting error:&lt;/p&gt;&lt;pre class=&quot;overflow-visible! px-0!&quot; data-end=&quot;2720&quot; data-start=&quot;2690&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre!&quot;&gt;&lt;span class=&quot;hljs-attribute&quot;&gt;does&lt;/span&gt; not support tools
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;2780&quot; data-start=&quot;2722&quot;&gt;This is a &lt;strong data-end=&quot;2748&quot; data-start=&quot;2732&quot;&gt;hard failure&lt;/strong&gt;, not a timeout or config issue.&lt;/p&gt;&lt;h3 data-end=&quot;2796&quot; data-start=&quot;2782&quot;&gt;Conclusion&lt;/h3&gt;&lt;blockquote data-end=&quot;2846&quot; data-start=&quot;2798&quot;&gt;
&lt;p data-end=&quot;2846&quot; data-start=&quot;2800&quot;&gt;Gemma works for chat, &lt;strong data-end=&quot;2829&quot; data-start=&quot;2822&quot;&gt;not&lt;/strong&gt; for Claude Code.&lt;/p&gt;
&lt;/blockquote&gt;&lt;hr data-end=&quot;2851&quot; data-start=&quot;2848&quot; /&gt;&lt;h2 data-end=&quot;2888&quot; data-start=&quot;2853&quot;&gt;Step 4: Use a Tool-Capable Model&lt;/h2&gt;&lt;p data-end=&quot;2921&quot; data-start=&quot;2890&quot;&gt;You need a model that supports:&lt;/p&gt;&lt;ul data-end=&quot;2982&quot; data-start=&quot;2922&quot;&gt;
&lt;li data-end=&quot;2945&quot; data-start=&quot;2922&quot;&gt;
&lt;p data-end=&quot;2945&quot; data-start=&quot;2924&quot;&gt;structured tool calls&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2962&quot; data-start=&quot;2946&quot;&gt;
&lt;p data-end=&quot;2962&quot; data-start=&quot;2948&quot;&gt;JSON responses&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2982&quot; data-start=&quot;2963&quot;&gt;
&lt;p data-end=&quot;2982&quot; data-start=&quot;2965&quot;&gt;streaming + edits&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;h3 data-end=&quot;2999&quot; data-start=&quot;2984&quot;&gt;Recommended&lt;/h3&gt;&lt;pre class=&quot;overflow-visible! px-0!&quot; data-end=&quot;3041&quot; data-start=&quot;3001&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-bash&quot;&gt;ollama pull qwen2.5-coder:7b
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;3111&quot; data-start=&quot;3043&quot;&gt;This is the &lt;strong data-end=&quot;3082&quot; data-start=&quot;3055&quot;&gt;smallest reliable model&lt;/strong&gt; that works with Claude Code.&lt;/p&gt;&lt;hr data-end=&quot;3116&quot; data-start=&quot;3113&quot; /&gt;&lt;h2 data-end=&quot;3161&quot; data-start=&quot;3118&quot;&gt;Step 5: Create Claude-Compatible Aliases&lt;/h2&gt;&lt;p data-end=&quot;3184&quot; data-start=&quot;3163&quot;&gt;Create a &lt;code data-end=&quot;3183&quot; data-start=&quot;3172&quot;&gt;Modelfile&lt;/code&gt;:&lt;/p&gt;&lt;pre class=&quot;overflow-visible! px-0!&quot; data-end=&quot;3215&quot; data-start=&quot;3186&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre!&quot;&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;FROM&lt;/span&gt; qwen2.&lt;span class=&quot;hljs-number&quot;&gt;5&lt;/span&gt;-coder:&lt;span class=&quot;hljs-number&quot;&gt;7&lt;/span&gt;b
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;3257&quot; data-start=&quot;3217&quot;&gt;Then create aliases Claude Code expects:&lt;/p&gt;&lt;pre class=&quot;overflow-visible! px-0!&quot; data-end=&quot;3393&quot; data-start=&quot;3259&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-bash&quot;&gt;ollama create claude-sonnet -f Modelfile
ollama create claude-opus   -f Modelfile
ollama create claude-haiku  -f Modelfile
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;3449&quot; data-start=&quot;3395&quot;&gt;No weights are duplicated.&lt;br data-end=&quot;3424&quot; data-start=&quot;3421&quot; /&gt;
Only metadata is created.&lt;/p&gt;&lt;p data-end=&quot;2143&quot; data-start=&quot;2126&quot;&gt;





























&lt;/p&gt;&lt;p data-end=&quot;3504&quot; data-start=&quot;3451&quot;&gt;Now Ollama can answer requests for &lt;code data-end=&quot;3503&quot; data-start=&quot;3486&quot;&gt;claude-sonnet-*&lt;/code&gt;.&lt;/p&gt;&lt;h2 data-end=&quot;3991&quot; data-start=&quot;3957&quot;&gt;Result: Fully Local Claude Code&lt;/h2&gt;&lt;p data-end=&quot;4024&quot; data-start=&quot;3993&quot;&gt;At this point, Claude Code can:&lt;/p&gt;&lt;ul data-end=&quot;4111&quot; data-start=&quot;4026&quot;&gt;
&lt;li data-end=&quot;4040&quot; data-start=&quot;4026&quot;&gt;
&lt;p data-end=&quot;4040&quot; data-start=&quot;4028&quot;&gt;create files&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4053&quot; data-start=&quot;4041&quot;&gt;
&lt;p data-end=&quot;4053&quot; data-start=&quot;4043&quot;&gt;edit files&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4074&quot; data-start=&quot;4054&quot;&gt;
&lt;p data-end=&quot;4074&quot; data-start=&quot;4056&quot;&gt;run shell commands&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4090&quot; data-start=&quot;4075&quot;&gt;
&lt;p data-end=&quot;4090&quot; data-start=&quot;4077&quot;&gt;refactor code&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4111&quot; data-start=&quot;4091&quot;&gt;
&lt;p data-end=&quot;4111&quot; data-start=&quot;4093&quot;&gt;reason about repos&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;p data-end=&quot;3504&quot; data-start=&quot;3451&quot;&gt;


&lt;/p&gt;&lt;p data-end=&quot;4142&quot; data-start=&quot;4113&quot;&gt;All &lt;strong data-end=&quot;4141&quot; data-start=&quot;4117&quot;&gt;without cloud access&lt;/strong&gt;.&lt;/p&gt;&lt;p data-end=&quot;4142&quot; data-start=&quot;4113&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiSfEZPwYWlCqJEwYENT6QW5PD-Bv7Xhw91DcI0yCsJNILt9NSwUeISmRhLRn945hhJl0MifA1eZ1LncwuiI3jODNdg5hRYKwp4Iqc6h-Pwnm8EcH2uHTfneP-tjCSeKpv7iPRTy96DapQ6tjwKI3HTF4krOl6d98EJb2bclRc1M9LkyqK_ra-Rge9ab5o&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;880&quot; data-original-width=&quot;1437&quot; height=&quot;392&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEiSfEZPwYWlCqJEwYENT6QW5PD-Bv7Xhw91DcI0yCsJNILt9NSwUeISmRhLRn945hhJl0MifA1eZ1LncwuiI3jODNdg5hRYKwp4Iqc6h-Pwnm8EcH2uHTfneP-tjCSeKpv7iPRTy96DapQ6tjwKI3HTF4krOl6d98EJb2bclRc1M9LkyqK_ra-Rge9ab5o=w640-h392&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgy8YSIlu5-MQgrqg-0s4_CMnLFkCWs3pDxIK_eGSc24exhoiYBN94Xrp4nw2-eWfKxEavd_mig2wOqwybj9o22BNb5UkGmEZdgB6jR2s3E5S-pZegxeFv7wrW4lKJQNHvJugD77bCUOZGtDcsGOdjhK8gMjZtxVNQJWzfwBaHzJsmFh7DZnqv0JNVx-MY&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;991&quot; data-original-width=&quot;1902&quot; height=&quot;334&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgy8YSIlu5-MQgrqg-0s4_CMnLFkCWs3pDxIK_eGSc24exhoiYBN94Xrp4nw2-eWfKxEavd_mig2wOqwybj9o22BNb5UkGmEZdgB6jR2s3E5S-pZegxeFv7wrW4lKJQNHvJugD77bCUOZGtDcsGOdjhK8gMjZtxVNQJWzfwBaHzJsmFh7DZnqv0JNVx-MY=w640-h334&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2026/01/running-claude-code-fully-locally-with.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9WeixbvB2oF-WOzD69_uFgmOsJ0xiUFPaV5rjs39NGGc1-0axBdzGYBQBpMNL3Nh4SXifIJOZCaYeja16D9YLWvRXk2xRfrtx0FNWIIZCx9dMgZatSXY7BiFPUz0z9gZpI-YhgNfgLbjqisB6y6ZUW1qnNjyywe5KZTvYWjzFPFKEE-xK5td5rAaYh-Q/s72-c/ChatGPT%20Image%20Jan%2025,%202026,%2005_20_49%20PM.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-6190973653015376234</guid><pubDate>Sun, 12 Oct 2025 19:23:00 +0000</pubDate><atom:updated>2026-03-15T21:35:57.801+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AG2</category><category domain="http://www.blogger.com/atom/ns#">AI</category><category domain="http://www.blogger.com/atom/ns#">asset allocation</category><category domain="http://www.blogger.com/atom/ns#">AutoGen</category><category domain="http://www.blogger.com/atom/ns#">budgeting</category><category domain="http://www.blogger.com/atom/ns#">Chainlit</category><category domain="http://www.blogger.com/atom/ns#">ChatBot</category><category domain="http://www.blogger.com/atom/ns#">compliance</category><category domain="http://www.blogger.com/atom/ns#">Finance</category><category domain="http://www.blogger.com/atom/ns#">group chat</category><category domain="http://www.blogger.com/atom/ns#">Investment</category><category domain="http://www.blogger.com/atom/ns#">llm</category><category domain="http://www.blogger.com/atom/ns#">macroeconomics</category><category domain="http://www.blogger.com/atom/ns#">market analysis</category><category domain="http://www.blogger.com/atom/ns#">multi-agent</category><category domain="http://www.blogger.com/atom/ns#">portfolio</category><category domain="http://www.blogger.com/atom/ns#">Python</category><category domain="http://www.blogger.com/atom/ns#">recommendation</category><category domain="http://www.blogger.com/atom/ns#">risk tolerance</category><title>Building a Multi‑Agent Finance Chatbot with AG2</title><description>&lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg14JYKYf79T4ii7Dw86A3QqUwnV2Qmk-XRd0ylU6TvzO_pbmDwgLxOXFXoUFO7RQaA9BoB2Sauim8pPRugTLgZMGlot1gzigjLiMcBzn_qxJ-uFuqlJVyVeXl-FjMmffDKKg1d6xUvxW6XshSNlkDnF55_UIc8irs7MeM5Rmwvo3qafDQthN4Jox8XpNo/s1258/post.png&quot; style=&quot;clear: left; display: inline !important; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1258&quot; height=&quot;260&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg14JYKYf79T4ii7Dw86A3QqUwnV2Qmk-XRd0ylU6TvzO_pbmDwgLxOXFXoUFO7RQaA9BoB2Sauim8pPRugTLgZMGlot1gzigjLiMcBzn_qxJ-uFuqlJVyVeXl-FjMmffDKKg1d6xUvxW6XshSNlkDnF55_UIc8irs7MeM5Rmwvo3qafDQthN4Jox8XpNo/s320/post.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&amp;nbsp;Financial conversations often touch on complex markets, risk preferences and compliance constraints.  A single large‑language model may struggle to handle every nuance, so &lt;strong data-end=&quot;230&quot; data-start=&quot;223&quot;&gt;AG2&lt;/strong&gt; (formerly AutoGen) offers a multi‑agent architecture where each agent specializes in part of the workflow.  This article demonstrates how to create a &lt;em data-end=&quot;390&quot; data-start=&quot;381&quot;&gt;finance&lt;/em&gt; chatbot using AG2 and Python.  We will define specialised agents, orchestrate them via a group chat and show a sample interaction.  The approach is inspired by multi‑agent healthcare chatbots but adapted to investment discussions.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;h2 data-end=&quot;651&quot; data-start=&quot;623&quot;&gt;Why multi‑agent chatbots?&lt;/h2&gt;&lt;p data-end=&quot;1508&quot; data-start=&quot;653&quot;&gt;Recent tutorials explain that multi‑agent chatbots divide complex interactions into specialised roles.  Each agent focuses on one task—answering questions, providing recommendations or analysing data—so the overall system can handle nuanced conversations more accurately (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.analyticsvidhya.com/blog/2024/11/multi-agent-chatbots-with-autogen/#:~:text=Multi,responses%20compared%20to%20a%20single&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.analyticsvidhya.com/blog/2024/11/multi-agent-chatbots-with-autogen/#:~:text=Multi,responses%20compared%20to%20a%20single&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;analyticsvidhya.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  The AG2 documentation notes that multi‑agent architectures are easier to maintain and extend; they allow developers to add or remove functionality without redesigning the entire system (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=The%20multi,agents%2C%20one%20can%20compose%20and&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=The%20multi,agents%2C%20one%20can%20compose%20and&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;docs.ag2.ai&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&amp;nbsp; Multi‑agent designs also support different conversation patterns (sequential, group or nested chat) and enable tool use and code execution (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=combining%20models%20and%20agents,&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=combining%20models%20and%20agents,&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;docs.ag2.ai&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  In regulated industries such as healthcare or finance, this modularity improves accuracy and reduces risk (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=There%20are%20at%20least%20two,agent%20systems&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=There%20are%20at%20least%20two,agent%20systems&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;docs.ag2.ai&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;p data-end=&quot;2074&quot; data-start=&quot;1510&quot;&gt;Another advantage is practicality.  Building a chatbot for stock‑market questions is complex, but packages like &lt;strong data-end=&quot;1634&quot; data-start=&quot;1622&quot;&gt;Chainlit&lt;/strong&gt; and &lt;strong data-end=&quot;1646&quot; data-start=&quot;1639&quot;&gt;AG2&lt;/strong&gt; simplify the process by handling chat orchestration and tool calls (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://tinztwinshub.com/investment-research/build-a-multi-agent-stock-market-analyst-to-compare-stock-price-performance/#:~:text=Building%20a%20chatbot%20app%20for,AG2%20make%20it%20much%20easier&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://tinztwinshub.com/investment-research/build-a-multi-agent-stock-market-analyst-to-compare-stock-price-performance/#:~:text=Building%20a%20chatbot%20app%20for,AG2%20make%20it%20much%20easier&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;tinztwinshub.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  These frameworks allow you to run large‑language models locally or through an API and to register Python functions as tools (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://tinztwinshub.com/investment-research/build-a-multi-agent-stock-market-analyst-to-compare-stock-price-performance/#:~:text=,the%20function%20and%20its%20registration&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://tinztwinshub.com/investment-research/build-a-multi-agent-stock-market-analyst-to-compare-stock-price-performance/#:~:text=,the%20function%20and%20its%20registration&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;tinztwinshub.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  As open‑source models like Meta’s Llama 3.1 gain popularity, developers can deploy multi‑agent systems on local machines (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://tinztwinshub.com/investment-research/build-a-multi-agent-stock-market-analyst-to-compare-stock-price-performance/#:~:text=Our%20chatbot%20uses%20Meta%E2%80%99s%20,or%20tools%20to%20the%20application&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://tinztwinshub.com/investment-research/build-a-multi-agent-stock-market-analyst-to-compare-stock-price-performance/#:~:text=Our%20chatbot%20uses%20Meta%E2%80%99s%20,or%20tools%20to%20the%20application&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;tinztwinshub.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;h3 data-end=&quot;2093&quot; data-start=&quot;2076&quot;&gt;Pros and cons&lt;/h3&gt;&lt;div class=&quot;_tableContainer_1rjym_1&quot;&gt;&lt;div class=&quot;group _tableWrapper_1rjym_13 flex w-fit flex-col-reverse&quot; tabindex=&quot;-1&quot;&gt;&lt;table class=&quot;w-fit min-w-(--thread-content-width)&quot; data-end=&quot;3548&quot; data-start=&quot;2095&quot;&gt;&lt;thead data-end=&quot;2299&quot; data-start=&quot;2095&quot;&gt;&lt;tr data-end=&quot;2299&quot; data-start=&quot;2095&quot;&gt;&lt;th data-col-size=&quot;sm&quot; data-end=&quot;2119&quot; data-start=&quot;2095&quot;&gt;Aspect&lt;/th&gt;&lt;th data-col-size=&quot;lg&quot; data-end=&quot;2203&quot; data-start=&quot;2119&quot;&gt;Pros&lt;/th&gt;&lt;th data-col-size=&quot;md&quot; data-end=&quot;2299&quot; data-start=&quot;2203&quot;&gt;Cons &amp;amp; Considerations&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody data-end=&quot;3548&quot; data-start=&quot;2505&quot;&gt;&lt;tr data-end=&quot;2746&quot; data-start=&quot;2505&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2528&quot; data-start=&quot;2505&quot;&gt;&lt;strong data-end=&quot;2521&quot; data-start=&quot;2507&quot;&gt;Modularity&lt;/strong&gt;&lt;/td&gt;&lt;td data-col-size=&quot;lg&quot; data-end=&quot;2644&quot; data-start=&quot;2528&quot;&gt;Each agent has a focused role, making the system easier to extend or modify (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=The%20multi,It%27s%20very&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=The%20multi,It%27s%20very&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;docs.ag2.ai&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/td&gt;&lt;td data-col-size=&quot;md&quot; data-end=&quot;2746&quot; data-start=&quot;2644&quot;&gt;More agents increase the number of interactions to manage and may introduce coordination overhead.&lt;/td&gt;&lt;/tr&gt;&lt;tr data-end=&quot;2979&quot; data-start=&quot;2747&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2770&quot; data-start=&quot;2747&quot;&gt;&lt;strong data-end=&quot;2767&quot; data-start=&quot;2749&quot;&gt;Specialisation&lt;/strong&gt;&lt;/td&gt;&lt;td data-col-size=&quot;lg&quot; data-end=&quot;2881&quot; data-start=&quot;2770&quot;&gt;Domain‑specific agents can outperform a monolithic model on their task (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.analyticsvidhya.com/blog/2024/11/multi-agent-chatbots-with-autogen/#:~:text=Multi,responses%20compared%20to%20a%20single&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.analyticsvidhya.com/blog/2024/11/multi-agent-chatbots-with-autogen/#:~:text=Multi,responses%20compared%20to%20a%20single&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;analyticsvidhya.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/td&gt;&lt;td data-col-size=&quot;md&quot; data-end=&quot;2979&quot; data-start=&quot;2881&quot;&gt;Requires careful prompt design to keep agents within their remit and avoid conflicting advice.&lt;/td&gt;&lt;/tr&gt;&lt;tr data-end=&quot;3258&quot; data-start=&quot;2980&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3003&quot; data-start=&quot;2980&quot;&gt;&lt;strong data-end=&quot;3000&quot; data-start=&quot;2982&quot;&gt;Resource usage&lt;/strong&gt;&lt;/td&gt;&lt;td data-col-size=&quot;lg&quot; data-end=&quot;3156&quot; data-start=&quot;3003&quot;&gt;Multi‑agent architectures can run cheaper models for some roles while leveraging larger models only where needed (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=,4%20agent&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=,4%20agent&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;docs.ag2.ai&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/td&gt;&lt;td data-col-size=&quot;md&quot; data-end=&quot;3258&quot; data-start=&quot;3156&quot;&gt;Running multiple models in parallel increases latency and can be costly for high‑volume workloads.&lt;/td&gt;&lt;/tr&gt;&lt;tr data-end=&quot;3548&quot; data-start=&quot;3259&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3283&quot; data-start=&quot;3259&quot;&gt;&lt;strong data-end=&quot;3277&quot; data-start=&quot;3261&quot;&gt;Transparency&lt;/strong&gt;&lt;/td&gt;&lt;td data-col-size=&quot;lg&quot; data-end=&quot;3444&quot; data-start=&quot;3283&quot;&gt;By exposing distinct agent responses, users can understand how conclusions are derived (e.g., one agent summarises market data, another suggests allocations).&lt;/td&gt;&lt;td data-col-size=&quot;md&quot; data-end=&quot;3548&quot; data-start=&quot;3444&quot;&gt;In some contexts users may prefer a single, unified answer; too much detail may confuse non‑experts.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 data-end=&quot;3588&quot; data-start=&quot;3550&quot;&gt;Defining the finance chatbot agents&lt;/h2&gt;&lt;p data-end=&quot;3895&quot; data-start=&quot;3590&quot;&gt;Our finance chatbot will engage the user to understand their goals, analyse market conditions and provide general asset‑allocation suggestions.  It is &lt;strong data-end=&quot;3748&quot; data-start=&quot;3741&quot;&gt;not&lt;/strong&gt; a licensed financial adviser; recommendations are for educational purposes.  We adapt the roles from a mental‑health chatbot example into finance:&lt;/p&gt;&lt;ol data-end=&quot;4353&quot; data-start=&quot;3897&quot;&gt;
&lt;li data-end=&quot;3996&quot; data-start=&quot;3897&quot;&gt;
&lt;p data-end=&quot;3996&quot; data-start=&quot;3900&quot;&gt;&lt;strong data-end=&quot;3916&quot; data-start=&quot;3900&quot;&gt;Client&amp;nbsp;Agent&lt;/strong&gt; – Captures the user’s financial situation, risk tolerance and investment goals.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4181&quot; data-start=&quot;3997&quot;&gt;
&lt;p data-end=&quot;4181&quot; data-start=&quot;4000&quot;&gt;&lt;strong data-end=&quot;4025&quot; data-start=&quot;4000&quot;&gt;Market&amp;nbsp;Analysis&amp;nbsp;Agent&lt;/strong&gt; – Analyses the market based on user input.  It summarises macro‑economic trends, asset‑class performance and volatility without giving personalised advice.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4353&quot; data-start=&quot;4182&quot;&gt;
&lt;p data-end=&quot;4353&quot; data-start=&quot;4185&quot;&gt;&lt;strong data-end=&quot;4219&quot; data-start=&quot;4185&quot;&gt;Portfolio&amp;nbsp;Recommendation&amp;nbsp;Agent&lt;/strong&gt; – Suggests a high‑level asset allocation based on the analysis and user profile.  It does not trade or recommend specific securities.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p data-end=&quot;4394&quot; data-start=&quot;4355&quot;&gt;The roles can be summarized as follows:&lt;/p&gt;&lt;div class=&quot;_tableContainer_1rjym_1&quot;&gt;&lt;div class=&quot;group _tableWrapper_1rjym_13 flex w-fit flex-col-reverse&quot; tabindex=&quot;-1&quot;&gt;&lt;table class=&quot;w-fit min-w-(--thread-content-width)&quot; data-end=&quot;5281&quot; data-start=&quot;4396&quot;&gt;&lt;thead data-end=&quot;4452&quot; data-start=&quot;4396&quot;&gt;&lt;tr data-end=&quot;4452&quot; data-start=&quot;4396&quot;&gt;&lt;th data-col-size=&quot;sm&quot; data-end=&quot;4422&quot; data-start=&quot;4396&quot;&gt;Agent&lt;/th&gt;&lt;th data-col-size=&quot;lg&quot; data-end=&quot;4432&quot; data-start=&quot;4422&quot;&gt;Purpose&lt;/th&gt;&lt;th data-col-size=&quot;lg&quot; data-end=&quot;4452&quot; data-start=&quot;4432&quot;&gt;Key instructions&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody data-end=&quot;5281&quot; data-start=&quot;4509&quot;&gt;&lt;tr data-end=&quot;4725&quot; data-start=&quot;4509&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4535&quot; data-start=&quot;4509&quot;&gt;&lt;strong data-end=&quot;4527&quot; data-start=&quot;4511&quot;&gt;Client&amp;nbsp;Agent&lt;/strong&gt;&lt;/td&gt;&lt;td data-col-size=&quot;lg&quot; data-end=&quot;4631&quot; data-start=&quot;4535&quot;&gt;Collects information about the user’s financial goals, investment horizon and risk tolerance.&lt;/td&gt;&lt;td data-col-size=&quot;lg&quot; data-end=&quot;4725&quot; data-start=&quot;4631&quot;&gt;Ask the user about their objectives, constraints and any particular interests or concerns.&lt;/td&gt;&lt;/tr&gt;&lt;tr data-end=&quot;5003&quot; data-start=&quot;4726&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;4754&quot; data-start=&quot;4726&quot;&gt;&lt;strong data-end=&quot;4753&quot; data-start=&quot;4728&quot;&gt;Market&amp;nbsp;Analysis&amp;nbsp;Agent&lt;/strong&gt;&lt;/td&gt;&lt;td data-col-size=&quot;lg&quot; data-end=&quot;4873&quot; data-start=&quot;4754&quot;&gt;Summarises market conditions, including recent trends in equities, bonds, commodities and macro‑economic indicators.&lt;/td&gt;&lt;td data-col-size=&quot;lg&quot; data-end=&quot;5003&quot; data-start=&quot;4873&quot;&gt;Do not provide personal investment advice; instead, report high‑level insights such as whether markets are volatile or stable.&lt;/td&gt;&lt;/tr&gt;&lt;tr data-end=&quot;5281&quot; data-start=&quot;5004&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5041&quot; data-start=&quot;5004&quot;&gt;&lt;strong data-end=&quot;5040&quot; data-start=&quot;5006&quot;&gt;Portfolio&amp;nbsp;Recommendation&amp;nbsp;Agent&lt;/strong&gt;&lt;/td&gt;&lt;td data-col-size=&quot;lg&quot; data-end=&quot;5141&quot; data-start=&quot;5041&quot;&gt;Provides general asset‑allocation suggestions based on the user’s profile and the market summary.&lt;/td&gt;&lt;td data-col-size=&quot;lg&quot; data-end=&quot;5281&quot; data-start=&quot;5141&quot;&gt;Recommend broad categories (e.g., 60&amp;nbsp;% equities, 30&amp;nbsp;% bonds, 10&amp;nbsp;% alternatives); include a disclaimer that this is not financial advice.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 data-end=&quot;5312&quot; data-start=&quot;5283&quot;&gt;Setting up the environment&lt;/h2&gt;&lt;p data-end=&quot;5751&quot; data-start=&quot;5314&quot;&gt;To follow along you need Python 3.11+ and the &lt;code data-end=&quot;5365&quot; data-start=&quot;5360&quot;&gt;ag2&lt;/code&gt; library.  You can install AG2 via pip (&lt;code data-end=&quot;5430&quot; data-start=&quot;5405&quot;&gt;pip install ag2[openai]&lt;/code&gt;).  If you plan to run local models, install an API wrapper like &lt;code data-end=&quot;5503&quot; data-start=&quot;5495&quot;&gt;ollama&lt;/code&gt; and download a model such as Llama&amp;nbsp;3.1 (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://tinztwinshub.com/investment-research/build-a-multi-agent-stock-market-analyst-to-compare-stock-price-performance/#:~:text=Our%20chatbot%20uses%20Meta%E2%80%99s%20,or%20tools%20to%20the%20application&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://tinztwinshub.com/investment-research/build-a-multi-agent-stock-market-analyst-to-compare-stock-price-performance/#:~:text=Our%20chatbot%20uses%20Meta%E2%80%99s%20,or%20tools%20to%20the%20application&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;tinztwinshub.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  You will also need API keys if using cloud models.  In this example we configure the LLM via &lt;code data-end=&quot;5689&quot; data-start=&quot;5677&quot;&gt;llm_config&lt;/code&gt; without specifying an API key; supply your own key as needed.&lt;/p&gt;&lt;h2 data-end=&quot;5779&quot; data-start=&quot;5753&quot;&gt;Implementing the agents&lt;/h2&gt;&lt;p data-end=&quot;6000&quot; data-start=&quot;5781&quot;&gt;The following Python code illustrates how to create the finance chatbot.  It borrows the group‑chat structure from the mental‑health example and uses AG2’s &lt;code data-end=&quot;5955&quot; data-start=&quot;5937&quot;&gt;ConversableAgent&lt;/code&gt;, &lt;code data-end=&quot;5968&quot; data-start=&quot;5957&quot;&gt;GroupChat&lt;/code&gt; and &lt;code data-end=&quot;5991&quot; data-start=&quot;5973&quot;&gt;GroupChatManager&lt;/code&gt; classes:&lt;/p&gt;&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;8899&quot; data-start=&quot;6002&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;from&lt;/span&gt; autogen &lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; ConversableAgent, GroupChat, GroupChatManager

&lt;span class=&quot;hljs-comment&quot;&gt;# Replace None with your OpenAI or local model API key if required&lt;/span&gt;
llm_config = {&lt;span class=&quot;hljs-string&quot;&gt;&quot;config_list&quot;&lt;/span&gt;: [{&lt;span class=&quot;hljs-string&quot;&gt;&quot;model&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;gpt-4o&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;api_key&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-literal&quot;&gt;None&lt;/span&gt;}]}

&lt;span class=&quot;hljs-comment&quot;&gt;# 1. Client Agent: collects user goals and risk tolerance&lt;/span&gt;
client_agent = ConversableAgent(
    name=&lt;span class=&quot;hljs-string&quot;&gt;&quot;client&quot;&lt;/span&gt;,
    system_message=(
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;You are the client interface.\n&quot;&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;Your job is to describe your financial goals, risk tolerance, investment horizon, &quot;&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;and any budget constraints or ethical considerations.&quot;&lt;/span&gt;
    ),
    llm_config=llm_config
)

&lt;span class=&quot;hljs-comment&quot;&gt;# 2. Market Analysis Agent: summarises current market conditions&lt;/span&gt;
market_analysis_agent = ConversableAgent(
    name=&lt;span class=&quot;hljs-string&quot;&gt;&quot;market_analysis&quot;&lt;/span&gt;,
    system_message=(
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;You analyse the current market based on the client&#39;s input.&quot;&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;Summarise macroeconomic factors (interest rates, inflation), sector trends and recent market volatility. &quot;&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;Do not provide personalised investment advice; simply report high-level observations.&quot;&lt;/span&gt;
    ),
    llm_config=llm_config
)

&lt;span class=&quot;hljs-comment&quot;&gt;# 3. Portfolio Recommendation Agent: proposes a high-level allocation&lt;/span&gt;
portfolio_recommendation_agent = ConversableAgent(
    name=&lt;span class=&quot;hljs-string&quot;&gt;&quot;portfolio_recommendation&quot;&lt;/span&gt;,
    system_message=(
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;You suggest a general asset allocation based on the analysis from the Market Analysis Agent.&quot;&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;Consider the client&#39;s risk tolerance and goals.&quot;&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;Recommend proportions across asset classes such as equities, bonds, and alternatives.&quot;&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;Include a disclaimer that the suggestions are educational and not financial advice.&quot;&lt;/span&gt;
    ),
    llm_config=llm_config
)

&lt;span class=&quot;hljs-comment&quot;&gt;# Configure a group chat where the analysis and recommendation agents converse&lt;/span&gt;
&lt;span class=&quot;hljs-comment&quot;&gt;# The manager orchestrates turn-taking in round‑robin fashion&lt;/span&gt;
finance_groupchat = GroupChat(
    agents=[market_analysis_agent, portfolio_recommendation_agent],
    messages=[],
    max_round=&lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt;,
    speaker_selection_method=&lt;span class=&quot;hljs-string&quot;&gt;&quot;round_robin&quot;&lt;/span&gt;
)

manager = GroupChatManager(name=&lt;span class=&quot;hljs-string&quot;&gt;&quot;manager&quot;&lt;/span&gt;, groupchat=finance_groupchat)

&lt;span class=&quot;hljs-comment&quot;&gt;# Function to start the finance chatbot&lt;/span&gt;

&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;start_finance_chat&lt;/span&gt;():
    &lt;span class=&quot;hljs-built_in&quot;&gt;print&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&quot;\nWelcome to the AI Finance Chatbot!&quot;&lt;/span&gt;)
    user_input = &lt;span class=&quot;hljs-built_in&quot;&gt;input&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&quot;Please describe your financial goals and risk tolerance: &quot;&lt;/span&gt;)

    &lt;span class=&quot;hljs-comment&quot;&gt;# The client agent initiates the conversation with the manager&lt;/span&gt;
    &lt;span class=&quot;hljs-built_in&quot;&gt;print&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&quot;\nAnalysing your profile and market conditions...&quot;&lt;/span&gt;)
    response = client_agent.initiate_chat(
        manager,
        message=(
            &lt;span class=&quot;hljs-string&quot;&gt;f&quot;My financial goals and risk tolerance are: &lt;span class=&quot;hljs-subst&quot;&gt;{user_input}&lt;/span&gt;&lt;/span&gt;. &quot;
            &lt;span class=&quot;hljs-string&quot;&gt;&quot;Based on this, what insights and recommendations can you provide?&quot;&lt;/span&gt;
        )
    )

    &lt;span class=&quot;hljs-comment&quot;&gt;# Fallback in case the first response is empty&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;not&lt;/span&gt; response:
        response = portfolio_recommendation_agent.initiate_chat(
            manager,
            message=&lt;span class=&quot;hljs-string&quot;&gt;&quot;Based on the client&#39;s goals, please provide portfolio recommendations.&quot;&lt;/span&gt;
        )

&lt;span class=&quot;hljs-comment&quot;&gt;# Run the chatbot&lt;/span&gt;
start_finance_chat()
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h3 data-end=&quot;8917&quot; data-start=&quot;8901&quot;&gt;How it works&lt;/h3&gt;&lt;ol data-end=&quot;9766&quot; data-start=&quot;8919&quot;&gt;
&lt;li data-end=&quot;9082&quot; data-start=&quot;8919&quot;&gt;
&lt;p data-end=&quot;9082&quot; data-start=&quot;8922&quot;&gt;&lt;strong data-end=&quot;8936&quot; data-start=&quot;8922&quot;&gt;User input&lt;/strong&gt; – The &lt;code data-end=&quot;8957&quot; data-start=&quot;8943&quot;&gt;client_agent&lt;/code&gt; collects the investor’s goals and risk tolerance.  In a real deployment you might ask follow‑up questions to ensure clarity.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;9330&quot; data-start=&quot;9083&quot;&gt;
&lt;p data-end=&quot;9330&quot; data-start=&quot;9086&quot;&gt;&lt;strong data-end=&quot;9105&quot; data-start=&quot;9086&quot;&gt;Market analysis&lt;/strong&gt; – The &lt;code data-end=&quot;9135&quot; data-start=&quot;9112&quot;&gt;market_analysis_agent&lt;/code&gt; reviews the input and provides a concise summary of current market conditions.  It does not provide personalised advice, reflecting a common compliance requirement for general market commentary.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;9532&quot; data-start=&quot;9331&quot;&gt;
&lt;p data-end=&quot;9532&quot; data-start=&quot;9334&quot;&gt;&lt;strong data-end=&quot;9358&quot; data-start=&quot;9334&quot;&gt;Portfolio suggestion&lt;/strong&gt; – The &lt;code data-end=&quot;9397&quot; data-start=&quot;9365&quot;&gt;portfolio_recommendation_agent&lt;/code&gt; uses the market summary and client profile to recommend an asset allocation.  It emphasises diversification and includes a disclaimer.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;9766&quot; data-start=&quot;9533&quot;&gt;
&lt;p data-end=&quot;9766&quot; data-start=&quot;9536&quot;&gt;&lt;strong data-end=&quot;9561&quot; data-start=&quot;9536&quot;&gt;Group chat management&lt;/strong&gt; – The &lt;code data-end=&quot;9586&quot; data-start=&quot;9568&quot;&gt;GroupChatManager&lt;/code&gt; coordinates the exchange.  We use a round‑robin method to ensure both agents contribute (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.analyticsvidhya.com/blog/2024/11/multi-agent-chatbots-with-autogen/#:~:text=import%20autogen&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.analyticsvidhya.com/blog/2024/11/multi-agent-chatbots-with-autogen/#:~:text=import%20autogen&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;analyticsvidhya.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  The &lt;code data-end=&quot;9731&quot; data-start=&quot;9720&quot;&gt;max_round&lt;/code&gt; parameter prevents infinite loops.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;h4 data-end=&quot;9791&quot; data-start=&quot;9768&quot;&gt;Sample interaction&lt;/h4&gt;&lt;p data-end=&quot;9953&quot; data-start=&quot;9793&quot;&gt;Suppose a user says: &lt;em data-end=&quot;9915&quot; data-start=&quot;9814&quot;&gt;“I’m saving for retirement in 20&amp;nbsp;years, have moderate risk tolerance and want to invest ethically.”&lt;/em&gt;  The agents might respond as follows:&lt;/p&gt;&lt;blockquote data-end=&quot;10703&quot; data-start=&quot;9955&quot;&gt;
&lt;p data-end=&quot;10300&quot; data-start=&quot;9957&quot;&gt;&lt;strong data-end=&quot;9983&quot; data-start=&quot;9957&quot;&gt;Market&amp;nbsp;Analysis&amp;nbsp;Agent:&lt;/strong&gt; “Based on the current environment, central banks are signalling that interest rates may remain high to curb inflation, equities have been volatile with technology and renewable‑energy sectors outperforming, and bond yields offer moderate returns.  Commodities like gold have appreciated due to geopolitical tension.”&lt;/p&gt;
&lt;p data-end=&quot;10703&quot; data-start=&quot;10305&quot;&gt;&lt;strong data-end=&quot;10340&quot; data-start=&quot;10305&quot;&gt;Portfolio&amp;nbsp;Recommendation&amp;nbsp;Agent:&lt;/strong&gt; “Given your 20‑year horizon and moderate risk tolerance, a diversified allocation could be 60&amp;nbsp;% equities (with a tilt towards ESG‑focused funds), 30&amp;nbsp;% bonds (combining government and investment‑grade corporate bonds), and 10&amp;nbsp;% alternatives such as real‑estate investment trusts or commodities.  This is general information and not personalised financial advice.”&lt;/p&gt;
&lt;/blockquote&gt;&lt;p data-end=&quot;10955&quot; data-start=&quot;10705&quot;&gt;In a real‑world application you could register functions that fetch real‑time market data or compute portfolio statistics using AG2’s &lt;code data-end=&quot;10858&quot; data-start=&quot;10839&quot;&gt;register_function&lt;/code&gt; API (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://tinztwinshub.com/investment-research/build-a-multi-agent-stock-market-analyst-to-compare-stock-price-performance/#:~:text=,the%20function%20and%20its%20registration&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://tinztwinshub.com/investment-research/build-a-multi-agent-stock-market-analyst-to-compare-stock-price-performance/#:~:text=,the%20function%20and%20its%20registration&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;tinztwinshub.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;, but our simple example relies on the language model.&lt;/p&gt;&lt;h2 data-end=&quot;10990&quot; data-start=&quot;10957&quot;&gt;Pros and cons of this approach&lt;/h2&gt;&lt;ul data-end=&quot;12200&quot; data-start=&quot;10992&quot;&gt;
&lt;li data-end=&quot;11623&quot; data-start=&quot;10992&quot;&gt;
&lt;p data-end=&quot;11003&quot; data-start=&quot;10994&quot;&gt;&lt;strong data-end=&quot;11003&quot; data-start=&quot;10994&quot;&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-end=&quot;11623&quot; data-start=&quot;11006&quot;&gt;
&lt;li data-end=&quot;11265&quot; data-start=&quot;11006&quot;&gt;
&lt;p data-end=&quot;11265&quot; data-start=&quot;11008&quot;&gt;&lt;em data-end=&quot;11039&quot; data-start=&quot;11008&quot;&gt;Modularity and specialisation&lt;/em&gt; – Each agent has a clear purpose, which can improve the relevance and quality of responses (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=The%20multi,agents%2C%20one%20can%20compose%20and&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=The%20multi,agents%2C%20one%20can%20compose%20and&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;docs.ag2.ai&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  For example, one agent summarises macro data while another focuses on portfolio construction.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;11461&quot; data-start=&quot;11268&quot;&gt;
&lt;p data-end=&quot;11461&quot; data-start=&quot;11270&quot;&gt;&lt;em data-end=&quot;11289&quot; data-start=&quot;11270&quot;&gt;Ease of extension&lt;/em&gt; – You can add agents to handle regulatory checks, tax considerations or portfolio rebalancing without rewriting existing components (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=The%20multi,agents%2C%20one%20can%20compose%20and&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=The%20multi,agents%2C%20one%20can%20compose%20and&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;docs.ag2.ai&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;11623&quot; data-start=&quot;11464&quot;&gt;
&lt;p data-end=&quot;11623&quot; data-start=&quot;11466&quot;&gt;&lt;em data-end=&quot;11480&quot; data-start=&quot;11466&quot;&gt;Transparency&lt;/em&gt; – Users can see how the system arrives at recommendations because each agent’s contribution is visible (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=Benefits%20of%20multi&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=Benefits%20of%20multi&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;docs.ag2.ai&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;12200&quot; data-start=&quot;11625&quot;&gt;
&lt;p data-end=&quot;11636&quot; data-start=&quot;11627&quot;&gt;&lt;strong data-end=&quot;11636&quot; data-start=&quot;11627&quot;&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-end=&quot;12200&quot; data-start=&quot;11639&quot;&gt;
&lt;li data-end=&quot;11794&quot; data-start=&quot;11639&quot;&gt;
&lt;p data-end=&quot;11794&quot; data-start=&quot;11641&quot;&gt;&lt;em data-end=&quot;11664&quot; data-start=&quot;11641&quot;&gt;Coordination overhead&lt;/em&gt; – More agents mean more messages and potential latency.  Tuning &lt;code data-end=&quot;11740&quot; data-start=&quot;11729&quot;&gt;max_round&lt;/code&gt; and speaker selection helps avoid long conversations.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;11971&quot; data-start=&quot;11797&quot;&gt;
&lt;p data-end=&quot;11971&quot; data-start=&quot;11799&quot;&gt;&lt;em data-end=&quot;11818&quot; data-start=&quot;11799&quot;&gt;Prompt management&lt;/em&gt; – Agents must be carefully prompted to avoid stepping outside their roles.  If the market‑analysis agent gives advice, it could cause compliance issues.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;12200&quot; data-start=&quot;11974&quot;&gt;
&lt;p data-end=&quot;12200&quot; data-start=&quot;11976&quot;&gt;&lt;em data-end=&quot;11992&quot; data-start=&quot;11976&quot;&gt;Resource usage&lt;/em&gt; – Running multiple models or using powerful models for each agent can be costly.  Techniques like mixing cheap and expensive models or limiting context length can help (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=,4%20agent&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=,4%20agent&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;docs.ag2.ai&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;h2 data-end=&quot;12224&quot; data-start=&quot;12202&quot;&gt;Future enhancements&lt;/h2&gt;&lt;ul data-end=&quot;13221&quot; data-start=&quot;12226&quot;&gt;
&lt;li data-end=&quot;12539&quot; data-start=&quot;12226&quot;&gt;
&lt;p data-end=&quot;12539&quot; data-start=&quot;12228&quot;&gt;&lt;strong data-end=&quot;12259&quot; data-start=&quot;12228&quot;&gt;Real‑time data integration:&lt;/strong&gt; Register functions to call APIs (e.g., yfinance, Bloomberg, or custom data feeds) so the analysis agent can deliver up‑to‑the‑minute market summaries.  The Tinz&amp;nbsp;Twins example shows how to plot year‑to‑date gains using a registered function (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://tinztwinshub.com/investment-research/build-a-multi-agent-stock-market-analyst-to-compare-stock-price-performance/#:~:text=,the%20function%20and%20its%20registration&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://tinztwinshub.com/investment-research/build-a-multi-agent-stock-market-analyst-to-compare-stock-price-performance/#:~:text=,the%20function%20and%20its%20registration&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;tinztwinshub.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;12741&quot; data-start=&quot;12540&quot;&gt;
&lt;p data-end=&quot;12741&quot; data-start=&quot;12542&quot;&gt;&lt;strong data-end=&quot;12568&quot; data-start=&quot;12542&quot;&gt;Regulatory compliance:&lt;/strong&gt; Add an agent that checks whether recommendations adhere to regional regulations (e.g., MIFID&amp;nbsp;II or SEC guidelines).  It could flag issues before suggestions reach the user.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;13022&quot; data-start=&quot;12742&quot;&gt;
&lt;p data-end=&quot;13022&quot; data-start=&quot;12744&quot;&gt;&lt;strong data-end=&quot;12779&quot; data-start=&quot;12744&quot;&gt;Advanced conversation patterns:&lt;/strong&gt; Use nested or constrained group chats to handle multi‑step workflows, such as gathering risk questionnaires, performing Monte‑Carlo simulations and then generating a report.  AG2 supports these patterns (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=combining%20models%20and%20agents,&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=combining%20models%20and%20agents,&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;docs.ag2.ai&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;13221&quot; data-start=&quot;13023&quot;&gt;
&lt;p data-end=&quot;13221&quot; data-start=&quot;13025&quot;&gt;&lt;strong data-end=&quot;13048&quot; data-start=&quot;13025&quot;&gt;Multi‑modal output:&lt;/strong&gt; Integrate charting tools to visualise asset allocations or risk/return trade‑offs.  For instance, register a function that plots allocation pie charts and returns an image.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;h2 data-end=&quot;13236&quot; data-start=&quot;13223&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;



























&lt;/p&gt;&lt;p data-end=&quot;14083&quot; data-start=&quot;13238&quot;&gt;AG2’s multi‑agent architecture makes it straightforward to build tailored chatbots for complex domains such as finance.  By dividing the conversation into specialised roles, developers can achieve greater accuracy, transparency and extensibility compared with monolithic chatbots (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.analyticsvidhya.com/blog/2024/11/multi-agent-chatbots-with-autogen/#:~:text=Multi,responses%20compared%20to%20a%20single&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.analyticsvidhya.com/blog/2024/11/multi-agent-chatbots-with-autogen/#:~:text=Multi,responses%20compared%20to%20a%20single&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;analyticsvidhya.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  The simple example above illustrates how you can adapt a mental‑health chatbot pattern to a personal‑finance advisor.  Although multi‑agent systems introduce coordination challenges and resource overhead, careful design and modern frameworks like AG2 or Chainlit can mitigate these issues (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=The%20multi,It%27s%20very&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://docs.ag2.ai/0.8.7/docs/blog/2024/05/24/Agent/#:~:text=The%20multi,It%27s%20very&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;docs.ag2.ai&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  As open‑source models and tooling evolve, we can expect multi‑agent finance chatbots to become more capable—potentially integrating real‑time data, compliance checks and sophisticated analytics.&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2025/10/building-multiagent-finance-chatbot.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg14JYKYf79T4ii7Dw86A3QqUwnV2Qmk-XRd0ylU6TvzO_pbmDwgLxOXFXoUFO7RQaA9BoB2Sauim8pPRugTLgZMGlot1gzigjLiMcBzn_qxJ-uFuqlJVyVeXl-FjMmffDKKg1d6xUvxW6XshSNlkDnF55_UIc8irs7MeM5Rmwvo3qafDQthN4Jox8XpNo/s72-c/post.png" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>London, UK</georss:featurename><georss:point>51.5072178 -0.1275862</georss:point><georss:box>23.196983963821154 -35.2838362 79.817451636178845 35.0286638</georss:box></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-6691039309025967621</guid><pubDate>Sat, 11 Oct 2025 18:41:00 +0000</pubDate><atom:updated>2026-03-15T21:41:43.688+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Agentic AI</category><category domain="http://www.blogger.com/atom/ns#">AI Agents</category><category domain="http://www.blogger.com/atom/ns#">AI Chatbot</category><category domain="http://www.blogger.com/atom/ns#">Compliance Automation</category><category domain="http://www.blogger.com/atom/ns#">CrewAI</category><category domain="http://www.blogger.com/atom/ns#">ESMA</category><category domain="http://www.blogger.com/atom/ns#">Financial Regulation</category><category domain="http://www.blogger.com/atom/ns#">GPT-4o</category><category domain="http://www.blogger.com/atom/ns#">HuggingFace</category><category domain="http://www.blogger.com/atom/ns#">MiFID II</category><category domain="http://www.blogger.com/atom/ns#">MiFIR</category><category domain="http://www.blogger.com/atom/ns#">openAI</category><category domain="http://www.blogger.com/atom/ns#">PDFSearchTool</category><category domain="http://www.blogger.com/atom/ns#">RegTech</category><category domain="http://www.blogger.com/atom/ns#">Task-Centric AI</category><title>Building a MiFID II Compliance Chatbot with CrewAI</title><description>&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKVOPmJuVHpPEhxkDQ2Sj6ingI7wZ0Ws9zVFQcLCDR1qkPnEy1-yP-g5giG3WC1Gw2GfjRj6UJphLVY275G07DoGmsBtye8REn3q7NhHc7hJ2GGAT2E5yRsxoqRPkZqJzMlIMNCREUFrWKvAzvH1mWTstt1u_jhcK8uhl-vk6mp3dt2ChWacW2azK5b34/s1024/ChatGPT%20Image%20Oct%2010,%202025,%2007_11_05%20PM.png&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1024&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKVOPmJuVHpPEhxkDQ2Sj6ingI7wZ0Ws9zVFQcLCDR1qkPnEy1-yP-g5giG3WC1Gw2GfjRj6UJphLVY275G07DoGmsBtye8REn3q7NhHc7hJ2GGAT2E5yRsxoqRPkZqJzMlIMNCREUFrWKvAzvH1mWTstt1u_jhcK8uhl-vk6mp3dt2ChWacW2azK5b34/s320/ChatGPT%20Image%20Oct%2010,%202025,%2007_11_05%20PM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;Financial regulation is notoriously complex. Investment firms must interpret and comply with lengthy documents such as the&amp;nbsp;&lt;em data-end=&quot;1090&quot; data-start=&quot;1046&quot;&gt;Markets in Financial Instruments Directive&lt;/em&gt;&amp;nbsp;(MiFID&amp;nbsp;II) and its companion regulation (MiFIR), while preparing for MiFID&amp;nbsp;III. Compliance officers routinely pore over hundreds of pages of policy statements, ESMA Q&amp;amp;As and Regulatory Technical Standards (RTS). At the same time, regulators are tightening expectations: by September&amp;nbsp;2025 firms will need to implement the MiFID&amp;nbsp;III amendments and be ready to demonstrate that surveillance and inducement controls work across every channel&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=9%3A12&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=9%3A12&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;&amp;nbsp;(luware.com&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=communications%20that%20may%20lead%20to,auditable%20communications%20across%20all%20channels&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=communications%20that%20may%20lead%20to,auditable%20communications%20across%20all%20channels&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;luware.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;. Manual search is inefficient and error‑prone.&lt;/div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;p data-end=&quot;2068&quot; data-start=&quot;1654&quot;&gt;This article shows how to build a&amp;nbsp;&lt;strong data-end=&quot;1719&quot; data-start=&quot;1688&quot;&gt;MiFID&amp;nbsp;II compliance chatbot&lt;/strong&gt;&amp;nbsp;using CrewAI, a multi‑agent framework. You will learn how to organise the workflow, assign tools at the agent or task level, avoid common validation errors, and anticipate upcoming regulatory changes. The techniques apply beyond finance; they illustrate a pattern for building reliable agentic systems that combine retrieval with language models.&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div class=&quot;_tableContainer_1rjym_1&quot;&gt;&lt;div class=&quot;group _tableWrapper_1rjym_13 flex w-fit flex-col-reverse&quot; tabindex=&quot;-1&quot;&gt;&lt;table class=&quot;w-fit min-w-(--thread-content-width)&quot; data-end=&quot;903&quot; data-start=&quot;66&quot;&gt;&lt;thead data-end=&quot;86&quot; data-start=&quot;66&quot;&gt;&lt;tr data-end=&quot;86&quot; data-start=&quot;66&quot;&gt;&lt;th data-col-size=&quot;sm&quot; data-end=&quot;75&quot; data-start=&quot;66&quot;&gt;&lt;/th&gt;&lt;th data-col-size=&quot;lg&quot; data-end=&quot;86&quot; data-start=&quot;75&quot;&gt;&lt;br /&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody data-end=&quot;903&quot; data-start=&quot;97&quot;&gt;&lt;tr data-end=&quot;213&quot; data-start=&quot;97&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;113&quot; data-start=&quot;97&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;td data-col-size=&quot;lg&quot; data-end=&quot;213&quot; data-start=&quot;113&quot;&gt;&lt;strong data-end=&quot;112&quot; data-start=&quot;99&quot;&gt;Framework&lt;/strong&gt;&lt;br /&gt;CrewAI orchestrates multiple AI agents to search MiFID-related PDFs and draft compliance answers&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr data-end=&quot;321&quot; data-start=&quot;214&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;229&quot; data-start=&quot;214&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;td data-col-size=&quot;lg&quot; data-end=&quot;321&quot; data-start=&quot;229&quot;&gt;&lt;strong data-end=&quot;228&quot; data-start=&quot;216&quot;&gt;Approach&lt;/strong&gt;&lt;br /&gt;Compare agent‑centric vs task‑centric tool assignment for reliability and predictability&lt;br /&gt;&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr data-end=&quot;453&quot; data-start=&quot;322&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;336&quot; data-start=&quot;322&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;td data-col-size=&quot;lg&quot; data-end=&quot;453&quot; data-start=&quot;336&quot;&gt;&lt;strong data-end=&quot;335&quot; data-start=&quot;324&quot;&gt;Key Fix&lt;/strong&gt;&lt;br /&gt;Teach the language model to include the mandatory &lt;code data-end=&quot;409&quot; data-start=&quot;388&quot;&gt;{&quot;query&quot;: &quot;&amp;lt;text&amp;gt;&quot;}&lt;/code&gt; argument when calling the PDF search tool&lt;br /&gt;&lt;br /&gt;&lt;strong data-end=&quot;478&quot; data-start=&quot;456&quot;&gt;Regulatory Horizon&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr data-end=&quot;660&quot; data-start=&quot;454&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;479&quot; data-start=&quot;454&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;td data-col-size=&quot;lg&quot; data-end=&quot;660&quot; data-start=&quot;479&quot;&gt;MiFID&amp;nbsp;III amendments enter force March&amp;nbsp;28&amp;nbsp;2024 with a main implementation deadline of September&amp;nbsp;29&amp;nbsp;2025 (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=9%3A12&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=9%3A12&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;luware.com&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=MiFID%20III%20follows%20a%20structured,compliance%20strategies%20with%20these%20milestones&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=MiFID%20III%20follows%20a%20structured,compliance%20strategies%20with%20these%20milestones&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;luware.com&lt;/span&gt;&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;&lt;table class=&quot;w-fit min-w-(--thread-content-width)&quot; data-end=&quot;903&quot; data-start=&quot;66&quot;&gt;&lt;tbody data-end=&quot;903&quot; data-start=&quot;97&quot;&gt;&lt;tr data-end=&quot;903&quot; data-start=&quot;661&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;680&quot; data-start=&quot;661&quot;&gt;&lt;strong data-end=&quot;679&quot; data-start=&quot;663&quot;&gt;New Features&lt;/strong&gt;&lt;/td&gt;&lt;td data-col-size=&quot;lg&quot; data-end=&quot;903&quot; data-start=&quot;680&quot;&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr data-end=&quot;903&quot; data-start=&quot;661&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;680&quot; data-start=&quot;661&quot;&gt;&lt;/td&gt;&lt;td data-col-size=&quot;lg&quot; data-end=&quot;903&quot; data-start=&quot;680&quot;&gt;CrewAI’s MCP standardises tool access and data sources (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=MCP%20&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=MCP%20&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;blog.crewai.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt; while guardrails and event buses increase reliability (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=Guardrails&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=Guardrails&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;blog.crewai.com&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=CrewAI%20now%20includes%20an%20event,the%20following%20events%20are%20emitted&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=CrewAI%20now%20includes%20an%20event,the%20following%20events%20are%20emitted&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;blog.crewai.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 data-end=&quot;920&quot; data-start=&quot;905&quot;&gt;Agent‑centric vs Task‑centric Tool Assignment&lt;/h2&gt;&lt;p data-end=&quot;2466&quot; data-start=&quot;2261&quot;&gt;CrewAI allows tools—such as a PDF searcher or a web search API—to be provided either to the &lt;em data-end=&quot;2360&quot; data-start=&quot;2353&quot;&gt;agent&lt;/em&gt; as a global toolbox or to individual &lt;em data-end=&quot;2405&quot; data-start=&quot;2398&quot;&gt;tasks&lt;/em&gt;.  These two patterns affect reliability and maintainability.&lt;/p&gt;&lt;h3 data-end=&quot;2498&quot; data-start=&quot;2468&quot;&gt;Agent‑centric (Generalist)&lt;/h3&gt;&lt;p data-end=&quot;2926&quot; data-start=&quot;2500&quot;&gt;In this common setup the agent receives a list of tools that it may call at any time.  The agent decides which tool to use based on the prompt and its internal reasoning.  This is flexible and quick to prototype, but it can lead to mis‑calls.  For example, the PDF search tool expects a JSON argument with a &lt;code data-end=&quot;2815&quot; data-start=&quot;2808&quot;&gt;query&lt;/code&gt; string.  Without explicit instructions, the LLM may omit the argument, leading to the familiar Pydantic error:&lt;/p&gt;&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;2994&quot; data-start=&quot;2928&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre!&quot;&gt;&lt;span class=&quot;hljs-title class_&quot;&gt;Error&lt;/span&gt;: &lt;span class=&quot;hljs-title class_&quot;&gt;Arguments&lt;/span&gt; validation &lt;span class=&quot;hljs-attr&quot;&gt;failed&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;`query`&lt;/span&gt; field required
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;3262&quot; data-start=&quot;2996&quot;&gt;When the agent owns the entire toolbox, every task must encode tool‑calling rules in its prompt to avoid such errors.  In our experience this reduces predictability as the system grows; even seasoned engineers forget to repeat the tool signature in each description.&lt;/p&gt;&lt;h3 data-end=&quot;3293&quot; data-start=&quot;3264&quot;&gt;Task‑centric (Specialist)&lt;/h3&gt;&lt;p data-end=&quot;3632&quot; data-start=&quot;3295&quot;&gt;The task‑centric approach grants tools &lt;strong data-end=&quot;3342&quot; data-start=&quot;3334&quot;&gt;only&lt;/strong&gt; to the tasks that need them.  When the agent begins a task it is temporarily given the required tools and cannot access others.  This design leads to more predictable tool calls because the agent knows &lt;em data-end=&quot;3552&quot; data-start=&quot;3545&quot;&gt;which&lt;/em&gt; tools are available for that job and the prompt can focus on &lt;em data-end=&quot;3619&quot; data-start=&quot;3614&quot;&gt;how&lt;/em&gt; to use them.&lt;/p&gt;&lt;p data-end=&quot;3947&quot; data-start=&quot;3634&quot;&gt;In regulated domains this scoping is critical.  It reduces the chance that a general‑purpose LLM will call a web search when only document evidence is allowed, and it makes unit testing straightforward: each task can be executed in isolation with its own tools and guardrails (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=Guardrails&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=Guardrails&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;blog.crewai.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;h3 data-end=&quot;3966&quot; data-start=&quot;3949&quot;&gt;Pros and Cons&lt;/h3&gt;&lt;ul data-end=&quot;4452&quot; data-start=&quot;3968&quot;&gt;
&lt;li data-end=&quot;4207&quot; data-start=&quot;3968&quot;&gt;
&lt;p data-end=&quot;3990&quot; data-start=&quot;3970&quot;&gt;&lt;strong data-end=&quot;3987&quot; data-start=&quot;3970&quot;&gt;Agent‑centric&lt;/strong&gt;:&lt;/p&gt;
&lt;ul data-end=&quot;4207&quot; data-start=&quot;3993&quot;&gt;
&lt;li data-end=&quot;4079&quot; data-start=&quot;3993&quot;&gt;
&lt;p data-end=&quot;4079&quot; data-start=&quot;3995&quot;&gt;&lt;em data-end=&quot;4001&quot; data-start=&quot;3995&quot;&gt;Pros&lt;/em&gt;: Simple configuration; fewer moving parts; agent decides which tool to use.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4207&quot; data-start=&quot;4082&quot;&gt;
&lt;p data-end=&quot;4207&quot; data-start=&quot;4084&quot;&gt;&lt;em data-end=&quot;4090&quot; data-start=&quot;4084&quot;&gt;Cons&lt;/em&gt;: LLM may mis‑call tools without guidance; prompts must repeat tool signatures; difficult to test tasks in isolation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4452&quot; data-start=&quot;4208&quot;&gt;
&lt;p data-end=&quot;4229&quot; data-start=&quot;4210&quot;&gt;&lt;strong data-end=&quot;4226&quot; data-start=&quot;4210&quot;&gt;Task‑centric&lt;/strong&gt;:&lt;/p&gt;
&lt;ul data-end=&quot;4452&quot; data-start=&quot;4232&quot;&gt;
&lt;li data-end=&quot;4367&quot; data-start=&quot;4232&quot;&gt;
&lt;p data-end=&quot;4367&quot; data-start=&quot;4234&quot;&gt;&lt;em data-end=&quot;4240&quot; data-start=&quot;4234&quot;&gt;Pros&lt;/em&gt;: Predictable and secure; tools scoped to tasks; easier to unit‑test and update; better adherence to compliance restrictions.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4452&quot; data-start=&quot;4370&quot;&gt;
&lt;p data-end=&quot;4452&quot; data-start=&quot;4372&quot;&gt;&lt;em data-end=&quot;4378&quot; data-start=&quot;4372&quot;&gt;Cons&lt;/em&gt;: Slightly more verbose setup; requires careful mapping of tasks to tools.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;h2 data-end=&quot;4473&quot; data-start=&quot;4454&quot;&gt;Code Walkthrough&lt;/h2&gt;&lt;p data-end=&quot;4735&quot; data-start=&quot;4475&quot;&gt;Let’s examine the core elements of the chatbot.  The code below instantiates an LLM, sets up PDF search tools for each regulatory document, defines an agent with a system prompt and tool‑calling guide, and creates two tasks orchestrated sequentially by a crew. Code can be found here: (&lt;a href=&quot;https://github.com/JordiCorbilla/langgraph-cookbook/tree/main/09%20-%20Mifid%20CrewAI%20Chatbot&quot;&gt;langgraph-cookbook/09 - Mifid CrewAI Chatbot at main · JordiCorbilla/langgraph-cookbook&lt;/a&gt;)&lt;/p&gt;&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;7152&quot; data-start=&quot;4737&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;from&lt;/span&gt; crewai &lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; Agent, Task, Crew, Process
&lt;span class=&quot;hljs-keyword&quot;&gt;from&lt;/span&gt; crewai &lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; LLM
&lt;span class=&quot;hljs-keyword&quot;&gt;from&lt;/span&gt; crewai_tools &lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; PDFSearchTool, SerperDevTool

llm = LLM(model=&lt;span class=&quot;hljs-string&quot;&gt;&quot;openai/gpt-4o-mini&quot;&lt;/span&gt;)

web_search_tool = SerperDevTool()

pdf_search_tool = PDFSearchTool(
    pdf=&lt;span class=&quot;hljs-string&quot;&gt;&quot;https://www.fca.org.uk/publication/policy/ps17-14.pdf&quot;&lt;/span&gt;,
    config={ &lt;span class=&quot;hljs-string&quot;&gt;&#39;embedder&#39;&lt;/span&gt;: { &lt;span class=&quot;hljs-string&quot;&gt;&#39;provider&#39;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&#39;huggingface&#39;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&#39;config&#39;&lt;/span&gt;: { &lt;span class=&quot;hljs-string&quot;&gt;&#39;model&#39;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&#39;sentence-transformers/all-MiniLM-L6-v2&#39;&lt;/span&gt; } } }
)
pdf_search_tool2 = PDFSearchTool(
    pdf=&lt;span class=&quot;hljs-string&quot;&gt;&quot;https://www.fca.org.uk/publication/consultation/cp16-19.pdf&quot;&lt;/span&gt;,
    config={ ... }
)
pdf_search_tool3 = PDFSearchTool(
    pdf=&lt;span class=&quot;hljs-string&quot;&gt;&quot;https://www.esma.europa.eu/sites/default/files/library/2016-1452_guidelines_mifid_ii_transaction_reporting.pdf&quot;&lt;/span&gt;,
    config={ ... }
)

COMPLIANCE_SYSTEM_PROMPT = &lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&quot;
You are a MiFID II / MiFIR compliance analyst. Answer ONLY with information grounded in the provided PDFs.  Always include article‑level citations.  Prefer the latest ESMA RTS for conflicts.  No speculation.
&quot;&quot;&quot;&lt;/span&gt;

CITATION_FORMAT_PROMPT = &lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&quot;
Format citations as markdown footnotes: [MiFID II&amp;nbsp;Art&amp;nbsp;X(Y)](SOURCE_URL#page=?), [MiFIR&amp;nbsp;Art&amp;nbsp;X(Y)](SOURCE_URL#page=?).
&quot;&quot;&quot;&lt;/span&gt;

TOOL_CALLING_GUIDE = &lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&quot;
When using &quot;Search a PDF&#39;s content&quot;, you MUST pass a JSON argument like {&quot;query&quot;: &quot;&amp;lt;short search string&amp;gt;&quot;}.  Never omit &#39;query&#39;.
&quot;&quot;&quot;&lt;/span&gt;

agent_centric_agent = Agent(
    role=&lt;span class=&quot;hljs-string&quot;&gt;&quot;MiFID Compliance Analyst&quot;&lt;/span&gt;,
    goal=&lt;span class=&quot;hljs-string&quot;&gt;&quot;Answer MiFID II / MiFIR questions with exact citations.&quot;&lt;/span&gt;,
    backstory=&lt;span class=&quot;hljs-string&quot;&gt;&quot;Seasoned regulatory analyst trained on MiFID II, MiFIR and FCA/ESMA RTS/Q&amp;amp;A.&quot;&lt;/span&gt;,
    tools=[pdf_search_tool, pdf_search_tool2, pdf_search_tool3, web_search_tool],
    llm=llm,
    system_prompt=COMPLIANCE_SYSTEM_PROMPT + CITATION_FORMAT_PROMPT + TOOL_CALLING_GUIDE
)

&lt;span class=&quot;hljs-comment&quot;&gt;# Define tasks and crew&lt;/span&gt;
agent_centric_task = Task(
    description=(
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;Answer &#39;{customer_query}&#39;. First, search the PDFs using &#39;Search a PDF&#39;s content&#39; with {\&quot;query\&quot;: \&quot;&amp;lt;your search string&amp;gt;\&quot;}. Extract relevant passages and synthesise the answer.&quot;&lt;/span&gt;),
    tools=[pdf_search_tool, pdf_search_tool2, pdf_search_tool3],
    agent=agent_centric_agent
)

response_drafting_task = Task(
    description=&lt;span class=&quot;hljs-string&quot;&gt;&quot;Use the gathered information to draft a comprehensive response to &#39;{customer_query}&#39;.&quot;&lt;/span&gt;,
    agent=agent_centric_agent,
    context=[agent_centric_task]
)

task_centric_crew = Crew(
    agents=[agent_centric_agent],
    tasks=[agent_centric_task, response_drafting_task],
    process=Process.sequential
)
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;7507&quot; data-start=&quot;7154&quot;&gt;The &lt;strong data-end=&quot;7175&quot; data-start=&quot;7158&quot;&gt;system prompt&lt;/strong&gt; instructs the agent to answer using only information from the PDFs, include citations, and avoid speculation.  We append a &lt;strong data-end=&quot;7321&quot; data-start=&quot;7299&quot;&gt;tool‑calling guide&lt;/strong&gt; that explicitly defines the JSON argument required by the &lt;code data-end=&quot;7395&quot; data-start=&quot;7380&quot;&gt;PDFSearchTool&lt;/code&gt;.  Without this guide the agent might call the tool incorrectly, causing the validation error described earlier.&lt;/p&gt;&lt;p data-end=&quot;7783&quot; data-start=&quot;7509&quot;&gt;The first task instructs the agent to search the PDFs for relevant text using a JSON query.  The second task uses the extracted passages to draft the final answer.  By assigning the PDF search tools only to the search task, we prevent accidental web queries during drafting.&lt;/p&gt;&lt;h2 data-end=&quot;7820&quot; data-start=&quot;7785&quot;&gt;Preparing for MiFID&amp;nbsp;III&lt;/h2&gt;&lt;p data-end=&quot;9451&quot; data-start=&quot;8642&quot;&gt;Our chatbot currently answers questions under MiFID&amp;nbsp;II and MiFIR.  However, regulatory evolution continues.  MiFID&amp;nbsp;III amendments were published in February&amp;nbsp;2024, entered into force in March&amp;nbsp;2024 and must be implemented by September&amp;nbsp;29&amp;nbsp;2025 (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=MiFID%20III%20follows%20a%20structured,compliance%20strategies%20with%20these%20milestones&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=MiFID%20III%20follows%20a%20structured,compliance%20strategies%20with%20these%20milestones&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;luware.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  The third iteration does not overhaul the framework but raises the bar on surveillance and inducements: compliance teams must prepare for heightened expectations around off‑channel communications and record‑keeping (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=9%3A12&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=9%3A12&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;luware.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  MiFID&amp;nbsp;III builds on MiFID&amp;nbsp;II by widening the focus to behavioural risks such as conflicts of interest and pricing opacity (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=communications%20that%20may%20lead%20to,auditable%20communications%20across%20all%20channels&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=communications%20that%20may%20lead%20to,auditable%20communications%20across%20all%20channels&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;luware.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;, and regulators expect active monitoring rather than passive archiving (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=The%20foundational%20requirement%20to%20record,III%E2%80%94but%20regulators%20will%20expect%20more&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=The%20foundational%20requirement%20to%20record,III%E2%80%94but%20regulators%20will%20expect%20more&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;luware.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;p data-end=&quot;9467&quot; data-start=&quot;9453&quot;&gt;To stay ahead:&lt;/p&gt;&lt;ul data-end=&quot;10554&quot; data-start=&quot;9469&quot;&gt;
&lt;li data-end=&quot;9648&quot; data-start=&quot;9469&quot;&gt;
&lt;p data-end=&quot;9648&quot; data-start=&quot;9471&quot;&gt;&lt;strong data-end=&quot;9509&quot; data-start=&quot;9471&quot;&gt;Integrate multi‑channel monitoring&lt;/strong&gt;: capture voice, chat, video and messaging across platforms; apply AI to detect misconduct patterns (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=2,requirements&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=2,requirements&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;luware.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;9769&quot; data-start=&quot;9649&quot;&gt;
&lt;p data-end=&quot;9769&quot; data-start=&quot;9651&quot;&gt;&lt;strong data-end=&quot;9680&quot; data-start=&quot;9651&quot;&gt;Extend the knowledge base&lt;/strong&gt;: ingest ESMA Q&amp;amp;As, RTS documents and MiFID&amp;nbsp;III draft texts; update embeddings regularly.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;9932&quot; data-start=&quot;9770&quot;&gt;
&lt;p data-end=&quot;9932&quot; data-start=&quot;9772&quot;&gt;&lt;strong data-end=&quot;9799&quot; data-start=&quot;9772&quot;&gt;Use CrewAI’s guardrails&lt;/strong&gt;: implement output‑length checks or hallucination detection to ensure answers remain grounded (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=Guardrails&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=Guardrails&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;blog.crewai.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;10132&quot; data-start=&quot;9933&quot;&gt;
&lt;p data-end=&quot;10132&quot; data-start=&quot;9935&quot;&gt;&lt;strong data-end=&quot;9959&quot; data-start=&quot;9935&quot;&gt;Leverage event buses&lt;/strong&gt; for audit logs and compliance evidence: CrewAI’s event bus emits events such as task started, tool use error, and LLM call completed (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=CrewAI%20now%20includes%20an%20event,the%20following%20events%20are%20emitted&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=CrewAI%20now%20includes%20an%20event,the%20following%20events%20are%20emitted&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;blog.crewai.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;10369&quot; data-start=&quot;10133&quot;&gt;
&lt;p data-end=&quot;10369&quot; data-start=&quot;10135&quot;&gt;&lt;strong data-end=&quot;10160&quot; data-start=&quot;10135&quot;&gt;Adopt query rewriting&lt;/strong&gt; and &lt;em data-end=&quot;10178&quot; data-start=&quot;10165&quot;&gt;agentic RAG&lt;/em&gt; to improve retrieval.  CrewAI now supports query rewriting to focus on relevant keywords and integrates with vector databases like Qdrant and Weaviate (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=Agentic%20RAG&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=Agentic%20RAG&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;blog.crewai.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;10554&quot; data-start=&quot;10370&quot;&gt;
&lt;p data-end=&quot;10554&quot; data-start=&quot;10372&quot;&gt;&lt;strong data-end=&quot;10394&quot; data-start=&quot;10372&quot;&gt;Plan for MiFID&amp;nbsp;III&lt;/strong&gt; deadlines: ensure your system can adapt to new PFOF bans, stricter inducement rules, and expanded communication capture (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=communications%20that%20may%20lead%20to,auditable%20communications%20across%20all%20channels&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=communications%20that%20may%20lead%20to,auditable%20communications%20across%20all%20channels&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;luware.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;h2 data-end=&quot;10594&quot; data-start=&quot;10556&quot;&gt;Beyond Compliance – General Lessons&lt;/h2&gt;&lt;p data-end=&quot;10698&quot; data-start=&quot;10596&quot;&gt;While this capstone project addresses a specific regulatory domain, the design patterns are broadly applicable:&lt;/p&gt;&lt;ol data-end=&quot;11856&quot; data-start=&quot;10700&quot;&gt;
&lt;li data-end=&quot;10941&quot; data-start=&quot;10700&quot;&gt;
&lt;p data-end=&quot;10941&quot; data-start=&quot;10703&quot;&gt;&lt;strong data-end=&quot;10740&quot; data-start=&quot;10703&quot;&gt;Be explicit about tool signatures&lt;/strong&gt;: LLMs cannot infer parameter names; include examples and constraints in prompts.  For production systems, consider using structured tool calling (JSON Schema) or wrappers that handle input validation.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;11105&quot; data-start=&quot;10942&quot;&gt;
&lt;p data-end=&quot;11105&quot; data-start=&quot;10945&quot;&gt;&lt;strong data-end=&quot;10969&quot; data-start=&quot;10945&quot;&gt;Scope tools to tasks&lt;/strong&gt;: Minimises misuse and aids debugging.  For high‑impact domains (finance, healthcare) this can be critical for legal and safety reasons.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;11305&quot; data-start=&quot;11106&quot;&gt;
&lt;p data-end=&quot;11305&quot; data-start=&quot;11109&quot;&gt;&lt;strong data-end=&quot;11135&quot; data-start=&quot;11109&quot;&gt;Instrument your agents&lt;/strong&gt;: Use CrewAI’s event bus to collect telemetry on tool use and LLM calls.  This data helps debug issues and demonstrates compliance (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=CrewAI%20now%20includes%20an%20event,the%20following%20events%20are%20emitted&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=CrewAI%20now%20includes%20an%20event,the%20following%20events%20are%20emitted&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;blog.crewai.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;11590&quot; data-start=&quot;11306&quot;&gt;
&lt;p data-end=&quot;11590&quot; data-start=&quot;11309&quot;&gt;&lt;strong data-end=&quot;11328&quot; data-start=&quot;11309&quot;&gt;Stay up to date&lt;/strong&gt;: Regulations change.  Regularly ingest new documents and search the web to supplement your knowledge base.  MiFID&amp;nbsp;III emphasises behaviour monitoring, so your system must adapt to detect tone and sentiment across channels (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=2,requirements&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://luware.com/blog/mifid-iii-a-compliance-guide-for-financial-businesses#:~:text=2,requirements&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;luware.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;11856&quot; data-start=&quot;11591&quot;&gt;
&lt;p data-end=&quot;11856&quot; data-start=&quot;11594&quot;&gt;&lt;strong data-end=&quot;11624&quot; data-start=&quot;11594&quot;&gt;Embrace guardrails and RAG&lt;/strong&gt;: Guardrails catch excessive or irrelevant outputs; agentic RAG (retrieval augmented generation) tailors the knowledge retrieval to each query, increasing precision and reducing hallucinations (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=Agentic%20RAG&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=Agentic%20RAG&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;blog.crewai.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;h2 data-end=&quot;11871&quot; data-start=&quot;11858&quot;&gt;Conclusion&lt;/h2&gt;&lt;p data-end=&quot;12430&quot; data-start=&quot;11873&quot;&gt;By combining CrewAI’s multi‑agent orchestration with dedicated tools for PDF and web search, we built a robust MiFID&amp;nbsp;II compliance chatbot.  Assigning tools at the &lt;strong data-end=&quot;12045&quot; data-start=&quot;12037&quot;&gt;task&lt;/strong&gt; level rather than the &lt;strong data-end=&quot;12077&quot; data-start=&quot;12068&quot;&gt;agent&lt;/strong&gt; level yields more predictable and testable workflows.  The critical missing‑&lt;code data-end=&quot;12161&quot; data-start=&quot;12154&quot;&gt;query&lt;/code&gt; error was resolved by teaching the LLM to call tools correctly.  Looking ahead, MiFID&amp;nbsp;III will demand even greater surveillance and behavioural oversight; our modular architecture can adapt by adding new documents, query‑rewriting agents, guardrails and event logging.&lt;/p&gt;&lt;p data-end=&quot;2068&quot; data-start=&quot;1654&quot;&gt;




























&lt;/p&gt;&lt;p data-end=&quot;12881&quot; data-start=&quot;12432&quot;&gt;Agentic AI is evolving rapidly.  CrewAI’s adoption of the MCP, guardrails and event bus underscores this progress (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=MCP%20&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=MCP%20&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;blog.crewai.com&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=Guardrails&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=Guardrails&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;blog.crewai.com&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=CrewAI%20now%20includes%20an%20event,the%20following%20events%20are%20emitted&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://blog.crewai.com/how-crewai-is-evolving-beyond-orchestration-to-create-the-most-powerful-agentic-ai-platform/#:~:text=CrewAI%20now%20includes%20an%20event,the%20following%20events%20are%20emitted&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;blog.crewai.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  With careful design, developers can harness these advances to build trustworthy, compliance‑ready chatbots that empower experts rather than replace them.  Start small, iterate quickly, and always validate your tools.&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2025/10/building-mifid-iicompliance-chatbot.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKVOPmJuVHpPEhxkDQ2Sj6ingI7wZ0Ws9zVFQcLCDR1qkPnEy1-yP-g5giG3WC1Gw2GfjRj6UJphLVY275G07DoGmsBtye8REn3q7NhHc7hJ2GGAT2E5yRsxoqRPkZqJzMlIMNCREUFrWKvAzvH1mWTstt1u_jhcK8uhl-vk6mp3dt2ChWacW2azK5b34/s72-c/ChatGPT%20Image%20Oct%2010,%202025,%2007_11_05%20PM.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-8738696650963479179</guid><pubDate>Sat, 04 Oct 2025 19:04:00 +0000</pubDate><atom:updated>2026-03-15T21:42:17.275+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Compliance Automation</category><category domain="http://www.blogger.com/atom/ns#">Financial Regulation</category><category domain="http://www.blogger.com/atom/ns#">MiFID II/III Updates</category><category domain="http://www.blogger.com/atom/ns#">Multi-Agent AI (CrewAI)</category><category domain="http://www.blogger.com/atom/ns#">Near Real-Time Reporting</category><category domain="http://www.blogger.com/atom/ns#">PFOF Ban &amp; Consolidated Tape</category><title>Automating MiFID Compliance with CrewAI</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOr8-FWpQA-zYy0LaIni5kpGsvWvvr5pu0pAnfxR0W6DpjSKQTBURDXutYc5PPF013_tRLxzdjvLIinrNfwbd8yC7-ZyePvGFjHvJ3JM9Z_lU_rcMOlrbgPdl4ijPc70ah2pN1kUVP_TbRWKWkNr9NpP4U7YAOvE_h2l0RDgcj3tL_hQBfpgp42JRFG0U/s1024/59aa09fa-33f8-4483-a4f9-78c2723cdbdd.png&quot; style=&quot;display: inline; margin-left: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1024&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOr8-FWpQA-zYy0LaIni5kpGsvWvvr5pu0pAnfxR0W6DpjSKQTBURDXutYc5PPF013_tRLxzdjvLIinrNfwbd8yC7-ZyePvGFjHvJ3JM9Z_lU_rcMOlrbgPdl4ijPc70ah2pN1kUVP_TbRWKWkNr9NpP4U7YAOvE_h2l0RDgcj3tL_hQBfpgp42JRFG0U/s320/59aa09fa-33f8-4483-a4f9-78c2723cdbdd.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;h1 style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/h1&gt;&lt;p&gt;&lt;/p&gt;
&lt;div class=&quot;_tableContainer_1rjym_1&quot;&gt;&lt;div class=&quot;group _tableWrapper_1rjym_13 flex w-fit flex-col-reverse&quot; tabindex=&quot;-1&quot;&gt;&lt;table class=&quot;w-fit min-w-(--thread-content-width)&quot; data-end=&quot;2287&quot; data-start=&quot;43&quot;&gt;&lt;thead data-end=&quot;68&quot; data-start=&quot;43&quot;&gt;&lt;/thead&gt;&lt;tbody data-end=&quot;2287&quot; data-start=&quot;79&quot;&gt;&lt;tr data-end=&quot;618&quot; data-start=&quot;79&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;104&quot; data-start=&quot;79&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;td data-col-size=&quot;xl&quot; data-end=&quot;618&quot; data-start=&quot;104&quot;&gt;&lt;strong data-end=&quot;103&quot; data-start=&quot;81&quot;&gt;Regulatory context&lt;/strong&gt;&lt;br /&gt;The MiFID II/III reviews introduce a single 7&amp;nbsp;% dark‑trading volume cap, ban payment for order flow (PFOF) and require Member States to ensure data‑quality standards for consolidated tape providers (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://legal.pwc.de/en/news/articles/mifir-mifid-ii-review-making-sense-of-the-key-amendments#:~:text=In%20more%20detail%2C%20the%20Review%3A&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://legal.pwc.de/en/news/articles/mifir-mifid-ii-review-making-sense-of-the-key-amendments#:~:text=In%20more%20detail%2C%20the%20Review%3A&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;legal.pwc.de&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  MiFID&amp;nbsp;III mandates extended data fields and near‑real‑time reporting by September&amp;nbsp;2025 (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=September%202025%20marks%20the%20deadline,authorities%20in%20near%20real%20time&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=September%202025%20marks%20the%20deadline,authorities%20in%20near%20real%20time&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;asctechnologies.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  PFOF will be fully banned and a consolidated tape for equities, ETFs and derivatives will launch in June&amp;nbsp;2026 (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=In%20June%202026%2C%20the%20PFOF,for%20routing%20customer%20orders%20there&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=In%20June%202026%2C%20the%20PFOF,for%20routing%20customer%20orders%20there&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;asctechnologies.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;strong data-end=&quot;638&quot; data-start=&quot;621&quot;&gt;Key deadlines&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr data-end=&quot;987&quot; data-start=&quot;619&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;639&quot; data-start=&quot;619&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;td data-col-size=&quot;xl&quot; data-end=&quot;987&quot; data-start=&quot;639&quot;&gt;Member States must transpose the MiFID&amp;nbsp;II/MiFIR review into national law by 28&amp;nbsp;September&amp;nbsp;2025 (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://legal.pwc.de/en/news/articles/mifir-mifid-ii-review-making-sense-of-the-key-amendments#:~:text=1,into%20force%20in%20late%202025&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://legal.pwc.de/en/news/articles/mifir-mifid-ii-review-making-sense-of-the-key-amendments#:~:text=1,into%20force%20in%20late%202025&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;legal.pwc.de&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  September&amp;nbsp;2025 is also the deadline for MiFID&amp;nbsp;III reporting obligations (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=September%202025%20marks%20the%20deadline,authorities%20in%20near%20real%20time&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=September%202025%20marks%20the%20deadline,authorities%20in%20near%20real%20time&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;asctechnologies.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;; the PFOF ban and consolidated tape come into force in June&amp;nbsp;2026 (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=In%20June%202026%2C%20the%20PFOF,for%20routing%20customer%20orders%20there&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=In%20June%202026%2C%20the%20PFOF,for%20routing%20customer%20orders%20there&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;asctechnologies.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;strong data-end=&quot;1004&quot; data-start=&quot;990&quot;&gt;Challenges&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr data-end=&quot;1517&quot; data-start=&quot;988&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1005&quot; data-start=&quot;988&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;td data-col-size=&quot;xl&quot; data-end=&quot;1517&quot; data-start=&quot;1005&quot;&gt;Firms face increased operational complexity: they must implement a single‑volume cap, monitor dark trading, ensure data quality for consolidated tapes and update policies and procedures (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://legal.pwc.de/en/news/articles/mifir-mifid-ii-review-making-sense-of-the-key-amendments&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://legal.pwc.de/en/news/articles/mifir-mifid-ii-review-making-sense-of-the-key-amendments&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;legal.pwc.de&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  Near‑real‑time reporting and expanded data fields demand modern IT infrastructure (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=Extended%20reporting%20requirements%20and%20more,control%20by%20ESMA&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=Extended%20reporting%20requirements%20and%20more,control%20by%20ESMA&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;asctechnologies.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  All customer conversations across channels must be recorded and analysed for misconduct, requiring AI‑powered risk detection (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=Impact%20on%20recording%20requirements&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=Impact%20on%20recording%20requirements&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;asctechnologies.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;strong data-end=&quot;1537&quot; data-start=&quot;1520&quot;&gt;Opportunities&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr data-end=&quot;1917&quot; data-start=&quot;1518&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1538&quot; data-start=&quot;1518&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;td data-col-size=&quot;xl&quot; data-end=&quot;1917&quot; data-start=&quot;1538&quot;&gt;Greater transparency and harmonised data help investors and regulators.  The EU‑wide consolidated tape offers real‑time market data (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=Kategorie%20MiFID%20II%20,for%20data%20and%20volume%20violations&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=Kategorie%20MiFID%20II%20,for%20data%20and%20volume%20violations&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;asctechnologies.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  AI and automation can transform compliance from a cost centre into a strategic differentiator: multi‑agent systems can handle large volumes of data, detect compliance risks and produce accessible reports.&lt;br /&gt;&lt;br /&gt;&lt;strong data-end=&quot;1943&quot; data-start=&quot;1920&quot;&gt;Automation approach&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr data-end=&quot;2287&quot; data-start=&quot;1918&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1944&quot; data-start=&quot;1918&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;td data-col-size=&quot;xl&quot; data-end=&quot;2287&quot; data-start=&quot;1944&quot;&gt;CrewAI is a framework for building teams of autonomous agents that work together.  A research agent gathers the latest MiFID updates using web‑search tools and summarises them; a writer agent turns this research into an engaging blog post.  Tasks are executed sequentially, ensuring that the output of research feeds directly into writing.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 data-end=&quot;2316&quot; data-start=&quot;2289&quot;&gt;Why MiFID Updates Matter&lt;/h2&gt;
&lt;p data-end=&quot;2953&quot; data-start=&quot;2318&quot;&gt;MiFID&amp;nbsp;II was the EU’s flagship reform for securities markets.  Seven years after its introduction, financial markets look very different: algorithmic trading, retail investing apps and AI‑assisted research have become mainstream.  The European Commission therefore adopted a &lt;strong data-end=&quot;2618&quot; data-start=&quot;2593&quot;&gt;MiFID&amp;nbsp;II/MiFIR review&lt;/strong&gt; in February&amp;nbsp;2024.  The review aims to boost transparency and investor protection by removing redundant provisions, creating a consolidated tape and banning PFOF (payment&amp;nbsp; for order flow) (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://legal.pwc.de/en/news/articles/mifir-mifid-ii-review-making-sense-of-the-key-amendments#:~:text=In%20more%20detail%2C%20the%20Review%3A&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://legal.pwc.de/en/news/articles/mifir-mifid-ii-review-making-sense-of-the-key-amendments#:~:text=In%20more%20detail%2C%20the%20Review%3A&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;legal.pwc.de&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  Member States have until &lt;strong data-end=&quot;2867&quot; data-start=&quot;2846&quot;&gt;28&amp;nbsp;September&amp;nbsp;2025&lt;/strong&gt; to transpose the amendments into national law (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://legal.pwc.de/en/news/articles/mifir-mifid-ii-review-making-sense-of-the-key-amendments#:~:text=1,into%20force%20in%20late%202025&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://legal.pwc.de/en/news/articles/mifir-mifid-ii-review-making-sense-of-the-key-amendments#:~:text=1,into%20force%20in%20late%202025&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;legal.pwc.de&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p data-end=&quot;3509&quot; data-start=&quot;2955&quot;&gt;In parallel, the so‑called &lt;strong data-end=&quot;2995&quot; data-start=&quot;2982&quot;&gt;MiFID&amp;nbsp;III&lt;/strong&gt; package—an informal term for amendments to MiFID&amp;nbsp;II and MiFIR—modernises the rulebook to reflect digitalisation.  It requires firms to document transactions in greater detail and report them to supervisory authorities in near real time (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=September%202025%20marks%20the%20deadline,authorities%20in%20near%20real%20time&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=September%202025%20marks%20the%20deadline,authorities%20in%20near%20real%20time&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;asctechnologies.com&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;), introduces a single 7&amp;nbsp;% dark‑trading cap (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=Kategorie%20MiFID%20II%20,for%20data%20and%20volume%20violations&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=Kategorie%20MiFID%20II%20,for%20data%20and%20volume%20violations&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;asctechnologies.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;, bans PFOF (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=In%20June%202026%2C%20the%20PFOF,for%20routing%20customer%20orders%20there&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=In%20June%202026%2C%20the%20PFOF,for%20routing%20customer%20orders%20there&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;asctechnologies.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt; and launches a consolidated tape for equities, ETFs and derivatives (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=In%20June%202026%2C%20the%20PFOF,for%20routing%20customer%20orders%20there&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=In%20June%202026%2C%20the%20PFOF,for%20routing%20customer%20orders%20there&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;asctechnologies.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p data-end=&quot;3961&quot; data-start=&quot;3511&quot;&gt;These changes present both challenges and opportunities for financial institutions.  On the one hand, compliance will demand investments in data governance, monitoring tools and process redesign (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://legal.pwc.de/en/news/articles/mifir-mifid-ii-review-making-sense-of-the-key-amendments&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://legal.pwc.de/en/news/articles/mifir-mifid-ii-review-making-sense-of-the-key-amendments&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;legal.pwc.de&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  On the other hand, harmonised data and advanced analytics can enhance risk management and investor confidence.  Adopting &lt;strong data-end=&quot;3894&quot; data-start=&quot;3868&quot;&gt;multi‑agent AI systems&lt;/strong&gt; such as CrewAI can help firms navigate this landscape efficiently.&lt;/p&gt;
&lt;h2 data-end=&quot;4014&quot; data-start=&quot;3963&quot;&gt;Building a Multi‑Agent MiFID&amp;nbsp;Compliance Workflow&lt;/h2&gt;
&lt;p data-end=&quot;4358&quot; data-start=&quot;4016&quot;&gt;The traditional approach to regulatory change management involves human analysts reading lengthy legislative texts, summarising them and drafting internal or external communications.  This process is time‑consuming and error‑prone.  &lt;strong data-end=&quot;4259&quot; data-start=&quot;4249&quot;&gt;CrewAI&lt;/strong&gt; offers an alternative: you can orchestrate multiple AI agents to collaborate on complex workflows.&lt;/p&gt;
&lt;p data-end=&quot;4445&quot; data-start=&quot;4360&quot;&gt;Below is a high‑level example of how to automate MiFID&amp;nbsp;research and content creation:&lt;/p&gt;
&lt;ol data-end=&quot;5625&quot; data-start=&quot;4447&quot;&gt;
&lt;li data-end=&quot;4987&quot; data-start=&quot;4447&quot;&gt;
&lt;p data-end=&quot;4987&quot; data-start=&quot;4450&quot;&gt;&lt;strong data-end=&quot;4468&quot; data-start=&quot;4450&quot;&gt;Research agent&lt;/strong&gt; – A specialist agent queries real‑time web search tools (such as Serper) for the latest MiFID&amp;nbsp;II/III developments.  It extracts key facts—deadlines, major amendments, compliance obligations—and generates a structured report.  The report highlights the 28&amp;nbsp;September&amp;nbsp;2025 deadline for transposition (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://legal.pwc.de/en/news/articles/mifir-mifid-ii-review-making-sense-of-the-key-amendments#:~:text=1,into%20force%20in%20late%202025&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://legal.pwc.de/en/news/articles/mifir-mifid-ii-review-making-sense-of-the-key-amendments#:~:text=1,into%20force%20in%20late%202025&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;legal.pwc.de&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;, the September&amp;nbsp;2025 start of near‑real‑time reporting (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=September%202025%20marks%20the%20deadline,authorities%20in%20near%20real%20time&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=September%202025%20marks%20the%20deadline,authorities%20in%20near%20real%20time&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;asctechnologies.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;, and the June&amp;nbsp;2026 PFOF ban and consolidated tape (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=In%20June%202026%2C%20the%20PFOF,for%20routing%20customer%20orders%20there&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=In%20June%202026%2C%20the%20PFOF,for%20routing%20customer%20orders%20there&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;asctechnologies.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;5416&quot; data-start=&quot;4988&quot;&gt;
&lt;p data-end=&quot;5416&quot; data-start=&quot;4991&quot;&gt;&lt;strong data-end=&quot;5013&quot; data-start=&quot;4991&quot;&gt;Content strategist&lt;/strong&gt; – This agent receives the research report and crafts a reader‑friendly article.  It explains why the single‑volume cap and consolidated tape matter, discusses the pros and cons of the new regime, and includes practical advice for compliance teams.  It also emphasises how AI and automation can help firms handle increased data volumes and monitoring requirements (&lt;span data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=Impact%20on%20recording%20requirements&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.asctechnologies.com/blog/post/mifid-iii-regulatory-changes-and-investor-protection-in-capital-markets/#:~:text=Impact%20on%20recording%20requirements&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;asctechnologies.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;5625&quot; data-start=&quot;5417&quot;&gt;
&lt;p data-end=&quot;5625&quot; data-start=&quot;5420&quot;&gt;&lt;strong data-end=&quot;5446&quot; data-start=&quot;5420&quot;&gt;Workflow orchestration&lt;/strong&gt; – A Crew object ties these agents and tasks together in a sequential process.  The research task must finish before the writing task begins, ensuring that the output is coherent.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-end=&quot;5652&quot; data-start=&quot;5627&quot;&gt;Sample Implementation&lt;/h3&gt;
&lt;p data-end=&quot;5881&quot; data-start=&quot;5654&quot;&gt;Below is a simplified Python script (see &lt;code data-end=&quot;5720&quot; data-start=&quot;5695&quot;&gt;&lt;a href=&quot;https://github.com/JordiCorbilla/langgraph-cookbook/tree/main/08%20-%20Mifid%20CrewAI%20Agent&quot;&gt;crewai_mifid_example.py&lt;/a&gt;&lt;/code&gt;) that demonstrates this workflow.  It uses the CrewAI framework and assumes you have set a valid Serper API key and configured a supported large language model.&lt;/p&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;7606&quot; data-start=&quot;5883&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; os
&lt;span class=&quot;hljs-keyword&quot;&gt;from&lt;/span&gt; crewai &lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; Agent, Task, Crew, Process, LLM
&lt;span class=&quot;hljs-keyword&quot;&gt;from&lt;/span&gt; crewai_tools &lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; SerperDevTool

&lt;span class=&quot;hljs-comment&quot;&gt;# Set your Serper API key&lt;/span&gt;
os.environ[&lt;span class=&quot;hljs-string&quot;&gt;&#39;SERPER_API_KEY&#39;&lt;/span&gt;] = &lt;span class=&quot;hljs-string&quot;&gt;&#39;your_serper_api_key&#39;&lt;/span&gt;

&lt;span class=&quot;hljs-comment&quot;&gt;# Configure your LLM&lt;/span&gt;
llm = LLM(
    model=&lt;span class=&quot;hljs-string&quot;&gt;&quot;gtp-4&quot;&lt;/span&gt;,
    max_tokens=&lt;span class=&quot;hljs-number&quot;&gt;2000&lt;/span&gt;,
)

&lt;span class=&quot;hljs-comment&quot;&gt;# Define the research agent&lt;/span&gt;
research_agent = Agent(
    role=&lt;span class=&quot;hljs-string&quot;&gt;&quot;MiFID Research Analyst&quot;&lt;/span&gt;,
    goal=&lt;span class=&quot;hljs-string&quot;&gt;&quot;Investigate and summarize recent MiFID II and III updates&quot;&lt;/span&gt;,
    backstory=&lt;span class=&quot;hljs-string&quot;&gt;&quot;Expert in EU financial regulation&quot;&lt;/span&gt;,
    verbose=&lt;span class=&quot;hljs-literal&quot;&gt;True&lt;/span&gt;,
    allow_delegation=&lt;span class=&quot;hljs-literal&quot;&gt;False&lt;/span&gt;,
    llm=llm,
    tools=[SerperDevTool()],
)

&lt;span class=&quot;hljs-comment&quot;&gt;# Define the writer agent&lt;/span&gt;
writer_agent = Agent(
    role=&lt;span class=&quot;hljs-string&quot;&gt;&quot;RegTech Content Strategist&quot;&lt;/span&gt;,
    goal=&lt;span class=&quot;hljs-string&quot;&gt;&quot;Write an engaging article about MiFID updates and AI compliance&quot;&lt;/span&gt;,
    backstory=&lt;span class=&quot;hljs-string&quot;&gt;&quot;Skilled communicator for tech audiences&quot;&lt;/span&gt;,
    verbose=&lt;span class=&quot;hljs-literal&quot;&gt;True&lt;/span&gt;,
    allow_delegation=&lt;span class=&quot;hljs-literal&quot;&gt;True&lt;/span&gt;,
    llm=llm,
)

&lt;span class=&quot;hljs-comment&quot;&gt;# Define tasks&lt;/span&gt;
research_task = Task(
    description=&lt;span class=&quot;hljs-string&quot;&gt;&quot;Gather and analyse MiFID II/III deadlines, reporting requirements and PFOF ban.&quot;&lt;/span&gt;,
    agent=research_agent,
    expected_output=&lt;span class=&quot;hljs-string&quot;&gt;&quot;A structured report on MiFID updates&quot;&lt;/span&gt;,
)
writer_task = Task(
    description=&lt;span class=&quot;hljs-string&quot;&gt;&quot;Draft a 4‑paragraph blog post based on the research.&quot;&lt;/span&gt;,
    agent=writer_agent,
    expected_output=&lt;span class=&quot;hljs-string&quot;&gt;&quot;A blog post summarising MiFID updates and the role of AI in compliance.&quot;&lt;/span&gt;,
)

crew = Crew(
    agents=[research_agent, writer_agent],
    tasks=[research_task, writer_task],
    process=Process.sequential,
    verbose=&lt;span class=&quot;hljs-literal&quot;&gt;True&lt;/span&gt;,
)

&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; __name__ == &lt;span class=&quot;hljs-string&quot;&gt;&#39;__main__&#39;&lt;/span&gt;:
    result = crew.kickoff(inputs={&lt;span class=&quot;hljs-string&quot;&gt;&#39;topic&#39;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&#39;MiFID II and MiFID III updates 2025&#39;&lt;/span&gt;})
    &lt;span class=&quot;hljs-built_in&quot;&gt;print&lt;/span&gt;(result)
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;h3 data-end=&quot;7667&quot; data-start=&quot;7608&quot;&gt;Pros and Cons of Using CrewAI for Regulatory Compliance&lt;/h3&gt;
&lt;ul data-end=&quot;8950&quot; data-start=&quot;7669&quot;&gt;
&lt;li data-end=&quot;8321&quot; data-start=&quot;7669&quot;&gt;
&lt;p data-end=&quot;7679&quot; data-start=&quot;7671&quot;&gt;&lt;strong data-end=&quot;7679&quot; data-start=&quot;7671&quot;&gt;Pros&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-end=&quot;8321&quot; data-start=&quot;7682&quot;&gt;
&lt;li data-end=&quot;7867&quot; data-start=&quot;7682&quot;&gt;
&lt;p data-end=&quot;7867&quot; data-start=&quot;7684&quot;&gt;&lt;em data-end=&quot;7706&quot; data-start=&quot;7684&quot;&gt;Efficiency and scale&lt;/em&gt;: AI agents can monitor regulatory updates 24/7, quickly summarising amendments like the MiFID reviews.  This reduces manual labour and speeds up response times.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;7993&quot; data-start=&quot;7870&quot;&gt;
&lt;p data-end=&quot;7993&quot; data-start=&quot;7872&quot;&gt;&lt;em data-end=&quot;7885&quot; data-start=&quot;7872&quot;&gt;Consistency&lt;/em&gt;: Automated pipelines deliver uniform analysis and writing quality, avoiding variability in human reporting.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;8159&quot; data-start=&quot;7996&quot;&gt;
&lt;p data-end=&quot;8159&quot; data-start=&quot;7998&quot;&gt;&lt;em data-end=&quot;8012&quot; data-start=&quot;7998&quot;&gt;Adaptability&lt;/em&gt;: Changing the topic (e.g., from MiFID to ESG or AI regulation) simply requires passing a different input string; no need to redesign the workflow.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;8321&quot; data-start=&quot;8162&quot;&gt;
&lt;p data-end=&quot;8321&quot; data-start=&quot;8164&quot;&gt;&lt;em data-end=&quot;8183&quot; data-start=&quot;8164&quot;&gt;Integrative power&lt;/em&gt;: Agents can be equipped with multiple tools—web search, document parsing, API connectors—allowing them to pull data from diverse sources.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;8950&quot; data-start=&quot;8323&quot;&gt;
&lt;p data-end=&quot;8333&quot; data-start=&quot;8325&quot;&gt;&lt;strong data-end=&quot;8333&quot; data-start=&quot;8325&quot;&gt;Cons&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-end=&quot;8950&quot; data-start=&quot;8336&quot;&gt;
&lt;li data-end=&quot;8488&quot; data-start=&quot;8336&quot;&gt;
&lt;p data-end=&quot;8488&quot; data-start=&quot;8338&quot;&gt;&lt;em data-end=&quot;8359&quot; data-start=&quot;8338&quot;&gt;Context limitations&lt;/em&gt;: AI models may misinterpret nuanced legal language or miss implicit regulatory interactions.  Human oversight remains essential.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;8637&quot; data-start=&quot;8491&quot;&gt;
&lt;p data-end=&quot;8637&quot; data-start=&quot;8493&quot;&gt;&lt;em data-end=&quot;8518&quot; data-start=&quot;8493&quot;&gt;Data‑source constraints&lt;/em&gt;: Tools like Serper rely on publicly available information.  Some proprietary regulatory databases may be inaccessible.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;8764&quot; data-start=&quot;8640&quot;&gt;
&lt;p data-end=&quot;8764&quot; data-start=&quot;8642&quot;&gt;&lt;em data-end=&quot;8657&quot; data-start=&quot;8642&quot;&gt;Initial setup&lt;/em&gt;: Configuring secure LLM access and API keys requires careful attention to privacy and compliance policies.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;8950&quot; data-start=&quot;8767&quot;&gt;
&lt;p data-end=&quot;8950&quot; data-start=&quot;8769&quot;&gt;&lt;em data-end=&quot;8791&quot; data-start=&quot;8769&quot;&gt;Regulatory liability&lt;/em&gt;: Over‑reliance on automated interpretations could pose risks if the system outputs incorrect advice.  Always validate AI‑generated content with legal experts.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-end=&quot;8972&quot; data-start=&quot;8952&quot;&gt;Conda Environment&lt;/h2&gt;
&lt;p data-end=&quot;9110&quot; data-start=&quot;8974&quot;&gt;To reproduce the workflow locally, create a &lt;strong data-end=&quot;9039&quot; data-start=&quot;9018&quot;&gt;conda environment&lt;/strong&gt; named&amp;nbsp;&lt;code data-end=&quot;9052&quot; data-start=&quot;9046&quot;&gt;crew&lt;/code&gt; with the following specification (see &lt;code data-end=&quot;9108&quot; data-start=&quot;9091&quot;&gt;environment.yml&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;9415&quot; data-start=&quot;9112&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;crew&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;channels:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;defaults&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;conda-forge&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;dependencies:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;python=3.10&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;pip&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;pip:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;crewai==0.193.2&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;crewai-tools==0.38.0&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;langchain==0.3.20&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;langchain-community==0.3.19&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;databricks-sdk==0.57.0&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;jupyter&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ipykernel&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;openai&lt;/span&gt;
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;p data-end=&quot;9445&quot; data-start=&quot;9417&quot;&gt;Create the environment with:&lt;/p&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;9514&quot; data-start=&quot;9447&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-bash&quot;&gt;conda &lt;span class=&quot;hljs-built_in&quot;&gt;env&lt;/span&gt; create -f environment.yml
conda activate crew
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;p data-end=&quot;9621&quot; data-start=&quot;9516&quot;&gt;Install any additional models or API keys (e.g., for Serper or your chosen LLM) as environment variables.&lt;/p&gt;
&lt;h2 data-end=&quot;9650&quot; data-start=&quot;9623&quot;&gt;Final Thoughts&lt;/h2&gt;
&lt;p data-end=&quot;10466&quot; data-start=&quot;10085&quot;&gt;The MiFID II/MiFIR review and the forthcoming MiFID&amp;nbsp;III rules represent the biggest shake‑up of EU securities regulation since 2018.  Firms must prepare for tighter reporting timelines, a 7&amp;nbsp;% dark‑trading cap and the end of PFOF.  At the same time, greater transparency and harmonised data open new opportunities for data‑driven trading strategies and improved investor protection.&lt;/p&gt;
&lt;p data-end=&quot;10874&quot; data-start=&quot;10468&quot;&gt;Leveraging frameworks like &lt;strong data-end=&quot;10505&quot; data-start=&quot;10495&quot;&gt;CrewAI&lt;/strong&gt; allows compliance teams to automate the tedious parts of regulatory change management.  By delegating research and writing tasks to specialised agents, practitioners can focus on high‑level analysis and strategic decision‑making.  As always, technology is a tool—not a substitute for legal judgement—but used wisely it can turn regulation into a competitive advantage.&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2025/10/automating-mifid-compliance-with-crewai.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOr8-FWpQA-zYy0LaIni5kpGsvWvvr5pu0pAnfxR0W6DpjSKQTBURDXutYc5PPF013_tRLxzdjvLIinrNfwbd8yC7-ZyePvGFjHvJ3JM9Z_lU_rcMOlrbgPdl4ijPc70ah2pN1kUVP_TbRWKWkNr9NpP4U7YAOvE_h2l0RDgcj3tL_hQBfpgp42JRFG0U/s72-c/59aa09fa-33f8-4483-a4f9-78c2723cdbdd.png" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>London, UK</georss:featurename><georss:point>51.5072178 -0.1275862</georss:point><georss:box>23.196983963821154 -35.2838362 79.817451636178845 35.0286638</georss:box></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-2904676072726619968</guid><pubDate>Sun, 28 Sep 2025 18:14:00 +0000</pubDate><atom:updated>2025-09-28T18:14:08.949+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI workflow</category><category domain="http://www.blogger.com/atom/ns#">dynamic portfolio</category><category domain="http://www.blogger.com/atom/ns#">fictional personas</category><category domain="http://www.blogger.com/atom/ns#">investment advisor</category><category domain="http://www.blogger.com/atom/ns#">investment strategies</category><category domain="http://www.blogger.com/atom/ns#">LLM integration</category><category domain="http://www.blogger.com/atom/ns#">orchestrator-worker pattern</category><category domain="http://www.blogger.com/atom/ns#">reflection pattern</category><category domain="http://www.blogger.com/atom/ns#">risk grades</category><title>Reflection Pattern for Investment Advisory: A Fictional Take</title><description>&lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOt6jmB2FyPTB8Xb-m6aO1etGbOAhQzU2I-XUOClZEgjEEWJbVq47-r0URqC5IjkSucUxHHGqJB4bf0s2u-tUvDDdj4LOYlST5yYZw5UMIWgNYRdKKmT2X4oZ_mnsaDp7-R9TsxgNc3XiJjJfPFRlihWNnHb9qgQywVtOM41v9FZ5tyJEESFgT1583O2Q/s1024/fe69c127-4310-47ee-8e47-436d8fd58eb1.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; display: inline !important; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1024&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOt6jmB2FyPTB8Xb-m6aO1etGbOAhQzU2I-XUOClZEgjEEWJbVq47-r0URqC5IjkSucUxHHGqJB4bf0s2u-tUvDDdj4LOYlST5yYZw5UMIWgNYRdKKmT2X4oZ_mnsaDp7-R9TsxgNc3XiJjJfPFRlihWNnHb9qgQywVtOM41v9FZ5tyJEESFgT1583O2Q/w320-h320/fe69c127-4310-47ee-8e47-436d8fd58eb1.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/p&gt;&lt;h1 style=&quot;text-align: left;&quot;&gt;Reflection Pattern for Investment Advisory: A Fictional Take&lt;/h1&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;This article explores how the &lt;strong data-end=&quot;572&quot; data-start=&quot;550&quot;&gt;reflection pattern,&amp;nbsp;&lt;/strong&gt;an &lt;b&gt;agentic AI &lt;/b&gt;design pattern in which a model iteratively generates, critiques and refines its own work, can be applied to investment advisory.  We’ll introduce fictional superstar advisors inspired by pop culture, explain how the reflection loop fits within an &lt;strong data-end=&quot;857&quot; data-start=&quot;834&quot;&gt;orchestrator–worker&lt;/strong&gt; architecture, and provide a full Python implementation.  Along the way, we highlight benefits, drawbacks and speculative future applications.&lt;/p&gt;&lt;h2 data-end=&quot;86&quot; data-start=&quot;64&quot;&gt;Summary at a Glance&lt;/h2&gt;&lt;p&gt;
&lt;/p&gt;&lt;div class=&quot;_tableContainer_1rjym_1&quot;&gt;&lt;div class=&quot;group _tableWrapper_1rjym_13 flex w-fit flex-col-reverse&quot; tabindex=&quot;-1&quot;&gt;&lt;table class=&quot;w-fit min-w-(--thread-content-width)&quot; data-end=&quot;518&quot; data-start=&quot;88&quot;&gt;&lt;thead data-end=&quot;176&quot; data-start=&quot;88&quot;&gt;&lt;tr data-end=&quot;176&quot; data-start=&quot;88&quot;&gt;&lt;th data-col-size=&quot;sm&quot; data-end=&quot;98&quot; data-start=&quot;88&quot;&gt;Persona&lt;/th&gt;&lt;th data-col-size=&quot;md&quot; data-end=&quot;106&quot; data-start=&quot;98&quot;&gt;Style&lt;/th&gt;&lt;th data-col-size=&quot;md&quot; data-end=&quot;176&quot; data-start=&quot;106&quot;&gt;Typical Allocation (Equities / Alternatives / Fixed‑Income / Cash)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody data-end=&quot;518&quot; data-start=&quot;191&quot;&gt;&lt;tr data-end=&quot;294&quot; data-start=&quot;191&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;211&quot; data-start=&quot;191&quot;&gt;&lt;strong data-end=&quot;210&quot; data-start=&quot;193&quot;&gt;Bobby Axelrod&lt;/strong&gt;&lt;/td&gt;&lt;td data-col-size=&quot;md&quot; data-end=&quot;271&quot; data-start=&quot;211&quot;&gt;Aggressive hedge‑fund maverick willing to ride volatility&lt;/td&gt;&lt;td data-col-size=&quot;md&quot; data-end=&quot;294&quot; data-start=&quot;271&quot;&gt;70% / 20% / 5% / 5%&lt;/td&gt;&lt;/tr&gt;&lt;tr data-end=&quot;402&quot; data-start=&quot;295&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;314&quot; data-start=&quot;295&quot;&gt;&lt;strong data-end=&quot;313&quot; data-start=&quot;297&quot;&gt;Gordon Gekko&lt;/strong&gt;&lt;/td&gt;&lt;td data-col-size=&quot;md&quot; data-end=&quot;379&quot; data-start=&quot;314&quot;&gt;Opportunistic raider who loves leverage, options and arbitrage&lt;/td&gt;&lt;td data-col-size=&quot;md&quot; data-end=&quot;402&quot; data-start=&quot;379&quot;&gt;75% / 15% / 5% / 5%&lt;/td&gt;&lt;/tr&gt;&lt;tr data-end=&quot;518&quot; data-start=&quot;403&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;421&quot; data-start=&quot;403&quot;&gt;&lt;strong data-end=&quot;420&quot; data-start=&quot;405&quot;&gt;Richie&amp;nbsp;Rich&lt;/strong&gt;&lt;/td&gt;&lt;td data-col-size=&quot;md&quot; data-end=&quot;482&quot; data-start=&quot;421&quot;&gt;Cautious wealth‑preserver who favours blue chips and bonds&lt;/td&gt;&lt;td data-col-size=&quot;md&quot; data-end=&quot;518&quot; data-start=&quot;482&quot;&gt;30–60% / 10–20% / 15–45% / 5–15%&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2 data-end=&quot;1036&quot; data-start=&quot;1001&quot;&gt;The Reflection Pattern Explained&lt;/h2&gt;&lt;p data-end=&quot;1855&quot; data-start=&quot;1038&quot;&gt;The reflection pattern originates in cognitive science and has recently been adopted for large language models (LLMs).  The idea is simple yet powerful: rather than trusting a single draft, the model runs a &lt;strong data-end=&quot;1273&quot; data-start=&quot;1245&quot;&gt;generate–critique–refine&lt;/strong&gt; loop.  According to Analytics&amp;nbsp;Vidhya, the pattern “is a method where the model generates, critiques and refines its outputs through an iterative self‑assessment process” (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.analyticsvidhya.com/blog/2024/10/agentic-ai-reflection-pattern/#:~:text=Overview&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.analyticsvidhya.com/blog/2024/10/agentic-ai-reflection-pattern/#:~:text=Overview&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;analyticsvidhya.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  This loop mimics human editing, catching mistakes, clarifying ambiguities and improving quality across multiple passes (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.analyticsvidhya.com/blog/2024/10/agentic-ai-reflection-pattern/#:~:text=Overview&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.analyticsvidhya.com/blog/2024/10/agentic-ai-reflection-pattern/#:~:text=Overview&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;analyticsvidhya.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  A second LLM (or the same model with a different prompt) acts as a &lt;b&gt;critic&lt;/b&gt;, evaluating the initial output against well‑defined criteria and feeding feedback back to the generator (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.philschmid.de/agentic-pattern#:~:text=Reflection%20Pattern&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.philschmid.de/agentic-pattern#:~:text=Reflection%20Pattern&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;philschmid.de&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;p data-end=&quot;1901&quot; data-start=&quot;1857&quot;&gt;Key steps in the reflection pattern include:&lt;/p&gt;&lt;ol data-end=&quot;2618&quot; data-start=&quot;1903&quot;&gt;
&lt;li data-end=&quot;2090&quot; data-start=&quot;1903&quot;&gt;
&lt;p data-end=&quot;2090&quot; data-start=&quot;1906&quot;&gt;&lt;strong data-end=&quot;1921&quot; data-start=&quot;1906&quot;&gt;Generation:&lt;/strong&gt; produce a first draft based on the user’s prompt.  This can be done in a zero‑shot manner, generating a candidate without examples (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.analyticsvidhya.com/blog/2024/10/agentic-ai-reflection-pattern/#:~:text=1&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.analyticsvidhya.com/blog/2024/10/agentic-ai-reflection-pattern/#:~:text=1&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;analyticsvidhya.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2263&quot; data-start=&quot;2091&quot;&gt;
&lt;p data-end=&quot;2263&quot; data-start=&quot;2094&quot;&gt;&lt;strong data-end=&quot;2114&quot; data-start=&quot;2094&quot;&gt;Self‑reflection:&lt;/strong&gt; the model reviews its own work, identifies errors, missing details or stylistic issues, and generates feedback (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.analyticsvidhya.com/blog/2024/10/agentic-ai-reflection-pattern/#:~:text=The%20reflection%20step%20is%20a,This%20step%20involves&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.analyticsvidhya.com/blog/2024/10/agentic-ai-reflection-pattern/#:~:text=The%20reflection%20step%20is%20a,This%20step%20involves&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;analyticsvidhya.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2618&quot; data-start=&quot;2264&quot;&gt;
&lt;p data-end=&quot;2618&quot; data-start=&quot;2267&quot;&gt;&lt;strong data-end=&quot;2294&quot; data-start=&quot;2267&quot;&gt;Iteration &amp;amp; refinement:&lt;/strong&gt; the feedback is incorporated into a new draft, and the cycle repeats until the output &lt;u&gt;meets quality thresholds&lt;/u&gt; or a maximum number of iterations is reached (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://tpiros.dev/blog/building-with-reflection-a-practical-agentic-ai-workflow/#:~:text=The%20Reflection%20Loop%20Pattern&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://tpiros.dev/blog/building-with-reflection-a-practical-agentic-ai-workflow/#:~:text=The%20Reflection%20Loop%20Pattern&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;tpiros.dev&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;).&amp;nbsp; Stopping conditions prevent infinite loops and include quality targets or iteration limits (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.analyticsvidhya.com/blog/2024/10/agentic-ai-reflection-pattern/#:~:text=,loops%20in%20the%20reflection%20process&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.analyticsvidhya.com/blog/2024/10/agentic-ai-reflection-pattern/#:~:text=,loops%20in%20the%20reflection%20process&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;analyticsvidhya.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p data-end=&quot;3040&quot; data-start=&quot;2620&quot;&gt;Practical applications of the reflection pattern include code generation, content drafting, data analysis and decision‑making.  Because the model critiques itself, it can catch inconsistencies and gradually improve the output.  However, iterative generation consumes time and compute, and repeated prompts can induce drift or hallucination.  A disciplined stopping criterion and robust evaluation criteria are essential.&lt;/p&gt;&lt;h2 data-end=&quot;3076&quot; data-start=&quot;3042&quot;&gt;The Orchestrator–Worker Pattern&lt;/h2&gt;&lt;p data-end=&quot;3606&quot; data-start=&quot;3078&quot;&gt;The reflection loop fits naturally into an &lt;strong data-end=&quot;3157&quot; data-start=&quot;3121&quot;&gt;orchestrator–worker architecture&lt;/strong&gt;, another agentic design pattern.  In this setup, a central orchestrator decomposes a complex task into subtasks and dispatches them to specialized worker agents.  The orchestrator maintains global oversight, monitors worker performance and synthesizes partial results into a final answer (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://dzone.com/articles/ai-agent-architectures-patterns-applications-guide#:~:text=1.%20Orchestrator&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://dzone.com/articles/ai-agent-architectures-patterns-applications-guide#:~:text=1.%20Orchestrator&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;dzone.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  Workers operate as domain‑specific experts, focusing solely on their assigned tasks (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://dzone.com/articles/ai-agent-architectures-patterns-applications-guide#:~:text=1.%20Orchestrator&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://dzone.com/articles/ai-agent-architectures-patterns-applications-guide#:~:text=1.%20Orchestrator&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;dzone.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;p data-end=&quot;3660&quot; data-start=&quot;3608&quot;&gt;This separation of concerns brings several benefits:&lt;/p&gt;&lt;ul data-end=&quot;4243&quot; data-start=&quot;3662&quot;&gt;
&lt;li data-end=&quot;3790&quot; data-start=&quot;3662&quot;&gt;
&lt;p data-end=&quot;3790&quot; data-start=&quot;3664&quot;&gt;&lt;strong data-end=&quot;3695&quot; data-start=&quot;3664&quot;&gt;Modularity &amp;amp; extensibility:&lt;/strong&gt; new worker agents can be added without rewriting the orchestrator, making the system scalable.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3880&quot; data-start=&quot;3791&quot;&gt;
&lt;p data-end=&quot;3880&quot; data-start=&quot;3793&quot;&gt;&lt;strong data-end=&quot;3809&quot; data-start=&quot;3793&quot;&gt;Parallelism:&lt;/strong&gt; tasks can run concurrently, reducing latency and improving throughput.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4039&quot; data-start=&quot;3881&quot;&gt;
&lt;p data-end=&quot;4039&quot; data-start=&quot;3883&quot;&gt;&lt;strong data-end=&quot;3898&quot; data-start=&quot;3883&quot;&gt;Resilience:&lt;/strong&gt; if one worker fails, the orchestrator can still produce a meaningful result from the remaining outputs (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://pytrick.medium.com/agentic-ai-design-pattern-orchestrator-worker-6d76ffc09f0c#:~:text=Think%20of%20the%20Orchestrator%20as,manager%20and%20Workers%20as%20specialists&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://pytrick.medium.com/agentic-ai-design-pattern-orchestrator-worker-6d76ffc09f0c#:~:text=Think%20of%20the%20Orchestrator%20as,manager%20and%20Workers%20as%20specialists&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;pytrick.medium.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4243&quot; data-start=&quot;4040&quot;&gt;
&lt;p data-end=&quot;4243&quot; data-start=&quot;4042&quot;&gt;&lt;strong data-end=&quot;4062&quot; data-start=&quot;4042&quot;&gt;Central control:&lt;/strong&gt; centralized monitoring ensures that quality standards are enforced across workers and that fallback strategies are applied when errors occur (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://dzone.com/articles/ai-agent-architectures-patterns-applications-guide#:~:text=1.%20Orchestrator&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://dzone.com/articles/ai-agent-architectures-patterns-applications-guide#:~:text=1.%20Orchestrator&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;dzone.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;p data-end=&quot;4880&quot; data-start=&quot;4245&quot;&gt;The orchestrator–worker pattern is particularly useful for workflows that combine planning, data retrieval, analysis and synthesis.  For example, in a technical stock analysis system, an orchestrator fetches data once, sends it to workers computing RSI, MACD and other indicators, then aggregates the signals into a report (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://pytrick.medium.com/agentic-ai-design-pattern-orchestrator-worker-6d76ffc09f0c#:~:text=Think%20of%20the%20Orchestrator%20as,manager%20and%20Workers%20as%20specialists&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://pytrick.medium.com/agentic-ai-design-pattern-orchestrator-worker-6d76ffc09f0c#:~:text=Think%20of%20the%20Orchestrator%20as,manager%20and%20Workers%20as%20specialists&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;pytrick.medium.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  Our fictional investment advisor will reuse this structure: the orchestrator iterates through different personas (workers), each generating an investment plan; an evaluator agent critiques the plan; and the orchestrator decides whether to accept or request another draft.&lt;/p&gt;&lt;h2 data-end=&quot;4916&quot; data-start=&quot;4882&quot;&gt;Fictional Investment Superstars&lt;/h2&gt;&lt;p data-end=&quot;5201&quot; data-start=&quot;4918&quot;&gt;Real investment icons like Cathie&amp;nbsp;Wood or Warren&amp;nbsp;Buffett offer recognisable strategies, but they also introduce brand‑specific bias.  To avoid conflating advice with real identities, and to show that agentic workflows can model a variety of styles, we propose three fictional personas:&lt;/p&gt;&lt;ul data-end=&quot;6007&quot; data-start=&quot;5203&quot;&gt;
&lt;li data-end=&quot;5494&quot; data-start=&quot;5203&quot;&gt;
&lt;p data-end=&quot;5494&quot; data-start=&quot;5205&quot;&gt;&lt;strong data-end=&quot;5223&quot; data-start=&quot;5205&quot;&gt;Bobby&amp;nbsp;Axelrod:&lt;/strong&gt; loosely modelled on a hedge‑fund manager character.  Bobby favours high‑growth sectors and alternative assets.  In our template he allocates ~70 % to equities and 20 % to alternatives, leaving minimal fixed income and cash.  This results in an &lt;strong data-end=&quot;5482&quot; data-start=&quot;5468&quot;&gt;aggressive&lt;/strong&gt; risk grade.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;5761&quot; data-start=&quot;5495&quot;&gt;
&lt;p data-end=&quot;5761&quot; data-start=&quot;5497&quot;&gt;&lt;strong data-end=&quot;5514&quot; data-start=&quot;5497&quot;&gt;Gordon&amp;nbsp;Gekko:&lt;/strong&gt; inspired by the 1980s corporate raider, Gordon prioritises concentrated bets, options and arbitrage.  He rarely recommends conservative portfolios, pushing equities to 75 % and leaving a token 5 % to fixed income.  Another &lt;strong data-end=&quot;5752&quot; data-start=&quot;5738&quot;&gt;aggressive&lt;/strong&gt; profile.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;6007&quot; data-start=&quot;5762&quot;&gt;
&lt;p data-end=&quot;6007&quot; data-start=&quot;5764&quot;&gt;&lt;strong data-end=&quot;5780&quot; data-start=&quot;5764&quot;&gt;Richie&amp;nbsp;Rich:&lt;/strong&gt; a wealthy heir who values security.  Richie’s allocation varies by target grade: 60–30 % equities, 10–20 % alternatives, 15–45 % fixed income and the rest cash.  He can satisfy &lt;strong data-end=&quot;5974&quot; data-start=&quot;5958&quot;&gt;conservative&lt;/strong&gt; or &lt;strong data-end=&quot;5990&quot; data-start=&quot;5978&quot;&gt;moderate&lt;/strong&gt; risk tolerances.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;p data-end=&quot;6470&quot; data-start=&quot;6009&quot;&gt;These personas serve as “workers” in our orchestrator.  They each provide a distinct draft plan based on the investor’s profile and desired risk grade.  A simple evaluator reads the plan, calculates the equity allocation and assigns a risk grade (conservative, moderate or aggressive).  If the assigned grade doesn’t match the target, the orchestrator calls another persona to propose a new plan.  After a preset number of iterations, the best plan is returned.&lt;/p&gt;&lt;h2 data-end=&quot;6505&quot; data-start=&quot;6472&quot;&gt;LLM Integration with LangChain&lt;/h2&gt;&lt;p data-end=&quot;7204&quot; data-start=&quot;6507&quot;&gt;While deterministic templates offer repeatable outputs, they lack the spontaneity and nuance of natural language generation.  To make the advisor more generative and human‑like, the latest version integrates an optional language model using &lt;strong data-end=&quot;6774&quot; data-start=&quot;6748&quot;&gt;LangChain’s ChatOpenAI&lt;/strong&gt; interface.  At the top of the code we attempt to import &lt;code data-end=&quot;6843&quot; data-start=&quot;6831&quot;&gt;ChatOpenAI&lt;/code&gt; and &lt;code data-end=&quot;6868&quot; data-start=&quot;6848&quot;&gt;ChatPromptTemplate&lt;/code&gt;.  A helper function &lt;code data-end=&quot;6900&quot; data-start=&quot;6889&quot;&gt;get_llm()&lt;/code&gt; tries to instantiate a &lt;strong data-end=&quot;6939&quot; data-start=&quot;6924&quot;&gt;gpt‑4o‑mini&lt;/strong&gt; model with a moderate temperature (0.7).  When this succeeds, each persona constructs a structured prompt containing the investor’s profile and target grade; the model then generates a bespoke Markdown plan with asset allocations, rationale and rebalancing advice.&lt;/p&gt;&lt;p data-end=&quot;7717&quot; data-start=&quot;7206&quot;&gt;The beauty of this design is its &lt;strong data-end=&quot;7260&quot; data-start=&quot;7239&quot;&gt;graceful fallback&lt;/strong&gt;: if LangChain isn’t installed or the model fails to initialize (for instance, due to missing API keys), the code reverts to deterministic templates.  The reflection loop and evaluator remain unchanged, so the orchestrator–worker architecture stays intact.  Only the plan generation mechanism becomes dynamic when an LLM is available.  This modularity demonstrates how real‑world systems can incrementally adopt generative AI without sacrificing robustness.&lt;/p&gt;&lt;h3 data-end=&quot;7742&quot; data-start=&quot;7719&quot;&gt;Code Implementation&lt;/h3&gt;&lt;p data-end=&quot;8201&quot; data-start=&quot;7744&quot;&gt;To put these ideas into practice, we provide a fully working Python module that mirrors the reflection loop described in the Skills&amp;nbsp;Network lab while swapping the real investors for our fictional superstars.  The module defines a shared &lt;strong data-end=&quot;7990&quot; data-start=&quot;7981&quot;&gt;state&lt;/strong&gt; dictionary, prompts for each persona, an evaluator inspired by Richie&amp;nbsp;Rich’s prudence, and a simple Python loop that iteratively generates and critiques investment plans until the risk grade matches the target.&lt;/p&gt;&lt;p data-end=&quot;8226&quot; data-start=&quot;8203&quot;&gt;Key components include:&lt;/p&gt;&lt;ul data-end=&quot;9004&quot; data-start=&quot;8228&quot;&gt;
&lt;li data-end=&quot;8343&quot; data-start=&quot;8228&quot;&gt;
&lt;p data-end=&quot;8343&quot; data-start=&quot;8230&quot;&gt;&lt;strong data-end=&quot;8258&quot; data-start=&quot;8230&quot;&gt;&lt;code data-end=&quot;8256&quot; data-start=&quot;8232&quot;&gt;determine_target_grade&lt;/code&gt;&lt;/strong&gt; – uses an LLM to pick a suitable risk grade for the investor based on their profile.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;8502&quot; data-start=&quot;8344&quot;&gt;
&lt;p data-end=&quot;8502&quot; data-start=&quot;8346&quot;&gt;&lt;strong data-end=&quot;8377&quot; data-start=&quot;8346&quot;&gt;&lt;code data-end=&quot;8375&quot; data-start=&quot;8348&quot;&gt;investment_plan_generator&lt;/code&gt;&lt;/strong&gt; – produces an initial Bobby&amp;nbsp;Axelrod–style plan and refines it using Gordon&amp;nbsp;Gekko’s opportunistic rules when feedback exists.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;8667&quot; data-start=&quot;8503&quot;&gt;
&lt;p data-end=&quot;8667&quot; data-start=&quot;8505&quot;&gt;&lt;strong data-end=&quot;8524&quot; data-start=&quot;8505&quot;&gt;&lt;code data-end=&quot;8522&quot; data-start=&quot;8507&quot;&gt;evaluate_plan&lt;/code&gt;&lt;/strong&gt; – calls a Richie&amp;nbsp;Rich–style evaluator to classify the risk level and provide feedback on diversification, volatility and capital preservation.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;8804&quot; data-start=&quot;8668&quot;&gt;
&lt;p data-end=&quot;8804&quot; data-start=&quot;8670&quot;&gt;&lt;strong data-end=&quot;8692&quot; data-start=&quot;8670&quot;&gt;&lt;code data-end=&quot;8690&quot; data-start=&quot;8672&quot;&gt;route_investment&lt;/code&gt;&lt;/strong&gt; – decides whether to accept the current plan or request another iteration, enforcing a maximum number of loops.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;9004&quot; data-start=&quot;8805&quot;&gt;
&lt;p data-end=&quot;9004&quot; data-start=&quot;8807&quot;&gt;&lt;strong data-end=&quot;8836&quot; data-start=&quot;8807&quot;&gt;&lt;code data-end=&quot;8834&quot; data-start=&quot;8809&quot;&gt;run_reflection_workflow&lt;/code&gt;&lt;/strong&gt; – orchestrates the entire generate–evaluate–route cycle in plain Python without relying on LangGraph, returning the final plan along with feedback and iteration count.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;p data-end=&quot;9098&quot; data-start=&quot;9006&quot;&gt;Here is a high‑level sketch of the module’s structure (see&amp;nbsp;&lt;a href=&quot;https://github.com/JordiCorbilla/langgraph-cookbook/tree/main/06%20-%20Investment%20Agent&quot;&gt;langgraph-cookbook/06 - Investment Agent at main · JordiCorbilla/langgraph-cookbook&lt;/a&gt;&amp;nbsp;for complete code):&lt;/p&gt;&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;10435&quot; data-start=&quot;9100&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;from&lt;/span&gt; typing &lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; TypedDict, &lt;span class=&quot;hljs-type&quot;&gt;Dict&lt;/span&gt;, &lt;span class=&quot;hljs-type&quot;&gt;Literal&lt;/span&gt;, Annotated
&lt;span class=&quot;hljs-keyword&quot;&gt;from&lt;/span&gt; langchain_openai &lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; ChatOpenAI
&lt;span class=&quot;hljs-keyword&quot;&gt;from&lt;/span&gt; langchain_core.prompts &lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; ChatPromptTemplate

grades = &lt;span class=&quot;hljs-type&quot;&gt;Literal&lt;/span&gt;[&lt;span class=&quot;hljs-string&quot;&gt;&quot;ultra-conservative&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;conservative&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;moderate&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;aggressive&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;high risk&quot;&lt;/span&gt;]

&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;State&lt;/span&gt;(&lt;span class=&quot;hljs-title class_ inherited__&quot;&gt;TypedDict&lt;/span&gt;):
    investment_plan: &lt;span class=&quot;hljs-built_in&quot;&gt;str&lt;/span&gt;
    investor_profile: &lt;span class=&quot;hljs-built_in&quot;&gt;str&lt;/span&gt;
    target_grade: grades
    feedback: &lt;span class=&quot;hljs-built_in&quot;&gt;str&lt;/span&gt;
    grade: grades
    n: &lt;span class=&quot;hljs-built_in&quot;&gt;int&lt;/span&gt;

&lt;span class=&quot;hljs-comment&quot;&gt;# Initialise the LLM (gpt-4o-mini) and build prompt templates for each persona&lt;/span&gt;

&lt;span class=&quot;hljs-comment&quot;&gt;# … Bobby&amp;nbsp;Axelrod prompt and pipeline&lt;/span&gt;
&lt;span class=&quot;hljs-comment&quot;&gt;# … Gordon&amp;nbsp;Gekko prompt and pipeline with adaptation rules&lt;/span&gt;
&lt;span class=&quot;hljs-comment&quot;&gt;# … Richie&amp;nbsp;Rich evaluator prompt and structured output&lt;/span&gt;

&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;determine_target_grade&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;state: State&lt;/span&gt;) -&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;Dict&lt;/span&gt;[&lt;span class=&quot;hljs-built_in&quot;&gt;str&lt;/span&gt;, grades]:
    &lt;span class=&quot;hljs-comment&quot;&gt;# Ask the LLM to choose the target risk grade based on the profile&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;pass&lt;/span&gt;

&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;investment_plan_generator&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;state: State&lt;/span&gt;) -&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;Dict&lt;/span&gt;[&lt;span class=&quot;hljs-built_in&quot;&gt;str&lt;/span&gt;, &lt;span class=&quot;hljs-built_in&quot;&gt;str&lt;/span&gt;]:
    &lt;span class=&quot;hljs-comment&quot;&gt;# Generate or refine the plan using Bobby or Gordon prompts&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;pass&lt;/span&gt;

&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;evaluate_plan&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;state: State&lt;/span&gt;) -&amp;gt; &lt;span class=&quot;hljs-type&quot;&gt;Dict&lt;/span&gt;[&lt;span class=&quot;hljs-built_in&quot;&gt;str&lt;/span&gt;, &lt;span class=&quot;hljs-built_in&quot;&gt;object&lt;/span&gt;]:
    &lt;span class=&quot;hljs-comment&quot;&gt;# Evaluate the plan with the Richie Rich evaluator&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;pass&lt;/span&gt;

&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;route_investment&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;state: State, iteration_limit: &lt;span class=&quot;hljs-built_in&quot;&gt;int&lt;/span&gt;&lt;/span&gt; = &lt;span class=&quot;hljs-number&quot;&gt;5&lt;/span&gt;) -&amp;gt; &lt;span class=&quot;hljs-built_in&quot;&gt;str&lt;/span&gt;:
    &lt;span class=&quot;hljs-comment&quot;&gt;# Decide whether to accept the plan or loop again&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;pass&lt;/span&gt;

&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;run_reflection_workflow&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;profile: &lt;span class=&quot;hljs-built_in&quot;&gt;str&lt;/span&gt;&lt;/span&gt;) -&amp;gt; State:
    &lt;span class=&quot;hljs-comment&quot;&gt;# Orchestrate the reflection loop until the target grade is met or the iteration limit is reached&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;10763&quot; data-start=&quot;10437&quot;&gt;The complete implementation can be downloaded as &lt;code data-end=&quot;10519&quot; data-start=&quot;10486&quot;&gt;custom_investment_reflection.py&lt;/code&gt; (file&amp;nbsp;ID &lt;code data-end=&quot;10565&quot; data-start=&quot;10529&quot;&gt; :agentCitation{citationIndex=&#39;0&#39;}&lt;/code&gt;).  This script uses LangChain’s ChatOpenAI to generate and evaluate plans, falls back gracefully if the LLM isn’t available, and demonstrates how to adapt the reflection pattern to custom personas.&lt;/p&gt;&lt;h3 data-end=&quot;10785&quot; data-start=&quot;10765&quot;&gt;Sample Execution&lt;/h3&gt;&lt;p data-end=&quot;11234&quot; data-start=&quot;10787&quot;&gt;As an example, suppose you run &lt;code data-end=&quot;10845&quot; data-start=&quot;10818&quot;&gt;run_reflection_workflow()&lt;/code&gt; from the &lt;code data-end=&quot;10888&quot; data-start=&quot;10855&quot;&gt;custom_investment_reflection.py&lt;/code&gt; module with a 35‑year‑old investor earning $100 k, holding $100 k in assets, aiming to retire by 55 with a generous travel budget, and tolerating high risk.  The workflow determines an &lt;strong data-end=&quot;11088&quot; data-start=&quot;11074&quot;&gt;aggressive&lt;/strong&gt; target grade and iteratively refines the plan.  One possible output (actual results vary slightly with the generative model) is summarised below:&lt;/p&gt;&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;12619&quot; data-start=&quot;11236&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre!&quot;&gt;🎯 Final Investment Plan Summary
========================================

📌 Investor Profile:
&lt;span class=&quot;hljs-symbol&quot;&gt;Age:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;35&lt;/span&gt;
&lt;span class=&quot;hljs-symbol&quot;&gt;Salary:&lt;/span&gt; $&lt;span class=&quot;hljs-number&quot;&gt;100&lt;/span&gt;,&lt;span class=&quot;hljs-number&quot;&gt;000&lt;/span&gt;
&lt;span class=&quot;hljs-symbol&quot;&gt;Assets:&lt;/span&gt; $&lt;span class=&quot;hljs-number&quot;&gt;100&lt;/span&gt;,&lt;span class=&quot;hljs-number&quot;&gt;000&lt;/span&gt;
&lt;span class=&quot;hljs-symbol&quot;&gt;Goal:&lt;/span&gt; Retire comfortably &lt;span class=&quot;hljs-keyword&quot;&gt;by&lt;/span&gt; age &lt;span class=&quot;hljs-number&quot;&gt;55&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;with&lt;/span&gt; ample travel budget
Risk tolerance: high

📈 Target Risk Grade: aggressive
📊 Final Assigned Grade: aggressive
🔁 Iterations Taken: &lt;span class=&quot;hljs-number&quot;&gt;2&lt;/span&gt;

📝 Evaluator Feedback:
----------------------------------
This portfolio &lt;span class=&quot;hljs-built_in&quot;&gt;is&lt;/span&gt; classified &lt;span class=&quot;hljs-keyword&quot;&gt;as&lt;/span&gt; aggressive due &lt;span class=&quot;hljs-keyword&quot;&gt;to&lt;/span&gt; its heavy allocation &lt;span class=&quot;hljs-keyword&quot;&gt;to&lt;/span&gt; equities (&lt;span class=&quot;hljs-number&quot;&gt;75%&lt;/span&gt;) &lt;span class=&quot;hljs-built_in&quot;&gt;and&lt;/span&gt; lower commitment &lt;span class=&quot;hljs-keyword&quot;&gt;to&lt;/span&gt; fixed income.  Ensure the investor &lt;span class=&quot;hljs-built_in&quot;&gt;is&lt;/span&gt; comfortable &lt;span class=&quot;hljs-keyword&quot;&gt;with&lt;/span&gt; higher volatility &lt;span class=&quot;hljs-built_in&quot;&gt;and&lt;/span&gt; periodic drawdowns.

📃 Final Investment Plan:
----------------------------------
Gordon Gekko advises the investor &lt;span class=&quot;hljs-keyword&quot;&gt;to&lt;/span&gt; pursue an assertive course:

### Asset Allocation
- **Equities (&lt;span class=&quot;hljs-number&quot;&gt;75%&lt;/span&gt;)**: Concentrated bets &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; blue chips &lt;span class=&quot;hljs-built_in&quot;&gt;and&lt;/span&gt; cyclical industries, supplemented &lt;span class=&quot;hljs-keyword&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;option&lt;/span&gt; strategies.
- **Alternatives (&lt;span class=&quot;hljs-number&quot;&gt;15%&lt;/span&gt;)**: Distressed debt, merger arbitrage &lt;span class=&quot;hljs-built_in&quot;&gt;and&lt;/span&gt; real‑estate investment trusts.
- **Fixed Income (&lt;span class=&quot;hljs-number&quot;&gt;5%&lt;/span&gt;)**: Opportunistic &lt;span class=&quot;hljs-type&quot;&gt;short&lt;/span&gt;‑dated corporate notes.
- **Cash (&lt;span class=&quot;hljs-number&quot;&gt;5%&lt;/span&gt;)**: Dry powder &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; takeover opportunities.

### Rationale
Prioritizing blue chip equities &lt;span class=&quot;hljs-built_in&quot;&gt;and&lt;/span&gt; sector ETFs &lt;span class=&quot;hljs-keyword&quot;&gt;with&lt;/span&gt; active options overlays, complemented &lt;span class=&quot;hljs-keyword&quot;&gt;by&lt;/span&gt; distressed debt &lt;span class=&quot;hljs-built_in&quot;&gt;and&lt;/span&gt; merger arbitrage funds.  Minimal fixed income ensures capital &lt;span class=&quot;hljs-built_in&quot;&gt;is&lt;/span&gt; working hard.

### Risk Management
Use &lt;span class=&quot;hljs-keyword&quot;&gt;stop&lt;/span&gt;‑loss orders &lt;span class=&quot;hljs-built_in&quot;&gt;and&lt;/span&gt; hedges; rebalance monthly &lt;span class=&quot;hljs-keyword&quot;&gt;to&lt;/span&gt; maintain leverage discipline.
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;12916&quot; data-start=&quot;12621&quot;&gt;The first iteration used the Bobby&amp;nbsp;Axelrod persona, which produced a 70 % equity allocation (moderate grade).  The evaluator recommended increasing risk exposure, and the orchestrator rotated to Gordon Gekko, whose 75 % equity allocation met the target.  The system stopped after two iterations.&lt;/p&gt;&lt;h2 data-end=&quot;12971&quot; data-start=&quot;12918&quot;&gt;Pros and Cons of the Reflection Pattern in Finance&lt;/h2&gt;&lt;h3 data-end=&quot;12987&quot; data-start=&quot;12973&quot;&gt;Advantages&lt;/h3&gt;&lt;ul data-end=&quot;14307&quot; data-start=&quot;12989&quot;&gt;
&lt;li data-end=&quot;13259&quot; data-start=&quot;12989&quot;&gt;
&lt;p data-end=&quot;13259&quot; data-start=&quot;12991&quot;&gt;&lt;strong data-end=&quot;13034&quot; data-start=&quot;12991&quot;&gt;Improved quality through self‑critique:&lt;/strong&gt; By iteratively evaluating its own recommendations, the advisor reduces the likelihood of omissions or contradictory allocations.  It mirrors the human practice of drafting and revising (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.philschmid.de/agentic-pattern#:~:text=Reflection%20Pattern&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.philschmid.de/agentic-pattern#:~:text=Reflection%20Pattern&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;philschmid.de&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;13463&quot; data-start=&quot;13260&quot;&gt;
&lt;p data-end=&quot;13463&quot; data-start=&quot;13262&quot;&gt;&lt;strong data-end=&quot;13282&quot; data-start=&quot;13262&quot;&gt;Personalisation:&lt;/strong&gt; Different personas allow the system to explore varied strategies.  Investors can compare aggressive, moderate and conservative approaches without requiring multiple human advisors.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;13728&quot; data-start=&quot;13464&quot;&gt;
&lt;p data-end=&quot;13728&quot; data-start=&quot;13466&quot;&gt;&lt;strong data-end=&quot;13492&quot; data-start=&quot;13466&quot;&gt;Generative creativity:&lt;/strong&gt; When an LLM is available, each persona can craft bespoke investment narratives instead of fixed templates.  This results in plans that read more naturally and incorporate subtle nuances while still adhering to the requested risk grade.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;13931&quot; data-start=&quot;13729&quot;&gt;
&lt;p data-end=&quot;13931&quot; data-start=&quot;13731&quot;&gt;&lt;strong data-end=&quot;13754&quot; data-start=&quot;13731&quot;&gt;Reduced human bias:&lt;/strong&gt; Using fictional characters avoids overreliance on real gurus.  The system still captures different risk appetites but does not misappropriate any celebrity’s actual statements.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;14128&quot; data-start=&quot;13932&quot;&gt;
&lt;p data-end=&quot;14128&quot; data-start=&quot;13934&quot;&gt;&lt;strong data-end=&quot;13952&quot; data-start=&quot;13934&quot;&gt;Extensibility:&lt;/strong&gt; New personas or evaluators can be added without changing the core loop, demonstrating the modularity of the orchestrator–worker pattern (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://dzone.com/articles/ai-agent-architectures-patterns-applications-guide#:~:text=1.%20Orchestrator&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://dzone.com/articles/ai-agent-architectures-patterns-applications-guide#:~:text=1.%20Orchestrator&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;dzone.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;14307&quot; data-start=&quot;14129&quot;&gt;
&lt;p data-end=&quot;14307&quot; data-start=&quot;14131&quot;&gt;&lt;strong data-end=&quot;14160&quot; data-start=&quot;14131&quot;&gt;Autonomy and scalability:&lt;/strong&gt; The loop can be run asynchronously, and evaluation metrics can be refined over time to align with regulatory guidelines or individual preferences.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;h3 data-end=&quot;14324&quot; data-start=&quot;14309&quot;&gt;Limitations&lt;/h3&gt;&lt;ul data-end=&quot;15606&quot; data-start=&quot;14326&quot;&gt;
&lt;li data-end=&quot;14655&quot; data-start=&quot;14326&quot;&gt;
&lt;p data-end=&quot;14655&quot; data-start=&quot;14328&quot;&gt;&lt;strong data-end=&quot;14352&quot; data-start=&quot;14328&quot;&gt;Heuristic evaluator:&lt;/strong&gt; Our evaluation function parses simple percentages and assigns grades.  Real‑world risk analysis is far more complex, involving volatility, correlation and drawdown metrics.  More sophisticated evaluators (e.g., risk‑parity or Monte Carlo simulators) could improve accuracy but also increase complexity.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;14859&quot; data-start=&quot;14656&quot;&gt;
&lt;p data-end=&quot;14859&quot; data-start=&quot;14658&quot;&gt;&lt;strong data-end=&quot;14681&quot; data-start=&quot;14658&quot;&gt;Computational cost:&lt;/strong&gt; Iterative generation and evaluation consume more compute cycles and may require multiple LLM calls.  For high‑frequency use cases, latency and API cost could become prohibitive.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;15098&quot; data-start=&quot;14860&quot;&gt;
&lt;p data-end=&quot;15098&quot; data-start=&quot;14862&quot;&gt;&lt;strong data-end=&quot;14883&quot; data-start=&quot;14862&quot;&gt;Unpredictability:&lt;/strong&gt; Generative models may occasionally omit required allocation details or introduce inconsistent formatting.  Although the evaluator catches gross mismatches, manual review is advisable when using LLM‑generated plans.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;15357&quot; data-start=&quot;15099&quot;&gt;
&lt;p data-end=&quot;15357&quot; data-start=&quot;15101&quot;&gt;&lt;strong data-end=&quot;15128&quot; data-start=&quot;15101&quot;&gt;Potential oscillations:&lt;/strong&gt; Without careful stopping rules, the system might ping‑pong between personas or produce contradictory adjustments.  Clear termination criteria and penalizing repeated mistakes are essential (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://www.analyticsvidhya.com/blog/2024/10/agentic-ai-reflection-pattern/#:~:text=,loops%20in%20the%20reflection%20process&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://www.analyticsvidhya.com/blog/2024/10/agentic-ai-reflection-pattern/#:~:text=,loops%20in%20the%20reflection%20process&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;analyticsvidhya.com&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;15606&quot; data-start=&quot;15358&quot;&gt;
&lt;p data-end=&quot;15606&quot; data-start=&quot;15360&quot;&gt;&lt;strong data-end=&quot;15389&quot; data-start=&quot;15360&quot;&gt;Lack of external context:&lt;/strong&gt; Our fictional personas rely on templates rather than real market data.  Integrating live data feeds and dynamic modelling would make the advice more actionable but also raises regulatory and liability considerations.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2 data-end=&quot;17406&quot; data-start=&quot;17393&quot;&gt;Conclusion&lt;/h2&gt;&lt;p&gt;



































&lt;/p&gt;&lt;p data-end=&quot;18087&quot; data-start=&quot;17408&quot;&gt;The reflection pattern offers a simple yet powerful mechanism for improving AI outputs by letting the model serve as its own critic.  When combined with an orchestrator–worker architecture, it provides a modular and extensible framework for tasks like investment advice, where balancing risk and return requires careful iteration.  By experimenting with fictional personas, we avoid real‑world bias while illustrating how different risk appetites can be encoded in code.  Although our current implementation uses heuristic evaluators and templated advice, it lays the foundation for more sophisticated systems that combine generative AI, financial models and ethical constraints.&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2025/09/reflection-pattern-for-investment.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOt6jmB2FyPTB8Xb-m6aO1etGbOAhQzU2I-XUOClZEgjEEWJbVq47-r0URqC5IjkSucUxHHGqJB4bf0s2u-tUvDDdj4LOYlST5yYZw5UMIWgNYRdKKmT2X4oZ_mnsaDp7-R9TsxgNc3XiJjJfPFRlihWNnHb9qgQywVtOM41v9FZ5tyJEESFgT1583O2Q/s72-w320-h320-c/fe69c127-4310-47ee-8e47-436d8fd58eb1.png" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>London, UK</georss:featurename><georss:point>51.5072178 -0.1275862</georss:point><georss:box>23.196983963821154 -35.2838362 79.817451636178845 35.0286638</georss:box></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-6441961157422215659</guid><pubDate>Sat, 27 Sep 2025 11:44:00 +0000</pubDate><atom:updated>2025-09-27T11:44:11.843+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">continuous batching</category><category domain="http://www.blogger.com/atom/ns#">dynamic quantization</category><category domain="http://www.blogger.com/atom/ns#">GPT‑2</category><category domain="http://www.blogger.com/atom/ns#">latency reduction</category><category domain="http://www.blogger.com/atom/ns#">LLM inference</category><category domain="http://www.blogger.com/atom/ns#">memory reduction</category><category domain="http://www.blogger.com/atom/ns#">performance optimization</category><category domain="http://www.blogger.com/atom/ns#">quantization</category><category domain="http://www.blogger.com/atom/ns#">throughput improvement</category><category domain="http://www.blogger.com/atom/ns#">weight‑only quantization</category><title>Benchmarking Dynamic Quantization for Larger Language Models</title><description>&lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgU2q3_2nAM97mJ16i75w4dOy2Pizqsb504QDWm83pzRUpjNRsHF-JsOsifyEW0zb9fHnYFYGqqndT_PDB8jlYiJtMNQVceRLM89Bi3gVAbCepYpYMKGq6bVevrlc8MHf_niLJiPHEtU7LiVQVi0G_z739NtTeQ98Lg47gAe-c0c3qWnsYNBeUmA6LPpEc&quot; style=&quot;clear: left; display: inline !important; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1024&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgU2q3_2nAM97mJ16i75w4dOy2Pizqsb504QDWm83pzRUpjNRsHF-JsOsifyEW0zb9fHnYFYGqqndT_PDB8jlYiJtMNQVceRLM89Bi3gVAbCepYpYMKGq6bVevrlc8MHf_niLJiPHEtU7LiVQVi0G_z739NtTeQ98Lg47gAe-c0c3qWnsYNBeUmA6LPpEc=w320-h320&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Benchmarking Dynamic Quantization for Larger Language Models&lt;/h2&gt;&lt;p&gt;&lt;/p&gt;&lt;p data-end=&quot;817&quot; data-start=&quot;64&quot;&gt;Modern language models can answer complex queries and generate high‑quality text, but they are heavy: hundreds of megabytes of weights have to be loaded into memory and millions of multiply and accumulate operations run per query.  To reduce latency and cost, practitioners use optimisation techniques such as &lt;strong data-end=&quot;392&quot; data-start=&quot;370&quot;&gt;model distillation&lt;/strong&gt;, &lt;strong data-end=&quot;410&quot; data-start=&quot;394&quot;&gt;quantization&lt;/strong&gt;, &lt;strong data-end=&quot;423&quot; data-start=&quot;412&quot;&gt;pruning&lt;/strong&gt;, &lt;strong data-end=&quot;456&quot; data-start=&quot;425&quot;&gt;dynamic/continuous batching&lt;/strong&gt;, and &lt;strong data-end=&quot;480&quot; data-start=&quot;462&quot;&gt;KV‑cache reuse&lt;/strong&gt;.  When these methods are combined, costs can be slashed by up to &lt;strong data-end=&quot;554&quot; data-start=&quot;546&quot;&gt;80&amp;nbsp;%&lt;/strong&gt; while maintaining acceptable quality&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://latitude-blog.ghost.io/blog/llm-inference-optimization-speed-scale-and-savings/#:~:text=,up%20processing%20for%20long%20sequences&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://latitude-blog.ghost.io/blog/llm-inference-optimization-speed-scale-and-savings/#:~:text=,up%20processing%20for%20long%20sequences&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;latitude-blog.ghost.io&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;.  This post focuses on &lt;strong data-end=&quot;681&quot; data-start=&quot;652&quot;&gt;dynamic INT8 quantization&lt;/strong&gt; for a medium‑sized GPT‑2 model, shows the performance difference between full‑precision and quantised models, and discusses trade‑offs.&lt;/p&gt;&lt;h2 data-end=&quot;839&quot; data-start=&quot;819&quot;&gt;Why Quantization?&lt;/h2&gt;&lt;p data-end=&quot;1474&quot; data-start=&quot;841&quot;&gt;Quantization reduces the precision of a model’s weights from 32‑bit floating‑point values to 8‑bit (or lower) integers.  PyTorch’s quantization documentation notes that &lt;strong data-end=&quot;1154&quot; data-start=&quot;1010&quot;&gt;INT8 quantization can shrink the model size and memory bandwidth by roughly&amp;nbsp;4× and that INT8 operations are typically 2–4&amp;nbsp;× faster than FP32 (&lt;/strong&gt;&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://docs.pytorch.org/docs/stable/quantization.html#:~:text=Quantization%20refers%20to%20techniques%20for,only%20the%20forward%20pass%20is&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://docs.pytorch.org/docs/stable/quantization.html#:~:text=Quantization%20refers%20to%20techniques%20for,only%20the%20forward%20pass%20is&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;docs.pytorch.org&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  For example, a 500‑million‑parameter model occupying &lt;strong data-end=&quot;1263&quot; data-start=&quot;1247&quot;&gt;2&amp;nbsp;GB in FP32&lt;/strong&gt; can be reduced to &lt;strong data-end=&quot;1292&quot; data-start=&quot;1282&quot;&gt;0.5&amp;nbsp;GB&lt;/strong&gt; when quantized to INT8 (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://latitude-blog.ghost.io/blog/llm-inference-optimization-speed-scale-and-savings/#:~:text=limited%20edge%20device%2C%20quantization%20makes,models%20to%20run%20efficiently%20on&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://latitude-blog.ghost.io/blog/llm-inference-optimization-speed-scale-and-savings/#:~:text=limited%20edge%20device%2C%20quantization%20makes,models%20to%20run%20efficiently%20on&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;latitude-blog.ghost.io&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  This smaller footprint allows large language models to run on memory‑constrained devices and reduces inference latency.&lt;/p&gt;&lt;p data-end=&quot;1980&quot; data-start=&quot;1476&quot;&gt;Weight‑only quantization (e.g. 4‑bit AWQ or GPTQ) can improve speed even more.  In real deployments, quantized LLMs show dramatic throughput gains: DeepSeek‑7B’s throughput on an NVIDIA&amp;nbsp;RTX&amp;nbsp;4090 increases from &lt;strong data-end=&quot;1717&quot; data-start=&quot;1686&quot;&gt;52 tokens/s to 130 tokens/s&lt;/strong&gt; using AWQ, while Mistral‑7B on an AWS G5.xlarge instance jumps from &lt;strong data-end=&quot;1816&quot; data-start=&quot;1786&quot;&gt;28&amp;nbsp;tokens/s to 88&amp;nbsp;tokens/s (&lt;/strong&gt;&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://latitude-blog.ghost.io/blog/llm-inference-optimization-speed-scale-and-savings/#:~:text=Quantized%20models%20are%20game%20changers,to%2088%20tokens%20per%20second&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://latitude-blog.ghost.io/blog/llm-inference-optimization-speed-scale-and-savings/#:~:text=Quantized%20models%20are%20game%20changers,to%2088%20tokens%20per%20second&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;latitude-blog.ghost.io&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  A 4‑bit GPTQ version of Llama‑3.2&amp;nbsp;1B maintained its F1 score and gained &lt;strong data-end=&quot;1936&quot; data-start=&quot;1928&quot;&gt;30&amp;nbsp;%&lt;/strong&gt; speed (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://latitude-blog.ghost.io/blog/llm-inference-optimization-speed-scale-and-savings/#:~:text=Databricks%20also%20reported%20impressive%20results,Similarly&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://latitude-blog.ghost.io/blog/llm-inference-optimization-speed-scale-and-savings/#:~:text=Databricks%20also%20reported%20impressive%20results,Similarly&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;latitude-blog.ghost.io&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;p data-end=&quot;2416&quot; data-start=&quot;1982&quot;&gt;Despite these benefits, quantization can degrade output quality if not applied carefully.  Lower‑precision representations introduce rounding errors and remove representational capacity.  Advanced techniques like AWQ/GPTQ mitigate this by weighting channel scaling per layer and calibrating on sample data.  Our example uses PyTorch’s simple &lt;strong data-end=&quot;2348&quot; data-start=&quot;2324&quot;&gt;dynamic quantization&lt;/strong&gt;, which is less powerful than weight‑only methods but easy to apply.&lt;/p&gt;&lt;h2 data-end=&quot;2463&quot; data-start=&quot;2418&quot;&gt;The Example: Measuring FP32 vs. INT8 GPT‑2&lt;/h2&gt;&lt;p data-end=&quot;2671&quot; data-start=&quot;2465&quot;&gt;We built a script (&lt;code data-end=&quot;2512&quot; data-start=&quot;2484&quot;&gt;inference_example_large.py&lt;/code&gt;) that loads GPT‑2 (124M parameters), processes a handful of prompts, quantizes the model dynamically, and then processes the same prompts.  The key steps are:&lt;/p&gt;&lt;ol data-end=&quot;3416&quot; data-start=&quot;2673&quot;&gt;
&lt;li data-end=&quot;2958&quot; data-start=&quot;2673&quot;&gt;
&lt;p data-end=&quot;2958&quot; data-start=&quot;2676&quot;&gt;&lt;strong data-end=&quot;2705&quot; data-start=&quot;2676&quot;&gt;Load model and tokenizer.&lt;/strong&gt;  We use Hugging&amp;nbsp;Face’s &lt;code data-end=&quot;2751&quot; data-start=&quot;2729&quot;&gt;AutoModelForCausalLM&lt;/code&gt; with the GPT‑2 checkpoint and its tokenizer.  Because GPT‑2 lacks a padding token, we assign the end‑of‑sequence token (&lt;code data-end=&quot;2883&quot; data-start=&quot;2872&quot;&gt;eos_token&lt;/code&gt;) as the pad token and set &lt;code data-end=&quot;2931&quot; data-start=&quot;2910&quot;&gt;padding_side=&#39;left&#39;&lt;/code&gt; to avoid runtime warnings.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3089&quot; data-start=&quot;2959&quot;&gt;
&lt;p data-end=&quot;3089&quot; data-start=&quot;2962&quot;&gt;&lt;strong data-end=&quot;2985&quot; data-start=&quot;2962&quot;&gt;Run FP32 inference.&lt;/strong&gt;  The script encodes a list of prompts, generates up to 50 new tokens for each, and measures total time.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3347&quot; data-start=&quot;3090&quot;&gt;
&lt;p data-end=&quot;3347&quot; data-start=&quot;3093&quot;&gt;&lt;strong data-end=&quot;3124&quot; data-start=&quot;3093&quot;&gt;Apply dynamic quantization.&lt;/strong&gt;  &lt;code data-end=&quot;3163&quot; data-start=&quot;3126&quot;&gt;torch.quantization.quantize_dynamic&lt;/code&gt; quantizes only linear layers in the model (attention projections and feed‑forward layers).  Embedding and layer‑norm parameters remain in FP32, so the model size reduction is limited.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3416&quot; data-start=&quot;3348&quot;&gt;
&lt;p data-end=&quot;3416&quot; data-start=&quot;3351&quot;&gt;&lt;strong data-end=&quot;3373&quot; data-start=&quot;3351&quot;&gt;Run INT8 inference&lt;/strong&gt; with the quantized model and measure time.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;p data-end=&quot;3471&quot; data-start=&quot;3418&quot;&gt;Here is a condensed version of the code’s core logic (full code here:&amp;nbsp;&lt;a href=&quot;https://github.com/JordiCorbilla/langgraph-cookbook&quot;&gt;JordiCorbilla/langgraph-cookbook: langgraph-cookbook&lt;/a&gt;):&lt;/p&gt;&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;4989&quot; data-start=&quot;3473&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;from&lt;/span&gt; transformers &lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; AutoModelForCausalLM, AutoTokenizer
&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; torch, time, psutil

model_name = &lt;span class=&quot;hljs-string&quot;&gt;&quot;gpt2&quot;&lt;/span&gt;
dev = torch.device(&lt;span class=&quot;hljs-string&quot;&gt;&quot;cpu&quot;&lt;/span&gt;)

&lt;span class=&quot;hljs-comment&quot;&gt;# Load and patch tokenizer (GPT-2 has no pad token)&lt;/span&gt;
tokenizer = AutoTokenizer.from_pretrained(model_name)
&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; tokenizer.pad_token_id &lt;span class=&quot;hljs-keyword&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;None&lt;/span&gt;:
    tokenizer.pad_token = tokenizer.eos_token
    tokenizer.padding_side = &lt;span class=&quot;hljs-string&quot;&gt;&quot;left&quot;&lt;/span&gt;

&lt;span class=&quot;hljs-comment&quot;&gt;# Load full-precision model&lt;/span&gt;
model = AutoModelForCausalLM.from_pretrained(model_name).to(dev)
model.&lt;span class=&quot;hljs-built_in&quot;&gt;eval&lt;/span&gt;()

prompts = [&lt;span class=&quot;hljs-string&quot;&gt;&quot;Write a poem about quantization.&quot;&lt;/span&gt;,
           &lt;span class=&quot;hljs-string&quot;&gt;&quot;Why is dynamic batching useful?&quot;&lt;/span&gt;,  &lt;span class=&quot;hljs-comment&quot;&gt;# 4 prompts total...&lt;/span&gt;
          ]
max_new_tokens = &lt;span class=&quot;hljs-number&quot;&gt;50&lt;/span&gt;

&lt;span class=&quot;hljs-comment&quot;&gt;# FP32 inference&lt;/span&gt;
start = time.time()
&lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; p &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; prompts:
    inp = tokenizer(p, return_tensors=&lt;span class=&quot;hljs-string&quot;&gt;&quot;pt&quot;&lt;/span&gt;).to(dev)
    out = model.generate(
        input_ids=inp[&lt;span class=&quot;hljs-string&quot;&gt;&quot;input_ids&quot;&lt;/span&gt;],
        attention_mask=inp[&lt;span class=&quot;hljs-string&quot;&gt;&quot;attention_mask&quot;&lt;/span&gt;],
        max_new_tokens=max_new_tokens,
        do_sample=&lt;span class=&quot;hljs-literal&quot;&gt;False&lt;/span&gt;,
        pad_token_id=tokenizer.eos_token_id,
    )
end = time.time()
fp32_time = end - start

&lt;span class=&quot;hljs-comment&quot;&gt;# Dynamically quantize linear layers&lt;/span&gt;
model_int8 = torch.quantization.quantize_dynamic(
    model, {torch.nn.Linear}, dtype=torch.qint8
)

&lt;span class=&quot;hljs-comment&quot;&gt;# INT8 inference&lt;/span&gt;
start = time.time()
&lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; p &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; prompts:
    inp = tokenizer(p, return_tensors=&lt;span class=&quot;hljs-string&quot;&gt;&quot;pt&quot;&lt;/span&gt;).to(dev)
    out = model_int8.generate(
        input_ids=inp[&lt;span class=&quot;hljs-string&quot;&gt;&quot;input_ids&quot;&lt;/span&gt;],
        attention_mask=inp[&lt;span class=&quot;hljs-string&quot;&gt;&quot;attention_mask&quot;&lt;/span&gt;],
        max_new_tokens=max_new_tokens,
        do_sample=&lt;span class=&quot;hljs-literal&quot;&gt;False&lt;/span&gt;,
        pad_token_id=tokenizer.eos_token_id,
    )
end = time.time()
int8_time = end - start
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h3 data-end=&quot;5002&quot; data-start=&quot;4991&quot;&gt;Results&lt;/h3&gt;&lt;p data-end=&quot;5069&quot; data-start=&quot;5004&quot;&gt;Our run on a Windows laptop (CPU) produced the following numbers:&lt;/p&gt;&lt;div class=&quot;_tableContainer_1rjym_1&quot;&gt;&lt;div class=&quot;group _tableWrapper_1rjym_13 flex w-fit flex-col-reverse&quot; tabindex=&quot;-1&quot;&gt;&lt;table class=&quot;w-fit min-w-(--thread-content-width)&quot; data-end=&quot;5313&quot; data-start=&quot;5071&quot;&gt;&lt;thead data-end=&quot;5143&quot; data-start=&quot;5071&quot;&gt;&lt;tr data-end=&quot;5143&quot; data-start=&quot;5071&quot;&gt;&lt;th data-col-size=&quot;sm&quot; data-end=&quot;5079&quot; data-start=&quot;5071&quot;&gt;Model&lt;/th&gt;&lt;th data-col-size=&quot;sm&quot; data-end=&quot;5091&quot; data-start=&quot;5079&quot;&gt;Precision&lt;/th&gt;&lt;th data-col-size=&quot;sm&quot; data-end=&quot;5105&quot; data-start=&quot;5091&quot;&gt;Weight size&lt;/th&gt;&lt;th data-col-size=&quot;sm&quot; data-end=&quot;5132&quot; data-start=&quot;5105&quot;&gt;Total time for 4 prompts&lt;/th&gt;&lt;th data-col-size=&quot;sm&quot; data-end=&quot;5143&quot; data-start=&quot;5132&quot;&gt;Speedup&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody data-end=&quot;5313&quot; data-start=&quot;5214&quot;&gt;&lt;tr data-end=&quot;5255&quot; data-start=&quot;5214&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5222&quot; data-start=&quot;5214&quot;&gt;GPT‑2&lt;/td&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5229&quot; data-start=&quot;5222&quot;&gt;FP32&lt;/td&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5239&quot; data-start=&quot;5229&quot;&gt;~474&amp;nbsp;MB&lt;/td&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5249&quot; data-start=&quot;5239&quot;&gt;~6.57&amp;nbsp;s&lt;/td&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5255&quot; data-start=&quot;5249&quot;&gt;1×&lt;/td&gt;&lt;/tr&gt;&lt;tr data-end=&quot;5313&quot; data-start=&quot;5256&quot;&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5264&quot; data-start=&quot;5256&quot;&gt;GPT‑2&lt;/td&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5279&quot; data-start=&quot;5264&quot;&gt;INT8 dynamic&lt;/td&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5290&quot; data-start=&quot;5279&quot;&gt;~474&amp;nbsp;MB*&lt;/td&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5300&quot; data-start=&quot;5290&quot;&gt;~5.78&amp;nbsp;s&lt;/td&gt;&lt;td data-col-size=&quot;sm&quot; data-end=&quot;5313&quot; data-start=&quot;5300&quot;&gt;&lt;strong data-end=&quot;5311&quot; data-start=&quot;5302&quot;&gt;1.14×&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p data-end=&quot;5634&quot; data-start=&quot;5315&quot;&gt;*Dynamic quantization only converts &lt;code data-end=&quot;5363&quot; data-start=&quot;5352&quot;&gt;nn.Linear&lt;/code&gt; layers.  Embedding and layer‑norm weights remain in FP32, so the overall model footprint does not shrink.  Weight‑only quantization or AWQ/GPTQ can compress all weights and yield 4×–8× reductions (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://docs.pytorch.org/docs/stable/quantization.html#:~:text=Quantization%20refers%20to%20techniques%20for,only%20the%20forward%20pass%20is&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://docs.pytorch.org/docs/stable/quantization.html#:~:text=Quantization%20refers%20to%20techniques%20for,only%20the%20forward%20pass%20is&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;docs.pytorch.org&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://latitude-blog.ghost.io/blog/llm-inference-optimization-speed-scale-and-savings/#:~:text=limited%20edge%20device%2C%20quantization%20makes,models%20to%20run%20efficiently%20on&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://latitude-blog.ghost.io/blog/llm-inference-optimization-speed-scale-and-savings/#:~:text=limited%20edge%20device%2C%20quantization%20makes,models%20to%20run%20efficiently%20on&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;latitude-blog.ghost.io&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;&lt;h3 data-end=&quot;5654&quot; data-start=&quot;5636&quot;&gt;Interpretation&lt;/h3&gt;&lt;ul data-end=&quot;6904&quot; data-start=&quot;5656&quot;&gt;
&lt;li data-end=&quot;6055&quot; data-start=&quot;5656&quot;&gt;
&lt;p data-end=&quot;6055&quot; data-start=&quot;5658&quot;&gt;&lt;strong data-end=&quot;5691&quot; data-start=&quot;5658&quot;&gt;Modest gains on small models.&lt;/strong&gt;  On a 124M‑parameter GPT‑2, dynamic quantization offered only a &lt;strong data-end=&quot;5764&quot; data-start=&quot;5756&quot;&gt;14&amp;nbsp;%&lt;/strong&gt; speedup and no memory savings.  This is because the model’s largest tensors are the embeddings and LM head, which were not quantized.  Dynamic quantization shines on CPU‑bound workloads with many linear operations but yields diminishing returns on small models or GPU‑accelerated inference.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;6448&quot; data-start=&quot;6056&quot;&gt;
&lt;p data-end=&quot;6448&quot; data-start=&quot;6058&quot;&gt;&lt;strong data-end=&quot;6089&quot; data-start=&quot;6058&quot;&gt;Larger models benefit more.&lt;/strong&gt;  Weight‑only quantization of big models (e.g., Llama‑3&amp;nbsp;70B) can reduce memory from 2&amp;nbsp;GB to 0.5&amp;nbsp;GB and double token throughput (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://latitude-blog.ghost.io/blog/llm-inference-optimization-speed-scale-and-savings/#:~:text=limited%20edge%20device%2C%20quantization%20makes,models%20to%20run%20efficiently%20on&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://latitude-blog.ghost.io/blog/llm-inference-optimization-speed-scale-and-savings/#:~:text=limited%20edge%20device%2C%20quantization%20makes,models%20to%20run%20efficiently%20on&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;latitude-blog.ghost.io&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://latitude-blog.ghost.io/blog/llm-inference-optimization-speed-scale-and-savings/#:~:text=Quantized%20models%20are%20game%20changers,to%2088%20tokens%20per%20second&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://latitude-blog.ghost.io/blog/llm-inference-optimization-speed-scale-and-savings/#:~:text=Quantized%20models%20are%20game%20changers,to%2088%20tokens%20per%20second&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;latitude-blog.ghost.io&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.  AWQ and GPTQ compress all weights, producing 2×–4× speedups while preserving or even improving some evaluation metrics (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://latitude-blog.ghost.io/blog/llm-inference-optimization-speed-scale-and-savings/#:~:text=Databricks%20also%20reported%20impressive%20results,5%C3%97%20on%20A6000%20GPUs&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://latitude-blog.ghost.io/blog/llm-inference-optimization-speed-scale-and-savings/#:~:text=Databricks%20also%20reported%20impressive%20results,5%C3%97%20on%20A6000%20GPUs&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;latitude-blog.ghost.io&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;6904&quot; data-start=&quot;6449&quot;&gt;
&lt;p data-end=&quot;6904&quot; data-start=&quot;6451&quot;&gt;&lt;strong data-end=&quot;6475&quot; data-start=&quot;6451&quot;&gt;Accuracy trade‑offs.&lt;/strong&gt;  Lower‑bit quantization introduces rounding error.  While our 8‑bit dynamic model produced sensible text, 4‑bit quantization must be applied with care (e.g., per‑channel scaling, calibration) to avoid harming output quality.  The Latitude article emphasises that quantization maintains accuracy “good enough for most applications” (&lt;span class=&quot;&quot; data-state=&quot;closed&quot;&gt;&lt;span class=&quot;ms-1 inline-flex max-w-full items-center relative top-[-0.094rem] animate-[show_150ms_ease-in]&quot; data-testid=&quot;webpage-citation-pill&quot;&gt;&lt;a alt=&quot;https://latitude-blog.ghost.io/blog/llm-inference-optimization-speed-scale-and-savings/#:~:text=Model%20quantization%20is%20all%20about,good%20enough%20for%20most%20applications&quot; class=&quot;flex h-4.5 overflow-hidden rounded-xl px-2 text-[9px] font-medium transition-colors duration-150 ease-in-out text-token-text-secondary! bg-[#F4F4F4]! dark:bg-[#303030]!&quot; href=&quot;https://latitude-blog.ghost.io/blog/llm-inference-optimization-speed-scale-and-savings/#:~:text=Model%20quantization%20is%20all%20about,good%20enough%20for%20most%20applications&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;span class=&quot;relative start-0 bottom-0 flex h-full w-full items-center&quot;&gt;latitude-blog.ghost.io&lt;/span&gt;&lt;/a&gt;)&lt;/span&gt;&lt;/span&gt;, but domain‑specific tasks may require careful evaluation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;h2 data-end=&quot;6940&quot; data-start=&quot;6906&quot;&gt;Continuous and Dynamic Batching&lt;/h2&gt;&lt;p data-end=&quot;7385&quot; data-start=&quot;6942&quot;&gt;Quantization isn’t the only lever for faster LLMs.  &lt;strong data-end=&quot;7028&quot; data-start=&quot;6994&quot;&gt;Dynamic or continuous batching&lt;/strong&gt; groups multiple inference requests arriving close in time into a single batch.  Instead of running one forward pass per request, the server merges them into micro‑batches and runs a single forward pass.  Systems like vLLM achieve &lt;strong data-end=&quot;7289&quot; data-start=&quot;7259&quot;&gt;up to 23× throughput gains&lt;/strong&gt; by continuously adding new prompts mid‑generation and sharing key/value caches across requests.&lt;/p&gt;&lt;p data-end=&quot;7710&quot; data-start=&quot;7387&quot;&gt;In our earlier example (with a tiny MLP), we saw that the first request took ~23&amp;nbsp;ms, while subsequent requests processed within 0.5–2&amp;nbsp;ms because they piggy‑backed on the existing batch.  Continuous batching is especially powerful when serving interactive chat applications where many users send short messages concurrently.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Conclusion&lt;/h2&gt;&lt;p data-end=&quot;9299&quot; data-is-only-node=&quot;&quot; data-start=&quot;8766&quot;&gt;Dynamic quantization is an easy way to reduce the computational cost of LLM inference, but its benefits scale with model size.  Our GPT‑2 experiment shows only modest gains because embeddings dominate memory usage and remain in FP32.  For significant improvements, practitioners should adopt weight‑only quantization and pair it with continuous batching, KV‑cache optimisation, and model distillation.  These methods, when applied thoughtfully, can make deploying large language models feasible even in cost‑constrained environments.&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2025/09/benchmarking-dynamic-quantization-for.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/a/AVvXsEgU2q3_2nAM97mJ16i75w4dOy2Pizqsb504QDWm83pzRUpjNRsHF-JsOsifyEW0zb9fHnYFYGqqndT_PDB8jlYiJtMNQVceRLM89Bi3gVAbCepYpYMKGq6bVevrlc8MHf_niLJiPHEtU7LiVQVi0G_z739NtTeQ98Lg47gAe-c0c3qWnsYNBeUmA6LPpEc=s72-w320-h320-c" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>London, UK</georss:featurename><georss:point>51.5072178 -0.1275862</georss:point><georss:box>23.196983963821154 -35.2838362 79.817451636178845 35.0286638</georss:box></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-5551546921138613405</guid><pubDate>Sat, 20 Sep 2025 13:07:00 +0000</pubDate><atom:updated>2025-09-20T13:07:44.772+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Agent Orchestration</category><category domain="http://www.blogger.com/atom/ns#">AI Agents</category><category domain="http://www.blogger.com/atom/ns#">AST Evaluation</category><category domain="http://www.blogger.com/atom/ns#">Calculator Tool</category><category domain="http://www.blogger.com/atom/ns#">Deterministic AI</category><category domain="http://www.blogger.com/atom/ns#">LangChain</category><category domain="http://www.blogger.com/atom/ns#">LangGraph</category><category domain="http://www.blogger.com/atom/ns#">Observability</category><category domain="http://www.blogger.com/atom/ns#">openAI</category><category domain="http://www.blogger.com/atom/ns#">Prompt Engineering</category><category domain="http://www.blogger.com/atom/ns#">Python</category><category domain="http://www.blogger.com/atom/ns#">ReAct</category><category domain="http://www.blogger.com/atom/ns#">Safe Evaluation</category><category domain="http://www.blogger.com/atom/ns#">Streaming Outputs</category><category domain="http://www.blogger.com/atom/ns#">Tool Calling</category><title>ReAct, For Real: Building Deterministic Tool-Using Agents with LangGraph</title><description>&lt;h1 style=&quot;text-align: left;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKxkA-9Wcigqx224NpjGb5mGJWreFqkrm8T8ZqUs71-6wfDp5MorUDOwo9klqkZjGnwNM05Uka_12GY7vGwAc6vfnmT5k0dplSgoU_cOTP1sJAr8QDlxw6ToS-aSyOi7uzyKYj2xi9nTVPycYyCbplUr4h_kW9kRkH0zb6zzsTO-pjm2bCmGJk91JZ9W4/s1536/ChatGPT%20Image%20Sep%2019,%202025,%2007_48_35%20AM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1536&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKxkA-9Wcigqx224NpjGb5mGJWreFqkrm8T8ZqUs71-6wfDp5MorUDOwo9klqkZjGnwNM05Uka_12GY7vGwAc6vfnmT5k0dplSgoU_cOTP1sJAr8QDlxw6ToS-aSyOi7uzyKYj2xi9nTVPycYyCbplUr4h_kW9kRkH0zb6zzsTO-pjm2bCmGJk91JZ9W4/s320/ChatGPT%20Image%20Sep%2019,%202025,%2007_48_35%20AM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;ReAct, For Real: Building Deterministic Tool-Using Agents with LangGraph&lt;/h1&gt;
&lt;p data-end=&quot;766&quot; data-start=&quot;735&quot;&gt;&lt;em data-end=&quot;766&quot; data-start=&quot;735&quot;&gt;(feat. a Safe AST Calculator)&lt;/em&gt;&lt;/p&gt;
&lt;blockquote data-end=&quot;965&quot; data-start=&quot;768&quot;&gt;
&lt;p data-end=&quot;965&quot; data-start=&quot;770&quot;&gt;A practical guide to wiring a &lt;strong data-end=&quot;816&quot; data-start=&quot;800&quot;&gt;reason + act&lt;/strong&gt; loop that’s auditable, stable, and observable. We use &lt;strong data-end=&quot;884&quot; data-start=&quot;871&quot;&gt;LangGraph&lt;/strong&gt; for orchestration and a &lt;strong data-end=&quot;936&quot; data-start=&quot;909&quot;&gt;hardened AST calculator&lt;/strong&gt; for math, no evals, no vibes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-end=&quot;986&quot; data-start=&quot;967&quot;&gt;Why this pattern&lt;/h2&gt;
&lt;ul data-end=&quot;1383&quot; data-start=&quot;988&quot;&gt;
&lt;li data-end=&quot;1107&quot; data-start=&quot;988&quot;&gt;
&lt;p data-end=&quot;1107&quot; data-start=&quot;990&quot;&gt;&lt;strong data-end=&quot;1017&quot; data-start=&quot;990&quot;&gt;Determinism over vibes.&lt;/strong&gt; The calculator is pure Python AST (whitelisted ops), so results are exact and testable.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1254&quot; data-start=&quot;1108&quot;&gt;
&lt;p data-end=&quot;1254&quot; data-start=&quot;1110&quot;&gt;&lt;strong data-end=&quot;1130&quot; data-start=&quot;1110&quot;&gt;Controlled loop.&lt;/strong&gt; First model step is &lt;strong data-end=&quot;1161&quot; data-start=&quot;1151&quot;&gt;forced&lt;/strong&gt; to call the tool; the second &lt;strong data-end=&quot;1201&quot; data-start=&quot;1191&quot;&gt;cannot&lt;/strong&gt; call tools and must answer. No infinite ping-pong.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1383&quot; data-start=&quot;1255&quot;&gt;
&lt;p data-end=&quot;1383&quot; data-start=&quot;1257&quot;&gt;&lt;strong data-end=&quot;1275&quot; data-start=&quot;1257&quot;&gt;Observability.&lt;/strong&gt; Streamed console output shows precisely when the &lt;strong data-end=&quot;1332&quot; data-start=&quot;1325&quot;&gt;LLM&lt;/strong&gt; runs vs. when the &lt;strong data-end=&quot;1359&quot; data-start=&quot;1351&quot;&gt;tool&lt;/strong&gt; runs, with token usage.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1388&quot; data-start=&quot;1385&quot; /&gt;
&lt;h2 data-end=&quot;1417&quot; data-start=&quot;1390&quot;&gt;Architecture at a glance&lt;/h2&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;2154&quot; data-start=&quot;1419&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre!&quot;&gt;┌──────────────┐     (tools_condition)      ┌────────────┐
│   agent LLM  │ ─────────────────────────► │  ToolNode  │
│ (forced &lt;span class=&quot;hljs-number&quot;&gt;1s&lt;/span&gt;t) │                            │ calculator │
└──────┬───────┘                            └─────┬──────┘
       │                                          │ ToolMessage
       │ no tool calls → END                      ▼
       │                                    ┌──────────────┐
       └─────────────────────────────────── │  agent LLM   │
                                            │ (free finish)│
                                            └──────┬───────┘
                                                   │ AiMessage (final)
                                                   ▼ END
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;p data-end=&quot;2171&quot; data-start=&quot;2156&quot;&gt;Key primitives:&lt;/p&gt;
&lt;ul data-end=&quot;2497&quot; data-start=&quot;2172&quot;&gt;
&lt;li data-end=&quot;2259&quot; data-start=&quot;2172&quot;&gt;
&lt;p data-end=&quot;2259&quot; data-start=&quot;2174&quot;&gt;&lt;strong data-end=&quot;2193&quot; data-start=&quot;2174&quot;&gt;&lt;code data-end=&quot;2191&quot; data-start=&quot;2176&quot;&gt;MessagesState&lt;/code&gt;&lt;/strong&gt;: appends messages, preserving the OpenAI tool-calling protocol.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2304&quot; data-start=&quot;2260&quot;&gt;
&lt;p data-end=&quot;2304&quot; data-start=&quot;2262&quot;&gt;&lt;strong data-end=&quot;2276&quot; data-start=&quot;2262&quot;&gt;&lt;code data-end=&quot;2274&quot; data-start=&quot;2264&quot;&gt;ToolNode&lt;/code&gt;&lt;/strong&gt;: executes declared tools.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2392&quot; data-start=&quot;2305&quot;&gt;
&lt;p data-end=&quot;2392&quot; data-start=&quot;2307&quot;&gt;&lt;strong data-end=&quot;2328&quot; data-start=&quot;2307&quot;&gt;&lt;code data-end=&quot;2326&quot; data-start=&quot;2309&quot;&gt;tools_condition&lt;/code&gt;&lt;/strong&gt;: routes to tools only when the assistant produced tool calls.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2497&quot; data-start=&quot;2393&quot;&gt;
&lt;p data-end=&quot;2497&quot; data-start=&quot;2395&quot;&gt;&lt;strong data-end=&quot;2419&quot; data-start=&quot;2395&quot;&gt;One-tool-call policy&lt;/strong&gt;: first LLM is &lt;strong data-end=&quot;2443&quot; data-start=&quot;2434&quot;&gt;bound&lt;/strong&gt; to the calculator; the finisher LLM is &lt;strong data-end=&quot;2496&quot; data-start=&quot;2483&quot;&gt;tool-free&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2502&quot; data-start=&quot;2499&quot; /&gt;
&lt;h2 data-end=&quot;2536&quot; data-start=&quot;2504&quot;&gt;The safe calculator (snippet)&lt;/h2&gt;
&lt;p data-end=&quot;2609&quot; data-start=&quot;2538&quot;&gt;&lt;strong data-end=&quot;2547&quot; data-start=&quot;2538&quot;&gt;Goal:&lt;/strong&gt; evaluate math deterministically, with a tiny NL preprocessor.&lt;/p&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;4244&quot; data-start=&quot;2611&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Whitelisted ops only&lt;/span&gt;
_ALLOWED_OPS = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul,
                ast.Div: op.truediv, ast.Pow: op.&lt;span class=&quot;hljs-built_in&quot;&gt;pow&lt;/span&gt;, ast.Mod: op.mod,
                ast.UAdd: op.pos, ast.USub: op.neg}

&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;_preprocess&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;s: &lt;span class=&quot;hljs-built_in&quot;&gt;str&lt;/span&gt;&lt;/span&gt;) -&amp;gt; &lt;span class=&quot;hljs-built_in&quot;&gt;str&lt;/span&gt;:
    s = s.strip().lower()
    s = re.sub(&lt;span class=&quot;hljs-string&quot;&gt;r&quot;\b(please|thanks|thank you|what\s*is|what&#39;s|calculate|compute|equals?)\b&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&lt;/span&gt;, s)
    s = re.sub(&lt;span class=&quot;hljs-string&quot;&gt;r&quot;\bthe\b&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&lt;/span&gt;, s)
    s = re.sub(&lt;span class=&quot;hljs-string&quot;&gt;r&quot;\bplus\b&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;+&quot;&lt;/span&gt;, s); s = re.sub(&lt;span class=&quot;hljs-string&quot;&gt;r&quot;\bminus\b&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;-&quot;&lt;/span&gt;, s)
    s = re.sub(&lt;span class=&quot;hljs-string&quot;&gt;r&quot;\btimes\b&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;*&quot;&lt;/span&gt;, s); s = re.sub(&lt;span class=&quot;hljs-string&quot;&gt;r&quot;\bdivided by\b&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;/&quot;&lt;/span&gt;, s)
    s = re.sub(&lt;span class=&quot;hljs-string&quot;&gt;r&quot;\b(?:the\s+)?square root of\b&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;sqrt(&quot;&lt;/span&gt;, s)
    s = re.sub(&lt;span class=&quot;hljs-string&quot;&gt;r&quot;√\s*&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;sqrt(&quot;&lt;/span&gt;, s)
    s = re.sub(&lt;span class=&quot;hljs-string&quot;&gt;r&quot;sqrt\(\s*([0-9\.]+)\s*\)?&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;r&quot;sqrt(\1)&quot;&lt;/span&gt;, s)
    s = re.sub(&lt;span class=&quot;hljs-string&quot;&gt;r&quot;(\d+(?:\.\d+)?)\s*%\s*of\s*([0-9\.]+)&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;r&quot;(\1/100*\2)&quot;&lt;/span&gt;, s)
    s = re.sub(&lt;span class=&quot;hljs-string&quot;&gt;r&quot;(\d+(?:\.\d+)?)\s*%&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;r&quot;(\1/100)&quot;&lt;/span&gt;, s)
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; s.replace(&lt;span class=&quot;hljs-string&quot;&gt;&quot;^&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;**&quot;&lt;/span&gt;).strip()

&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;_eval_ast&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;node: ast.AST&lt;/span&gt;) -&amp;gt; &lt;span class=&quot;hljs-built_in&quot;&gt;float&lt;/span&gt;:
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;isinstance&lt;/span&gt;(node, ast.Constant) &lt;span class=&quot;hljs-keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;isinstance&lt;/span&gt;(node.value, (&lt;span class=&quot;hljs-built_in&quot;&gt;int&lt;/span&gt;, &lt;span class=&quot;hljs-built_in&quot;&gt;float&lt;/span&gt;)):
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;float&lt;/span&gt;(node.value)
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;isinstance&lt;/span&gt;(node, ast.UnaryOp) &lt;span class=&quot;hljs-keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;type&lt;/span&gt;(node.op) &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; _ALLOWED_OPS:
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; _ALLOWED_OPS[&lt;span class=&quot;hljs-built_in&quot;&gt;type&lt;/span&gt;(node.op)](_eval_ast(node.operand))
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;isinstance&lt;/span&gt;(node, ast.BinOp) &lt;span class=&quot;hljs-keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;type&lt;/span&gt;(node.op) &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; _ALLOWED_OPS:
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; _ALLOWED_OPS[&lt;span class=&quot;hljs-built_in&quot;&gt;type&lt;/span&gt;(node.op)](_eval_ast(node.left), _eval_ast(node.right))
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;isinstance&lt;/span&gt;(node, ast.Call) &lt;span class=&quot;hljs-keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;isinstance&lt;/span&gt;(node.func, ast.Name) &lt;span class=&quot;hljs-keyword&quot;&gt;and&lt;/span&gt; node.func.&lt;span class=&quot;hljs-built_in&quot;&gt;id&lt;/span&gt; == &lt;span class=&quot;hljs-string&quot;&gt;&quot;sqrt&quot;&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;len&lt;/span&gt;(node.args) == &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;:
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; math.sqrt(_eval_ast(node.args[&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;]))
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;isinstance&lt;/span&gt;(node, ast.Expr):
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; _eval_ast(node.value)
    &lt;span class=&quot;hljs-keyword&quot;&gt;raise&lt;/span&gt; ValueError(&lt;span class=&quot;hljs-string&quot;&gt;&quot;Unsupported syntax.&quot;&lt;/span&gt;)
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;p data-end=&quot;4259&quot; data-start=&quot;4246&quot;&gt;Tool wrapper:&lt;/p&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;4540&quot; data-start=&quot;4261&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;&lt;span class=&quot;hljs-meta&quot;&gt;@tool(&lt;span class=&quot;hljs-params&quot;&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;calculator_tool&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;)
&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;calculator_tool&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;expression: &lt;span class=&quot;hljs-built_in&quot;&gt;str&lt;/span&gt;&lt;/span&gt;) -&amp;gt; &lt;span class=&quot;hljs-built_in&quot;&gt;str&lt;/span&gt;:
    &lt;span class=&quot;hljs-keyword&quot;&gt;try&lt;/span&gt;:
        pre = _preprocess(expression)
        value = _eval_ast(ast.parse(pre, mode=&lt;span class=&quot;hljs-string&quot;&gt;&quot;eval&quot;&lt;/span&gt;).body)
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;str&lt;/span&gt;(&lt;span class=&quot;hljs-built_in&quot;&gt;float&lt;/span&gt;(value))
    &lt;span class=&quot;hljs-keyword&quot;&gt;except&lt;/span&gt; Exception &lt;span class=&quot;hljs-keyword&quot;&gt;as&lt;/span&gt; e:
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;f&quot;Error: &lt;span class=&quot;hljs-subst&quot;&gt;{e}&lt;/span&gt;&lt;/span&gt;&quot;
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;p data-end=&quot;4680&quot; data-start=&quot;4542&quot;&gt;&lt;strong data-end=&quot;4563&quot; data-start=&quot;4542&quot;&gt;Why this is safe:&lt;/strong&gt; Only numeric literals and whitelisted operators/functions are allowed; no names, attrs, or imports can ever execute.&lt;/p&gt;
&lt;hr data-end=&quot;4685&quot; data-start=&quot;4682&quot; /&gt;
&lt;h2 data-end=&quot;4716&quot; data-start=&quot;4687&quot;&gt;LangGraph wiring (snippet)&lt;/h2&gt;
&lt;p data-end=&quot;4816&quot; data-start=&quot;4718&quot;&gt;Bind two LLM phases: &lt;strong data-end=&quot;4755&quot; data-start=&quot;4739&quot;&gt;forced first&lt;/strong&gt; (must call calculator) and &lt;strong data-end=&quot;4798&quot; data-start=&quot;4783&quot;&gt;free finish&lt;/strong&gt; (no tools bound).&lt;/p&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;5808&quot; data-start=&quot;4818&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;llm_forced_first = ChatOpenAI(...).bind_tools(
    [calculator_tool],
    tool_choice={&lt;span class=&quot;hljs-string&quot;&gt;&quot;type&quot;&lt;/span&gt;:&lt;span class=&quot;hljs-string&quot;&gt;&quot;function&quot;&lt;/span&gt;,&lt;span class=&quot;hljs-string&quot;&gt;&quot;function&quot;&lt;/span&gt;:{&lt;span class=&quot;hljs-string&quot;&gt;&quot;name&quot;&lt;/span&gt;:&lt;span class=&quot;hljs-string&quot;&gt;&quot;calculator_tool&quot;&lt;/span&gt;}}
)
llm_free = ChatOpenAI(...)

&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;_has_calc_tool_call&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;msgs&lt;/span&gt;) -&amp;gt; &lt;span class=&quot;hljs-built_in&quot;&gt;bool&lt;/span&gt;:
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;any&lt;/span&gt;(&lt;span class=&quot;hljs-built_in&quot;&gt;isinstance&lt;/span&gt;(m, ToolMessage) &lt;span class=&quot;hljs-keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;getattr&lt;/span&gt;(m,&lt;span class=&quot;hljs-string&quot;&gt;&quot;name&quot;&lt;/span&gt;,&lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&lt;/span&gt;)==&lt;span class=&quot;hljs-string&quot;&gt;&quot;calculator_tool&quot;&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; m &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; msgs)

&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;agent_node&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;state: MessagesState&lt;/span&gt;):
    first_phase = &lt;span class=&quot;hljs-keyword&quot;&gt;not&lt;/span&gt; _has_calc_tool_call(state[&lt;span class=&quot;hljs-string&quot;&gt;&quot;messages&quot;&lt;/span&gt;])
    llm = llm_forced_first &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; first_phase &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt; llm_free
    ai = llm.invoke([SystemMessage(content=SYSTEM_RULE), *state[&lt;span class=&quot;hljs-string&quot;&gt;&quot;messages&quot;&lt;/span&gt;]],
                    config={&lt;span class=&quot;hljs-string&quot;&gt;&quot;tags&quot;&lt;/span&gt;: [&lt;span class=&quot;hljs-string&quot;&gt;&quot;agent_llm&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;forced_first&quot;&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; first_phase &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;free_finish&quot;&lt;/span&gt;]})
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; {&lt;span class=&quot;hljs-string&quot;&gt;&quot;messages&quot;&lt;/span&gt;: [ai]}

tool_node = ToolNode([calculator_tool])

g = StateGraph(MessagesState)
g.add_node(&lt;span class=&quot;hljs-string&quot;&gt;&quot;agent&quot;&lt;/span&gt;, agent_node)
g.add_node(&lt;span class=&quot;hljs-string&quot;&gt;&quot;tools&quot;&lt;/span&gt;, tool_node)
g.add_conditional_edges(&lt;span class=&quot;hljs-string&quot;&gt;&quot;agent&quot;&lt;/span&gt;, tools_condition, {&lt;span class=&quot;hljs-string&quot;&gt;&quot;tools&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;tools&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;__end__&quot;&lt;/span&gt;: END})
g.add_edge(&lt;span class=&quot;hljs-string&quot;&gt;&quot;tools&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;agent&quot;&lt;/span&gt;)
g.set_entry_point(&lt;span class=&quot;hljs-string&quot;&gt;&quot;agent&quot;&lt;/span&gt;)
graph = g.&lt;span class=&quot;hljs-built_in&quot;&gt;compile&lt;/span&gt;()
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;p data-end=&quot;5986&quot; data-start=&quot;5810&quot;&gt;&lt;strong data-end=&quot;5834&quot; data-start=&quot;5810&quot;&gt;Why it ends cleanly:&lt;/strong&gt; After tools run, the graph goes back to the agent &lt;strong data-end=&quot;5893&quot; data-start=&quot;5885&quot;&gt;once&lt;/strong&gt; to produce the final answer; &lt;code data-end=&quot;5940&quot; data-start=&quot;5923&quot;&gt;tools_condition&lt;/code&gt; routes to &lt;code data-end=&quot;5956&quot; data-start=&quot;5951&quot;&gt;END&lt;/code&gt; if no tool calls are present.&lt;/p&gt;
&lt;hr data-end=&quot;5991&quot; data-start=&quot;5988&quot; /&gt;
&lt;h2 data-end=&quot;6044&quot; data-start=&quot;5993&quot;&gt;Observability: know exactly when the LLM is used&lt;/h2&gt;
&lt;p data-end=&quot;6132&quot; data-start=&quot;6046&quot;&gt;Add a tiny callback to print LLM start/end and token usage; gate with &lt;code data-end=&quot;6131&quot; data-start=&quot;6116&quot;&gt;DEBUG_AGENT=1&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;6764&quot; data-start=&quot;6134&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;ConsoleLLMHandler&lt;/span&gt;(&lt;span class=&quot;hljs-title class_ inherited__&quot;&gt;BaseCallbackHandler&lt;/span&gt;):
    &lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;on_llm_start&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;self, serialized, prompts, **kw&lt;/span&gt;):
        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; os.getenv(&lt;span class=&quot;hljs-string&quot;&gt;&quot;DEBUG_AGENT&quot;&lt;/span&gt;)==&lt;span class=&quot;hljs-string&quot;&gt;&quot;1&quot;&lt;/span&gt;:
            &lt;span class=&quot;hljs-built_in&quot;&gt;print&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;f&quot;[LLM START] &lt;span class=&quot;hljs-subst&quot;&gt;{serialized.get(&lt;span class=&quot;hljs-string&quot;&gt;&#39;name&#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;,&lt;span class=&quot;hljs-string&quot;&gt;&#39;LLM&#39;&lt;/span&gt;)} | tags=&lt;span class=&quot;hljs-subst&quot;&gt;{kw.get(&lt;span class=&quot;hljs-string&quot;&gt;&#39;tags&#39;&lt;/span&gt;&lt;/span&gt;,[])}&quot;, file=sys.stderr)
    &lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;on_llm_end&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;self, result, **kw&lt;/span&gt;):
        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; os.getenv(&lt;span class=&quot;hljs-string&quot;&gt;&quot;DEBUG_AGENT&quot;&lt;/span&gt;)==&lt;span class=&quot;hljs-string&quot;&gt;&quot;1&quot;&lt;/span&gt;:
            &lt;span class=&quot;hljs-built_in&quot;&gt;print&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;f&quot;[LLM END] tags=&lt;span class=&quot;hljs-subst&quot;&gt;{kw.get(&lt;span class=&quot;hljs-string&quot;&gt;&#39;tags&#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;,[])} | usage=&lt;span class=&quot;hljs-subst&quot;&gt;{(result.llm_output &lt;span class=&quot;hljs-keyword&quot;&gt;or&lt;/span&gt;&lt;/span&gt; {}).get(&#39;token_usage&#39;,&lt;span class=&quot;hljs-subst&quot;&gt;{}&lt;/span&gt;)}&quot;, file=sys.stderr)

handler = ConsoleLLMHandler()
llm_forced_first = ChatOpenAI(..., callbacks=[handler]).bind_tools(...)
llm_free = ChatOpenAI(..., callbacks=[handler])
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;p data-end=&quot;6847&quot; data-start=&quot;6766&quot;&gt;&lt;strong data-end=&quot;6779&quot; data-start=&quot;6766&quot;&gt;Optional:&lt;/strong&gt; &lt;code data-end=&quot;6794&quot; data-start=&quot;6780&quot;&gt;DEBUG_CALC=1&lt;/code&gt; prints NL → normalized math expression for the tool.&lt;/p&gt;
&lt;hr data-end=&quot;6852&quot; data-start=&quot;6849&quot; /&gt;
&lt;h2 data-end=&quot;6867&quot; data-start=&quot;6854&quot;&gt;Running it&lt;/h2&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;7045&quot; data-start=&quot;6869&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-bash&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; OPENAI_API_KEY=sk-...
&lt;span class=&quot;hljs-comment&quot;&gt;# telemetry on:&lt;/span&gt;
&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; DEBUG_AGENT=1
&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; DEBUG_CALC=1

python calculator_agent.py &lt;span class=&quot;hljs-string&quot;&gt;&quot;Calculate 12% of 255 plus the square root of 244&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;p data-end=&quot;7071&quot; data-start=&quot;7047&quot;&gt;Sample (trimmed) output:&lt;/p&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;7375&quot; data-start=&quot;7073&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;================================ Human Message =================================

Calculate 12% of 255 plus the square root of 244

[LLM START] ChatOpenAI | tags=[&#39;seq:step:1&#39;, &#39;agent_llm&#39;, &#39;forced_first&#39;]
[LLM END] tags=[&#39;seq:step:1&#39;, &#39;agent_llm&#39;, &#39;forced_first&#39;] | usage={&#39;completion_tokens&#39;: 14, &#39;prompt_tokens&#39;: 161, &#39;total_tokens&#39;: 175, &#39;completion_tokens_details&#39;: {&#39;accepted_prediction_tokens&#39;: 0, &#39;audio_tokens&#39;: 0, &#39;reasoning_tokens&#39;: 0, &#39;rejected_prediction_tokens&#39;: 0}, &#39;prompt_tokens_details&#39;: {&#39;audio_tokens&#39;: 0, &#39;cached_tokens&#39;: 0}}
================================== Ai Message ==================================
Tool Calls:
  calculator_tool (call_134y7dqSU2udc2rr0OTNHKQs)
  Args:
    expression: 12% of 255 plus sqrt(244)

================================= Tool Message =================================
Name: calculator_tool

46.2204993518133

[LLM START] ChatOpenAI | tags=[&#39;seq:step:1&#39;, &#39;agent_llm&#39;, &#39;free_finish&#39;]
[LLM END] tags=[&#39;seq:step:1&#39;, &#39;agent_llm&#39;, &#39;free_finish&#39;] | usage={&#39;completion_tokens&#39;: 23, &#39;prompt_tokens&#39;: 121, &#39;total_tokens&#39;: 144, &#39;completion_tokens_details&#39;: {&#39;accepted_prediction_tokens&#39;: 0, &#39;audio_tokens&#39;: 0, &#39;reasoning_tokens&#39;: 0, &#39;rejected_prediction_tokens&#39;: 0}, &#39;prompt_tokens_details&#39;: {&#39;audio_tokens&#39;: 0, &#39;cached_tokens&#39;: 0}}
================================== Ai Message ==================================

&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre!&quot;&gt;The result of 12% of 255 plus the square root of 244 is approximately 46.22.
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;hr data-end=&quot;7380&quot; data-start=&quot;7377&quot; /&gt;
&lt;h2 data-end=&quot;7412&quot; data-start=&quot;7382&quot;&gt;Design choices: pros &amp;amp; cons&lt;/h2&gt;
&lt;p data-end=&quot;7422&quot; data-start=&quot;7414&quot;&gt;&lt;strong data-end=&quot;7422&quot; data-start=&quot;7414&quot;&gt;Pros&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-end=&quot;7650&quot; data-start=&quot;7423&quot;&gt;
&lt;li data-end=&quot;7488&quot; data-start=&quot;7423&quot;&gt;
&lt;p data-end=&quot;7488&quot; data-start=&quot;7425&quot;&gt;&lt;strong data-end=&quot;7442&quot; data-start=&quot;7425&quot;&gt;Deterministic&lt;/strong&gt; math; no LLM hallucinations for arithmetic.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;7554&quot; data-start=&quot;7489&quot;&gt;
&lt;p data-end=&quot;7554&quot; data-start=&quot;7491&quot;&gt;&lt;strong data-end=&quot;7511&quot; data-start=&quot;7491&quot;&gt;Single tool call&lt;/strong&gt; guarantees predictable latency and flow.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;7650&quot; data-start=&quot;7555&quot;&gt;
&lt;p data-end=&quot;7650&quot; data-start=&quot;7557&quot;&gt;&lt;strong data-end=&quot;7569&quot; data-start=&quot;7557&quot;&gt;Great DX&lt;/strong&gt;: streaming, token accounting, and NL conveniences (&lt;code data-end=&quot;7627&quot; data-start=&quot;7621&quot;&gt;% of&lt;/code&gt;, &lt;code data-end=&quot;7632&quot; data-start=&quot;7629&quot;&gt;√&lt;/code&gt;, word operators).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;7660&quot; data-start=&quot;7652&quot;&gt;&lt;strong data-end=&quot;7660&quot; data-start=&quot;7652&quot;&gt;Cons&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-end=&quot;8433&quot; data-start=&quot;7923&quot;&gt;
&lt;li data-end=&quot;7751&quot; data-start=&quot;7661&quot;&gt;
&lt;p data-end=&quot;7751&quot; data-start=&quot;7663&quot;&gt;Narrow by design (&lt;code data-end=&quot;7687&quot; data-start=&quot;7681&quot;&gt;sqrt&lt;/code&gt;, basic ops). Extend via whitelist if you need &lt;code data-end=&quot;7748&quot; data-start=&quot;7734&quot;&gt;log/exp/pi/e&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;7821&quot; data-start=&quot;7752&quot;&gt;
&lt;p data-end=&quot;7821&quot; data-start=&quot;7754&quot;&gt;Requires an LLM key for orchestration (the math itself is local).&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;7884&quot; data-start=&quot;7822&quot;&gt;
&lt;p data-end=&quot;7884&quot; data-start=&quot;7824&quot;&gt;NL preprocessor is conservative to keep parsing predictable.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;8438&quot; data-start=&quot;8435&quot; /&gt;
&lt;h2 data-end=&quot;8456&quot; data-start=&quot;8440&quot;&gt;Code &amp;amp; assets&lt;/h2&gt;
&lt;ul data-end=&quot;8618&quot; data-start=&quot;8458&quot;&gt;
&lt;li data-end=&quot;8513&quot; data-start=&quot;8458&quot;&gt;
&lt;p data-end=&quot;8513&quot; data-start=&quot;8460&quot;&gt;&lt;strong data-end=&quot;8471&quot; data-start=&quot;8460&quot;&gt;Source:&lt;/strong&gt; &lt;span data-end=&quot;8511&quot; data-start=&quot;8472&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;&lt;a href=&quot;https://github.com/JordiCorbilla/langgraph-cookbook&quot;&gt;https://github.com/JordiCorbilla/langgraph-cookbook&lt;/a&gt;&lt;/mi&gt;&lt;/mrow&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;8623&quot; data-start=&quot;8620&quot; /&gt;
&lt;h3 data-end=&quot;8645&quot; data-start=&quot;8625&quot;&gt;Snippet appendix&lt;/h3&gt;
&lt;p data-end=&quot;8687&quot; data-start=&quot;8647&quot;&gt;&lt;strong data-end=&quot;8687&quot; data-start=&quot;8647&quot;&gt;System rule (keeps the agent honest)&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;8915&quot; data-start=&quot;8689&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;SYSTEM_RULE = (
  &lt;span class=&quot;hljs-string&quot;&gt;&quot;You are a math assistant. For ANY numeric computation, &quot;&lt;/span&gt;
  &lt;span class=&quot;hljs-string&quot;&gt;&quot;call `calculator_tool` exactly once, then return the final answer. &quot;&lt;/span&gt;
  &lt;span class=&quot;hljs-string&quot;&gt;&quot;Do not call tools again after you have the numeric result.&quot;&lt;/span&gt;
)
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;p data-end=&quot;8936&quot; data-start=&quot;8917&quot;&gt;&lt;strong data-end=&quot;8936&quot; data-start=&quot;8917&quot;&gt;Smoke test idea&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;9042&quot; data-start=&quot;8938&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Expect: ≥2 AI messages (forced+final), exactly 1 ToolMessage, final AI has no tool_calls&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;</description><link>https://thundaxsoftware.blogspot.com/2025/09/react-for-real-building-deterministic.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKxkA-9Wcigqx224NpjGb5mGJWreFqkrm8T8ZqUs71-6wfDp5MorUDOwo9klqkZjGnwNM05Uka_12GY7vGwAc6vfnmT5k0dplSgoU_cOTP1sJAr8QDlxw6ToS-aSyOi7uzyKYj2xi9nTVPycYyCbplUr4h_kW9kRkH0zb6zzsTO-pjm2bCmGJk91JZ9W4/s72-c/ChatGPT%20Image%20Sep%2019,%202025,%2007_48_35%20AM.png" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>London, UK</georss:featurename><georss:point>51.5072178 -0.1275862</georss:point><georss:box>23.196983963821154 -35.2838362 79.817451636178845 35.0286638</georss:box></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-3226449771189446065</guid><pubDate>Sun, 14 Sep 2025 11:57:00 +0000</pubDate><atom:updated>2025-09-14T11:57:48.577+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI Agents</category><category domain="http://www.blogger.com/atom/ns#">chunked summarization</category><category domain="http://www.blogger.com/atom/ns#">fixed chain</category><category domain="http://www.blogger.com/atom/ns#">function calling</category><category domain="http://www.blogger.com/atom/ns#">LangChain</category><category domain="http://www.blogger.com/atom/ns#">LLM tool calling</category><category domain="http://www.blogger.com/atom/ns#">map-reduce</category><category domain="http://www.blogger.com/atom/ns#">OpenAI GPT-4o</category><category domain="http://www.blogger.com/atom/ns#">Python CLI</category><category domain="http://www.blogger.com/atom/ns#">RAG</category><category domain="http://www.blogger.com/atom/ns#">recursive agent</category><category domain="http://www.blogger.com/atom/ns#">YouTube summarization</category><category domain="http://www.blogger.com/atom/ns#">youtube-transcript-api</category><category domain="http://www.blogger.com/atom/ns#">yt-dlp</category><title>A Practical, Production-Ready Tool for Summarizing YouTube Videos with LLMs and Agents</title><description>&lt;p&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgF1o_DfQwCZlI9sxzkHy3dpf97E_J4dmJQiePAiWVxtZDoqVsYVdxOoEgaB6_02ukpe_lZDBFAEY2m9KDBXjUsDS0PqTuDsuobkyMmXsV1oOBtssw32A5NZnWz3oUCwR43Kn7GahuG_86ohEcPc2xIhOlAM1PVOClcaZJp5iIg5uTeJEfec4NJbLh2LSc/s1536/ChatGPT%20Image%20Sep%2013,%202025,%2001_20_50%20PM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; display: inline !important; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1536&quot; height=&quot;213&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgF1o_DfQwCZlI9sxzkHy3dpf97E_J4dmJQiePAiWVxtZDoqVsYVdxOoEgaB6_02ukpe_lZDBFAEY2m9KDBXjUsDS0PqTuDsuobkyMmXsV1oOBtssw32A5NZnWz3oUCwR43Kn7GahuG_86ohEcPc2xIhOlAM1PVOClcaZJp5iIg5uTeJEfec4NJbLh2LSc/s320/ChatGPT%20Image%20Sep%2013,%202025,%2001_20_50%20PM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;A Practical, Production-Ready Tool for Summarizing YouTube Videos with LLMs and Agents&lt;/h2&gt;&lt;p&gt;&lt;/p&gt;
&lt;blockquote data-end=&quot;352&quot; data-start=&quot;90&quot;&gt;
&lt;p data-end=&quot;352&quot; data-start=&quot;92&quot;&gt;This post is about a &lt;strong data-end=&quot;121&quot; data-start=&quot;113&quot;&gt;tool&lt;/strong&gt;. It’s a CLI that uses &lt;strong data-end=&quot;175&quot; data-start=&quot;155&quot;&gt;LLM tool calling&lt;/strong&gt; and a tiny agent loop to extract IDs, fetch transcripts/metadata, and produce high-quality summaries of &lt;strong data-end=&quot;288&quot; data-start=&quot;280&quot;&gt;long&lt;/strong&gt; YouTube videos without tripping rate limits or context windows.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-end=&quot;357&quot; data-start=&quot;354&quot; /&gt;
&lt;h2 data-end=&quot;367&quot; data-start=&quot;359&quot;&gt;TL;DR&lt;/h2&gt;
&lt;div class=&quot;_tableContainer_1rjym_1&quot;&gt;&lt;div class=&quot;group w-fit _tableWrapper_1rjym_13 flex flex-col-reverse&quot; tabindex=&quot;-1&quot;&gt;&lt;table class=&quot;w-fit min-w-(--thread-content-width)&quot; data-end=&quot;830&quot; data-start=&quot;369&quot;&gt;&lt;thead data-end=&quot;429&quot; data-start=&quot;369&quot;&gt;&lt;tr data-end=&quot;429&quot; data-start=&quot;369&quot;&gt;&lt;th data-col-size=&quot;md&quot; data-end=&quot;382&quot; data-start=&quot;369&quot;&gt;What it is&lt;/th&gt;&lt;th data-col-size=&quot;md&quot; data-end=&quot;400&quot; data-start=&quot;382&quot;&gt;Why it’s useful&lt;/th&gt;&lt;th data-col-size=&quot;lg&quot; data-end=&quot;415&quot; data-start=&quot;400&quot;&gt;How it works&lt;/th&gt;&lt;th data-col-size=&quot;md&quot; data-end=&quot;429&quot; data-start=&quot;415&quot;&gt;How to run&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody data-end=&quot;830&quot; data-start=&quot;448&quot;&gt;&lt;tr data-end=&quot;830&quot; data-start=&quot;448&quot;&gt;&lt;td data-col-size=&quot;md&quot; data-end=&quot;531&quot; data-start=&quot;448&quot;&gt;A &lt;strong data-end=&quot;464&quot; data-start=&quot;452&quot;&gt;CLI tool&lt;/strong&gt; that summarizes YouTube videos using an &lt;strong data-end=&quot;530&quot; data-start=&quot;505&quot;&gt;LLM with tools/agents&lt;/strong&gt;&lt;/td&gt;&lt;td data-col-size=&quot;md&quot; data-end=&quot;607&quot; data-start=&quot;531&quot;&gt;Handles &lt;strong data-end=&quot;556&quot; data-start=&quot;541&quot;&gt;long videos&lt;/strong&gt; and real-world edge cases (no transcript, quotas)&lt;/td&gt;&lt;td data-col-size=&quot;lg&quot; data-end=&quot;752&quot; data-start=&quot;607&quot;&gt;LangChain &lt;strong data-end=&quot;659&quot; data-start=&quot;619&quot;&gt;tool calling&lt;/strong&gt;+ fixed/recursive chains + &lt;strong data-end=&quot;709&quot; data-start=&quot;687&quot;&gt;chunked map-reduce&lt;/strong&gt; summarization + &lt;strong data-end=&quot;751&quot; data-start=&quot;726&quot;&gt;bare-LLM finalization&lt;/strong&gt;&lt;/td&gt;&lt;td data-col-size=&quot;md&quot; data-end=&quot;830&quot; data-start=&quot;752&quot;&gt;&lt;code data-end=&quot;828&quot; data-start=&quot;754&quot;&gt;python youtube_tool_agent.py summarize --url &quot;&amp;lt;video-url&amp;gt;&quot; --language en&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;
&lt;hr data-end=&quot;835&quot; data-start=&quot;832&quot; /&gt;
&lt;h2 data-end=&quot;859&quot; data-start=&quot;837&quot;&gt;What this tool does&lt;/h2&gt;
&lt;ul data-end=&quot;1451&quot; data-start=&quot;861&quot;&gt;
&lt;li data-end=&quot;939&quot; data-start=&quot;861&quot;&gt;
&lt;p data-end=&quot;939&quot; data-start=&quot;863&quot;&gt;&lt;strong data-end=&quot;875&quot; data-start=&quot;863&quot;&gt;Extracts&lt;/strong&gt; YouTube video IDs (robust to &lt;code data-end=&quot;915&quot; data-start=&quot;905&quot;&gt;watch?v=&lt;/code&gt;, &lt;code data-end=&quot;928&quot; data-start=&quot;917&quot;&gt;youtu.be/&lt;/code&gt;, &lt;code data-end=&quot;938&quot; data-start=&quot;930&quot;&gt;embed/&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1041&quot; data-start=&quot;940&quot;&gt;
&lt;p data-end=&quot;1041&quot; data-start=&quot;942&quot;&gt;&lt;strong data-end=&quot;965&quot; data-start=&quot;942&quot;&gt;Fetches transcripts&lt;/strong&gt; (and can be extended to fall back to &lt;strong data-end=&quot;1027&quot; data-start=&quot;1003&quot;&gt;captions/description&lt;/strong&gt; via &lt;code data-end=&quot;1040&quot; data-start=&quot;1032&quot;&gt;yt-dlp&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1133&quot; data-start=&quot;1042&quot;&gt;
&lt;p data-end=&quot;1133&quot; data-start=&quot;1044&quot;&gt;&lt;strong data-end=&quot;1075&quot; data-start=&quot;1044&quot;&gt;Pulls metadata &amp;amp; thumbnails&lt;/strong&gt; via &lt;code data-end=&quot;1088&quot; data-start=&quot;1080&quot;&gt;yt-dlp&lt;/code&gt; (title, views, likes, chapters, image sizes)&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1174&quot; data-start=&quot;1134&quot;&gt;
&lt;p data-end=&quot;1174&quot; data-start=&quot;1136&quot;&gt;&lt;strong data-end=&quot;1148&quot; data-start=&quot;1136&quot;&gt;Searches&lt;/strong&gt; YouTube by query (PyTube)&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1213&quot; data-start=&quot;1175&quot;&gt;
&lt;p data-end=&quot;1213&quot; data-start=&quot;1177&quot;&gt;&lt;strong data-end=&quot;1203&quot; data-start=&quot;1177&quot;&gt;(Best-effort) Trending&lt;/strong&gt; by region&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1359&quot; data-start=&quot;1214&quot;&gt;
&lt;p data-end=&quot;1359&quot; data-start=&quot;1216&quot;&gt;Generates &lt;strong data-end=&quot;1265&quot; data-start=&quot;1226&quot;&gt;succinct, expert-oriented summaries&lt;/strong&gt; of &lt;strong data-end=&quot;1277&quot; data-start=&quot;1269&quot;&gt;long&lt;/strong&gt; videos using a &lt;strong data-end=&quot;1315&quot; data-start=&quot;1293&quot;&gt;chunked map-reduce&lt;/strong&gt; flow that avoids &lt;strong data-end=&quot;1348&quot; data-start=&quot;1333&quot;&gt;TPM/context&lt;/strong&gt; explosions&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1451&quot; data-start=&quot;1360&quot;&gt;
&lt;p data-end=&quot;1451&quot; data-start=&quot;1362&quot;&gt;Includes a &lt;strong data-end=&quot;1380&quot; data-start=&quot;1373&quot;&gt;CLI&lt;/strong&gt; and &lt;strong data-end=&quot;1404&quot; data-start=&quot;1385&quot;&gt;verbose tracing&lt;/strong&gt; so you can see exactly what the agent is doing&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1456&quot; data-start=&quot;1453&quot; /&gt;
&lt;h2 data-end=&quot;1480&quot; data-start=&quot;1458&quot;&gt;Install &amp;amp; first run&lt;/h2&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;1818&quot; data-start=&quot;1482&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-bash&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Python 3.11+ recommended&lt;/span&gt;&lt;/span&gt;&lt;span&gt;
pip install -r requirements.txt

&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Set your model key (OpenAI shown)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt;&lt;/span&gt;&lt;span&gt; OPENAI_API_KEY=&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;sk-...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Windows PowerShell:  $env:OPENAI_API_KEY=&quot;sk-...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;

&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Summarize a video&lt;/span&gt;&lt;/span&gt;&lt;span&gt;
python youtube_tool_agent.py --verbose summarize \
  --url &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;https://www.youtube.com/watch?v=8TJQhQ2GZ0Y&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt; \
  --language en
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;p data-end=&quot;1845&quot; data-start=&quot;1820&quot;&gt;You’ll see progress like:&lt;/p&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;2058&quot; data-start=&quot;1846&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre!&quot;&gt;&lt;span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-meta&quot;&gt;LLM&lt;/span&gt;&lt;/span&gt;&lt;span&gt;] OpenAI provider model=gpt&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-number&quot;&gt;-4&lt;/span&gt;&lt;/span&gt;&lt;span&gt;o provider=openai
&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-meta&quot;&gt;SUM&lt;/span&gt;&lt;/span&gt;&lt;span&gt;] Chunk &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-number&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-number&quot;&gt;30000&lt;/span&gt;&lt;/span&gt;&lt;span&gt; chars)
[&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-meta&quot;&gt;SUM&lt;/span&gt;&lt;/span&gt;&lt;span&gt;] Chunk &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-number&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-number&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-number&quot;&gt;22007&lt;/span&gt;&lt;/span&gt;&lt;span&gt; chars)
&amp;lt;final polished summary printed here&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;hr data-end=&quot;2063&quot; data-start=&quot;2060&quot; /&gt;
&lt;h2 data-end=&quot;2096&quot; data-start=&quot;2065&quot;&gt;Why agents for this problem?&lt;/h2&gt;
&lt;p data-end=&quot;2166&quot; data-start=&quot;2098&quot;&gt;A “just call an API and prompt” script breaks on real-world YouTube:&lt;/p&gt;
&lt;ul data-end=&quot;2461&quot; data-start=&quot;2168&quot;&gt;
&lt;li data-end=&quot;2246&quot; data-start=&quot;2168&quot;&gt;
&lt;p data-end=&quot;2246&quot; data-start=&quot;2170&quot;&gt;Some videos have &lt;strong data-end=&quot;2213&quot; data-start=&quot;2187&quot;&gt;no official transcript&lt;/strong&gt; (only auto-captions or nothing).&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2335&quot; data-start=&quot;2247&quot;&gt;
&lt;p data-end=&quot;2335&quot; data-start=&quot;2249&quot;&gt;Transcripts can be &lt;strong data-end=&quot;2276&quot; data-start=&quot;2268&quot;&gt;huge&lt;/strong&gt;, exceeding model &lt;strong data-end=&quot;2305&quot; data-start=&quot;2294&quot;&gt;context&lt;/strong&gt; or your org’s &lt;strong data-end=&quot;2327&quot; data-start=&quot;2320&quot;&gt;TPM&lt;/strong&gt; limits.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2461&quot; data-start=&quot;2336&quot;&gt;
&lt;p data-end=&quot;2461&quot; data-start=&quot;2338&quot;&gt;Tool-calling models often respond with &lt;strong data-end=&quot;2394&quot; data-start=&quot;2377&quot;&gt;empty content&lt;/strong&gt; + a request to call another tool, unless you orchestrate correctly.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2506&quot; data-start=&quot;2463&quot;&gt;An &lt;strong data-end=&quot;2475&quot; data-start=&quot;2466&quot;&gt;agent&lt;/strong&gt; (even a tiny one) solves this:&lt;/p&gt;
&lt;ul data-end=&quot;2814&quot; data-start=&quot;2508&quot;&gt;
&lt;li data-end=&quot;2607&quot; data-start=&quot;2508&quot;&gt;
&lt;p data-end=&quot;2607&quot; data-start=&quot;2510&quot;&gt;It &lt;strong data-end=&quot;2524&quot; data-start=&quot;2513&quot;&gt;decides&lt;/strong&gt; which tool to call and &lt;strong data-end=&quot;2565&quot; data-start=&quot;2548&quot;&gt;in what order&lt;/strong&gt; (ID → transcript → metadata → summarize).&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2699&quot; data-start=&quot;2608&quot;&gt;
&lt;p data-end=&quot;2699&quot; data-start=&quot;2610&quot;&gt;It &lt;strong data-end=&quot;2622&quot; data-start=&quot;2613&quot;&gt;loops&lt;/strong&gt; until it has what it needs (recursive chain), then &lt;strong data-end=&quot;2687&quot; data-start=&quot;2674&quot;&gt;finalizes&lt;/strong&gt; the answer.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2814&quot; data-start=&quot;2700&quot;&gt;
&lt;p data-end=&quot;2814&quot; data-start=&quot;2702&quot;&gt;You get &lt;strong data-end=&quot;2726&quot; data-start=&quot;2710&quot;&gt;traceability&lt;/strong&gt;: every tool call is logged with payload sizes so you can reason about cost and latency.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2819&quot; data-start=&quot;2816&quot; /&gt;
&lt;h2 data-end=&quot;2870&quot; data-start=&quot;2821&quot;&gt;Core ideas (architectural choices that matter)&lt;/h2&gt;
&lt;h3 data-end=&quot;2903&quot; data-start=&quot;2872&quot;&gt;1) Tool calling (LangChain)&lt;/h3&gt;
&lt;p data-end=&quot;2977&quot; data-start=&quot;2904&quot;&gt;We expose capabilities as &lt;strong data-end=&quot;2949&quot; data-start=&quot;2930&quot;&gt;annotated tools&lt;/strong&gt; the model can call by name:&lt;/p&gt;
&lt;ul data-end=&quot;3099&quot; data-start=&quot;2978&quot;&gt;
&lt;li data-end=&quot;3099&quot; data-start=&quot;2978&quot;&gt;
&lt;p data-end=&quot;3099&quot; data-start=&quot;2980&quot;&gt;&lt;code data-end=&quot;2998&quot; data-start=&quot;2980&quot;&gt;extract_video_id&lt;/code&gt;, &lt;code data-end=&quot;3018&quot; data-start=&quot;3000&quot;&gt;fetch_transcript&lt;/code&gt;, &lt;code data-end=&quot;3039&quot; data-start=&quot;3020&quot;&gt;get_full_metadata&lt;/code&gt;, &lt;code data-end=&quot;3057&quot; data-start=&quot;3041&quot;&gt;get_thumbnails&lt;/code&gt;, &lt;code data-end=&quot;3075&quot; data-start=&quot;3059&quot;&gt;search_youtube&lt;/code&gt;, &lt;code data-end=&quot;3098&quot; data-start=&quot;3077&quot;&gt;get_trending_videos&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3204&quot; data-start=&quot;3101&quot;&gt;These functions return &lt;strong data-end=&quot;3146&quot; data-start=&quot;3124&quot;&gt;plain JSON/strings&lt;/strong&gt;, which keeps the glue simple and makes debugging obvious.&lt;/p&gt;
&lt;blockquote data-end=&quot;3362&quot; data-start=&quot;3206&quot;&gt;
&lt;p data-end=&quot;3362&quot; data-start=&quot;3208&quot;&gt;Source code can be found here:&amp;nbsp;&lt;/p&gt;&lt;a href=&quot;https://github.com/JordiCorbilla/langgraph-cookbook&quot;&gt;JordiCorbilla/langgraph-cookbook: langgraph-cookbook&lt;/a&gt;
&lt;/blockquote&gt;
&lt;h3 data-end=&quot;3394&quot; data-start=&quot;3364&quot;&gt;2) Two orchestration modes&lt;/h3&gt;
&lt;ul data-end=&quot;3667&quot; data-start=&quot;3396&quot;&gt;
&lt;li data-end=&quot;3532&quot; data-start=&quot;3396&quot;&gt;
&lt;p data-end=&quot;3446&quot; data-start=&quot;3398&quot;&gt;&lt;strong data-end=&quot;3413&quot; data-start=&quot;3398&quot;&gt;Fixed chain&lt;/strong&gt; for &lt;strong data-end=&quot;3445&quot; data-start=&quot;3418&quot;&gt;deterministic summarize&lt;/strong&gt;:&lt;/p&gt;
&lt;ol data-end=&quot;3532&quot; data-start=&quot;3449&quot;&gt;
&lt;li data-end=&quot;3532&quot; data-start=&quot;3449&quot;&gt;
&lt;p data-end=&quot;3532&quot; data-start=&quot;3452&quot;&gt;Extract ID → 2) Fetch transcript → 3) Summarize (chunked) → 4) Polish (bare LLM)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3667&quot; data-start=&quot;3533&quot;&gt;
&lt;p data-end=&quot;3586&quot; data-start=&quot;3535&quot;&gt;&lt;strong data-end=&quot;3566&quot; data-start=&quot;3535&quot;&gt;Universal (recursive) chain&lt;/strong&gt; for &lt;strong data-end=&quot;3585&quot; data-start=&quot;3571&quot;&gt;open “ask”&lt;/strong&gt;:&lt;/p&gt;
&lt;ul data-end=&quot;3667&quot; data-start=&quot;3589&quot;&gt;
&lt;li data-end=&quot;3667&quot; data-start=&quot;3589&quot;&gt;
&lt;p data-end=&quot;3667&quot; data-start=&quot;3591&quot;&gt;Keep calling tools while the model requests them. Stop when it returns text.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;3708&quot; data-start=&quot;3669&quot;&gt;3) Chunked map-reduce summarization&lt;/h3&gt;
&lt;p data-end=&quot;3918&quot; data-start=&quot;3710&quot;&gt;&lt;strong data-end=&quot;3718&quot; data-start=&quot;3710&quot;&gt;Why:&lt;/strong&gt; long transcripts blow up &lt;strong data-end=&quot;3755&quot; data-start=&quot;3744&quot;&gt;context&lt;/strong&gt; and &lt;strong data-end=&quot;3767&quot; data-start=&quot;3760&quot;&gt;TPM&lt;/strong&gt;.&lt;br data-end=&quot;3771&quot; data-start=&quot;3768&quot; /&gt;
&lt;strong data-end=&quot;3779&quot; data-start=&quot;3771&quot;&gt;How:&lt;/strong&gt; split the transcript into overlapping chunks → summarize each chunk (map) → merge the bullet lists (reduce) → optional polish for clarity.&lt;/p&gt;
&lt;p data-end=&quot;3933&quot; data-start=&quot;3920&quot;&gt;Key tunables:&lt;/p&gt;
&lt;ul data-end=&quot;4086&quot; data-start=&quot;3934&quot;&gt;
&lt;li data-end=&quot;3996&quot; data-start=&quot;3934&quot;&gt;
&lt;p data-end=&quot;3996&quot; data-start=&quot;3936&quot;&gt;Chunk size: &lt;strong data-end=&quot;3963&quot; data-start=&quot;3948&quot;&gt;3k–4k chars&lt;/strong&gt; (or larger if you have headroom)&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4025&quot; data-start=&quot;3997&quot;&gt;
&lt;p data-end=&quot;4025&quot; data-start=&quot;3999&quot;&gt;Overlap: &lt;strong data-end=&quot;4025&quot; data-start=&quot;4008&quot;&gt;200–300 chars&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4086&quot; data-start=&quot;4026&quot;&gt;
&lt;p data-end=&quot;4086&quot; data-start=&quot;4028&quot;&gt;Short &lt;code data-end=&quot;4041&quot; data-start=&quot;4034&quot;&gt;sleep&lt;/code&gt; between chunk calls to smooth &lt;strong data-end=&quot;4079&quot; data-start=&quot;4072&quot;&gt;TPM&lt;/strong&gt; bursts&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-end=&quot;4116&quot; data-start=&quot;4088&quot;&gt;4) Bare-LLM finalization&lt;/h3&gt;
&lt;p data-end=&quot;4320&quot; data-start=&quot;4118&quot;&gt;Tool-calling models often return &lt;strong data-end=&quot;4168&quot; data-start=&quot;4151&quot;&gt;empty content&lt;/strong&gt; when they want another tool. The final step uses a &lt;strong data-end=&quot;4232&quot; data-start=&quot;4220&quot;&gt;bare LLM&lt;/strong&gt; (no tools bound). That forces a textual answer and &lt;strong data-end=&quot;4319&quot; data-start=&quot;4284&quot;&gt;prevents “one more tool?” loops&lt;/strong&gt;.&lt;/p&gt;&lt;h2 data-end=&quot;3554&quot; data-start=&quot;3527&quot;&gt;Architecture at a glance&lt;/h2&gt;&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;4499&quot; data-start=&quot;3556&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre!&quot;&gt;┌──────────────┐      ┌───────────────────────┐
│  CLI (args)  ├────▶ |  Orchestrator Chain   │
└─────┬────────┘      └──────────┬────────────┘
      │                           │  tool_calls
      │                           ▼
      │                 ┌─────────────────────┐
      │                 │  Tools (LangChain)  │
      │                 │  - extract_video_id │
      │                 │  - fetch_transcript │
      │                 │  - search_youtube   │
      │                 │  - get_full_metadata│
      │                 │  - get_thumbnails   │
      │                 └──────────┬──────────┘
      │                            │ results (text/JSON)
      │                            ▼
      │                   ┌─────────────────┐
      │                   │  Bare LLM Call  │  ← no-tools finalization
      │                   │ (map→reduce→polish)
      ▼                   └─────────────────┘
  stdout (summary)
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;4320&quot; data-start=&quot;4118&quot;&gt;

&lt;/p&gt;&lt;ul data-end=&quot;4680&quot; data-start=&quot;4501&quot;&gt;
&lt;li data-end=&quot;4680&quot; data-start=&quot;4501&quot;&gt;
&lt;p data-end=&quot;4680&quot; data-start=&quot;4503&quot;&gt;&lt;strong data-end=&quot;4533&quot; data-start=&quot;4503&quot;&gt;Why “bare” LLM at the end?&lt;/strong&gt; Tool-calling models often return &lt;strong data-end=&quot;4586&quot; data-start=&quot;4567&quot;&gt;empty &lt;code data-end=&quot;4584&quot; data-start=&quot;4575&quot;&gt;content&lt;/code&gt;&lt;/strong&gt; when they still want to call tools. Finalizing with a &lt;strong data-end=&quot;4653&quot; data-start=&quot;4641&quot;&gt;no-tools&lt;/strong&gt; model guarantees &lt;strong data-end=&quot;4679&quot; data-start=&quot;4671&quot;&gt;text&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;hr data-end=&quot;4325&quot; data-start=&quot;4322&quot; /&gt;
&lt;h2 data-end=&quot;4353&quot; data-start=&quot;4327&quot;&gt;CLI you’ll actually use&lt;/h2&gt;
&lt;p data-end=&quot;4382&quot; data-start=&quot;4355&quot;&gt;Point tools (fast, no LLM):&lt;/p&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;4697&quot; data-start=&quot;4383&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-bash&quot;&gt;&lt;span&gt;&lt;span&gt;python youtube_tool_agent.py metadata   --url &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;https://youtu.be/8TJQhQ2GZ0Y&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;
python youtube_tool_agent.py thumbnails --url &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;https://youtu.be/8TJQhQ2GZ0Y&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;
python youtube_tool_agent.py search     --query &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;Retrieval-Augmented Generation&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;
python youtube_tool_agent.py trending   --region GB&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;p data-end=&quot;4730&quot; data-start=&quot;4699&quot;&gt;Deterministic, chunked summary:&lt;/p&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;4865&quot; data-start=&quot;4731&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-bash&quot;&gt;&lt;span&gt;&lt;span&gt;python youtube_tool_agent.py --verbose summarize \
  --url &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;https://www.youtube.com/watch?v=8TJQhQ2GZ0Y&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt; \
  --language en
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;p data-end=&quot;4894&quot; data-start=&quot;4867&quot;&gt;Agentic “do what it takes”:&lt;/p&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;5078&quot; data-start=&quot;4895&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-bash&quot;&gt;&lt;span&gt;&lt;span&gt;python youtube_tool_agent.py --verbose ask \
  --query &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;Summarize this YouTube video and explain the leveraging strategy (en): https://www.youtube.com/watch?v=8TJQhQ2GZ0Y&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;hr data-end=&quot;5083&quot; data-start=&quot;5080&quot; /&gt;
&lt;h2 data-end=&quot;5115&quot; data-start=&quot;5085&quot;&gt;Under the hood (high-level)&lt;/h2&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;5568&quot; data-start=&quot;5117&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre!&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;CLI&lt;/span&gt;&lt;/span&gt;&lt;span&gt; → &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;Orchestrator&lt;/span&gt;&lt;/span&gt;&lt;span&gt;
  ├─ &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;Fixed&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;chain&lt;/span&gt;&lt;/span&gt;&lt;span&gt; (summarize): &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;ID&lt;/span&gt;&lt;/span&gt;&lt;span&gt; → &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;transcript&lt;/span&gt;&lt;/span&gt;&lt;span&gt; → &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;map&lt;/span&gt;&lt;/span&gt;&lt;span&gt;→&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;reduce&lt;/span&gt;&lt;/span&gt;&lt;span&gt;→&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;polish&lt;/span&gt;&lt;/span&gt;&lt;span&gt; (bare LLM)
  └─ &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;Universal&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;chain&lt;/span&gt;&lt;/span&gt;&lt;span&gt; (ask): &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;recurse&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;while&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;the&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;model&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;returns&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;tool_calls&lt;/span&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;Tools&lt;/span&gt;&lt;/span&gt;&lt;span&gt; (LangChain &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-variable&quot;&gt;@tool&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)
  ├─ &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;extract_video_id&lt;/span&gt;&lt;/span&gt;&lt;span&gt;
  ├─ &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;fetch_transcript&lt;/span&gt;&lt;/span&gt;&lt;span&gt;  (YouTubeTranscriptAPI; optional yt-dlp captions fallback)
  ├─ &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;get_full_metadata&lt;/span&gt;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;get_thumbnails&lt;/span&gt;&lt;/span&gt;&lt;span&gt;
  ├─ &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;search_youtube&lt;/span&gt;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;get_trending_videos&lt;/span&gt;&lt;/span&gt;&lt;span&gt; (best-effort)
&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;Finalization&lt;/span&gt;&lt;/span&gt;&lt;span&gt;
  └─ &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;Bare&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;LLM&lt;/span&gt;&lt;/span&gt;&lt;span&gt; (no tools) → &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;guaranteed&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-selector-tag&quot;&gt;text&lt;/span&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;p data-end=&quot;5784&quot; data-start=&quot;5570&quot;&gt;&lt;strong data-end=&quot;5591&quot; data-start=&quot;5570&quot;&gt;Why the fallback?&lt;/strong&gt; In production you’ll meet videos with no official transcript. Add a &lt;code data-end=&quot;5668&quot; data-start=&quot;5660&quot;&gt;yt-dlp&lt;/code&gt; captions fallback and, as a last resort, summarize metadata/description so you &lt;strong data-end=&quot;5758&quot; data-start=&quot;5748&quot;&gt;always&lt;/strong&gt; produce something useful.&lt;/p&gt;
&lt;hr data-end=&quot;5789&quot; data-start=&quot;5786&quot; /&gt;
&lt;h2 data-end=&quot;5839&quot; data-start=&quot;5791&quot;&gt;Operational tips (so it stays fast and cheap)&lt;/h2&gt;
&lt;ul data-end=&quot;7470&quot; data-start=&quot;6950&quot;&gt;
&lt;li data-end=&quot;5992&quot; data-start=&quot;5841&quot;&gt;
&lt;p data-end=&quot;5992&quot; data-start=&quot;5843&quot;&gt;&lt;strong data-end=&quot;5862&quot; data-start=&quot;5843&quot;&gt;TPM discipline:&lt;/strong&gt; throttle chunk calls with short sleeps; prefer &lt;strong data-end=&quot;5928&quot; data-start=&quot;5910&quot;&gt;smaller models&lt;/strong&gt; for map steps, then a slightly stronger model for merge/polish.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;6138&quot; data-start=&quot;5993&quot;&gt;
&lt;p data-end=&quot;6138&quot; data-start=&quot;5995&quot;&gt;&lt;strong data-end=&quot;6023&quot; data-start=&quot;5995&quot;&gt;Chapters-aware chunking:&lt;/strong&gt; if &lt;code data-end=&quot;6035&quot; data-start=&quot;6027&quot;&gt;yt-dlp&lt;/code&gt; returns chapters, chunk on &lt;strong data-end=&quot;6085&quot; data-start=&quot;6063&quot;&gt;chapter boundaries&lt;/strong&gt; first—better topical coherence and fewer duplicates.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;6251&quot; data-start=&quot;6139&quot;&gt;
&lt;p data-end=&quot;6251&quot; data-start=&quot;6141&quot;&gt;&lt;strong data-end=&quot;6150&quot; data-start=&quot;6141&quot;&gt;Cache&lt;/strong&gt; transcripts/metadata to disk (simple JSON files). You’ll save both &lt;strong data-end=&quot;6226&quot; data-start=&quot;6218&quot;&gt;time&lt;/strong&gt; and &lt;strong data-end=&quot;6240&quot; data-start=&quot;6231&quot;&gt;money&lt;/strong&gt; on reruns.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;6345&quot; data-start=&quot;6252&quot;&gt;
&lt;p data-end=&quot;6345&quot; data-start=&quot;6254&quot;&gt;&lt;strong data-end=&quot;6272&quot; data-start=&quot;6254&quot;&gt;Observability:&lt;/strong&gt; log tool names and &lt;strong data-end=&quot;6309&quot; data-start=&quot;6292&quot;&gt;payload sizes&lt;/strong&gt;. When costs drift, you’ll know why.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;7475&quot; data-start=&quot;7472&quot; /&gt;
&lt;h2 data-end=&quot;7507&quot; data-start=&quot;7477&quot;&gt;&lt;/h2&gt;
&lt;h2 data-end=&quot;8143&quot; data-start=&quot;8124&quot;&gt;Closing thoughts&lt;/h2&gt;
&lt;p data-end=&quot;8463&quot; data-start=&quot;8145&quot;&gt;This is a &lt;strong data-end=&quot;8163&quot; data-start=&quot;8155&quot;&gt;tool&lt;/strong&gt; designed for the messiness of real YouTube content. The combination of &lt;strong data-end=&quot;8251&quot; data-start=&quot;8235&quot;&gt;tool calling&lt;/strong&gt;, a minimal &lt;strong data-end=&quot;8277&quot; data-start=&quot;8263&quot;&gt;agent loop&lt;/strong&gt;, &lt;strong data-end=&quot;8304&quot; data-start=&quot;8279&quot;&gt;chunked summarization&lt;/strong&gt;, and &lt;strong data-end=&quot;8335&quot; data-start=&quot;8310&quot;&gt;bare-LLM finalization&lt;/strong&gt; makes it predictable, debuggable, and resilient. You can drop it into a pipeline today and get reliable results on long videos.&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2025/09/a-practical-production-ready-tool-for.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgF1o_DfQwCZlI9sxzkHy3dpf97E_J4dmJQiePAiWVxtZDoqVsYVdxOoEgaB6_02ukpe_lZDBFAEY2m9KDBXjUsDS0PqTuDsuobkyMmXsV1oOBtssw32A5NZnWz3oUCwR43Kn7GahuG_86ohEcPc2xIhOlAM1PVOClcaZJp5iIg5uTeJEfec4NJbLh2LSc/s72-c/ChatGPT%20Image%20Sep%2013,%202025,%2001_20_50%20PM.png" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>United Kingdom</georss:featurename><georss:point>55.378051 -3.435973</georss:point><georss:box>27.067817163821154 -38.592223 83.688284836178838 31.720277</georss:box></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-1683967296355320681</guid><pubDate>Sun, 31 Aug 2025 20:17:00 +0000</pubDate><atom:updated>2025-08-31T20:17:08.843+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AI Agents</category><category domain="http://www.blogger.com/atom/ns#">Artificial Intelligence</category><category domain="http://www.blogger.com/atom/ns#">Data Analysis</category><category domain="http://www.blogger.com/atom/ns#">Data Science</category><category domain="http://www.blogger.com/atom/ns#">Data Visualization</category><category domain="http://www.blogger.com/atom/ns#">Generative AI</category><category domain="http://www.blogger.com/atom/ns#">LangChain</category><category domain="http://www.blogger.com/atom/ns#">llm</category><category domain="http://www.blogger.com/atom/ns#">Machine Learning</category><category domain="http://www.blogger.com/atom/ns#">Matplotlib</category><category domain="http://www.blogger.com/atom/ns#">openAI</category><category domain="http://www.blogger.com/atom/ns#">Pandas Agent</category><category domain="http://www.blogger.com/atom/ns#">Prompt</category><category domain="http://www.blogger.com/atom/ns#">Python</category><category domain="http://www.blogger.com/atom/ns#">RAG</category><category domain="http://www.blogger.com/atom/ns#">Retrieval Augmented Generation</category><title>Querying and Plotting Data with LangChain’s Pandas Agent + OpenAI</title><description>&lt;h1 data-end=&quot;225&quot; data-start=&quot;158&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJMe4Gyqi7yGzZvvxjqCT22C4W9JSdTqrAGQxlvIg-GxB9rmSPkXO0-WB3LtEklawcmSEsQs-2QBLLDPCPUSBd1-dx2PWjmbxbsebcGZ4F0Eh7_VZ6H5ZnCPT3shSw4pus6_b4llSFNa1Faxs8VV2syL3LOVk_Ke9yCtQ9mkzx0FZlSqaEjcJA4CjAOJM/s1024/ChatGPT%20Image%20Aug%2031,%202025,%2009_13_33%20PM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1024&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJMe4Gyqi7yGzZvvxjqCT22C4W9JSdTqrAGQxlvIg-GxB9rmSPkXO0-WB3LtEklawcmSEsQs-2QBLLDPCPUSBd1-dx2PWjmbxbsebcGZ4F0Eh7_VZ6H5ZnCPT3shSw4pus6_b4llSFNa1Faxs8VV2syL3LOVk_Ke9yCtQ9mkzx0FZlSqaEjcJA4CjAOJM/s320/ChatGPT%20Image%20Aug%2031,%202025,%2009_13_33%20PM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;Querying and Plotting Data with LangChain’s Pandas Agent + OpenAI&lt;/h1&gt;&lt;p data-end=&quot;553&quot; data-start=&quot;227&quot;&gt;Natural language interfaces for data analysis are moving from research into everyday engineering practice.&lt;br data-end=&quot;336&quot; data-start=&quot;333&quot; /&gt;
With &lt;a class=&quot;decorated-link cursor-pointer&quot; data-end=&quot;383&quot; data-start=&quot;341&quot; rel=&quot;noopener&quot; target=&quot;_new&quot;&gt;LangChain&amp;nbsp;&lt;/a&gt;and OpenAI, you can now &lt;strong data-end=&quot;449&quot; data-start=&quot;408&quot;&gt;query a DataFrame directly in English&lt;/strong&gt;, let the model write and execute the Pandas/Matplotlib code, and even plot results, all inside Python.&lt;/p&gt;&lt;p data-end=&quot;659&quot; data-start=&quot;555&quot;&gt;In this post, I’ll show you how I built a &lt;strong data-end=&quot;623&quot; data-start=&quot;597&quot;&gt;Pandas DataFrame Agent&lt;/strong&gt; with LangChain + OpenAI that can:&lt;/p&gt;&lt;ul data-end=&quot;802&quot; data-start=&quot;660&quot;&gt;
&lt;li data-end=&quot;705&quot; data-start=&quot;660&quot;&gt;
&lt;p data-end=&quot;705&quot; data-start=&quot;662&quot;&gt;Answer tabular questions about a dataset.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;754&quot; data-start=&quot;706&quot;&gt;
&lt;p data-end=&quot;754&quot; data-start=&quot;708&quot;&gt;Generate Python plotting code automatically.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;802&quot; data-start=&quot;755&quot;&gt;
&lt;p data-end=&quot;802&quot; data-start=&quot;757&quot;&gt;Execute that code safely to produce charts.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;hr data-end=&quot;807&quot; data-start=&quot;804&quot; /&gt;&lt;h2 data-end=&quot;820&quot; data-start=&quot;809&quot;&gt;🔧 Setup&lt;/h2&gt;&lt;p data-end=&quot;845&quot; data-start=&quot;822&quot;&gt;We need a few packages:&lt;/p&gt;&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;938&quot; data-start=&quot;847&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-bash&quot;&gt;pip install langchain langchain-openai langchain-experimental pandas matplotlib
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;975&quot; data-start=&quot;940&quot;&gt;And of course, set your OpenAI key:&lt;/p&gt;&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;1017&quot; data-start=&quot;977&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-bash&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;export&lt;/span&gt; OPENAI_API_KEY=...
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;hr data-end=&quot;1022&quot; data-start=&quot;1019&quot; /&gt;&lt;h2 data-end=&quot;1041&quot; data-start=&quot;1024&quot;&gt;📊 The Dataset&lt;/h2&gt;&lt;p data-end=&quot;1092&quot; data-start=&quot;1043&quot;&gt;For demo purposes, let’s mock up some sales data:&lt;/p&gt;&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;1438&quot; data-start=&quot;1094&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; pandas &lt;span class=&quot;hljs-keyword&quot;&gt;as&lt;/span&gt; pd

data = {
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;month&quot;&lt;/span&gt;: [&lt;span class=&quot;hljs-string&quot;&gt;&quot;2025-01&quot;&lt;/span&gt;,&lt;span class=&quot;hljs-string&quot;&gt;&quot;2025-02&quot;&lt;/span&gt;,&lt;span class=&quot;hljs-string&quot;&gt;&quot;2025-03&quot;&lt;/span&gt;,&lt;span class=&quot;hljs-string&quot;&gt;&quot;2025-04&quot;&lt;/span&gt;,&lt;span class=&quot;hljs-string&quot;&gt;&quot;2025-05&quot;&lt;/span&gt;,&lt;span class=&quot;hljs-string&quot;&gt;&quot;2025-06&quot;&lt;/span&gt;],
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;region&quot;&lt;/span&gt;: [&lt;span class=&quot;hljs-string&quot;&gt;&quot;EMEA&quot;&lt;/span&gt;,&lt;span class=&quot;hljs-string&quot;&gt;&quot;EMEA&quot;&lt;/span&gt;,&lt;span class=&quot;hljs-string&quot;&gt;&quot;EMEA&quot;&lt;/span&gt;,&lt;span class=&quot;hljs-string&quot;&gt;&quot;AMER&quot;&lt;/span&gt;,&lt;span class=&quot;hljs-string&quot;&gt;&quot;AMER&quot;&lt;/span&gt;,&lt;span class=&quot;hljs-string&quot;&gt;&quot;APAC&quot;&lt;/span&gt;],
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;units&quot;&lt;/span&gt;:  [&lt;span class=&quot;hljs-number&quot;&gt;120&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;150&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;130&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;200&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;180&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;160&lt;/span&gt;],
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;price&quot;&lt;/span&gt;:  [&lt;span class=&quot;hljs-number&quot;&gt;10.0&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;10.0&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;10.0&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;12.0&lt;/span&gt;, &lt;span class=&quot;hljs-number&quot;&gt;12.0&lt;/span&gt;,  &lt;span class=&quot;hljs-number&quot;&gt;9.0&lt;/span&gt;],
}
df = pd.DataFrame(data)
df[&lt;span class=&quot;hljs-string&quot;&gt;&quot;revenue&quot;&lt;/span&gt;] = df[&lt;span class=&quot;hljs-string&quot;&gt;&quot;units&quot;&lt;/span&gt;] * df[&lt;span class=&quot;hljs-string&quot;&gt;&quot;price&quot;&lt;/span&gt;]
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;hr data-end=&quot;1443&quot; data-start=&quot;1440&quot; /&gt;&lt;h2 data-end=&quot;1469&quot; data-start=&quot;1445&quot;&gt;🤖 Creating the Agent&lt;/h2&gt;&lt;p data-end=&quot;1599&quot; data-start=&quot;1471&quot;&gt;The magic comes from &lt;code data-end=&quot;1523&quot; data-start=&quot;1492&quot;&gt;create_pandas_dataframe_agent&lt;/code&gt;.&lt;br data-end=&quot;1527&quot; data-start=&quot;1524&quot; /&gt;
This wraps the DataFrame in a tool the LLM can call with code execution:&lt;/p&gt;&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;1927&quot; data-start=&quot;1601&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;from&lt;/span&gt; langchain_openai &lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; ChatOpenAI
&lt;span class=&quot;hljs-keyword&quot;&gt;from&lt;/span&gt; langchain_experimental.agents.agent_toolkits &lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; create_pandas_dataframe_agent

llm = ChatOpenAI(model=&lt;span class=&quot;hljs-string&quot;&gt;&quot;gpt-4o-mini&quot;&lt;/span&gt;, temperature=&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;)

agent = create_pandas_dataframe_agent(
    llm=llm,
    df=df,
    agent_type=&lt;span class=&quot;hljs-string&quot;&gt;&quot;openai-tools&quot;&lt;/span&gt;,
    verbose=&lt;span class=&quot;hljs-literal&quot;&gt;False&lt;/span&gt;,  &lt;span class=&quot;hljs-comment&quot;&gt;# cleaner logs&lt;/span&gt;
)
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;hr data-end=&quot;1932&quot; data-start=&quot;1929&quot; /&gt;&lt;h2 data-end=&quot;1967&quot; data-start=&quot;1934&quot;&gt;🔎 Asking Questions in English&lt;/h2&gt;&lt;p data-end=&quot;2020&quot; data-start=&quot;1969&quot;&gt;Now you can query the data without touching Pandas:&lt;/p&gt;&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;2164&quot; data-start=&quot;2022&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;resp = agent.invoke({
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;input&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;Compute total revenue by region as a tidy table sorted descending.&quot;&lt;/span&gt;
})
&lt;span class=&quot;hljs-built_in&quot;&gt;print&lt;/span&gt;(resp[&lt;span class=&quot;hljs-string&quot;&gt;&quot;output&quot;&lt;/span&gt;])
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;2173&quot; data-start=&quot;2166&quot;&gt;Output:&lt;/p&gt;&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;2287&quot; data-start=&quot;2175&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre!&quot;&gt;&lt;span class=&quot;hljs-string&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;region&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;revenue&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;hljs-string&quot;&gt;|--------|---------|&lt;/span&gt;
&lt;span class=&quot;hljs-string&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;AMER&lt;/span&gt;   &lt;span class=&quot;hljs-string&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;4560.0&lt;/span&gt;  &lt;span class=&quot;hljs-string&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;hljs-string&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;EMEA&lt;/span&gt;   &lt;span class=&quot;hljs-string&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;4000.0&lt;/span&gt;  &lt;span class=&quot;hljs-string&quot;&gt;|&lt;/span&gt;
&lt;span class=&quot;hljs-string&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;APAC&lt;/span&gt;   &lt;span class=&quot;hljs-string&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;1440.0&lt;/span&gt;  &lt;span class=&quot;hljs-string&quot;&gt;|&lt;/span&gt;
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;2353&quot; data-start=&quot;2289&quot;&gt;The agent generated and executed the Pandas code under the hood.&lt;/p&gt;&lt;hr data-end=&quot;2358&quot; data-start=&quot;2355&quot; /&gt;&lt;h2 data-end=&quot;2383&quot; data-start=&quot;2360&quot;&gt;📈 Asking for a Plot&lt;/h2&gt;&lt;p data-end=&quot;2456&quot; data-start=&quot;2385&quot;&gt;We can go one step further — ask the model to &lt;strong data-end=&quot;2455&quot; data-start=&quot;2431&quot;&gt;plot monthly revenue&lt;/strong&gt;:&lt;/p&gt;&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;3080&quot; data-start=&quot;2458&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;plot_code = &lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&quot;
import pandas as pd
import matplotlib.pyplot as plt
from pathlib import Path

df[&#39;month&#39;] = pd.to_datetime(df[&#39;month&#39;])
monthly_revenue = df.groupby(&#39;month&#39;)[&#39;revenue&#39;].sum()

plt.figure(figsize=(10, 5))
plt.plot(monthly_revenue.index, monthly_revenue.values, marker=&#39;o&#39;)
plt.title(&#39;Monthly Revenue Over Time&#39;)
plt.xlabel(&#39;Month&#39;)
plt.ylabel(&#39;Total Revenue&#39;)
plt.grid()
plt.xticks(rotation=45)
plt.tight_layout()

out = Path(&#39;monthly_revenue.png&#39;)
plt.savefig(out)
print(f&#39;Chart saved to: {out.resolve()}&#39;)
&quot;&quot;&quot;&lt;/span&gt;
agent.invoke({&lt;span class=&quot;hljs-string&quot;&gt;&quot;input&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;f&quot;Execute this exact code:\n```python\n&lt;span class=&quot;hljs-subst&quot;&gt;{plot_code}&lt;/span&gt;&lt;/span&gt;\n```&quot;})
&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;3096&quot; data-start=&quot;3082&quot;&gt;This produces:&lt;/p&gt;&lt;p data-end=&quot;3143&quot; data-start=&quot;3098&quot;&gt;&lt;/p&gt;&lt;div&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;hr data-end=&quot;3148&quot; data-start=&quot;3145&quot; /&gt;&lt;br /&gt;&lt;h2 data-end=&quot;3556&quot; data-start=&quot;3534&quot;&gt;🚀 Why This Matters&lt;/h2&gt;&lt;p data-end=&quot;3581&quot; data-start=&quot;3558&quot;&gt;This pattern unlocks:&lt;/p&gt;&lt;ul data-end=&quot;3843&quot; data-start=&quot;3582&quot;&gt;
&lt;li data-end=&quot;3658&quot; data-start=&quot;3582&quot;&gt;
&lt;p data-end=&quot;3658&quot; data-start=&quot;3584&quot;&gt;Rapid &lt;strong data-end=&quot;3610&quot; data-start=&quot;3590&quot;&gt;ad-hoc analytics&lt;/strong&gt;: Ask questions in English, get code + charts.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3727&quot; data-start=&quot;3659&quot;&gt;
&lt;p data-end=&quot;3727&quot; data-start=&quot;3661&quot;&gt;&lt;strong data-end=&quot;3684&quot; data-start=&quot;3661&quot;&gt;Non-technical users&lt;/strong&gt; can explore data without writing Pandas.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3843&quot; data-start=&quot;3728&quot;&gt;
&lt;p data-end=&quot;3843&quot; data-start=&quot;3730&quot;&gt;Bridges &lt;strong data-end=&quot;3757&quot; data-start=&quot;3738&quot;&gt;RAG + analytics&lt;/strong&gt;: Instead of only text retrieval, you can augment LLMs with structured data queries.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;





































&lt;/p&gt;&lt;p data-end=&quot;4026&quot; data-start=&quot;3845&quot;&gt;Of course, for production you’d want &lt;strong data-end=&quot;3896&quot; data-start=&quot;3882&quot;&gt;guardrails&lt;/strong&gt; (e.g., AST linting before execution, schema checks, sandboxing). But as a prototyping tool, this workflow is incredibly powerful.&lt;/p&gt;&lt;p data-end=&quot;4026&quot; data-start=&quot;3845&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p data-end=&quot;4026&quot; data-start=&quot;3845&quot;&gt;Source code can be found here:&amp;nbsp;&lt;a href=&quot;https://github.com/JordiCorbilla/langgraph-cookbook&quot;&gt;JordiCorbilla/langgraph-cookbook: langgraph-cookbook&lt;/a&gt;&lt;/p&gt;</description><link>https://thundaxsoftware.blogspot.com/2025/08/querying-and-plotting-data-with.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJMe4Gyqi7yGzZvvxjqCT22C4W9JSdTqrAGQxlvIg-GxB9rmSPkXO0-WB3LtEklawcmSEsQs-2QBLLDPCPUSBd1-dx2PWjmbxbsebcGZ4F0Eh7_VZ6H5ZnCPT3shSw4pus6_b4llSFNa1Faxs8VV2syL3LOVk_Ke9yCtQ9mkzx0FZlSqaEjcJA4CjAOJM/s72-c/ChatGPT%20Image%20Aug%2031,%202025,%2009_13_33%20PM.png" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>London, UK</georss:featurename><georss:point>51.5072178 -0.1275862</georss:point><georss:box>23.196983963821154 -35.2838362 79.817451636178845 35.0286638</georss:box></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-2016849911793834103</guid><pubDate>Sun, 31 Aug 2025 19:57:00 +0000</pubDate><atom:updated>2025-08-31T20:17:46.460+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AgenticAI</category><category domain="http://www.blogger.com/atom/ns#">AIAgents</category><category domain="http://www.blogger.com/atom/ns#">GenerativeAI</category><category domain="http://www.blogger.com/atom/ns#">GraphBasedWorkflows</category><category domain="http://www.blogger.com/atom/ns#">LangChain</category><category domain="http://www.blogger.com/atom/ns#">LangGraph</category><category domain="http://www.blogger.com/atom/ns#">openAI</category><category domain="http://www.blogger.com/atom/ns#">Pydantic</category><category domain="http://www.blogger.com/atom/ns#">Python</category><category domain="http://www.blogger.com/atom/ns#">RAG</category><category domain="http://www.blogger.com/atom/ns#">ToolsAndOrchestration</category><title>Building a Tiny LangGraph Agent with a Tool: A Weather-Aware Trip Planner</title><description>&lt;h1 data-end=&quot;392&quot; data-start=&quot;317&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcrYJdRvDeC2ZuKf4ltRhE7s2ksZButKFHFmB_Tw5D-Qr-n3a-OJ7ld3QXGIp4pxi2gZFYvNsWqC_tdgm1CN3dkr7NnzhFpGjDEC9tbbsZ9XTPdPvIqwJXd0M62Qbzs0JMAL83l8OANq9v8frFMvBvJJ1UySHklT8tRP86JpwMdDaxa5F7cphqrDf1rQk/s1024/ChatGPT%20Image%20Aug%2031,%202025,%2008_48_17%20PM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1024&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcrYJdRvDeC2ZuKf4ltRhE7s2ksZButKFHFmB_Tw5D-Qr-n3a-OJ7ld3QXGIp4pxi2gZFYvNsWqC_tdgm1CN3dkr7NnzhFpGjDEC9tbbsZ9XTPdPvIqwJXd0M62Qbzs0JMAL83l8OANq9v8frFMvBvJJ1UySHklT8tRP86JpwMdDaxa5F7cphqrDf1rQk/s320/ChatGPT%20Image%20Aug%2031,%202025,%2008_48_17%20PM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;Building a Tiny LangGraph Agent with a Tool: A Weather-Aware Trip Planner&lt;/h1&gt;
&lt;p data-end=&quot;686&quot; data-start=&quot;394&quot;&gt;Most agent demos are either too simple (toy calculators) or too complex (enterprise-scale pipelines).&lt;br data-end=&quot;498&quot; data-start=&quot;495&quot; /&gt;
Here’s a &lt;strong data-end=&quot;524&quot; data-start=&quot;507&quot;&gt;middle ground&lt;/strong&gt;: a &lt;strong data-end=&quot;554&quot; data-start=&quot;528&quot;&gt;5-node LangGraph agent&lt;/strong&gt; that builds a half-day itinerary for a city, adapts to weather, respects user preferences, and &lt;strong data-end=&quot;671&quot; data-start=&quot;650&quot;&gt;calls a real tool&lt;/strong&gt; along the way.&lt;/p&gt;
&lt;hr data-end=&quot;691&quot; data-start=&quot;688&quot; /&gt;
&lt;h2 data-end=&quot;713&quot; data-start=&quot;693&quot;&gt;Why this example?&lt;/h2&gt;
&lt;ul data-end=&quot;1129&quot; data-start=&quot;715&quot;&gt;
&lt;li data-end=&quot;799&quot; data-start=&quot;715&quot;&gt;
&lt;p data-end=&quot;799&quot; data-start=&quot;717&quot;&gt;&lt;strong data-end=&quot;746&quot; data-start=&quot;717&quot;&gt;Small, visualizable graph&lt;/strong&gt;: &lt;code data-end=&quot;799&quot; data-start=&quot;748&quot;&gt;plan → weather → search → pick → draft → finalize&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;870&quot; data-start=&quot;800&quot;&gt;
&lt;p data-end=&quot;870&quot; data-start=&quot;802&quot;&gt;&lt;strong data-end=&quot;825&quot; data-start=&quot;802&quot;&gt;Conditional routing&lt;/strong&gt;: rainy → indoor picks, sunny → outdoor picks&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;964&quot; data-start=&quot;871&quot;&gt;
&lt;p data-end=&quot;964&quot; data-start=&quot;873&quot;&gt;&lt;strong data-end=&quot;892&quot; data-start=&quot;873&quot;&gt;Real tool usage&lt;/strong&gt;: &lt;code data-end=&quot;910&quot; data-start=&quot;894&quot;&gt;random_weather&lt;/code&gt; is a LangChain tool, called explicitly from the graph&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;999&quot; data-start=&quot;965&quot;&gt;
&lt;p data-end=&quot;999&quot; data-start=&quot;967&quot;&gt;&lt;strong data-end=&quot;982&quot; data-start=&quot;967&quot;&gt;Typed state&lt;/strong&gt; with Pydantic v2&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1062&quot; data-start=&quot;1000&quot;&gt;
&lt;p data-end=&quot;1062&quot; data-start=&quot;1002&quot;&gt;&lt;strong data-end=&quot;1018&quot; data-start=&quot;1002&quot;&gt;Optional LLM&lt;/strong&gt; for nicer itinerary text (but not required), this example used OpenAI&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1129&quot; data-start=&quot;1063&quot;&gt;
&lt;p data-end=&quot;1129&quot; data-start=&quot;1065&quot;&gt;&lt;strong data-end=&quot;1091&quot; data-start=&quot;1065&quot;&gt;Two runnable scenarios&lt;/strong&gt;: London (rainy) and Barcelona (sunny)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1134&quot; data-start=&quot;1131&quot; /&gt;
&lt;h2 data-end=&quot;1151&quot; data-start=&quot;1136&quot;&gt;Architecture&lt;/h2&gt;
&lt;p data-end=&quot;1230&quot; data-start=&quot;1153&quot;&gt;&lt;/p&gt;&lt;div&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;
&lt;ul data-end=&quot;1685&quot; data-start=&quot;1232&quot;&gt;
&lt;li data-end=&quot;1543&quot; data-start=&quot;1232&quot;&gt;
&lt;p data-end=&quot;1245&quot; data-start=&quot;1234&quot;&gt;&lt;strong data-end=&quot;1243&quot; data-start=&quot;1234&quot;&gt;Nodes&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-end=&quot;1543&quot; data-start=&quot;1248&quot;&gt;
&lt;li data-end=&quot;1279&quot; data-start=&quot;1248&quot;&gt;
&lt;p data-end=&quot;1279&quot; data-start=&quot;1250&quot;&gt;&lt;code data-end=&quot;1256&quot; data-start=&quot;1250&quot;&gt;plan&lt;/code&gt;: log initial context&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1340&quot; data-start=&quot;1282&quot;&gt;
&lt;p data-end=&quot;1340&quot; data-start=&quot;1284&quot;&gt;&lt;code data-end=&quot;1293&quot; data-start=&quot;1284&quot;&gt;weather&lt;/code&gt;: calls &lt;code data-end=&quot;1317&quot; data-start=&quot;1301&quot;&gt;random_weather&lt;/code&gt; tool → Weather state&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1373&quot; data-start=&quot;1343&quot;&gt;
&lt;p data-end=&quot;1373&quot; data-start=&quot;1345&quot;&gt;&lt;code data-end=&quot;1353&quot; data-start=&quot;1345&quot;&gt;search&lt;/code&gt;: load city places&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1432&quot; data-start=&quot;1376&quot;&gt;
&lt;p data-end=&quot;1432&quot; data-start=&quot;1378&quot;&gt;&lt;code data-end=&quot;1384&quot; data-start=&quot;1378&quot;&gt;pick&lt;/code&gt;: filter by weather &amp;amp; prefs (budget, dislikes)&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1471&quot; data-start=&quot;1435&quot;&gt;
&lt;p data-end=&quot;1471&quot; data-start=&quot;1437&quot;&gt;&lt;code data-end=&quot;1444&quot; data-start=&quot;1437&quot;&gt;draft&lt;/code&gt;: generate itinerary text&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1519&quot; data-start=&quot;1474&quot;&gt;
&lt;p data-end=&quot;1519&quot; data-start=&quot;1476&quot;&gt;&lt;code data-end=&quot;1486&quot; data-start=&quot;1476&quot;&gt;fallback&lt;/code&gt;: relax prefs if nothing chosen&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1543&quot; data-start=&quot;1522&quot;&gt;
&lt;p data-end=&quot;1543&quot; data-start=&quot;1524&quot;&gt;&lt;code data-end=&quot;1534&quot; data-start=&quot;1524&quot;&gt;finalize&lt;/code&gt;: wrap up&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1685&quot; data-start=&quot;1545&quot;&gt;
&lt;p data-end=&quot;1558&quot; data-start=&quot;1547&quot;&gt;&lt;strong data-end=&quot;1556&quot; data-start=&quot;1547&quot;&gt;Edges&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-end=&quot;1685&quot; data-start=&quot;1561&quot;&gt;
&lt;li data-end=&quot;1629&quot; data-start=&quot;1561&quot;&gt;
&lt;p data-end=&quot;1629&quot; data-start=&quot;1563&quot;&gt;Normal path: &lt;code data-end=&quot;1627&quot; data-start=&quot;1576&quot;&gt;plan → weather → search → pick → draft → finalize&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1685&quot; data-start=&quot;1632&quot;&gt;
&lt;p data-end=&quot;1685&quot; data-start=&quot;1634&quot;&gt;Fallback path: &lt;code data-end=&quot;1685&quot; data-start=&quot;1649&quot;&gt;pick → fallback → draft → finalize&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1690&quot; data-start=&quot;1687&quot; /&gt;
&lt;h2 data-end=&quot;1711&quot; data-start=&quot;1692&quot;&gt;The Weather Tool&lt;/h2&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;2167&quot; data-start=&quot;1713&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;from&lt;/span&gt;&lt;/span&gt;&lt;span&gt; langchain_core.tools &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt;&lt;/span&gt;&lt;span&gt; tool
&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt;&lt;/span&gt;&lt;span&gt; random

&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-meta&quot;&gt;@tool&lt;/span&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-title function_&quot;&gt;random_weather&lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-params&quot;&gt;city: &lt;span class=&quot;hljs-built_in&quot;&gt;str&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;) -&amp;gt; &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;dict&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:
    &lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&quot;
    Return randomized weather for the city.
    &quot;&quot;&quot;&lt;/span&gt;
    conditions = [&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;sunny&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;cloudy&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;rain&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;storm&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;]
    cond = random.choice(conditions)
    low, high = (&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-number&quot;&gt;12&lt;/span&gt;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-number&quot;&gt;30&lt;/span&gt;&lt;/span&gt;&lt;span&gt;)
    &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;&lt;/span&gt;&lt;span&gt; {
        &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;: city,
        &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;condition&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;: cond,
        &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;high_c&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;float&lt;/span&gt;&lt;/span&gt;&lt;span&gt;(random.randint(&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-number&quot;&gt;20&lt;/span&gt;&lt;/span&gt;&lt;span&gt;, high)),
        &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;low_c&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;float&lt;/span&gt;&lt;/span&gt;&lt;span&gt;(random.randint(low, &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-number&quot;&gt;19&lt;/span&gt;&lt;/span&gt;&lt;span&gt;))
    }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;p data-end=&quot;2195&quot; data-start=&quot;2169&quot;&gt;And in the &lt;code data-end=&quot;2194&quot; data-start=&quot;2180&quot;&gt;weather_node&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;2496&quot; data-start=&quot;2197&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-title function_&quot;&gt;weather_node&lt;/span&gt;&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-params&quot;&gt;s: GraphState&lt;/span&gt;&lt;/span&gt;&lt;span&gt;) -&amp;gt; GraphState:
    w = random_weather.invoke({&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;city&quot;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;: s.city})  &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# tool call&lt;/span&gt;&lt;/span&gt;&lt;span&gt;
    s.weather = Weather(**w)
    s.audit.append(
        &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-string&quot;&gt;f&quot;weather: &lt;span class=&quot;hljs-subst&quot;&gt;{s.weather.city}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-subst&quot;&gt;{s.weather.condition}&lt;/span&gt;&lt;/span&gt;&lt;span&gt; &quot;
        &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-string&quot;&gt;f&quot;&lt;span class=&quot;hljs-subst&quot;&gt;{s.weather.high_c}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-subst&quot;&gt;{s.weather.low_c}&lt;/span&gt;&lt;/span&gt;&lt;span&gt;°C&quot;
    )
    &lt;/span&gt;&lt;span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt;&lt;/span&gt;&lt;span&gt; s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-python&quot;&gt;&lt;span&gt;&lt;span&gt;Full code can be found here -&amp;gt; &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;a href=&quot;https://github.com/JordiCorbilla/langgraph-cookbook&quot;&gt;JordiCorbilla/langgraph-cookbook: langgraph-cookbook&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;hr data-end=&quot;2501&quot; data-start=&quot;2498&quot; /&gt;
&lt;h2 data-end=&quot;2517&quot; data-start=&quot;2503&quot;&gt;Example Run&lt;/h2&gt;
&lt;h3 data-end=&quot;2577&quot; data-start=&quot;2519&quot;&gt;Scenario A: London, dislikes museums, free-only budget&lt;/h3&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;2932&quot; data-start=&quot;2579&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre!&quot;&gt;&lt;span&gt;&lt;span&gt;=== Scenario A: London + dislikes=[&#39;museum&#39;], free_only ===
[Node] plan
[Node] weather
[Tool] random_weather(London) -&amp;gt; {&#39;city&#39;: &#39;London&#39;, &#39;condition&#39;: &#39;sunny&#39;, &#39;high_c&#39;: 30.0, &#39;low_c&#39;: 28.0}
[Node] search
[Node] pick
[Node] draft
[Node] finalize
plan: city=London, half_day=morning, date=2025-08-31
weather: London sunny 30.0/28.0°C
search: 5 candidates
pick: Thames Riverside Walk, Hyde Park Loop, Sky Garden
draft: itinerary ready
finalize: done

Itinerary:
 - **Time:** 9:00 AM - 10:00 AM
 - **Tip:** Begin your day with a refreshing stroll along the river. Enjoy the views and the morning sun. Bring water and wear sunscreen!
 - **Transport:** Take the Tube from Embankment Station to Lancaster Gate (Circle or District Line).
 - **Tip:** This journey takes about 15 minutes. Consider grabbing a coffee from a nearby café before heading to the park.
 - **Time:** 10:15 AM - 11:30 AM
 - **Tip:** Enjoy the beautiful greenery and perhaps find a shaded spot to relax. Look out for the Serpentine Lake and the Diana Memorial Fountain.
 - **Transport:** Take the Tube from Lancaster Gate to Monument Station (Circle or District Line).
 - **Tip:** Allocate about 30 minutes for travel and check-in. Arrive by 11:45 AM to enjoy the stunning views of London.
 - **Time:** 12:00 PM - 12:45 PM
 - **Tip:** Enjoy a drink or snack at the café while taking in panoramic views of the city. Make sure to book your free entry in advance to avoid long waits!
Notes: Weather: sunny&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;p data-end=&quot;2951&quot; data-start=&quot;2934&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-end=&quot;3004&quot; data-start=&quot;2953&quot;&gt;&lt;/p&gt;&lt;div&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;
&lt;hr data-end=&quot;3009&quot; data-start=&quot;3006&quot; /&gt;
&lt;h3 data-end=&quot;3063&quot; data-start=&quot;3011&quot;&gt;Scenario B: Barcelona, no dislikes, mixed budget&lt;/h3&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;3431&quot; data-start=&quot;3065&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre!&quot;&gt;&lt;span&gt;&lt;span&gt;=== Scenario B: Barcelona + no dislikes, mixed budget ===
[Node] plan
[Node] weather
[Tool] random_weather(Barcelona) -&amp;gt; {&#39;city&#39;: &#39;Barcelona&#39;, &#39;condition&#39;: &#39;sunny&#39;, &#39;high_c&#39;: 29.0, &#39;low_c&#39;: 26.0}
[Node] search
[Node] pick
[Node] draft
[Node] finalize
plan: city=Barcelona, half_day=afternoon, date=2025-08-31
weather: Barcelona sunny 29.0/26.0°C
search: 5 candidates
pick: La Sagrada Família (Exterior), Gothic Quarter Stroll, Park Güell
draft: itinerary ready
finalize: done

Itinerary:
 - **Start at La Sagrada Família (3:00 PM - 3:45 PM)**
 - Arrive early to admire the stunning exterior of this iconic basilica. Take photos and enjoy the intricate details of Gaudí&#39;s masterpiece.
 - **Gothic Quarter Stroll (4:00 PM - 5:00 PM)**
 - Take a short taxi or tram ride (about 10-15 minutes) to the Gothic Quarter. Wander through the narrow streets, explore hidden squares, and admire the medieval architecture.
 - **Park Güell (5:15 PM - 6:45 PM)**
 - Head to Park Güell via taxi or public transport (approximately 15-20 minutes). Enjoy a leisurely walk through the park, taking in Gaudí&#39;s colorful mosaics and panoramic views of the city.
 - **Transport:** Use taxis or rideshare apps for quick transfers between locations, especially in the heat.
 - **Hydration:** Bring water to stay hydrated, as temperatures can be high.
 - **Footwear:** Wear comfortable shoes for walking on cobblestone streets and park paths.
Notes: Weather: sunny&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;p data-end=&quot;3450&quot; data-start=&quot;3433&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-end=&quot;3509&quot; data-start=&quot;3452&quot;&gt;&lt;/p&gt;&lt;div&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;
&lt;hr data-end=&quot;3514&quot; data-start=&quot;3511&quot; /&gt;
&lt;h2 data-end=&quot;3535&quot; data-start=&quot;3516&quot;&gt;Why this matters&lt;/h2&gt;
&lt;ul data-end=&quot;3790&quot; data-start=&quot;3537&quot;&gt;
&lt;li data-end=&quot;3592&quot; data-start=&quot;3537&quot;&gt;
&lt;p data-end=&quot;3592&quot; data-start=&quot;3539&quot;&gt;&lt;strong data-end=&quot;3561&quot; data-start=&quot;3539&quot;&gt;Graph, not guesses&lt;/strong&gt;: deterministic orchestration&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3664&quot; data-start=&quot;3593&quot;&gt;
&lt;p data-end=&quot;3664&quot; data-start=&quot;3595&quot;&gt;&lt;strong data-end=&quot;3614&quot; data-start=&quot;3595&quot;&gt;Tools in action&lt;/strong&gt;: explicit &lt;code data-end=&quot;3636&quot; data-start=&quot;3625&quot;&gt;.invoke()&lt;/code&gt; calls with visible output&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3730&quot; data-start=&quot;3665&quot;&gt;
&lt;p data-end=&quot;3730&quot; data-start=&quot;3667&quot;&gt;&lt;strong data-end=&quot;3685&quot; data-start=&quot;3667&quot;&gt;Policy in code&lt;/strong&gt;: dislikes, budget filters, weather routing&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3790&quot; data-start=&quot;3731&quot;&gt;
&lt;p data-end=&quot;3790&quot; data-start=&quot;3733&quot;&gt;&lt;strong data-end=&quot;3749&quot; data-start=&quot;3733&quot;&gt;Verbose logs&lt;/strong&gt;: you can follow the agent step by step&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;3795&quot; data-start=&quot;3792&quot; /&gt;
&lt;h2 data-end=&quot;3811&quot; data-start=&quot;3797&quot;&gt;Pros &amp;amp; Cons&lt;/h2&gt;
&lt;p data-end=&quot;3821&quot; data-start=&quot;3813&quot;&gt;&lt;strong data-end=&quot;3821&quot; data-start=&quot;3813&quot;&gt;Pros&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-end=&quot;3962&quot; data-start=&quot;3822&quot;&gt;
&lt;li data-end=&quot;3849&quot; data-start=&quot;3822&quot;&gt;
&lt;p data-end=&quot;3849&quot; data-start=&quot;3824&quot;&gt;Compact, easy-to-follow&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3891&quot; data-start=&quot;3850&quot;&gt;
&lt;p data-end=&quot;3891&quot; data-start=&quot;3852&quot;&gt;Demonstrates both &lt;strong data-end=&quot;3889&quot; data-start=&quot;3870&quot;&gt;nodes and tools&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3924&quot; data-start=&quot;3892&quot;&gt;
&lt;p data-end=&quot;3924&quot; data-start=&quot;3894&quot;&gt;Works with or without an LLM&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3962&quot; data-start=&quot;3925&quot;&gt;
&lt;p data-end=&quot;3962&quot; data-start=&quot;3927&quot;&gt;Great for teaching graph concepts&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3972&quot; data-start=&quot;3964&quot;&gt;&lt;strong data-end=&quot;3972&quot; data-start=&quot;3964&quot;&gt;Cons&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-end=&quot;4079&quot; data-start=&quot;3973&quot;&gt;
&lt;li data-end=&quot;4005&quot; data-start=&quot;3973&quot;&gt;
&lt;p data-end=&quot;4005&quot; data-start=&quot;3975&quot;&gt;Weather is mocked/randomized&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4047&quot; data-start=&quot;4006&quot;&gt;
&lt;p data-end=&quot;4047&quot; data-start=&quot;4008&quot;&gt;Places are static, not from real APIs&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4079&quot; data-start=&quot;4048&quot;&gt;
&lt;p data-end=&quot;4079&quot; data-start=&quot;4050&quot;&gt;No travel-time optimization&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;4084&quot; data-start=&quot;4081&quot; /&gt;
&lt;h2 data-end=&quot;4099&quot; data-start=&quot;4086&quot;&gt;How to run&lt;/h2&gt;
&lt;pre class=&quot;overflow-visible!&quot; data-end=&quot;4139&quot; data-start=&quot;4101&quot;&gt;&lt;div class=&quot;contain-inline-size rounded-2xl relative bg-token-sidebar-surface-primary&quot;&gt;&lt;div class=&quot;sticky top-9&quot;&gt;&lt;div class=&quot;absolute end-0 bottom-0 flex h-9 items-center pe-2&quot;&gt;&lt;div class=&quot;bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;overflow-y-auto p-4&quot; dir=&quot;ltr&quot;&gt;&lt;code class=&quot;whitespace-pre! language-bash&quot;&gt;&lt;span&gt;&lt;span&gt;python trip_agent.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;
&lt;ul data-end=&quot;4260&quot; data-start=&quot;4141&quot;&gt;
&lt;li data-end=&quot;4206&quot; data-start=&quot;4141&quot;&gt;
&lt;p data-end=&quot;4206&quot; data-start=&quot;4143&quot;&gt;Scenario A: London, free-only, dislikes museums → rainy picks&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4260&quot; data-start=&quot;4207&quot;&gt;
&lt;p data-end=&quot;4260&quot; data-start=&quot;4209&quot;&gt;Scenario B: Barcelona, mixed budget → sunny picks&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;</description><link>https://thundaxsoftware.blogspot.com/2025/08/building-tiny-langgraph-agent-with-tool.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcrYJdRvDeC2ZuKf4ltRhE7s2ksZButKFHFmB_Tw5D-Qr-n3a-OJ7ld3QXGIp4pxi2gZFYvNsWqC_tdgm1CN3dkr7NnzhFpGjDEC9tbbsZ9XTPdPvIqwJXd0M62Qbzs0JMAL83l8OANq9v8frFMvBvJJ1UySHklT8tRP86JpwMdDaxa5F7cphqrDf1rQk/s72-c/ChatGPT%20Image%20Aug%2031,%202025,%2008_48_17%20PM.png" height="72" width="72"/><thr:total>0</thr:total><georss:featurename>London, UK</georss:featurename><georss:point>51.5072178 -0.1275862</georss:point><georss:box>23.196983963821154 -35.2838362 79.817451636178845 35.0286638</georss:box></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-7022755517551007355.post-4458032808663823019</guid><pubDate>Tue, 22 Apr 2025 08:24:00 +0000</pubDate><atom:updated>2025-04-22T08:24:32.513+00:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">AICodingAssistant</category><category domain="http://www.blogger.com/atom/ns#">CodeLlama7B</category><category domain="http://www.blogger.com/atom/ns#">ContinueExtension</category><category domain="http://www.blogger.com/atom/ns#">DeveloperProductivity</category><category domain="http://www.blogger.com/atom/ns#">LocalLLM</category><category domain="http://www.blogger.com/atom/ns#">OfflineCopilot</category><category domain="http://www.blogger.com/atom/ns#">Ollama</category><category domain="http://www.blogger.com/atom/ns#">PrivacyFirstAI</category><category domain="http://www.blogger.com/atom/ns#">VSCode</category><category domain="http://www.blogger.com/atom/ns#">Windows11Dev</category><title>Build Your Own Local Copilot in VS Code with Continue + Ollama (CodeLlama‑7B)</title><description>&lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;strong&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJe701ODOhBsFeeJBrKPqWh1xfVZEZXcm1RB7qfpnYfa3Stgx4dC8P7iLF1ZJhIrkquemJOKRKmlwmHMKm7Lsp12V02QGNTupoldQYF2BvKEAT4uVs72rpdcbUjW4kx_jMXAjCO23ccDW0BKV5JfOAGDeZNqaVhdhfCFH-0Hrois1GChXsmT1C2NJZRqo/s1024/ChatGPT%20Image%20Apr%2022,%202025,%2009_08_48%20AM.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1024&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJe701ODOhBsFeeJBrKPqWh1xfVZEZXcm1RB7qfpnYfa3Stgx4dC8P7iLF1ZJhIrkquemJOKRKmlwmHMKm7Lsp12V02QGNTupoldQYF2BvKEAT4uVs72rpdcbUjW4kx_jMXAjCO23ccDW0BKV5JfOAGDeZNqaVhdhfCFH-0Hrois1GChXsmT1C2NJZRqo/s320/ChatGPT%20Image%20Apr%2022,%202025,%2009_08_48%20AM.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/strong&gt;&lt;/div&gt;&lt;h1 data-pm-slice=&quot;1 1 []&quot;&gt;&lt;strong&gt;Build Your Own Local Copilot in VS&amp;nbsp;Code with Continue&amp;nbsp;+&amp;nbsp;Ollama (CodeLlama‑7B)&lt;/strong&gt;&lt;/h1&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&amp;nbsp;— In under 30 minutes you can have a fully‑offline, ChatGPT‑style coding assistant that &lt;em&gt;reads&lt;/em&gt; your entire codebase and never leaks a byte to the cloud.&lt;p&gt;&lt;/p&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;&lt;br /&gt;&lt;/h2&gt;&lt;h2&gt;&lt;br /&gt;&lt;/h2&gt;&lt;h2&gt;1&amp;nbsp;·&amp;nbsp;Why bother?&lt;/h2&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Privacy first&lt;/strong&gt;&amp;nbsp;— No proprietary code leaves your laptop.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Low latency&lt;/strong&gt;&amp;nbsp;— Replies stream in milliseconds, not seconds.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Free&lt;/strong&gt;&amp;nbsp;— No API keys, no per‑token billing.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hackable&lt;/strong&gt;&amp;nbsp;— Swap models, add custom prompts, or fine‑tune later.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;2&amp;nbsp;·&amp;nbsp;What we’ll build&lt;/h2&gt;&lt;p&gt;A local stack where:&lt;/p&gt;&lt;ol data-spread=&quot;false&quot; start=&quot;1&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ollama&lt;/strong&gt; runs the &lt;code&gt;codellama:7b&lt;/code&gt; model at &lt;code&gt;http://localhost:11434&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Continue&lt;/strong&gt; in VS&amp;nbsp;Code chats with that model.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Continue indexes &lt;em&gt;all&lt;/em&gt; your repositories so you can ask questions like:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;“Where do we create the AWS&amp;nbsp;SQS client?”&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;3&amp;nbsp;·&amp;nbsp;Prerequisites&lt;/h2&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Item&lt;/th&gt;&lt;th&gt;Minimum&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;OS&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Windows&amp;nbsp;11&amp;nbsp;22H2+&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;RAM&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;16&amp;nbsp;GB (32&amp;nbsp;GB recommended)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;GPU (optional)&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;NVIDIA card with ≥ 8&amp;nbsp;GB VRAM &amp;amp; latest CUDA driver&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;WSL&amp;nbsp;2&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Auto‑installed by Ollama if missing&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;VS&amp;nbsp;Code&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;1.88 or newer&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;4&amp;nbsp;·&amp;nbsp;Install Ollama&lt;/h2&gt;&lt;ol data-spread=&quot;false&quot; start=&quot;1&quot;&gt;&lt;li&gt;&lt;p&gt;Download the Windows installer from &lt;a disabled=&quot;true&quot;&gt;https://ollama.ai/download/windows&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Run the MSI and accept defaults. &lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Verify the CLI:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;ollama --version   # e.g. v0.1.32&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ol&gt;&lt;blockquote&gt;&lt;p&gt;**Heads‑up&amp;nbsp;**&amp;nbsp;— The installer registers &lt;strong&gt;Ollama&lt;/strong&gt; as a Windows service and listens on port &lt;strong&gt;11434&lt;/strong&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;5&amp;nbsp;·&amp;nbsp;Pull CodeLlama‑7B&lt;/h2&gt;&lt;pre&gt;&lt;code&gt;ollama pull codellama:7b    # 4‑bit Q4_0 fits in 8&amp;nbsp;GB RAM&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You’ll see a 100&amp;nbsp;% progress bar; the model lives in &lt;code&gt;%USERPROFILE%\.ollama\models&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgrqFEPHFstQD8rxjDNi-YUtBso-dTH-Gt5eV9V37Rr-tuZAW4yQ5nDJpHm_rxAwbfMYnUvRaBN8K3sXutjpz9PaoeDp0aoyEQzMvAFG1jG7EZX2LmJUE4MQh4aBbjjehy70E2zeYVfiTQWFxnNfz_B75M4e7qh77SWTy9WTyc3fAKZLwU2_325TmHks_M&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;203&quot; data-original-width=&quot;1662&quot; height=&quot;78&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgrqFEPHFstQD8rxjDNi-YUtBso-dTH-Gt5eV9V37Rr-tuZAW4yQ5nDJpHm_rxAwbfMYnUvRaBN8K3sXutjpz9PaoeDp0aoyEQzMvAFG1jG7EZX2LmJUE4MQh4aBbjjehy70E2zeYVfiTQWFxnNfz_B75M4e7qh77SWTy9WTyc3fAKZLwU2_325TmHks_M=w640-h78&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhiEEOsjFkpHzagHrPWRrKZbU39yh9epLNBAlacjFiSd38xupArnHCffBvZkBLpFdeLVomfrpiuI6dbXvK4SUeMXylAIge1sWUodk_IL48xlgi2e2b4sZunQ3KJoUYdfBsHu_snzc8h-weSOGiTNlWpPinViv-c30DuPiBxlA9Fhy9Qo8zRts9ra2hlMZg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;168&quot; data-original-width=&quot;1889&quot; height=&quot;56&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhiEEOsjFkpHzagHrPWRrKZbU39yh9epLNBAlacjFiSd38xupArnHCffBvZkBLpFdeLVomfrpiuI6dbXvK4SUeMXylAIge1sWUodk_IL48xlgi2e2b4sZunQ3KJoUYdfBsHu_snzc8h-weSOGiTNlWpPinViv-c30DuPiBxlA9Fhy9Qo8zRts9ra2hlMZg=w640-h56&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;6&amp;nbsp;·&amp;nbsp;Install Continue for VS&amp;nbsp;Code&lt;/h2&gt;&lt;ol data-spread=&quot;false&quot; start=&quot;1&quot;&gt;&lt;li&gt;&lt;p&gt;Open VS&amp;nbsp;Code&amp;nbsp;→&amp;nbsp;&lt;strong&gt;Extensions&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Search &lt;strong&gt;“Continue”&lt;/strong&gt; by &lt;em&gt;Continue.dev&lt;/em&gt; and click &lt;em&gt;Install&lt;/em&gt;.&lt;br /&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgcoM60V1HHvXaFWc-MTYYQpM6pZx0dsMivSLg_m5vfmVs8iiS6Qcvx84sywBjlaNu0caQNHkposMCJ-OW7hjgqOqHAy6-nlCRP-xqDVwo2vJxEtMoWxw_GLLUQhmWArSAoIJ8Z7_flRZJqzeVWutnDIVSHZYaKEpgXXJgdQ2osdZZr6lgHPVFvtSgdTko&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;504&quot; data-original-width=&quot;803&quot; height=&quot;251&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEgcoM60V1HHvXaFWc-MTYYQpM6pZx0dsMivSLg_m5vfmVs8iiS6Qcvx84sywBjlaNu0caQNHkposMCJ-OW7hjgqOqHAy6-nlCRP-xqDVwo2vJxEtMoWxw_GLLUQhmWArSAoIJ8Z7_flRZJqzeVWutnDIVSHZYaKEpgXXJgdQ2osdZZr6lgHPVFvtSgdTko=w400-h251&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Reload VS&amp;nbsp;Code; a sunflower icon shows up in the Activity Bar.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;7&amp;nbsp;·&amp;nbsp;Point Continue at Ollama&lt;/h2&gt;&lt;ol data-spread=&quot;false&quot; start=&quot;1&quot;&gt;&lt;li&gt;&lt;p&gt;Click the &lt;strong&gt;sunflower icon&lt;/strong&gt; → gear ⚙ →&amp;nbsp;&lt;em&gt;Settings&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Choose &lt;strong&gt;Model Provider:&amp;nbsp;Custom&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Fill the fields:&lt;/p&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Field&lt;/td&gt;&lt;td&gt;Value&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Provider&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;ollama&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Base&amp;nbsp;URL&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;http://localhost:11434&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Model&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;codellama:7b&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Press &lt;strong&gt;Save&lt;/strong&gt;. &lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;em&gt;(This writes the same keys to your &lt;/em&gt;&lt;code&gt;&lt;em&gt;settings.json&lt;/em&gt;&lt;/code&gt;&lt;em&gt;.)&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;&lt;/em&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;em&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhamFyImT73-ucOGEoiBaAfv41UH2j_6U2ajgoKpxnqzfHdjYtrMVWWk5iNjcr4yk7OdkAJ6XMdbHMZB3G4fxZbEmakJGpuaFRe0E1qz2q6Rw2d5nJkn_PPrjDnJ9v9mo8CcoCjvQbEFaLqF5vhMUKY4s5GgBLqGLVyKE2kTeWy1V1OiV0oO31R2Zktq4Y&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;982&quot; data-original-width=&quot;1485&quot; height=&quot;424&quot; src=&quot;https://blogger.googleusercontent.com/img/a/AVvXsEhamFyImT73-ucOGEoiBaAfv41UH2j_6U2ajgoKpxnqzfHdjYtrMVWWk5iNjcr4yk7OdkAJ6XMdbHMZB3G4fxZbEmakJGpuaFRe0E1qz2q6Rw2d5nJkn_PPrjDnJ9v9mo8CcoCjvQbEFaLqF5vhMUKY4s5GgBLqGLVyKE2kTeWy1V1OiV0oO31R2Zktq4Y=w640-h424&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/em&gt;&lt;/div&gt;&lt;em&gt;&lt;br /&gt;&lt;/em&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;8&amp;nbsp;·&amp;nbsp;Index your repositories (no training required)&lt;/h2&gt;&lt;p&gt;Add one line to &lt;em&gt;User&lt;/em&gt; or &lt;em&gt;Workspace&lt;/em&gt; settings:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&quot;continue.directoryContext&quot;: [
  { &quot;path&quot;: &quot;D:/code&quot;, &quot;depth&quot;: 4, &quot;maxFiles&quot;: 25000 }
]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Continue walks the tree once, stores embeddings in &lt;code&gt;%APPDATA%\continue\index&lt;/code&gt;, and keeps them fresh in the background.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;9&amp;nbsp;·&amp;nbsp;Smoke‑test the setup&lt;/h2&gt;&lt;ol data-spread=&quot;false&quot; start=&quot;1&quot;&gt;&lt;li&gt;&lt;p&gt;Press &lt;strong&gt;Ctrl&amp;nbsp;+&amp;nbsp;Shift&amp;nbsp;+&amp;nbsp;L&lt;/strong&gt; to open chat.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Type:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;@Codebase Which file defines the KafkaConsumer settings?&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The reply should include a &lt;em&gt;Context&lt;/em&gt; block listing real file paths.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Optional: enable the &lt;strong&gt;Continue Console&lt;/strong&gt; (Settings → &lt;em&gt;Enable&amp;nbsp;Console&lt;/em&gt;) to watch retrieved files live.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;10&amp;nbsp;·&amp;nbsp;Tame hallucinations (optional but recommended)&lt;/h2&gt;&lt;p&gt;Add this to &lt;strong&gt;settings.json&lt;/strong&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&quot;continue.systemMessage&quot;: &quot;You are a senior full‑stack engineer. Answer concisely and reference only provided context. If unsure, say &#39;IDK&#39;.&quot;,
&quot;continue.modelOptions&quot;: {
  &quot;temperature&quot;: 0.2,
  &quot;top_p&quot;: 0.9,
  &quot;repeat_penalty&quot;: 1.1
}&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;11&amp;nbsp;·&amp;nbsp;Daily cheat‑sheet&lt;/h2&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Task&lt;/td&gt;&lt;td&gt;How&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Explain selected code&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Select → right‑click → &lt;em&gt;Continue: Explain&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Generate tests&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Same menu → &lt;em&gt;Generate&amp;nbsp;Tests&lt;/em&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Inline autocomplete&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Toggle &lt;em&gt;Tab Autocomplete&lt;/em&gt; in Continue footer&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Force retrieval&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Prefix prompt with &lt;code&gt;@Codebase&lt;/code&gt;, &lt;code&gt;@File&lt;/code&gt;, or &lt;code&gt;@Folder&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;12&amp;nbsp;·&amp;nbsp;Troubleshooting&lt;/h2&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Symptom&lt;/td&gt;&lt;td&gt;Fix&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;connect ECONNREFUSED 11434&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Tray → &lt;em&gt;Start&amp;nbsp;Ollama Service&lt;/em&gt; or &lt;code&gt;Start‑Service Ollama&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Model OOM on pull&lt;/td&gt;&lt;td&gt;Use quantised tag: &lt;code&gt;codellama:7b:Q4_0&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;No &lt;em&gt;Context&lt;/em&gt; block&lt;/td&gt;&lt;td&gt;Check &lt;code&gt;directoryContext&lt;/code&gt; path / depth; rebuild index&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;&lt;h2&gt;13&amp;nbsp;·&amp;nbsp;Next steps&lt;/h2&gt;&lt;ul data-spread=&quot;false&quot;&gt;&lt;li&gt;&lt;p&gt;Swap in &lt;strong&gt;StarCoder2‑15B&lt;/strong&gt; or &lt;strong&gt;DeepSeek‑Coder‑33B&lt;/strong&gt; for higher quality.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Drop temperature to&amp;nbsp;0 and let it draft unit tests you tweak.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Graduate to &lt;strong&gt;Tabby LoRA&lt;/strong&gt; if you need inline completions that mimic your exact style.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;</description><link>https://thundaxsoftware.blogspot.com/2025/04/build-your-own-local-copilot-in-vscode.html</link><author>noreply@blogger.com (Jordi Corbilla)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJe701ODOhBsFeeJBrKPqWh1xfVZEZXcm1RB7qfpnYfa3Stgx4dC8P7iLF1ZJhIrkquemJOKRKmlwmHMKm7Lsp12V02QGNTupoldQYF2BvKEAT4uVs72rpdcbUjW4kx_jMXAjCO23ccDW0BKV5JfOAGDeZNqaVhdhfCFH-0Hrois1GChXsmT1C2NJZRqo/s72-c/ChatGPT%20Image%20Apr%2022,%202025,%2009_08_48%20AM.png" height="72" width="72"/><thr:total>0</thr:total></item></channel></rss>