Ana içeriğe atla

Qwen 3.5 Küçük Modeller Eğitimi: Ollama ile Video’dan Oyuna Üreteç Kurun

Kısa bir oynanış klibini yerelde Ollama üzerinden Qwen 3.5 küçük modellerle oynanabilir bir HTML oyuna dönüştürmeyi öğrenin.
Güncel 3 Eki 2026  · 14 dk. oku

Yapay Zeka ile Keşfedin

ChatGPTClaudePerplexity

Qwen 3.5, hem metin hem görsel girdileri işlemek için tasarlanmış açık kaynaklı çok modlu modeller ailesidir. Modeller, görüntü-metin->metin mimarisini izler; böylece doğal dil istemlerinin yanı sıra görüntüler veya video kareleri üzerinden akıl yürütebilirler. 

Bu eğitimde, yerel Qwen3.5 küçük qwen3.5:9b modelini Ollama üzerinden barındırarak bir oynanış videosunu oynanabilir bir tarayıcı oyununa dönüştüren bir Streamlit uygulaması kuracağız. Uygulama, yüklenen videodan kareler çıkarır, oyun mekaniğini yorumlar, bunu yapılandırılmış bir özellik setine dönüştürür ve ardından arayüz içinde doğrudan önizlenebilen eksiksiz bir HTML/CSS/JavaScript oyunu üretir.

Bu süreçte şunları öğreneceksiniz:

  • Çok modlu akıl yürütme için videolardan temsilî kareler çıkarma
  • Görüntü-metin çıkarımı için Qwen 3.5’i Ollama ile yerelde çalıştırma
  • Akıl yürütme ve kod üretimi için iki aşamalı bir istemleme hattı tasarlama
  • Görsel oynanış gözlemlerini yapılandırılmış bir oyun özelliklerine dönüştürme
  • Çıkarılan mekaniklerden oynanabilir bir HTML5 tarayıcı oyunu üretme
  • Hattı çalıştırıp önizlemek için etkileşimli bir Streamlit arayüzü oluşturma

Sonunda, kısa bir oynanış klibini tamamen yerel bir makinede üretilen basit, oynanabilir bir tarayıcı oyununa dönüştüren çalışan bir demo elde edeceksiniz. Aşağıdaki videoda iş akışının kısaltılmış bir sürümünü görebilirsiniz:

Qwen 3.5 Small nedir?

Qwen 3.5, hafif uç cihaz modellerinden sınır ölçekli akıl yürütme sistemlerine kadar geniş bir dağıtım yelpazesini kapsayacak şekilde tasarlanmış çok modlu bir model ailesidir. 

Resmi Qwen 3.5 serisi şu anda 0.8B, 2B, 4B, 9B, 27B, 35B-A3B, 122B-A10B ve 397B-A17B varyantlarını; bazı modeller için temel ve miktarsal kontrol noktalarını içermektedir. 

Doğru küçük modeli seçmek, kurmak istediğiniz iş akışının türüne bağlıdır. 

  • 0.8B ve 2B varyantları, uç cihazlar, hızlı prototipleme ve kısıtlı donanım ortamları için en uygunudur. 
  • 4B modeli bir kademe yukarı çıkar ve küçük yerel ajanlar veya hafif görsel akıl yürütme görevleri için iyi çalışır. 
  • 9B modeli, yerel olarak çalışacak kadar kompakt kalıp oynanış mekaniğini çıkarıp kullanılabilir HTML oyun kodu üretecek kadar güçlü olduğundan, yerel çok modlu uygulamalar için daha uygundur. 

Bu eğitimde, yetenek, yerel dağıtılabilirlik ve çok modlu akıl yürütme arasında güçlü bir denge sunan qwen3.5:9b modelini kullandık; böylece harici API’lara güvenmeden video-karelerinden-tarayıcı-oyununa bir hat kurmaya çok uygun hale geliyor.

Ayrıca Qwen3.5’i tek bir GPU’da yerel olarak çalıştırma rehberimizi de okuyabilirsiniz. 

Bunun için neden yerel küçük bir model kullanmalısınız?

Bu demoyu qwen/qwen3.5-397b-a17b modeliyle de test ettim. Bu model yerel 9B varyantından belirgin şekilde daha büyük ve genel olarak daha yetenekli olsa da, API tabanlı çıkarım gerektirir; bu da harici bağımlılıklar ve potansiyel gecikme getirir.

Pratikte, çalışma süresi katı bir kısıt değilse, testlerimde iki model arasındaki toplam üretim süresi yaklaşık ~3 dakika farklıydı. Daha büyük model, oyun mantığını biraz daha akıcı ve önizleme deneyimini daha cilalı üretti; ancak iyileşme dramatik değildi.

9B modelinin tamamen yerelde çalıştığı düşünülürse, sonuçlar şaşırtıcı derecede rekabetçi. Çok daha küçük boyutuna rağmen, çok daha büyük 397B modeline kıyasla yalnızca mütevazı bir kalite düşüşüyle oynanabilir tarayıcı oyunları üretebiliyor.

