Llamada a herramientas con Ollama

Autor

Joshua Noble

Data Scientist

Llamada a herramientas con Ollama

La llamada a herramientas en modelos de lenguaje de gran tamaño (LLM) es la capacidad del LLM para interactuar con herramientas externas, servicios o API para realizar tareas. Esto permite a los LLM ampliar su funcionalidad, mejorando su capacidad para manejar tareas del mundo real que pueden requerir acceso a datos externos, información en tiempo real o aplicaciones específicas. Cuando un LLM utiliza una herramienta de búsqueda web, puede llamar a la web para obtener datos en tiempo real que no están disponibles en los datos de entrenamiento del modelo. Otros tipos de herramientas pueden incluir Python para cálculos, análisis de datos o visualización, o llamar a un endpoint de servicio para obtener datos. Las llamadas a herramientas pueden hacer que un chatbot sea más dinámico y adaptable, lo que le permite proporcionar respuestas más precisas, relevantes y detalladas basadas en datos en vivo o tareas especializadas fuera de su base de conocimientos inmediata. Los marcos populares para la llamada a herramientas incluyen Langchain y ahora ollama.

Ollama es una plataforma que ofrece modelos de IA locales de código abierto para su uso en dispositivos personales, de modo que los usuarios puedan ejecutar LLM directamente en sus ordenadores. A diferencia de un servicio como la API de OpenAI, no hay necesidad de una cuenta, ya que el modelo está en su máquina local. Ollama se centra en la privacidad, el rendimiento y la facilidad de uso, lo que permite a los usuarios acceder e interactuar con modelos de IA sin enviar datos a servidores externos. Esto puede ser particularmente atractivo para aquellos preocupados por la protección de datos o que desean evitar la dependencia de API externas. La plataforma de Ollama está diseñada para ser fácil de configurar y usar, y admite varios modelos, brindando a los usuarios una gama de herramientas para el procesamiento del lenguaje natural, la generación de código y otras tareas de IA directamente en su propio hardware. Se adapta bien a una arquitectura de llamada a herramientas porque puede acceder a todas las capacidades de un entorno local, incluidos datos, programas y software personalizado.

En este tutorial aprenderá a configurar la llamada a herramientas utilizando ollama para examinar un sistema de archivos local, una tarea que sería difícil de realizar con un LLM remoto. Hay muchos modelos de ollama disponibles para llamar a herramientas y crear agentes de IA como Mistral y Llama 3.2. Puede consultar una lista completa en el sitio web de Ollama. En este caso, utilizaremos IBM® Granite 3.2 Dense, que tiene soporte para herramientas. Los modelos 2B y 8B son LLM densos de solo texto entrenados y diseñados para admitir casos de uso y para la generación aumentada por recuperación (RAG), agilizando la generación de código, la traducción y la corrección de errores.

El cuaderno de este tutorial se puede descargar desde Github aquí.

Paso 1: Instalar Ollama

Primero, descargue ollama desde https://ollama.com/download e instálelo en su sistema operativo. En OSX, esto se hace mediante un archivo .dmg; en Linux, mediante un único comando de shell; y en Windows, con un instalador. Es posible que necesite acceso de administrador en su equipo para ejecutar el instalador.

Puede comprobar que ollama está correctamente instalado abriendo un terminal o símbolo del sistema e introduciendo:

ollama -v 

 

Paso 2: Instalar bibliotecas

A continuación, añada las importaciones iniciales. Esta demo utilizará la biblioteca ollama python para comunicarse con ollama y la biblioteca pymupdf para leer archivos PDF en el sistema de archivos.

!pip install pymupdf

import ollama
import os
import pymupdf

 

A continuación, extraiga el modelo que utilizará a lo largo de este tutorial. Con esto, se descargan los pesos del modelo desde ollama a su ordenador local y se almacenan para su uso sin necesidad de realizar llamadas API remotas más adelante.

!ollama pull granite3.2
!ollama pull granite3.2-vision

