-
Notifications
You must be signed in to change notification settings - Fork 7
Output Formatter
The Agents Output Formatter is a feature designed to format the output of AI agents in a structured manner. It provides a flexible way to define output schemas and ensure that the AI's responses conform to these schemas. The main components of this feature are:
-
FormatterInterface: An interface that defines the contract for output formatters. -
JsonSchemaFormatter: A concrete implementation ofFormatterInterfacethat formats output based on JSON schemas. -
EnumFormatter: Another implementation ofFormatterInterfacethat formats output based on PHP enums. -
SelectFormatter: A formatter that ensures the output is one of a predefined set of options.
The feature allows developers to specify the desired output format, which can be a JSON schema, an enum, or a set of predefined options. The formatter then processes the AI's output to ensure it matches the specified format, throwing exceptions if the output doesn't conform.
a) Formatting user profile data:
$formatter = new JsonSchemaFormatter($schemaMapper);
$formatter = $formatter->withJsonSchema(UserProfile::class);
$userProfileData = $formatter->format($aiOutput);
class UserProfile {
public function __construct(
public readonly string $name,
public readonly int $age,
public readonly string $email
) {}
}
// Usage
$profile = new UserProfile(
name: $userProfileData->name,
age: $userProfileData->age,
email: $userProfileData->email
);b) Ensuring a valid response type:
enum ResponseType {
case Yes;
case No;
case Maybe;
}
$formatter = new EnumFormatter(ResponseType::class);
$response = $formatter->format($aiOutput);
// Usage
match ($response) {
ResponseType::Yes => handleYesResponse(),
ResponseType::No => handleNoResponse(),
ResponseType::Maybe => handleMaybeResponse(),
};c) Validating multiple-choice answers:
$formatter = new SelectFormatter('A', 'B', 'C', 'D');
$selectedAnswer = $formatter->format($aiOutput);
// Usage
$question = new MultipleChoiceQuestion(
text: "What is the capital of France?",
options: ['A' => 'London', 'B' => 'Paris', 'C' => 'Berlin', 'D' => 'Rome'],
correctAnswer: $selectedAnswer
);d) Formatting structured product data:
$formatter = new JsonSchemaFormatter($schemaMapper);
$formatter = $formatter->withJsonSchema(ProductData::class);
$productData = $formatter->format($aiOutput);
class ProductData {
public function __construct(
public readonly string $name,
public readonly float $price,
public readonly array $categories,
public readonly ?string $description = null
) {}
}
// Usage
$product = new Product(
name: $productData->name,
price: $productData->price,
categories: $productData->categories,
description: $productData->description
);e) Ensuring valid sentiment analysis output:
enum Sentiment {
case Positive;
case Neutral;
case Negative;
}
$formatter = new EnumFormatter(Sentiment::class);
$sentiment = $formatter->format($aiOutput);
// Usage
$review = new ProductReview(
text: $reviewText,
rating: $userRating,
sentiment: $sentiment
);The main configuration options are available through the JsonSchemaFormatter class:
a) JSON Schema:
- Default: None (must be set)
- Configuration:
$formatter = new JsonSchemaFormatter($schemaMapper);
$formatter = $formatter->withJsonSchema(UserProfile::class);b) Schema Mapper:
- Default: None (must be provided in the constructor)
- Configuration:
$schemaMapper = new SchemaMapper();
$formatter = new JsonSchemaFormatter(schemaMapper: $schemaMapper);For the EnumFormatter and SelectFormatter, the configuration is done through the constructor:
$enumFormatter = new EnumFormatter(enumClass: ResponseType::class);
$selectFormatter = new SelectFormatter('Option1', 'Option2', 'Option3');a) SchemaMapperInterface:
- Purpose: Defines the contract for mapping between JSON schemas and PHP objects.
- Interaction: Used by
JsonSchemaFormatterto convert between JSON schemas and PHP classes.
b) FormatterException:
- Purpose: Custom exception class for formatting errors.
- Interaction: Thrown by formatters when the output doesn't conform to the specified schema or options.
c) InvalidArgumentException:
- Purpose: Standard PHP exception for invalid arguments.
- Interaction: Thrown when invalid configuration options are provided to the formatters.
classDiagram
class FormatterInterface {
+format(output: string|Stringable): mixed
+getInstruction(): string|null
}
class JsonSchemaFormatter {
-schemaMapper: SchemaMapperInterface
-jsonSchema: string|null
-class: string|null
+withJsonSchema(jsonSchema: string): self
+format(output: string|Stringable): mixed
+getInstruction(): string|null
}
class EnumFormatter {
-enum: ReflectionEnum
-formatter: FormatterInterface
+format(output: string|Stringable): mixed
+getInstruction(): string|null
}
class SelectFormatter {
-options: array
+format(output: string|Stringable): string
+getInstruction(): string|null
}
class SchemaMapperInterface {
+toJsonSchema(class: string): array
+toObject(json: string, class: string|null): object
}
FormatterInterface <|-- JsonSchemaFormatter
FormatterInterface <|-- EnumFormatter
FormatterInterface <|-- SelectFormatter
JsonSchemaFormatter --> SchemaMapperInterface
This class diagram illustrates the relationships between the main FormatterInterface and its implementations, as well as the dependency on SchemaMapperInterface for the JsonSchemaFormatter.
Here's an expanded example that demonstrates how to use the formatter with an agent:
<?php
declare(strict_types=1);
namespace App\Agents\WebScraper;
use LLM\Agents\AgentExecutor\ExecutorInterface;
use LLM\Agents\LLM\Options;
use LLM\Agents\LLM\Output\JsonSchemaFormatter;
use LLM\Agents\Tool\SchemaMapperInterface;
use LLM\Agents\LLM\Prompt\Chat\Prompt;
use LLM\Agents\LLM\Prompt\Chat\MessagePrompt;
use Spiral\JsonSchemaGenerator\Attribute\Field;
final class FoundLinks implements \JsonSerializable
{
public function __construct(
/**
* @var array<string>
*/
#[Field(title: 'Links', description: 'Links found on the page')]
public array $links,
) {}
public function jsonSerialize(): array
{
return [
'links' => $this->links,
];
}
}
class WebScraperAgent
{
public function __construct(
private ExecutorInterface $executor,
private SchemaMapperInterface $schemaMapper
) {}
public function scrapeLinks(string $url): FoundLinks
{
$formatter = new JsonSchemaFormatter($this->schemaMapper);
$formatter = $formatter->withJsonSchema(FoundLinks::class);
$options = new Options([
'output_formatter' => $formatter,
]);
$prompt = new Prompt([
MessagePrompt::system("You are a web scraping assistant. Your task is to extract all links from the given URL."),
MessagePrompt::user("Extract all links from the following URL: $url"),
]);
$result = $this->executor->execute(
agent: 'web-scraper',
prompt: $prompt,
options: $options
);
// The result will be automatically formatted as a FoundLinks object
return $result->content;
}
}
// Usage
$webScraperAgent = new WebScraperAgent($executor, $schemaMapper);
$foundLinks = $webScraperAgent->scrapeLinks('https://spiral.dev');
// Access the formatted results
foreach ($foundLinks->links as $link) {
echo "Found link: $link\n";
}Let's break down how this example works with the formatting feature:
-
We define the
FoundLinksclass, which represents the structured output we expect from the web scraper agent. This class uses attributes to define the JSON schema. -
In the
WebScraperAgentclass, we create a methodscrapeLinksthat encapsulates the logic for using the agent with the formatter. -
We create a
JsonSchemaFormatterinstance and configure it with theFoundLinksclass:$formatter = new JsonSchemaFormatter($this->schemaMapper); $formatter = $formatter->withJsonSchema(FoundLinks::class);
-
We create an
Optionsobject and set theoutput_formatterto our configured formatter:$options = new Options([ 'output_formatter' => $formatter, ]);
-
We create a
Promptobject with system and user messages to instruct the agent:$prompt = new Prompt([ MessagePrompt::system("You are a web scraping assistant. Your task is to extract all links from the given URL."), MessagePrompt::user("Extract all links from the following URL: $url"), ]);
-
We execute the agent using the executor, passing the prompt and options:
$result = $this->executor->execute( agent: 'web-scraper', prompt: $prompt, options: $options );
-
The executor will use the specified formatter to process the agent's output. The
JsonSchemaFormatterensures that the output conforms to theFoundLinksschema. -
The formatted result is returned as a
FoundLinksobject, which we can then use to access the extracted links.
This example demonstrates how to integrate the formatting feature with an agent using the executor. The formatter ensures that the agent's output is structured according to the specified schema, making it easy to work with the results in a type-safe manner.