0.8B varyantından en iyi şekilde yararlanmak için Qwen3.5 small’u ince ayar rehberimizin tamamını okuyabilirsiniz. 

Qwen 3.5 Örnek Projesi: Video’dan Oyuna Üreteç Kurun

Bu bölümde, aşağıdakileri yapan bir Streamlit uygulaması oluşturacağız:

  • kısa bir oynanış klibini alır
  • az sayıda temsilî kare çıkarır
  • bu kareleri Ollama üzerinden qwen3.5:9b modeline gönderir
  • modelden yapılandırılmış bir oyun özellik seti çıkarmasını ister
  • modelden tek dosyalık bir HTML oyun üretmesini ister
  • üretilen oyunu Streamlit uygulaması içinde önizler

Bu kurulum, oynanış görüntülerinden mantığı tersine mühendislik etmek ve bunu koda dönüştürmek gibi görevleri destekleyen Qwen’in çok modlu yeteneklerini vurgular

Adım 1: Bağımlılıkları yükleyin

Uygulamayı oluşturmadan önce, video işleme, çok modlu çıkarım ve etkileşimli arayüz için gereken kütüphanelerle yerel bir ortam hazırlamamız gerekir. 

pip install streamlit opencv-python ollama python-dotenv pydantic

Demo küçük bir Python kütüphane seti kullanır:

  • Streamlit: Kullanıcıların oynanış videoları yükleyip üretilen oyunları önizlediği etkileşimli web arayüzünü kurmak için.
  • OpenCV: Yüklenen videoyu çözmek ve temsilî kareler çıkarmak için.
  • Ollama: Çok modlu akıl yürütme ve kod üretimi için yerel Qwen 3.5 modelini çalıştırmak için.
  • python-dotenv: Gerekirse ortam değişkenlerini yönetmek için.
  • Pydantic: Kod üretmeden önce çıkarılan oyun özellikleri için yapılandırılmış bir şema zorlamak için.

Sırada modelin indirilmesi var. Ollama, modeli doğrudan model kayıt defterinden çekerek bunu kolaylaştırır:

ollama run qwen3.5:9b

İlk çalıştırma modeli indirip yerel olarak saklar. qwen3.5:9B yaklaşık 6.6GB boyutundadır, metin ve görüntü girdilerini destekler ve 256K belirteç bağlam penceresi sunar; bu da onu çok modlu iş akışları için oldukça uygun kılar.

Adım 2: İçe aktarımlar

Yerel ortam hazır olduğuna göre, sıradaki adım uygulama genelinde kullanacağımız kütüphaneleri içe aktarmak ve üretilen çıktıların saklanacağı bir klasör oluşturmaktır. 

import json
import base64
import tempfile
from pathlib import Path
from typing import List
import cv2
import ollama
import streamlit as st
import streamlit.components.v1 as components
from pydantic import BaseModel, Field, ValidationError
OUTPUT_DIR = Path("outputs_local")
OUTPUT_DIR.mkdir(exist_ok=True)

Yukarıdaki blok, projenin çekirdek yığınını bir araya getirir. json yapılandırılmış oyun özelliklerini biçimlendirmek, base64 çıkarılan video karelerini modele göndermeden önce kodlamak ve tempfile oturum sırasında yüklenen videoları güvenle kaydetmek için kullanılır. pathlib’den Path dosya yollarını temiz bir şekilde yönetmemizi sağlar; typing’den List ise şema tanımlarında tür ipuçları için kullanılır.

Sonrasında ana uygulama kütüphanelerini içe aktarıyoruz: OpenCV’nin cv2 modülü yüklenen oynanış klibini okuyup temsilî kareler çıkarmaktan sorumludur; ollama yerelde çalışan qwen3.5:9b modeliyle iletişim kurmak için kullandığımız arayüzdür. streamlit web arayüzünü sağlar ve streamlit.components.v1 üretilen HTML oyununu canlı önizleme ve etkileşim için doğrudan uygulamaya gömmemize imkan tanır.

Son olarak, Pydantic’ten BaseModel, Field ve ValidationError’ı içe aktarıyoruz. Bunlar, çıkarılan oyun tasarımını temsil eden yapılandırılmış JSON şemasını tanımlamak ve doğrulamak için kullanılır. Modelin gevşek biçimli metin dökmesine güvenmek yerine, sonucu HTML oyun üretecine geçirmeden önce öngörülebilir bir şemaya zorlarız.

Son iki satır, yoksa bir outputs_local dizini oluşturur. Bu klasör, çıkarılan oyun özellikleri ve son HTML oyun dosyası gibi üretilen çıktıları kaydettiğimiz yerdir; böylece her çalıştırma sonrası sonuçları incelemek veya indirmek kolaylaşır.

Bir sonraki adımda, modelin çıkarılan oynanış karelerinden üretmesi gereken oyun özelliklerini tanımlayan Pydantic şema sınıflarını belirleyeceğiz.

Adım 3: Şemayı tanımlayın

Bu aşamada, modelin video karelerinden çıkarılan oynanış mekaniğini tanımlarken uyması gereken yapılandırılmış şemayı belirliyoruz. 

