Omnichannel support desk
Goal
Let customers (or coworkers) ask questions in the chat tool they already use, Slack, Discord, or Telegram, and get answers grounded in your knowledge base, with a clean handoff to a specialist when the front-line agent is out of its depth. This is the chat surface (multi-turn, channel-driven) sitting on top of the same KB Q&A from the RAG recipe.
Every step below is shown two ways: first in the console (which page to
open, what to click), then a Via the CLI block with the exact primectl
command. Pick whichever you prefer; the two paths build the same desk, and you
can drive the running chat from either the Chats page or primectl chat.
Ingredients
- An LLM provider and an embedding provider (for the KB).
- A knowledge base collection (see the RAG recipe) and a front-line support agent; optionally one or more specialist agents to hand off to.
- A channel with chats enabled (Slack/Discord/Telegram) if you want the desk
to live in a chat app. You can also drive a chat directly from the console
Chats page or
primectl chatwithout a channel at all.
If you have not connected primectl yet, see "Connecting the CLI" in the
RAG knowledge base recipe.
Walkthrough
1. Build the KB and the agents
Create the kb collection and ingest your support docs (the
RAG recipe builds one). Then a front-line agent
that answers from it, and a specialist for escalations.
In the console:
- Go to Compute > Agents and click New agent. On Basic set ID
to
support-agentand pick the LLM provider + Model; on Tools checksystem__search_collection; on Advanced paste the front-line prompt. Click Create. - Repeat for
billing-specialistwith the billing prompt.
Via the CLI:
primectl create -f support-agent.yaml
primectl create -f billing-specialist.yaml
where support-agent.yaml is:
kind: agent
spec:
id: support-agent
description: Front-line support.
model:
provider_id: <llm>
model_name: <model>
tools:
- system__search_collection
system_prompt:
- >-
You are front-line support. Answer from the kb collection (call
search_collection) and cite the doc. If a question is about billing
specifics, tell the user you are handing them to a billing specialist.
and billing-specialist.yaml is:
kind: agent
spec:
id: billing-specialist
description: Billing specialist.
model:
provider_id: <llm>
model_name: <model>
tools:
- system__search_collection
system_prompt:
- >-
You are a billing specialist. Answer billing questions in detail using
the kb collection.
2. Enable chats on a channel
Register the channel (see the stock-news monitor for the channel-provider and channel setup) but turn chats on and point it at the front-line agent.
In the console:
- Go to Channels > Channels and open (or create) your channel.
- In the Chats block check Enable chats and set the Default agent
to
support-agent. Click Save.
Via the CLI:
primectl create -f support-channel.yaml
where support-channel.yaml is:
kind: channel
spec:
id: support
provider_id: <provider>
provider: discord
external_id: <channel-id>
label: Support
config:
chats:
enabled: true
default_agent_id: support-agent
Now a user messaging that channel opens a chat with support-agent; the
channel's chat commands (/new, /agent, /switch, /list) manage the
conversation. Toggling chats on takes effect immediately; the channel starts
serving the chat on its next inbound message, no restart needed.
3. Open and drive a chat
You do not need a channel to use the desk. The console Chats page (and
primectl chat) drive the same chat directly, which is also how you test the
desk before wiring it to Slack or Discord.
In the console:
- Go to Chats and click New chat, bound to
support-agent. - Type a question into the composer (for example "how do I reset my
password?") and send it. The agent calls
search_collectionand replies in the thread, citing the source doc.
Via the CLI:
primectl create -f support-chat.yaml
primectl chat say <chat-id> "how do I reset my password?"
where support-chat.yaml is:
kind: chat
spec:
agent_id: support-agent
create -f prints the new chat/<id>. chat say appends your message and
wakes the worker to run the turn; read the reply back with
primectl call chat messages-get <chat-id> --param after_seq=0 once the turn
drains (the chat resource carries both a send and a list operation on
messages, so the read-back is the suffixed messages-get action).
4. Hand off to a specialist
Escalation is just switching the chat's agent. The message history is preserved, so the specialist picks up with full context.
In the console: open the chat, use the agent control in the thread header
to switch it to billing-specialist, then ask the billing question in the same
thread.
Via the CLI:
primectl chat switch <chat-id> billing-specialist
primectl chat say <chat-id> "how do I get a refund?"
The same switch is available in-channel via /agent billing-specialist, and an
agent can trigger it itself with a handoff tool.
A few things worth knowing:
- Chats are channel-driven in production, but drivable directly. In a
channel, user turns arrive from Slack/Discord/Telegram. From the console Chats
page or
primectl chat say, you append a user turn and the worker drives it the same way, which is how the test below exercises the whole desk headlessly. - Switching agents auto-rejects any pending gate. If the front-line agent is parked on an approval or question when you hand off, that gate is cancelled so the new agent starts clean.
- Gates degrade to conversation. When a chat agent asks for approval or asks the user a question, it does not park the session; it just asks in the chat, and the user's next message is consumed as the answer (see the tiered help desk for the full soft-yield loop).
Testing
A scripted end-to-end test pins the AUTOMATABLE core of this desk
(tests/e2e/test_cookbook_support_desk.py, SMK-COOKBOOK-05): a real
embedder-backed KB plus the two agents, each searching the KB and answering
grounded, citing the source. A second test
(tests/e2e/test_cookbook_support_desk_cli.py, SMK-COOKBOOK-CLI-14) drives
the whole desk over the published CLI path: it creates the agents and a chat
with primectl create -f, sends the customer turn with primectl chat say,
hands off with primectl chat switch, and asks the specialist a billing
question, reading each reply back from the chat transcript. The agents are
scripted (deterministic mock LLM); the embedder, the indexer, and the vector
search are REAL.
Expected outcome (verified):
- Message to grounded answer: sending "how do I reset my password?" creates
a chat bound to
support-agent, which callssearch_collectionand replies citing the password doc (for example "...go to id.company.com, click 'Forgot Password' ... (Source: password.md)"). - Handoff: switching the chat to
billing-specialist, then asking "how do I get a refund?" in the same thread has the specialist independently callsearch_collectionand answer from the billing doc, the same conversation, a new agent, history intact.
The recipe was also verified live end-to-end over Discord: the channel inbound
turn opened the chat, the front-line agent answered into the thread, and the
in-thread /agent switch handed off to the specialist. One thing learned the
hard way:
/agentand the other thread commands only work inside a chat thread. Run from the main channel they reply ephemerally ("run inside a thread"), which reads as nothing happening. Switch from inside the thread, from the console Chats page, or withprimectl chat switch.