Model or dataset
cognesy/instructor-php avatar
cognesy/instructor-php

cognesy/instructor-php: structured LLM output and an agent SDK for PHP

Unified LLM API, structured data outputs with LLMs, and agent SDK - in PHP

327 stars27 forksPHPMIT

At a glance

What is it?
A monorepo of framework-agnostic PHP packages for typed LLM extraction, a unified provider API, embeddings and tool-using agents. The extraction path is the mature part; the Symfony package is still being introduced.
Who is it for?
Adopt it if you already run PHP and want typed extraction, multi-provider inference and an agent loop without writing your own runtime; the Laravel package is the more settled framework path. Do not adopt it expecting a finished Symfony bundle, and do not pick it over Python's Instructor if your team's LLM work lives in Python.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 18, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What cognesy/instructor-php actually solves for a PHP team

The problem is the seam between an LLM response and the rest of a PHP application. A chat completion returns text or a JSON blob; your code then has to decide whether it parsed, whether the fields are the right types, and what to do when a model returns "twenty-eight" where an int was expected. Instructor for PHP closes that seam by letting you declare a plain PHP class with typehints and asking the library to populate it.

The README frames the benefit in exactly those terms: you stop hand-parsing JSON or text before using LLM results. The intended audience is a PHP developer already calling a provider's HTTP API directly, or one who has written a thin wrapper and is tired of maintaining it. The repository is a monorepo, not a single library, and the README lists three main capabilities: Instructor for structured data extraction, Polyglot for a unified LLM API, and an Agent SDK. Laravel and Symfony integration packages sit alongside them.

It is worth being precise about scope. This is not a framework, and the README describes the components as dev-friendly and framework agnostic. You can install the extraction package alone and ignore the agent runtime entirely. That matters for adoption: the pieces are separable, so you are not buying an architecture when you want a parser.

How the extraction path works, and where validation fits

The mechanism visible in the README is a response model plus a retry loop. You define a class, pass its name as responseModel alongside your input, and call get(). The library builds the request so the model returns data shaped for that class, deserializes the result into an instance, and hands it back. The README's own example is a two-field Person class extracted from the sentence "Jason is 28 years old."

Validation is the second half. According to the README, the response model generated by the LLM can be validated automatically, and Instructor currently supports only Symfony validation. You can also pass a context object to use enhanced validator capabilities. That single-provider constraint is a real design decision, not a footnote: if your project validates with something else, you either add Symfony validation as a dependency or you validate the returned object yourself after get().

Retries are the third piece. The README states you can set the number of retry attempts, and that Instructor repeats requests on validation or deserialization error up to the specified number of times, trying to get a valid response. Read that as a latency multiplier. A three-retry budget on a slow provider is four round trips in the worst case, and the library cannot tell you in advance how often a given model will fail your schema. The README also notes the library supports custom LLM output processors, not just JSON, which is the escape hatch when your target format is not JSON at all.

Installing it and extracting your first typed object

Installation is a single Composer command. The README gives it directly, with no repository registration or extension step:

bash
composer require cognesy/instructor-php

Before the first call you need a provider key in the environment. The README's basic example instructs you to create a .env file in your project root containing OPENAI_API_KEY=your_api_key. The library reads provider configuration from the environment, so the key name is tied to the provider you name in using().

Now the smallest working extraction. Define a plain class with typed public properties, then call StructuredOutput with the provider name, your input message and the class name. The README's example looks like this:

php
use Cognesy\Instructor\StructuredOutput;

final class Person {
    public string $name;
    public int $age;
}

$person = StructuredOutput::using('openai')
    ->with(messages: 'Jason is 28 years old.', responseModel: Person::class)
    ->get();

What you should see is an instance of Person with name and age populated. If the model returns something that does not deserialize into those types, the retry logic described above kicks in rather than throwing on the first attempt. The same StructuredOutput entry point is what the README uses for text, images or OpenAI-style chat sequence arrays as input.

The unified inference path is separate and simpler. If you only want provider portability, the README's example is a single call that returns text:

php
use Cognesy\Polyglot\Inference\Inference;

$text = Inference::using('openai')
    ->withMessages('Say hello in one sentence.')
    ->get();

Swapping 'openai' for another supported provider name is the whole migration for request code, which is the point of that package.

Provider coverage, and the drivers the README does not promise

The README lists out-of-the-box support for a long set of providers: A21 / Mamba, Anthropic, Azure OpenAI, Cerebras, Cohere (v2 OpenAI compatible), Deepseek, Fireworks, Google Gemini (native and OpenAI compatible), Groq, HuggingFace, Inception, Minimaxi, Mistral, Moonshot / Kimi, Ollama (on localhost), OpenAI, OpenRouter, Perplexity, Qwen, Sambanova and xAI / Grok. It also states you can write your own LLM drivers.

Two things about that list deserve attention. First, several entries are OpenAI-compatible endpoints rather than native integrations, and the README marks those as such for Cohere and Gemini. Compatibility layers drift; a provider that changes its compatibility surface can break a driver that never claimed to be native. Second, Ollama is described as running on localhost, which tells you the intended deployment for local models but nothing about how the library handles a remote Ollama host.

