ABONAMENTE VIDEO REDACȚIA
RO
EN
Numărul 153
NOU
Numărul 152
Numărul 151 Numărul 150 Numărul 149 Numărul 148 Numărul 147 Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼

Introducere în sistemele RAG

Darius Dima
Software System Engineer @ Centrul de Inginerie Bosch Cluj



PROGRAMARE

Într-o eră digitală în care informația curge într-un ritm amețitor, tehnologiile care combină căutarea eficientă cu generarea de conținut devin esențiale. Aceste soluții inovatoare ne ajută să navigăm mai eficient prin complexitatea datelor și să găsim rapid răspunsuri relevante la întrebările noastre. Odată cu popularizarea modelelor de limbaj de mari dimensiuni (LLM), căutările semantice au evoluat semnificativ, devenind un instrument valoros în procesul de informare. Din păcate, antrenarea unui LLM necesită resurse greu accesibile, iar situațiile în care căutarea trebuie să extragă rezultate precise și de actualitate sunt tot mai frecvente. În acest context, sistemele care integrează extragerea informațiilor cu generarea de conținut vin să acopere acest gol punând în valoare puterea căutărilor semantice pe date relevante.

Ce este un sistem RAG ?

Un sistem RAG (Retriever-Augmented Generation) este o combinație a două elemente: un retriever și un generator.

  1. Retriever: Folosește baze de date vectoriale (precum Qdrant, Pinecone sau FAISS) pentru a căuta fragmentele relevante din documente.

  2. Generator: Un model LLM (Large Language Model), ce poate fi local (folosind instrumente precum Ollama sau LM Studio), sau accesat prin API-uri cloud cum sunt cele de la OpenAI (ChatGPT), Deepseek etc.

Retrieverul parcurge datele în căutarea fragmentelor contextuale relevante pentru întrebarea pusă, iar generatorul creează și oferă răspunsurile folosind acest context.

De ce sunt importante sistemele RAG ?

Sistemele RAG revoluționează modul în care interacționăm cu informația. Iată câteva exemple de aplicații ce pot fi implementate folosind un sistem RAG:

Sună interesant ? Haideți să ne construim propriul sistem RAG local

În acest articol propunem un setup local, ușor de utilizat, ce ne permite să păstrăm private datele sensibile.

Setup

1.Descărcați LM Studio pentru sistemul dumneavoastră de operare de pe site-ul oficial.

2.Mergeți la secțiunea Discover și alegeți un model pe care să îl descărcați. Voi folosi qwen2.5-7B-instruct-1M, deoarece este un model mic, însă suficient de capabil, dar vă sfătuiesc să experimentați cu diferite modele.

3.Încărcați modelul și mergeți la secțiunea Developer pentru a porni serverul (Ctrl + R). Acest lucru va face modelul disponibil local.

4.Creați un mediu virtual de Python în directorul proiectului RAG:

python -m venv .venv
source .venv/Scripts/activate  
# exemplu pentru terminalul Git Bash

5.Instalați dependințele:

pip install numpy faiss-cpu transformers
sentence-transformers openai

Structura proiectului:

Construirea retrieverului (data_retriever.py):

1.La început, documentele sunt împărțite în fragmente de o dimensiune aleasă (valoarea predefinită fiind de 512 caractere). Spre exemplu, câteva fișiere text pot vor deveni sute (sau mii) de fragmente text de 512 caractere fiecare. Acestea sunt apoi indexate pentru căutare rapidă și transformate în codificări numerice numite embedings.

class DataRetriever:
  def __init__(self, directory_path, 
  chunk_size=512, top_k=5):
    self.directory_path = directory_path
    if not os.path.isdir(self.directory_path):
       raise ValueError(f"Directory does not exist: 
       {self.directory_path}")
    self.model = SentenceTransformer(
    'all-MiniLM-L6-v2')
    self.chunk_size = chunk_size
    self.top_k = top_k
    self.index_path = 'faiss_index'
    self.embeddings_path = 'embeddings.pkl'
    self.document_chunks = self._chunk_documents()
    self.index, self.embeddings = 
    self._load_or_build_index()

2.Când utilizatorul abordează o cerință, aceasta este, de asemenea, codificată în reprezentări numerice (embedings). Retrieverul returnează primele top_k (în cazul nostru 5) fragmente de text din documente cu conținut similar cerinței utilizatorului. Folosind reprezentările numerice, căutarea este mult mai amplă decât o simplă căutare după cuvinte cheie, întrucât acum se iau în considerare și cuvintele apropiate ca sens (sinonime, familie de cuvinte etc.).

def retrieve(self, query):
  query_embedding = self.model.encode([query], 
convert_to_tensor=True).cpu().numpy()
    distances, indices = self.index.search(
    query_embedding, self.top_k)

    relevant_texts = [f"... {
    self.document_chunks[idx]} ..." for 
    idx in indices[0]]

return '\n'.join(relevant_texts)

Implementarea celorlalte metode ajutătoare se poate găsi în codul sursă.

Construirea generatorului de răspuns (response_generator.py):

1.LM Studio folosește API-ul openai. Dorim totuși, să fim flexibili, lăsând deschisă posibilitatea de a folosi sistemul RAG și cu alte API-uri (precum Ollama). Astfel, vom crea, mai întâi o interfață pe care să o moștenim:

