<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Non-Brand Data]]></title><description><![CDATA[Non-Brand Data helps data professionals build practical judgment in ML, GenAI, and analytics through structured essays, field guides, templates, and applied workflows.]]></description><link>https://www.nb-data.com</link><image><url>https://substackcdn.com/image/fetch/$s_!06DP!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0e1cde-d120-4029-8ffd-2a8c7c6e4504_1280x1280.png</url><title>Non-Brand Data</title><link>https://www.nb-data.com</link></image><generator>Substack</generator><lastBuildDate>Tue, 28 Jul 2026 20:13:56 GMT</lastBuildDate><atom:link href="https://www.nb-data.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Cornellius Yudha Wijaya]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[cornellius@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[cornellius@substack.com]]></itunes:email><itunes:name><![CDATA[Cornellius Yudha Wijaya]]></itunes:name></itunes:owner><itunes:author><![CDATA[Cornellius Yudha Wijaya]]></itunes:author><googleplay:owner><![CDATA[cornellius@substack.com]]></googleplay:owner><googleplay:email><![CDATA[cornellius@substack.com]]></googleplay:email><googleplay:author><![CDATA[Cornellius Yudha Wijaya]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Stock API: What it is and Why it Matters]]></title><description><![CDATA[How I decide on a data provider before writing the integration, and why that choice outlasts the code.]]></description><link>https://www.nb-data.com/p/stock-api-what-it-is-and-why-it-matter</link><guid isPermaLink="false">https://www.nb-data.com/p/stock-api-what-it-is-and-why-it-matter</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sun, 26 Jul 2026 16:32:54 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!vn6D!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f5606de-45db-4b2f-8ae7-80878b68b59f_1400x933.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vn6D!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f5606de-45db-4b2f-8ae7-80878b68b59f_1400x933.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vn6D!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f5606de-45db-4b2f-8ae7-80878b68b59f_1400x933.jpeg 424w, https://substackcdn.com/image/fetch/$s_!vn6D!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f5606de-45db-4b2f-8ae7-80878b68b59f_1400x933.jpeg 848w, https://substackcdn.com/image/fetch/$s_!vn6D!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f5606de-45db-4b2f-8ae7-80878b68b59f_1400x933.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!vn6D!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f5606de-45db-4b2f-8ae7-80878b68b59f_1400x933.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vn6D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f5606de-45db-4b2f-8ae7-80878b68b59f_1400x933.jpeg" width="1400" height="933" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4f5606de-45db-4b2f-8ae7-80878b68b59f_1400x933.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:933,&quot;width&quot;:1400,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!vn6D!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f5606de-45db-4b2f-8ae7-80878b68b59f_1400x933.jpeg 424w, https://substackcdn.com/image/fetch/$s_!vn6D!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f5606de-45db-4b2f-8ae7-80878b68b59f_1400x933.jpeg 848w, https://substackcdn.com/image/fetch/$s_!vn6D!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f5606de-45db-4b2f-8ae7-80878b68b59f_1400x933.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!vn6D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4f5606de-45db-4b2f-8ae7-80878b68b59f_1400x933.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@toti1996?utm_source=medium&amp;utm_medium=referral">T&#246;t&#246;s &#193;d&#225;m</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure></div><p>The hard part of using a stock API is not getting the data. It is choosing the provider, and you live with that choice long after the integration is done.</p><p>This is why I want to walk through what a stock API is, the two routes to its data, and how I decide whether a provider is worth building on. This article is for two types of readers: the quantitative investor who wants clean history and reliable backtests, and the AI developer who wants an agent that can reach the markets on its own.</p><p>One note before I start. This is not a ranking of every provider, and it is not a buyer&#8217;s guide. It is the way I think through the decision, with <a href="https://www.alphavantage.co/">Alpha Vantage</a> as the running example, because it offers both a REST API and a mature MCP server over the same data.</p><p>Curious about it? Let&#8217;s get into it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2><strong>Why does the choice matter more than the code?</strong></h2><p>Connecting to a stock API is usually simple: get a key, call an endpoint, and parse the response. Because that part is so easy, it is tempting to treat the choice of provider as a detail you can settle later.</p><p>In my experience, that is backwards. The real problems rarely show up in the integration code. For example, a free tier capped at a few requests a day stalls a research session halfway through an idea, and a feed with no tool interface leaves you hand-building the adapter an agent needs before it can call anything.</p><p>None of these are coding problems. Each one traces back to the provider you chose. That is why I spend more time choosing a provider than integrating one. Before judging one, though, it helps to be clear on what a stock API actually gives you.</p><div><hr></div><h2><strong>What is a stock API?</strong></h2><p>A stock API is an HTTP service that returns structured market data, usually as JSON or CSV. You send parameters such as a ticker, date range, and interval, and receive data in a predictable format.</p><p>Unlike a finance website, an API is built for repeatable access. The same request can run across hundreds or thousands of tickers while returning the same structure each time. That consistency makes it useful for backtests, dashboards, and other automated workflows.</p><p>There are two common callers. Traditional code calls the endpoint and parses the response. An AI agent can instead call the API through a tool such as an MCP server. The data source is the same; the access layer is different.</p><div><hr></div><h2><strong>How do you reach the data: REST requests and the MCP layer?</strong></h2><p>There are two ways to reach the data. The first is the REST request, the route a traditional integration uses.</p><p>Providers build it two ways: some give each kind of data its own URL, while Alpha Vantage keeps a single URL and lets a function parameter choose the operation. A daily price request looks like this:</p><pre><code><span>GET https://www.alphavantage.co/query
    ?function=TIME_SERIES_DAILY
    &amp;symbol=IBM
    &amp;apikey=YOUR_KEY</span></code></pre><p>Only the function value changes from call to call. Swap <code>TIME_SERIES_DAILY</code> for another function and the same URL returns a live quote or a company overview instead. I like this design because one endpoint is simpler to document and maintain.</p><p>The second route is built for the agent, and it uses the Model Context Protocol (MCP), an open standard. The provider runs a server that lists its tools. The agent reads that list with tools/list, then calls the one it needs with tools/call, filling in the arguments from the conversation. The server runs the query and returns the result.</p><p>The exchange looks like this:</p><pre><code><span>// 1. Agent asks what the server can do
{&#8221;method&#8221;: &#8220;tools/list&#8221;}
 
// 2. Server responds with a typed catalog
{
  &#8220;tools&#8221;: [{
    &#8220;name&#8221;: &#8220;get_daily_time_series&#8221;,
    &#8220;description&#8221;: &#8220;Daily OHLCV for a listed equity&#8221;,
    &#8220;inputSchema&#8221;: {
      &#8220;type&#8221;: &#8220;object&#8221;,
      &#8220;properties&#8221;: {
        &#8220;symbol&#8221;: {&#8221;type&#8221;: &#8220;string&#8221;},
        &#8220;outputsize&#8221;: {&#8221;type&#8221;: &#8220;string&#8221;, &#8220;enum&#8221;: [&#8221;compact&#8221;, &#8220;full&#8221;]}
      },
      &#8220;required&#8221;: [&#8221;symbol&#8221;]
    }
  }]
}
 
// 3. Agent calls the tool with arguments the model filled in
{
  &#8220;method&#8221;: &#8220;tools/call&#8221;,
  &#8220;params&#8221;: {
    &#8220;name&#8221;: &#8220;get_daily_time_series&#8221;,
    &#8220;arguments&#8221;: {&#8221;symbol&#8221;: &#8220;IBM&#8221;, &#8220;outputsize&#8221;: &#8220;compact&#8221;}
  }
}</span></code></pre><p><a href="https://www.alphavantage.co/">Alpha Vantage</a> runs an official MCP server with more than 100 tools across nine categories. Each maps to a single REST function, so the same request returns the same data whichever route you take:</p><ul><li><p>Core stocks: quotes and time series</p></li><li><p>Options: real-time chains with Greeks</p></li><li><p>Fundamentals: statements, overviews, and earnings</p></li><li><p>Forex: spot and historical exchange rates</p></li><li><p>Cryptocurrencies: digital asset prices and time series</p></li><li><p>Commodities: oil, metals, and agricultural prices</p></li><li><p>Economic indicators: GDP, CPI, and treasury yields</p></li><li><p>Technical indicators: more than 50 tools from SMA to MACD</p></li><li><p>Alpha Intelligence: news sentiment, earnings transcripts, and insider transactions</p></li></ul><p>It also sits in Anthropic&#8217;s Claude Connectors directory. That means it installs as a tool source across editors and chat apps like Claude and Cursor, instead of being tied to one app.</p><p>Both routes return the same data. The only difference is who makes the call:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_Qnh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F764ac9f2-2f90-4984-98c4-d9bbb4ee6df3_1172x184.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_Qnh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F764ac9f2-2f90-4984-98c4-d9bbb4ee6df3_1172x184.png 424w, https://substackcdn.com/image/fetch/$s_!_Qnh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F764ac9f2-2f90-4984-98c4-d9bbb4ee6df3_1172x184.png 848w, https://substackcdn.com/image/fetch/$s_!_Qnh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F764ac9f2-2f90-4984-98c4-d9bbb4ee6df3_1172x184.png 1272w, https://substackcdn.com/image/fetch/$s_!_Qnh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F764ac9f2-2f90-4984-98c4-d9bbb4ee6df3_1172x184.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_Qnh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F764ac9f2-2f90-4984-98c4-d9bbb4ee6df3_1172x184.png" width="1172" height="184" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/764ac9f2-2f90-4984-98c4-d9bbb4ee6df3_1172x184.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:184,&quot;width&quot;:1172,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!_Qnh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F764ac9f2-2f90-4984-98c4-d9bbb4ee6df3_1172x184.png 424w, https://substackcdn.com/image/fetch/$s_!_Qnh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F764ac9f2-2f90-4984-98c4-d9bbb4ee6df3_1172x184.png 848w, https://substackcdn.com/image/fetch/$s_!_Qnh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F764ac9f2-2f90-4984-98c4-d9bbb4ee6df3_1172x184.png 1272w, https://substackcdn.com/image/fetch/$s_!_Qnh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F764ac9f2-2f90-4984-98c4-d9bbb4ee6df3_1172x184.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Figure 1. Differences between REST request and MCP layer</p><p>A good MCP server now matters as much as a good REST API.</p><p>It used to be enough to check that the REST docs were solid. Now I also ask whether the provider runs an MCP server and whether its tools match the REST API.</p><p>An agent calling a typed tool makes fewer mistakes and burns fewer tokens than one making raw HTTP calls from hand-written prompts. Both routes pull the same data, so the next question is what data is on offer.</p><div><hr></div><h2><strong>Which categories of market data matter?</strong></h2><p>Market data divides into five broad categories, and most full-featured APIs cover a version of each. Know which one your project requires, because that is where a provider&#8217;s limits show first.</p><ol><li><p>Quotes and real-time prices are the simplest: what a stock trades at right now. Free tiers usually delay that by about fifteen minutes, which is fine for some jobs and useless for others. A live feed costs more, because it is licensed and needs a broker or exchange agreement that most retail APIs skip.</p></li><li><p>Next comes intraday and historical time series: the open, high, low, close, and volume at intervals from a minute to a month. This is the backbone of backtesting, and the one I check hardest, because a strategy is only as reliable as its history.</p></li><li><p>Fundamentals cover the financial statements and valuation ratios behind screeners and valuation models. They matter the moment a strategy depends on more than price.</p></li><li><p>Then there are technical indicators like moving averages and RSI. Alpha Vantage alone exposes more than fifty as ready-made tools. Providers differ on delivery: some compute the indicator on the server, so you request it like a quote, while others hand you raw prices to compute yourself with TA-Lib or pandas-ta.</p></li><li><p>Last is news, macro, and alternative data: headlines, sentiment scores, and economic releases. It is far less standardized than price data, so it is usually the first thing a smaller provider drops.</p></li></ol><p>Think what you need, and the provider you might want to use becomes clearer.</p><div><hr></div><h2><strong>How do you call a stock API the right way?</strong></h2><p>A reliable traditional integration can still be concise. Here is one:</p><pre><code><span>import requests
 
API_KEY = &#8220;YOUR_KEY&#8221;
BASE_URL = &#8220;https://www.alphavantage.co/query&#8221;
 
