jaczad commited on
Commit
0e2081a
·
1 Parent(s): ca81217

Aplikacja tworzy bazę po uruchomieniu

Browse files
Files changed (4) hide show
  1. app.py +45 -142
  2. chat_utils.py +87 -0
  3. database_setup.py +159 -0
  4. scrap.py +2 -0
app.py CHANGED
@@ -1,87 +1,20 @@
1
- import gradio as gr
2
  import uuid
3
- from langchain_chroma import Chroma
4
- from langchain_openai import OpenAIEmbeddings, ChatOpenAI
5
- from langchain.chains import create_history_aware_retriever, create_retrieval_chain
6
- from langchain.chains.combine_documents import create_stuff_documents_chain
7
- from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
8
- from langchain_core.chat_history import BaseChatMessageHistory
9
- from langchain_community.chat_message_histories import ChatMessageHistory
10
- from langchain_core.runnables.history import RunnableWithMessageHistory
11
-
12
- # --- 1. Inicjalizacja modeli i retrievera ---
13
- # Inicjalizacja modelu językowego oraz embeddera
14
- llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.0)
15
- embedder = OpenAIEmbeddings(model="text-embedding-3-small")
16
- # Inicjalizacja bazy Chroma z embeddingami
17
- baza = Chroma(
18
- collection_name="szuflada",
19
- embedding_function=embedder,
20
- persist_directory="./szuflada"
21
- )
22
- # Możliwe typy wyszukiwania w retrieverze Chroma:
23
- # - "similarity" (domyślne, bez progu)
24
- # - "mmr" (Maximal Marginal Relevance)
25
- # - "similarity_score_threshold" (z progiem score_threshold)
26
-
27
- # Retriever do wyszukiwania podobnych fragmentów
28
- retriever = baza.as_retriever(
29
- search_kwargs={"k": 3}
30
- )
31
-
32
- # --- 2. Tworzenie łańcucha RAG z historią ---
33
- # Prompt do kontekstualizacji pytania na podstawie historii czatu
34
- contextualize_q_system_prompt = (
35
- "Biorąc pod uwagę historię czatu i ostatnie pytanie użytkownika, "
36
- "które może odnosić się do kontekstu w historii czatu, "
37
- "sformułuj samodzielne pytanie, które można zrozumieć bez historii czatu. "
38
- "NIE odpowiadaj na pytanie, po prostu przeformułuj je, jeśli to konieczne, "
39
- "a w przeciwnym razie zwróć je w niezmienionej formie."
40
- )
41
- contextualize_q_prompt = ChatPromptTemplate.from_messages(
42
- [
43
- ("system", contextualize_q_system_prompt),
44
- MessagesPlaceholder("chat_history"),
45
- ("human", "{input}"),
46
- ]
47
- )
48
- # Łańcuch do kontekstualizacji pytania z historią
49
- history_aware_retriever = create_history_aware_retriever(
50
- llm, retriever, contextualize_q_prompt
51
- )
52
 
53
- # Prompt do generowania odpowiedzi na podstawie kontekstu
54
- qa_system_prompt = (
55
- "Jesteś asystentem do zadawania pytań i odpowiedzi na temat treści ze strony mojaszuflada.pl. "
56
- "Użyj poniższych fragmentów odzyskanego kontekstu, aby odpowiedzieć na pytanie. "
57
- "Odpowiadaj zawsze w języku polskim. "
58
- "Jeśli nie znasz odpowiedzi, po prostu powiedz, że tego nie wiesz. "
59
- "Zachowaj zwięzłość odpowiedzi, ale bądź pomocny i przyjazny."
60
- "\n\n{context}"
61
- )
62
- qa_prompt = ChatPromptTemplate.from_messages(
63
- [
64
- ("system", qa_system_prompt),
65
- MessagesPlaceholder("chat_history"),
66
- ("human", "{input}"),
67
- ]
68
- )
69
- # Łańcuch generujący odpowiedź na podstawie dokumentów
70
- question_answer_chain = create_stuff_documents_chain(llm, qa_prompt)
71
-
72
- # Połączenie retrievera z generatorem odpowiedzi
73
- rag_chain = create_retrieval_chain(history_aware_retriever, question_answer_chain)
74
 
