diff --git a/src/lib/ai/rapor.ts b/src/lib/ai/rapor.ts index b7cc15a..fd32c8c 100644 --- a/src/lib/ai/rapor.ts +++ b/src/lib/ai/rapor.ts @@ -53,11 +53,20 @@ import { ILK_YIL } from "../veri-yillari"; // Modele giden adlar (5 Eki 2026): üçüncü liste telde `guvenli`dir, iç anahtar // `garanti` kalır. Prompt'ta "garanti" alan adı ve dilim değeri olarak 10+ kez // geçince model sözcüğü adaya dönük metne taşıyordu (ölçüm: 19/888 gerekçe, -// 14/37 üst metin). Program kimliği alanı kısa (`id`): çıktı token'ı. -const AiTercihSchema = z.object({ - id: z.string(), - gerekce: z.string(), -}); +// 14/37 üst metin). +// +// Satır biçimi: nesne ({"id": …, "gerekce": …}) değil TEK DİZGİ, "id|gerekçe". +// Çıktı token'ının ~%20'si satır başına yinelenen JSON anahtarlarıydı; gecikme +// çıktı token'ıyla doğru orantılı (ölçüm aşağıda, raporUretAyrintili). +const SATIR_AYRACI = "|"; +type AiSatir = { id: string; gerekce: string }; +/** "106510077|Gerekçe cümlesi." → { id, gerekce }; ayraç yoksa satırın tamamı kimlik sayılır. */ +function satirCoz(ham: string): AiSatir { + const i = ham.indexOf(SATIR_AYRACI); + if (i < 0) return { id: ham.trim(), gerekce: "" }; + return { id: ham.slice(0, i).trim(), gerekce: ham.slice(i + 1).trim() }; +} +const AiTercihSchema = z.string(); type TelListe = "hayal" | "dengeli" | "guvenli"; const TEL_ADI: Record = { hayal: "hayal", @@ -95,6 +104,9 @@ KURALLAR (kesin): - Havuz üç dizi hâlinde verilir: havuz.hayal, havuz.dengeli, havuz.guvenli. hayal listesine YALNIZCA havuz.hayal içindeki programları koy; dengeli ve guvenli için de aynısı. Bir programı başka dizinin listesine taşıma. +- Listelerin her elemanı TEK dizgidir, biçimi: "id|gerekce" (ör. + "123456789|Seçtiğin alanla eşleşen, kontenjanı geniş bir program."). id, + havuzdaki programın id'sidir; dik çizgiden sonra gerekce gelir. - SADECE havuzdaki id'leri kullan; havuzda olmayan program ASLA önerme. Aynı id'yi birden fazla kez kullanma. - Seçerken adayın ilgi alanlarını ve illerini, kontenjan/yerleşen hareketini @@ -200,7 +212,8 @@ function secimiOnar( const gorulen = new Set(); if (parsed) { for (const d of DILIMLER) { - for (const t of parsed[TEL_ADI[d]]) { + for (const ham of parsed[TEL_ADI[d]]) { + const t = satirCoz(ham); const p = havuzMap.get(t.id); if (!p) { onarim.havuzDisi++; @@ -577,9 +590,11 @@ export async function raporUretAyrintili( : `${kullaniciMesaji}\n\nÖNCEKİ DENEMENDE HATA VARDI: ${sonHata} Kurallara tam uy.`, sema: AiRaporGevsek, wireSema: aiSemaWire(hedef), - // Küçük şema (24 satır × gerekce + genel + uyarılar) ~1-1.5k token; - // reasoning kapalıyken 4000 rahat bir tavan, kaçak uzun üretimi de keser. - maxTokens: 4000, + // Ölçüm (5 Eki 2026, 38 sağlıklı çağrı): çıktı 746–1.099 token. + // 2000 rahat bir tavan; reasoning'i kapatmayan üst sağlayıcıya düşen + // çağrı da ~50 sn yerine ~25 sn'de kesilir ve ikinci deneme o + // sağlayıcı atlanarak yapılır (bkz. cagri.ts). + maxTokens: 2000, gozlem: (g) => cagrilar.push(g), }); } catch (err) {