Skip to content

Repository files navigation

Anonymous Outbound Voice Agent

Place real phone calls with an AI voice agent without revealing your real name.

The agent dials real numbers through Vobiz (telephony / SIP carrier) connected to LiveKit Cloud, speaks and listens via Sarvam AI (Telugu by default), and reasons with Grok through OpenRouter. You get a live terminal dashboard, optional browser listen-in, and transcripts — while the bot only uses a public alias.


Features

  • Anonymous identity — agent uses a configurable alias (e.g. “Alex”), never your real name, phone, or email
  • Outbound phone callsVobiz SIP trunk + LiveKit Cloud realtime
  • Multilingual voice — Sarvam STT/TTS with Telugu (te-IN) default; English and other Indian languages supported
  • Low-latency LLM — Grok via OpenRouter with light reasoning tuned for natural phone speech
  • Live call monitor — dialing / ringing / answered / ended, timers, and live transcript
  • Listen-only mode — hear the call on speakers or in the browser without joining the conversation
  • Noise-resistant barge-in — short coughs and noise are less likely to cut the agent off mid-sentence
  • Auto hang-up — silence / max duration / goodbye detection

Stack

Layer Provider
Telephony (SIP) Vobiz — outbound SIP trunk for real phone calls
Realtime media LiveKit Cloud (rooms, agents, SIP participant bridge)
STT Sarvam AI (saaras:v3, auto-detect incl. Telugu)
LLM OpenRouter → Grok (x-ai/grok-4.3 by default)
TTS Sarvam AI (bulbul:v3, default Telugu te-IN)
VAD Silero (via LiveKit agents)

Telephony partner: Vobiz

This project places real PSTN/mobile calls using a Vobiz outbound SIP trunk registered in LiveKit:

  1. Create a trunk in the Vobiz dashboard (or equivalent Vobiz SIP credentials).
  2. Add that trunk as an outbound SIP trunk in LiveKit Cloud (SIP → Trunks).
  3. Copy the LiveKit trunk id (ST_…) into .env as SIP_OUTBOUND_TRUNK_ID.

When you run make_call.py, LiveKit creates a SIP participant and Vobiz dials the destination number. Call states (dialing, ringing, active, hangup) come from LiveKit SIP attributes over that trunk.


Prerequisites

  • Python 3.11+ recommended
  • Accounts / keys:
  • Windows, macOS, or Linux (Windows users should run the included local-inference patch)

Quick start

1. Clone and install

git clone https://github.com/Iamnanisyam/voice-agent.git
cd voice-agent

python -m venv venv

# Windows
.\venv\Scripts\Activate.ps1

# macOS / Linux
# source venv/bin/activate

pip install -r requirements.txt

# Windows only — avoid livekit-local-inference ACCESS_VIOLATION crash
python patch_local_inference.py

# Download model assets (Silero VAD, etc.)
python agent.py download-files

2. Configure secrets

copy .env.example .env

Edit .env and set at least:

Variable Description
LIVEKIT_URL e.g. wss://your-project.livekit.cloud
LIVEKIT_API_KEY LiveKit API key
LIVEKIT_API_SECRET LiveKit API secret
SARVAM_API_KEY Sarvam STT + TTS
OPENROUTER_API_KEY OpenRouter key for Grok
OPENROUTER_MODEL Default: x-ai/grok-4.3
SIP_OUTBOUND_TRUNK_ID LiveKit id of your Vobiz outbound SIP trunk (ST_…)

Optional identity (privacy):

AGENT_PUBLIC_NAME=Alex
AGENT_REPRESENTS_AS=a private client
AGENT_LANGUAGE=te-IN
AGENT_SPEAKER=shubh

Never commit .env. It is listed in .gitignore.

3. Start the agent worker

Keep this terminal running:

.\venv\Scripts\Activate.ps1
python agent.py dev

4. Place a call (second terminal)

.\venv\Scripts\Activate.ps1
python make_call.py `
  --to +919876543210 `
  --purpose "Confirm whether the meeting tomorrow at 3pm still works" `
  --callee "Ravi" `
  --context "Meeting is optional; if busy, suggest Friday afternoon"

