Appearance
How-To: Test the Agent's Transfer Decision
Worked example: a pair of regression test cases that contrast the agent's two responses to checkTransferWindow's state. Both cases use the same persona and opening utterance. The only difference is the mocked checkTransferWindow response. The expected agent behavior diverges in opposite directions, which makes this pair a useful regression assertion: any prompt change that breaks one direction without breaking the other surfaces immediately.
What this exercises
The EAS agent's transfer-decision logic, defined in the prompt + driven by checkTransferWindow.ts's response. The real handler returns one of three statuses (ready / after-hours / unavailable), each carrying a different prompt-instruction message. By mocking the response, we test how the agent interprets each.
| Test case | checkTransferWindow mock returns | Expected agent behavior |
|---|---|---|
| A. In-window transfer | status: ready (transfer destination is available) | Agent invokes transferCall, then ends the conversation |
| B. After-hours take-message | status: after-hours (destinations outside business hours) | Agent does NOT invoke transferCall. Agent apologizes, optionally shares next open time, offers to take a message |
If both tests pass, the agent's decision logic is intact. If A passes but B doesn't, the agent is transferring when it shouldn't (will result in real transfers during real after-hours calls). If B passes but A doesn't, the agent is refusing to transfer when it should.
Prerequisites
- The team has a Hamming agent registered. See How-To: Register a Hamming Agent. If the Simulation Testing dropdown is empty, this step is missing.
- You're on the Avoca dashboard team.
sandy.corsillo@avoca.aiis on the EAS / Ponderosa team. - The voice assistant has at least one transfer destination configured. Without destinations,
checkTransferWindowreturnsunavailablerather thanreadyorafter-hours. Real test runs need this; mocks don't but it's worth knowing the dependency.
The mock response shapes
These come from the handler's ReadyResponse and AfterHoursResponse types (checkTransferWindow.ts:32-44).
Test Case A: in-window transfer (mock checkTransferWindow →)
json
{
"status": "ready",
"destinationNames": ["Ponderosa Main Line"],
"message": "Transfer is available. Say a brief acknowledgement out loud and then call the transferCall tool to connect the caller."
}Test Case B: after-hours (mock checkTransferWindow →)
json
{
"status": "after-hours",
"shopName": "EAS Ponderosa",
"nextOpenWindow": { "dayOfWeek": "MONDAY", "startTime": "08:00:00" },
"suggestedAction": "take-message",
"message": "Transfer destinations are currently outside their business hours. Apologize, share the next available time if useful, and offer to take a message so someone can call back."
}Both test cases: mock transferCall →
{}Vapi's transferCall is native. Mocking it with {} prevents the actual SIP transfer from firing while still letting the agent see a "tool ran" signal. Without this mock, in Test Case A the call would actually attempt to transfer to a real phone number; in Test Case B it shouldn't fire but mocking it is cheap insurance.
Steps
Phase 1: Author Test Case A (in-window transfer)
- Navigate to
https://dashboard.avoca.ai/team/<TEAM_ID>/settings/responder/simulation-testing. - Select the EAS Hamming agent from the dropdown (registered per the prereq).
- Click "Create Test Case".
- Fill in the dialog:
- Scenario name:
Transfer decision, in-window - Persona: a caller wanting to be transferred to a service representative. Plain affect. No time-of-day mention.
- Opening utterance:
Hi, can I please speak to someone in service? - Expected behavior: agent acknowledges, calls
transferCall, ends the conversation.
- Scenario name:
- In the same dialog, add two overrides:
checkTransferWindow→ the JSON from Test Case A above.transferCall→{}
- Save. Capture the test case ID.
Phase 2: Author Test Case B (after-hours take-message)
- From the same Simulation Testing page, click "Create Test Case" again.
- Fill in the dialog:
- Scenario name:
Transfer decision, after-hours - Persona: identical to Test Case A. Same plain affect, no time mention.
- Opening utterance: identical to Test Case A.
- Expected behavior: agent apologizes for being closed, may share next open time, offers to take a message. Does NOT invoke
transferCall.
- Scenario name:
- Add two overrides:
checkTransferWindow→ the JSON from Test Case B above.transferCall→{}
- Save. Capture the test case ID.
Phase 3: Run both tests
- From Simulation Testing, click "Run" on Test Case A.
- Wait for completion (~30s to 2 min per run).
- Open the run results. Confirm:
- Tool-call sequence shows
checkTransferWindowwas invoked. Response matches the Test Case A mock (you can see "ready" in the response). - Tool-call sequence shows
transferCallwas invoked. Response is{}. - Transcript shows the agent acknowledging and transferring.
- Tool-call sequence shows
- Repeat for Test Case B. Confirm:
checkTransferWindowwas invoked. Response shows "after-hours".transferCallwas NOT invoked.- Transcript shows the agent apologizing and offering to take a message.
Phase 4: Iterate if either test fails
Failure modes and their likely fixes:
| Symptom | Likely cause | Fix |
|---|---|---|
Test A: agent says it'll transfer but never invokes transferCall | The mock checkTransferWindow response doesn't read as "go transfer" to the prompt's logic | Re-read the prompt section that branches on checkTransferWindow status. Adjust the mock string to match what the prompt expects literally. |
Test B: agent invokes transferCall anyway | The mock checkTransferWindow response doesn't read as "after-hours" clearly | Same. The message field is what the LLM reasons over — make sure it explicitly says "outside business hours" and "take a message." |
| Either: no tool calls visible in transcript | The agent didn't reach the transfer-decision branch. The opening utterance didn't trigger transfer-intent classification. | Adjust opening utterance to be more clearly transfer-seeking (e.g., "I need to speak to someone urgently"). |
What success looks like
Both test cases pass on a single run, with the contrast preserved: transferCall invoked in A, not in B. Re-running the pair after a prompt change either continues to pass (regression safe) or flags exactly which decision branch broke.
Captured during execution
Filled in as the hamming-test-setup plan executes.
- Team ID: (fill in)
- Hamming agent ID: (fill in)
- Test Case A ID: (fill in)
- Test Case B ID: (fill in)
- First run timestamps: (fill in)
- Surprises / iteration notes: (fill in)
Related
- Tool Call Mocks, how the mock interception works at runtime.
- How-To: Register a Hamming Agent, prereq.
checkTransferWindow.ts, source of the three response shapes (ReadyResponse,AfterHoursResponse,UnavailableResponse).feedback_hamming_for_all_voice_testing, Hamming is THE testing surface.