Skip to content

Teknis — Chatbot Service

Service inferensi & orkestrasi knowledge RAGA yang dibangun di atas Python + FastAPI. Menerima pertanyaan user beserta daftar sumber pengetahuan yang relevan, mengumpulkan konteks dari Elasticsearch dan service pendukung secara paralel, menyusun system prompt dinamis, lalu memanggil salah satu provider LLM (lokal maupun cloud) untuk menghasilkan jawaban. Dipanggil oleh api-tarantula (env var CHATBOT_URL) untuk setiap chat masuk.

Repository

KeyValue
Git Remotehttps://git.tlab.co.id/tarantula/service/chatbot-service.git
Branch Aktifmain
bash
git clone https://git.tlab.co.id/tarantula/service/chatbot-service.git
cd chatbot-service

Tech Stack

LayerTeknologi
RuntimePython (FastAPI + uvicorn)
Search / Knowledge IndexElasticsearch
LLM Clientopenai SDK (OpenAI-compatible untuk semua provider)
File ParsingPyPDF2, python-docx, openpyxl
ObservabilityOpenTelemetry (opentelemetry-instrumentation-fastapi/requests/logging, OTLP exporter)
Secret Managementinfisical_sdk (Python)
Testingpytest + pytest-mock + pytest-cov

Struktur Folder

chatbot-service/
├── app/
│   ├── main.py                 # FastAPI app, endpoint /chatbot & /health, orkestrasi knowledge source
│   ├── run.py                  # Entry point menjalankan uvicorn
│   ├── queue_manager.py        # Queue manager alternatif — TIDAK dipakai oleh main.py (lihat catatan di bawah)
│   ├── config/
│   │   ├── settings.py         # Loader konfigurasi via Infisical + konstanta provider LLM
│   │   ├── logging_config.py
│   │   └── telemetry.py        # Setup OpenTelemetry (aman-gagal, tidak crash jika OTel bermasalah)
│   ├── models/
│   │   └── request.py          # Skema request Pydantic (ChatbotRequest, KMItem)
│   ├── services/
│   │   ├── llm_service.py      # Koneksi multi-provider LLM, penyusunan prompt, streaming, analisis gambar
│   │   ├── data_service.py     # Query Elasticsearch untuk tiap jenis knowledge source
│   │   ├── sql_service.py      # Text-to-SQL: generate query + panggil Database Connect
│   │   └── api_service.py      # Text-to-API: generate call + panggil API Connect
│   └── utils/
│       └── helpers.py          # Download & ekstraksi isi file dari URL (txt/md/log/pdf/docx/xlsx)
└── tests/                      # Unit test per service

Environment Variables

Konfigurasi diambil dari Infisical (INFISICAL_PROJECT_ID, INFISICAL_ENVIRONMENT, INFISICAL_SECRET_PATH, INFISICAL_CLIENT_ID, INFISICAL_CLIENT_SECRET, INFISICAL_HOST), dengan fallback ke os.getenv() jika pengambilan dari Infisical gagal.

Elasticsearch

VariableKeterangan
URL_ELASTICURL Elasticsearch server
USER_ELASTICUsername Elasticsearch
PASS_ELASTICPassword Elasticsearch
VERIFY_ELASTICVerifikasi sertifikat TLS (default false)

Catatan: app/.env.example di repo ini berisi nilai contoh yang terlihat seperti kredensial nyata (host IP dan password spesifik), bukan placeholder generik seperti your_xxx_key. Sebaiknya nilai di file contoh ini diganti jadi placeholder murni, dan kredensial yang mungkin sudah pernah ter-commit ini dirotasi.

Index Elasticsearch

VariableDefaultKeterangan
ES_INDEX_RDBMS_LISTrdbms_listDaftar koneksi database eksternal
ES_INDEX_API_LISTapi_listDaftar definisi API eksternal
ES_INDEX_BASIC_KNOWLEDGEbasic_knowledgeKnowledge dasar (topic)
ES_INDEX_AUDIO_KNOWLEDGEaudio_knowledgeMetadata audio
ES_INDEX_AUDIO_SUMMARIZE_CHUNKsummarize_audio_chunkRingkasan per-chunk transkrip audio
ES_INDEX_AUDIO_SUMMARIZEsummarize_audioRingkasan penuh audio
ES_INDEX_OCR_KNOWLEDGEtarantula-ocrHasil OCR dokumen
ES_INDEX_OCR_SUMMARIZE_CHUNKsummarize_document_chunkRingkasan per-chunk dokumen
ES_INDEX_OCR_SUMMARIZEsummarize_documentRingkasan penuh dokumen
ES_INDEX_CHAT_HISTORIESchat_historiesRiwayat percakapan

