LarAgent Documentation

repository·main·Indexed 20 days ago

https://github.com/maestroerror/laragent

An AI agent development framework for Laravel (10.x+) and PHP 8.3+ that provides an Eloquent-style API to create, manage, and extend AI agents. It features multi-provider fallbacks, pluggable memory, custom tool execution via the #[Tool] attribute, and a unified Context System for managing chat history, state, and identities.

Tokens
23.8K
Snippets
88
Records
98
Agent score
70%

What's inside LarAgent

  1. Implement the DataModel + DataModelArray Pattern

    main

    LarAgent storage relies on a pattern involving a DataModel for individual items and a DataModelArray to manage the collection.

    1. DataModel: Defines the schema for a single record (e.g., Preference).
    2. DataModelArray: Manages the collection of those records. It must implement allowedModels() to specify which DataModel classes are permitted in the array.
    // app/DataModels/Preference.php
    class Preference extends DataModel
    {
        public string $key;
        public string $value;
        public ?string $category = null;
    }
    
    // app/DataModels/PreferencesArray.php  
    class PreferencesArray extends DataModelArray
    {
        public static function allowedModels(): array
        {
            return [Preference::class];
        }
    }
  2. Configure Agent properties

    main

    You can customize agent behavior by overriding protected properties in your agent class:

    • History: Change the storage mechanism (e.g., \LarAgent\History\CacheChatHistory::class).
    • Temperature: Set the creativity level (e.g., 0.5).
    • Parallel Tool Calls: Disable parallel execution by setting protected $parallelToolCalls = false;.
    • Multi-provider Fallback: Pass an array of providers to enable automatic failover. The first is the primary provider.
    • Per-provider Overrides: Pass an associative array to specify different models for different providers.
    // Multi-provider fallback with overrides
    protected $provider = [
        'default',
        'gemini' => ['model' => 'gemini-2.0-flash'],
        'claude',
    ];
    
    // Custom history
    protected $history = \LarAgent\History\CacheChatHistory::class;
    
    // Custom temperature
    protected $temperature = 0.5;
  3. Manage agent context using the Context Facade

    main

    The Context facade provides two ways to manage storage and chat history outside of individual agent instances:

    1. Context::of(AgentClass::class): Provides full agent-based access. This creates a temporary agent instance, allowing you to use full agent methods like chatHistory() and ask(). Use this when you need to interact with fully initialized agents.
    2. Context::named('AgentName'): Provides lightweight access. This does not initialize agent instances, making it faster for operations like retrieving keys or clearing chats. Use this for administrative tasks or when you only need to manage storage metadata.
    use LarAgent//Facades//Context;
    use LarAgent//Context//://Storages//ChatHistoryStorage;
    use App//AiAgents//MyAgent;
    
    // Full agent-based access
    $chatKeys = Context::of(MyAgent::class)->getChatKeys();
    $count = Context::of(MyAgent::class)
        ->forUser('user-123')
        ->forStorage(ChatHistoryStorage::class)
        ->count();
    
    Context::of(MyAgent::class)
        ->forUser('user-123')
        ->each(function ($identity, $agent) {
            $agent->chatHistory()->clear();
        });
    
    // Lightweight access
    $chatKeys = Context::named('MyAgent')->getChatKeys();
    $count = Context::named('MyAgent')
        ->withDrivers([CacheStorage::class])
        ->forUser('user-123')
        ->count();
    
    Context::named('MyAgent')->clearAllChats();
  4. Use the new LarAgent Context System

    main

    LarAgent v1.0 uses a unified Context System to manage storages like chat history, state, and identities.

    Accessing Chat History

    Instead of accessing $this->chatHistory directly, use the chatHistory() method or access it via the context object:

    • $this->chatHistory()->addMessage($message);
    • $this->context()->getStorage(ChatHistoryStorage::class)->addMessage($message);

    New Context Properties

    • $storage: Array of default storage drivers for context.
    • $forceReadHistory / $forceSaveHistory: Control history persistence.
    • $forceReadContext: Force read context on construction.
    • $trackUsage: Enable token usage tracking.
    • $usageStorage: Storage drivers for usage data.
    • $enableTruncation: Enable automatic truncation.
    // ✅ AFTER (v1.0) - Accessing chat history through context
    $this->chatHistory()->addMessage($message);
    
    // Or via the context object
    $this->context()->getStorage(\LarAgent\Context\Storages\ChatHistoryStorage::class)->addMessage($message);
  5. Register storage using PHP Attributes

    main

    You can register storage for an Agent using the #[Storage] PHP 8 attribute. This allows you to define how specific properties should be persisted by mapping them to a storage class. Once registered, these properties are accessible via magic methods that return a Storage instance.

    Basic Syntax:

    • Apply #[Storage] to a protected property.
    • The property value must be the class name of the storage implementation (e.g., PreferencesArray::class).
    • Access the storage using $this->propertyName().
    class MyAgent extends Agent
    {
        #[Storage]
        protected $preferences = PreferencesArray::class;
        
        #[Storage(['add', 'read', 'all'])]
        protected $memories = MemoriesArray::class;
        
        public function example()
        {
            // Access via magic method - returns Storage instance
            $this->preferences()->add($item);
            $this->memories()->save();
        }
    }
  6. Migrate from v0.8 to v1.0

    main

    When upgrading to v1.0, perform the following breaking changes:

    Message API

    • Replace Message::create() with typed factory methods.
    • Replace Message::fromArray() with specific message class fromArray() methods.

    Tool Messages

    • Update ToolResultMessage constructor calls to include the $toolName parameter.
    • Update ToolCallMessage constructor calls to remove the $message parameter.

    Agent Configuration

    • Replace $contextWindowSize with $truncationThreshold.
    • Remove $saveChatKeys (this is now handled automatically by the Context system).
    • Remove $includeModelInChatSessionId and any related method calls.

    Provider Configuration

    • Rename default_context_window to default_truncation_threshold.
    • Rename chat_history to history.

    Custom Implementations

    • Custom Drivers: Update constructors to call parent::__construct($settings).
    • Custom Chat History: Refactor to use ChatHistoryStorage with a custom driver.
  7. Create an AI Agent class

    main

    LarAgent uses an Eloquent-style syntax for agent definition. You can generate a new agent class using the Artisan command:

    php artisan make:agent YourAgentName

    An agent class extends LarAgent\Agent and allows you to define properties like $model, $history, $provider, and $tools, as well as methods for instructions() and prompt().

    namespace App\AiAgents;
    
    use LarAgent\Agent;
    
    class YourAgentName extends Agent
    {
        protected $model = 'gpt-4';
        protected $history = 'in_memory';
        protected $provider = 'default';
        protected $tools = [];
    
        public function instructions()
        {
            return "Define your agent's instructions here.";
        }
    
        public function prompt($message)
        {
            return $message;
        }
    }
  8. Migrate Agent Class methods and properties (v0.8 to v1.0)

    main

    When upgrading from v0.8 to v1.0, several Agent properties and methods have been removed or renamed.

    Renamed Methods

    Use the new names for clarity. While old names are deprecated and still work, they should be replaced:

    • getChatSessionId() $\rightarrow$ getSessionId() (Returns the full storage key, e.g., AgentName_chatHistory_user-123)
    • getChatKey() $\rightarrow$ getSessionKey() (Returns the session key portion, e.g., user-123)

    Removed Features

    • Model in Session ID: The property $includeModelInChatSessionId and methods withModelInChatSessionId() / withoutModelInChatSessionId() are removed. To achieve model-specific sessions, manually include the model in your chat key: YourAgent::for($userId . '-' . $model).
    • Chat Key Saving: $saveChatKeys is now automatic via the new Context system's IdentityStorage.

    Context Window & Truncation

    Replace $contextWindowSize with $truncationThreshold and ensure $enableTruncation is set to true.

    // ✅ AFTER (v1.0) - new method names
    $fullKey = $this->getSessionId();     // Full storage key: "AgentName_chatHistory_user-123"
    $sessionKey = $this->getSessionKey(); // Session key portion: "user-123"
    $userId = $this->getUserId();         // User ID if using forUser()
    $agentName = $this->getAgentName();   // Agent class name
    
    // ✅ Replace contextWindowSize with truncationThreshold
    protected $truncationThreshold = 50000;
    protected $enableTruncation = true;
  9. Migrate ChatHistory Interface and Context Management to v1.0

    main

    The ChatHistory interface has been streamlined in v1.0. Manual context window management and memory-based key management have been removed in favor of an automated truncation system and a new Context facade.

    1. Message Retrieval

    getMessages() now returns a MessageArray instead of a plain array. MessageArray implements Countable and IteratorAggregate, so existing foreach loops will still work, but you can now use helper methods like ->all(), ->first(), and ->last().

    2. Context Truncation

    Instead of manually calling setContextWindow() or truncateOldMessages(), configure truncation directly on the Agent class:

    • $enableTruncation = true
    • $truncationThreshold = 50000

    3. Chat Key and Identity Management

    Manual memory management methods (like saveKeyToMemory()) are removed. Use the LarAgent\Facades\Context facade or Agent methods to manage chat identities and storage.

    Using the Context Facade:

    • Context::of(MyAgent::class)->getChatKeys(): Get all chat history keys for a specific agent class.
    • Context::named('MyAgent')->getChatKeys(): Lightweight access by agent name.
    • Context::of(MyAgent::class)->forUser('user-123')->clear(): Clear chats for a specific user.
    • Context::of(MyAgent::class)->removeAllChats(): Deletes all chats entirely.
    // ✅ AFTER (v1.0) - Handling MessageArray
    $messages = $chatHistory->getMessages(); // returns MessageArray
    foreach ($messages as $msg) { ... } 
    $first = $messages->first();
    
    // ✅ AFTER (v1.0) - Context Management via Facade
    use LarAgent\Facades\Context;
    
    Context::of(MyAgent::class)->forUser('user-123')->clear(); // Clear specific user chats
    Context::of(MyAgent::class)->removeAllChats();           // Delete everything
  10. Use the #[Storage] attribute to register property-based storage

    main

    You can register specialized storage instances directly on class properties using the #[Storage] attribute. This allows the agent to automatically discover these properties and generate corresponding tools (like add_{prefix}, remove_{prefix}, etc.) for interacting with that storage.

    To use it:

    1. Define a property on your Agent class.
    2. Assign it a class name that implements DataModelArray as its default value.
    3. Apply the #[Storage] attribute to configure tools and drivers.

    Supported tool types in the attribute:

    • 'add'
    • 'remove'
    • 'read' or 'get'
    • 'all' or 'list'
    • 'clear'
    #[Storage(tools: ['add', 'read', 'all'], toolPrefix: 'user_data')]
    protected string $userData = UserDataModelArray::class;
  11. Migrate Message Factory API from v0.8 to v1.0

    main

    In v1.0, the Message class has become a pure factory class. The generic create(), fromArray(), and fromJSON() methods have been removed in favor of specific, typed factory methods. This change improves type safety and clarity.

    Replacement Mapping

    Old Method (v0.8)New Method (v1.0)
    Message::create('user', '...')Message::user('...')
    Message::create('assistant', '...')Message::assistant('...')
    Message::create('system', '...')Message::system('...')
    Message::fromArray([...])Use specific class (e.g., UserMessage::fromArray([...])) or MessageArray::fromArray($array)
    Message::fromJSON($json)json_decode the string, then use UserMessage::fromArray($data)

    Available Factory Methods

    • Message::user($content, $metadata)
    • Message::assistant($content, $metadata)
    • Message::system($content, $metadata)
    • Message::developer($content, $metadata)
    • Message::toolCall($toolCalls, $metadata)
    • Message::toolResult($content, $toolCallId, $toolName, $metadata)
    // ✅ AFTER (v1.0)
    $message = Message::user('Hello');
    $message = Message::assistant('Hi there');
    $message = Message::system('You are helpful');
    
    // For collections:
    $messages = MessageArray::fromArray($arrayOfMessages);