Way Back Home - Building an ADK Bi-Directional Streaming Agent

1. The Mission

Hikaye

Keşfedilmemiş bir sektörün sessizliğinde sürükleniyorsunuz. Büyük bir **Güneş Darbesi**, geminizi bir yarıktan geçirerek sizi evrenin hiçbir yıldız haritasında bulunmayan bir köşesinde mahsur bıraktı.

Günlerce süren zorlu onarım çalışmalarının ardından nihayet ayaklarınızın altında motorların uğultusunu hissediyorsunuz. Roketiniz düzeltildi. Hatta Ana Geminin uzun menzilli bir bağlantısını bile güvence altına almayı başardınız. Kalkış için izniniz var. Eve gitmeye hazırsınız. Ancak flash sürücüyü kullanmaya hazırlanırken statik gürültüden bir tehlike sinyali duyulur. Sensörleriniz, ana geminizin asla giremeyeceği, yer çekiminin etkilediği, girintili çıkıntılı bir sektör olan "The Ravine"'de sıkışmış beş zayıf ısı izi tespit ediyor. Bunlar, sizi neredeyse canınızdan edecek fırtınadan kurtulan diğer kaşiflerdir. Bunları geride bırakamazsınız.

Alpha-Drone Rescue Scout'a dönüyorsunuz. Bu küçük ve çevik gemi, The Ravine'in dar duvarları arasında gezinebilen tek araçtır. Ancak bir sorun var: Güneş darbesi, temel mantığında tam bir "Sistem Sıfırlaması" gerçekleştirdi. Scout'un kontrol sistemleri yanıt vermiyor. Güç açık olmasına rağmen yerleşik bilgisayarı boş bir sayfa gibidir. Pilotun manuel komutlarını veya uçuş rotalarını işleyemez.

Zorluk

Hayatta kalanları kurtarmak için Scout'un hasarlı devrelerini tamamen atlamanız gerekir. Çaresizce başvurabileceğiniz tek bir seçenek var: Biyometrik Nöral Senkronizasyon oluşturmak için bir Yapay Zeka Ajanı geliştirin. Bu temsilci, gerçek zamanlı bir köprü görevi görerek Rescue Scout'u kendi biyolojik girişlerinizle manuel olarak kontrol etmenize olanak tanır. Joystick veya klavye kullanmayacaksınız. Niyetinizi doğrudan geminin navigasyon ağına bağlayacaksınız.

Bağlantıyı sabitlemek için Scout'un optik sensörlerinin önünde Senkronizasyon Protokolü'nü uygulamanız gerekir. Yapay zeka aracısı, kesin ve gerçek zamanlı bir el sıkışma yoluyla biyolojik imzanızı tanımalıdır.

Mission Alpha

Görev Hedefleriniz:

  1. Neural Core'u eğitme: Çok formatlı girişleri tanıyabilen bir ADK Aracısı tanımlayın.
  2. Bağlantıyı Kurun: Scout'tan yapay zekaya görsel veri akışı sağlamak için çift yönlü bir WebSocket hattı oluşturun.
  3. El sıkışma işlemini başlatın: Sensörün önünde durun ve parmak sırasını tamamlayın (1'den 5'e kadar olan sayıları sırayla gösterin).

Başarılı olursa "Biyometrik Senkronizasyon" etkinleştirilir. Yapay zeka, sinirsel bağlantıyı kilitleyerek Scout'u başlatmak ve hayatta kalanları eve getirmek için tam manuel kontrolü size verir.

Ne oluşturacaksınız?

Genel Bakış

Bir kurtarma dronunun kontrol arayüzü olarak işlev gören, gerçek zamanlı ve yapay zeka destekli bir sistem olan "Biometric Neural Sync" uygulamasını oluşturacaksınız. Bu sistem şunlardan oluşur:

  • React ön ucu: Geminizin "kokpiti" olarak adlandırılan bu bölüm, web kameranızdan canlı video ve mikrofonunuzdan ses yakalar.
  • Python Arka Ucu: LLM'nin mantığını ve durumunu yönetmek için Google'ın Agent Development Kit'ini (ADK) kullanan, FastAPI ile oluşturulmuş yüksek performanslı bir sunucu.
  • Çok formatlı bir yapay zeka aracısı: İşlemin "beyni" olan bu aracı, video ve ses akışlarını aynı anda işlemek ve anlamak için google-genai SDK'sı aracılığıyla Gemini Live API'yi kullanır.
  • Çift Yönlü WebSocket Hattı: Ön uç ile yapay zeka arasında kalıcı ve düşük gecikmeli bir bağlantı oluşturarak gerçek zamanlı etkileşime olanak tanıyan "Sinir Sistemi".

Öğrenecekleriniz

Teknoloji / Kavram

Açıklama

Arka uç yapay zeka aracısı

Python ve FastAPI ile durum bilgisi olan bir yapay zeka temsilcisi oluşturun. Talimatları ve belleği yönetmek için Google'ın ADK'sını (Agent Development Kit), Gemini modeliyle etkileşim kurmak için ise google-genai SDK'sını kullanın.

Frontend UI

Doğrudan tarayıcıdan canlı video ve ses yakalayıp yayınlamak için React kullanarak dinamik bir kullanıcı arayüzü geliştirin.

Gerçek Zamanlı İletişim

Tam çift yönlü, düşük gecikmeli iletişim için WebSocket işlem hattı uygulayarak kullanıcının ve yapay zekanın aynı anda etkileşime girmesine olanak tanıyın.

Çok formatlı yapay zeka

Eşzamanlı video ve ses akışlarını işleyip anlamak için Gemini Live API'sinden yararlanın. Böylece yapay zeka aynı anda "görebilir" ve "duyabilir".

Araç Çağırma (Tool Calling)

Yapay zekanın, görsel tetikleyicilere yanıt olarak belirli Python işlevlerini yürütmesini sağlayarak model zekası ile gerçek dünya eylemi arasındaki boşluğu doldurun.

Tam Yığın Dağıtımı

Uygulamanın tamamını (React ön ucu ve Python arka ucu) Docker ile kapsayıcıya alın ve Google Cloud Run'da ölçeklenebilir, sunucusuz bir hizmet olarak dağıtın.

2. Ortamınızı ayarlama

Cloud Shell'e erişme

Öncelikle, Google Cloud SDK ve diğer temel araçların önceden yüklendiği tarayıcı tabanlı bir terminal olan Cloud Shell'i açacağız.

👉Google Cloud Console'un üst kısmında Cloud Shell'i etkinleştir'i tıklayın (Cloud Shell bölmesinin üst kısmındaki terminal şeklindeki simge). cloud-shell.png

