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

Python · 24 lines
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

Python · 31 lines
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

Python · 5 lines
from zerda.router import HierarchicalRouter

router = HierarchicalRouter()
for group in (rag, tools, chat):
    router.register_group(group)

4. Try a few queries

Python · 7 lines
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:

Python · 3 lines
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.

Next steps

Related pages