PHPackages                             phpro/agent-rules - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. phpro/agent-rules

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

phpro/agent-rules
=================

Declarative rule engine for conversational agents and interactive workflows

0.3.0(1mo ago)4125[1 PRs](https://github.com/phpro/agent-rules/pulls)MITPHPPHP ~8.3.0 || ~8.4.0 || ~8.5.0CI passing

Since Jan 21Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/phpro/agent-rules)[ Packagist](https://packagist.org/packages/phpro/agent-rules)[ RSS](/packages/phpro-agent-rules/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (2)Dependencies (6)Versions (6)Used By (0)

Agent Rules
===========

[](#agent-rules)

A declarative rule engine designed for conversational agents, AI assistants, and interactive workflows.

Perfect for building chatbots, AI agents, CLI wizards, or any system that guides users through multi-step processes by asking questions and validating responses.

Overview
--------

[](#overview)

Agent Rules provides a logical processing unit for handling **unstructured input in a structured and interactive way**. It enables you to define validation rules that can request missing information, enforce business logic, and provide intelligent feedback to users or AI agents.

Built to be compatible with [Symfony AI Agent](https://github.com/symfony/ai-agent), this library integrates seamlessly with AI-powered tools and conversational interfaces.

Key Features
------------

[](#key-features)

- **Declarative Rule Definition**: Define validation logic as composable rule objects
- **Automatic Dependency Resolution**: Rules declare dependencies, engine handles execution order
- **Interactive Feedback**: Rules can request missing data with structured result types
- **Composable Logic**: Combine rules with `Sequence` (AND), `Any` (OR), and `Either` patterns
- **Type-Safe**: Full PHP 8.4+ type safety with readonly classes and generic support
- **Framework Agnostic**: Works independently with optional Symfony AI integration

Installation
------------

[](#installation)

```
composer require phpro/agent-rules
```

Quick Start
-----------

[](#quick-start)

### 1. Define Your Subject

[](#1-define-your-subject)

The subject is the data being validated:

```
final class OrderRequest
{
    public function __construct(
        public readonly ?string $email,
        public readonly ?string $productId,
        public readonly ?int $quantity,
    ) {}
}
```

### 2. Create Validation Rules

[](#2-create-validation-rules)

Each rule checks one aspect and can request missing information:

```
use Phpro\AgentRules\RuleInterface;
use Phpro\AgentRules\RuleEvaluation;
use Phpro\AgentRules\Result\IncompleteResult;

/**
 * @implements RuleInterface
 */
class EmailRule implements RuleInterface
{
    public function name(): string
    {
        return 'email_validation';
    }

    public function dependencies(): array
    {
        return []; // No dependencies
    }

    public function check(mixed $subject): RuleEvaluation
    {
        // Missing email? Ask for it!
        if ($subject->email === null) {
            return RuleEvaluation::respond(
                new IncompleteResult(
                    missingField: 'email',
                    message: 'Please provide your email address to proceed.'
                )
            );
        }

        // Invalid format? Request correction!
        if (!filter_var($subject->email, FILTER_VALIDATE_EMAIL)) {
            return RuleEvaluation::respond(
                new IncompleteResult(
                    missingField: 'email',
                    message: 'The email address is invalid. Please provide a valid email.'
                )
            );
        }

        // All good!
        return RuleEvaluation::pass();
    }
}
```

### 3. Run the Engine

[](#3-run-the-engine)

```
use Phpro\AgentRules\RuleEngine;

$engine = new RuleEngine(
    new EmailRule(),
    new ProductRule(),
    new QuantityRule(),
);

$request = new OrderRequest(
    email: null,
    productId: 'PROD-123',
    quantity: 2
);

$evaluation = $engine->evaluate($request);

if (!$evaluation->isPass()) {
    $result = $evaluation->result;
    echo $result->message; // "Please provide your email address to proceed."
    echo $result->missingField; // "email"
}
```

Result Types
------------

[](#result-types)

The engine provides four result types to communicate different states:

Result TypeStatusUse CaseKey Fields`CompleteResult``complete`Validation passed`message``IncompleteResult``incomplete`Missing/invalid data`missingField`, `message``BlockedResult``blocked`Access denied`reason`, `message``ErrorResult``error`Processing error`message`, `resolution`### Complete Result

[](#complete-result)

```
return RuleEvaluation::respond(
    new CompleteResult(
        message: 'Order validated successfully!'
    )
);
```

### Incomplete Result

[](#incomplete-result)

```
return RuleEvaluation::respond(
    new IncompleteResult(
        missingField: 'shippingAddress',
        message: 'We need your shipping address to complete the order.'
    )
);
```

### Blocked Result

[](#blocked-result)

```
return RuleEvaluation::respond(
    new BlockedResult(
        reason: 'Product out of stock',
        message: 'This product is currently unavailable.'
    )
);
```

### Error Result

[](#error-result)

```
return RuleEvaluation::respond(
    new ErrorResult(
        message: 'Unable to validate product availability.',
        resolution: 'Please try again later or contact support.'
    )
);
```

Composable Rules
----------------

[](#composable-rules)

### Sequence (AND Logic)

[](#sequence-and-logic)

All rules must pass. First failure stops execution:

```
use Phpro\AgentRules\Sequence;

$allRequired = new Sequence(
    new EmailRule(),
    new ProductRule(),
    new QuantityRule(),
);
```

### Any (OR Logic)

[](#any-or-logic)

At least one rule must pass:

```
use Phpro\AgentRules\Any;

$paymentMethod = new Any(
    new CreditCardRule(),
    new PayPalRule(),
    new BankTransferRule(),
);
```

### Either (Binary Choice)

[](#either-binary-choice)

Try left, fall back to right:

```
use Phpro\AgentRules\Either;

$authentication = new Either(
    new OAuthRule(),
    new PasswordRule(),
);
```

Dependency Resolution
---------------------

[](#dependency-resolution)

Rules can declare dependencies to ensure proper execution order:

```
class DiscountRule implements RuleInterface
{
    public function dependencies(): array
    {
        return ['product_validation', 'user_authentication'];
    }

    public function name(): string
    {
        return 'discount_calculation';
    }

    public function check(mixed $subject): RuleEvaluation
    {
        // This runs only after dependencies pass
        // ...
    }
}
```

The engine automatically:

- Resolves dependency order using topological sort
- Detects cyclic dependencies
- Validates that all dependencies exist

Adding Context with Sources
---------------------------

[](#adding-context-with-sources)

You can attach contextual information to results that helps users or AI agents understand where to find more information:

```
use Phpro\AgentRules\Source\Source;

$result = new IncompleteResult(
    missingField: 'productId',
    message: 'Please provide the product ID.'
);

$result->sources()->add(
    new Source(
        name: 'Product Catalog',
        reference: 'https://example.com/products',
        content: 'Browse our product catalog to find product IDs.'
    )
);

return RuleEvaluation::respond($result);
```

Sources are particularly useful for:

- Documentation links
- Example values
- Help text
- Related resources
- API references

### Symfony Configuration

[](#symfony-configuration)

Configure the rule engine as a service:

```
# config/services.yaml
services:
    OrderValidationEngine:
        class: Phpro\AgentRules\RuleEngine
        factory: ['Phpro\AgentRules\RuleEngine', 'fromIterable']
        arguments:
            - !tagged_iterator order.validation.rule
```

Tag your rules:

```
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;

#[AutoconfigureTag('order.validation.rule', ['priority' => 100])]
class EmailRule implements RuleInterface
{
    // ...
}
```

Real-World Example: Form Validation Assistant
---------------------------------------------

[](#real-world-example-form-validation-assistant)

Imagine building a conversational AI that helps users complete a multi-step registration form. The AI needs to:

- Ask for information in the right order
- Validate data as it's provided
- Give helpful feedback on errors
- Know when all required data is collected

 ```
sequenceDiagram
    participant User
    participant AI Agent
    participant Registration Tool
    participant Rule Engine
    participant Rules

    User->>AI Agent: I want to create an account
    AI Agent->>Registration Tool: validate_registration(context={})
    Registration Tool->>Rule Engine: evaluate(request)
    Rule Engine->>Rules: Check email rule
    Rules-->>Rule Engine: IncompleteResult(missing: email)
    Rule Engine-->>Registration Tool: First rule failed
    Registration Tool-->>AI Agent: IncompleteResult
    AI Agent->>User: What's your email address?

    User->>AI Agent: john@example.com
    AI Agent->>Registration Tool: validate_registration(email="john@example.com")
    Registration Tool->>Rule Engine: evaluate(request)
    Rule Engine->>Rules: Check email rule
    Rules-->>Rule Engine: Pass ✓
    Rule Engine->>Rules: Check password rule
    Rules-->>Rule Engine: IncompleteResult(missing: password)
    Rule Engine-->>Registration Tool: Second rule failed
    Registration Tool-->>AI Agent: IncompleteResult
    AI Agent->>User: Please create a password (min 8 chars)

    User->>AI Agent: MySecurePass123
    AI Agent->>Registration Tool: validate_registration(email="...", password="...")
    Registration Tool->>Rule Engine: evaluate(request)
    Rule Engine->>Rules: Check all rules
    Rules-->>Rule Engine: All Pass ✓
    Rule Engine-->>Registration Tool: RuleEvaluation::pass()
    Registration Tool-->>AI Agent: CompleteResult
    AI Agent->>User: Account created successfully!
```

      Loading ### Implementation

[](#implementation)

#### 1. Define the Request Context

[](#1-define-the-request-context)

```
use Symfony\AI\Platform\Contract\JsonSchema\Attribute\With;

final class RegistrationRequest
{
    public function __construct(
        public readonly ?string $email = null,
        #[With(minLength: 10, maxLength: 255)]
        #[\SensitiveParameter]
        public readonly ?string $password = null,
        public readonly ?string $fullName = null,
        public readonly ?bool $termsAccepted = null,
    ) {}
}
```

#### 2. Create Validation Rules

[](#2-create-validation-rules-1)

```
#[AutoconfigureTag('registration.rule', ['priority' => 100])]
class EmailRule implements RuleInterface
{
    public function name(): string { return 'email'; }
    public function dependencies(): array { return []; }

    public function check(mixed $subject): RuleEvaluation
    {
        if (!$subject->email) {
            return RuleEvaluation::respond(
                new IncompleteResult(
                    missingField: 'email',
                    message: "What's your email address?"
                )
            );
        }

        if (!filter_var($subject->email, FILTER_VALIDATE_EMAIL)) {
            return RuleEvaluation::respond(
                new IncompleteResult(
                    missingField: 'email',
                    message: 'Please provide a valid email address.'
                )
            );
        }

        return RuleEvaluation::pass();
    }
}
```

```
#[AutoconfigureTag('registration.rule', ['priority' => 90])]
class PasswordRule implements RuleInterface
{
    public function name(): string { return 'password'; }
    public function dependencies(): array { return ['email']; }

    public function check(mixed $subject): RuleEvaluation
    {
        if (!$subject->password) {
            return RuleEvaluation::respond(
                new IncompleteResult(
                    missingField: 'password',
                    message: 'Please create a password (minimum 8 characters).'
                )
            );
        }

        if (strlen($subject->password) < 8) {
            return RuleEvaluation::respond(
                new IncompleteResult(
                    missingField: 'password',
                    message: 'Password must be at least 8 characters long.'
                )
            );
        }

        return RuleEvaluation::pass();
    }
}
```

#### 3. Create the AI tool

[](#3-create-the-ai-tool)

This is an example of linking the rule engine to a Symfony AI agent tool:

```
#[AsTool(
    name: 'validate_registration',
    description: 'Validates user registration data and requests missing information.'
)]
final class RegistrationTool implements HasSourcesInterface
{
    use HasSourcesTrait;

    public function __construct(
        #[Autowire(service: 'RegistrationEngine')]
        private RuleEngine $ruleEngine,
    ) {}

    public function __invoke(
        RegistrationRequest $request,
    ): ResultInterface {
        $evaluation = $this->ruleEngine->evaluate($request);
        $result = $evaluation->result ?? new CompleteResult(
            message: 'Registration validated! Your account has been created.'
        );

        foreach ($result->sources() as $source) {
            $this->addSource(new \Symfony\AI\Agent\Toolbox\Source\Source(
                name: $source->name,
                reference: $source->reference,
                content: $source->content
            ));
        }

        return $result;
    }
}
```

With following agent configuration:

```
ai:
    agent:
        registration_assistant:
            platform: 'ai.platform.openai'
            model: 'gpt-4'
            prompt: |
                You help users create accounts by collecting required information.

                WORKFLOW:
                1. Call validate_registration with any info you have
                2. Tool returns what's missing → ask the user
                3. Call validate_registration again with ALL collected data
                4. Repeat until you get "complete" status

                NEVER make up information. Always call the tool to know what to ask next.
            tools:
                - { service: 'RegistrationTool' }
```

### Conversation Flow

[](#conversation-flow)

```
User: I want to sign up

AI: [Calls validate_registration()]
    What's your email address?

User: john@example.com

AI: [Calls validate_registration(email="john@example.com")]
    Please create a password (minimum 8 characters).

User: MyPass

AI: [Calls validate_registration(email="...", password="MyPass")]
    Password must be at least 8 characters long.

User: MySecurePassword123

AI: [Calls validate_registration(email="...", password="MySecurePassword123")]
    What's your full name?

User: John Doe

AI: [Calls validate_registration(email="...", password="...", fullName="John Doe")]
    Registration validated! Your account has been created.

```

Use Cases
---------

[](#use-cases)

### Chatbot Form Wizards

[](#chatbot-form-wizards)

Guide users through complex forms by requesting one piece of information at a time based on validation rules.

### AI-Powered Customer Support

[](#ai-powered-customer-support)

Validate customer requests interactively, asking clarifying questions when information is missing or invalid.

### CLI Configuration Tools

[](#cli-configuration-tools)

Build interactive command-line tools that guide users through setup by validating configuration step-by-step.

### API Request Validation

[](#api-request-validation)

Validate API requests from AI agents, providing structured feedback on missing or invalid parameters.

### Document Processing Workflows

[](#document-processing-workflows)

Check documents for completeness, requesting missing sections or data from users or automated systems.

Advanced Patterns
-----------------

[](#advanced-patterns)

### Conditional Rules

[](#conditional-rules)

```
class ConditionalShippingRule implements RuleInterface
{
    public function check(mixed $subject): RuleEvaluation
    {
        // Only check shipping if physical product
        if ($subject->productType !== 'physical') {
            return RuleEvaluation::pass();
        }

        if (!$subject->shippingAddress) {
            return RuleEvaluation::respond(
                new IncompleteResult(
                    missingField: 'shippingAddress',
                    message: 'Physical products require a shipping address.'
                )
            );
        }

        return RuleEvaluation::pass();
    }
}
```

### Nested Composites

[](#nested-composites)

```
$checkoutValidation = new Sequence(
    new UserAuthenticationRule(),
    new Any(
        new GuestCheckoutRule(),
        new AccountRequiredRule(),
    ),
    new Either(
        new ExpressCheckoutRule(),
        new Sequence(
            new ShippingRule(),
            new PaymentRule(),
            new BillingRule(),
        ),
    ),
);
```

### Stateful Rules

[](#stateful-rules)

```
class InventoryRule implements RuleInterface
{
    public function __construct(
        private ProductRepository $products,
    ) {}

    public function check(mixed $subject): RuleEvaluation
    {
        $product = $this->products->find($subject->productId);

        if (!$product || $product->stock < $subject->quantity) {
            return RuleEvaluation::respond(
                new BlockedResult(
                    reason: 'Insufficient inventory',
                    message: 'This product is out of stock.'
                )
            );
        }

        // You could even save products based on the subject input.

        return RuleEvaluation::pass();
    }
}
```

Testing
-------

[](#testing)

```
use PHPUnit\Framework\TestCase;

class EmailRuleTest extends TestCase
{
    public function test_it_requests_missing_email(): void
    {
        $rule = new EmailRule();
        $request = new OrderRequest(email: null, productId: 'P1', quantity: 1);

        $evaluation = $rule->check($request);

        $this->assertFalse($evaluation->isPass());
        $this->assertInstanceOf(IncompleteResult::class, $evaluation->result);
        $this->assertEquals('email', $evaluation->result->missingField);
    }

    public function test_it_validates_email_format(): void
    {
        $rule = new EmailRule();
        $request = new OrderRequest(email: 'invalid-email', productId: 'P1', quantity: 1);

        $evaluation = $rule->check($request);

        $this->assertFalse($evaluation->isPass());
        $this->assertStringContainsString('valid email', $evaluation->result->message);
    }

    public function test_it_passes_with_valid_email(): void
    {
        $rule = new EmailRule();
        $request = new OrderRequest(email: 'john@example.com', productId: 'P1', quantity: 1);

        $evaluation = $rule->check($request);

        $this->assertTrue($evaluation->isPass());
    }
}
```

Architecture
------------

[](#architecture)

### Design Principles

[](#design-principles)

- **Immutability**: All classes are `readonly` and `final` for predictable behavior
- **Type Safety**: Full generic support with `@template` annotations
- **Composition**: Rules are composable building blocks
- **Separation of Concerns**: Each rule validates one aspect
- **Fail-Fast**: Short-circuit evaluation stops on first failure

### Core Components

[](#core-components)

```
RuleInterface
    ↑
    ├── RuleEngine (orchestrator)
    ├── Sequence (AND composite)
    ├── Any (OR composite)
    ├── Either (binary composite)
    └── [Your custom rules]

RuleEvaluation
    ├── result: null (pass)
    └── result: ResultInterface (fail)

ResultInterface
    ├── CompleteResult
    ├── IncompleteResult
    ├── BlockedResult
    └── ErrorResult

```

Performance Considerations
--------------------------

[](#performance-considerations)

- **Short-Circuit Evaluation**: Execution stops at first failure
- **Lazy Evaluation**: Only evaluates rules in dependency order
- **Topological Sort**: O(V + E) dependency resolution using PSL Graph
- **No Reflection**: Pure constructor injection, no runtime reflection overhead

Contributing
------------

[](#contributing)

Contributions are welcome! Please feel free to submit a Pull Request.

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE) file for details.

###  Health Score

41

—

FairBetter than 89% of packages

Maintenance85

Actively maintained with recent releases

Popularity17

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 91.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~33 days

Total

3

Last Release

52d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/cab1b0de06c75fec5317beded0c2978f3e9e560e6830c1d9048f3e37b3287342?d=identicon)[phpro](/maintainers/phpro)

---

Top Contributors

[![veewee](https://avatars.githubusercontent.com/u/1618158?v=4)](https://github.com/veewee "veewee (11 commits)")[![janssensglenn](https://avatars.githubusercontent.com/u/32476955?v=4)](https://github.com/janssensglenn "janssensglenn (1 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/phpro-agent-rules/health.svg)

```
[![Health](https://phpackages.com/badges/phpro-agent-rules/health.svg)](https://phpackages.com/packages/phpro-agent-rules)
```

###  Alternatives

[phpro/soap-client

A general purpose SoapClient library

8885.6M46](/packages/phpro-soap-client)[roave/backward-compatibility-check

Tool to compare two revisions of a public API to check for BC breaks

5953.3M56](/packages/roave-backward-compatibility-check)[php-soap/ext-soap-engine

An ext-soap engine implementation

443.2M7](/packages/php-soap-ext-soap-engine)[symfony/polyfill-php54

Symfony polyfill backporting some PHP 5.4+ features to lower PHP versions

9630.8M5](/packages/symfony-polyfill-php54)[php-soap/wsdl

Deals with WSDLs

173.5M12](/packages/php-soap-wsdl)[kartik-v/yii2-widget-colorinput

An enhanced Yii 2 widget encapsulating the HTML 5 color input (sub repo split from yii2-widgets)

324.8M10](/packages/kartik-v-yii2-widget-colorinput)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
