Nick Jantz

  • Adding Live Sports Scores to a Voice Assistant using SerpAPI and an LLM

    Background

    In 2023 Home Assistant focused their efforts on the 'Year of the Voice' in an attempt to bring more and better voice control to the already great Home Assistant platform. Combined with the ability to use LLMs in integration with their voice assistant this gives some great features for making a self hosted voice assistant that can do a lot with minimal custom coding.

    One major limitation of many LLMs is they're only as good as their knowledge cutoff. Which makes answering up to date or even questions about live events impossible. A growing number have the ability to search the web and use that data, but right now the voice assistant integrations with them limit that.

    I often ask my Alexa questions around the current score of my favorite sports teams or when their next game is, and getting an LLM to answer this question accurately proves to be a challenge. Today I'll show you how I managed to solve this problem using a few simple Home Assistant scripts.

    Setup

    The most basic setup for this needs the following tools:

    Goal and Method

    My end goal for this will be to have a locally hosted voice assistant that can answer questions about the current score and game status of any sports teams I ask it about.

    To do this I will use the Sports Results card API from SerpAPI to gather the latest sports results and feed it into my LLM and have it return a natural language answer to my original question.

    Idea Validation

    Before starting a project I like to come up with an idea and validate that it will work by going through each step manually. I've come up with the following flowchart of how this will work.

    1. The first step is, given a question, we need to return a google search term that will return a sports card showing the answer to our question. This is pretty easy using an LLM and a basic prompt:

    1. Take the previous search keyword and feed it into the SerpAPI sports results endpoint. We should end up with a URL like this:

    https://serpapi.com/search.json?q=Real+Madrid+Score&api_key=YOUR_API_KEY

    When you make the GET call to this URL your response result will be a large json object with everything from the search result page. The key portion of this data is in the 'sports_results' key. You can see a sample search result here.

    1. Following this you can feed that data into another LLM and with a bit of prompt magic you will get a natural language sounding result to your original question.

    You can choose exactly how much data you want to feed into the prompt, however for my purposes I wanted to include as much as possible so I chose to send the entire 'sports_results' object. I found that when I would try to limit it sometimes the answers would come back weird as the LLM did not have information such as who was the home and away team. When it has all the data it is usually able to infer things like home and away team based on which stadium the game is being played at. This is a perfect example of why feeding this to an LLM makes a lot of sense, it already has a wealth of knowledge at it's disposal to add additional context to it's understanding of the raw JSON data.

    Looks like everything is working as we'd like, minus some prompt refining, we can go ahead and make a home assistant script that will handle the above question.

    Writing the Home Assistant Integration

    Now comes the fun part, integrating it into home assistant. The following instructions assume the following:

    1. You have home assistant with a voice assistant active
    2. Your voice assistant has an integration with an LLM
      • Unfortunately there's no tutorial on how to do this, however you can follow this tutorial on how to create a personality for you voice assistant and leave out step 4 about modifying the initial prompt to allow for you voice assistant to integrate with an LLM.
    3. You have some method to hand off your SerpAPI key to the script that makes the actual API call. I've chosen to use a input text helper however, you can chose to hardcode it into your script or anything else you feel comfortable with

    Script Title and Description

    One thing a lot of people don't realize is that you don't have to write any sort of special commands sentance or anything like that to get a voice assistant to actually trigger a custom script in home assistant. As long as the script is exposed to the voice assistant and it is descriptive enough, the assistant will know to trigger it. For this case, I've decided to pack my description and title with keywords:

    alias: Current Sports Scores and Schedule Script
    description: >-
      This script uses live data to get current sports scores and schedule
      information for a variety of sports
    

    The script needs a default field that is going to get passed as the question:

    fields:
      question:
        selector:
          text: null
        name: Question
        required: true
        default: What is the score of the cubs game?
    

    The default value can be left blank, however I use it as a fall back to ensure the LLM gets some sort of valid data and doesn't end up halluclinating with nothing that results in a strange sentence getting read back to the user.

    Script Steps:

    Following the exact same steps as above, just in a yaml that home assistant understands.

    1. Get a search term using an LLM:

      - action: conversation.process
        metadata: {}
        data:
          agent_id: conversation.chatgpt
          text: >
            Given the following question, return a team name that you could google
            to get the answer to the question. This info will be fed into an API, so
            only give the text of the team name, no other info or text
    
            Example Question: What is the score of the cubs game?
    
            Example Output: Chicago Cubs Score
    
            Real Question: 
        response_variable: serp_keyword
    

    This just creates a standalone chatgpt process(my LLM for this example) and feeds it the prompt we used above. It then saves the response variable as serp_keyword so it's accessible to the rest of the script.

    2. Hit the proper SerpAPI endpoint for that search term:

      - action: rest_command.fetch_sports_scores
        data:
          team: "" 
        response_variable: serp_data
    

    For this step it uses a custom rest command script that accepts the value of the serp_keyword as a parameter to fill into the right API endpoint.

    I've also added the following script to my home assistant config file:

    rest_command:
      fetch_sports_scores:
        url: "https://serpapi.com/search.json?q={{ team }}&api_key={{ states('input_text.serpapi_api_key') }}"
        method: "GET"
        timeout: 30
        headers:
          Content-Type: "application/json"
    

    3. Ask the LLM to give me an answer to my question based on the data:

      - action: conversation.process
        metadata: {}
        data:
          agent_id: conversation.chatgpt
          text: >
            Given the following data, please provide a relevant answer to the
            question '' include any relevant information, such as
            score if the game is happening or future and/or past games if there's
            not one happening now. Data: 
        response_variable: answer
      - stop: Finished
        response_variable: answer
    

    Again this time, just starting a standalone chatgpt process that asks for the answer to the given question. The only home assistant extra is to put a hard stop at the end of the script and pass the response_variable to the voice assistant.

    Done!

    Just wrap the script in the sequence tag and you'll have a complete script that should look like this:

    alias: Current Sports Scores and Schedule Script
    description: >-
      This script uses live data to get current sports scores and schedule
      information for a variety of sports
    fields:
      question:
        selector:
          text: null
        name: Question
        required: true
        default: What is the score of the cubs game?
    sequence:
      - action: conversation.process
        metadata: {}
        data:
          agent_id: conversation.chatgpt
          text: >
            Given the following question, return a team name that you could google
            to get the answer to the question. This info will be fed into an API, so
            only give the text of the team name, no other info or text
    
            Example Question: What is the score of the cubs game?
    
            Example Output: Chicago Cubs Score
    
            Real Question: 
        response_variable: serp_keyword
      - action: rest_command.fetch_sports_scores
        data:
          team: ""
        response_variable: serp_data
      - action: conversation.process
        metadata: {}
        data:
          agent_id: conversation.chatgpt
          text: >
            Given the following data, please provide a relevant answer to the
            question '' include any relevant information, such as
            score if the game is happening or future and/or past games if there's
            not one happening now. Data: 
        response_variable: answer
      - stop: Finished
        response_variable: answer