Skip to content

Blog

Technical deep-dives into AI engineering, full-stack architecture, and lessons learned.

One email per post. No newsletter, no marketing, and your address is never shared. Unsubscribe in one click from any of them.

Series

1 series

13-Part Series

How to Architect an Enterprise AI System (And Why the Engineer Still Matters)

Every post in this series is a decision I made that no model would have made on its own. Not because the model is bad , because the model doesn't know what it doesn't know.

13 of 13 parts published

Read the series
0The Day My AI Forgot Everything (So I Built a Context-Continuity Inference Stack)
1I Stopped Letting Emails Poison My Extractor: The Pre-LLM Gate That Made the Rest of the Pipeline Reliable
2I Turned Temperature Up to Save My Extractions: The 3‑Node LangGraph That Trades Variance for Truth
10 more parts planned

Posts

38 posts

Notification Adjudication in My Ops Intelligence Agent: Canonical Events, Cheap Arbitration, and a Sender That Refuses to Spam
ml-systemsobservabilitypythonanomaly-detectionincident-response

Notification Adjudication in My Ops Intelligence Agent: Canonical Events, Cheap Arbitration, and a Sender That Refuses to Spam

I built an Ops Intelligence Agent alongside a recruitment platform Operations Dashboard, to turn a noisy real-time event stream into a small number of Microsoft Teams notifications worth reading. The interesting part isn’t “sending a webhook.” It’s adjudication: normalizing heterogeneous telemetry, scoring it fast, collapsing duplicates, and dispatching defensively so the first alert lands quickly without setting off an alert storm.

Daniel Anthony Romitelli Jr. · July 28, 2026

A Cache Key Is an Equivalence Relation
cachingtypescriptvideo-generationsystems-design

A Cache Key Is an Equivalence Relation

How I designed the video generation pipeline’s content-addressable lookup so retries reuse the same artifact while different model routes, seeds, and inputs get separate results.

Daniel Anthony Romitelli Jr. · July 28, 2026

Defensive Multi‑Agent Scoring: How I Made LLM Reviews Clamp, Stream, and Fail Loudly
LLMsTypeScriptMulti-agent systemsReliability engineeringEvaluation

Defensive Multi‑Agent Scoring: How I Made LLM Reviews Clamp, Stream, and Fail Loudly

A weighted average is the easy part. The hard part is what a review stage does when one judge returns truncated JSON, a half-filled object, or nothing at all. Here's how I made failure explicit with a sentinel review, kept partial streams observable for debugging, and how aggregation is meant to keep malformed output from poisoning the score.

Daniel Anthony Romitelli Jr. · July 27, 2026

Diversification After Scoring: The Step That Stops My Scene Compiler From Picking Five Paraphrases
rankingdiversityrerankingselectionpromptingpipelines

Diversification After Scoring: The Step That Stops My Scene Compiler From Picking Five Paraphrases

My scene compiler kept returning a "top 5" that was one idea wearing five outfits. The fix sits in the post-scoring selection layer rather than in generation: a diversification pass that runs after scoring (and after any routing or gating decision), penalizes candidates that repeat what's already been picked, then re-ranks before final selection.

Daniel Anthony Romitelli Jr. · July 27, 2026

My RAG Stack for Code Retrieval: pgvector HNSW + Metadata Filters + Reranking (and the Parts I Refuse to Guess About)
ragpgvectorembeddingsretrievaltypescript

My RAG Stack for Code Retrieval: pgvector HNSW + Metadata Filters + Reranking (and the Parts I Refuse to Guess About)

My portfolio repo references a RAG stack for code retrieval: OpenAI embeddings stored in pgvector with HNSW indexing, a cross-encoder rerank pass via BGE, and metadata-filtered search on top. The honest version: the components exist and I can show some of the glue code around retrieval, deduping, and formatting, but what I can point to doesn't include the actual SQL query shapes, the BGE batching code, or the latency-budget enforcement logic. So this post documents exactly what's implemented and names the parts I refuse to guess about.

Daniel Anthony Romitelli Jr. · July 27, 2026

