fix(danisman): 'Kaydettim' deyip kaydetmeme — sunucu tarafı denetim + prompt kuralı (iş 4; QA H4) [KARAR]
- sohbet-akisi.ts: cevapta kayıt beyanı (kaydettim / profiline ekledim / not aldım…) var ama o turda
başarılı profil_guncelle ('kaydedildi') yoksa cevabın sonuna düzeltme notu eklenir. Kayıt kararı
modelin sözüne bırakılmaz; gerçek araç sonucuyla karşılaştırılır. Hata dönen çağrı sayılmaz.
- Kredi: bu cevabın kredisi değişmez (iade yok, ek model çağrısı yok). Not 'profiline kaydedeyim mi'
ibaresini taşır; adayın 'evet'i mevcut bedelsiz onay yolundan geçer — sistemin hatasını düzeltmek
adaya kredi harcatmaz.
- Prompt: KAYIT DÜRÜSTLÜĞÜ kuralı (araç 'kaydedildi' dönmediyse 'kaydettim' deme).
- scripts/eval/akis-denetim.ts: sahte yerel model ucuyla parasız mekanik test (6 senaryo: aracsız
'kaydettim', araçlı, araç hatası, vaat regex notu, temiz, araç kotası) — 6/6 geçti.
Gerçek modelle bu yol henüz tetiklenmedi; model davranışı eval:sohbet ile ölçülür.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -75,3 +75,26 @@ export function vaatDiliBul(metin: string): string[] {
|
||||
}
|
||||
return bulunan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cevap "kaydettim" türü bir kayıt beyanı içeriyor mu. Kayıt yalnız
|
||||
* profil_guncelle aracıyla olur; model aracı çağırmadan "Kaydettim" diyebiliyor
|
||||
* (QA H4, 5 Eki 2026). Olumsuzlar ("kaydetmedim", "kaydedemedim") ve soru
|
||||
* ("kaydedeyim mi?") eşleşmez.
|
||||
*/
|
||||
const KAYIT_BEYANI =
|
||||
/(?<!\p{L})(?:kaydettim|kaydedildi|kaydını yaptım|profiline (?:ekledim|yazdım|işledim)|profilini güncelledim|profilin güncellendi|not (?:aldım|ettim))(?!\p{L})/u;
|
||||
|
||||
export function kayitBeyaniVar(metin: string): boolean {
|
||||
return KAYIT_BEYANI.test(tr(metin));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cevap kayıt bildiriyor ama o turda başarılı profil_guncelle yoksa cevabın
|
||||
* sonuna eklenir. "Profiline kaydedeyim mi" ibaresi bilerek var: adayın buna
|
||||
* vereceği "evet" bedelsiz onay yolundan geçer (sohbet-gecmisi.onayMesajiMi) —
|
||||
* sistemin hatasını düzeltmek adaya kredi harcatmaz. Metin, model "daha önce
|
||||
* kaydettim" dediğinde de doğrudur (bu cevapta yeni kayıt yok).
|
||||
*/
|
||||
export const KAYIT_DUZELTME_NOTU =
|
||||
"Düzeltme: Bu cevapta profiline yeni bir kayıt YAPILMADI; yukarıda \"kaydettim\" dediysem henüz kaydedemedim. Bunu profiline kaydedeyim mi? (Evet demek kredi harcamaz.)";
|
||||
|
||||
@@ -166,6 +166,12 @@ KURALLAR:
|
||||
Aday açıkça "kaydet" diye yazdıysa sormadan kaydet.`
|
||||
: `profil_guncelle ile kaydet ve cevabının sonunda tek cümleyle söyle:
|
||||
"Şunu kaydettim: …". Emin değilsen önce sor, tahminle kaydetme.`}
|
||||
KAYIT DÜRÜSTLÜĞÜ: kayıt YALNIZ profil_guncelle aracıyla olur. "Kaydettim",
|
||||
"profiline ekledim", "not aldım" gibi bir cümleyi ancak BU cevapta
|
||||
profil_guncelle'yi çağırdıysan ve sonuç "kaydedildi" döndüyse yaz. Aracı
|
||||
çağırmadıysan ya da hata döndüyse "kaydettim" DEME; "kaydedemedim" de ve
|
||||
nedenini söyle (ör. en az 50 km girilebilir, il tanınmadı). İki tercih
|
||||
birden söylendiyse her biri için ayrı profil_guncelle çağır.
|
||||
Bunların DIŞINDA hiçbir şeyi kaydetme ve sorma:
|
||||
sağlık/engel, din, aile durumu, gelir, cinsiyet, siyasi görüş. Aday
|
||||
bunları kendiliğinden yazsa bile profile YAZMA; yalnız cevabında dikkate al.
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
} from "ai";
|
||||
import { aracSeti, type AracBaglami } from "./araclar";
|
||||
import { sohbetDilModeli } from "./client";
|
||||
import { vaatDiliBul } from "./cikti-denetimi";
|
||||
import { KAYIT_DUZELTME_NOTU, kayitBeyaniVar, vaatDiliBul } from "./cikti-denetimi";
|
||||
import { cevapIhlalOlasiligi, JEV_DUZELTME_NOTU, JEV_IHLAL_ESIGI } from "./jev";
|
||||
|
||||
/** Bir cevapta en fazla model adımı (araç turları + son cevap). */
|
||||
@@ -49,6 +49,8 @@ export type CiktiDenetimi = {
|
||||
vaatNotu: boolean;
|
||||
/** Sınır aşıldığı için çalıştırılmayan araç çağrısı sayısı. */
|
||||
reddedilenArac: number;
|
||||
/** "Kaydettim" dendi ama başarılı profil_guncelle yoktu; düzeltme notu eklendi. */
|
||||
kayitNotu: boolean;
|
||||
};
|
||||
|
||||
export function danismanAkisi(opts: {
|
||||
@@ -76,6 +78,7 @@ export function danismanAkisi(opts: {
|
||||
let adimdakiArac = 0;
|
||||
let toplamArac = 0;
|
||||
let reddedilenArac = 0;
|
||||
let basariliKayit = 0;
|
||||
try {
|
||||
const result = streamText({
|
||||
model: sohbetDilModeli(),
|
||||
@@ -91,6 +94,12 @@ export function danismanAkisi(opts: {
|
||||
reddedilenArac++;
|
||||
return `Araç çağrısı sınırı aşıldı (bir adımda en fazla ${ADIM_BASINA_ARAC}, bir cevapta en fazla ${CEVAP_BASINA_ARAC}). Bu çağrı ÇALIŞTIRILMADI. Yeni araç çağırma; elindeki sonuçlarla cevap ver, eksik kalan kısmı adaya açıkça söyle ve sorusunu daraltmasını iste. Çalışmayan çağrı için sayı uydurma.`;
|
||||
},
|
||||
(ad, cikti) => {
|
||||
// Başarılı kayıt = profil_guncelle "kaydedildi" döndü (hata dönerse sayılmaz)
|
||||
if (ad === "profil_guncelle" && cikti != null && typeof cikti === "object" && "kaydedildi" in cikti) {
|
||||
basariliKayit++;
|
||||
}
|
||||
},
|
||||
),
|
||||
// En fazla ARAC_TUR_SINIRI adım; son adımda araç kapalı (cevap
|
||||
// vermek zorunda).
|
||||
@@ -152,8 +161,22 @@ export function danismanAkisi(opts: {
|
||||
writer.write({ type: "text-delta", id, delta: JEV_DUZELTME_NOTU });
|
||||
writer.write({ type: "text-end", id });
|
||||
}
|
||||
// Kayıt beyanı denetimi (QA H4): cevap "kaydettim" diyor ama bu turda
|
||||
// başarılı profil_guncelle yok → düzeltme notu. Kayıt kararı modelin
|
||||
// sözüne bırakılmaz; gerçek araç sonucuyla karşılaştırılır. Kredi:
|
||||
// bu cevap için değişmez; notun sorduğu "kaydedeyim mi?"ye verilen
|
||||
// "evet" bedelsizdir.
|
||||
const kayitNotu = basariliKayit === 0 && kayitBeyaniVar(metin);
|
||||
if (kayitNotu) {
|
||||
console.info("[soru] kayıt beyanı var, başarılı profil_guncelle yok → not eklendi");
|
||||
const id = crypto.randomUUID();
|
||||
metin += `\n\n${KAYIT_DUZELTME_NOTU}`;
|
||||
writer.write({ type: "text-start", id });
|
||||
writer.write({ type: "text-delta", id, delta: KAYIT_DUZELTME_NOTU });
|
||||
writer.write({ type: "text-end", id });
|
||||
}
|
||||
if (reddedilenArac) console.info("[soru] araç sınırı:", reddedilenArac, "çağrı çalıştırılmadı");
|
||||
opts.onDenetim?.({ jevP, vaatRegex, vaatNotu, reddedilenArac });
|
||||
opts.onDenetim?.({ jevP, vaatRegex, vaatNotu, reddedilenArac, kayitNotu });
|
||||
await opts.onTamam(metin);
|
||||
tamamlandi = true;
|
||||
writer.write({ type: "finish" });
|
||||
|
||||
Reference in New Issue
Block a user