Paso 3: Definir las herramientas

Ahora defina las herramientas a las que tendrá acceso la instancia de ollama tools. Dado que la finalidad de las herramientas es leer archivos y examinar imágenes en el sistema de archivos local, cree dos funciones Python para cada una de esas herramientas. La primera se llamasearch_text_files y toma una palabra clave para buscar en los archivos locales. A los efectos de esta demo, el código solo busca archivos en una carpeta específica, pero podría ampliarse para incluir un segundo parámetro que establezca en qué carpeta buscará la herramienta.

Podría utilizar una simple coincidencia de cadenas para ver si la palabra clave está en el documento, pero como ollama facilita la llamada a los LLM locales,search_text_files utilizaremos Granite 3.2 para determinar si la palabra clave describe el texto del documento. Esto se hace leyendo el documento en una cadena llamadadocument_text . A continuación, la función llama a ollama.chat y solicita al modelo lo siguiente:

"Respond only 'yes' or 'no', do not add any additional information. Is the following text about " + keyword + "? " + document_text 

Si el modelo responde "sí", la función devuelve el nombre del archivo que contiene la palabra clave que el usuario indicó en la instrucción. Si ninguno de los archivos parece contener la información, la función devuelve "Ninguno" como una cadena.

Esta función puede ejecutarse lentamente la primera vez, ya que ollama descargará Granite 3.2 Dense. 

def search_text_files(keyword: str) -> str:
  
  directory = os.listdir("./files/")
  for fname in directory:
    
    # look through all the files in our directory that aren't hidden files
    if os.path.isfile("./files/" + fname) and not fname.startswith('.'):

        if(fname.endswith(".pdf")):
           
           document_text = ""
           doc = pymupdf.open("./files/" + fname)

           for page in doc: # iterate the document pages
               document_text += page.get_text() # get plain text (is in UTF-8)
               
           doc.close()

           prompt = "Respond only 'yes' or 'no', do not add any additional information. Is the following text about " + keyword + "? " + document_text 

           res = ollama.chat(
                model="granite3.2:8b",
                messages=[{'role': 'user', 'content': prompt}]
            )

           if 'Yes' in res['message']['content']:
                return "./files/" + fname

        elif(fname.endswith(".txt")):

            f = open("./files/" + fname, 'r')
            file_content = f.read()
            
            prompt = "Respond only 'yes' or 'no', do not add any additional information. Is the following text about " + keyword + "? " + file_content 

            res = ollama.chat(
                model="granite3.2:8b",
                messages=[{'role': 'user', 'content': prompt}]
            )
           
            if 'Yes' in res['message']['content']:
                f.close()
                return "./files/" + fname

  return "None"

La segunda herramienta se llama buscar_imagen_archivos_de_imagen  y toma una palabra clave para buscar en las fotos locales. La búsqueda se realiza con el modelo de descripción de imágenes Granite 3.2 Vision a través de ollama. Este modelo devolverá una descripción textual de cada archivo de imagen de la carpeta y buscará la palabra clave en la descripción. Una de las ventajas de utilizar ollama es que se pueden crear fácilmente sistemas multiagente para llamar a un modelo con otro.

La función devuelve una cadena, que es el nombre del archivo cuya descripción contiene la palabra clave que el usuario indicó en la instrucción.

def search_image_files(keyword:str) -> str:

    directory = os.listdir("./files/")
    image_file_types = ("jpg", "png", "jpeg")

    for fname in directory:

        if os.path.isfile("./files/" + fname) and not fname.startswith('.') and fname.endswith(image_file_types):
            res = ollama.chat(
                model="granite3.2-vision",
                messages=[
                    {
                        'role': 'user',
                        'content': 'Describe this image in short sentences. Use simple phrases first and then describe it more fully.',
                        'images': ["./files/" + fname]
                    }
                ]
            )

            if keyword in res['message']['content']:
                return "./files/" + fname
    
    return "None"

Paso 4: Definir las herramientas para ollama

