Pico assistant: now with Google search capabilities

I posted about this project before, but I’ve since made some significant improvements.

Github here: https://github.com/sgreenb/pico_assistant

One extremally simple modification dramatically improves the ability of a GPT to answer questions: letting it Google stuff.

Here’s a demo:


The implementation works like this.

  1. A user enters an input.
  2. An agent called “Executive” looks at the input and decides if an API like Spotify, Twillio, or Gmail is needed or if it can be answered by the chatbot alone.
  3. If the chatbot is needed the input is first sent to a Google agent. The Google agent’s system message looks like this:
{"role":"system", "content": "You analyze a user's input to a large language model with \
training data that cuts off at September 2021. The current year is 2023. You decide how \
likely it is that a user's request will benefit from a Google search to help address the\
question. Respond with a number in the range 1-10, where 1 is very unlikely that a \
Google search would be beneficial, and 10 meaning a Google search is highly necessary."}

This is quite fast, since it only needs to generate one or two tokens.

If the output is above some threshold (say 7), then we call another agent, the query agent, otherwise we return False and default to the normal chat agent.

    google_probability = int(completion.choices[0].message.content)
    if google_probability >= cutoff:
        search_results = trim_text(search_and_scrape(prompt))
        query_with_context = prompt + str(search_results)
        print("\nPico: ", end='', flush=True)
        response = query_agent_stream(query_with_context)
        return response
        return False

When we call the query agent, we feed it the first part of a Google search we get from searching the input. We get that from the very simple trim_text and search_and_scrape functions that look like this:

def search_and_scrape(query):
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"
        url = f"https://www.google.com/search?q={query}"
        response = requests.get(url, headers=headers)

        if response.status_code == 200:
            soup = BeautifulSoup(response.text, 'html.parser')
            text = soup.get_text()
            cleaned_text = ' '.join(text.split())
            return cleaned_text
            print(f"Failed to fetch search results for query: {query}, status code: {response.status_code}")
            return None

    except Exception as e:
        print(f"Error fetching search results for query: {query}, error: {e}")
        return None

def trim_text(text, start_index = 450, length=1500):
    return text[start_index:start_index + length]

The query agent has this system message:

{"role":"system", "content": "You answer a user's question, given some text as context to help\
answer the question. The user request will be followed by the context. The context given is\
from the user's Google search results, it is current and up to date.\
Do not contradict the contents of the given text in your answer."}

And that’s it. You can change the cutoff threshold or get more sophisticated with fetching web results. I hope you find this useful.