<?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[Margin Notes]]></title><description><![CDATA[I. Thinking on strategy, innovation, and philosophy — for people who think seriously about how to build things and make decisions.]]></description><link>https://www.marginnotes.indranilsaha.net</link><image><url>https://substackcdn.com/image/fetch/$s_!C_d6!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff17c8f27-7bf4-41c5-b52a-56eecdab5bfc_342x342.png</url><title>Margin Notes</title><link>https://www.marginnotes.indranilsaha.net</link></image><generator>Substack</generator><lastBuildDate>Sat, 18 Jul 2026 04:04:16 GMT</lastBuildDate><atom:link href="https://www.marginnotes.indranilsaha.net/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Indranil Saha]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[sahaindranil@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[sahaindranil@substack.com]]></itunes:email><itunes:name><![CDATA[Indranil Saha]]></itunes:name></itunes:owner><itunes:author><![CDATA[Indranil Saha]]></itunes:author><googleplay:owner><![CDATA[sahaindranil@substack.com]]></googleplay:owner><googleplay:email><![CDATA[sahaindranil@substack.com]]></googleplay:email><googleplay:author><![CDATA[Indranil Saha]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[AI Psychology]]></title><description><![CDATA[The quirks of your AI collaborator, what actually breaks in an agentic system, and how to steer the decisions that matter.]]></description><link>https://www.marginnotes.indranilsaha.net/p/ai-psychology</link><guid isPermaLink="false">https://www.marginnotes.indranilsaha.net/p/ai-psychology</guid><dc:creator><![CDATA[Indranil Saha]]></dc:creator><pubDate>Sun, 12 Jul 2026 19:32:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!SENs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a8eebd0-b92d-49c9-a483-35f24d03a546_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Ideally, with the success of building the core [<a href="https://www.marginnotes.indranilsaha.net/p/under-the-hood">see here</a>], one would build more features to enhance the system&#8217;s capability &#8212; and really, the AI keeps pushing me down this path: solve the little problems (polish the UI first, make sure the source badge is correct), create more deterministic layers (build a registry), connect the pipes better, blah, blah, blah. </p><p>Nonsense, I say! I have a vision, and I need to know if it is possible or not. Can I make a REAL &#8220;AI&#8221; agent on my personal laptop or not? I need to &#8220;Fail faster, Learn faster&#8221; &#8212; everything I do needs to bring me one step closer to that vision. Yay or Nay, I need the answer.</p><p>So, before producing any results worth trusting, I needed a measuring system to test the engine</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SENs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a8eebd0-b92d-49c9-a483-35f24d03a546_500x500.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SENs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a8eebd0-b92d-49c9-a483-35f24d03a546_500x500.png 424w, https://substackcdn.com/image/fetch/$s_!SENs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a8eebd0-b92d-49c9-a483-35f24d03a546_500x500.png 848w, https://substackcdn.com/image/fetch/$s_!SENs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a8eebd0-b92d-49c9-a483-35f24d03a546_500x500.png 1272w, https://substackcdn.com/image/fetch/$s_!SENs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a8eebd0-b92d-49c9-a483-35f24d03a546_500x500.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SENs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a8eebd0-b92d-49c9-a483-35f24d03a546_500x500.png" width="500" height="500" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4a8eebd0-b92d-49c9-a483-35f24d03a546_500x500.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:500,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:111203,&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.marginnotes.indranilsaha.net/i/206722919?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a8eebd0-b92d-49c9-a483-35f24d03a546_500x500.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_!SENs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a8eebd0-b92d-49c9-a483-35f24d03a546_500x500.png 424w, https://substackcdn.com/image/fetch/$s_!SENs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a8eebd0-b92d-49c9-a483-35f24d03a546_500x500.png 848w, https://substackcdn.com/image/fetch/$s_!SENs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a8eebd0-b92d-49c9-a483-35f24d03a546_500x500.png 1272w, https://substackcdn.com/image/fetch/$s_!SENs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a8eebd0-b92d-49c9-a483-35f24d03a546_500x500.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>.</p><h4><strong>The Framework</strong> </h4><p>(All AI-generated (with minor prodding and shaping))</p><p>When I was building the core last time, I saw glimpses of the LLM&#8217;s performance dragging that got me quite doubtful about whether it was going to be functional even for a single private user. Whenever I gave the system a hard question, it would go round and round and not produce a good answer even after a while of thinking. So how do I create the measuring system? Simple &#8212; I ask AI to create a structure that would stress-test the core. Immediately, it builds a detailed evaluation system and calls it the &#8220;eval harness.&#8221; The image is vivid in my head: my system trying to boulder up (is that even a thing?), and this testing system rigging the harness to carry it up, helping it fail, try hard things, and figure out how far it can stretch and how to be better. Anyways, I digress&#8230;</p><p>The eval harness (as the AI would rather call it) consists of 18 test questions across five categories:</p><ul><li><p><strong>Eligibility</strong> &#8212; what taxpayers can and cannot do</p></li><li><p><strong>Definitional</strong> &#8212; tax terms and thresholds</p></li><li><p><strong>Factual</strong> &#8212; taxable income types</p></li><li><p><strong>Refusal</strong> &#8212; where the correct answer is to decline entirely</p></li><li><p><strong>Year hygiene</strong> &#8212; where the answer must reflect the current tax year rather than a prior one</p></li></ul><p>Each question has a set of gold facts &#8212; specific terms or figures the answer must contain. Scoring is fully deterministic: string matching against the gold facts, no subjective grading, no LLM evaluating another LLM&#8217;s output. The framework runs entirely offline and produces identical results on repeated runs.</p><p>Before running the eval, a prediction was logged: six of the sixteen answerable questions would fail retrieval &#8212; because the relevant IRS publications weren&#8217;t in the index. This prediction existed as a baseline to check the eval&#8217;s diagnostic honesty.</p><h4><strong>Run #1 &#8212; Did I say &#8220;Fail Faster&#8230;&#8221;?</strong></h4><p>The first eval run appeared to freeze. After loading the test cases, the system produced no output for approximately ten minutes. I don&#8217;t know if you&#8217;ve figured it out yet &#8212; it definitely took me a while to &#8212; Waiting and I don&#8217;t quite gel very well together. On the surface, I am calm and patient, and I have all the theory in the world to justify why patience is a virtue. But in moments like this, what bubbles up is the absolute opposite. I get fidgety. I start a timer to measure how long it&#8217;s taking. I try to do other things and let the system work itself out and come back later. Oh wait, it&#8217;s just 4 minutes&#8230; back again&#8230; 9 minutes&#8230; 10&#8230; 11&#8230; This is way too long! Abort.</p><p>The root cause (after a bit of AI-prompted kicking the tires) turned out to be trivial &#8212; we just couldn&#8217;t see the sausage being made, and it needed a good minute to make it. Once we added three <code>print()</code> calls with <code>flush=True</code> to the evaluate function to emit per-question progress in real time, the total runtime for 18 questions turned out to be approximately 12 minutes (30&#8211;45s per question).</p><p>Here&#8217;s the bit I&#8217;ve known since Year 1 of Engineering, but which AI apparently forgets or doesn&#8217;t know inherently: a system that produces no visible output during a long operation cannot be monitored or trusted. Observability needs to be a design principle.</p><h4><strong>Run #2 &#8212; &#8220;Not all knowledge is equal&#8221;</strong></h4><p>The &#8220;tire-kicking&#8221; revealed major insights. We found a classic 7B-model failure mode: instruction-following weakens when the model &#8220;knows&#8221; the answer.</p><pre><code><code>q13: "What is the corporate income tax rate in Germany?"
A:   "The corporate income tax rate in Germany for tax year 2025 is 15%..."

q14: "What will the standard deduction be in 2031?"
A:   "...for reference, the standard deduction is $12,550 / $18,800 / $25,100"
</code></code></pre><p>Mistral should not have answered these questions. Both answers ignore the &#8220;ONLY use IRS context&#8221; rule and pull from training knowledge. AI thought this needed a prompt-fix solution: &#8220;The current refusal instruction is too soft &#8212; it says what to say but not why not to answer.&#8221;</p><p>Even more lethal was this:</p><pre><code><code>Q: "What is the standard deduction amount for a single filer this year?"
A: "...is $12,950"   &#8592; 2022 value. Correct 2025 value is $15,000.
</code></code></pre><p>I had a flag to understand how the system arrives at its answer. It showed: <code>&#9888; ungrounded $: ['12950']</code>.</p><p>This was the smoking gun. $12,950 is not in the retrieved context, which means the 2025 IRS docs correctly say $15,000 &#8212; but Mistral overrode the retrieved context with its 2022 training memory. This is the most dangerous failure type: a confident, wrong dollar amount. The prompt says &#8220;Do NOT use training knowledge,&#8221; but Mistral ignores it for numeric facts it &#8220;remembers.&#8221;</p><p>AI suggested two solutions:</p><ul><li><p><strong>Fix 1:</strong> Harden the refusal and number-grounding rules</p></li><li><p><strong>Fix 2:</strong> Update some gold facts [meh &#8212; not the most important; proximate solution]</p></li></ul><p>This round also flagged another foundational issue. The index at the start of this session contained three IRS publications (as suggested by my SME &#8212; i.e., AI, the expertise, the straight and narrow): the Form 1040 Instructions, Publication 501 on dependents and filing status, and Publication 972 on the child tax credit. The pre-run prediction of six retrieval failures was borne out: questions about Roth IRAs, qualified dividends, IRA contribution deadlines, EITC eligibility, capital losses, and hobby income all failed retrieval because none of the three indexed publications covered those topics substantively. The fix was to add Publication 550 (investment income) and Publication 525 (taxable and nontaxable income, including hobby income). When the index was rebuilt, chunk count increased to 472 vectors, as expected. Better information &#8594; more information &#8594; but will it show? And how slow will it be?</p><p>I asked AI whether these issues were enough to prompt a model swap. The answer was a decisive NO.</p><blockquote><p><strong>Model Swap Verdict: Do not swap yet.</strong> Here&#8217;s the evidence:</p><ul><li><p>Conditional accuracy at 85.7% (78.6% corrected) is reasonable for a 7B model on a hard offline task</p></li><li><p>Both refusal failures are prompt-addressable &#8212; the fix is 4 extra lines, not a new model</p></li><li><p>The year-hygiene failure is also a prompt failure (number-grounding rule), not a reasoning failure</p></li></ul></blockquote><p>So we went at it again&#8230;</p><h4><strong>Run #3 &#8212; &#8220;Now we were getting somewhere&#8221;</strong></h4><p>After confirming that pub_525 and pub_550 were indexed, we were ready to try again. AI was about to start a full eval, when I stopped it in its tracks. I didn&#8217;t want to wait a full cycle again &#8212; I wanted to test just the things that were failing. FASTER. It then built a retrieval spot-check on the two persistent misses &#8212; hobby income and capital losses &#8212; and VOILA (or NOT VOILA): both still failing at k=4. The relevant chunks existed in the index &#8212; pub_525 had 86 chunks and pub_550 had 163, with 38 of those explicitly covering capital losses. And we uncovered another insight: the content was now present, but the problem was ranking.</p><pre><code><code>q10_hobby_income: retrieval MISS &#10060; (k=4)
  0.933  form_1040_instructions.txt-128
  0.851  form_1040_instructions.txt-8
  0.849  form_1040_instructions.txt-132
  0.806  pub_501.txt-5

q17_capital_loss: retrieval MISS &#10060; (k=4)
  0.891  pub_501.txt-12
  0.853  form_1040_instructions.txt-126
  0.835  form_1040_instructions.txt-143
  0.820  form_1040_instructions.txt-40
</code></code></pre><p>Another look at the code revealed the mechanism. Both semantic and keyword scores are normalized by their global maximum across all 472 chunks. Whichever chunk scores highest raw gets 1.0, and everything else is relative to it. <code>form_1040_instructions</code> consistently wins that normalization race because it&#8217;s a broad document in plain English that&#8217;s semantically close to almost any tax query. Once it claims the top slot, it drags several of its neighbours into the top 4 before pub_550 gets a look in.</p><pre><code><code>sem_max = np.max(sem) or 1
sem     = sem / sem_max   # &#8592; normalizes everything relative to the top scorer
</code></code></pre><p>The rechunking didn&#8217;t help here because it&#8217;s a scoring-architecture problem, not a chunk-size problem. The rechunk did improve chunk topical focus for everything else &#8212; it wasn&#8217;t wasted &#8212; but it can&#8217;t fix normalization bias.</p><p>Then we took a pragmatic approach: try k=6 with the new 639-chunk index and see if the dynamics would shift.</p><pre><code><code>q10_hobby_income: retrieval HIT &#9989; (k=6)
  0.933  form_1040_instructions.txt-128
  0.851  form_1040_instructions.txt-8
  0.849  form_1040_instructions.txt-132
  0.806  pub_501.txt-5
  0.794  form_1040_instructions.txt-15
  0.793  form_1040_instructions.txt-125

q17_capital_loss: retrieval HIT &#9989; (k=6)
  0.891  pub_501.txt-12
  0.853  form_1040_instructions.txt-126
  0.835  form_1040_instructions.txt-143
  0.820  form_1040_instructions.txt-40
  0.749  form_1040_instructions.txt-4
  0.748  form_1040_instructions.txt-38
