Design the HR assistant
The scope is fixed: a gate-then-fan-out entry, plus Policy Q&A, HR-letter requests, and health-checkup booking. Design is where you turn that into a plan before you open the builder. Treat it as writing a short spec, and settle four things in order: the shared Configuration every agent inherits, the tools and inputs each journey needs, the shape each journey takes, and the gate every transactional journey opens with. Northwind's team spent an afternoon here and it saved a week of rework.
The discipline that makes this pay off is simple: write the design down, journey by journey, as intent plus variables plus tools plus shape, and check it hangs together before you build. What you design is then what you build, node for node.
Start with Configuration, not an agent
A Nexus assistant is not one big agent. The project-wide behaviour (persona, tone, conversation rules, fallback, safety) lives in Configuration, and every agent inherits it. Fill that in first and get it answering policy-style questions sensibly before you add a single transactional agent.
For HR, two Configuration choices matter most:
- Conversation rules. "Never speculate about an individual's pay or performance" belongs here, once, so it applies to every agent instead of being re-typed into each. So does "answer policy questions only from the handbook."
- Fallback routes to a person. When nothing matches, hand to HR. A confidently wrong HR answer is worse than "let me get someone."
Decide the tools and inputs first
Every journey is really a set of tools (workflows it can call) and variables (what it looks up or collects). List those before you shape anything, because they are what the journeys share. Northwind's assistant needs one identity lookup, one access check, a validator per messy input, and a few action workflows:
| Tool | What it does | Used by |
|---|---|---|
getEmployeeDetails | Identity lookup: returns the employee, their entitlements, and eligibility flags. | Entry, letter, booking, policy |
accessCheck | Self-vs-manager decision: may this person act on another's record? | Letter |
validateYear, validateDOB, validateAppointmentDate, validatePhone, validateState, validateCity | One validator per messy input; each returns a clean value or a clear reason. | Letter, booking |
generateLetter, checkAvailability, bookAppointment, notify | The action workflows that do the real work. | Letter, booking |
The inputs those tools read and write become your shared variables: an employee object (id, region, entitlements, eligibility), identityVerified, and selectedService (which journey the entry routed to). Name the object's fields now, because a condition or a goal can only read a variable that exists with the right name and type.
All three are global, and deliberately so: every journey reads employee, and a flow that verifies identity has to leave identityVerified somewhere a different agent can see it. A journey variable would die with the flow that wrote it. Everything else these flows collect — a letter year, an appointment date — stays journey-scoped, because nothing outside the flow reads it. See which scope it belongs in.
Load the employee once, at session start
getEmployeeDetails runs as an On session start hook, not inside a node:
On session start → @getEmployeeDetails → Store output in `employee`
By the time the first message is answered, every agent already knows who's asking and what they're entitled to. Three things this buys:
- One lookup per conversation, not one per journey. An employee who asks a policy question and then requests a letter is looked up once.
- No agent needs the identity tool. They read
employeeinstead — fewer tools attached is less weight on every turn, and one less thing to pick wrongly. - The entry flow gets shorter.
greet-and-confirmno longer fetches; it confirms what's already loaded and handles the case where nothing was.
Decide what happens when the lookup fails, because the hook runs before anyone has said anything and its failure is otherwise invisible. Have the workflow write a status alongside the record, so downstream steps can tell "we couldn't check" from "they aren't entitled" — during an outage those two look identical, and collapsing them denies a legitimate request.
While you are building a template, every tool is a dummy-safe workflow that returns realistic sample data, never a live
apiCall. Swap in the real system on adoption; the design does not change.
The entry: gate, then fan out
Here is the pattern that shapes the whole assistant. Before Northwind's assistant offers anything, it proves who is asking and looks up what they are entitled to, then offers only those services. That is the gate-then-fan-out shape, and it is the recommended entry for anything with entitlements:
@Quick RepliesThree design points to take from it:
- The lookup happens before the menu, not inside it. The session-start hook has already resolved the employee, so the menu is built from a confirmed role rather than a guess — and
greet-and-confirmstill owns the handling: what to say when no record came back, and where that goes. - Menu contents are data; menu destinations are exits. The list of services shown comes from a variable the lookup wrote; the branches are the handful of real destinations. When the catalogue grows next quarter, you change a variable, not the flow.
- A failed lookup is not "no entitlements".
not_foundandlookup_failedgo to a human, never to a silent denial. Keeping a system failure separate from a business "no" is the single most common thing to get right, and you will see it in every flow below.
Pick the shape per journey
You choose single agent versus flow per journey, and the rule is simple. If skipping a step would be a compliance or correctness problem (proving identity, checking access, validating an input, confirming availability), build a multi-agent flow so that step is a node the run cannot skip. If the path cannot be predicted and no step is load-bearing, build a single agent.
Run the three journeys through it:
| Journey | Shape | Why |
|---|---|---|
| Policy Q&A | Single agent | Open-ended, answered from the handbook. Nothing is skippable, so the model can own the turn. |
| HR-letter request | Flow | The self-vs-manager access check must gate the generate step. That is a data-privacy rule, not a preference. |
| Health-checkup booking | Flow | Validate each field, then an availability check that must gate the booking. |
The Policy Q&A agent earns one more rule of its own: it is read-only and grounded. It answers only from the tagged handbook, scopes the answer to the employee's region where policies differ, and says "that is not covered, let me get HR" when the handbook is silent. It cannot submit a request or generate a document. Keeping it apart from the transactional flows is what stops "what is the leave policy?" turning into an accidental leave application.
Identity and access, up front
Every transactional flow opens with the same gate, running two checks that do two different jobs:
@getEmployeeDetails@accessCheck- Eligibility. Should this person get this service at all? It is a read of a profile flag; deny politely and finish if it fails. That is a business outcome, so it gets its own exit, not a handover.
- Access (RBAC). May this person act on someone else's record? If the check says self-only and they name a colleague, refuse. Skipping this is how an assistant cheerfully hands one employee another's letter, which is exactly why access is a node, not a line in a prompt: the wiring enforces it, luck does not.
Build this gate once and reuse it. In the letter and booking flows below it is the same two nodes.
You are ready to build when
- Configuration is set, and the assistant answers policy questions sensibly with no transactional agents attached.
- The shared tools and the
employeeobject are named, with the fields each journey reads and writes. - The entry is a gate-then-fan-out: identify first, then offer only what is confirmed.
- Every journey has a shape (single or flow) with the reason written down.
- The identity lookup and access check are each one tool, reused, not copied per agent.
Next: Build it.