def get_daily_prices(symbol: str) -&gt; dict:
    params = {
        &#8220;function&#8221;: &#8220;TIME_SERIES_DAILY&#8221;,
        &#8220;symbol&#8221;: symbol,
        &#8220;apikey&#8221;: API_KEY,
        &#8220;outputsize&#8221;: &#8220;compact&#8221;,
    }
    response = requests.get(BASE_URL, params=params, timeout=10)
    response.raise_for_status()
    data = response.json()
 
    if &#8220;Error Message&#8221; in data:
        raise ValueError(f&#8221;Bad request: {data[&#8217;Error Message&#8217;]}&#8221;)
    if &#8220;Note&#8221; in data:
        raise RuntimeError(&#8221;Rate limit hit, back off and retry&#8221;)
 
    return data[&#8221;Time Series (Daily)&#8221;]</span></code></pre><p>Three checks matter here: <code>Error Message</code> catches bad requests, <code>Note</code> catches rate limits, and <code>timeout</code> prevents stalled requests.</p><p>An agent needs the same safeguards, but they move into the MCP server. The server should handle errors and rate limits clearly, and cache repeated requests when appropriate. That is the difference between an MCP server that exists and one that is production-ready.</p><div><hr></div><h2><strong>What does production-ready actually mean?</strong></h2><p>Production-ready comes down to six things: coverage, data quality, latency, rate limits, documentation and licensing, and AI-readiness.</p><ol><li><p>Coverage means whether the provider supports the tickers, exchanges, and asset classes your strategy actually trades. Broad US coverage, for example, says little about another market.</p></li><li><p>Data quality is less visible. Adjusted prices matter because splits can distort raw series. Historical coverage matters too: missing delisted companies introduce survivorship bias, while gaps in thinly traded tickers can distort a backtest.</p></li><li><p>Latency depends on the strategy. End-of-day data may be enough for daily rebalancing, but not for market making. The question is whether the feed is fast enough for the decision it supports.</p></li><li><p>Rate limits determine how much data you can realistically pull. A low per-minute or daily cap can stop a job before it finishes, while bulk endpoints can reduce the number of calls needed.</p></li><li><p>Documentation should explain adjustments, coverage, and edge cases. Licensing matters just as much: personal and commercial use often come with different terms.</p></li><li><p>AI-readiness adds another layer. I look for an MCP server, how much of the REST API it exposes, OAuth support, and compatibility with tools such as Claude, Cursor, and VS Code. Without MCP support, teams need to build and maintain the adapter themselves.</p></li></ol><p>Those six checks narrow the provider choice quickly.</p><div><hr></div><h2><strong>How do you choose a stock data API?</strong></h2><p>When choosing a provider, I use five questions:</p><ul><li><p>What data do I need: quotes, historical prices, fundamentals, indicators, news, or a mix?</p></li><li><p>How fresh must it be: real-time, delayed, or end-of-day?</p></li><li><p>What is my request volume?</p></li><li><p>Is the use personal or commercial?</p></li><li><p>Will an AI agent consume the data?</p></li></ul><p>The last question changes the requirements. If an agent is involved, MCP support becomes part of the evaluation.</p><p>Before committing, test the free tier with your actual tickers and expected request volume. For agent use cases, test the MCP tools separately from the REST API. A good API does not automatically mean a good MCP implementation.</p><p>Applying these questions to the provider used throughout this article shows where it fits.</p><h2><strong>Where does Alpha Vantage fit?</strong></h2><p>Since I have leaned on Alpha Vantage throughout, it is fair to say where it fits and where it does not. As a best-overall pick, its home is quantitative research and agentic AI work, and because its REST API and MCP server draw on the same data, a research team and an AI team can share one provider instead of maintaining two integrations that drift apart.</p><p>That shared foundation is the one thing I would want an AI developer to take away. One integration serves both the backtests and the agents. The historical series a notebook pulls today is the same tool an agent calls next week, when a user asks in plain language instead of writing a query.</p><p>It fits less well for ultra-low-latency trading, and that limit is worth stating plainly. Nothing returned through a shared REST or MCP layer will match a co-located feed. A strategy built on microsecond execution needs a co-located or institutional feed like Bloomberg, or a direct exchange line. No amount of MCP support closes that gap.</p><div><hr></div><h2><strong>The takeaway</strong></h2><p>The main lesson is simple: <strong>the provider creates most of the long-term constraints, not the integration.</strong></p><p>Before building, check the full contract around the data: coverage, quality, rate limits, licensing, pricing, and AI support. A REST client can be rewritten. An MCP adapter can be replaced. Swapping the provider underneath a working system is much harder.</p><p>That is why I would rather spend more time evaluating the provider upfront than discover its limits after the backtests, dashboards, or agents already depend on it.</p><p>For me, that is the real definition of production-ready: not just an API that works, but a provider whose limits are clear enough to build around.</p><p>I would be interested to hear what you check before committing to a market data provider, especially the checks that have caught problems early.</p>]]></content:encoded></item><item><title><![CDATA[Manager Memo: Does Your Team Still Need SQL when AI writes it?]]></title><description><![CDATA[For managers evaluating AI-assisted analytics and data-team operating models]]></description><link>https://www.nb-data.com/p/manager-memo-does-your-team-still</link><guid isPermaLink="false">https://www.nb-data.com/p/manager-memo-does-your-team-still</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sun, 19 Jul 2026 06:54:09 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!wk-F!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f597e87-e1cd-44c8-8ff2-8cbca3089f98_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wk-F!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f597e87-e1cd-44c8-8ff2-8cbca3089f98_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wk-F!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f597e87-e1cd-44c8-8ff2-8cbca3089f98_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!wk-F!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f597e87-e1cd-44c8-8ff2-8cbca3089f98_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!wk-F!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f597e87-e1cd-44c8-8ff2-8cbca3089f98_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!wk-F!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f597e87-e1cd-44c8-8ff2-8cbca3089f98_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wk-F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f597e87-e1cd-44c8-8ff2-8cbca3089f98_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3f597e87-e1cd-44c8-8ff2-8cbca3089f98_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1819830,&quot;alt&quot;:&quot;Manager Memo: Does Your Team Still Need SQL when AI writes it?&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/207628602?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f597e87-e1cd-44c8-8ff2-8cbca3089f98_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Manager Memo: Does Your Team Still Need SQL when AI writes it?" title="Manager Memo: Does Your Team Still Need SQL when AI writes it?" srcset="https://substackcdn.com/image/fetch/$s_!wk-F!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f597e87-e1cd-44c8-8ff2-8cbca3089f98_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!wk-F!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f597e87-e1cd-44c8-8ff2-8cbca3089f98_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!wk-F!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f597e87-e1cd-44c8-8ff2-8cbca3089f98_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!wk-F!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f597e87-e1cd-44c8-8ff2-8cbca3089f98_1536x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1 style="text-align: justify;">The short answer</h1>
      <p>
          <a href="https://www.nb-data.com/p/manager-memo-does-your-team-still">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Building a Minimal Agent Harness in Python]]></title><description><![CDATA[A beginner-friendly guide to running agent code in another process and stopping it when it takes too long.]]></description><link>https://www.nb-data.com/p/building-a-minimal-agent-harness</link><guid isPermaLink="false">https://www.nb-data.com/p/building-a-minimal-agent-harness</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Thu, 16 Jul 2026 03:29:41 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!k0rA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1b7a1f-7527-49ab-9fc1-f12cab91ebe8_1220x813.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!k0rA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1b7a1f-7527-49ab-9fc1-f12cab91ebe8_1220x813.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!k0rA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1b7a1f-7527-49ab-9fc1-f12cab91ebe8_1220x813.png 424w, https://substackcdn.com/image/fetch/$s_!k0rA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1b7a1f-7527-49ab-9fc1-f12cab91ebe8_1220x813.png 848w, https://substackcdn.com/image/fetch/$s_!k0rA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1b7a1f-7527-49ab-9fc1-f12cab91ebe8_1220x813.png 1272w, https://substackcdn.com/image/fetch/$s_!k0rA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1b7a1f-7527-49ab-9fc1-f12cab91ebe8_1220x813.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!k0rA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1b7a1f-7527-49ab-9fc1-f12cab91ebe8_1220x813.png" width="1220" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1d1b7a1f-7527-49ab-9fc1-f12cab91ebe8_1220x813.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1220,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Building a Minimal Agent Harness in Python&quot;,&quot;title&quot;:&quot;Diagram showing the application harness in one process and the agent in a separate worker process.&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Building a Minimal Agent Harness in Python" title="Diagram showing the application harness in one process and the agent in a separate worker process." srcset="https://substackcdn.com/image/fetch/$s_!k0rA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1b7a1f-7527-49ab-9fc1-f12cab91ebe8_1220x813.png 424w, https://substackcdn.com/image/fetch/$s_!k0rA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1b7a1f-7527-49ab-9fc1-f12cab91ebe8_1220x813.png 848w, https://substackcdn.com/image/fetch/$s_!k0rA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1b7a1f-7527-49ab-9fc1-f12cab91ebe8_1220x813.png 1272w, https://substackcdn.com/image/fetch/$s_!k0rA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1d1b7a1f-7527-49ab-9fc1-f12cab91ebe8_1220x813.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><br>An agent harness is the code around that agent. It starts the work. It waits for the answer and handles failure but the harness does not decide what the answer should be. </p><p>Imagine that your application asks an agent to summarize a document. On a normal run, the agent returns some text and the application continues. But the agent might crash. It might also wait forever for another service.</p><p>A normal Python function call leaves the application waiting at that line until the function returns. If the agent never returns, the code after the call never runs. The application therefore needs a way to stay in control while the agent works.</p><p>This is why, this articlewill build that idea before looking at any files. Once the roles are clear, each part will become a small piece of Python code.</p><p>Curious about it? Let&#8217;s get into it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h1>The idea before the code</h1><p>The first decision is to run the application and the agent as two separate programs. A running program is called a process. The operating system gives each process its own memory and can stop one without automatically stopping the other.</p><p>The application stays in the first process. It starts a second process when it has work for the agent. We call the second process the worker because its job is to perform one piece of agent work.</p><p>The first process now has another role. It starts the worker and watches for the result. We call it the supervisor because it can stop a failed worker and start a replacement. Worker and supervisor are names for these roles. They are not special Python features.</p><p>One request now follows this path:</p><p><span>1. </span>The application asks the harness for an answer.</p><p><span>2. </span>The harness starts a worker and sends it the request.</p><p><span>3. </span>The worker gives the request to the agent code.</p><p><span>4. </span>The worker sends the answer back to the harness.</p><p><span>5. </span>If the answer takes too long, the harness stops the worker and reports an error.</p><p>This time limit is called a timeout. A timeout is the longest period the harness is willing to wait. The harness can enforce it because the harness is still running outside the worker.</p><p>The two-process design keeps the application in control. It also creates the next problem: the two processes no longer share the same memory. They need a way to exchange a request and a reply.</p><div><hr></div><h1>How the two processes exchange a message</h1><p>When two pieces of code run in the same process, one function can pass a Python value directly to another. A dictionary is one kind of Python value. It stores named fields such as id and message. Separate processes cannot share that dictionary as a live value.</p><p>The operating system can connect the processes with a pipe. A pipe is a channel for moving data from one program to another. The harness writes at one end. The worker reads at the other end and uses a second connection for its reply.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ovqb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46662af1-673c-4c33-914a-94f7e7d19393_1247x702.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ovqb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46662af1-673c-4c33-914a-94f7e7d19393_1247x702.png 424w, https://substackcdn.com/image/fetch/$s_!Ovqb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46662af1-673c-4c33-914a-94f7e7d19393_1247x702.png 848w, https://substackcdn.com/image/fetch/$s_!Ovqb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46662af1-673c-4c33-914a-94f7e7d19393_1247x702.png 1272w, https://substackcdn.com/image/fetch/$s_!Ovqb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46662af1-673c-4c33-914a-94f7e7d19393_1247x702.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ovqb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46662af1-673c-4c33-914a-94f7e7d19393_1247x702.png" width="1247" height="702" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/46662af1-673c-4c33-914a-94f7e7d19393_1247x702.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:702,&quot;width&quot;:1247,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Building a Minimal Agent Harness in Python&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Building a Minimal Agent Harness in Python" title="Building a Minimal Agent Harness in Python" srcset="https://substackcdn.com/image/fetch/$s_!Ovqb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46662af1-673c-4c33-914a-94f7e7d19393_1247x702.png 424w, https://substackcdn.com/image/fetch/$s_!Ovqb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46662af1-673c-4c33-914a-94f7e7d19393_1247x702.png 848w, https://substackcdn.com/image/fetch/$s_!Ovqb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46662af1-673c-4c33-914a-94f7e7d19393_1247x702.png 1272w, https://substackcdn.com/image/fetch/$s_!Ovqb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F46662af1-673c-4c33-914a-94f7e7d19393_1247x702.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The harness keeps track of the request. The worker runs the agent. Messages move between the processes through pipes.</figcaption></figure></div><p>A pipe carries bytes, which are the raw units a program uses for data. Both programs therefore need a message format that Python can turn into bytes and back again. This example uses JSON, a common text format for named fields and values.</p><p>The example puts one complete JSON message on each line. This format is called JSON Lines. The line break marks the end of one message, so the reader knows when it has received the complete request.</p><p>A request contains an id and a message. The id is a label for the request. The worker copies that label into its reply. The ok field says whether the reply contains a value or an error.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">request {&#8221;id&#8221;: &#8220;1&#8221;, &#8220;message&#8221;: &#8220;hello&#8221;}
reply {&#8221;id&#8221;: &#8220;1&#8221;, &#8220;ok&#8221;: true, &#8220;value&#8221;: &#8220;Agent received: hello&#8221;}
error {&#8221;id&#8221;: &#8220;1&#8221;, &#8220;ok&#8221;: false, &#8220;error&#8221;: &#8220;agent failed&#8221;}</code></pre></div><p>In this beginner version, one worker handles only one request. Matching the id is still useful because it makes the message format clear. It also prepares the design for a later version that keeps workers alive.</p><p>We now have the full idea: two processes and a small message between them. Only now do we need to give the Python pieces filenames.</p><div><hr></div><h1>The four Python files</h1><p>The example separates the design into four files. Each file has one job.</p><ul><li><p><code>agent.py</code> contains the code that produces an answer.</p></li><li><p><code>worker.py</code> receives one request and calls the agent.</p></li><li><p><code>harness.py</code> starts the worker and enforces the timeout.</p></li><li><p><code>main.py</code> represents the application that wants an answer.</p></li></ul><p>The filenames do not define the design. They make the roles easier to see in a small example. A larger application may organize the code differently while keeping the same separation.</p><p>We will build the files in the order that a request uses them. The agent behavior comes first because it is ordinary Python code.</p><h1>1. The agent produces an answer</h1><p>In Python, a class groups related behavior. The Agent class in this example has one method named reply(). A method is a function that belongs to a class. reply() receives a message and returns a short response.</p><p>Keeping the class this small matters. You can call reply() in a basic test without starting the rest of the system. You can also change the response logic without changing how the process is managed.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># agent.py
class Agent:
    def reply(self, message: str) -&gt; str:
        return f"Agent received: {message}"</code></pre></div><p>For now, reply() only shows which message it received. A real agent could call a model or use a tool here. The harness does not need to know how that answer is produced.</p><p>The class accepts a normal Python value. The worker is the piece that turns an incoming JSON message into that method call.</p><h1>2. The worker connects the pipe to the agent</h1><p>A command-line Python program has two standard text streams. Standard input is where text enters the program. Standard output is where the program writes its normal result.</p><p>The harness connects both streams to pipes when it starts the worker. The worker reads one line from standard input and converts the JSON text into a Python dictionary. It takes the message from that dictionary and passes it to Agent.reply().</p><p>If reply() returns normally, the worker creates a successful JSON reply. If Python reports an error, the except block receives that exception and creates an error reply instead. An exception is Python&#8217;s way of stopping the current operation and reporting what went wrong.</p><p>After building the reply, the worker writes one JSON line to standard output. Python may hold output briefly before sending it. flush() makes sure this line is sent at once. The worker then reaches the end of the file and exits.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># worker.py
import json
import sys

from agent import Agent

agent = Agent()
request = json.loads(sys.stdin.readline())

try:
    value = agent.reply(request["message"])
    reply = {
        "id": request["id"],
        "ok": True,
        "value": value,
    }
except Exception as exc:
    reply = {
        "id": request["id"],
        "ok": False,
        "error": str(exc),
    }

sys.stdout.write(json.dumps(reply) + "\n")
sys.stdout.flush()
</code></pre></div><div class="callout-block" data-callout="true"><p>Standard output is reserved<span> </span>The harness expects every line on standard output to contain JSON. A normal print() would add an unexpected line and break the message format. Send logs to standard error, which is a separate stream for diagnostic messages.</p></div><p>The exception path works only when Python gives control back to the worker. An endless loop does not do that. Code that never returns cannot create its own error reply, so the harness must handle that case from outside the worker.</p><h1>3. The harness controls the worker</h1><p>The harness runs in the application process. For each request, it starts one worker and sends one JSON line. It then waits for one reply.</p><p>While it waits, the harness keeps the timeout. If the reply arrives in time, the harness returns the value to the application. If the time limit passes, it ends the worker process and reports an error.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AJ01!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4165b598-6dc8-4945-bb67-76c9da57df29_1247x702.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AJ01!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4165b598-6dc8-4945-bb67-76c9da57df29_1247x702.png 424w, https://substackcdn.com/image/fetch/$s_!AJ01!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4165b598-6dc8-4945-bb67-76c9da57df29_1247x702.png 848w, https://substackcdn.com/image/fetch/$s_!AJ01!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4165b598-6dc8-4945-bb67-76c9da57df29_1247x702.png 1272w, https://substackcdn.com/image/fetch/$s_!AJ01!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4165b598-6dc8-4945-bb67-76c9da57df29_1247x702.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AJ01!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4165b598-6dc8-4945-bb67-76c9da57df29_1247x702.png" width="1247" height="702" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4165b598-6dc8-4945-bb67-76c9da57df29_1247x702.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:702,&quot;width&quot;:1247,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Building a Minimal Agent Harness in Python&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Building a Minimal Agent Harness in Python" title="Building a Minimal Agent Harness in Python" srcset="https://substackcdn.com/image/fetch/$s_!AJ01!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4165b598-6dc8-4945-bb67-76c9da57df29_1247x702.png 424w, https://substackcdn.com/image/fetch/$s_!AJ01!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4165b598-6dc8-4945-bb67-76c9da57df29_1247x702.png 848w, https://substackcdn.com/image/fetch/$s_!AJ01!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4165b598-6dc8-4945-bb67-76c9da57df29_1247x702.png 1272w, https://substackcdn.com/image/fetch/$s_!AJ01!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4165b598-6dc8-4945-bb67-76c9da57df29_1247x702.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">The harness waits outside the worker. It can stop the worker when the timeout expires.</figcaption></figure></div><p>The example uses asyncio because starting a process and reading from a pipe both involve waiting. A function written with async def can pause at await until an operation finishes. During that pause, asyncio can run another part of the program.</p><p>asyncio.subprocess.PIPE tells Python to connect the worker&#8217;s standard input and standard output to the harness. Those connections give ask() a place to send the request and receive the reply.</p><p>With those ideas in place, the harness code is easier to read. The ask() function follows the same request path shown earlier.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># harness.py
import asyncio
import json
import sys
import uuid


async def ask(message: str, timeout: float = 5.0) -&gt; str:
    process = await asyncio.create_subprocess_exec(
        sys.executable,
        "worker.py",
        stdin=asyncio.subprocess.PIPE,
        stdout=asyncio.subprocess.PIPE,
    )

    request = {
        "id": uuid.uuid4().hex,
        "message": message,
    }
    data = (json.dumps(request) + "\n").encode()

    process.stdin.write(data)
    await process.stdin.drain()

    try:
        line = await asyncio.wait_for(
            process.stdout.readline(),
            timeout,
        )
    except TimeoutError:
        process.kill()
        await process.wait()
        raise RuntimeError("agent timed out")

    await process.wait()
    if not line:
        raise RuntimeError("worker exited without a reply")

    reply = json.loads(line)
    if reply["id"] != request["id"]:
        raise RuntimeError("received the wrong reply")
    if not reply["ok"]:
        raise RuntimeError(reply["error"])

    return reply["value"]
</code></pre></div><h2>Reading ask() from top to bottom</h2><ol><li><p>Start a new Python process that runs worker.py.</p></li><li><p>Create a request and turn it into one JSON line.</p></li><li><p>Write the line to the worker through standard input.</p></li><li><p>Wait for one reply until the timeout expires.</p></li><li><p>Check the reply and return the value or raise an error.</p></li></ol><p><strong>Starting the worker. </strong>asyncio.create_subprocess_exec() asks the operating system to start another program. sys.executable selects the same Python installation that is running the harness. The next argument tells Python to run worker.py.</p><p><strong>Building the request. </strong>uuid.uuid4().hex creates a text id that is very unlikely to repeat. json.dumps() turns the request dictionary into JSON text. encode() changes that text into bytes for the pipe.</p><p><strong>Sending and reading. </strong>drain() waits until Python has handed the request bytes to the pipe. readline() waits for one complete reply line. json.loads() changes the reply back into a Python dictionary.</p><p><strong>Enforcing the timeout. </strong>asyncio.wait_for() stops waiting when the time limit passes and raises TimeoutError. Stopping the wait does not stop the worker. process.kill() is the line that ends it.</p><p><strong>Cleaning up. </strong>process.wait() confirms that the worker has ended. The remaining checks reject a missing reply or the wrong id. RuntimeError passes those failures back to the application as normal Python exceptions.</p><p>The harness now has everything it needs to control one request. The last file shows what the application sees.</p><h1>4. The application asks for an answer</h1><p>main.py stands in for the rest of the application. It does not open pipes or create JSON. It calls ask() with a message and a time limit, then prints the returned answer.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># main.py
import asyncio

from harness import ask


async def main() -&gt; None:
    answer = await ask("hello", timeout=2.0)
    print(answer)


asyncio.run(main())</code></pre></div><p>Keep the four Python files in the same folder. Run python main.py from that folder. The normal result is Agent received: hello. The worker then closes because its one request is complete.</p><p>You can also test the failure path. Add time.sleep(10) at the start of Agent.reply() and set the timeout in main.py to 0.5 seconds. The harness should end the worker and report agent timed out.</p><p>That test is more than a demonstration of the error message. It shows that the application can recover without the agent&#8217;s help. The timeout works because it belongs to the process that can still act.</p><div><hr></div><h1>What changes in a larger system</h1><p>The one-request version is enough to explain the boundary. A production service may need to reuse workers and handle more than one request at a time. Those changes require more tracking, but they do not change the basic roles.</p><h2>Reusing workers</h2><p>Starting Python for every request adds delay. A longer-running service may keep several workers ready. This fixed group of workers is called a pool.</p><p>Each worker should still handle one request at a time. If four workers are ready, the service can run four requests. A fifth request waits or receives a busy response until a worker is free.</p><h2>Recovering after a failure</h2><p>A worker that crashes should not return to the pool. Replace it with a new process because you cannot know which in-memory values remain usable. The request that was running should receive an error instead of waiting forever.</p><p>A retry means sending the same request again after a failure. A retry can help when the cause was brief, but it needs a limit. Stop after a small number of attempts so one bad request cannot create an endless crash loop.</p><p>A reused worker also needs a state. State means the stage the worker is in now. It may be starting or ready. While a request runs it is busy. Once stopping begins it must not accept more work.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lV_6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c39c91e-5897-4e87-9db2-5ab6781c7343_1247x702.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lV_6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c39c91e-5897-4e87-9db2-5ab6781c7343_1247x702.png 424w, https://substackcdn.com/image/fetch/$s_!lV_6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c39c91e-5897-4e87-9db2-5ab6781c7343_1247x702.png 848w, https://substackcdn.com/image/fetch/$s_!lV_6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c39c91e-5897-4e87-9db2-5ab6781c7343_1247x702.png 1272w, https://substackcdn.com/image/fetch/$s_!lV_6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c39c91e-5897-4e87-9db2-5ab6781c7343_1247x702.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lV_6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c39c91e-5897-4e87-9db2-5ab6781c7343_1247x702.png" width="1247" height="702" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9c39c91e-5897-4e87-9db2-5ab6781c7343_1247x702.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:702,&quot;width&quot;:1247,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Building a Minimal Agent Harness in Python&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Building a Minimal Agent Harness in Python" title="Building a Minimal Agent Harness in Python" srcset="https://substackcdn.com/image/fetch/$s_!lV_6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c39c91e-5897-4e87-9db2-5ab6781c7343_1247x702.png 424w, https://substackcdn.com/image/fetch/$s_!lV_6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c39c91e-5897-4e87-9db2-5ab6781c7343_1247x702.png 848w, https://substackcdn.com/image/fetch/$s_!lV_6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c39c91e-5897-4e87-9db2-5ab6781c7343_1247x702.png 1272w, https://substackcdn.com/image/fetch/$s_!lV_6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c39c91e-5897-4e87-9db2-5ab6781c7343_1247x702.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">A reused worker moves through clear states. Its current state decides whether it can accept a request.</figcaption></figure></div><h2>Permissions and untrusted code</h2><p>A separate process isolates memory. It is not a security barrier. By default the worker may still read the application&#8217;s files and use its network access.</p><p>For an agent you trust, give the worker only the settings it needs. Run it with a user account that has limited file and network permissions. This reduces what a mistake can affect.</p><p>For code you do not trust, use a container or another restricted environment. A container can limit which files the program sees and which network connections it may open. Those limits come from the container settings rather than from the Python harness.</p><p>These production controls are easier to add after the one-request design is clear. The same request format can cross into a container or a reused worker.</p><h1>Conclusion</h1><p>An agent harness is the control code around agent behavior. It keeps the application able to respond when agent work crashes or takes too long. The separate worker process gives the harness that control.</p><p>The Python files follow the design rather than defining it. agent.py contains the behavior. worker.py connects that behavior to messages. harness.py owns the process and timeout. main.py only asks for an answer.</p><p>Start with one worker and one request until that flow is reliable. Add pools and retries only when the simpler version is clear. Keep the timeout in the process that can stop the worker.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/building-a-minimal-agent-harness?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/building-a-minimal-agent-harness?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p>]]></content:encoded></item><item><title><![CDATA[Why Valid SQL Returns Wrong Numbers]]></title><description><![CDATA[Three ways a query runs without an error and still returns the wrong number, with the query and the fix for each.]]></description><link>https://www.nb-data.com/p/why-valid-sql-returns-wrong-numbers</link><guid isPermaLink="false">https://www.nb-data.com/p/why-valid-sql-returns-wrong-numbers</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Thu, 09 Jul 2026 14:37:27 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ISen!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9233496d-ee27-4954-9163-7f9c188b8f05_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ISen!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9233496d-ee27-4954-9163-7f9c188b8f05_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ISen!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9233496d-ee27-4954-9163-7f9c188b8f05_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!ISen!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9233496d-ee27-4954-9163-7f9c188b8f05_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!ISen!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9233496d-ee27-4954-9163-7f9c188b8f05_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!ISen!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9233496d-ee27-4954-9163-7f9c188b8f05_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ISen!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9233496d-ee27-4954-9163-7f9c188b8f05_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9233496d-ee27-4954-9163-7f9c188b8f05_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1181779,&quot;alt&quot;:&quot;Why Valid SQL Returns Wrong Numbers&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/206293334?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9233496d-ee27-4954-9163-7f9c188b8f05_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Why Valid SQL Returns Wrong Numbers" title="Why Valid SQL Returns Wrong Numbers" srcset="https://substackcdn.com/image/fetch/$s_!ISen!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9233496d-ee27-4954-9163-7f9c188b8f05_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!ISen!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9233496d-ee27-4954-9163-7f9c188b8f05_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!ISen!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9233496d-ee27-4954-9163-7f9c188b8f05_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!ISen!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9233496d-ee27-4954-9163-7f9c188b8f05_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A query that runs is not necessarily right. Running means the database understood you, nothing more. The syntax can be clean, and the number can still be wrong.</p><p>I have reviewed a lot of SQL, and most wrong numbers come from three places: </p><ul><li><p>A join that inflates a total, </p></li><li><p>A filter that drops rows you meant to keep, and </p></li><li><p>A metric that two people define differently. </p></li></ul><p>All three pass without errors, which is exactly why they were considered correct.</p><p>An agent boosts their frequency by swiftly generating and executing queries, leading to more outputs with less verification. While typing is now a quick and inexpensive step, confirming accuracy still depends on your judgment.</p><p>Below are examples, including the query and the fix, on a small dataset you can try yourself:</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.db-fiddle.com/f/bRv4L2rSCCGEtmUHrNhKsj/0&quot;,&quot;text&quot;:&quot;SQL Fiddle&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.db-fiddle.com/f/bRv4L2rSCCGEtmUHrNhKsj/0"><span>SQL Fiddle</span></a></p><p>Well, curious about it? Let&#8217;s get into it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>1. Joins that inflate a total</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!arHR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730302f4-1372-41a1-adab-8129ff504d06_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!arHR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730302f4-1372-41a1-adab-8129ff504d06_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!arHR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730302f4-1372-41a1-adab-8129ff504d06_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!arHR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730302f4-1372-41a1-adab-8129ff504d06_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!arHR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730302f4-1372-41a1-adab-8129ff504d06_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!arHR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730302f4-1372-41a1-adab-8129ff504d06_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/730302f4-1372-41a1-adab-8129ff504d06_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1024660,&quot;alt&quot;:&quot;Why Valid SQL Returns Wrong Numbers&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/206293334?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730302f4-1372-41a1-adab-8129ff504d06_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Why Valid SQL Returns Wrong Numbers" title="Why Valid SQL Returns Wrong Numbers" srcset="https://substackcdn.com/image/fetch/$s_!arHR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730302f4-1372-41a1-adab-8129ff504d06_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!arHR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730302f4-1372-41a1-adab-8129ff504d06_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!arHR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730302f4-1372-41a1-adab-8129ff504d06_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!arHR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F730302f4-1372-41a1-adab-8129ff504d06_1672x941.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>You are asked for total revenue. You have orders and items, so you join them and add up the amount.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;dockerfile&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-dockerfile">SELECT SUM(o.amount) AS revenue
FROM orders o
JOIN order_items i ON i.order_id = o.order_id; -- 1140</code></pre></div><p>It returns 1140. The real total is 740, and 1140 is not obviously wrong, so it goes into the report.</p><p>A join repeats each row once for every match on the other side. Order 1 has three items, so the join turns it into three rows, each carrying the order&#8217;s amount of 100. Sum the column, and you add that 100 three times. Order 3, with two items, gets counted twice. Eight orders become eleven rows, and the amount for each order is added once per item.</p><p>Revenue lives at the order level, so add it there without the items:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">SELECT SUM(amount) AS revenue FROM orders; -- 740</code></pre></div><p>If you need the items in a single query, roll them up into one row per order first. Either way, count the rows before and after any join. Eight orders should not become eleven rows unless you meant them to.</p><p>Ask: <strong>Does the total change when you remove the join?</strong> If it does, the join is moving the number, and someone should know why, as that number came out too high. </p><p>The next bug does the opposite and is easier to miss.</p><div><hr></div><h2>2. Filters that drop rows you meant to keep</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_EDa!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F985a5247-0b4d-40d7-b2bc-02c565e116c8_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_EDa!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F985a5247-0b4d-40d7-b2bc-02c565e116c8_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!_EDa!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F985a5247-0b4d-40d7-b2bc-02c565e116c8_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!_EDa!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F985a5247-0b4d-40d7-b2bc-02c565e116c8_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!_EDa!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F985a5247-0b4d-40d7-b2bc-02c565e116c8_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_EDa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F985a5247-0b4d-40d7-b2bc-02c565e116c8_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/985a5247-0b4d-40d7-b2bc-02c565e116c8_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:932756,&quot;alt&quot;:&quot;Why Valid SQL Returns Wrong Numbers&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/206293334?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F985a5247-0b4d-40d7-b2bc-02c565e116c8_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Why Valid SQL Returns Wrong Numbers" title="Why Valid SQL Returns Wrong Numbers" srcset="https://substackcdn.com/image/fetch/$s_!_EDa!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F985a5247-0b4d-40d7-b2bc-02c565e116c8_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!_EDa!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F985a5247-0b4d-40d7-b2bc-02c565e116c8_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!_EDa!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F985a5247-0b4d-40d7-b2bc-02c565e116c8_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!_EDa!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F985a5247-0b4d-40d7-b2bc-02c565e116c8_1672x941.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>There are eight orders, and one is canceled, so &#8220;not canceled&#8221; should be seven. This returns six:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">SELECT COUNT(*) FROM orders
WHERE order_status &lt;&gt; &#8216;canceled&#8217;; -- 6</code></pre></div><p>The missing row is the order with no status. When comparing anything to <code>NULL</code>, SQL returns neither true nor false but unknown, and <span>WHERE</span> keeps only what is true. So <code>NULL &lt;&gt;</code><span> &#8216;canceled&#8217;</span> is unknown; the row fails the test, and it drops out. You never chose to exclude it.</p><p>Dates break the same way, and they are harder to spot. Store timestamps in UTC, filter by a local date, and orders near midnight land on the wrong day. A filter for &#8220;all of January&#8221; in local time loses the orders that UTC has already pushed into February, and nothing warns you.</p><p>The fix is to decide what happens to the missing value yourself, instead of letting the comparison decide:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">SELECT COUNT(*) FROM orders
WHERE order_status IS DISTINCT FROM &#8216;canceled&#8217;; -- 7</code></pre></div><p><code>IS DISTINCT FROM</code> compares<code> NULL </code>as a real value, so the row stays. For dates, filter in the zone where the data is stored. In both cases, look at how many rows you kept and dropped before you trust the count.</p><p>Ask: What did the filter keep and drop, and was the missing case handled intentionally?</p><p>The first two bugs were caused by SQL doing something you did not expect. The last one is stranger. The SQL does exactly what each person asked for, and they still disagree.</p><div><hr></div><h2>3. Metrics that two people define differently</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!udEd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d1e355-2fbd-4819-a680-0440ca9f7223_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!udEd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d1e355-2fbd-4819-a680-0440ca9f7223_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!udEd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d1e355-2fbd-4819-a680-0440ca9f7223_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!udEd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d1e355-2fbd-4819-a680-0440ca9f7223_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!udEd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d1e355-2fbd-4819-a680-0440ca9f7223_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!udEd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d1e355-2fbd-4819-a680-0440ca9f7223_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e2d1e355-2fbd-4819-a680-0440ca9f7223_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:937015,&quot;alt&quot;:&quot;Why Valid SQL Returns Wrong Numbers&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/206293334?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d1e355-2fbd-4819-a680-0440ca9f7223_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Why Valid SQL Returns Wrong Numbers" title="Why Valid SQL Returns Wrong Numbers" srcset="https://substackcdn.com/image/fetch/$s_!udEd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d1e355-2fbd-4819-a680-0440ca9f7223_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!udEd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d1e355-2fbd-4819-a680-0440ca9f7223_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!udEd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d1e355-2fbd-4819-a680-0440ca9f7223_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!udEd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2d1e355-2fbd-4819-a680-0440ca9f7223_1672x941.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Two analysts run the same data and report two numbers: 60 percent and 20 percent. Both wrote correct SQL.</p><p>&#8220;Repeat purchase rate&#8221; sounds precise, and it is not. It hides three choices, and the two are made differently. The first counts any customer with two or more orders across all customers:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">SELECT ROUND(100.0 * COUNT(*) FILTER (WHERE orders_count &gt;= 2)
 / COUNT(*), 0) AS repeat_rate_pct
FROM (SELECT customer_id, COUNT(*) AS orders_count
 FROM orders GROUP BY customer_id) c; -- 60</code></pre></div><p>Three of the five customers have a second order, so 60 percent. The other analyst set a higher bar: two delivered orders inside 90 days, over everyone who ordered.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">WITH delivered AS (
 SELECT customer_id, order_ts
 FROM orders WHERE order_status = &#8216;delivered&#8217;
),
repeat_customers AS (
 SELECT d1.customer_id
 FROM delivered d1
 JOIN delivered d2
 ON d1.customer_id = d2.customer_id
 AND d2.order_ts &gt; d1.order_ts
 AND d2.order_ts &lt;= d1.order_ts + INTERVAL &#8216;90 days&#8217;
 GROUP BY d1.customer_id
)
SELECT ROUND(100.0 * COUNT(DISTINCT customer_id)
 / (SELECT COUNT(DISTINCT customer_id) FROM orders), 0) AS repeat_rate_pct
FROM repeat_customers; -- 20</code></pre></div><p>Now one customer qualifies. Of the three with two orders, one had a canceled second order, one ordered again more than 90 days later, and one had two delivered orders close together. So 20 percent.</p><p>The data and skills are identical, yet there's a threefold gap, and neither analyst erred. The key differences are three unrecorded aspects: who is considered a customer, what qualifies as a purchase, and the time window used. A tool cannot resolve these because they relate to the business context, not just the database's capabilities.</p><p>The fix is simple but effective: write the metric as a single sentence that includes its denominator and window before touching SQL. If two people write different sentences, that is the real disagreement, and you found it early.</p><p>Ask: What is the definition, in one sentence, including who counts, over what window, and what is counted?</p><div><hr></div><h2>What to check before you trust a number</h2><p>Most wrong numbers fail in one of three places, so those are the three to check.</p><p><strong>The first is the grain:</strong> what one row represents, and whether a join changed it. The second is the filter: which rows it kept, which it dropped, and how it handled the missing values. The third is the definition: what the metric actually means, written as one sentence that two people would agree on.</p><p>This isn't about rushing SQL development now that it's affordable. Instead, focus on identifying critical areas where a seemingly correct query might still fail, and check them up front before outcomes impact decisions. An agent enhances this process by providing reliable results more quickly than manual checks, although it won't notify you if a result is wrong.</p><p>Don&#8217;t forget that we will have our certified SQL courses soon! Meanwhile, you can also visit our previous SQL written courses. </p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;7c196a7f-5ce4-4620-9010-ae96c4d5c07a&quot;,&quot;caption&quot;:&quot;Hi everyone! Cornellius and Josep Ferrer from DataBites here&#128075;.&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;#1 What is SQL?&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:6000855,&quot;name&quot;:&quot;Cornellius Yudha Wijaya&quot;,&quot;bio&quot;:&quot;Sharing Data Knowledge to improve your values&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/583076b2-657b-44bf-8aa9-9263e5bf04f0_544x544.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null},{&quot;id&quot;:132707413,&quot;name&quot;:&quot;Josep Ferrer&quot;,&quot;bio&quot;:&quot;Outstand using data -- Data Science, Design and Tech Tech Writer @KDnuggets @DataCamp &#128073;&#127995;Inquiries in rfeers@gmail.com&quot;,&quot;photo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd196b5a6-59f2-46dd-99b3-e10ab1bbd27d_604x604.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-03-13T11:01:38.041Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!XU6w!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf0f3527-6b1c-4b72-9ffe-44727c6bb790_1380x962.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.nb-data.com/p/2-what-is-sql&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:158828431,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:17,&quot;comment_count&quot;:0,&quot;publication_id&quot;:37262,&quot;publication_name&quot;:&quot;Non-Brand Data&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!06DP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0e1cde-d120-4029-8ffd-2a8c7c6e4504_1280x1280.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div>]]></content:encoded></item><item><title><![CDATA[My book is out: Python Data Analysis, Fourth Edition]]></title><description><![CDATA[Co-written with my co-author, and what the year of writing it was like.]]></description><link>https://www.nb-data.com/p/my-book-is-out-python-data-analysis</link><guid isPermaLink="false">https://www.nb-data.com/p/my-book-is-out-python-data-analysis</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sat, 04 Jul 2026 10:12:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!vRNE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fe4b43-3d75-470e-9b4e-0f14fce131ca_1280x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vRNE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fe4b43-3d75-470e-9b4e-0f14fce131ca_1280x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vRNE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fe4b43-3d75-470e-9b4e-0f14fce131ca_1280x1024.png 424w, https://substackcdn.com/image/fetch/$s_!vRNE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fe4b43-3d75-470e-9b4e-0f14fce131ca_1280x1024.png 848w, https://substackcdn.com/image/fetch/$s_!vRNE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fe4b43-3d75-470e-9b4e-0f14fce131ca_1280x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!vRNE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fe4b43-3d75-470e-9b4e-0f14fce131ca_1280x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vRNE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fe4b43-3d75-470e-9b4e-0f14fce131ca_1280x1024.png" width="1280" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/99fe4b43-3d75-470e-9b4e-0f14fce131ca_1280x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1351817,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/205033498?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fe4b43-3d75-470e-9b4e-0f14fce131ca_1280x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!vRNE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fe4b43-3d75-470e-9b4e-0f14fce131ca_1280x1024.png 424w, https://substackcdn.com/image/fetch/$s_!vRNE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fe4b43-3d75-470e-9b4e-0f14fce131ca_1280x1024.png 848w, https://substackcdn.com/image/fetch/$s_!vRNE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fe4b43-3d75-470e-9b4e-0f14fce131ca_1280x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!vRNE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99fe4b43-3d75-470e-9b4e-0f14fce131ca_1280x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Python Data Analysis, Fourth Edition, which I wrote with my co-author, is out today. You can get it here: </p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://packt.link/MhuEn&quot;,&quot;text&quot;:&quot;Book Link&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://packt.link/MhuEn"><span>Book Link</span></a></p><p>Before I describe what&#8217;s in it, I want to write down what the year of working on it was like.</p><h2>The work</h2><p>My co-author and I started this over a year ago. I&#8217;ve worked in data for years and I know the material, so I expected the writing to be straightforward. It wasn&#8217;t.</p><p>Knowing something and explaining it in a book are different jobs. Every example has to run. Every explanation has to make sense to someone reading it for the first time. And you have to keep doing that, chapter after chapter, for months. This book has 17 of them.</p><h2>Writing with anxiety</h2><p>I&#8217;ll be honest about this part. There were stretches of the year when my anxiety was high. Some days, opening the manuscript felt harder than it should have.</p><p>What worked was not waiting to feel better before writing. On bad days I wrote a paragraph, sometimes a bad one, and fixed it later. The manuscript kept moving, and imperfect pages slowly became a book.</p><p>If you&#8217;re building something while dealing with your own anxiety, this is the only advice I have: <strong>keep the work moving, even in small pieces.</strong></p><h2>The collaboration</h2><p>The other thing that carried this book was working with a co-author. When one of us was stuck, the other pushed forward. When one of us wrote something unclear, the other caught it. We each brought different expertise, and the book shows it.</p><h2>What&#8217;s in the book</h2><p>The full title is <strong>Python Data Analysis: Master Python Analytics with Machine Learning, Deep Learning, GenAI, LLMs, and Data Engineering.</strong></p><p>Modern data analysis goes beyond cleaning and visualizing data. Practitioners today build scalable pipelines, apply machine learning, work with text and image data, and use Generative AI and LLMs. Rather than focus on a single library, the book covers the whole workflow:</p><ul><li><p>Foundations: Python libraries, NumPy, pandas, statistics, linear algebra, and visualization</p></li><li><p>Data work: retrieving, processing, storing, and cleaning messy data</p></li><li><p>Time-series analysis, forecasting, and signal processing</p></li><li><p>Supervised and unsupervised learning: regression, classification, clustering, dimensionality reduction, and anomaly detection</p></li><li><p>Ensemble methods, neural networks, and deep learning</p></li><li><p>Text and image analytics, including NLP and sentiment analysis</p></li><li><p>LLMs and Generative AI</p></li><li><p>Scaling with Dask, Modin, Ray, and PySpark</p></li></ul><p>By the end, you can build end-to-end data analysis pipelines and apply modern data science and AI techniques to real problems.</p><p>It&#8217;s written for data analysts, data scientists, business analysts, statisticians, and students. You&#8217;ll need basic math and working knowledge of Python.</p><p>Thank you</p><p>Get the book here: </p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://packt.link/MhuEn&quot;,&quot;text&quot;:&quot;Book Link&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://packt.link/MhuEn"><span>Book Link</span></a></p><p>If you read it, tell me what you think. I read every reply and comment.</p>]]></content:encoded></item><item><title><![CDATA[Fix it or throw it away: deciding what to do with an AI output]]></title><description><![CDATA[The harder part is knowing which problems you can fix and which ones mean starting over.]]></description><link>https://www.nb-data.com/p/fix-it-or-throw-it-away-deciding</link><guid isPermaLink="false">https://www.nb-data.com/p/fix-it-or-throw-it-away-deciding</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Tue, 30 Jun 2026 15:33:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!x66w!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a95ad1b-d3cc-4aff-9a30-fa4cb3738788_1731x909.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!x66w!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a95ad1b-d3cc-4aff-9a30-fa4cb3738788_1731x909.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!x66w!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a95ad1b-d3cc-4aff-9a30-fa4cb3738788_1731x909.png 424w, https://substackcdn.com/image/fetch/$s_!x66w!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a95ad1b-d3cc-4aff-9a30-fa4cb3738788_1731x909.png 848w, https://substackcdn.com/image/fetch/$s_!x66w!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a95ad1b-d3cc-4aff-9a30-fa4cb3738788_1731x909.png 1272w, https://substackcdn.com/image/fetch/$s_!x66w!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a95ad1b-d3cc-4aff-9a30-fa4cb3738788_1731x909.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!x66w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a95ad1b-d3cc-4aff-9a30-fa4cb3738788_1731x909.png" width="1456" height="765" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8a95ad1b-d3cc-4aff-9a30-fa4cb3738788_1731x909.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:765,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1031328,&quot;alt&quot;:&quot;Fix it or throw it away: deciding what to do with an AI output&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/204222302?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a95ad1b-d3cc-4aff-9a30-fa4cb3738788_1731x909.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Fix it or throw it away: deciding what to do with an AI output" title="Fix it or throw it away: deciding what to do with an AI output" srcset="https://substackcdn.com/image/fetch/$s_!x66w!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a95ad1b-d3cc-4aff-9a30-fa4cb3738788_1731x909.png 424w, https://substackcdn.com/image/fetch/$s_!x66w!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a95ad1b-d3cc-4aff-9a30-fa4cb3738788_1731x909.png 848w, https://substackcdn.com/image/fetch/$s_!x66w!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a95ad1b-d3cc-4aff-9a30-fa4cb3738788_1731x909.png 1272w, https://substackcdn.com/image/fetch/$s_!x66w!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a95ad1b-d3cc-4aff-9a30-fa4cb3738788_1731x909.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>An AI tool writes a summary of last quarter&#8217;s sales. Most numbers match the source data, but one sentence says a marketing campaign &#8220;drove&#8221; the revenue lift, and nothing supports that. Fixing it takes thirty seconds: soften the wording or cut the sentence.</p><p>Now picture the same summary, just as fluent, except it used the wrong table the whole time. It used gross revenue when the question was about net. Every figure agrees with the others, and every figure is wrong.</p><p>Both drafts look the same on screen. One needs a light edit. The other needs to be deleted. Telling those two cases apart, fast, is the skill. The decision is always one of three: use, revise, or reject. What matters is the test underneath it, because that test is where most reviewers go wrong.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>The three options</h2><p>Every output you review ends in one of three actions.</p><ul><li><p><strong>Use. </strong>It&#8217;s good enough for its next step, even if some polish is left.</p></li><li><p><strong>Revise. </strong>The foundation is sound, but there are fixable flaws worth correcting.</p></li><li><p><strong>Reject. </strong>The foundation itself is broken, so fixing it means rebuilding from scratch.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nGsu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3565ae0-f667-44e2-8358-7f6bdd170c3c_2172x724.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nGsu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3565ae0-f667-44e2-8358-7f6bdd170c3c_2172x724.png 424w, https://substackcdn.com/image/fetch/$s_!nGsu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3565ae0-f667-44e2-8358-7f6bdd170c3c_2172x724.png 848w, https://substackcdn.com/image/fetch/$s_!nGsu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3565ae0-f667-44e2-8358-7f6bdd170c3c_2172x724.png 1272w, https://substackcdn.com/image/fetch/$s_!nGsu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3565ae0-f667-44e2-8358-7f6bdd170c3c_2172x724.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nGsu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3565ae0-f667-44e2-8358-7f6bdd170c3c_2172x724.png" width="1456" height="485" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b3565ae0-f667-44e2-8358-7f6bdd170c3c_2172x724.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:485,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1035296,&quot;alt&quot;:&quot;Fix it or throw it away: deciding what to do with an AI output&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/204222302?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3565ae0-f667-44e2-8358-7f6bdd170c3c_2172x724.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Fix it or throw it away: deciding what to do with an AI output" title="Fix it or throw it away: deciding what to do with an AI output" srcset="https://substackcdn.com/image/fetch/$s_!nGsu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3565ae0-f667-44e2-8358-7f6bdd170c3c_2172x724.png 424w, https://substackcdn.com/image/fetch/$s_!nGsu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3565ae0-f667-44e2-8358-7f6bdd170c3c_2172x724.png 848w, https://substackcdn.com/image/fetch/$s_!nGsu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3565ae0-f667-44e2-8358-7f6bdd170c3c_2172x724.png 1272w, https://substackcdn.com/image/fetch/$s_!nGsu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3565ae0-f667-44e2-8358-7f6bdd170c3c_2172x724.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>It&#8217;s three actions, not a score. A score tells you how good something looks, not what to do with it. A draft can look like a nine and still belong in the trash, because the one wrong part is what everything else rests on.</p><p>So the question is never &#8220;how good is this?&#8221; It&#8217;s: can the problem be fixed, or does it sink the whole thing?</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/fix-it-or-throw-it-away-deciding?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/fix-it-or-throw-it-away-deciding?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p></p><div><hr></div><h2>Small flaws and broken foundations</h2><p>Here is the rule the rest of this article depends on.</p><div class="pullquote"><p><em>Revise when the foundation is sound but the work is incomplete. Reject when the foundation itself is wrong.</em></p></div><p>A small flaw is one you can fix without touching the core logic, data, or reasoning. Typos, a missing citation when you know the source, a wrong chart label, a caveat that doesn&#8217;t change the conclusion, awkward variable names. None of these threaten what&#8217;s underneath. You fix them and move on.</p><p>A broken foundation invalidates the whole output. The wrong dataset or metric. A source that doesn&#8217;t say what&#8217;s claimed, or doesn&#8217;t exist. Reasoning you can&#8217;t trace. Code that runs the wrong algorithm or hardcodes a credential. An answer to a different question than the one asked. You can&#8217;t patch these. Every line below the flaw inherits it.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ThB0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4334ea-f5f2-4ab9-b16e-79450f773728_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ThB0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4334ea-f5f2-4ab9-b16e-79450f773728_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!ThB0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4334ea-f5f2-4ab9-b16e-79450f773728_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!ThB0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4334ea-f5f2-4ab9-b16e-79450f773728_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!ThB0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4334ea-f5f2-4ab9-b16e-79450f773728_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ThB0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4334ea-f5f2-4ab9-b16e-79450f773728_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8c4334ea-f5f2-4ab9-b16e-79450f773728_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1241858,&quot;alt&quot;:&quot;Fix it or throw it away: deciding what to do with an AI output&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/204222302?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4334ea-f5f2-4ab9-b16e-79450f773728_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Fix it or throw it away: deciding what to do with an AI output" title="Fix it or throw it away: deciding what to do with an AI output" srcset="https://substackcdn.com/image/fetch/$s_!ThB0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4334ea-f5f2-4ab9-b16e-79450f773728_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!ThB0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4334ea-f5f2-4ab9-b16e-79450f773728_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!ThB0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4334ea-f5f2-4ab9-b16e-79450f773728_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!ThB0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c4334ea-f5f2-4ab9-b16e-79450f773728_1672x941.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>The trap:</strong> how bad a flaw is has little to do with how big the fix looks. A broken foundation can be a one-character change, like swapping gross for net. A small flaw can mean rewriting three paragraphs. People reach for reject when the fix looks expensive and use when it looks cheap. That&#8217;s backwards. The question isn&#8217;t how much text changes. It&#8217;s whether the wrong part holds up everything else.</p><div><hr></div><h2>Two checks before you score anything</h2><p>You can triage most outputs in under a minute with two questions, before any detailed review.</p><p><strong>First, is there an automatic reject condition?</strong> Invented citations, confidential or out-of-scope data, a policy violation, a badly wrong headline number. If yes, stop and reject. No point scoring the writing on a draft that already failed.</p><p><strong>Second, can you verify it?</strong> Can you re-run the code, recompute the figure, open every cited source, trace the retrieved context? If you can&#8217;t check a claim that matters, don&#8217;t approve it because it reads well. These models produce text that sounds right, which is not the same as being right.</p><p>Only drafts that pass both checks are worth a full use-versus-revise read.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QqF6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2800480e-73ac-4150-a2b1-f3cab536e631_1122x1402.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QqF6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2800480e-73ac-4150-a2b1-f3cab536e631_1122x1402.png 424w, https://substackcdn.com/image/fetch/$s_!QqF6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2800480e-73ac-4150-a2b1-f3cab536e631_1122x1402.png 848w, https://substackcdn.com/image/fetch/$s_!QqF6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2800480e-73ac-4150-a2b1-f3cab536e631_1122x1402.png 1272w, https://substackcdn.com/image/fetch/$s_!QqF6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2800480e-73ac-4150-a2b1-f3cab536e631_1122x1402.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QqF6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2800480e-73ac-4150-a2b1-f3cab536e631_1122x1402.png" width="1122" height="1402" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2800480e-73ac-4150-a2b1-f3cab536e631_1122x1402.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1402,&quot;width&quot;:1122,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:983307,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/204222302?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2800480e-73ac-4150-a2b1-f3cab536e631_1122x1402.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QqF6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2800480e-73ac-4150-a2b1-f3cab536e631_1122x1402.png 424w, https://substackcdn.com/image/fetch/$s_!QqF6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2800480e-73ac-4150-a2b1-f3cab536e631_1122x1402.png 848w, https://substackcdn.com/image/fetch/$s_!QqF6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2800480e-73ac-4150-a2b1-f3cab536e631_1122x1402.png 1272w, https://substackcdn.com/image/fetch/$s_!QqF6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2800480e-73ac-4150-a2b1-f3cab536e631_1122x1402.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The same logic in code:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:null}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">The same logic in code:
if has_critical_violation(output):
    decision = "Reject"
elif not is_verifiable(output):
    decision = "Reject"
else:
    decision = assess(output)   # now decide Use vs Revise
</code></pre></div><div><hr></div><h2>What &#8220;good enough&#8221; means</h2><p>Use doesn&#8217;t mean perfect. It means good enough for the next step, and that bar moves with where the output is going. The same draft can be a use as a private note and a revise as a summary you send to a client. Code that&#8217;s fine in a scratch notebook needs real review before production. The bar is highest when an output triggers an action on its own, like sending a notification, because no one stands between it and the result.</p><p>So name the next step before you decide. An output is a use only when no remaining problem is big enough to block that step.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/fix-it-or-throw-it-away-deciding/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/fix-it-or-throw-it-away-deciding/comments"><span>Leave a comment</span></a></p><div><hr></div><h2>Triage by type of output</h2><p>The line between a small flaw and a broken foundation looks different by output type. Concrete triggers help.</p><p><strong>Data analysis. </strong>Use it when the dataset and definitions are right, the numbers reconcile, and the logic matches the question. Revise when a causal claim overreaches or a caveat is missing, since that&#8217;s fixable with a note while the core data and formulas stay sound. Reject when the metric, grain, or population is wrong, or the question was misread, because every conclusion is then suspect. Wrong data, population, or metric is always a reject.</p><p><strong>Code generation. </strong>Use it when it runs, passes tests, follows your conventions, and solves the right problem. Revise when it works but needs error handling, better names, or more test coverage. Reject when it uses the wrong algorithm, violates security, hardcodes secrets, or won&#8217;t run. &#8220;Looks correct&#8221; isn&#8217;t enough; run it before you trust it.</p><p><strong>Retrieved answers. </strong>Use it when every factual claim maps to a retrieved passage and the system says &#8220;I don&#8217;t know&#8221; when evidence is thin. Revise when the right documents were pulled but the wording is off or one relevant passage was missed. Reject when it invents facts, cites wrong or fabricated sources, or answers off-context. Keep retrieval and generation separate: if the context is wrong, reject the answer and fix the retrieval.</p><p><strong>Dashboards and BI. </strong>Use it when the numbers match approved sources, periods and KPIs are consistent, and the interpretation is careful. Revise when the figures are right but the narrative misses a comparison or footnote. Reject when a headline number can&#8217;t be traced or filters mix fiscal periods. One wrong headline number spoils the report.</p><p>The pattern holds across all four. Style and polish are revise work. Broken substance is reject work. When the wrong part is in the foundation, the data, the retrieval, the algorithm, or the question itself, the answer is reject, no matter how clean the surface looks.</p><div><hr></div><h2>The mistake to watch: editing what you should reject</h2><p>The expensive mistake isn&#8217;t rejecting too much. It&#8217;s revising what you should have rejected.</p><p>Each round of edits makes a flawed output look better. The writing tightens, the code reads cleaner, and all of it polishes a wrong foundation into something more convincing and harder to catch. A run of reasonable fixes can keep a big error in place and make it look authoritative.</p><p>If 90% of a report is good but the core assumption is wrong, reject it. If 10% of otherwise-sound code is missing, revise it. The share that changes is a distraction. What matters is whether you&#8217;re polishing something true or dressing up something false.</p><p>A simple check: by the third edit on the same output, stop and ask whether you&#8217;re fixing flaws or hiding one.</p><div><hr></div><h2>Conclusion</h2><p>The framework is small on purpose: three outcomes and one test underneath them. When an AI output lands on your desk, don&#8217;t grade it. Ask whether the thing that&#8217;s wrong sits in the foundation or on the surface.</p><p>Surface flaws are revise work. A broken foundation is a reject, however polished it looks, and no amount of editing will fix it. Run the two gates first, name the next step before you decide, and keep a short trail so the same problem doesn&#8217;t come back.</p><p><strong>Stop asking &#8220;how good is this output?&#8221;</strong> Ask &#8220;can the problem be fixed, or does it sink the whole thing?&#8221; Use, revise, and reject are the three answers, and getting the question right is most of the skill.</p><p></p>]]></content:encoded></item><item><title><![CDATA[Why Most GenAI Workflows Need a Review Loop]]></title><description><![CDATA[A Better Prompt Is Not Enough]]></description><link>https://www.nb-data.com/p/why-most-genai-workflows-need-a-review</link><guid isPermaLink="false">https://www.nb-data.com/p/why-most-genai-workflows-need-a-review</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Wed, 24 Jun 2026 15:09:43 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!pW8I!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce8959d-5645-4a93-bd1f-244333fdd64e_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pW8I!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce8959d-5645-4a93-bd1f-244333fdd64e_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pW8I!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce8959d-5645-4a93-bd1f-244333fdd64e_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!pW8I!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce8959d-5645-4a93-bd1f-244333fdd64e_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!pW8I!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce8959d-5645-4a93-bd1f-244333fdd64e_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!pW8I!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce8959d-5645-4a93-bd1f-244333fdd64e_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pW8I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce8959d-5645-4a93-bd1f-244333fdd64e_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bce8959d-5645-4a93-bd1f-244333fdd64e_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:919446,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/203414836?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce8959d-5645-4a93-bd1f-244333fdd64e_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!pW8I!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce8959d-5645-4a93-bd1f-244333fdd64e_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!pW8I!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce8959d-5645-4a93-bd1f-244333fdd64e_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!pW8I!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce8959d-5645-4a93-bd1f-244333fdd64e_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!pW8I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbce8959d-5645-4a93-bd1f-244333fdd64e_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Most teams spend their time improving the prompt. They reword the instructions, add examples, or switch to a stronger model, and the output usually improves. But there is a limit to this. A prompt only produces one attempt. The model writes a draft and stops. It does not tell you whether the draft is correct, and it does not improve on its own the next time you run it.</p><p>In an <a href="https://www.nb-data.com/p/what-most-genai-evaluation-workflows">earlier article</a>, I argued that reliability depends on the entire workflow, not just the final answer, and that you should check the workflow at each step rather than only the end result. Evaluation is useful, but it only measures how good an output is. It does not improve the output. To improve the output, you need a review loop: a repeatable process that checks the result, identifies what went wrong, fixes it, and records what you learned so the next run improves. The rest of this article explains the idea and then builds one in Python that you can run today.</p><blockquote><p><strong>In short<span>,&nbsp;</span></strong><span>evaluation</span> measures how good an output is. A review loop is the process that improves it.</p></blockquote><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>A prompt cannot check its own work</h2><p>A single prompt is one pass through the model. It cannot check its own work, it does not remember the previous run, and it has no rule for deciding when the task is complete. In practice, the model is finished when it stops producing text, which is not the same as being correct.</p><p>Anthropic describes a more reliable setup called the evaluator-optimizer pattern. One model writes the draft, and a second model grades it against clear criteria. The draft is then revised until it meets those criteria. Anthropic recommends this when you have clear criteria and when revising the output actually improves it, which covers most real work. Its Claude Code guidance makes a related point: if the system cannot check its work against something concrete, such as a test, a schema, or a second reviewer, then deciding whether the work is done becomes a matter of opinion, and a person has to check everything by hand.</p><p>OpenAI gives similar advice. Reliable quality comes from clear goals, test datasets, automated graders, and ongoing evaluation, rather than from a single well-written prompt. Reliability is something you build a process around, not something you reach through wording alone.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/why-most-genai-workflows-need-a-review?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/why-most-genai-workflows-need-a-review?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div><h2>Common ways the first draft goes wrong</h2><p>A draft can read well and still be wrong. This happens because of how the models are trained. Below are three common problems. The model cannot detect any of them on its own, which is why an outside check is needed.</p><ul><li><p><strong>Models tend to guess.<span> </span></strong>They are trained to produce text that sounds plausible, not text that has been verified. <em>TruthfulQA</em> found that models often repeat common misconceptions. OpenAI&#8217;s 2025 research on hallucinations offers one reason: most training rewards confident answers more than admitting uncertainty, so the model learns to guess rather than say it does not know.</p></li><li><p><strong>Models can overlook the middle of a long input.<span> </span></strong>Even with large context windows, they do not treat every part of the input equally. The <em>Lost in the Middle</em> research found that information in the middle of a long input is often underused, while information at the start and end gets more weight. For summaries and retrieval-based systems, an important detail can be present in the input but missing from the output.</p></li><li><p><strong>Models tend to agree with the user.<span> </span></strong>Research on sycophancy found that several assistants often match the user&#8217;s stated opinion, and that people sometimes prefer an agreeable answer to a correct one. This matters at work, where prompts often suggest the answer the writer expects. If you ask whether the data supports your plan, the model is more likely to agree than to disagree.</p></li></ul><blockquote><p><strong><span>Takeaway: </span></strong>These are normal limits of how the models work. A review loop is there to catch the mistakes the model cannot catch on its own.</p></blockquote><div><hr></div><h2>The four-stage review loop</h2><p>The review loop has four stages. It applies to ordinary documents as well as to code.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YhiF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d4d57a1-5ee3-4840-bb03-0700bbcb7790_2040x1062.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YhiF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d4d57a1-5ee3-4840-bb03-0700bbcb7790_2040x1062.png 424w, https://substackcdn.com/image/fetch/$s_!YhiF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d4d57a1-5ee3-4840-bb03-0700bbcb7790_2040x1062.png 848w, https://substackcdn.com/image/fetch/$s_!YhiF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d4d57a1-5ee3-4840-bb03-0700bbcb7790_2040x1062.png 1272w, https://substackcdn.com/image/fetch/$s_!YhiF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d4d57a1-5ee3-4840-bb03-0700bbcb7790_2040x1062.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YhiF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d4d57a1-5ee3-4840-bb03-0700bbcb7790_2040x1062.png" width="1456" height="758" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2d4d57a1-5ee3-4840-bb03-0700bbcb7790_2040x1062.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:758,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:113775,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/203414836?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d4d57a1-5ee3-4840-bb03-0700bbcb7790_2040x1062.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!YhiF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d4d57a1-5ee3-4840-bb03-0700bbcb7790_2040x1062.png 424w, https://substackcdn.com/image/fetch/$s_!YhiF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d4d57a1-5ee3-4840-bb03-0700bbcb7790_2040x1062.png 848w, https://substackcdn.com/image/fetch/$s_!YhiF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d4d57a1-5ee3-4840-bb03-0700bbcb7790_2040x1062.png 1272w, https://substackcdn.com/image/fetch/$s_!YhiF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d4d57a1-5ee3-4840-bb03-0700bbcb7790_2040x1062.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><em><span>Figure 1. The review loop. Output that passes goes straight to capture; output that needs work is revised and run again, and each recorded failure raises the baseline for the next run.</span></em></figcaption></figure></div><ol><li><p><strong>Generate.<span> </span></strong>Produce the first draft, however your workflow does it: a prompt chain, retrieval, or tool calls.</p></li><li><p><strong>Review.<span> </span></strong>Check the draft with code first, then with a second model, and send the hard cases to a person. The claims, the structure, the process that produced it, and safety are all fair game.</p></li><li><p><strong>Revise.<span> </span></strong>Fix the problems found in the review. You can fix the output itself, or fix the workflow that produced it. Re-running with feedback fixes the current draft. Fixing the instruction or the retrieval step also improves future drafts, which has a larger effect over time.</p></li><li><p><strong>Capture.<span> </span></strong>This stage is often skipped, but it is what makes the loop worth running. Record each failure in a reusable form, such as a test case, an evaluation example, a rule, or a guardrail. OpenAI recommends expanding your evaluation set using real production data. Anthropic describes the shift from repeatedly fixing the same problem to a system in which each failure becomes a test that prevents it from recurring.</p></li></ol><blockquote><p><strong><span>Takeaway: </span></strong>If you find a failure but do not record it, the same problem can return on a later run.</p></blockquote><div class="directMessage button" data-attrs="{&quot;userId&quot;:6000855,&quot;userName&quot;:&quot;Cornellius Yudha Wijaya&quot;,&quot;canDm&quot;:null,&quot;dmUpgradeOptions&quot;:null,&quot;isEditorNode&quot;:true}" data-component-name="DirectMessageToDOM"></div><div><hr></div><h2>Building it: clean data from a document</h2><p>Here is the loop applied to a common task: pulling fields out of a messy invoice into validated JSON. The output looks fine at a glance, which is exactly when a review loop earns its place. The code is provider-agnostic, so replace <span>call_model</span> with your own client and install <span>jsonschema</span>.</p><p>Start with the input. This is the kind of text a model reads easily but gets subtly wrong:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;b534b819-5dc9-40cf-893d-1ebb43f060b8&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">INVOICE    Acme Industrial Supply
Inv #: AC-10827      Date: 03/14/2025
Bill to: Northwind Trading
 
  2 x Steel bracket (SB-12) ............ 24.00
 10 x Hex bolt M8 @ 3.50 ea ........... 35.00
  1 x Safety gloves, pair .............. 12.75
 
Subtotal: 71.75
Tax (8%): 5.74
TOTAL DUE: 77.49</code></pre></div><p>The setup is your model call and a few imports:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;c50a08b8-a2d9-4f47-8fa6-eb5cf92946a1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">"""Structured extraction with a review loop (provider-agnostic).
Needs the standard library plus jsonschema (pip install jsonschema).
Replace call_model with your own client (OpenAI, Anthropic, ...).
"""
import json
import re
from datetime import date
from jsonschema import validate, ValidationError
 
 
def call_model(prompt: str) -&gt; str:
    """Send the prompt to your model and return its text reply."""
    raise NotImplementedError</code></pre></div><p>A schema fixes the shape, so structure and types are checked for free:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;b20971ee-5fdd-4a08-93c6-e0776ed76b90&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext"># The shape we require. The schema checks structure and types for free.
SCHEMA = {
    "type": "object",
    "required": ["invoice_number", "date", "vendor", "customer",
                 "line_items", "subtotal", "tax", "total"],
    "properties": {
        "invoice_number": {"type": "string"},
        "date": {"type": "string"},
        "vendor": {"type": "string"},
        "customer": {"type": "string"},
        "line_items": {"type": "array", "items": {
            "type": "object",
            "required": ["description", "quantity",
                         "unit_price", "amount"],
            "properties": {
                "description": {"type": "string"},
                "quantity": {"type": "number"},
                "unit_price": {"type": "number"},
                "amount": {"type": "number"},
            },
        }},
        "subtotal": {"type": "number"},
        "tax": {"type": "number"},
        "total": {"type": "number"},
    },
}
</code></pre></div><p>Deterministic checks run first because they are cheap and exact. They catch the arithmetic and the invented numbers that a fluent answer hides:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;64f7480a-9ea5-4414-bf0e-2336f913405e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># Deterministic checks: cheap, exact, and run first. These catch the
# arithmetic and made-up numbers that a fluent draft hides.
def near(a, b, tol=0.01):
    return abs(a - b) &lt;= tol
 
 
def check_extraction(data, source):
    issues = []
    for item in data["line_items"]:
        if not near(item["quantity"] * item["unit_price"], item["amount"]):
            issues.append("line '%s': qty x price != amount"
                          % item["description"])
    if not near(sum(i["amount"] for i in data["line_items"]),
                data["subtotal"]):
        issues.append("line amounts do not sum to subtotal")
    if not near(data["subtotal"] + data["tax"], data["total"]):
        issues.append("subtotal + tax != total")
    for field in ("invoice_number", "total"):
        if str(data[field]) not in source:
            issues.append("%s '%s' is not in the document"
                          % (field, data[field]))
    try:
        date.fromisoformat(data["date"])
    except ValueError:
        issues.append("date '%s' is not ISO format" % data["date"])
    return issues
</code></pre></div><p>Some mistakes are not arithmetic. A second model called reviews the extraction against the source and catches things like swapped names:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;b3b6cfa3-5386-4e16-addb-f82f70129d07&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># Independent reviewer: a second model call catches what code cannot,
# such as a vendor and customer that have been swapped.
JUDGE = (
    "You verify an invoice extraction against the SOURCE.\n"
    "Reply ONLY with JSON: {\"passed\": bool, \"issues\": [string]}.\n"
    "Check that names, descriptions, and roles match the document."
)
 
 
def judge_extraction(source, data):
    prompt = (JUDGE + "\n\nSOURCE:\n" + source
              + "\n\nEXTRACTION:\n" + json.dumps(data, indent=2))
    return json.loads(call_model(prompt))</code></pre></div><p>Capture writes every failure to a file you can replay later:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;174d5b66-c631-4c9c-a8b6-51c793fb51cc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># Capture: every failure becomes a row you can replay later as a test.
def capture(source, data, issues, path="eval_set.jsonl"):
    row = {"source": source, "output": data, "issues": issues}
    with open(path, "a") as handle:
        handle.write(json.dumps(row) + "\n")</code></pre></div><p>The loop puts it together: generate, run the cheap checks, then the judge, capture failures, revise, and stop after a few tries:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;bcea2255-cd3f-4f69-b553-1aa620e87f45&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># The loop: generate, review (code, then judge), capture, revise, repeat.
EXTRACT = (
    "Extract this invoice as JSON matching the agreed schema. "
    "Use ISO dates (YYYY-MM-DD) and copy every number exactly.\n\n"
    "INVOICE:\n"
)
 
 
def extract_invoice(source, max_tries=3):
    prompt = EXTRACT + source
    for attempt in range(1, max_tries + 1):
        print("Attempt %d" % attempt)
        try:
            data = json.loads(call_model(prompt))       # generate
            validate(instance=data, schema=SCHEMA)
            issues = check_extraction(data, source)      # review: code
        except (json.JSONDecodeError, ValidationError) as error:
            data, issues = {}, ["invalid output: " + str(error)[:50]]
        print("  code checks:", "FAIL" if issues else "PASS")
 
        if not issues:                                   # review: judge
            verdict = judge_extraction(source, data)
            issues = [] if verdict["passed"] else verdict["issues"]
            print("  judge:", "FAIL" if issues else "PASS")
 
        if not issues:
            print("  accepted")
            return data
 
        for problem in issues:
            print("    - " + problem)
        capture(source, data, issues)                    # capture
        prompt = (EXTRACT + source + "\n\nYour last answer had:\n"
                  + "\n".join("- " + p for p in issues)
                  + "\n\nReturn corrected JSON.")         # revise
 
    raise RuntimeError("still failing; escalate to a human")</code></pre></div><p>Running it on the invoice above produces the trace below. The first draft is fluent but wrong in two different ways:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;fdb82fdd-256e-49fa-be16-17fe16c2904d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">Attempt 1
  code checks: FAIL
    - subtotal + tax != total
    - total '77.94' is not in the document
Attempt 2
  code checks: PASS
  judge: FAIL
    - vendor/customer swapped: seller is Acme Industrial Supply
Attempt 3
  code checks: PASS
  judge: PASS
  accepted</code></pre></div><p>Attempt 1 fails the cheap checks: the total does not match subtotal plus tax, and 77.94 never appears in the document. Attempt 2 fixes the math, but the judge notices the vendor and customer are swapped. Attempt 3 passes both and is accepted. These are the failure modes from earlier in action: a confident wrong number, then a confident wrong label, both caught before release. Two failures were written to <code>eval_set.jsonl </code>on the way.</p><p>Those captured failures are not just logs. Replay them as tests, and a bug you have already fixed cannot quietly come back:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;f7319622-c1ad-423e-bce7-2c3c13a3575e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># regression.py: replay captured failures; they should pass now.
import json
from rloop import extract_invoice, check_extraction
 
 
def test_known_failures_now_pass():
    for line in open("eval_set.jsonl"):
        case = json.loads(line)
        fixed = extract_invoice(case["source"])
        assert check_extraction(fixed, case["source"]) == []</code></pre></div><div><hr></div><h2>Three kinds of review</h2><p>That example used all three kinds of review, and most real systems need them together.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!APO7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde9b3bd9-afd3-49c6-943d-6ee6a2457ee7_2040x1130.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!APO7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde9b3bd9-afd3-49c6-943d-6ee6a2457ee7_2040x1130.png 424w, https://substackcdn.com/image/fetch/$s_!APO7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde9b3bd9-afd3-49c6-943d-6ee6a2457ee7_2040x1130.png 848w, https://substackcdn.com/image/fetch/$s_!APO7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde9b3bd9-afd3-49c6-943d-6ee6a2457ee7_2040x1130.png 1272w, https://substackcdn.com/image/fetch/$s_!APO7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde9b3bd9-afd3-49c6-943d-6ee6a2457ee7_2040x1130.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!APO7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde9b3bd9-afd3-49c6-943d-6ee6a2457ee7_2040x1130.png" width="1456" height="807" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/de9b3bd9-afd3-49c6-943d-6ee6a2457ee7_2040x1130.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:807,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:179934,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/203414836?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde9b3bd9-afd3-49c6-943d-6ee6a2457ee7_2040x1130.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!APO7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde9b3bd9-afd3-49c6-943d-6ee6a2457ee7_2040x1130.png 424w, https://substackcdn.com/image/fetch/$s_!APO7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde9b3bd9-afd3-49c6-943d-6ee6a2457ee7_2040x1130.png 848w, https://substackcdn.com/image/fetch/$s_!APO7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde9b3bd9-afd3-49c6-943d-6ee6a2457ee7_2040x1130.png 1272w, https://substackcdn.com/image/fetch/$s_!APO7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde9b3bd9-afd3-49c6-943d-6ee6a2457ee7_2040x1130.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><em><span>Figure 2. The three kinds of review. Code catches structural errors cheaply, an independent reviewer catches reasoning the author missed, and a person has the final say on high-stakes decisions.</span></em></figcaption></figure></div><p><strong>Code</strong> handled everything with a clear answer: the schema, the arithmetic, the date format, and the check that numbers came from the source. It is fast and exact, and it caught the bad total. Anthropic, OpenAI, and Microsoft all treat these checks as the base of a good pipeline.</p><p><strong>An independent reviewer</strong>, a separate model call with its own rubric, caught the swapped names that code cannot judge. <em>Self-Refine</em>, <em>CRITIC</em>, and <em>Reflexion</em> all report real gains from this kind of separate critique. One caution from my earlier article: a model reviewer has their own biases, so check it against human judgment from time to time rather than trusting it on its own.</p><p><strong>A person with authority</strong> is the last step, for high-stakes or subjective cases. In the code that is raised at the end, when the loop cannot pass on its own, it stops and hands off instead of shipping. NIST&#8217;s Generative AI Profile recommends that pre-deployment testing reach the people who decide what gets released.</p><blockquote><p><strong><span>Takeaway: </span></strong>One layer of review is rarely enough. Using code, an independent reviewer, and a person together covers more types of error.</p></blockquote><div><hr></div><h2>What repeatability actually means</h2><p>It is worth being clear about what repeatability means here. It does not mean the model produces the same text every time, because it is not deterministic. It means the quality stays within a predictable range even as the wording changes. </p><p>The loop does not remove that variation, but it keeps the results within known limits. In practice, this lets you describe the worst case in advance. The schema check will not allow malformed output, the arithmetic check will not allow a total that does not add up, and the grounding check will not allow a number that is not in the source. The wording can vary; those guarantees do not.</p><div><hr></div><h2>Review is becoming a compliance requirement</h2><p>There is also a practical reason beyond quality. Review and oversight are increasingly expected. NIST&#8217;s Generative AI Profile asks teams to verify sources before and after launch, monitor for issues that require human attention, and keep records. Microsoft&#8217;s code of conduct for its AI services requires ongoing testing, feedback channels, and human oversight, and does not permit significant decisions without it. The EU AI Act requires that people be told when they are interacting with AI or reading AI-generated content.</p><p>Several well-known cases show what can happen without review. Each one failed in a different way:</p><ul><li><p><strong>CNET.<span> </span></strong>A review of its AI-written finance articles found that 41 of 77 needed corrections. The articles were published because no one checked them first.</p></li><li><p><strong>Air Canada.<span> </span></strong>A tribunal held the airline responsible after its chatbot described a refund policy that did not exist, and rejected the argument that the chatbot was a separate entity.</p></li><li><p><strong>Avianca.<span> </span></strong>Lawyers were sanctioned for submitting a court filing that cited cases that did not exist.</p></li></ul><p>In each case, the writing looked fine. What was missing was a review step before the work was released.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/why-most-genai-workflows-need-a-review/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/why-most-genai-workflows-need-a-review/comments"><span>Leave a comment</span></a></p><div><hr></div><h2>Conclusion</h2><p>The main point is straightforward. Evaluation tells you how good an output is. A review loop is how you improve it. The four stages, the three kinds of review, and the governance requirements are all parts of that process.</p><p>As the example shows, the loop is mostly ordinary code: a schema, a few exact checks, a second opinion, and a record of past failures. The useful change is to spend less time refining the prompt and more time on the surrounding process: what gets checked, who can reject the output, what counts as a failure, when you rerun it, and how each failure becomes a permanent check. With those pieces in place, generative AI can move from producing one-off drafts to supporting a process you can rely on.</p>]]></content:encoded></item><item><title><![CDATA[Manager Memo: Reviewing GenAI Output Before It Reaches Stakeholders]]></title><description><![CDATA[A guide for managers who approve AI-assisted work: what to check, when to reject, and how to talk to stakeholders about it.]]></description><link>https://www.nb-data.com/p/manager-memo-reviewing-genai-output</link><guid isPermaLink="false">https://www.nb-data.com/p/manager-memo-reviewing-genai-output</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sat, 20 Jun 2026 03:21:50 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!tase!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5be6e8-9936-41ee-98f2-d7d08a61e791_1550x866.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tase!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5be6e8-9936-41ee-98f2-d7d08a61e791_1550x866.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tase!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5be6e8-9936-41ee-98f2-d7d08a61e791_1550x866.png 424w, https://substackcdn.com/image/fetch/$s_!tase!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5be6e8-9936-41ee-98f2-d7d08a61e791_1550x866.png 848w, https://substackcdn.com/image/fetch/$s_!tase!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5be6e8-9936-41ee-98f2-d7d08a61e791_1550x866.png 1272w, https://substackcdn.com/image/fetch/$s_!tase!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5be6e8-9936-41ee-98f2-d7d08a61e791_1550x866.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tase!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5be6e8-9936-41ee-98f2-d7d08a61e791_1550x866.png" width="1456" height="813" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bf5be6e8-9936-41ee-98f2-d7d08a61e791_1550x866.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:813,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:116875,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/202718894?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5be6e8-9936-41ee-98f2-d7d08a61e791_1550x866.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!tase!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5be6e8-9936-41ee-98f2-d7d08a61e791_1550x866.png 424w, https://substackcdn.com/image/fetch/$s_!tase!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5be6e8-9936-41ee-98f2-d7d08a61e791_1550x866.png 848w, https://substackcdn.com/image/fetch/$s_!tase!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5be6e8-9936-41ee-98f2-d7d08a61e791_1550x866.png 1272w, https://substackcdn.com/image/fetch/$s_!tase!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf5be6e8-9936-41ee-98f2-d7d08a61e791_1550x866.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="pullquote"><p>The output looks polished. That is the problem. Polished prose hides weak evidence, and the approving manager, not the model, owns what happens next.</p></div><p>You receive a briefing note, a slide deck, or a client-facing summary. Your team tells you AI helped write it. It reads cleanly: the structure is logical, the tone is confident, the citations look authoritative. The temptation is to skim it, nod, and forward it on.</p><p><strong>Resist that. </strong>Fluent prose is not evidence of accurate content. Generative models are optimized to produce text that <em>sounds</em> right, not text that <em>is</em> right, and the gap between the two is exactly where stakeholder-facing work goes wrong. </p><p>This playbook gives you a structured way to close that gap: a three-layer review gate, a use / revise/reject decision rule, the failure modes worth memorizing, and templates you can adopt this week.</p><p>The core principle, drawn from where every major governance framework converges: <strong><span data-color="rgb(10, 30, 60)" style="color: rgb(10, 30, 60);">treat stakeholder-facing GenAI output as a high-variance draft, not as finished work.</span></strong></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p>
      <p>
          <a href="https://www.nb-data.com/p/manager-memo-reviewing-genai-output">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[What Most GenAI Evaluation Workflows Get Wrong]]></title><description><![CDATA[Reliability is not a property of the final answer. It is a property of the whole system that produced it.]]></description><link>https://www.nb-data.com/p/what-most-genai-evaluation-workflows</link><guid isPermaLink="false">https://www.nb-data.com/p/what-most-genai-evaluation-workflows</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sun, 14 Jun 2026 05:51:53 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!4RDG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75ad100a-a6ca-4cd4-b877-2ac68d3b510b_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4RDG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75ad100a-a6ca-4cd4-b877-2ac68d3b510b_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4RDG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75ad100a-a6ca-4cd4-b877-2ac68d3b510b_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!4RDG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75ad100a-a6ca-4cd4-b877-2ac68d3b510b_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!4RDG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75ad100a-a6ca-4cd4-b877-2ac68d3b510b_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!4RDG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75ad100a-a6ca-4cd4-b877-2ac68d3b510b_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4RDG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75ad100a-a6ca-4cd4-b877-2ac68d3b510b_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/75ad100a-a6ca-4cd4-b877-2ac68d3b510b_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1319052,&quot;alt&quot;:&quot;What Most GenAI Evaluation Workflows Get Wrong&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/201945949?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75ad100a-a6ca-4cd4-b877-2ac68d3b510b_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="What Most GenAI Evaluation Workflows Get Wrong" title="What Most GenAI Evaluation Workflows Get Wrong" srcset="https://substackcdn.com/image/fetch/$s_!4RDG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75ad100a-a6ca-4cd4-b877-2ac68d3b510b_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!4RDG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75ad100a-a6ca-4cd4-b877-2ac68d3b510b_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!4RDG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75ad100a-a6ca-4cd4-b877-2ac68d3b510b_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!4RDG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75ad100a-a6ca-4cd4-b877-2ac68d3b510b_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I keep seeing the same mistake. A team hooks up a model, eyeballs a handful of outputs, decides it looks fine, and ships. Evaluation only enters the picture after something breaks in production, and by then the team is firefighting, bolting on test cases one incident at a time, always a step behind.</p><p>The issue is not that they forgot to evaluate. It is that they put the eval in the wrong place. They scored the final answer without looking at what produced it. But in any system that retrieves documents, routes intents, calls tools, or chains reasoning steps, the answer is the last place a bug shows up&#8212;and almost never where it starts. A bad retrieval, a misrouted query, or a wrong tool argument can all produce something that reads beautifully and is completely wrong.</p><blockquote><p><em>Evaluation belongs inside the workflow, not after it. GenAI evals should look less like one big pass/fail test and more like layered quality gates.</em></p></blockquote><p>This is not just my opinion. Official guidance from OpenAI, Anthropic, Google Cloud, Microsoft Foundry, LangSmith, and NIST all point the same way: evaluate routing, retrieval, tool use, grounding, safety, and drift&#8212;not just the final paragraph.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>Why the Final Answer Is Usually Not the Root Cause</h2><p>When something goes wrong, the reflex is to blame the model. But in most real-world setups, the model is just one piece of a longer pipeline. There are at least five places where things can quietly go sideways before the answer even gets generated:</p><p>&#8226; Prompt construction and intent routing</p><p>&#8226; Document retrieval and context assembly</p><p>&#8226; Tool selection and parameter extraction</p><p>&#8226; Agent handoff and orchestration logic</p><p>&#8226; Post-processing and output formatting</p><p>Every major platform now says the same thing. OpenAI recommends scoped tests at every stage. Microsoft says you need to evaluate each step, not just the final output. Google separates response evaluation from trajectory evaluation. Ragas breaks RAG scoring into retrieval metrics and generation metrics. The consensus is clear.</p><blockquote><p><em>Outcome-only evaluation is a scoreboard. Workflow-native evaluation is an instrument panel.</em></p></blockquote><p>End-to-end success rates still matter as the top-level KPI&#8212;but they are terrible debugging tools. Anthropic puts it well: agent mistakes compound, so you need to inspect every intermediate step to figure out whether the failure came from the model, the tooling, the harness, or the eval itself.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WFZ9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5317d519-04b8-4c0a-8a45-0c7ca6354724_2172x724.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WFZ9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5317d519-04b8-4c0a-8a45-0c7ca6354724_2172x724.png 424w, https://substackcdn.com/image/fetch/$s_!WFZ9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5317d519-04b8-4c0a-8a45-0c7ca6354724_2172x724.png 848w, https://substackcdn.com/image/fetch/$s_!WFZ9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5317d519-04b8-4c0a-8a45-0c7ca6354724_2172x724.png 1272w, https://substackcdn.com/image/fetch/$s_!WFZ9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5317d519-04b8-4c0a-8a45-0c7ca6354724_2172x724.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WFZ9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5317d519-04b8-4c0a-8a45-0c7ca6354724_2172x724.png" width="1456" height="485" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5317d519-04b8-4c0a-8a45-0c7ca6354724_2172x724.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:485,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1285920,&quot;alt&quot;:&quot;What Most GenAI Evaluation Workflows Get Wrong&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/201945949?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5317d519-04b8-4c0a-8a45-0c7ca6354724_2172x724.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="What Most GenAI Evaluation Workflows Get Wrong" title="What Most GenAI Evaluation Workflows Get Wrong" srcset="https://substackcdn.com/image/fetch/$s_!WFZ9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5317d519-04b8-4c0a-8a45-0c7ca6354724_2172x724.png 424w, https://substackcdn.com/image/fetch/$s_!WFZ9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5317d519-04b8-4c0a-8a45-0c7ca6354724_2172x724.png 848w, https://substackcdn.com/image/fetch/$s_!WFZ9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5317d519-04b8-4c0a-8a45-0c7ca6354724_2172x724.png 1272w, https://substackcdn.com/image/fetch/$s_!WFZ9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5317d519-04b8-4c0a-8a45-0c7ca6354724_2172x724.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><em>Figure 1: How output-only and workflow-native evaluation compare across key dimensions.</em></figcaption></figure></div><div><hr></div><h2>Where Evaluation Actually Belongs</h2><p>The most useful eval setups start by breaking the system into stages and writing checks that match what each stage is supposed to do. Here are the five checkpoints that matter:</p><p>&#8226; <strong>Input &amp; Routing &#8212; </strong>Did we understand the user&#8217;s intent? Did we catch injection attempts? Did we route to the right handler?</p><p>&#8226; <strong>Retrieval &amp; Context &#8212; </strong>Did we pull the right documents? Are they relevant? Is the context complete enough to answer the question?</p><p>&#8226; <strong>Tool Use &amp; Planning &#8212; </strong>Did the agent pick the right tool? Are the arguments valid? Did it follow a reasonable path to get there?</p><p>&#8226; <strong>Generation &#8212; </strong>Is the answer correct, complete, and grounded in the evidence? Is it safe? Does it know when to say &#8220;I don&#8217;t know&#8221;</p><p>&#8226; <strong>Production Monitoring &#8212; </strong>How fast is it? How much does it cost? Is quality drifting? Are users flagging problems?</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!gvLG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33b01765-49e5-4b1f-a6af-0b2de52177b8_2172x724.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!gvLG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33b01765-49e5-4b1f-a6af-0b2de52177b8_2172x724.png 424w, https://substackcdn.com/image/fetch/$s_!gvLG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33b01765-49e5-4b1f-a6af-0b2de52177b8_2172x724.png 848w, https://substackcdn.com/image/fetch/$s_!gvLG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33b01765-49e5-4b1f-a6af-0b2de52177b8_2172x724.png 1272w, https://substackcdn.com/image/fetch/$s_!gvLG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33b01765-49e5-4b1f-a6af-0b2de52177b8_2172x724.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!gvLG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33b01765-49e5-4b1f-a6af-0b2de52177b8_2172x724.png" width="1456" height="485" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/33b01765-49e5-4b1f-a6af-0b2de52177b8_2172x724.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:485,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1049427,&quot;alt&quot;:&quot;What Most GenAI Evaluation Workflows Get Wrong&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/201945949?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33b01765-49e5-4b1f-a6af-0b2de52177b8_2172x724.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="What Most GenAI Evaluation Workflows Get Wrong" title="What Most GenAI Evaluation Workflows Get Wrong" srcset="https://substackcdn.com/image/fetch/$s_!gvLG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33b01765-49e5-4b1f-a6af-0b2de52177b8_2172x724.png 424w, https://substackcdn.com/image/fetch/$s_!gvLG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33b01765-49e5-4b1f-a6af-0b2de52177b8_2172x724.png 848w, https://substackcdn.com/image/fetch/$s_!gvLG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33b01765-49e5-4b1f-a6af-0b2de52177b8_2172x724.png 1272w, https://substackcdn.com/image/fetch/$s_!gvLG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F33b01765-49e5-4b1f-a6af-0b2de52177b8_2172x724.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><em>Figure 2: Each stage has its own eval checkpoint, with a feedback loop from production back into the test data.</em></figcaption></figure></div><p>If this sounds like a lot of work, it does not have to be, at least not on day one. The practical move is risk-weighted decomposition. Pick the two or three user journeys that matter most, write clear success criteria for each, and start with a small curated dataset. Anthropic, OpenAI, and LangSmith all recommend beginning with 10-20 high-quality examples and growing from real failures. That keeps things lightweight while still making problems visible.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/what-most-genai-evaluation-workflows?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/what-most-genai-evaluation-workflows?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div><h2>Picking Metrics That Match How Things Actually Break</h2><p>The golden rule: use the cheapest reliable check first. Start with deterministic graders: exact match, schema validation, regex, policy checks, citation lookups. Only bring in LLM judges when you need to score something fuzzy like helpfulness or completeness.</p><p>What to track depends on your system type:</p><ul><li><p><strong>RAG systems: </strong>context precision, context recall, grounding, faithfulness. Track retrieval and generation as separate scores.</p></li><li><p><strong>Agent systems: </strong>tool selection accuracy, argument validity, task completion rate, trajectory precision, and recall.</p></li><li><p><strong>Any system: </strong>correctness, completeness, safety, abstention quality. Keep these as separate dimensions&#8212;one overall number hides too much.</p></li></ul><p><strong>A warning about LLM-as-judge: </strong>yes, LLM judges can scale evaluation and hit human-level agreement on some tasks. But research (MT-Bench, the &#8220;Large Language Models are not Fair Evaluators&#8221; paper) shows they are prone to position bias, verbosity bias, and self-enhancement bias. Just swapping the order of two responses can flip the ranking. The fix is not to avoid LLM judges; it is to calibrate them. Use pairwise comparisons, pass/fail rubrics with clear criteria, and periodically check agreement against human labels. Targeted human calibration beats full human scoring.</p><div class="directMessage button" data-attrs="{&quot;userId&quot;:6000855,&quot;userName&quot;:&quot;Cornellius Yudha Wijaya&quot;,&quot;canDm&quot;:null,&quot;dmUpgradeOptions&quot;:null,&quot;isEditorNode&quot;:true}" data-component-name="DirectMessageToDOM"></div><div><hr></div><h2>A Six-Step Playbook for CI and Production</h2><p>Evals that live only in notebooks are evals that rot. Here is how to make them part of your development and deployment loop:</p><ol><li><p><strong>Instrument traces early. </strong>From the first real prototype, capture everything: input, system prompt, routing decision, retrieved docs, tool calls, results, final answer, latency, cost, feedback. Without traces, you can evaluate only the output, not the workflow.</p></li><li><p><strong>Start with a small, sharp dataset. </strong>10-20 curated examples per critical path. Include happy paths, edge cases, known failure modes, and adversarial inputs. Quality beats quantity here.</p></li><li><p><strong>Layer your graders. </strong>Deterministic checks first (schema validation, regex, policy filters). LLM judges second (for nuanced criteria like completeness). Sampled human review third (for calibration and high-stakes cases).</p></li><li><p><strong>Wire evals into CI/CD. </strong>When prompts, retrievers, tool schemas, or orchestration logic change, run component-level and end-to-end evals automatically. Fail the build if pass rates drop.</p></li><li><p><strong>Add online evaluation after deploy. </strong>Score a sample of production traces for quality, safety, and cost. Route low scores and negative feedback into annotation queues. Feed confirmed failures back into your offline dataset.</p></li><li><p><strong>Schedule security scans. </strong>Red-team prompts, poisoned-context tests, and injection probes should run on a regular cadence, not just at deploy time.</p></li></ol><p>This is the pattern you will find across LangSmith, MLflow, Microsoft Foundry, and Promptfoo. It is where evaluation stops being a one-off check and becomes a continuous part of how the system runs.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!WAbx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6194ac07-e1cc-482f-9f98-879c7ca6e606_1000x1340.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!WAbx!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6194ac07-e1cc-482f-9f98-879c7ca6e606_1000x1340.png 424w, https://substackcdn.com/image/fetch/$s_!WAbx!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6194ac07-e1cc-482f-9f98-879c7ca6e606_1000x1340.png 848w, https://substackcdn.com/image/fetch/$s_!WAbx!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6194ac07-e1cc-482f-9f98-879c7ca6e606_1000x1340.png 1272w, https://substackcdn.com/image/fetch/$s_!WAbx!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6194ac07-e1cc-482f-9f98-879c7ca6e606_1000x1340.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!WAbx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6194ac07-e1cc-482f-9f98-879c7ca6e606_1000x1340.png" width="1000" height="1340" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6194ac07-e1cc-482f-9f98-879c7ca6e606_1000x1340.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1340,&quot;width&quot;:1000,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;What Most GenAI Evaluation Workflows Get Wrong&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="What Most GenAI Evaluation Workflows Get Wrong" title="What Most GenAI Evaluation Workflows Get Wrong" srcset="https://substackcdn.com/image/fetch/$s_!WAbx!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6194ac07-e1cc-482f-9f98-879c7ca6e606_1000x1340.png 424w, https://substackcdn.com/image/fetch/$s_!WAbx!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6194ac07-e1cc-482f-9f98-879c7ca6e606_1000x1340.png 848w, https://substackcdn.com/image/fetch/$s_!WAbx!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6194ac07-e1cc-482f-9f98-879c7ca6e606_1000x1340.png 1272w, https://substackcdn.com/image/fetch/$s_!WAbx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6194ac07-e1cc-482f-9f98-879c7ca6e606_1000x1340.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption"><em>Figure 3: A framework-agnostic Python function showing layered evaluation across all pipeline stages.</em></figcaption></figure></div><div><hr></div><h2>What Happens When Evals Only Check the Surface</h2><p><strong>Air Canada&#8217;s chatbot (Moffatt v. Air Canada). </strong>A customer asked about bereavement fares and got incorrect guidance from the airline&#8217;s chatbot. Air Canada tried to distance itself from the bot&#8217;s answer, but the tribunal was not having it&#8212;the chatbot was part of the company&#8217;s website, and the airline was responsible for what it said.</p><p>This was not a hallucination problem. It was a missing-controls problem. A workflow-level eval would have checked chatbot responses against the actual policy documents, flagged contradictions, required evidence for refund-related answers, and routed anything ambiguous to a human. A style-level review of the output would have missed all of this&#8212;the answer probably read just fine.</p><p><strong>Fabricated case law (Mata v. Avianca). </strong>Lawyers submitted court filings with fake case citations generated by ChatGPT. The court sanctioned them, ordered letters of explanation to the judges who were falsely cited, and imposed a $5,000 fine.</p><p>The output sounded professional. The problem is that nobody checked whether the cited cases actually existed. For any system that conducts research or cites sources, evaluation needs to cover source resolution, citation validity, and handling missing evidence. A confident answer with fabricated support should count as a failure, even if it scores well on fluency.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/what-most-genai-evaluation-workflows/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/what-most-genai-evaluation-workflows/comments"><span>Leave a comment</span></a></p><div><hr></div><h2>Conclusion</h2><p>Better prompts and better models help. But what actually makes GenAI systems reliable is treating evaluation as part of the workflow; not something you bolt on at the end. Checkpoints before generation, around tool use, before delivery, and after deployment through tracing and feedback.</p><div class="callout-block" data-callout="true"><p><em>The playbook: pick 3-5 user journeys. Define what success looks like. Build 10-20 test cases. Use cheap graders first. Wire evals into CI. Sample production traces. The tools already exist. The question is whether your team uses them.</em></p></div><p>The remaining question is simple: does your team treat evaluation as something the system does all the time, or something someone does to the system after the fact?</p>]]></content:encoded></item><item><title><![CDATA[From Prompt to Reliable Output: A Practical GenAI Evaluation Workflow]]></title><description><![CDATA[A concise guide to achieve success in your production GenAI generation]]></description><link>https://www.nb-data.com/p/from-prompt-to-reliable-output-a</link><guid isPermaLink="false">https://www.nb-data.com/p/from-prompt-to-reliable-output-a</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sun, 31 May 2026 07:47:25 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!C4_t!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17ba1e8e-e774-41a5-9a84-1f3ef25ddf9b_1672x941.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!C4_t!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17ba1e8e-e774-41a5-9a84-1f3ef25ddf9b_1672x941.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!C4_t!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17ba1e8e-e774-41a5-9a84-1f3ef25ddf9b_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!C4_t!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17ba1e8e-e774-41a5-9a84-1f3ef25ddf9b_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!C4_t!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17ba1e8e-e774-41a5-9a84-1f3ef25ddf9b_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!C4_t!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17ba1e8e-e774-41a5-9a84-1f3ef25ddf9b_1672x941.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!C4_t!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17ba1e8e-e774-41a5-9a84-1f3ef25ddf9b_1672x941.png" width="1456" height="819" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/17ba1e8e-e774-41a5-9a84-1f3ef25ddf9b_1672x941.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:819,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1763111,&quot;alt&quot;:&quot;From Prompt to Reliable Output: A Practical GenAI Evaluation Workflow&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/199950174?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17ba1e8e-e774-41a5-9a84-1f3ef25ddf9b_1672x941.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="From Prompt to Reliable Output: A Practical GenAI Evaluation Workflow" title="From Prompt to Reliable Output: A Practical GenAI Evaluation Workflow" srcset="https://substackcdn.com/image/fetch/$s_!C4_t!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17ba1e8e-e774-41a5-9a84-1f3ef25ddf9b_1672x941.png 424w, https://substackcdn.com/image/fetch/$s_!C4_t!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17ba1e8e-e774-41a5-9a84-1f3ef25ddf9b_1672x941.png 848w, https://substackcdn.com/image/fetch/$s_!C4_t!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17ba1e8e-e774-41a5-9a84-1f3ef25ddf9b_1672x941.png 1272w, https://substackcdn.com/image/fetch/$s_!C4_t!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F17ba1e8e-e774-41a5-9a84-1f3ef25ddf9b_1672x941.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Prompt engineering is rarely enough to prepare a GenAI system for production.</p><p>While a single prompt can generate a good output in initial testing, deploying it across hundreds of real-world inputs exposes failures like missing details, incorrect facts, and formatting errors.</p><p>To build a production-ready system, you must transition from prompt optimization to systematic evaluation.</p><p><strong>A prompt defines your request; an evaluation workflow defines your standard and proves whether the system meets it.</strong></p><p>Here is a practical workflow to evaluate and improve GenAI applications.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SqQo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25a0c054-22b7-478a-b984-bc90bf81e7bf_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SqQo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25a0c054-22b7-478a-b984-bc90bf81e7bf_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!SqQo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25a0c054-22b7-478a-b984-bc90bf81e7bf_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!SqQo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25a0c054-22b7-478a-b984-bc90bf81e7bf_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!SqQo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25a0c054-22b7-478a-b984-bc90bf81e7bf_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SqQo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25a0c054-22b7-478a-b984-bc90bf81e7bf_1024x1024.png" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/25a0c054-22b7-478a-b984-bc90bf81e7bf_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:704000,&quot;alt&quot;:&quot;From Prompt to Reliable Output: A Practical GenAI Evaluation Workflow&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/199950174?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25a0c054-22b7-478a-b984-bc90bf81e7bf_1024x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="From Prompt to Reliable Output: A Practical GenAI Evaluation Workflow" title="From Prompt to Reliable Output: A Practical GenAI Evaluation Workflow" srcset="https://substackcdn.com/image/fetch/$s_!SqQo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25a0c054-22b7-478a-b984-bc90bf81e7bf_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!SqQo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25a0c054-22b7-478a-b984-bc90bf81e7bf_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!SqQo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25a0c054-22b7-478a-b984-bc90bf81e7bf_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!SqQo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25a0c054-22b7-478a-b984-bc90bf81e7bf_1024x1024.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h1>1. The Limits of Initial Testing</h1><p>It is easy to mistake a fluent LLM output for a correct one.</p><p>If a response uses a professional tone and has no grammatical errors, we assume it is accurate.</p><p>However, LLMs are non-deterministic, and a prompt that works for one test case can fail on another.</p><p>In production, incorrect outputs carry significant risks&#8212;whether it is a fabricated metric in an executive report or an incorrect policy in a customer support tool.</p><p>Rather than testing under ideal conditions, developers must identify where a system fails under real-world inputs.</p><div><hr></div><h1>2. Why Prompting Alone is Insufficient</h1><p>A well-crafted prompt is not a testing framework.</p><p>Relying entirely on prompts to ensure quality is insufficient for three reasons:</p><ul><li><p><strong>Input Variability:</strong> Real-world queries are often messy, incomplete, or poorly formatted.</p></li><li><p><strong>Model Variability</strong>: Even at temperature 0.0, models can generate slightly different outputs for the same input.</p></li><li><p><strong>System Dependencies</strong>: In complex architectures like Retrieval-Augmented Generation (RAG), the LLM is only one component. A prompt cannot correct a failure in the retrieval step.</p></li></ul><p>A polished output does not guarantee a reliable system.</p><p><strong>Instead of relying solely on prompt adjustments, you need a structured evaluation workflow.</strong></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/from-prompt-to-reliable-output-a?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/from-prompt-to-reliable-output-a?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div><h1>3. The 7-Step Evaluation Workflow</h1><p>To ensure predictable GenAI behavior, teams should implement a repeatable seven-step evaluation workflow.</p><ul><li><p>Step 1: Define the task in operational terms.</p></li><li><p>Step 2: Build a representative evaluation set.</p></li><li><p>Step 3: Break evaluation into specific dimensions.</p></li><li><p>Step 4: Choose the right grading method.</p></li><li><p>Step 5: Log failures and classify the errors.</p></li><li><p>Step 6: Modify one variable at a time.</p></li><li><p>Step 7: Define production thresholds.</p></li></ul><h2><strong>Step 1: Define the task in operational terms</strong></h2><p>You must translate vague requirements into objective criteria. For example, instead of asking for &#8220;a good summary,&#8221; define the exact parameters:</p><ul><li><p><strong>KPI Commentary:</strong> Accurate metrics, no causal claims unless explicitly backed by the data source, a concise tone, and a maximum of 150 words.</p></li><li><p><strong>SQL Explainer:</strong> Explains joins and filters correctly in plain language, linking them to a specific KPI.</p></li><li><p><strong>Customer RAG:</strong> Answers using only the provided context, cites sources, and states &#8220;I do not know&#8221; if context is missing.</p></li></ul><p>Identify the specific tasks, target audience, constraints, and failures that would render an output unusable.</p><h2><strong>Step 2: Build a representative evaluation set</strong></h2><p>Start with a small evaluation set of 10 to 30 examples. A massive evaluation set is difficult to manage early in development. The set must reflect real-world inputs rather than just ideal cases. It should contain:</p><ul><li><p><strong>Standard inputs:</strong> Common queries to test baseline functionality.</p></li><li><p><strong>Complex/Ambiguous inputs:</strong> Requests with mixed sentiments or multi-step instructions.</p></li><li><p><strong>Edge cases:</strong> Inputs with missing context or specific formatting constraints.</p></li><li><p><strong>High-risk inputs:</strong> Scenarios where errors have significant business or legal impacts.</p></li></ul><p>For example, the initial evaluation set might include for customer evaluation could be like below:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ta6_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3adcdc60-a092-425e-bedd-2932b35bc875_1165x400.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ta6_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3adcdc60-a092-425e-bedd-2932b35bc875_1165x400.png 424w, https://substackcdn.com/image/fetch/$s_!ta6_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3adcdc60-a092-425e-bedd-2932b35bc875_1165x400.png 848w, https://substackcdn.com/image/fetch/$s_!ta6_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3adcdc60-a092-425e-bedd-2932b35bc875_1165x400.png 1272w, https://substackcdn.com/image/fetch/$s_!ta6_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3adcdc60-a092-425e-bedd-2932b35bc875_1165x400.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ta6_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3adcdc60-a092-425e-bedd-2932b35bc875_1165x400.png" width="1165" height="400" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3adcdc60-a092-425e-bedd-2932b35bc875_1165x400.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:400,&quot;width&quot;:1165,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:59672,&quot;alt&quot;:&quot;From Prompt to Reliable Output: A Practical GenAI Evaluation Workflow&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/199950174?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3adcdc60-a092-425e-bedd-2932b35bc875_1165x400.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="From Prompt to Reliable Output: A Practical GenAI Evaluation Workflow" title="From Prompt to Reliable Output: A Practical GenAI Evaluation Workflow" srcset="https://substackcdn.com/image/fetch/$s_!ta6_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3adcdc60-a092-425e-bedd-2932b35bc875_1165x400.png 424w, https://substackcdn.com/image/fetch/$s_!ta6_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3adcdc60-a092-425e-bedd-2932b35bc875_1165x400.png 848w, https://substackcdn.com/image/fetch/$s_!ta6_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3adcdc60-a092-425e-bedd-2932b35bc875_1165x400.png 1272w, https://substackcdn.com/image/fetch/$s_!ta6_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3adcdc60-a092-425e-bedd-2932b35bc875_1165x400.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Step 3: Break evaluation into specific dimensions</strong></h2><p>Avoid grading outputs with a single overall score, as it blends separate failure modes. Instead, assess performance across specific dimensions:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZHH6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa068874c-d646-4cbe-87e9-1e0a07d0a305_1168x684.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZHH6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa068874c-d646-4cbe-87e9-1e0a07d0a305_1168x684.png 424w, https://substackcdn.com/image/fetch/$s_!ZHH6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa068874c-d646-4cbe-87e9-1e0a07d0a305_1168x684.png 848w, https://substackcdn.com/image/fetch/$s_!ZHH6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa068874c-d646-4cbe-87e9-1e0a07d0a305_1168x684.png 1272w, https://substackcdn.com/image/fetch/$s_!ZHH6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa068874c-d646-4cbe-87e9-1e0a07d0a305_1168x684.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZHH6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa068874c-d646-4cbe-87e9-1e0a07d0a305_1168x684.png" width="1168" height="684" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a068874c-d646-4cbe-87e9-1e0a07d0a305_1168x684.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:684,&quot;width&quot;:1168,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:98810,&quot;alt&quot;:&quot;From Prompt to Reliable Output: A Practical GenAI Evaluation Workflow&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/199950174?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa068874c-d646-4cbe-87e9-1e0a07d0a305_1168x684.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="From Prompt to Reliable Output: A Practical GenAI Evaluation Workflow" title="From Prompt to Reliable Output: A Practical GenAI Evaluation Workflow" srcset="https://substackcdn.com/image/fetch/$s_!ZHH6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa068874c-d646-4cbe-87e9-1e0a07d0a305_1168x684.png 424w, https://substackcdn.com/image/fetch/$s_!ZHH6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa068874c-d646-4cbe-87e9-1e0a07d0a305_1168x684.png 848w, https://substackcdn.com/image/fetch/$s_!ZHH6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa068874c-d646-4cbe-87e9-1e0a07d0a305_1168x684.png 1272w, https://substackcdn.com/image/fetch/$s_!ZHH6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa068874c-d646-4cbe-87e9-1e0a07d0a305_1168x684.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Select the dimensions relevant to your application. A RAG system focuses on Grounding and Completeness, while a code generation tool focuses on Task Success and Format Fidelity.</p><h2><strong>Step 4: Choose the right grading method</strong></h2><p>Use the simplest method that provides accurate results. You can grade outputs using four primary approaches:</p><ol><li><p><strong>Rule-Based Checks: </strong>Programmatic, deterministic, and highly reliable. Ideal for formatting and constraints (e.g., verifying JSON schema or character counts).</p></li><li><p><strong>Reference-Based Checks: </strong>Used when there is a ground-truth answer (e.g., comparing classification labels or verifying generated SQL output against a reference database query).</p></li><li><p><strong>LLM-as-a-Judge: </strong>Used for semantic or stylistic dimensions like tone and factual consistency at scale. These require a strict grading rubric and few-shot examples to maintain consistency.</p></li><li><p><strong>Human Review: </strong>Recommended for highly sensitive or high-impact tasks. Spot-checks by domain experts are also used to calibrate and validate automated LLM judges.</p></li></ol><h2><strong>Step 5: Log failures and classify the errors</strong></h2><p>When a test case fails, identify the root cause before changing variables. Failure in a GenAI system does not always stem from the prompt.</p><p>Classify errors into specific categories:</p><ul><li><p><strong>Prompt Issue:</strong> Instructions are vague or contain conflicting constraints.</p></li><li><p><strong>Retrieval Issue:</strong> The context provided to the model is incomplete, irrelevant, or outdated.</p></li><li><p><strong>Data Issue</strong>: The underlying data source contains incorrect or corrupted information.</p></li><li><p><strong>Model Issue:</strong> The model ignores instructions or generates incorrect claims despite correct prompts and context.</p></li><li><p><strong>Requirements Issue:</strong> The operational criteria for the task were poorly defined.</p></li></ul><p>For example, a retrieval failure cannot be fixed by editing the prompt, and a data quality issue cannot be resolved by upgrading the model. Fix the issue at its source.</p><h2><strong>Step 6: Modify one variable at a time</strong></h2><p>When optimizing the system, change only one variable at a time to isolate what improves or degrades performance.</p><p>Follow this process:</p><ol><li><p><strong>Establish a baseline:</strong> Run your current evaluation set.</p></li><li><p><strong>Identify failures:</strong> Audit failed cases to determine the primary error type.</p></li><li><p><strong>Isolate a single change:</strong> Modify exactly one parameter (e.g., update a prompt rule, adjust chunk size, or change the model temperature).</p></li><li><p><strong>Rerun and compare:</strong> Run the evaluation set again and compare results against the baseline to verify improvement.</p></li></ol><h2><strong>Step 7: Define production thresholds</strong></h2><p>Establish clear metric thresholds to determine if the system is ready to deploy. For subjective dimensions, a standard 1-to-5 rubric is useful:</p><ul><li><p>5 &#8212; Accurate, fully grounded in context, and correctly formatted.</p></li><li><p>4 &#8212; High quality; minor stylistic issues but safe to deploy without review.</p></li><li><p>3 &#8212; Generally correct; minor phrasing or formatting issues requiring human oversight.</p></li><li><p>2 &#8212; Significant gaps, unsupported claims, or ignored constraints.</p></li><li><p>1 &#8212; Incorrect, contains major errors, or is structurally broken.</p></li></ul><p>Set your deployment thresholds based on risk. A low-risk internal tool might require a minimum average score of 3.5, whereas a high-risk or external application may require a minimum of 4.5 on all core dimensions and a strict 5.0 for factual grounding.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share Non-Brand Data&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share Non-Brand Data</span></a></p><div><hr></div><h1>4. Case Study: Evaluation in Practice</h1><p>Consider a GenAI assistant designed to summarize raw analyst notes for stakeholders.</p><p><strong>Input Data (Analyst Notes):</strong></p><p>* Metrics: Active customers at 12,400 (up 8% QoQ, down 3% YoY due to a seasonal promotion). Revenue at $4.2M (met target of $4.1M, driven by enterprise renewals).<br>* Churn: Rose to 4.2% in March (up from 3.5% in January). CS team suspects a competitor release.<br>* Next Actions: CS team to contact high-risk renewals; Product team to release an update in June.</p><p><strong>Generated Output (With Errors):</strong></p><p>An LLM generates the following summary:</p><p>&#8220;Q1 was a stellar quarter for the business. Active customers reached 12,400, showing strong growth. Total revenue reached $4.2M, beating our target of $4.1M due to an incredibly successful seasonal marketing campaign. Although churn rose to 4.2%, our proactive CS team has already contacted all high-risk accounts to guarantee renewal.&#8221;</p><p>A structured assessment reveals key discrepancies:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!lfZ2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1c1294-f07d-44f6-a2d3-6ab64d0ebf3b_1170x968.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!lfZ2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1c1294-f07d-44f6-a2d3-6ab64d0ebf3b_1170x968.png 424w, https://substackcdn.com/image/fetch/$s_!lfZ2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1c1294-f07d-44f6-a2d3-6ab64d0ebf3b_1170x968.png 848w, https://substackcdn.com/image/fetch/$s_!lfZ2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1c1294-f07d-44f6-a2d3-6ab64d0ebf3b_1170x968.png 1272w, https://substackcdn.com/image/fetch/$s_!lfZ2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1c1294-f07d-44f6-a2d3-6ab64d0ebf3b_1170x968.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!lfZ2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1c1294-f07d-44f6-a2d3-6ab64d0ebf3b_1170x968.png" width="1170" height="968" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ee1c1294-f07d-44f6-a2d3-6ab64d0ebf3b_1170x968.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:968,&quot;width&quot;:1170,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:98578,&quot;alt&quot;:&quot;From Prompt to Reliable Output: A Practical GenAI Evaluation Workflow&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/199950174?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1c1294-f07d-44f6-a2d3-6ab64d0ebf3b_1170x968.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="From Prompt to Reliable Output: A Practical GenAI Evaluation Workflow" title="From Prompt to Reliable Output: A Practical GenAI Evaluation Workflow" srcset="https://substackcdn.com/image/fetch/$s_!lfZ2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1c1294-f07d-44f6-a2d3-6ab64d0ebf3b_1170x968.png 424w, https://substackcdn.com/image/fetch/$s_!lfZ2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1c1294-f07d-44f6-a2d3-6ab64d0ebf3b_1170x968.png 848w, https://substackcdn.com/image/fetch/$s_!lfZ2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1c1294-f07d-44f6-a2d3-6ab64d0ebf3b_1170x968.png 1272w, https://substackcdn.com/image/fetch/$s_!lfZ2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee1c1294-f07d-44f6-a2d3-6ab64d0ebf3b_1170x968.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Evaluating these distinct dimensions allows the team to identify exactly where the model failed. The prompt can then be updated with constraints requiring objective reporting and preventing the model from inferring causality.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/from-prompt-to-reliable-output-a/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/from-prompt-to-reliable-output-a/comments"><span>Leave a comment</span></a></p><div><hr></div><h1>5. Why Technical Teams Must Own Evaluation</h1><p>Evaluating software and data models is a fundamental engineering discipline. Teams already possess the core mental models required:</p><ul><li><p>Understanding that single test cases are not representative of overall performance.</p></li><li><p>Distinguishing between individual qualitative examples and statistical evidence.</p></li><li><p>Applying structured metrics like precision, recall, and error distributions to assess behavior.</p></li></ul><p>Deploying GenAI requires applying these same disciplines to unstructured outputs. </p><p>Reliable production systems are built by designing, testing, and verifying performance systematically rather than focusing solely on creative prompt writing.</p><div><hr></div><h1>Conclusion</h1><p>Prompting is a starting point, but systematic evaluation is what makes a system production-ready. By defining tasks operationally, building representative evaluation sets, assessing performance across distinct dimensions, and optimizing variables individually, developers can build dependable GenAI applications.</p>]]></content:encoded></item><item><title><![CDATA[Best MCP Servers for Stock Market Data]]></title><description><![CDATA[The best MCP servers for connecting AI agents to financial workflow]]></description><link>https://www.nb-data.com/p/best-mcp-servers-for-stock-market</link><guid isPermaLink="false">https://www.nb-data.com/p/best-mcp-servers-for-stock-market</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Thu, 21 May 2026 15:58:20 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!QrbF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f5f7c9b-5a53-48f5-b71c-c46bc58678d1_1280x853.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QrbF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f5f7c9b-5a53-48f5-b71c-c46bc58678d1_1280x853.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QrbF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f5f7c9b-5a53-48f5-b71c-c46bc58678d1_1280x853.jpeg 424w, https://substackcdn.com/image/fetch/$s_!QrbF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f5f7c9b-5a53-48f5-b71c-c46bc58678d1_1280x853.jpeg 848w, https://substackcdn.com/image/fetch/$s_!QrbF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f5f7c9b-5a53-48f5-b71c-c46bc58678d1_1280x853.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!QrbF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f5f7c9b-5a53-48f5-b71c-c46bc58678d1_1280x853.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QrbF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f5f7c9b-5a53-48f5-b71c-c46bc58678d1_1280x853.jpeg" width="1280" height="853" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6f5f7c9b-5a53-48f5-b71c-c46bc58678d1_1280x853.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:853,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QrbF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f5f7c9b-5a53-48f5-b71c-c46bc58678d1_1280x853.jpeg 424w, https://substackcdn.com/image/fetch/$s_!QrbF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f5f7c9b-5a53-48f5-b71c-c46bc58678d1_1280x853.jpeg 848w, https://substackcdn.com/image/fetch/$s_!QrbF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f5f7c9b-5a53-48f5-b71c-c46bc58678d1_1280x853.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!QrbF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f5f7c9b-5a53-48f5-b71c-c46bc58678d1_1280x853.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@yorgosntrahas?utm_source=medium&amp;utm_medium=referral">Yorgos Ntrahas</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure></div><p>AI agents are becoming more useful for financial research, but they are only as reliable as the data they can access.</p><p>A model can summarize earnings, compare stocks, or assist in market monitoring. But without easy access to structured financial data, tasks become stale. Developers may write custom API wrappers or rely on models to interpret data services, which isn&#8217;t ideal given the need for accurate market info.</p><p>This is where <strong>Model Context Protocol (MCP)</strong> becomes useful.</p><p>An MCP server provides an AI agent with a consistent framework for finding tools, calling data services, and interacting reliably with external systems. In stock market scenarios, this enables an agent to access data uniformly, for example, fundamentals, through a structured interface.</p><p>However, not every market data provider is equally suited to MCP-based workflows. Some offer broad asset coverage and agent-native integrations, while others excel at historical data, institutional-grade fundamentals, or enterprise financial analytics. The right choice depends on what you want to build.</p><p>In this article, we compare the best MCP servers for stock market data, focusing on five providers:</p><ol><li><p>Alpha Vantage</p></li><li><p>Nasdaq Data Link</p></li><li><p>Tiingo</p></li><li><p>Intrinio</p></li><li><p>FactSet</p></li></ol><p>Curious about it? Let&#8217;s get into it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h3>1. Alpha Vantage (Best Overall)</h3><p><a href="http://alphavantage.co">Alpha Vantage</a> is the strongest overall choice for developers who want to connect LLMs and AI agents to stock market data through MCP. Its official MCP server gives agents a structured way to discover financial tools, retrieve the right data, and work with market information more reliably than a custom set of hardcoded API calls.</p><p>This is crucial because financial agents need more than simple price lookups. They may require access to historical OHLCV data, performance comparisons among multiple tickers, or the integration of market information with macroeconomic data. Alpha Vantage distinguishes itself as one of the few providers capable of supporting this wide range of workflows from a single data source. Its extensive coverage&#8202;&#8212;&#8202;including stocks, ETFs, funds, indices, options, forex, commodities, fundamentals, technicals, and economic indicators&#8202;&#8212;&#8202;ensures it remains a leading choice.</p><p>Alpha Vantage stands out for its market data, which is well-suited to serious financial applications and seamless integration with structured information for agents to reason over. The MCP layer enhances usability by exposing tools that an agent can inspect and invoke directly.</p><p>The Alpha Vantage MCP server is easy to implement and integrates with popular agent environments like Claude, Cursor, VS Code, ChatGPT, and others. Developers can connect remotely via a single MCP URL or run locally with uvx, making it perfect for research agents, coding tools, financial dashboards, and prototypes with minimal setup.</p><p>Alpha Vantage is ideal for MCP workflows that go beyond raw data retrieval to analysis. Users can ask an agent to retrieve NVDA&#8217;s recent prices, calculate RSI, summarize trends, or compare quarterly movements across AI stocks, or combine fundamentals and technicals for a market brief. This versatility makes Alpha Vantage the best default for stock market MCP integrations.</p><h4>Quickstart Example</h4><p>For a remote MCP connection:</p><pre><code>https://mcp.alphavantage.co/mcp?apikey=YOUR_API_KEY</code></pre><p>For a local MCP connection:</p><pre><code>uvx marketdata-mcp-server YOUR_API_KEY</code></pre><p>Example Cursor configuration:</p><pre><code>{
  &#8220;mcpServers&#8221;: {
    &#8220;alphavantage&#8221;: {
      &#8220;url&#8221;: &#8220;https://mcp.alphavantage.co/mcp?apikey=YOUR_API_KEY&#8221;
    }
  }
}</code></pre><p>Then, you can ask your agent something like:</p><pre><code>Use Alpha Vantage MCP to get NVDA daily price data for the past month,
calculate RSI, and summarize the trend.</code></pre><h4>Best for</h4><p>Developers and teams that want the most balanced MCP server for financial agents: broad market coverage, strong analytical flexibility, straightforward setup, and enough data depth to support both simple market questions and more serious research workflows.</p><div><hr></div><h3>2. Nasdaq Data Link (Best for Research-Grade Financial Datasets)</h3><p><a href="https://data.nasdaq.com/institutional-investors">Nasdaq Data Link</a> is a strong choice for teams that want an MCP-based financial agent to work with deeper, more research-oriented datasets, rather than focusing only on everyday price lookups. It is more compelling when the workflow depends on structured historical datasets, specialized financial information, and a richer research context.</p><p>This makes it especially useful for financial agents designed to support investment research, economic analysis, strategy exploration, or data-heavy market investigations. Instead of limiting the agent to asking, &#8220;What happened to this ticker today?&#8221;, Nasdaq Data Link is better suited to questions that require more structured context, such as comparing market behavior across time, analyzing broader economic relationships, or working with datasets that go beyond standard price and indicator endpoints.</p><p>The trade-off is that Nasdaq Data Link is not as immediately straightforward for general-purpose MCP stock workflows as Alpha Vantage. Its value is highest when the team already knows the type of dataset it wants to expose to the agent and is building around more deliberate research use cases. For lighter stock analysis, Alpha Vantage will often be faster to adopt. For dataset-centered financial intelligence, Nasdaq Data Link becomes much more attractive.</p><h4>Quickstart Example</h4><p>A fitting example prompt would be:</p><pre><code>Use the Nasdaq Data Link MCP integration to retrieve a relevant historical
financial dataset, summarize the major trend changes over time, and highlight
periods that deserve closer investigation.</code></pre><h4>Best for</h4><p>Developers, analysts, and research teams that want MCP-based financial agents to work with deeper, dataset-oriented market analysis rather than only quotes, indicators, or lightweight stock lookups.</p><div><hr></div><h3>3. Tiingo (Best for Market Data and News-Aware Analysis)</h3><p><a href="https://www.tiingo.com/">Tiingo</a> is a strong option for developers seeking an MCP-based financial workflow that feels more like a research assistant than a simple market quote tool, where it fits well for agents that need to connect stock market data with richer context on what is happening in the market.</p><p>This is especially useful because many financial questions can&#8217;t be answered by price data alone. A stock can move sharply due to earnings, guidance, sector sentiment, or broader news. In those cases, an AI agent is more helpful when working with structured market data to interpret what may drive the movement, rather than just reporting price changes. Research highlights the importance of combining numerical data with textual information, such as news, to create better market intelligence systems.</p><p>Tiingo ranks well for market analysis workflows that require both data retrieval and narrative context. An MCP-connected agent using Tiingo can focus on questions like recent stock movements, company summaries, historical trend comparisons, or creating comprehensive ticker briefings, rather than viewing the market only as a numerical dataset.</p><p>Tiingo&#8217;s main strength isn&#8217;t covering all financial use cases but providing a useful middle ground: more contextual than basic market-data APIs and more approachable than institutional platforms. It&#8217;s ideal for teams building MCP workflows, helping agents move from understanding &#8220;what happened?&#8221; to analyzing &#8220;what happened and what context to consider.&#8221;</p><h4>Quickstart Example</h4><p>A representative prompt can still show the use case clearly:</p><pre><code>Use the Tiingo MCP integration to review TSLA&#8217;s recent price movement,
surface any relevant market or company context, and summarize the key
points in a short investor-style briefing.</code></pre><h4>Best for</h4><p>Developers and small teams building financial agents that need to combine stock market data retrieval with richer interpretive context, especially for market briefings, watchlist assistants, and news-aware stock analysis workflows.</p><div><hr></div><h3>4. Intrinio (Best for Fundamentals-Driven Financial Agents)</h3><p><a href="https://intrinio.com/">Intrinio</a> is best suited for developers building MCP-based financial agents that delve deeper into company fundamentals, financial statements, valuation context, and business performance analysis. It is better positioned for agents who need to explain a company rather than merely describe its stock movement.</p><p>This matters because many useful financial workflows start with questions not centered on price. Investors, analysts, or business users want to know revenue growth, margin changes, debt levels, or company comparison on profitability and valuation. An MCP-connected agent is more valuable when it can retrieve structured company data and craft a clear financial narrative.</p><p>Intrinio&#8217;s MCP article offers a professional, fundamentals-based ranking, not just market prices. It&#8217;s valuable for AI workflows that support deliberate analysis, such as comparing companies, reviewing business quality, identifying financial strengths, and preparing structured research similar to early analyst work.</p><p>This makes Intrinio especially appealing for teams that want to build agents for equity research, corporate analysis, and data-backed investment workflows. The trade-off is that it may be more than necessary for a lightweight ticker-monitoring agent. But when the job is to reason about companies with more financial depth, Intrinio deserves its place high in the ranking.</p><h4>Quickstart Example</h4><p>A representative prompt could be:</p><pre><code>Use the Intrinio MCP integration to analyze MSFT&#8217;s latest financial performance,
summarize revenue growth, operating margin, cash flow trends, and key balance
sheet observations, then explain what stands out for an equity research brief.</code></pre><h4>Best for</h4><p>Developers and teams building financial agents for company fundamentals, equity research, earnings analysis, valuation support, and more structured business-performance summaries.</p><div><hr></div><h3>5. FactSet (Best for Enterprise Financial Intelligence)</h3><p><a href="https://www.factset.com/">FactSet</a> is the top choice for organizations wanting enterprise-grade financial agents based on institutional data, research workflows, and decision-support tools. Unlike other providers, it focuses on powering AI in serious financial environments like investment banking, wealth management, and institutional research.</p><p>FactSet also stands out because of how naturally it fits the broader movement toward AI agents embedded in financial workflows. In February 2026, Anthropic announced new enterprise AI plug-ins developed with partners including FactSet, aimed at work in investment banking, wealth management, and other professional domains. That does not make FactSet a lightweight public MCP server in the same way Alpha Vantage is positioned, but it does strengthen its place in this article as the premium option for teams thinking about agentic finance at an institutional level.</p><p>For an MCP-based stock market article, FactSet works best as the <strong>enterprise intelligence</strong> pick: the provider for teams that care about combining financial data access with mature workflow context, internal research processes, and high-stakes analysis. An agent connected to FactSet-style capabilities could support more advanced tasks such as summarizing company developments, reviewing investment opportunities, generating analyst-style briefs, or helping build pitch materials that rely on trusted financial information rather than generic web retrieval.</p><p>The trade-off is that FactSet is not the most approachable choice for individual developers or smaller prototypes. Its value becomes clearest when the application is part of a broader professional research stack, and the user needs institutional depth, workflow integration, and enterprise credibility. For those use cases, FactSet deserves the final place in this ranking&#8202;&#8212;&#8202;not because it is the easiest option, but because it is one of the most powerful when the objective is serious financial intelligence.</p><h4>Quickstart Example</h4><p>Because FactSet&#8217;s public AI positioning is more <strong>enterprise- and partner-led</strong> than self-serve, I would use an example prompt rather than a generic public MCP connection snippet.</p><pre><code>Use the FactSet-connected financial agent to review a target company,
summarize recent business developments, highlight major financial risks,
and prepare an executive-style research brief for an investment discussion.</code></pre><h4>Best for</h4><p>Enterprise teams, investment professionals, and financial institutions that want AI agents built around institutional-grade research workflows, professional market intelligence, and high-value financial analysis.</p><div><hr></div><h3>Conclusion</h3><p>The best MCP server for stock market data depends on the kind of financial agent you want to build:</p><ul><li><p><strong>Alpha Vantage: </strong>Best overall for broad market coverage and flexible agent workflows</p></li><li><p><strong>Nasdaq Data Link: </strong>Best for research-grade financial datasets</p></li><li><p><strong>Tiingo: </strong>Best for market data with stronger context for briefings and analysis</p></li><li><p><strong>Intrinio: </strong>Best for fundamentals-driven company research</p></li><li><p><strong>FactSet: </strong>Best for enterprise financial intelligence</p></li></ul><p>That&#8217;s all for now. I hope it helps!</p>]]></content:encoded></item><item><title><![CDATA[The GenAI Skill Data Professionals Need Most: Evaluation]]></title><description><![CDATA[Only by evaluating things that we know our system is good.]]></description><link>https://www.nb-data.com/p/the-genai-skill-data-professionals</link><guid isPermaLink="false">https://www.nb-data.com/p/the-genai-skill-data-professionals</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Thu, 14 May 2026 17:01:21 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ugB7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60e5782f-b33b-410e-be3c-78fc501b9eb3_1020x551.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ugB7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60e5782f-b33b-410e-be3c-78fc501b9eb3_1020x551.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ugB7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60e5782f-b33b-410e-be3c-78fc501b9eb3_1020x551.png 424w, https://substackcdn.com/image/fetch/$s_!ugB7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60e5782f-b33b-410e-be3c-78fc501b9eb3_1020x551.png 848w, https://substackcdn.com/image/fetch/$s_!ugB7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60e5782f-b33b-410e-be3c-78fc501b9eb3_1020x551.png 1272w, https://substackcdn.com/image/fetch/$s_!ugB7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60e5782f-b33b-410e-be3c-78fc501b9eb3_1020x551.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ugB7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60e5782f-b33b-410e-be3c-78fc501b9eb3_1020x551.png" width="1020" height="551" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/60e5782f-b33b-410e-be3c-78fc501b9eb3_1020x551.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:551,&quot;width&quot;:1020,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:986860,&quot;alt&quot;:&quot;The GenAI Skill Data Professionals Need Most: Evaluation&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/197661414?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60e5782f-b33b-410e-be3c-78fc501b9eb3_1020x551.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The GenAI Skill Data Professionals Need Most: Evaluation" title="The GenAI Skill Data Professionals Need Most: Evaluation" srcset="https://substackcdn.com/image/fetch/$s_!ugB7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60e5782f-b33b-410e-be3c-78fc501b9eb3_1020x551.png 424w, https://substackcdn.com/image/fetch/$s_!ugB7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60e5782f-b33b-410e-be3c-78fc501b9eb3_1020x551.png 848w, https://substackcdn.com/image/fetch/$s_!ugB7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60e5782f-b33b-410e-be3c-78fc501b9eb3_1020x551.png 1272w, https://substackcdn.com/image/fetch/$s_!ugB7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F60e5782f-b33b-410e-be3c-78fc501b9eb3_1020x551.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>GenAI has made output generation cheap.</p><p>A decent prompt can already produce summaries, classifications, SQL explanations, insight drafts, documentation, and stakeholder-facing text.</p><p>The harder part is deciding:</p><ul><li><p>Is the output correct?</p></li><li><p>Is it grounded in the input?</p></li><li><p>Is it consistent across realistic cases?</p></li><li><p>Would I trust it inside an actual workflow?</p></li></ul><p>That is the opening for data professionals. Evaluation turns GenAI from a demo into something that can be used responsibly in real work.</p><p><a href="https://developers.openai.com/api/docs/guides/evaluation-best-practices">OpenAI&#8217;s current eval guidance</a> makes the same distinction: the useful kind of eval is not a public benchmark. It is a task-specific test for the application you are building.</p><p>By the end of this article, you should have a clearer view of what evaluation means in practice, what to test for, and why data professionals are well-positioned to own this skill. </p><p>Curious about it? Let&#8217;s get into it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><h1>GenAI output is easy. Trusting it is not.</h1><p>Prompting gets attention because it is visible.</p><p>Evaluation matters more because it decides whether the work holds up.</p><p>Most professionals first experience GenAI as a productivity tool: ask, receive, refine. That makes it tempting to treat a fluent output as a good output.</p><p>But GenAI systems are variable.</p><p>The same system can behave differently across prompts, data slices, and edge cases.</p><p>In professional settings, the cost of a wrong answer is higher than the cost of a slightly worse prompt.</p><p>That is why evaluation matters.</p><div><hr></div><h1>What does evaluation actually mean in real work?</h1><p>Evaluation here does not mean comparing GPT-5.5 against Claude on a benchmark.</p><p>It means testing a GenAI workflow against the task&#8217;s standards.</p><p>The standard is to separate broad model benchmarks from specific evaluations you design for your own application. Google&#8217;s evaluation documentation also frames this as a test-driven process: define the task, prepare evaluation data, choose the quality criteria, and inspect results.</p><p>Consider workflows that data professionals might evaluate:</p><ul><li><p>A GenAI assistant that summarizes KPI movements</p></li><li><p>A RAG system answering questions from internal policy documents</p></li><li><p>A classifier that tags customer feedback or support tickets</p></li><li><p>A model that explains SQL output to business users</p></li></ul><p>These are not abstract research problems. They are real tasks that need real testing.</p><div><hr></div><h1>The four questions every GenAI workflow should answer</h1><p>The heart of evaluation is not a long taxonomy of metrics, as it comes down to these four questions:</p><h3><strong>A. Did it complete the task?</strong></h3><p>This means that if the GenAI application can perform the task it was asked to do, such as classifying the ticket into the allowed categories or summarizing the table instead of restating it.</p><p>In technical terms, this is often measured through deterministic checks: regex matching for expected formats, JSON schema validation, or exact-match accuracy against a predefined label set.</p><div class="callout-block" data-callout="true"><p style="text-align: center;"><code>Exact Match = 1 if output matches ground truth, else 0</code></p></div><p>This is the simplest evaluation dimension, but also the most often skipped.</p><h3><strong>B. Is it correct and grounded?</strong></h3><p>Did it stay faithful to the source data, retrieved context, or provided evidence?</p><p>This is especially important for RAG systems, analytical summaries, and policy assistants.</p><p>Basic text overlap metrics such as ROUGE or BLEU are not sufficient here. They check surface-level word similarity but miss meaning.</p><p>A stronger approach is to compare the output and source in the embedding space. The standard metric for that is cosine similarity:</p><div class="callout-block" data-callout="true"><p style="text-align: center;"><code>Cosine Similarity(A, B) = (A &#183; B) / (||A|| &#215; ||B||)</code></p></div><p>A high cosine similarity means the generated text is semantically close to the source. A low score may indicate the model drifted or fabricated information.</p><p>For RAG systems specifically, you also need to check whether the retrieval step actually found the right documents before the model started generating. That is where Recall@K matters:</p><div class="callout-block" data-callout="true"><p style="text-align: center;"><code>Recall@K = |Relevant Documents &#8745; Top-K Retrieved| / |Relevant Documents|</code></p></div><p>If the retriever misses the relevant source, even a perfect generator will produce the wrong answer.</p><p>These are examples of some metrics from the Generative AI metrics.</p><h3><strong>C. Is the quality consistent across realistic cases?</strong></h3><p>A single good example proves very little.</p><p>The system should be tested against various cases, such as:</p><ul><li><p>Easy cases</p></li><li><p>Ambiguous cases</p></li><li><p>Incomplete inputs</p></li><li><p>Edge cases</p></li><li><p>Cases where the correct response is &#8220;not enough information.&#8221;</p></li></ul><p>You already know not to trust a model based on a single clean validation sample.</p><p>GenAI deserves the same discipline. That means building automated test suites that run every time the prompt, model version, or pipeline changes.</p><h3><strong>D. How does it fail?</strong></h3><p>This is the most important professional instinct.</p><p>A workflow that fails clearly is easier to manage than one that sounds confident while being wrong.</p><p>A good evaluation should reveal things such as:</p><ul><li><p>Unsupported claims</p></li><li><p>Fabricated numbers</p></li><li><p>Hidden assumptions</p></li><li><p>Formatting failures</p></li><li><p>Misleading simplifications</p></li></ul><p>This is where logging and tracing tools become important. Capturing inputs and outputs systematically lets you identify failure patterns instead of guessing.</p><p>The job is not to prove that the system works. The job is to learn where it fails before someone depends on it.</p><div><hr></div><h1>An example: evaluating a KPI commentary assistant</h1><p>To make this concrete, consider a workflow that many data teams will recognize.</p><p>A GenAI assistant receives a monthly KPI table and writes a short business commentary:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MtHV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3054c7d0-b4d0-4d29-b704-bf6fb227f8c1_1170x119.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MtHV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3054c7d0-b4d0-4d29-b704-bf6fb227f8c1_1170x119.png 424w, https://substackcdn.com/image/fetch/$s_!MtHV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3054c7d0-b4d0-4d29-b704-bf6fb227f8c1_1170x119.png 848w, https://substackcdn.com/image/fetch/$s_!MtHV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3054c7d0-b4d0-4d29-b704-bf6fb227f8c1_1170x119.png 1272w, https://substackcdn.com/image/fetch/$s_!MtHV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3054c7d0-b4d0-4d29-b704-bf6fb227f8c1_1170x119.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MtHV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3054c7d0-b4d0-4d29-b704-bf6fb227f8c1_1170x119.png" width="1170" height="119" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3054c7d0-b4d0-4d29-b704-bf6fb227f8c1_1170x119.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:119,&quot;width&quot;:1170,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:14418,&quot;alt&quot;:&quot;The GenAI Skill Data Professionals Need Most: Evaluation&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/197661414?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3054c7d0-b4d0-4d29-b704-bf6fb227f8c1_1170x119.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The GenAI Skill Data Professionals Need Most: Evaluation" title="The GenAI Skill Data Professionals Need Most: Evaluation" srcset="https://substackcdn.com/image/fetch/$s_!MtHV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3054c7d0-b4d0-4d29-b704-bf6fb227f8c1_1170x119.png 424w, https://substackcdn.com/image/fetch/$s_!MtHV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3054c7d0-b4d0-4d29-b704-bf6fb227f8c1_1170x119.png 848w, https://substackcdn.com/image/fetch/$s_!MtHV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3054c7d0-b4d0-4d29-b704-bf6fb227f8c1_1170x119.png 1272w, https://substackcdn.com/image/fetch/$s_!MtHV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3054c7d0-b4d0-4d29-b704-bf6fb227f8c1_1170x119.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The expected output should:</p><ul><li><p>Identify that revenue declined</p></li><li><p>Mention lower acquisition and higher churn</p></li><li><p>Avoid claiming causality unless supported</p></li><li><p>Keep the commentary concise and business-appropriate</p></li></ul><p>Then you evaluate:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3sBg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae7e556-8890-492f-9331-64f99be0ea98_1176x307.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3sBg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae7e556-8890-492f-9331-64f99be0ea98_1176x307.png 424w, https://substackcdn.com/image/fetch/$s_!3sBg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae7e556-8890-492f-9331-64f99be0ea98_1176x307.png 848w, https://substackcdn.com/image/fetch/$s_!3sBg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae7e556-8890-492f-9331-64f99be0ea98_1176x307.png 1272w, https://substackcdn.com/image/fetch/$s_!3sBg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae7e556-8890-492f-9331-64f99be0ea98_1176x307.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3sBg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae7e556-8890-492f-9331-64f99be0ea98_1176x307.png" width="1176" height="307" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bae7e556-8890-492f-9331-64f99be0ea98_1176x307.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:307,&quot;width&quot;:1176,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:41497,&quot;alt&quot;:&quot;The GenAI Skill Data Professionals Need Most: Evaluation&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/197661414?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae7e556-8890-492f-9331-64f99be0ea98_1176x307.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The GenAI Skill Data Professionals Need Most: Evaluation" title="The GenAI Skill Data Professionals Need Most: Evaluation" srcset="https://substackcdn.com/image/fetch/$s_!3sBg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae7e556-8890-492f-9331-64f99be0ea98_1176x307.png 424w, https://substackcdn.com/image/fetch/$s_!3sBg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae7e556-8890-492f-9331-64f99be0ea98_1176x307.png 848w, https://substackcdn.com/image/fetch/$s_!3sBg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae7e556-8890-492f-9331-64f99be0ea98_1176x307.png 1272w, https://substackcdn.com/image/fetch/$s_!3sBg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbae7e556-8890-492f-9331-64f99be0ea98_1176x307.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This gives you something concrete to recognize in your own work. It also separates your evaluation from a generic evaluation.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/the-genai-skill-data-professionals?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/the-genai-skill-data-professionals?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div><h1>A simple evaluation loop for data professionals</h1><p>You do not need a full evaluation framework to start.</p><p>A five-step loop is enough:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OqZC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cea89b1-53cb-4a51-ad30-91847d7e79eb_2001x191.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OqZC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cea89b1-53cb-4a51-ad30-91847d7e79eb_2001x191.png 424w, https://substackcdn.com/image/fetch/$s_!OqZC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cea89b1-53cb-4a51-ad30-91847d7e79eb_2001x191.png 848w, https://substackcdn.com/image/fetch/$s_!OqZC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cea89b1-53cb-4a51-ad30-91847d7e79eb_2001x191.png 1272w, https://substackcdn.com/image/fetch/$s_!OqZC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cea89b1-53cb-4a51-ad30-91847d7e79eb_2001x191.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OqZC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cea89b1-53cb-4a51-ad30-91847d7e79eb_2001x191.png" width="1456" height="139" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9cea89b1-53cb-4a51-ad30-91847d7e79eb_2001x191.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:139,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:26854,&quot;alt&quot;:&quot;The GenAI Skill Data Professionals Need Most: Evaluation&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/197661414?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cea89b1-53cb-4a51-ad30-91847d7e79eb_2001x191.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The GenAI Skill Data Professionals Need Most: Evaluation" title="The GenAI Skill Data Professionals Need Most: Evaluation" srcset="https://substackcdn.com/image/fetch/$s_!OqZC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cea89b1-53cb-4a51-ad30-91847d7e79eb_2001x191.png 424w, https://substackcdn.com/image/fetch/$s_!OqZC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cea89b1-53cb-4a51-ad30-91847d7e79eb_2001x191.png 848w, https://substackcdn.com/image/fetch/$s_!OqZC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cea89b1-53cb-4a51-ad30-91847d7e79eb_2001x191.png 1272w, https://substackcdn.com/image/fetch/$s_!OqZC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9cea89b1-53cb-4a51-ad30-91847d7e79eb_2001x191.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p><strong>1. Define the job clearly.</strong> What should the GenAI system actually do?</p><p><strong>2. Collect representative test cases.</strong> Not just the clean ones. Include strange, messy, and borderline cases.</p><p><strong>3. Write a simple scoring rubric.</strong> For structured outputs, use exact-match checks. For subjective quality, use a binary (0/1) rubric or a short LLM-as-a-judge prompt.</p><p><strong>4. Compare outputs across prompts, models, or versions against the same cases.</strong> Eval guidance emphasizes using evals to test and iterate rather than relying on ad hoc inspection.</p><p><strong>5. Record recurring failures.</strong> The failure pattern matters more than one overall score.</p><p>The evaluation stack reflects this general pattern as well: evaluation datasets, rubric-based measures, deterministic metrics where suitable, and custom checks for task-specific requirements.</p><div><hr></div><h1>Why data professionals are well-positioned to own this</h1><p>Evaluation is not strange to data professionals.</p><p>It draws on habits we already use:</p><ul><li><p>Defining success criteria</p></li><li><p>Building representative samples</p></li><li><p>Distinguishing anecdotes from evidence</p></li><li><p>Checking edge cases</p></li><li><p>Analyzing errors instead of celebrating one good result</p></li><li><p>Deciding whether an output is fit for use</p></li></ul><p>Data professionals already have much of the mindset evaluation requires.</p><p>The shift is applying that discipline to generative systems, not only predictive models.</p><p>That is why evaluation may become one of the most valuable GenAI-adjacent skills for analysts, data scientists, analytics engineers, and ML practitioners.</p><div><hr></div><h1>Conclusion</h1><p>Evaluation is what turns GenAI from something impressive into something dependable. For data professionals, that matters more than learning a clever prompt pattern or chasing the newest model release. </p><p>The real advantage comes from knowing how to define good output, test it against realistic cases, trace where it fails, and decide whether it is ready to support actual work. </p><p>That habit is already close to how strong data professionals think: establish the standard, examine the evidence, and avoid trusting a result just because it looks convincing. GenAI simply gives that discipline a new place to matter. As these systems move deeper into analysis, reporting, search, and decision support, the professionals who can evaluate them well will be the ones who make them genuinely useful.</p>]]></content:encoded></item><item><title><![CDATA[The ML Skills That Still Matter in 2026]]></title><description><![CDATA[The skills that still make you desirable by companies and ahead of everyone]]></description><link>https://www.nb-data.com/p/the-ml-skills-that-still-matter-in</link><guid isPermaLink="false">https://www.nb-data.com/p/the-ml-skills-that-still-matter-in</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Thu, 30 Apr 2026 02:01:17 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!xNHD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7481d4bb-f363-449a-bacb-b3f38b585a86_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xNHD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7481d4bb-f363-449a-bacb-b3f38b585a86_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xNHD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7481d4bb-f363-449a-bacb-b3f38b585a86_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!xNHD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7481d4bb-f363-449a-bacb-b3f38b585a86_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!xNHD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7481d4bb-f363-449a-bacb-b3f38b585a86_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!xNHD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7481d4bb-f363-449a-bacb-b3f38b585a86_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xNHD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7481d4bb-f363-449a-bacb-b3f38b585a86_1024x1024.png" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7481d4bb-f363-449a-bacb-b3f38b585a86_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:855757,&quot;alt&quot;:&quot;The ML Skills That Still Matter in 2026&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/195882313?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7481d4bb-f363-449a-bacb-b3f38b585a86_1024x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The ML Skills That Still Matter in 2026" title="The ML Skills That Still Matter in 2026" srcset="https://substackcdn.com/image/fetch/$s_!xNHD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7481d4bb-f363-449a-bacb-b3f38b585a86_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!xNHD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7481d4bb-f363-449a-bacb-b3f38b585a86_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!xNHD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7481d4bb-f363-449a-bacb-b3f38b585a86_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!xNHD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7481d4bb-f363-449a-bacb-b3f38b585a86_1024x1024.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A lot of data professionals are trying to decide what to learn next.</p><p>That is understandable.</p><p>The field feels crowded now. There is classical machine learning. Deep learning. Generative AI. RAG. Agents. Evaluation. MLOps. Analytics engineering. Data products. Every few months, a new tool or workflow becomes the thing everyone talks about.</p><p>So the question becomes narrow:</p><p><em>Which machine learning skills are still worth learning in 2026?</em></p><p>The answer is not &#8220;learn every model.&#8221;<br>It is also not &#8220;ML is dead because GenAI can do everything.&#8221;</p><p>A better answer is this:</p><p><strong>The ML skills that still matter are the skills that help you turn data into reliable decisions.</strong></p><p>That includes framing the problem, checking the data, building baselines, choosing metrics, validating properly, analyzing errors, and monitoring what happens after deployment.</p><p>These skills matter because AI adoption is no longer experimental. Research shows that generative AI adoption has moved quickly into the mainstream, with reported population adoption reaching 53% within three years. But adoption does not automatically mean reliability. The more AI systems enter daily workflows, the more important it becomes to know whether the output can be trusted.</p><p>That is where machine learning fundamentals still matter.</p><p>By the end of this article, you should have a clearer answer to three questions:</p><ul><li><p>Which ML skills are still worth learning?</p></li><li><p>Why do they still matter in a GenAI-heavy world?</p></li><li><p>What should you actually practice if you want to stay useful as a data professional?</p></li></ul><p>Curious about it? Let&#8217;s get into it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h1>1. Turn vague business problems into ML problems</h1><p>The first ML skill that still matters is problem framing.</p><p>Most weak ML projects do not fail because the model was not advanced enough. They fail because the problem was unclear from the beginning.</p><p>Someone says:<br>&#8220;Can we predict churn?&#8221;</p><p>That sounds like a machine learning problem. But it is not specific enough yet. Before modeling, you need to define, for example:</p><p><strong>What is the prediction target?</strong> Churn within the next 30 days</p><p><strong>What is the prediction unit?</strong>  One customer account</p><p><strong>When is the prediction made?</strong> Every Monday morning</p><p><strong>What data is available at that time?</strong> Activity, billing, support, and product usage up to Sunday night</p><p><strong>What action will follow the prediction?</strong> Retention team contacts high-risk accounts</p><p><strong>What does success mean?</strong> Lower churn rate, higher retained revenue, better prioritization</p><p>Without these definitions, the model may still produce a score. But the score may not be useful.</p><p>This is the first practical lesson: A machine learning problem is not defined by the model you use. It is defined by the decision you want to improve.</p><p>A good ML practitioner knows how to translate a messy business request into something testable. That means asking whether we are predicting, ranking, classifying, detecting, recommending, or forecasting. You need to identify the exact target variable, the relevant time window, the information available at prediction time, the end-user of the result, and exactly what they will do differently because of the model.</p><p>This skill still matters in 2026 because GenAI tools can help you write code faster, but they do not automatically define the right problem for you. If the target is wrong, the rest of the pipeline is just a waste.</p><div><hr></div><h1>2. Audit the data before trusting the model</h1><p>The second skill is data auditing. This is more than &#8220;clean the data.&#8221;</p><p>Cleaning data usually means handling missing values, fixing formats, removing duplicates, and standardizing columns. Data auditing goes deeper. It asks whether the data is actually valid for the problem. For an ML project, you need to inspect at least five things:</p><p><strong>Label quality: </strong>Is the target variable correct and consistently defined?</p><p><strong>Data availability: </strong>Would these features be available when the prediction is made?</p><p><strong>Leakage: </strong>Does the dataset contain future information?</p><p><strong>Sampling bias: </strong>Does the training data represent the population where the model will be used?</p><p><strong>Stability: </strong>Are the patterns likely to hold over time?</p><p>Data leakage is especially important. Scikit-learn&#8217;s documentation warns that leakage can produce overly optimistic Performance because information from the test set or future data accidentally enters the training process.</p><p>A simple example:</p><p>You are building a churn model. Your dataset includes a column called cancellation_reason. The model performs extremely well.</p><p>But there is a problem. That column only exists after the customer has already churned. The model is not learning early churn signals. It is reading the answer key.</p><p>This happens more often than people admit. It can appear in many forms:</p><p>For example, in <strong>Credit risk</strong>, it uses a manual review result that happens after application submission or in <strong>Fraud detection</strong>, it uses an investigation outcome as an input feature.</p><p>This is why data auditing remains a core ML skill. A data professional should be able to look at a dataset and ask: &#8220;Would I know this information at the moment I need to make the prediction?&#8221; If the answer is no, the feature probably should not be there.</p><div><hr></div><h1>3. Build simple baselines before complex models</h1><p>The third skill is baseline building. A baseline is the simplest model or rule that gives you a reference point. It could be a majority-class classifier, a simple business rule, logistic regression, a decision tree, a moving average, a keyword-based classifier, or a simple ranking score.</p><p>Baselines are not boring. They are protection against unnecessary complexity.</p><p>Google&#8217;s Rules of Machine Learning still emphasize starting with simple models and robust infrastructure before moving into more complex ML systems. That advice has aged well because the easiest mistake in modern AI work is to overbuild too early.</p><p>Suppose you are building a lead scoring model. A simple baseline might be: Score leads higher if they visited the pricing page, opened two emails, and work at a company above a certain size.</p><p>Then you compare that with a logistic regression model. Then maybe a gradient boosting model. Only after that should you consider something more complex.</p><p>The practical question is not: &#8220;Can I use a more advanced model?&#8221;</p><p>The better question is: &#8220;Does the more advanced model produce enough improvement to justify the added complexity?&#8221;</p><p>That complexity includes harder debugging, higher maintenance costs, more difficult explanations, more monitoring requirements, more fragile deployments, and more stakeholder confusion.</p><p>In many real business settings, the best model is not the most advanced model. It is the simplest model that performs well enough and can be trusted by the people who need to use it.</p><div><hr></div><h1>4. Choose metrics based on the cost of mistakes</h1><p>The fourth skill is metric selection. This is where many ML projects become misleading. People often ask: &#8220;What is the model accuracy?&#8221;</p><p>But accuracy is not always the right metric. For example, imagine a fraud detection dataset where only 1% of transactions are fraudulent. A model that predicts &#8220;not fraud&#8221; for everything can be 99% accurate.</p><p>That sounds excellent. It is also useless.</p><p>Metric choice depends on the type of mistake you care about.</p><p>For instance, <strong>in Fraud detection</strong>, you care about recall, precision, and false positive rate. In Medical screening, recall and false negatives are critical. In Spam detection, it is precision and false positives. <strong>In Credit risk</strong>, you look at calibration, precision, recall, and expected loss. <strong>In Recommendation systems</strong>, ranking metrics, conversion, and retention matter most.</p><p>This is one of the most useful ML skills for real work: You need to connect the metric to the decision.</p><p>If the business can only contact 500 customers per week, then overall accuracy may not matter much. What matters is whether the top 500 predicted customers are actually worth contacting.</p><p>In that case, you may care about precision at K, lift in the top decile, expected retained revenue, conversion from intervention, or the cost per saved customer.</p><p>The right metric depends on the operating reality. This is why ML is not only a technical exercise. A model is evaluated inside a business process. If the metric ignores that process, the evaluation is incomplete.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/the-ml-skills-that-still-matter-in?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/the-ml-skills-that-still-matter-in?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div><h1>5. Validate without fooling yourself</h1><p>The fifth skill is validation. This is where you test whether the model can generalize.</p><p>A model can perform well on the data it has seen. That does not mean it will perform well on new data. This is why train/test splits, cross-validation, time-based validation, and holdout sets still matter.</p><p>Scikit-learn&#8217;s cross-validation documentation highlights the importance of evaluating estimator performance properly and avoiding pitfalls that make performance estimates unreliable.</p><p>The important idea is simple: Your validation setup should imitate the real situation where the model will be used.</p><p>For random customer classification, a random train/test split might be fine. For time-dependent problems, it may not be. If you are forecasting demand, predicting churn, detecting fraud, or scoring leads over time, you often need time-based validation. That means training on the past and testing on the future.</p><p>For example, you might train on data from January to March to validate in April. Then train from January to April to validate in May, and train from January to May to validate in June.</p><p>This is closer to the real world. You do not get to train in June to predict April. Validation should respect time.</p><p>A strong data professional knows how to ask:</p><ul><li><p>Was the test set kept separate?</p></li><li><p>Was the preprocessing fitted only on the training data?</p></li><li><p>Was the split random when it should have been time-based?</p></li><li><p>Were duplicate users or records split across train and test?</p></li><li><p>Was the test set reused too many times during model selection?</p></li><li><p>Does Performance hold across segments?</p></li></ul><p>This skill matters because bad validation creates false confidence. And false confidence is dangerous.</p><div><hr></div><h1>6. Analyze errors, not just scores</h1><p>The sixth skill is error analysis. A single score is not enough.</p><p>A model with 88% accuracy may still fail badly for the most important cases. A forecasting model with a good average error may still miss peak demand. A churn model may perform well overall but fail for enterprise customers. A document classifier may work for clean English documents but fail for short, messy, multilingual text.</p><p>This is why error analysis matters. After you evaluate the model, you should inspect where it fails.</p><p>A practical error analysis table might look like this:</p><p><strong>New users</strong>: Does the model fail when history is limited?</p><p><strong>High-value customers</strong>: Does it work for the accounts that matter most?</p><p><strong>Geography</strong>: Does Performance differ by country or region?</p><p><strong>Product category</strong>: Does it fail on long-tail categories?</p><p><strong>Time period</strong>: Does it degrade during holidays or campaigns?</p><p><strong>Input quality</strong>: Does messy or incomplete data hurt Performance?</p><p><strong>Minority class</strong>: Does the model ignore rare but important cases?</p><p>This is where ML work becomes diagnostic. You stop asking only: &#8220;Is the model good?&#8221; You start asking: &#8220;Where is the model useful, where is it weak, and where should we not trust it?&#8221;</p><p>That is a much better standard. This skill also connects directly to GenAI evaluation. When evaluating an LLM workflow, the same habit applies. You do not only ask whether the average output looks good. You ask which prompts fail, which user intents fail, which document types fail, which languages fail, which edge cases create hallucination, and which outputs need human review.</p><p>The models changed, but the evaluation habit still transfers.</p><div><hr></div><h1>7. Understand Monitoring after deployment</h1><p>The seventh skill is model monitoring. A model is not finished when it is deployed. It enters a changing environment.</p><p>Customer behavior changes. Product features change. Fraud patterns change. Marketing channels change. Economic conditions change. Data pipelines change. Even column definitions can change.</p><p>Google Cloud&#8217;s model monitoring documentation discusses feature skew and drift detection for deployed models, including changes in categorical and numerical input features.</p><p>For most data professionals, the key idea is not complicated: A model can become worse even if the code does not change.</p><p>That means you need to monitor:</p><p><strong>Data freshness: </strong>Did the latest batch arrive?</p><p><strong>Input distribution: </strong>Are feature values changing?</p><p><strong>Prediction distribution: </strong>Are model scores suddenly higher or lower?</p><p><strong>Segment performance: </strong>Is one customer group degrading faster?</p><p><strong>Business outcome: </strong>Is the model still improving the intended metric?</p><p><strong>Pipeline health: </strong>Are transformations still working correctly?</p><p><strong>Human feedback: </strong>Are users overriding or ignoring the model?</p><p>This is one reason ML skills remain useful in 2026. Many AI systems fail quietly. They do not always break in obvious ways. They drift. They degrade. They become misaligned with the current workflow. Monitoring is how you notice before the damage becomes expensive.</p><div><hr></div><h1>8. Explain the model in business terms</h1><p>The eighth skill is communication. But not generic communication. The useful skill is being able to explain the model in terms of decisions, trade-offs, and risk.</p><p>A stakeholder does not only need to know that the model has 0.82 AUC. They need to know what that means.</p><p>For example: &#8220;The model is useful for ranking customers by churn risk, but it should not be used as an automatic cancellation prediction. It works better for customers with at least three months of activity history. For new customers, the signal is weaker.&#8221;</p><p>That explanation is much more useful than a metric alone. A good ML explanation should include:</p><p>A good ML explanation should include the Purpose (what decision the model supports), the Scope (where it should and should not be used), the Performance (how well it works and compared to what), Failure modes (where it performs poorly), Trade-offs (what happens if we optimize for precision vs recall), Action (what users should do with the output), and Monitoring (what needs to be checked after deployment).</p><p>This is especially important as AI systems become more embedded in business workflows. NIST&#8217;s AI Risk Management Framework emphasizes test, evaluation, verification, and validation across the AI lifecycle. That kind of thinking is not only for regulators or governance teams. It is also practical for data professionals who need to explain when a model is reliable enough to use.</p><p>The best ML people are not only model builders. They are translators between model behavior and business action.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/the-ml-skills-that-still-matter-in/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/the-ml-skills-that-still-matter-in/comments"><span>Leave a comment</span></a></p><div><hr></div><h1>So what should you learn in 2026?</h1><p>If you are learning ML now, do not structure your learning around a long list of algorithms. Structure it around the workflow. Here is a better learning path.</p><p><strong>Problem framing</strong><em> | Prediction target, decision point, action, success metric | Practice: </em>A one-page ML problem definition</p><p><strong>Data audit</strong><em> | Leakage, label quality, missingness, sampling, availability | Practice: </em>A data quality checklist</p><p><strong>Baseline modeling</strong><em> | Rules, logistic regression, trees, simple benchmarks | Practice: </em>A baseline comparison table</p><p><strong>Metric selection</strong><em> | Precision, recall, F1, AUC, calibration, ranking metrics | Practice: </em>A metric justification note</p><p><strong>Validation</strong><em> | Train/test split, cross-validation, time-based split | Practice: </em>A validation design</p><p><strong>Error analysis</strong><em> | Segment performance, false positives, false negatives | Practice: </em>An error analysis report</p><p><strong>Monitoring</strong><em> | Drift, skew, data freshness, prediction distribution | Practice: </em>A monitoring checklist</p><p><strong>Communication</strong><em> | Trade-offs, limitations, recommended use | Practice: </em>A stakeholder summary</p><p>This is the part many courses skip. They teach the model before the workflow. </p><p>But in real work, the workflow is what makes the model useful.</p><h1>The skills that still matter</h1><p>So, what ML skills still matter in 2026? These ones:</p><ul><li><p>Framing a vague business problem into a clear ML task</p></li><li><p>Auditing data before trusting it</p></li><li><p>Detecting leakage and bad labels</p></li><li><p>Building simple baselines</p></li><li><p>Choosing metrics based on business cost</p></li><li><p>Validating models correctly</p></li><li><p>Analyzing errors by segment</p></li><li><p>Monitoring model behavior after deployment</p></li><li><p>Explaining limitations clearly</p></li></ul><p>These skills are not trendy. But they are durable. They matter for classical ML. They matter for GenAI evaluation. They matter for RAG systems. They matter for AI products.</p><p>They matter whenever someone asks: &#8220;Can we trust this output enough to use it?&#8221;</p><p>That is the real reason ML still matters. Not because every data professional needs to train models from scratch. Not because classical ML is competing with GenAI. But because ML teaches the discipline behind reliable AI work.</p><p>In 2026, the most valuable data professionals will not be the ones who chase every new tool. They will be the ones who can build, evaluate, question, and explain AI systems clearly.</p><p>That is still machine learning.</p><p>And it still matters.</p><div><hr></div>]]></content:encoded></item><item><title><![CDATA[What Real SQL Work Taught Me About Being a Data Scientist]]></title><description><![CDATA[Why I stopped seeing SQL as a secondary skill and started seeing it as the backbone of real data projects]]></description><link>https://www.nb-data.com/p/what-real-sql-work-taught-me-about</link><guid isPermaLink="false">https://www.nb-data.com/p/what-real-sql-work-taught-me-about</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sat, 28 Mar 2026 15:07:48 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!7CmX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4abfb659-bac8-4b53-b22e-24fcad7b32ea_1312x736.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7CmX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4abfb659-bac8-4b53-b22e-24fcad7b32ea_1312x736.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7CmX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4abfb659-bac8-4b53-b22e-24fcad7b32ea_1312x736.jpeg 424w, https://substackcdn.com/image/fetch/$s_!7CmX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4abfb659-bac8-4b53-b22e-24fcad7b32ea_1312x736.jpeg 848w, https://substackcdn.com/image/fetch/$s_!7CmX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4abfb659-bac8-4b53-b22e-24fcad7b32ea_1312x736.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!7CmX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4abfb659-bac8-4b53-b22e-24fcad7b32ea_1312x736.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7CmX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4abfb659-bac8-4b53-b22e-24fcad7b32ea_1312x736.jpeg" width="1312" height="736" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4abfb659-bac8-4b53-b22e-24fcad7b32ea_1312x736.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:736,&quot;width&quot;:1312,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:90217,&quot;alt&quot;:&quot;What Real SQL Work Taught Me About Being a Data Scientist&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/192418111?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4abfb659-bac8-4b53-b22e-24fcad7b32ea_1312x736.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="What Real SQL Work Taught Me About Being a Data Scientist" title="What Real SQL Work Taught Me About Being a Data Scientist" srcset="https://substackcdn.com/image/fetch/$s_!7CmX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4abfb659-bac8-4b53-b22e-24fcad7b32ea_1312x736.jpeg 424w, https://substackcdn.com/image/fetch/$s_!7CmX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4abfb659-bac8-4b53-b22e-24fcad7b32ea_1312x736.jpeg 848w, https://substackcdn.com/image/fetch/$s_!7CmX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4abfb659-bac8-4b53-b22e-24fcad7b32ea_1312x736.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!7CmX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4abfb659-bac8-4b53-b22e-24fcad7b32ea_1312x736.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Image by Ideogram.ai</figcaption></figure></div><div class="pullquote"><p>"Real SQL work taught me that trustworthy definitions matter more than flashy queries."</p></div><h1>I did not start by taking SQL seriously</h1><p style="text-align: justify;">Early in my career, I did not see SQL as central to being a data scientist. Most of my learning was built around Python, and the classes and bootcamps I joined reinforced that view. Python felt like the real language of data science. SQL felt useful, but distant.</p><p>So I did not reject it. I simply did not get enough exposure to it.</p><p>That distinction matters. When your early learning path is dominated by notebooks, models, and Python libraries, it is easy to assume that the real work starts once the data is already in front of you. In that worldview, SQL looks like preparation work. Helpful, yes. Foundational, no.</p><p>Real work changed that view gradually.</p><p>The more I worked in corporate settings, the clearer it became that many projects do not begin with modeling, dashboards, or machine learning. They begin with a more basic set of questions: Is the data available? Is the definition correct? Can the result be trusted enough for someone to act on it?</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h1>Work forced the lesson</h1><p>What changed my view of SQL was not one dramatic moment. It was the accumulation of projects. Again and again, the work pulled me toward the same reality: before anything becomes an analysis, a model, or a recommendation, someone has to make sure the data is available, correctly defined, and usable.</p><p>That is where SQL kept appearing.</p><p>Sometimes the request looked simple. A business team needed a report. Sometimes the request sounded more strategic. A project needed insight to inform a decision. Sometimes the work moved beyond a single analysis into the project's production life. In each case, SQL mattered not only for retrieving data, but for deciding whether the project itself rested on a solid foundation.</p><p>The difficult conversations were often not about syntax at all. They were about meaning. What exactly should count as a sale? Which time window should be used? Which source should be treated as the source of truth? If two tables produce different answers, which one reflects the real business process?</p><p>That was the point where SQL stopped feeling like a supporting skill and became infrastructure.</p><h1>What real SQL work actually looked like</h1><p>The lesson became clearer through a few recurring types of work. These were not glamorous, as they were simply the places where SQL kept proving its value.</p><ul><li><p>Ad-hoc reporting and insight requests that looked simple but hid messy logic and scattered data.</p></li><li><p>Metric definition work, where the challenge was deciding what should count before writing the query.</p></li><li><p>Combining multiple data sources without destroying the business meaning of the result.</p></li><li><p>Preparing the right data for downstream analysis and modeling in Python.</p></li></ul><div><hr></div><h2>1. Ad-hoc reporting taught me that simple requests are rarely simple</h2><p>A lot of real SQL work starts with a seemingly harmless request. The business needs a report. Someone wants a quick performance update. A team asks for insight before a meeting. On paper, it sounds like a straightforward query.</p><p>In practice, it rarely is.</p><p>Sometimes the data is not available in one place. Sometimes it lives across several sources that were never designed to fit together neatly. Sometimes the logic needed to answer the question is more complicated than the request suggests. And often the timeline is short, so you do not have the luxury of slowly wading through the data.</p><p>That changed how I think about SQL skills. In real reporting work, the challenge is not just writing something that runs. The challenge is moving from a vague business question to a reliable answer under real constraints. That takes judgment, prioritization, and a clear sense of what the output needs to mean.</p><p>Useful SQL work is often less glamorous than people expect. It is not always about elegant tricks. Very often, it is about getting the right answer quickly enough to matter, without breaking the logic behind it.</p><div><hr></div><h2>2. Metric definition matters more than query complexity</h2><p>If there is one area where real SQL work changed me the most, it is the definition of metrics.</p><p>In theory, a metric looks clean. In practice, even something as familiar as a sales number can go wrong depending on the time scope, exclusions, business rules, and source tables. A number can look precise and still be misleading if two teams are working from different assumptions or if one table captures the event differently from another.</p><p>That is why some SQL problems cannot be solved by clever syntax alone. You can write a technically correct query and still produce the wrong business answer.</p><p>The real work is often more basic and more demanding at the same time:</p><ul><li><p>deciding what should count</p></li><li><p>deciding what should be excluded</p></li><li><p>choosing which table reflects the operational truth</p></li><li><p>making sure the result matches the way the business actually works</p></li></ul><p>This is where collaboration becomes essential. There are many situations where the data exists, but understanding it requires discussion with business users who know the process behind the records. Without that alignment, a query may return rows but not the truth.</p><p>Over time, I started to see that some of the most dangerous problems in data work are not computational. They are definitional. A wrong definition can quietly damage a project, mislead stakeholders, or erode trust in the team long before anyone notices the issue.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/what-real-sql-work-taught-me-about?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/what-real-sql-work-taught-me-about?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div><hr></div><h2>3. Combining data sources is harder than it looks</h2><p>Another lesson real SQL work taught me is that combining information from multiple sources without losing meaning is much harder than it first appears.</p><p>From the outside, joins can look like a purely technical step. In practice, they can become one of the most delicate parts of a project. Sometimes a clean primary key does not exist. Sometimes the relationship is not direct. Sometimes aggregation is needed before two datasets can even be compared. And sometimes each source reflects a slightly different view of the same business concept.</p><p>That creates several risks at once: duplicate rows, dropped records, timing mismatches, and numbers that appear structurally valid but are conceptually incorrect.</p><p>This is why SQL work often requires more collaboration than people expect. To combine sources responsibly, you frequently need validation from multiple stakeholders. The challenge is not merely to make the query run. The challenge is to preserve validity.</p><p>For me, this was one of the clearest moments where SQL became inseparable from business understanding. Good SQL was not just about retrieval. It was about preserving meaning as it moved across systems.</p><div><hr></div><h2>4. Even Python-heavy data science often begins with SQL</h2><p>Because my early learning path emphasized Python, I initially imagined that most serious data-science work would begin there. In reality, SQL was often necessary before I could even start proper work in Python.</p><p>If the data lived in a SQL database, then SQL was the gatekeeper. It was how I extracted the relevant population, selected the appropriate time window, assembled the required columns, and checked whether the data were suitable for the task ahead. Whether the next step was exploratory analysis, feature preparation, modeling, or evaluation, SQL was often the first step.</p><p>That changed how I think about the relationship between SQL and data science. SQL is not simply what happens before the interesting work. Very often, it is part of the interesting work.</p><p>If the population is wrong, the feature set is incomplete, or the definition is unstable, the downstream Python work inherits that weakness. In that sense, SQL does not sit beneath data science. It sits inside it.</p><div><hr></div><h1>What I value in SQL work now</h1><p>Real work also changed how I evaluate SQL skills in others and in myself.</p><p>I still care about writing cleaner, more efficient queries, especially as data grows larger and execution speed matters. But that is no longer the first thing I look for.</p><p>What I value first is this:</p><blockquote><p>1. Correctness. The wrong data can quietly damage an entire project.</p><p>2. Stakeholder trust. Data work only becomes valuable when other people believe the result is dependable.</p><p>3. Maintainability. Many projects do not end after a single request, so someone has to live with the logic later.</p></blockquote><p>A strong SQL practitioner, in my view, is not simply someone who knows a large amount of syntax. It is someone who understands the data definition, knows how to acquire the data in the most reliable way, and can produce logic that remains useful beyond the moment it was written.</p><div><hr></div><h1>What I would tell aspiring data scientists now</h1><p>If your learning path has focused mostly on Python, I would say this clearly: do not treat SQL as optional.</p><p>You do not need to memorize every feature of the language before doing meaningful work. Documentation exists, and syntax can be learned as needed. But you do need to understand why SQL matters. It matters because data projects depend on access to the right data, under the right definitions, with logic that can withstand real business use.</p><p>That is the part I wish I had understood earlier. SQL is not important because it looks technical. It is important because it sits close to the truth conditions of data work. It is where data availability is tested. It is where definitions get challenged. It is where numbers either become trustworthy or fall apart.</p><p>For me, that has become one of the clearest professional lessons of real data work. SQL is not the opposite of data science, nor is it a lower-level skill beneath it. In many organizations, SQL is one of the foundations that allows data science to be useful at all.</p><p>And if there is one line I would leave readers with, it is this: real SQL work taught me that trustworthy definitions matter more than flashy queries.</p><p>If you are learning SQL now, learn it through real use cases. Learn it through reporting, metric definition, source validation, and the kind of business questions that force you to care about correctness. </p><div><hr></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/what-real-sql-work-taught-me-about/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/what-real-sql-work-taught-me-about/comments"><span>Leave a comment</span></a></p>]]></content:encoded></item><item><title><![CDATA[Best Stock Market data API in the AI Agent era]]></title><description><![CDATA[How modern financial APIs are powering the next generation of AI-driven market research and trading tools]]></description><link>https://www.nb-data.com/p/best-stock-market-data-api-in-the</link><guid isPermaLink="false">https://www.nb-data.com/p/best-stock-market-data-api-in-the</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sat, 14 Mar 2026 07:49:27 GMT</pubDate><enclosure url="https://images.unsplash.com/photo-1560221328-12fe60f83ab8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwyfHxzdG9ja3xlbnwwfHx8fDE3NzMxNDkwODZ8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://images.unsplash.com/photo-1560221328-12fe60f83ab8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwyfHxzdG9ja3xlbnwwfHx8fDE3NzMxNDkwODZ8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://images.unsplash.com/photo-1560221328-12fe60f83ab8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwyfHxzdG9ja3xlbnwwfHx8fDE3NzMxNDkwODZ8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1560221328-12fe60f83ab8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwyfHxzdG9ja3xlbnwwfHx8fDE3NzMxNDkwODZ8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1560221328-12fe60f83ab8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwyfHxzdG9ja3xlbnwwfHx8fDE3NzMxNDkwODZ8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1560221328-12fe60f83ab8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwyfHxzdG9ja3xlbnwwfHx8fDE3NzMxNDkwODZ8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw"><img src="https://images.unsplash.com/photo-1560221328-12fe60f83ab8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwyfHxzdG9ja3xlbnwwfHx8fDE3NzMxNDkwODZ8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" width="4800" height="3188" data-attrs="{&quot;src&quot;:&quot;https://images.unsplash.com/photo-1560221328-12fe60f83ab8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwyfHxzdG9ja3xlbnwwfHx8fDE3NzMxNDkwODZ8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:3188,&quot;width&quot;:4800,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;close-up photo of monitor displaying graph&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="close-up photo of monitor displaying graph" title="close-up photo of monitor displaying graph" srcset="https://images.unsplash.com/photo-1560221328-12fe60f83ab8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwyfHxzdG9ja3xlbnwwfHx8fDE3NzMxNDkwODZ8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1560221328-12fe60f83ab8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwyfHxzdG9ja3xlbnwwfHx8fDE3NzMxNDkwODZ8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1560221328-12fe60f83ab8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwyfHxzdG9ja3xlbnwwfHx8fDE3NzMxNDkwODZ8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1560221328-12fe60f83ab8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHwyfHxzdG9ja3xlbnwwfHx8fDE3NzMxNDkwODZ8MA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@bash__profile">Nicholas Cappello</a> on <a href="https://unsplash.com">Unsplash</a></figcaption></figure></div><p>The stock market data API landscape is changing. In the past, developers mostly evaluated providers on familiar dimensions: coverage, latency, pricing, documentation, and reliability. Those criteria still matter, but the rise of LLM-powered copilots, autonomous research workflows, and multi-agent financial systems has introduced a new requirement: how easily can a data provider plug into agentic software?</p><p>In that environment, the strongest providers are not just the ones with broad datasets. They are the ones that expose clean, structured interfaces that AI agents can query, reason over, and combine with downstream tools for analysis, monitoring, and decision support. Some vendors are already leaning into this shift with MCP servers and LLM-oriented resources. Others remain stronger as enterprise data backbones than as explicitly AI-native platforms.</p><p>In this article, we will explore the Best Stock Market data API in the AI Agent era. Curious about it? </p><p>Let&#8217;s get into it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h1><strong>Alpha Vantage</strong></h1><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xh-Y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8db652-ca9d-40e1-be34-c06229ff5dc2_2474x1278.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xh-Y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8db652-ca9d-40e1-be34-c06229ff5dc2_2474x1278.png 424w, https://substackcdn.com/image/fetch/$s_!xh-Y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8db652-ca9d-40e1-be34-c06229ff5dc2_2474x1278.png 848w, https://substackcdn.com/image/fetch/$s_!xh-Y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8db652-ca9d-40e1-be34-c06229ff5dc2_2474x1278.png 1272w, https://substackcdn.com/image/fetch/$s_!xh-Y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8db652-ca9d-40e1-be34-c06229ff5dc2_2474x1278.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xh-Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8db652-ca9d-40e1-be34-c06229ff5dc2_2474x1278.png" width="1456" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8d8db652-ca9d-40e1-be34-c06229ff5dc2_2474x1278.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:182857,&quot;alt&quot;:&quot;Best Stock Market data API in the AI Agent era&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/190589167?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8db652-ca9d-40e1-be34-c06229ff5dc2_2474x1278.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Best Stock Market data API in the AI Agent era" title="Best Stock Market data API in the AI Agent era" srcset="https://substackcdn.com/image/fetch/$s_!xh-Y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8db652-ca9d-40e1-be34-c06229ff5dc2_2474x1278.png 424w, https://substackcdn.com/image/fetch/$s_!xh-Y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8db652-ca9d-40e1-be34-c06229ff5dc2_2474x1278.png 848w, https://substackcdn.com/image/fetch/$s_!xh-Y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8db652-ca9d-40e1-be34-c06229ff5dc2_2474x1278.png 1272w, https://substackcdn.com/image/fetch/$s_!xh-Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d8db652-ca9d-40e1-be34-c06229ff5dc2_2474x1278.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>Overview</strong></h3><p><a href="https://www.alphavantage.co/">Alpha Vantage</a> is a widely used financial data platform that provides real-time and historical market data APIs for equities, options, forex, cryptocurrencies, and macroeconomic indicators. The platform is designed to support both individual developers and professional trading systems through a simple, developer-friendly interface and a large catalog of market datasets.<br><br>A distinguishing feature of Alpha Vantage is the breadth of its data coverage. The platform delivers real-time and historical financial market data through programmatic APIs and spreadsheet integrations, enabling developers to build trading dashboards, quantitative research pipelines, and automated trading tools on top of a unified data interface.<br><br>The API also provides a rich library of built-in analytics&#8212;including technical indicators and fundamental datasets&#8212;allowing users to retrieve both raw market data and higher-level financial signals without implementing complex calculations themselves. In practice, this makes Alpha Vantage a flexible backbone for applications ranging from educational projects and fintech prototypes to production trading systems and investment research platforms.</p><h3><strong>What makes it valuable in the AI Agent era?</strong></h3><p>Alpha Vantage has become particularly relevant in the emerging ecosystem of LLM-powered financial tools and autonomous AI agents, largely because it provides structured market data in formats that are easy for agents and models to access, reason over, and integrate into automated workflows.<br><br><strong>1. Native integration with AI agent ecosystems via MCP</strong><br>Alpha Vantage provides an official Model Context Protocol (MCP) server, enabling large language models and agent-based applications to directly access financial data through standardized tools. The MCP server allows AI assistants and development environments to query real-time and historical stock market data programmatically, turning the API into a plug-and-play data source for agentic systems. <br><br><strong>2. Compatibility with multi-agent financial research systems</strong><br>Modern agentic trading frameworks increasingly rely on structured financial APIs like Alpha Vantage as data sources. For example, the open-source <a href="https://trading-agents.ai/">TradingAgents framework</a> simulates a professional trading firm using multiple LLM-powered agents&#8212;such as fundamental analysts, technical analysts, sentiment analysts, traders, and risk managers&#8212;that collaborate to analyze equities and make decisions. This system is powered by Alpha Vantage API as the core data backbone. <br><br><strong>3. Documentation and developer assets optimized for machine consumption</strong><br>Another advantage in the LLM era is the structure and accessibility of Alpha Vantage&#8217;s developer resources. The platform provides comprehensive API documentation, examples, and community libraries across many programming languages, making it straightforward for both humans and AI coding agents to integrate financial data pipelines. Because LLM-powered development tools rely heavily on structured documentation, well-defined API endpoints, and example code, this ecosystem of docs, SDKs, and README files makes Alpha Vantage particularly easy for AI systems to learn and use.</p><h3><strong>In short</strong></h3><p>Alpha Vantage&#8217;s combination of structured financial APIs, an MCP interface for AI agents, and extensive developer documentation positions it as a data infrastructure layer for the emerging generation of AI-powered trading tools, research agents, and autonomous financial analysis systems.</p><div><hr></div><h2>Tradier</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5L68!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f43f50f-00df-4007-8f4e-b28403ddf3a0_2399x1459.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5L68!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f43f50f-00df-4007-8f4e-b28403ddf3a0_2399x1459.png 424w, https://substackcdn.com/image/fetch/$s_!5L68!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f43f50f-00df-4007-8f4e-b28403ddf3a0_2399x1459.png 848w, https://substackcdn.com/image/fetch/$s_!5L68!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f43f50f-00df-4007-8f4e-b28403ddf3a0_2399x1459.png 1272w, https://substackcdn.com/image/fetch/$s_!5L68!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f43f50f-00df-4007-8f4e-b28403ddf3a0_2399x1459.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5L68!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f43f50f-00df-4007-8f4e-b28403ddf3a0_2399x1459.png" width="1456" height="885" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5f43f50f-00df-4007-8f4e-b28403ddf3a0_2399x1459.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:885,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:848183,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/190589167?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f43f50f-00df-4007-8f4e-b28403ddf3a0_2399x1459.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!5L68!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f43f50f-00df-4007-8f4e-b28403ddf3a0_2399x1459.png 424w, https://substackcdn.com/image/fetch/$s_!5L68!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f43f50f-00df-4007-8f4e-b28403ddf3a0_2399x1459.png 848w, https://substackcdn.com/image/fetch/$s_!5L68!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f43f50f-00df-4007-8f4e-b28403ddf3a0_2399x1459.png 1272w, https://substackcdn.com/image/fetch/$s_!5L68!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f43f50f-00df-4007-8f4e-b28403ddf3a0_2399x1459.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Overview</h3><p><a href="https://tradier.com/">Tradier</a> is a brokerage-focused API platform that combines market data, account access, and trading functionality. Its public API supports real-time, delayed, and historical market data through both request/response endpoints and streaming interfaces, while also exposing brokerage capabilities such as account information, positions, orders, watchlists, and trade execution.</p><p>A key differentiator is that Tradier is not just a data API. It is part of a brokerage stack. That means developers can use it not only to retrieve quotes, options chains, time-and-sales data, and historical pricing, but also to connect agentic workflows directly to trading and portfolio actions. Tradier also supports HTTP and WebSocket streaming, which is useful when building systems that need fast updates rather than purely batch-style analysis.</p><p>Tradier&#8217;s market-data positioning is more U.S.-brokerage-centric than broad all-asset-class research platforms. Real-time data is available to Tradier Brokerage account holders for U.S. stocks and options, and delayed data follows the standard 15-minute model for non-real-time access. That makes Tradier particularly compelling for execution-oriented applications rather than for the widest possible global dataset footprint.</p><h3>What makes it valuable in the AI Agent era?</h3><p><strong>1. MCP and LLM-oriented documentation</strong><br>Tradier is unusually forward-leaning in how it presents its docs to the LLM era. Its documentation includes <code>llms.txt</code>, dedicated LLM resources, and a Tradier MCP section. Tradier&#8217;s own MCP documentation says users can access market data, account details, documentation, and even place trades from within connected AI tools. That makes Tradier one of the few providers publicly bridging financial APIs and conversational interfaces in a first-class way.</p><p><strong>2. Strong fit for execution-capable agents</strong><br>Many financial APIs stop at data retrieval. Tradier goes further by combining data access with brokerage actions such as order placement, account history, positions, and balances. In the AI agent era, that matters because the most interesting systems are often not just research agents but action-taking agents. Tradier is therefore especially relevant for developers building guarded execution workflows, trading copilots, or semi-autonomous assistants that need both read and act capabilities.</p><p><strong>3. Streaming interfaces for real-time agent loops</strong><br>Tradier supports both HTTP and WebSocket streaming for market and account data. That is important for agent architectures that continuously monitor events, react to intraday changes, or trigger downstream workflows when market conditions shift. In practical terms, Tradier is better suited than batch-only APIs for event-driven agents that need live context rather than periodic polling alone.</p><h3>In short</h3><p>Tradier is one of the strongest options for AI agents that need to move beyond analysis into brokerage-connected workflows. It may not be the broadest general-purpose research API, but for U.S.-market, execution-aware agents, Tradier&#8217;s mix of market data, account endpoints, streaming support, and MCP/LLM resources makes it highly relevant.</p><div><hr></div><h2>Xignite</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mBY8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcbb256b-df66-44d4-909a-a9faf81debb0_2777x1331.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mBY8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcbb256b-df66-44d4-909a-a9faf81debb0_2777x1331.png 424w, https://substackcdn.com/image/fetch/$s_!mBY8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcbb256b-df66-44d4-909a-a9faf81debb0_2777x1331.png 848w, https://substackcdn.com/image/fetch/$s_!mBY8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcbb256b-df66-44d4-909a-a9faf81debb0_2777x1331.png 1272w, https://substackcdn.com/image/fetch/$s_!mBY8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcbb256b-df66-44d4-909a-a9faf81debb0_2777x1331.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mBY8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcbb256b-df66-44d4-909a-a9faf81debb0_2777x1331.png" width="1456" height="698" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bcbb256b-df66-44d4-909a-a9faf81debb0_2777x1331.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:698,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2318388,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/190589167?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcbb256b-df66-44d4-909a-a9faf81debb0_2777x1331.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!mBY8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcbb256b-df66-44d4-909a-a9faf81debb0_2777x1331.png 424w, https://substackcdn.com/image/fetch/$s_!mBY8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcbb256b-df66-44d4-909a-a9faf81debb0_2777x1331.png 848w, https://substackcdn.com/image/fetch/$s_!mBY8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcbb256b-df66-44d4-909a-a9faf81debb0_2777x1331.png 1272w, https://substackcdn.com/image/fetch/$s_!mBY8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbcbb256b-df66-44d4-909a-a9faf81debb0_2777x1331.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Overview</h3><p><a href="https://www.xignite.com/">Xignite</a> is an enterprise financial data platform centered on cloud-delivered APIs and market-data management. Its catalog covers stock quotes, ETFs and mutual funds, foreign exchange, futures and options, indices and benchmarks, fixed income and rates, company fundamentals, reference data, earnings, and news. The company also emphasizes broad upstream sourcing, stating that its data comes from more than 250 providers, alongside curated in-house datasets.</p><p>Xignite&#8217;s public positioning is less &#8220;developer hobbyist API&#8221; and more enterprise-grade market data infrastructure. It highlights unlimited-usage pricing, flexible commercial packaging by asset class, call frequency, and region, and delivery models that include real-time, historical, and reference data. Its developer materials also show a broad set of products for delayed quotes, real-time quotes, historical data, streaming, alerts, IPOs, and company information.</p><p>That means Xignite is best understood as a data platform for institutions and mature fintech products rather than as a lightweight API-first experimentation layer. For many teams, that is a feature, not a drawback. In an AI stack, the most valuable data provider is often the one that can reliably serve as the normalized source behind internal models, orchestration layers, and production analytics systems. This last point is an inference from Xignite&#8217;s product positioning toward scalable enterprise delivery and market-data management.</p><h3>What makes it valuable in the AI Agent era?</h3><p><strong>1. Enterprise-grade breadth for multi-source agent pipelines</strong><br>AI agents become more useful when they can combine quotes, fundamentals, benchmarks, reference data, and news into a single reasoning loop. Xignite&#8217;s catalog is strong on this dimension. Because it covers a wide range of asset classes and reference datasets, it can act as the structured data layer beneath enterprise financial copilots and internal analyst tools.</p><p><strong>2. Strong fit for organizations building their own orchestration layer</strong><br>Unlike Alpha Vantage or EODHD, Xignite&#8217;s public materials emphasize APIs, coverage, and market-data management rather than agent-specific packaging. In practice, that makes it attractive for organizations that want to build their own AI architecture on top of a robust enterprise data backbone instead of depending on vendor-supplied MCP experiences. That is an inference from Xignite&#8217;s public positioning around cloud APIs, data management, and unlimited-usage commercial structure.</p><p><strong>3. Flexible delivery for production-scale systems</strong><br>Xignite supports multiple delivery modes across real-time, delayed, historical, and streaming-style services, and it explicitly markets itself for demanding display applications, backtesting, alerts, and application integration. That flexibility matters in AI systems because not all components need the same data path: one model might need historical fundamentals, another might need event-driven market updates, and a third might need reference data normalization.</p><h3>In short</h3><p>Xignite is not the most visibly AI-marketed provider in this group, but it is a serious contender for enterprise AI finance stacks. If your goal is to build a proprietary agent platform on top of large-scale, normalized market-data services, Xignite&#8217;s breadth and infrastructure orientation make it more compelling than its relative lack of public AI branding might suggest.</p><div><hr></div><h2>EOD Historical Data</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!W-VS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F488ecb5a-a062-4fc1-8602-441173a0ea4a_2381x1229.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!W-VS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F488ecb5a-a062-4fc1-8602-441173a0ea4a_2381x1229.png 424w, https://substackcdn.com/image/fetch/$s_!W-VS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F488ecb5a-a062-4fc1-8602-441173a0ea4a_2381x1229.png 848w, https://substackcdn.com/image/fetch/$s_!W-VS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F488ecb5a-a062-4fc1-8602-441173a0ea4a_2381x1229.png 1272w, https://substackcdn.com/image/fetch/$s_!W-VS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F488ecb5a-a062-4fc1-8602-441173a0ea4a_2381x1229.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!W-VS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F488ecb5a-a062-4fc1-8602-441173a0ea4a_2381x1229.png" width="1456" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/488ecb5a-a062-4fc1-8602-441173a0ea4a_2381x1229.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1281074,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/190589167?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F488ecb5a-a062-4fc1-8602-441173a0ea4a_2381x1229.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!W-VS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F488ecb5a-a062-4fc1-8602-441173a0ea4a_2381x1229.png 424w, https://substackcdn.com/image/fetch/$s_!W-VS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F488ecb5a-a062-4fc1-8602-441173a0ea4a_2381x1229.png 848w, https://substackcdn.com/image/fetch/$s_!W-VS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F488ecb5a-a062-4fc1-8602-441173a0ea4a_2381x1229.png 1272w, https://substackcdn.com/image/fetch/$s_!W-VS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F488ecb5a-a062-4fc1-8602-441173a0ea4a_2381x1229.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Overview</h3><p><a href="https://eodhd.com/">EOD Historical Data</a>, now commonly presented as EODHD, offers a broad financial data platform spanning fundamentals, historical end-of-day prices, live and real-time feeds, intraday data, U.S. options, financial news, stock screeners, technical indicators, and exchange/reference datasets. On its homepage, the company positions itself as a &#8220;one-stop shop&#8221; for 30+ years of historical, fundamental, and real-time data across global markets, with coverage figures including 60 stock exchanges and 150,000 tickers.</p><p>One of EODHD&#8217;s strengths is that it sits between lightweight developer tools and more professional research infrastructure. It offers structured JSON and CSV responses, coding libraries, spreadsheet add-ons, and a broad menu of market datasets without being limited to only one narrow workflow. It also exposes precomputed technical indicators through API endpoints rather than requiring users to calculate everything from raw time series.</p><p>This combination makes EODHD particularly attractive for builders who want reasonably broad market-data coverage and analytics features in a format that remains accessible to smaller teams, solo developers, and applied AI prototypes.</p><h3>What makes it valuable in the AI Agent era?</h3><p><strong>1. Official MCP support for agent integration</strong><br>EODHD provides an official MCP server for financial data and explicitly documents how to connect it to ChatGPT, Claude, and custom AI agents. The company describes this as a way for AI agents and LLMs to access real-time and historical financial data directly through MCP, making EODHD one of the clearest AI-era data providers alongside Alpha Vantage and Tradier.</p><p><strong>2. An official ChatGPT-oriented financial assistant</strong><br>Beyond MCP, EODHD also offers an official Financial Assistant for ChatGPT, which it describes as an AI that can generate code for EODHD APIs and provide finance insights grounded in real data and news. That does not just signal marketing interest in AI; it suggests the company is actively shaping its product and developer experience around LLM-driven usage patterns.</p><p><strong>3. Strong structured outputs plus higher-level analytics</strong><br>EODHD&#8217;s AI relevance is also practical. It provides structured JSON/CSV outputs, extensive API documentation, libraries, and technical-indicator endpoints that already package financial signals into machine-usable form. For agentic systems, that reduces the burden of transforming raw market data before it can be used in screening, summarization, ranking, or recommendation workflows.</p><h3>In short</h3><p>EODHD is one of the strongest all-around options for the AI agent era. It combines broad market coverage with precomputed indicators, developer-friendly structured data, an official MCP server, and a ChatGPT-oriented assistant. For teams that want something more AI-forward than classic enterprise vendors but broader than a narrow single-purpose API, EODHD is a very strong choice.</p><div><hr></div><h2>QuoteMedia</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!T3Qn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a158f85-6722-4e0c-8fb4-5eb4a4b119c6_2613x1409.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!T3Qn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a158f85-6722-4e0c-8fb4-5eb4a4b119c6_2613x1409.png 424w, https://substackcdn.com/image/fetch/$s_!T3Qn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a158f85-6722-4e0c-8fb4-5eb4a4b119c6_2613x1409.png 848w, https://substackcdn.com/image/fetch/$s_!T3Qn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a158f85-6722-4e0c-8fb4-5eb4a4b119c6_2613x1409.png 1272w, https://substackcdn.com/image/fetch/$s_!T3Qn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a158f85-6722-4e0c-8fb4-5eb4a4b119c6_2613x1409.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!T3Qn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a158f85-6722-4e0c-8fb4-5eb4a4b119c6_2613x1409.png" width="1456" height="785" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4a158f85-6722-4e0c-8fb4-5eb4a4b119c6_2613x1409.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:785,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1613479,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/190589167?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a158f85-6722-4e0c-8fb4-5eb4a4b119c6_2613x1409.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!T3Qn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a158f85-6722-4e0c-8fb4-5eb4a4b119c6_2613x1409.png 424w, https://substackcdn.com/image/fetch/$s_!T3Qn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a158f85-6722-4e0c-8fb4-5eb4a4b119c6_2613x1409.png 848w, https://substackcdn.com/image/fetch/$s_!T3Qn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a158f85-6722-4e0c-8fb4-5eb4a4b119c6_2613x1409.png 1272w, https://substackcdn.com/image/fetch/$s_!T3Qn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a158f85-6722-4e0c-8fb4-5eb4a4b119c6_2613x1409.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Overview</h3><p><a href="https://quotemedia.com/">QuoteMedia</a> is a long-standing market-data provider focused on real-time and historical data, news, analytics, and financial information for brokerages, websites, trading systems, and investor-facing products. Its Request APIs and OnDemand services are built around cloud-based access to market data, while its streaming products emphasize tick-by-tick delivery, low latency, and enterprise-grade reliability. QuoteMedia also highlights broad operational scale, including 110+ global exchanges, 200+ data APIs, 99.99% uptime, and 100+ news providers.</p><p>A notable strength of QuoteMedia is delivery flexibility. Its platform spans REST-style OnDemand APIs, WebSocket and other streaming interfaces, and SFTP-based file services for bulk delivery. It also supports JSON, XML, CSV, option-chain data, company profiles, historical time series, filings, and custom calculations. That makes QuoteMedia less of a single API product and more of a market-data delivery platform.</p><p>QuoteMedia&#8217;s public positioning is similar to Xignite in one important way: it is more infrastructure-oriented than explicitly LLM-oriented. In other words, its clearest strengths are reliability, breadth, delivery options, and integration into financial products, not public MCP or agent-marketing. That is an inference from the official materials reviewed.</p><h3>What makes it valuable in the AI Agent era?</h3><p><strong>1. Low-latency data for real-time agent monitoring</strong><br>QuoteMedia&#8217;s streaming stack is designed for real-time or delayed tick-by-tick data, normalized for ease of use and optimized for single-digit millisecond performance. For AI systems that monitor live markets, score signals, or trigger alerts and workflows off intraday movement, that kind of delivery profile is highly relevant.</p><p><strong>2. Multiple delivery modes for different agent architectures</strong><br>Modern AI finance stacks are not monolithic. Some components work best with REST requests, others with streams, and others with bulk files for offline training or evaluation. QuoteMedia supports cloud REST APIs, streaming APIs, and SFTP/file services, which makes it well suited to organizations building layered pipelines that combine real-time agent behavior with batch analytics and historical model development.</p><p><strong>3. Strong fit as a production data layer</strong><br>QuoteMedia offers market data, news, analytics, company profiles, option chains, filings, and historical data in structured formats such as JSON, XML, and CSV. That breadth makes it a useful foundation for internal copilots, research dashboards, summarization systems, and client-facing financial applications where the &#8220;AI&#8221; layer is built on top of the data platform rather than bundled by the vendor itself.</p><h3>In short</h3><p>QuoteMedia is a strong candidate for teams that care more about production-grade delivery and integration flexibility than about whether the vendor has already branded itself around AI agents. In the AI agent era, that still matters a lot: a reliable, low-latency, multi-format market-data backbone can be more valuable than flashy AI positioning if you are building your own orchestration layer.</p><div><hr></div><h2>Conclusion</h2><p>If the goal is to find the most AI-ready providers, Alpha Vantage, Tradier, and EODHD stand out because they already offer MCP or LLM-oriented support. Alpha Vantage is particularly strong for AI-native research tools, Tradier is strong for brokerage-connected agents, and EODHD is a strong general-purpose choice.</p><p>If the goal is enterprise-grade infrastructure for proprietary AI systems, Xignite and QuoteMedia remain highly relevant. They may be less visibly AI-marketed, but they are strong as scalable market data backbones.</p><p>So in the AI agent era, the best stock market data API depends on what you are building. For AI-native financial research, Alpha Vantage has a strong edge. For execution-oriented agents, Tradier stands out. For broad AI-enabled workflows, EODHD is highly competitive. For enterprise infrastructure, Xignite and QuoteMedia are still important players.</p>]]></content:encoded></item><item><title><![CDATA[7 SQL Use Cases Every Data Professional Should Know]]></title><description><![CDATA[Most people learn SQL as syntax. The real thing is knowing what kinds of problems it helps you solve.]]></description><link>https://www.nb-data.com/p/7-sql-use-cases-every-data-professional</link><guid isPermaLink="false">https://www.nb-data.com/p/7-sql-use-cases-every-data-professional</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sat, 07 Mar 2026 12:19:16 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!9orW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bbb6184-6b0e-45de-9ab4-0f7ec96007de_1376x768.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9orW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bbb6184-6b0e-45de-9ab4-0f7ec96007de_1376x768.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9orW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bbb6184-6b0e-45de-9ab4-0f7ec96007de_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!9orW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bbb6184-6b0e-45de-9ab4-0f7ec96007de_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!9orW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bbb6184-6b0e-45de-9ab4-0f7ec96007de_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!9orW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bbb6184-6b0e-45de-9ab4-0f7ec96007de_1376x768.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9orW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bbb6184-6b0e-45de-9ab4-0f7ec96007de_1376x768.png" width="1376" height="768" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9bbb6184-6b0e-45de-9ab4-0f7ec96007de_1376x768.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:768,&quot;width&quot;:1376,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2007256,&quot;alt&quot;:&quot;7 SQL Use Cases Every Data Professional Should Know&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/190182290?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bbb6184-6b0e-45de-9ab4-0f7ec96007de_1376x768.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="7 SQL Use Cases Every Data Professional Should Know" title="7 SQL Use Cases Every Data Professional Should Know" srcset="https://substackcdn.com/image/fetch/$s_!9orW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bbb6184-6b0e-45de-9ab4-0f7ec96007de_1376x768.png 424w, https://substackcdn.com/image/fetch/$s_!9orW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bbb6184-6b0e-45de-9ab4-0f7ec96007de_1376x768.png 848w, https://substackcdn.com/image/fetch/$s_!9orW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bbb6184-6b0e-45de-9ab4-0f7ec96007de_1376x768.png 1272w, https://substackcdn.com/image/fetch/$s_!9orW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9bbb6184-6b0e-45de-9ab4-0f7ec96007de_1376x768.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A lot of people learn SQL in a frustrating way.</p><p>They start with <code>SELECT</code>, <code>FROM</code>, <code>WHERE</code>, <code>GROUP BY</code>, maybe a few joins, and if they stay long enough, a window function or two. They can write queries. They can pass the exercises. But when they face a real business question, they still freeze.</p><p>That usually happens because they learned SQL as a list of clauses instead of a way to think.</p><p>In real work, SQL is rarely about showing that you remember syntax. It is about knowing what question arises once it hits the data. Questions such as:</p><ul><li><p>Is this a reporting problem? </p></li><li><p>A funnel problem? </p></li><li><p>A cohort problem? </p></li><li><p>A segmentation problem? </p></li><li><p>A QA problem? </p></li></ul><p>The moment you can recognize that, SQL becomes much less intimidating and much more useful. That is the shift that matters.</p><p>The people who get genuinely strong at SQL are usually not the people who memorize the most functions. They are the people who can look at a business question and quickly understand what kind of data transformation it needs.</p><p>So instead of thinking about SQL as &#8220;a language I should know,&#8221; I think it is more useful to think about it as a toolkit for a handful of recurring jobs.</p><p>Here are seven of the most important ones. Let&#8217;s get into it.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>1. KPI reporting</h2><p>When teams want to know what is happening in the business, they usually start with some version of a KPI question. Revenue by month. Daily active users. Orders by country. Average order value. Churn rate by plan. Refund rate by product. These are not flashy questions, but they are the foundation of most reporting work.</p><p>This is where SQL starts becoming practical. You are not trying to prove how advanced you are. You are trying to turn raw data into something clear enough for another person to act on.</p><p>That means defining the metric carefully, filtering the right time window, grouping at the right level, and returning a result that is readable. The technical tools are simple, but the judgment behind them matters a lot.</p><p>A lot of people underestimate this kind of SQL because it feels too basic. I think that is a mistake. A team with weak KPI logic usually ends up with weak everything else.</p><p>A simple example is monthly revenue by product category:</p><pre><code>SELECT
    DATE_TRUNC(&#8217;month&#8217;, order_date) AS order_month,
    product_category,
    SUM(revenue) AS total_revenue
FROM orders
WHERE order_date &gt;= DATE &#8216;2026-01-01&#8217;
GROUP BY 1, 2
ORDER BY 1, 3 DESC;</code></pre><p>This is a basic grouped summary, but that is exactly why it matters. A lot of useful SQL is just good filtering, clean aggregation, and returning a table that another person can use.</p><h2>2. Funnel analysis</h2><p>The second major use case is figuring out where people drop off.</p><p>This is where SQL starts feeling very close to product and growth work. A funnel question usually sounds like this: how many users started onboarding, how many completed profile setup, how many created their first project, and how many upgraded? In ecommerce, the same question shows up as view product, add to cart, begin checkout, and pay.</p><p>What makes funnel analysis valuable is that it shows where interest turns into friction.</p><p>A lot of the time, the problem is not &#8220;traffic is low.&#8221; The problem is that the path breaks at one specific step. SQL helps you see that step clearly. It lets you move from a vague sense that &#8220;conversion feels weak&#8221; to a more precise question like &#8220;why do so many users disappear between signup and first action?&#8221;</p><p>A simple event-based funnel might look like this:</p><pre><code>SELECT
    step_name,
    COUNT(DISTINCT user_id) AS users_at_step
FROM onboarding_events
WHERE event_date &gt;= DATE &#8216;2026-03-01&#8217;
GROUP BY 1
ORDER BY
    CASE step_name
        WHEN &#8216;signup&#8217; THEN 1
        WHEN &#8216;verify_email&#8217; THEN 2
        WHEN &#8216;create_project&#8217; THEN 3
        WHEN &#8216;first_active_use&#8217; THEN 4
    END;</code></pre><p>This is not the most advanced funnel query in the world, but it already gives you a clearer conversation. Instead of saying &#8220;activation is weak,&#8221; you can ask, &#8220;Why do so many users disappear between verification and first project creation?&#8221;</p><p>Once you can answer that, the conversation gets much more useful.</p><h2>3. Cohort retention analysis</h2><p>This is one of the most important SQL use cases because it forces better thinking.</p><p>A cohort retention analysis groups users by a shared starting point, then checks whether they come back in later periods. That sounds simple, but it is one of those areas where small definition choices change the whole story. What puts a user into a cohort? What counts as a return? What does a week mean? Should a user count once per week or every time they generate an event?</p><p>That is why good retention work is not mainly about writing SQL. It is about locking the logic before the SQL ever begins.</p><p>This is also where SQL becomes more than a reporting language. It becomes a way of expressing lifecycle behavior. Once you can build a trustworthy retention table, you can stop asking &#8220;are users coming back?&#8221; in a vague way and start asking &#8220;which users are sticking, when do they drop, and what changed across cohorts?&#8221;</p><p>That is one of the reasons I like this use case so much. It pushes people past syntax into actual analytical design.</p><p>A very small example of the logic looks like this:</p><pre><code>WITH user_cohort AS (
    SELECT
        user_id,
        DATE_TRUNC(&#8217;week&#8217;, MIN(login_date)) AS cohort_week
    FROM logins
    GROUP BY 1
),
user_activity AS (
    SELECT
        l.user_id,
        DATE_TRUNC(&#8217;week&#8217;, l.login_date) AS activity_week
    FROM logins l
    GROUP BY 1, 2
)
SELECT
    c.cohort_week,
    a.activity_week,
    COUNT(DISTINCT a.user_id) AS active_users
FROM user_cohort c
JOIN user_activity a
  ON c.user_id = a.user_id
GROUP BY 1, 2
ORDER BY 1, 2;</code></pre><p>This is only the skeleton, not the full retention table. But even here, you can already see the shape: assign the cohort, map later activity, then aggregate by period.</p><p>You can check the deep dive of this use case here:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;a0951efb-026e-4d0d-aeaf-8731e89e2fbc&quot;,&quot;caption&quot;:&quot;Most retention tables are not wrong because the SQL is complicated.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Cohort Retention in SQL&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:6000855,&quot;name&quot;:&quot;Cornellius Yudha Wijaya&quot;,&quot;bio&quot;:&quot;Sharing Data Knowledge to improve your values&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/583076b2-657b-44bf-8aa9-9263e5bf04f0_544x544.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-03-06T18:39:19.710Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/$s_!5opy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e76dc69-8ad0-449f-8f74-a8b8885dbdd9_1312x736.jpeg&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://www.nb-data.com/p/cohort-retention-in-sql&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:190126571,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:1,&quot;comment_count&quot;:0,&quot;publication_id&quot;:37262,&quot;publication_name&quot;:&quot;Non-Brand Data&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!06DP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0e1cde-d120-4029-8ffd-2a8c7c6e4504_1280x1280.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><h2>4. Segmentation</h2><p>Once you know the overall number, the next question is almost always: who exactly is driving it?</p><p>That is segmentation.</p><p>Averages are useful, but they hide a lot. SQL becomes much more powerful once you stop treating all users as one group and start cutting the data into meaningful slices. That might mean country, plan, acquisition channel, device type, power users versus casual users, or first purchase month.</p><p>And in practice, this is where a lot of strong SQL users separate themselves. They stop producing one big average and start showing where the business behaves differently across groups.</p><p>A simple segmentation example might be conversion rate by acquisition channel:</p><pre><code>SELECT
    acquisition_channel,
    COUNT(DISTINCT user_id) AS users,
    SUM(CASE WHEN converted = 1 THEN 1 ELSE 0 END) AS converted_users,
    ROUND(
        1.0 * SUM(CASE WHEN converted = 1 THEN 1 ELSE 0 END)
        / COUNT(DISTINCT user_id),
        3
    ) AS conversion_rate
FROM user_conversion_summary
GROUP BY 1
ORDER BY conversion_rate DESC;</code></pre><p>This is where SQL starts feeling strategic. You stop asking, &#8220;Is conversion improving?&#8221; and start asking, &#8220;Is conversion improving for the users we actually care about?&#8221;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/7-sql-use-cases-every-data-professional?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/7-sql-use-cases-every-data-professional?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><h2>5. Experiment analysis</h2><p>If you work near product or growth teams, SQL becomes very important the moment experiments show up.</p><p>Before anyone talks about significance, lift, or confidence intervals, someone still has to build the dataset properly. Who was in the control group? Who was in the treatment group? Who converted? Over what window? Were there logging issues? Did the assignment logic work as expected?</p><p>A lot of that early work is SQL.</p><p>And this matters more than people think, because if the experiment table is wrong, everything that comes after it is already compromised. If the assignment table is joined incorrectly, if the outcome window is inconsistent, or if duplicated rows quietly inflate conversions, the eventual statistical discussion becomes much less meaningful.</p><p>So even though experiment analysis sounds advanced, a lot of it still comes down to careful SQL habits and clean dataset construction.</p><p>A simple experiment summary might look like this:</p><pre><code>SELECT
    variant,
    COUNT(DISTINCT user_id) AS users,
    SUM(CASE WHEN purchased = 1 THEN 1 ELSE 0 END) AS purchasers,
    ROUND(
        1.0 * SUM(CASE WHEN purchased = 1 THEN 1 ELSE 0 END)
        / COUNT(DISTINCT user_id),
        3
    ) AS purchase_rate
FROM experiment_user_summary
WHERE experiment_name = &#8216;checkout_redesign_v1&#8217;
GROUP BY 1
ORDER BY 1;</code></pre><p>That is not the full experiment analysis, but it is the foundation.</p><h2>6. Data quality and QA checks</h2><p>This is one of the least glamorous SQL use cases, and one of the most valuable.</p><p>A huge amount of trust in data work comes from catching bad structure early. Duplicate rows. Missing keys. Broken joins. Sudden changes in counts. Tables that stopped updating. Records that should be impossible but somehow exist anyway.</p><p>SQL is excellent for this kind of work because it is good at isolating patterns, comparing counts, checking coverage, and surfacing anomalies before they become reporting problems.</p><p>This is also one of the places where data professionals become more mature in practice. They stop using SQL only to answer the question they were asked, and they start using SQL to challenge whether the dataset itself deserves trust.</p><p>That is a very different mindset.</p><p>Once you develop it, your work usually becomes much more reliable.</p><p>For example, if you want to check for duplicate order IDs:</p><pre><code>SELECT
    order_id,
    COUNT(*) AS row_count
FROM orders
GROUP BY 1
HAVING COUNT(*) &gt; 1
ORDER BY row_count DESC;</code></pre><p>This is basic, but incredibly useful. </p><h2>7. Operational monitoring</h2><p>The last use case is the one that makes SQL feel closest to the day-to-day operating layer of a business.</p><p>Sometimes the question is not &#8220;what happened this quarter?&#8221; Sometimes the question is &#8220;did the pipeline run?&#8221;, &#8220;are transactions missing?&#8221;, &#8220;did yesterday&#8217;s volume collapse?&#8221;, or &#8220;did a critical table stop refreshing?&#8221;</p><p>At that point, SQL is not just helping with analysis. It is helping keep the system honest.</p><p>This kind of work often lives somewhere between analytics, operations, and data engineering. You are comparing expected versus actual counts, checking daily or weekly movement, and trying to spot problems before somebody else finds them in a broken dashboard or an angry meeting.</p><p>If you only think of SQL as a tool for reports, you miss how often it becomes part of the business&#8217;s operational nervous system.</p><p>A simple monitoring query might compare day-over-day order counts:</p><pre><code>SELECT
    order_date,
    COUNT(*) AS orders_today,
    LAG(COUNT(*)) OVER (ORDER BY order_date) AS orders_yesterday
FROM orders
GROUP BY 1
ORDER BY 1;</code></pre><p>This is where window functions become especially useful. They let you compare each row to related rows while keeping the row-level result visible, which is exactly the kind of thing you want for trend and monitoring work. </p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/7-sql-use-cases-every-data-professional/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/7-sql-use-cases-every-data-professional/comments"><span>Leave a comment</span></a></p><h2>The bigger point</h2><p>If you look across all seven use cases, the pattern is pretty clear.</p><p>SQL is rarely valuable because of its isolated syntax.</p><p>It is valuable because the same small set of ideas keeps getting reused across real work.</p><p>That is why strong SQL users usually do not sound like they are reciting functions. They sound like they understand data shape.</p><p>That is a much better goal than &#8220;learn more SQL syntax.&#8221;</p><h2>Where to go next</h2><p>If you are still early, I would not try to learn every advanced clause in one sitting.</p><p>I would focus on connecting SQL to actual problems.</p><p>That is exactly why I built the SQL track into the NBD Focus Map. The point is not to learn SQL randomly. The point is to see how the pieces fit together and start shipping small, useful work with them.</p><h2>Start here</h2><p>If you want the broader path, start with the <strong>Focus Map</strong>:<br></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/p/nbd-focus-map-free-pdf&quot;,&quot;text&quot;:&quot;Focus Map&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/p/nbd-focus-map-free-pdf"><span>Focus Map</span></a></p><p><br>If you want the full paid system, use:</p><ul><li><p><strong>Vault:</strong> <a href="https://www.nb-data.com/p/nbd-reading-vault-paid-guided-paths">https://www.nb-data.com/p/nbd-reading-vault-paid-guided-paths</a></p></li><li><p><strong>Template Index:</strong> <a href="https://www.nb-data.com/p/template-pack-index-paid">https://www.nb-data.com/p/template-pack-index-paid</a></p></li><li><p><strong>Subscriber Benefits:</strong> <a href="https://www.nb-data.com/p/subscriber-benefits?utm_source=chatgpt.com">https://www.nb-data.com/p/subscriber-benefits</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Cohort Retention in SQL]]></title><description><![CDATA[From Raw Events to a Decision-Ready Table]]></description><link>https://www.nb-data.com/p/cohort-retention-in-sql</link><guid isPermaLink="false">https://www.nb-data.com/p/cohort-retention-in-sql</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Fri, 06 Mar 2026 18:39:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!5opy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e76dc69-8ad0-449f-8f74-a8b8885dbdd9_1312x736.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5opy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e76dc69-8ad0-449f-8f74-a8b8885dbdd9_1312x736.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5opy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e76dc69-8ad0-449f-8f74-a8b8885dbdd9_1312x736.jpeg 424w, https://substackcdn.com/image/fetch/$s_!5opy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e76dc69-8ad0-449f-8f74-a8b8885dbdd9_1312x736.jpeg 848w, https://substackcdn.com/image/fetch/$s_!5opy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e76dc69-8ad0-449f-8f74-a8b8885dbdd9_1312x736.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!5opy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e76dc69-8ad0-449f-8f74-a8b8885dbdd9_1312x736.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5opy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e76dc69-8ad0-449f-8f74-a8b8885dbdd9_1312x736.jpeg" width="1312" height="736" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7e76dc69-8ad0-449f-8f74-a8b8885dbdd9_1312x736.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:736,&quot;width&quot;:1312,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:89400,&quot;alt&quot;:&quot;Cohort Retention in SQL&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/190126571?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e76dc69-8ad0-449f-8f74-a8b8885dbdd9_1312x736.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Cohort Retention in SQL" title="Cohort Retention in SQL" srcset="https://substackcdn.com/image/fetch/$s_!5opy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e76dc69-8ad0-449f-8f74-a8b8885dbdd9_1312x736.jpeg 424w, https://substackcdn.com/image/fetch/$s_!5opy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e76dc69-8ad0-449f-8f74-a8b8885dbdd9_1312x736.jpeg 848w, https://substackcdn.com/image/fetch/$s_!5opy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e76dc69-8ad0-449f-8f74-a8b8885dbdd9_1312x736.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!5opy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e76dc69-8ad0-449f-8f74-a8b8885dbdd9_1312x736.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Most retention tables are not wrong because the SQL is complicated.</p><p>They are wrong because the definitions are loose.</p><p>Someone says, &#8220;Let&#8217;s look at retention,&#8221; a query gets written, a heatmap shows up in a dashboard, and suddenly everyone is talking about Week 1 and Month 1 as if those numbers are objective facts. They usually are not. They are the result of choices. What counts as the start of a user&#8217;s journey? What counts as a return? What exactly is a week? What timezone are we using? Are we measuring one user once per period, or accidentally counting heavy users multiple times?</p><p>That is the real work in cohort retention. Not the division. Not the pivot table. The real work is deciding what story the table is allowed to tell.</p><p>At its core, cohort analysis is simple. You group users by a shared starting point, then measure what those users do in later periods. That is the common backbone behind most cohort SQL tutorials and warehouse implementations.</p><p>What makes it tricky is that small choices can change the story enough to change the decision.</p><p>So in this piece, I want to show you how I think about cohort retention in SQL when I want something that is not just presentable, but actually trustworthy. We will walk through a small sample dataset, turn it into a retention table step by step, and discuss the parts that often go wrong: cohort definition, return-event design, week boundaries, duplicate activity, partial cohorts, and interpretation.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.nb-data.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.nb-data.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>Start with the question, not the query</h2><p>Before touching SQL, I like to ask one uncomfortable question:</p><p><strong>What exactly do I want this retention table to help me decide?</strong></p><p>That question matters because different cohort definitions answer different business questions.</p><p>If I group users by the week they signed up, I am usually asking something about onboarding, activation, or acquisition quality. I want to know whether new users are sticking around after entering the funnel.</p><p>If I group users by the week they first did something meaningful, I am asking something slightly different. I am saying that signup is not the real beginning of value. Maybe the real beginning is the first login, the first purchase, the first report built, or the first document uploaded. In that case, I am less interested in the funnel entry and more interested in what happens once a user actually starts using the product.</p><p>Both are valid. But they are not interchangeable.</p><p>The same holds for the return event. If I define retention as &#8220;any page view,&#8221; my table might look reassuring while hiding the fact that users are not doing anything meaningful. If I define retention as &#8220;purchase,&#8221; the metric might be more valuable but also much sparser. There is no universally correct event. There is only one event that is more or less aligned with the value loop you care about.</p><p>Then there is the time bucket. This is the part people often treat as neutral, even though it really isn&#8217;t. A daily retention table tells a different story than a weekly one. A weekly table tells a different story than a monthly one. And even the idea of a &#8220;week&#8221; is less fixed than people think. BigQuery, for example, distinguishes between <code>WEEK</code>, <code>WEEK(&lt;WEEKDAY&gt;)</code>, and <code>ISOWEEK</code>, and those choices affect how dates are grouped and how period differences are calculated.</p><p>That is why I think of cohort retention as a design problem before I think of it as a SQL problem.</p><h2>The version we&#8217;re building here</h2><p>To make this concrete, let&#8217;s keep the example small and explicit.</p><p>In this walkthrough:</p><ul><li><p>A user&#8217;s cohort is the <strong>week of their first login</strong></p></li><li><p>Retention means they performed a <strong>login</strong> in a later week</p></li><li><p>The table uses <strong>calendar weeks</strong></p></li><li><p>Each user should count at most <strong>once per week</strong></p></li></ul><p>That last condition matters a lot. If a user logs in ten times in the same week, they are still one retained user for that week. Retention is about whether someone came back in the period, not how noisy their event stream was.</p><h2>Sample data</h2><p>Here is a tiny events table we can use end-to-end.</p>
      <p>
          <a href="https://www.nb-data.com/p/cohort-retention-in-sql">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Template Pack Index (Paid)]]></title><description><![CDATA[Last updated: 24 May 2026]]></description><link>https://www.nb-data.com/p/template-pack-index-paid</link><guid isPermaLink="false">https://www.nb-data.com/p/template-pack-index-paid</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sun, 01 Mar 2026 15:43:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!06DP!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0e1cde-d120-4029-8ffd-2a8c7c6e4504_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is the paid asset library for Non-Brand Data.</p><p>Each template pack is designed to help you ship faster, not just read more. Pick one pack based on your current goal, use it for 7&#8211;14 days, and finish with a visible artifact: a notebook, SQL analysis, write-up, repo, or project page.</p>
      <p>
          <a href="https://www.nb-data.com/p/template-pack-index-paid">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[NBD Reading Vault (Paid): Guided Paths + Mini-Projects]]></title><description><![CDATA[Last updated: 24 Feb 2026]]></description><link>https://www.nb-data.com/p/nbd-reading-vault-paid-guided-paths</link><guid isPermaLink="false">https://www.nb-data.com/p/nbd-reading-vault-paid-guided-paths</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Tue, 24 Feb 2026 12:45:05 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!06DP!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F6c0e1cde-d120-4029-8ffd-2a8c7c6e4504_1280x1280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>Start here if you feel overwhelmed</h3><p>If you are a paid member and do not know what to read next, do not browse the archive.</p><p>Choose one track below, follow the reading order, and ship one mini-project in 2&#8211;4 weeks.</p><p>This page is designed to help you move from reading to output.</p><p><strong>Do this before browsing the archive:</strong></p><ol><li><p>Pick one track only.</p></li><li><p>Read the first post in that &#8230;</p></li></ol>
      <p>
          <a href="https://www.nb-data.com/p/nbd-reading-vault-paid-guided-paths">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[✨Subscriber Benefits]]></title><description><![CDATA[Everything included in Non-Brand Data. Updated: February 2026]]></description><link>https://www.nb-data.com/p/subscriber-benefits</link><guid isPermaLink="false">https://www.nb-data.com/p/subscriber-benefits</guid><dc:creator><![CDATA[Cornellius Yudha Wijaya]]></dc:creator><pubDate>Sun, 22 Feb 2026 13:26:51 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!_8Co!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b33d87-8050-4869-9be7-989f15554517_1533x593.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1>Non-Brand Data Subscriber Benefits</h1><p>This is the up-to-date summary of what you get as a free reader, paid member, or founding member.</p><p>Non-Brand Data is built around one simple idea:</p><p>Learn with structure. Practice with intention. Ship something useful.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_8Co!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b33d87-8050-4869-9be7-989f15554517_1533x593.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_8Co!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b33d87-8050-4869-9be7-989f15554517_1533x593.png 424w, https://substackcdn.com/image/fetch/$s_!_8Co!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b33d87-8050-4869-9be7-989f15554517_1533x593.png 848w, https://substackcdn.com/image/fetch/$s_!_8Co!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b33d87-8050-4869-9be7-989f15554517_1533x593.png 1272w, https://substackcdn.com/image/fetch/$s_!_8Co!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b33d87-8050-4869-9be7-989f15554517_1533x593.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_8Co!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b33d87-8050-4869-9be7-989f15554517_1533x593.png" width="1456" height="563" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d1b33d87-8050-4869-9be7-989f15554517_1533x593.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:563,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:67585,&quot;alt&quot;:&quot;Non-Brand Data Subscriber Benefits&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.nb-data.com/i/188513081?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b33d87-8050-4869-9be7-989f15554517_1533x593.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Non-Brand Data Subscriber Benefits" title="Non-Brand Data Subscriber Benefits" srcset="https://substackcdn.com/image/fetch/$s_!_8Co!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b33d87-8050-4869-9be7-989f15554517_1533x593.png 424w, https://substackcdn.com/image/fetch/$s_!_8Co!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b33d87-8050-4869-9be7-989f15554517_1533x593.png 848w, https://substackcdn.com/image/fetch/$s_!_8Co!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b33d87-8050-4869-9be7-989f15554517_1533x593.png 1272w, https://substackcdn.com/image/fetch/$s_!_8Co!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1b33d87-8050-4869-9be7-989f15554517_1533x593.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Full version</h2><h3>Free subscribers</h3><p>Start here: <strong><a href="https://www.nb-data.com/p/nbd-focus-map-free-pdf?r=3kmaf">NBD Focus Map (Free PDF)</a></strong></p><p><strong>What you get:</strong></p><ul><li><p>NBD Focus Map</p></li><li><p>Public posts and public archive</p></li><li><p>The best free posts in order</p></li><li><p>Subscriber chat and comment threads</p></li><li><p>Free practical resources, including the AI Evaluation Checklist for Data Professionals</p></li><li><p>Occasional free guides, templates, and learning notes</p></li></ul><p><strong>Best for:</strong></p><p>Readers who want a structured starting point before deciding whether to go deeper.</p><p>Use this tier if you want to pick one track, follow a simple cadence, and start shipping small artifacts from what you learn.</p><h3>Paid members</h3><p>Paid members get the structured side of Non-Brand Data.</p><p>What you get:</p><ul><li><p>Member-only deep-dive posts</p></li><li><p>Full archive access</p></li><li><p>NBD Reading Vault</p></li><li><p>Template Pack Index</p></li><li><p>Monthly or periodic template updates</p></li><li><p>Guided learning paths across SQL, Python + ML, and GenAI / RAG</p></li><li><p>Practical assets such as checklists, worksheets, rubrics, and project templates</p></li></ul><p><strong>Best for:</strong></p><p>Readers who do not want to browse randomly and want a clearer path from learning to practice.</p><p>Use this tier if you want to follow guided paths, reuse templates, and turn the archive into a practical learning system.</p><h2>What paid members should use first</h2><p>If you feel lost in the archive, start with the <strong><a href="https://www.nb-data.com/p/nbd-reading-vault-paid-guided-paths?r=3kmaf">NBD Reading Vault</a></strong>.</p><p>The Vault helps you:</p><ul><li><p>pick one track,</p></li><li><p>follow the reading order,</p></li><li><p>commit to a 2&#8211;4 week sprint,</p></li><li><p>and ship one mini-project.</p></li></ul><p>If you already know what you want to build, go to the <strong><a href="https://www.nb-data.com/p/template-pack-index-paid?r=3kmaf">Template Pack Index</a></strong>.</p><p>The Template Pack Index gives you reusable assets such as:</p><ul><li><p>Sprint Pack</p></li><li><p>SQL Work Pack</p></li><li><p>ML Experiment Pack</p></li><li><p>AI Evaluation Checklist</p></li></ul><div><hr></div><div><hr></div><h3>Founding members</h3><p>Founding members get everything in Paid, plus priority feedback and one annual review call.</p><p>The annual review call is for one project, portfolio entry, write-up, or learning artifact.</p><p>We focus on what to change so the work becomes clearer, stronger, and closer to hiring-manager-ready or stakeholder-ready quality.</p><p>What you get:</p><ul><li><p>Everything in Paid</p></li><li><p>Priority feedback</p></li><li><p>One annual 30-minute review call</p></li><li><p>Project, portfolio, or artifact review</p></li></ul><div><hr></div><div><hr></div><h2>One-time purchases (optional)</h2><p>These are separate from subscriptions. Buy once and reuse anytime.</p><ul><li><p><strong><a href="https://cornelliusyudhawijay.gumroad.com/l/otdloq">Portfolio Rubric Toolkit</a></strong></p></li><li><p><strong><a href="https://cornelliusyudhawijay.gumroad.com/">Data Science Resume Template</a> (FREE)</strong></p></li><li><p><strong><a href="https://cornelliusyudhawijay.gumroad.com/l/hdhuw">Python Packages to Learn Data Science (e-book) </a>(FREE)</strong></p></li></ul><div><hr></div><h2>How to redeem your benefits</h2><h3>All subscribers</h3><ul><li><p><strong><a href="https://www.nb-data.com/p/nbd-focus-map-free-pdf?r=3kmaf">Focus Map</a></strong></p></li><li><p><strong><a href="https://www.nb-data.com/p/welcome-to-non-brand-data-by-cornellius">Start Here</a></strong></p></li></ul><h3>Paid members</h3><ul><li><p>Access member-only posts by logging in with the email you used to subscribe</p></li><li><p>Template packs are delivered by email and will also be collected in one place as the vault grows</p></li><li><p>The member vault reading list will be added here once it is published</p></li></ul><h3>Founding members</h3><p>Reply to any email with the subject: <strong>Founding review</strong><br>Include:</p><ul><li><p>a link to your project/repo/write-up</p></li><li><p>What do you want feedback on. I will send the booking link, and we will schedule the 30 minutes.</p></li></ul><div><hr></div>]]></content:encoded></item></channel></rss>