Cookbook: RAG + Tools + Chat Support Bot
A complete, production-shaped recipe combining everything documented on this site: three intent groups, keyword boosts, a fallback handler, feedback, and observability.
from zerda.router import (
HierarchicalRouter, RouterConfig, FeedbackConfig, ObservabilityConfig, LogLevel,
make_rag_group, make_tools_group, make_chat_group,
Route, RouteKeywords,
BaseHandler, HandlerRequest, HandlerResponse,
)
# --- Handlers -----------------------------------------------------------
class DocsQAHandler(BaseHandler):
def __init__(self, retriever):
self.retriever = retriever
async def handle_async(self, request: HandlerRequest) -> HandlerResponse:
docs = await self.retriever.aretrieve(request.query)
if not docs:
return HandlerResponse.fail("No relevant documents found.")
return HandlerResponse.ok(docs[0].text, source=docs[0].source)
def handle(self, request: HandlerRequest) -> HandlerResponse:
docs = self.retriever.retrieve(request.query)
if not docs:
return HandlerResponse.fail("No relevant documents found.")
return HandlerResponse.ok(docs[0].text, source=docs[0].source)
class RefundToolHandler(BaseHandler):
def handle(self, request: HandlerRequest) -> HandlerResponse:
order_id = request.context.get("order_id")
if not order_id:
return HandlerResponse.fail("Missing order_id — cannot process refund.")
# ... call your real refund API here ...
return HandlerResponse.ok(f"Refund initiated for order {order_id}.")
class ChitChatHandler(BaseHandler):
def handle(self, request: HandlerRequest) -> HandlerResponse:
return HandlerResponse.ok("Happy to chat! How can I help with your order today?")
class ClarifyHandler(BaseHandler):
def handle(self, request: HandlerRequest) -> HandlerResponse:
return HandlerResponse.ok("I'm not sure I understood — could you rephrase that?")
# --- Assembly -------------------------------------------------------------
config = RouterConfig(
feedback=FeedbackConfig(enabled=True, min_samples_to_adapt=20, persist_path="feedback.json"),
observability=ObservabilityConfig(log_level=LogLevel.INFO, structured_logging=True),
)
with HierarchicalRouter(config=config) as router:
router.set_fallback(ClarifyHandler())
rag = make_rag_group()
rag.add_route(Route(
name="docs_qa",
description="Answer from product documentation",
handler=DocsQAHandler(my_retriever),
examples=["What's the return policy?", "How do I reset my password?"],
))
router.register_group(rag)
tools = make_tools_group()
tools.add_route(Route(
name="refund",
description="Process a refund for an order",
handler=RefundToolHandler(),
examples=["I want a refund", "please refund my order"],
keywords=RouteKeywords(any_of=["refund", "money back", "cancel order"], boost=0.2),
))
router.register_group(tools)
chat = make_chat_group()
chat.add_route(Route(
name="chitchat",
description="Casual conversation",
handler=ChitChatHandler(),
examples=["hi", "how are you"],
))
router.register_group(chat)
# --- Handle a request ---------------------------------------------
result = router.route_query(
"I'd like a refund for my last order",
context={"order_id": "ORD-5521"},
)
print(result.content)
# --- Record feedback once outcome is known -------------------------
router.feedback(route_name=result.route_name, success=True, score=0.95)
Related pages: Quickstart, Concepts, RouteGroup templates