mirror of
https://github.com/forcedotcom/afv-library.git
synced 2026-08-09 08:53:18 +08:00
195 lines
9.5 KiB
Markdown
195 lines
9.5 KiB
Markdown
# Voice Latency Heuristics
|
||
|
||
Voice agents live and die on latency. Under ~2 seconds between "caller stops
|
||
speaking" and "agent starts speaking" feels natural; every additional second
|
||
erodes the illusion of talking to a person. Text agents can hide a slow tool
|
||
behind a spinner — voice agents can't. This reference encodes the latency
|
||
anti-patterns worth catching at **authoring** time (`/agentforce-generate`) and
|
||
diagnosing from **traces** (`/agentforce-observe`).
|
||
|
||
Each pattern has three parts: **detection** (how to spot it in the `.agent`
|
||
bundle or a trace), **impact** (typical cost), and **fix** (instructional patch,
|
||
action/tool change, or flag-for-human).
|
||
|
||
> **Fix classification.** Only *purely instructional* fixes (adding an ack
|
||
> phrase, tightening turn length, adding a spoken-form rule) are safe to apply
|
||
> automatically. Anything that changes an action's implementation, its
|
||
> synchronicity, or an org/channel-level setting is **flag-only** — surface it
|
||
> with the specific object/action named and let a human decide.
|
||
|
||
---
|
||
|
||
## 1. Synchronous writes on the live-call critical path
|
||
|
||
The single biggest latency killer in the field. Any write (DML, external
|
||
callout, trigger cascade) that runs *inside* the turn between caller-speaks and
|
||
agent-speaks adds directly to perceived response time.
|
||
|
||
**Detection**
|
||
- An action's target performs a write and runs synchronously in the reasoning turn.
|
||
- The action name/description implies mutation: `update`, `insert`, `log`, `commit`, `save`, `record`, `create`, `submit`.
|
||
- A trigger fires on the target object (custom or managed-package).
|
||
|
||
**Impact**
|
||
- Simple insert with a normal trigger: ~300–800ms.
|
||
- Managed-package trigger cascade on a heavy object: multiple seconds.
|
||
- Chained callout inside the trigger: add ~1–3s per hop.
|
||
|
||
**Fix**
|
||
- **Preferred:** move the write to post-call via a Platform Event or async queue.
|
||
- **Interim:** wrap the write in a Queueable and return immediately from the action.
|
||
- **Instructional (auto):** add an ack phrase before the action ("One moment while I get that on file.").
|
||
- **Flag for human:** if the write MUST happen mid-turn (authentication, payment authorization), don't rewrite — flag with the specific object and trigger names.
|
||
|
||
## 2. Large result sets returned raw to the reasoning LLM
|
||
|
||
Retrieval that returns the full body of every matching record is a hidden
|
||
latency *and* accuracy tax — the model has to filter needles from a hayfield
|
||
every turn, on a bloated context.
|
||
|
||
**Detection**
|
||
- Action description mentions `search`, `retrieve`, `knowledge`, `articles`, `documents`.
|
||
- Action returns more than ~5 items or ~2KB of text per call by default.
|
||
- No `limit` / `top_k` / `max_chars` parameter surfaced to the planner.
|
||
- Return type is the raw object (article, record), not a summarized shape.
|
||
|
||
**Impact**
|
||
- Seconds per call, plus downstream reasoning slowdown on the bloated context.
|
||
- Degraded answer accuracy.
|
||
|
||
**Fix**
|
||
- **Action change:** summarize first (title + 1–2 sentence excerpt), page the rest behind a follow-up action.
|
||
- **Action change:** add a `limit` parameter with a low default (3–5); name the allowed range in its `description` (Agent Script has no `enum` input attribute — see `actions-reference.md` "Voice-Safe Action Authoring" rule "Enumerate small value sets in the description").
|
||
- **Instructional:** if the action stays bulky, add an ack phrase ("This can take a few seconds — hang with me.").
|
||
- **Flag for human:** if the customer intentionally wants wide-net RAG over their whole KB, surface the tradeoff instead of rewriting.
|
||
|
||
## 3. Chained external callouts
|
||
|
||
Every external HTTP hop is a chance for tail latency. Two hops brings the p95 of
|
||
both to the caller.
|
||
|
||
**Detection**
|
||
- An action's implementation makes 2+ external HTTP calls per invocation.
|
||
- A trigger on the target object hits a webhook or external system.
|
||
|
||
**Impact**
|
||
- Per hop: ~200–800ms typical, seconds at p95.
|
||
- Two chained hops: p95 easily crosses several seconds.
|
||
|
||
**Fix**
|
||
- **Flag for human** with the specific hops named — the topology matters.
|
||
- Where feasible, propose consolidating hops server-side or pre-fetching in parallel.
|
||
|
||
## 4. Over-decomposed multi-subagent routing
|
||
|
||
A monolithic agent is often faster *and* more accurate than a deep tree of
|
||
subagents fronted by a classifier. Each planner turn costs ~1–2s; each subagent
|
||
handoff can add another. This is the voice cost of the "dead hub" and
|
||
"over-routing" anti-patterns already tracked in `known-issues.md`.
|
||
|
||
**Detection**
|
||
- The bundle has several subagents whose only job is to route to one other subagent.
|
||
- A classifier/guardrail subagent intercepts every utterance before the primary agent.
|
||
|
||
**Impact**
|
||
- Extra planner round-trips per turn; unexpected orchestrator calls when subagents chain.
|
||
|
||
**Fix**
|
||
- **Flag for human:** collapsing multi-subagent → flatter routing is a design decision.
|
||
- Where the structure must stay, ensure each hop has an ack phrase.
|
||
- Consider whether a guardrail subagent can become instruction-level rules in `system:`.
|
||
|
||
## 5. Long agent turns trip the silence/nudge timer
|
||
|
||
On long agent turns, the platform's "are you still there?" nudge (Speak-Up) can
|
||
fire before the caller has had a chance to respond, producing an awkward overlap
|
||
or dead air. This is the same root cause as the response-verbosity check in
|
||
`/agentforce-observe`.
|
||
|
||
**Detection**
|
||
- Response templates or instructions that produce turns over ~30 words / ~10s of speech.
|
||
- Welcome/greeting message longer than one or two sentences.
|
||
|
||
**Impact**
|
||
- Dead air, overlap, or a spurious "still there?" prompt on the first or a long turn.
|
||
|
||
**Fix**
|
||
- **Instructional (auto):** enforce ≤2-sentence turns everywhere (the "Keep responses concise" rule in `voice-modality-reference.md`); keep the greeting to one sentence.
|
||
- **Flag for human:** if long turns are legally required (disclaimers), the silent/nudge timing is an **org-level** setting, not an agent-bundle change.
|
||
|
||
## 6. TTS number / currency / ID garble
|
||
|
||
TTS engines read `$172,576.81` or `+14155551212` as garble unless the model
|
||
pre-formats them into spoken form.
|
||
|
||
**Detection**
|
||
- Instructions or action outputs surface prices, phone numbers, IDs, dates, or account numbers.
|
||
- No rule telling the model to render numbers in spoken form.
|
||
|
||
**Impact**
|
||
- Callers hear unintelligible strings; erodes trust and forces repeats.
|
||
|
||
**Fix**
|
||
- **Instructional (auto):** add the spoken-form number rule (the "Render numbers, prices, and IDs in spoken form" rule in `voice-modality-reference.md`).
|
||
- **Flag for human:** if the deployment language has weak TTS number-normalization coverage, note the gap so the instruction rule carries the load.
|
||
|
||
## 7. Premature end-of-turn under noise (STT endpointing)
|
||
|
||
Callers in noisy environments (drive-throughs, call centers, home appliances)
|
||
get cut off before they finish because the STT endpointer signals end-of-turn
|
||
too aggressively.
|
||
|
||
**Detection**
|
||
- Deployment is telephony (PSTN/SIP) rather than a web client.
|
||
- Customer domain suggests a noisy environment (auto, retail, home services).
|
||
- Endpointing silence threshold left at the default.
|
||
|
||
**Impact**
|
||
- Truncated caller utterances, repeated turns, frustration.
|
||
|
||
**Fix**
|
||
- **Flag for human (org-level):** raise the endpointing silence threshold for noisy environments (typical safe range ~300–900ms; lean higher when noisy). This is a channel/STT setting, not an agent-bundle change.
|
||
- **Instructional (auto):** add an ASR repair prompt (Rule for misheard input in `voice-modality-reference.md`) so a truncated utterance recovers gracefully.
|
||
|
||
## 8. Telephony transport hops (SIP / multi-region)
|
||
|
||
Every trunk hop and region crossing on the SIP path adds latency. Largely
|
||
outside the agent bundle's control, but worth flagging.
|
||
|
||
**Detection**
|
||
- Deployment metadata mentions multiple trunks, a non-local region, or a third-party telephony proxy.
|
||
|
||
**Fix**
|
||
- **Flag for human only.** Not an agent-bundle-level fix.
|
||
|
||
---
|
||
|
||
## Automatable checks
|
||
|
||
Grep the `.agent` bundle (and, for `/agentforce-observe`, cross-reference the
|
||
trace) for these:
|
||
|
||
| Pattern | Meaning | Severity | Fix class |
|
||
|---|---|---|---|
|
||
| Mutating action (name/description implies write) runs synchronously in the turn | Sync write on critical path | high | flag (+ ack phrase auto) |
|
||
| Retrieval action returns > ~5 items / > ~2KB with no `limit` parameter | Bulky retrieval | high | flag |
|
||
| Action implementation makes 2+ external HTTP calls | Chained callout | medium | flag |
|
||
| Several subagents that only route to one other subagent | Over-routing / dead hub | medium | flag |
|
||
| Response templates > ~30 words, or greeting > 1–2 sentences | Nudge/long-turn risk | medium | instructional (auto) |
|
||
| Prices / phone / IDs referenced with no spoken-form rule | TTS garble | medium | instructional (auto) |
|
||
| Slow action (SOQL, external HTTP, retrieval) with no ack phrase in instructions | Missing filler | high | instructional (auto) |
|
||
|
||
## What NOT to change automatically
|
||
|
||
- Synchronous writes required to happen mid-turn (payment auth, identity verification) — flag with the reason.
|
||
- Retrieval where the customer explicitly wants wide-net RAG — flag with the tradeoff.
|
||
- Endpointing / silence / nudge thresholds — these are **channel/org-level** settings, not agent-bundle.
|
||
- SIP topology — a platform/telephony decision.
|
||
|
||
## Related
|
||
|
||
- `voice-modality-reference.md` — instruction rules (ack phrases, spoken-form numbers, repair prompts, turn length).
|
||
- `actions-reference.md` — voice-safe action authoring (descriptions, parameter names, error shapes).
|
||
- `known-issues.md` — dead-hub / over-routing anti-patterns.
|
||
- `/agentforce-observe` — trace-driven latency diagnosis (Phase 1 voice checks).
|