Preserve question source chunks and reject fabricated citations
This commit is contained in:
+9
-17
@@ -1,3 +1,5 @@
|
||||
import { createHash } from 'node:crypto';
|
||||
import { questionChunks, qualifyQuestionAnswer } from './document-question.mjs';
|
||||
import { qualifyOfferAudit } from './offer-audit.mjs';
|
||||
import { documentContent } from './document-content.mjs';
|
||||
// Teklif asistanı — İhaleAB'den taşınan üç AI özelliği, ihaleist'in mevcut
|
||||
@@ -18,17 +20,7 @@ export function terimler(soru) {
|
||||
return [...new Set(norm(soru).split(' ').filter((k) => k && !DURAK.has(k) && (k.length >= 3 || /^\d+$/.test(k))))].slice(0, 12);
|
||||
}
|
||||
|
||||
/** Metni ~1200 karakterlik parçalara böler (paragraf sınırlarında). */
|
||||
export function parcala(metin, hedef = 1200) {
|
||||
const p = []; let tampon = '';
|
||||
for (const satir of String(metin ?? '').replace(/\r/g, '').split('\n')) {
|
||||
if (tampon.length + satir.length > hedef * 1.5 && tampon.trim()) { p.push(tampon.trim()); tampon = ''; }
|
||||
tampon += satir + '\n';
|
||||
if (tampon.length >= hedef && satir.trim() === '') { p.push(tampon.trim()); tampon = ''; }
|
||||
}
|
||||
if (tampon.trim().length >= 40) p.push(tampon.trim());
|
||||
return p;
|
||||
}
|
||||
export const parcala = questionChunks;
|
||||
|
||||
/** Terim kapsamasına göre en iyi k parça. */
|
||||
export function getir(parcalar, soru, k = 8) {
|
||||
@@ -68,13 +60,13 @@ export async function belgeyeSor({ ikn, soru, text, tender, chain }) {
|
||||
if (!metin) throw new Error('Bu ihale için doküman metni bulunamadı — PDF\'i "Yapay Zeka Asistanı"ndan yükleyip tekrar sorun.');
|
||||
const parcalar = parcala(metin);
|
||||
const kaynaklar = getir(parcalar, q, 8);
|
||||
if (!kaynaklar.length) return { cevap: 'Belgede bu soruyla eşleşen bir bölüm bulunamadı; belgedeki terimlerle (teminat, teslim, ceza, adet) yeniden deneyin.', kaynaklar: [], kaynak };
|
||||
const baglam = kaynaklar.map((k) => `[${k.n}]\n${k.icerik.slice(0, 1400)}`).join('\n\n');
|
||||
const sistem = `Sen Türkiye kamu ihalesi uzmanısın. Sana bir ihale dokümanının numaralı parçaları verilecek. YALNIZCA bu parçalardaki bilgiyle yanıt ver; parçalarda yoksa "Belgede bu bilgi bulunamadı" de, tahmin etme. Her iddianın sonuna dayandığı parça numarasını köşeli parantezle yaz: [1], [3]. Sayı, tarih, tutar, süre değerlerini aynen aktar. Kısa ve net Türkçe. Yalnızca JSON döndür: {"cevap":"..."}`;
|
||||
if (!kaynaklar.length) return { cevap: 'Terim aramasında bu soruyla eşleşen bir bölüm bulunamadı; bu, şartın belgede bulunmadığını kanıtlamaz; belgedeki terimlerle (teminat, teslim, ceza, adet) yeniden deneyin.', kaynaklar: [], kaynak };
|
||||
const baglam = kaynaklar.map((k) => `[${k.n}]\n${k.icerik}`).join('\n\n');
|
||||
const sistem = `Sen Türkiye kamu ihalesi uzmanısın. Sana bir ihale dokümanının numaralı parçaları verilecek. YALNIZCA bu parçalardaki bilgiyle yanıt ver; parçalarda yoksa "Seçilen kaynak parçalarında bu bilgi bulunamadı" de ve kontrol edilen parçalara atıf ver, tahmin etme. Her iddianın sonuna dayandığı parça numarasını köşeli parantezle yaz: [1], [3]. Sayı, tarih, tutar, süre değerlerini aynen aktar. Kısa ve net Türkçe. Yalnızca JSON döndür: {"cevap":"..."}`;
|
||||
const out = await analyzeSpec({ text: `PARÇALAR:\n\n${baglam}\n\nSORU: ${q}\n\nYalnızca {"cevap":"..."} JSON'u döndür.`, tender, chain, systemOverride: sistem });
|
||||
const cevap = String(out?.result?.cevap ?? '').trim() || 'Yanıt üretilemedi.';
|
||||
const kullanilan = new Set([...cevap.matchAll(/\[(\d+)\]/g)].map((m) => Number(m[1])));
|
||||
return { cevap, kaynaklar: (kullanilan.size ? kaynaklar.filter((k) => kullanilan.has(k.n)) : kaynaklar.slice(0, 3)).map((k) => ({ n: k.n, alinti: k.icerik.slice(0, 1400) })), kaynak };
|
||||
return {...qualifyQuestionAnswer(out?.result, kaynaklar), kaynak,
|
||||
sourceReview:{sourceHash:createHash('sha256').update(metin).digest('hex'), totalChunks:parcalar.length, selectedChunks:kaynaklar.length},
|
||||
kapsam:'Yanıt yalnız soruyla eşleşen kaynak parçalarına dayanır. Kaynak numarası kontrolü, yanıtın anlam bakımından doğruluğunu tek başına kanıtlamaz; diğer bölümlerde ek veya çelişen şartlar bulunabilir.'};
|
||||
}
|
||||
|
||||
function gereklilikMetni(tender, gereklilikler, metin) {
|
||||
|
||||
Reference in New Issue
Block a user