Back to projects

Modular Design: sales, CRM and internal tools

~2,000
automated tests on the order system
3
email languages on the order system
~70%
of CRM email sorted by rules; a model sorts the rest
Illustration
Inbox · IT08:42
buyer@client.example
Ordine settimana 50

Buongiorno, vorremmo ordinare 40 pezzi dell'articolo 1042 e 15 dell'articolo 2210, con consegna entro il 15 dicembre. Stesso prezzo dell'ultimo ordine? Grazie, Giulia

Proposed orderNeeds you
  • CustomerMatched to account
  • Art. 104240 pcs
  • Art. 221015 pcs · 9 in stock
  • DeliveryBy 15 Dec
  • PriceAs last order

A customer email comes in, here in Italian.

Most of the systems I build look different from the outside and almost identical underneath: something comes in, something reads it, something checks it, an action follows, and a person needs to see what happened. So I build those steps as tested blocks, per system, with AI agents proposing, rules checking and a person approving. This page explains that approach and shows the sales systems and CRM built with it, and the internal platform in general terms.

Why I build this way

Most of the automations and tools I have built look different from the outside and almost identical underneath. Each one has something that comes in, something that reads it, something that checks it, an action that follows, and a follow-up that has to happen later. And in every case, someone needs to see what the system did.

In many companies the same internal processes are repeated by hand: collect what is needed, check it against the rules, decide, and keep track as things change. It is slow, hard to audit and easy to get wrong. Small companies lose hours to the same sales and order work every week: emails in several languages, orders typed over by hand, follow-ups that slip, and a CRM nobody keeps up to date.

A script that does the whole job end to end is the fastest way to automate that, and I still write them for one-off analyses. The trouble starts at the second run. A new input format arrives, the same step is needed in a different workflow, and the script turns out to hold three responsibilities in one function, a configuration value in a filename and no record of what it did last Tuesday.

  1. Something comes intrigger
  2. Something reads itintake
  3. Something checks itcheck
  4. An action followsaction
  5. A follow-up happens laterwatch
  6. Someone sees what it diddashboard
Different systems, the same steps underneath

What a block is

A block is the opposite trade-off. It does one step, with:

  • a contract: a typed input, a typed output and a schema it validates against;
  • tests against real examples, including the ugly ones;
  • monitoring and logs, so a failure is visible the day it happens rather than the week someone notices;
  • an explicit answer to "what happens when this goes wrong": retry, park for a person, or stop.

Building a block costs more than writing the equivalent lines in a script. That cost is paid once; every later workflow in that system starts from parts that already run.

block:  read-order-email
input:  Email
output: Order (validated schema)
tests:  real examples, ugly ones too
logs:   every run
on failure:
  retry
  park for a person
  stop
Made-up example of one block

Agents propose, people approve

The systems I architect and build rest on one principle: agents propose, people approve. A language model reads what comes in and fills a fixed schema. The model is the least interesting part; the schema validation and the confidence threshold around it are what make it usable. Rules then compare the proposal against reference data, such as a customer list or a price list, and anything that does not match waits for a person.

Where that line sits is a decision per system, not a constant. Steps with a side effect outside the system get extra care: sending an invoice has to be idempotent, so running it twice for the same order does not produce two invoices.

Every block reports what it did into one shared log, which is what lets a person trust a system they did not write.

  1. Something comes ininput
  2. An AI agent proposesAI agent
  3. Rules check itrules
  4. A person accepts or overridesperson
  5. It is applied, with an audit recordrecord
Where the person sits is decided per system

The same steps in other shapes

The pattern is the one I have used for years in data work. The house-price index study for Eurostat followed it: scraping was the intake, deduplication and validation were the check, the hedonic regression was the action, and the report was the follow-up. Each system has its own blocks, and what differs most is where the person sits. What does not change is that each block is built, tested and monitored as a unit.

One pattern, two shapes
  • StepOrder emailHouse-price index
  • IntakeRead the emailScraping
  • CheckReference dataDeduplication, validation
  • ActionInvoiceHedonic regression
  • Follow-upReminderReport
The order email is a made-up illustration

Where blocks are the wrong choice

Blocks have a tax. A one-off analysis that answers one question and is then archived does not need contracts and monitoring; a notebook is the right tool. A domain that is still being discovered also resists early abstraction: drawing block boundaries before the steps are understood produces blocks with the wrong seams, and those are harder to fix than a script. My rule of thumb is to write the script first, run it until the second run hurts, and only then cut it into blocks along the seams that the second run exposed.

The rest of this page shows the systems built this way.

An order-intake agent and light ERP for a trading company

Orders, quote requests and negotiations arrive by email in three languages. The system reads the mailbox, classifies and extracts each message through a workflow engine built from agent blocks, proposes the order, quote or counter-offer, and puts anything that needs a decision in a "needs you" queue. Every change is audited and can be undone; invoices, contracts and commissions run in a light ERP behind an adapter, so the company's own ERP can replace it.

The order system is in acceptance testing with the owner after four test rounds, with about 2,000 automated tests.

An internal CRM and outreach engine for a small B2B data firm

Contact and deal pipeline, mailbox sync across accounts, email classification (rules handle about 70%, a model the rest) and AI-drafted outreach and replies. Every AI-written email is saved as a draft; a person sends it. The CRM runs internally.

  1. Mailboxes sync across accountssync
  2. Rules sort about 70% of emailrules
  3. A model sorts the restmodel
  4. AI drafts the outreach or replyAI agent
  5. A person sends itperson
Every AI-written email stays a draft until a person sends it

How the sales systems run

Data is pseudonymised before any text reaches an external model; one model gateway holds the keys, enforces budgets and runs on EU hosting. Partly on Azure, with Microsoft 365 mail integration; the rest on Docker on EU servers, with nightly off-site backups and staging environments.

A modular internal platform

As architect and lead developer: a platform where every workflow is assembled from small blocks. AI agents propose decisions, a person decides, and every decision can be traced.

The platform is in progress, and the client stays anonymous.

Earlier platform work

Before these systems I moved a data platform on eight production servers to Airflow, Iceberg and FastAPI.

Written with AI, checked by me.

Back to projects