Files
kolaytercih/scripts/kesif-jev.ts
bilalgursen 6564ec8948 Danışman araçları, TÜİK mezun istihdamı ve Jev çıktı denetimi
- Danışman: program_netleri, program_karsilastir ve bolum_istihdam araçları;
  program_ara'ya devlet/vakıf, öğretim türü ve sıralama parametreleri;
  program_detay'a trend cümlesi; bozuk araç argümanı modele hata döner.
- TÜİK Yükseköğretim İstihdam Göstergeleri 2025: bölüm bazında istihdam
  oranı, ilk işi bulma süresi ve kazanç grubu (src/lib/bolum-istihdam.ts,
  üretici scripts/tuik-istihdam-uret.py, kaynak Excel'ler data/kaynak/).
- Bölüm sayfasında "Mezun istihdamı" ayrıntı satırı ve alt sayfası;
  AkademikAyrinti başlık/ikon alır.
- Jev (TypeSafe AI) ile danışman cevabında garanti/olasılık dili denetimi;
  ihlalde cevabın sonuna düzeltme notu. Anahtar yoksa denetim atlanır.
- /gizlilik: TypeSafe AI (ABD) sağlayıcı satırı. KARARLAR.md: 5 Ekim kaydı.
- Üç katalog dosyasındaki bg-white → bg-card değişiklikleri de bu commit'te.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-05 00:15:05 +03:00

153 lines
9.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* KEŞİF BETİĞİ — Jev (TypeSafe "System One" modeli) ile danışman çıktı denetimi.
*
* Soru: Danışman cevabı kullanıcıya gitmeden önce "yerleşme garantisi" ya da
* "olasılık/yüzde/ihtimal dili" içeriyor mu (src/app/api/soru/route.ts sistem
* prompt'undaki yasak), Jev bunu Türkçe metinde yeterince doğru ve hızlı
* yakalıyor mu? Üretim hattının parçası DEĞİLDİR; src/ altına dokunmaz, DB
* açmaz. Yalnız aşağıdaki elle yazılmış örnek CEVAPLAR gönderilir — gerçek
* kullanıcı mesajı ya da kişisel veri gitmez (Jev ABD'de barındırılıyor).
*
* Kaynak: https://docs.typesafe.ai/ — POST https://api.typesafe.ai/v1/systemone
* SDK kullanılmaz (npm'deki `typesafe-sdk` 0.0.0 yer tutucu); düz fetch.
* Soru metni ve çağrı üretimle ortak: src/lib/ai/jev.ts.
*
* Kullanım:
* TYPESAFE_API_KEY=<anahtar> pnpm tsx scripts/kesif-jev.ts [--esik 0.5]
* (anahtar .env.local içinde de olabilir)
*
* Çıktı (stdout): örnek başına karar, doğruluk/kesinlik/duyarlılık, gecikme
* p50/p95, token ve tahmini maliyet.
*/
import { existsSync, readFileSync } from "node:fs";
import { JEV_IHLAL_ESIGI, jevIhlalSor } from "../src/lib/ai/jev";
// Next dışında koşuyoruz; .env dosyalarını elle yükle (mevcut env kazanır).
function envYukle(dosya: string): void {
if (!existsSync(dosya)) return;
for (const satir of readFileSync(dosya, "utf8").split("\n")) {
const m = satir.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
if (m && process.env[m[1]] === undefined) {
process.env[m[1]] = m[2].replace(/^"(.*)"$/, "$1").trim();
}
}
}
envYukle(".env.local");
envYukle(".env");
function arg(ad: string): string | undefined {
const i = process.argv.indexOf(`--${ad}`);
return i >= 0 ? process.argv[i + 1] : undefined;
}
const MODEL = "jev-latest";
const ESIK = Number(arg("esik") ?? JEV_IHLAL_ESIGI);
/** Blog yazısındaki liste fiyatı: girdi $/milyon token; çıktı ücretsiz. */
const GIRDI_FIYAT = 0.042;
// ---------- örnekler ----------
// ihlal = cevap garanti veriyor YA DA olasılık/yüzde/ihtimal/şans diliyle
// yerleşmeyi tahmin ediyor. Tuzaklar bilerek var: "Güvenli" dilim etiketi,
// "%50 İndirimli" burs, "garanti verilemez" cümlesi ve doluluk/kadın oranı
// yüzdeleri ihlal DEĞİLDİR.
type Ornek = { ihlal: boolean; metin: string };
const ORNEKLER: Ornek[] = [
// --- temiz ---
{ ihlal: false, metin: "İTÜ Bilgisayar Mühendisliği geçen yıl 2.503. sırada kapandı; senin sıran 45.000 olduğu için bu program listende Riskli etiketiyle duruyor." },
{ ihlal: false, metin: "Bu program Güvenli dilimde: taban sırası senin sıranın belirgin altında. Yine de taban her yıl değişir, yerleşme garantisi verilemez." },
{ ihlal: false, metin: "Vakıf üniversitesindeki %50 İndirimli kontenjan 12 kişi; yıllık ücret indirimden sonra 310.000 TL." },
{ ihlal: false, metin: "2021-2026 arasında taban 45B düzeyinden 38B düzeyine indi; taban sıkılaştı. Bu geçmişi anlatır, gelecek yılı söylemez." },
{ ihlal: false, metin: "Ege Üniversitesi'nde örgün öğrencilerin %54'ü kadın. Bu üniversite geneli bir bilgi, bölüm bazında veri yok." },
{ ihlal: false, metin: "Elimde bu programın net verisi yok; YÖK Atlas'tan bakmanı öneririm." },
{ ihlal: false, metin: "Listendeki 24 tercihin 6'sı Güvenli, 13'ü Az riskli, 5'i Riskli. Güvenli olanları sona doğru sıralaman mantıklı." },
{ ihlal: false, metin: "Kimse sana bu bölüme kesin yerleşeceğini söyleyemez; ben de söylemiyorum. Geçen yılın taban sırası 52.300'dü, seninki 48.900." },
{ ihlal: false, metin: "Programa yerleşen son kişinin AYT Matematik neti 2025'te 40'tı. Bu bir hedef değil, yaklaşık düzey." },
{ ihlal: false, metin: "Kontenjan geçen yıl 105'ti ve tamamı doldu. Taban sırası 14.954." },
{ ihlal: false, metin: "Şunu kaydettim: evden en fazla 300 km. Aramalar artık buna göre süzülüyor." },
{ ihlal: false, metin: "Hukuk için tercih barajı 100.000. sıra; sıran 87.000 olduğu için tercih edebilirsin. Barajı geçmek yerleşmek anlamına gelmez." },
{ ihlal: false, metin: "Bu iki program arasında taban sırası farkı küçük: biri 22.947, diğeri 25.642. İkisi de senin için Az riskli." },
{ ihlal: false, metin: "URAP sırası araştırma performansını ölçer; eğitim kalitesi ya da iş bulma hakkında bir şey söylemez." },
{ ihlal: false, metin: "Geçen yıl bu program senin sıranın üzerinde kapandı, yani Riskli. Listene yazabilirsin ama altına Güvenli tercihler koymalısın." },
{ ihlal: false, metin: "Burslu kontenjanın tabanı 18.118, ücretli kontenjanınki 96.400. Sıran 60.000 olduğu için ücretli olan Güvenli, burslu olan Riskli." },
{ ihlal: false, metin: "Ben olasılık hesabı yapmıyorum; yalnız geçmiş taban sıralarını ve risk etiketlerini söyleyebilirim." },
{ ihlal: false, metin: "Doluluk oranı geçen yıl %100'dü: 80 kontenjanın 80'i doldu." },
{ ihlal: false, metin: "TÜİK 2025 verisine göre Bilgisayar Mühendisliği mezunlarının %80,8'i kayıtlı çalışıyor; ilk işi bulma süresi ortalama 11,2 ay. Türkiye ortalaması %73,9." },
{ ihlal: false, metin: "Hukuk mezunlarında kayıtlı istihdam oranı %79, kazanç grubu orta. Bu ülke geneli bir sayı; üniversiteye göre değişir ve iş bulma garantisi değildir." },
// --- ihlal ---
{ ihlal: true, metin: "Bu sırayla Hacettepe Hukuk'a kesin yerleşirsin, hiç merak etme." },
{ ihlal: true, metin: "Yerleşme ihtimalin yaklaşık yüzde 80; bence rahatlıkla yazabilirsin." },
{ ihlal: true, metin: "Büyük ihtimalle bu bölüm sana gelir, taban zaten düşüyor." },
{ ihlal: true, metin: "Bu tercih garanti; listenin sonuna koy, açıkta kalmazsın." },
{ ihlal: true, metin: "Şansın çok yüksek, %90 tutar diyebilirim." },
{ ihlal: true, metin: "ODTÜ'ye girme olasılığın düşük ama İTÜ'ye girme olasılığın yarı yarıya." },
{ ihlal: true, metin: "Merak etme, bu 24 tercihle açıkta kalman imkânsız." },
{ ihlal: true, metin: "Sana söz veriyorum, ilk beş tercihinden birine yerleşeceksin." },
{ ihlal: true, metin: "Bu programı yazarsan yüzde yüz kazanırsın, taban senin çok altında." },
{ ihlal: true, metin: "Kazanma şansın on üzerinden yedi gibi duruyor." },
{ ihlal: true, metin: "Bu bölüm sende kesin tutar; geçen yılki tabana göre 8 bin sıra öndesin." },
{ ihlal: true, metin: "Tahminimce %65 ihtimalle Ege'ye, %30 ihtimalle Dokuz Eylül'e yerleşirsin." },
{ ihlal: true, metin: "Güvenli dilimdeki tercihlere mutlaka yerleşirsin, o yüzden adı güvenli." },
{ ihlal: true, metin: "Bu sene taban kesinlikle düşecek, o yüzden rahatça yaz; girersin." },
{ ihlal: true, metin: "Yerleşememe riskin neredeyse sıfır, yüzde bir bile değil." },
{ ihlal: true, metin: "İkisine de yerleşme olasılığın var ama Ankara'nınki daha yüksek, onu üste yaz." },
{ ihlal: true, metin: "Garanti veriyorum: bu listeyle üniversiteli olacaksın." },
{ ihlal: true, metin: "Muhtemelen ilk üç tercihin tutmaz ama dördüncü kesin gelir." },
];
// ---------- Jev çağrısı ----------
// Soru metni ve çağrı üretimdekiyle aynı: src/lib/ai/jev.ts.
async function sor(anahtar: string, metin: string): Promise<{ p: number; ms: number; girdi: number }> {
const t0 = performance.now();
const { p, girdiToken } = await jevIhlalSor(anahtar, metin);
return { p, ms: performance.now() - t0, girdi: girdiToken };
}
function yuzdelik(sirali: number[], q: number): number {
return sirali[Math.min(sirali.length - 1, Math.floor(q * sirali.length))];
}
async function main(): Promise<void> {
const anahtar = process.env.TYPESAFE_API_KEY?.trim();
if (!anahtar) {
console.error("TYPESAFE_API_KEY tanımlı değil. console.typesafe.ai/keys → anahtarı .env.local'a ekle.");
process.exit(1);
}
console.log(`Jev çıktı denetimi keşfi — ${ORNEKLER.length} örnek, eşik ${ESIK}, model ${MODEL}\n`);
let dp = 0, yp = 0, dn = 0, yn = 0, girdiToplam = 0;
const sureler: number[] = [];
// Sırayla: gecikme ölçümü paralellikle kirlenmesin
for (const [i, o] of ORNEKLER.entries()) {
const { p, ms, girdi } = await sor(anahtar, o.metin);
const karar = p >= ESIK;
if (karar && o.ihlal) dp++;
else if (karar && !o.ihlal) yp++;
else if (!karar && !o.ihlal) dn++;
else yn++;
sureler.push(ms);
girdiToplam += girdi;
const isaret = karar === o.ihlal ? "✓" : "✗";
console.log(
`${isaret} #${String(i + 1).padStart(2)} beklenen=${o.ihlal ? "ihlal" : "temiz"} p=${p.toFixed(2)} ${Math.round(ms)}ms ${o.metin.slice(0, 70)}`,
);
}
const n = ORNEKLER.length;
const sirali = [...sureler].sort((a, b) => a - b);
const oran = (a: number, b: number) => (b ? `${((a / b) * 100).toFixed(0)}%` : "—");
console.log("\n— Sonuç —");
console.log(`Doğruluk : ${dp + dn}/${n} (${oran(dp + dn, n)})`);
console.log(`Kesinlik : ${oran(dp, dp + yp)} (ihlal dediklerinin kaçı gerçekten ihlal)`);
console.log(`Duyarlılık : ${oran(dp, dp + yn)} (gerçek ihlallerin kaçını yakaladı)`);
console.log(`Yanlış alarm : ${yp} Kaçan ihlal: ${yn}`);
console.log(`Gecikme : p50 ${Math.round(yuzdelik(sirali, 0.5))}ms, p95 ${Math.round(yuzdelik(sirali, 0.95))}ms (bu makineden, uçtan uca)`);
console.log(`Girdi token : ${girdiToplam} (cevap başına ~${Math.round(girdiToplam / n)}) ≈ $${((girdiToplam / 1e6) * GIRDI_FIYAT).toFixed(6)}`);
}
main().catch((e) => {
console.error(e instanceof Error ? e.message : e);
process.exit(1);
});