Hi everyone,
I’m running into a persistent issue where my local custom intents, defined in custom_sentences/es/my_custom_intents.yaml, are not being processed by the Home Assistant local NLU. Instead, the command is always passed to my primary conversation agent, Google Generative AI, even though “Prefer handling commands locally” is enabled in my Assist pipeline configuration.
My Setup:
-
Home Assistant Version: 2025.4.4
-
Assist Pipeline Configuration:
-
Conversation agent: Google Generative AI
-
Prefer handling commands locally: Enabled (toggle is ON)
-
Speech-to-text (STT): faster-whisper (Spanish)
-
Text-to-speech (TTS): Google Translate (es_ES)
-
-
Language: Spanish (es)
The Goal:
I want to create a local custom intent (AddCalendarEvent) to add events to my Google Calendar using an intent_script.
custom_sentences/es/calendar_add.yml:
language: "es"
intents:
AddCalendarEvent:
slots:
resumen_evento:
type: text
cuando_evento:
type: datetime
data:
- sentences:
- "Amigo (añade|agenda|crea) [un] evento [llamado] {resumen_evento} (para|el|en) {cuando_evento}"
- "Amigo (registra|anota) [en el calendario] [el evento] {resumen_evento} (para|el|en) {cuando_evento}"
- "Amigo pon [en el calendario] {resumen_evento} (para|el|en) {cuando_evento}"
- "Amigo evento {resumen_evento} fecha {cuando_evento}"
lists:
resumen_evento:
wildcard: true
cuando_evento:
wildcard: true
configuration.yaml:
intent_script:
AddCalendarEvent:
action:
- service: calendar.create_event
target:
entity_id: calendar.my_google_calendar_id
data:
summary: "{{ resumen_evento | default('Event without title') }}"
start_date_time: "{{ cuando_evento }}" # Expecting HA to parse this string
end_date_time: >
{% set start_text = cuando_evento if cuando_evento else "now" %}
{% set start_dt_obj = as_datetime(start_text) %}
{% if start_dt_obj %}
{{ (start_dt_obj + timedelta(hours=1)).isoformat() }}
{% else %}
{{ (now() + timedelta(hours=1)).isoformat() }}
{% endif %}
speech:
text: >
{% set start_dt_obj = as_datetime(cuando_evento) %}
{% if resumen_evento and start_dt_obj %}
OK, scheduling '{{ resumen_evento }}' for {{ start_dt_obj.strftime('%A, %B %d, %Y at %-I:%M %p') }}.
{% elif resumen_evento and cuando_evento %}
Trying to schedule '{{ resumen_evento }}' for '{{ cuando_evento }}', but could not fully interpret the date/time. Please check the calendar.
{% else %}
I could not get all the details to create the event.
{% endif %}
The Problem:
Developer Tools > Sentence Parser:
When I input a test phrase like “Amigo añade evento Mi Reunión Importante para el próximo martes a las 3 de la tarde
”, the sentence parser correctly identifies my AddCalendarEvent intent and captures the slots resumen_evento and cuando_evento with the raw text.
intent:
name: AddCalendarEvent
slots:
resumen_evento: Mi Reunión Importante
cuando_evento: el próximo martes a las 3 de la tarde
# ... (details section matches) ...
match: false # Interestingly, this is often false
sentence_template: Amigo (añade|agenda|crea) [un] evento [llamado] {resumen_evento} (para|el|en) {cuando_evento}
source: custom
file: es/calendar_add.yaml
Assist Pipeline Debugger (Full Pipeline Test):
When I use the exact same phrase in the Assist pipeline debugger, the intent is NOT processed locally.
The debug output consistently shows:
// ...
"events": [
// ...
{
"type": "intent-start",
"data": {
"engine": "conversation.google_generative_ai", // <--- Goes to Gemini
"intent_input": "Amigo añade evento Mi Reunión Importante para el próximo martes a las 3 de la tarde",
"prefer_local_intents": true
}
},
{
"type": "intent-end",
"data": {
"processed_locally": false, // <--- Not processed locally
"intent_output": {
"response": {
"speech": {
"plain": {
"speech": "I am sorry, I cannot fulfill this request. I am missing the functionality to add calendar events." // Gemini's response
}
}
}
}
}
}
// ...
]
// ...
This happens even with extremely unique test phrases designed to only match my custom intent. We’ve also confirmed through various hassil errors (MissingListError, Unknown slot list type) that my custom_sentences YAML file is being read by hassil, and we’ve resolved those specific YAML formatting issues.
My Conclusion:
It seems that even though the local NLU (via the Sentence Parser tool) can recognize my custom intent, the Assist pipeline’s logic (when the primary agent is external like Google Generative AI) has a confidence threshold or prioritization that prevents my local intent from being considered a strong enough match to stop it from falling back to Google Generative AI. The match: false in the sentence parser might be a symptom of this low confidence.
Question:
-
Is there a way to increase the “priority” or “confidence threshold” for local custom intents when “Prefer handling commands locally” is enabled, and an external agent is the primary?
-
Why would the Sentence Parser tool show a successful intent recognition, but the full Assist pipeline still forwards the command to the external agent?
-
Has anyone else experienced this discrepancy between the Sentence Parser and the full pipeline behavior with prefer_local_intents?
I know I can solve this by changing the pipeline’s primary conversation agent to “Home Assistant,” but I’d like to keep Google Generative AI for its general knowledge capabilities and only have specific, well-defined local intents (like this calendar one) override it.
Thanks.