Trong phần đầu tiên, chúng ta đã khám phá cách giao tiếp với một mô hình để nhận được một cuộc trò chuyện dựa trên các thông tin mà nó chưa hề được huấn luyện. Trong phần thứ hai, chúng ta đã xem xét chiến lược để tạo lập các chỉ mục và lưu trữ chúng. Trong phần cuối cùng này, chúng ta sẽ tiếp cận, qua một trường hợp thực tế, cách tạo một hệ thống RAG hoàn chỉnh và khởi tạo một cuộc đối thoại, để có thể cho người dùng cơ hội thảo luận với mô hình của chúng ta đã được bổ sung với dữ liệu của chính chúng ta.

Nhắc lại rằng, tất cả thông tin đều có trong bộ tài liệu có sẵn tại đây. Do đó, bạn không thực sự cần đọc những bài viết này. Tuy nhiên, việc hiểu biết sẽ dễ dàng hơn khi có các giải thích bổ sung. Một số thông tin có thể có vẻ ngẫu nhiên, điều này là bình thường. Rất khó giẻi thuật một lĩnh vực rộng lớn chỉ trong vài dòng. Hơn nữa, ngành đang phát triển nhanh chóng, cùng với các công cụ sẵn có. Chúng tôi đã cố tình chọn lọc cụ thể một số khía cạnh trong khi chỉ nhấn mạnh đôi chút về những phần khác. Người đang phải thiết lập một hệ thống như vậy, như bạn, nên tìm hiểu sâu rộng hơn tùy theo nhu cầu của mình.

Vì vậy, chúng ta sẽ bắt đầu với một mục tiêu nhỏ:

Trang web của chúng tôi partitech.fr có một blog kỹ thuật. Chúng tôi xuất bản nội dung đa dạng, chỉ cần chúng có thể hữu ích cho ai đó. Thông thường, một đồng nghiệp hoặc một khách hàng gửi cho chúng tôi một câu hỏi, và điều đó làm chúng tôi nghĩ: 'Này, nếu người này có câu hỏi này, tại sao chúng tôi không viết một ghi chép ngắn? Điều đó chắc chắn có thể hữu ích cho người khác.' Đó là cơ hội để chúng tôi nghiên cứu sâu hơn về một chủ đề và biến nó thành ghi chú cá nhân. Nói ngắn gọn, chúng tôi có một blog...

Chúng tôi có thể truy cập vào các tập tin của blog thông qua Sitemap. Điều này rất tiện lợi, bởi vì bạn cũng có thể tiếp cận với nguồn tài nguyên này. Vì thế, chúng ta sẽ đi qua Sitemap và lập chỉ mục cho nội dung của nó để có thể thực hiện các truy vấn trên đó. Tuyệt vời!

Để bắt đầu, chúng ta cần nguyên liệu thô của mình, vì vậy chúng ta sẽ nhanh chóng phát triển một kịch bản nhỏ để tìm kiếm nội dung của chúng ta. Chúng ta sẽ gọi nó là 'SonataExtraBlog'.

sonata_extra_blog.py:

import requests
import xml.etree.ElementTree as ET
from txtai.pipeline import Textractor
from urllib.parse import urlparse
from bs4 import BeautifulSoup
import tempfile
import os


