HaystackHaystack 3.1 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
19 small wins to finish your path

Policy desk for customers and staff

Put the course together: one set of policies answered for customers and staff, permissions in the retriever, sourced answers, an agent for orders, and tests.

The project uses desk.py, policies.py and test_desk.py from lesson 17, and shop_chat.py from lesson 11.

Exampleapp.py
from desk import build_desk
from policies import POLICIES
from shop_chat import ShopChat

customer_desk = build_desk(POLICIES, ShopChat(), audience="customer")
staff_desk = build_desk(POLICIES, ShopChat(), audience="staff")

for desk, who in [(customer_desk, "customer"), (staff_desk, "staff")]:
    for question in ["who can approve refunds", "when do parcels ship"]:
        answer = desk.run(question=question)["answers"][0]
        print(f"{who:8} {question:26} -> {answer.data}")
Example
python app.py

The same question gives different answers by audience. For a customer, the staff rule is filtered out before ranking, so the best remaining match is the refunds document. For staff, the staff rule is found. Parcels are public, so both see the same answer.

One desk, answered by audience
build_desk(POLICIES, ShopChat(), audience)promptdocumentsInMemoryDocumentStoreone policy is staff onlyretrieverBM25, top 2, audience filterpromptChatPromptBuilderllmShopChat, the stand-inanswerAnswerBuilder, with sources
Hover or tap a piece to see what it is and which lesson built it.
Ask the desk

Adding order lookups

Example
desk = build_desk(POLICIES, ShopChat(), audience="customer")
policy_tool = ComponentTool(component=desk, name="search_policies", description="Answer questions about shop policies.")
agent = Agent(chat_generator=ShopChat(), tools=[lookup_order, policy_tool])

reply = agent.run(messages=[ChatMessage.from_user("Where is A-1002?")])["messages"][-1]
print(reply.text)
print([tool.name for tool in agent.tools])

ComponentTool turned the whole desk into a tool. The stand-in always calls the first tool for an order id, so this ticket went to lookup_order; a real model would read both descriptions and call search_policies for policy questions.

The desk's tests

Example
pytest -q

Switching the desk to OpenAI

Pass OpenAIChatGenerator(model="gpt-4.1-mini") to build_desk in app.py and set OPENAI_API_KEY. The tests keep the stand-ins, because the permission rule and the sources do not depend on the model. Measure retrieval with lesson 14's evaluators on real customer questions before changing splitting or retrieval.

Haystack components not covered

TopicWhat it is for
Hybrid retrievalBM25 and embeddings together with a document joiner and a ranker.
RankersReordering retrieved documents with a cross-encoder model.
Converters for PDF, HTML and Office filesIndexing documents that are not plain text.
AsyncPipeline and streamingRunning components in parallel and streaming replies.
BreakpointsPausing a pipeline to inspect or change state.
HayhooksServing pipelines as REST APIs and MCP servers.
Model-judged evaluatorsFaithfulness and context relevance, with a real model.

Slow is fine. Stopping is the only problem.