---
title: "CallSense: Sales-Call Intelligence Without a Transcription Fleet"
date: "2026-09-09"
author: "Pierre Brunelle"
tags:
  - Audio
  - Whisper
  - Sales Intelligence
  - LLM
  - TableModel
  - Multimodal AI
  - Pixeltable Cloud
description: "Ingest MP3s, transcribe on insert, extract objections and competitors, search past calls. Whisper and the LLM are columns. The index follows the transcript."
url: "https://www.pixeltable.com/blog/callsense-sales-call-intelligence"
---

# CallSense: Sales-Call Intelligence Without a Transcription Fleet

**Summary:** CallSense is what a revenue-intelligence lead asks for: every uploaded call becomes a transcript, a summary, objections, competitor names, and a sentiment score — then managers search “pricing” or competitor X across the library. Whisper is not a fleet. The LLM is not a second DAG. Both are columns. Insert the audio. The rest follows. `pip install pixeltable`.

 
## The Product

 

 - Store the recording with `call_id`, rep, account

 - Transcribe on insert

 - Extract summary, pain points, competitors, sentiment (1–10)

 - Search past calls by what was said

 

 
The Whisper-only tutorial is [OpenAI Whisper integration](/blog/whisper-transcription-pixeltable). Parallel batching at scale is [a different post](/blog/whisper-transcription-pixeltable). This one is the call object: audio plus structured judgment.

 
## The Stitch You Delete

 
S3 for the WAV. A worker that calls Whisper. Another worker that calls GPT. A CRM field you paste into. Postgres plus pgvector for “find the pricing objection.” Pixeltable is one table. The [Whisper post](/blog/whisper-transcription-pixeltable) already argued transcription as a column. CallSense is that column plus the extract and the index.

 
## The Receipt

 
```python
import json
import pixeltable as pxt
from pixeltable.functions import gemini, whisper
from pixeltable.functions.huggingface import sentence_transformer
from pixeltable.serving import FastAPIRouter

TableModel = pxt.model_base()
text_embed = sentence_transformer.using(model_id='all-MiniLM-L6-v2')

INSIGHT_PROMPT = (
 'Return JSON only: summary (one paragraph), pain_points (list of strings), '
 'competitors (list of company names), sentiment (integer 1-10).'
)

@pxt.udf
def parse_insights(raw: str) -> dict:
 try:
 return json.loads(raw)
 except json.JSONDecodeError:
 return {'summary': raw, 'pain_points': [], 'competitors': [], 'sentiment': 0}

class Calls(TableModel, name='calls'):
 call_id: pxt.String
 rep_name: pxt.String
 customer_account: pxt.String
 audio: pxt.Audio

 transcript = whisper.transcribe(audio, model='base').text.astype(pxt.String)
 insights_raw = gemini.generate_content(
 [transcript, INSIGHT_PROMPT],
 model='gemini-2.5-flash',
 )
 insights = parse_insights(insights_raw)

 __indexes__ = [pxt.EmbeddingIndex(transcript, string_embed=text_embed)]

@pxt.query
def search_calls(query: str, top_k: int = 10):
 sim = Calls.transcript.similarity(string=query)
 return (
 Calls.order_by(sim, asc=False)
 .limit(top_k)
 .select(Calls.call_id, Calls.rep_name, Calls.customer_account, Calls.insights)
 )

api = FastAPIRouter(name='callsense')
api.add_insert_route(
 Calls,
 path='/calls',
 inputs=[Calls.audio, Calls.call_id, Calls.rep_name, Calls.customer_account],
)
api.add_query_route(path='/search', query=search_calls)
```

 
“Find calls where they brought up pricing or competitor X” is `search_calls('pricing concerns')` or `search_calls('Acme')`. The embedding is on the transcript; the JSON is for the manager’s card. Same file on Cloud: `pxt db update` → `pxt schema update` → `pxt service update`.

 
## What This Is Not

 
Not Gong. Not diarization as a product. Not a CRM. Auth and deal TPV stay where they are. The recording graph — bytes, transcript, extract, search — is the table.

 
## People Also Ask

 
**Which Whisper?** `model='base'` matches the homepage. Swap the model string; the column stays.

 
**Why JSON in a UDF?** Gemini returns text. `parse_insights` is the typed object you query. If the model emits garbage, you still have the raw column.

 
**How do I go to Cloud?** Same `app.py`. `PIXELTABLE_API_KEY`, `pxt://org:db`, then db → schema → service.

 
**Is this an AI automation workflow?** Yes. Insert an MP3; Whisper and the extract run as columns. The category is [AI automation workflow](/blog/ai-automation-workflow).

 
## Listen Once, Query Forever

 
Insert the MP3. Read the transcript. Search the library. Do not stand up a transcription fleet to get there.

 
Chaptering and a highlight clip on the same transcript graph is [ReelForge](/blog/reelforge-podcast-chaptering-viral-clips).

 

 - **[Pixeltable on GitHub](https://github.com/pixeltable/pixeltable)**: `pip install pixeltable`

 - **[ClipFinder](/blog/clipfinder-semantic-video-moment-search)** — NL video moments

 - **[DocuVision](/blog/docuvision-pdf-chart-qa)** — PDF Q&A

 - **[SnapCatalog](/blog/snapcatalog-product-visual-search)** — shop-the-look

 - **[SafeStream](/blog/safestream-ugc-video-moderation)** — UGC gate

 - **[ClaimBot](/blog/claimbot-multimodal-fnol-triage)** — claims triage

 - **[Pixeltable documentation](https://docs.pixeltable.com)**