Read
Live
Sabah BaskısıPazartesi, 10 Ağustos 2026Barcelona
04 · Teardown

Sıfır uydurma veri — kendi yazdıklarım hariç

Bir dil modeli asla rakam uyduramasın diye bir hisse araştırma hattı kurdum. İşe yaradı. Sonra içinde üç uydurma rakam buldum. Üçü de benimdi.

nocashflow.net · Finance Engineering ~10 dk · TR/EN · Ağustos 2026

01 — Ne inşa ediyordum

NoCashFlow Stock Analyzer bir hisse kodu alıyor ve yedi sayfalık iki dilli bir hisse araştırma raporu döndürüyor: yapısal trendler, çeyreklik momentum, finansal sağlık, emsallere karşı değerleme, fiyat hareketi ve ortaklık yapısı, riskler ve sentezlenmiş bir hüküm. Altı uzman ajan paralel çalışıyor; bir editör ajan onların taslaklarını sentezliyor.

Mimarinin tek bir kuralı var, geri kalan her şey ondan türüyor. Adı API-First: rapordaki her sayı, veri API'sinden Python'da hesaplanır. Modeller ham tabloları hiç görmez ve hiç aritmetik yapmaz. Ellerine bitmiş rakamlardan oluşan bir JSON bloğu geçer ve sistem prompt'unda onunla ne yapabilecekleri söylenir:

Hiçbir sayıyı yeniden hesaplama, yeniden türetme ya da uydurma — verilen rakamları olduğu gibi kullan. Bir rakam null ise tahmin etmek yerine verinin mevcut olmadığını söyle.

Bu iki kez uygulanıyor: bir kez prompt'ta, bir kez mimaride. Hiçbir ajanın hesap makinesi yok. Bir modelin sayıları etkilediği tek yer emsal seçimi — bir hisse kodu listesi döndürüyor, o kadar; çarpanların kendisi yine Python'da çekilip medyanlanıyor.

Bu tasarımdan emindim. Hâlâ eminim. İddia ettiği şeyi tam olarak yapıyor.

Sadece, kapsadığını sandığım şeyi kapsamıyormuş.

02 — Üç rakam

Bir

Hiç çalışmamış bir modele teşekkür eden künye

Sistemin ürettiği her rapor, onu üretmemiş bir modeli künyesinde anıyordu. Hat başka bir sağlayıcıya taşınmıştı; künye metni yerinden kıpırdamamıştı. Eskisinin adını yazıyordu. Aynı dosyada, API anahtarı olmayan kullanıcıya gösterilen mesaj, kodun hiç okumadığı bir kimlik bilgisini ayarlamasını söylüyordu — o talimatı izleyen biri sistemi hiç başlatamazdı.

İki

Her şirkete aynı üç senaryo

Her senaryo değerlemesi −%25 ayı, %0 baz ve +%25 boğa senaryosu döndürüyordu. Her şirket için. Fiyat, kâr, büyüme, sektör ya da raporun içindeki başka hiçbir şey fark etmeksizin.

Üç

Kendi ölçeğine erişemeyen bir hüküm

Hüküm skorunun beş rejimi vardı. İkisine hiçbir zaman ulaşılamıyordu.

Bunların hiçbiri bir dil modelinden çıkmadı. Korkuluk kusursuz tuttu. Modellerin dokunduğu her rakam doğru, kaynaklı ve tekrar üretilebilirdi. Uydurmalar Python'ın içindeydi.

03 — Neden inandırıcı görünüyorlardı

Künye, onu yazdığım gün doğruydu. Mekanizmanın tamamı bu. Bir sağlayıcı değişikliği import'ları, ortam değişkenlerini, model kimliklerini değiştirir — ve bunların her biri kendini duyurur, çünkü yanlış yaparsanız kod çalışmayı bırakır. Düzyazı bunu yapmaz. Üretilen bir raporun altındaki cümle, yazarı tarafından tam olarak bir kez okunur — yazıldığı gün — ve bir daha okunmaz. Hata üretmedi, düşen test üretmedi, bozuk çıktı üretmedi. Ürettiği tek şey, tüm varlık sebebi kaynak izlenebilirliği olan bir sistemin her sayfaya bastığı, kaynak hakkında yanlış bir iddiaydı.