Ahora que se han definido las funciones que debe llamar la ollama, configure la información de la herramienta para la propia ollama. El primer paso es crear un objeto que asigne el nombre de la herramienta a las funciones para la llamada de funciones de ollama:

available_functions = {
  'Search inside text files':search_text_files,
  'Search inside image files':search_image_files
}

A continuación, configure una matriz de herramientas para indicar a ollama a cuáles tendrá acceso y qué requisitos tienen. Se trata de una matriz con un esquema de objeto por herramienta que indica al marco de llamada de herramientas de ollama cómo llamar a la herramienta y qué devuelve.

En el caso de las dos herramientas que ha creado anteriormente, se trata de funciones que requieren un parámetro de palabra clave. Actualmente solo se admiten funciones, aunque esto podría cambiar en el futuro. La descripción de la función y del parámetro ayudan al modelo a llamar a la herramienta correctamente. El campo Descripción de la función de cada herramienta se transmite al LLM cuando este selecciona qué herramienta utilizar. La Descripción de la palabra clave se transmite al modelo cuando este genera los parámetros que se transmitirán a la herramienta. Ambos son lugares en los que puede buscar para afinar las instrucciones cuando cree sus propias aplicaciones de llamada a herramientas con ollama.

# tools don't need to be defined as an object but this helps pass the correct parameters
# to the tool call itself by giving the model a prompt of how the tool is to be used
ollama_tools=[
     {
      'type': 'function',
      'function': {
        'name': 'Search inside text files',
        'description': 'This tool searches in PDF or plaintext or text files in the local file system for descriptions or mentions of the keyword.',
        'parameters': {
          'type': 'object',
          'properties': {
            'keyword': {
              'type': 'string',
              'description': 'Generate one keyword from the user request to search for in text files',
            },
          },
          'required': ['keyword'],
        },
      },
    },
    {
      'type': 'function',
      'function': {
        'name': 'Search inside image files',
        'description': 'This tool searches for photos or image files in the local file system for the keyword.',
        'parameters': {
          'type': 'object',
          'properties': {
            'keyword': {
              'type': 'string',
              'description': 'Generate one keyword from the user request to search for in image files',
            },
          },
          'required': ['keyword'],
        },
      },
    },
  ]


Utilizará esta definición de herramientas cuando llame a ollama con la entrada del usuario.

Paso 5: Pasar la entrada del usuario a ollama

Ahora es el momento de pasar la entrada del usuario a ollama y hacer que devuelva los resultados de las llamadas a la herramienta. En primer lugar, asegúrese de que ollama se está ejecutando en su sistema:

# if ollama is not currently running, start it
import subprocess
subprocess.Popen(["ollama","serve"], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)

Si Ollama se está ejecutando, devolverá lo siguiente:

<Popen: returncode: None args: ['ollama', 'serve']>

Ahora solicite la entrada al usuario. También puede codificar la entrada o recuperarla desde una interfaz de chat, dependiendo de cómo haya configurado su aplicación. La función entrada  esperará la entrada del usuario antes de continuar.

# input
user_input = input("What would you like to search for?")
print(user_input)

Por ejemplo, si el usuario introduce "Información sobre perros", esta celda mostrará:

Information about dogs

Ahora la consulta del usuario se pasa al propio ollama. Los mensajes necesitan una función para el usuario y el contenido que este introduce. Esto se pasa a ollama utilizando la función de chat. El primer parámetro es el modelo que desea utilizar, en este caso Granite 3.2 Dense, luego el mensaje con la entrada del usuario y, por último, la matriz de herramientas que ha configurado anteriormente.

La función función generará una salida seleccionando qué herramienta utilizar y qué parámetros deben pasarse a ella en las llamadas a herramientas posteriores.

messages = [{'role': 'user', 'content':user_input}]

response: ollama.ChatResponse = ollama.chat(
   
  # set which model we're using
  'granite3.2:8b',

  # use the message from the user
  messages=messages,

  tools=ollama_tools
)

