feat: hibrit uyum skoru (sozluksel + anlamsal bonus) ve firma esigi
Anlamsal bonus: profil metni sunucuda gomulur (multilingual-e5-small, ONNX q8, @huggingface/transformers — Python/GPU yok), havuzdaki her ihaleyle kosinus alinir, havuz ici z-skor → +0..20 puan. Sozluksel skor ana eksen kalir; bonus eklenir. Sozluksel 0 iken bonus yalniz ≥10'sa gorunur. NEDEN BONUS, ESIK DEGIL (olculdu): e5 kosinusleri 0,79-0,86 bandina sikisiyor (havuz ort 0,814 sd 0,026); kirtasiye ile rontgen arasi 0,06. kaanvp/kobi-ihale-eslestirme'nin 0,65 esigiyle trafo bakimi da "eslesti" sayilirdi. Ama siralama dogru ve sozlukselin kacirdigini yakaliyor: "GENEL TIBBI SARF" profilde birebir terim yokken kirtasiyenin ustune cikiyor. - server/uyum-gomme.mjs: acilista arka planda isitma (~45 sn soguk), ihale gommeleri bellekte (metin imzasi degisince yenilenir), model data/modeller volume'unda bir kez indirilir; paket/yukleme yoksa sessizce kapali - POST /api/uyum/gomme, GET /api/health/uyum - Dockerfile: calisma imajina @huggingface/transformers@3 (package.json'a degil — sunucu sifir-bagimlilik, calisma katmani ayri npm i) - Olculen: 3.2 bin ihale ilk cagri 7,6 sn (tumu gomuluyor), ikinci 17 ms; +20 alan 170, ≥10 alan 610 ihale Firma esigi: profilde uyumEsigi (varsayilan 60, kalici); "Uyumlu" hizli filtresi bu esigi kullanir; profil modalinda kaydirici + bonus durumu. Modaldaki "%98+ dogruluk" cumlesi kaldirildi — olculmemis iddiaydi. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,7 @@ import { arsivMetni, sunucuOkuyabilir } from './office-text.mjs';
|
||||
import { kalemleriCikar } from './kalem-cikar.mjs';
|
||||
import { searchInDocuments, searchByOkas, okasAra } from './ekap-v2.mjs';
|
||||
import { iknCikar } from './kalem-cikar.mjs';
|
||||
import { bonusHesapla, gommeDurumu, arkaPlandaIsit } from './uyum-gomme.mjs';
|
||||
|
||||
// Ürün araması EKAP'a uzak çağrı yapar; aynı sorgu tekrarlanırsa kaynağı yorma.
|
||||
const urunAramaCache = new Map();
|
||||
@@ -799,6 +800,24 @@ const server = http.createServer(async (req, res) => {
|
||||
return json(res, 502, { success: false, error: e.message });
|
||||
}
|
||||
}
|
||||
// --- Anlamsal uyum bonusu: profil metni ↔ havuz (sunucuda gömme) ---
|
||||
if (url.pathname === '/api/uyum/gomme' && req.method === 'POST') {
|
||||
const user = userFromAuthHeader(req.headers['authorization']);
|
||||
if (!user) return json(res, 401, { success: false, error: 'oturum yok' });
|
||||
try {
|
||||
const body = await readBody(req, 64 * 1024);
|
||||
const metin = String(body.profilMetni || '').trim();
|
||||
if (metin.length < 10) return json(res, 400, { success: false, error: 'profil metni yok' });
|
||||
const c = await getTenders(false);
|
||||
const out = await bonusHesapla(metin, c.tenders || []);
|
||||
return json(res, 200, { success: true, ...out });
|
||||
} catch (e) {
|
||||
return json(res, 500, { success: false, error: e.message });
|
||||
}
|
||||
}
|
||||
if (url.pathname === '/api/health/uyum') {
|
||||
return json(res, 200, { success: true, ...gommeDurumu() });
|
||||
}
|
||||
if (url.pathname === '/api/health/kalem') {
|
||||
return json(res, 200, { success: true, ...kalemStore.kalemSayilari(), depo: kalemStore.available() });
|
||||
}
|
||||
@@ -1343,6 +1362,8 @@ const server = http.createServer(async (req, res) => {
|
||||
});
|
||||
|
||||
server.listen(PORT, () => {
|
||||
// Gömme modeli soğuk yüklemesi ~45 sn; ilk kullanıcı beklemesin diye arka planda ısıt.
|
||||
arkaPlandaIsit();
|
||||
console.log(`[ihaleist-api] http://localhost:${PORT} — canlı veri: ilan.gov.tr`);
|
||||
getTenders().catch((e) => console.error('[ihaleist-api] ilk yükleme hatası:', e.message));
|
||||
// Bildirim işi tender verisine ihtiyaç duyar; enjekte ederek başlat
|
||||
|
||||
Reference in New Issue
Block a user