Yapay zekâya telefonla gerçek arama nasıl yaptırılır?
Bu sistem şu an CRM'imde ve kişisel asistanımda çalışıyor. Vadesi geçen bir ödemeyi hatırlatıyor, yeni gelen bir müşteri adayıyla ön görüşme yapıyor, berberden randevu alıyor. Her arama bir senaryoyla başlıyor: görev, en fazla kaç dakika konuşacağı ve ilk cümlesi. Görüşme sırasında dökümü canlı izliyorum, istersem tarayıcıdan dinliyorum, istersem görüşmeyi devralıyorum. Bitince özet, çıkarılan bilgiler ve bir WhatsApp raporu geliyor. Hazır platformları (Vapi, Retell) kullanmadım. Sebebi basit: ses zaten OpenAI'da işleniyor, aradaki platform yalnızca köprü kuruyor ve bunun için dakika başına pay alıyor. Üstelik iş kuralları, araçlar ve veriler onların sisteminde kalıyor. Ben köprüyü kendim kurdum: sunucumda bir Asterisk santrali, onu yöneten küçük bir Node servisi ve araçları çalıştıran CRM. Bu rehber ileri seviye. SIP, RTP, Docker ve iptables'ı en azından duymuş olmalısın. Anlatmadan geçmediğim yerler, iki gün boyunca kafa yorduğum yerler oldu. Rehberin sonunda, aynı sistemi Claude Code'a baştan kurdurabileceğin hazır bir komut var.
Neye ihtiyacın var?
- Docker kurulu bir Linux sunucu (santral 512 MB bellekle rahat çalışıyor)
- SIP hesabı veren bir operatör ve giden arama dakikası. Ben 0850'li bir hat kullandım
- GPT-Live erişimi olan bir OpenAI projesi ve webhook imza anahtarı
- Asterisk 20 ve Node.js 22 (çağrı yöneticisi için)
- Araçları çalıştıracak bir arka uç (benimki PHP'li bir CRM) ve bir bildirim kanalı (WhatsApp API)
- SIP, RTP, NAT ve iptables temelleri
Adım adım
1. Mimariyi kafanda kur: dört parça
Karşı tarafın telefonu, operatörün SIP hattı, benim santralim ve OpenAI. OpenAI'ın GPT-Live modeli doğrudan SIP konuşabiliyor: bir SIP adresi var, oraya arama yapınca bir sesli oturum açılıyor. Yani bana WebRTC sunucusu ya da LiveKit gerekmiyor. İki SIP bacağını aynı köprüde birleştiren bir santral yeterli: bir bacak operatöre, bir bacak OpenAI'a. Bunun için Asterisk'i seçtim, çünkü olgun ve ARI (REST + WebSocket) ile her kanalı koddan yönetebiliyorum. Asterisk'i yöneten beyin ayrı bir Node servisi, ben ona "çağrı yöneticisi" diyorum. Kimi ne zaman arayacağına, ne zaman kapatacağına ve hangi işi CRM'e yaptıracağına o karar veriyor. Konuşmanın kendisi hiçbir zaman benim sunucumda bir yapay zekâdan geçmiyor. Ses santralden akıp gidiyor, ben sadece yönetiyorum.

2. Asterisk'i Docker'da, kapıları kapalı kur
Asterisk'i host ağıyla çalıştırıyorum. RTP için 200 portluk bir aralık (10000-10199) açmak, Docker'ın port eşlemesiyle uğraşmaktan çok daha temiz. Konteyner bütün Linux yetkileri düşürülmüş (cap_drop ALL) ve no-new-privileges ile çalışıyor. Asterisk açılışta root'u bırakıp kendi kullanıcısına geçiyor, bellek ve işlemci sınırlı. Yapılandırma dosyaları salt-okunur bağlanıyor. modules.conf'ta dışarıya port açan ne varsa (eski chan_sip, AMI, dışa açık HTTP) kapalı. ARI yalnız 127.0.0.1'de. Bütün gizli değerler (operatör şifresi, OpenAI anahtarı, webhook anahtarı, ARI şifresi, servis jetonu) root'un okuyabildiği TEK bir dosyada. Küçük bir kurulum betiği Asterisk'in gizli yapılandırmalarını bu dosyadan üretip konteynerleri yeniden başlatıyor. Böylece hiçbir anahtar kodun ya da yapılandırmanın içinde durmuyor.
3. Operatöre kayıt olmadan bağlan
Operatörle PJSIP üzerinden konuşuyorum ama REGISTER yapmıyorum. Kayıt olsaydım numarama gelen aramalar santrale düşerdi. Oysa hattın "ulaşılamazsa cep telefonuma yönlendir" ayarı olduğu gibi kalmalıydı. Giden arama, INVITE'a gelen kimlik doğrulama sorusuna (digest) outbound_auth ile cevap vererek yapılıyor. Gelen trafik yalnız operatörün IP bloklarından kabul ediliyor (identify). Codec olarak A-law ve µ-law var. Dialplan yalnız yurt içi cep ve sabit numaraları arıyor. 00'la başlayan, 0900'lü ya da kısa numaralar reddediliyor. Küçük ama sinir bozucu bir tuzak: azami süreyi numaranın içinde taşımak istedim (numara-150 gibi), ama Asterisk kalıplarda "-" işaretini yok sayıyor. Ayırıcıyı bir harf yaptım (05XXXXXXXXXm150). Arama Dial(...,S(saniye)) ile açılıyor. Açıldığı an kanala TIMEOUT(absolute) yazılıyor, böylece çağrı servisi çökse bile görüşme sonsuza kadar açık kalmıyor.
İlk gün her arama "486 User Busy" döndü ve saatlerce telefon meşgul sandım. SIP dökümündeki "Server" başlığına bakınca meşgul cevabını operatörün kendi sunucusunun verdiği anlaşıldı. Sebep: hatta bakiye ve dakika yoktu. Paket alınca düzeldi.
4. OpenAI'a SIP-TLS ile bağlan: joker sertifika tuzağı
OpenAI tarafı sip:proj_<proje-kimliği>@sip.api.openai.com adresi, 5061 portu, TLS taşıma ve SDES-SRTP ses şifrelemesi. İlk bağlantıda TLS el sıkışması düştü. Sebebi şu: OpenAI'ın sertifikası joker (*.api.openai.com), pjproject ise SIP için RFC 5922 gereği joker sertifikayı reddediyor. Sunucu adını başka bir şey yapmayı denedim. OpenAI, TLS'i yalnız SNI "sip.api.openai.com" ile kabul ediyor, başka adla "internal error" dönüyor. Çıkış yolu iki parçalı: santralde sunucu sertifikası doğrulamasını kapattım (verify_server=no), karşılığında güvenlik duvarına bir ÇIKIŞ kuralı koydum. 5061 portuna yalnız OpenAI'ın SIP adreslerine çıkılabiliyor. Adresler her gün cron ile DNS'ten tazeleniyor. DNS zehirlense bile santral başka bir sunucuya bağlanamıyor, bağlantı yine şifreli. Bir not daha: OpenAI'ın AB giriş noktası (sip-eu) yalnız veri konumu AB olarak açılmış projelere açık. Normal projede 401 dönüyor.
5. Webhook ile oturumu kabul et, yan kanalı aç
Santral OpenAI'ı aradığında OpenAI sana bir webhook gönderiyor: live.transport.incoming. İlk iş imzayı doğrulamak. İmza, Standard Webhooks biçiminde: "id.zaman.gövde" üzerinden HMAC-SHA256, anahtar whsec_ önekinden sonraki base64 değer. Zaman damgası 5 dakikadan eskiyse reddediyorum. İkinci iş, bu oturumun HANGİ aramaya ait olduğunu bulmak. Bunun için aramayı başlatırken INVITE'a aramaya özel rastgele bir jeton koyduğum özel bir SIP başlığı ekliyorum. Webhook'ta o başlık geri geliyor. Eşleşme varsa POST /v1/live/sessions/{id}/accept ile oturumu kabul ediyorum: senaryonun talimatları, ses (ben marin kullanıyorum) ve delegation {type: "client"}. Ardından wss://…/live/sessions/{id}/attach ile yan kanalı (sideband) açıyorum. Döküm, devretme olayları ve oturum kapanışı buradan geliyor. Talimat ve sonuç da buradan gönderiliyor. Eşleşme yoksa ya da imza tutmazsa oturum reddediliyor.
6. Önce asistan hazır olsun, sonra müşteri aransın
Sıralama çok önemli. Önce OpenAI bacağı açılıyor, oturum kabul ediliyor, yan kanal bağlanıyor. Asistan hazır olmadan müşteri hiç aranmıyor. Sonra müşteri, Local kanal üzerinden dialplan'a gönderilerek aranıyor. Karşı taraf açınca iki bacak aynı "mixing" köprüye giriyor ve yan kanaldan bir talimat gidiyor: "Müşteri telefonu açtı. İlk cümlen tam olarak şu olsun: …". Talimatların başında da "müşteri açtı talimatı gelene kadar konuşma" kuralı var. Yoksa model çalma sırasında boşluğa konuşuyor. Bu sıralamayla asistan, telefon açıldıktan 2-3 saniye içinde konuşmaya başlıyor.

7. En zor hata: asistan konuşuyor ama duymuyor
İlk gerçek aramada asistan güzelce selam verdi, sonra karşı taraf ne derse desin cevap vermedi. 19 saniye sonra oturum düştü. Teşhis için pjsip show channelstats ile iki bacağın RX/TX sayaçlarını iki saniyede bir izledim. Operatörden ses geliyordu (RX artıyor) ama OpenAI'a giden (TX) müşteri açtığı anda donuyordu. Sebep asterisk.conf'taki transmit_silence=yes ayarıydı. Dial beklerken arayan Local kanala bir "sessizlik üreteci" takıyor ve müşterinin sesi o kanala yazılırken düşüyordu. transmit_silence=no ile düzeldi. Ama bu sefer ikinci bir sorun çıktı. GPT-Live'ın zaman çizelgesi yalnız GELEN ses kareleriyle ilerliyor. Çalma süresince hiç kare gelmeyince talimatlar işlenmiyor ("context_injection_incomplete") ve oturum yaklaşık 30 saniyede kapanıyordu. Çözüm, telefon çalarken OpenAI bacağına sessiz bir ton (tone:0/1000) çalmak. Bir de strictrtp'yi kapattım: operatör erken sesi (early media) ve asıl sesi farklı medya sunucularından gönderiyor. Kaynak denetimini güvenlik duvarı yapıyor.
Ses sorunlarında tahmin yürütme, ölç. Hangi bacakta hangi yönün durduğunu channelstats gösteriyor. Telefonsuz test için de müşteri yerine Asterisk'in Milliwatt tonunu bağlayan bir test araması ekledim.
8. Gecikmeyi kıs: uçtan uca A-law
Operatör A-law konuşuyor, OpenAI de G.711 kabul ediyor. İki bacağı da ARI'de formats=alaw ile açınca santralde hiç dönüşüm (transcoding) kalmıyor. Hem gecikme hem işlemci kazancı var. Karşı taraf açar açmaz selam talimatı gidiyor, arada bekleme yok. Devretme olayı geldiğinde yalnız 500 ms bekliyorum, çünkü olay karşı tarafın son kelimelerinin dökümünden ÖNCE gelebiliyor. Kalan gecikmenin büyük kısmı coğrafi: OpenAI'ın ses sunucuları ABD'de, Avrupa'dan yaklaşık 150 ms gidiş-dönüş var. Bunu kısaltmanın yolu veri konumu AB olan bir proje açıp sip-eu girişini kullanmak.
9. Asistanın elleri: işi devretmek
Telefondaki model kendi başına WhatsApp atamaz, kayıt açamaz. "Client delegation" ile iş bana geliyor. Talimatın sonunda düz metin bir "Delegation policy" var: hangi arka plan araçları var, ne zaman devredilir, ne zaman devredilmez. Model iş gerektiğini anlayınca yan kanaldan session.delegation.created geliyor. Çağrı yöneticisi o ana kadarki dökümü CRM'in görev ucuna yolluyor. Orada hızlı bir model (akıl yürütmesi kapalı), senaryonun izin verdiği araçlarla dökümün SON kısmına bakıp aracı çağırıyor. Araçlar: görüşmeyi bitir, ödeme sözü kaydet, geri arama kur, not al, IBAN'ı karşı tarafın WhatsApp'ına gönder, Şafak Bey'e bağla ve Şafak Bey'e WhatsApp'tan ilet. Önemli bir hız hilesi: araç, sesli modelin söyleyeceği cümleyi kendisi döndürüyor ("Not alındı, not aldım de"). Böylece ikinci bir model turuna gerek kalmıyor, her devirde 1-2 saniye kazanılıyor. Cümle session.commentary.append ile delegation_id'ye bağlanarak geri gidiyor. "Görüşmeyi bitir" aracı ise hiçbir şey söyletmiyor, asistan susar susmaz hat kapanıyor.

10. Talimatı akıllı yaz: işi bitiren kurallar
İlk denemelerde asistan kibar ama işe yaramazdı. Berbere "müsait misiniz" dedi, "evet" cevabını alınca teşekkür edip kapattı. Hangi saatin boş olduğunu hiç sormadı. Şimdi her senaryonun talimatında "işi bitirme kuralları" var. Görevin amacına ulaş, "evet" ile yetinme, somut bilgiyi iste (hangi gün, hangi saatler). Söyleneni tekrar ederek teyit et. Tercih verilmediyse kendin karar verme, seçenekleri topla. "Sorum yok" bitirme sebebi değildir: görüşmeyi bir sonraki adıma yönelt. Talimata o anın tarih ve saatini de koyuyorum. Karşı taraf "yarım saat sonra gel" deyince asistan "yani 16:40 gibi, doğru mu?" diye saate çevirip teyit ediyor ve bana saatle iletiyor. Süre dolmadan 20 saniye önce bir uyarı gidiyor: "cevabını beklediğin bir soru varsa önce onu al, teyit et, sonra vedalaş". Hitap da ayrı bir kural. Tek kelimelik bir isimde model "Merhaba Ahmet" diyebiliyor. Şablon "efendim"e düşüyor ve modele "Bey/Hanım" kuralı veriliyor. Kimlik seçilebiliyor: "Gurizon adına" ya da "Ben Şafak Tozar'ın yapay zekâ asistanıyım". İnsan olduğunu asla söylemiyor, sorulursa yapay zekâ olduğunu açıkça söylüyor.
11. Telefonu ne zaman kapatmalı?
Sesli yapay zekâda en çok hafife alınan konu bu. Modelin "görüşmeyi bitir" aracı yaklaşık 5 saniye sürüyordu, karşı taraf bekleyip kendisi kapatıyordu. Şimdi vedayı çağrı yöneticisi dökümden kendisi yakalıyor. Asistanın cümlesi güçlü bir vedayla BİTİYORSA ("iyi günler dilerim", "kolay gelsin", "hoşça kalın Ahmet Bey") görüşme "veda kipine" giriyor ve 1,5 saniye sonra, asistan susunca kapanıyor. Zayıf veda ("teşekkür ederim", "iletiyorum") tek başına kapatmıyor, iki taraf 4 saniye susarsa kapatıyor. Son düzelttiğim tuzak: veda anında arka planda bir not işi sürüyordu ve asistan "not ediyorum… kapatıyorum…" diye ara sözler söyledi. Bu sözler kapanışı iptal ediyordu ve veda 20 saniye uzadı. Artık veda kipinde ara sözler kapanışı iptal etmiyor. Yalnız asistanın yeni bir SORU sorması ya da karşı tarafın sözünü bitirip yeni bir konu açması iptal ediyor. Karşı taraf veda dışında bir şey söylerken de hat asla kapanmıyor. Türkçe için ayrı bir tuzak var: JavaScript'te /i bayrağı büyük "İ"yi "i" ile eşleştirmiyor, önce toLocaleLowerCase('tr-TR') gerekiyor. Sessizlikte asistan bir kez "Beni duyabiliyor musunuz?" diyor, cevap yoksa kapatıyor. En dışta da santraldeki mutlak süre sınırı duruyor.

12. Sıcak aktarma ve canlı dinleme
Karşı taraf "Şafak Bey'le görüşmek istiyorum" derse asistan devrediyor. Müşteri çalma sesi duyarken santral beni arıyor. Açınca bir fısıltı notu çalıyor: gpt-4o-mini-tts ile üretilmiş 24 kHz ham PCM, Asterisk'in .sln24 dosyası olarak. Not şuna benziyor: "X firmasından Y, ödeme konusunda sizinle görüşmek istiyor, bağlanmak için bire basın". 1'e basarsam (DTMF) köprüye giriyorum ve asistan çıkıyor. Açmazsam ya da basmazsam müşteri asistana geri dönüyor, asistan "şu an bağlayamadım, size dönüş yapacak" diyor. Canlı dinlemenin iki yolu var. Telefonla dinlerken santral beni arıyor ve köprüye sesim kapalı giriyorum. "Devral" deyince sesim açılıyor, asistan çıkıyor. Tarayıcıda "Dinle"ye basınca santral müşteri bacağına bir snoop (spy=both) açıyor. externalMedia ile µ-law RTP'yi yerel bir UDP portuna akıtıyor, çağrı yöneticisi bunu WebSocket'e çeviriyor, nginx dışarı açıyor. Tarayıcı µ-law'ı bir tabloyla çözüp WebAudio'da 150 ms tamponla çalıyor. Bağlantı adresi 60 saniyelik, HMAC ile imzalı bir anahtar taşıyor. Son dinleyici çıkınca snoop kapanıyor.
13. Asistan uygulamasına bağla: "berberi ara"
Aynı arama altyapısını kişisel asistanıma (yazılı ve sesli) bir araç olarak ekledim. "Berberi ara, bugün için randevu al" deyince asistan telefonumdan eşitlenen rehberde "berber"i buluyor. Birden fazla eşleşme varsa hangisi olduğunu soruyor. Randevu işinde zaman tercihi bilinmiyorsa araç bilerek hata dönüyor ve model önce "Ne zaman için?" diye sormak zorunda kalıyor. Sonra ekrana bir arama kartı geliyor: kimi, hangi görevle, en fazla kaç dakika, hangi açılış cümlesiyle arayacak. Ben "Ara" deyince arama başlıyor, bitince özet ve dökümün bağlantısı sohbete düşüyor. Sesli modda iki güvenlik kilidi var. Birincisi: kart kurulduğu turda onaylanamıyor. Önce bana okunmalı, onay bir sonraki sözümden gelmeli. İkincisi sonradan eklendi: sesli model bir kez "Tamam, aramayı başlatıyorum" deyip işi arka plana hiç devretmedi. Arama başlamadı, sonra "aradın mı" soruma da eski bir aramanın sonucunu okudu. Artık bekleyen bir kart varken "ara", "evet", "gönder" gibi kısa bir sözden sonra 1,5 saniye içinde devretme gelmezse sayfa işi kendisi arka plana yolluyor. Model sonradan devrederse aynı iş iki kez çalışmıyor.
14. Güvenlik: santral kimseye dokunmamalı
Bu santral, sitelerimin çalıştığı sunucuda duruyor. Kendi iptables zincirleri var ve başka hiçbir kurala dokunmuyor. Girişte SIP yalnız operatörün IP bloklarından, ses (RTP) yalnız operatör ve OpenAI medya aralıklarından kabul ediliyor. Santral API'si, çağrı servisi ve dinleme WebSocket'i yalnız localhost'a açık. Çıkışta 5061 portu yalnız OpenAI SIP adreslerine gidebiliyor. Düşen paketler, hız sınırlı bir önekle günlüğe yazılıyor. Tek yönlü ses olursa ilk bakılacak yer orası. Uygulamada webhook imzası ve zaman penceresi, servisler arası jeton ve sabit zamanlı karşılaştırma var. İş kurallarında test modu var (yalnız izinli numaralar aranabiliyor), ayrıca arama saatleri, günlük ve eşzamanlı sınır, aynı numarayı tekrar arama aralığı. Bir bekçi betiği 5 dakikada bir sağlık kontrolü yapıyor. Bozuksa konteynerleri kaldırıyor, iki kez üst üste bozuksa bana WhatsApp atıyor.

15. Hazır komutla Claude Code'a kurdur
Bu sistemin tamamını Claude Code ile, sunucuya SSH üzerinden bağlanarak kurdum. Aşağıdaki komut, bu rehberdeki mimariyi ve bütün tuzakların çözümlerini içeriyor. Claude Code'a yapıştır, en alttaki köşeli parantezleri kendi operatörün, arayan numaran ve arka ucunla doldur. Her yapılandırmayı uygulamadan önce sana göstermesini ve test modu açıkken yalnız kendi numaranı aramasını istiyor. İlk hedefin, Milliwatt test aramasında asistanın seni "duyması" olsun. Sonra kendi cep telefonuna gerçek bir arama yaptır. Ancak ondan sonra gerçek bir kişiyi ara.
Build me an outbound AI phone-calling system on my Linux server. The voice is OpenAI GPT-Live over SIP; my own Asterisk bridges it to my SIP operator. Work step by step, show me every config before you apply it, and never call a real number until I say so: add a test mode that only allows my own number.
Architecture
- Asterisk 20 in Docker: host network, cap_drop ALL, no-new-privileges, config mounted read-only. Load only the modules you need; ARI/HTTP on 127.0.0.1 only; RTP range 10000-10199.
- A Node 22 "call manager" (only dependency: ws) on 127.0.0.1, protected by a token. It drives Asterisk through ARI and OpenAI through the webhook + sideband WebSocket.
- My backend exposes three token-protected endpoints: event (live transcript), task (tool loop), end (summary + report).
- All secrets live in one root-only env file; a setup script renders the Asterisk secret configs from it.
Operator (PJSIP)
- Outbound auth only, NO REGISTER (inbound routing must not change). Identify the operator by its IP blocks. Codecs: alaw, ulaw.
- The dialplan only allows national mobile/landline patterns. Pass the max duration inside the extension with a letter separator (Asterisk ignores "-" in patterns). Dial with S(max) and set TIMEOUT(absolute) on answer, so calls end even if the manager crashes.
OpenAI (PJSIP)
- sip:<project_id>@sip.api.openai.com:5061;transport=tls with media_encryption=sdes.
- pjproject rejects OpenAI's wildcard certificate and OpenAI only accepts SNI sip.api.openai.com, so set verify_server=no AND add an iptables OUTPUT rule that allows port 5061 only to the resolved OpenAI SIP IPs (refresh daily from cron).
- Add a random per-call SIP header on originate. On the webhook "live.transport.incoming": verify the Standard Webhooks signature (HMAC-SHA256 with the whsec_ key, 5-minute window), match the header to the call, POST /v1/live/sessions/{id}/accept with instructions, voice and delegation {"type":"client"}, then open wss://api.openai.com/v1/live/sessions/{id}/attach.
Call flow
1. Originate the AI leg first (formats=alaw). Dial the person only after the session is accepted and the sideband is open (formats=alaw, no transcoding).
2. While the phone rings, play tone:0/1000 into the AI leg: the GPT-Live timeline only advances with incoming audio frames.
3. asterisk.conf: transmit_silence=no (a silence generator on the Local channel makes the AI deaf). rtp.conf: strictrtp=no (early media and media can come from different operator servers; the firewall does the source filtering).
4. On answer: stop the tone, put both legs in one mixing bridge, send session.instructions.append: "The person answered. Your first sentence is exactly: ...".
5. Delegation: on session.delegation.created wait 500 ms, send the transcript to my task endpoint; it runs a fast model with scenario-dependent tools and returns the sentence to say, so no second model round. Send it with session.commentary.append (delegation_id). If the tool is hangup, say nothing and hang up once the assistant is quiet.
6. Goodbye detection in the manager, don't wait for the model. Lowercase with toLocaleLowerCase('tr-TR'). A strong goodbye at the end of an assistant sentence enters "goodbye mode": hang up 1.5 s later once the assistant is quiet. Filler sentences don't cancel it; only a new question from the assistant, or a finished non-goodbye sentence from the person, cancels it. Never hang up while the person is saying something that isn't a goodbye. Weak goodbyes ("thank you") close after 4 s of silence on both sides.
7. Put the current date and time in the instructions so relative times ("in half an hour") become clock times. Warn 20 s before the limit: "if you're waiting for an answer, get it first, confirm it, then say goodbye". Silence: ask "can you hear me?" once, then hang up.
8. Optional warm transfer: ringing tone to the person, call me, play a TTS whisper note, bridge only if I press 1, otherwise hand the person back to the assistant.
Security and limits
- An iptables chain: SIP only from the operator's IP blocks, RTP only from the operator and OpenAI media ranges, ARI and the manager only from loopback. Don't touch any other rule on the server.
- Max 3 concurrent calls, a daily cap, calling hours, a repeat-call interval.
- A watchdog cron every 5 minutes that restarts unhealthy containers and messages me after two failures in a row.
Diagnostics
- Log every OpenAI event type once per call, with the call id on every line.
- Use `pjsip show channelstats` to see which direction the audio stops in.
- A test call that bridges the AI to Milliwatt instead of a real phone.
My operator is [OPERATOR], my caller ID is [NUMBER], my backend is written in [PHP / NODE / PYTHON].İpuçları
- İlk günlerde test modunu açık tut ve yalnız kendi numaranı ara. Kendini müşteri yerine koyup zorla: sözünü kes, konuyu değiştir, sus, "iyi" deyip kapat.
- Aramaları kısa tut: benim senaryolarım en fazla 2-2,5 dakika. Süre sınırını hem çağrı servisinde hem santralde tut.
- Ses kaydı tutma, yazılı döküm yeterli. Hem kişisel verilerin korunması açısından daha rahat hem de arama geçmişinde metin aramak daha kolay.
- Her görüşmede her OpenAI olay türünü bir kez, arama numarasıyla birlikte günlüğe yaz. Bir şey ters gittiğinde hangi adımın gelmediğini hemen görürsün.
- Veda mantığını gerçek dökümlerle yerelde canlandırarak test et. Zamanlayıcı hatalarını telefonda yakalamak çok pahalı.
- 0850'li numaraları insanlar daha az açıyor gibi. Mümkünse coğrafi ya da tanıdık bir numaradan ara.
Sık sorulanlar
Neden Vapi ya da Retell kullanmadın?
Ses zaten OpenAI'da işleniyor. Aradaki platform köprü kuruyor ve bunun için dakika başına pay alıyor. Kendi santralimle dakika maliyeti yalnız OpenAI ile operatörden oluşuyor. Asıl kazanç kontrol: talimat, araçlar, veri ve kapanış mantığı tamamen bende. Vapi'deki panelin sade bir benzerini de CRM'e kendim ekledim: senaryolar, süre, ses, canlı döküm, dinle, devral.
LiveKit ya da WebRTC gerekir mi?
Telefon araması için hayır. GPT-Live doğrudan SIP konuşuyor, iki SIP bacağını birleştiren bir santral yeterli. WebRTC'yi yalnız tarayıcıdaki sesli asistanımda kullanıyorum, o da doğrudan OpenAI'a bağlanıyor.
Bir arama kaça mal oluyor?
Ölçtüğüm gerçek rakamlar: 52 saniyelik bir görüşme 0,047 $, 2 dakika 31 saniyelik bir görüşme 0,13 $. Kabaca asistan dakikası 5 sent civarı, üstüne operatörün dakika ücreti geliyor. Dikkat: OpenAI oturumu telefon çalarken de açık, çünkü asistan önce hazırlanıyor. Yani çalma süresi de faturaya giriyor.
Yapay zekâ olduğunu söylüyor mu?
Evet, her zaman. İlk cümlesinde kendini yapay zekâ asistanı olarak tanıtıyor ve insan olduğunu asla söylemiyor. Bunu talimatta kesin kural olarak tutuyorum.
Gelen aramaları da karşılayabilir mi?
Aynı parçalarla yapılabilir: operatöre kayıt olup gelen aramayı OpenAI bacağına bağlarsın. Ben bilerek kapalı tuttum. Numaram ulaşılamayınca cep telefonuma yönleniyor, santral o akışa hiç dokunmuyor.
Türkçesi ne kadar iyi?
Konuşma akıcı. Sorun çıkaran yerler dil değil, kurallar oldu: hitap ("Merhaba Ahmet" yerine "Ahmet Bey"), tutarları doğal okumak, göreli zamanı saate çevirmek ve Türkçe büyük/küçük harf. Hepsi talimat ve kodla çözüldü.
Ticari aramalarda yasal bir kısıt var mı?
Var. Türkiye'de ticari amaçlı arama ve mesajlar için izin ve İYS kuralları geçerli. Ben bu sistemi kendi müşterilerime, iş ilişkim olan kişilere ve kişisel işlerime kullanıyorum, toplu arama yapmıyorum. Bu rehber hukuki tavsiye değil. Toplu kullanmadan önce bir hukukçuya danış.