The Startup Gate That Makes a Python App Feel Native
pythonstartupconfigurationdesktop-appopenai

The Startup Gate That Makes a Python App Feel Native

A speech-to-text desktop app whose console entry point refuses to start until `pyaudio`, `keyboard`, and `openai` all import. A walk through the startup gate in `app/yapper.py`, the import order behind it, and the settings path that `app/core/config.py` pins to the app tree instead of the current working directory.

Daniel Anthony Romitelli Jr. · July 27, 2026

Workflow JSON Is Generated Code
n8nworkflow automationcode generationscreen analysispython

Workflow JSON Is Generated Code

Generating n8n automations from screen analysis: the structure lives in typed Python objects, and JSON is only what comes out at the end.

Daniel Anthony Romitelli Jr. · July 27, 2026

Validation Geometry Is Part of the Model
machine-learningtime-seriesfinancial-mllightgbmvalidationcryptoresearch-engineering

Validation Geometry Is Part of the Model

A LightGBM capacity-control baseline for the ICAIF 2026 minute-ceiling paper that rebuilds its labels from parquet, and why label source, stride, purge, and temporal split geometry matter as much as the classifier in time-series ML.

Daniel Anthony Romitelli Jr. · May 31, 2026

Model Failure Is a Time Series
machine-learningtime-seriesmodel-reliabilitytrading-systemscalibration

Model Failure Is a Time Series

Model failure is a time series, so I gave it its own model. CTF watches the primary predictor’s recent uncertainty telemetry, labels correctness without lookahead, holds training and serving to the same feature contract, and hands the execution gate a probability that the next forecast is worth using.

Daniel Anthony Romitelli Jr. · May 28, 2026

Four Vectors, One Record: How I Split Embeddings Before They Hit Search
embeddingssearchredispythonazure-ai-searchproduction-systems

Four Vectors, One Record: How I Split Embeddings Before They Hit Search

One blended embedding per candidate record kept surfacing people whose experience looked vaguely relevant while their skills were wrong, or the reverse. So I split each record into four semantic views and generate the four embeddings in parallel, cached under a stable Redis key. The job layer validates content length and vector dimension before it retries transient failures or routes terminal ones to the DLQ. The record stays one record. It just stops pretending it means one thing.

Daniel Anthony Romitelli Jr. · May 28, 2026

Vector Split by Chunk: Why My Retrieval Stops at the Boundary I Drew
embeddingsragvector-searchsupabasetypescript

Vector Split by Chunk: Why My Retrieval Stops at the Boundary I Drew

I split embeddings by chunk because the retrieval layer needed something finer than a whole-document vector could give me. That one choice runs through the whole pipeline: how text gets chunked, how each slice gets embedded, and the file-path lookup that can pull exact spans back when I need them.

Daniel Anthony Romitelli Jr. · May 27, 2026

Small in Code. Large in Behavior.
ocrtypescripthugging-faceneuroloqai-tutoringtext-recovery

Small in Code. Large in Behavior.

Neuroloq decides whether an attachment is document-like before it decides which OCR mode reads it, and that routing call is the product.

Daniel Anthony Romitelli Jr. · May 27, 2026

Why I Kept Search Scope Inside a Single Supabase RPC
SupabasePostgrespgvectorRAGTypeScript

Why I Kept Search Scope Inside a Single Supabase RPC

Vector search handed back a plausible chunk from the wrong repository, so I made the vector, the scope filter, and the candidate count travel together through one `search_embeddings` RPC. The database applies the metadata predicate inside SQL, and the same contract drives the pgvector HNSW index on the embedding column.

Daniel Anthony Romitelli Jr. · April 16, 2026

The AgentGroupChat Pattern That Keeps the Mapper from Drifting
semantic-kernelazure-foundrypromptflowagent-orchestrationstate-managementworkflow-automation

The AgentGroupChat Pattern That Keeps the Mapper from Drifting

