Tutorial: Building a Multi-Group Router
On this page
This tutorial builds a router with three intent domains — RAG, tools, chat — using the built-in group templates, mirroring a realistic support-bot setup.
1. Define your handlers
from zerda.router import BaseHandler, HandlerRequest, HandlerResponse
class KnowledgeBaseHandler(BaseHandler):
def __init__(self, kb: dict):
self.kb = kb
def handle(self, request: HandlerRequest) -> HandlerResponse:
for keyword, answer in self.kb.items():
if keyword in request.query.lower():
return HandlerResponse.ok(answer, source="kb")
return HandlerResponse.fail("Nothing relevant found in the knowledge base.")
class OrderStatusHandler(BaseHandler):
def handle(self, request: HandlerRequest) -> HandlerResponse:
order_id = request.context.get("order_id")
if not order_id:
return HandlerResponse.fail("No order_id provided in context.")
return HandlerResponse.ok(f"Order {order_id} is in transit.")
class ChitChatHandler(BaseHandler):
def handle(self, request: HandlerRequest) -> HandlerResponse:
return HandlerResponse.ok("I'm just a support bot, but I'm doing well, thanks!")
2. Build groups from templates
from zerda.router import make_rag_group, make_tools_group, make_chat_group, Route, RouteKeywords
kb = {
"refund": "Refunds are processed within 5-7 business days.",
"shipping": "Standard shipping takes 3-5 business days.",
}
rag = make_rag_group()
rag.add_route(Route(
name="kb_lookup",
description="Answer from the internal knowledge base",
handler=KnowledgeBaseHandler(kb),
examples=["What's your refund policy?", "How long does shipping take?"],
))
tools = make_tools_group()
tools.add_route(Route(
name="order_status",
description="Look up the status of an order",
handler=OrderStatusHandler(),
examples=["Where is my order?", "Track my package"],
keywords=RouteKeywords(any_of=["order", "track", "shipment", "package"], boost=0.15),
))
chat = make_chat_group()
chat.add_route(Route(
name="chitchat",
description="Casual conversation",
handler=ChitChatHandler(),
examples=["how are you", "what's up"],
))
3. Register everything
from zerda.router import HierarchicalRouter
router = HierarchicalRouter()
for group in (rag, tools, chat):
router.register_group(group)
4. Try a few queries
for query, ctx in [
("What's your refund policy?", None),
("Where is my order?", {"order_id": "A1234"}),
("How's it going?", None),
]:
result = router.route_query(query, context=ctx)
print(f"{query!r} -> route={result.route_name!r}, content={result.content!r}")
5. Handle the tie-breaking case
Try a deliberately ambiguous query that could plausibly belong to more than one group:
result = router.route_query("Can you help me with my order refund?")
print(result.trace.notes) # inspect why one group won over another
print(result.trace.to_dict()["candidates"]) # see the full scored candidate list
Because tools has a higher default priority (20) than rag (10), and both groups' keyword/semantic signals for "order" and "refund" are plausible, use result.trace to understand exactly which group and route won and why — this is the core value of the RoutingTrace object.