- Ahora es posible restringir las respuestas de modelos locales para que cumplan con un JSON Schema, lo que reduce la carga de parsing posterior y hace más consistentes los resultados de extracción de datos
- Los usuarios pueden solicitar salidas estructuradas de la misma forma en cURL, Python y JavaScript, pasando el esquema en el parámetro
format
- En Python, usar Pydantic y en JavaScript usar Zod facilita conectar la definición del esquema y la validación de respuestas a nivel de código
- Los ejemplos devuelven y validan información de países, texto sobre mascotas y resultados de análisis de imágenes con
llama3.2-vision en campos JSON definidos
- Si se necesitan resultados estables, se recomienda incluir “return as JSON” en el prompt y bajar la temperature a 0
Restringir respuestas del modelo con JSON Schema
- Ollama admite salidas estructuradas para restringir la salida del modelo de modo que siga un formato específico de JSON Schema
- Las librerías de Ollama para Python y JavaScript también se actualizaron para admitir salidas estructuradas
- Esta función puede usarse para las siguientes tareas
- Parsing de datos desde documentos
- Extracción de datos desde imágenes
- Estructuración de respuestas de modelos de lenguaje
- Obtener mayor confiabilidad y consistencia que con JSON mode
Instalación y forma de invocación
- Es necesario descargar la versión más reciente de Ollama
- La librería de Python se actualiza a la versión más reciente con el siguiente comando
pip install -U ollama
- La librería de JavaScript se instala con el siguiente comando
npm i ollama
- Al solicitar salidas estructuradas, se pasa el esquema en el parámetro
format ya sea en la solicitud cURL o en las librerías de Python y JavaScript
Enviar JSON Schema con cURL
- El ejemplo con cURL envía una solicitud a
http://localhost:11434/api/chat, con model configurado como llama3.1 y stream como false
- En
format se incluye un esquema de objeto con name, capital y languages
name: cadena
capital: cadena
languages: arreglo de cadenas
- Los tres campos están marcados como
required
- La respuesta se devuelve con el formato del JSON Schema incluido en la solicitud
{
"capital": "Ottawa",
"languages": [
"English",
"French"
],
"name": "Canada"
}
Uso de las librerías de Python y JavaScript
-
Python
- La librería de Ollama para Python pasa el esquema como objeto JSON en el parámetro
format
- El esquema puede pasarse como
dict, y la forma recomendada es serializarlo con model_json_schema() de Pydantic
- El ejemplo define un modelo
Country con los campos name, capital y languages, y valida la respuesta con Country.model_validate_json()
country = Country.model_validate_json(response.message.content)
- La salida de ejemplo devuelve
Canada, Ottawa, English y French en los campos definidos
-
JavaScript
- La librería de Ollama para JavaScript pasa el esquema como objeto JSON en el parámetro
format
- El esquema puede pasarse como
object, y la forma recomendada es usar Zod junto con zodToJsonSchema()
- El ejemplo define el esquema
Country como un objeto Zod y, después de aplicar JSON.parse() al cuerpo de la respuesta, valida con Country.parse()
const country = Country.parse(JSON.parse(response.message.content));
Extracción de datos desde texto e imágenes
-
Extracción de texto sobre mascotas
- Las salidas estructuradas pueden usarse para extraer la información necesaria desde texto
- El ejemplo define los modelos Pydantic
Pet y PetList para devolver información sobre mascotas en una estructura JSON
Pet: name, animal, age, color, favorite_toy
PetList: arreglo pets
- El texto de entrada incluye información sobre dos gatos
- Luna: 5 años, pelaje gris, le gusta el yarn
- Loki: 2 años, color negro, le gustan las tennis balls
- La salida se valida como una lista de objetos
Pet conforme al esquema definido
-
Descripción de imágenes con modelo de visión
- Las salidas estructuradas pueden usarse junto con un modelo de visión
- El ejemplo analiza una imagen con
llama3.2-vision y devuelve un resultado ajustado al esquema ImageDescription
- El esquema incluye los siguientes campos
summary
objects
scene
colors
time_of_day
setting
text_content
- La solicitud de ejemplo indica analizar en la imagen objetos, escena, colores y texto detectable
- En las opciones,
temperature se configura en 0 para usar una salida más determinística
- La salida de ejemplo resume una escena de palmeras en la playa y devuelve en campos estructurados objetos como
tree y beach, además de colores, hora del día y un entorno exterior
Uso de la API compatible con OpenAI
- El ejemplo compatible con OpenAI configura el cliente
OpenAI con base_url="http://localhost:11434/v1" y api_key="ollama"
- Usa
client.beta.chat.completions.parse() para pasar el modelo Pydantic PetList en response_format
- En la respuesta se revisa
completion.choices[0].message, y si existe parsed, se imprime el resultado parseado
- Si existe
refusal, se imprime la respuesta de rechazo, y openai.LengthFinishReasonError se trata como un caso de demasiados tokens
Configuración para salidas estables y planes futuros
- Para definir el esquema de respuesta, se recomienda usar Pydantic en Python o Zod en JavaScript
- Es recomendable agregar “return as JSON” al prompt para que el modelo entienda la solicitud
- Si se quiere una salida más determinística, se configura la temperature en 0
- Los planes a futuro son los siguientes
- Exposición de logits para generación controlada
- Mejoras de rendimiento y precisión en las salidas estructuradas
- Aceleración por GPU para sampling
- Soporte para formatos adicionales más allá de JSON Schema
1 comentarios
Opiniones en Hacker News
Si necesitas restricciones de salida más fuertes, llama.cpp soporta GBNF
https://github.com/ggerganov/llama.cpp/blob/master/grammars/...
A simple vista, como los modelos han visto mucho más JSON, da la impresión de que generarían mejor JSON que otros formatos
Es una buena noticia
Estaba pensando en cómo armar un prompt general para crear datos CSV sin frases innecesarias al principio o al final como "Here is your data" o "Please note blah blah", así que me alegra que ahora se pueda definir exactamente el formato de retorno deseado y pasar la salida estructurada directamente como CSV
Si no, podrías obtener una salida que técnicamente tiene formato CSV, pero no significa nada. Puede ser que el modelo en realidad intentara escribir una respuesta en párrafos, pero el sampler de tokens eligió tokens de baja probabilidad que el modelo no tenía muchas ganas de decir
\n y hacer que se detenga enpuede evitar este problemaFunciona. Le pasé la siguiente oración a gemma2:2b y obtuve el JSON que quería
You have spent 190 at Fresh Mart. Current balance: 5098El resultado fue
{"amount": 190, "balance": 5098, "category": "Shopping", "place": "Fresh Mart"}Realmente sorprendente. Era una de las funciones que de verdad quería
ollama me gusta porque hace sentir que puedes usar un LLM como cualquier otro programa de UNIX, y que los LLM pertenecen de forma natural a UNIX
Eso sí, me pregunto si alguien lo ha logrado correr bien en GPU AMD. He oído que es más difícil, pero cuando compre una tarjeta el año que viene quisiera apoyar a la competencia
En una mini PC con una 780M, asigné 16 GB de memoria compartida desde el BIOS y funcionó bastante bien
Me pregunto qué efecto tienen estas restricciones en la calidad de salida del LLM
En algunos casos, si la calidad de salida es mayor, preferiría parsear Markdown o texto plano
En la primera, usas un modelo pesado y lenguaje natural para manejar el razonamiento en secciones Markdown y entregar la respuesta final en lenguaje natural. Si es posible, la etiquetas claramente con encabezados Markdown
En la segunda, usas un modelo más barato y rápido para convertir esa respuesta a un formato de salida estructurado, para que la consuma la parte no LLM del pipeline
Básicamente, con el modo de esquema JSON creas límites claros alrededor de las partes ambiguas en lenguaje natural, y haces que el LLM actúe como un preprocesador que captura su propia salida en un formato útil
Además, hay que informarle el esquema al modelo. Si no, aparecen más problemas raros de tokenización
Por ejemplo, si el esquema espera la clave JSON
"foobarbaz"y la tokenización BPE estándar es["foobar", "baz"], las máscaras de tokens que generan las bibliotecas actuales de salida restringida pueden permitir que el modelo elija entre"f","foo"o"foobar". Si el modelo elige"foo", la restricción fuerza como siguiente token, por ejemplo,"bar"y"baz". Entonces el modelo ve["foo", "bar", "baz"]en vez de["foobar", "baz"], y se confunde [0]Si el modelo sabe por el prompt que
"foobarbaz"es una de las claves del esquema, normalmente preferirá"foobar"antes que"foo"[0] En los modelos más recientes, por la normalización, esos tokens están relacionados entre sí, pero no son lo mismo
Depende mucho de si el LLM y el prompt ya tenían probabilidades de producir una respuesta JSON. Cuanto más fuerzas al LLM, menos probable es que genere una entrada normal
En modelos pequeños, llegas antes al borde del espacio donde hay capacidad predictiva significativa, y la salida empieza a parecerse más a ruido aleatorio
No es una medición rigurosa, sino una intuición después de pasar mucho tiempo en varios proyectos con LLM. Todavía no he probado estas herramientas específicas, pero antes ollama ya podía garantizar salidas JSON con algo que parecía una técnica similar, y con un socio también trabajé en algo parecido a jsonformer para oobabooga, otra herramienta de runtime para LLM
Espero que, si se incorporan cambios de ese tipo, también sea posible generar estructuras generales no limitadas a JSON
Es fácil quemar muchos tokens, pero si lo que intentas hacer justifica ese costo, puedes empujarlo bastante fuerte. Aunque no sea la máxima calidad absoluta, una herramienta que te da un 95% sin esfuerzo merece estar en la caja de herramientas
Me pregunto si se puede usar con cualquier modelo compatible
Pregunto porque en mi hardware solo puedo correr de forma estable modelos de 1B a 3B
Con modelos pequeños los resultados pueden ser irregulares, pero pedir “devuelve x como JSON” suele ayudar a la precisión
El PR relacionado con esta función estuvo abierto casi 1 año
Es un poco decepcionante que los mantenedores hayan estado tan callados
Espero que el próximo año podamos atender y fusionar mejor los PR de la comunidad
Todavía uso oobabooga. Gracias al soporte de exlv2, se puede hacer inferencia de forma mucho más eficiente en una dual 3090
Según recuerdo, aunque exl2 era más rápido, la cuantización gptq parecía ser mejor en precisión, especialmente con profundidades de bits bajas
Me pregunto qué valor agregado tiene frente a
outlineshttps://www.souzatharsis.com/tamingLLMs/notebooks/structured...
Espero que, con base en investigaciones recientes relacionadas con outlines/xgrammar, podamos actualizar el muestreo para admitir más formatos, aumentar la precisión y mejorar el rendimiento
Me pregunto si existe una mejor forma de proporcionar entrada estructurada a un LLM
Por ejemplo, en un caso en el que se le pasan 100 oraciones y se le pide clasificar cada una de varias maneras. Recibir datos estructurados es fácil, pero mi método de anteponer números de línea se siente tosco
Sin embargo, en este caso lo mejor es darle las oraciones una por una para que el modelo no se confunda
Si estructuras el prompt como
"Clasifica la siguiente oración. Las reglas son ..."+ la oración, puedes aprovechar la caché de prefijo, así que incluso podría rendir mejor que hacer la consulta de una sola vezPor supuesto, esto es posible cuando hay caché de prefijo y no se cobra por token de entrada. Hoy en día, la mayoría de los proveedores permiten usarla a menor costo si indicas que quieres utilizar la caché de prefijo