Ana içeriğe geç

DeepSeek-V3.2-Speciale Eğitimi: Bir Veri Analisti Aracısı Oluşturun

DeepSeek V3.2 Speciale ve Streamlit kullanarak herhangi bir CSV'yi etkileşimli bir analitik asistanına dönüştürmeyi öğrenin.
· 11 dk. oku

Yapay Zeka ile Keşfedin

DeepSeek V3.2 Speciale, karmaşık, araç destekli iş akışları için ayarlanmış bir yüksek hesaplama kapasiteli akıl yürütme modelidir. Soruları doğrudan yanıtlamak yerine, eylem planları yapmaya, akıl yürütmeye ve araçları koordine etmeye yönelik tasarlanmıştır; bu sayede aracısal analitik sistemler inşa edilebilir.

Bu eğitimde, DeepSeek V3.2 Speciale ile herhangi bir CSV'nin üzerinde çalışan Yapay Zekâ Veri Analisti Aracısı oluşturacağız. Akış şu şekilde işler:

  1. Kullanıcı bir CSV dosyası yükler ve doğal dilde bir soru sorar 
  2. Bir planlayıcı aracısı soruyu yapılandırılmış bir JSON analiz planına dönüştürür
  3. Ardından bazı Python araçları planı yürütür ve tablolar/grafikler üretir.
  4. Son olarak bir açıklayıcı aracısı ham sayıları içgörü özetine dönüştürür.

Sonunda, açık planlar ve tekrarlanabilir analiz adımları olan bir Streamlit uygulamanız olacak. Yapay zekâ aracılar hakkında daha fazla bilgi edinmek isterseniz, AI Agent Fundamentals beceri yolunu incelemenizi öneririm.  

DeepSeek V3.2 Speciale nedir?

DeepSeek V3.2 Speciale, gündelik sohbetten ziyade derin akıl yürütme için özel olarak tasarlanmış, yüksek hesaplama kapasiteli ve pekiştirmeli öğrenmeyle güçlendirilmiş DeepSeek V3.2 ailesinin bir varyantıdır. 

Karmaşık görevlerde Gemini 3 Pro gibi modellere rakip olacak düzeyde performans sunar ve IMO, CMO, ICPC Dünya Finalleri ve IOI 2025 gibi kıyaslamalarda altın madalya dereceleri elde etmiştir.

DeepSeek V3.2 Speciale şu alanlar için optimize edilmiştir:

  • Zor matematik, mantık ve çok adımlı akıl yürütme
  • Modelin tek seferde yanıt vermek yerine eylemleri planladığı aracısal iş akışlarını da destekler
  • Rakipleri gibi, uzun bağlamı ve düşünce zinciri tarzı akıl yürütmeyle yapılandırılmış çıktıları da destekler

V3.2 Speciale yalnızca düşünme modunu destekler ve şu anda resmi API üzerinden araç çağırmayı desteklemez. Sadece temperature ayarını değiştirerek ilk denemede temiz JSON bekleyemezsiniz. Speciale modeli uzun uzun düşünecek ve çoğu zaman akıl yürütmesini nihai yanıtla iç içe verecektir.

DeepSeek-V3.2-Speciale Örnek Proje: Yapay Zekâ Veri Analisti Aracısı

Bu bölümde, Streamlit uygulamasına sarılmış bir DeepSeek V3.2 Speciale modeliyle bir Yapay Zekâ Veri Analisti Aracısı uygulayacağız. Üst düzeyde, nihai uygulama şunları yapar:

  • Herhangi bir CSV dosyasını yükleyin. Kaggle'da iyi örnekler bulabilirsiniz (bu demoda kullandığım satış tahmini gibi)
  • Uygulama tarih sütunlarını otomatik olarak algılar ve yıl, ay ve yıl_ay yardımcı alanları ekler.
  • Bir soru yazarsınız ve DeepSeek V3.2 Speciale bir Planlayıcı Aracısı olarak JSON bir planla yanıt verir:
    • hangi sütunlara göre gruplama yapılacağı
    • hangi metriklerin toplanacağı
    • isteğe bağlı filtreler (ör. “Year == 2019”)
    • çubuk grafik, çizgi grafik veya pasta grafik çizilip çizilmeyeceği
  • Saf bir Python Veri Çalışanı, planı pandas ile yürütür.
  • Bir Grafik Oluşturucu, matplotlib ve seaborn ile çubuk/çizgi/pasta grafik oluşturur.
  • Bir Açıklayıcı Aracı, sonucu sade bir dille özetler ve kilit içgörüleri vurgular.

DeepSeek v3.2 Speciale Final demo output

Adım adım inşa edelim.

Adım 1: Önkoşullar

Önce temel bağımlılıkları yükleyin ve DeepSeek'in API platformundan bir API anahtarı alın:

pip install streamlit pandas matplotlib seaborn python-dotenv openai

