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…

这是什么?一个开源、可自托管的 AI 呼叫中心:来电落在 FreeSWITCH 上,默认由语音到语音的语音模型接听,并由流程决定何时把来电者转交到队列中的真人坐席。它以单个 Go 二进制发布,自带 REST+SSE API 和 OpenAPI 规范,底层搭配 PostgreSQL 与 FreeSWITCH。
这是我嵌入的库,还是我部署的独立服务器?它是一个可自托管的独立服务:单个 Go 二进制捆绑了 REST API、SSE 事件流和整个 Web UI,与 PostgreSQL 和 FreeSWITCH 一同运行。
怎么让它跑起来?有一行式安装器支持 Linux 和 macOS,文档见 deploy/one-line-installer.md。
除了安装器本身,我还需要什么?FreeSWITCH 和 PostgreSQL 是技术栈的一部分,另外还要在部署启动时选择一个语音提供商:中国大陆用 Qwen 或豆包(Doubao),其他地区用 OpenAI Realtime 或 Gemini,或者用你自己的 Realtime 兼容网关。
它真的针对生产级话务量做过压力测试吗?人工路径、AI 路径和打包都已对照真实的 FreeSWITCH 和真实提供商验证过,但尚未进行基准压测,因此所述容量/延迟预算只是目标,不是经实测保证的指标。
安装器在 Windows 上也能用吗?只提供 Linux 和 macOS 的一行式安装器;没有提到任何 Windows 安装途径。
真人坐席实际上是怎么处理通话音频的?坐席在浏览器中进行在线状态管理、呼叫控制和回访,但音频本身由名为 web-sip-phone 的配套 Chrome 扩展承载,该扩展持有坐席的 SIP 注册并通过 REST 调用控制。
机器人会说什么语言?界面和机器人都全程支持英语和中文,特定部署选择哪个 AI 提供商与来电者的语言相互独立。
我能在这上面外挂经典的 ASR+LLM+TTS 流水线吗?不能,核心进程刻意排除了这种做法;任何级联的 ASR/LLM/TTS 组合都应放在使用相同协议的独立服务中实现。
如果机器人接听、真人结束通话,我会得到一条记录还是多条?只有一条:由机器人接听、转接到队列、再由坐席结束的通话,会产生一条带一份转写和一份录音的单一 CDR,不会拆成碎片化的多条记录。
Creator
rasonyang
Size
7 MB
Stars
283
Age
2mo
Commits · 90 Days