feat: Dava Özeti, Çelişki Avcısı ve Dilekçe Taslağı'na mevzuat entegrasyonu
mevzuat-etl'in doldurduğu Supabase mevzuat/mevzuat_madde tablolarından (aynı proje) ilgili kanun maddelerini bulup analiz/taslak üretiminden önce prompt'a ekliyor. Bulma: önce dava metnindeki açık atıfları regex ile yakala (TCK m.141, 5237 sayılı Kanun'un 141. maddesi gibi, tur-agnostik), boşsa precedent.controller.ts'deki AI-keyword-sıkıştırma + tam-metin arama desenine düşer. Hiçbir zaman exception fırlatmaz, mevzuat bulunamazsa analiz normal devam eder. Dilekçe Taslağı (generateDraft) farklı ele alınıyor — çıktısı JSON değil düz metin ve aynen dilekçe belgesi olarak kullanılıyor, bu yüzden referans bloğu ayrıca işaretlenip sistem promptuna "kopyalama, sadece atıf yap" talimatı eklendi. generateAngarya (Süre Tutum/Mazeret/Vekaletname) bilerek dışarıda bırakıldı — hukuki tartışma içermeyen idari dilekçeler. Tasarım: mevzuat-etl/docs/superpowers/specs/2026-08-17-laawos-mevzuat-entegrasyonu-design.md Not: drafts.used_legislation (jsonb) sütunu Supabase Studio'dan elle eklendi.
This commit is contained in:
@@ -3,6 +3,19 @@ import { supabase } from '../lib/supabase';
|
||||
import { AuthenticatedRequest } from '../middleware/auth';
|
||||
import { runIddianameAnalysis, runCaseNarrativeAnalysis, runStatementAnalysis, runDeficiencyAnalysis, runStrategyAnalysis, runMediationAnalysis, runDigitalInternQa } from './document.controller';
|
||||
import { findOrCreateCaseByTitle } from '../lib/caseLookup';
|
||||
import { findRelevantLegislation, LegislationMatch } from '../lib/legislationLookup';
|
||||
|
||||
function formatLegislationBlock(matches: LegislationMatch[]): string {
|
||||
if (matches.length === 0) return '';
|
||||
const body = matches
|
||||
.map((m) => `${m.kanun} madde ${m.maddeNo}${m.baslik ? ` - ${m.baslik}` : ''}: ${m.metin}`)
|
||||
.join('\n\n');
|
||||
return `İLGİLİ MEVZUAT:\n${body}\n\n---\n\n`;
|
||||
}
|
||||
|
||||
function toTraceableLegislation(matches: LegislationMatch[]) {
|
||||
return matches.map(({ maddeNo, baslik, kanun }) => ({ maddeNo, baslik, kanun }));
|
||||
}
|
||||
|
||||
export const createCase = async (req: AuthenticatedRequest, res: Response) => {
|
||||
try {
|
||||
@@ -89,15 +102,17 @@ export const summarizeCase = async (req: AuthenticatedRequest, res: Response) =>
|
||||
.map((d) => `--- ${d.filename} ---\n${String(d.extracted_text).slice(0, perDocLimit)}`)
|
||||
.join('\n\n');
|
||||
|
||||
const analysisInput = iddianameJson
|
||||
const legislationMatches = await findRelevantLegislation(combinedText);
|
||||
|
||||
const analysisInput = formatLegislationBlock(legislationMatches) + (iddianameJson
|
||||
? `İDDİANAME ÖN ANALİZİ (rapora bunu öncelikli bağlam olarak yansıt):\n${JSON.stringify(iddianameJson)}\n\n---\n\nDOSYADAKİ TÜM BELGELER:\n${combinedText}`
|
||||
: combinedText;
|
||||
: combinedText);
|
||||
|
||||
const { summaryJson, aiModel } = await runCaseNarrativeAnalysis(analysisInput);
|
||||
|
||||
const { data: analysisRecord, error: analysisError } = await supabase
|
||||
.from('analyses')
|
||||
.insert([{ case_id: caseId, document_id: null, summary_json: summaryJson, model_used: aiModel }])
|
||||
.insert([{ case_id: caseId, document_id: null, summary_json: { ...summaryJson, kullanilan_mevzuat: toTraceableLegislation(legislationMatches) }, model_used: aiModel }])
|
||||
.select()
|
||||
.single();
|
||||
if (analysisError) throw analysisError;
|
||||
@@ -145,14 +160,15 @@ export const analyzeStatements = async (req: AuthenticatedRequest, res: Response
|
||||
.map((d) => `--- ${d.filename} ---\n${String(d.extracted_text).slice(0, perDocLimit)}`)
|
||||
.join('\n\n');
|
||||
|
||||
const analysisInput = `Aşağıdaki belgeler sadece ifade ve sorgu evraklarıdır:\n\n${combinedText}`;
|
||||
const legislationMatches = await findRelevantLegislation(combinedText);
|
||||
const analysisInput = formatLegislationBlock(legislationMatches) + `Aşağıdaki belgeler sadece ifade ve sorgu evraklarıdır:\n\n${combinedText}`;
|
||||
|
||||
const { summaryJson, aiModel } = await runStatementAnalysis(analysisInput);
|
||||
|
||||
// Çelişki analizi sonucunu 'analyses' tablosuna kaydet
|
||||
const { data: analysisRecord, error: analysisError } = await supabase
|
||||
.from('analyses')
|
||||
.insert([{ case_id: caseId, document_id: null, summary_json: { statementAnalysis: summaryJson }, model_used: aiModel }])
|
||||
.insert([{ case_id: caseId, document_id: null, summary_json: { statementAnalysis: summaryJson, kullanilan_mevzuat: toTraceableLegislation(legislationMatches) }, model_used: aiModel }])
|
||||
.select()
|
||||
.single();
|
||||
if (analysisError) throw analysisError;
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Response } from 'express';
|
||||
import { supabase } from '../lib/supabase';
|
||||
import { AuthenticatedRequest } from '../middleware/auth';
|
||||
import { callAiModelRaw } from './document.controller';
|
||||
import { findRelevantLegislation, LegislationMatch } from '../lib/legislationLookup';
|
||||
|
||||
const DRAFT_SYSTEM_PROMPT = `
|
||||
Sen uzman bir Türk avukatısın. Sana verilen dosya bilgilerine göre, istenen türde
|
||||
@@ -14,6 +15,10 @@ const DRAFT_SYSTEM_PROMPT = `
|
||||
ESKİ dosyaya ait somut bilgileri ASLA kullanma veya kopyalama — onların yerine SADECE
|
||||
aşağıda verilen YENİ dosyanın gerçek bilgilerini koy.
|
||||
|
||||
EĞER sana "REFERANS MADDELER" verilmişse: bunları gerektiğinde "HUKUKİ SEBEPLER"
|
||||
bölümünde doğal bir atıfla (örn. "TCK'nın 141. maddesi uyarınca...") kullan —
|
||||
metinlerini olduğu gibi kopyalama, sadece atıf için referans al.
|
||||
|
||||
ÖNEMLİ — BİÇİMLENDİRME: Bu çıktı doğrudan bir dilekçe belgesine dönüştürülecek, o yüzden
|
||||
KESİNLİKLE Markdown biçimlendirmesi kullanma (**, ##, -, *, 1. gibi işaretler YASAK).
|
||||
Sadece düz metin yaz. Başlık/vurgu gerekiyorsa BÜYÜK HARF veya standart dilekçe
|
||||
@@ -23,6 +28,18 @@ const DRAFT_SYSTEM_PROMPT = `
|
||||
Sadece dilekçe taslağının kendisini döndür — başlık, açıklama veya ek yorum ekleme.
|
||||
`;
|
||||
|
||||
function formatReferenceBlock(matches: LegislationMatch[]): string {
|
||||
if (matches.length === 0) return '';
|
||||
const body = matches
|
||||
.map((m) => `${m.kanun} madde ${m.maddeNo}${m.baslik ? ` - ${m.baslik}` : ''}: ${m.metin}`)
|
||||
.join('\n\n');
|
||||
return `\n---\nREFERANS MADDELER (SADECE ATIF İÇİNDİR, DOĞRUDAN KOPYALAMA):\n${body}\n---\n`;
|
||||
}
|
||||
|
||||
function toTraceableLegislation(matches: LegislationMatch[]) {
|
||||
return matches.map(({ maddeNo, baslik, kanun }) => ({ maddeNo, baslik, kanun }));
|
||||
}
|
||||
|
||||
// Dilekçe Hazırlama ekranındaki "AI ile Taslak Oluştur" — dosyanın gerçek bilgilerini
|
||||
// (başlık, UYAP'tan gelen taraflar, varsa Dosya Özeti) ve kullanıcının notlarını,
|
||||
// isteğe bağlı olarak seçilen büro şablonunun DİLİNİ/FORMATINI referans alarak
|
||||
@@ -85,7 +102,11 @@ export const generateDraft = async (req: AuthenticatedRequest, res: Response) =>
|
||||
userContent += `\n---\nBÜRO ŞABLONU "${template.name}" (SADECE dil/format/üslup referansı — içeriğini kopyalama):\n${template.extracted_text.slice(0, 6000)}\n`;
|
||||
}
|
||||
|
||||
const { aiContent, aiModel } = await callAiModelRaw(DRAFT_SYSTEM_PROMPT, userContent, 2500);
|
||||
const legislationLookupText = `${caseRow.title}\n${partiesText}\n${notes || ''}\n${typeof ozet === 'string' ? ozet : ''}`;
|
||||
const legislationMatches = await findRelevantLegislation(legislationLookupText);
|
||||
userContent += formatReferenceBlock(legislationMatches);
|
||||
|
||||
const { aiContent, aiModel } = await callAiModelRaw(DRAFT_SYSTEM_PROMPT, userContent, 2500, 180_000);
|
||||
|
||||
// Taslağı kalıcı olarak kaydediyoruz — hem "Dilekçelerim" listesinde hem ilgili
|
||||
// dava dosyasının kendi "Dilekçeler" bölümünde görünsün, sayfadan çıkınca kaybolmasın.
|
||||
@@ -97,6 +118,7 @@ export const generateDraft = async (req: AuthenticatedRequest, res: Response) =>
|
||||
petition_type,
|
||||
content: aiContent,
|
||||
template_id: template_id || null,
|
||||
used_legislation: toTraceableLegislation(legislationMatches),
|
||||
}])
|
||||
.select('id, created_at')
|
||||
.single();
|
||||
@@ -104,7 +126,7 @@ export const generateDraft = async (req: AuthenticatedRequest, res: Response) =>
|
||||
console.error('Save Draft Error:', draftError);
|
||||
}
|
||||
|
||||
res.json({ draft: aiContent, draftId: draftRecord?.id || null, model: aiModel, usedTemplate: !!template });
|
||||
res.json({ draft: aiContent, draftId: draftRecord?.id || null, model: aiModel, usedTemplate: !!template, usedLegislation: toTraceableLegislation(legislationMatches) });
|
||||
} catch (error: any) {
|
||||
console.error('Generate Draft Error:', error);
|
||||
res.status(500).json({ error: error.message || 'Internal Server Error' });
|
||||
|
||||
Reference in New Issue
Block a user