MADI: An Intent-Driven AI Agent for Marketing Analytics
Featured Project

MADI: An Intent-Driven AI Agent for Marketing Analytics

MADI (Marketing Analytics & Data Intelligence) is Click Here Digital's first proprietary AI agent. I architected it around an intent-driven workflow that uses a language model as a router rather than a calculator—turning a plain-English question into deterministic, auditable analytics across seven marketing channels in seconds instead of hours.

Technologies Used

Domain-Driven DesignFiberGoGoogle Sheets APIHelmKubernetesModel Context Protocoln8nOAuth2OpenRouterPostgreSQLPrometheusRedissqlc

Overview

MADI—Marketing Analytics & Data Intelligence—is Click Here Digital's first proprietary AI agent, built into the ClickIQ platform. An account manager asks a question in plain English: "How is Acme Corp performing this month across all channels compared to last month?" MADI returns a complete cross-channel performance report, with period-over-period comparisons and recommendations, in under thirty seconds. The same report previously took an analyst about three hours to assemble by hand.

The interesting part of MADI is not that it uses a language model. It is where the language model is not allowed to go.

The Problem With Letting an LLM Do the Math

Marketing data demands absolute precision. If a client asks for last month's ad spend, $125,450 is the only acceptable answer. "$125k-ish," or a number a model invented because it misread a column name, is not a wrong answer—it is a liability.

Large language models are exceptional reasoning engines and unreliable calculators. They are not databases. Any architecture that asks a model to read raw campaign rows and total them is one hallucinated digit away from an incorrect client invoice conversation.

So MADI's core design constraint became: the model may decide what to compute, but it may never compute it.

The Solution: Intent as a Contract

The intent-driven workflow splits the system at a hard boundary. Natural language goes in one side, a structured, validated object comes out, and everything downstream of that object is ordinary deterministic Go.

  Plain English question
          │
          ▼
  ┌───────────────────┐
  │   LLM  ·  ROUTER  │   Classifies intent, resolves dates,
  │  (non-authoritative)│  matches clients, expands channels
  └─────────┬─────────┘
            │  structured intents (validated, typed)
            ▼
  ┌───────────────────┐
  │  DETERMINISTIC GO │   Parameterized SQL, fixed formulas,
  │   (authoritative) │   no model in the arithmetic path
  └─────────┬─────────┘
            ▼
     Auditable report

The LLM's entire job is translation:

  • Intent classification — is this a performance question, a task question, or neither?
  • Date intelligence — "this month" becomes an explicit YYYY-MM-DD range, validated against what data actually exists.
  • Client resolution — a fuzzy name is matched against the client database with a confidence score, and anything under a 60% threshold is refused rather than guessed.
  • Channel extraction — "Google Ads" maps to Search, "all channels" fans out to all seven tactics.

The output is a list of typed intent objects. "How is Acme doing this month vs. last month?" becomes fourteen of them—seven channels across two periods. From that point forward there is no model in the loop. Every number is produced by parameterized SQL and fixed formulas, which means every number is reproducible and every number can be traced back to a row.

Where the Agent Lives: MCP Behind an n8n Workflow

MADI is not a chatbot with a bespoke UI. It is a Model Context Protocol server—a Go service that exposes its capabilities as MCP tools—sitting behind an n8n workflow that owns the conversation.

That split is deliberate, and it is the decision I would defend hardest.

┌──────────────────────────────────────────────┐
│  n8n WORKFLOW  ·  the conversation           │
│  • chat UI + session/user identity           │
│  • message history & feedback capture        │
│  • agent loop: which tool, in what order     │
└───────────────────┬──────────────────────────┘
                    │  OAuth2 client credentials
                    │  → POST /token  → Bearer
                    │  MCP over HTTP + SSE
                    ▼
┌──────────────────────────────────────────────┐
│  MADI MCP SERVER  ·  the capability          │
│                                              │
│   date_context          get_clients          │
│   search_clients        generate_intents     │
│   process_intents       handle_intent_request│
└───────────────────┬──────────────────────────┘
                    ▼
     operational DB  +  data warehouse

n8n holds the parts that change weekly—prompt wording, conversation flow, which tool to reach for, how a response is phrased, where the transcript is stored. The Go server holds the parts that must never drift—authentication, client resolution, SQL, metric math, channel strategies.

The practical payoff: a marketer's request to reword a response, or an operator's request to reorder the agent's steps, is a change in an n8n canvas. It does not require a Go release, a container build, or a Kubernetes rollout. Meanwhile nothing in that canvas can reach past the MCP tool boundary and change how a number is calculated.

Because MADI speaks MCP rather than a private HTTP contract, the same server is reachable from any MCP client. n8n is the production surface today; Claude Desktop works against the identical tool definitions for debugging.