Etkileşimli web uygulaması arayüzünü güçlendirmek için streamlit, CSV verilerini yüklemek ve işlemek için pandas, grafik ve görselleştirme oluşturmak için matplotlib ve seaborn gibi birkaç temel kütüphane kullanacağız. python-dotenv kütüphanesi, .env dosyasından ortam değişkenlerini yüklemek için, OpenAI ise DeepSeek'in OpenAI uyumlu API'siyle konuşmak için istemci olarak kullanılacaktır.

Sonra, DeepSeek API platformu'na giriş yapın, API Keys sekmesine gidin ve Create a new API key'e tıklayın.

API Keys

Son olarak, anahtarınıza bir ad verin ve üretilen anahtarı kopyalayın. 

Create a new API Key

DeepSeek API anahtarınızı bir ortam değişkeni olarak ayarlayın:

export DEEPSEEK_API_KEY="your_api_key_here"

Artık ortamımız hazır.

Adım 2: İçe aktarımlar ve DeepSeek istemcisi

Bağımlılıkları yükledikten sonra, bir sonraki adım bunları betiğimize almak ve planlayıcı ve açıklayıcı aracılarımızı çalıştıracak DeepSeek istemcisini kurmaktır.

import io
import json
import os
import re
from datetime import datetime
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
import streamlit as st
from dotenv import load_dotenv
from openai import OpenAI
plt.style.use('seaborn-v0_8-darkgrid')
sns.set_palette("husl")
load_dotenv()
DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
if not DEEPSEEK_API_KEY:
    raise RuntimeError("Please set DEEPSEEK_API_KEY env variable.")
DEEPSEEK_BASE_URL = os.getenv(
    "DEEPSEEK_BASE_URL",
    "https://api.deepseek.com/v3.2_speciale_expires_on_20251215",
)
client = OpenAI(
    api_key=DEEPSEEK_API_KEY,
    base_url=DEEPSEEK_BASE_URL,
)

Yukarıdaki kod bloğu üç şeyi yapar. Temel kütüphaneleri içe aktarır, bir çizim stili uygular ve .env dosyasından ortam değişkenlerini yükler. Ardından DEEPSEEK_API_KEY'i okur, doğrular ve her bir sonraki LLM çağrısının doğru modelden geçmesi için OpenAI istemcisini DeepSeek'in geçici V3.2 Speciale uç noktasına yönlendirerek oluşturur. 

DeepSeek V3.2 Speciale'ın şu anda 15 Aralık 2025'te süresi dolan geçici bir uç noktadan sunulduğunu unutmayın. Sonrasında dağıtım URL'si değişebilir ancak model adı deepseek-reasoner olarak kalacaktır. En güncel uç noktalar ve kullanım ayrıntıları için resmi DeepSeek belgelerine bakın.

İstemciyi yapılandırdıktan sonra iki aracımızı tanımlayalım: biri analizi planlamak, diğeri sonuçları açıklamak için.

Adım 3: Planlayıcı Aracısı 

İstemci hazırken, verilerle hangi analizin yapılacağına karar veren Planlayıcı Aracısını oluşturabiliriz. Modelin DataFrame üzerinde doğrudan işlem yapmasına izin vermek yerine, Python “veri çalışanımızın” güvenle yürütebileceği yapılandırılmış bir JSON planı üretmesini istiyoruz. 

Planlayıcı, veri kümesinin metinsel bir açıklamasını kullanıcı sorusuyla birlikte görür ve işlem, gruplama sütunları, filtreler, metrik ve grafik türünü tanımlayan bir JSON nesnesi döndürür.