Senaryo değerlemesi, metot adı takınmış bir totolojiydi. Satır, olduğu gibi:

src/intelligence.py · scenario_valuation()exhibit
# mult senaryo çarpanı: 0.75, 1.00, 1.25.
# Şirket hesaba hiç girmiyor:
"upside_downside_pct": round(((mult - 1) * 100), 1)
# → ayı −25.0, baz 0.0, boğa +25.0. Her hisse için, sonsuza kadar.

Bunun hayatta kalma sebebi hatanın kendisinden daha ilginç: baz çarpan şirketin kendi forward F/K'sıydı, temel büyüklük de kendi forward hisse başı kârıydı. Bu ikisini çarpınca tanımı gereği güncel hisse fiyatını elde edersiniz. Baz senaryo sıfırdan başka bir şey olamazdı. Baz bugüne çivilendikten sonra ayı ve boğa senaryoları da benim seçtiğim sayılardan ibaret.

Ve analiz gibi görünüyordu. Bir metot adı vardı (scenario_valuation), yapılandırılmış bir çıktısı vardı, üç adlandırılmış senaryosu vardı ve — belirleyici olan — Python'da hesaplanıyordu. API-First kuralının güvenilirliğini, o işin hiçbirini yapmadan devraldı.

Skor kendi ölçeğiyle hiç buluşmadı. Yedi boyut, her biri 60–70 puanla sınırlı, ortalaması alınıyor. Ulaşılabilir en yüksek değer 67'ydi. En üst hükmün eşiği 75'ti. "Strongly Constructive" erişilemeyen koddu. Diğer uçtaki "Negative" de öyle — taban 35'ti. Sistemin bugüne dek analiz ettiği her şirket ortadaki üç rejime düşüyordu ve bu, şirketler hakkında bir bulgu değildi.

Bir boyut kendi cümlesini hak ediyor. Değerleme skoru şöyleydi:

src/intelligence.py · model_score()exhibit
"valuation": 60 if v.get("pe_ttm") is not None else 45,
# F/K var mı diye ölçüyor. Yüksek mi diye değil.

Veri mevcudiyetini puanlayan bir değerleme boyutu, başka bir etiket takmış ikinci bir veri kalitesi metriğidir.

04 — Onları nasıl yakaladım

İzleme sayesinde değil — hiç yoktu. Düşen bir test sayesinde değil — bunları kapsayan test yoktu. Yanlış bir çıktıdan da değil, çünkü hiçbiri yanlış görünen bir çıktı üretmiyordu.

Onları yakaladım çünkü proje için herkese açık bir build log kaydı yazıyordum ve kartta Result etiketli bir alan vardı. Orayı dürüstçe doldurmak için kendi kodumu, ne yaptığını anlatabilecek kadar yakından okumak zorunda kaldım — ve bir şeyi anlatmak, onu yazmaktan başka bir eylem. Bir saat içinde elimde üç defekt vardı; depoda onları tarihleyecek bir git geçmişi oluşmadan önceden beri oradaydılar.

Bir sistem hakkında iddiayı yayımlama niyeti, o sistemi çalıştırmaktan daha iyi bir denetçidir.

Ders rahatsız edici ve bence genellenebilir. Yanlış bir varsayımı, onu kontrol edebilecek insanların önünde ifade etmek zorunda kalmaktan daha hızlı açığa çıkaran bir şey yok.

05 — Kök neden

Tek cümle: API-First sayıların nereden geldiğini yönetir. Bir anlam taşıyıp taşımadıkları hakkında hiçbir şey söylemez.