class SonataExtraBlog:
    def __init__(self, sitemap_url):
        self.sitemap_url = sitemap_url
        self.textractor = Textractor(sentences=True)

    def is_valid_url(self, url):
        parsed = urlparse(url)
        return bool(parsed.netloc) and bool(parsed.scheme)

    def getData(self):
        # déclaration de notre retour de données
        data_list = []

        # Télécharger le fichier sitemap
        response = requests.get(self.sitemap_url)
        sitemap_content = response.content

        # Parser le contenu XML
        root = ET.fromstring(sitemap_content)

        # Extraire les URLs
        urls = [url.text for url in root.findall('.//{http://www.sitemaps.org/schemas/sitemap/0.9}loc')]

        # Parcourir chaque URL pour télécharger et traiter le contenu
        for url in urls:
            # Vérifier si l'URL est valide
            if not self.is_valid_url(url):
                print(f"URL invalide : {url}")
                continue

            try:
                print(f"Traitement de : {url}")
                # Télécharger le contenu HTML
                response = requests.get(url)
                html_content = response.content.decode('utf-8')

                # Utiliser BeautifulSoup pour extraire le contenu de la div avec la classe 'content'
                soup = BeautifulSoup(html_content, 'html.parser')
                content_div = soup.find('div', class_='site-content')
                if content_div:
                    # Créer un fichier temporaire pour le contenu HTML
                    with tempfile.NamedTemporaryFile(delete=False, suffix='.html', prefix='txtextractor_',
                                                     mode='w') as temp_file:
                        temp_file.write(str(content_div))
                        temp_file_path = temp_file.name

                    # Extraire le texte à l'aide de Textractor
                    text = self.textractor(temp_file_path)

                    # Supprimer le fichier temporaire
                    os.remove(temp_file_path)
                    # print(text)
                    data_list.append({"id": url, "text": text})
                else:
                    text = "Pas de contenu trouvé dans la div 'content'"


            except requests.RequestException as e:
                print(f"Erreur lors du téléchargement de {url}: {e}")
        return data_list

Các bình luận nằm trong mã, vì vậy không cần những giải thích dài. Nói chung, chúng ta đi qua các liên kết của sitemap, trích xuất khu vực nội dung của từng trang, lấy thông tin văn bản và đặt nó vào một bảng mà chúng ta trả về. Thực sự, sẽ khó có thể đơn giản hơn (mặc dù, thực tế, chúng ta đã làm đơn giản hơn trong phần 4, nhưng thực tế đó không phải là chủ đề của bài viết này. Các vấn đề kỹ thuật và thẩm mỹ của mã sẽ được bàn ở một lần khác).

Chúng ta đã có lớp của mình để thu thập thông tin của mình. Giờ đây, chúng ta sẽ tạo tập tin của mình để thu thập thông tin này và tạo lập chỉ mục.

Đừng quên khởi động máy chủ Postgresql của chúng ta:

services:
  db:
    hostname: db
    image: ankane/pgvector
    ports:
     - 5432:5432
    restart: always
    environment:
      - POSTGRES_DB=vectordb
      - POSTGRES_USER=testuser
      - POSTGRES_PASSWORD=testpwd
      - POSTGRES_HOST_AUTH_METHOD=trust
    volumes:
     - ./init.sql:/docker-entrypoint-initdb.d/init.sql

Chạy tất cả

docker compose up -d
pipenv shell
python3 index.py

Các tập tin dưới định dạng FAISS đã được tạo.

Và chúng ta kết thúc với 3 bảng được tự động tạo ra và chứa đầy thông tin.

Bây giờ, chúng ta có thể đặt câu hỏi của mình bằng cách yêu cầu txtai vui lòng thêm một ngữ cảnh tìm kiếm được dựa trên câu hỏi của chúng ta.

from txtai.embeddings import Embeddings
from llama_cpp import Llama

# on déclare notre sytème d'embeddings
embeddings = Embeddings(
    content="postgresql+psycopg2://testuser:testpwd@localhost:5432/vectordb",
    objects=True,
    backend="faiss"
)

# on charge les embeddings
embeddings.load("./index_blog_partitech")

llm = Llama(
    model_path="/Data/Projets/Llm/Models/openchat_3.5.Q2_K.gguf", n_ctx=90000
)


def execute(question, context):
    prompt = f"""GPT4 User: system You are a friendly assistant. You answer questions from users.
        user Answer the following question using only the context below. Only include information 
        specifically discussed or general AI and LLMs related subject. 
      question: {question}
      context: {context} <|end_of_turn|>
      GPT4 Assistant:
      """
    return llm(prompt,
               temperature=0,
               max_tokens=10000,
               top_p=0.2,
               top_k=10,
               repeat_penalty=1.2)


def rag(question):
    context = "\n".join(x["text"] for x in embeddings.search(question))
    return execute(question, context)


