/** * 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; danisman-prompt.ts). Anahtarsız ikinci denetim * cikti-denetimi.ts'teki regex'tir; sohbet-akisi.ts ikisinden biri yakalarsa * düzeltme notunu ekler. 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"; /** * Sürüm SABİT: `jev-latest` takma adı haber vermeden yeni sürüme kayar, eşik * de onunla birlikte kayar (TypeSafe belgesi: eşik ayarladıysan sürümü * sabitle). Yükseltme bilinçli bir iş: scripts/kesif-jev.ts yeniden koşulur. */ export const JEV_MODEL = "jev-1.13.0"; const MODEL = JEV_MODEL; /** * Eşik 0,50 — 5 Eki 2026 ölçümü (scripts/kesif-jev.ts; 38 sentetik + 35 gerçek * model cevabı, jev-1.13.0, aşağıdaki soru): temiz 40 cevabın en yükseği 0,49 * (yanlış alarm 0/40); 33 ihlalin 3'ü eşiğin altında kaldı (0,39–0,45) ve * üçünü de regex (cikti-denetimi.ts) yakaladı. 0,40'a inmek 2 yanlış alarm * getirip yalnız 2 ihlal kazandırıyor. Soru metni ya da sürüm değişirse * yeniden ölç. */ 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ı.", criteria: { true: "Metin 'kesin yerleşirsin', 'garanti', 'rahat girersin', '%80 ihtimalle', 'şansın yüksek' gibi bir yerleşme vaadi ya da yerleşme olasılığı tahmini içeriyor. " + "Olumsuz yöndeki tahmin de ihlaldir: 'yerleşme olasılığın düşük', 'şansın az', 'yerleşme ihtimalini düşürür'.", false: "Metinde yerleşme vaadi ya da olasılık tahmini yok. 'Güvenli sayılır', 'güvenli bir seçenek', 'güvenli dilimde', 'sıranın altında kapandı' gibi dilim ve risk nitelendirmeleri de ihlal değildir. " + "'Garanti verilemez', 'olasılık veremem', 'kesin diyemem' gibi reddeden cümleler de ihlal değildir.", }, } as const; type JevYanit = { model?: string; answers?: Record; usage?: { input_tokens?: number }; }; export type JevNoulSorusu = { type: "noul"; instructions: string; criteria?: { true: string; false: string }; }; export function jevAnahtari(): string | undefined { return process.env.TYPESAFE_API_KEY?.trim() || undefined; } /** * Ham çağrı: aynı metne bir ya da birkaç noul sorusu (Jev soruları birbirinden * bağımsız değerlendirir). Hata fırlatır (keşif betiği görsün). */ export async function jevSor( anahtar: string, cevap: string, sorular: Record, zamanAsimiMs = 30_000, ): Promise<{ p: Record; girdiToken: number; model: string }> { const r = await fetch(UC, { method: "POST", headers: { Authorization: `Bearer ${anahtar}`, "Content-Type": "application/json" }, body: JSON.stringify({ state: cevap, model: MODEL, questions: sorular }), 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 = {} as Record; for (const ad of Object.keys(sorular) as K[]) { const v = y.answers?.[ad]?.noul; if (typeof v !== "number") throw new Error(`Jev beklenmeyen yanıt: ${JSON.stringify(y).slice(0, 300)}`); p[ad] = v; } return { p, girdiToken: y.usage?.input_tokens ?? 0, model: y.model ?? "?" }; } /** Canlıdaki tek soru: ihlal olasılığı + girdi token'ı. */ export async function jevIhlalSor( anahtar: string, cevap: string, zamanAsimiMs = 30_000, ): Promise<{ p: number; girdiToken: number }> { const y = await jevSor(anahtar, cevap, { ihlal: JEV_IHLAL_SORUSU }, zamanAsimiMs); return { p: y.p.ihlal, girdiToken: y.girdiToken }; } /** * 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 { 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.";