Local Custom Intent (sentences.ini / custom_sentences) Not Being Processed - Falls Back to Primary Agent (Google Generative AI) Despite prefer_local_intents: true

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:

  1. 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?

  2. Why would the Sentence Parser tool show a successful intent recognition, but the full Assist pipeline still forwards the command to the external agent?

  3. 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.

Sorry if I didn’t catch that, but just to clarify: the intent works if your primary conversation agent is set to home assistant?

Hi Lasekater and thanks.

No, that’s the puzzling part. Even when I switch the pipeline’s primary conversation agent to “Home Assistant,” and the pipeline debug log confirms engine: conversation.home_assistant and processed_locally: true, I still get a “Sorry, I couldn’t understand that” response.

However, the “Developer Tools > Sentence Parser” does correctly identify my custom intent (AddCalendarEvent) and its slots when I input the same test phrase. This discrepancy between the Sentence Parser and the full local pipeline behavior is what’s confusing.

Could you tell me in which documentation you looked up the need to specify this data?

It’s not very clear how your code should handle this variable
cuando_evento: el próximo martes a las 3 de la tarde

Your intent cannot execute (as evidenced by “Sorry, I couldn’t understand that” ) and is passed to the LLM for a response

1 Like

Hi mchk and thanks for your time as well.

You are truth, slots part is not needed (I played a lot with this trying to make it work, im not sure where I saw it).

So, the case is:

  1. Sentence Parser can identify the intent
  2. Although the intent is identified, arguments aren’t so the pipeline fails
  3. As the pipeline failed, is sent to the LLM

Is this correct? So I have to focus on fix the arguments parsing

Yeah, that’s right.
Developer Tools >Templates are the best helper in testing logical constructs that are then used in scripts