Ahora que el modelo ha generado llamadas a herramientas en la salida, ejecute todas las llamadas a herramientas con los parámetros que ha generado el modelo y compruebe la salida. En esta aplicación, Granite 3.2 Dense se utiliza también para generar la salida final, por lo que los resultados de las llamadas a herramientas se añaden a la entrada inicial del usuario y luego se pasan al modelo.

Varias llamadas a herramientas pueden devolver coincidencias de archivos, por lo que las respuestas se recopilan en una matriz que luego se pasa a Granite 3.2 para generar una respuesta. La instrucción que precede a los datos indica al modelo cómo responder:

If the tool output contains one or more file names, then give the user only the filename found. Do not add additional details. 
If the tool output is empty ask the user to try again. Here is the tool output: 

El resultado final se genera utilizando los nombres de archivo devueltos o 

# this is a place holder that to use to see whether the tools return anything 
output = []

if response.message.tool_calls:
  
  # There may be multiple tool calls in the response
  for tool_call in response.message.tool_calls:

    # Ensure the function is available, and then call it
    if function_to_call := available_functions.get(tool_call.function.name):
      print('Calling tool: ', tool_call.function.name, ' \n with arguments: ', tool_call.function.arguments)
      tool_res = function_to_call(**tool_call.function.arguments)

      print(" Tool response is " + str(tool_res))

      if(str(tool_res) != "None"):
        output.append(str(tool_res))
        print(tool_call.function.name, ' has output: ', output)
    else:
      print('Could not find ', tool_call.function.name)

  # Now chat with the model using the tool call results
  # Add the function response to messages for the model to use
  messages.append(response.message)

  prompt = '''
    If the tool output contains one or more file names, 
    then give the user only the filename found. Do not add additional details. 
    If the tool output is empty ask the user to try again. Here is the tool output: 
  '''

  messages.append({'role': 'tool', 'content': prompt + " " + ", ".join(str(x) for x in output)})
  
  # Get a response from model with function outputs
  final_response = ollama.chat('granite3.2:8b', messages=messages)
  print('Final response:', final_response.message.content)

else:

  # the model wasn't able to pick the correct tool from the prompt
  print('No tool calls returned from model')

Utilizando los archivos proporcionados para este tutorial, la instrucción "Información sobre perros" devolverá:

    Calling tool:  Search inside text files  
     with arguments:  {'keyword': 'dogs'}
     Tool response is ./files/File4.pdf
    Search inside text files  has output:  ['./files/File4.pdf']
    Calling tool:  Search inside image files  
     with arguments:  {'keyword': 'dogs'}
     Tool response is None
    Final response: The keyword "dogs" was found in File4.pdf.

Puede ver que Granite 3.2 ha seleccionado la palabra clave correcta de la entrada, "perros", y ha buscado en los archivos de la carpeta, encontrando la palabra clave en un archivo PDF. Dado que los resultados de LLM no son puramente deterministas, es posible que obtenga resultados ligeramente diferentes con la misma instrucción o con instrucciones muy similares.

Soluciones relacionadas
Agentes de IA para empresas

Cree, implemente y gestione potentes asistentes y agentes de IA que automaticen flujos de trabajo y procesos con IA generativa.

    Explore watsonx Orchestrate
    Soluciones de agente de IA de IBM

    Construya el futuro de su empresa con soluciones de IA en las que puede confiar.

    Explore las soluciones de los agentes de IA
    Servicios de IA de IBM Consulting

    Los servicios de IA de IBM Consulting ayudan a reinventar la forma de trabajar de las empresas usando IA para la transformación.

    Explore los servicios de inteligencia artificial
    Dé el siguiente paso

    Tanto si opta por personalizar las aplicaciones y habilidades prediseñadas como si prefiere crear e implementar servicios agentivos personalizados mediante un estudio de IA, la plataforma IBM watsonx le ofrece todo lo que necesita.

    Explore watsonx Orchestrate Explore watsonx.ai