Kural belirli bir tehdide karşı tasarlanmıştı: bir modelin, hiçbir kaynakta bulunmayan bir rakamı ileri sürmesi. O tehdidi tamamen bertaraf ediyor. Ama sessizce ikinci bir kategori yarattı ve ben ona hiç ad vermedim: deterministik olarak hesaplanan ve yine de bilgiden yoksun rakamlar. Bunlar halüsinasyonlardan daha tehlikeli, üç sebeple. İstikrarlılar, dolayısıyla hiç anormal görünmezler. Tekrar üretilebilirler, dolayısıyla incelemeden sağ çıkarlar. Ve tam olarak raporu güvenilir kılması gereken kural tarafından korunurlar — bir sayı Python'da hesaplandığı anda onu sorgulamayı bıraktım.

Bunun altında üç somut neden. Kaynak bilgisinin düzyazı olarak saklanması: sağlayıcı adı birkaç yerde geçiyordu, hiçbiri kodun çalıştırdığı bir şeyden türetilmiyordu. Cevabını girdi olarak kullanan bir değerleme: şirketin kendi çarpanına çapalanmak, baz senaryonun mevcut durum olmasını garantiler. Ölçeği kendi eşikleriyle hiç kontrol edilmemiş bileşik bir skor: en üst bandın ulaşılabilir olup olmadığını kimse sormamıştı.

06 — Gerçekte ne düzelttim

Üçünün ikisi yeniden inşa edildi. Biri edilmedi ve bunu yayımlamadan önce sessizce kapatmak yerine böyle yazıyorum, çünkü asıl mesele hangisinin hayatta kaldığı.

Senaryo değerlemesi artık fiyat üretiyor. Her senaryo temel büyüklük × çarpan; girdiler çarpılmadan önce yuvarlanıyor, böylece basılı iki sayıyı çarpan okur, basılı gerçeğe değere varıyor. Getiri her zaman gerçeğe değer / fiyat − 1. Her iki özdeşlik de hiçbir şey render edilmeden önce doğrulanıyor; kendi aritmetiğiyle çelişen bir değerleme okura ulaşmak yerine hata fırlatıp aşamayı düşürüyor.

Totoloji ortadan kalkmadı — açığa çıkarıldı. Baz senaryo hâlâ, tanımı gereği, aşağı yukarı bugünün fiyatı; çünkü baz çarpan hâlâ şirketin kendisininki. Değişen şu: rapor artık bunu söylüyor, buna piyasaya çapalı senaryo diyor ve yanına emsale çapalı rakamı koyuyor — senaryo bazını ve emsal medyanına dayalı göreli değeri sabit yarı yarıya harmanlayan tek bir House Fair Value. Emsal seti güvenilecek kadar sağlam değilse göreli bacak sessizce ağırlığı azaltılmak yerine tamamen düşürülüyor ve rapor bu dışlamayı adıyla söylüyor.

Motor ayrıca reddetmeyi öğrendi. Eşleşen çarpanı olan pozitif bir hisse başı temel büyüklük yoksa, dekoratif bir sayı yerine gerekçesi belirtilmiş unavailable dönüyor. 100×'in üzerindeki bir kâr çarpanı, piyasanın kâra ne ödediği hakkında değil, sıfıra yakın bir paydanın hakkında bir ifadedir — o dayanak atlanıyor ve atlama raporlanıyor.

Düzeltirken görmediğim dördüncü bir sorun ortaya çıktı. Değerleme, veri çekme anında, yani emsal seçimi çalışmadan önce hesaplanıyordu — dolayısıyla emsale çapalı her şey, her çalıştırmada boş bir emsal setine karşı hesaplanacaktı, sonsuza kadar. Blok artık emsaller geldikten sonra yeniden kuruluyor ve tutarlılığı orada yeniden kontrol ediliyor.

Skorun gerçek bir ölçeği var — büyük ölçüde. Boyutlar artık 25–70 yerine 25–85 aralığına yayılıyor ve değerleme boyutu F/K'nın var olup olmadığını değil, emsal medyanına göre primi ya da iskontoyu ölçüyor. Mevcut ağırlıklarla ulaşılabilir aralık 26,8 ile 83,1; yani en üst hüküm artık erişilebilir. Alttaki hâlâ değil: "Negative" için 25'in altında bir skor gerekiyor, taban ise 26,8. Kendime bitti diye anlattığım iki şey daha bitmemiş: eksik bir boyut hâlâ ortalamadan çıkarılmak yerine 50 ile dolduruluyor ve yedi boyuttan kaçının gerçekten puanlandığını hiçbir yer raporlamıyor.

