Using Grammars in ASR Requests
In grammar-based recognition, the grammar is part of every request, so how you deliver it affects latency, reliability, and what the engine can match. The decisions are practical: send the grammar text or a URL, load a grammar once and reuse it or send it each time, run one grammar or several together, and use a lexicon to reconcile what the engine hears with the form your grammar expects. This article covers those decisions for both the gRPC and MRCP interfaces, and what the platform does behind the scenes so you can make them well.
Writing grammars is covered elsewhere. To learn to author grammars, start with A SRGS Introduction and A Simple Grammar, then see Intro to Semantic Interpretation for returning structured results. For the gRPC call sequence around these requests, see ASR gRPC Integration.
Ways to supply a grammar
A grammar reaches the engine in one of four ways. The right one depends on how often the grammar changes and how often it is used.
| How | gRPC | MRCP | Best for |
|---|---|---|---|
| Inline text | inline_grammar_text in grammars | The grammar embedded in the DEFINE-GRAMMAR or RECOGNIZE body | Grammars generated per call, such as a list built from the caller’s account |
| URL | grammar_url | A URI reference in the request | Stable grammars that many requests reuse. This is far more efficient than resending the text. |
| Loaded once, referenced later | session_load_grammar, then session_grammar_label | DEFINE-GRAMMAR, then a session: URI in RECOGNIZE | Large grammars, or one grammar used repeatedly in a call |
| Built-in | builtin_voice_grammar or builtin_dtmf_grammar | A builtin: URI | Common types such as digits, dates, and currency |
Grammars cannot be loaded at the deployment (global) level, and the global_grammar_label field is deprecated. For the built-in grammars, see Builtin Grammars. For the MRCP methods, see Recognizer DEFINE-GRAMMAR and Recognizer RECOGNIZE. A URL must include its scheme, such as https://. See URIs and Capacity.
Compiling and caching
The engine cannot read a grammar until it has been compiled into a binary form. It keeps compiled grammars in a cache, so a large grammar is compiled once and reused on later requests, as long as the grammar has not changed. Most compilation is nearly instant, so you notice it only on unusually large or complex grammars.
What happens when you supply a URL
When a grammar is supplied by URL, the platform fetches it over HTTP or HTTPS, and it caches what it fetches so that a grammar requested hundreds of times a second is not downloaded every time.
- The cache is keyed by the exact URL, including the query string. A different query string is a different cache entry.
- A cached grammar is reused without any network request while it is fresh. The default freshness is 300 seconds (5 minutes).
- When the entry is stale, the platform revalidates it with a conditional request if the server supplied an
ETag. A304 Not Modifiedresponse lets it reuse the cached content without downloading the grammar again. - You can set the freshness for one grammar with a
Cache-Controlquery parameter on the URL, such as?Cache-Control=max-age%3D600. Percent-encode the equals sign.
The full mechanism, including the other URL parameters, is in HTTP Caching.
Practical guidance
- Load big grammars ahead of time. Loading a grammar before recognition starts (
session_load_grammarin gRPC,DEFINE-GRAMMARin MRCP) moves the download and compile work off the critical path of the recognition. - To force a refresh, change the URL. Because the cache is keyed by the exact URL, adding or changing a query parameter fetches a fresh copy, which is useful when you publish a new grammar version.
- Keep the load timeout in mind. A grammar that takes too long to load raises a timeout error. The limit is
grammar_settings.load_grammar_timeout_ms, and a grammar larger thangrammar_thresholdfails to load. See the ASR & Transcription Settings Reference. - Verify certificates for HTTPS grammars.
grammar_settings.ssl_verify_peeris true by default, so the server’s certificate must be trusted.
Using multiple grammars
You can use several grammars in the same request. The engine searches all of them for a match. For example, you could activate a yes/no grammar, a digits grammar, and a grammar containing the word “representative” at once, to catch whichever the caller says. In gRPC, list them in grammars, one entry for each root grammar to activate. In MRCP, specify them in the RECOGNIZE request.
| { "interaction_request": { "interaction_create_asr": { "language": "en-US", "grammars": [ { "builtin_voice_grammar": "BUILTIN_GRAMMAR_BOOLEAN", "label": "yes-no" }, { "builtin_voice_grammar": "BUILTIN_GRAMMAR_DIGITS", "label": "digits" }, { "inline_grammar_text": "#ABNF 1.0 UTF-8; language en-US; mode voice; root $root; $root = representative | agent | operator;", "label": "agent-request" } ] } } } |
Field names are shown as they appear in the API reference.
When grammars overlap
On rare occasions the same word appears in more than one grammar. If that is not intended, decide which grammar should keep the word and remove it from the others. If it is intended, the engine selects the first match it finds, and that match determines the final semantic interpretation. The other matches are still returned: they appear in the semantic_interpretations list of the result, and you can view them in the Analysis Portal.
Give each grammar a label. Each semantic interpretation reports the grammar_label of the grammar that produced it, so labels let your application tell which grammar matched. Labels can contain only letters, digits, hyphens, and underscores. To prefer one grammar over another, you can also set a grammar_weight. See Applying Grammar Weights.
Lexicons and aliases
Sometimes the engine hears a word correctly but not in the form your application or grammar expects. A lexicon lets you correct that. The most useful tool in a lexicon is the alias: it tells the platform that when it recognizes word A, it should treat it as word B. Aliases help predicted words match your grammar entries, and produce the written form of a domain term that the engine would otherwise spell differently.
The platform supports the W3C Pronunciation Lexicon Specification (PLS). Of its elements, these work in your lexicons:
| Element | Supported |
|---|---|
lexeme | Yes |
grapheme | Yes |
alias | Yes |
phoneme | No. Phonemes are not supported, because the neural recognition engine does not use them. |
Here is a lexicon with two aliases. The first replaces the recognized word “vulnerable” with “SHAKY”. The second makes the recognized text “eye phone” come out as “iPhone”:
| <lexicon version="1.0" xml:lang="en-US" alphabet="application/lumenvox"> <lexeme> <grapheme>SHAKY</grapheme> <alias>vulnerable</alias> </lexeme> <lexeme> <grapheme>iPhone</grapheme> <alias>eye phone</alias> </lexeme> </lexicon> |
Reference the lexicon from your grammar by URI, as you would an external grammar. A grammar can reference external grammars and lexicons by URL. See URIs and Capacity. For the exact syntax of the elements, see the W3C Pronunciation Lexicon Specification.
gRPC and MRCP at a glance
| gRPC | MRCP | |
|---|---|---|
| Supplying a grammar | Fields in the grammars list of interaction_create_asr | The body or URI of DEFINE-GRAMMAR or RECOGNIZE |
| Loading ahead | session_load_grammar, then session_grammar_label | DEFINE-GRAMMAR, then a session: URI |
| Multiple grammars | Several entries in grammars | Several grammars in RECOGNIZE |
| Built-in grammars | The builtin_voice_grammar enumeration | builtin: URIs |
| Settings | grammar_settings on the request | client_property.conf and MRCP headers |
| A grammar fails to load | A non-success status in the load response, or an error event on the response stream | A 407 response with a Completion-Cause header |
Troubleshooting
| Symptom | Likely cause and fix |
|---|---|
| A changed grammar is not picked up | A URL grammar is served from the cache while it is fresh (300 seconds by default). Change the URL (for example with a version query parameter), set a shorter Cache-Control max-age, or make sure the server sends an ETag so a changed file is detected. |
| The first recognition with a large grammar is slow | The grammar is being downloaded and compiled during the request. Load it ahead of time with session_load_grammar or DEFINE-GRAMMAR. |
| A grammar fails to load from a URL | Check that the platform can reach the URL, that the URL includes its scheme, and that an HTTPS certificate is trusted (ssl_verify_peer defaults to true). See Problems Loading or Using Grammars. |
| A grammar load times out | Review load_grammar_timeout_ms and the size of the grammar (grammar_threshold). |
| The result comes from the wrong grammar | The same word is in more than one grammar. The first match determines the interpretation. Remove the word from the grammar that should not own it. |
| You cannot tell which grammar matched | Set a label on each grammar and read the grammar_label of each semantic interpretation. |
| A lexicon alias has no effect | Check that the grammar references the lexicon, and that it uses only lexeme, grapheme, and alias. Phoneme elements are not supported. |
| A session grammar label is not found | Load it with session_load_grammar in the same session before you reference it. |
MRCP: DEFINE-GRAMMAR returns 407 | The grammar failed to download, failed to compile, or was supplied in an unsupported form. Check the Completion-Cause header. |