Provider LLM

Setiap provider punya tiga variabel: API_KEY_*, BASE_URL_*, MODEL_*.

Provider (llm)Default Base URLDefault Model
localhttp://192.168.0.25:8026/v1Qwen/Qwen2.5-14B-Instruct-AWQ
local_v2http://192.168.0.27:18000/v1Qwen/Qwen2.5-32B-Instruct-AWQ
sambanovahttps://api.sambanova.ai/v1Meta-Llama-3.1-70B-Instruct
groqhttps://api.groq.com/openai/v1llama-3.3-70b-versatile
openaihttps://api.openai.com/v1gpt-4o-mini
alibabahttps://dashscope-intl.aliyuncs.com/compatible-mode/v1qwen-plus

Jika base_url_llm + api_key_llm + model_llm dikirim langsung di request, ketiganya dipakai apa adanya (override) tanpa melihat llm.

Model Multimodal (Analisis Gambar)

VariableDefaultKeterangan
MULTIMODAL_SERVERlocalProvider yang dipakai untuk analisis gambar (local atau alibaba)
API_KEY_LOCAL_MULTIMODALEMPTYAPI key VLM lokal
BASE_URL_LOCAL_MULTIMODALhttp://192.168.0.25:8026/v1Base URL VLM lokal
MODEL_LOCAL_MULTIMODALQwen/Qwen2.5-VL-32B-Instruct-AWQModel VLM lokal
API_KEY_ALIBABA_MULTIMODALEMPTYAPI key VLM Alibaba
BASE_URL_ALIBABA_MULTIMODALhttps://dashscope-intl.aliyuncs.com/compatible-mode/v1Base URL VLM Alibaba
MODEL_ALIBABA_MULTIMODALqwen-vl-maxModel VLM Alibaba

Integrasi & Concurrency

VariableDefaultKeterangan
API_URL_SQLhttp://192.168.0.25:8105/executeEndpoint Database Connect untuk Text-to-SQL
API_URL_GATEhttp://192.168.0.25:8037/proxyEndpoint API Connect untuk Text-to-API
VLLM_BASE_URLShttp://192.168.0.27:18011,http://192.168.0.28:18011Daftar base URL VLLM (comma-separated), di-poll lewat /metrics
MAX_VLLM_RUNNING4Batas request VLLM yang sedang berjalan sebelum request baru di-queue
MAX_VLLM_WAITING4Batas request VLLM yang menunggu sebelum request baru di-queue
MAX_CONCURRENT_SLOTS2Jumlah slot pemrosesan /chatbot paralel (threading.Semaphore)
POLL_INTERVAL1Interval polling (detik) saat menunggu slot/antrian
TIMEOUT_VLLM30Timeout (detik) saat memanggil /metrics VLLM
USE_QUEUEtrueDibaca oleh queue_manager.py — lihat catatan di bawah

Catatan arsitektur: app/queue_manager.py mendefinisikan queue manager sendiri tapi tidak diimpor atau dipakai oleh main.py. Mekanisme concurrency yang benar-benar aktif adalah threading.Semaphore(MAX_CONCURRENT_SLOTS) plus polling manual ke /metrics VLLM di main.py. Modul ini kemungkinan sisa refactor yang belum dibersihkan.

Endpoints

MethodPathKeterangan
POST/chatbotEndpoint utama: proses pertanyaan, kumpulkan knowledge, panggil LLM
GET/healthHealth check — memeriksa konektivitas Elasticsearch

POST /chatbot

Field request utama (lihat ChatbotRequest):

FieldTipeKeterangan
questionstringWajib. Pertanyaan user
internalboolJika true, LLM boleh menjawab pakai pengetahuan umum; jika false (default), LLM dibatasi hanya pada data yang tersedia
chainboolJika true, riwayat chat (room_chat_id) disertakan sebagai konteks
room_chat_idstringID room chat, dipakai untuk mengambil riwayat
personalizationstringInstruksi gaya bahasa custom; kosong = default menyesuaikan bahasa user
llmstringProvider: local, local_v2, sambanova, groq, openai, alibaba
base_url_llm / model_llm / api_key_llmstringOverride manual provider (opsional)
faqstringKonteks tambahan untuk mode FAQ
kmarrayDaftar { type, topic_id[] } — knowledge source yang ingin disertakan (basic, ocr, rdbms, api, audio)
streamboolJika true, response berupa stream NDJSON
sourceboolJika true, sertakan data mentah sumber (source_data) di response
thinkboolJika true, tambahkan suffix /think; default /no_think
image / image_urlbool / arrayAktifkan analisis gambar via model multimodal
files / files_urlbool / arrayAktifkan ekstraksi & tanya-jawab atas file dari URL