def call_planner_llm(schema_text: str, question: str) -> tuple[dict, str]:
    system_prompt = """
    You are a data analysis planner. Convert user questions into JSON plans.
    JSON Structure (REQUIRED):
    {
      "operation": "group_by_summary",
      "group_by": ["column_name"],
      "filters": [],
      "target_column": "column_to_analyze",
      "metric": "sum",
      "need_chart": true,
      "chart_type": "bar"
    }
    Fields:
    - operation: Always use "group_by_summary" (most flexible)
    - group_by: Columns to group by (e.g., ["Product Name"] or ["Category", "Region"])
    - filters: Optional filters, e.g., [{"column": "Year", "op": "==", "value": "2023"}]
    - target_column: Column to aggregate (must exist in schema!)
    - metric: One of: sum, mean, count, max, min
    - need_chart: true (always show chart)
    - chart_type: bar, line, or pie
    Quick Rules:
    - Use ONLY columns from the provided schema
    - For "this year", look for year columns or use the latest year in data
    - For "top N", use appropriate group_by and metric
    - For comparisons, use filters to split groups 
    Think briefly, then output the complete JSON.
    """.strip()
    messages = [
        {"role": "system", "content": system_prompt},
        {
            "role": "user",
            "content": f"Dataset:\n{schema_text}\n\nQuestion:\n{question}\n\nReminder: After analyzing, output the complete JSON plan.",
        },
    ]
    resp = client.chat.completions.create(
        model="deepseek-reasoner",
        messages=messages,
        max_tokens=8192,
    )
    message = resp.choices[0].message
    content = message.content or ""
    content = content.strip() 
    reasoning_content = getattr(message, 'reasoning_content', None) or ""
    finish_reason = resp.choices[0].finish_reason
    def extract_json_from_text(text: str) -> dict | None:
        if not text:
            return None
        try:
            return json.loads(text)
        except json.JSONDecodeError:
            pass   
        code_block_match = re.search(r'```(?:json)?\s*(\{[\s\S]*?\})\s*```', text, re.DOTALL)
        if code_block_match:
            try:
                return json.loads(code_block_match.group(1))
            except json.JSONDecodeError:
                pass
        def find_all_json_objects(s):
            objects = []
            depth = 0
            start_idx = None
            in_string = False
            escape_next = False            
            for i, char in enumerate(s):
                if escape_next:
                    escape_next = False
                    continue                
                if char == '\\':
                    escape_next = True
                    continue                
                if char == '"' and not in_string:
                    in_string = True
                elif char == '"' and in_string:
                    in_string = False
                elif not in_string:
                    if char == '{':
                        if depth == 0:
                            start_idx = i
                        depth += 1
                    elif char == '}':
                        depth -= 1
                        if depth == 0 and start_idx is not None:
                            objects.append(s[start_idx:i+1])
                            start_idx = None            
            return objects 
        for candidate in find_all_json_objects(text):
            try:
                parsed = json.loads(candidate)
                if isinstance(parsed, dict) and any(k in parsed for k in ['operation', 'target_column', 'metric', 'group_by']):
                    return parsed
            except json.JSONDecodeError:
                continue
        for i in range(len(text)):
            if text[i] == '{':
                decoder = json.JSONDecoder()
                try:
                    parsed, _ = decoder.raw_decode(text[i:])
                    if isinstance(parsed, dict) and any(k in parsed for k in ['operation', 'target_column', 'metric', 'group_by']):
                        return parsed
                except json.JSONDecodeError:
                    continue        
        return None
    if reasoning_content:
        plan = extract_json_from_text(reasoning_content)
        if plan is not None:
            return plan, reasoning_content
    plan = extract_json_from_text(content)
    if plan is not None:
        return plan, reasoning_content
    error_msg = (
        f"Planner returned non-JSON content.\n"
        f"Finish reason: {finish_reason}\n"
        f"Content received: {repr(content[:500]) if content else '(empty)'}\n"
        f"Reasoning content length: {len(reasoning_content) if reasoning_content else 0}\n"
        f"Reasoning content sample: {reasoning_content[:500] if reasoning_content else '(none)'}"
    )
    if finish_reason == "length":
        error_msg += "\n\nNote: Response was truncated due to token limit. The model didn't finish generating the JSON."   
    raise ValueError(error_msg)

Önce, system_prompt Planlayıcı Aracısı için sözleşmeyi tanımlar; kullanıcı sorusunu bir JSON planına dönüştürür ve group_by sütunları listesi, isteğe bağlı filters, bir target_column, bir metric ve bir chart_type gibi beklediğimiz alanları tam olarak belirtir. 

Ayrıca modelin “bu yıl” veya “ilk N” gibi ifadeleri nasıl ele alacağına dair birkaç sezgisel kural ekliyoruz; böylece model, genel bir sohbet botundan ziyade kıdemsiz bir veri analisti gibi davranır.

Sonraki adımda, talimatları taşıyan sistem iletisini oluştururuz ve iki unsuru bir araya getiririz: veri kümesi şemasının (sütun adları, veri tipleri, örnek satırlar) metinsel açıklaması ve kullanıcının sorusu. Bu, modele geçerli sütun adlarını seçmesi ve soruya uygun bir toplama seçmesi için yeterli bağlam sağlar.

Ardından DeepSeek V3.2 Speciale, client.chat.completions.create() ile çağrılır. Bu bir akıl yürütme modeli olduğundan, iki kanalda çıktılar üretebilir: görünür içerik dizesi ve çoğu “düşünme” ile nihai yanıtın yer aldığı dahili reasoning_content dizesi. Her ikisini de yanıttan çeker ve kısaltmayı tespit edebilmek için finish_reason'ı takip ederiz.

Asıl ağır iş extract_json_from_text() fonksiyonunda yapılır. Akıl yürütme modelleri her zaman temiz JSON döndürmediğinden, bunu bir markdown içine de sarabilir. Planlayıcıyı sağlamlaştırmak için bu yardımcı fonksiyon birden fazla yaklaşımı sırayla dener:

  • Önce tüm dizeyi json.loads ile ayrıştırmayı dener
  • Sonra JSON kod bloklarını arar ve içeriğini ayrıştırır
  • Son olarak, bir parantez eşleme rutiniyle dengeli blokları tarar ve ham bir JSON ayrıştırıcı kullanır.

