====== 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