Extracción de datos estructurados en PHP, impulsada por LLM. Diseñado para ofrecer simplicidad, transparencia y control.
Instructor es una biblioteca que le permite extraer datos estructurados y validados de múltiples tipos de entradas: texto, imágenes o matrices de secuencias de chat estilo OpenAI. Está impulsado por modelos de lenguajes grandes (LLM).
El instructor simplifica la integración de LLM en proyectos PHP. Maneja la complejidad de extraer datos estructurados de los resultados de LLM, para que pueda concentrarse en desarrollar la lógica de su aplicación e iterar más rápido.
Instructor para PHP está inspirado en la biblioteca Instructor para Python creada por Jason Liu.
A continuación se muestra una aplicación de demostración CLI sencilla que utiliza Instructor para extraer datos estructurados de texto:
Structure
Consulte las implementaciones en otros idiomas a continuación:
Si desea trasladar el Instructor a otro idioma, comuníquese con nosotros en Twitter. ¡Nos encantaría ayudarlo a comenzar!
El instructor presenta tres mejoras clave en comparación con el uso directo de API.
Simplemente especifica una clase PHP para extraer datos a través de la "magia" de completar el chat LLM. Y eso es todo.
El instructor reduce la fragilidad del código al extraer la información de los datos textuales aprovechando las respuestas estructuradas de LLM.
El instructor le ayuda a escribir código más simple y fácil de entender: ya no tendrá que definir largas definiciones de llamadas a funciones ni escribir código para asignar JSON devuelto a objetos de datos de destino.
El modelo de respuesta generado por LLM se puede validar automáticamente, siguiendo un conjunto de reglas. Actualmente, Instructor solo admite la validación de Symfony.
También puede proporcionar un objeto de contexto para utilizar capacidades de validación mejoradas.
Puede establecer el número de reintentos para las solicitudes.
El instructor repetirá las solicitudes en caso de error de validación o deserialización hasta el número de veces especificado, intentando obtener una respuesta válida de LLM.
Instalar Instructor es sencillo. Ejecute el siguiente comando en su terminal y estará en camino a una experiencia de manejo de datos más fluida.
composer require cognesy/instructor-php
Este es un ejemplo sencillo que demuestra cómo el instructor recupera información estructurada del texto proporcionado (o de una secuencia de mensajes de chat).
La clase de modelo de respuesta es una clase PHP simple con sugerencias que especifican los tipos de campos del objeto.
use Cognesy Instructor Instructor ;
// Step 0: Create .env file in your project root:
// OPENAI_API_KEY=your_api_key
// Step 1: Define target data structure(s)
class Person {
public string $ name ;
public int $ age ;
}
// Step 2: Provide content to process
$ text = " His name is Jason and he is 28 years old. " ;
// Step 3: Use Instructor to run LLM inference
$ person = ( new Instructor )-> respond (
messages: $ text ,
responseModel: Person ::class,
);
// Step 4: Work with structured response data
assert ( $ person instanceof Person ); // true
assert ( $ person -> name === ' Jason ' ); // true
assert ( $ person -> age === 28 ); // true
echo $ person -> name ; // Jason
echo $ person -> age ; // 28
var_dump ( $ person );
// Person {
// name: "Jason",
// age: 28
// }
NOTA: El instructor admite clases/objetos como modelos de respuesta. En caso de que desee extraer enumeraciones o tipos simples, debe incluirlos en el adaptador escalar; consulte la sección siguiente: Extracción de valores escalares.
El instructor le permite definir múltiples conexiones API en el archivo llm.php
. Esto es útil cuando desea utilizar diferentes LLM o proveedores de API en su aplicación.
La configuración predeterminada se encuentra en /config/llm.php
en el directorio raíz del código base del Instructor. Contiene un conjunto de conexiones predefinidas a todas las API de LLM admitidas de forma inmediata por Instructor.
El archivo de configuración define las conexiones a las API de LLM y sus parámetros. También especifica la conexión predeterminada que se utilizará al llamar al Instructor sin especificar la conexión del cliente.
// This is fragment of /config/llm.php file
' defaultConnection ' => ' openai ' ,
// . . .
' connections ' => [
' anthropic ' => [ ... ],
' azure ' => [ ... ],
' cohere1 ' => [ ... ],
' cohere2 ' => [ ... ],
' fireworks ' => [ ... ],
' gemini ' => [ ... ],
' grok ' => [ ... ],
' groq ' => [ ... ],
' mistral ' => [ ... ],
' ollama ' => [
' providerType ' => LLMProviderType :: Ollama -> value ,
' apiUrl ' => ' http://localhost:11434/v1 ' ,
' apiKey ' => Env :: get ( ' OLLAMA_API_KEY ' , '' ),
' endpoint ' => ' /chat/completions ' ,
' defaultModel ' => ' qwen2.5:0.5b ' ,
' defaultMaxTokens ' => 1024 ,
' httpClient ' => ' guzzle-ollama ' , // use custom HTTP client configuration
],
' openai ' => [ ... ],
' openrouter ' => [ ... ],
' together ' => [ ... ],
// ...
Para personalizar las conexiones disponibles, puede modificar las entradas existentes o agregar las suyas propias.
Conectarse a la API de LLM a través de una conexión predefinida es tan simple como llamar al método withClient
con el nombre de la conexión.
<?php
// ...
$ user = ( new Instructor )
-> withConnection ( ' ollama ' )
-> respond (
messages: " His name is Jason and he is 28 years old. " ,
responseModel: Person ::class,
);
// ...
Puede cambiar la ubicación de los archivos de configuración para que los use el Instructor a través de la variable de entorno INSTRUCTOR_CONFIG_PATH
. Puede utilizar copias de los archivos de configuración predeterminados como punto de partida.
El instructor ofrece una forma de utilizar datos estructurados como entrada. Esto es útil cuando desea utilizar datos de objetos como entrada y obtener otro objeto con el resultado de la inferencia LLM.
El campo input
de los métodos respond()
y request()
del Instructor puede ser un objeto, pero también una matriz o simplemente una cadena.
<?php
use Cognesy Instructor Instructor ;
class Email {
public function __construct (
public string $ address = '' ,
public string $ subject = '' ,
public string $ body = '' ,
) {}
}
$ email = new Email (
address: ' joe@gmail ' ,
subject: ' Status update ' ,
body: ' Your account has been updated. '
);
$ translation = ( new Instructor )-> respond (
input: $ email ,
responseModel: Email ::class,
prompt: ' Translate the text fields of email to Spanish. Keep other fields unchanged. ' ,
);
assert ( $ translation instanceof Email ); // true
dump ( $ translation );
// Email {
// address: "joe@gmail",
// subject: "Actualización de estado",
// body: "Su cuenta ha sido actualizada."
// }
?>
El instructor valida los resultados de la respuesta de LLM con respecto a las reglas de validación especificadas en su modelo de datos.
Para obtener más detalles sobre las reglas de validación disponibles, consulte Restricciones de validación de Symfony.
use Symfony Component Validator Constraints as Assert ;
class Person {
public string $ name ;
#[ Assert PositiveOrZero ]
public int $ age ;
}
$ text = " His name is Jason, he is -28 years old. " ;
$ person = ( new Instructor )-> respond (
messages: [[ ' role ' => ' user ' , ' content ' => $ text ]],
responseModel: Person ::class,
);
// if the resulting object does not validate, Instructor throws an exception
En caso de que se proporcione el parámetro maxRetries y la respuesta LLM no cumpla con los criterios de validación, el instructor realizará intentos de inferencia posteriores hasta que los resultados cumplan con los requisitos o se alcance maxRetries.
El instructor utiliza errores de validación para informar a LLM sobre los problemas identificados en la respuesta, de modo que LLM pueda intentar autocorregirse en el siguiente intento.
use Symfony Component Validator Constraints as Assert ;
class Person {
#[ Assert Length (min: 3 )]
public string $ name ;
#[ Assert PositiveOrZero ]
public int $ age ;
}
$ text = " His name is JX, aka Jason, he is -28 years old. " ;
$ person = ( new Instructor )-> respond (
messages: [[ ' role ' => ' user ' , ' content ' => $ text ]],
responseModel: Person ::class,
maxRetries: 3 ,
);
// if all LLM's attempts to self-correct the results fail, Instructor throws an exception
Puede llamar al método request()
para establecer los parámetros de la solicitud y luego llamar a get()
para obtener la respuesta.
use Cognesy Instructor Instructor ;
$ instructor = ( new Instructor )-> request (
messages: " His name is Jason, he is 28 years old. " ,
responseModel: Person ::class,
);
$ person = $ instructor -> get ();
Instructor admite la transmisión de resultados parciales, lo que le permite comenzar a procesar los datos tan pronto como estén disponibles.
<?php
use Cognesy Instructor Instructor ;
$ stream = ( new Instructor )-> request (
messages: " His name is Jason, he is 28 years old. " ,
responseModel: Person ::class,
options: [ ' stream ' => true ]
)-> stream ();
foreach ( $ stream as $ partialPerson ) {
// process partial person data
echo $ partialPerson -> name ;
echo $ partialPerson -> age ;
}
// after streaming is done you can get the final, fully processed person object...
$ person = $ stream -> getLastUpdate ()
// . . . to, for example, save it to the database
$ db -> save ( $ person );
?>
Puede definir la devolución de llamada onPartialUpdate()
para recibir resultados parciales que se pueden usar para comenzar a actualizar la interfaz de usuario antes de que LLM complete la inferencia.
NOTA: Las actualizaciones parciales no se validan. La respuesta sólo se valida después de que se haya recibido en su totalidad.
use Cognesy Instructor Instructor ;
function updateUI ( $ person ) {
// Here you get partially completed Person object update UI with the partial result
}
$ person = ( new Instructor )-> request (
messages: " His name is Jason, he is 28 years old. " ,
responseModel: Person ::class,
options: [ ' stream ' => true ]
)-> onPartialUpdate (
fn( $ partial ) => updateUI ( $ partial )
)-> get ();
// Here you get completed and validated Person object
$ this -> db -> save ( $ person ); // ...for example: save to DB
Puede proporcionar una cadena en lugar de una serie de mensajes. Esto es útil cuando desea extraer datos de un solo bloque de texto y desea mantener su código simple.
// Usually, you work with sequences of messages:
$ value = ( new Instructor )-> respond (
messages: [[ ' role ' => ' user ' , ' content ' => " His name is Jason, he is 28 years old. " ]],
responseModel: Person ::class,
);
// ...but if you want to keep it simple, you can just pass a string:
$ value = ( new Instructor )-> respond (
messages: " His name is Jason, he is 28 years old. " ,
responseModel: Person ::class,
);
A veces solo queremos obtener resultados rápidos sin definir una clase para el modelo de respuesta, especialmente si intentamos obtener una respuesta directa y simple en forma de cadena, entero, booleano o flotante. El instructor proporciona una API simplificada para tales casos.
use Cognesy Instructor Extras Scalar Scalar ;
use Cognesy Instructor Instructor ;
$ value = ( new Instructor )-> respond (
messages: " His name is Jason, he is 28 years old. " ,
responseModel: Scalar :: integer ( ' age ' ),
);
var_dump ( $ value );
// int(28)
En este ejemplo, extraemos un único valor entero del texto. También puedes usar Scalar::string()
, Scalar::boolean()
y Scalar::float()
para extraer otros tipos de valores.
Además, puede utilizar el adaptador Scalar para extraer una de las opciones proporcionadas mediante Scalar::enum()
.
use Cognesy Instructor Extras Scalar Scalar ;
use Cognesy Instructor Instructor ;
enum ActivityType : string {
case Work = ' work ' ;
case Entertainment = ' entertainment ' ;
case Sport = ' sport ' ;
case Other = ' other ' ;
}
$ value = ( new Instructor )-> respond (
messages: " His name is Jason, he currently plays Doom Eternal. " ,
responseModel: Scalar :: enum ( ActivityType ::class, ' activityType ' ),
);
var_dump ( $ value );
// enum(ActivityType:Entertainment)
Sequence es una clase contenedora que se puede utilizar para representar una lista de objetos que el Instructor extraerá del contexto proporcionado.
Generalmente es más conveniente no crear una clase dedicada con una única propiedad de matriz solo para manejar una lista de objetos de una clase determinada.
Una característica adicional y única de las secuencias es que se pueden transmitir por cada elemento completado en una secuencia, en lugar de en cualquier actualización de propiedad.
class Person
{
public string $ name ;
public int $ age ;
}
$ text = <<<TEXT
Jason is 25 years old. Jane is 18 yo. John is 30 years old
and Anna is 2 years younger than him.
TEXT ;
$ list = ( new Instructor )-> respond (
messages: [[ ' role ' => ' user ' , ' content ' => $ text ]],
responseModel: Sequence :: of ( Person ::class),
options: [ ' stream ' => true ]
);
Ver más sobre secuencias en la sección Secuencias.
Utilice sugerencias de tipo PHP para especificar el tipo de datos extraídos.
Utilice tipos que acepten valores NULL para indicar que el campo dado es opcional.
class Person {
public string $ name ;
public ? int $ age ;
public Address $ address ;
}
También puede utilizar comentarios de estilo PHP DocBlock para especificar el tipo de datos extraídos. Esto es útil cuando desea especificar tipos de propiedad para LLM, pero no puede o no quiere imponer el tipo a nivel de código.
class Person {
/** @var string */
public $ name ;
/** @var int */
public $ age ;
/** @var Address $address person's address */
public $ address ;
}
Consulte la documentación de PHPDoc para obtener más detalles en el sitio web de DocBlock.
PHP actualmente no admite genéricos ni sugerencias para especificar tipos de elementos de matriz.
Utilice comentarios de estilo PHP DocBlock para especificar el tipo de elementos de la matriz.
class Person {
// ...
}
class Event {
// ...
/** @var Person[] list of extracted event participants */
public array $ participants ;
// ...
}
El instructor puede recuperar estructuras de datos complejas a partir de texto. Su modelo de respuesta puede contener objetos, matrices y enumeraciones anidadas.
use Cognesy Instructor Instructor ;
// define a data structures to extract data into
class Person {
public string $ name ;
public int $ age ;
public string $ profession ;
/** @var Skill[] */
public array $ skills ;
}
class Skill {
public string $ name ;
public SkillType $ type ;
}
enum SkillType {
case Technical = ' technical ' ;
case Other = ' other ' ;
}
$ text = " Alex is 25 years old software engineer, who knows PHP, Python and can play the guitar. " ;
$ person = ( new Instructor )-> respond (
messages: [[ ' role ' => ' user ' , ' content ' => $ text ]],
responseModel: Person ::class,
); // client is passed explicitly, can specify e.g. different base URL
// data is extracted into an object of given class
assert ( $ person instanceof Person ); // true
// you can access object's extracted property values
echo $ person -> name ; // Alex
echo $ person -> age ; // 25
echo $ person -> profession ; // software engineer
echo $ person -> skills [ 0 ]-> name ; // PHP
echo $ person -> skills [ 0 ]-> type ; // SkillType::Technical
// ...
var_dump ( $ person );
// Person {
// name: "Alex",
// age: 25,
// profession: "software engineer",
// skills: [
// Skill {
// name: "PHP",
// type: SkillType::Technical,
// },
// Skill {
// name: "Python",
// type: SkillType::Technical,
// },
// Skill {
// name: "guitar",
// type: SkillType::Other
// },
// ]
// }
Si desea definir la forma de los datos durante el tiempo de ejecución, puede utilizar la clase Structure
.
Las estructuras le permiten definir y modificar la forma arbitraria de los datos que LLM extraerá. Es posible que las clases no sean las más adecuadas para este propósito, ya que no es posible declararlas o cambiarlas durante la ejecución.
Con las estructuras, puede definir formas de datos personalizadas de forma dinámica, por ejemplo, según la entrada del usuario o el contexto del procesamiento, para especificar la información que necesita que LLM infiera a partir del texto proporcionado o los mensajes de chat.
El siguiente ejemplo demuestra cómo definir una estructura y utilizarla como modelo de respuesta:
<?php
use Cognesy Instructor Extras Structure Field ;
use Cognesy Instructor Extras Structure Structure ;
enum Role : string {
case Manager = ' manager ' ;
case Line = ' line ' ;
}
$ structure = Structure :: define ( ' person ' , [
Field :: string ( ' name ' ),
Field :: int ( ' age ' ),
Field :: enum ( ' role ' , Role ::class),
]);
$ person = ( new Instructor )-> respond (
messages: ' Jason is 25 years old and is a manager. ' ,
responseModel: $ structure ,
);
// you can access structure data via field API...
assert ( $ person -> field ( ' name ' ) === ' Jason ' );
// ...or as structure object properties
assert ( $ person -> age === 25 );
?>
Para más información ver sección Estructuras.
Puede especificar el modelo y otras opciones que se pasarán al punto final OpenAI/LLM.
use Cognesy Instructor Features LLM Data LLMConfig ;
use Cognesy Instructor Features LLM Drivers OpenAIDriver ;
use Cognesy Instructor Instructor ;
// OpenAI auth params
$ yourApiKey = Env :: get ( ' OPENAI_API_KEY ' ); // use your own API key
// Create instance of OpenAI driver initialized with custom parameters
$ driver = new OpenAIDriver ( new LLMConfig (
apiUrl: ' https://api.openai.com/v1 ' , // you can change base URI
apiKey: $ yourApiKey ,
endpoint: ' /chat/completions ' ,
metadata: [ ' organization ' => '' ],
model: ' gpt-4o-mini ' ,
maxTokens: 128 ,
));
/// Get Instructor with the default client component overridden with your own
$ instructor = ( new Instructor )-> withDriver ( $ driver );
$ user = $ instructor -> respond (
messages: " Jason (@jxnlco) is 25 years old and is the admin of this project. He likes playing football and reading books. " ,
responseModel: User ::class,
model: ' gpt-3.5-turbo ' ,
options: [ ' stream ' => true ]
);
El instructor ofrece soporte listo para usar para los siguientes proveedores de API:
Para ver ejemplos de uso, consulte la sección Hub o el directorio de examples
en el repositorio de código.
Puede utilizar PHP DocBlocks (/** */) para proporcionar instrucciones adicionales para LLM a nivel de clase o campo, por ejemplo, para aclarar qué espera o cómo LLM debe procesar sus datos.
El instructor extrae los comentarios de PHP DocBlocks de la clase y propiedad definidas y los incluye en la especificación del modelo de respuesta enviado a LLM.
No es necesario utilizar las instrucciones de PHP DocBlocks, pero a veces es posible que desee aclarar sus intenciones para mejorar los resultados de inferencia de LLM.
/**
* Represents a skill of a person and context in which it was mentioned.
*/
class Skill {
public string $ name ;
/** @var SkillType $type type of the skill, derived from the description and context */
public SkillType $ type ;
/** Directly quoted, full sentence mentioning person's skill */
public string $ context ;
}
Puede utilizar el rasgo ValidationMixin para agregar la capacidad de validar objetos de datos personalizados y fáciles.
use Cognesy Instructor Features Validation Traits ValidationMixin ;
class User {
use ValidationMixin ;
public int $ age ;
public int $ name ;
public function validate () : array {
if ( $ this -> age < 18 ) {
return [ " User has to be adult to sign the contract. " ];
}
return [];
}
}
El instructor utiliza el componente de validación de Symfony para validar los datos extraídos. Puede utilizar la anotación #[Assert/Callback] para crear una lógica de validación totalmente personalizada.
use Cognesy Instructor Instructor ;
use Symfony Component Validator Constraints as Assert ;
use Symfony