The tools

ToolResponsibility
date_contextGrounds the model in the current date so relative ranges resolve correctly
get_clients / search_clientsConfidence-scored client lookup against the operational database
generate_intentsTurns a question into validated, typed intent objects
process_intentsExecutes intents in parallel batches and computes metrics
handle_intent_requestSingle-call orchestration for the common end-to-end path

Architecture

The server is Go 1.26 on Fiber, organized under strict Domain-Driven Design boundaries: the domain layer imports nothing, the application layer never imports infrastructure, and only infrastructure depends inward. That rule is enforced in CI rather than in code review, because a boundary that is merely encouraged is a boundary that erodes.

  • Channel strategies — each of the seven channels (Search, Social, Display, Video, CTV, Audio, SEO) has its own strategy implementation with platform-specific data sources, metric definitions, and recommendation rules. Adding a channel means adding a strategy, not editing a switch statement.
  • Multi-database topology — client and accounting data live on the operational instance; campaign performance lives in a separate data warehouse. Analytics workloads cannot degrade the systems that run the business.
  • Parallel execution — intents are processed in concurrent batches with a configurable ceiling, capped at 50 intents per query as a safety limit.
  • Stateless pods, shared session state — OAuth2 tokens and MCP sessions live in Redis, so the service scales horizontally behind a round-robin Kubernetes service with no sticky sessions. The application validates this at startup and refuses to boot misconfigured.
  • Observability — Prometheus metrics and Sentry error tracking throughout.
  • Exports — reports render to JSON, styled Excel workbooks, or Google Sheets with automatic sharing.

Testing an AI System Without Paying for It

The hardest engineering problem here was not the architecture. It was proving the architecture keeps working.

An agent whose first stage is a live model call is, by default, untestable in CI: the calls cost money, the latency is unpredictable, and the outputs are non-deterministic. So the intent pipeline uses a fixture record-and-replay pattern. Test scenarios are declared in YAML, recorded once against the real model, and replayed in CI in a few seconds with no network calls and no spend. CI fails if a test lacks a recorded fixture, which makes it impossible to accidentally introduce a test that bills on every push.

That is what lets the project hold an 80% coverage floor on an AI-driven system, and it is why "the model changed its mind" shows up as a failing test rather than as a client-facing error.

My Role

I architected and built MADI. The first iteration was designed in partnership with IntellegenAI, a generative AI consulting firm led by Gilberto Mizrahi—they brought the generative AI expertise, I brought the deep knowledge of our data, systems, and business context. That collaboration produced the blueprint: the tools, how to use them, and what to monitor. I took it from there through the production system, the DDD refactor, the MCP interface, the testing strategy, and the Kubernetes deployment.

Impact

  • 3 hours to under 30 seconds for a full cross-channel performance report.
  • Zero hallucinated metrics, by construction—no language model sits in the arithmetic path.
  • Seven channels in a single question, where the manual process handled one platform at a time.
  • Real-time answers during client calls, instead of "let me pull that and get back to you."
  • Analysts moved from data gathering to data strategy, which was always the point.

What I Took Away

The instinct when a language model gets something wrong is to make the model smarter—better prompts, a bigger model, more examples. On MADI, nearly every reliability win came from the opposite move: shrinking the model's authority and handing the work to code that cannot improvise.

The model is very good at understanding what someone meant. It should never be the thing that decides what is true.

I wrote about the reasoning behind this architecture in more depth in The Architecture of Trust.

// work with me

Want a build like this for your own business? I take on a limited number of engagements spanning technology consulting, AI engineering services, and website development & hosting.

// more from the record

Related Projects

ClickIQ: An AI-Powered Marketing Intelligence & Automation Platform
project · related

ClickIQ: An AI-Powered Marketing Intelligence & Automation Platform

As the lead architect and engineer, I spearheaded the development of ClickIQ, an AI-driven advertising intelligence platform that transformed Click Here Digital's operations by automating campaign management, unifying data analytics, and providing a single source of truth for measuring ROI across a vast digital landscape.

Amazon Web ServicesGoKubernetes
view project →
Lo $teve: A Self-Updating Artist Site Wired to the Live Catalog
project · related

Lo $teve: A Self-Updating Artist Site Wired to the Live Catalog

The official site for Lo $teve, a New Orleans hip-hop artist. Built as a 1970s soul-record sleeve in hand-written CSS, it reads the artist's live Spotify catalog and Printful storefront at runtime—so a new single or a new shirt appears on the site with no code change and no redeploy.

Amazon Web ServicesDockerGitHub Actions
view project →

tell me what you're building.

Free 30-minute consult. If I'm not the right fit, I'll say so and point you to who is.

Book a consultation