Extração estruturada de dados em PHP, desenvolvida por LLMs. Projetado para simplicidade, transparência e controle.
Instructor é uma biblioteca que permite extrair dados estruturados e validados de vários tipos de entradas: texto, imagens ou matrizes de sequência de bate-papo no estilo OpenAI. É alimentado por Large Language Models (LLMs).
O instrutor simplifica a integração do LLM em projetos PHP. Ele lida com a complexidade de extrair dados estruturados de resultados do LLM, para que você possa se concentrar na construção da lógica do seu aplicativo e iterar com mais rapidez.
Instructor for PHP é inspirado na biblioteca Instructor for Python criada por Jason Liu.
Aqui está um aplicativo de demonstração CLI simples usando o Instructor para extrair dados estruturados do texto:
Structure
Confira abaixo as implementações em outras linguagens:
Se você deseja migrar o Instructor para outro idioma, entre em contato conosco no Twitter, adoraríamos ajudá-lo a começar!
O instrutor apresenta três melhorias principais em comparação ao uso direto da API.
Você apenas especifica uma classe PHP para extrair dados por meio da 'mágica' da conclusão do chat LLM. E é isso.
O instrutor reduz a fragilidade do código, extraindo informações de dados textuais, aproveitando respostas estruturadas do LLM.
O Instructor ajuda você a escrever códigos mais simples e fáceis de entender - você não precisa mais definir longas definições de chamada de função ou escrever código para atribuir JSON retornado em objetos de dados de destino.
O modelo de resposta gerado pelo LLM pode ser validado automaticamente, seguindo um conjunto de regras. Atualmente, o Instructor oferece suporte apenas à validação do Symfony.
Você também pode fornecer um objeto de contexto para usar recursos aprimorados do validador.
Você pode definir o número de novas tentativas para solicitações.
O instrutor repetirá as solicitações em caso de erro de validação ou desserialização até o número especificado de vezes, tentando obter uma resposta válida do LLM.
Instalar o Instrutor é simples. Execute o seguinte comando em seu terminal e você estará no caminho certo para uma experiência de manipulação de dados mais tranquila!
composer require cognesy/instructor-php
Este é um exemplo simples que demonstra como o Instrutor recupera informações estruturadas do texto fornecido (ou sequência de mensagens de chat).
A classe do modelo de resposta é uma classe PHP simples com dicas de tipo especificando os tipos de campos do 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: O instrutor oferece suporte a classes/objetos como modelos de resposta. Caso você queira extrair tipos simples ou enums, você precisa envolvê-los no adaptador Escalar - veja a seção abaixo: Extraindo Valores Escalares.
O instrutor permite que você defina múltiplas conexões API no arquivo llm.php
. Isso é útil quando você deseja usar diferentes LLMs ou provedores de API em seu aplicativo.
A configuração padrão está localizada em /config/llm.php
no diretório raiz da base de código do instrutor. Ele contém um conjunto de conexões predefinidas para todas as APIs LLM suportadas imediatamente pelo Instrutor.
O arquivo de configuração define conexões com APIs LLM e seus parâmetros. Também especifica a conexão padrão a ser usada ao chamar o Instrutor sem especificar a conexão do 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 as conexões disponíveis, você pode modificar as entradas existentes ou adicionar as suas próprias.
Conectar-se à API LLM por meio de conexão predefinida é tão simples quanto chamar o método withClient
com o nome da conexão.
<?php
// ...
$ user = ( new Instructor )
-> withConnection ( ' ollama ' )
-> respond (
messages: " His name is Jason and he is 28 years old. " ,
responseModel: Person ::class,
);
// ...
Você pode alterar a localização dos arquivos de configuração para uso do Instrutor por meio da variável de ambiente INSTRUCTOR_CONFIG_PATH
. Você pode usar cópias dos arquivos de configuração padrão como ponto de partida.
O instrutor oferece uma maneira de usar dados estruturados como entrada. Isso é útil quando você deseja usar dados de objeto como entrada e obter outro objeto com resultado de inferência LLM.
O campo input
dos métodos respond()
e request()
do instrutor pode ser um objeto, mas também um array ou apenas uma string.
<?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."
// }
?>
O instrutor valida os resultados da resposta do LLM em relação às regras de validação especificadas no seu modelo de dados.
Para obter mais detalhes sobre as regras de validação disponíveis, verifique as restrições de validação do 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
Caso o parâmetro maxRetries seja fornecido e a resposta do LLM não atenda aos critérios de validação, o Instrutor fará tentativas de inferência subsequentes até que os resultados atendam aos requisitos ou maxRetries seja alcançado.
O instrutor usa erros de validação para informar o LLM sobre os problemas identificados na resposta, para que o LLM possa tentar a autocorreção na próxima tentativa.
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
Você pode chamar o método request()
para definir os parâmetros da solicitação e então chamar get()
para obter a resposta.
use Cognesy Instructor Instructor ;
$ instructor = ( new Instructor )-> request (
messages: " His name is Jason, he is 28 years old. " ,
responseModel: Person ::class,
);
$ person = $ instructor -> get ();
O Instructor suporta streaming de resultados parciais, permitindo que você comece a processar os dados assim que estiverem disponíveis.
<?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 );
?>
Você pode definir o retorno de chamada onPartialUpdate()
para receber resultados parciais que podem ser usados para iniciar a atualização da UI antes que o LLM conclua a inferência.
NOTA: Atualizações parciais não são validadas. A resposta só é validada depois de totalmente recebida.
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
Você pode fornecer uma string em vez de uma série de mensagens. Isso é útil quando você deseja extrair dados de um único bloco de texto e manter seu código simples.
// 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,
);
Às vezes, queremos apenas obter resultados rápidos sem definir uma classe para o modelo de resposta, especialmente se estivermos tentando obter uma resposta direta e simples na forma de string, inteiro, booleano ou flutuante. O Instructor fornece uma API simplificada para tais 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)
Neste exemplo, estamos extraindo um único valor inteiro do texto. Você também pode usar Scalar::string()
, Scalar::boolean()
e Scalar::float()
para extrair outros tipos de valores.
Além disso, você pode usar o adaptador Scalar para extrair uma das opções fornecidas usando 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 é uma classe wrapper que pode ser usada para representar uma lista de objetos a serem extraídos pelo Instrutor do contexto fornecido.
Geralmente é mais conveniente não criar uma classe dedicada com uma única propriedade de array apenas para manipular uma lista de objetos de uma determinada classe.
Um recurso adicional e exclusivo das sequências é que elas podem ser transmitidas para cada item concluído em uma sequência, em vez de qualquer atualização de propriedade.
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 ]
);
Veja mais sobre sequências na seção Sequências.
Use dicas de tipo PHP para especificar o tipo de dados extraídos.
Use tipos anuláveis para indicar que determinado campo é opcional.
class Person {
public string $ name ;
public ? int $ age ;
public Address $ address ;
}
Você também pode usar comentários no estilo PHP DocBlock para especificar o tipo de dados extraídos. Isso é útil quando você deseja especificar tipos de propriedade para LLM, mas não pode ou não deseja impor o tipo no nível do código.
class Person {
/** @var string */
public $ name ;
/** @var int */
public $ age ;
/** @var Address $address person's address */
public $ address ;
}
Consulte a documentação do PHPDoc para obter mais detalhes no site DocBlock.
PHP atualmente não suporta genéricos ou dicas de tipo para especificar tipos de elementos de array.
Use comentários no estilo PHP DocBlock para especificar o tipo de elementos do array.
class Person {
// ...
}
class Event {
// ...
/** @var Person[] list of extracted event participants */
public array $ participants ;
// ...
}
O instrutor pode recuperar estruturas de dados complexas do texto. Seu modelo de resposta pode conter objetos aninhados, matrizes e enumerações.
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
// },
// ]
// }
Se você deseja definir a forma dos dados durante o tempo de execução, você pode usar a classe Structure
.
As estruturas permitem definir e modificar formas arbitrárias de dados a serem extraídos pelo LLM. As classes podem não ser as mais adequadas para esse propósito, pois não é possível declará-las ou alterá-las durante a execução.
Com estruturas, você pode definir formas de dados personalizadas dinamicamente, por exemplo, com base na entrada do usuário ou no contexto do processamento, para especificar as informações que você precisa que o LLM infira a partir do texto fornecido ou das mensagens de bate-papo.
O exemplo abaixo demonstra como definir uma estrutura e usá-la como modelo de resposta:
<?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 mais informações consulte a seção Estruturas.
Você pode especificar o modelo e outras opções que serão passadas para o endpoint 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 ]
);
O instrutor oferece suporte imediato para os seguintes provedores de API:
Para exemplos de uso, verifique a seção Hub ou o diretório de examples
no repositório de código.
Você pode usar PHP DocBlocks (/** */) para fornecer instruções adicionais para LLM em nível de classe ou campo, por exemplo, para esclarecer o que você espera ou como o LLM deve processar seus dados.
O instrutor extrai comentários PHP DocBlocks da classe e propriedade definidas e os inclui na especificação do modelo de resposta enviado ao LLM.
O uso de instruções PHP DocBlocks não é obrigatório, mas às vezes você pode querer esclarecer suas intenções para melhorar os resultados de inferência do 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 ;
}
Você pode usar a característica ValidationMixin para adicionar capacidade de validação fácil e personalizada de objetos de dados.
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 [];
}
}
O instrutor usa o componente de validação Symfony para validar os dados extraídos. Você pode usar a anotação #[Assert/Callback] para construir uma lógica de validação totalmente personalizada.
use Cognesy Instructor Instructor ;
use Symfony Component Validator Constraints as Assert ;
use Symfony