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)

Searching Database…

Что это такое?Опенсорсный самоместный ИИ-колл-центр: звонки приходят на FreeSWITCH и по умолчанию отвечаются речевой моделью speech-to-speech, а сценарий решает, когда передать звонящего живому оператору в очередь. Поставляется одним Go-бинарником с REST+SSE API и OpenAPI-спецификацией, а под ними PostgreSQL и FreeSWITCH.
Это библиотека для встраивания или отдельный сервер для развёртывания?Это отдельный самоместный сервис: один Go-бинарник, включающий REST API, поток событий SSE и весь веб-интерфейс, работающий рядом с PostgreSQL и FreeSWITCH.
Как это запустить?Есть однострочный установщик для Linux и macOS, описанный в deploy/one-line-installer.md.
Что нужно, кроме самого установщика?FreeSWITCH и PostgreSQL — часть стека, плюс один провайдер голоса, выбираемый при старте развёртывания: Qwen или Doubao (для материкового Китая), OpenAI Realtime или Gemini в остальных случаях, либо ваш собственный шлюз, совместимый с Realtime.
Проходил ли он реальное нагрузочное тестирование под производственные объёмы звонков?Человеческий путь, ИИ-путь и упаковка проверены на живом FreeSWITCH и живых провайдерах, но кампании бенчмарков ещё не было, так что заявленный бюджет ёмкости/задержек — это намерение, а не измеренная гарантия.
Работает ли установщик и на Windows?Предоставлены только однострочные установщики для Linux и macOS; пути установки для Windows не упоминается.
Как операторы на деле работают со звуком звонка?Операторы работают в браузере — присутствие, управление звонками и обратные вызовы, — но сам звук несёт сопутствующее расширение Chrome web-sip-phone, которое держит SIP-регистрацию оператора и управляется через REST-вызовы.
На каких языках говорит бот?Английский и китайский — и в интерфейсе, и у бота, причём ИИ-провайдер для конкретного развёртывания выбирается независимо от языка звонящего.
Можно ли приладить классический конвейер ASR+LLM+TTS?Нет, это сознательно исключено из основного процесса; любая каскадная композиция ASR/LLM/TTS должна жить в отдельном сервисе, говорящем на том же протоколе.
Если бот начал звонок, а человек завершил, я получу одну запись или несколько?Только одну: звонок, отвеченный ботом, переведённый в очередь и завершённый оператором, даёт единственный CDR с одной расшифровкой и одной записью, а не фрагментированные куски.
Creator
rasonyang
Size
7.1 MB
Stars
293
Age
2mo
Commits · 90 Days