Strukturierte Datenextraktion in PHP, unterstützt durch LLMs. Entwickelt für Einfachheit, Transparenz und Kontrolle.
Instructor ist eine Bibliothek, mit der Sie strukturierte, validierte Daten aus mehreren Eingabetypen extrahieren können: Text, Bilder oder Chat-Sequenz-Arrays im OpenAI-Stil. Es basiert auf Large Language Models (LLMs).
Instructor vereinfacht die LLM-Integration in PHP-Projekten. Es bewältigt die Komplexität der Extraktion strukturierter Daten aus LLM-Ausgaben, sodass Sie sich auf den Aufbau Ihrer Anwendungslogik konzentrieren und schneller iterieren können.
Instructor for PHP ist von der von Jason Liu erstellten Instructor-Bibliothek für Python inspiriert.
Hier ist eine einfache CLI-Demo-App, die Instructor zum Extrahieren strukturierter Daten aus Text verwendet:
Structure
Schauen Sie sich unten Implementierungen in anderen Sprachen an:
Wenn Sie Instructor in eine andere Sprache portieren möchten, kontaktieren Sie uns bitte auf Twitter. Wir helfen Ihnen gerne beim Einstieg!
Der Kursleiter stellt drei wichtige Verbesserungen im Vergleich zur direkten API-Nutzung vor.
Sie geben einfach eine PHP-Klasse an, in die Daten mithilfe der „Magie“ der LLM-Chat-Vervollständigung extrahiert werden sollen. Und das ist es.
Der Kursleiter reduziert die Sprödigkeit des Codes, indem er die Informationen aus Textdaten extrahiert, indem er strukturierte LLM-Antworten nutzt.
Der Instructor hilft Ihnen, einfacheren, leichter verständlichen Code zu schreiben – Sie müssen keine langwierigen Funktionsaufrufdefinitionen mehr definieren oder Code schreiben, um zurückgegebenes JSON den Zieldatenobjekten zuzuweisen.
Das von LLM generierte Antwortmodell kann gemäß einer Reihe von Regeln automatisch validiert werden. Derzeit unterstützt Instructor nur die Symfony-Validierung.
Sie können auch ein Kontextobjekt bereitstellen, um erweiterte Validatorfunktionen zu nutzen.
Sie können die Anzahl der Wiederholungsversuche für Anfragen festlegen.
Der Kursleiter wiederholt Anfragen im Falle eines Validierungs- oder Deserialisierungsfehlers bis zur angegebenen Anzahl von Malen und versucht, eine gültige Antwort von LLM zu erhalten.
Die Installation von Instructor ist einfach. Führen Sie den folgenden Befehl in Ihrem Terminal aus und Sie sind auf dem Weg zu einer reibungsloseren Datenverarbeitung!
composer require cognesy/instructor-php
Dies ist ein einfaches Beispiel, das zeigt, wie der Kursleiter strukturierte Informationen aus bereitgestelltem Text (oder einer Chat-Nachrichtensequenz) abruft.
Die Antwortmodellklasse ist eine einfache PHP-Klasse mit Typhinweisen, die die Feldtypen des Objekts angeben.
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
// }
HINWEIS: Der Kursleiter unterstützt Klassen/Objekte als Antwortmodelle. Wenn Sie einfache Typen oder Aufzählungen extrahieren möchten, müssen Sie diese in einen Skalaradapter einschließen – siehe Abschnitt unten: Extrahieren von Skalarwerten.
Mit dem Kursleiter können Sie mehrere API-Verbindungen in der Datei llm.php
definieren. Dies ist nützlich, wenn Sie in Ihrer Anwendung verschiedene LLMs oder API-Anbieter verwenden möchten.
Die Standardkonfiguration befindet sich in /config/llm.php
im Stammverzeichnis der Instructor-Codebasis. Es enthält eine Reihe vordefinierter Verbindungen zu allen LLM-APIs, die von Instructor standardmäßig unterstützt werden.
Die Konfigurationsdatei definiert Verbindungen zu LLM-APIs und deren Parameter. Außerdem wird die Standardverbindung angegeben, die beim Aufruf von Instructor verwendet werden soll, ohne die Clientverbindung anzugeben.
// 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 ' => [ ... ],
// ...
Um die verfügbaren Verbindungen anzupassen, können Sie entweder vorhandene Einträge ändern oder eigene hinzufügen.
Die Verbindung zur LLM-API über eine vordefinierte Verbindung ist so einfach wie der Aufruf withClient
-Methode mit dem Verbindungsnamen.
<?php
// ...
$ user = ( new Instructor )
-> withConnection ( ' ollama ' )
-> respond (
messages: " His name is Jason and he is 28 years old. " ,
responseModel: Person ::class,
);
// ...
Sie können den Speicherort der Konfigurationsdateien für die Verwendung durch den Kursleiter über die Umgebungsvariable INSTRUCTOR_CONFIG_PATH
ändern. Sie können Kopien der Standardkonfigurationsdateien als Ausgangspunkt verwenden.
Der Kursleiter bietet eine Möglichkeit, strukturierte Daten als Eingabe zu verwenden. Dies ist nützlich, wenn Sie Objektdaten als Eingabe verwenden und ein anderes Objekt mit einem Ergebnis der LLM-Inferenz erhalten möchten.
Das input
der Methoden respond()
und request()
des Instructors kann ein Objekt, aber auch ein Array oder nur ein String sein.
<?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."
// }
?>
Der Kursleiter validiert die Ergebnisse der LLM-Antwort anhand der in Ihrem Datenmodell angegebenen Validierungsregeln.
Weitere Einzelheiten zu den verfügbaren Validierungsregeln finden Sie unter Symfony-Validierungseinschränkungen.
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
Falls der Parameter „maxRetries“ bereitgestellt wird und die LLM-Antwort die Validierungskriterien nicht erfüllt, unternimmt der Kursleiter weitere Rückschlussversuche, bis die Ergebnisse den Anforderungen entsprechen oder „maxRetries“ erreicht ist.
Der Kursleiter verwendet Validierungsfehler, um LLM über die in der Antwort identifizierten Probleme zu informieren, sodass LLM beim nächsten Versuch eine Selbstkorrektur versuchen kann.
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
Sie können die Methode request()
aufrufen, um die Parameter der Anfrage festzulegen, und dann get()
aufrufen, um die Antwort zu erhalten.
use Cognesy Instructor Instructor ;
$ instructor = ( new Instructor )-> request (
messages: " His name is Jason, he is 28 years old. " ,
responseModel: Person ::class,
);
$ person = $ instructor -> get ();
Der Kursleiter unterstützt das Streamen von Teilergebnissen, sodass Sie mit der Verarbeitung der Daten beginnen können, sobald diese verfügbar sind.
<?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 );
?>
Sie können den Rückruf onPartialUpdate()
definieren, um Teilergebnisse zu erhalten, die zum Starten der Aktualisierung der Benutzeroberfläche verwendet werden können, bevor LLM die Inferenz abschließt.
HINWEIS: Teilaktualisierungen werden nicht validiert. Die Antwort wird erst validiert, nachdem sie vollständig empfangen wurde.
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
Sie können eine Zeichenfolge anstelle eines Arrays von Nachrichten bereitstellen. Dies ist nützlich, wenn Sie Daten aus einem einzelnen Textblock extrahieren und Ihren Code einfach halten möchten.
// 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,
);
Manchmal möchten wir einfach nur schnelle Ergebnisse erhalten, ohne eine Klasse für das Antwortmodell zu definieren, insbesondere wenn wir versuchen, eine klare, einfache Antwort in Form einer Zeichenfolge, einer Ganzzahl, eines Booleschen Werts oder einer Gleitkommazahl zu erhalten. Für solche Fälle stellt der Kursleiter eine vereinfachte API bereit.
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)
In diesem Beispiel extrahieren wir einen einzelnen ganzzahligen Wert aus dem Text. Sie können auch Scalar::string()
, Scalar::boolean()
und Scalar::float()
verwenden, um andere Arten von Werten zu extrahieren.
Darüber hinaus können Sie den Scalar-Adapter verwenden, um eine der bereitgestellten Optionen zu extrahieren, indem Sie Scalar::enum()
verwenden.
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 ist eine Wrapper-Klasse, die zur Darstellung einer Liste von Objekten verwendet werden kann, die vom Kursleiter aus dem bereitgestellten Kontext extrahiert werden sollen.
Normalerweise ist es praktischer, keine dedizierte Klasse mit einer einzelnen Array-Eigenschaft zu erstellen, nur um eine Liste von Objekten einer bestimmten Klasse zu verarbeiten.
Ein weiteres einzigartiges Merkmal von Sequenzen besteht darin, dass sie für jedes abgeschlossene Element in einer Sequenz und nicht für jede Eigenschaftsaktualisierung gestreamt werden können.
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 ]
);
Weitere Informationen zu Sequenzen finden Sie im Abschnitt „Sequenzen“.
Verwenden Sie PHP-Typhinweise, um den Typ der extrahierten Daten anzugeben.
Verwenden Sie nullfähige Typen, um anzugeben, dass das angegebene Feld optional ist.
class Person {
public string $ name ;
public ? int $ age ;
public Address $ address ;
}
Sie können auch Kommentare im PHP-DocBlock-Stil verwenden, um den Typ der extrahierten Daten anzugeben. Dies ist nützlich, wenn Sie Eigenschaftstypen für LLM angeben möchten, den Typ jedoch nicht auf Codeebene erzwingen können oder möchten.
class Person {
/** @var string */
public $ name ;
/** @var int */
public $ age ;
/** @var Address $address person's address */
public $ address ;
}
Weitere Informationen finden Sie in der PHPDoc-Dokumentation auf der DocBlock-Website.
PHP unterstützt derzeit keine Generics oder Typehints zur Angabe von Array-Elementtypen.
Verwenden Sie Kommentare im PHP-DocBlock-Stil, um den Typ der Array-Elemente anzugeben.
class Person {
// ...
}
class Event {
// ...
/** @var Person[] list of extracted event participants */
public array $ participants ;
// ...
}
Der Kursleiter kann komplexe Datenstrukturen aus Texten abrufen. Ihr Antwortmodell kann verschachtelte Objekte, Arrays und Aufzählungen enthalten.
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
// },
// ]
// }
Wenn Sie die Form von Daten zur Laufzeit definieren möchten, können Sie Structure
verwenden.
Mit Strukturen können Sie beliebige Formen von Daten definieren und ändern, die von LLM extrahiert werden sollen. Klassen eignen sich für diesen Zweck möglicherweise nicht am besten, da sie während der Ausführung nicht deklariert oder geändert werden können.
Mit Strukturen können Sie benutzerdefinierte Datenformen dynamisch definieren, beispielsweise basierend auf der Benutzereingabe oder dem Kontext der Verarbeitung, um die Informationen anzugeben, die LLM aus den bereitgestellten Text- oder Chat-Nachrichten ableiten muss.
Das folgende Beispiel zeigt, wie eine Struktur definiert und als Antwortmodell verwendet wird:
<?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 );
?>
Weitere Informationen finden Sie im Abschnitt „Strukturen“.
Sie können Modell- und andere Optionen angeben, die an den OpenAI-/LLM-Endpunkt übergeben werden.
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 ]
);
Der Kursleiter bietet sofort einsatzbereiten Support für die folgenden API-Anbieter:
Anwendungsbeispiele finden Sie im Hub-Abschnitt oder im examples
im Code-Repository.
Sie können PHP DocBlocks (/** */) verwenden, um zusätzliche Anweisungen für LLM auf Klassen- oder Feldebene bereitzustellen, um beispielsweise zu klären, was Sie erwarten oder wie LLM Ihre Daten verarbeiten soll.
Der Kursleiter extrahiert PHP-DocBlocks-Kommentare aus der definierten Klasse und Eigenschaft und fügt sie in die Spezifikation des an LLM gesendeten Antwortmodells ein.
Die Verwendung von PHP-DocBlocks-Anweisungen ist nicht erforderlich, aber manchmal möchten Sie vielleicht Ihre Absichten zur Verbesserung der Inferenzergebnisse von LLM klarstellen.
/**
* 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 ;
}
Sie können die ValidationMixin-Eigenschaft verwenden, um die Möglichkeit einer einfachen, benutzerdefinierten Datenobjektvalidierung hinzuzufügen.
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 [];
}
}
Der Kursleiter verwendet die Symfony-Validierungskomponente, um extrahierte Daten zu validieren. Sie können die Annotation #[Assert/Callback] verwenden, um eine vollständig angepasste Validierungslogik zu erstellen.
use Cognesy Instructor Instructor ;
use Symfony Component Validator Constraints as Assert ;
use Symfony