LLM を利用した PHP での構造化データ抽出。シンプルさ、透明性、制御性を重視して設計されています。
Instructor は、テキスト、画像、または OpenAI スタイルのチャット シーケンス配列など、複数の種類の入力から構造化され検証されたデータを抽出できるライブラリです。これは、Large Language Model (LLM) によって強化されています。
Instructor は、PHP プロジェクトでの LLM の統合を簡素化します。 LLM 出力から構造化データを抽出する複雑さを処理するため、アプリケーション ロジックの構築に集中して反復処理を高速化できます。
Instructor for PHP は、Jason Liu によって作成された Python 用の Instructor ライブラリからインスピレーションを得ています。
Instructor を使用してテキストから構造化データを抽出する簡単な CLI デモ アプリを次に示します。
Structure
クラスを使用した動的データ形状以下の他の言語での実装を確認してください。
Instructor を別の言語に移植したい場合は、Twitter までご連絡ください。お手伝いをいたします。
インストラクターは、API の直接使用と比較して 3 つの重要な機能強化を紹介します。
LLM チャット補完の「魔法」を介してデータを抽出する PHP クラスを指定するだけです。それで終わりです。
インストラクターは、構造化された LLM 応答を活用することで、テキスト データから情報を抽出するコードの脆弱性を軽減します。
インストラクターは、よりシンプルで理解しやすいコードを作成するのに役立ちます。長い関数呼び出し定義を定義したり、返された JSON をターゲット データ オブジェクトに割り当てるためのコードを作成したりする必要はもうありません。
LLM によって生成された応答モデルは、一連のルールに従って自動的に検証できます。現在、Instructor は Symfony 検証のみをサポートしています。
拡張されたバリデーター機能を使用するためにコンテキスト オブジェクトを提供することもできます。
リクエストの再試行回数を設定できます。
インストラクターは、検証または逆シリアル化エラーが発生した場合に、指定された回数までリクエストを繰り返し、LLM から有効な応答を取得しようとします。
インストラクターのインストールは簡単です。ターミナルで次のコマンドを実行すると、よりスムーズなデータ処理エクスペリエンスが得られます。
composer require cognesy/instructor-php
これは、インストラクターが提供されたテキスト (またはチャット メッセージ シーケンス) から構造化情報を取得する方法を示す簡単な例です。
応答モデル クラスは、オブジェクトのフィールドの型を指定するタイプヒントを備えたプレーンな PHP クラスです。
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
// }
注:インストラクターは、応答モデルとしてクラス/オブジェクトをサポートします。単純な型または列挙型を抽出する場合は、それらをスカラー アダプターでラップする必要があります。以下のセクション「スカラー値の抽出」を参照してください。
Instructor を使用すると、 llm.php
ファイルで複数の API 接続を定義できます。これは、アプリケーションでさまざまな LLM または API プロバイダーを使用する場合に便利です。
デフォルト設定は、インストラクター コードベースのルート ディレクトリの/config/llm.php
にあります。これには、インストラクターがすぐに使用できるすべての LLM API への事前定義された接続のセットが含まれています。
構成ファイルは、LLM API への接続とそのパラメーターを定義します。また、クライアント接続を指定せずに Instructor を呼び出すときに使用するデフォルトの接続も指定します。
// 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 ' => [ ... ],
// ...
利用可能な接続をカスタマイズするには、既存のエントリを変更するか、独自のエントリを追加します。
事前定義された接続を介した LLM API への接続は、接続名を指定してwithClient
メソッドを呼び出すだけで簡単です。
// ...
$ user = ( new Instructor )
-> withConnection ( ' ollama ' )
-> respond (
messages: " His name is Jason and he is 28 years old. " ,
responseModel: Person ::class,
);
// ...
INSTRUCTOR_CONFIG_PATH
環境変数を介して、インストラクターが使用する構成ファイルの場所を変更できます。デフォルトの構成ファイルのコピーを開始点として使用できます。
インストラクターは、構造化データを入力として使用する方法を提供します。これは、オブジェクト データを入力として使用し、LLM 推論の結果を持つ別のオブジェクトを取得する場合に便利です。
インストラクターのrespond()
とrequest()
メソッドのinput
フィールドには、オブジェクトだけでなく、配列や単なる文字列も使用できます。
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."
// }
?>
インストラクターは、データ モデルで指定された検証ルールに照らして LLM 応答の結果を検証します。
利用可能な検証ルールの詳細については、Symfony Validation 制約を確認してください。
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
maxRetries パラメータが指定されており、LLM 応答が検証基準を満たしていない場合、インストラクターは、結果が要件を満たすか、maxRetries に達するまで、その後の推論を試行します。
インストラクターは検証エラーを使用して応答で特定された問題を LLM に通知し、LLM が次回の試行で自己修正を試行できるようにします。
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
request()
メソッドを呼び出してリクエストのパラメータを設定し、その後get()
を呼び出して応答を取得できます。
use Cognesy Instructor Instructor ;
$ instructor = ( new Instructor )-> request (
messages: " His name is Jason, he is 28 years old. " ,
responseModel: Person ::class,
);
$ person = $ instructor -> get ();
インストラクターは部分的な結果のストリーミングをサポートしているため、データが利用可能になるとすぐに処理を開始できます。
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 );
?>
onPartialUpdate()
コールバックを定義して、LLM が推論を完了する前に UI の更新を開始するために使用できる部分的な結果を受け取ることができます。
注: 部分的な更新は検証されません。応答は完全に受信された後にのみ検証されます。
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
メッセージの配列の代わりに文字列を指定できます。これは、単一のテキスト ブロックからデータを抽出し、コードを単純に保ちたい場合に便利です。
// 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,
);
特に、文字列、整数、ブール値、または浮動小数点数の形式で単純な答えを取得しようとしている場合は、応答モデルのクラスを定義せずに、単に結果を迅速に取得したい場合があります。 Instructor は、そのような場合に備えて簡素化された API を提供します。
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)
この例では、テキストから単一の整数値を抽出しています。 Scalar::string()
、 Scalar::boolean()
、およびScalar::float()
を使用して、他のタイプの値を抽出することもできます。
さらに、Scalar アダプターを使用してScalar::enum()
を使用して提供されたオプションの 1 つを抽出できます。
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 は、提供されたコンテキストから Instructor によって抽出されるオブジェクトのリストを表すために使用できるラッパー クラスです。
通常は、特定のクラスのオブジェクトのリストを処理するためだけに、単一の配列プロパティを持つ専用のクラスを作成しない方が便利です。
シーケンスの追加のユニークな機能は、プロパティの更新ではなく、シーケンス内の完了したアイテムごとにストリーミングできることです。
class Person
{
public string $ name ;
public int $ age ;
}
$ 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 ]
);
シーケンスの詳細については、「シーケンス」セクションを参照してください。
PHP の型ヒントを使用して、抽出されたデータの型を指定します。
指定されたフィールドがオプションであることを示すには、null 許容型を使用します。
class Person {
public string $ name ;
public ? int $ age ;
public Address $ address ;
}
PHP DocBlock スタイルのコメントを使用して、抽出されたデータの種類を指定することもできます。これは、LLM のプロパティ タイプを指定したいが、コード レベルでタイプを強制できない、または強制したくない場合に便利です。
class Person {
/** @var string */
public $ name ;
/** @var int */
public $ age ;
/** @var Address $address person's address */
public $ address ;
}
詳細については、DocBlock Web サイトの PHPDoc ドキュメントを参照してください。
PHP は現在、配列要素の型を指定するためのジェネリックスやタイプヒントをサポートしていません。
PHP DocBlock スタイルのコメントを使用して、配列要素の型を指定します。
class Person {
// ...
}
class Event {
// ...
/** @var Person[] list of extracted event participants */
public array $ participants ;
// ...
}
講師はテキストから複雑なデータ構造を取得できます。応答モデルには、ネストされたオブジェクト、配列、列挙型を含めることができます。
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
// },
// ]
// }
実行時にデータの形状を定義したい場合は、 Structure
クラスを使用できます。
構造体を使用すると、LLM によって抽出されるデータの任意の形状を定義および変更できます。クラスは実行中に宣言または変更できないため、この目的には最適ではない可能性があります。
構造体を使用すると、たとえばユーザー入力や処理のコンテキストに基づいてカスタム データ形状を動的に定義し、提供されたテキストやチャット メッセージから LLM が推測するために必要な情報を指定できます。
以下の例は、構造を定義し、それを応答モデルとして使用する方法を示しています。
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 );
?>
詳細については、「構造」セクションを参照してください。
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 ]
);
インストラクターは、次の API プロバイダーに対するすぐに使用できるサポートを提供します。
使用例については、コード リポジトリのハブ セクションまたはexamples
ディレクトリを確認してください。
PHP DocBlocks (/** */) を使用すると、クラスまたはフィールド レベルで LLM に追加の指示を提供できます。たとえば、期待される内容や LLM がデータをどのように処理するかを明確にすることができます。
インストラクターは、定義されたクラスおよびプロパティから PHP DocBlocks コメントを抽出し、LLM に送信される応答モデルの仕様に含めます。
PHP DocBlocks 命令の使用は必須ではありませんが、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 ;
}
ValidationMixin トレイトを使用すると、簡単なカスタム データ オブジェクト検証の機能を追加できます。
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 [];
}
}
講師は Symfony 検証コンポーネントを使用して、抽出されたデータを検証します。 #[Assert/Callback] アノテーションを使用して、完全にカスタマイズされた検証ロジックを構築できます。
use Cognesy Instructor Instructor ;
use Symfony Component Validator Constraints as Assert ;
use Symfony Component Validator Context ExecutionContextInterface ;
class UserDetails
{
public string $ name ;
public int $ age ;
#[ Assert Callback ]
public function validateName ( ExecutionContextInterface $ context , mixed $ payload ) {
if ( $ this -> name !== strtoupper ( $ this -> name )) {
$ context -> buildViolation ( " Name must be in uppercase. " )
-> atPath ( ' name ' )
-> setInvalidValue ( $ this -> name )
-> addViolation ();
}
}
}
$ user = ( new Instructor )-> respond (
messages: [[ ' role ' => ' user ' , ' content ' => ' jason is 25 years old ' ]],
responseModel: UserDetails ::class,
maxRetries: 2
);
assert ( $ user -> name === " JASON " );
コールバック制約の使用方法の詳細については、Symfony のドキュメントを参照してください。
Instructor for PHP はリクエストを処理する際に、いくつかの段階を経ます。
Instructor を使用すると、イベントを介してリクエストとレスポンスの処理のあらゆる段階で詳細情報を受け取ることができます。
(new Instructor)->onEvent(string $class, callable $callback)
メソッド - 指定されたタイプのイベントが送出されたときにコールバックを受け取ります(new Instructor)->wiretap(callable $callback)
メソッド - インストラクターによって送出されたイベントを受信します。デバッグやパフォーマンス分析に役立つ場合があります。イベントを受信すると、実行プロセスを監視するのに役立ち、開発者が処理上の問題を理解し、解決することが容易になります。
$ instructor = ( new Instructor )
// see requests to LLM
-> onEvent ( RequestSentToLLM ::class, fn( $ e ) => dump ( $ e ))
// see responses from LLM
-> onEvent ( ResponseReceivedFromLLM ::class, fn( $ event ) => dump ( $ event ))
// see all events in console-friendly format
-> wiretap (fn( $ event ) => dump ( $ event -> toConsole ()));
$ instructor -> respond (
messages: " What is the population of Paris? " ,
responseModel: Scalar :: integer (),
);
// check your console for the details on the Instructor execution
インストラクターは、応答モデルとして提供される複数のタイプの入力を処理できるため、ライブラリとの対話方法がより柔軟になります。
Instructor のrespond()
メソッドのシグネチャでは、 responseModel
文字列、オブジェクト、または配列のいずれかであることが示されています。
string
値が指定された場合、それは応答モデルのクラスの名前として使用されます。
インストラクターは、クラスが存在するかどうかを確認し、クラスとプロパティの型情報とドキュメントのコメントを分析して、LLM 応答制約を指定するために必要なスキーマを生成します。
応答モデル クラスの名前を指定する最良の方法は、文字列の代わりにNameOfTheClass::class
を使用することです。これにより、IDE が型チェックを実行したり、リファクタリングを処理したりできるようになります。
object
値が指定された場合、それは応答モデルのインスタンスとみなされます。インストラクターはインスタンスのクラスをチェックし、インスタンスとそのプロパティ タイプ データを分析して LLM 応答制約を指定します。
array
値が提供された場合、それは生の JSON スキーマとみなされ、そのため、インストラクターは (適切なコンテキストでラップした後 - 関数呼び出しなど)、LLM リクエストでそれを直接使用できます。
Instructor は、データを適切な型に正しく逆シリアル化できるように、JSON スキーマ内の各ネストされたオブジェクトのクラスに関する情報を必要とします。
$responseModel をクラス名またはインスタンスとして渡す場合、この情報はインストラクターに利用可能ですが、生の JSON スキーマにはありません。
現在の設計では、プロパティの JSON スキーマ$comment
フィールドを使用して、これを解決しています。インストラクターは、開発者が$comment
フィールドを使用して、オブジェクトまたは列挙型のプロパティ データを逆シリアル化するために使用されるターゲット クラスの完全修飾名を提供することを期待します。
Instructor を使用すると、クラスまたはインスタンスが実装するインターフェイスを確認することによって、$responseModel 値の処理をカスタマイズすることもできます。
CanProvideJsonSchema
- JSON スキーマまたは応答モデルを提供できるように実装し、$responseModel 値クラス情報を分析する Instructor のデフォルトのアプローチをオーバーライドします。CanDeserializeSelf
- LLM からの応答が JSON から PHP オブジェクトに逆シリアル化される方法をカスタマイズするために実装します。CanValidateSelf
- 逆シリアル化されたオブジェクトの検証方法をカスタマイズするために実装します。CanTransformSelf
- 検証されたオブジェクトを呼び出し元が受け取るターゲット値に変換するために実装します (たとえば、単純型をクラスからスカラー値にラップ解除します)。 PHP エコシステムには、Instructor for Python の中核となる Pydantic に相当する強力な機能が (まだ) ありません。
ここで必要な重要な機能を提供するために、Instructor for PHP は以下を活用します。
Instructor for PHP は PHP 8.2 以降と互換性があり、依存関係が最小限に抑えられているため、任意のフレームワークで動作するはずです。
いくつかの追加機能には追加の依存関係が必要です。
サポートしたい場合は、いくつかの問題を確認してください。コードの改善、ドキュメント、バグレポート、ブログ投稿/記事、または新しいクックブックやアプリケーションの例など、あらゆる貢献を歓迎します。
このプロジェクトは、MIT ライセンスの条件に基づいてライセンスされています。
ご質問がある場合やサポートが必要な場合は、Twitter または GitHub までご連絡ください。