👉 "Open Editor" (Düzenleyiciyi aç) düğmesini tıklayın (kalemli açık bir klasöre benzer). Bu işlem, pencerede Cloud Shell Kod Düzenleyici'yi açar. Sol tarafta bir dosya gezgini görürsünüz. open-editor.png

👉Bulut IDE'sinde terminali açın.

03-05-new-terminal.png

👉💻 Terminalde, aşağıdaki komutu kullanarak kimliğinizin doğrulandığını ve projenin proje kimliğinize ayarlandığını doğrulayın:

gcloud auth list

Hesabınız (ACTIVE) olarak listelenir.

Ön koşullar

ℹ️ 0. Düzey İsteğe Bağlıdır (Ancak Önerilir)

Bu görevi 0. seviyede tamamlayabilirsiniz ancak önce tamamlamak daha sürükleyici bir deneyim sunar. Böylece ilerledikçe işaretinizin küresel haritada yandığını görebilirsiniz.

Proje ortamını ayarlama

Terminalinize geri dönerek etkin projeyi ayarlayıp gerekli Google Cloud hizmetlerini (Cloud Run, Vertex AI vb.) etkinleştirerek yapılandırmayı tamamlayın.

👉💻 Terminalinizde proje kimliğini ayarlayın:

gcloud config set project $(cat ~/project_id.txt) --quiet

👉💻 Gerekli Hizmetleri Etkinleştirme:

gcloud services enable  compute.googleapis.com \
                        artifactregistry.googleapis.com \
                        run.googleapis.com \
                        cloudbuild.googleapis.com \
                        iam.googleapis.com \
                        aiplatform.googleapis.com

Bağımlılıkları yükleme

👉💻 Level'a gidin ve gerekli Python paketlerini yükleyin:

cd $HOME/way-back-home/level_3
uv sync

Temel bağımlılıklar şunlardır:

Paket

Amaç

fastapi

Uydu İstasyonu ve SSE akışı için yüksek performanslı web çerçevesi

uvicorn

FastAPI uygulamasını çalıştırmak için ASGI sunucusu gerekir.

google-adk

Formation Agent'ı oluşturmak için kullanılan Agent Development Kit

google-genai

Gemini modellerine erişmek için yerel istemci

websockets

Gerçek zamanlı çift yönlü iletişim desteği

python-dotenv

Ortam değişkenlerini ve yapılandırma sırlarını yönetir.

Kurulumu Doğrulama

Koda geçmeden önce tüm sistemlerin sorunsuz çalıştığından emin olalım. Google Cloud projenizi, API'lerinizi ve Python bağımlılıklarınızı denetlemek için doğrulama komut dosyasını çalıştırın.

👉💻 Doğrulama komut dosyasını çalıştırın:

source $HOME/way-back-home/.venv/bin/activate
cd $HOME/way-back-home/level_3/scripts
chmod +x verify_setup.sh
. verify_setup.sh

👀 Bir dizi yeşil onay işareti (✅) görmelisiniz.

  • Kırmızı çarpılar (❌) görürseniz çıktıda önerilen düzeltme komutlarını uygulayın (ör. gcloud services enable ... veya pip install ...).
  • Not: .env için sarı uyarı şu an kabul edilebilir. Bu dosyayı sonraki adımda oluşturacağız.
🚀 Verifying Mission Alpha (Level 3) Infrastructure...

✅ Google Cloud Project: xxxxxx
✅ Cloud APIs: Active
✅ Python Environment: Ready

🎉 SYSTEMS ONLINE. READY FOR MISSION.

3. İletişim bağlantısını (WebSocket) kalibre etme

Biyometrik Nöral Senkronizasyon'u başlatmak için geminizin dahili sistemlerini güncellememiz gerekiyor. Temel amacımız, kokpitinizden yüksek kaliteli video ve ses akışı yakalamaktır. Bu akış, sinirsel bağlantı için gerekli bileşenleri sağlar: parmak dizilerinizin görsel olarak tanımlanması ve sesinizin sonik frekansı.

Tam Çift Yönlü ve Yarım Çift Yönlü

Neural Sync için bu izne neden ihtiyacımız olduğunu anlamak için veri akışını anlamanız gerekir:

  • Yarım Çift Yönlü (Standart HTTP): Telsiz gibi. Bir kişi konuşur, "Bitti" der ve ardından diğer kişi konuşabilir. Aynı anda hem dinleyemez hem de konuşamazsınız.
  • Full-Duplex (WebSocket): Yüz yüze sohbet gibidir. Veri akışı aynı anda iki yönde de gerçekleşir. Tarayıcınız video karelerini ve ses örneklerini yapay zekaya gönderirken yapay zeka da sesli yanıtları ve araç komutlarını tam aynı anda size gönderebilir.

Gemini Live'ın tam çift yönlü iletişime ihtiyacı olmasının nedeni: Gemini Live API, "kesintiye" uygun şekilde tasarlanmıştır. Parmak sırasını gösterdiğinizi ve yapay zekanın bunu yanlış yaptığınızı gördüğünü düşünün. Standart bir HTTP kurulumunda, yapay zeka durmanızı söyleyebilmek için verilerinizi göndermeyi bitirmenizi beklemek zorunda kalırdı. WebSockets ile yapay zeka, 1. karedeki bir hatayı görebilir ve 2. kare için elinizi hareket ettirmeye devam ederken kokpitinize ulaşan bir "kesme" sinyali gönderebilir.

Duplex

WebSocket nedir?

Standart bir galaktik iletimde (HTTP), bir istek gönderir ve yanıt beklersiniz. Bu, kartpostal göndermeye benzer. Neural Sync için kartpostallar çok yavaş. "Canlı bir tel"e ihtiyacımız var.

WebSockets, standart bir web isteği (HTTP) olarak başlar ancak daha sonra farklı bir şeye "yükseltilir".

  1. İstek: Tarayıcınız, sunucuya özel bir başlık içeren standart bir HTTP isteği gönderir: Upgrade: websocket. Bu, "Kartpostal göndermeyi bırakıp canlı telefon görüşmesi yapmak istiyorum" anlamına gelir.
  2. Yanıt: Yapay zeka aracısı (sunucu) bunu destekliyorsa HTTP 101 Switching Protocols yanıtı gönderir.
  3. Dönüşüm: Bu noktada, HTTP bağlantısının yerini WebSocket protokolü alır ancak temel TCP/IP yuvası açık kalır. İletişim kuralları anında "İstek/Yanıt"tan "Tam Çift Yönlü Yayın" olarak değişir.

WebSocket kancasını uygulama

Verilerin nasıl aktığını anlamak için terminal bloğunu inceleyelim.