Her adayda, yalnızca geçerli bir plana benzeyen nesneleri kabul eder; yani en azından operation, target_column, metric veya group_by alanlarından birini içermelidir. 

Son olarak, her şeyi birbirine bağlamak için extract_json_from_text() fonksiyonunu reasoning_content'a uygularız.

Bu parçalar birlikte, herhangi bir doğal dildeki soruyu ve şema anlık görüntüsünü yapılandırılmış bir analiz planına dönüştürür. 

Adım 4: Açıklayıcı Aracısı

Veri Çalışanı pandas ile analizi çalıştırdıktan sonra, sonuç tablosunu JSON planla birlikte tekrar DeepSeek'e gönderir ve her şeyi somut içgörüler içeren bir özete dönüştürürüz.

def call_explainer_llm(question: str, plan: dict, result_summary: list) -> tuple[str, str]:
    system_prompt = """
    You are a senior data analyst explaining insights to business stakeholders.
    The user asked a question about their CSV data.
    Another agent already designed an analysis plan and we executed it in Python.
    You will now explain the results in clear, engaging, non-technical language.
    Requirements:
    - Start with a direct 1-2 sentence answer to their question
    - Use bullet points (3-5 points) to highlight key insights, trends, or comparisons
    - Include specific numbers and percentages where relevant
    - Use business-friendly language (avoid technical jargon like "aggregation", "groupby")
    - If the data shows interesting patterns, point them out
    - End with a brief recommendation or next step if appropriate    
    Keep it concise and actionable.
    """.strip()
    limited_summary = result_summary[:20] if len(result_summary) > 20 else result_summary
    user_content = f"""
    User question:
    {question}
    Analysis performed:
    {json.dumps(plan, indent=2)}
    Results (top rows):
    {json.dumps(limited_summary, indent=2)}    
    Total rows in result: {len(result_summary)}
    """.strip()
    messages = [
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": user_content},
    ]
    resp = client.chat.completions.create(
        model="deepseek-reasoner",
        messages=messages,
        max_tokens=2048,
    )   
    message = resp.choices[0].message
    content = message.content or ""
    content = content.strip()
    reasoning_content = getattr(message, 'reasoning_content', None) or ""    
    if not content:
        if reasoning_content:
            content = reasoning_content.strip()  
    return (content if content else "Unable to generate explanation from the model response.", 
            reasoning_content)

Açıklayıcı aracı, DeepSeek'e iş paydaşlarıyla konuşan kıdemli bir veri analisti gibi davranmasını söyleyen system_prompt'u ayarlayarak başlar. Yanıtın tam şeklini ve taslak oluşturma için diğer önerileri belirtiriz.

Sonra, modele ihtiyaç duyduğu tüm bağlamı veren user_content'i hazırlarız. Orijinal soruyu, JSON analiz planını (hangi hesaplamaların çalıştırıldığını bilmesi için) ve toplam satır sayısını geçiririz.

Ardından ileti listesini derler ve client.chat.completions.create() aracılığıyla DeepSeek V3.2 Speciale'ı çağırırız. Planlayıcıda olduğu gibi, model metni hem content hem de reasoning_content içinde döndürebilir; bu nedenle her ikisini de ele alır ve content boşsa reasoning_content'a geri döneriz. 

Son olarak, fonksiyon hem açıklamayı hem de açılır bölümde gösterilen ham akıl yürütme dizesini içeren bir ikili (tuple) döndürür.

Bu Açıklayıcı Aracı ile döngü tamamlanır:

  • Planlayıcı Aracısı neyin hesaplanacağına karar verir,
  • Veri Çalışanı planı pandas ve matplotlib ile yürütür ve
  • Açıklayıcı Aracı sayıları uygulanabilir içgörülere çevirir

Adım 5: Yardımcı fonksiyonlar

DeepSeek'in herhangi bir şey planlamasına izin vermeden önce, verileri net biçimde tanımlayıp analize uygun hâle getirmemiz gerekir. Bu, bir dizi yardımcı fonksiyonla sağlanır.

Adım 5.1 Şema planlayıcı

Planlayıcı Aracısı DataFrame'inizi doğrudan göremez, bu yüzden veri kümesinin kompakt bir metinsel özetini göndeririz.

def get_schema_description(df: pd.DataFrame, max_rows: int = 3) -> str:
    schema_lines = ["Columns:"]
    for col, dtype in df.dtypes.items():
        if dtype == 'object' and df[col].nunique() < 20:
            unique_vals = df[col].dropna().unique()[:5]
            schema_lines.append(f"- {col} ({dtype}) - sample values: {', '.join(map(str, unique_vals))}")
        else:
            schema_lines.append(f"- {col} ({dtype})")
    sample = df.head(max_rows).to_markdown(index=False)
    return "\n".join(schema_lines) + "\n\nSample rows:\n" + sample

Bu yardımcı fonksiyon tüm sütunlarda döngü yapar. Ürün adları, bölgeler veya kategoriler gibi şeyler için birkaç örnek değer de ekler. F