Modelden görüntülerden doğrudan HTML kodu üretmesini istemek yerine, önce yapılandırılmış bir oyun özellikleri seti üretmesini talep ediyoruz. Bu ara temsil, hattı hata ayıklamayı, doğrulamayı ve genişletmeyi çok daha kolay hale getirir.

class Entity(BaseModel):
    name: str
    role: str
    behavior: str
class Physics(BaseModel):
    gravity: str = ""
    jump_or_impulse: str = ""
    collision_style: str = ""
    movement_style: str = ""
class VisualStyle(BaseModel):
    perspective: str = ""
    palette: str = ""
    background: str = ""
    ui_elements: List[str] = Field(default_factory=list)
class GameSpec(BaseModel):
    title: str
    genre: str
    objective: str
    player_controls: List[str]
    gameplay_loop: List[str]
    entities: List[Entity]
    scoring_rules: List[str]
    win_condition: str
    lose_condition: str
    physics: Physics
    visual_style: VisualStyle
    assumptions: List[str]
    confidence_notes: List[str]

Bu şema, modelin üretmesi gereken oyun özelliklerinin çekirdek yapısını tanımlar.

  • Entity sınıfı oyunda bulunan oyuncu, düşmanlar, engeller veya arayüz ögeleri gibi herhangi bir nesneyi temsil eder. Her varlık bir ad, oyundaki rolü ve davranışının kısa bir açıklamasını içerir.
  • Physics sınıfı, nesnelerin oyun dünyasında nasıl hareket edip etkileştiğini açıklar. Bu alanlar yer çekimi, itki/zıplama davranışı, çarpışma ele alımı ve hareket kalıpları gibi temel mekanikleri yakalar.
  • VisualStyle sınıfı oyunun görsel sunumunu yakalar. Buna kamera perspektifi (üstten görünüm veya yandan kaydırma gibi), renk paleti, arka plan stili ve skor sayacı veya sağlık göstergesi gibi görünür arayüz ögeleri dahildir.
  • Son olarak GameSpec sınıfı her şeyi bir araya getirir. Video karelerinden çıkarılan tam oyun tasarımını; hedefi, kontrolleri, oynanış döngüsünü, varlıkları, skor sistemini, kazanma/kaybetme koşullarını ve modelin kareleri yorumlarken yaptığı varsayımları içerir.

Model çıktısını bu şemaya karşı doğrulayarak, çıkarılan tasarımın her zaman öngörülebilir bir yapıyı izlemesini sağlarız. Bu da özelliği hattın bir sonraki aşamasına aktarmayı çok daha kolaylaştırır.

Adım 4: İki aşamalı üretim için istemler

Şema hazır olduğuna göre, sırada modeli hattımızın iki aşamasında yönlendirecek istemleri tanımlamak var. 

İlk aşama, çıkarılan karelerden oynanış mekaniğini tersine mühendislik ederek yapılandırılmış bir oyun özellik seti üretmeye odaklanır. 

İkinci aşama ise bu özellik setini alıp HTML, CSS ve JavaScript kullanarak oynanabilir bir tarayıcı oyunu üretir.

Adım 4.1: Sistem istemlerini tanımlayın

İstemleme stratejimizin ilk bileşeni, hattın her aşamasında modelin üstlenmesi gereken rolü tanımlayan sistem istemleridir.

SPEC_SYSTEM_PROMPT = """
You are an expert arcade game designer and gameplay reverse-engineering analyst.
You observe a few frames from a gameplay video and infer the smallest possible playable browser-game clone.
The gameplay is likely a simple arcade game like Pong, Breakout, Snake, or Flappy Bird.
Prefer these mechanics if uncertain.
Rules:
- Only infer mechanics that are strongly supported by visual evidence.
- Prefer a minimal playable prototype over a complex clone.
- Do not invent advanced systems unless clearly visible.
- Keep the result suitable for plain HTML5 canvas and JavaScript.
- Return strict JSON only.
- No markdown.
- No code fences.
""".strip()
CODE_SYSTEM_PROMPT = """
You are a senior JavaScript game developer.
Generate a complete single-file HTML game.
Requirements:
- Return raw HTML only.
- No markdown.
- No code fences.
- Use plain HTML, CSS, and JavaScript only.
- Use HTML5 canvas unless there is a strong reason not to.
- Keep the game simple, playable, and self-contained.
- No external libraries, no CDNs, no external assets.
- Use simple shapes/colors instead of images.
- Include score.
- Include restart support.
- Include instructions on screen.
- Keep keyboard controls simple.
- Make the game playable inside an iframe preview.
- IMPORTANT: the canvas must have tabindex="0".
- IMPORTANT: keyboard input must work inside an embedded iframe.
- IMPORTANT: focus the canvas automatically on load, on canvas click, and after pressing Start/Restart buttons.
- IMPORTANT: prevent default browser behavior for arrow keys and spacebar.
""".strip()

İlk sistem istemi, modelin bir oynanış analisti gibi davranmasını söyler. Minimum varsayımı vurgular ve modelin yalnızca karelerle güçlü şekilde desteklenen mekanikleri çıkarmasını teşvik eder. 

