feat: narrative "Dosya Özeti" report instead of field-by-field JSON

summarizeCase now produces a full Markdown narrative report (Olayın
Özeti, Yargılama Süreci ve Kararlar, Taraf İddia/Savunmaları, Önemli
Bulgular ve Belgeler, Sonuç) instead of short structured fields - the
iddianame pre-analysis (when found) still runs first and is fed in as
context. The raw AI text is stored as {ozet: string} rather than
JSON-parsed, since asking the model to escape a multi-paragraph report
inside JSON was fragile.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
mstfyldz
2026-08-09 20:09:31 +03:00
co-authored by Claude Sonnet 5
parent c939dcf564
commit 50f6cbb1b2
2 changed files with 60 additions and 28 deletions
+47 -11
View File
@@ -31,7 +31,44 @@ const IDDIANAME_SYSTEM_PROMPT = `
Sadece ve sadece JSON döndür, başka hiçbir açıklama ekleme.
`;
// Dosya Ayrıntı ekranındaki "Dosya Özeti" kartı için — kısa alan-alan JSON yerine
// verilen örnekteki gibi kapsamlı, bölümlere ayrılmış bir anlatı raporu istiyoruz.
// Serbest metin JSON içine gömülüp modelin onu doğru kaçışlaması (escape) riskli
// olduğundan, bunun çıktısını JSON olarak PARSE ETMİYORUZ — ham metni alıp
// { ozet: <metin> } olarak biz sarıyoruz (bkz. callAiModelRaw / runCaseNarrativeAnalysis).
const CASE_NARRATIVE_SYSTEM_PROMPT = `
Sen uzman bir Türk avukatısın. Sana bir dava dosyasındaki TÜM belgelerin metni
verilecek (varsa önce bir iddianame ön analizi de eklenmiş olabilir, buna öncelik ver).
Bu dosya hakkında kapsamlı, iyi yapılandırılmış bir "Dosya Özeti" raporu yaz.
Rapor Markdown formatında olsun ve şu yapıyı izlesin:
- Kısa bir giriş paragrafı: mahkeme/daire, esas no'su ve konusu.
- "1. Olayın/Uyuşmazlığın Özeti" — ne olduğunu anlat.
- "2. Yargılama Süreci ve Kararlar" — ilk derece, istinaf/temyiz, bozma vb. aşamaları madde madde anlat.
- "3. Tarafların İddia ve Savunmaları" — davacı/davalı ya da sanık/müştekinin ne dediğini özetle.
- "4. Önemli Bulgular ve Belgeler" — bilirkişi raporu, banka kayıtları, HTS/baz istasyonu, tanık ifadeleri gibi somut delilleri madde madde listele.
- Kısa bir "Sonuç" paragrafı: dosyanın şu anki durumu ve beklenen sonraki adım.
Dava türüne göre terminolojiyi uyarla (ceza davasında "sanık/müşteki", hukuk davasında
"davacı/davalı", icra dosyasında "alacaklı/borçlu" gibi). SADECE dosyada gerçekten
geçen bilgileri kullan, uydurma bilgi ekleme; bir bölüm için yeterli bilgi yoksa o
bölümü kısa geç veya atla. SADECE Markdown metni döndür — JSON, kod bloğu işareti
veya başka bir açıklama ekleme.
`;
async function callAiModel(systemPrompt: string, userText: string): Promise<{ resultJson: any; aiModel: string }> {
const { aiContent, aiModel } = await callAiModelRaw(systemPrompt, userText);
let resultJson: any = {};
try {
resultJson = JSON.parse(aiContent || '{}');
} catch (e) {
console.error('Failed to parse AI response as JSON:', aiContent);
resultJson = { error: 'AI did not return valid JSON', raw: aiContent };
}
return { resultJson, aiModel };
}
async function callAiModelRaw(systemPrompt: string, userText: string): Promise<{ aiContent: string; aiModel: string }> {
const { runpodUrl, aiModel } = getAiConfig();
const response = await fetch(`${runpodUrl}/v1/chat/completions`, {
@@ -53,19 +90,11 @@ async function callAiModel(systemPrompt: string, userText: string): Promise<{ re
}
const data = await response.json();
const aiContent = data.message?.content || (data.choices && data.choices[0]?.message?.content);
let resultJson: any = {};
try {
resultJson = JSON.parse(aiContent || '{}');
} catch (e) {
console.error('Failed to parse AI response as JSON:', aiContent);
resultJson = { error: 'AI did not return valid JSON', raw: aiContent };
}
return { resultJson, aiModel };
const aiContent = data.message?.content || (data.choices && data.choices[0]?.message?.content) || '';
return { aiContent: String(aiContent).trim(), aiModel };
}
// uploadDocument, registerLocalDocument ve summarizeCase'in (case.controller.ts)
// ortak kullandığı genel analiz adımı.
// uploadDocument, registerLocalDocument'in kullandığı genel (kısa, alan-alan) analiz adımı.
export async function runDocumentAnalysis(extractedText: string): Promise<{ summaryJson: any; aiModel: string }> {
const { resultJson, aiModel } = await callAiModel(ANALYSIS_SYSTEM_PROMPT, extractedText);
return { summaryJson: resultJson, aiModel };
@@ -77,6 +106,13 @@ export async function runIddianameAnalysis(extractedText: string): Promise<{ sum
return { summaryJson: resultJson, aiModel };
}
// summarizeCase'in Dosya Özeti raporu için kullandığı adım — çıktı serbest Markdown
// metni, JSON'a çevrilmeye çalışılmıyor.
export async function runCaseNarrativeAnalysis(combinedText: string): Promise<{ summaryJson: any; aiModel: string }> {
const { aiContent, aiModel } = await callAiModelRaw(CASE_NARRATIVE_SYSTEM_PROMPT, combinedText);
return { summaryJson: { ozet: aiContent }, aiModel };
}
// UYAP Evrak İndirici eklentisinden gelen, Electron ana sürecinde YERELDE metni
// çıkarılmış belgeleri kaydeder. Ham dosya baytları bu isteğe hiç dahil değil —
// sadece çıkarılan metin (JSON) geliyor, Supabase Storage'a hiçbir yükleme yapılmıyor.