inalde, modelin akıl yürütebilmesi için somut örnekler sağlaması adına ilk birkaç satırın kısa bir markdown tablosunu ekler.

Bu şema dizesi, planlayıcının verileriniz üzerindeki gözü gibidir; geçerli sütun adları ve gruplamaları seçmesini sağlar.

Adım 5.2 Otomatik tarih ön işleme

Tarih sütunlarını otomatik olarak algılar ve <col>_year ve <col>_year_month gibi yardımcı alanlar ekleriz; bu, “geçen yıl” veya “aya göre” gibi sorulara DeepSeek'in daha kolay yanıt vermesini sağlar.

def preprocess_dates(df: pd.DataFrame) -> pd.DataFrame:
    df = df.copy()
    date_columns = []    
    for col in df.columns:
        if pd.api.types.is_datetime64_any_dtype(df[col]):
            date_columns.append(col)
            continue
        if any(x in col.lower() for x in ['date', 'time', 'day']):
            try:
                df[col] = pd.to_datetime(df[col], errors='coerce')
                if df[col].notna().sum() > 0: 
                    date_columns.append(col)
            except:
                pass
    for col in date_columns:
        if col in df.columns and pd.api.types.is_datetime64_any_dtype(df[col]):
            df[f'{col}_year'] = df[col].dt.year
            df[f'{col}_month'] = df[col].dt.month
            df[f'{col}_year_month'] = df[col].dt.to_period('M').astype(str) 
    return df

Önce sütunları tararız; bir sütun zaten datetime ise veya adı bir tarihe işaret ediyorsa (order_date veya day gibi) onu pd.to_datetime ile ayrıştırmayı deneriz. Her tarih sütunu için <col>_year, <col>_month ve <col>_year_month gibi öznitelikler oluştururuz.

Bu ön işleme sayesinde planlayıcı, her veri kümesi için manuel zaman özellikleri oluşturmanıza gerek kalmadan year ve year_month sütunlarına güvenle atıfta bulunabilir.

Adım 5.3 Filtre uygulama

Planlayıcı plana basit satır filtreleri ekleyebilir ve bu yardımcı fonksiyon bunları pandas filtrelerine dönüştürür.

def apply_filters(df: pd.DataFrame, filters: list) -> pd.DataFrame:
    if not filters:
        return df
    filtered = df.copy()
    for f in filters:
        col = f.get("column")
        op = f.get("op")
        val = f.get("value")
        if col not in filtered.columns:
            continue
        try:
            val_cast = pd.to_numeric(val)
        except Exception:
            val_cast = val
        if op == "==":
            filtered = filtered[filtered[col] == val_cast]
        elif op == "!=":
            filtered = filtered[filtered[col] != val_cast]
        elif op == ">":
            filtered = filtered[filtered[col] > val_cast]
        elif op == "<":
            filtered = filtered[filtered[col] < val_cast]
        elif op == ">=":
            filtered = filtered[filtered[col] >= val_cast]
        elif op == "<=":
            filtered = filtered[filtered[col] <= val_cast]
        elif op == "contains":
            filtered = filtered[filtered[col].astype(str).str.contains(str(val_cast), case=False, na=False)]
    return filtered

Her filtre için sütunun varlığını kontrol eder, mümkünse değeri sayıya dönüştürür ve ardından uygun karşılaştırmayı uygularız. Tüm filtreler sıralı olarak uygulanır; böylece “Region contains West” ve “Year >= 2021” gibi koşulları doğal olarak birleştirebilirsiniz.

Bu fonksiyon, plandaki doğal dil kısıtlarını satır alt kümelerine bağlar.

Adım 5.4: Analiz planı 

Şimdi JSON planını okuyup karşılık gelen pandas toplulaştırmalarını çalıştıran Veri Çalışanı katmanını uyguluyoruz.

def run_analysis_plan(df: pd.DataFrame, plan: dict) -> pd.DataFrame:
    op = plan.get("operation")
    target_col = plan.get("target_column")
    group_by = plan.get("group_by") or []
    filters = plan.get("filters") or []
    metric = plan.get("metric", "sum")
    if filters:
        df = apply_filters(df, filters)
    if not op or not target_col or target_col not in df.columns:
        return df.head(20)
    if op in ("aggregate_compare", "group_by_summary", "filter_then_aggregate"):
        if not group_by:
            if metric == "sum":
                value = df[target_col].sum()
            elif metric == "mean":
                value = df[target_col].mean()
            elif metric == "count":
                value = df[target_col].count()
            elif metric == "max":
                value = df[target_col].max()
            elif metric == "min":
                value = df[target_col].min()
            else:
                raise ValueError(f"Unsupported metric: {metric}")
            return pd.DataFrame({f"{metric}_{target_col}": [value]})
        if metric == "sum":
            agg_df = df.groupby(group_by)[target_col].sum().reset_index()
        elif metric == "mean":
            agg_df = df.groupby(group_by)[target_col].mean().reset_index()
        elif metric == "count":
            agg_df = df.groupby(group_by)[target_col].count().reset_index()
        elif metric == "max":
            agg_df = df.groupby(group_by)[target_col].max().reset_index()
        elif metric == "min":
            agg_df = df.groupby(group_by)[target_col].min().reset_index()
        else:
            raise ValueError(f"Unsupported metric: {metric}")
        agg_df = agg_df.sort_values(by=target_col, ascending=False)       
        return agg_df
    return df.head(20)

