Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Yazı Tipi BoyutlandırıcıAa
  • Anasayfa
  • Teknoloji
    • Siber Güvenlik
    • Yapay Zeka
    • Donanım
    • Bilim
  • Yazılım
  • Savunma & İstihbarat
  • Oyun
  • Yaşam
    • Finans
    • Sinema
    • Dünyadan Haberler
  • İş Birliği
Okuma: Laravel ile Sunucu Gönderilen Olaylar Kullanarak AI Yanıtlarını Akış Halinde Sağlama
Paylaş
Yazı Tipi BoyutlandırıcıAa
Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Ara
Bizi Takip Et
  • Hakkımızda
  • Gizlilik politikası
  • Tanıtım Yazısı ve Backlink Hizmeti
© 2026 Teknomers. All Rights Reserved.

Anasayfa » Laravel ile Sunucu Gönderilen Olaylar Kullanarak AI Yanıtlarını Akış Halinde Sağlama

Yazılım

Laravel ile Sunucu Gönderilen Olaylar Kullanarak AI Yanıtlarını Akış Halinde Sağlama

teknomers
Son güncelleme: 16 Temmuz 2026 11:06
teknomers
Paylaş
Paylaş

Bu yazıda, Laravel’da bir AI chatbot’un nasıl daha iyi bir kullanıcı deneyimi sunabileceğini keşfedeceğiz. İlk sürümde, bir mesaj gönderdiğinizde yanıtı almak için beş saniye beklemek zorundasınız. Ancak bu süreç, kullanıcı deneyimini olumsuz etkileyebiliyor. Kullanıcıların, gelen yanıtları anında görebilmeleri, hız algısını artırarak daha iyi bir deneyim sunacaktır. Bu nedenle streaming özelliğini kullanacağız.

<h2>
    <a name="why-sse-and-not-websockets" href="#why-sse-and-not-websockets">
    </a>
    Neden SSE ve WebSockets değil?
</h2>

<p>WebSockets çift yönlü iletişim sunar ve uzun süreli sunucu gerektirir. Ancak chat tamamlama için yalnızca tek yönlü iletişim gerekir: sunucu → tarayıcı. SSE tam olarak bunu sağlar ve Laravel 11 ile birlikte <code>response()->eventStream()</code> ile birlikte gelmektedir.</p>

<h2>
    <a name="1-stream-from-the-openai-client" href="#1-stream-from-the-openai-client">
    </a>
    1. OpenAI istemcisinden akış
</h2>

<p>Burada, ilk bölümdeki <code>ChatService</code>'i koruyarak akış özelliği ekliyoruz. <code>openai-php</code> istemcisi <code>createStreamed()</code> metodunu sunar ve bu metod delta'ların bir iterator'unu döner:</p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="cp"><?php </span>

namespace App\Services;

use Generator;
use OpenAI\Laravel\Facades\OpenAI;

