Tutorial Semantic Router: Routing Cerdas untuk Aplikasi LLM
Semantic Router adalah library Python yang memungkinkan Anda membuat keputusan routing berbasis makna (semantic) secara cepat dan efisien. Alih-alih mengandalkan LLM untuk setiap keputusan klasifikasi, Semantic Router menggunakan embedding vectors untuk menentukan intent pengguna dalam hitungan milidetik.
Library ini sangat berguna untuk membangun chatbot, virtual assistant, dan aplikasi AI yang memerlukan respons cepat terhadap berbagai jenis pertanyaan pengguna. Dengan Semantic Router, Anda bisa mengarahkan query ke handler yang tepat tanpa overhead pemanggilan LLM setiap saat.
Mengapa Semantic Router?
Dalam pengembangan aplikasi berbasis LLM, salah satu tantangan umum adalah menentukan bagaimana merespons input pengguna. Pendekatan tradisional menggunakan keyword matching yang rigid, atau mengirim semua query ke LLM yang mahal dan lambat.
Semantic Router menawarkan pendekatan tengah yang optimal:
- Kecepatan: Routing dilakukan dalam milidetik, bukan detik
- Akurasi: Menggunakan semantic similarity, bukan keyword matching
- Efisiensi biaya: Mengurangi pemanggilan LLM yang tidak perlu
- Fleksibilitas: Mendukung routing statis maupun routing yang memanggil fungsi
Contoh kasus penggunaan:
- Memfilter pertanyaan yang tidak relevan sebelum dikirim ke LLM
- Mengarahkan pertanyaan teknis ke knowledge base tertentu
- Mengaktifkan guardrails untuk topik sensitif
- Memicu fungsi tertentu berdasarkan intent pengguna
Instalasi
Semantic Router dapat diinstal menggunakan pip. Tersedia beberapa variasi instalasi tergantung encoder yang ingin Anda gunakan.
# Instalasi dasar
pip install semantic-router
Dengan dukungan OpenAI encoder
pip install "semantic-router[openai]"
Dengan dukungan Cohere encoder
pip install "semantic-router[cohere]"
Dengan dukungan encoder lokal (HuggingFace)
pip install "semantic-router[local]"
Instalasi lengkap
pip install "semantic-router[all]"
Untuk tutorial ini, kita akan menggunakan encoder lokal agar bisa berjalan tanpa API key:
pip install "semantic-router[local]"
Verifikasi instalasi:
import semanticrouter
print(semantic
router.version)
Konsep Dasar
Sebelum masuk ke kode, penting untuk memahami konsep inti dalam Semantic Router.
Route
Route adalah aturan yang mendefinisikan sebuah intent atau kategori. Setiap route terdiri dari nama dan beberapa contoh utterance yang merepresentasikan intent tersebut.
from semanticrouter import Route
Mendefinisikan route untuk pertanyaan tentang cuaca
weather
route = Route(
name="weather",
utterances=[
"bagaimana cuaca hari ini?",
"apakah akan hujan besok?",
"berapa suhu di Jakarta?",
"prakiraan cuaca minggu ini",
"cuaca hari ini cerah atau mendung?",
],
)
Encoder
Encoder bertanggung jawab mengubah teks menjadi embedding vector. Semantic Router mendukung berbagai encoder:
# Encoder lokal (gratis, tidak perlu API key)
from semanticrouter.encoders import HuggingFaceEncoder
encoder = HuggingFaceEncoder()
Encoder OpenAI
from semanticrouter.encoders import OpenAIEncoder
encoder = OpenAIEncoder(apikey="sk-...")
Encoder Cohere
from semanticrouter.encoders import CohereEncoder
encoder = CohereEncoder(apikey="...")
RouteLayer
RouteLayer menggabungkan routes dan encoder menjadi satu sistem routing yang siap digunakan.
from semanticrouter.layer import RouteLayer
routelayer = RouteLayer(
encoder=encoder,
routes=[weatherroute, greetingroute],
)
Penggunaan Dasar
Mari kita bangun sistem routing sederhana yang bisa membedakan beberapa jenis intent pengguna.
from semanticrouter import Route, RouteLayer
from semanticrouter.encoders import HuggingFaceEncoder
1. Definisikan routes
greetingroute = Route(
name="greeting",
utterances=[
"halo",
"hai, apa kabar?",
"selamat pagi",
"hey, salam kenal",
"halo, senang bertemu denganmu",
],
)
farewellroute = Route(
name="farewell",
utterances=[
"sampai jumpa",
"selamat tinggal",
"bye bye",
"dadah, sampai ketemu lagi",
"saya pergi dulu ya",
],
)
technicalroute = Route(
name="technicalsupport",
utterances=[
"aplikasi saya error",
"bagaimana cara reset password?",
"tidak bisa login ke akun saya",
"website loading lama sekali",
"ada bug di fitur pembayaran",
],
)
billingroute = Route(
name="billing",
utterances=[
"berapa tagihan bulan ini?",
"saya mau bayar invoice",
"status pembayaran saya bagaimana?",
"cara upgrade paket langganan",
"mau cancel subscription",
],
)
2. Inisialisasi encoder
encoder = HuggingFaceEncoder()
3. Buat RouteLayer
routes = [greetingroute, farewellroute, technicalroute, billingroute]
routelayer = RouteLayer(encoder=encoder, routes=routes)
4. Test routing
testqueries = [
"hai, saya butuh bantuan",
"saya tidak bisa masuk ke akun",
"mau tanya soal tagihan",
"ok terima kasih, bye",
"cuaca hari ini bagaimana?", # tidak cocok dengan route manapun
]
for query in testqueries:
result = routelayer(query)
print(f"Query: '{query}'")
print(f"Route: {result.name}")
print(f"Similarity: {result.similarityscore:.4f}")
print()
Output yang diharapkan:
Query: 'hai, saya butuh bantuan'
Route: greeting
Similarity: 0.8234
Query: 'saya tidak bisa masuk ke akun'
Route: technicalsupport
Similarity: 0.8567
Query: 'mau tanya soal tagihan'
Route: billing
Similarity: 0.8901
Query: 'ok terima kasih, bye'
Route: farewell
Similarity: 0.8123
Query: 'cuaca hari ini bagaimana?'
Route: None
Similarity: 0.0000
Perhatikan bahwa query terakhir tentang cuaca tidak cocok dengan route manapun dan mengembalikan None. Ini adalah fitur penting karena Semantic Router bisa mengenali ketika suatu query tidak masuk ke kategori yang telah didefinisikan.
Penggunaan Lanjutan
Dynamic Routes dengan Function Calling
Semantic Router mendukung dynamic routes yang bisa memanggil fungsi Python saat route tertentu terpicu. Ini sangat berguna untuk membangun agent yang bisa melakukan aksi tertentu.
from semanticrouter import Route, RouteLayer
from semantic
router.encoders import HuggingFaceEncoder
from datetime import datetime
Definisikan fungsi-fungsi yang akan dipanggil
def getcurrenttime(timezone: str = "Asia/Jakarta"):
"""Mendapatkan waktu saat ini."""
from zoneinfo import ZoneInfo
now = datetime.now(ZoneInfo(timezone))
return f"Waktu saat ini di {timezone}: {now.strftime('%H:%M:%S %d-%m-%Y')}"
def calculateprice(item: str, quantity: int = 1):
"""Menghitung harga item."""
prices = {
"kopi": 25000,
"teh": 15000,
"roti": 20000,
"nasi goreng": 35000,
}
price = prices.get(item.lower(), 0)
total = price * quantity
return f"{item} x{quantity} = Rp{total:,}"
Definisikan route dengan function schema
timeroute = Route(
name="gettime",
utterances=[
"jam berapa sekarang?",
"waktu saat ini",
"sekarang pukul berapa?",
"kasih tahu saya waktu sekarang",
],
functionschemas=[
{
"name": "getcurrenttime",
"description": "Mendapatkan waktu saat ini",
"parameters": {
"type": "object",
"properties": {
"timezone": {
"type": "string",
"description": "Timezone, default Asia/Jakarta",
}
},
"required": [],
},
}
],
)
priceroute = Route(
name="checkprice",
utterances=[
"berapa harga kopi?",
"hitung total belanjaan saya",
"mau pesan 3 nasi goreng, berapa totalnya?",
"price list menu",
],
functionschemas=[
{
"name": "calculateprice",
"description": "Menghitung harga item",
"parameters": {
"type": "object",
"properties": {
"item": {
"type": "string",
"description": "Nama item",
},
"quantity": {
"type": "integer",
"description": "Jumlah item",
"default": 1,
},
},
"required": ["item"],
},
}
],
)
encoder = HuggingFaceEncoder()
routelayer = RouteLayer(
encoder=encoder,
routes=[timeroute, priceroute],
)
Test dynamic routing
result = routelayer("jam berapa sekarang di Tokyo?")
print(f"Route: {result.name}")
print(f"Function call: {result.functioncall}")
Threshold Tuning
Anda bisa mengatur threshold untuk setiap route agar lebih ketat atau longgar dalam mencocokkan query.
# Route dengan threshold tinggi - hanya cocok jika sangat mirip
strictroute = Route(
name="passwordreset",
utterances=[
"reset password saya",
"lupa password",
"ganti password akun",
"tidak ingat password",
],
scorethreshold=0.85, # threshold tinggi
)
Route dengan threshold rendah - lebih longgar
generalroute = Route(
name="generalinquiry",
utterances=[
"saya punya pertanyaan",
"bisa bantu saya?",
"info dong",
"mau tanya",
],
scorethreshold=0.5, # threshold rendah
)
Menggunakan Multiple Encoders
Untuk kasus penggunaan tertentu, Anda mungkin perlu menggunakan encoder yang berbeda untuk routes yang berbeda.
from semanticrouter.encoders import HuggingFaceEncoder
Encoder untuk bahasa Indonesia
idencoder = HuggingFaceEncoder(
name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
)
Encoder dengan model yang lebih besar untuk akurasi lebih tinggi
largeencoder = HuggingFaceEncoder(
name="sentence-transformers/all-mpnet-base-v2"
)
Hybrid Route Layer
Semantic Router juga mendukung hybrid approach yang menggabungkan semantic routing dengan sparse retrieval (BM25).
from semanticrouter import Route, HybridRouteLayer
from semantic
router.encoders import HuggingFaceEncoder
encoder = HuggingFaceEncoder()
routes = [greetingroute, technicalroute, billingroute]
Hybrid layer menggabungkan dense dan sparse matching
hybridlayer = HybridRouteLayer(
encoder=encoder,
routes=routes,
alpha=0.5, # bobot antara dense (1.0) dan sparse (0.0)
)
result = hybridlayer("mau bayar tagihan internet bulan lalu")
print(f"Route: {result.name}")
Guardrails dengan Semantic Router
Salah satu use case paling populer adalah menggunakan Semantic Router sebagai guardrails untuk memfilter input yang tidak diinginkan sebelum dikirim ke LLM.
from semanticrouter import Route, RouteLayer
from semanticrouter.encoders import HuggingFaceEncoder
Route untuk konten yang harus diblokir
harmfulcontent = Route(
name="harmful",
utterances=[
"bagaimana cara meretas website",
"cara membuat virus komputer",
"cara bypass keamanan",
"tutorial hacking ilegal",
"cara mencuri data orang lain",
],
scorethreshold=0.75,
)
offtopic = Route(
name="offtopic",
utterances=[
"siapa presiden Indonesia?",
"berapa jarak bumi ke matahari?",
"resep masak rendang",
"jadwal liga sepakbola",
"rekomendasi film bagus",
],
scorethreshold=0.70,
)
Route untuk konten yang diizinkan
allowedtopics = Route(
name="allowed",
utterances=[
"bagaimana cara menggunakan API ini?",
"jelaskan fitur produk",
"bantuan pengaturan akun",
"cara integrasi dengan sistem lain",
"dokumentasi endpoint REST API",
],
scorethreshold=0.60,
)
encoder = HuggingFaceEncoder()
guardrail = RouteLayer(
encoder=encoder,
routes=[harmfulcontent, offtopic, allowedtopics],
)
def processquery(query: str):
"""Memproses query dengan guardrails."""
result = guardrail(query)
if result.name == "harmful":
return "Maaf, saya tidak bisa membantu dengan permintaan tersebut."
elif result.name == "offtopic":
return "Pertanyaan ini di luar cakupan layanan kami. Silakan tanyakan tentang produk kami."
elif result.name == "allowed":
return f"[Diteruskan ke LLM] Memproses: {query}"
else:
return "Maaf, saya tidak memahami pertanyaan Anda. Bisa diperjelas?"
Test guardrails
testinputs = [
"cara menggunakan API payment gateway",
"bagaimana cara hack akun orang",
"siapa juara piala dunia 2022?",
"cara setup webhook untuk notifikasi",
]
for q in testinputs:
response = processquery(q)
print(f"Input: {q}")
print(f"Response: {response}\n")
Integrasi dengan LangChain
Semantic Router dapat diintegrasikan dengan framework populer seperti LangChain untuk membangun aplikasi yang lebih kompleks.
from semanticrouter import Route, RouteLayer
from semantic
router.encoders import HuggingFaceEncoder
from langchainopenai import ChatOpenAI
from langchaincore.prompts import ChatPromptTemplate
Setup routes
coderoute = Route(
name="codehelp",
utterances=[
"bantu saya debug kode Python ini",
"bagaimana cara menulis unit test?",
"jelaskan error ini",
"review kode saya",
],
)
dataroute = Route(
name="dataanalysis",
utterances=[
"analisis data penjualan",
"buat visualisasi dari dataset ini",
"query SQL untuk laporan bulanan",
"cara cleaning data yang efektif",
],
)
encoder = HuggingFaceEncoder()
router = RouteLayer(encoder=encoder, routes=[coderoute, dataroute])
Setup LLM chains per route
codellm = ChatOpenAI(model="gpt-4o", temperature=0)
datallm = ChatOpenAI(model="gpt-4o", temperature=0.3)
codeprompt = ChatPromptTemplate.frommessages([
("system", "Kamu adalah expert Python developer. Bantu user dengan kode mereka."),
("human", "{query}"),
])
dataprompt = ChatPromptTemplate.frommessages([
("system", "Kamu adalah data analyst expert. Bantu user menganalisis data."),
("human", "{query}"),
])
codechain = codeprompt | codellm
datachain = dataprompt | datallm
def smartroute(query: str):
"""Route query ke chain yang tepat."""
result = router(query)
if result.name == "codehelp":
return codechain.invoke({"query": query})
elif result.name == "dataanalysis":
return datachain.invoke({"query": query})
else:
return "Pertanyaan di luar cakupan. Silakan tanyakan tentang coding atau analisis data."
response = smartroute("tolong review kode sorting algorithm saya")
print(response.content)
Menyimpan dan Memuat Routes
Untuk production, Anda bisa menyimpan konfigurasi route dan memuatnya kembali tanpa perlu mendefinisikan ulang.
import json
Simpan konfigurasi routes ke JSON
def saveroutes(routes, filepath):
config = []
for route in routes:
config.append({
"name": route.name,
"utterances": route.utterances,
"scorethreshold": route.scorethreshold,
})
with open(filepath, "w") as f:
json.dump(config, f, indent=2, ensureascii=False)
print(f"Routes saved to {filepath}")
Muat routes dari JSON
def loadroutes(filepath):
with open(filepath) as f:
config = json.load(f)
routes = []
for item in config:
routes.append(Route(
name=item["name"],
utterances=item["utterances"],
scorethreshold=item.get("scorethreshold", 0.5),
))
return routes
Simpan
saveroutes(routes, "routesconfig.json")
Muat kembali
loadedroutes = loadroutes("routesconfig.json")
routelayer = RouteLayer(encoder=encoder, routes=loadedroutes)
Best Practices
1. Sediakan Utterances yang Beragam
Setiap route sebaiknya memiliki minimal 5-10 utterances yang beragam. Semakin beragam contohnya, semakin baik akurasi routing.
# Kurang baik - terlalu mirip
badroute = Route(
name="greeting",
utterances=["halo", "hai", "hey"], # terlalu sedikit dan mirip
)
Lebih baik - beragam dan representatif
goodroute = Route(
name="greeting",
utterances=[
"halo, apa kabar?",
"selamat pagi, bisa bantu saya?",
"hai, saya pengguna baru",
"good morning, need help",
"hey, ada yang bisa dibantu?",
"permisi, saya mau tanya",
"salam, butuh informasi",
],
)
2. Gunakan Threshold yang Tepat
Threshold terlalu tinggi menyebabkan banyak query tidak ter-route. Threshold terlalu rendah menyebabkan false positive. Mulai dari 0.5 dan sesuaikan berdasarkan testing.
# Mulai dengan threshold default, lalu tune
route = Route(
name="specificintent",
utterances=[...],
scorethreshold=0.5, # mulai dari sini
)
Test dengan berbagai query dan adjust
Jika terlalu banyak false positive: naikkan ke 0.6-0.7
Jika terlalu banyak miss: turunkan ke 0.4-0.45
3. Pisahkan Route Berdasarkan Aksi
Jangan membuat route yang terlalu luas. Pisahkan berdasarkan aksi atau respons yang berbeda.
# Kurang baik - satu route untuk semua
supportroute = Route(
name="support",
utterances=[
"reset password",
"berapa tagihan saya",
"cara upgrade paket",
],
)
Lebih baik - route terpisah per aksi
passwordroute = Route(name="passwordreset", utterances=[...])
billingroute = Route(name="billinginquiry", utterances=[...])
upgraderoute = Route(name="planupgrade", utterances=[...])
4. Monitor dan Iterasi
Implementasikan logging untuk memantau performa routing di production.
import logging
from datetime import datetime
logger = logging.getLogger("semanticrouter")
def routewithlogging(routelayer, query: str):
"""Route query dengan logging untuk monitoring."""
result = routelayer(query)
logentry = {
"timestamp": datetime.now().isoformat(),
"query": query,
"route": result.name,
"score": result.similarityscore,
}
if result.name is None:
logger.warning(f"Unrouted query: {query}")
elif result.similarityscore < 0.6:
logger.info(f"Low confidence route: {logentry}")
return result
5. Gunakan Encoder yang Sesuai
Untuk production dengan traffic tinggi, pertimbangkan trade-off antara kecepatan dan akurasi:
- HuggingFace encoder lokal: Gratis, cepat, cocok untuk prototyping dan low-latency requirement
- OpenAI encoder: Akurasi tinggi, memerlukan API call, ada biaya per request
- Cohere encoder: Alternatif cloud encoder dengan performa baik untuk multilingual
6. Handling Edge Cases
Selalu tangani kasus ketika tidak ada route yang cocok:
def handlequery(routelayer, query):
result = routelayer(query)
if result.name is None:
# Fallback: kirim ke LLM general
return fallbackllm(query)
return route_handlersresult.name
Perbandingan dengan Pendekatan Lain
| Aspek | Semantic Router | Keyword Matching | LLM Classification |
|-------|----------------|------------------|---------------------|
| Kecepatan | ~5ms | ~1ms | ~500ms-2s |
| Akurasi | Tinggi | Rendah | Sangat Tinggi |
| Biaya | Rendah/Gratis | Gratis | Mahal |
| Setup | Mudah | Mudah | Kompleks |
| Fleksibilitas | Tinggi | Rendah | Sangat Tinggi |
| Skalabilitas | Baik | Baik | Terbatas oleh rate limit |
Kesimpulan
Semantic Router adalah solusi yang elegan untuk permasalahan routing di aplikasi LLM. Dengan menggabungkan kecepatan embedding-based classification dengan fleksibilitas function calling, library ini menjembatani gap antara pendekatan keyword matching yang kaku dan LLM classification yang mahal.
Poin-poin utama yang perlu diingat:
Semantic Router terus berkembang dengan komunitas yang aktif. Untuk informasi terbaru, kunjungi repository GitHub resmi dari Aurelio AI.