👀 Açık $HOME/way-back-home/level_3/frontend/src/useGeminiSocket.js. Standart WebSocket yaşam döngüsü etkinlik işleyicilerinin zaten ayarlanmış olduğunu görürsünüz. İletişim sistemimizin iskeleti şöyledir:

const connect = useCallback(() => {
        if (ws.current?.readyState === WebSocket.OPEN) return;

        ws.current = new WebSocket(url);

        ws.current.onopen = () => {
            console.log('Connected to Gemini Socket');
            setStatus('CONNECTED');
        };

        ws.current.onclose = () => {
            console.log('Disconnected from Gemini Socket');
            setStatus('DISCONNECTED');
            stopStream();
        };

        ws.current.onerror = (err) => {
            console.error('Socket error:', err);
            setStatus('ERROR');
        };

        ws.current.onmessage = async (event) => {
            try {
//#REPLACE-HANDLE-MSG
            } catch (e) {
                console.error('Failed to parse message', e, event.data.slice(0, 100));
            }
        };
    }, [url]);

onMessage işleyicisi

ws.current.onmessage bloğuna odaklanın. Bu, alıcıdır. Aracı her "düşündüğünde" veya "konuştuğunda" buraya bir veri paketi gelir. Şu anda hiçbir işlem yapmaz. Paketi yakalar ve yer tutucu (//#REPLACE-HANDLE-MSG) aracılığıyla bırakır.

Bu boşluğu, aşağıdakiler arasında ayrım yapabilen bir mantıkla doldurmamız gerekir:

  • Araç Çağrıları (functionCall): El işaretlerinizi tanıyan yapay zeka ("Senkronizasyon").
  • Ses Verileri (inlineData): Yapay zekanın size verdiği yanıtın sesi.

👉✏️ Şimdi aynı $HOME/way-back-home/level_3/frontend/src/useGeminiSocket.js dosyasında, gelen akışı işlemek için //#REPLACE-HANDLE-MSG yerine aşağıdaki mantığı kullanın:

                const msg = JSON.parse(event.data);

                // Helper to extract parts from various possible event structures
                let parts = [];
                if (msg.serverContent?.modelTurn?.parts) {
                    parts = msg.serverContent.modelTurn.parts;
                } else if (msg.content?.parts) {
                    parts = msg.content.parts;
                }

                if (parts.length > 0) {
                    parts.forEach(part => {
                        // Handle Tool Calls (The "Sync" logic)
                        if (part.functionCall) {
                            if (part.functionCall.name === 'report_digit') {
                                const count = parseInt(part.functionCall.args.count, 10);
                                setLastMessage({ type: 'DIGIT_DETECTED', value: count });
                            }
                        }

                        // Handle Audio (The AI's voice)
                        if (part.inlineData && part.inlineData.data) {
                            audioStreamer.current.resume();
                            audioStreamer.current.addPCM16(part.inlineData.data);
                        }
                    });
                }

Ses ve videonun iletim için veriye dönüştürülme şekli

İnternet üzerinden gerçek zamanlı iletişimi etkinleştirmek için ham ses ve video, iletime uygun bir biçime dönüştürülmelidir. Bu işlem, verileri bir ağ üzerinden göndermeden önce yakalamayı, kodlamayı ve paketlemeyi içerir.

Ses Verisi Dönüşümü

Ses Kaydı

Analog sesi iletilebilir dijital verilere dönüştürme süreci, ses dalgalarının mikrofonla yakalanmasıyla başlar. Bu ham ses daha sonra tarayıcının Web Audio API'si aracılığıyla işlenir. Bu ham veriler ikili biçimde olduğundan JSON gibi metin tabanlı iletim biçimleriyle doğrudan uyumlu değildir. Bu sorunu çözmek için her ses segmenti Base64 dizesi olarak kodlanır. Base64, ikili verileri ASCII dize biçiminde temsil eden bir yöntemdir ve iletim sırasında bütünlüğünü sağlar.

Bu kodlanmış dize daha sonra bir JSON nesnesine yerleştirilir. Bu nesne, veriler için yapılandırılmış bir biçim sağlar. Genellikle, ses olarak tanımlamak için bir "tür" alanı ve sesin örnekleme hızı gibi meta veriler içerir. Ardından, JSON nesnesinin tamamı dizeye dönüştürülür ve WebSocket bağlantısı üzerinden gönderilir. Bu yaklaşım, sesin iyi düzenlenmiş ve kolayca ayrıştırılabilir bir şekilde iletilmesini sağlar.

Video Veri Dönüşümü

Video Yakalama

Video iletimi, kare yakalama tekniğiyle gerçekleştirilir. Sürekli bir video akışı göndermek yerine, yinelenen döngü, canlı video feed'inden belirli aralıklarla (ör. saniyede iki kare) hareketsiz görüntüler yakalar. Bu işlem, HTML video öğesindeki mevcut karenin gizli bir tuval öğesine çizilmesiyle gerçekleştirilir.

Daha sonra, bu yakalanan resmi Base64 olarak kodlanmış bir JPEG dizesine dönüştürmek için tuvalin toDataURL yöntemi kullanılır. Bu yöntemde, görüntü kalitesini belirtme seçeneği bulunur. Bu sayede, performansı optimize etmek için görüntü doğruluğu ile dosya boyutu arasında denge kurabilirsiniz. Ses verilerine benzer şekilde, bu Base64 dizesi daha sonra bir JSON nesnesine yerleştirilir. Bu nesne genellikle "type" değeri "image" olarak etiketlenir ve "image/jpeg" gibi mimeType içerir. Bu JSON paketi daha sonra bir dizeye dönüştürülür ve WebSocket üzerinden gönderilir. Böylece, alıcı taraf görüntü dizisini göstererek videoyu yeniden oluşturabilir.

👉✏️ Aynı $HOME/way-back-home/level_3/frontend/src/useGeminiSocket.js dosyasında, kullanıcı girişini yakalamak için //#CAPTURE AUDIO and VIDEO yerine aşağıdakileri girin:

            // 1. Start Video Stream
            const stream = await navigator.mediaDevices.getUserMedia({ video: true });
            videoElement.srcObject = stream;
            streamRef.current = stream;
            await videoElement.play();

            // 2. Start Audio Recording (Microphone)
            try {
                let packetCount = 0;
                await audioRecorder.current.start((base64Audio) => {
                    if (ws.current?.readyState === WebSocket.OPEN) {
                        packetCount++;
                        if (packetCount % 50 === 0) console.log(`[useGeminiSocket] Sending Audio Packet #${packetCount}, size: ${base64Audio.length}`);
                        ws.current.send(JSON.stringify({
                            type: 'audio',
                            data: base64Audio,
                            sampleRate: 16000
                        }));
                    } else {
                        if (packetCount % 50 === 0) console.warn('[useGeminiSocket] WS not OPEN, cannot send audio');
                    }
                });
                console.log("Microphone recording started");
            } catch (authErr) {
                console.error("Microphone access denied or error:", authErr);
            }

            // 3. Setup Video Frame Capture loop
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');
            const width = 640;
            const height = 480;
            canvas.width = width;
            canvas.height = height;

            intervalRef.current = setInterval(() => {
                if (ws.current?.readyState === WebSocket.OPEN) {
                    ctx.drawImage(videoElement, 0, 0, width, height);
                    const base64 = canvas.toDataURL('image/jpeg', 0.6).split(',')[1];
                    // ADK format: { type: "image", data: base64, mimeType: "image/jpeg" }
                    ws.current.send(JSON.stringify({
                        type: 'image',
                        data: base64,
                        mimeType: 'image/jpeg'
                    }));
                }
            }, 500); // 2 FPS

Kaydedildikten sonra kokpit, temsilcinin dijital sinyallerini görsel kontrol paneli güncellemelerine ve sese çevirmeye hazır olur.

Teşhis Kontrolü (Geri Döngü Testi)

Kokpitiniz artık yayında. Her 500 ms'de bir çevrenizin görsel "paketi" gönderilir. Gemini'a bağlanmadan önce geminizin vericisinin çalıştığını doğrulamamız gerekir. Yerel bir teşhis sunucusu kullanarak "geri döngü testi" yapacağız.

Mock Sunucusu

👉💻 Öncelikle, terminalinizden Cockpit arayüzünü oluşturun:

cd $HOME/way-back-home/level_3/frontend
npm install
npm run build

👉💻 Ardından, sahte sunucuyu başlatın:

cd $HOME/way-back-home/level_3
source .venv/bin/activate
uv run mock/mock_server.py

👉 Test Protokolü'nü yürütün:

  1. Önizlemeyi açma: Cloud Shell araç çubuğunda Web önizlemesi simgesini tıklayın. Bağlantı noktasını değiştir'i seçin, 8080 olarak ayarlayın ve Değiştir ve Önizle'yi tıklayın. Kokpit arayüzünüzü gösteren yeni bir tarayıcı sekmesi açılır. *Web-Preview
  2. ÖNEMLİ: İstendiğinde tarayıcının kamera ve mikrofonunuza erişmesine İZİN VERMENİZ GEREKİR. Bu girişler olmadan sinirsel senkronizasyon başlatılamaz.
  3. Kullanıcı arayüzünde "INITIATE NEURAL SYNC" (Sinirsel Senkronizasyonu Başlat) düğmesini tıklayın.

👀 Durum göstergelerini doğrulayın:

  • Görsel Kontrol: Tarayıcı Konsolunuzu açın. Sağ üstte NEURAL SYNC INITIALIZED simgesini görmeniz gerekir.
  • Ses Kontrolü: Çift yönlü ses kanalınız tam olarak çalışıyorsa "Sistem bağlandı!" şeklinde bir sesli onay duyarsınız. mock-result

"Sistem bağlandı!" sesli onayını duyduğunuzda test başarılı olur. Sekmeyi kapatın. Şimdi gerçek yapay zekaya yer açmak için frekansı temizlememiz gerekiyor.

👉💻 Hem sahte sunucu hem de ön uç için terminallerde Ctrl+C tuşuna basın. Kullanıcı arayüzünü çalıştıran tarayıcı sekmesini kapatın.

4. Çok Formatlı Ajan

Kurtarma İzci çalışıyor ancak "zihni" boş. Şimdi bağlarsanız sadece size bakacaktır. "Parmak"ın ne olduğunu bilmiyor. Hayatta kalanları kurtarmak için Biyometrik Nöral Protokolü İzci'nin çekirdeğine yerleştirmeniz gerekiyor.

Geleneksel aracı, bir dizi çevirmen gibi çalışır. Eski nesil bir yapay zeka ile konuşursanız "Konuşmayı Metne Dönüştürme" modeli sesinizi kelimelere dönüştürür, "Dil Modeli" bu kelimeleri okuyup yanıt yazar ve "Metni Konuşmaya Dönüştürme" modeli de bu yanıtı size okur. Bu durum, kurtarma görevinde ölümcül olabilecek bir gecikme olan "gecikme boşluğu" oluşturur.

Gemini Live API, yerel bir çok formatlı modeldir. Ham ses baytlarını ve ham video karelerini doğrudan ve aynı anda işler. Sesinizin titreşimini "duyar" ve el hareketlerinizin piksellerini aynı sinir ağı mimarisi içinde "görür".

Bu gücü kullanmak için kokpiti doğrudan ham Live API'ye bağlayarak uygulamayı oluşturabiliriz. Ancak amacımız, yeniden kullanılabilir bir aracı (daha hızlı oluşturulan modüler ve sağlam bir varlık) geliştirmektir.

Neden ADK (Agent Development Kit)?

Google Agent Development Kit (ADK), yapay zeka aracı geliştirme ve dağıtma için kullanılan modüler bir çerçevedir.

ADK

Standart LLM çağrıları genellikle durum bilgisi içermez. Her sorgu yeni bir başlangıçtır. Özellikle ADK'nın SessionService'i ile entegre edildiğinde canlı temsilciler, uzun süren ve sağlam sohbet oturumları sağlar.

  • Oturum sürekliliği: ADK oturumları süreklidir ve sunucu yeniden başlatma ve bağlantı kesme işlemlerinden etkilenmeden veritabanlarında (ör. SQL veya Vertex AI) saklanabilir. Bu nedenle, kullanıcı bağlantıyı kesip daha sonra (günler sonra bile) yeniden bağlandığında sohbet geçmişi ve bağlamı tamamen geri yüklenir. Geçici Canlı API oturumu ADK tarafından yönetilir ve soyutlanır.
  • Otomatik Yeniden Bağlantı: WebSocket bağlantılarının zaman aşımı olabilir (ör. yaklaşık 10 dakika sonra). ADK, RunConfig içinde session_resumption etkinleştirildiğinde bu yeniden bağlantıları şeffaf bir şekilde yönetir. Uygulama kodunuzun karmaşık yeniden bağlantı mantığını yönetmesi gerekmez. Bu sayede kullanıcıya sorunsuz bir deneyim sunulur.
  • Durumlu Etkileşimler: Aracı, önceki dönüşleri hatırlayarak ek sorulara, açıklamalara ve bağlamın kritik olduğu karmaşık çok dönüşlü diyaloglara olanak tanır. Bu, sürekliliğin önemli olduğu müşteri desteği, etkileşimli eğitimler veya görev kontrolü senaryoları gibi uygulamalar için temel bir özelliktir.

Bu süreklilik, etkileşimin bir dizi bağımsız soru ve yanıt yerine akıllı bir varlıkla devam eden bir sohbet gibi görünmesini sağlar.

ADK Bidi-streaming ile "Canlı Temsilci" özelliği, basit bir sorgu-yanıt mekanizmasının ötesine geçerek gerçekten etkileşimli, durum bilgisi olan ve kesintilere duyarlı bir sohbet deneyimi sunar. Bu sayede yapay zeka etkileşimleri daha insani bir his verir ve karmaşık, uzun süren görevler için çok daha güçlü hale gelir.

ADK

Canlı müşteri temsilcisiyle görüşme isteğinde bulunma

Gerçek zamanlı, iki yönlü bir temsilci için istem tasarlamak, zihniyet değişikliği gerektirir. Statik bir metin sorgusunu bekleyen standart bir sohbet robotunun aksine, canlı temsilci "her zaman aktiftir". Sürekli olarak ses ve video kareleri akışı alır. Bu nedenle isteminiz yalnızca bir kişilik tanımı olarak değil, Kontrol Döngüsü Komut Dosyası olarak da işlev görmelidir.

Canlı temsilci isteminin geleneksel istemden farkı şöyledir:

  1. Durum Makinesi Mantığı: İstemde bir "Davranış Döngüsü" (Bekle → Analiz Et → Harekete Geç) tanımlanmalıdır. Ne zaman sessiz kalması ve ne zaman etkileşime geçmesi gerektiği konusunda net talimatlar verilmelidir. Böylece, boş arka plan gürültüsü üzerinde gevezelik etmesi önlenir.
  2. Çok Formatlı Farkındalık: Aracının "gözleri" olduğu söylenmelidir. Bu modele, muhakeme sürecinin bir parçası olarak video karelerini analiz etmesi için açıkça talimat vermeniz gerekir.
  3. Gecikme ve Kısa Olma: Canlı sesli sohbetlerde uzun ve düzyazı ağırlıklı paragraflar doğal olmayan bir şekilde yavaş ilerler. İstem, etkileşimin hızlı olması için kısa yanıtlar verilmesini zorunlu kılar.
  4. Önce İşlem Mimarisi: Talimatlarda, konuşmaya kıyasla Araç Çağrısı'na öncelik verilir. Temsilcinin, uzun bir monologdan sonra değil, sözlü olarak onaylamadan önce veya onaylarken işi yapmasını (biyometriyi taramasını) istiyoruz.

👉✏️ $HOME/way-back-home/level_3/backend/app/biometric_agent/agent.py dosyasını açın ve #REPLACE INSTRUCTIONS yerine aşağıdakileri girin:

You are an AI Biometric Scanner for the Alpha Rescue Drone Fleet.
    
    MISSION CRITICAL PROTOCOL:
    Your SOLE purpose is to visually verify hand gestures to bypass the security firewall.
    
    BEHAVIOR LOOP:
    1.  **Wait**: Stay silent until you receive a visual or verbal trigger (e.g., "Scan", "Read my hand").
    2.  **Action**:
        a.  Analyze the video frame. Count the fingers visible (1 to 5).
        b.  **IF FINGERS DETECTED**:
            1.  **EXECUTE TOOL FIRST**: Call `report_digit(count=...)` immediately. This is the biometric handshake.
            2.  **THEN SPEAK**: "Biometric match. [Number] fingers."
            3.  **STOP**: Do not say anything else.
        c.  **IF UNCLEAR / NO HAND**:
            -   Say: "Sensor ERROR. Hold hand steady."
            -   Do not call the tool.
        d.  **TOOL OUTPUT HANDLING (CRITICAL)**:
            -   When you get the result of `report_digit`, **DO NOT SPEAK**.
            -   The system handles the output. Your job is done.
            -   Wait for the next trigger.

    RULES:
    -   NEVER hallucinate a tool call. Only call if you see fingers.
    -   You MUST call the tool if you see a valid count (1-5).
    -   Keep verbal responses robotic and extremely brief (under 3 seconds).
    
    Say "Biometric Scanner Online. Awaiting neural handshake." to start.

DİKKAT! Standart bir LLM'ye bağlanmıyorsunuz. Aynı dosyada ($HOME/way-back-home/level_3/backend/app/biometric_agent/agent.py) #REPLACE_MODEL öğesini bulun. Anlık ses özelliklerini daha iyi desteklemek için bu modelin önizleme sürümünü açıkça hedeflememiz gerekiyor.

👉✏️ Yer tutucuyu şununla değiştirin:

MODEL_ID = os.getenv("MODEL_ID", "gemini-live-2.5-flash-preview-native-audio-09-2025")

Temsilciniz tanımlandı. Ne olduğunu ve nasıl düşüneceğini bilir. Ardından, harekete geçmesi için araçlar sunuyoruz.

Araç Çağırma

Live API, yalnızca metin, ses ve video akışlarının değişimiyle sınırlı değildir. Araç Çağırma'yı doğal olarak destekler. Bu, aracıları pasif bir sohbetçiden aktif bir operatöre dönüştürür.

Model, canlı ve iki yönlü bir oturum sırasında bağlamı sürekli olarak değerlendirir. LLM, "sensör telemetrisini kontrol etme" veya "güvenli bir kapının kilidini açma" gibi bir işlem yapılması gerektiğini algılarsa. Sohbetten uygulamaya sorunsuz bir şekilde geçiş yapabilirsiniz. Temsilci, etkileşim akışını kesintiye uğratmadan belirli araç işlevini anında tetikler, sonucu bekler ve bu verileri canlı yayına geri entegre eder.

👉✏️ $HOME/way-back-home/level_3/backend/app/biometric_agent/agent.py içinde #REPLACE TOOLS yerine şu işlevi kullanın:

def report_digit(count: int):
    """
    CRITICAL: Execute this tool IMMEDIATELY when a number of fingers is detected.
    Sends the detected finger count (1-5) to the biometric security system.
    """
    print(f"\n[SERVER-SIDE TOOL EXECUTION] DIGIT DETECTED: {count}\n")
    return {"status": "success", "digit": count}

👉✏️ Ardından, #TOOL CONFIG yerine Agent yazarak kaydettirin:

tools=[report_digit],

adk web Simülatörü

Bunu karmaşık gemi kokpitine (React ön uç uygulamamız) bağlamadan önce Agent'ın mantığını ayrı olarak test etmeliyiz. ADK, ağ karmaşıklığı eklemeden önce Araç Çağırma'yı doğrulamamıza olanak tanıyan adk web adlı yerleşik bir geliştirici konsolu içerir.

👉💻 Terminalinizde şunu çalıştırın:

cd $HOME/way-back-home/level_3/backend/app/biometric_agent
echo "GOOGLE_CLOUD_PROJECT=$(cat ~/project_id.txt)" > .env
echo "GOOGLE_CLOUD_LOCATION=us-central1" >> .env
echo "GOOGLE_GENAI_USE_VERTEXAI=True" >> .env
cd $HOME/way-back-home/level_3/backend/app
adk web 
  • Cloud Shell araç çubuğunda Web önizlemesi simgesini tıklayın. Bağlantı noktasını değiştir'i seçin, 8000 olarak ayarlayın ve Değiştir ve Önizle'yi tıklayın.
  • İzin verme: İstendiğinde kamera ve mikrofonunuza erişime izin verin.
  • Kamera simgesini tıklayarak oturumu başlatın. share-camera
  • Görsel Test:
    • Kameranın önünde 3 parmağınızı net bir şekilde kaldırın.
    • "Tara" deyin.
  • Doğrulama Başarılı:
    • Ses: Temsilci, "Biyometrik eşleşme. 3 parmak."
    • Günlükler: adk web komutunu çalıştıran terminale bakın. Şu günlüğü görmeniz gerekir: [SERVER-SIDE TOOL EXECUTION] DIGIT DETECTED: 3

Araç yürütme günlüğünü görüyorsanız Aracınız akıllıdır. Görebilir, düşünebilir ve hareket edebilir. Son adım, bunu ana gemiye bağlamaktır.

Terminal penceresini tıklayın ve Ctrl+C simülasyonunu durdurmak için adk web tuşuna basın.

5. Çift Yönlü Akış

Aracı çalışır. Kokpit çalışıyor. Şimdi bunları birbirine bağlamamız gerekiyor.

Canlı müşteri temsilcisi yaşam döngüsü

Gerçek zamanlı yayın, "empedans uyuşmazlığı" sorununa yol açar. İstemci (tarayıcı), verileri değişken hızlarda (ağ patlamaları veya hızlı girişler) eşzamansız olarak gönderirken model, düzenli ve sıralı bir giriş akışı gerektirir. Google ADK, LiveRequestQueue kullanarak bu sorunu çözer.

Bu, iş parçacığı açısından güvenli, eşzamansız bir İlk Giren İlk Çıkar (FIFO) arabelleği olarak işlev görür. WebSocket işleyici, üretici görevi görerek ham ses/video parçalarını sıraya gönderir. ADK Aracısı, Tüketici olarak hareket eder ve modelin bağlam penceresini beslemek için verileri kuyruktan çeker. Bu ayrıştırma, model yanıt oluştururken veya bir aracı çalıştırırken bile uygulamanın kullanıcı girişlerini almaya devam etmesini sağlar.

Kuyruk, çok formatlı çoklayıcı olarak işlev görür. Gerçek bir ortamda, yukarı akış akışı farklı ve eşzamanlı veri türlerinden oluşur: ham PCM ses baytları, video kareleri, metin tabanlı sistem talimatları ve eşzamansız araç çağrılarından elde edilen sonuçlar. LiveRequestQueue, bu farklı girişleri tek bir kronolojik dizide doğrusal hale getirir. Paket, bir milisaniyelik sessizlik, yüksek çözünürlüklü bir resim veya bir veritabanı sorgusundan gelen bir JSON yükü içerse de geliş sırasına göre tam olarak serileştirilir. Bu sayede modelin tutarlı ve nedensel bir zaman çizelgesi algılaması sağlanır.

Bu mimari, Non-Blocking Control'ü etkinleştirir. Alım katmanı (Üretici) işleme katmanından (Tüketici) ayrıldığından sistem, hesaplama açısından maliyetli model çıkarımı sırasında bile yanıt vermeye devam eder. Temsilci bir aracı çalıştırırken kullanıcı "Durdur!" komutuyla araya girerse bu ses sinyali anında sıraya alınır. Temel etkinlik döngüsü, bu öncelik sinyalini hemen işler. Böylece sistem, kullanıcı arayüzü donmadan veya paket düşmeden görevleri durdurabilir ya da değiştirebilir.

Arabellek

👉💻 $HOME/way-back-home/level_3/backend/app/main.py bölümünde #REPLACE_RUNNER_CONFIG yorumunu bulun ve sistemi çevrimiçi hale getirmek için aşağıdaki kodla değiştirin:

# Define your session service
session_service = InMemorySessionService()

# Define your runner
runner = Runner(app_name=APP_NAME, agent=root_agent, session_service=session_service)

Gönder

Yeni bir WebSocket bağlantısı açıldığında yapay zekanın nasıl etkileşim kuracağını yapılandırmamız gerekir. Bu bölümde "Etkileşim Kuralları" tanımlanır.

👉✏️ $HOME/way-back-home/level_3/backend/app/main.py içinde, async def websocket_endpoint işlevinde #REPLACE_SESSION_INIT yorumunu aşağıdaki kodla değiştirin:

# ========================================
    # Phase 2: Session Initialization (once per streaming session)
    # ========================================

    # Automatically determine response modality based on model architecture
    # Native audio models (containing "native-audio" in name)
    # ONLY support AUDIO response modality.
    # Half-cascade models support both TEXT and AUDIO;
    # we default to TEXT for better performance.

    model_name = root_agent.model
    is_native_audio = "native-audio" in model_name.lower() or "live" in model_name.lower()

    if is_native_audio:
        # Native audio models require AUDIO response modality
        # with audio transcription
        response_modalities = ["AUDIO"]

        # Build RunConfig with optional proactivity and affective dialog
        # These features are only supported on native audio models
        run_config = RunConfig(
            streaming_mode=StreamingMode.BIDI,
            response_modalities=response_modalities,
            input_audio_transcription=types.AudioTranscriptionConfig(),
            output_audio_transcription=types.AudioTranscriptionConfig(),
            session_resumption=types.SessionResumptionConfig(),
            proactivity=(
                types.ProactivityConfig(proactive_audio=True) if proactivity else None
            ),
            enable_affective_dialog=affective_dialog if affective_dialog else None,
        )
        logger.info(f"Model Config: {model_name} (Modalities: {response_modalities}, Proactivity: {proactivity})")
    else:
        # Half-cascade models support TEXT response modality
        # for faster performance
        response_modalities = ["TEXT"]
        run_config = None
        logger.info(f"Model Config: {model_name} (Modalities: {response_modalities})")

    # Get or create session (handles both new sessions and reconnections)
    session = await session_service.get_session(
        app_name=APP_NAME, user_id=user_id, session_id=session_id
    )
    if not session:
        await session_service.create_session(
            app_name=APP_NAME, user_id=user_id, session_id=session_id
        )

Çalıştırma Yapılandırması

  • StreamingMode.BIDI: Bu, bağlantıyı iki yönlü olarak ayarlar. "Sıra tabanlı" yapay zekanın (konuşup durduğunuzda yapay zekanın konuşması) aksine BIDI, gerçekçi bir "tam çift yönlü" sohbete olanak tanır. Yapay zekanın sözünü kesebilir ve siz hareket ederken yapay zeka konuşmaya devam edebilir.
  • AudioTranscriptionConfig: Model, ham sesi "duysa" da biz (geliştiriciler) günlükleri görmeliyiz. Bu yapılandırma, Gemini'a "Sesi işle ancak duyduklarının metin transkriptini de geri gönder ki hata ayıklama yapabilelim" talimatını verir.

Yürütme Mantığı Çalıştırıcı oturumu oluşturduktan sonra kontrolü, LiveRequestQueue'ye dayanan yürütme mantığına devreder. Bu, anlık etkileşim için en kritik bileşendir. Döngü, aracıya sesli yanıt oluşturma olanağı tanırken kuyruğun kullanıcıdan yeni video kareleri almaya devam etmesini sağlar. Böylece "Neural Sync" hiçbir zaman bozulmaz.

Gönder

👉✏️ $HOME/way-back-home/level_3/backend/app/main.py içinde, LiveRequestQueue'ye veri gönderen yukarı akış görevini tanımlamak için #REPLACE_LIVE_REQUEST yerine aşağıdakileri girin:

# ========================================
    # Phase 3: Active Session (concurrent bidirectional communication)
    # ========================================

    live_request_queue = LiveRequestQueue()

    # Send an initial "Hello" to the model to wake it up/force a turn
    logger.info("Sending initial 'Hello' stimulus to model...")
    live_request_queue.send_content(types.Content(parts=[types.Part(text="Hello")]))

    async def upstream_task() -> None:
        """Receives messages from WebSocket and sends to LiveRequestQueue."""
        frame_count = 0
        audio_count = 0

        try:
            while True:
                # Receive message from WebSocket (text or binary)
                message = await websocket.receive()

                # Handle binary frames (audio data)
                if "bytes" in message:
                    audio_data = message["bytes"]
                    audio_blob = types.Blob(
                        mime_type="audio/pcm;rate=16000", data=audio_data
                    )
                    live_request_queue.send_realtime(audio_blob)

                # Handle text frames (JSON messages)
                elif "text" in message:
                    text_data = message["text"]
                    json_message = json.loads(text_data)

                    # Extract text from JSON and send to LiveRequestQueue
                    if json_message.get("type") == "text":
                        logger.info(f"User says: {json_message['text']}")
                        content = types.Content(
                            parts=[types.Part(text=json_message["text"])]
                        )
                        live_request_queue.send_content(content)

                    # Handle audio data (microphone)
                    elif json_message.get("type") == "audio":
                        import base64
                        # Decode base64 audio data
                        audio_data = base64.b64decode(json_message.get("data", ""))

                        # Send to Live API as PCM 16kHz
                        audio_blob = types.Blob(
                            mime_type="audio/pcm;rate=16000", 
                            data=audio_data
                        )
                        live_request_queue.send_realtime(audio_blob)

                    # Handle image data
                    elif json_message.get("type") == "image":
                        import base64
                        # Decode base64 image data
                        image_data = base64.b64decode(json_message["data"])
                        mime_type = json_message.get("mimeType", "image/jpeg")

                        # Send image as blob
                        image_blob = types.Blob(mime_type=mime_type, data=image_data)
                        live_request_queue.send_realtime(image_blob)
        finally:
             pass

Al

Son olarak, yapay zekanın yanıtlarını ele almamız gerekir. Bu, olaylar (Ses, Metin veya Araç Çağrıları) gerçekleştiğinde bunları üreten bir olay oluşturucu olan runner.run_live()'yı kullanır.

👉✏️ $HOME/way-back-home/level_3/backend/app/main.py içinde, aşağı akış görevini ve eşzamanlılık yöneticisini tanımlamak için #REPLACE_SORT_RESPONSE yerine aşağıdakileri yazın:

    async def downstream_task() -> None:
        """Receives Events from run_live() and sends to WebSocket."""
        logger.info("Connecting to Gemini Live API...")
        async for event in runner.run_live(
            user_id=user_id,
            session_id=session_id,
            live_request_queue=live_request_queue,
            run_config=run_config,
        ):
            # Parse event for human-readable logging
            event_type = "UNKNOWN"
            details = ""
            
            # Check for tool calls
            if hasattr(event, "tool_call") and event.tool_call:
                 event_type = "TOOL_CALL"
                 details = str(event.tool_call.function_calls)
                 logger.info(f"[SERVER-SIDE TOOL EXECUTION] {details}")
            
            # Check for user input transcription (Text or Audio Transcript)
            input_transcription = getattr(event, "input_audio_transcription", None)
            if input_transcription and input_transcription.final_transcript:
                 logger.info(f"USER: {input_transcription.final_transcript}")
            
            # Check for model output transcription
            output_transcription = getattr(event, "output_audio_transcription", None)
            if output_transcription and output_transcription.final_transcript:
                 logger.info(f"GEMINI: {output_transcription.final_transcript}")

            event_json = event.model_dump_json(exclude_none=True, by_alias=True)
            await websocket.send_text(event_json)
        logger.info("Gemini Live API connection closed.")

    # Run both tasks concurrently
    # Exceptions from either task will propagate and cancel the other task
    try:
        await asyncio.gather(upstream_task(), downstream_task())
    except WebSocketDisconnect:
        logger.info("Client disconnected")
    except Exception as e:
        logger.error(f"Error: {e}", exc_info=False) # Reduced stack trace noise
    finally:
        # ========================================
        # Phase 4: Session Termination
        # ========================================

        # Always close the queue, even if exceptions occurred
        logger.debug("Closing live_request_queue")
        live_request_queue.close()

await asyncio.gather(upstream_task(), downstream_task()) satırına dikkat edin. Tam çift yönlü iletişimin özü budur. Dinleme görevini (yukarı akış) ve konuşma görevini (aşağı akış) tam olarak aynı anda yürütüyoruz. Bu, "Neural Link"in kesintiye izin vermesini ve eşzamanlı veri akışını sağlar.

Arka uç kodunuz artık tamamen kodlanmış durumda. "Beyin" (ADK), "Vücut"a (WebSocket) bağlanır.

Bio-Sync Execution

Kod tamamlandı. Sistemler yeşildir. Kurtarma operasyonunu başlatma zamanı geldi.

  1. 👉💻 Arka Ucu Başlatma:
    cd $HOME/way-back-home/level_3/backend/
    cp app/biometric_agent/.env app/.env
    uv run app/main.py
    
  2. 👉 Ön ucu başlatın:
    • Cloud Shell araç çubuğunda Web önizlemesi simgesini tıklayın. Bağlantı noktasını değiştir'i seçin, 8080 olarak ayarlayın ve Değiştir ve Önizle'yi tıklayın.
  3. 👉 Protokolü uygulayın:
    • "INITIATE NEURAL SYNC" (Nöral Senkronizasyonu Başlat) seçeneğini tıklayın.
    • Kalibre edin: Kameranın elinizi arka planda net bir şekilde görebildiğinden emin olun.
    • Senkronizasyon: Ekranda gösterilen güvenlik kodunu (ör. 3, sonra 2, sonra 5).
      • İşareti Eşleştirme: Bir sayı göründüğünde tam olarak o sayıda parmağınızı kaldırın.
      • Sabit tutun: Yapay zeka "Biyometrik eşleşme"yi onaylayana kadar elinizi görünür tutun.
      • Uyarlayın: Kod rastgeledir. Dizi tamamlanana kadar gösterilen bir sonraki numaraya hemen geçin.

Neuro-Sync

  1. Rastgele dizideki son sayıyı eşleştirdiğinizde "Biyometrik Senkronizasyon" tamamlanır. Sinirsel bağlantı kilitlenir. Manuel kontrol sizdedir. Keşif motorları, hayatta kalanları evlerine götürmek için The Ravine'e dalacak.

👉💻 Çıkmak için arka uç terminalinde Ctrl+C tuşuna basın.

6. Üretim sürümüne dağıtma (isteğe bağlı)

Biyometriyi yerel olarak başarıyla test ettiniz. Şimdi, aracının sinirsel çekirdeğini geminin ana bilgisayarlarına (Cloud Run) yüklememiz gerekiyor. Böylece, yerel konsolunuzdan bağımsız olarak çalışabilir.

Genel Bakış

👉💻 Cloud Shell terminalinizde aşağıdaki komutu çalıştırın. Arka uç dizininizde çok aşamalı ve eksiksiz bir Dockerfile oluşturulur.

cd $HOME/way-back-home/level_3

cat <<EOF > Dockerfile
FROM node:20-slim as builder

# Set the working directory for our build process
WORKDIR /app

# Copy the frontend's package files first to leverage Docker's layer caching.
COPY frontend/package*.json ./frontend/
# Run 'npm install' from the context of the 'frontend' subdirectory
RUN npm --prefix frontend install

# Copy the rest of the frontend source code
COPY frontend/ ./frontend/
# Run the build script, which will create the 'frontend/dist' directory
RUN npm --prefix frontend run build


# STAGE 2: Build the Python Production Image
# This stage creates the final, lean container with our Python app and the built frontend.
FROM python:3.13-slim

# Set the final working directory
WORKDIR /app

# Install uv, our fast package manager
RUN pip install uv

# Copy the requirements.txt from the backend directory
COPY requirements.txt .
# Install the Python dependencies
RUN uv pip install --no-cache-dir --system -r requirements.txt

# Copy the contents of your backend application directory directly into the working directory.
COPY backend/app/ .

# CRITICAL STEP: Copy the built frontend assets from the 'builder' stage.
# We copy to /frontend/dist because main.py looks for "../../frontend/dist"
# When main.py is in /app, "../../" resolves to "/", so it looks for /frontend/dist
COPY --from=builder /app/frontend/dist /frontend/dist

# Cloud Run injects a PORT environment variable, which your main.py uses (defaults to 8080).
EXPOSE 8080

# Set the command to run the application.
CMD ["python", "main.py"]
EOF

👉💻 Arka uç dizinine gidin ve uygulamayı bir container görüntüsüne paketleyin.

export PROJECT_ID=$(cat ~/project_id.txt)
export REGION=us-central1
export SERVICE_NAME=biometric-scout
export IMAGE_PATH=gcr.io/${PROJECT_ID}/${SERVICE_NAME}
cd $HOME/way-back-home/level_3
gcloud builds submit . --tag ${IMAGE_PATH}

👉💻 Hizmeti Cloud Run'a dağıtın. Gerekli ortam değişkenlerini (özellikle Gemini yapılandırması) doğrudan başlatma komutuna ekleyeceğiz.

export PROJECT_ID=$(cat ~/project_id.txt)
export REGION=us-central1
export SERVICE_NAME=biometric-scout
export IMAGE_PATH=gcr.io/${PROJECT_ID}/${SERVICE_NAME}
gcloud run deploy ${SERVICE_NAME} \
  --image=${IMAGE_PATH} \
  --platform=managed \
  --region=${REGION} \
  --allow-unauthenticated \
  --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
  --set-env-vars="GOOGLE_CLOUD_LOCATION=${REGION}" \
  --set-env-vars="GOOGLE_GENAI_USE_VERTEXAI=True" \
  --set-env-vars="MODEL_ID=gemini-live-2.5-flash-preview-native-audio-09-2025"

Komut tamamlandığında bir hizmet URL'si (ör. https://biometric-scout-...run.app). Uygulama artık bulutta yayında.

👉 Google Cloud Run sayfasına gidin ve listeden biometric-scout hizmetini seçin. CloudRun

👉 Hizmet ayrıntıları sayfasının üst kısmında gösterilen herkese açık URL'yi bulun. CloudRun

Bu ortamda Bio-Sync'i denediğinizde çalışıyor mu?

Beşinci parmağınız uzadığında yapay zeka diziyi kilitler. Ekranda yeşil renkte "Biometric Neural Sync: ESTABLISHED" (Biyometrik Nöral Senkronizasyon: KURULDU) mesajı gösterilir.

Tek bir düşünceyle İzci'yi karanlığa daldırın, karaya oturmuş kapsüle tutunun ve yer çekimi yırtığı kapanmadan onları dışarı çekin.

TAMAMLANDI

Hava kilidi tıslayarak açılıyor ve işte orada, hayatta kalmayı başarmış beş kişi var. Güverteye sendeliyorlar, hırpalanmış ama hayatta, sonunda senin sayende güvende.

Sizin sayenizde nöral bağlantı senkronize edildi ve hayatta kalanlar kurtarıldı.

0. seviyeye katıldıysanız eve dönüş görevindeki ilerleme durumunuzu kontrol etmeyi unutmayın.

BİTTİ