Asosiy qismga o'tish

Suhbat tarixi

19-bob 10 kod bloki 676 so'z

Hozirgacha har bir savol mustaqil edi. Shuning uchun bu ishlamaydi:

yaml4 qator
Foydalanuvchi: Standart tarif qancha?
Bot:           250 000 som.
Foydalanuvchi: Unga nima kiradi?
Bot:           "Unga" nima? Ma'lumotim yo'q.

Bot oldingi savolni eslamaydi.

Sessiya

python21 qator
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

yaml1 qator
_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:

sql2 qator
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

python1 qator
_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

python16 qator
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.

python8 qator
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

matn2 qator
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):

python14 qator
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:

matn1 qator
"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.

Telegramda yozish Narxlar