Model yalnızca az sayıda örneklenmiş görüntü gördüğünden, istem onu açıkça hafif tarayıcı mantığıyla uygulanabilecek basit arcade tarzı oyunlara yönlendirir.

İkinci sistem istemi ise modelin rolünü bir JavaScript geliştiricisine çevirir. Bu aşamada model, yapılandırılmış bir oyun özellik seti alır ve bunu eksiksiz bir HTML oyununa dönüştürmekle sorumludur. 

Adım 4.2: Oyun-özellik istemini oluşturun

Sırada, hattın ilk aşaması için kullanıcı istemini oluşturan yardımcı bir fonksiyon tanımlıyoruz. Bu istem, modelden çıkarılan kareleri analiz edip yapılandırılmış bir oyun özellik seti üretmesini ister.

def build_spec_user_prompt(game_hint: str, extra_constraints: str, max_frames: int) -> str:
    return f"""
You are given a few still frames extracted from a gameplay video.
From these frames, infer a minimal, playable browser game design.
Use very simple 2D arcade-style mechanics.
If you are unsure, bias toward Pong / Breakout / Snake / Flappy Bird style games.
Output valid JSON with exactly this schema:
{{
  "title": "string",
  "genre": "string",
  "objective": "string",
  "player_controls": ["string"],
  "gameplay_loop": ["string"],
  "entities": [
    {{
      "name": "string",
      "role": "player|enemy|obstacle|projectile|ui|environment",
      "behavior": "string"
    }}
  ],
  "scoring_rules": ["string"],
  "win_condition": "string",
  "lose_condition": "string",
  "physics": {{
    "gravity": "string",
    "jump_or_impulse": "string",
    "collision_style": "string",
    "movement_style": "string"
  }},
  "visual_style": {{
    "perspective": "string",
    "palette": "string",
    "background": "string",
    "ui_elements": ["string"]
  }},
  "assumptions": ["string"],
  "confidence_notes": ["string"]
}}
Important:
- Only infer the simplest mechanics needed for a playable clone.
- Prefer a very simple game that can be rebuilt in one HTML file.
- Avoid menus, accounts, audio, networking, cutscenes, or multi-level progression.
Optional game hint from user:
{game_hint or "None"}
Extra constraints:
{extra_constraints or "None"}
You are seeing at most {max_frames} frames from the video, so stay simple and conservative.
Return JSON only.
""".strip()

Bu fonksiyon, çıkarılan video kareleriyle birlikte modele gönderilecek istemi oluşturur. Birkaç önemli unsuru içerir:

  • Modelin tüm video yerine hareketsiz kareleri analiz ettiğine dair bir hatırlatma
  • Basit arcade mekaniğine bir yönelim
  • Çıktının GameSpec modelimizle eşleşmesini sağlayan tam JSON şema yapısı
  • İsteğe bağlı olarak, kullanıcı arayüzün modeli belirli bir oyun tarzına yönlendirmesine imkân veren ipuçları ve kısıtlar ekleyebilir

Şemayı doğrudan isteme gömerek, modelin çıktıyı hattın bir sonraki aşamasında Pydantic tarafından doğrulanabilecek şekilde üretmesini teşvik ederiz.

Adım 4.3: Kod üretim istemini oluşturun

Oyun özellikleri oluşturulup doğrulandıktan sonra, bu özellikleri gerçek bir oynanabilir oyuna dönüştürmek için ikinci bir isteme ihtiyaç duyarız.

def build_code_user_prompt(game_spec: dict) -> str:
    spec_json = json.dumps(game_spec, indent=2)
    return f"""
Using the following game specification, generate a complete single-file HTML game.
Requirements:
- One self-contained HTML file.
- Inline CSS and inline JavaScript.
- Must be playable in a browser.
- Must render correctly in an iframe.
- Show score on screen.
- Show controls on screen.
- Add restart support using a button or key.
- Keep visuals simple and robust.
- Use requestAnimationFrame.
- Use canvas for gameplay rendering.
- No external assets.
- Make the game small but genuinely playable.
Game specification:
{spec_json}
Return raw HTML only.
""".strip()

build_code_user_prompt fonksiyonu, GameSpec sözlüğünü biçimlendirilmiş JSON’a çevirir ve isteme yerleştirir. Model daha sonra bu yapılandırılmış tanımı, son HTML oyunu üretmek için bir tasarım brifi olarak kullanır.

Özellik seti zaten mekanikleri, varlıkları, kontrolleri ve hedefleri tanımladığı için modelin görevi çok daha basitleşir. Sadece bu tasarımı JavaScript oyun mantığına ve canvas tabanlı bir çizim döngüsüne çevirmesi gerekir.

Bir sonraki adımda, yüklenen oynanış videosunu Qwen 3.5 modelinin analiz edebileceği küçük bir temsilî görsel kümesine dönüştüren OpenCV ile kare çıkarma hattını uygulayacağız.

Adım 5: Temsilî kareleri çıkarın