result = rag("What about sonata-extra ?")
print(result)

result = rag("Who wrote sonata-extra ?")
print(result)

Và đây là câu trả lời cho câu hỏi đầu tiên:

{
  "id": "cmpl-63ebabf2-ec6b-4a0e-a0ae-4433c2df6ece",
  "object": "text_completion",
  "created": 1702485091,
  "model": "/Data/Projets/Llm/Models/openchat_3.5.Q2_K.gguf",
  "choices": [
    {
      "text": "\nThe Sonata-Extra Bundle is an extension to Symfony that enhances your experience with additional functionalities such as Activity Log, Approval Workflow, Assets Management, Blog integration, Content Security Policy management, Header Redirect Manager, Language Switcher, Multisite and multilingual support for SonataPageBundle, Sitemap generation, Smart services (AI-powered), WordPress import, Cookie Consent Block, Gutenberg Editor Integration, FAQ manager, Article manager with Gutenberg editor, additional form types, and more.\n\nThe bundle provides features like automatic translation through smart service functionality, integration of the Gutenberg editor for content creation, cookie consent management in compliance with GDPR regulations, and efficient loading of assets only when necessary. It also offers a flexible way to manage CSS and JavaScript assets in Sonata blocks, allowing developers to include external files or inline styles and scripts easily.\n\nTo use these features, you need to inject the required services into your block service using autowireDependencies method. Then, add assets to your block by calling methods like addCss, addJs, addJsInline, and addCssInline. To render the assets in Twig templates, use the provided functions such as sonata_extra_get_blocks_css('default'), sonata_extra_get_blocks_js('default'), etc., with custom indexes for grouping assets when developing custom blocks.",
      "index": 0,
      "logprobs": "None",
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 2560,
    "completion_tokens": 299,
    "total_tokens": 2859
  }
}

Câu trả lời cho câu hỏi thứ hai:

{
  "id": "cmpl-cf77a2a2-de5b-45ca-905e-a11094a805aa",
  "object": "text_completion",
  "created": 1702485287,
  "model": "/Data/Projets/Llm/Models/openchat_3.5.Q2_K.gguf",
  "choices": [
    {
      "text": "\\nThe authors of the Sonata-extra bundle are Geraud Bourdin and Thomas Bourdin. They work for partITech, a company that specializes in Symfony, Sonata, and other technologies to enhance digital experiences. The context provided does not mention any specific individual who wrote \"sonata-extra.\"",
      "index": 0,
      "logprobs": "None",
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 1360,
    "completion_tokens": 67,
    "total_tokens": 1427
  }
}

Như ta có thể thấy, các câu trả lời hoàn toàn phù hợp với nội dung mà chúng ta đã lập chỉ mục. Do đó, ta có thể dễ dàng hình dung cách một hệ thống như vậy có thể cải thiện tài liệu doanh nghiệp. Đúng vậy, chỉ với như vậy, chúng ta đã có khả năng lập chỉ mục các nội dung như hình ảnh, tài liệu Word, PDF... Miễn là dữ liệu được tổ chức tốt, chúng ta có thể tích hợp chúng một cách dễ dàng vào hệ thống thông tin của bạn.

Txtai thật sự tuyệt vời nếu xét đến số ít dòng mã đã cần thiết để tạo ra điều này. Bây giờ phải kết hợp nó với một hệ thống chat. Thật may, nhà phát triển của txtai cũng đã viết một công cụ, txtchat. Cần phải xem xét cách tổ chức tất cả. Trong một bài viết khác, chắc chắn là vậy.

Trong phần tiếp theo của chúng ta, chúng ta sẽ xem cách làm cùng một việc trực tiếp với LangChain, với cơ sở dữ liệu Postgresql để chứa tất cả các embeddings. Không cần các tệp FAISS nữa. Chúng ta sẽ thực hiện điều này với Python và LangChain, và sau đó là với JavaScript và LangChain. Vâng, thế giới này cũng đang mở ra cho các nhà phát triển web.