Fallback Pipeline¶
Maturity — Mature ⬤⬤⬤⬤⬤
Long-lived, battle-tested, and actively maintained. Depend on it freely. Rated by repository health, not version.
In a nutshell
When you say something and none of your assistant's regular skills know how to respond, the fallback pipeline is the safety net. It tries one last set of "catch-all" skills so the assistant still says something instead of going silent. Think of it as the help desk that gets your question only after everyone else has passed on it. It asks these backup skills in a set order until one of them handles the request. See the Converse Pipeline for what runs before this, or the Glossary for terms.
This is a flow stage: part of every standard pipeline rather than a matcher you choose between.
The rest of this page is for people deploying or customizing OVOS. If you only wanted to know what this stage does, you are done.
📐 Formal specification
The fallback plugin is specified by OVOS-FALLBACK-1 — Fallback Pipeline Plugin, built on OVOS-PIPELINE-1. See the spec index.
The Fallback Pipeline in OpenVoiceOS (OVOS) manages how fallback skills are queried when no primary skill handles a user's utterance. It coordinates multiple fallback handlers, so the system still attempts to respond even when regular intent matching fails.
How the spec frames it
A fallback skill declares no intent patterns. Instead it receives the raw utterance at query time and decides for itself whether it can respond (FALLBACK-1 §2). This is the right pattern for open-domain QA, LLM completions, and any coverage that cannot be modelled as a grammar.
The plugin builds an ordered handler pool from each skill's registered priority and the session preference session.fallback_handlers (FALLBACK-1 §4; current ovos-core orders purely by registered priority and does not yet consult session.fallback_handlers). It queries pool members one at a time via ovos.skills.fallback.ping / .pong, and returns a Match on the reserved intent_name fallback (PIPELINE-1 §7.3) targeting the first willing skill, dispatched on ovos.skills.fallback.{skill_id}.request. If the pool is exhausted it returns None, and the orchestrator emits ovos.intent.unmatched.
The high/medium/low stages below are one fallback plugin loaded at several pipeline positions, each restricted to a priority range (FALLBACK-1 §8.2).
Implementation¶
Module: ovos_core.intent_services.fallback_service.FallbackService
Pipeline plugin ID: ovos-fallback-pipeline-plugin
Stage names: ovos-fallback-pipeline-plugin-high, ovos-fallback-pipeline-plugin-medium, ovos-fallback-pipeline-plugin-low (deprecated aliases: fallback_high, fallback_medium, fallback_low)
FallbackService subclasses ConfidenceMatcherPipeline, so the single base ID auto-expands into the three match_high/match_medium/match_low matchers exposed as ovos-fallback-pipeline-plugin-high, -medium, and -low. It ships inside ovos-core:
[project.entry-points."opm.pipeline"]
ovos-fallback-pipeline-plugin = "ovos_core.intent_services.fallback_service:FallbackService"
Pipeline Stages¶
| Pipeline ID | Priority Range | Description | Use Case |
|---|---|---|---|
ovos-fallback-pipeline-plugin-high |
0 < p ≤ 5 |
High-priority fallback skills | Critical fallback handlers |
ovos-fallback-pipeline-plugin-medium |
5 < p ≤ 90 |
Medium-priority fallback skills | General fallback skills |
ovos-fallback-pipeline-plugin-low |
90 < p ≤ 101 |
Low-priority fallback skills | Catch-all or chatbot fallback skills |
Each matcher filters registered fallbacks with range.start < priority ≤ range.stop (exclusive start, inclusive stop). Lower priority numbers run first. A fallback that registers without a priority defaults to 101, placing it in the low tier. Priorities can be overridden by users via config.
How It Works¶
flowchart TD
U["Utterance<br/>(all matchers failed)"] --> H["-high<br/>(0 < p ≤ 5)"]
H -- no willing skill --> M["-medium<br/>(5 < p ≤ 90)"]
M -- no willing skill --> L["-low<br/>(90 < p ≤ 101)"]
H -- willing skill --> D["ovos.skills.fallback.<br/>{skill_id}.request"]
M -- willing skill --> D
L -- willing skill --> D
L -- no willing skill --> N["ovos.intent.<br/>unmatched"]
Diagram: an utterance that failed all other matchers is tried against high, then medium, then low priority fallback skills, dispatching to the first willing skill, or ending as ovos.intent.unmatched if none accept it.
-
A fallback stage is hit in the pipeline (after all other matchers fail)
-
FallbackService.match_high/medium/low()filters registered fallbacks to the stage's priority range -
It pings candidates via
ovos.skills.fallback.ping(carrying the priorityrangeand afallback_request_id) and collectsovos.skills.fallback.pongacknowledgements (can_handle, echoing the samefallback_request_id) within ~0.5s. The request id lets the service ignore a pong that answers a stale or concurrent poll round, instead of the one it is waiting on. -
Candidates are sorted by priority ascending; the winning match dispatches to that skill via
ovos.skills.fallback.{skill_id}.request -
First skill that handles the utterance wins — it is consumed
-
If no fallback skill accepts the utterance, no fallback response is generated
Skill Integration¶
Skills integrate as fallbacks by:
-
Inheriting from
FallbackSkilland decorating one or more handlers with@fallback_handler(priority=...)(lower number = higher priority; default50) -
Implementing
can_answer(self, message)to declare upfront whether the skill is willing to try (utterances are inmessage.data["utterances"]) — this is mandatory, the method is abstract and a skill without it does not load -
On startup
ovos-workshopregisters the skill's lowest handler priority with the service viaovos.skills.fallback.register -
Returning
Truefrom a handler when it consumes the utterance,Falseto pass to the next fallback
from ovos_workshop.skills.fallback import FallbackSkill
from ovos_workshop.decorators import fallback_handler
class MyFallback(FallbackSkill):
def can_answer(self, message) -> bool:
return True # always willing to try
@fallback_handler(priority=50)
def handle_fallback(self, message):
self.speak("I don't know, but I tried.")
return True # consumed
This lets you customize fallback behavior for your skill ecosystem.
Configuration¶
{
"skills": {
"fallbacks": {
"fallback_priorities": {
"my-skill-id": 10
},
"fallback_mode": "accept_all",
"fallback_whitelist": [],
"fallback_blacklist": []
}
}
}
| Config Key | Description |
|---|---|
fallback_priorities |
Override developer-defined priorities per skill ID |
fallback_mode |
"accept_all", "whitelist", or "blacklist" |
fallback_whitelist |
Skills allowed to act as fallbacks (when mode is whitelist) |
fallback_blacklist |
Skills blocked from fallback (when mode is blacklist) |
FallbackMode¶
| Mode | Description |
|---|---|
accept_all |
Default — any registered skill can act as fallback |
whitelist |
Only skills in fallback_whitelist can act as fallback |
blacklist |
All skills can act as fallback except those in fallback_blacklist |
Bus Events Handled¶
| Event | Handler |
|---|---|
ovos.skills.fallback.register |
handle_register_fallback |
ovos.skills.fallback.deregister |
handle_deregister_fallback |
ovos.skills.fallback.ping |
Skill-side: _handle_fallback_ack replies with ovos.skills.fallback.pong |
ovos.skills.fallback.pong |
Service-side: handle_ack collects a skill's can_handle reply |
ovos.skills.fallback.{skill_id}.request |
Skill-side: _handle_fallback_request actually runs the winning skill's fallback handlers |
Which skill a registration acts on¶
Both ovos.skills.fallback.register and ovos.skills.fallback.deregister act on
the skill_id in the message payload. That field names the target: the skill
whose fallback handler is added to or removed from the registry.
A message also carries context.skill_id, which names the source that
emitted it. The two are the same whenever a skill registers itself, which is the
ordinary case, and ovos-workshop emits the registration that way on startup.
They are not required to match. The service indexes under the payload value, does
not substitute the context value for it, and rejects nothing on the strength of a
difference or of a missing context skill_id — a source that is not a skill at
all, such as a provisioning tool, registers on a skill's behalf. A plugin logs
both at DEBUG when they differ.
Removing another skill's fallback handler is a form of remote uninstall, so a deployment may block cross-skill messages as hardening. The shape of that policy is left to the deployment.
Notes¶
-
The pipeline itself does not define or enforce a default fallback response.
-
The separate
ovos-skill-fallback-unknownskill implements the default "I don't understand" reply. -
This design lets developers create custom fallback strategies or add fallback chatbot skills without changing the core pipeline.
-
Fallback skills should implement some dialog if they consume the utterance.
Security¶
As with converse, a badly designed or malicious skill can hijack the fallback skill loop. This is less serious than with converse, but the pipeline still provides protections:
-
You can configure what skills are allowed to use the fallback mechanism via
fallback_mode. -
fallback_prioritieslets users adjust priorities when the default values don't fit the installed skill collection.
Source code: OpenVoiceOS/ovos-core.
Read next: Adapt Pipeline Related: Fallback Skill · OCP Pipeline · Converse Pipeline