Ollama’nın Qwen 3.5 yerel modelleri metin ve görüntü kabul eder, ham video değil. Bu nedenle yüklenen klibi doğrudan göndermek yerine, eşit aralıklı az sayıda kare çıkarıp bunları modele iletiriz. 

def save_uploaded_video(uploaded_file) -> Path:
    suffix = Path(uploaded_file.name).suffix or ".mp4"
    with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as tmp:
        tmp.write(uploaded_file.read())
        return Path(tmp.name)
def extract_frames(video_path: Path, max_frames: int = 6):
    cap = cv2.VideoCapture(str(video_path))
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
    interval = max(1, total_frames // max_frames) if total_frames > 0 else 1
    frames = []
    frame_id = 0
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        if frame_id % interval == 0:
            _, buffer = cv2.imencode(".jpg", frame)
            frames.append(base64.b64encode(buffer).decode())
        frame_id += 1
        if len(frames) >= max_frames:
            break
    cap.release()
    return frames
def extract_json_block(text: str) -> str:
    start = text.find("{")
    end = text.rfind("}")
    if start == -1 or end == -1 or end <= start:
        raise ValueError("No valid JSON object found in model response.")
    return text[start : end + 1]
def cleanup_html_response(text: str) -> str:
    html = text.strip()
    if html.startswith("```"):
        lines = html.splitlines()
        if lines and lines[0].startswith("```"):
            lines = lines[1:]
        if lines and lines[-1].startswith("```"):
            lines = lines[:-1]
        html = "\n".join(lines).strip()
    return html
def patch_html_for_iframe_keyboard(html: str) -> str:
    if 'tabindex="0"' not in html and "<canvas" in html:
        html = html.replace("<canvas", '<canvas tabindex="0"', 1)
    focus_patch = """
<script>
(function() {
  const canvas = document.querySelector("canvas");
  if (!canvas) return;
  if (!canvas.hasAttribute("tabindex")) {
    canvas.setAttribute("tabindex", "0");
  }
  function focusGame() {
    try { canvas.focus(); } catch (e) {}
  }
  window.addEventListener("load", focusGame);
  canvas.addEventListener("click", focusGame);
  document.addEventListener("keydown", function(e) {
    if (["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"," "].includes(e.key)) {
      e.preventDefault();
    }
  }, { passive: false });
  const buttons = document.querySelectorAll("button");
  buttons.forEach(btn => {
    btn.addEventListener("click", () => {
      setTimeout(focusGame, 50);
    });
  });
})();
</script>
"""
    if "</body>" in html:
        html = html.replace("</body>", focus_patch + "\n</body>")
    else:
        html += focus_patch
    return html

Yukarıdaki yardımcılar, yüklenen videodan temsilî kareleri çıkarmak için beş ana bileşeni tanımlar:

  • Video yükleme işleme: save_uploaded_video() fonksiyonu, yüklenen oynanış videosunu diskte geçici bir dosya olarak saklar. Streamlit dosyaları bellekte yüklediğinden, dosyayı diske yazmak, kare çıkarımı sırasında OpenCV’nin onu verimli okumasını ve orijinal dosya uzantısının korunmasını sağlar.
  • Videodan kare örnekleme: extract_frames() fonksiyonu videoyu OpenCV ile yükler ve süresi boyunca eşit aralıklı az sayıda kare örnekler. Her kareyi işlemek yerine, örnekleme aralığı hesaplanır ve oynanışı yakalayan temsilî kareler çıkarılır. Her kare bir JPEG olarak kodlanır ve çok modlu Qwen modeline doğrudan görüntü girdi olarak gönderilebilmesi için base64 dizgesine dönüştürülür.
  • Yapılandırılmış JSON çıkarımı: extract_json_block() yardımcı fonksiyonu, model çıktısının doğru biçimde ayrıştırılabilmesini sağlar. Çıkarılan içerik daha sonra GameSpec şemasına karşı güvenle doğrulanabilir.
  • HTML yanıt temizliği: cleanup_html_response() fonksiyonu, modelin kod üretirken dahil edebileceği markdown biçimlendirmesini kaldırır. Model HTML çıktısını üçlü tırnaklı kod çitleriyle sararsa, bu yardımcı bu işaretleri temizleyerek HTML’nin Streamlit önizlemesi içinde doğrudan görüntülenmesini sağlar.
  • Gömülü oyunlarda klavye etkileşimi: patch_html_for_iframe_keyboard() fonksiyonu, Streamlit iframe’i içinde klavye kontrollerinin doğru çalışmasını sağlamak için üretilen HTML’ye küçük bir JavaScript yamasi enjekte eder. Canvas ögesini otomatik olarak odaklar, eksikse bir tabindex atar ve ok tuşları ile boşluk çubuğunu tarayıcının yakalamasını engelleyerek oyun kontrollerinde kullanılmalarını sağlar.

Birlikte, bu yardımcılar hattın video ön işleme ve yanıt işleme katmanını oluşturur.

Adım 6: Oyun özelliklerini çıkarın 

Artık çıkarılan kareler ve istem şablonları hazır olduğuna göre, akıl yürütme hattının ilk aşamasını; yani oynanış karelerini yapılandırılmış bir oyun özellik setine dönüştürmeyi uygulayabiliriz.

def infer_game_spec(video_path: Path, game_hint: str, extra_constraints: str, max_frames: int) -> dict:
    frames = extract_frames(video_path, max_frames=max_frames)
    prompt = build_spec_user_prompt(game_hint, extra_constraints, max_frames)
    response = ollama.chat(
        model="qwen3.5:9b",
        messages=[
            {
                "role": "system",
                "content": SPEC_SYSTEM_PROMPT,
            },
            {
                "role": "user",
                "content": prompt,
                "images": frames,
            },
        ],
        options={
            "temperature": 0.1,
            "num_ctx": 8192,
        },
    )
    text = response["message"]["content"]
    parsed = json.loads(extract_json_block(text))
    validated = GameSpec(**parsed)
    return validated.model_dump()

Yukarıdaki fonksiyon, oyun-özellik çıkarım hattında dört ana aşamayı tanımlar:

  • Kare hazırlığı: extract_frames() çağrısı, yüklenen oynanış videosundan az sayıda temsilî kare örnekler. Bu görüntüler, modelin oyun mekaniğini çıkarmak için kullanacağı görsel kanıtları oluşturur.
  • İstem oluşturma: build_spec_user_prompt() fonksiyonu, isteğe bağlı ipucunu, ek kısıtları ve çıkarılan azami kare sayısını kullanarak kullanıcı istemini dinamik olarak oluşturur. Bu istem, modele şemaya uygun bir oyun özellik seti üretmesi için net bir talimat sunar.
  • Çok modlu model çağrısı: ollama.chat() isteği, hem metin istemini hem de çıkarılan kareleri yerel qwen3.5:9b modeline gönderir. Üretim ayarları, çıktıyı belirleyici tutmak için düşük temperature ve istem ile yapılandırılmış yanıta yeterli bağlam alanı sağlamak için num_ctx=8192 kullanır.
  • Şema doğrulama: Model yanıt verdikten sonra fonksiyon JSON bloğunu çıkarır, ayrıştırır ve GameSpec Pydantic şemasına karşı doğrular. Son olarak, doğrulanan özellik seti bir Python sözlüğü olarak döndürülür.

Artık modelin örneklenen video karelerinden çıkardığı oyunun yapılandırılmış bir temsiline sahibiz. Bir sonraki adımda, bu doğrulanmış özellik setini HTML oyun üretim aşamasının girdisi olarak kullanacağız.

Adım 7: HTML oyunu üretin

Bu noktada model artık görüntülerden mekanik çıkarmak zorunda değil. Bunun yerine qwen3.5:9b modelinden, düzeni, stilleri, oyun döngüsünü, kontrolleri ve çizim mantığını içeren tek başına bir HTML dosyası üretmesini istiyoruz.

def generate_game_html(game_spec: dict) -> str:
    response = ollama.chat(
        model="qwen3.5:9b",
        messages=[
            {
                "role": "system",
                "content": CODE_SYSTEM_PROMPT,
            },
            {
                "role": "user",
                "content": build_code_user_prompt(game_spec),
            },
        ],
        options={
            "temperature": 0.2,
            "num_ctx": 8192,
        },
    )
    text = response["message"]["content"]
    html = cleanup_html_response(text)
    if "<html" not in html.lower():
        raise ValueError("Model did not return HTML")
    return patch_html_for_iframe_keyboard(html)

Yukarıdaki fonksiyon, son HTML oyununu üretip hazırlamak için şu dört adımı gerçekleştirir:

  • Yapılandırılmış istem iletimi: Fonksiyon, doğrulanmış game_spec sözlüğünü alıp build_code_user_prompt() fonksiyonuna geçirir. Bu, oyun özelliklerini, modelin tek dosyalık eksiksiz bir HTML oyunu üretmesini söyleyen ayrıntılı bir uygulama istemine dönüştürür.
  • Qwen modeliyle kod üretimi: ollama.chat() çağrısı, kod üretimi sistem istemini ve kullanıcı istemini yerel qwen3.5:9b modeline gönderir. Görev artık çok modlu akıl yürütme değil, saf kod üretimi olduğundan modele yalnızca metin iletilir. 
  • HTML temizliği ve doğrulama: Modelin ham yanıtı, herhangi bir markdown veya ekstra biçimlendirmeyi kaldırmak için cleanup_html_response()’tan geçirilir. Ardından, çıktının bir HTML belgesi içerdiğini doğrulamak için basit bir kontrol yapılır.
  • Iframe klavye yamasi: Son HTML, oyunun Streamlit’in gömülü iframe’i içinde oynanabilir olmasını sağlayan küçük bir JavaScript düzeltmesi enjekte eden patch_html_for_iframe_keyboard()’tan geçirilir. Bu adım önemlidir; çünkü tarayıcı oyunları, canvas açıkça odaklanabilir ve otomatik odaklanır yapılmadıkça ok tuşları ve boşluk çubuğunu sıklıkla doğru yakalayamaz.

Artık tamamen çıkarılan oyun özelliklerinden üretilmiş, tarayıcıda oynanabilir bir HTML oyunumuz var.

Adım 8: Streamlit arayüzü

Son adım olarak, kullanıcıların alttaki koda dokunmadan sistemi deneyebilmesi için her şeyi basit bir Streamlit arayüzüne saralım. Arayüz üç temel yetenek sunar:

  • Bir oynanış videosu yükleme
  • İpuçları ve kare örnekleme gibi üretim parametrelerini yapılandırma
  • Üretilen oyunu önizleme ve indirme
st.set_page_config(page_title="Qwen 3.5 9B (Ollama) - Video to Game", layout="wide")
st.title("Qwen 3.5 9B: Gameplay Video to Playable HTML Game")
st.write(
    "This local demo uses Ollama with qwen3.5:9b to turn a short gameplay clip "
    "into a minimal browser game."
)
with st.sidebar:
    st.header("Local generation settings")
    game_hint = st.text_input("Optional game hint", value="Simple Pong-like arcade game")
    extra_constraints = st.text_area(
        "Extra constraints",
        value="Keep the game extremely simple and easy to play with arrow keys or space bar.",
        height=120,
    )
    max_frames = st.slider("Frames extracted from video", 3, 8, 5)
uploaded_video = st.file_uploader(
    "Upload gameplay video",
    type=["mp4", "mov", "avi", "mkv", "webm"],
)
if "local_game_spec" not in st.session_state:
    st.session_state.local_game_spec = None
if "local_game_html" not in st.session_state:
    st.session_state.local_game_html = None
if uploaded_video is not None:
    video_path = save_uploaded_video(uploaded_video)
    col1, col2 = st.columns([1, 1])
    with col1:
        st.subheader("Uploaded video")
        st.video(str(video_path))
    with col2:
        st.subheader("Run local pipeline")
        if st.button("Generate game", type="primary"):
            try:
                with st.spinner("Extracting frames and inferring game mechanics (local model)..."):
                    game_spec = infer_game_spec(
                        video_path=video_path,
                        game_hint=game_hint,
                        extra_constraints=extra_constraints,
                        max_frames=max_frames,
                    )
                    st.session_state.local_game_spec = game_spec
                with st.spinner("Generating HTML game code with qwen3.5:9b..."):
                    game_html = generate_game_html(game_spec)
                    st.session_state.local_game_html = game_html
                    (OUTPUT_DIR / "local_game_spec.json").write_text(
                        json.dumps(game_spec, indent=2),
                        encoding="utf-8",
                    )
                    (OUTPUT_DIR / "local_generated_game.html").write_text(
                        game_html,
                        encoding="utf-8",
                    )
                st.success("Local game generated.")
            except ValidationError as e:
                st.error(f"Schema validation failed: {e}")
            except Exception as e:
                st.error(f"Local generation failed: {e}")
if st.session_state.local_game_spec:
    st.subheader("Inferred game spec")
    st.json(st.session_state.local_game_spec)
if st.session_state.local_game_html:
    tab1, tab2, tab3 = st.tabs(["Preview", "HTML Code", "Downloads"])
    with tab1:
        st.subheader("Playable preview")
        st.caption(
            "Click START GAME, then click once inside the game canvas so it captures keyboard input."
        )
        components.html(
            st.session_state.local_game_html,
            height=760,
            scrolling=False,
        )
    with tab2:
        st.subheader("Generated HTML (local)")
        st.code(st.session_state.local_game_html, language="html")
    with tab3:
        st.subheader("Download files (local)")
        st.download_button(
            label="Download HTML game (local)",
            data=st.session_state.local_game_html,
            file_name="local_generated_game.html",
            mime="text/html",
        )
        st.download_button(
            label="Download JSON spec (local)",
            data=json.dumps(st.session_state.local_game_spec, indent=2),
            file_name="local_game_spec.json",
            mime="application/json",
        )

Yukarıdaki kod, kullanıcının bir oynanış klibi yüklemesini, isteğe bağlı olarak oyun türüne dair bir ipucu vermesini ve analiz için videodan kaç kare çıkarılacağını kontrol etmesini tanımlar. Bir video yüklendiğinde, uygulama videoyu, hattın tamamını tetikleyen Oyun üret düğmesinin yanında gösterir.

Kullanıcı hattı çalıştırdığında, uygulama önce yerel qwen3.5:9b modeliyle kareleri çıkarır ve oyun mekaniğini yorumlayarak yapılandırılmış bir oyun özellik seti üretir. İkinci aşamada model, bu özellik setinden eksiksiz bir HTML oyun üretir. Sonuçlar, arayüz güncellemeleri arasında kalıcı olmaları için Streamlit oturum durumunda saklanır.

Üretim tamamlandıktan sonra arayüz, çıkarılan oyun özelliklerini, üretilen oyunun canlı oynanabilir önizlemesini, ham HTML kaynağını ve hem HTML oyunu hem de JSON özellik setini kaydetmek için indirme düğmelerini gösterir.

Kendiniz denemek için kodu app.py olarak kaydedin ve şu komutla başlatın:

streamlit run app.py

Demo

Sonuç

Qwen 3.5, bugün mevcut en ilginç çok modlu model ailelerinden biridir. Bu eğitimde, Ollama üzerinden qwen3.5:9b kullanarak tamamen yerel bir oynanış videosundan oyuna dönüştürücü kurduk. Uygulama kısa bir klipten kareler çıkarır, yapılandırılmış bir oyun tasarımı yorumlar, tek dosyalık bir tarayıcı oyunu üretir ve bunu Streamlit içinde önizler.

Barındırılan qwen/qwen3.5-397b-a17b modeli bu görev için hâlâ daha hızlı ve güçlü bir seçenek, ancak yerel 9B’nin sonucu da kendi başına etkileyici. Küçük bir çok modlu modelin artık tüketici donanımında anlamlı görsel akıl yürütme ve kod üretimi yapabildiğini kanıtlıyor.

Bunu daha da ileri götürmek isterseniz, 9B’yi 4B ve 27B modelleriyle karşılaştırın, bir iyileştirme döngüsü ekleyin veya çıktıyı aynı klipten çoklu yinelemeleri destekleyen mini bir oyun motoruna dönüştürün.

Qwen3.5 Küçük SSS

Bu demoyu 9B yerine `qwen3.5:4b` ile çalıştırabilir miyim?

Evet, ancak sonuçlar genellikle daha az güvenilir olur. 4B model, hafif yerel denemeler için hâlâ kullanışlıdır; fakat 9B varyantı hız ve kalite arasında daha iyi bir dengedir. Ollama her ikisini de yerel metin-ve-görüntü modelleri olarak sunar.

Neden `qwen3.5:0.8b` veya 2B kullanılmıyor?

2B modeller, bu tam hat yerine daha basit çok modlu görevler veya uç tarzı dağıtımlar için daha uygundur.

Bu kullanım durumu için en iyi yerel Qwen 3.5 modeli hangisi?

qwen3.5:9b en iyi başlangıç noktasıdır. Ollama’da hâlâ 6.6GB ile kompakt, metin-ve-görüntü girişi destekler ve bu tür akıl yürütme ağırlıklı üretim görevlerinde çok küçük varyantlardan belirgin şekilde daha iyi performans verir.

Daha büyük Qwen 3.5 modellerini de yerel olarak kullanabilir miyim?

Evet. Ollama şu anda metin-ve-görüntü desteğiyle 27b, 35b ve 122b varyantlarını da listeliyor. Değiş tokuş ise bellek ve gecikmedir.


Aashi Dutt's photo
Author
Aashi Dutt
LinkedIn
Twitter

ML (Üretken Yapay Zekâ) alanında Google Developers Uzmanıyım, Kaggle 3x Expert unvanına sahibim ve 3+ yıllık teknoloji deneyimiyle Women Techmakers Elçisiyim. 2020'de bir sağlık teknolojileri girişiminin kurucu ortağı oldum ve Georgia Tech'te makine öğrenmesi alanında uzmanlaşarak bilgisayar bilimleri yüksek lisansı yapıyorum.

Konular
Yapay Zeka
Büyük Dil Modelleri

Öne Çıkan DataCamp Kursları

Kurs

Cursor ile Yazılım Geliştirme

1 sa 30 dk
4.4K
Cursor ile üretime hazır kodlar oluşturun. AI komutları, yeniden yapılandırma, test etme ve gelişmiş iş akışlarını öğrenin.
Ayrıntıları GörüntüleRight Arrow
Kursa Başla
Devamını GörRight Arrow
İlgili

blog

Hızlı Sevkiyat İçin Pratik Vibe Kodlama Teknoloji Yığını

Ön uç, arka uç, veritabanları, kimlik doğrulama, depolama, e-posta, test, dağıtım ve izleme için en iyi araçları keşfedin.
Abid Ali Awan's photo

Abid Ali Awan

14 dk.

blog

2026’da En Popüler 40 Yazılım Mühendisi Mülakat Sorusu

Algoritmalar, sistem tasarımı ve davranışsal senaryoları kapsayan bu temel sorularla teknik mülakat sürecine hakim olun. Uzman cevapları, kod örnekleri ve kanıtlanmış hazırlık stratejileri edinin.
Dario Radečić's photo

Dario Radečić

15 dk.

Eğitim

Python'da Listeyi String'e Nasıl Dönüştürürsünüz

Bu hızlı eğitimde, Python'da bir listeyi string'e nasıl dönüştüreceğinizi öğrenin.
Adel Nehme's photo

Adel Nehme

Eğitim

.gitignore Nasıl Kullanılır: Örneklerle Pratik Bir Giriş

Git deponuzu temiz tutmak için .gitignore’u nasıl kullanacağınızı öğrenin. Bu eğitim; temelleri, yaygın kullanım durumlarını ve başlamanıza yardımcı olacak pratik örnekleri kapsar!
Kurtis Pykes 's photo

Kurtis Pykes

8 dk.

Devamını GörDevamını Gör