I rebuilt the orchestration in the workflow analyzer SaaS around a narrow AgentGroupChat chain: analyzer, mapper, generator, validator, with state rehydration before the run and persistence after it. It can reject bad structure locally, retry from the right step, and resume from prior history instead of starting blind.

Daniel Anthony Romitelli Jr. · April 16, 2026

Coverage Before Creativity: The RAG Gate That Keeps My Blog Pipeline Honest
RAGSupabaseNext.jsTypeScriptblog pipelineretrievalcodebase indexing

Coverage Before Creativity: The RAG Gate That Keeps My Blog Pipeline Honest

Before the generator writes a paragraph, a three-lane query fan-out, file-path-aware dedupe, a sufficiency threshold, and pinned excerpts decide whether the retrieval covered enough of the repository to deserve a draft. The most useful thing this pipeline does is turn down topics whose evidence set is too thin.

Daniel Anthony Romitelli Jr. · April 14, 2026

The Reward Calibrator That Learns the Shape of Its Own Judgment
typescriptml-systemsscoringoptimizationvideo

The Reward Calibrator That Learns the Shape of Its Own Judgment

I built a reward calibrator that sits above the scoring signals and searches for better weights instead of hand-tuning them by feel. It normalizes each signal, combines them into a composite score, and evaluates candidate weight sets against benchmark data, so the system can measure itself against its own baseline.

Daniel Anthony Romitelli Jr. · April 13, 2026

How I Carve Objects Out of Depth Instead of Texture
computer-visiondepthgeometrynextjsgpu

How I Carve Objects Out of Depth Instead of Texture

Depth segmentation that keeps working with the lights off. The pipeline thresholds a LiDAR depth map, groups connected regions, and tests whether they behave like real planes, so it still returns labeled walls, windows, doors and trim when the RGB frame has nothing left to offer.

Daniel Anthony Romitelli Jr. · March 29, 2026

The Signal-Processing Boundary That Keeps Coaching Useful in Real Time
realtime-systemsaudio-processingwebsocketssignalrpython

The Signal-Processing Boundary That Keeps Coaching Useful in Real Time

The voice path for a recruiting platform, rebuilt as a deterministic streaming pipeline: ACS media frames reach the backend as JSON metadata plus binary PCM, the server holds audio until `session.updated`, resamples 16kHz to 24kHz with `resample_poly`, and streams partial transcripts plus coaching back through SignalR without blocking the live call.

Daniel Anthony Romitelli Jr. · March 29, 2026

Fresh Enough to Render: How I Encode Market-Data Trust in the Cache Layer
typescriptcachingfinancial-dashboardttlarchitecture

Fresh Enough to Render: How I Encode Market-Data Trust in the Cache Layer

In this financial dashboard, freshness is a rule the data layer enforces rather than a guess the UI makes. The interesting part is the TTL split: quotes, intraday history, news, and search each age at their own pace, and the cache decides whether a read is still trustworthy enough to render or should fall through to upstream data.

Daniel Anthony Romitelli Jr. · March 29, 2026

Text in a Frame Is Contamination, Not Decoration
typescriptnextjsocrvideo-processingscene-compilerscoring

Text in a Frame Is Contamination, Not Decoration

Inside `lib/scene-compiler/text-detector.ts`: how I normalize fal.ai and Florence-2 OCR output, classify subtitle and watermark regions by where they sit, and turn text contamination into a normalized router score the scene pipeline can act on.

Daniel Anthony Romitelli Jr. · March 29, 2026

I Gave My Video Generator Scratch Paper: How Think Frames Saved My GPU Budget
aivideogenerationscoringtypescript

I Gave My Video Generator Scratch Paper: How Think Frames Saved My GPU Budget

I added a pre-generation pass that lets my video pipeline explore cheap sketches before it commits to a full-quality keyframe. A small cohort of “think frames” gets scored by the same reward mixer I use elsewhere, the one that weighs several things at once, and the best path is picked by group-relative rank instead of an absolute cutoff.

Daniel Anthony Romitelli Jr. · March 26, 2026

The Boundary That Makes iOS Capture Safe on the Web
nextjscomputer-visioncalibrationpersistencegeometry