Önce, planı işlem, hedef sütun, gruplama sütunları, filtreler ve metrik parametreleriyle açarız. T

akiben, filtreler apply_filters() fonksiyonu ile uygulanır. Plan geçersiz bir sütuna atıfta bulunursa, basit bir önizleme döndürmeye geri döneriz. 

Aksi halde, desteklenen işlemler için ya tüm tablo üzerinde toplulaştırma yaparız ya da bir groupby ardından toplam, ortalama, adet, maksimum veya minimum gibi işlemler uygular ve sonucu metrik sütununa göre sıralarız.

Burası araç katmanının kalbidir: LLM neyin hesaplanacağına karar verir, bu fonksiyon ise bunu pandas ile güvenli biçimde nasıl hesaplayacağını belirler.

Adım 5.5: Grafik oluşturucu 

Son olarak, çıktıda hem sayılar hem de görseller olsun diye, ortaya çıkan tabloyu planın chart_type'ına uygun bir grafiğe dönüştürürüz.

def generate_chart(df: pd.DataFrame, plan: dict):
    if not plan.get("need_chart") or df.empty:
        return None
    chart_type = plan.get("chart_type", "bar")
    group_by = plan.get("group_by") or []
    target_col = plan.get("target_column")
    if not target_col or target_col not in df.columns:
        return None
    fig, ax = plt.subplots(figsize=(10, 6))    
    if group_by and len(group_by) > 0:
        x_col = group_by[0]
        if x_col not in df.columns:
            return None
        plot_df = df.head(15)        
        if chart_type == "bar":
            bars = ax.bar(range(len(plot_df)), plot_df[target_col], color=sns.color_palette("husl", len(plot_df)))
            ax.set_xticks(range(len(plot_df)))
            ax.set_xticklabels(plot_df[x_col], rotation=45, ha='right')
            ax.set_ylabel(target_col, fontsize=12)
            ax.set_xlabel(x_col, fontsize=12)
            for i, (bar, val) in enumerate(zip(bars, plot_df[target_col])):
                height = bar.get_height()
                ax.text(bar.get_x() + bar.get_width()/2., height,
                       f'{val:,.0f}' if abs(val) > 100 else f'{val:.2f}',
                       ha='center', va='bottom', fontsize=9)                 
        elif chart_type == "line":
            ax.plot(range(len(plot_df)), plot_df[target_col], marker='o', linewidth=2, markersize=8)
            ax.set_xticks(range(len(plot_df)))
            ax.set_xticklabels(plot_df[x_col], rotation=45, ha='right')
            ax.set_ylabel(target_col, fontsize=12)
            ax.set_xlabel(x_col, fontsize=12)
            ax.grid(True, alpha=0.3)    
        elif chart_type == "pie" and len(plot_df) <= 10:
            ax.pie(plot_df[target_col], labels=plot_df[x_col], autopct='%1.1f%%', startangle=90)
            ax.axis('equal')
        else:
            bars = ax.bar(range(len(plot_df)), plot_df[target_col])
            ax.set_xticks(range(len(plot_df)))
            ax.set_xticklabels(plot_df[x_col], rotation=45, ha='right')
        title = f"{target_col} by {x_col}"
    else:
        ax.bar([target_col], [df[target_col].iloc[0]])
        title = f"{target_col}"
    ax.set_title(title, fontsize=14, fontweight='bold', pad=20)
    fig.tight_layout()
    buf = io.BytesIO()
    fig.savefig(buf, format="png", dpi=150, bbox_inches='tight')
    buf.seek(0)
    plt.close(fig)
    return buf

Plandan chart_type, group_by ve target_column'ı okur ve bir Matplotlib şekli oluştururuz. Yukarıdaki generate_chart() fonksiyonu, Streamlit uygulamasının temel metriklerin yanında görüntülediği bellekte bir PNG arabelleği döndürür.

Bu yardımcı fonksiyonlar hazır olduğunda, sistemin geri kalanı çok daha basit hâle gelir. Planlayıcı, analizi tasarlamak için get_schema_description'ı kullanır; Veri Çalışanı, sayıları hesaplamak için preprocess_dates(), apply_filters() ve run_analysis_plan() fonksiyonlarını kullanır; generate_chart() fonksiyonu ise nihai tabloyu bir grafiğe dönüştürür.

Adım 6: Streamlit arayüzü

Planlayıcı, veri çalışanı, açıklayıcı ve yardımcı fonksiyonlar hazır olduğuna göre, her şeyi bir Streamlit uygulamasının içine sarabiliriz. 