from abc import ABC, abstractmethod

class IResponseGenerator(ABC):
  @abstractmethod
  def query(self, user_prompt):
    pass

class OpenaiResponseGenerator(IResponseGenerator):

2.Ca să folosim modelul local, vom specifica adresa serverului local: http://127.0.0.1:1234 pentru variabila base_url la inițializare.

   def __init__(self, base_url, api_key, model,
   history_length=20):

     self.client = OpenAI(base_url=base_url, 
     api_key=api_key)

3.Deoarece LLM-urile nu au capacitatea intrinsecă de a reține mai mult de un mesaj, dacă dorim să putem purta o conversație, va trebui să reținem mesajele anterioare într-o listă:

Într-o conversație cu un LLM există trei roluri posibile: cel de system, de user și de assistant. Rolul de system va fi întotdeauna primul și ne ajută să definim un comportament general pentru modelul de limbaj de-a lungul conversației. Vom adăuga posibilitatea de setare a unui prompt de sistem prin metoda set_system_prompt și vom folosi o metodă ajutătoare add_to_chat_history pentru a ne asigura că acesta este primul în conversație, chiar dacă se depășește mărimea istoricului.

def add_to_chat_history(self, message):
  sys_prompt_not_in_history = not any(
  msg['role'] == 'system' and msg['content'] == 
    self.system_prompt for msg in self.chat_history
  )
 if self.system_prompt and sys_prompt_not_in_history:
   self.chat_history.insert(0, {'role': 'system', 
                     'content': self.system_prompt})
   self.chat_history.append({'role': 'user', 
                     'content': message})
   if len(self.chat_history) > self.history_length:
     self.chat_history = self.chat_history[
                      -self.history_length:]

4.Metoda query este responsabilă pentru a extrage răspunsul LLM-ului.

def query(self, user_prompt):
  try:
    self.add_to_chat_history(user_prompt)
    response = self.client.chat.completions.create(
      messages=self.chat_history,
      model=self.model
    )
    assistant_response = {
      'role': 'assistant',
      'content': response.choices[0].message.content
    }
    self.chat_history.append(assistant_response)
    if len(self.chat_history) > self.history_length:
      self.chat_history = self.chat_history[
        -self.history_length:
      ]
    return response.choices[0].message.content
  except Exception as e:
    return f"S-a produs o excepție: {str(e)}"

Conectarea sistemului (rag.py):

După ce am construit retrieverul și generatorul, vom combina aceste două componente pentru a forma sistemul RAG. În esență, vom modifica promptul pentru a adăuga în context citatele relevante extrase din documente.

from data_retriever import DataRetriever
from response_generator import IResponseGenerator

class RAG:
  def __init__(
    self,
    retriever: DataRetriever,
    generator: IResponseGenerator
  ):
    self.retriever = retriever
    self.generator = generator

  def generate_response(self, user_query):
    retrieved_docs = self.retriever.retrieve(
      user_query
    )
    intro = (
      "Având în vedere anumite informații "
      "relevante din documente:"
    )
    pre_prompt = (
      "Răspunde la următoarea cerere "
      "a utilizatorului:"
    )
    full_prompt = (
      f"{intro}\n```\n{retrieved_docs}\n```\n"
      f"{pre_prompt}\n{user_query}"
    )
    response = self.generator.query(full_prompt)
    return response

Rulează sistemul RAG (main.py):

Definim funcționalitatea prin care utilizatorul interacționează cu sistemul.

def start_chat(rag_system):
  user_query = input("Utilizator: ")
  while user_query.lower() != 'quit':
    response = rag_system.generate_response(
      user_query
    )
    print(f"Asistent: {response}")
    user_query = input("Utilizator: ")

Implementăm metoda main, unde configurăm retrieverul și generatorul, iar apoi sistemul RAG.

def main():
  retriever = DataRetriever(
    'data',
    chunk_size=512,
    top_k=5
  )

  base_url, api_key = (
    "http://127.0.0.1:1234/v1",
    "api_key"
  )
  model = "qwen2.5-7b-instruct-1m"
  generator = OpenaiResponseGenerator(
    base_url,
    api_key,
    model,
    history_length=10
  )

  rag_system = RAG(retriever, generator)
  start_chat(rag_system)

if __name__ == "__main__":
  main()

Testare și îmbunătățiri

Concluzie

Marele beneficiu al sistemelor RAG (Retriever-Augmented Generation) este capacitatea acestora de a oferi răspunsuri mai precise și relevante prin integrarea informațiilor externe cu modele generative. Aceasta permite sistemelor de inteligență artificială să acceseze cunoștințe actualizate și să răspundă la întrebări complexe, îmbunătățind astfel acuratețea și utilitatea răspunsurilor. Această abordare ajută la depășirea limitărilor modelelor de limbaj bazate exclusiv pe datele cu care au fost antrenate.

LANSAREA NUMĂRULUI 153

Generative AI în Programare

Miercuri, 26 Martie, ora 18:00

sediul Betfair Romania Development

Facebook Meetup StreamEvent YouTube

Conferință TSM

NUMĂRUL 150 - Technologiile SAP ABAP

Sponsori

  • BT Code Crafters
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • GlobalLogic
  • Colors in projects