The Boundary That Makes iOS Capture Safe on the Web

The web app never has to guess what a segment, bounding box, or measurement means. Geometry gets normalized before it crosses the boundary, and only the version the viewer can trust gets persisted.

Daniel Anthony Romitelli Jr. · March 24, 2026

How I Built a Patient Check-In Kiosk for a Specialty Medical Practice
react-nativeexposupabasehealthcarequeue-management

How I Built a Patient Check-In Kiosk for a Specialty Medical Practice

A multilingual patient check-in kiosk I built for a specialty medical practice after watching the front desk come apart under real pressure. The interesting part isn't the iPad UI. The work sits in the queue engine, the real-time synchronization, and a fallback-heavy notification flow that keeps the room moving when patients, staff, and Wi-Fi are all imperfect at the same time.

Daniel Anthony Romitelli Jr. · March 23, 2026

Firecrawl Part 2: The Confidence Gate That Decides When Bing Gets a Vote
pythondata-qualitypipelinesfeature-flagscrm-systems

Firecrawl Part 2: The Confidence Gate That Decides When Bing Gets a Vote

Part 1 laid out the shape of my company research chain: Firecrawl first, Bing second, and a tracer so the whole thing stays legible when the web lies. Part 2 is about the decision boundary itself, the exact "needs improvement" gate that determines when I augment Firecrawl with Bing, and why I made that gate intentionally blunt.

Daniel Anthony Romitelli Jr. · March 13, 2026

Tracing an Extraction Pipeline Like a Ledger: Trace Nodes, DLQ Boundaries, and Replayable Failures
observabilitytracingpythonreliabilityworkflows

Tracing an Extraction Pipeline Like a Ledger: Trace Nodes, DLQ Boundaries, and Replayable Failures

I built a step-by-step extraction tracer so I can answer one question under pressure: what exactly happened inside this run? Here is the node schema, where the hooks sit in the extraction pipeline, how retries and DLQ boundaries show up in the trace, and how I replay a trace locally to reproduce and fix failures, without guessing.

Daniel Anthony Romitelli Jr. · March 12, 2026

Per‑Region PBR From One Photo: The Cropping Trick That Stops RGB‑X From Bleeding Materials Across Boundaries
pbrdiffusionsegmentationthreejsrendering

Per‑Region PBR From One Photo: The Cropping Trick That Stops RGB‑X From Bleeding Materials Across Boundaries

A wall’s “roughness” spilled into a roof the first time I ran RGB‑X on a full-frame photo, and it made the whole result feel fake. No new model fixed it. The fix was an engineering decision: run diffusion-based PBR decomposition per architectural region, cropped from SAM3 masks with padding and U‑Net-friendly sizing, plus a procedural fallback so the viewer never goes dark when the GPU pipeline isn’t available.

Daniel Anthony Romitelli Jr. · March 11, 2026

My Three‑Phase Parallel Orchestrator: Typed Results, Exception‑Proof Phases, and a Rollout That Never Flaps
pythonorchestrationvoicelatencyreliability

My Three‑Phase Parallel Orchestrator: Typed Results, Exception‑Proof Phases, and a Rollout That Never Flaps

I replaced a ~3,500ms linear voice pipeline with a parallel three-phase orchestrator that targets <600ms P95 by treating "agents" like a compilation pipeline: Phase 1 produces typed intermediate results, Phase 2 consumes them as precomputed search inputs, and Phase 3 formats the response. The trick isn't asyncio.gather. It's typed contracts plus a fallback cascade, so every phase keeps moving even when a component fails.

Daniel Anthony Romitelli Jr. · March 11, 2026

Caching LLM Extractions Without Lying: Conformal Gates + a Reasoning Budget Allocator
cachingconformal-predictioncost-engineeringllm-systemspython

Caching LLM Extractions Without Lying: Conformal Gates + a Reasoning Budget Allocator

My email extraction pipeline kept burning money re-deriving fields it already had. The bug wasn't a missing cache, it was caching without a validity model. What replaced it: a confidence-gated cache that decides reuse vs partial rebuild using conformal prediction, plus a reasoning budget allocator that spends compute per span only when quality is genuinely below target.