Response (non-stream) 200:

json
{
  "response": "jawaban dari LLM",
  "internal": false,
  "chain": false,
  "room_chat_id": "uuid",
  "token_usage": { "prompt_tokens": 123, "completion_tokens": 45, "total_tokens": 168 },
  "source_data": { "basic": [], "ocr": [], "rdbms": [], "api": [], "audio": [] },
  "degraded_sources": ["rdbms"]
}

degraded_sources hanya muncul kalau ada sumber yang gagal diambil (bukan error fatal — request tetap lanjut dengan sumber yang berhasil).

Response (stream) 200, media_type: application/jsonlines, tiap baris JSON:

json
{"type": "message", "data": "potongan teks jawaban"}
{"type": "metadata", "data": { "response": "...", "token_usage": {...}, "source_data": {...} }}

Alur Concurrency & Antrian VLLM

Pengumpulan Knowledge Source (Paralel)

Lima jenis knowledge source diambil bersamaan lewat ThreadPoolExecutor(max_workers=5); masing-masing dibungkus _safe_get sehingga kegagalan satu sumber tidak menggagalkan permintaan.

SumberFungsiIndex ElasticsearchUkuran Hasil
Basic (topic)get_dataES_INDEX_BASIC_KNOWLEDGE10 dokumen
Dokumen/OCRget_data_ocrES_INDEX_OCR_KNOWLEDGE + index ringkasan3 dokumen
Audioget_data_audioES_INDEX_AUDIO_KNOWLEDGE + index ringkasan10 dokumen
Database (RDBMS)get_data_rdbmsES_INDEX_RDBMS_LIST (via es.get, 1 dokumen skema)1 koneksi
API eksternalget_data_apiES_INDEX_API_LIST (via es.mget)sesuai jumlah topic_id
Riwayat chat (chain=true)get_historyES_INDEX_CHAT_HISTORIES20 pesan terakhir

Text-to-SQL & Text-to-API

Untuk sumber database dan API, isinya tidak langsung dipakai — Chatbot Service meminta LLM menyusun query/panggilan yang tepat, baru mengeksekusinya:

Panggilan API eksternal (jika lebih dari satu topic_id) diproses paralel lewat ThreadPoolExecutor. Jika salah satu panggilan API gagal atau mengembalikan data tidak valid, seluruh source_data["api"] untuk request tersebut dikosongkan (bukan hanya endpoint yang gagal).

Penyusunan System Prompt

process_latest() di llm_service.py merakit system prompt dari komponen berikut, persis seperti yang dideskripsikan di Raga Engine:

KomponenSumber
Waktu saat inidatetime.now()
Internal promptMode "hanya data yang tersedia" (default) atau "boleh pengetahuan umum" (internal=true)
Konten knowledge sourceDigabung dari basic + OCR + audio + hasil Text-to-SQL + hasil Text-to-API
Riwayat chatDari get_history, dibersihkan dari baris "tidak ada informasinya" berulang
File dari userHasil ekstraksi files_url lewat generate_answer()
Deskripsi gambarHasil analyze_images() — panggilan terpisah ke model multimodal
PersonalisasiInstruksi gaya bahasa custom atau default (menyesuaikan bahasa user)

User prompt disusun dari aggressive_prefix + pertanyaan + suffix (/think atau /no_think, mengontrol reasoning mode model).

Ekstraksi File (files_url)

TipeEkstensi
Teks.txt, .md, .log
Dokumen.pdf, .docx, .xlsx

File diunduh sementara dari URL, diekstrak isinya, lalu dijawab lewat generate_answer() sebelum digabung ke system prompt sebagai files_users.

Integrasi Eksternal

ServiceEnv VariableKeterangan
Database ConnectAPI_URL_SQLEksekusi query SQL hasil Text-to-SQL
API ConnectAPI_URL_GATEEksekusi panggilan API hasil Text-to-API
VLLM (lokal)VLLM_BASE_URLSSumber metrik antrian untuk provider local/local_v2
InfisicalINFISICAL_HOSTSumber seluruh secret runtime

Health Check

GET /health hanya memeriksa konektivitas Elasticsearch (es.ping()) — mengembalikan 200 ("status": "ok") atau 503 ("status": "degraded") jika Elasticsearch tidak terjangkau. Provider LLM dan service downstream (Database Connect, API Connect, VLLM) tidak termasuk dalam pengecekan ini.

Build & Run

bash
pip install -r requirements.txt

# Jalankan langsung
uvicorn app.run:app --host 0.0.0.0 --port 8080

# Docker
docker build -t chatbot-service .
docker run -p 8080:8080 chatbot-service