st.set_page_config(
    page_title="AI Data Analyst Agent",
    page_icon="📊",
    layout="wide",
    initial_sidebar_state="expanded"
)
st.markdown("""
<style>
    .main-header {
        
        font-weight: 700;
        
        
        text-align: center;
    }
    .sub-header {
        
        
        
        text-align: center;
    }
    .metric-card {
        background-
        
        border-radius: 0.5rem;
        border-left: 4px solid #1f77b4;
    }
    .insight-box {
        background-
        
        border-radius: 0.5rem;
        border-left: 4px solid #2ecc71;
        
    }
</style>
""", unsafe_allow_html=True)
st.markdown("<h1 class='main-header'>AI Data Analyst Agent</h1>", unsafe_allow_html=True)
uploaded_file = st.file_uploader("Upload CSV file", type=["csv"], help="Upload any CSV file to get started")
if uploaded_file is not None:
    try:
        df = pd.read_csv(uploaded_file)
        df = preprocess_dates(df)
    except Exception as e:
        st.error(f"Error loading CSV: {e}")
        st.stop()
    with st.expander("Preview Data", expanded=False):
        col1, col2, col3 = st.columns(3)
        col1.metric("Total Rows", f"{len(df):,}")
        col2.metric("Total Columns", len(df.columns))
        col3.metric("Memory Usage", f"{df.memory_usage(deep=True).sum() / 1024**2:.1f} MB")  
        st.dataframe(df.head(100), width='stretch', height=300)
        st.markdown("**Numeric Columns Summary:**")
        numeric_cols = df.select_dtypes(include=['number']).columns
        if len(numeric_cols) > 0:
            st.dataframe(df[numeric_cols].describe(), width='stretch')
    st.markdown("---")
    col1, col2 = st.columns([4, 1])
    with col1:
        question = st.text_input(
            " Ask a question about your data",
            placeholder="e.g., Which product category had the highest sales last year?",
            label_visibility="collapsed"
        )
    with col2:
        analyze_button = st.button("Analyze", type="primary", width='stretch')
    if analyze_button and question.strip():
        with st.spinner("Analyzing data..."):
            schema_text = get_schema_description(df)
            try:
                plan, planner_reasoning = call_planner_llm(schema_text, question)
            except Exception as e:
                st.error(f" Planner agent failed: {e}")
                st.stop()
            with st.expander(" Analysis Plan", expanded=False):
                col1, col2 = st.columns(2)
                with col1:
                    st.json(plan)
                with col2:
                    if planner_reasoning:
                        st.markdown("**AI Reasoning Process:**")
                        st.text_area("Planner Reasoning", planner_reasoning[:1000] + "..." if len(planner_reasoning) > 1000 else planner_reasoning, 
                                   height=200, label_visibility="collapsed")
            try:
                result_df = run_analysis_plan(df, plan)
            except Exception as e:
                st.error(f" Error running analysis: {e}")
                st.info(" Tip: Try rephrasing your question or be more specific about the columns you want to analyze.")
                st.stop()
        st.markdown("---")
        st.markdown("## Results")
        chart_buf = generate_chart(result_df, plan) 
        if chart_buf is not None:
            col1, col2 = st.columns([2, 1])
            with col1:
                st.image(chart_buf)
            with col2:
                st.markdown("### Key Metrics")
                target_col = plan.get("target_column")
                if target_col in result_df.columns:
                    st.metric("Total", f"{result_df[target_col].sum():,.2f}")
                    st.metric("Average", f"{result_df[target_col].mean():,.2f}")
                    st.metric("Max", f"{result_df[target_col].max():,.2f}")
                    st.metric("Min", f"{result_df[target_col].min():,.2f}")
        else:
            st.info("Chart generation skipped (not applicable for this query)")
        with st.expander("Results Table", expanded=True):
            st.dataframe(result_df, width='stretch', height=400)
            csv_data = result_df.to_csv(index=False)
            st.download_button(
                label="Download Results as CSV",
                data=csv_data,
                file_name=f"analysis_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
                mime="text/csv"
            )
        with st.spinner("Generating insights..."):
            result_df_serializable = result_df.copy()
            for col in result_df_serializable.columns:
                if pd.api.types.is_datetime64_any_dtype(result_df_serializable[col]):
                    result_df_serializable[col] = result_df_serializable[col].astype(str)
                elif pd.api.types.is_period_dtype(result_df_serializable[col]):
                    result_df_serializable[col] = result_df_serializable[col].astype(str)
            result_summary = result_df_serializable.to_dict(orient="records")
            explanation, explainer_reasoning = call_explainer_llm(
                question=question,
                plan=plan,
                result_summary=result_summary,
            )
        st.markdown("---")
        st.markdown("## AI Insights")
        st.markdown(f'<div class="insight-box">{explanation}</div>', unsafe_allow_html=True)
        if explainer_reasoning:
            with st.expander(" AI Reasoning Process", expanded=False):
                st.text_area("Explainer Reasoning", explainer_reasoning[:1500] + "..." if len(explainer_reasoning) > 1500 else explainer_reasoning,
                           height=300, label_visibility="collapsed")