Daniel Anthony Romitelli Jr. · March 11, 2026

The Closed‑Loop Consistency Trick: Keeping Scene 12 Faithful to Scene 1 Without Global Memory
ml-systemsvideocontrol-systemstypescriptpipelines

The Closed‑Loop Consistency Trick: Keeping Scene 12 Faithful to Scene 1 Without Global Memory

Long-form visual consistency looks like a memory problem, so I assumed it needed a growing story-so-far context hauled through every generation step. Scenematic works the other way around: propagate what must persist, measure what actually persisted, then correct what didn't, locally, with a periodic re-anchor that keeps small errors from compounding across a 20-scene run.

Daniel Anthony Romitelli Jr. · March 11, 2026

Search That Refuses to Think: The Pattern‑First Query Parser I Use for Fast Intent + Entity Extraction
searchnlpvoice-assistantsengineeringpythonlatencyinformation-retrieval

Search That Refuses to Think: The Pattern‑First Query Parser I Use for Fast Intent + Entity Extraction

In my voice-first operations product, I stopped treating “search” as retrieval and started treating it as compilation: speech → intent → entities → an executable query plan. The query parser came after an LLM-first attempt produced latency spikes and inconsistent structure. Here is the intent contract, the rule engine (compiled regex + token maps), the entity extractors (locations, titles, numeric limits), the caching strategy, ambiguity detection, and the benchmark I used to check latency at scale.

Daniel Anthony Romitelli Jr. · March 5, 2026

Multi‑Vector Embeddings in Production: Typed Vectors, Cache Keys, and a Generator That Refuses Poison Records
pythonembeddingscachingsearchdata-pipelines

Multi‑Vector Embeddings in Production: Typed Vectors, Cache Keys, and a Generator That Refuses Poison Records

An embedding pipeline for our recruitment platform that represents each record as four typed vectors instead of one pooled blob: profile, experience, skills, and general. Most of the engineering lives around the model call rather than in it: cache key design that includes model IDs, idempotent storage of multi-vector blobs, retry logic with backoff, and a DLQ path that keeps bad records from stalling reindex/backfill.

Daniel Anthony Romitelli Jr. · March 5, 2026

MR‑GRPO in Practice: The Reward Mixer That Stops CLIP From Lying to Your Scene Compiler
ml-systemsrankingnormalizationtypescriptprompt-engineering

MR‑GRPO in Practice: The Reward Mixer That Stops CLIP From Lying to Your Scene Compiler

CLIP-only ranking chose candidates that looked right in embedding space and still broke continuity, so I replaced it with a multi-signal reward mixer. It scores each candidate across independent reward signals, normalizes them group-relatively (GRPO-style), then composes a final score, skipping null signals and re-normalizing the weights over the heads that remain. Treating "unknown" as a first-class state instead of faking certainty makes the ranking harder to game and easier to debug.

Daniel Anthony Romitelli Jr. · March 4, 2026

My Voice Router That Refuses to Think: Pattern‑First Multi‑Agent Orchestration for Sub‑Second Latency
voicemulti-agentroutinglatencypythonarchitectureobservability

My Voice Router That Refuses to Think: Pattern‑First Multi‑Agent Orchestration for Sub‑Second Latency

I rebuilt my voice agent's orchestration around a stubborn rule: don't spend an LLM call on a problem a regex can solve. What forced the change was a real latency incident caused by model-first routing, where obvious intents still waited on classification and p95 crossed the point where a voice turn starts to feel broken. The fix was a pattern-first RouterAgent with an LLM fallback reserved for genuine ambiguity, an orchestrator that coordinates instead of improvising, and a voice integration layer that enforces formatting, timeouts, and stable contracts. This post walks through the architecture, the post-mortem, the measurement methodology behind the latency numbers, and a runnable reference implementation of the router, the orchestrator, and the voice processor.

Daniel Anthony Romitelli Jr. · March 4, 2026