hüküm ölçeği · ulaşılabilir aralıkexhibit
 ÖnceSonra
Ulaşılabilir skor35 – 6726,8 – 83,1
"Strongly Constructive" (≥ 75)erişilemezerişilebilir
"Negative" (< 25)erişilemezhâlâ erişilemez

Kaynak künyesi hâlâ elle yazılmış bir cümle. Defektin yarısı kapandı: anahtar yokken gösterilen mesaj artık yükleyicinin gerçekten okuduğu ortam değişkeninin adını veriyor, yani talimat izlenebilir. Künye izlenebilir değil. Hâlâ yanlış sağlayıcıyı anıyor, iki dilde de, düz bir metin sabiti olarak — bu yazının açılışındaki defektin ta kendisi, ben onun hakkında yazarken dosyada duruyor. Düzeltmesi tasarlandı, yazılmadı: sağlayıcıyı çalışan istemci nesnesinden oku, model kimliklerini gerçekten çalışan ekipten topla ve cümleyi bu durumdan doldur; böylece model değiştirmek künyeyi de değiştirir ve unutulacak bir metin kalmaz.

Test paketi gerçek, ve iddia ettiğim on iki test değil. 127 test geçiyor, 32'si değerleme motorunda; her biri yeni kodda geçsin diye değil, eski kodda düşsün diye yazıldı. En çok önemsediğim test_scenario_output_is_never_a_ratio_around_one. Yanında: test_no_positive_fundamental_yields_an_unavailable_scenario, test_weak_peer_set_drops_the_relative_leg_rather_than_hiding_it ve test_inconsistent_valuation_fails_the_stage. Henüz hiçbir testin doğrulamadığı şey ise tam da bu bölümün konusu: hüküm ölçeğindeki her rejimin erişilebilir olduğu.

07 — Yapısal olarak ne değişti

Bundan iki kural çıktı ve ikisi de üç hatadan daha geniş.

Sistem hakkındaki kullanıcıya görünen her iddia, elle yazılmış bir sabit değil, çalışma anındaki durumdan türetilmelidir. Rapor kendi nasıl üretildiği hakkında bir şey söylüyorsa, o cümlenin hesaplanmış olması gerekir. Bir sistem hakkındaki düzyazı, geçersiz kılma mekanizması olmayan bir cache'tir. Bu, ifade ettiğim ama henüz uygulamaya koymadığım bir kural — künyeye bakın.

Deterministik her rakam "mevcut değil" diyebilmelidir. Her zaman bir şey döndüren bir hesap, er ya da geç anlamsız bir şey döndürür; ve bunu hiç itiraf etmeyen anlamsız bir sayı, boşluktan beterdir. Değerleme motorunda artık her blokta bu yol var. Hüküm skorunda yok: eksik boyutları hâlâ 50'ye çözülüyor.

Yapısal bir düzeltme tam olarak yerine oturdu ve o da okuyarak değil, ölçerek bulundu. Tek bir uzman ajanın fırlattığı istisna thread havuzundan dışarı yayılıyor ve tüm çalıştırmayı öldürüyordu — tamamlanmış beş bölümü çöpe atarak. Eksik bir bölüm bir uyarıdır. Kaybedilmiş bir çalıştırma bir kesintidir. İkisi aynı olaydı. Artık her uzman yalıtılmış durumda: hatası kaydediliyor, bölümü boş bırakılıyor ve boşluk, çelişki dedektörü aracılığıyla editöre bildiriliyor.

