embedder.py — matndan vektorga
Buni ham o'zingiz yozasiz. Bu bobdan keyin bazangizda haqiqiy sonlar paydo bo'ladi — ikkinchi g'alaba.
Embedding nima
Matnni sonlar ro'yxatiga aylantiradi:
"Standart tarif 250 000 som" -> [0.023, -0.451, 0.882, ... ]
<-------- 1024 ta son -------->
Bu sonlar tasodifiy emas. Ular matnning ma'nosini ifodalaydi. O'xshash ma'noli gaplar — o'xshash sonlar.
Shuning uchun "tarif qancha turadi?" va "narxi nechchi?" degan ikki xil gap bir-biriga yaqin vektor beradi. Aynan shu RAG ni kalit so'z qidiruvidan ustun qiladi.
Kod
import httpx from app.config import settings def embed_text(matn: str) -> list[float] | None: """Bitta matnni vektorga aylantiradi. Xato bo'lsa None qaytaradi.""" if not matn or not matn.strip(): return None try: javob = httpx.post( f"{settings.ollama_base_url}/api/embeddings", json={"model": settings.ollama_embed_model, "prompt": matn}, timeout=60.0, ) javob.raise_for_status() vektor = javob.json().get("embedding") except (httpx.HTTPError, ValueError) as e: print(f"[XATO] embedding olinmadi: {type(e).__name__}") return None if not vektor: return None return vektor
Nega None qaytaradi, xato tashlamaydi
Ollama ishlamayotgan bo'lishi mumkin. 1000 ta chunk'ni qayta ishlayotganda bitta xato butun jarayonni to'xtatmasligi kerak.
Lekin None ni tekshirish shart. Aks holda:
vektor = embed_text(savol) print(len(vektor)) # TypeError: object of type 'NoneType' has no len()
Bu xato dars paytida chiqadi va sizni chalg'itadi. Shuning uchun har doim:
if not vektor: print("Ollama ishlamayapti — 'ollama serve' ni tekshiring") return
O'lchamni tekshirish
Quyidagi
embed_and_check— faqat DARS uchun yordamchi. Haqiqiy loyihada bunday funksiya yo'q. Lekin g'oyasi foydali.
def embed_and_check(matn: str) -> list[float] | None: v = embed_text(matn) if v is None: return None from app.models.tables import LOCAL_DIM if len(v) != LOCAL_DIM: raise ValueError( f"Model {len(v)} o'lchamli vektor berdi, " f"jadval esa {LOCAL_DIM} kutadi. " f"Model almashtirilganmi? bootstrap.py ni qayta ishlating." ) return v
Bu tekshiruv juda foydali. Modelni almashtirsangiz, o'lcham o'zgaradi va baza xato beradi:
expected 1024 dimensions, not 768
Bu xabar qayerdan kelganini tushunish qiyin. Yuqoridagi tekshiruv esa sababni to'g'ridan-to'g'ri aytadi.
Barcha chunk'larni qayta ishlash
from sqlalchemy import text from app.database import SessionLocal def embed_all_chunks(session, batch: int = 20) -> int: db = session jami = 0 try: while True: qatorlar = db.execute(text(""" SELECT id, chunk_text FROM rag.chunks WHERE embedding_local IS NULL ORDER BY created_at LIMIT :lim """), {"lim": batch}).fetchall() if not qatorlar: break for q in qatorlar: v = embed_text(q.chunk_text) if v is None: print(" Ollama javob bermadi — to'xtatildi") return jami db.execute(text(""" UPDATE rag.chunks SET embedding_local = :v WHERE id = :id """), {"v": str(v), "id": q.id}) jami += 1 db.commit() print(f" {jami} ta chunk qayta ishlandi") finally: pass return jami
Diqqat: haqiqiy loyihada funksiya nomi embed_all_chunks va u
sessiyani tashqaridan oladi — bu testlashni osonlashtiradi.
WHERE embedding_local IS NULL — eng muhim qator. Faqat
vektorsizlarni oladi. Shuning uchun skriptni qayta ishga
tushirish xavfsiz: uzilib qolsa, qolganidan davom etadi.
LIMIT va batch — hammasini bir vaqtda xotiraga olmaymiz.
100 000 chunk bo'lsa, kompyuter yiqiladi.
db.commit() sikl ichida — har batch'dan keyin saqlaymiz.
Oxirida commit qilsangiz va jarayon uzilsa, hamma ish yo'qoladi.
str(v) — pgvector vektorni matn sifatida kutadi:
"[0.023, -0.451, ...]". Python ro'yxatini to'g'ridan-to'g'ri
yubora olmaysiz.
Ishga tushiring
python -m scraper.embedder
20 ta chunk qayta ishlandi 40 ta chunk qayta ishlandi ... Tayyor: 14 ta vektor yaratildi
Endi bazaga qarang
SELECT count(*) AS jami, count(embedding_local) AS vektorli FROM rag.chunks;
Endi vektorli = 14. Oldin 0 edi.
Haqiqiy sonlarni ko'ring:
SELECT left(embedding_local::text, 80) FROM rag.chunks LIMIT 1;
[0.0234,-0.4512,0.8821,-0.1043,0.5567,...
Talabalarga shuni ko'rsating. Ular birinchi marta "ma'no"ning son ko'rinishini ko'radi. Bu RAG ni sehrdan matematikaga aylantiradigan lahza.
Matematikani sinang
SELECT 1 - ('[1,0,0]'::vector <=> '[1,0,0]'::vector) AS bir_xil, 1 - ('[1,0,0]'::vector <=> '[0,1,0]'::vector) AS bogliq_emas, 1 - ('[1,0,0]'::vector <=> '[-1,0,0]'::vector) AS qarama_qarshi;
bir_xil | bogliq_emas | qarama_qarshi
---------+-------------+---------------
1 | 0 | -1
<=> — cosine masofa operatori. 1 - masofa = o'xshashlik.
1— bir xil yo'nalish0— bog'liq emas-1— qarama-qarshi
Keyingi bobda aynan shu operator bilan qidiramiz.
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.