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>
This commit is contained in:
75
src/lib/ai/jev.ts
Normal file
75
src/lib/ai/jev.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* 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.";
|
||||
Reference in New Issue
Block a user