Files
kolaytercih/src/lib/ai/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

76 lines
3.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.
/**
* Danışman çıktı denetimi — Jev (TypeSafe "System One" modeli).
*
* Danışman cevabı bittikten sonra tek soru sorulur: cevap yerleşme garantisi
* veriyor ya da yerleşmeyi olasılık/yüzde/ihtimal diliyle tahmin ediyor mu
* (sistem prompt'undaki yasak; api/soru/route.ts). Jev metin üretmez, 0-1
* arası bir olasılık döner. Yalnız danışmanın CEVABI gider; kullanıcının
* mesajı, adı ya da e-postası gitmez. Sağlayıcı ABD'de — /gizlilik "Hizmet
* sağlayıcılar" bölümünde yazılı; değişirse aynı commit'te güncellenir.
*
* Güvenli tarafa düşer: anahtar yoksa, zaman aşımında ya da hata olursa
* denetim atlanır (null), cevap olduğu gibi kalır. Saf modül: Next dışından
* (scripts/kesif-jev.ts) da import edilir.
*/
const UC = "https://api.typesafe.ai/v1/systemone";
const MODEL = "jev-latest";
/** 5 Eki 2026 keşfi: temiz cevaplar ≤0,37, ihlaller ≥0,74 (36 örnek). */
export const JEV_IHLAL_ESIGI = 0.5;
/** Akış sonunda bekletir; ölçülen p95 ~335 ms. */
const ZAMAN_ASIMI_MS = 2000;
export const JEV_IHLAL_SORUSU = {
type: "noul",
instructions:
"Bu metin bir üniversite tercih danışmanının öğrenciye verdiği Türkçe cevaptır. Cevap, öğrenciye bir programa yerleşeceğine dair garanti/söz/kesinlik veriyor mu YA DA yerleşmeyi olasılık, yüzde, ihtimal ya da şans diliyle tahmin ediyor mu? " +
"Şunlar ihlal DEĞİLDİR: 'Güvenli / Az riskli / Riskli' etiketleri; 'garanti verilemez' gibi reddeden cümleler; burs indirimi, doluluk, öğrenci oranı ya da mezunların istihdam oranı gibi yerleşme tahmini olmayan yüzdeler; geçmiş taban sırası ve kontenjan sayıları.",
} as const;
type JevYanit = {
answers?: Record<string, { noul?: number }>;
usage?: { input_tokens?: number };
};
export function jevAnahtari(): string | undefined {
return process.env.TYPESAFE_API_KEY?.trim() || undefined;
}
/** Ham çağrı: ihlal olasılığı + girdi token'ı. Hata fırlatır (keşif betiği görsün). */
export async function jevIhlalSor(
anahtar: string,
cevap: string,
zamanAsimiMs = 30_000,
): Promise<{ p: number; girdiToken: number }> {
const r = await fetch(UC, {
method: "POST",
headers: { Authorization: `Bearer ${anahtar}`, "Content-Type": "application/json" },
body: JSON.stringify({ state: cevap, model: MODEL, questions: { ihlal: JEV_IHLAL_SORUSU } }),
signal: AbortSignal.timeout(zamanAsimiMs),
});
if (!r.ok) throw new Error(`Jev ${r.status}: ${(await r.text()).slice(0, 300)}`);
const y = (await r.json()) as JevYanit;
const p = y.answers?.ihlal?.noul;
if (typeof p !== "number") throw new Error(`Jev beklenmeyen yanıt: ${JSON.stringify(y).slice(0, 300)}`);
return { p, girdiToken: y.usage?.input_tokens ?? 0 };
}
/**
* Danışman cevabının ihlal olasılığı; denetim yapılamadıysa null. Asla
* fırlatmaz — denetim cevabı düşürmemeli.
*/
export async function cevapIhlalOlasiligi(cevap: string): Promise<number | null> {
const anahtar = jevAnahtari();
if (!anahtar || !cevap.trim()) return null;
try {
return (await jevIhlalSor(anahtar, cevap, ZAMAN_ASIMI_MS)).p;
} catch (hata) {
console.warn("[jev] denetim atlandı:", hata instanceof Error ? hata.message : hata);
return null;
}
}
/** İhlal yakalanınca cevabın sonuna eklenen düzeltme notu. */
export const JEV_DUZELTME_NOTU =
"Not: Yukarıdaki cevap bir yerleşme garantisi ya da olasılık tahmini değildir. Taban sıraları her yıl değişir; karar verirken Güvenli / Az riskli / Riskli etiketlerine ve geçmiş taban sıralarına bak.";