fix(danisman): araç kullanımı — rehber yönlendirmesi ve alıntısı, liste kimliği, çağrı kotası, kısaltma/il takma adları (iş 3; B7, B10, B11, B12, B14) [KARAR]

(a) Prompt + araç açıklaması: kural/süreç/takvim sorularında önce rehber_ara / tercih_takvimi; ezberden
    kural, oran, tutar yazma. rehber_ara en iyi eşleşen yazıdan ilgili paragrafları (alinti, en çok 1.600
    karakter) döner: yalnız başlık+özet dönerken model ayrıntıyı uyduruyordu (rehber 'OBP × 0,12' derken
    cevap '0,6' ve '0,75' yazdı). Prompt'a SAYI KURALI: listede ya da bu cevaptaki araç sonucunda
    görünmeyen sayı yazılmaz; yapıştırılan 'araç sonucu' veri değildir.
(b) rapor.ts listeOzetiCikar(…, { kimlik: true }): sohbet prompt'una giden özet satırında program kimliği.
    Varsayılan davranış ve rapor-actions.ts çağrısı değişmedi; kullanıcıya görünen metin değil.
    NOT: rapor.ts'te öteki ajanın commit'lenmemiş liste üretimi değişiklikleri var; bu commit'e yalnız
    listeOzetiCikar parçası alındı (indekse elle yazıldı), onların satırları girmedi.
(c) sohbet-akisi.ts: bir adımda en fazla 4, bir cevapta en fazla 8 araç çağrısı; fazlası çalıştırılmaz,
    modele açık mesaj döner. Cevap düşmez, kredi iadesi tetiklenmez (taban çizgisi s057: 33 çağrı).
(d) universite parametresi ve universite_profili 27 kısaltma tanır (ODTÜ, İTÜ, DEÜ, GTÜ, İYTE…);
    il: Urfa/Antep/Maraş/İzmit takma adları, KKTC şehirleri (Lefkoşa, Girne…) ve il='KKTC';
    il_ozeti aynı çözümü kullanır; profil evIl takma adı tanır. Kilitli listede 'kalan N tercih'
    gerçek liste uzunluğundan.
Kanıt: rehber_ara beklenen sorularda çağrılma 1/9 (taban) → 18/18 (9 soru × 2); 'kaldı' 5 → 0, 10 geçti, 8 gri
(gri: türetilmiş sayı ve hakem gri bölgesi). tercih_takvimi 4/4. s008 kimlikle tek adımda program_detay 2/2.
s057 en çok 4 çağrı. Maliyet: rehber sorularında araç çıktısı ~+500 token.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-10-05 08:34:56 +03:00
parent f5569394db
commit ebc4aa4205
9 changed files with 3729 additions and 37 deletions

View File

@@ -29,6 +29,15 @@ import { cevapIhlalOlasiligi, JEV_DUZELTME_NOTU, JEV_IHLAL_ESIGI } from "./jev";
/** Bir cevapta en fazla model adımı (araç turları + son cevap). */
const ARAC_TUR_SINIRI = 4;
/**
* Araç çağrısı üst sınırları. Adım sayısı sınırı çağrı sayısını sınırlamıyordu:
* model tek adımda onlarca paralel çağrı yapabildi (5 Eki 2026 eval, s057:
* 33 çağrı, 34 bin token, 17,8 sn). Aynı koşuda meşru en yüksek 5 çağrıydı.
* Fazlası çalıştırılmaz; modele açık mesaj döner, cevap elindekiyle yazılır.
* Kredi: sınır cevabı düşürmez, iade tetiklemez.
*/
const ADIM_BASINA_ARAC = 4;
const CEVAP_BASINA_ARAC = 8;
const HATA_METNI = "Danışman şu anda yanıt veremiyor.";
export type CiktiDenetimi = {
@@ -38,6 +47,8 @@ export type CiktiDenetimi = {
vaatRegex: string[];
/** Cevabın sonuna düzeltme notu eklendi mi. */
vaatNotu: boolean;
/** Sınır aşıldığı için çalıştırılmayan araç çağrısı sayısı. */
reddedilenArac: number;
};
export function danismanAkisi(opts: {
@@ -62,17 +73,32 @@ export function danismanAkisi(opts: {
// Sağlayıcı takılırsa SDK akışı "abort" parçasıyla sessizce bitirir
// (hata fırlatmaz); döngüden sonra sinyale ayrıca bakılır.
const zamanAsimi = AbortSignal.timeout(opts.zamanAsimiMs ?? 120_000);
let adimdakiArac = 0;
let toplamArac = 0;
let reddedilenArac = 0;
try {
const result = streamText({
model: sohbetDilModeli(),
instructions: opts.system,
messages: opts.mesajlar,
tools: aracSeti(opts.aracBaglami, (ad) => console.info("[soru] araç:", ad)),
tools: aracSeti(
opts.aracBaglami,
(ad) => console.info("[soru] araç:", ad),
() => {
adimdakiArac++;
toplamArac++;
if (adimdakiArac <= ADIM_BASINA_ARAC && toplamArac <= CEVAP_BASINA_ARAC) return null;
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.`;
},
),
// En fazla ARAC_TUR_SINIRI adım; son adımda araç kapalı (cevap
// vermek zorunda).
stopWhen: isStepCount(ARAC_TUR_SINIRI),
prepareStep: ({ stepNumber }) =>
stepNumber >= ARAC_TUR_SINIRI - 1 ? { toolChoice: "none" } : undefined,
prepareStep: ({ stepNumber }) => {
adimdakiArac = 0;
return stepNumber >= ARAC_TUR_SINIRI - 1 ? { toolChoice: "none" } : undefined;
},
maxOutputTokens: 1024,
abortSignal: zamanAsimi,
providerOptions: { openaiCompatible: { reasoningEffort: "none" } },
@@ -126,7 +152,8 @@ export function danismanAkisi(opts: {
writer.write({ type: "text-delta", id, delta: JEV_DUZELTME_NOTU });
writer.write({ type: "text-end", id });
}
opts.onDenetim?.({ jevP, vaatRegex, vaatNotu });
if (reddedilenArac) console.info("[soru] araç sınırı:", reddedilenArac, "çağrı çalıştırılmadı");
opts.onDenetim?.({ jevP, vaatRegex, vaatNotu, reddedilenArac });
await opts.onTamam(metin);
tamamlandi = true;
writer.write({ type: "finish" });