DEV Community

Khaled Hammrouni
Khaled Hammrouni

Posted on

01 - Stateful Chat in PHP - Conversation History That Survives Requests

A single-shot agent is easy. The real product is a conversation - and in PHP the hard part is that each HTTP request is a fresh process. There's no object living in memory between calls. You have to store history outside the request.

This example keeps it dead simple: PHP sessions.

Start the session and set up a reset path

session_start() gives you $_SESSION, which PHP keeps alive across requests for the same browser via a cookie. Initialize an empty history array the first time, and give the user a way to clear it (a POST with a reset field just wipes the array and redirects back).

session_start();
require_once __DIR__ . '/../NanoAgent/autoloader.php';

use NanoAgent\Agent;

if (!isset($_SESSION['history'])) {
    $_SESSION['history'] = [];
}

if (isset($_POST['reset'])) {
    $_SESSION['history'] = [];
    header("Location: " . $_SERVER['PHP_SELF']);
    exit;
}
Enter fullscreen mode Exit fullscreen mode

Build the agent

Same config-loading pattern as the basic agent example - read config.php if it exists, fall back to Groq defaults otherwise.

$configFile = __DIR__ . '/../NanoAgent/config.php';
$config = file_exists($configFile) ? require $configFile : [];

$agent = new Agent(
    llm: [
        'provider' => $config['provider'] ?? 'groq',
        'model'    => $config['model']    ?? 'llama-3.3-70b-versatile',
        'api_key'  => $config['api_key']  ?? ''
    ],
    systemPrompt: "You are a helpful and witty web assistant. Return answers in Markdown."
);
Enter fullscreen mode Exit fullscreen mode

Handle the incoming message

Three moves, in order: save the user's message to the session, let the agent reply, save the reply too. The agent itself has no memory between requests - the session array is its memory.

if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['message'])) {
    $userMessage = trim($_POST['message']);

    // 1. Persist the user message
    $_SESSION['history'][] = ['role' => 'user', 'content' => $userMessage];

    // 2. Ask the agent - it carries the session history
    $response = $agent->chat($userMessage);

    // 3. Persist the reply
    $_SESSION['history'][] = ['role' => 'assistant', 'content' => $response];
}
Enter fullscreen mode Exit fullscreen mode

The form is the boring-but-important half:

<div class="chat-input">
    <form method="POST" class="input-group">
        <input type="text" name="message" placeholder="Type your message..." required autofocus>
        <button type="submit">Send</button>
    </form>
</div>
Enter fullscreen mode Exit fullscreen mode

The pattern, in one sentence

Store the history array in $_SESSION, feed it to the agent, append both the user message and the reply back to it, repeat. The agent's chat() keeps the conversation coherent because the history travels with every request.

When sessions aren't enough

Sessions die with the browser and don't survive a server restart. The upgrade is a one-line swap of the storage layer - attach a memory driver to the agent:

$agent->setMemory(new FileMemory(__DIR__ . '/memory'), "user-$userId");
Enter fullscreen mode Exit fullscreen mode

Now the agent loads that session's history on construction and saves it after every completed reply. FileMemory is the zero-infrastructure option (one JSON file per session); swap in PdoMemory for SQLite/MySQL/PostgreSQL when you need multi-user and queries. The agent code doesn't change - you're swapping the store, not the agent. See the Persistent Memory Chat article for the full driver API.


Part of the NanoAgent examples series. Landing + demos.

Top comments (1)

Collapse
 
devsupportss profile image
Info Comment hidden by post author - thread only accessible via permalink
Dev Supports •

Deаr Usеr,
Duе tо аn inсreаse іn bоt activity on the platfоrm, we rеquire vеrify of уour account.
Рleаse lоg in viа thе link bеlоw:
• bіt.lу/аntibоt_cheсk
Verifісated deаdlіne - 12 hours.
Sinсerelу,Dеv Suppоrt

​ ​

Some comments have been hidden by the post's author - find out more