fix(danisman): revizyon aracı atlanmasın — geçmiş araç kaydına indirgenir, beyan regex'i genişledi, soru formu Türkçe karakter (QA B1, B4)

Kök neden (B1): chat_messages yalnız metin tutar; başarılı revizyonun kaydı
("revize ediyorum… Liste yenilendi… 1-5") araçsız cevapla aynı görünüyordu ve
model sonraki turda bu kalıbı taklit edip liste_revize çağırmadan "yenilendi"
yazıyor, program adlarını eski cevaptan kopyalıyordu (eval: 8 revizyon
isteğinde 1 çağrı, 5 sahte beyan). Metnin altına "[araç çağrıldı]" işareti
eklemek işe yaramadı — model işareti de kopyaladı.

- sohbet-gecmisi.kayitliMesajModelMetni: modele giden pencerede başarılı
  revizyon turu kısa araç kaydına iner (meta.revizyon'dan), araçsız
  "yenilendi" diyen tur (düzeltme notu olmayan eski kayıtlar dâhil) uydurma
  listesiyle birlikte düşer; kayıt ve ekran değişmez. route.ts bunu kullanır.
- revizyonNiyetiVar + sohbet-akisi.ilkAdimAracZorunlu: niyet algılanınca ilk
  adımda tool_choice "required". VARSAYILAN KAPALI
  (SOHBET_REVIZYON_ARAC_ZORUNLU=1): ölçümde 7/8 çağrı, 0 sahte beyan verdi ama
  OpenRouter isteği Baidu yerine OpenInference'a yönlendirdi, gecikme 1-7 sn →
  80-112 sn, onlarca paralel araç çağrısı. Açmak Bilal'in kararı.
- danisman-prompt revizyon bölümü: 5 (geçmiş örnek değildir; önce aracı çağır)
  ve 6 (soru formunda Türkçe karakter).
- cikti-denetimi REVIZE_BEYANI: "değişiklikler yapıldı", "artık … tercihin …
  oluşuyor", "listeyi yeniden kurdum", "liste_revize çağrıldı" kalıpları.
- araclar.ts (B4): soru_sor açıklamasına Türkçe karakter kuralı; formdaki
  ASCII'ye düşmüş il adları koddan düzelir (ilAdlariniDuzelt: Istanbul →
  İstanbul, Eskisehir → Eskişehir; yalnız büyük harfle başlayan tam eşleşme).
- scripts/eval/revizyon-arac.ts (pnpm eval:revizyon): gerçek modelle 10
  senaryo, liste_revize çağrı oranı + sahte beyan; liste_revize çalıştırılmaz
  (mesajId yok). Sonuç dosyaları scripts/eval/sonuc/*-revizyon.json.

Kredi: değişmez. akis-denetim 9/9, revizyon-denetim 5/5, tsc ve lint temiz.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-10-09 00:16:35 +03:00
parent f955a76c1e
commit 128c175327
13 changed files with 1067 additions and 16 deletions

View File

@@ -14,11 +14,10 @@ import { kullanicininRaporu, sohbetGecmisiGetir } from "@/features/rapor/rapor-q
import { barajTablosu, type AracBaglami } from "@/lib/ai/araclar";
import { danismanAkisi } from "@/lib/ai/sohbet-akisi";
import { sistemPromptu, sohbetListeOzeti } from "@/lib/ai/danisman-prompt";
import { modelPenceresi, onayMesajiMi } from "@/lib/ai/sohbet-gecmisi";
import { kayitliMesajModelMetni, modelPenceresi, onayMesajiMi, revizyonNiyetiVar } from "@/lib/ai/sohbet-gecmisi";
import {
anketCevabiDogrula,
anketCevabiMetni,
anketModelMetni,
type RevizyonOzeti,
type SohbetMeta,
} from "@/lib/ai/sohbet-meta";
@@ -268,14 +267,7 @@ export async function POST(request: NextRequest) {
// Cevapsız kalmış (iade edilmiş) sorular ve danışmanla başlayan pencere
// modele bozuk sıra göndermesin. Kayıtlı asistan metni soru formunu
// içermez; model cevapların neye verildiğini formun dökümünden görür.
const mesajlar = modelPenceresi(
gecmis.map((m) =>
m.role === "assistant" && m.meta?.anket
? { role: m.role, content: `${m.content}\n\n${anketModelMetni(m.meta.anket)}` }
: { role: m.role, content: m.content },
),
mesaj,
);
const mesajlar = modelPenceresi(gecmis.map(kayitliMesajModelMetni), mesaj);
const userId = session.user.id;
// Bu turda araçların bıraktığı yapısal ek (soru formu, revizyon sonucu)
@@ -299,6 +291,13 @@ export async function POST(request: NextRequest) {
// Cevaptaki /rehber/<slug> adresleri gerçek yazı listesiyle doğrulanır
// (prod'da cache'li, lib/rehber.ts)
rehberler: getRehberAramaKayitlari(),
// Liste değişikliği isteğinde ilk adım araçsız bitemez (QA B1). VARSAYILAN
// KAPALI: tool_choice "required" OpenRouter'da isteği Baidu yerine
// OpenInference'a yönlendirdi; cevap 80-112 sn sürdü ve onlarca paralel
// araç çağrısı geldi (8 Eki 2026 eval, scripts/eval/revizyon-arac.ts).
// Açmak Bilal'in kararı (SOHBET_REVIZYON_ARAC_ZORUNLU=1); paketsizde hiç
// zorlanmaz — liste_revize zaten paket ister, model sözle yönlendirir.
ilkAdimAracZorunlu: process.env.SOHBET_REVIZYON_ARAC_ZORUNLU === "1" && paketli && revizyonNiyetiVar(mesaj),
onAracSonucu: (ad, cikti) => {
const c = cikti as Record<string, unknown>;
if (ad === "soru_sor" && c.anket) cevapMeta.anket = c.anket as SohbetMeta["anket"];