Multi-Execution Conversations
Overview¶
An agent can handle multiple rounds of execution, where each round builds on the previous
conversation history. Just add a new user message to the returned state and call execute()
again — AgentLoop automatically resets completed executions before starting a new one.
This enables multi-turn interactions where the agent reasons over past tool results to answer follow-up questions without re-executing tools.
Key concepts:
- withUserMessage(): Appends a follow-up user message to the existing conversation
- The agent sees all prior messages including tool calls and results from previous executions
- Follow-up questions can reference data gathered in earlier rounds
- AgentLoop auto-resets terminal execution state (completed/failed) on entry
Example¶
<?php
require 'examples/boot.php';
use Cognesy\Agents\AgentLoop;
use Cognesy\Agents\Capability\File\ReadFileTool;
use Cognesy\Agents\Data\AgentState;
use Cognesy\Agents\Events\Support\AgentEventConsoleObserver;
$workDir = dirname(__DIR__, 3);
$logger = new AgentEventConsoleObserver(
useColors: true,
showTimestamps: true,
showContinuation: true,
showToolArgs: true,
);
// Build an agent with direct file reading tool
$loop = AgentLoop::default()
->withTool(ReadFileTool::inDirectory($workDir))
->wiretap($logger->wiretap());
// === Execution 1: Ask the agent to read composer.json ===
$query1 = 'Read the composer.json file and tell me the project name and its PHP version requirement.';
echo "=== Execution 1 ===\n";
echo "Query: {$query1}\n\n";
$state = AgentState::empty()->withUserMessage($query1);
$state = $loop->execute($state);
$response1 = $state->finalResponse()->toString() ?: 'No response';
echo "\nResponse: {$response1}\n\n";
// === Execution 2: Follow-up question using context from Execution 1 ===
$query2 = 'Based on what you read, does the project use PSR-4 autoloading? What are the namespace prefixes?';
echo "=== Execution 2 ===\n";
echo "Query: {$query2}\n\n";
// Just add a new message — AgentLoop auto-resets the completed execution
$state = $state->withUserMessage($query2);
$state = $loop->execute($state);
$response2 = $state->finalResponse()->toString() ?: 'No response';
echo "\nResponse: {$response2}\n\n";
// === Execution 3: Another follow-up — agent reasons without tools ===
$query3 = 'Given what you know about this project, what type of project is it — a library, framework, or application? Explain briefly.';
echo "=== Execution 3 ===\n";
echo "Query: {$query3}\n\n";
$state = $state->withUserMessage($query3);
$state = $loop->execute($state);
$response3 = $state->finalResponse()->toString() ?: 'No response';
echo "\nResponse: {$response3}\n";
if ($state->status()->value !== 'completed') {
echo "Skipping assertions because execution status is {$state->status()->value}.\n";
exit(1);
}
// Assertions
assert(!empty($response1) && $response1 !== 'No response', 'Expected non-empty response from execution 1');
assert(!empty($response2) && $response2 !== 'No response', 'Expected non-empty response from execution 2');
assert(!empty($response3) && $response3 !== 'No response', 'Expected non-empty response from execution 3');
assert($state->stepCount() >= 1, 'Expected at least 1 step in final execution');
?>