The provider list is also the thing most likely to age badly in this article. It reflects what the repository documented at the time of the last push on 2026-09-13, and the project ships releases frequently, so check the current README before assuming a provider is covered.

The agent loop, and why it is not the reason to adopt this

The Agent SDK is the largest surface in the monorepo and the least proven by the README. The example is short: build an AgentLoop with its default configuration and execute an AgentState seeded with a user message.

php
use Cognesy\Agents\AgentLoop;
use Cognesy\Agents\Data\AgentState;

$result = AgentLoop::default()->execute(
    AgentState::empty()->withUserMessage('What is 2+2?')
);

The README describes the broader feature set as custom tools, lifecycle hooks, subagents, context management, custom stop and continuation criteria, observability via events, packaged capabilities, agent templates and session management. The repository layout backs that up: examples are grouped into directories for agents, agent builders, agent templates, agent sessions, agent troubleshooting and agent evals, plus a separate agent-ctrl group for driving external coding agents like Codex, Claude Code and OpenCode from PHP.

That breadth is the trade-off. A simple loop over state, as the README characterizes it, is something most teams can write in an afternoon; the value here is in the surrounding machinery, and the surrounding machinery is what the README describes least concretely. If you need a tool-using agent in PHP, the examples directory is the real specification, not the README paragraph. Start there and read the D01 through D06 examples before deciding the SDK matches your control flow.

Laravel and Symfony integration are at different stages

The README is explicit that the Laravel package is a first-party integration covering facades, HTTP transport, native agents, telemetry, logging and testing. That is a described, complete surface.

The Symfony package is described differently: batteries-included, and currently being introduced under packages/symfony. The docs directory for it contains quickstart, configuration, operations and migration pages, which suggests the work is far enough along to document, but the README's own wording is the strongest signal available here. A migration document existing before a stable release is normal for a package in transition, and it also means the API you read about today may move.

If you are on Symfony, treat the integration as something to evaluate rather than adopt outright, and read packages/symfony/docs/migration.md first to see what the package expects you to change. If you are on Laravel, the integration is the documented path and the framework-agnostic packages remain available underneath it.

Maintenance, licence and what upgrades cost you

The repository is not archived, and the last push was on 2026-09-13, four days before this writing. Recent releases are v2.10.1 and v2.10.0, both dated 2026-09-13, with v2.9.5 on 2026-08-30. That cadence is a maintenance fact, not a quality claim, and it cuts both ways for an adopter: fixes arrive quickly, and so do minor version bumps you have to track.

The licence is MIT. In practical terms that is permissive, and the repository ships a LICENSE file at the top level. This is not legal advice; if your organisation has rules about dependency licences, run it past whoever owns that policy.

The upgrade cost is harder to estimate from the README alone. The monorepo contains a justfile that acts as the task runner for the project's own workflows, with recipes for setup, tests, QA, docs, packages, releases, git, CLI, examples and models, and a verify recipe that runs fast tests plus full QA. That is the maintainers' workflow, not yours, but it tells you the project invests in its own QA surface. For an application, the cost sits in the retry and validation configuration and in the provider drivers you rely on. A minor release that changes a driver's request shaping is invisible until a test fails.

Editorial conclusion

Adopt it if you already run PHP and want typed extraction, multi-provider inference and an agent loop without writing your own runtime; the Laravel package is the more settled framework path. Do not adopt it expecting a finished Symfony bundle, and do not pick it over Python's Instructor if your team's LLM work lives in Python. Before committing, verify that the provider driver you need is listed, that your validation rules fit the Symfony validator constraint, and that the retry budget you configure is acceptable against your latency target.

Frequently asked questions

How do I install cognesy/instructor-php?

Install it with Composer using composer require cognesy/instructor-php, as shown in the README. You then need a provider API key in your environment, for example OPENAI_API_KEY in a .env file at your project root.

Which LLM providers does cognesy/instructor-php support?

The README lists OpenAI, Anthropic, Azure OpenAI, Google Gemini (native and OpenAI compatible), Ollama on localhost, Mistral, Groq, Deepseek, OpenRouter, Perplexity, xAI / Grok, Cohere v2, Cerebras, Fireworks, HuggingFace, Qwen, Sambanova, Moonshot / Kimi, Minimaxi, Inception and A21 / Mamba. It also states you can write your own LLM drivers.

Does cognesy/instructor-php validate the data the model returns?

Yes, but the README states that Instructor currently supports only Symfony validation. You can also provide a context object to use enhanced validator capabilities.

What happens if the LLM returns data that does not match my response model?

Instructor repeats the request on validation or deserialization error up to a number of retry attempts you configure. The README documents setting that retry count but does not give a default value.

Is there a Laravel or Symfony package for cognesy/instructor-php?

Both exist. The README describes the Laravel package as a first-party integration for facades, HTTP transport, native agents, telemetry, logging and testing, while the Symfony package is described as batteries-included and currently being introduced under packages/symfony.

Official sources

  1. cognesy/instructor-php on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Community notes

Community notes