Proposal of conversation agent tools call fully in scripts

Hi,

I wanted to share my approach to introduce tool calls to my Voice Assistant and do it fully in scripts.

So I created a script called “LLM Tool Router” where I put the tools definition with description, parameters and example, like that:

alias: LLM Tool Router
description: >-
  Routes tool calls to appropriate scripts. Call with tool_name and parameters
  (as JSON).

  AVAILABLE TOOLS:

  [test_tool_weather] Get current weather and forecasts Params: location
  (string, optional), units ("metric"/"imperial")

  [test_tool_recipe] Find recipes by ingredients or cuisine  Params: ingredients
  (array), cuisine (string), max_results (number)

  [llm_tool_music_assistant] Play music using Music Assistant Params: media_type
  (required: "track"/"album"/"artist"/"playlist"), media_id (required:
  track/album/artist/playlist name, use semicolon for multiple), artist
  (required: artist name or empty), album (required: album name or empty),
  media_description (required: description of media), media_player (optional:
  player entity), shuffle (required: true/false) Example:
  {"media_type":"artist","media_id":"Queen","artist":"Queen","album":"","media_description":"Queen
  music","shuffle":false}

  [llm_tool_vacuum_cleaner] Start vacuum cleaner to clean rooms Params: rooms
  (required: comma-separated or array - shoes/exit/entrance, hallway/corridor, living room/kitchen, bedroom, toilet/restroom, bathroom, workroom/office, service point), times (optional: 1-3, default 1)
  Example: {"rooms":"kitchen, bedroom","times":2}
  
fields:
  tool_name:
    description: Tool name from available tools list
    required: true
    selector:
      text: null
  parameters:
    description: JSON parameters for the tool
    required: false
    default: "{}"
    selector:
      text: null
sequence:
  - action: script.{{ tool_name }}
    data:
      parameters: "{{ parameters }}"
    response_variable: tool_result
  - action: assist_satellite.announce
    metadata: {}
    target:
      entity_id: assist_satellite.home_assistant_voice_assist_satellite
    data:
      preannounce: true
      message: "{{tool_result if tool_result else 'Done.'}}"
  - stop: "{{ tool_result if tool_result else 'Done.' }}"
    enabled: true
mode: queued

This is the only script I expose The Voice Assistant. Tool scripts are not exposed. Conversation agent decides what tool to use and then the script runs this rule by name.

Each tool is actually another script with the it’s own descriptive name.
The tools are normal scripts. Each needs to have the same part to decode the parameters from JSON, so for example the file script llm_tool_vacuum_cleaner starts with:

alias: LLM Tool - Vacuum Cleaner
description: >-
  Starts the vacuum cleaner to clean specified rooms.

  Parameters (JSON):  
  - rooms: (required) Comma-separated room names or array.
  Valid: shoes/exit/entrance, hallway/corridor, living room/kitchen, bedroom, toilet/restroom, bathroom, workroom/office, service point. 
  Use "shoes, hallway, living room, bedroom, toilet, bathroom, office" for whole apartment.  
  - times: (optional) Number of cleaning passes per room (1-3), default is 1

  Example: {"rooms":"kitchen, bedroom","times":2}

fields:
  parameters:
    selector:
      text: null
    required: true
mode: single
icon: mdi:robot-vacuum
sequence:
  - variables:
      params: "{{ parameters | trim | from_json(default={}) }}"
      rooms_input: "{{ params.rooms | default('') }}"
      times: "{{ params.times | default(1) | int }}"

and the rest of the code goes like any other script.

I plan to create other tool script to get and parse all temperatures sensor readings from the house, so I don’t have to expose lots of temperatures sensors your Voice Assistant. I hope that it will increase the responsiveness.
And separate tool for all TRVs.

Any script or automation that has lots of temlating inside can be re-created as a tool and its description can be added to “LLM Tool Router”.

The thing I struggle with right now is returning the data from tool to the “LLM Tool Router” so the conversation agent can process the data or combine several tools data and decide on responses or actions.

What do you think?
Do you have any suggestions how to return data from one script to caller script call handle it?

I saw in Voice Assistant Debug that HASS team prepares some native way of tool call. Can’t wait for it to be introduced.

Why do you want to route through one tool script. I wouldn’t even THINK of trying to do this for many reasons. But before that. Why?

(and no there will be no efficiency because you’re now doing two calls for what should be one. Every. Single. Time.)

Also what do you mean by some way of tool call. Assist allows you to expose any script you want?