class ChatService
{
// … SYSTEM_PROMPT ve reply() buradan …

<span class="cd">/ 
 * @param array<int array="" string="" content:=""> $history
 */</int></span>
<span class="k">public</span> <span class="k">function</span> <span class="n">streamReply</span><span class="p">(</span><span class="kt">array</span> <span class="nv">$history</span><span class="p">,</span> <span class="kt">string</span> <span class="nv">$userMessage</span><span class="p">):</span> <span class="kt">Generator</span>
<span class="p">{</span>
    <span class="nv">$stream</span> <span class="o">=</span> <span class="nc">OpenAI</span><span class="o">::</span><span class="nf">chat</span><span class="p">()</span><span class="o">-&gt;</span><span class="nf">createStreamed</span><span class="p">([</span>
        <span class="s1">'model'</span> <span class="o">=&gt;</span> <span class="s1">'gpt-4o-mini'</span><span class="p">,</span>
        <span class="s1">'messages'</span> <span class="o">=&gt;</span> <span class="p">[</span>
            <span class="p">[</span><span class="s1">'role'</span> <span class="o">=&gt;</span> <span class="s1">'system'</span><span class="p">,</span> <span class="s1">'content'</span> <span class="o">=&gt;</span> <span class="k">self</span><span class="o">::</span><span class="no">SYSTEM_PROMPT</span><span class="p">],</span>
            <span class="mf">...</span><span class="nv">$history</span><span class="p">,</span>
            <span class="p">[</span><span class="s1">'role'</span> <span class="o">=&gt;</span> <span class="s1">'user'</span><span class="p">,</span> <span class="s1">'content'</span> <span class="o">=&gt;</span> <span class="nv">$userMessage</span><span class="p">],</span>
        <span class="p">],</span>
        <span class="s1">'max_tokens'</span> <span class="o">=&gt;</span> <span class="mi">500</span><span class="p">,</span>
    <span class="p">]);</span>

    <span class="k">foreach</span> <span class="p">(</span><span class="nv">$stream</span> <span class="k">as</span> <span class="nv">$response</span><span class="p">)</span> <span class="p">{</span>
        <span class="nv">$delta</span> <span class="o">=</span> <span class="nv">$response</span><span class="o">-&gt;</span><span class="n">choices</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">-&gt;</span><span class="n">delta</span><span class="o">-&gt;</span><span class="n">content</span><span class="p">;</span>

        <span class="k">if</span> <span class="p">(</span><span class="nv">$delta</span> <span class="o">!==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">yield</span> <span class="nv">$delta</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

}

<p><code>Generator</code>, burada doğru bir dönüş tipidir; kontrolcü, tam yanıtı bellek içinde tutmadan parçaları geldiği gibi iletebilir.</p>

<h2>
    <a name="2-the-streaming-controller" href="#2-the-streaming-controller">
    </a>
    2. Akış kontrolcüsü
</h2>

<p>Laravel 11.19+ ile birlikte <code>response()->eventStream()</code>, SSE formatını ( <code>event:</code> / <code>data:</code> satırları) sizin için otomatik olarak yönetir:</p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="cp"><?php </span>

namespace App\Http\Controllers;

use App\Services\ChatService;
use Illuminate\Http\Request;

class ChatStreamController extends Controller
{
public function __invoke(Request $request, ChatService $chat)
{
$validated = $request->validate([
‘message’ => [‘required’, ‘string’, ‘max:2000’],
]);

    <span class="nv">$history</span> <span class="o">=</span> <span class="nv">$request</span><span class="o">-&gt;</span><span class="nf">session</span><span class="p">()</span><span class="o">-&gt;</span><span class="nf">get</span><span class="p">(</span><span class="s1>'chat_history'</span><span class="p">,</span> <span class="p">[]);</span>

    <span class="c1">// KRİTİK: oturum kilidini serbest bırakın. Akış yanıtı uzun süreli bir istektir — varsayılan dosya/veritabanı oturum sürücüsü ile bu, bu kullanıcıdan gelen diğer tüm istekleri (sayfa yüklemeleri de dahil) akış sona erinceye kadar engelleyecektir.</span>
    <span class="nv">$request</span><span class="o">-&gt;</span><span class="nf">session</span><span class="p">()</span><span class="o">-&gt;</span><span class="nf">save</span><span class="p">();</span>

    <span class="k">return</span> <span class="nf">response</span><span class="p">()</span><span class="o">-&gt;</span><span class="nf">eventStream</span><span class="p">(</span><span class="k">function</span> <span class="p">()</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$request</span><span class="p">,</span> <span class="nv">$chat</span><span class="p">,</span> <span class="nv">$history</span><span class="p">,</span> <span class="nv">$validated</span><span class="p">)</span> <span class="p">{</span>
        <span class="nv">$full</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span>

        <span class="k">foreach</span> <span class="p">(</span><span class="nv">$chat</span><span class="o">-&gt;</span><span class="nf">streamReply</span><span class="p">(</span><span class="nv">$history</span><span class="p">,</span> <span class="nv">$validated</span><span class="p">[</span><span class="s1>'message'</span><span class="p">])</span> <span class="k">as</span> <span class="nv">$chunk</span><span class="p">)</span> <span class="p">{</span>
            <span class="nv">$full</span> <span class="mf">.</span><span class="o">=</span> <span class="nv">$chunk</span><span class="p">;</span>
            <span class="k">yield</span> <span class="nv">$chunk</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="c1">// akış tamamlandığında geçmişi kalıcı hale getirin</span>
        <span class="nv">$history</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span><span class="s1>'role'</span> <span class="o">=&gt;</span> <span class="s1>'user'</span><span class="p">,</span> <span class="s1>'content'</span> <span class="o">=&gt;</span> <span class="nv">$validated</span><span class="p">[</span><span class="s1>'message'</span><span class="p">]];</span>
        <span class="nv">$history</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span><span class="s1>'role'</span> <span class="o">=&gt;</span> <span class="s1>'assistant'</span><span class="p">,</span> <span class="s1>'content'</span> <span class="o">=&gt;</span> <span class="nv">$full</span><span class="p">];</span>
        <span class="nv">$request</span><span class="o">-&gt;</span><span class="nf">session</span><span class="p">()</span><span class="o">-&gt;</span><span class="nf">put</span><span class="p">(</span><span class="s1>'chat_history'</span><span class="p">,</span> <span class="nb">array_slice</span><span class="p">(</span><span class="nv">$history</span><span class="p">,</span> <span class="o">-</span><span class="mi">20</span><span class="p">));</span>
        <span class="nv">$request</span><span class="o">-&gt;</span><span class="nf">session</span><span class="p">()</span><span class="o">-&gt;</span><span class="nf">save</span><span class="p">();</span>
    <span class="p">},</span> <span class="n">headers</span><span class="o">:</span> <span class="p">[</span>
        <span class="s1>'X-Accel-Buffering'</span> <span class="o">=&gt;</span> <span class="s1>'no'</span><span class="p">,</span> <span class="c1">// nginx'e akışı tamponlamamasını söyleyin</span>
    <span class="p">]);</span>
<span class="p">}</span>

}

<p>Bu kontrolçüyü aynı sınırlandırmalarla yönlendirin:</p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="nc">Route</span><span class="o">::</span><span class="nf">post</span><span class="p">(</span><span class="s1>'/chat/stream'</span><span class="p">,</span> <span class="nc">ChatStreamController</span><span class="o">::</span><span class="n">class</span><span class="p">)</span>
        <span class="o">-&gt;</span><span class="nf">middleware</span><span class="p">([</span><span class="s1>'auth'</span><span class="p">,</span> <span class="s1>'throttle:20,1'</span><span class="p">]);</span>

<h2>
    <a name="3-reading-the-stream-in-the-browser" href="#3-reading-the-stream-in-the-browser">
    </a>
    3. Tarayıcıda akışı okumak
</h2>

<p>Natife <code>EventSource</code> API yalnızca GET isteklerini destekler, ancak bir mesaj gövdesi POST etmemiz gerekiyor — bu nedenle akışı <code>fetch</code> ile okuyacağız:</p>

<div class="highlight js-code-highlight">
    <pre class="highlight html"><code><span class="nt"><script/></span>

async function streamChat(message, botEl) {
const res = await fetch(<span class=”dl>’/chat/stream<span class=”dl>’, {
method: <span class=”dl>’POST<span class=”dl>’,
headers: {
<span class=”dl>’Content-Type<span class=”dl>’: <span class=”dl>’application/json<span class=”dl>’,
<span class=”dl>’X-CSRF-TOKEN<span class=”dl>’: document.querySelector(<span class=”dl>’meta[name=”csrf-token”]<span class=”dl>’).content,
},
body: JSON.stringify({ message }),
});

<span class="kd">const</span> <span class="nx">reader</span> <span class="o">=</span> <span class="nx">res</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nf">getReader</span><span class="p">();</span>
<span class="kd">const</span> <span class="nx">decoder</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">TextDecoder</span><span class="p">();</span>
<span class="kd">let</span> <span class="nx">buffer</span> <span class="o">=</span> <span class="dl">''</span><span class="p">;</span>

<span class="k">while </span><span class="p">(</span><span class="kc">true</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="p">{</span> <span class="nx">done</span><span class="p">,</span> <span class="nx">value</span> <span class="p">}</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">reader</span><span class="p">.</span><span class="nf">read</span><span class="p">();</span>
    <span class="k">if </span><span class="p">(</span><span class="nx">done</span><span class="p">)</span> <span class="k">break</span><span class="p">;</span>

    <span class="nx">buffer</span> <span class="o">+=</span> <span class="nx">decoder</span><span class="p">.</span><span class="nf">decode</span><span class="p">(</span><span class="nx">value</span><span class="p">,</span> <span class="p">{</span> <span class="na">stream</span><span class="p">:</span> <span class="kc">true</span> <span class="p">});</span>

    <span class="c1">// SSE mesajları boş bir satır ile ayrılır</span>
    <span class="kd">const</span> <span class="nx">events</span> <span class="o">=</span> <span class="nx">buffer</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="dl>'</span><span class="se">\n\n</span><span class="dl>'</span><span class="p">);</span>
    <span class="nx">buffer</span> <span class="o">=</span> <span class="nx">events</span><span class="p">.</span><span class="nf">pop</span><span class="p">();</span> <span class="c1">// oluşmamış kısmı tutun</span>

    <span class="k">for </span><span class="p">(</span><span class="kd">const</span> <span class="nx">evt</span> <span class="k">of</span> <span class="nx">events</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">evt</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="dl>'</span><span class="se">\n</span><span class="dl>'</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">filter</span><span class="p">(</span><span class="nx">l</span> <span class="o">=&gt;</span> <span class="nx">l</span><span class="p">.</span><span class="nf">startsWith</span><span class="p">(</span><span class="dl>'</span><span class="s1">data: </span><span class="dl>'</span><span class="p">))</span>
            <span class="p">.</span><span class="nf">map</span><span class="p">(</span><span class="nx">l</span> <span class="o">=&gt;</span> <span class="nx">l</span><span class="p">.</span><span class="nf">slice</span><span class="p">(</span><span class="mi">6</span><span class="p">))</span>
            <span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="dl>'</span><span class="se">\n</span><span class="dl>'</span><span class="p">);</span>

        <span class="k">if</span> <span class="p">(</span><span class="nx">data</span> <span class="o">&amp;&amp;</span> <span class="nx">data</span> <span class="o">!==</span> <span class="dl>'</span><span class="s1"/><span class="dl>'</span><span class="p">)</span> <span class="p">{</span>
            <span class="nx">botEl</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">+=</span> <span class="nx">data</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

}

<p><code/> Laravel'ın varsayılan son akış işaretidir — bunu filtreleyin (veya <code>endStreamWith</code> argümanıyla özelleştirin).</p>

<p>Tüm bunları uygulayın, bir mesaj gönderin ve yanıtın kendiliğinden yazıldığını izleyin.</p>

<h2>
    <a name="where-streaming-breaks-in-production" href="#where-streaming-breaks-in-production">
    </a>
    Üretim sırasında akışta neyin yanlış gittiği
</h2>

<p>Bu bölüm çoğu eğitimin atladığı yer. Akış, <code>php artisan serve</code> ile hemen çalışır; ancak sunucuda birden bire gelir. Sorun, her zaman <strong>PHP ile tarayıcı arasında bir yerde tamponlamadır</strong>:</p>

<ul>
    <li>
        <strong>nginx</strong>, varsayılan olarak proxy cevaplarını tamponlar. Yukarıdaki <code>X-Accel-Buffering: no</code> başlığı her cevabı ayrıca devre dışı bırakır; alternatif olarak <code>proxy_buffering off;</code> ile rotayı ayarlayın.</li>
    <li>
        <strong>PHP-FPM + çıktı tamponlaması</strong>: <code>php.ini</code>'de <code>output_buffering</code> değerini kontrol edin. Laravel'ın <code>eventStream</code> her yield'den sonra temizler, ancak dış bir tampon onu yutabilir.</li>
    <li>
        <strong>Cloudflare / yük dengeleyicileri</strong>: çoğunluğu SSE içerik türlerini dikkate alır, ama öncelikle üretimde <code>curl -N</code> ile doğrulayın.</li>
</ul>

<p>Debug ipucu: <code>curl -N -X POST https://yourapp.test/chat/stream ...</code> tam olarak neyin geldiğini ve ne zaman geldiğini görmenizi sağlar, arada tarayıcı sihiri yoktur.</p>

<h2>
    <a name="production-checklist" href="#production-checklist">
    </a>
    Üretim kontrol listesi
</h2>

<ul>
    <li>
        <strong>Akış başlamadan önce oturum kilidini salın</strong> (yukarıda yapıldı) — bu, Laravel'da SSE ile "uygulamam neden askıya alınıyor" hatasının en büyük sebebidir.</li>
    <li>
        <strong><code>X-Accel-Buffering: no</code></strong> başlığı için nginx (yukarıda yapıldı).</li>
    <li>
        <strong>Timeoutlar</strong>: bir akış <code>max_execution_time</code> ve web sunucunuzun gönderme zaman aşımını geçebilir. İkisini de en kötü senaryo üretim sürenizden fazla olacak şekilde ayarlayın (60s, <code>max_tokens: 500</code> ile mantıklı bir üst sınırdır).</li>
    <li>
        <strong>Akışta hata</strong>: generator döngüsünü try/catch ile sarmalayın ve kullanıcılara "Bir şeyler ters gitti" mesajı gönderin — mesajı olmayan öldürme akışı kullanıcılar için donmuş görünür.</li>
    <li>
        <strong>Endpoint'leri hız limiti uygulayın</strong> (yukarıda yapıldı) — akış her isteğin maliyeti olduğu gerçeğini değiştirmez.</li>
    <li>
        <strong>Her şeyi akış yapmayın</strong>: arka plan işler veya kısa yanıtlar için, birinci bölümdeki tamponlanmış endpoint daha basittir. Her ikisini de tutun.</li>
</ul>

<h2>
    <a name="next-in-this-series" href="#next-in-this-series">
    </a>
    Bu seride bir sonraki
</h2>

<p>Botumuz harika bir şekilde akış yapıyor, ancak hala yalnızca sistem talimatlarında olanları biliyor. Sıradaki: <strong>RAG in Laravel — embeddings ve pgvector</strong>, onu gerçek belgelerinize cevap vermek için geliştireceğiz.</p>

<hr />

<p><em>Ben Aditya Kumar — AI destekli web ürünleri geliştiren Tech Lead &amp; tam yığın geliştirici. Laravel, Vue ve LLM API'leri ile bulabilirsiniz.</em></p>

Kaynak: Orijinal Makale

Öğrenci’den Gerçek Projelere: Angular ve Laravel Uygulamaları Geliştirirken Öğrendiklerim
Artisan Kaydırmaktan Bıktınız mı? php artisan list –mine ile Tanışın
Erişim Hakkı ve Unutulma Hakkı: GDPR’ın Çatısı Altında Aynı Şekil
Laravel Sluggable Paketi: Nihayet Düşünceli Slug Oluşturma
Bireysel Geliştiricinin Takım Gibi İnşa Etme Yöntemi: Planı Dondurma, Kesişen Noktaları Dondurma
Bu Makaleyi Paylaş
Facebook Bağlantıyı Kopyala Yazdır
Paylaş
Önceki Makale Oyun ve Konsollara Uber Eats ile Anında Erişim Sağlayın
Sonraki Makale Zoom, Hesap Ele Geçirmeye Yol Açabilecek Kritik Windows Açığını Giderdi

Sanal Medya

FacebookBeğen
452Takip Et
PinterestSabitle
237Takip Et

Son Eklenenler

OnePlus Global Pazarından Vazgeçti mi?
Liste
PS3 Oyunlarının %75’i RPCS3 ile PC’de Oynanabiliyor
Donanım
Zoom, Hesap Ele Geçirmeye Yol Açabilecek Kritik Windows Açığını Giderdi
Siber Güvenlik
Oyun ve Konsollara Uber Eats ile Anında Erişim Sağlayın
Oyun
Temmuz 2026’da PlayStation Plus Extra ve Premium Oyunları Açıklandı
Oyun
Webhook Ölçekleme: Asenkron Kuyruk Buffers
Yazılım
//

Siber güvenlik, yapay zeka ve savunma sanayiinden; finans ve sinema dünyasına uzanan geniş bir yelpaze. Teknomers; teknoloji, strateji ve yazılım dünyasını sade bir dille sizlerle buluşturuyor.

Kurumsal

  • Hakkımızda
  • Gizlilik politikası
  • Tanıtım Yazısı ve Backlink Hizmeti

Kategoriler

  • Teknoloji
  • Oyun
  • Sinema
  • Siber Güvenlik
  • Bilim
  • Finans
  • Dünyadan Güncel Haberler

Populer

  • TV'de Ücretsiz İzlenebilen Şifresiz Erotik Kanallar (2025 Güncel Frekans Listesi)

  • The Last of Us PC Kontrolleri: Hızlı Silah Değiştirme ve Tüm Tuşlar (2025)

  • Hogwarts Legacy'de Odaklanma İksiri Nasıl Yapılır?

Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Bizi Takip Et
© 2026 Teknomers. All Rights Reserved.
Welcome Back!

Sign in to your account

Kullanıcı Adı veya E-posta Adresi
Şifre

Şifrenizi mi unuttunuz?