# Llamada a la función de OpenAI

La nueva API de llamada a funciones de OpenAI lo cambió todo, incluyendo MyScale.

Nueva versión de OpenAI

OpenAI acaba de lanzar muchas actualizaciones, incluyendo un modelo GPT-3.5 turbo con un contexto de 16K más económico y más modelos GPT-4 disponibles. También tenemos una nueva interfaz de API llamada llamada a función (opens new window), que puede ser una herramienta poderosa para los desarrolladores.

La llamada a función cambia la forma en que trabajamos con las indicaciones. Ya hay muchos intentos exitosos de automatizar tanto las consultas estructuradas como la búsqueda de vectores, por ejemplo, el SQLChain (opens new window) de LangChain y los recuperadores de autoconsulta (opens new window). Casi todos estos enfoques requieren indicaciones extensas, lo que significa que son costosos y difíciles de ajustar. La nueva llamada a función de OpenAI ahorra una gran cantidad de tokens para las indicaciones: no es necesario utilizar varios ejemplos largos para demostrar el uso de una función.

Descubrimos que el recién lanzado GPT-3.5 (gpt-3.5-turbo-0613) funciona bien con la nueva llamada a función. Con solo varios intentos, logramos desarrollar una indicación equivalente al recuperador de autoconsulta de LangChain. No es necesario definir operadores y comparadores, solo indicaciones. Puedes ver cómo lo logramos en github (opens new window).

# Antes de comenzar...

  • Necesitas instalar algunas dependencias:
pip3 install langchain openai clickhouse-connect

¿No sabes cómo hacerlo? Consulta nuestro documento oficial sobre gestión de clústeres.

# Echa un vistazo a la indicación

Para simplificar, reutilizamos los vectorstores de LangChain y algunas funciones auxiliares de los recuperadores de autoconsulta.

# Crea un vectorstore e inserta datos

Proporcionamos metadatos y resúmenes de 8 artículos (opens new window) de ArXiv (Gracias a arXiv (opens new window) por el uso de su interoperabilidad de acceso abierto). Puedes encontrar más datos en nuestro almacenamiento de AWS (opens new window).

Ahora ve al código:

import json
from langchain.schema import Document
from langchain.vectorstores import MyScale
from langchain.embeddings import HuggingFaceEmbeddings
def str2doc(_str):
    j = json.loads(_str)
    return Document(page_content=j['abstract'], metadata=j['metadata'])
with open('func_call_data.jsonl') as f:
    docs = [str2doc(l) for l in f.readlines()]
vectorstore = MyScale.from_documents(
    documents=docs, embedding=HuggingFaceEmbeddings())

# Define columnas de metadatos

Aquí tomamos prestada la función _format_attribute_info de LangChain. Nos ayuda a convertir esas AttributeInfo en cadenas simples, que se ajustan mejor a las indicaciones.

Se recomienda modificar esto para jugar con más tipos de datos y funciones. Puedes definir columnas con funciones, por ejemplo, length(metadata.categories) se refiere a la longitud de esa categoría. Intenta encontrar funciones más interesantes en la documentación de ClickHouse (opens new window), que admitimos nativamente junto con la búsqueda de vectores. ¡Y no olvides unirte a nuestro Discord (opens new window) para compartir tus nuevos descubrimientos o pensamientos, estaremos emocionados de mejorarlos contigo!

from langchain.chains.query_constructor.base import _format_attribute_info, AttributeInfo
metadata_field_info=[
    AttributeInfo(
        name="metadata.pubdate",
        description="La fecha en que se publicó el artículo, se necesita usar `parseDateTime32BestEffort()` para convertir las marcas de tiempo en formato de cadena a un formato comparable.", 
        type="timestamp", 
    ),
    AttributeInfo(
        name="metadata.authors",
        description="Lista de nombres de los autores", 
        type="list[string]", 
    ),
    AttributeInfo(
        name="metadata.title",
        description="Título del artículo", 
        type="string", 
    ),
    AttributeInfo(
        name="text",
        description="Resumen del artículo", 
        type="string", 
    ),
    AttributeInfo(
        name="metadata.categories",
        description="Categorías de arXiv para este artículo",
        type="list[string]"
    ),
    AttributeInfo(
        name="length(metadata.categories)",
        description="longitud de las categorías de arXiv para este artículo",
        type="int"
    ),
]
formated = _format_attribute_info(metadata_field_info)

# Llamada a función para construir una búsqueda de vectores filtrada

