====== Ollama ======
===== Instalacja =====
dnf install ollama
===== Uruchomienie =====
Mój przykład to uruchomienie na laptopie HP Victus z kartą graficzną NVIDIA GeForce RTX 3050.
Na jednej sesji terminala:
export HIP_VISIBLE_DEVICES=
export CUDA_VISIBLE_DEVICES=0
export OLLAMA_LLM_LIBRARY=cuda
ollama serve
Na innej sesji terminala:
Pobranie modelu np llama3:8b i to polecenie wykonujemy tylko raz.
ollama push llama3:8b
Uruchomienie pobranego modelu
ollama run llama3:8b
Pobranie qwen
ollama pull qwen2.5-coder:7b
===== Ciekawe modele =====
ollama list
NAME ID SIZE MODIFIED
qwen2.5-coder:7b dae161e27b0e 4.7 GB 2 hours ago
deepseek-coder:6.7b ce298d984115 3.8 GB 10 months ago
llama3:8b 365c0bd3c000 4.7 GB 10 months ago
deepseek-coder:1.3b 3ddd2d3fc8d2 776 MB 10 months ago
===== Pamiętanie sesji =====
Aby po ponownym uruchomieniu modelu nie zaczynać od początku to po zakończeniu pracy zanim wykonamy polecenie ''/bye'' należy wykonać ''/save nazwa_sesji''. Jeżeli będziemy chcieli do niej powrócić to nie wykonujemy polecenia ''ollama run qwen2.5-coder:7b'' tylko ''ollama run nazwa_sesji''. Jeżeli przed zakończeniem sesji ponownie wykonamy ''/save nazwa_sesji'' to ''konwersacja'' zostanie dołączona do poprzedniej.
===== Agenci =====
python -m venv agent_llm
source agent_llm/bin/activate
pip install langchain-ollama rank_bm25
==== Agent testowy ====
from langchain_ollama import ChatOllama
from langchain_core.messages import HumanMessage
# Wybieramy model bez slashy w nazwie
model_name = "qwen2.5-coder:7b"
print(f"Łączenie z modelem: {model_name}...")
llm = ChatOllama(model=model_name)
# Zadanie pytania
response = llm.invoke([HumanMessage(content="Cześć! Powiedz mi krótko, w czym możesz mi pomóc?")])
print("\nOdpowiedź modelu:")
print(response.content)
python test_agent.py
==== Agent bazujący na treści z konkretnego pliku pdf ====
from langchain_ollama import ChatOllama, OllamaEmbeddings
from langchain_core.messages import HumanMessage
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_chroma import Chroma
import os
pdf_path = "pdf/bialy montaz elektryczny.pdf"
if not os.path.exists(pdf_path):
print(f"Błąd: Nie znaleziono pliku {pdf_path}!")
exit()
print("--- INICJALIZACJA AGENTA ---")
print(f"1. Ładowanie pliku PDF ({pdf_path})...")
loader = PyPDFLoader(pdf_path)
docs = loader.load()
print(f"2. Dzielenie dokumentu ({len(docs)} stron) na fragmenty...")
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
splits = text_splitter.split_documents(docs)
print("3. Tworzenie lokalnej bazy wektorowej (to może chwilę potrwać)...")
embeddings = OllamaEmbeddings(model="qwen2.5-coder:7b")
vectorstore = Chroma.from_documents(documents=splits, embedding=embeddings)
retriever = vectorstore.as_retriever()
llm = ChatOllama(model="qwen2.5-coder:7b")
print("\nGotowe! Agent jest gotowy do rozmowy.")
print("Wpisz swoje pytanie do dokumentu (lub wpisz 'exit', aby zakończyć).\n")
# Pętla konwersacji
while True:
try:
pytanie = input("Ty: ")
if not pytanie.strip():
continue
if pytanie.lower() in ["exit", "wyjscie", "koniec", "q"]:
print("Do zobaczenia!")
break
# Szukamy fragmentów pasujących do pytania
relevant_docs = retriever.invoke(pytanie)
kontekst = "\n\n".join([doc.page_content for doc in relevant_docs])
# Tworzymy prompt dla modelu
prompt = f"""Odpowiedz na pytanie bazując WYŁĄCZNIE na poniższym fragmencie dokumentu PDF.
Jeśli nie masz tej informacji w tekście, napisz, że jej nie znasz.
Tekst z dokumentu PDF:
{kontekst}
Pytanie: {pytanie}
"""
response = llm.invoke([HumanMessage(content=prompt)])
print(f"\nAgent: {response.content}\n" + "-"*40)
except KeyboardInterrupt:
print("\nDo zobaczenia!")
break
==== Agent bazujący na treści z plików pdf z podkatalogu pdf ====
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)
from langchain_ollama import ChatOllama, OllamaEmbeddings
from langchain_core.messages import HumanMessage
from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_chroma import Chroma
from langchain_community.retrievers import BM25Retriever
import os
folder_path = "pdf"
if not os.path.exists(folder_path):
print(f"Błąd: Nie znaleziono folderu '{folder_path}'!")
exit()
print("--- INICJALIZACJA AGENTA HYBRYDOWEGO ---")
print(f"1. Ładowanie dokumentów z folderu '{folder_path}'...")
loader = DirectoryLoader(folder_path, glob="**/*.pdf", loader_cls=PyPDFLoader)
docs = loader.load()
if not docs:
print("Błąd: Brak plików PDF!")
exit()
print(f"Załadowano łącznie {len(docs)} stron.")
print("2. Dzielenie dokumentów na fragmenty...")
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
splits = text_splitter.split_documents(docs)
print("3. Tworzenie bazy wektorowej oraz indeksu słów kluczowych...")
embeddings = OllamaEmbeddings(model="qwen2.5-coder:7b")
vectorstore = Chroma.from_documents(documents=splits, embedding=embeddings)
chroma_retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
# Indeks słów kluczowych (BM25) do dokładnych numerów i indeksów
bm25_retriever = BM25Retriever.from_documents(splits)
bm25_retriever.k = 5
# Własna funkcja łącząca oba podejścia (hybryda)
def hybrid_retrieve(query, k=6):
docs_chroma = chroma_retriever.invoke(query)
docs_bm25 = bm25_retriever.invoke(query)
# Łączymy wyniki, usuwając duplikaty po treści
seen = set()
combined = []
for doc in docs_bm25 + docs_chroma:
if doc.page_content not in seen:
seen.add(doc.page_content)
combined.append(doc)
return combined[:k]
llm = ChatOllama(model="qwen2.5-coder:7b")
print("\nGotowe! Agent hybrydowy czeka na pytania.")
print("Wpisz swoje pytanie (lub 'exit', aby zakończyć).\n")
while True:
try:
pytanie = input("Ty: ")
if not pytanie.strip():
continue
if pytanie.lower() in ["exit", "wyjscie", "koniec", "q"]:
print("Do zobaczenia!")
break
# Pobieramy dokumenty hybrydowo
relevant_docs = hybrid_retrieve(pytanie)
kontekst = "\n\n".join([doc.page_content for doc in relevant_docs])
prompt = f"""Odpowiedz na pytanie bazując WYŁĄCZNIE na poniższych fragmentach dokumentów.
Jeśli nie masz tej informacji w tekstach, napisz, że jej nie znasz.
Teksty źródłowe:
{kontekst}
Pytanie: {pytanie}
"""
response = llm.invoke([HumanMessage(content=prompt)])
print(f"\nAgent: {response.content}\n" + "-"*40)
except KeyboardInterrupt:
print("\nDo zobaczenia!")
break
===== Ollama w Podman =====
sudo dnf install -y nvidia-container-toolkit
sudo mkdir -p /etc/cdi
sudo nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml
nvidia-ctk cdi list
INFO[0000] Found 3 CDI devices
nvidia.com/gpu=0
nvidia.com/gpu=GPU-f7c3e57b-e151-f70d-85ed-1727440608be
nvidia.com/gpu=all
podman run --rm --security-opt=label=disable --device=nvidia.com/gpu=all docker.io/nvidia/cuda:13.0.0-base-ubuntu24.04 nvidia-smi
podman run -d --name ollama --device=nvidia.com/gpu=all --security-opt=label=disable -p 127.0.0.1:11434:11434 -v "$HOME/.ollama:/root/.ollama" docker.io/ollama/ollama:latest
podman exec -it ollama ollama list
NAME ID SIZE MODIFIED
SpeakLeash/bielik-11b-v2.3-instruct:Q4_K_M b0dc93a20fdb 6.7 GB 3 weeks ago
qwen:latest 46be3b9ea9a0 4.7 GB 5 weeks ago
qwen2.5-coder:7b dae161e27b0e 4.7 GB 5 weeks ago
deepseek-coder:6.7b ce298d984115 3.8 GB 11 months ago
llama3:8b 365c0bd3c000 4.7 GB 11 months ago
deepseek-coder:1.3b 3ddd2d3fc8d2 776 MB 11 months ago
podman exec -it ollama ollama run deepseek-coder:1.3b
Jeżeli rozmiar modelu jest mniejszy niż rozmiar pamięci GPU to model korzysta z GPU.
podman exec ollama ollama ps
NAME ID SIZE PROCESSOR CONTEXT RUNNER UNTIL
deepseek-coder:1.3b 3ddd2d3fc8d2 1.6 GB 100% GPU 4096 llamacpp 3 minutes from now
Przy większych modelach obliczenia są realizowane przez GPU i CPU
podman exec ollama ollama ps
NAME ID SIZE PROCESSOR CONTEXT RUNNER UNTIL
SpeakLeash/bielik-11b-v2.3-instruct:Q4_K_M b0dc93a20fdb 4.0 GB 32%/68% CPU/GPU 4096 llamacpp 4 minutes from now