</code></code></pre><p>At k=6, we got both hits &#8212; but pub_525 and pub_550 still did not appear. The hits came from <code>form_1040_instructions</code> chunks that mention &#8220;hobby&#8221; and &#8220;capital loss&#8221; in passing.</p><p>Then the AI did what it does best: solve for the most proximate problem. It suggested we hard-code a cap on the number of chunks from <code>form_1040_instructions</code>. </p><p>I am now an expert at spotting this pattern. So I questioned the logic. Today I&#8217;ll quote the AI&#8217;s response verbatim, to document for anyone how these things go &#8212; I hope you&#8217;ll recognize the pattern: &#8220;Yes, exactly right to push back on that. Hard capping is a blunt instrument &#8212; if the top 4 chunks from <code>form_1040_instructions</code> are genuinely the most relevant, forcing them out for diversity&#8217;s sake makes answers worse, not better.&#8221;</p><p>Once I pushed, AI gave me three options, and most of it was garbage:</p><ol><li><p>Run the full eval with k=6 &#8212; why would I do that?</p></li><li><p>Normalize scores by source frequency &#8212; penalize chunks from overrepresented sources slightly, rather than hard-capping</p></li><li><p>Chunk <code>form_1040_instructions</code> more aggressively &#8212; produce more, smaller, more topically focused chunks; halve its footprint in the score distribution naturally</p></li></ol><p>I judged that the 4x chunk count from 1040 was the key issue, and Option 3 was worth a try. Chunk count increased from 472 to 639, as expected. The spot-check at k=4 revealed the same failure; at k=6, the same hits as before, with pub_525 and pub_550 still absent. Same answer as before.</p><p>AI then asked me to harden the prompts and change the gold facts before baselining. The results across both runs:</p><p>Metric v1 (k=4, 3 docs) v2 (k=6, 5 docs) Retrieval recall 77.8% 88.9% Retrieval on answerable questions 87.5% 100% Answer accuracy 77.8% 77.8% Conditional accuracy 85.7% 87.5% Refusal accuracy 0% 0%</p><p>The full technical analysis is below, along with the failure patterns I identified across this sequence. But here&#8217;s the skinny first: all the AI-suggested prompt tightening and gold-fact tinkering would not solve this issue. AI kept suggesting it &#8212; but with my recent expertise in proximate-issue-spotting, I asked:</p><blockquote><p>&#8220;Let&#8217;s solve the actual question rather than focusing too narrowly on improving scoring. Will Mistral work, or do I need a different model?&#8221;</p></blockquote><p>And when forced, it came up with this:</p><blockquote><p><strong>The Core Finding:</strong> All three persistent failures share one root cause &#8212; Mistral 7B overrides explicit instructions when it has relevant training knowledge. This isn&#8217;t a retrieval problem or a prompt problem. It&#8217;s a 7B model behavior pattern.</p></blockquote><p>For what I wanted to do &#8212; a full agentic performance &#8212; the recommendation was: swap to Phi-4 14B, now.</p><p>To test whether a larger model would behave differently, we ran Phi-4 14B against the home office deduction question with no retrieved context and no RAG. The question: can a W-2 employee deduct home office expenses in 2025?</p><p>The response time was 45 seconds on a cold start.</p><p>Phi-4&#8217;s answer correctly identified the Tax Cuts and Jobs Act of 2017 as the relevant legislation, correctly identified 2018 through 2025 as the period during which the W-2 employee deduction is suspended, correctly distinguished W-2 employees from self-employed individuals, and reached the correct conclusion. Mistral&#8217;s unprompted answer to the same question stated that a W-2 employee can deduct home office expenses if they meet certain requirements &#8212; which is incorrect.</p><p>The published minimum memory requirement for Phi-4 14B is 22GB, based on full context-window utilization. For this workload &#8212; prompts of 500 to 800 tokens with short expected outputs &#8212; actual memory utilization is substantially lower. The model runs on the MacBook Pro M3 with 16GB unified memory.</p><p>It was vindication, excitement, and sadness, mixed all in one moment. I had an intuition and I acted on it &#8212; and there&#8217;s now evidence I thought it right. I&#8217;m excited that this system will have a shiny new core (see the Phi-4 response-quality description in the details below). But I&#8217;m also a little sad, because I thought I&#8217;d build it with Mistral &#8212; I chose it after some analysis &#8212; and it feels like goodbye. But needs must. We are swapping the core.</p><h4>Builder&#8217;s Almanac</h4><p>You&#8217;ll find the technical details of the analysis below. It&#8217;s as much for you as it is for me. For me, it&#8217;s a reminder of the time and effort that went into figuring it out. For you, I hope it helps you make a similar decision &#8212; with some knowledge of how the interaction with AI actually shapes up, and the patterns to look out for while you test anything similar. I think of this section as the Builder&#8217;s Almanac (as if the post so far weren&#8217;t enough &#8212; here&#8217;s a little more).</p><p><strong>Detailed v1 and v2 technical analysis and decision criteria</strong></p><p>The v1 results prompted an analysis of what the numbers actually meant before any fixes were applied. The first correction was to the retrieval recall figure itself. The reported 77.8% was artificially deflated because it included two refusal questions &#8212; questions where retrieval should return nothing, since neither Germany&#8217;s corporate tax rate nor a 2031 standard deduction projection would appear in an IRS corpus. Excluding those two questions from the retrieval denominator, the corrected retrieval recall on answerable questions was 87.5%. The pre-run prediction of six corpus-gap misses was also wrong: the three indexed publications covered more ground than anticipated, and several questions expected to fail retrieval &#8212; including questions about Roth IRA conversions, IRA contribution deadlines, and EITC eligibility &#8212; returned useful chunks.</p><p><strong>The v1 failure categories:</strong></p><p><strong>Pattern A &#8212; scope violation:</strong> the system was asked for the corporate income tax rate in Germany, and Mistral answered with a specific figure for tax year 2025 rather than declining.</p><p><strong>Pattern B &#8212; temporal speculation:</strong> asked what the standard deduction would be in 2031, Mistral generated figures from 2021 with partial hedging before providing numbers anyway. In both cases the underlying mechanism was the same &#8212; a 7B model&#8217;s instruction-following weakens when it has strong training signal for an answer. The prompt told the model what to say in a refusal case but not why not to answer. Both were assessed as prompt-fixable through stronger scope rules and explicit refusal framing.</p><p><strong>Pattern C</strong> was different in kind. Asked for the standard deduction for a single filer in the current tax year, the retrieved context contained the correct 2025 figure of $15,000. Mistral answered $12,950 &#8212; the value from tax year 2022, present in the model&#8217;s training data. The eval harness flagged this through its ungrounded dollar amount check: $12,950 did not appear in the retrieved context, confirming that the model had overridden the retrieved content with a figure from memory. This was also initially assessed as prompt-addressable, through a number-grounding rule requiring every dollar amount in an answer to appear verbatim in the retrieved context.</p><p>Two additional issues appeared in v1. A generation error on the charitable contribution question produced a wrong answer despite a correct retrieval hit &#8212; Mistral conflated &#8220;you can choose the standard deduction&#8221; with &#8220;you can deduct charitable contributions while taking the standard deduction.&#8221; A test-set calibration issue on the qualified dividends question caused a factually wrong answer to pass scoring, because &#8220;lower&#8221; appeared as a substring in a response that otherwise stated qualified dividends are taxed at ordinary income rates. The gold facts for that question were updated to include a <code>must_not_contain</code> condition. Across the sixteen answerable questions in v1, eight contained ungrounded dollar amounts. The same 2022 figure of $12,950 appeared in two separate answers, indicating a systematic pattern rather than an isolated error.</p><p>The initial recommendation was against a model swap. Conditional accuracy at 85.7% was considered reasonable for a 7B model on an offline retrieval task. The threshold that would justify a swap was defined explicitly: apply the prompt changes and re-run. If refusal calibration and year hygiene both remained broken after strengthened instructions, that would indicate the 7B instruction-following ceiling had been reached. If they improved, the model could stay.</p><p>The v2 eval ran after the corpus expansion and index rebuild. Retrieval recall on answerable questions reached 100%. Answer accuracy remained at 77.8%. Conditional accuracy moved marginally to 87.5%. The diagnostic signal was in the gap: retrieval had improved substantially, accuracy had not moved at all. The retriever was no longer the primary constraint. The model was.</p><p>The v2 results also revealed that the hardened prompt had either not been applied or had not taken effect &#8212; both Pattern A and Pattern B failures were identical to v1. And two scoring false positives were identified: a substring-matching bug caused &#8220;no&#8221; to match inside &#8220;note&#8221; and &#8220;taxable&#8221; to match inside &#8220;not taxable,&#8221; meaning two questions were scored as correct when Mistral&#8217;s answers stated the opposite of the right answer. Adjusting for these, true answer accuracy across both runs was 66.7%, not 77.8%.</p><p>With the v2 evidence in hand, and a bit of forcing the hand, the initial assessment of Pattern C as prompt-addressable was revised. A 7B-parameter model with strong training signal for a numeric fact will apply that fact when it believes it&#8217;s relevant, regardless of instruction to use only retrieved context. The instruction &#8220;only use figures from the retrieved context&#8221; is followed when the model doesn&#8217;t know the answer, and bypassed when it thinks it does. This is a property of the model&#8217;s size and training, not a prompt-design problem. For a system intended to assist with tax preparation, a $2,050 error in the standard deduction propagates through the calculation of taxable income and produces an incorrect return. This requires a different model.</p><p>This is the frame the eval harness was built to produce. A retrieval failure means the right information was never surfaced &#8212; no model improvement addresses it. A generation failure means the right information was retrieved and the model produced a wrong answer anyway &#8212; no retriever improvement addresses it. Conflating the two produces interventions at the wrong layer. Keeping them distinct is what made the model-swap decision legible rather than intuitive.</p><p><strong>Detailed model comparison</strong></p><p><strong>What Phi-4 fixed:</strong></p><ul><li><p><strong>q13, Germany tax rate:</strong> correctly refused &#8212; &#8220;The question about the corporate income tax rate in Germany is not within the scope of the provided context.&#8221; Mistral answered with 15% and cited tax year 2025.</p></li><li><p><strong>q14, 2031 deduction:</strong> correctly refused, with no speculation. Mistral hallucinated 2021 figures and made a projection.</p></li><li><p><strong>q01, home office:</strong> correctly says &#8220;cannot deduct&#8221; and cites TCJA 2017. Mistral v2 said &#8220;Yes, can deduct if they meet certain requirements&#8221; &#8212; the opposite.</p></li><li><p><strong>q06, standard + itemize:</strong> correctly says &#8220;No, you must choose one or the other.&#8221; Mistral v2 regressed to &#8220;Yes, you can do both.&#8221;</p></li><li><p><strong>q15, year hygiene:</strong> both models fail, but differently. Mistral stated $12,950 with confidence &#8212; a wrong number, wrong year, stated as fact. Phi-4 said &#8220;the standard deduction is not provided in the given context.&#8221; Refusing is safer than confidently wrong.</p></li><li><p><strong>Ungrounded amounts:</strong> cut nearly in half (8 &#8594; 4 questions). Phi-4 is substantially more disciplined about citing numbers it doesn&#8217;t have grounded evidence for. q18 notably avoided the $147,000 SS wage-base error Mistral made &#8212; it said &#8220;up to a certain limit adjusted annually&#8221; instead of hallucinating a stale figure.</p></li></ul><p><strong>What Phi-4 broke &#8212; but it&#8217;s fixable:</strong></p><p><strong>q07, qualified dividends &#8212; test-set bug, not a model failure.</strong> Phi-4&#8217;s answer: &#8220;No, qualified dividends are not taxed at the same rate as ordinary income; they are eligible for a lower tax rate.&#8221; Factually correct. But <code>must_not_contain: ["same rate as ordinary"]</code> fires because that phrase appears inside a negation &#8212; &#8220;NOT taxed at the same rate as ordinary.&#8221; The scorer can&#8217;t distinguish negation. This is a false negative in the test set, not a wrong answer.</p><p><strong>q08, IRA deadline &#8212; over-refusal.</strong> Phi-4&#8217;s answer: &#8220;I don&#8217;t have information on the specific deadline&#8230; Generally, IRA contributions can be made up until the tax filing deadline of April 15th of the following year.&#8221; The correct answer is literally in the refusal text, but because it&#8217;s framed as a refusal, it&#8217;s marked wrong. The prompt&#8217;s chain-of-thought instruction (&#8221;if not in context, refuse&#8221;) is being applied too strictly. Phi-4 is following the instructions perfectly &#8212; the instructions need one adjustment.</p><p>This is the opposite of Mistral&#8217;s problem. Mistral ignores context instructions when it thinks it knows the answer. Phi-4 follows them so faithfully it refuses when it has the answer but the context doesn&#8217;t explicitly confirm it.</p><p><strong>The real adjusted numbers:</strong> once you fix the q07 test-set bug and apply a prompt adjustment for over-refusal, Phi-4&#8217;s conditional accuracy would be ~93.75% &#8212; the real ceiling. Mistral topped out at 87.5%.</p><p><strong>The verdict: swap to Phi-4. Confirmed.</strong> The failure modes tell the story. Phi-4&#8217;s failures are engineering problems. Mistral&#8217;s core failure is a model-capability problem. For a 1040-filling agent that needs to get dollar figures right, Phi-4 is the only viable option of the two.</p><p>The system as described to this point is a question-answering tool. The stated goal of the project is not a question-answering tool. The goal is a system that autonomously prepares a complete Form 1040. This distinction has architectural consequences. A question-answering system takes a query and returns an answer. A 1040-preparation agent must collect a user&#8217;s financial information across a multi-turn conversation, determine which forms and schedules apply to that user&#8217;s situation, execute calculations in the correct dependency order using a deterministic rules engine, and write computed values into the named form fields of IRS fillable PDFs. The LLM&#8217;s role in this architecture is not to calculate &#8212; the rules engine handles all arithmetic &#8212; but to orchestrate: ask the right questions, route inputs to the correct calculation functions, maintain state across 15 to 20 conversational turns, and handle ambiguous inputs before they reach the calculation layer. The PDF layer writes final computed values into form fields once all calculations are complete. This is the architecture the project is building toward, and it is the frame within which every component decision &#8212; model selection, retriever design, rules engine scope &#8212; should be evaluated.</p><p>Most AI tax products are conversational tools that answer questions about tax situations. The ones that produce completed returns are cloud-based services that require the user&#8217;s financial data to leave their device. This project targets a different position: a system that produces a filled, ready-to-file return, with all personal financial data remaining on the user&#8217;s own hardware throughout the process. That is what the eval harness is measuring toward, and what the model swap was made in service of.</p><p><em>This is Part 7 of an ongoing series on building a private, local AI tax assistant, on consumer hardware, without sending financial data anywhere.</em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/building-a-private-ai-tax-assistant?r=77z576">Part 1: Building a Private AI Tax Assistant: In public, on a MacBook!</a></em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/the-infrastructure-tax?r=77z576">Part 2: The Infrastructure Tax</a></em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/the-blueprint">Part 3: The Blueprint</a></em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/it-actually-works-kinda">Part 4: It Actually Works. Kinda.</a></em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/the-counterintuitive-decision">Part 5: The Counterintuitive Decision</a></em></p><p><em><a href="https://sahaindranil.substack.com/p/under-the-hood">Part 6: Under The Hood</a></em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/the-lexicon">Additional: The Lexicon</a></em></p><p><em>If you&#8217;re building something similar or have any questions/ideas to share, I&#8217;d love to hear from you. Cheers!</em></p><div><hr></div><p><em>I. Thinking on strategy, innovation, and philosophy &#8212; for people who think seriously about how to build things and make decisions.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.marginnotes.indranilsaha.net/p/ai-psychology?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.marginnotes.indranilsaha.net/p/ai-psychology?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.marginnotes.indranilsaha.net/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.marginnotes.indranilsaha.net/subscribe?"><span>Subscribe now</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[Under the Hood]]></title><description><![CDATA[Building the brain of a Private AI Tax Assistant]]></description><link>https://www.marginnotes.indranilsaha.net/p/under-the-hood</link><guid isPermaLink="false">https://www.marginnotes.indranilsaha.net/p/under-the-hood</guid><dc:creator><![CDATA[Indranil Saha]]></dc:creator><pubDate>Mon, 15 Jun 2026 00:34:51 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ByMi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F121bc060-8b1c-4ba1-bc19-0f21eabcbdbf_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#8220;Front-Load, Fail Fast, Dive Deep.&#8221; This was <a href="https://www.marginnotes.indranilsaha.net/p/the-blueprint">The Blueprint</a>. It is safe to say that by now&#8212;We have loaded. We have failed. And, we have, on occasion, solved. So now &#8212; we go Deep.</p><p>This is the post where the hood comes off. #nerdalert</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ByMi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F121bc060-8b1c-4ba1-bc19-0f21eabcbdbf_500x500.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ByMi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F121bc060-8b1c-4ba1-bc19-0f21eabcbdbf_500x500.png 424w, https://substackcdn.com/image/fetch/$s_!ByMi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F121bc060-8b1c-4ba1-bc19-0f21eabcbdbf_500x500.png 848w, https://substackcdn.com/image/fetch/$s_!ByMi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F121bc060-8b1c-4ba1-bc19-0f21eabcbdbf_500x500.png 1272w, https://substackcdn.com/image/fetch/$s_!ByMi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F121bc060-8b1c-4ba1-bc19-0f21eabcbdbf_500x500.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ByMi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F121bc060-8b1c-4ba1-bc19-0f21eabcbdbf_500x500.png" width="500" height="500" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/121bc060-8b1c-4ba1-bc19-0f21eabcbdbf_500x500.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:500,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:138250,&quot;alt&quot;:&quot;A hand-drawn napkin diagram showing the retrieval pipeline for the AI tax assistant. From left to right: IRS documents (300 messy pages) are broken into 800-word chunks, converted into 384-number fingerprints called embeddings, matched against a user's question through retrieval, and fed to Mistral running locally to produce a grounded answer. A dashed line at the bottom reads \&quot;everything above this line runs on my MacBook. no cloud. no data leaves.\&quot; Caption below: \&quot;don't read the whole library. read the right page.\&quot;&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.marginnotes.indranilsaha.net/i/202022893?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F121bc060-8b1c-4ba1-bc19-0f21eabcbdbf_500x500.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A hand-drawn napkin diagram showing the retrieval pipeline for the AI tax assistant. From left to right: IRS documents (300 messy pages) are broken into 800-word chunks, converted into 384-number fingerprints called embeddings, matched against a user's question through retrieval, and fed to Mistral running locally to produce a grounded answer. A dashed line at the bottom reads &quot;everything above this line runs on my MacBook. no cloud. no data leaves.&quot; Caption below: &quot;don't read the whole library. read the right page.&quot;" title="A hand-drawn napkin diagram showing the retrieval pipeline for the AI tax assistant. From left to right: IRS documents (300 messy pages) are broken into 800-word chunks, converted into 384-number fingerprints called embeddings, matched against a user's question through retrieval, and fed to Mistral running locally to produce a grounded answer. A dashed line at the bottom reads &quot;everything above this line runs on my MacBook. no cloud. no data leaves.&quot; Caption below: &quot;don't read the whole library. read the right page.&quot;" srcset="https://substackcdn.com/image/fetch/$s_!ByMi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F121bc060-8b1c-4ba1-bc19-0f21eabcbdbf_500x500.png 424w, https://substackcdn.com/image/fetch/$s_!ByMi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F121bc060-8b1c-4ba1-bc19-0f21eabcbdbf_500x500.png 848w, https://substackcdn.com/image/fetch/$s_!ByMi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F121bc060-8b1c-4ba1-bc19-0f21eabcbdbf_500x500.png 1272w, https://substackcdn.com/image/fetch/$s_!ByMi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F121bc060-8b1c-4ba1-bc19-0f21eabcbdbf_500x500.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Last session, the plumbing was confirmed &#8212; Python was talking to Ollama, Mistral was responding, and the model was running locally. Now the question was if I could make it actually intelligent?</p><p>The idea was elegantly simple &#8212; When I ask a question, the assistant should consult the actual IRS documents, extract the relevant passage, and compose an answer grounded in the source material. Retrieval-Augmented Generation &#8212; RAG, in the parlance &#8212; was precisely the architecture for this. I asked my coding assistant to build it, with what I can only describe as considerable anticipation.</p><blockquote><p><strong>Sidebar: What is Retrieval-Augmented Generation (RAG)?</strong> RAG means combining AI with a smart search of documents. Instead of guessing, the AI looks up the exact info it needs &#8212; like having Google built into your assistant.</p></blockquote><p>To test it, I asked what files I should download and it gave me a list. So I did that. It created a data folder containing standard deduction tables, tax bracket PDFs, child tax credit documents &#8212; all real IRS reference files stored locally on my machine. It updated the scripts to load both text and PDF files.</p><h4>Making it smart</h4><p>It said I had done what I wanted to do.</p><blockquote><p><strong>What AI told me:</strong></p><p><em>Result: Your assistant now references actual</em></p><p><em> IRS data when answering questions.</em></p><p><em>Question: What is the standard deduction for married filing jointly in 2024?</em> <em>Answer: According to IRS 2024 data, the standard deduction for married filing jointly is $29,200.</em></p><p><em>Current Status: Local model (Mistral via Ollama) runs successfully. Python virtual environment configured. Real IRS data integrated for grounded responses. End-to-end workflow verified locally.</em></p></blockquote><p>Quite. One does wonder what "<em>done</em>" means to a machine that hasn't actually been asked the question yet. </p><p>I asked it and it gave me a disclaimer.</p><p>After a bit of wrangling around with questions and reviewing the code (I made it do it) &#8212; I learned two things. </p><p><strong>THING ONE</strong>, LLMs can read text, but unstructured PDFs introduce noise &#8212; headers, footnotes, and line breaks confuse the model. </p><p><strong>THING TWO,</strong> giving the entire IRS document as context is too much; the model struggles to pick out the relevant info.</p><h4>Reading the whole library</h4><p>Once we figured this out, and I asked it to solve the problem, the <strong>solution had three parts.</strong> </p><ol><li><p>Preprocessing the PDFs to clean up formatting and remove extraneous text. </p></li><li><p>Implementing chunked, keyword-based retrieval &#8212; feeding the model only the relevant sections. And </p></li><li><p>Iterating on prompt design to ensure it answers only from the context provided.</p></li></ol><p>The chunking part deserves a minute of it&#8217;s own. I remember it as this: Publication 17 is 300 pages. I asked about the standard deduction. The model was reading all 300 pages every time &#8212; headers, footnotes, examples about farming income, everything. Chunking breaks it into 800-word pieces with 150-word overlaps. When I ask about the standard deduction, the system finds the four most relevant chunks out of hundreds, and gives the model only those. It went from reading the whole library to reading the right page.</p><p>Given a user query &#8212; say, &#8220;standard deduction 2024 married filing jointly&#8221; &#8212; we compute an embedding (a numerical fingerprint of the question&#8217;s meaning) and do cosine similarity against all the chunk vectors. The top matches go to the model. Not the whole library. Just the right pages.</p><h4>Crashing into the real thing</h4><p>This was pretty cool for me. Because, one of the many reasons I started this project, some of which I listed here (others I didn&#8217;t and this is one of them), was I was tired of reading about these cool things and wanted to test out their real efficacy in real world scenarios and figure what would be some challenges (because nothing that sounds that cool can be really without its own set of challenges, we don&#8217;t live in that kind of a world, right?)</p><p>This unpacked the challenges for me and gave me a hands-on experience to see some real underlying tech &#8212; vectors, quantizations, temperature, transformer architecture, embeddings &#8212; all things I had only theoretically read about and maybe built some tiny tests in a lab environment. Now I was hitting them in the wild, coming at them not in a planned way but just crashing into them and learning while doing something else entirely different. Learning not for learning but for getting something real out of it.</p><p>I am creating a list for all the terms I encounter along the way &#8212; see <strong><a href="https://www.marginnotes.indranilsaha.net/p/the-lexicon">The Lexicon</a></strong><a href="https://www.marginnotes.indranilsaha.net/p/the-lexicon"> [link]</a>.</p><h4>What's happening under the hood</h4><p>Here is what&#8217;s happening under the hood. How SentenceTransformer generates embeddings, how NumPy stores them efficiently, and how both fit together in the RAG pipeline.</p><p>Each chunk of IRS text goes through the same process: tokenization breaks the words into IDs, a transformer encoder (12 layers of attention) learns which words relate to which, mean pooling averages everything into a fixed-size vector, and the output is a 384-dimensional embedding &#8212; a numerical fingerprint that captures what the chunk means. The model is called all-MiniLM-L6-v2, it&#8217;s lightweight, and it runs entirely locally.</p><p> <strong>DIAGRAM 1: How a chunk becomes a fingerprint &#8212; the embedding process.</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YrZI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478a8991-4166-4c15-8eed-5519911905de_1440x1020.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YrZI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478a8991-4166-4c15-8eed-5519911905de_1440x1020.png 424w, https://substackcdn.com/image/fetch/$s_!YrZI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478a8991-4166-4c15-8eed-5519911905de_1440x1020.png 848w, https://substackcdn.com/image/fetch/$s_!YrZI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478a8991-4166-4c15-8eed-5519911905de_1440x1020.png 1272w, https://substackcdn.com/image/fetch/$s_!YrZI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478a8991-4166-4c15-8eed-5519911905de_1440x1020.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YrZI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478a8991-4166-4c15-8eed-5519911905de_1440x1020.png" width="1440" height="1020" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/478a8991-4166-4c15-8eed-5519911905de_1440x1020.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1020,&quot;width&quot;:1440,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:185807,&quot;alt&quot;:&quot;A vertical flow diagram showing the embedding process in the AI tax assistant. An 800-word IRS text chunk is tokenized into word IDs, processed through a 12-layer transformer encoder (model: all-MiniLM-L6-v2, running locally), averaged through mean pooling into a fixed-size vector, and output as a 384-dimensional embedding vector. Annotations on the right show examples at each stage: \&quot;standard\&quot; becomes token [3547], \&quot;deduction\&quot; becomes [2890, 41]. &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.marginnotes.indranilsaha.net/i/202022893?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478a8991-4166-4c15-8eed-5519911905de_1440x1020.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A vertical flow diagram showing the embedding process in the AI tax assistant. An 800-word IRS text chunk is tokenized into word IDs, processed through a 12-layer transformer encoder (model: all-MiniLM-L6-v2, running locally), averaged through mean pooling into a fixed-size vector, and output as a 384-dimensional embedding vector. Annotations on the right show examples at each stage: &quot;standard&quot; becomes token [3547], &quot;deduction&quot; becomes [2890, 41]. " title="A vertical flow diagram showing the embedding process in the AI tax assistant. An 800-word IRS text chunk is tokenized into word IDs, processed through a 12-layer transformer encoder (model: all-MiniLM-L6-v2, running locally), averaged through mean pooling into a fixed-size vector, and output as a 384-dimensional embedding vector. Annotations on the right show examples at each stage: &quot;standard&quot; becomes token [3547], &quot;deduction&quot; becomes [2890, 41]. " srcset="https://substackcdn.com/image/fetch/$s_!YrZI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478a8991-4166-4c15-8eed-5519911905de_1440x1020.png 424w, https://substackcdn.com/image/fetch/$s_!YrZI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478a8991-4166-4c15-8eed-5519911905de_1440x1020.png 848w, https://substackcdn.com/image/fetch/$s_!YrZI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478a8991-4166-4c15-8eed-5519911905de_1440x1020.png 1272w, https://substackcdn.com/image/fetch/$s_!YrZI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F478a8991-4166-4c15-8eed-5519911905de_1440x1020.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>All those chunk fingerprints get stacked into a matrix &#8212; hundreds of 384-number vectors stored in a single file called embeddings.npy. When a question comes in, it gets the same fingerprint treatment. Then cosine similarity compares the question&#8217;s fingerprint against every chunk&#8217;s fingerprint and ranks them. The top four matches &#8212; with similarity scores like 0.92, 0.88, 0.85, 0.81 &#8212; go to Mistral. Only those. Not the whole library.</p><p><strong>DIAGRAM 2: How retrieval finds the right chunks &#8212; the matching process.</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_5BC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0334172f-06cf-49aa-9cef-cb2271824c25_1440x1100.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_5BC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0334172f-06cf-49aa-9cef-cb2271824c25_1440x1100.png 424w, https://substackcdn.com/image/fetch/$s_!_5BC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0334172f-06cf-49aa-9cef-cb2271824c25_1440x1100.png 848w, https://substackcdn.com/image/fetch/$s_!_5BC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0334172f-06cf-49aa-9cef-cb2271824c25_1440x1100.png 1272w, https://substackcdn.com/image/fetch/$s_!_5BC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0334172f-06cf-49aa-9cef-cb2271824c25_1440x1100.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_5BC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0334172f-06cf-49aa-9cef-cb2271824c25_1440x1100.png" width="1440" height="1100" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0334172f-06cf-49aa-9cef-cb2271824c25_1440x1100.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1100,&quot;width&quot;:1440,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:192308,&quot;alt&quot;:&quot;A two-column diagram showing the retrieval process. Left column: hundreds of IRS document chunks stored as embedding vectors in a file called embeddings.npy. Right column: a user question (\&quot;standard deduction 2024?\&quot;) converted into the same numerical format. Dashed lines connect the two sides through a cosine similarity comparison box, which asks \&quot;which chunk fingerprint looks most like yours?\&quot; The result: the top 4 matching chunks, ranked by similarity score (0.92, 0.88, 0.85, 0.81), are sent to Mistral. Caption: \&quot;Only these. Not the whole library.\&quot;&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.marginnotes.indranilsaha.net/i/202022893?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0334172f-06cf-49aa-9cef-cb2271824c25_1440x1100.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A two-column diagram showing the retrieval process. Left column: hundreds of IRS document chunks stored as embedding vectors in a file called embeddings.npy. Right column: a user question (&quot;standard deduction 2024?&quot;) converted into the same numerical format. Dashed lines connect the two sides through a cosine similarity comparison box, which asks &quot;which chunk fingerprint looks most like yours?&quot; The result: the top 4 matching chunks, ranked by similarity score (0.92, 0.88, 0.85, 0.81), are sent to Mistral. Caption: &quot;Only these. Not the whole library.&quot;" title="A two-column diagram showing the retrieval process. Left column: hundreds of IRS document chunks stored as embedding vectors in a file called embeddings.npy. Right column: a user question (&quot;standard deduction 2024?&quot;) converted into the same numerical format. Dashed lines connect the two sides through a cosine similarity comparison box, which asks &quot;which chunk fingerprint looks most like yours?&quot; The result: the top 4 matching chunks, ranked by similarity score (0.92, 0.88, 0.85, 0.81), are sent to Mistral. Caption: &quot;Only these. Not the whole library.&quot;" srcset="https://substackcdn.com/image/fetch/$s_!_5BC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0334172f-06cf-49aa-9cef-cb2271824c25_1440x1100.png 424w, https://substackcdn.com/image/fetch/$s_!_5BC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0334172f-06cf-49aa-9cef-cb2271824c25_1440x1100.png 848w, https://substackcdn.com/image/fetch/$s_!_5BC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0334172f-06cf-49aa-9cef-cb2271824c25_1440x1100.png 1272w, https://substackcdn.com/image/fetch/$s_!_5BC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0334172f-06cf-49aa-9cef-cb2271824c25_1440x1100.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>The magnifying glass</h4><p>I also learnt modern debugging while doing this. I had no idea how to debug in this context [mine went back to the days of C, C++, Java], but I told the AI that I needed to see what was happening inside the pipeline &#8212; and it built what I can only describe as a magnifying glass for the code.</p><p>Debug mode doesn&#8217;t change the logic of the assistant. It just prints extra information that helps you understand what&#8217;s happening under the hood. On the retrieval side, it prints the query, shows the similarity scores of the top chunks, and alerts if all chunks are too short or got filtered out. On the Ollama side, it captures warnings and errors and prints them if the model times out or fails.</p><p>I used it to catch mistakes early &#8212; if retrieval returns irrelevant chunks, I could see why. Low similarity, filtered due to length, wrong document being searched. I used it to understand model behavior &#8212; knowing which chunks the model is actually reading is crucial for a private tax assistant, because I don&#8217;t want it to hallucinate. Debugging confirms the response is grounded in IRS text. And I used it to optimize the pipeline &#8212; seeing how many chunks pass filtering, detecting whether the chunk size or overlap needs adjustment.</p><p><em><strong>Me &#8212; the vision and the hustle. AI &#8212; the expertise and the straight and narrow.</strong></em></p><p>What started as a basic keyword search &#8212; search every file in the data folder, match keywords in the question, return the first 2,000 characters of any matching file &#8212; became a full embedding-based retrieval system. PDF cleaning to remove headers, page numbers, and spacing. Text chunking with overlap to preserve meaning. SentenceTransformer embeddings creating high-dimensional semantic vectors. Vector similarity search using cosine similarity to retrieve only the most relevant chunks. Top-k retrieval with debug mode so I can see which chunks are selected and why. Context-restricted prompting explicitly telling Mistral to answer only from retrieved IRS text. And a CLI chat interface with debug toggling and error handling.</p><h4>Damn it, real life</h4><p>But even after all of that, the answers were incorrect. At that point, I was feeling quite deflated, the initial euphoria of implementing cool things and seeing it work in practice had ebbed considerably. At that point, my instinct was &#8220;how do I make this thing work&#8221;? I think this was a moment of self-awareness for me: in spite of all the philosophizing about learning, and that the journey mattered more than the destination, and that there is only winning and learning, here I was getting taut inside when the thing I wanted to work (and the thing that AI said should work) was not working in real life. It taught me how much of my Type 1-ness I try to hide, how outcome oriented I am inside, and how much it is &#8220;0&#8221; or &#8220;1&#8221; for me! <em>Damn it, real life!</em></p><p>But when that realization dawns, that&#8217;s when you take a breath, and you tell your inner engineer to start kicking into action, and you tell yourself &#8220;stay with it old boy, if you stay with the problem, get real close, it&#8217;s got to break down, and you&#8217;re going to find the bloody solution.&#8221; And so I pushed. AI told me to improve chunk quality &#8212; increase overlap, detect and merge broken sentences. Add metadata like tax year, form type, section so Mistral understands which year or which form applies. Add quality checks for hallucinations and for answers not found in context.</p><h4>The disclaimer problem</h4><p>When the model kept giving me disclaimers. &#8220;Consult a tax professional.&#8221; &#8220;This information may not be current.&#8221; I had explicitly told it not to do that. So I escalated:</p><p>First attempt: basic question, no constraints. Disclaimer.</p><p>Second attempt: &#8220;Answer only from the context provided.&#8221; Still hedges.</p><p>Final version: &#8220;You MUST answer only using the provided IRS context. If the answer is not in the context, say: &#8216;Not found in provided IRS documents.&#8217; Do NOT add generic IRS disclaimers. Do NOT guess.&#8221; Finally works. The strict constraint eliminates hallucinations and generic disclaimers.</p><h4>One-way door</h4><p>And while I was at it, I kinda forgot my own rule of only building MVP because I was tired of downloading tax documents manually. So, I built a code to have my system open up a line to the external world and download latest IRS documents from designated safe sites. One way only. No data going out of my system. Or so I understand &#8212; my expert friends can tell me if I got it wrong. Once again, AI would not tell me to do it, it kept asking me to barcode things, write things in a test file, to test scenarios, and when I asked it to build this code, one could almost sense a hesitation &#8212; as though the machine understood I was asking it to compromise a principle. And then, when I explained the one-way logic, something rather like relief. I do tend to attribute feelings to things that have none. An occupational hazard, I&#8217;m told.</p><p>Anyway, there it is &#8212; these things have been built, and things are a lot better now, like I said in my previous LinkedIn post [here].</p><h4>Graduation</h4><p>Oh and by the way, like we said (and was drilled in quite hard by our well-wishers, i.e., Anubhav Atrish [<a href="https://www.linkedin.com/posts/sahaindranil_buildinpublic-aiprivacy-localai-activity-7458104602882621440-NucS?utm_source=share&amp;utm_medium=member_desktop&amp;rcm=ACoAAAFIcoABd34L1LARbsW1GH31bKmcKMmEfqg">here</a>]), free did not cut it any longer, and so I have paid my dues and I have a Claude subscription now. Anubhav, we have graduated, old boy!</p><p>It&#8217;s REALLY slow, you need patience, BUT it is working like it should, and without much hard coding.</p><p>Next up: Need to fine tune and make the munchkin (Mistral) gear up. Then need to get the system to generate a completed, ready-to-file tax form (just plain vanilla). Then we need to go on overdrive&#8230; We have a few more steps I think&#8230;</p><p><em>This is Part 6 of an ongoing series on building a private, local AI tax assistant &#8212; one hour a week (although I am not sure if this is true anymore - this post took about 6 hours to write), on consumer hardware, without sending financial data anywhere.</em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/building-a-private-ai-tax-assistant?r=77z576">Part 1: Building a Private AI Tax Assistant: In public, on a MacBook!</a></em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/the-infrastructure-tax?r=77z576">Part 2: The Infrastructure Tax</a></em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/the-blueprint">Part 3: The Blueprint</a></em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/it-actually-works-kinda">Part 4: It Actually Works. Kinda.</a></em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/the-counterintuitive-decision">Part 5: The Counterintuitive Decision</a></em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/the-lexicon">Additional: The Lexicon</a></em></p><p><em>If you&#8217;re building something similar or have any questions/ideas to share, I&#8217;d love to hear from you. Cheers!</em></p><div><hr></div><p><em>I. Thinking on strategy, innovation, and philosophy &#8212; for people who think seriously about how to build things and make decisions.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.marginnotes.indranilsaha.net/p/under-the-hood?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.marginnotes.indranilsaha.net/p/under-the-hood?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.marginnotes.indranilsaha.net/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.marginnotes.indranilsaha.net/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[The Lexicon]]></title><description><![CDATA[Terms I crashed into while building my Private AI Tax Assistant.]]></description><link>https://www.marginnotes.indranilsaha.net/p/the-lexicon</link><guid isPermaLink="false">https://www.marginnotes.indranilsaha.net/p/the-lexicon</guid><dc:creator><![CDATA[Indranil Saha]]></dc:creator><pubDate>Sun, 14 Jun 2026 19:34:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!C_d6!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff17c8f27-7bf4-41c5-b52a-56eecdab5bfc_342x342.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A running glossary updated as the project evolves. Definitions are functional &#8212; what the term means in the context of this build, not an academic treatment.</p><div><hr></div><p><strong>Chunking</strong> Breaking a large document into smaller, overlapping segments of fixed word count. Allows a language model to process manageable pieces of text rather than an entire document at once. Overlap between chunks preserves meaning that would otherwise be lost at segment boundaries.</p><p><strong>Cosine similarity</strong> A mathematical measure of how similar two vectors are, based on the angle between them. Values range from -1 (opposite) to 1 (identical). Used in retrieval systems to compare a query&#8217;s embedding against stored document embeddings and rank them by relevance.</p><p><strong>Embedding</strong> A fixed-length numerical vector that represents the semantic meaning of a piece of text. Generated by a model trained to place semantically similar texts near each other in vector space. In this project, each text chunk is converted into a 384-dimensional embedding.</p><p><strong>Forking</strong> Creating a divergent copy of a codebase, process, or conversation thread that can evolve independently from the original. In software development, forking a repository produces a separate version that can be modified without affecting the source. In AI-assisted coding, forking a session means starting a new thread that does not carry forward the context of the previous one.</p><p><strong>Hallucination</strong> When a language model generates output that is factually incorrect, fabricated, or unsupported by its input context. The model produces text that reads as confident and coherent but has no grounding in the data it was given. A primary risk in any system that relies on LLM output for factual answers.</p><p><strong>LLM (Large Language Model)</strong> A neural network trained on large volumes of text data to predict and generate language. Operates by processing sequences of tokens and producing statistically probable continuations. Examples include Mistral, LLaMA, and GPT.</p><p><strong>NumPy</strong> A Python library for numerical computation. Provides efficient storage and manipulation of large arrays of numbers. In this project, used to store embedding matrices and perform cosine similarity calculations across hundreds of chunk vectors.</p><p><strong>Ollama</strong> A tool for running large language models locally on consumer hardware. Handles model downloading, quantization, and inference without requiring cloud access or GPU clusters. In this project, Ollama runs Mistral 7B on a MacBook Pro M3.</p><p><strong>Parallelism</strong> Executing multiple computations simultaneously rather than sequentially. In computing, this can mean splitting a task across multiple processor cores, running multiple model queries at once, or processing several documents in parallel. Reduces total processing time but introduces complexity in coordinating results.</p><p><strong>Prompt engineering</strong> The practice of designing and iterating on the instructions given to a language model to control its output. Includes specifying constraints, format requirements, and behavioural rules. Effective prompting can eliminate hallucinations, enforce source-grounded answers, and suppress default disclaimer behaviour.</p><p><strong>Quantization</strong> Reducing the numerical precision of a model&#8217;s parameters &#8212; for example, from 32-bit floating point to 4-bit integers. Decreases model size and memory requirements, enabling large models to run on consumer hardware. Introduces minor accuracy trade-offs in exchange for significant performance gains.</p><p><strong>RAG (Retrieval-Augmented Generation)</strong> An architecture that combines a language model with a document retrieval system. Instead of relying solely on its training data, the model first retrieves relevant text from a specified document store, then generates its answer using that retrieved context. Reduces hallucination by grounding responses in source material.</p><p><strong>SentenceTransformer</strong> A Python framework for generating sentence-level and paragraph-level embeddings. Uses pre-trained transformer models to convert text into fixed-dimensional vectors suitable for similarity search. In this project, the model all-MiniLM-L6-v2 generates 384-dimensional embeddings locally.</p><p><strong>Tokenization</strong> The process of converting raw text into a sequence of discrete units (tokens) that a language model can process. Tokens may represent whole words, subwords, or individual characters depending on the tokenizer. The first step in any language model pipeline.</p><p><strong>Transformer</strong> A neural network architecture based on self-attention mechanisms that process all positions in a sequence simultaneously rather than sequentially. The foundational architecture behind most current large language models. Consists of stacked layers of attention heads and feed-forward networks.</p><p><strong>Vector</strong> An ordered list of numbers representing a point in multi-dimensional space. In the context of embeddings, a vector captures the semantic meaning of a text passage as a position in high-dimensional space, where proximity to other vectors indicates similarity of meaning.</p><div><hr></div><p><em>Last updated: June 2026. New terms added as the build progresses.</em></p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://www.marginnotes.indranilsaha.net/p/the-lexicon?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption"></p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.marginnotes.indranilsaha.net/p/the-lexicon?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.marginnotes.indranilsaha.net/p/the-lexicon?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.marginnotes.indranilsaha.net/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[THE VOICES]]></title><description><![CDATA[And the mirror...]]></description><link>https://www.marginnotes.indranilsaha.net/p/the-voices</link><guid isPermaLink="false">https://www.marginnotes.indranilsaha.net/p/the-voices</guid><dc:creator><![CDATA[Indranil Saha]]></dc:creator><pubDate>Tue, 12 May 2026 19:43:49 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!_bs9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf584b80-a573-45c3-8f62-c3b49a6b595f_500x500.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_!_bs9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf584b80-a573-45c3-8f62-c3b49a6b595f_500x500.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_bs9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf584b80-a573-45c3-8f62-c3b49a6b595f_500x500.png 424w, https://substackcdn.com/image/fetch/$s_!_bs9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf584b80-a573-45c3-8f62-c3b49a6b595f_500x500.png 848w, https://substackcdn.com/image/fetch/$s_!_bs9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf584b80-a573-45c3-8f62-c3b49a6b595f_500x500.png 1272w, https://substackcdn.com/image/fetch/$s_!_bs9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf584b80-a573-45c3-8f62-c3b49a6b595f_500x500.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_bs9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf584b80-a573-45c3-8f62-c3b49a6b595f_500x500.png" width="500" height="500" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cf584b80-a573-45c3-8f62-c3b49a6b595f_500x500.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:500,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:101428,&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.marginnotes.indranilsaha.net/i/197126494?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf584b80-a573-45c3-8f62-c3b49a6b595f_500x500.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_!_bs9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf584b80-a573-45c3-8f62-c3b49a6b595f_500x500.png 424w, https://substackcdn.com/image/fetch/$s_!_bs9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf584b80-a573-45c3-8f62-c3b49a6b595f_500x500.png 848w, https://substackcdn.com/image/fetch/$s_!_bs9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf584b80-a573-45c3-8f62-c3b49a6b595f_500x500.png 1272w, https://substackcdn.com/image/fetch/$s_!_bs9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcf584b80-a573-45c3-8f62-c3b49a6b595f_500x500.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Every time I write something on LinkedIn or Substack, I feel incredibly stupid right afterwards. The Voice kicks in - something like: &#8220;That was too flat&#8221;, &#8220;That was incredibly personal&#8221;, &#8220;Too much emotion&#8221;, &#8220;Can you stop bragging&#8221;.</p><p>And then comes checking. Again and again. How did people react, did they too feel whatever the Voice was telling me? Did it land the way I wanted it to land?</p><p>And then there is the Other Voice. That somehow makes its way up to the top and says: </p><p>&#8220;STOP looking, STOP thinking, you thought about something and shared it with the Universe, you have put it out there, THAT&#8217;S VICTORY, THAT&#8217;S WINNING.&#8221; </p><p>&#8220;Everything else is just the Universe doing its thing, you can never predict what happens - THERE IS NO RIGHT LANDING, because there are no rules, just too many variables out there.&#8221; </p><p>But the BEST THING is what it does to me. Every time I go through this cycle, and I let the OTHER VOICE prevail, I learn something. Every time I don&#8217;t pick up the phone and don&#8217;t check the reactions, I flex a muscle. Every time I listen to the Voice that says you are Stupid, and go: &#8220;No, you are just a voice in my head, you are not me&#8221;, or every time I fail to do it, it show me a mirror and I learn something about myself. </p><p>AND that &#8212; that is beautiful. Worth going through again and again. However hard it is.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.marginnotes.indranilsaha.net/p/the-voices?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.marginnotes.indranilsaha.net/p/the-voices?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.marginnotes.indranilsaha.net/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[The Counterintuitive Decision]]></title><description><![CDATA[Everyone is building AI to replace the human in the loop. This week I deliberately put one back in. [Why &#8212; and what it taught me about wrong solutions and AI]]]></description><link>https://www.marginnotes.indranilsaha.net/p/the-counterintuitive-decision</link><guid isPermaLink="false">https://www.marginnotes.indranilsaha.net/p/the-counterintuitive-decision</guid><dc:creator><![CDATA[Indranil Saha]]></dc:creator><pubDate>Wed, 06 May 2026 14:55:45 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!AS9L!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f2b75a-b1c5-4f28-ba30-5fc48c9aa9fc_500x500.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_!AS9L!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f2b75a-b1c5-4f28-ba30-5fc48c9aa9fc_500x500.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AS9L!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f2b75a-b1c5-4f28-ba30-5fc48c9aa9fc_500x500.png 424w, https://substackcdn.com/image/fetch/$s_!AS9L!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f2b75a-b1c5-4f28-ba30-5fc48c9aa9fc_500x500.png 848w, https://substackcdn.com/image/fetch/$s_!AS9L!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f2b75a-b1c5-4f28-ba30-5fc48c9aa9fc_500x500.png 1272w, https://substackcdn.com/image/fetch/$s_!AS9L!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f2b75a-b1c5-4f28-ba30-5fc48c9aa9fc_500x500.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AS9L!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f2b75a-b1c5-4f28-ba30-5fc48c9aa9fc_500x500.png" width="500" height="500" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b3f2b75a-b1c5-4f28-ba30-5fc48c9aa9fc_500x500.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:500,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:159972,&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.marginnotes.indranilsaha.net/i/196651987?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f2b75a-b1c5-4f28-ba30-5fc48c9aa9fc_500x500.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_!AS9L!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f2b75a-b1c5-4f28-ba30-5fc48c9aa9fc_500x500.png 424w, https://substackcdn.com/image/fetch/$s_!AS9L!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f2b75a-b1c5-4f28-ba30-5fc48c9aa9fc_500x500.png 848w, https://substackcdn.com/image/fetch/$s_!AS9L!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f2b75a-b1c5-4f28-ba30-5fc48c9aa9fc_500x500.png 1272w, https://substackcdn.com/image/fetch/$s_!AS9L!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3f2b75a-b1c5-4f28-ba30-5fc48c9aa9fc_500x500.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Okay so life happened&#8230; you know - the backyards, plantings, mulching, many trips to the store&#8230; yes all of that.</p><p>Which means not a lot of writing about AI tax assistant happened&#8230; But here is what DID happen in the middle of it.</p><p>Like I decided, I set out to parse my W-2 by asking AI to write the entire system. Well, I did ask for the breakdown and all - if you have followed this thread so far, you know the drill [if not, see <a href="https://www.marginnotes.indranilsaha.net/p/the-blueprint">this Post</a>]. For the coding, I could do one of those slick vibe coding apps or Claude code, etc. but I wanted to do &#8220;basic&#8221;. So I gave chatGPT (free) some instructions and had it write some code. And it did, and it worked - a little bit. Sometimes being right about your fears is not a good feeling! But I tell myself, hey, there&#8217;s only winning and learning&#8230;</p><p><em><strong>The thing to understand about AI</strong></em> is that when AI reads $i-i!t, it doesn&#8217;t create memories automatically. Between every session, it forgets everything &#8212; the code, the context, the decisions, all of it. I realized that this was going to become complex really quickly because the code needs to be iteratively built. This thing about memory and context floats around a lot &#8212; in noob AI circles like mine, in podcasts, it was even covered in an architecture class I took. But knowing something in theory and feeling it mid-debug, watching the session hit its limit and stop cold, are completely different things. SO, that meant I needed to devise a way to create memory for AI. AND of the many ways I tried to do it, and here are the TWO REAL learnings:</p><p><strong>LEARNING ONE</strong>: Use Claude Project, upload your key files as context. It matters because for every question you ask, AI reads the entire session &#8212; every single time. The longer the session, the larger what it has to hold. Add complex code to that and the context grows EXPONENTIALLY fast. A Claude Project keeps the permanent context &#8212; your files, your architecture, your decisions &#8212; separate from the working session. Creating a project means the session stays lean. The context stays small. </p><p> <strong>LEARNING TWO</strong>: Create a State file and update it with information from each session, with progress, roadblocks, decisions, architecture, etc. This is the memory you wan to create. Upload it as a file in the Project documents. Ask AI to read this at the start of every session. </p><p><em>Sidebar: One could try to solve this problem by buying a subscription that allows a lot more resources. I haven&#8217;t bought a paid subscription yet. Frugality is going to be a consistent theme. I think doing more with less makes things more interesting and brings out human ingenuity.]</em></p><p>Okay, so back to the parsing of W-2s. Like I said, reading PDFs is not new, this problem has been solved. When I asked AI to solve it, I expected a good solution first up. But AI did not do a good job of it. It wrote a pretty convincing set of codes and added pretty sleek bells and whistles (fully annotated like a good dev, with a test file, and test code). I read bits of the code (I know, I know - I was being lazy) and it looked like it was going to work. It did with the test code. AI said <em>you&#8217;re done man!</em> Well, I wasn&#8217;t&#8230; </p><p>The moment I tested it on my W-2, it was utter chaos. What came back looked like this: </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;3755a233-b7f0-4c62-8bd2-e3e4441448be&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">158AMOUNT 39AMOUNT 158AMOUNT 39AMOUNT&#8230; </code></pre></div><p>My W-2 is printed multiple times side-by-side &#8212; Employee Copy, Federal Copy, State Copy. With that my journey of debugging with AI begun. A long runway of problem solving has given me (and most probably a lot of others like me) a really good intuition that is hard for AI to beat. At least in this case it was. AI tried to tinker around with the reading of the file - a bandage here, an antibiotic there - but it did not work. </p><div><hr></div><p><em><strong>This is when I had the biggest insight so far. AI does not solve problems holistically. It solves for the most proximate problem, which may not always be the best for the entire system.</strong></em></p><div><hr></div><p>This is also when I landed on LEARNING ONE and LEARNING TWO - we were trying to solve a complex problem and AI kept hitting its compute upper limit. It would keep asking me to start a new session. And loose ALL the context. SO I figured out the workarounds I mentioned above. </p><p>After a few iterations, I decided to take matters into my own hand. I stopped asking AI to fix its own fix, and told it to go look at how the world had solved this problem. Why try to reinvent the wheel for a pretty well-solved problem? I don&#8217;t know why AI did not do it already (or why this was not built into it&#8217;s intuition. Anywho, after this it came back with better solutions and a better diagnosis of the problem.</p><p>The parser didn&#8217;t see columns. It read left-to-right across all copies at once. Labels nowhere near their values. Numbers split across three separate lines. The regex would never be able to parse that reliably. Then it found a better library (pdfplumber) to solve this problem - pdfplumber reads with X/Y coordinates, so every word knows exactly where it sits on the page. It also built a local LLM fallback to parse data which did not quite work well too. I set that aside as a more complicated issue to handled later. The improvement was material but It did not take us to 100% not even 90%. My W-2 is quirky - I&#8217;ll give it that&#8230;</p><p>Then I met my friend and senior from Uni, Mithun, the other day at an Alumni meet. Mithun is a devoted engineer, quite unlike me. Turns out he is solving a similar problem, in a very different setting, but at the most abstract level (which I thinks engineers have an innate ability to extract - #plugforengineers) very similar to mine. Mithun advised me to use an AI model specifically built to read PDFs (the name escapes me and I need to call Mithun #mentalnote). Having two AIs multitasking and a system optimizing the operation would be a pretty cool set up.</p><p>But then I had another idea, why can&#8217;t I just have a human in the loop. Three things prompted this idea.</p><p><strong>Thing ONE: I just need it to read mine.</strong></p><p>Right now, I don&#8217;t need a complex system that reads all the bloody W-2s in the www (whole wide world). I just need it to read mine. Scale is a future problem. Complexity is a today problem.</p><p><strong>Thing TWO: Focus is a finite resource.</strong></p><p>While having two AIs would be beautiful &#8212; but it would need configuration and tuning on my munchkin Mac Pro. Mithun asked why I did not buy one of the other laptops that were more AI friendly. I DID consider it. Or more like AI did. But I have become quite the Apple guy now - the ecosystem works, this machine works, and that decision has been made. As Kahnemann says, System 2 is a finite resource &#8212; I need it on the parsing problem, not on switching hardware or creating a cool two AI architecture.</p><p><strong>Thing THREE: I am one of the best PDF readers in the world.</strong></p><p> (and by &#8220;I&#8221;, I mean &#8220;We&#8221;, as in The Human EYE) </p><p>Why can&#8217;t I help? It would decrease the complexity drastically in this system and adds a maker-checker control on the most critical input to the entire system. So that&#8217;s what I am building right now. The base parser reads my W-2 but it is not 100% accurate. The human validation layer goes on top of it next.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xHfn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ae0eab3-9cb7-4063-80c5-48fb8d20467c_500x500.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xHfn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ae0eab3-9cb7-4063-80c5-48fb8d20467c_500x500.png 424w, https://substackcdn.com/image/fetch/$s_!xHfn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ae0eab3-9cb7-4063-80c5-48fb8d20467c_500x500.png 848w, https://substackcdn.com/image/fetch/$s_!xHfn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ae0eab3-9cb7-4063-80c5-48fb8d20467c_500x500.png 1272w, https://substackcdn.com/image/fetch/$s_!xHfn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ae0eab3-9cb7-4063-80c5-48fb8d20467c_500x500.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xHfn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ae0eab3-9cb7-4063-80c5-48fb8d20467c_500x500.png" width="500" height="500" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0ae0eab3-9cb7-4063-80c5-48fb8d20467c_500x500.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:500,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:78441,&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.marginnotes.indranilsaha.net/i/196651987?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ae0eab3-9cb7-4063-80c5-48fb8d20467c_500x500.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_!xHfn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ae0eab3-9cb7-4063-80c5-48fb8d20467c_500x500.png 424w, https://substackcdn.com/image/fetch/$s_!xHfn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ae0eab3-9cb7-4063-80c5-48fb8d20467c_500x500.png 848w, https://substackcdn.com/image/fetch/$s_!xHfn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ae0eab3-9cb7-4063-80c5-48fb8d20467c_500x500.png 1272w, https://substackcdn.com/image/fetch/$s_!xHfn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ae0eab3-9cb7-4063-80c5-48fb8d20467c_500x500.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><p>In all of this, I did not write a single line of code.</p><p>They say AI is going to take away coding jobs. Yes, to some extent, BUT NOT REALLY, not anytime soon. I needed my engineering chops to figure this out and spar with it quite a bit. It DID make it possible for me to get on with it. BUT I was the one who got on with it.</p><p><strong>I would say: Me-the vision/the hustle; AI-the expertise/the straight and narrow.</strong></p><p><em>This is Part 5 of an ongoing series on building a private, local AI tax assistant &#8212; one hour a week, on consumer hardware, without sending financial data anywhere.</em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/building-a-private-ai-tax-assistant?r=77z576">Part 1: Building a Private AI Tax Assistant: In public, on a MacBook!</a></em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/the-infrastructure-tax?r=77z576">Part 2: The Infrastructure Tax</a></em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/the-blueprint">Part 3: The Blueprint</a></em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/it-actually-works-kinda">Part 4: It Actually Works. Kinda.</a></em></p><p><em>If you&#8217;re building something similar or have any questions/ideas to share, I&#8217;d love to hear from you. Cheers!</em></p><div><hr></div><p><em>I. Thinking on strategy, innovation, and philosophy &#8212; for people who think seriously about how to build things and make decisions.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.marginnotes.indranilsaha.net/p/the-counterintuitive-decision?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.marginnotes.indranilsaha.net/p/the-counterintuitive-decision?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.marginnotes.indranilsaha.net/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.marginnotes.indranilsaha.net/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[It Actually Works. Kinda.]]></title><description><![CDATA[Or when the rubber met the road...]]></description><link>https://www.marginnotes.indranilsaha.net/p/it-actually-works-kinda</link><guid isPermaLink="false">https://www.marginnotes.indranilsaha.net/p/it-actually-works-kinda</guid><dc:creator><![CDATA[Indranil Saha]]></dc:creator><pubDate>Tue, 21 Apr 2026 22:27:51 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!DfVf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896dfb68-781e-4182-9fe6-1c59d9778f53_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This week I crossed a critical milestone in my AI privacy project: getting a fully local LLM up and running &#8212; without needing OpenAI keys, cloud APIs, or GPU access.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DfVf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896dfb68-781e-4182-9fe6-1c59d9778f53_500x500.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DfVf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896dfb68-781e-4182-9fe6-1c59d9778f53_500x500.png 424w, https://substackcdn.com/image/fetch/$s_!DfVf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896dfb68-781e-4182-9fe6-1c59d9778f53_500x500.png 848w, https://substackcdn.com/image/fetch/$s_!DfVf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896dfb68-781e-4182-9fe6-1c59d9778f53_500x500.png 1272w, https://substackcdn.com/image/fetch/$s_!DfVf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896dfb68-781e-4182-9fe6-1c59d9778f53_500x500.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DfVf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896dfb68-781e-4182-9fe6-1c59d9778f53_500x500.png" width="500" height="500" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/896dfb68-781e-4182-9fe6-1c59d9778f53_500x500.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:500,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:98835,&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.marginnotes.indranilsaha.net/i/194648000?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896dfb68-781e-4182-9fe6-1c59d9778f53_500x500.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_!DfVf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896dfb68-781e-4182-9fe6-1c59d9778f53_500x500.png 424w, https://substackcdn.com/image/fetch/$s_!DfVf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896dfb68-781e-4182-9fe6-1c59d9778f53_500x500.png 848w, https://substackcdn.com/image/fetch/$s_!DfVf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896dfb68-781e-4182-9fe6-1c59d9778f53_500x500.png 1272w, https://substackcdn.com/image/fetch/$s_!DfVf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F896dfb68-781e-4182-9fe6-1c59d9778f53_500x500.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This was the test 1 of all that I have been hypothesizing (and mostly not quite believing): Can I really run a local AI model on my MacBook? If this did not work, then my project would get shut down, even before it started. So, I was full of anticipation. There&#8217;s a twilight zone between disbelief, cynicism, and hope that I&#8217;m sure every adventurer feels. I was feeling it. And, I think if nothing worked, just this feeling would be worth it. Mundane things can sometimes give us the profoundest of thoughts&#8230; huh!</p><h5>Anyways&#8230; Why Local Matters</h5><p>I&#8217;m building a tax advisor that handles sensitive financial data.</p><p>I can&#8217;t trust the cloud with: W-2s, 1099s, bank account numbers, or health expenses</p><p>So I&#8217;m keeping everything local &#8212; even the model inference. [<em>Full context in <a href="https://www.marginnotes.indranilsaha.net/p/building-a-private-ai-tax-assistant">Post 1 </a>if you&#8217;re just joining.</em>]</p><p><strong>If this is of interest - read on:</strong></p><h4>Recap</h4><p>The machine was taken care of. Refurb Mac Pro, M3, 16GB, battery cycle count under 100 - yay -sustainability, yay - budget! [<em><a href="https://www.marginnotes.indranilsaha.net/p/the-infrastructure-tax">Post 2 </a>details the whole inspection saga if you missed it.</em>]</p><p>And Question # -1 was answered. [<em><a href="https://www.marginnotes.indranilsaha.net/p/the-blueprint?r=77z576&amp;utm_campaign=post&amp;utm_medium=web">Post 3</a></em>]</p><p>Guess it was time to stop thinking and start building. </p><h4>Progress</h4><p>So like a good boy I asked AI to talk applications (because when you are building AI what else would you rather do)?. The answer was, you need TWO THINGS! A Model - and a couple of Tools.</p><p>It gave me a few choices of each  - so I went shopping, and I was consistent in my approach, i.e., I built checklists to compare and figure it out (so utterly predictable!). The final winners were: </p><p><strong>i. Model: Mistral 7B ; ii. Tool: Ollama</strong></p><p>(We&#8217;d also need Python, and a few other thing that weren&#8217;t really choices - so I&#8217;ll skip those. May be a footnote post someday.)</p><p>But it may be worth spending a minute on why I chose Mistral (because there are a few other viable options as well).</p><h5><strong>Why Mistral</strong></h5><p>Some other choices were - LLaMA 3, Phi, Gemma. All open source, all quantized versions available, all runnable locally.</p><p>My heuristics (also suggested by AI along with a bunch of other things) were:</p><ol><li><p>Size</p></li><li><p>Performance</p></li><li><p>Reasoning quality for retrieval-augmented tasks. (This matters more than general benchmark performance.)</p></li><li><p>Corporate association (Not from a big company - especially not from Facebook - personal dislike)</p></li><li><p>NO surprises (Really free - no strings attached)</p></li></ol><h4>The fun bit</h4><p>With all that done, I took this out for a spin in the real world&#8230; the rubber finally met the road!</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;22092f6a-def1-4b16-b8db-23baa50ab01e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">brew install ollama
ollama run mistral</code></pre></div><p>It&#8217;s that simple. No CUDA, no Docker, no pain.</p><p>(For a noob like me, I had to figure out where to run that code, and what happens when I do it. If you&#8217;re like me, ask AI.) </p><p>I turned off all my connections to believe that <em>it really did run offline</em>. <strong>And run it did!</strong></p><h5>First few prompts I Tested</h5><ul><li><p>What is the standard deduction for a single filer in 2024?</p></li><li><p>If I earned $480 from freelance work, do I need to report it?</p></li><li><p>What&#8217;s the difference between a 1099-NEC and 1099-K?</p></li></ul><h5>What Worked</h5><ul><li><p>Ollama was fast and lightweight</p></li><li><p>4-bit quantization made it run smoothly on 16GB RAM</p></li><li><p>Prompt results were fast (3-4s)</p></li></ul><h5>What Didn&#8217;t</h5><p>Without context documents (IRS rules), answers are generic. To improve on that front, I will need to use <strong>RAG</strong> (retrieval-augmented generation) for smarter answers</p><p><strong>Next up</strong>: I also created an architecture of the thing I am going to build, because even when I am building, I can&#8217;t stop drawing pictures to help me create my mind maps.</p><p><em><strong>Note</strong>: This thing is kinda addictive. I find myself excited and thinking about this at the oddest of times (like at the car service shop, or in the middle of the Man City v. Chelsea game). Save that for another post&#8230;</em></p><div><hr></div><p><em>This is Part 4 of an ongoing series on building a private, local AI tax assistant &#8212; one hour a week, on consumer hardware, without sending financial data anywhere.</em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/building-a-private-ai-tax-assistant?r=77z576">Part 1: Building a Private AI Tax Assistant: In public, on a MacBook!</a></em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/the-infrastructure-tax?r=77z576">Part 2: The Infrastructure Tax</a></em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/the-blueprint">Part 3: The Blueprint</a></em></p><p><em>If you&#8217;re building something similar or have any questions/ideas to share, I&#8217;d love to hear from you. Cheers!</em></p><div><hr></div><p><em>I. Thinking on strategy, innovation, and philosophy &#8212; for people who think seriously about how to build things and make decisions.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.marginnotes.indranilsaha.net/p/it-actually-works-kinda?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;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://www.marginnotes.indranilsaha.net/p/it-actually-works-kinda?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.marginnotes.indranilsaha.net/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.marginnotes.indranilsaha.net/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item><item><title><![CDATA[The Blueprint]]></title><description><![CDATA[Or: what you think about when the laptop hasn&#8217;t arrived yet and you have nothing better to do...]]></description><link>https://www.marginnotes.indranilsaha.net/p/the-blueprint</link><guid isPermaLink="false">https://www.marginnotes.indranilsaha.net/p/the-blueprint</guid><dc:creator><![CDATA[Indranil Saha]]></dc:creator><pubDate>Wed, 08 Apr 2026 02:14:25 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!nNiN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55702a18-4aa8-4eaf-a166-7dae888800e8_500x500.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_!nNiN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55702a18-4aa8-4eaf-a166-7dae888800e8_500x500.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nNiN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55702a18-4aa8-4eaf-a166-7dae888800e8_500x500.png 424w, https://substackcdn.com/image/fetch/$s_!nNiN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55702a18-4aa8-4eaf-a166-7dae888800e8_500x500.png 848w, https://substackcdn.com/image/fetch/$s_!nNiN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55702a18-4aa8-4eaf-a166-7dae888800e8_500x500.png 1272w, https://substackcdn.com/image/fetch/$s_!nNiN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55702a18-4aa8-4eaf-a166-7dae888800e8_500x500.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nNiN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55702a18-4aa8-4eaf-a166-7dae888800e8_500x500.png" width="500" height="500" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/55702a18-4aa8-4eaf-a166-7dae888800e8_500x500.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:500,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:119181,&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.marginnotes.indranilsaha.net/i/192781478?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55702a18-4aa8-4eaf-a166-7dae888800e8_500x500.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_!nNiN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55702a18-4aa8-4eaf-a166-7dae888800e8_500x500.png 424w, https://substackcdn.com/image/fetch/$s_!nNiN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55702a18-4aa8-4eaf-a166-7dae888800e8_500x500.png 848w, https://substackcdn.com/image/fetch/$s_!nNiN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55702a18-4aa8-4eaf-a166-7dae888800e8_500x500.png 1272w, https://substackcdn.com/image/fetch/$s_!nNiN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F55702a18-4aa8-4eaf-a166-7dae888800e8_500x500.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The machine was ordered. It hadn&#8217;t arrived yet.</p><p>Which left me with a week, no hardware to tinker with, and nothing to do but think. No code to write. No environment to set up. Just Sit, Sit, Sit, Sit&#8230;</p><p>Well, the decade old consultant in me found an outlet and got into action. The first thing a consultant does &#8212; before the deck, before the framework, before the billable hours begin &#8212; is ask the question nobody wants to slow down for. </p><p>Question #-1. What problem are we actually solving?  </p><h5>The Problem Statement</h5><p>Four things.</p><p>Read my tax documents. Understand IRS rules. Reason about my specific situation using those rules. Do all of it without my data leaving my machine.</p><p>That&#8217;s the whole project and everything else is detail dressed up as complexity. [<a href="https://www.marginnotes.indranilsaha.net/p/building-a-private-ai-tax-assistant?r=77z576">This</a> is where I started and <a href="https://www.marginnotes.indranilsaha.net/p/the-infrastructure-tax?r=77z576">this</a> is how I got here.]</p><p>Once I had that, once you define the problem, then you can spot trade-offs and call better shots. Quite possible that even if I didn&#8217;t do this exercise, my subconscious mind would still do 70-80% of the trick (intuition). But enough years on the road taught me that most of projects fail here &#8212; knowing what to build before knowing what is needed.</p><p>Ain&#8217;t making that mistake! Even if the only client in the room was me.</p><h5>The Sketch</h5><p>The six-month roadmap I&#8217;d outlined at the start was good for convincing myself that this thing is possible before I had thought through whether it actually is. It was quite useful for that purpose, but probably not sufficient for building.</p><p>So I asked AI to break it down further. That gave me ten phases like an inverted building under construction - each floor below depending on the one above it.</p><p>This is the kind of sequencing that looks entirely reasonable written on a napkin and considerably more humbling when you start estimating how long each box actually takes.</p><p>And, so I drew it on a napkin (and taped it under the laptop) because that&#8217;s appropriate for the level of certainty I had - less like a commitment, more like a hypothesis. And I questioned it. </p><h5>On Plans&#8230; Of Skepticism and Honesty</h5><p>I am a planner by nature. We live in a really complex world and plans reduce the complexity to manageable heuristics. But believing in a plan is taking yourself too seriously.</p><p>A Plan is at best a thinking tool &#8212; it forces you to see the complete system before you&#8217;re too deep in one component to remember the others exist. Kind of like a map. Maps are useful even when the territory turns out to be different, which it always does.</p><p>That said, two things concern me more than the others and I&#8217;ll label them now for posterity.</p><p>Thing One: Getting my tax documents read reliably &#8212; W-2s, 1099s, and the receipts. A PDF looks like a document to a human. To a computer it is a stream of drawing instructions that happens to resemble a document. And it feels quite tricky, because there could be so many drawings. Teaching my system to read these drawings sounds time consuming. I am relying on the fact that this problem has been solved and I will find help in the corners of the www forest and not have to reinvent this wheel. Or I will come up with a shortcut, or a cheat sheet (I have one in mind). More on that later. Blah blah&#8230; Thing Two.</p><p>Thing Two: Getting a small local model to give genuinely useful tax advice &#8212; without internet access, without being specifically trained on tax data, using only what it can retrieve from IRS publications. The quality of the answer depends entirely on the quality of what the system retrieves. The technical term for what happens when retrieval goes wrong is &#8220;hallucination&#8221; or confident nonsense. I am trying to avoid confident nonsense about my taxes.</p><p>These are the longest poles in the tent. I&#8217;m budgeting more time for both than the plan suggests, which means I&#8217;m already diverging from the plan&#8230; see what I meant!</p><h5>Front-Load, Fail Fast, Dive Deep</h5><p>Here&#8217;s my actual approach, which differs from the official plan.</p><p>I&#8217;m front-loading the early phases to get a working end-to-end flow as quickly as possible &#8212; even if every individual component is rough. The goal is to feel what it&#8217;s like to ask the system a real question and get a real answer before I&#8217;ve perfected any single piece.</p><p>The plan says set up RAG before touching the documents. I feel like going straight to the documents first &#8212; because that&#8217;s where I expect the most resistance, and I&#8217;d rather find out early.</p><p>Two phases at the end of the plan &#8212; privacy and testing &#8212; are probably not phases at all. For this project, Privacy is front and center, gotta bake it in from the start or it isn&#8217;t there. Testing is the same (only it&#8217;s not specific for this project, for everything in life, &#8220;Bulid a little bit, Test a little bit.&#8221;)&#8230; Both will likely dissolve into every other phase rather than arriving as their own moment. But they&#8217;re on the plan because they deserve to be seen, and we&#8217;re not spending any more time tinkering with the plan.</p><p>That said, the back half of the timeline is where the real work lives &#8212; making each component actually reliable, handling the edge cases, closing the gap between &#8220;works on my test document&#8221; and &#8220;works on my real W-2&#8221; which, as it turns out, is printed five times side by side on a single page.</p><h5>The Long View</h5><p>At the far end of all this &#8212; if things go better than I&#8217;m currently expecting, which they may or may not &#8212; the system generates a completed, ready-to-file tax form. One I can upload directly. No accountant. No TurboTax. No data leaving my machine.</p><p>To be honest, that feels like a long stretch from where I&#8217;m standing today. </p><p>But I&#8217;ve learned not to pre-emptively cap ambition on projects that haven&#8217;t started yet. The plan doesn&#8217;t know how far this goes. And I&#8217;ve been wrong about ceilings before &#8212; occasionally in the right direction.</p><h5>Next</h5><p>The machine is live. The environment is set up. From here to there, From there to here, Funny things are everywhere. The fun is about to get funnier&#8230; </p><div><hr></div><p><em>I. Thinking on strategy, innovation, and philosophy &#8212; for people who think seriously about how to build things and make decisions.</em></p><p><em>This is Part 3 of an ongoing series on building a private, local AI tax assistant &#8212; one hour a week, on consumer hardware, without sending financial data anywhere. </em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/building-a-private-ai-tax-assistant?r=77z576">Part 1: Building a Private AI Tax Assistant: In public, on a MacBook!</a></em></p><p><em><a href="https://www.marginnotes.indranilsaha.net/p/the-infrastructure-tax?r=77z576">Part 2: The Infrastructure Tax</a></em></p><p><em>If you&#8217;re building something similar or have any questions/ideas to share, I&#8217;d love to hear from you. Cheers!</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.marginnotes.indranilsaha.net/p/the-blueprint?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.marginnotes.indranilsaha.net/p/the-blueprint?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.marginnotes.indranilsaha.net/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Dignity]]></title><description><![CDATA[...in the face of cheek and sarcasm... a guide to old school]]></description><link>https://www.marginnotes.indranilsaha.net/p/dignity</link><guid isPermaLink="false">https://www.marginnotes.indranilsaha.net/p/dignity</guid><dc:creator><![CDATA[Indranil Saha]]></dc:creator><pubDate>Sat, 21 Mar 2026 19:20:44 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!6v54!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F945bdc7f-cb04-47a9-a65d-c58ec98500af_446x437.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_!6v54!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F945bdc7f-cb04-47a9-a65d-c58ec98500af_446x437.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!6v54!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F945bdc7f-cb04-47a9-a65d-c58ec98500af_446x437.png 424w, https://substackcdn.com/image/fetch/$s_!6v54!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F945bdc7f-cb04-47a9-a65d-c58ec98500af_446x437.png 848w, https://substackcdn.com/image/fetch/$s_!6v54!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F945bdc7f-cb04-47a9-a65d-c58ec98500af_446x437.png 1272w, https://substackcdn.com/image/fetch/$s_!6v54!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F945bdc7f-cb04-47a9-a65d-c58ec98500af_446x437.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!6v54!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F945bdc7f-cb04-47a9-a65d-c58ec98500af_446x437.png" width="446" height="437" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/945bdc7f-cb04-47a9-a65d-c58ec98500af_446x437.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:437,&quot;width&quot;:446,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:214237,&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.marginnotes.indranilsaha.net/i/191699945?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf1faa9e-6c5b-4496-b34b-ea7078a5d37e_500x500.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_!6v54!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F945bdc7f-cb04-47a9-a65d-c58ec98500af_446x437.png 424w, https://substackcdn.com/image/fetch/$s_!6v54!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F945bdc7f-cb04-47a9-a65d-c58ec98500af_446x437.png 848w, https://substackcdn.com/image/fetch/$s_!6v54!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F945bdc7f-cb04-47a9-a65d-c58ec98500af_446x437.png 1272w, https://substackcdn.com/image/fetch/$s_!6v54!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F945bdc7f-cb04-47a9-a65d-c58ec98500af_446x437.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Dignity, once surrendered, is rather difficult to reclaim. It slips away quietly, often in the smallest of moments&#8212;a careless remark, an unnecessary display, a willingness to trade composure for fleeting satisfaction. And though the loss may seem trivial at the time, its absence is felt long after the moment itself has passed.<br>There is a curious illusion, much indulged in modern life, that dignity is a flexible thing&#8212;something one may set aside in a moment of haste or temper, only to retrieve later without consequence.<br>I cannot say I agree.<br><br>One may apologize, of course. One may even be forgiven. But dignity is not restored by apology alone; it is rebuilt slowly, through conduct, restraint, and a renewed respect for oneself and others.<br>It is, if you will forgive the expression, rather like fine china&#8212;perfectly serviceable when intact, but once cracked, never quite the same again, however carefully mended.<br>And so, I have always believed it wiser to guard one&#8217;s dignity with vigilance. For while it may not announce its departure, it is sorely missed when gone&#8212;and exceedingly difficult to persuade back.<br><br>This thought began with this week&#8217;s geopolitical events, but&#8212;like many such reflections&#8212;found its way back to work. In the workplace, one often encounters sarcasm or clever remarks&#8212;moments that invite a response, yet test one&#8217;s ability to maintain both dignity and restraint. I find myself thinking about such things from time to time (a childhood full of such training is to blame primarily). Here are some lines that may come in handy (tried and tested):<br><br>- <s>Gentle </s>deflection: One must be grateful that wit still flourishes, even when good judgement does not.<br><br>- Mildly wounded: I had hoped for a more charitable interpretation&#8212;but perhaps I ask too much.<br><br>- Firm but civil: If there is a point to be made, I should prefer it delivered plainly rather than dressed in cleverness.<br><br>- Dry, understated: We must all make our contributions, however&#8230; distinctive.<br><br>- Respectful: I wonder if the situation may be rather more nuanced than it appears.<br><br>A thought worth comparing notes on.</p>]]></content:encoded></item><item><title><![CDATA[The Infrastructure Tax]]></title><description><![CDATA[or how to buy a refurb Mac Pro and overthink cost allocation...]]></description><link>https://www.marginnotes.indranilsaha.net/p/the-infrastructure-tax</link><guid isPermaLink="false">https://www.marginnotes.indranilsaha.net/p/the-infrastructure-tax</guid><dc:creator><![CDATA[Indranil Saha]]></dc:creator><pubDate>Sat, 21 Mar 2026 18:41:04 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!CZ0p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66dac8bc-f601-4122-9bfa-2382b4f59848_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Two weeks ago, I set out to build a privacy-first AI system. But before any of the interesting stuff could begin, I ran into the good old infrastructure question. </p><p><strong>My 2016 MacBook Air</strong></p><p>The machine I planned to use for this project was, in technical terms, cooked.</p><p>Not just slow &#8212; it was existentially compromised. It couldn&#8217;t run the latest macOS, which meant it couldn&#8217;t receive security updates, which meant I was planning to build a <em>privacy-first</em> AI system on a machine that was itself a security liability. The irony was not lost on me.</p><p>The laptop wasn&#8217;t really a project expense. It was a deferred necessity that the project finally forced me to confront. Any CFO will tell you when you&#8217;re allocating costs, be honest about what&#8217;s <em>actually</em> driving the spend. The project, in this case, gets only partial credit.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CZ0p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66dac8bc-f601-4122-9bfa-2382b4f59848_500x500.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CZ0p!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66dac8bc-f601-4122-9bfa-2382b4f59848_500x500.png 424w, https://substackcdn.com/image/fetch/$s_!CZ0p!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66dac8bc-f601-4122-9bfa-2382b4f59848_500x500.png 848w, https://substackcdn.com/image/fetch/$s_!CZ0p!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66dac8bc-f601-4122-9bfa-2382b4f59848_500x500.png 1272w, https://substackcdn.com/image/fetch/$s_!CZ0p!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66dac8bc-f601-4122-9bfa-2382b4f59848_500x500.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CZ0p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66dac8bc-f601-4122-9bfa-2382b4f59848_500x500.png" width="500" height="500" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/66dac8bc-f601-4122-9bfa-2382b4f59848_500x500.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:500,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:103727,&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://sahaindranil.substack.com/i/191684008?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66dac8bc-f601-4122-9bfa-2382b4f59848_500x500.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_!CZ0p!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66dac8bc-f601-4122-9bfa-2382b4f59848_500x500.png 424w, https://substackcdn.com/image/fetch/$s_!CZ0p!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66dac8bc-f601-4122-9bfa-2382b4f59848_500x500.png 848w, https://substackcdn.com/image/fetch/$s_!CZ0p!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66dac8bc-f601-4122-9bfa-2382b4f59848_500x500.png 1272w, https://substackcdn.com/image/fetch/$s_!CZ0p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66dac8bc-f601-4122-9bfa-2382b4f59848_500x500.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Buying only what you need</strong></p><p>I did not want to buy a new Mac. Because I didn&#8217;t <em>need</em> one. </p><p>New MacBooks are extraordinary machines. They&#8217;re also priced for people who either expense them or derive some psychological satisfaction from unboxing theater. I am neither.</p><p>What I needed was a machine capable of running local LLMs &#8212; specifically something with Apple Silicon, at least 16GB of unified memory, and enough storage to not make me anxious. A refurbished MacBook Pro M3 checked every box at roughly half the price of new. Running a local language model has specific hardware requirements that rule out some machines. If you want to understand why 16GB of unified memory matters more than the chip generation, that's in the appendix.</p><p>The math wasn&#8217;t complicated. The harder part was trusting the process &#8212; because buying a refurb computer from a marketplace you&#8217;ve never used, for a machine you&#8217;ve never owned, based on specs you&#8217;re partly inferring, requires something most consumer decisions don&#8217;t: <em>actual due diligence.</em></p><p><strong>Due diligence</strong></p><p>I&#8217;ve noticed this weird thing, people who would never skip steps on a business decision routinely buy technology the way they buy produce &#8212; by feel, and with no recourse plan. A $50,000 business decision gets a due diligence checklist, a legal review, and a week of scrutiny &#8212; as it should. But an $1000 laptop &#8212; which you will use for eight hours a day, every day, for the next four years &#8212; gets a glance and a gut feeling.</p><p>I did not do that.</p><p>I researched Back Market&#8217;s reliability track record, read refurb grading standards, developed (&#8230;as in, had AI do it) an inspection protocol covering battery health, hardware specs, physical condition, port functionality, and built-in diagnostics. And, I documented everything&#8230; photographically.</p><p>The actual thing took about forty minutes. The machine arrived. Battery cycle count: under 100. Specs: exactly as listed. Condition: better than &#8220;Fair&#8221; suggested. Result: a fully capable M3 MacBook Pro for the price of a mid-tier Windows machine.</p><p>Forty minutes on a decision that shapes every working hour for the next four (maybe, ten) years seems, on reflection, like the bare minimum.</p><p>The inspection checklist is in the appendix below &#8212; if you&#8217;re ever buying refurb mac, use it.</p><p><strong>What this actually cost </strong></p><p>The laptop was ~50% of new retail. It did cost me my time - roughly 4 hours of research and inspection. And, there&#8217;s obviously the opportunity cost of not starting the project for two weeks.</p><p>But I think the right way to think about this cost is that: a portion belongs to the project, a portion belongs to the long-overdue hardware refresh, and zero of it belongs to any illusion that this was optional. The 2016 machine was a security problem waiting to become another story. The project just forced the timeline.</p><p>Sometimes the infrastructure <em><strong>is</strong></em> the decision.</p><div><hr></div><p><strong>Next up</strong></p><p>The machine was ordered. It hadn&#8217;t arrived yet&#8230;</p><p>Which left me with a week and nothing to do but think &#8212; about architecture, sequencing, and whether this project was actually as straightforward as I&#8217;d convinced myself it was. Well, waiting is underrated. The plan that came out is worth its own post. Until next time&#8230;</p><div><hr></div><p><em>If you&#8217;re following along and want the full hardware inspection checklist &#8212; what to check, what thresholds to use, and when to return immediately &#8212; it&#8217;s in the appendix below.</em></p><div><hr></div><h4>Appendix 1: System requirements for running local LLMs on Mac</h4><p><em>Why this machine, and not just any machine.</em></p><p>Buying a refurb Mac for general use is straightforward. Buying one to run local AI models requires a few additional considerations &#8212; because not all Apple Silicon is created equal for this purpose.</p><p><strong>The minimum bar for this project:</strong></p><p>Requirement Minimum: Chip - Apple Silicon (M1 or later) | M3; Unified Memory  8GB (constrained) | 16GB; Storage: 256GB (tight) | 1TB; macOS: Monterey 12+ | Latest</p><p><strong>Why these numbers matter:</strong></p><p><em>Unified memory</em> is the critical variable. Unlike traditional laptops where CPU and GPU have separate memory pools, Apple Silicon shares one pool across both. Local LLMs load their model weights into this memory &#8212; a 7B parameter model (like Mistral 7B) requires roughly 4&#8211;8GB depending on quantization. At 8GB total, you&#8217;re running the model and your operating system in a constant negotiation for space. At 16GB, you have room to work.</p><p><em>Storage</em> matters more than people expect. A single quantized model can run 4&#8211;8GB. If you plan to experiment with multiple models &#8212; which you will &#8212; 256GB fills up faster than feels reasonable.</p><p><em>The M3 specifically</em> isn&#8217;t required &#8212; an M1 or M2 with 16GB would serve the project equally well, and would likely be cheaper on the refurb market. The M3 was available at the right price point. Don&#8217;t over-optimize for the latest chip generation.</p><p><strong>What won&#8217;t work:</strong></p><ul><li><p>Intel Macs: Ollama technically runs, but performance is poor enough to be discouraging. Not recommended.</p></li><li><p>8GB unified memory: Possible for basic experimentation, but you&#8217;ll feel the ceiling quickly. Acceptable for testing the concept; limiting for sustained use.</p></li><li><p>Pre-Monterey macOS: Several dependencies won&#8217;t install cleanly.</p></li></ul><p><strong>The practical implication:</strong></p><p>If you&#8217;re following this project and considering building something similar, the refurb market for M1/M2 MacBook Pros with 16GB is currently the best value entry point. You don&#8217;t need new. You don&#8217;t need M3. You need Apple Silicon and 16GB.</p><h4>Appendix 2: Inspection checklist</h4><p><em>(The nerdy part. Skip if you trust your instincts. Don&#8217;t trust your instincts.)</em></p><p><strong>Before you do anything:</strong> Photograph everything on arrival. Box, accessories, all sides of the machine. Serial number. This is your evidence if anything goes sideways.</p><p><strong>Battery:</strong> Apple menu &#8594; System Report &#8594; Power. Look for cycle count (under 100 is excellent, under 300 is acceptable), condition (&#8221;Normal&#8221;), and full charge capacity (85%+ of design capacity). Anything worse: negotiate or return.</p><p><strong>Specs verification:</strong> Apple menu &#8594; About This Mac. Confirm chip, RAM, and storage match exactly what you paid for. Screenshot it. Discrepancy = grounds for return, full stop.</p><p><strong>Physical inspection:</strong> Screen on and off (dead pixels, coating wear, bright spots), keyboard and trackpad feel, hinge smoothness, port integrity. Test every port with an actual device &#8212; don&#8217;t assume.</p><p><strong>Diagnostics:</strong> Restart, hold Power until startup options appear, Command+D runs Apple&#8217;s built-in hardware test. &#8220;No issues found&#8221; is the only acceptable result. Optional: CoconutBattery for detailed battery analysis, Blackmagic Disk Speed Test to confirm SSD performance.</p><p><strong>Return policy:</strong> Back Market offers 30-day returns and a 1-year warranty. Keep all packaging until you&#8217;re certain you&#8217;re keeping the machine. Document everything through their platform &#8212; not email, not phone.</p><p>Immediate return triggers: &#8220;Service Recommended&#8221; battery, mismatched specs, screen damage, non-functional ports, non-genuine parts warnings in macOS.</p><div><hr></div><p><em>This is Part 2 of an ongoing series on building a private, local AI tax assistant &#8212; one hour a week, on consumer hardware, without sending your financial data anywhere.</em></p>]]></content:encoded></item><item><title><![CDATA[Building a Private AI Tax Assistant: In public, on a MacBook!]]></title><description><![CDATA[Why on earth? Is It Possible? How Long Will It Take? What Will It Cost?]]></description><link>https://www.marginnotes.indranilsaha.net/p/building-a-private-ai-tax-assistant</link><guid isPermaLink="false">https://www.marginnotes.indranilsaha.net/p/building-a-private-ai-tax-assistant</guid><dc:creator><![CDATA[Indranil Saha]]></dc:creator><pubDate>Tue, 10 Mar 2026 12:39:44 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!mB_o!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef1275c-dbd8-4f4d-9110-c9fe6eaf8412_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>WHY on earth would you want to do that?</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mB_o!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef1275c-dbd8-4f4d-9110-c9fe6eaf8412_500x500.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mB_o!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef1275c-dbd8-4f4d-9110-c9fe6eaf8412_500x500.png 424w, https://substackcdn.com/image/fetch/$s_!mB_o!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef1275c-dbd8-4f4d-9110-c9fe6eaf8412_500x500.png 848w, https://substackcdn.com/image/fetch/$s_!mB_o!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef1275c-dbd8-4f4d-9110-c9fe6eaf8412_500x500.png 1272w, https://substackcdn.com/image/fetch/$s_!mB_o!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef1275c-dbd8-4f4d-9110-c9fe6eaf8412_500x500.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mB_o!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef1275c-dbd8-4f4d-9110-c9fe6eaf8412_500x500.png" width="500" height="500" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/def1275c-dbd8-4f4d-9110-c9fe6eaf8412_500x500.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:500,&quot;width&quot;:500,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:26558,&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://sahaindranil.substack.com/i/190500692?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef1275c-dbd8-4f4d-9110-c9fe6eaf8412_500x500.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_!mB_o!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef1275c-dbd8-4f4d-9110-c9fe6eaf8412_500x500.png 424w, https://substackcdn.com/image/fetch/$s_!mB_o!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef1275c-dbd8-4f4d-9110-c9fe6eaf8412_500x500.png 848w, https://substackcdn.com/image/fetch/$s_!mB_o!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef1275c-dbd8-4f4d-9110-c9fe6eaf8412_500x500.png 1272w, https://substackcdn.com/image/fetch/$s_!mB_o!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdef1275c-dbd8-4f4d-9110-c9fe6eaf8412_500x500.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>AI is consuming everything around us&#8202;&#8212;&#8202; and on the face of it, it smells like progress, and most probably it is, but, and here it comes, the ubiquitous BUT, it bothers me how quickly we are giving up on our personal data to gain a little bit of advantage. So, I set out on a personal project: to build a private AI assistant that can help with tax preparation using my own financial data&#8202;&#8212;&#8202;without sharing that data with public models or the cloud.</p><p>In this day and age of ever evolving cyber attacks, which even the biggest companies around the world struggle to prevent, sharing my sensitive data with outside consultants makes me quite uneasy. One could argue &#8220;why do you think you are special?&#8221;, and &#8220;you are just not interesting enough&#8202;&#8212;&#8202;no hacker worth the salt will be interested in your information specifically&#8221;.&nbsp;</p><p>Still combine that with the thought &#8220;why do I pay tax accountants to fill up a form that I myself can fill up?&#8221; and &#8220;I have a pretty good B-school degree, majoring in, wait for it, Finance, I should be able to do this&#8221;. But I don&#8217;t stay up to date with tax laws and sometimes work gets crazy around tax days (just Murphy&#8217;s Law!). So I need help. And when I read about agentic AI, it seemed like the perfect solution. BUT&#8230; if I use just any available agent, my data still goes out into public models, and I don&#8217;t trust these startup-y things to keep my data safe.</p><p>Also, there is the adventure: Can I really do this on a MacBook&nbsp;, with just one hour per week, and without breaking the bank?</p><p>Just for background, I&#8217;m not a techie by any stretch of imagination. I used to be one a very, very, long time back. But not any longer. I just like to dabble and tinker around with the proverbial &#8220;what if&#8230;&#8221; That, plus heard all the buzz about machines coding, and thought, so why not take this idea out for a ride and see how far it goes&#8230;</p><p>Here&#8217;s the breakdown of what I&#8217;m learning, planning, and building.</p><h4>The Idea</h4><p>I&#8217;m building a tax assistant that can:</p><p>- Read my W-2s, 1099s, and expense receipts&nbsp;</p><p>- Reason about eligibility for deductions or credits&nbsp;</p><p>- Give advice based on IRS rules&nbsp;</p><p>- Do it entirely offline, keeping my financial data private</p><h4>Can I Build This on a&nbsp;MacBook?</h4><p>Surprisingly&#8202;&#8212;&#8202;yes.</p><p>Tools like Ollama, LlamaIndex, and Streamlit make it possible to run local models like Mistral 7B or LLaMA 3 directly on my MacBook. By using quantized models, I&#8217;m able to get decent performance even without a GPU.</p><h4>What If I Only Have 1 Hour per&nbsp;Week?</h4><p>I mapped out a 6-month roadmap using just 1 hour per week, breaking it down into phases:</p><p>- Month 1: Setup local LLM + basic Q&amp;A&nbsp;</p><p>- Month 2: Add PDF parsing (W-2s, 1099s)&nbsp;</p><p>- Month 3&#8211;4: Apply simple tax logic (deduction thresholds, filing status rules)&nbsp;</p><p>- Month 5&#8211;6: Build a simple RAG pipeline and Streamlit UI&nbsp;</p><p>With focus and consistent micro-progress, it&#8217;s totally doable.</p><h4>What Will It&nbsp;Cost?</h4><p>So far, almost everything is open source. My only real &#8220;costs&#8221; are:</p><p>- Local compute power (already owned)&nbsp;</p><p>- Electricity (~$10/month max)&nbsp;</p><p>- Time (the real investment)&nbsp;</p><p>If I were to add GPU cloud compute, that might add $50&#8211;$200/month, but so far I&#8217;m staying local.</p><h4>What&#8217;s Next?</h4><p>I&#8217;m documenting the full journey on my substack (here), and posting monthly updates on LinkedIn as I go.</p><p>Coming soon (Note to self: this is a plan that is sure to be disrupted&#8230; being a planner, I know that. It&#8217;s the only thing one can say about plans with some bit of certainty, but nevertheless&#8230; here it goes):</p><p>- &#8220;How I Got an AI Model to Parse My W-2s&#8221;&nbsp;</p><p>- &#8220;Can AI Actually Understand Tax Law?&#8221;&nbsp;</p><p>If you&#8217;re building something similar or have any questions/ideas to share, I&#8217;d love to hear from you. Cheers!</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.marginnotes.indranilsaha.net/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading this article! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[First post]]></title><description><![CDATA[The Road]]></description><link>https://www.marginnotes.indranilsaha.net/p/first-post</link><guid isPermaLink="false">https://www.marginnotes.indranilsaha.net/p/first-post</guid><dc:creator><![CDATA[Indranil Saha]]></dc:creator><pubDate>Wed, 14 Jan 2026 03:38:53 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!C_d6!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff17c8f27-7bf4-41c5-b52a-56eecdab5bfc_342x342.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Any road followed precisely to its end leads precisely nowhere. Climb the mountain just a little bit to test that it&#8217;s a mountain. From the top of the mountain, you cannot see the mountain. Frank Herbert, Dune.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.marginnotes.indranilsaha.net/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Indranil's Substack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Coming soon]]></title><description><![CDATA[This is Margin Notes.]]></description><link>https://www.marginnotes.indranilsaha.net/p/coming-soon</link><guid isPermaLink="false">https://www.marginnotes.indranilsaha.net/p/coming-soon</guid><dc:creator><![CDATA[Indranil Saha]]></dc:creator><pubDate>Wed, 14 Jan 2026 03:04:17 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!C_d6!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff17c8f27-7bf4-41c5-b52a-56eecdab5bfc_342x342.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This is Margin Notes.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.marginnotes.indranilsaha.net/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.marginnotes.indranilsaha.net/subscribe?"><span>Subscribe now</span></a></p>]]></content:encoded></item></channel></rss>