75
- # Słownik do przechowywania historii czatu dla sesji
76
- store = {}
 
77
 
78
- def get_session_history(session_id: str) -> BaseChatMessageHistory:
79
- # Zwraca historię czatu dla danej sesji (tworzy jeśli nie istnieje)
80
- if session_id not in store:
81
- store[session_id] = ChatMessageHistory()
82
- return store[session_id]
83
 
84
- # Łańcuch RAG z obsługą historii wiadomości
 
85
  conversational_rag_chain = RunnableWithMessageHistory(
86
  rag_chain,
87
  get_session_history,
@@ -90,42 +23,41 @@ conversational_rag_chain = RunnableWithMessageHistory(
90
  output_messages_key="answer",
91
  )
92
 
93
- # --- 3. Funkcje pomocnicze dla Gradio ---
94
- def format_sources(source_docs):
95
- # Formatuje listę źródeł do wyświetlenia w odpowiedzi
96
- if not source_docs:
97
- return "?"
98
-
99
- sources = []
100
- for doc in source_docs:
101
- metadata = doc.metadata
102
- title = metadata.get("title", "Brak tytułu")
103
- source_url = metadata.get("source", "Brak URL")
104
-
105
- pub_date_raw = metadata.get("published_time")
106
- if pub_date_raw:
107
- pub_date = pub_date_raw.split("T")[0]
108
- sources.append(f"- [{title}]({source_url}) ({pub_date})")
109
- else:
110
- sources.append(f"- [{title}]({source_url})")
111
- return "\n".join(sources)
 
 
 
 
 
 
112
 
113
- # --- 4. Budowa interfejsu Gradio ---
114
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue"), title="Szuflada Chatbot") as demo:
115
  session_id = gr.State(lambda: str(uuid.uuid4()))
116
 
117
  gr.Markdown(
118
- """# Czat z Moją Szufladą\n### Zadaj pytanie na temat treści ze strony [mojaszuflada.pl](https://mojaszuflada.pl)
119
- """
120
  )
 
121
 
122
- # Komponent czatu
123
- chatbot = gr.Chatbot(
124
- label="Rozmowa",
125
- height=500,
126
- )
127
-
128
- # Pole tekstowe i przycisk do wysyłania wiadomości
129
  with gr.Row():
130
  msg = gr.Textbox(
131
  show_label=False,
@@ -135,39 +67,10 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue"), title="Szuflada Chatbot
135
  )
136
  submit_btn = gr.Button("Wyślij", variant="primary", scale=1)
137
 
138
- # Funkcja obsługująca odpowiedź na wiadomość użytkownika
139
- def respond(message, chat_history, sess_id):
140
- # Wywołanie łańcucha RAG z historią
141
- response = conversational_rag_chain.invoke(
142
- {"input": message},
143
- config={"configurable": {"session_id": sess_id}},
144
- )
145
- context_docs = response.get("context", [])
146
- # Debug: wypisz poziom podobieństwa dla znalezionych chunków
147
- debug_scores = baza.similarity_search_with_score(message, k=len(context_docs))
148
- for i, (doc, score) in enumerate(debug_scores):
149
- print(f"Chunk {i+1}: similarity_score={score}, title={doc.metadata.get('title')}")
150
- sources_md = format_sources(context_docs)
151
- answer = response.get("answer") or ""
152
- answer_with_sources = answer + "\n\n**Źródła:**\n" + sources_md
153
- chat_history.append((message, answer_with_sources))
154
- return chat_history
155
-
156
- # Obsługa kliknięcia przycisku "Wyślij"
157
- submit_btn.click(
158
- respond,
159
- [msg, chatbot, session_id],
160
- [chatbot]
161
- ).then(lambda: gr.update(value=""), None, [msg], queue=False)
162
-
163
- # Obsługa wysłania wiadomości enterem
164
- msg.submit(
165
- respond,
166
- [msg, chatbot, session_id],
167
- [chatbot]
168
- ).then(lambda: gr.update(value=""), None, [msg], queue=False)
169
 
170
- # --- 5. Uruchomienie aplikacji ---
171
  if __name__ == "__main__":
172
- # Uruchom aplikację Gradio z publicznym linkiem
173
- demo.launch(inbrowser=True)
 
1
+ import sys
2
  import uuid
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
+ import gradio as gr
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
+ from database_setup import initialize_database
7
+ from chat_utils import create_rag_chain, format_sources, create_session_history_manager
8
+ from langchain_core.runnables.history import RunnableWithMessageHistory
9
 
10
+ print("Inicjalizacja bazy danych...")
11
+ baza = initialize_database()
12
+ if baza is None:
13
+ print("Nie udało się zainicjalizować bazy danych. Zakończenie pracy.")
14
+ sys.exit(1)
15
 
16
+ rag_chain = create_rag_chain(baza)
17
+ get_session_history = create_session_history_manager()
18
  conversational_rag_chain = RunnableWithMessageHistory(
19
  rag_chain,
20
  get_session_history,
 
23
  output_messages_key="answer",
24
  )
25
 
26
+ def respond(message, chat_history, sess_id):
27
+ """Obsługuje odpowiedź na wiadomość użytkownika."""
28
+ try:
29
+ response = conversational_rag_chain.invoke(
30
+ {"input": message},
31
+ config={"configurable": {"session_id": sess_id}},
32
+ )
33
+ except Exception as e:
34
+ chat_history.append((message, f"Błąd podczas przetwarzania: {e}"))
35
+ return chat_history
36
+
37
+ context_docs = response.get("context", [])
38
+ # Debug: wypisz poziom podobieństwa
39
+ try:
40
+ debug_scores = baza.similarity_search_with_score(message, k=len(context_docs))
41
+ for i, (doc, score) in enumerate(debug_scores):
42
+ print(f"Chunk {i+1}: similarity_score={score}, title={doc.metadata.get('title')}")
43
+ except Exception:
44
+ pass
45
+
46
+ sources_md = format_sources(context_docs)
47
+ answer = response.get("answer") or ""
48
+ answer_with_sources = f"{answer}\n\n**Źródła:**\n{sources_md}"
49
+ chat_history.append((message, answer_with_sources))
50
+ return chat_history
51
 
 
52
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue"), title="Szuflada Chatbot") as demo:
53
  session_id = gr.State(lambda: str(uuid.uuid4()))
54
 
55
  gr.Markdown(
56
+ "# Czat z Moją Szufladą\n"
57
+ "### Zadaj pytanie na temat treści ze strony [mojaszuflada.pl](https://mojaszuflada.pl)"
58
  )
59
+ chatbot = gr.Chatbot(label="Rozmowa", height=500)
60
 
 
 
 
 
 
 
 
61
  with gr.Row():
62
  msg = gr.Textbox(
63
  show_label=False,
 
67
  )
68
  submit_btn = gr.Button("Wyślij", variant="primary", scale=1)
69
 
70
+ submit_btn.click(respond, [msg, chatbot, session_id], [chatbot]) \
71
+ .then(lambda: gr.update(value=""), None, [msg], queue=False)
72
+ msg.submit(respond, [msg, chatbot, session_id], [chatbot]) \
73
+ .then(lambda: gr.update(value=""), None, [msg], queue=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
 
75
  if __name__ == "__main__":
76
+ demo.launch(inbrowser=True)
 
chat_utils.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_chroma import Chroma
2
+ from langchain_openai import OpenAIEmbeddings, ChatOpenAI
3
+ from langchain.chains import create_history_aware_retriever, create_retrieval_chain
4
+ from langchain.chains.combine_documents import create_stuff_documents_chain
5
+ from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
6
+ from langchain_core.chat_history import BaseChatMessageHistory
7
+ from langchain_community.chat_message_histories import ChatMessageHistory
8
+ from langchain_core.runnables.history import RunnableWithMessageHistory
9
+
10
+ def create_rag_chain(database: Chroma):
11
+ """
12
+ Tworzy łańcuch RAG z obsługą historii konwersacji.
13
+ """
14
+ llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.0)
15
+ retriever = database.as_retriever(search_kwargs={"k": 3})
16
+
17
+ # Prompt do kontekstualizacji pytania
18
+ contextualize_q_system_prompt = (
19
+ "Biorąc pod uwagę historię czatu i ostatnie pytanie użytkownika, "
20
+ "które może odnosić się do kontekstu w historii czatu, "
21
+ "sformułuj samodzielne pytanie, które można zrozumieć bez historii czatu. "
22
+ "NIE odpowiadaj na pytanie, po prostu przeformułuj je, jeśli to konieczne, "
23
+ "a w przeciwnym razie zwróć je w niezmienionej formie."
24
+ )
25
+ contextualize_q_prompt = ChatPromptTemplate.from_messages([
26
+ ("system", contextualize_q_system_prompt),
27
+ MessagesPlaceholder("chat_history"),
28
+ ("human", "{input}"),
29
+ ])
30
+
31
+ history_aware_retriever = create_history_aware_retriever(
32
+ llm, retriever, contextualize_q_prompt
33
+ )
34
+
35
+ # Prompt do generowania odpowiedzi
36
+ qa_system_prompt = (
37
+ "Jesteś asystentem do zadawania pytań i odpowiedzi na temat treści ze strony mojaszuflada.pl. "
38
+ "Użyj poniższych fragmentów odzyskanego kontekstu, aby odpowiedzieć na pytanie. "
39
+ "Odpowiadaj zawsze w języku polskim. "
40
+ "Jeśli nie znasz odpowiedzi, po prostu powiedz, że tego nie wiesz. "
41
+ "Zachowaj zwięzłość odpowiedzi, ale bądź pomocny i przyjazny."
42
+ "\n\n{context}"
43
+ )
44
+ qa_prompt = ChatPromptTemplate.from_messages([
45
+ ("system", qa_system_prompt),
46
+ MessagesPlaceholder("chat_history"),
47
+ ("human", "{input}"),
48
+ ])
49
+
50
+ question_answer_chain = create_stuff_documents_chain(llm, qa_prompt)
51
+ rag_chain = create_retrieval_chain(history_aware_retriever, question_answer_chain)
52
+
53
+ return rag_chain
54
+
55
+ def format_sources(source_docs):
56
+ """
57
+ Formatuje listę źródeł do wyświetlenia w odpowiedzi.
58
+ """
59
+ if not source_docs:
60
+ return "?"
61
+
62
+ sources = []
63
+ for doc in source_docs:
64
+ metadata = doc.metadata
65
+ title = metadata.get("title", "Brak tytułu")
66
+ source_url = metadata.get("source", "Brak URL")
67
+
68
+ pub_date_raw = metadata.get("published_time")
69
+ if pub_date_raw:
70
+ pub_date = pub_date_raw.split("T")[0]
71
+ sources.append(f"- [{title}]({source_url}) ({pub_date})")
72
+ else:
73
+ sources.append(f"- [{title}]({source_url})")
74
+ return "\n".join(sources)
75
+
76
+ def create_session_history_manager():
77
+ """
78
+ Tworzy menedżer historii sesji.
79
+ """
80
+ store = {}
81
+
82
+ def get_session_history(session_id: str) -> BaseChatMessageHistory:
83
+ if session_id not in store:
84
+ store[session_id] = ChatMessageHistory()
85
+ return store[session_id]
86
+
87
+ return get_session_history
database_setup.py ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from bs4 import BeautifulSoup
2
+ import re
3
+ from langchain_chroma import Chroma
4
+ from langchain_openai import OpenAIEmbeddings
5
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
6
+ from langchain_core.documents import Document
7
+ import requests
8
+ from tqdm import tqdm
9
+
10
+ def process_documents(docs: list[Document]) -> list[Document]:
11
+ """
12
+ Przetwarza listę dokumentów, wyodrębniając treść i metadane z HTML.
13
+ """
14
+ processed_docs = []
15
+ for doc in docs:
16
+ soup = BeautifulSoup(doc.page_content, "lxml")
17
+
18
+ # Wyodrębnienie głównej treści
19
+ article = soup.find("article")
20
+ if article:
21
+ content = article.get_text(separator="\n", strip=True)
22
+ else:
23
+ content = soup.get_text(separator="\n", strip=True)
24
+
25
+ # Wyodrębnienie metadanych
26
+ metadata = doc.metadata.copy()
27
+
28
+ # Title ze znacznika <title>
29
+ if soup.title:
30
+ title_text = soup.title.get_text(strip=True)
31
+ if title_text:
32
+ metadata["title"] = title_text
33
+
34
+ # Data publikacji
35
+ pub_date_tag = soup.find("meta", property="article:published_time")
36
+ if pub_date_tag and pub_date_tag.get("content"):
37
+ metadata["published_time"] = pub_date_tag["content"]
38
+ else:
39
+ time_tag = soup.find("time")
40
+ if time_tag and time_tag.get("datetime"):
41
+ metadata["published_time"] = time_tag.get("datetime")
42
+ elif time_tag and time_tag.get_text(strip=True):
43
+ metadata["published_time"] = time_tag.get_text(strip=True)
44
+ else:
45
+ text = soup.get_text(separator="\n", strip=True)
46
+ m = re.search(r"Opublikowano(?: w dniu)?[:\s]+([0-9]{1,2}\s+\w+\s+\d{4})", text, re.IGNORECASE)
47
+ if m:
48
+ metadata["published_time"] = m.group(1)
49
+
50
+ # Kategorie
51
+ categories = [
52
+ tag["content"]
53
+ for tag in soup.find_all("meta", property="article:section")
54
+ if tag.get("content")
55
+ ]
56
+ if categories:
57
+ metadata["categories"] = ", ".join(categories)
58
+
59
+ # Słowa kluczowe
60
+ keywords = [
61
+ tag["content"]
62
+ for tag in soup.find_all("meta", property="article:tag")
63
+ if tag.get("content")
64
+ ]
65
+ if keywords:
66
+ metadata["keywords"] = ", ".join(keywords)
67
+
68
+ processed_docs.append(Document(page_content=content, metadata=metadata))
69
+ return processed_docs
70
+
71
+ def initialize_database(persist_directory="./szuflada", clear_existing=True):
72
+ """
73
+ Inicjalizuje bazę danych Chroma z danymi ze strony mojaszuflada.pl
74
+ """
75
+ embedder = OpenAIEmbeddings(model="text-embedding-3-small", show_progress_bar=True)
76
+ baza = Chroma(collection_name="szuflada", embedding_function=embedder, persist_directory=persist_directory)
77
+
78
+ if clear_existing:
79
+ print("Czyszczenie istniejącej kolekcji w bazie danych...")
80
+ try:
81
+ baza.delete_collection()
82
+ print("Kolekcja została wyczyszczona.")
83
+ baza = Chroma(collection_name="szuflada", embedding_function=embedder, persist_directory=persist_directory)
84
+ except Exception as e:
85
+ print(f"Nie można było wyczyścić kolekcji (może nie istniała): {e}")
86
+
87
+ print("Pobieranie i parsowanie mapy strony...")
88
+ headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'}
89
+ sitemap_url = "https://mojaszuflada.pl/wp-sitemap.xml"
90
+ docs = []
91
+
92
+ try:
93
+ response = requests.get(sitemap_url, headers=headers)
94
+ response.raise_for_status()
95
+ sitemap_xml = response.text
96
+
97
+ sitemap_soup = BeautifulSoup(sitemap_xml, "xml")
98
+ urls = [loc.text for loc in sitemap_soup.find_all("loc")]
99
+
100
+ sitemap_urls = [url for url in urls if url.endswith(".xml")]
101
+ page_urls = [url for url in urls if not url.endswith(".xml")]
102
+
103
+ for sub_sitemap_url in tqdm(sitemap_urls, desc="Parsowanie pod-map"):
104
+ try:
105
+ response = requests.get(sub_sitemap_url, headers=headers)
106
+ response.raise_for_status()
107
+ sub_sitemap_xml = response.text
108
+ sub_sitemap_soup = BeautifulSoup(sub_sitemap_xml, "xml")
109
+ page_urls.extend([loc.text for loc in sub_sitemap_soup.find_all("loc")])
110
+ except requests.RequestException as e:
111
+ print(f"Pominięto pod-mapę {sub_sitemap_url}: {e}")
112
+
113
+ print(f"Znaleziono {len(page_urls)} adresów URL do przetworzenia.")
114
+
115
+ for url in tqdm(page_urls, desc="Pobieranie stron"):
116
+ try:
117
+ response = requests.get(url, headers=headers)
118
+ response.raise_for_status()
119
+ doc = Document(
120
+ page_content=response.text,
121
+ metadata={"source": url, "loc": url}
122
+ )
123
+ docs.append(doc)
124
+ except requests.RequestException as e:
125
+ print(f"Pominięto stronę {url}: {e}")
126
+
127
+ except requests.RequestException as e:
128
+ print(f"Krytyczny błąd: Nie udało się pobrać głównej mapy strony: {e}")
129
+
130
+ if not docs:
131
+ print("Nie załadowano żadnych dokumentów.")
132
+ return None
133
+
134
+ processed_docs = process_documents(docs)
135
+ print(f"\nPrzetworzono {len(processed_docs)} dokumentów.")
136
+
137
+ text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
138
+ chunks = text_splitter.split_documents(processed_docs)
139
+
140
+ # Walidacja metadanych
141
+ required_meta_keys = ["source", "title", "published_time"]
142
+ missing_counts = {k: 0 for k in required_meta_keys}
143
+ for chunk in chunks:
144
+ md = chunk.metadata or {}
145
+ for k in required_meta_keys:
146
+ if not md.get(k):
147
+ missing_counts[k] += 1
148
+
149
+ print(f"Liczba chunków: {len(chunks)}")
150
+ print("Braki metadanych:", missing_counts)
151
+
152
+ # Dodawanie chunków do bazy
153
+ batch_size = 1000
154
+ for i in range(0, len(chunks), batch_size):
155
+ baza.add_documents(documents=chunks[i:i + batch_size])
156
+
157
+ print("Baza danych została zainicjalizowana pomyślnie.")
158
+ return baza
159
+ return baza
scrap.py CHANGED
@@ -1,3 +1,4 @@
 
1
  from langchain_community.document_loaders import SitemapLoader
2
  from bs4 import BeautifulSoup
3
  import re
@@ -173,3 +174,4 @@ for sample in chunks[:5]:
173
 
174
  for i in range(0, len(chunks), batch_size):
175
  baza.add_documents(documents=chunks[i:i + batch_size])
 
 
1
+ # Ten plik jest odpowiedzialny za scrapowanie danych ze strony.
2
  from langchain_community.document_loaders import SitemapLoader
3
  from bs4 import BeautifulSoup
4
  import re
 
174
 
175
  for i in range(0, len(chunks), batch_size):
176
  baza.add_documents(documents=chunks[i:i + batch_size])
177
+