Suhbat tarixi
Hozirgacha har bir savol mustaqil edi. Shuning uchun bu ishlamaydi:
Foydalanuvchi: Standart tarif qancha? Bot: 250 000 som. Foydalanuvchi: Unga nima kiradi? Bot: "Unga" nima? Ma'lumotim yo'q.
Bot oldingi savolni eslamaydi.
Sessiya
import uuid SESSION_COOKIE = "rag_session" _sessions: dict[str, list[dict]] = {} MAX_MESSAGES = 10 def _get_session_id(request: Request) -> str: sid = request.cookies.get(SESSION_COOKIE) if not sid: sid = str(uuid.uuid4()) return sid def _get_history(session_id: str) -> list[dict]: return _sessions.get(session_id, []) def _save_history(session_id: str, history: list[dict]) -> None: _sessions[session_id] = history[-MAX_MESSAGES:]
DIQQAT: tarix XOTIRADA
_sessions: dict[str, list[dict]] = {}
Bu oddiy Python lug'ati — RAM da. Ya'ni:
- Server qayta ishga tushsa — hamma suhbat yo'qoladi
- Ikkita server ishlasa — har birida o'z tarixi
Bazada rag.chat_history jadvali bor, lekin bu kod uni
ishlatmaydi. Dars uchun bu yetarli va soddaroq.
Production uchun tarixni bazaga yozish kerak:
INSERT INTO rag.chat_history (session_id, role, content) VALUES (:sid, :role, :content);
Talabaga bu farqni ayting — bu tipik "demo vs production" misoli.
MAX_MESSAGES nega kerak
_sessions[session_id] = history[-MAX_MESSAGES:]
Ikki sabab:
1. Kontekst oynasi. Har bir xabar promptga qo'shiladi. 100 ta xabar bo'lsa, prompt modelning chegarasidan oshadi.
2. Xotira. 1000 foydalanuvchi × cheksiz tarix = server yiqiladi.
Oxirgi 10 ta xabar — amalda yetarli.
Promptga qo'shish
def build_prompt(chunks, question, history=None): xabarlar = [{"role": "system", "content": TIZIM}] if history: xabarlar.extend(history[-6:]) kontekst = "\n\n".join( f"[{i}] {c['title']}\n{c['chunk_text']}" for i, c in enumerate(chunks, 1) ) xabarlar.append({ "role": "user", "content": f"=== KONTEKST ===\n{kontekst}\n\n" f"=== SAVOL ===\n{question}", }) return xabarlar
history[-6:] — kontekstda saqlanadigan tarix MAX_MESSAGES
dan kamroq. Sababi: tarix ham, chunk'lar ham joy egallaydi.
Ikkalasini birga hisoblang.
Cookie o'rnatish
javob = JSONResponse({"answer": matn, "sources": manbalar}) javob.set_cookie( SESSION_COOKIE, sid, max_age=3600, httponly=True, samesite="lax", ) return javob
httponly=True — JavaScript cookie'ni o'qiy olmaydi. XSS
hujumida sessiya o'g'irlanmaydi.
samesite="lax" — boshqa saytdan yuborilgan so'rovda cookie
ketmaydi. CSRF himoyasi.
Bu ikki parametr bir qator, lekin ikkita hujum turini to'sadi. Har doim yozing.
Muhim muammo: qidiruv tarixni bilmaydi
Savol 1: Standart tarif qancha? Savol 2: Unga nima kiradi?
Ikkinchi savol vektorga aylanganda — "Unga nima kiradi?" degan matn. Bu hech narsaga o'xshamaydi. Qidiruv noto'g'ri chunk topadi.
Tarix LLM ga yordam beradi, lekin qidiruvga yo'q.
Yechim — savolni to'ldirish (query rewriting):
def savolni_toldir(savol: str, history: list[dict]) -> str: if not history: return savol oxirgi = history[-2:] kontekst = " ".join(h["content"] for h in oxirgi) prompt = ( f"Oldingi suhbat: {kontekst}\n" f"Yangi savol: {savol}\n\n" f"Yangi savolni MUSTAQIL tushunarli qilib qayta yozing. " f"Faqat savolni yozing, boshqa hech narsa." ) return llm_chaqir(prompt) or savol
Natija:
"Unga nima kiradi?" -> "Standart tarifga nima kiradi?"
Endi qidiruv to'g'ri ishlaydi.
Narxi: har bir savolga qo'shimcha LLM chaqiruvi. Buni faqat tarix bo'lganda qiling.
Bu bobni bepul o'qidingiz.
Kitob o'rgatadi, lekin sizning kodingiz nega ishlamaganini ayta olmaydi. Kursda uy vazifangiz tekshiriladi.