Llamada de herramientas con Ollama

Autor

Joshua Noble

Data Scientist

Llamada a herramientas con Ollama

La llamada a herramientas en modelos de lenguaje grandes (LLM) es la capacidad del LLM para interactuar con herramientas, servicios o API externos 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 local de código abierto para su uso en dispositivos personales para que los usuarios puedan ejecutar LLM directamente en sus computadoras. A diferencia de un servicio como la API de OpenAI, no es necesario tener 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 privacidad 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, lo que brinda a los usuarios una gama de herramientas para el procesamiento de 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 de 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 mediante el uso de Ollama para examinar un sistema de archivos local, una tarea que sería difícil de realizar con un LLM remoto. Muchos modelos de Ollama están disponibles para llamar a herramientas y crear agentes de IA como Mistral y Llama 3.2. Una lista completa está disponible 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 para este tutorial se puede descargar desde Github aquí.

Paso 1: Instalar Ollama

Primero descargará ollama desde https://ollama.com/download y lo instalará para su sistema operativo. En OSX, esto se hace a través de un archivo .dmg en Linux a través de un único comando de shell y en Windows con un instalador. Es posible que necesite acceso de administrador en su máquina para ejecutar el instalador.

Puede probar que ollama está instalado correctamente abriendo una terminal o una instrucción y escribiendo:

ollama -v 

 

Paso 2: Instalar bibliotecas

A continuación, agregará las importaciones iniciales. Esta demostración utilizará la biblioteca python de ollama 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, extraerá el modelo que utilizará a lo largo de este tutorial. Esto descarga los pesos del modelo de ollama a su computadora local y los almacena para su uso sin necesidad de realizar llamadas remotas a la API más adelante.

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

Paso 3: Definir las herramientas

Ahora definirá las herramientas a las que tendrá acceso la instancia de ollama tools. Dado que la intención de las herramientas es leer archivos y buscar imágenes en el sistema de archivos local, creará dos funciones de Python para cada una de esas herramientas. El primero se llamasearch_text_files y se necesita una palabra clave para buscar en los archivos locales. A los efectos de esta demostración, 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 usar una simple coincidencia de cadenas para ver si la palabra clave está en el documento, pero debido a que ollama facilita la llamada a llms locales,search_text_files utilizará 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 Luego, la función llama a ollama.chat y le da una instrucción al modelo con 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 porque 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 search_image_files  y se necesita una palabra clave para buscar en las fotos locales. La búsqueda se realiza mediante el modelo de descripción de imagen Granite 3.2 Vision mediante Ollama. Este modelo devolverá una descripción de texto de cada archivo de imagen en la carpeta y buscará la palabra clave en la descripción. Uno de los puntos fuertes del uso de 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 para que ollama llame, configurará 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 llamar a la función 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 decirle a ollama a qué herramientas tendrá acceso y qué requieren esas herramientas. Esta es una matriz con un esquema de objeto por herramienta que le dice a la infraestructura de llamada de herramientas de ollama cómo llamar a la herramienta y qué devuelve.

En el caso de las dos herramientas que creó anteriormente, son funciones que requieren un parámetro keyword. Actualmente solo se admiten funciones, aunque esto puede 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 Descripción campo para la función de cada herramienta se pasa al LLM cuando selecciona qué herramienta usar. El Descripción de la palabra clave se pasa al modelo cuando genera los parámetros que se pasarán a la herramienta. Ambos son lugares en los que puede buscar para ajustar las instrucciones cuando cree sus propias aplicaciones de llamada de 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. Primero, 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, esto devolverá:

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

Ahora pídale al usuario que ingrese. También puede codificar la entrada o recuperarla desde una interfaz de chat dependiendo de la configuración de su aplicación. El 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 ingresa "Información sobre perros", esta celda imprimirá:

Information about dogs

Ahora la consulta del usuario se pasa a la propia ollama. Los mensajes necesitan un rol para el usuario y el contenido que el usuario da entrada. Esto se pasa a ollama usando la función de chat. El primer parámetro es el modelo que desea usar, en este caso Granite 3.2 Dense, luego el mensaje con la entrada del usuario y, finalmente, la matriz de herramientas que configuró anteriormente.

El función de generará una salida seleccionando qué herramienta usar y qué parámetros deben pasarse en las siguientes llamadas a la herramienta.

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 generó el modelo y verifique la salida. En esta aplicación, Granite 3.2 Dense también se utiliza para generar el resultado final, por lo que los Resultados de las llamadas a la herramienta se agregan 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: 

A continuación, la salida 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')

Con 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 eligió la palabra clave correcta de la entrada, "perros", y buscó 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 instrucciones muy similares.

Soluciones relacionadas
Agentes de IA para empresas

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

    Explore watsonx Orchestrate
    Soluciones de agentes de IA de IBM

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

    Explorar las soluciones de agentes de IA
    Servicios de IA de IBM Consulting

    Los servicios de IA de IBM Consulting ayudan a reinventar la forma en que las empresas trabajan con IA para la transformación.

    Explorar los servicios de inteligencia artificial
    Dé el siguiente paso

    Ya sea que elija personalizar aplicaciones y habilidades predefinidas o crear y desplegar servicios agénticos personalizados utilizando un estudio de IA, la plataforma IBM watsonx responde a sus necesidades.

    Explore watsonx Orchestrate Explore watsonx.ai