Hâlâ eksik olan, ölçümün kendisi. Hat, kendi çalıştırmaları hakkındaki temel soruları yanıtlayamıyor — bir tur ne kadar sürüyor, kaç model çağrısı yapıyor, kaç rakam boş döndü, hangi adımlar neden atlandı. İlerleme hâlâ bir callback'e geçirilip atılan, insan-okunur metinler. Bu, adım başına durum ve faz başına süre içeren yapılandırılmış bir log hâline gelene kadar, bu sistemin davranışı hakkında yaptığım her performans iddiası bir ölçüm değil, bir hatıradır.

08 — Asıl öğrendiğim

Deterministik, doğru ile eşanlamlı değil. Bir hesabı modelden çıkarıp Python'a taşımak bir hata modunu kaldırır ve yerine bir yenisini koyar: incelenmemiş formül. Modelin çıktısı hiç değilse okunur — insan inceleme adımının tüm amacı budur. Python'ın çıktısına güvenilir; tam da Python'dan geldiği için.

Finansal yapay zekâda önemli olan hata modları gürültülü olanlar değil. Bariz biçimde yanlış bir sayı üreten bir model çözülmüş bir problemdir; fark edersiniz, düzeltirsiniz. İstikrarlı, makul, tekrar üretilebilir ve yapısal olarak anlamsız bir sayı üreten bir hat ise sahip olduğunuz her incelemeden süresiz olarak geçer — çünkü hiçbir yanı başarısızlığa benzemez.

İlke

Python'da hesaplanmış bir sayı kanıt değildir. Görgüsü daha iyi bir iddiadır — ve onu güvenilir kılan korkuluk, tam olarak beni onu okumaktan alıkoyan korkuluktur.

09 — Hâlâ güvenmediklerim

Veri kaynağı. Her şey tek bir resmî olmayan, oran-limitli API'den geliyor. Yeniden deneme ve cache yokluğu yönetiyor. Hattaki hiçbir şey yanlışlığı yönetmiyor — makul ama hatalı bir rakam gelirse aşağıdaki her kontrol onu geçirir ve her ajan onu sadakatle yorumlar. "Model bunu uydurmadı" ile "bu doğru" farklı ifadelerdir. Şu an yalnızca birincisini garanti edebiliyorum.

Emsal seti. Karşılaştırılabilirleri bir model seçiyor. Çarpanlar Python'ın, ama karşılaştırma seti değil — ve artık House Fair Value'nun yarısını çapalıyor, bu da o seçimin bedelini yükseltiyor. Künyede açıklanıyor. Çözülmüş değil.

Bileşik skor. Artık matematiksel olarak tutarlı ve hâlâ kimsenin ulaşamadığı bir rejimi var. Eşikler benim yargım ve bir şeye, benim seçtiğim bir çizgiyi bir sayı geçtiği için "Constructive" demek, tam da bu yazının konusu olan sahte kesinlik türü. Raporda mı kalmalı yoksa içeride bir sıralama aracı mı olmalı, karar vermiş değilim.

Kaynak satırı. Yukarıda adı kondu, hâlâ açık, her sayfaya basılıyor.

Anlatısal nedensellik. Ajanlara, sonuçları Observed, Inferred ya da Speculative olarak etiketlemeleri ve sunulmuş kanıt olmadan asla nedensellik iddia etmemeleri talimatı veriliyor. Bu bir talimat, bir zorlama değil. Uyumu doğrulayan bir mekanizmam yok.

Kimsenin okumadığı her rapor. Bunların hiçbiri, bir insan okumadan sistemden çıkmamalı. Bu, daha iyi modeller beklenen geçici bir kısıt değil.

Hat, bir dil modelinin veri uydurmasını engellemek için kuruldu. Bunu yapıyor. Yapamadığı şey beni engellemekti — ve korkuluğu tasarlamaya o kadar uzun zaman ayırmıştım ki, duvarın kendi durduğum tarafına bakmak hiç aklıma gelmedi.

Stock Analyzer canlı bir yapım. Güncel mimari, stack ve açık konular Finance Engineering bölümünde.

Bu yazı finansal/teknik bir eğitim notudur; yatırım tavsiyesi değildir. Kod referansları projeyi yazıldığı andaki haliyle anlatır; depo ilerler.

← Finance Engineering