analytics
Portafolio de Iván
Projects chevron_right WhatsApp agents with local AI

Production system · two instances

smart_toy Local AI (Ollama) search Hybrid search database PostgreSQL + pgvector record_voice_over Voice transcribed locally

WhatsApp sales agents running 100% local AI

One engine serves customers over WhatsApp for two different businesses — a haberdashery shop and a travel agency — with language models running on my own server. No conversation ever travels to an AI vendor.

Key numbers

2

instances of the same engine in different businesses

0

conversations sent to external AI services

15 min

incremental catalogue synchronisation

3 in 1

exact, vector and full-text search, fused together

Tech stack

Node 22ExpressOllamaPostgreSQL 17pgvectorSpanish full-text searchWhatsApp Cloud APIwhisper.cpp (Metal)
schema

A message's journey

Everything involved in understanding the customer happens inside the server. The only thing that leaves is the reply, through WhatsApp's official channel.

  1. chat Input

    Message or voice note

    Arrives through the official WhatsApp API; audio is transcribed locally.

  2. manage_search Search

    Hybrid and fused

    Exact match, vector similarity and Spanish full-text search, combined with RRF.

  3. memory Model

    Answer from a local model

    The chat model runs on my own server, with the real catalogue as context.

  4. trending_up Sales

    Score and next step

    Deterministic lead scoring and a recommended next action.

  5. support_agent Human

    Handoff to a person

    When it is time to close, or the case leaves the script, a salesperson takes over.

store

Context

Two very different businesses had the same bottleneck: WhatsApp. A haberdashery shop answering catalogue questions all day, and a travel agency in Chiapas quoting packages by hand, message by message, with the salesperson copying data between screens.

The first agent was built for the shop. When the agency described its need, the question was not «shall we build it again?» but «can the engine be instantiated?». The second instance was raised following a portability guide written for that purpose, not by copying and pasting the previous project.

In both cases the starting requirement was the same, and it was not negotiable: customer conversations do not leave my own server.

report

The problem

Local AI or nothing: no conversation and no catalogue leaves the machine.

Delegating support to a cloud AI service solves the technical problem and opens another one: every customer message — with its data, its doubts and its buying intent — ends up in a third party's hands. For a small business that is competitive and personal information at once.

The second problem is answer quality. A model that «knows» the catalogue from memory invents references that do not exist, and in a shop with thousands of product keys that means promising an item nobody can ship.

And the third is about roles: a bot that quotes on its own and sends a number to the customer can commit the business to a price the salesperson never approved.

rule

Key decisions

01 The whole model stays in-house

Chat and embeddings run on local models on my own server. Voice notes are also transcribed locally, with an engine compiled to use hardware acceleration and running as a permanent service.

psychology Because the promise «your conversations don't leave here» only holds if there is no exception at all. A single external call to transcribe audio would break the entire guarantee, and that is precisely where the most personal data travels.

02 Hybrid search, not the model's memory

Every answer is grounded in a search that combines exact key matching, vector similarity and Spanish full-text search, fusing the three rankings. The catalogue syncs every 15 minutes and only changed items are re-embedded, compared by hash.

psychology Exact matching wins when the customer types a product key; vector search, when they describe what they want in their own words; full-text, when they use the trade's term. None of the three always wins, so they are fused instead of chosen.

03 The bot prepares, the salesperson decides

At the travel agency the agent does not generate the quote PDF or send prices to the customer. It assembles the form state and creates the record through the real quoting tool's API, so the salesperson recomputes with the real engine and closes the deal.

psychology A second pricing engine inside the bot would be a second place where the price can be wrong. Besides, anything sent to a customer with the business's name on it should be approved by a person from that business.

04 Verify the signature without going deaf

The webhook accepted requests without checking their signature. The fix shipped in two deployments: first measuring with real deliveries, then requiring the signature.

psychology A misconfigured strict mode does not fail loudly: it leaves the bot silently deaf, the channel retries and eventually disables the webhook. Turning verification on blindly would have swapped a security hole for an outage that is hard to diagnose.

construction

The solution

The engine runs on Node with Express over PostgreSQL with a vector extension: the same place stores the catalogue, the embeddings and the commercial state of each conversation. There is no separate database for «the AI stuff».

On top of that sits a deliberately boring commercial layer: deterministic lead scoring, a recommended next action, and handoff to a person when the case calls for it. None of that depends on the language model, which is why it behaves the same every day.

The second instance proved the engine was portable: it was raised following a guide written for instantiating it, with its own catalogue, its own tone and its own integration — in that case against the tour operator's quoting tool — without forking the code into two projects that would then need separate maintenance.

emoji_events

Results

The security incident is told in the abstract, with no dates or exposure details, as the project's publication rule requires.

lightbulb

What I learned

That «local AI» is a product decision before it is a technical one. It is more work and less convenience, and in exchange it lets you tell a client something almost nobody can: your conversations are not sitting on another company's server.

That the part that adds the most value is not the model, it is the search. A mid-sized model with good context answers better than a large one guessing, and you can also explain why it answered what it did.

And that hardening security needs its own ladder too. Switching on verification at once in a channel that retries and punishes silence can take the service down; measuring first cost one extra deployment and avoided an outage.