You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/learnings/assistants.md
+40Lines changed: 40 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -62,8 +62,48 @@ If omitted, transcripts with confidence below 0.4 may be **ignored** entirely (n
62
62
63
63
[Smart Denoising (Krisp)](https://docs.vapi.ai/documentation/assistants/conversation-behavior/background-speech-denoising#smart-denoising-krisp) is recommended over [Fourier Denoising](https://docs.vapi.ai/documentation/assistants/conversation-behavior/background-speech-denoising#fourier-denoising-experimental) (experimental). Enable it via `backgroundDenoisingEnabled: true` or `smartDenoisingPlan.enabled: true`.
64
64
65
+
### Custom keyword/keyterm boosting
66
+
65
67
Boost domain-specific vocabulary with [Custom Keywords](https://docs.vapi.ai/customization/custom-keywords) to improve recognition of brand names, product names, and industry terms.
66
68
69
+
**Nova-3 uses `keyterm` (not `keywords`).** The legacy `keywords` field only works on Nova-2 and older models. For Nova-3, use `keyterm` — an array of words or multi-word phrases (no intensifiers). Supports up to 100 terms (~500 tokens).
70
+
71
+
**`keyterm` works in multilingual mode.** As of November 2025, `model: nova-3` with `language: multi` supports keyterm prompting. Previously this combination returned a 400 error.
72
+
73
+
```yaml
74
+
transcriber:
75
+
provider: deepgram
76
+
model: nova-3
77
+
language: multi
78
+
keyterm:
79
+
- your-brand-name
80
+
- industry-specific-term
81
+
- product-name
82
+
- technical-acronym
83
+
```
84
+
85
+
### Pronunciation dictionaries (TTS-level)
86
+
87
+
Pronunciation dictionaries control how TTS voices say specific words. They are **provider-specific**:
88
+
89
+
| Provider | Support | Config field | Model requirement |
| **Cartesia** | Full IPA + sounds-like across all languages | `pronunciationDictId` on voice config | `sonic-3` only |
92
+
| **ElevenLabs** | Phoneme rules (IPA/CMU, English only) + alias rules (all languages) | `pronunciationDictionaryLocators` on voice config | Phoneme: `eleven_turbo_v2`, `eleven_flash_v2`. Alias: all models |
93
+
| **Vapi built-in** | None | N/A | N/A |
94
+
95
+
**Cartesia pronunciation dictionaries** are created via the Vapi API (`POST /provider/cartesia/pronunciation-dictionary`), then referenced by ID in the voice config. This is the same pattern as `credentialId` — the provider resource lives outside gitops, the reference is gitops-managed.
96
+
97
+
```yaml
98
+
voice:
99
+
provider: cartesia
100
+
model: sonic-3
101
+
voiceId: your-voice-id
102
+
pronunciationDictId: pdict_xxxxxxxxxxxxx
103
+
```
104
+
105
+
**Recommendation:** For multilingual use cases, Cartesia sonic-3 with a pronunciation dictionary is the strongest option — IPA works across all languages. Combine with prompt-level pronunciation rules (belt-and-suspenders) and transcriber `keyterm` for a three-layer approach: TTS output, LLM text generation, and STT input.
106
+
67
107
### `smartEndpointingPlan` **owns** turn detection when set
68
108
69
109
If you configure `startSpeakingPlan.smartEndpointingPlan`, the transcriber's own `endpointing` settings (voice activity detection timeouts, etc.) are **not used** for turn detection. Smart endpointing takes full control.
| Best multilingual + pronunciation control | IPA dictionaries across all languages | Cartesia Sonic-3 with `pronunciationDictId`|
127
128
128
129
For conversation, **responsiveness almost always wins over voice quality**. A slight quality reduction that saves 100ms of Time to First Audio Byte is worth it in most use cases.
130
+
131
+
**Cartesia Sonic-3** is Vapi's default voice provider — sub-200ms latency across 42 languages with pronunciation dictionary support. For multilingual use cases needing pronunciation control, it's the strongest choice.
Copy file name to clipboardExpand all lines: docs/learnings/multilingual.md
+9-3Lines changed: 9 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -211,17 +211,23 @@ transcriber:
211
211
provider: deepgram
212
212
model: nova-3
213
213
language: multi
214
+
keyterm: # Nova-3 multilingual supports keyterm since Nov 2025
215
+
- your-brand-name
216
+
- domain-specific-term
214
217
215
218
voice:
216
-
provider: eleven-labs
217
-
model: eleven_multilingual_v2
219
+
provider: cartesia
220
+
model: sonic-3
218
221
voiceId: your-voice-id
222
+
pronunciationDictId: pdict_xxx # IPA works across all languages
219
223
220
224
model:
221
225
provider: openai
222
226
model: gpt-4.1
223
227
```
224
228
229
+
**Alternative voice:** ElevenLabs `eleven_multilingual_v2` if you need ElevenLabs-specific features (alias rules work across all languages, but IPA phoneme rules are English-only).
| Agent understands Spanish but speaks English | TTS voice is English-only | Use multilingual TTS (ElevenLabs multilingual_v2, OpenAI) |
249
+
| Agent understands Spanish but speaks English | TTS voice is English-only | Use multilingual TTS (Cartesia sonic-3, ElevenLabs multilingual_v2, OpenAI) |
244
250
| Tool messages always in English | Active language defaults to `"en"` with `"multi"` STT | Use `contents[]` with explicit language variants |
245
251
| Spanish STT accuracy worse than dedicated | Multi-language models trade accuracy for flexibility | Use dedicated per-language assistants (Approach 2) |
246
252
| Self-handoff infinite loop | LLM re-triggers handoff after seeing same conversation | Clear prompt: "Do not trigger language switch if already in correct language"|
**Voice mode (`vapi.websocket`)** exercises the full STT/TTS pipeline — use for latency testing and realistic end-to-end validation. **Chat mode (`vapi.webchat`)** is faster and cheaper — use for rapid iteration on outcome evaluations.
68
+
69
+
**Squad simulations test the full stack**: all tools attached to squad members (KB lookups, logging, email tools) actually fire during the simulation. If a tool endpoint is down, the simulation produces different results. Factor this into test design.
70
+
71
+
---
72
+
73
+
## A/B Testing Squads with Simulations
74
+
75
+
Run the same simulation suite against two squad variants and compare results:
76
+
77
+
1. Create both squad variants as separate resources (e.g., Squad A vs Squad B)
78
+
2. Run the same suite against each with identical iterations
79
+
3. Compare pass/fail rates on structured output evaluations
80
+
4. For latency comparison, use voice mode and analyze `end-of-call-report` webhook data from each run
81
+
82
+
**Limitation:** Simulations evaluate outcomes via structured output comparators, not platform-level metrics. There is no built-in p95 latency or dead-air measurement. For quantitative latency data, pipe `end-of-call-report` webhooks to your own analytics and compute metrics from `artifact.performanceMetrics`.
83
+
84
+
---
85
+
86
+
## Structured Outputs in Simulation Evaluations
87
+
88
+
Scenario evaluations reference structured outputs either by `structuredOutputId` (existing resource) or inline via `structuredOutput`. The key constraint: **evaluation schemas must use primitive types** (`string`, `number`, `integer`, `boolean`).
89
+
90
+
If your post-call analytics structured output uses `type: object` with nested schemas, you **cannot** use it directly as a scenario evaluation. Instead, create separate primitive-typed structured outputs for each evaluation criterion (e.g., `eval-call-outcome` as `type: string`, `eval-goal-achieved` as `type: boolean`). The full analytics output can still run via the squad's `artifactPlan.structuredOutputIds` — it just can't be used with comparators.
91
+
92
+
---
93
+
94
+
## Simulation File Names After Push
95
+
96
+
Simulation resource files use placeholder UUIDs (`a0000000`) locally. After the first push, the gitops engine creates platform resources and maps local filenames to platform UUIDs in `.vapi-state.<env>.json`. On subsequent state syncs (bootstrap), filenames may be updated to include the platform name — this triggers `name_mismatch` warnings that are resolved automatically by re-running bootstrap.
Copy file name to clipboardExpand all lines: docs/learnings/squads.md
+30Lines changed: 30 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -99,3 +99,33 @@ The final assistant configuration is built by merging these layers in order:
99
99
Later layers win on conflicts. `variableValues` from all layers are merged separately.
100
100
101
101
**Gotcha:** Liquid template substitution replaces undefined variables with **empty strings**, not errors. If a variable isn't in the merged `variableValues`, any `{{ myVar }}` reference silently becomes `""`.
102
+
103
+
---
104
+
105
+
## toolIds in assistantOverrides require UUIDs
106
+
107
+
`assistantOverrides.model.toolIds`in squad members must use **Vapi platform UUIDs**, not local filenames. The gitops engine resolves filenames to UUIDs for base assistant `model.toolIds`, but it does **not** resolve them inside squad `assistantOverrides`. If you use a local filename, the push will fail with `each value in toolIds must be a UUID`.
108
+
109
+
**Workaround:** Look up the tool's UUID from `.vapi-state.<env>.json` and use it directly.
110
+
111
+
```yaml
112
+
# WRONG — filename, will fail in assistantOverrides
113
+
assistantOverrides:
114
+
model:
115
+
toolIds:
116
+
- my-tool-name
117
+
118
+
# CORRECT — platform UUID
119
+
assistantOverrides:
120
+
model:
121
+
toolIds:
122
+
- a1b2c3d4-e5f6-7890-abcd-ef1234567890
123
+
```
124
+
125
+
---
126
+
127
+
## FAQ agent consolidation pattern
128
+
129
+
When a squad has multiple specialist agents that each carry one knowledge base tool, the LLM must correctly classify and route the question before it even reaches a KB. If the routing is wrong, the KB returns "I don't have enough information" — not because the knowledge doesn't exist, but because the wrong KB was queried.
130
+
131
+
**Fix:** Consolidate specialist agents into a single FAQ agent with access to all KB tools. The FAQ agent's LLM picks the right tool based on improved tool descriptions with explicit routing boundaries and "Do NOT use for..." cross-references. This eliminates the routing classification step from the handoff layer and moves it to the tool selection layer, where descriptions give the LLM more direct guidance.
Copy file name to clipboardExpand all lines: docs/learnings/tools.md
+21Lines changed: 21 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -199,6 +199,27 @@ An unrecognized message type is coerced to `request-failed` with default error c
199
199
200
200
After one delayed message plays, another won't play for at least 2.5 seconds — even if multiple timing thresholds are crossed. Missing `timingMilliseconds` defaults to the 0ms bucket.
201
201
202
+
### Dead air during KB/API tool calls
203
+
204
+
If a tool has no `request-start` content (or empty content), the caller hears silence while the tool executes. For knowledge base tools and API requests that take 2–5 seconds, this feels like dead air.
205
+
206
+
**Fix with two layers:**
207
+
208
+
1.**`request-start`** with `blocking: false` — speaks a filler line ("Good question — let me look that up") in parallel with the tool call starting.
209
+
2.**`request-response-delayed`** at 4000ms — safety net if the tool takes longer than expected.
210
+
211
+
```yaml
212
+
messages:
213
+
- type: request-start
214
+
content: "Good question — let me look that up."
215
+
blocking: false
216
+
- type: request-response-delayed
217
+
content: "Still looking that up for you."
218
+
timingMilliseconds: 4000
219
+
```
220
+
221
+
Optionally add prompt-level instructions ("say a brief acknowledgment before calling the tool") as a belt-and-suspenders approach — the prompt handles cases where the LLM speaks before calling the tool, while the tool message handles cases where the LLM calls the tool silently.
0 commit comments