En üstte, sayfayı st.set_page_config ve panoyu stillendirmek için CSS ile yapılandırırız. Kullanıcı önce bir CSV yükler; bunu bir DataFrame'e alır, preprocess_dates'ten geçirir ve satır/sütun sayıları, bellek kullanımı ve hızlı bir özetle birlikte Preview Data açılır bölümünde gösteririz.

Arka planda, get_schema_description() fonksiyonuyla bir şema oluşturur, JSON planını almak için call_planner_llm'yi çağırır ve hem planı hem de modelin akıl yürütmesini bir açılır bölümde gösteririz. Daha sonra Veri Çalışanı planı pandas ile yürütür ve Results bölümü generate_chart'tan gelen bir grafiği temel metriklerle birlikte gösterir.

Son olarak, AI Insights kutusu için bir açıklama üretmek üzere soruyu, planı ve sonuç özetini call_explainer_llm ile çağırırız; kaputun altına göz atmak isteyenler için isteğe bağlı bir AI Reasoning Process açılır bölümü de vardır.

Her şeyi app.py olarak kaydedin ve çalıştırın:

streamlit run app.py

Aşağıdaki videoda, iş akışının kısaltılmış bir sürümünü eylem hâlinde görebilirsiniz:

Sonuç

Bu eğitimde, herhangi bir CSV'yi alan, modeli buna göre tanımlayan, DeepSeek V3.2 Speciale modeline bir JSON analiz planı tasarlatan, bu planı pandas ile çalıştıran, çıktıyı Matplotlib/Seaborn ile görselleştiren ve ardından sonuçları kullanıcı dostu içgörülere çeviren bir Streamlit uygulaması inşa ettik.

Buradan, aynı mimariyi SQL ambarlarına, Parquet dosyalarına veya tam bir BI yığına genişletebilirsiniz. Yalnızca CSV yükleyicisini bir veritabanı bağlayıcısıyla değiştirin, yeni işlemler ekleyin veya başka akıl yürütme modellerini bağlayın. 

Daha fazla DeepSeek ile çalışma hakkında bilgi edinmek isterseniz, Working with DeepSeek in Python kursuna göz atmanızı öneririm. Ayrıca LangSmith Agent Builder ve Google Antigravity eğitimimizi de okumalısınız.

DeepSeek V3.2 Speciale SSS

DeepSeek V3.2 Speciale yerine farklı bir model kullanabilir miyim?

Mimari, modeli talimatları izleyip yapılandırılmış JSON üretebildiği sürece modelden bağımsızdır. Herhangi bir OpenAI uyumlu modeli kullanabilirsiniz; istemcide sadece base_url ve model adını güncelleyin.

Bu Yapay Zekâ Veri Analisti Aracısı hangi tür soruları en iyi şekilde işler?

Mevcut kurulum, group-by özetleri, karşılaştırmalar, temel filtreler ve basit toplulaştırmalar gibi betimleyici analitiklerde en güçlüdür. Keyfi SQL sorguları için tasarlanmamıştır; ancak JSON şemasını ve run_analysis_plan()'i daha karmaşık işlemleri destekleyecek şekilde genişletebilirsiniz.

CSV'im ne kadar büyük olursa bu sistem sorun yaşamaya başlar?

Sınırlayıcı faktör sadece model değil, aynı zamanda pandas ve bellek de olabilir. Uygulama, donanımınıza bağlı olarak küçük ve orta ölçekli veri kümelerini işler. Çok büyük tablolarla çalışıyorsanız, örnekleme, ön-aggregasyon yapma veya CSV/pandas katmanını bir veritabanıyla değiştirme seçeneklerini değerlendirin.

Bunu hassas verilerle çalıştırmak güvenli mi?

Varsayılan olarak, şemayı ve sonuçları modele tanımladığınızda yüklediğiniz tüm veriler DeepSeek'in API'sine gönderilir. Hassas veya düzenlemeye tâbi veri kümeleri için sütunları anonimleştirmeyi, gerekli olanın ötesinde ham değerler göndermekten kaçınmayı ve uyumlu bir akıl yürütme modelini yerelde barındırmayı düşünmelisiniz.

Neden doğrudan modele sormak yerine birden fazla aracı kullanıyoruz?

Ham CSV'yi doğrudan modele gönderebilirsiniz, ancak yeniden üretilebilirliği ve kontrolü kaybedersiniz. İş akışını Planlayıcı (JSON plan), Veri Çalışanı (pandas yürütme) ve Açıklayıcı (doğal dil içgörüleri) olarak ayırarak her adımı şeffaf, ayıklanabilir ve denetlenebilir hâle getirirsiniz.

Aashi Dutt

Yazar

Aashi Dutt

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

İlgili