Skip to content
Back to work
Case study · personal project

ProductLens

A product-sourcing research platform that does not just store the research, it does it. An AI agent runs the investigation loop and catalogues every finding, with provenance, as it goes.

TypeScriptFastifyPostgres + pgvectorDrizzleZodAgent harness
7
Investigation stages
16
Typed agent tools
21
Domain tables
ProductLens Opportunities view: a table of products under evaluation with category, sell price in rand, priority, and pipeline status.
The pipeline: every product under evaluation, with category, price, and where it sits in the workflow.

Overview

A product research analyst investigates products end to end to decide whether to stock them. That work usually lives across spreadsheets, inboxes, PDFs, and browser tabs, and the reasoning is hard to reconstruct when someone asks why this product. ProductLens is one tool that does two things: a central repository for the whole investigation, and an agent harness that runs the research and fills the repository in.

It is a personal project. The build is what I care about here: an agent that produces structured, sourced records rather than a wall of chat.

The agent does not return an answer. It returns records, each with a source, a confidence level, and who entered it.

How an investigation runs

The analyst commissions an investigation. The agent then works a continuous seven-stage loop: identify the product, research it, find suppliers, track engagement, manage samples, assess feasibility, and reach a decision. It runs until the product is ready to decide, pausing only at approval gates for anything that costs money or leaves the building. The analyst directs and supervises; the agent does the legwork.

ProductLens sourcing dossier: a product with estimated landed cost, suggested sell price and margin in rand, plus a landed-cost calculator using the SARS VAT method.
A sourcing dossier: landed cost and margin computed with the local SARS duty and VAT method, exportable to Excel.

The engineering

It is a TypeScript monorepo with three processes over one domain library: a React and Vite web app, a Fastify API, and a Node worker that is the agent runtime. The deciding reason for one language end to end is that the core is structured records validated everywhere. A single Zod schema per entity becomes the type, the runtime validator, the model tool-definition, and the structured-output contract, defined once and shared across the front end, the API, the worker, and the model.

The agent runs on a durable Postgres job queue rather than a workflow engine. Each step is a job; the worker claims one at a time with SELECT ... FOR UPDATE SKIP LOCKED under a time-boxed lease, heartbeats to survive long multi-turn steps, and a reaper requeues jobs whose worker died. Failures retry with backoff, up to a cap. Steps that cost money or send mail write a pending approval and stop, so a human decision is always in the loop.

Provenance is the backbone. Facts are append-only and per-field, each carrying a source, a confidence level, and whether a human or the agent entered it. An agent write over a human-authored value is never an overwrite: it lands as a proposal for the analyst to accept or reject. A separate append-only event log does triple duty as the audit trail, the live feed, and the structured logs.

Confidence is earned, not asserted
When the agent records a fact from a source, a verification step re-fetches the cited page and checks that the quoted text is actually there. Claims it cannot confirm are downgraded to unverified and flagged, so a confident-sounding answer cannot launder an unsupported one.

Around that core sit the parts that make it useful: a supplier reliability graph that dedupes vendors across investigations and ranks them on real outcomes, a learning loop where the analyst's approve and reject decisions feed back into what the agent scouts next, semantic search over the records with pgvector, and a landed-cost engine that prices imports with local duty and VAT.

ProductLens Activity view: an agent run log showing each step with status and attempts, including failed steps that timed out, plus a live activity feed of searches.
Observability by design. The run log shows every step, its attempts, and its errors, including where a run timed out.

Where it stands

ProductLens is an MVP that is well past prototype: twenty test files including integration tests against a real Postgres, a CI pipeline that runs them against pgvector, Drizzle migrations, and Docker images for all three processes. Login is deferred by design, with the ownership columns already in place so it lands later without a migration.

The run log above is real, timeouts and all. Surfacing where the agent got stuck is a feature, not something to hide. It is the same instinct I bring to systems at work: you cannot fix what you cannot see.

Back to
Selected work