Files
dashboard-cpsp/AI Insight/query_rag.py
T

166 lines
6.7 KiB
Python

"""
CLI / local-dev helper to query Chroma + an OpenAI-compatible LLM (e.g. LM Studio).
NOT used by the dashboard runtime. Production/lab path is:
Django insight_service → HTTP → rag_service.py (/query) → Ollama from Django.
Prefer `rag_service.py` + `populate_db.py` when testing what the app actually calls.
"""
import os
import sys
import requests
from dotenv import load_dotenv
import chromadb
from sentence_transformers import SentenceTransformer
# Set offline mode agar sentence-transformers tidak mencoba menghubungi Hugging Face di jaringan on-premise
os.environ["HF_HUB_OFFLINE"] = "1"
# Load environment variables
load_dotenv()
CHROMA_DB_DIR = os.getenv("CHROMA_DB_DIR", "chroma_db")
EMBEDDING_MODEL_NAME = os.getenv("EMBEDDING_MODEL_NAME", "all-MiniLM-L6-v2")
OPENAI_BASE_URL = os.getenv("OPENAI_BASE_URL", "http://localhost:1234/v1")
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "lm-studio")
LLM_MODEL_NAME = os.getenv("LLM_MODEL_NAME", "qwen2.5:7b")
def main():
# 1. Inisialisasi Database Vektor
if not os.path.exists(CHROMA_DB_DIR):
print(f"Database Chroma DB di '{CHROMA_DB_DIR}' tidak ditemukan. Silakan jalankan extract_text.py dan populate_db.py terlebih dahulu.")
return
print("Menghubungkan ke Chroma DB...")
chroma_client = chromadb.PersistentClient(path=CHROMA_DB_DIR)
try:
collection = chroma_client.get_collection(name="company_sop")
except Exception as e:
print(f"Koleksi 'company_sop' tidak ditemukan di database. Pastikan populate_db.py sudah dijalankan dengan sukses. Error: {e}")
return
# 2. Inisialisasi Model Embedding lokal
print(f"Memuat model embedding lokal '{EMBEDDING_MODEL_NAME}' untuk kueri...")
embedding_model = SentenceTransformer(EMBEDDING_MODEL_NAME)
print("Model embedding berhasil dimuat.")
# 3. Konfigurasi koneksi LM Studio native v1 API
LM_STUDIO_API_URL = os.getenv("LM_STUDIO_API_URL")
if not LM_STUDIO_API_URL:
openai_base = os.getenv("OPENAI_BASE_URL", "http://localhost:1234/v1")
if openai_base.endswith("/v1"):
LM_STUDIO_API_URL = openai_base.replace("/v1", "/api/v1/chat")
else:
LM_STUDIO_API_URL = f"{openai_base.rstrip('/')}/api/v1/chat"
print(f"Mengonfigurasi koneksi LLM ke native API: {LM_STUDIO_API_URL} (Model: {LLM_MODEL_NAME})...")
print("\n" + "="*60)
print(" PIPELINE RAG LOKAL - ASISTEN SOP PERUSAHAAN (QWEN 2.5)")
print(" Ketik 'keluar' atau 'exit' untuk menyudahi percakapan.")
print("="*60 + "\n")
while True:
try:
query = input("\nPertanyaan Anda: ").strip()
if not query:
continue
if query.lower() in ["keluar", "exit", "q", "quit"]:
print("Sampai jumpa!")
break
print("\n[1/3] Mencari dokumen referensi relevan di database lokal...", end="", flush=True)
# Buat embedding kueri
query_embedding = embedding_model.encode([query])[0].tolist()
# Cari kueri di Chroma DB (ambil 6 chunk teratas)
results = collection.query(
query_embeddings=[query_embedding],
n_results=6
)
print(" Selesai!")
retrieved_chunks = results['documents'][0]
retrieved_metadatas = results['metadatas'][0]
if not retrieved_chunks or len(retrieved_chunks) == 0:
print("⚠️ Tidak ditemukan referensi dokumen yang cocok dengan pertanyaan Anda.")
continue
# Tampilkan referensi yang ditemukan
print("\n[Referensi yang Ditemukan]:")
for idx, meta in enumerate(retrieved_metadatas):
print(f" - [{idx+1}] File: {meta['source']} (Chunk: {meta['chunk_index']})")
# 4. Susun Prompt dengan Konteks SOP
context = "\n\n---\n\n".join(retrieved_chunks)
system_prompt = (
"Anda adalah asisten AI perusahaan yang profesional. Tugas Anda adalah memberikan jawaban "
"yang valid, akurat, dan sesuai dengan Standar Operasional Prosedur (SOP) atau dokumen acuan perusahaan "
"yang disediakan di bawah ini.\n"
"Patuhi aturan berikut:\n"
"1. Jawablah HANYA berdasarkan informasi yang ada dalam dokumen acuan di bawah.\n"
"2. Jika jawaban tidak dapat ditemukan di dalam dokumen tersebut secara eksplisit atau logis, katakan dengan sopan "
"bahwa 'Maaf, informasi tersebut tidak ditemukan dalam dokumen SOP/acuan perusahaan kami.' Jangan mengarang informasi.\n"
"3. Sajikan data dengan valid dan rapi."
)
user_prompt = f"""Dokumen SOP / Acuan Perusahaan:
=========================================
{context}
=========================================
Pertanyaan Pengguna: {query}
Jawaban berdasarkan Dokumen Acuan:"""
print(f"\n[2/3] Menghubungi LLM Qwen 2.5 lokal di {LM_STUDIO_API_URL}...", end="", flush=True)
# Panggil LM Studio native v1 API
headers = {
"Content-Type": "application/json"
}
if OPENAI_API_KEY and OPENAI_API_KEY != "lm-studio":
headers["Authorization"] = f"Bearer {OPENAI_API_KEY}"
payload = {
"model": LLM_MODEL_NAME,
"input": user_prompt,
"system_prompt": system_prompt,
"temperature": 0.1,
"max_output_tokens": 32000
}
response = requests.post(LM_STUDIO_API_URL, json=payload, headers=headers)
response.raise_for_status()
print(" Selesai!")
answer = response.json()["response"]
# Pisahkan proses berpikir (<think>) jika ada (khusus model reasoning seperti Qwen 2.5)
import re
think_match = re.search(r'<think>(.*?)</think>', answer, re.DOTALL)
clean_answer = re.sub(r'<think>.*?</think>', '', answer, flags=re.DOTALL).strip()
if think_match and think_match.group(1).strip():
print(" Selesai!")
print("\n[Proses Berpikir Qwen 2.5]:")
print("." * 50)
print(think_match.group(1).strip())
print("." * 50)
else:
print(" Selesai!")
print("\n[3/3] Respon Asisten SOP:")
print("-"*50)
print(clean_answer)
print("-"*50)
except Exception as e:
print(f"\n❌ Terjadi kesalahan: {e}")
print("Harap pastikan server LLM lokal Anda (LM Studio/vLLM/llama.cpp) sedang berjalan dan dapat diakses.")
if __name__ == "__main__":
main()