Multi‑Agent Firecrawl Research: My Fallback Chain That Refuses to Pretend It Knows the Company
pythonweb-researchfirecrawlbing-searchdata-quality

Multi‑Agent Firecrawl Research: My Fallback Chain That Refuses to Pretend It Knows the Company

A company research pipeline that treats enrichment like an investigation: start with the best source, log every step, and hand off gracefully to a second one when the web lies or stops answering. The idea underneath is separating "fetching" from "deciding," so Firecrawl, the Bing fallback, and the tracer each do their jobs without smearing uncertainty across the output.

Daniel Anthony Romitelli Jr. · March 3, 2026

Cache-First Geocoding with Azure Maps: Key Topology, TTL Heuristics, and Quota Smoothing
pythonfastapiazure-mapscachinggeocodinghttpxrate-limitingobservabilitylanggraph

Cache-First Geocoding with Azure Maps: Key Topology, TTL Heuristics, and Quota Smoothing

Our Azure Maps integration is cache-first because the first real failures were wasted calls rather than bad results. The LangGraph enrichment flow picked up address-vs-POI routing and a hard short-circuit when Firecrawl had already produced city/state, and that pushed the whole thing toward treating geocoding as a budgeted, deduplicated service: stable cache keys (geohash plus query normalization), TTLs that reflect how volatile a location answer is, and quota smoothing with backoff so bursts don't turn into 429 storms. The same discipline runs through the advisor-enrichment worker (credits_used, max_credits, feature flags), where cost and reliability are explicit outputs of the pipeline.

Daniel Anthony Romitelli Jr. · March 3, 2026

Adaptive Keyframe Sampling: How I Spend a Frame Budget Like It’s Cash
video-understandingcomputer-visionmultimodalcost-engineeringcloud-runnextjswebhookshmac

Adaptive Keyframe Sampling: How I Spend a Frame Budget Like It’s Cash

Uniform frame sampling made my screen-workflow analyzer expensive and blind to short UI bursts at the same time. I replaced it with an adaptive sampler that scores cheap visual change, segments the timeline, and allocates a fixed keyframe budget with guardrails. Here's the failure that forced the rewrite, the production integration seam it sits in (Cloud Run to webhook to Next.js), and complete runnable code for scoring, segmentation, allocation, and frame extraction, plus proper HMAC verification on the webhook receiver.

Daniel Anthony Romitelli Jr. · March 2, 2026

I got SAM3 video tracking wrong: the session wasn’t the problem, my reprojection was
computer-visionvideoinferencetrackingdebugging

I got SAM3 video tracking wrong: the session wasn’t the problem, my reprojection was

My GPU server streams SAM3 masks frame by frame. When the masks flickered and the labels churned, I blamed "model instability." The real culprit was how I handled session reuse, mixed-resolution frames, and reprojecting outputs back to each frame's original size. Here's the inference session lifecycle, the streaming output shape assumptions (batch size 1), and the debug hooks I added, like /segment/debug-model, to make flicker diagnosable instead of mystical.

Daniel Anthony Romitelli Jr. · February 28, 2026

Turning CRM Audit Noise into a Transition Graph: Normalizing Events, Sessionizing Creation Bursts, and Extracting Time‑Weighted State Edges
data-engineeringprocess-miningcrmobservabilitypython

Turning CRM Audit Noise into a Transition Graph: Normalizing Events, Sessionizing Creation Bursts, and Extracting Time‑Weighted State Edges

A pipeline for rebuilding deal timelines out of messy webhook and API audit trails: normalize heterogeneous events into one shape, split them into deal-centric sessions, compress those into canonical state paths, then extract a transition graph whose edges carry both counts and observed durations. Missing and out-of-order events get handled without pretending the data is perfect.

Daniel Anthony Romitelli Jr. · February 28, 2026

Audiobook

New

Enterprise AI Architecture

How to Architect an Enterprise AI System (And Why the Engineer Still Matters)

2h 16m · 13 chapters

The full series narrated as a 2h 16m audiobook. Listen on your preferred platform.