ai-native-callcenter README

The project's README from github.com/rasonyang/ai-native-callcenter. As one markdown file: README.md.

AI Native Call Center

An open-source call center where the AI is the default answer, not an add-on.

Calls arrive at FreeSWITCH and are answered by a voice model over a speech-to-speech connection the application terminates itself. The model talks; a flow steers the conversation and decides when a person is needed. When one is, the caller is transferred into a real queue where real agents are waiting, their SIP leg held by a companion Chrome extension. Everything the two halves do lands in one call record.

It is one Go binary — REST API, event stream and the whole web interface inside it — plus PostgreSQL and FreeSWITCH.

简体中文 · Deploying · Design

Agent desktop: an inbound call is answered, the live transcript fills in, then after-call work

Try it

Linux:

curl -fsSL https://github.com/rasonyang/ai-native-callcenter/releases/latest/download/install.sh | sudo sh

macOS:

curl -fsSL https://github.com/rasonyang/ai-native-callcenter/releases/latest/download/install.sh | sh

Details: deploy/one-line-installer.md.

What it does

Answers with a model, not a menu. The AI leg is a SIP endpoint inside the application: FreeSWITCH bridges the caller to it and audio goes straight to the provider — G.711 passed through byte for byte where the provider accepts it, so nothing decodes or resamples on the way.

Steers without scripting the conversation. The model owns the dialogue; the flow owns the phase. A phase carries instructions and a list of tools the model may use; transitions fire on tool results. A phase may also carry a line of its own — a greeting, a hand-over script, a goodbye — which the bot is meant to say as written rather than paraphrase: doubao is handed the words to speak, the other four providers are instructed to repeat them word for word. The built-in tools may refuse — "the queue is closed" is something to talk about, not an error — and the persona, the rules and the bot's voice are published and versioned together.

Hands over to people properly. Transfers go into mod_callcenter queues, and the agent's screen has popped by the time the phone rings: a PARTY_RINGING event over SSE says who is calling and carries the call's userData, and the bot-phase transcript is already there to read, because the call id is minted before any leg exists and survives the transfer. Agents work in the browser: presence, call control, callbacks — but not the audio, which belongs to the web-sip-phone Chrome extension, a separate repository. It holds the agent's SIP registration with credentials this application issues at sign-in, and it has no dialpad of its own: answering, holding and hanging up are REST calls here, carried to the phone over ESL uuid_phone_event. Supervisors get a live wallboard, the queue view and the roster.

Keeps one record per conversation. A call that a bot answered, handed to a queue and an agent finished is one CDR with one transcript and one recording — not three fragments. Recordings go to a filesystem or to any S3-compatible store.

Speaks two languages, and admits which provider it runs. English and Chinese throughout, interface and bot. One provider answers every call in a deployment, chosen at startup: qwen or doubao inside mainland China, openai or gemini elsewhere, or gateway for a Realtime gateway of your own. A call's language never selects it. Three of those are profiles of one protocol; doubao and gemini each speak a different one and have a client of their own (how a provider is added).

How it fits together

                    ┌──────────── one Go binary ────────────┐
  caller ──▶ FreeSWITCH ──▶ SIP UAS ──▶ provider (Realtime, speech-to-speech)
                 │            │
                 │            └─ flow engine: phases, tools, transfers
                 │
                 ├─ mod_callcenter queues ──▶ agents (browser + web-sip-phone)
                 │
                 └─ ESL ──▶ call registry ──▶ REST + SSE ──▶ web interface
                                                    │
                                              PostgreSQL

FreeSWITCH reads its directory and its mod_callcenter queues from the database, through Lua. The dialplan is static XML, but its rules decide nothing by themselves: each one hands the call to a Lua script that looks the answer up. Adding an extension, a queue or a number is a database change; the switch's own files change only when the routing itself does.

Two design notes worth knowing before reading the code:

The full design is in docs/design/, starting with the overview.

Building

make dev-up            # PostgreSQL in Docker
cd web && npm install && npm run build && cd ..
make build             # bin/aicc, with the interface embedded
./bin/aicc useradd -username admin -password '…' -role ADMIN
./bin/aicc

For frontend work, make web-dev runs Vite on 5173 against the API on 8080.

go test -race ./...    # always -race; it has caught real bugs here
make lint              # go vet, gofmt, oxlint
make api-check         # the API contract gate

The HTTP API is spec-first: docs/openapi.json is the single source of truth, and the Go server and the TypeScript client are generated from it. Edit the contract, run make api-generate, then implement. Never the other way round.

Extending it

Status

The human path, the AI path, the product surface and the packaging are built and verified against live FreeSWITCH and live providers.

Performance is not yet a claim this project makes. The design has a capacity budget and a latency target (design 06), and the harness to test them against is in the repository (docs/load-tests.md) — but the benchmark campaign itself is still to come, so treat the budget as an intention rather than a measurement.

What is deliberately not here, and will not be: any cascaded ASR + LLM + TTS pipeline inside this process. That composition belongs in a separate service speaking the same protocol.

License

Apache-2.0. See LICENSE.

ai-native-callcenter (rasonyang/ai-native-callcenter)

webrtc
Searching Database…

What is this?An open-source, self-hosted AI call center: calls land on FreeSWITCH and are answered by a speech-to-speech voice model by default, with a flow deciding when to hand the caller to a real human agent in a queue. It ships as one Go binary with a REST+SSE API, OpenAPI spec, PostgreSQL and FreeSWITCH underneath.
Is this a library I embed, or a standalone server I deploy?It's a standalone self-hosted service: one Go binary that bundles the REST API, the SSE event stream and the whole web UI, running alongside PostgreSQL and FreeSWITCH.
How do I get it running?There's a one-line installer for Linux and macOS, documented in deploy/one-line-installer.md.
What do I need besides the installer itself?FreeSWITCH and PostgreSQL are part of the stack, plus one voice provider picked at deployment startup: Qwen or Doubao (for mainland China), OpenAI Realtime or Gemini elsewhere, or your own Realtime-compatible gateway.
Has it actually been load-tested for production call volumes?The human path, AI path and packaging are verified against live FreeSWITCH and live providers, but no benchmark campaign has run yet, so the stated capacity/latency budget is an intention, not a measured guarantee.
Does the installer work on Windows too?Only Linux and macOS one-line installers are provided; there's no mention of a Windows install path.
How do human agents actually handle the call audio?Agents work in the browser for presence, call control and callbacks, but the audio itself is carried by a companion Chrome extension called web-sip-phone, which holds the agent's SIP registration and is controlled via REST calls.
What languages can the bot speak?English and Chinese throughout both the interface and the bot, with the AI provider for a given deployment chosen independently of the caller's language.
Can I bolt a classic ASR+LLM+TTS pipeline onto this?No, that's deliberately excluded from the core process; any cascaded ASR/LLM/TTS composition is expected to live in a separate service speaking the same protocol.
If a bot starts the call and a human finishes it, do I get one record or several?Just one: a call answered by the bot, transferred to a queue and finished by an agent produces a single CDR with one transcript and one recording, not fragmented pieces.
Creator
rasonyang
Size
7 MB
Stars
283
Age
2mo
Commits · 90 Days