feat(sohbet): liste revizyonu sohbete taşındı — soru formu ve yapısal kural
Eski revizyon kutusu kaldırıldı: serbest metin modele gidiyor, sıra ve havuz koddan belirlendiği için "ilk 5 X ilinden olsun" gibi isteklerin karşılığı yoktu. Danışman artık belirsiz isteği soru formuyla (soru_sor → shadcn Questionnaire, proje diline çevrildi) netleştirir, sonra liste_revize ile yapısal kural verir: il/alan/tip farkı, "ilk N" ölçütü, çıkar/ekle, iskelet (lib/revizyon-kurali.ts). Kurallar havuz, seçim ve sırada koddan uygulanır; "ilk N" ölçütünün ili/alanı süzgece kendiliğinden eklenir. - chat_messages.meta sütunu (göç 0006): form, form cevabı ve revizyon sonucu kayıttan geri kurulur; form cevabı kredi harcamaz. - Akış korumaları: form açılınca başka araç yok, revizyonda zaman aşımı uzar, "yeniden kurdum" beyanı araçsızsa düzeltme notu. - /listem "Revize et" sohbeti odaklar; başarılı revizyonda liste yenilenir. - /dev/sohbet senaryosu, DESIGN.md §4.20 notu ve §4.33. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -177,6 +177,13 @@ function dilimBasinaKirp(havuz: AdayProgram[], n: number): AdayProgram[] {
|
||||
);
|
||||
}
|
||||
|
||||
/** Revizyonun "ekle" kimlikleri: dilim evreninde bulunanlar, katman 0 ile. */
|
||||
function ekstraSec(evren: AdayProgram[], idler?: readonly string[]): AdayProgram[] {
|
||||
if (!idler?.length) return [];
|
||||
const iste = new Set(idler);
|
||||
return evren.filter((p) => iste.has(p.id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Katmanları öncelik sırasıyla, tekrarsız birleştirir. Gevşetmede kullanılır:
|
||||
* gevşeyen filtreye UYAN programlar önce gelir ve kırpmada korunur, kalan
|
||||
@@ -271,9 +278,24 @@ function dilimEkle(r: {
|
||||
* Adayın yazamayacağı programlar hiçbir basamakta havuza girmez: başarı
|
||||
* sırası barajının gerisinde kaldığı bölümler ve özel kontenjan satırları.
|
||||
*/
|
||||
/**
|
||||
* Sohbet revizyonunun havuza dokunan kısmı (lib/ai/rapor.ts kurar):
|
||||
* `cikar` eşleşenler hiçbir katmana girmez; `oncelikli` eşleşenler kırpmadan
|
||||
* önce dilim içinde öne alınır ("ilk 5 İstanbul olsun" için İstanbul havuzda
|
||||
* kalsın); `ekstraIdler` adayın açıkça istediği programlar — sihirbaz
|
||||
* süzgecine takılsa bile (baraj ve özel kontenjan kuralı yine geçer) havuza
|
||||
* kendi dilimiyle eklenir.
|
||||
*/
|
||||
export type HavuzSecenekleri = {
|
||||
cikar?: (p: AdayProgram) => boolean;
|
||||
oncelikli?: (p: AdayProgram) => boolean;
|
||||
ekstraIdler?: readonly string[];
|
||||
};
|
||||
|
||||
export function havuzOlustur(
|
||||
params: RaporParams,
|
||||
profil?: DanismanProfili,
|
||||
secenekler: HavuzSecenekleri = {},
|
||||
): HavuzSonuc {
|
||||
const uniturGrubu =
|
||||
params.universiteTipi && params.universiteTipi !== "farketmez"
|
||||
@@ -307,7 +329,39 @@ export function havuzOlustur(
|
||||
const adaylar = (ozellik?: OzellikSuzgeci) =>
|
||||
dilimEkle(
|
||||
havuzAdaylari(params.sira, params.tur, { uniturGrubu, ozellik }),
|
||||
).filter((p) => !ozelKontenjanSatiriMi(p.isim));
|
||||
).filter(
|
||||
(p) => !ozelKontenjanSatiriMi(p.isim) && !(secenekler.cikar?.(p) ?? false),
|
||||
);
|
||||
|
||||
// 1. Tam filtre: üniversite tipi (SQL), iller + kategori (JS). Süzgeçsiz
|
||||
// dilim evreni (ilsizHam) revizyonun "ekle" kimliklerinin de kaynağıdır.
|
||||
const ilsizHam = adaylar();
|
||||
const ekstra = ekstraSec(ilsizHam, secenekler.ekstraIdler);
|
||||
// Revizyon önceliği gevşetme katmanından sonra, uyum puanından önce gelir:
|
||||
// kararlı sıralama, eşleşenler dilim içinde öne (kırpma öndekileri tutar).
|
||||
const oncelikle = (havuz: AdayProgram[]) =>
|
||||
secenekler.oncelikli
|
||||
? [...havuz].sort(
|
||||
(a, b) =>
|
||||
(a.katman ?? 0) - (b.katman ?? 0) ||
|
||||
Number(secenekler.oncelikli!(b)) - Number(secenekler.oncelikli!(a)),
|
||||
)
|
||||
: havuz;
|
||||
// Ekstralar katman 0 ile başa; ötekilerin gevşetme katmanı korunur
|
||||
const ekstraIdKumesi = new Set(ekstra.map((p) => p.id));
|
||||
const kirpVe = (havuz: AdayProgram[]) =>
|
||||
dilimBasinaKirp(
|
||||
oncelikle(
|
||||
uyumlaSirala(
|
||||
[
|
||||
...ekstra.map((p) => ({ ...p, katman: 0 })),
|
||||
...havuz.filter((p) => !ekstraIdKumesi.has(p.id)),
|
||||
],
|
||||
baglam,
|
||||
),
|
||||
),
|
||||
PROMPT_LIMIT_DILIM,
|
||||
);
|
||||
|
||||
const profilGevsetildi: Gevsetilen[] = [];
|
||||
// Profil gevşerse profile uyan (24'ten az) programlar yine en önde tutulur
|
||||
@@ -320,22 +374,17 @@ export function havuzOlustur(
|
||||
profilliFiltreli = kategoriFiltre(profilliHam);
|
||||
if (profilliFiltreli.length >= HEDEF) {
|
||||
return {
|
||||
havuz: dilimBasinaKirp(uyumlaSirala(profilliFiltreli, baglam), PROMPT_LIMIT_DILIM),
|
||||
havuz: kirpVe(profilliFiltreli),
|
||||
gevsetildi: [],
|
||||
};
|
||||
}
|
||||
profilGevsetildi.push("profil");
|
||||
}
|
||||
|
||||
// 1. Tam filtre: üniversite tipi (SQL), iller + kategori (JS)
|
||||
const ilsizHam = adaylar();
|
||||
const tamHam = ilFiltre(ilsizHam, iller);
|
||||
const tamFiltreli = kategoriFiltre(tamHam);
|
||||
const kirp = (...katmanlar: AdayProgram[][]) =>
|
||||
dilimBasinaKirp(
|
||||
uyumlaSirala(oncelikliBirlestir(profilliFiltreli, ...katmanlar), baglam),
|
||||
PROMPT_LIMIT_DILIM,
|
||||
);
|
||||
kirpVe(oncelikliBirlestir(profilliFiltreli, ...katmanlar));
|
||||
if (tamFiltreli.length >= HEDEF) {
|
||||
return { havuz: kirp(tamFiltreli), gevsetildi: profilGevsetildi };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user