Al igual que los recuperadores de autoconsulta, nuestro consultor basado en llamadas a funciones también devuelve tres argumentos para llamar a la interfaz del vectorstore:

  • query: cadena de consulta que se convertirá en vectores más adelante.
  • where_str: una cadena genérica WHERE para realizar el filtrado. Pero sin la palabra clave WHERE.
  • limit: se devolverán los primeros limit elementos. Por defecto, es 4.

Así es como trabajamos con la llamada a función de OpenAI:

import openai
# ¡Simple, ¿verdad?
query = "¿Qué es una red bayesiana?"
# Hagamos algunas restricciones...
query += "Por favor, utiliza artículos publicados después de febrero de 2013 "
# ¿Quieres algo más? Filtros de palabras clave.
query += "y cuyo resumen sea similar a `artificial` "
# ¡Mucho mejor! Artículos de modalidad cruzada...
query += "con más de 2 categorías "
# Soy un experto en CV, solo quiero algunos artículos de CV
query += "y debe tener `cs.CV` en su categoría."
# Ahora la indicación estructurada:
completion = openai.ChatCompletion.create(
    # utilizamos el nuevo modelo gpt
    model="gpt-3.5-turbo-0613",
    # temperatura establecida en 0 para garantizar un comportamiento estable
    temperature=0,
    # Ahora la llamada a función:
    # necesitas:
    # 1. un nombre para la función.
    # 2. una descripción de tu función
    # 3. definiciones de parámetros en nombre y tipos
    functions=[{"name": "to_structued_sql",
                "description":
                ("Convierte la consulta en una cadena de consulta y "
                 "una cadena de filtro para filtrar esta consulta. "
                 "Cuando verifiques si los elementos están en una lista, "
                 "utiliza `has(column, element)`."),
                "parameters": {"type": "object",
                               "properties": {
                                   "query": {"type": "string"},
                                   "where_str": {"type": "string", },
                                   "limit": {
                                       "type": "integer",
                                       "description": "por defecto es 4"}
                               },
                               "required": ["subject", "where_str", "limit"]
                               }
                },],
    # El nombre de la función a llamar, si es 'auto' el modelo decidirá por sí mismo
    function_call="auto",
    # Aquí está nuestro viejo amigo: la indicación de completado de chat...
    # Puedes escribir algunas reglas para limitar el comportamiento del modelo.
    messages=[
        {
            "role": "system",
            "content": ("Necesitas proporcionar `metadata` para construir SQL. "
                        "Utilizaré `parseDateTime32BestEffort()` para "
                        "convertir las marcas de tiempo en formato de cadena "
                        "a un formato comparable."),
        },
        {
            "role": "user",
            "content": f"Metadata: {formated}"
        },
        {
            "role": "system",
            "content": "Ahora puedes ingresar tu consulta",
        },
        {
            "role": "user",
            "content": query
        },
    ],
)

¡Luego busca!

Esto es exactamente lo mismo que los recuperadores de autoconsulta de LangChain. Y es más flexible: puede escribir cualquier SQL... e incluso funciones definidas por el usuario. No hay límites, ¡así que crea un caso para ejecutar!

ret = vectorstore.similarity_search(**search_kwargs)
for r in ret:
    print(r)

# Consejos al usar llamadas a funciones

Aquí tienes algunos consejos sobre las llamadas a funciones:

  1. Los nombres legibles por humanos proporcionan más información a los LLM, esto es cierto tanto si estás utilizando esta nueva llamada a función como si estás utilizando indicaciones tradicionales.
  2. Si quieres establecer reglas para una columna, por ejemplo, conversión de tipo al comparar con otros valores en nuestro ejemplo, hazlo con la definición de la columna. Las reglas deben estar cerca de los datos asociados.
  3. Los LLM tienden a imitar la función de asignación entre los datos con los que están familiarizados. Como en esta demostración, tanto el lenguaje natural como el SQL son bastante comunes para los LLM, por lo que el resultado supera las expectativas.

¡Creemos que puedes descubrir más sobre esto! ¿Por qué no compartes tus descubrimientos con MyScale en Discord (opens new window)?

# En resumen

Creemos que esta será la forma en que las personas o los sistemas inteligentes trabajarán con bases de datos de vectores en el futuro. Estamos muy emocionados de ver estas actualizaciones y nuevas capacidades para desarrollar un sistema AGI más escalable y estable. 🚀 Nos gustaría que MyScale pueda ayudarte a crecer y tener éxito, ¡y haremos que eso se haga realidad! ¡Únete a nuestro Discord (opens new window) hoy mismo y recibe un cálido abrazo! 🤗 🤗