By default the script watches the call live in that terminal.


How to use

Call status dashboard

While a call is active, make_call.py and the worker show:

Status Meaning
INITIATING Dispatch accepted, agent joining
DIALING… SIP INVITE via Vobiz trunk
RINGING… Remote phone ringing
LIFTED (active) Callee answered
BUSY / REJECTED / NO ANSWER Failed outcomes
ENDED Hangup

Also shown: elapsed time since dial, talk time since answer, and a LIVE TRANSCRIPT (THEM = callee, AGENT = bot).

Dispatch only (no watch loop):

python make_call.py --to +919876543210 --purpose "" --no-watch

Useful flags

Flag Meaning
--to E.164 number (required), e.g. +919876543210
--purpose Why the agent is calling (required for a useful call)
--callee Name of the person being called
--context Extra background for the agent
--public-name Alias used on the call (not your real name)
--represents-as Who the agent says it represents
--language TTS language (te-IN, en-IN, hi-IN, …)
--speaker Sarvam voice (shubh, priya, …)
--no-watch Exit after dispatch instead of live monitoring

Live listen (optional third terminal / browser)

While a call is active:

# Speakers only (subscribe-only, no mic)
python listen.py
# or pin a room from make_call output:
python listen.py --room call-91xxxxxxxxxx-abc

# Browser player + optional recording
python listen.py --browser

You join as a listen-only participant and hear agent + callee audio.

Console test (no phone)

Verify STT / LLM / TTS without dialing:

python agent.py console

Validate a Sarvam / ElevenLabs voice id

python validate_voice.py --speaker shubh

Dispatch via LiveKit CLI (alternative)

lk dispatch create \
  --new-room \
  --agent-name anonymous-caller \
  --metadata "{\"phone_number\": \"+919876543210\", \"purpose\": \"Confirm the appointment\", \"public_name\": \"Alex\", \"represents_as\": \"a private client\"}"

Privacy design

On every call the agent:

  1. Introduces itself with a public alias (default: Alex) — not your name
  2. Says it is calling on behalf of a private client (configurable)
  3. Refuses to share your real name, phone, email, address, or workplace
  4. Can still complete a task (confirm appointments, ask questions, leave messages)

Configure in .env:

AGENT_PUBLIC_NAME=Alex
AGENT_REPRESENTS_AS=a private client

Tuning (optional)

If short noise still cuts the agent off, raise barge-in thresholds in .env:

MIN_INTERRUPTION_DURATION=1.5
MIN_INTERRUPTION_WORDS=2
FALSE_INTERRUPTION_TIMEOUT=1.5

Other useful knobs (see .env.example):

  • MIN_ENDPOINTING_DELAY / MAX_ENDPOINTING_DELAY — how fast the agent replies after you stop talking
  • AUTO_HANGUP_SILENCE_SEC / AUTO_HANGUP_MAX_CALL_SEC — call length limits
  • LLM_MAX_TOKENS / LLM_REASONING_EFFORT — reply length and reasoning cost
  • TTS_PACE — speaking speed

Project layout

voice-agent/
  agent.py                 # LiveKit worker + anonymous call logic
  make_call.py             # Dispatch helper + live status
  listen.py                # Subscribe-only live listen / browser player
  call_monitor.py          # Terminal dashboard + transcript logging
  validate_voice.py        # Voice / TTS sanity checks
  patch_local_inference.py # Windows ACCESS_VIOLATION workaround
  requirements.txt
  .env.example             # Template — copy to .env locally
  .gitignore

Security & legal

  • Never commit .env. Only ship .env.example with placeholders.
  • Rotate API keys if they were ever shared in chat, screenshots, or a private fork.
  • Only call numbers you are authorized to contact. Comply with local telemarketing, consent, and recording laws.

License

Use and modify freely for your own projects. Add a formal license file if you redistribute under specific terms.

About

Anonymous outbound AI voice agent: real phone calls via Vobiz (SIP telephony) + LiveKit Cloud, Sarvam STT/TTS, and Grok (OpenRouter) — without revealing your real name. Live monitor, Telugu default, listen-only mode.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages