fix(danisman): liste revizyonu koda bağlandı — model artık revizyon başlatamaz (QA B1)
Revizyon sohbet modelinin gönüllü liste_revize araç seçimine bağlıydı (8 Eki eval: 8 istekte 5 çağrı, 3 sahte beyan; tool_choice required OpenRouter'da 80-112 sn). Şimdi: - Niyet kodda: revizyonNiyetiVar (regex) + paketli → lib/ai/revizyon-niyeti.ts yapılandırılmış çıkarım (yapilandirilmisUret, json_schema; eski liste_revize parametre şeması ve revizyonKuraliKur aynen yeniden kullanılır, kopya yok). Model niyetModeli() (OPENROUTER_NIYET_MODEL, yoksa rapor modeli), 20 sn, OpenInference bu çağrıda atlanır (11-12,5 sn ölçüldü). - Dallanma kodda: kural geçerli → listeRevizeKoddan (paket/kilit/kredi kapıları aynı); kural eksik → soru formu koddan (modelin formu kod doğrular, yoksa varsayılan form); niyet soru/diger → normal sohbet; çıkarım başarısız → koddan "okuyamadım" cevabı + kredi iadesi (normal sohbete düşünce model uydurma adlarla "kurdum" yazıyordu). - Cevap da kodda: revizyon/form turunda model hiç çağrılmaz; sayı ve program adları sunucu sonucundan şablonla (sabitCevap). Model sonuç önündeyken dağılımı 8'de 2 yanlış yazdı. Ekrana eski araç parçaları (tool-liste_revize / tool-soru_sor) aynen gider; istemci ve kayıt değişmez. Başarısız revizyon artık ekranda "kuramadım" hapı (output-error). - liste_revize modelin araç listesinden çıktı; prompt revizyon bölümü "kurdum deme, net istek iste ya da soru_sor" diye sadeleşti. - Güvenlik ağı: beyan regex'i kaldı; beyan var + revizyon yok → not + mesajın sohbet kredisi iade (grantCredits refund, refId=mesajId, idempotent), not bunu söyler. liste-revizyon iade refId'si "<mesajId>:revizyon" oldu: (reason, refId) tekilliği sohbet iadesiyle çakışıyordu. - Geçmiş penceresi: "[Araç kaydı: …]" etiketi modelce kopyalanıyordu; yerine doğal tek cümle. - Kaldırıldı: ilkAdimAracZorunlu / SOHBET_REVIZYON_ARAC_ZORUNLU. Ölçüm (deepseek-v4-flash @ openrouter, pnpm eval:revizyon): koddan revizyon 8/8, kural doğru 8/8, sahte beyan 0, 10/10; revizyon turu 1 küçük çağrı (ort. 1-3 sn, 0,00005 $) + liste üretimi; sohbet çağrısı yok. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -11,10 +11,12 @@ import {
|
||||
import { createUIMessageStream, createUIMessageStreamResponse } from "ai";
|
||||
import { anahtarVar, SORUMLULUK_REDDI } from "@/lib/ai/client";
|
||||
import { kullanicininRaporu, sohbetGecmisiGetir } from "@/features/rapor/rapor-queries";
|
||||
import { barajTablosu, type AracBaglami } from "@/lib/ai/araclar";
|
||||
import { danismanAkisi } from "@/lib/ai/sohbet-akisi";
|
||||
import { barajTablosu, listeRevizeKoddan, soruFormuSonucu, type AracBaglami } from "@/lib/ai/araclar";
|
||||
import { danismanAkisi, type KoddanIslem } from "@/lib/ai/sohbet-akisi";
|
||||
import { sistemPromptu, sohbetListeOzeti } from "@/lib/ai/danisman-prompt";
|
||||
import { kayitliMesajModelMetni, modelPenceresi, onayMesajiMi, revizyonNiyetiVar } from "@/lib/ai/sohbet-gecmisi";
|
||||
import { revizyonNiyetiCikar } from "@/lib/ai/revizyon-niyeti";
|
||||
import { kuralOzeti } from "@/lib/revizyon-kurali";
|
||||
import {
|
||||
anketCevabiDogrula,
|
||||
anketCevabiMetni,
|
||||
@@ -282,22 +284,55 @@ export async function POST(request: NextRequest) {
|
||||
createdAt: new Date(),
|
||||
});
|
||||
|
||||
// Liste revizyonu KODA BAĞLI (QA B1, 9 Eki 2026): adayın mesajı liste
|
||||
// değişikliği istiyorsa (regex) ve aday paketliyse niyet ve kural küçük bir
|
||||
// yapılandırılmış çağrıyla çıkarılır (lib/ai/revizyon-niyeti.ts); işlem ve
|
||||
// cevap koddan, sohbet modeli o turda çağrılmaz. Paketsizde çağrı
|
||||
// yok: liste değiştirilemez, model /paket'e yönlendirir. Çıkarım adayın
|
||||
// kredisine dokunmaz; başarısızsa normal sohbet (model listeyi zaten
|
||||
// değiştiremez; "kurdum" derse güvenlik ağı not ekler ve kredi iade edilir).
|
||||
let islem: KoddanIslem | undefined;
|
||||
if (paketli && revizyonNiyetiVar(mesaj)) {
|
||||
const t0 = Date.now();
|
||||
const niyet = await revizyonNiyetiCikar({ mesaj, params: raporParams, oncekiForm: anketCevabiHam ? sonAnket : null });
|
||||
console.info("[soru] revizyon niyeti:", niyet.niyet, "kural" in niyet ? "kural" : "form" in niyet ? `form (eksik: ${niyet.eksik.join(", ") || "—"})` : "neden" in niyet ? niyet.neden : "", `${Date.now() - t0} ms`);
|
||||
if (niyet.niyet === "revizyon" && "kural" in niyet) {
|
||||
const kural = niyet.kural;
|
||||
islem = {
|
||||
ad: "liste_revize",
|
||||
girdi: kural,
|
||||
calistir: () => listeRevizeKoddan(aracBaglami, kural),
|
||||
anlatim: { ozet: kuralOzeti(kural), ilkN: Boolean(kural.ilkTercihler) },
|
||||
};
|
||||
} else if (niyet.niyet === "revizyon") {
|
||||
const sonuc = soruFormuSonucu(niyet.form);
|
||||
islem = { ad: "soru_sor", girdi: {}, calistir: async () => sonuc };
|
||||
} else if (niyet.niyet === "belirsiz") {
|
||||
// Çıkarım yapılamadı (zaman aşımı / sağlayıcı hatası). Normal sohbete
|
||||
// düşülmez: 9 Eki eval r04'te model bu durumda uydurma adlarla
|
||||
// "yeniden kurdum" yazdı (güvenlik ağı yakaladı). Cevap koddan, kredi
|
||||
// iade (bedelsiz mesajda zaten düşmedi); aday tekrar yazar.
|
||||
const iade = bedelsiz
|
||||
? false
|
||||
: (await grantCredits({ userId: session.user.id, delta: 1, reason: "refund", refId: kullaniciMesajId }).catch(() => ({ granted: false }))).granted;
|
||||
islem = {
|
||||
ad: "liste_revize",
|
||||
girdi: {},
|
||||
calistir: async () => ({ hata: `isteğini şu an okuyamadım (geçici bir sorun). Birazdan aynı isteği tekrar yazar mısın?${iade ? " Bu mesajın kredisi iade edildi." : ""}` }),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Cevap AI SDK'nın UI mesaj akışıyla gider (lib/ai/sohbet-akisi.ts;
|
||||
// istemci: useChat). Kayıt ve kredi iadesi burada kalır.
|
||||
const stream = danismanAkisi({
|
||||
system,
|
||||
mesajlar,
|
||||
aracBaglami,
|
||||
islem,
|
||||
// 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"];
|
||||
@@ -307,6 +342,15 @@ export async function POST(request: NextRequest) {
|
||||
cevapMeta.revizyon = ozet as RevizyonOzeti;
|
||||
}
|
||||
},
|
||||
// Güvenlik ağı: model "yeniden kurdum" dedi ama bu turda revizyon yok →
|
||||
// mesajın sohbet kredisi iade edilir (güvenlik-uyum önerisi, 7 Eki 2026).
|
||||
// Aynı refId ile idempotent: yarım kalma iadesiyle çakışmaz (ikisi aynı
|
||||
// turda olamaz), ağ tekrarında ikinci kez düşmez. Bedelsiz mesajda yok.
|
||||
onRevizeBeyani: async () => {
|
||||
if (bedelsiz) return false;
|
||||
const r = await grantCredits({ userId, delta: 1, reason: "refund", refId: kullaniciMesajId });
|
||||
return r.granted;
|
||||
},
|
||||
onTamam: async (metin) => {
|
||||
await cevabiKaydet(metin);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user