Manuel Porras

Private Call Analysis Pipeline

266 customer calls turned into an auditable dataset — without the audio leaving the building

The Problem

A US home-services company had 20.7 hours of inbound phone calls — 266 recordings sitting in their field-service platform, never analysed. Based on someone listening to 22 of them by hand, they believed most calls were being picked up by an answering service rather than staff who could actually book a job. If true, that meant lost revenue on every missed booking.

Two constraints ruled out the obvious approach. The recordings could not leave the premises — customer calls contain names, phone numbers and home addresses, so sending them to a hosted transcription or LLM API was not an option. And the findings had to be defensible: a number that could not be traced back to specific calls was no use in a decision about staffing.

The Approach

A three-stage local pipeline. Nothing in it makes a network call.

YOUR INFRASTRUCTURE · NO NETWORK EGRESS 266 MP320.7 hours Transcribe faster-whisperlarge-v3 · CUDA Rules layer auditable · traceable Local LLM subjective fields Redaction build step Fulllocal only Redactedshareable No hosted API at any stage · zero marginal cost per recording
Three local stages. The dashed boundary is the point: no stage reaches the network.

Transcription. faster-whisper large-v3 on a single RTX 3090, float16 with voice-activity detection. I chose the large model deliberately: the audio was 8 kHz telephone quality with crosstalk and hold music, exactly where smaller models degrade — and where a mis-transcribed model number or street name silently corrupts every downstream figure. Throughput reached 8–14× realtime. The runner resumes rather than restarts, skipping anything already processed and surviving a malformed file instead of aborting the batch.

Analysis, in two deliberately separate layers. Deterministic rules handle anything that informs a decision — who answered, whether a job was booked, whether the call ended as a message only. Every figure there traces to a named pattern and a list of call IDs, and can be re-derived by anyone. A local LLM handles the genuinely subjective fields. Mixing the two produces a report where nobody can tell which numbers are auditable and which are a model's opinion; keeping them apart meant the staffing decision rested only on the auditable half.

Redaction as a build step, not a final pass. Phone numbers, addresses, emails and card numbers fell to pattern matching. Personal names did not — my first approach caught self-introductions and missed a caller introduced by someone else, leaking a name into what was meant to be the shareable file. I fixed it by having the local model enumerate every person named in each transcript, scrubbing against that list, and re-running the corpus. Two outputs: a full one that never leaves the machine, and a redacted one safe to hand to anyone.

The Outcome

The client's hypothesis was wrong, and the pipeline could prove it.

WHAT 22 CALLS SUGGESTED 15 different names — looks like an answering service WHAT ALL 266 CALLS SHOWED Jessica — 172 calls Heather — 72 28 one-off names — 30 calls (after-hours overflow) Of the 205 calls reaching those two: booked work — 67% message-only — 0% Real exposure: ~30 calls (11%) landed with someone who could not book, 10 ended as a message.
The sample and the corpus told opposite stories. Only one of them was measured.

The 22-call sample had shown 15 different names answering. Across all 266 calls the distribution was almost the opposite: 77% of calls reached one of just two people, and those two booked work on 67% of their calls and took a message-only outcome on none. The 28 one-off names that made the sample look chaotic were after-hours overflow, accounting for 11% of calls.

So the real exposure was not systemic — it was roughly 30 calls landing with someone who could not book, of which 10 ended as a message. A specific, bounded, fixable problem instead of a vague structural one, and a far cheaper fix than the reorganisation the original theory implied.

266/266
Transcribed, zero failures
20.7 h
Audio processed
0
Bytes sent to any API
8–14×
Realtime throughput
Why It Matters

Zero marginal cost per unit processed. Local inference on owned hardware — the same corpus through a hosted API is a recurring bill; here it was electricity. That inverts the economics of anything you want to run continuously.

Data residency by architecture, not by policy. For EU work this is the whole conversation: not a processing agreement and a promise, but a pipeline with no egress. Nothing to audit because nothing left.

It overturned the brief. The client asked me to confirm something. The honest answer was that it was not true, and the value was in the disproof.

Where This Applies

Any organisation sitting on unstructured voice or document data it has never examined, with a reason it cannot be sent to a third party: support and sales calls, intake and triage recordings, clinical or legal dictation, claims handling, field-service dispatch, multilingual call centres. The pipeline is domain-agnostic; the rules layer is written per domain, and it is the small part.

Tech Stack
faster-whisper large-v3CUDA / float16PythonLocal LLM (ollama)SQLitePII redactionRTX 3090
Related

LLM Evaluation Harness

← Back to all projects