Skip to main content

Overview

This example keeps the normal agent console event output, but also exports the same execution lifecycle to Logfire. It is a practical pattern for debugging agent behavior locally while still keeping a remote execution trace. Key concepts:
  • AgentEventConsoleObserver: local event visibility during execution
  • RuntimeEventBridge: turns the agent event stream into telemetry
  • AgentsTelemetryProjector: maps agent lifecycle events
  • PolyglotTelemetryProjector: captures the nested LLM work done by the agent
  • UseBash: forces the example to emit telemetry for real tool execution, not just one LLM response

Example

<?php
require 'examples/boot.php';
require_once 'examples/_support/logfire.php';

use Cognesy\Agents\Builder\AgentBuilder;
use Cognesy\Agents\Capability\Bash\UseBash;
use Cognesy\Agents\Capability\Core\UseContextConfig;
use Cognesy\Agents\Capability\Core\UseGuards;
use Cognesy\Agents\Capability\Core\UseLLMConfig;
use Cognesy\Agents\Data\AgentState;
use Cognesy\Agents\Enums\ExecutionStatus;
use Cognesy\Agents\Events\Support\AgentEventConsoleObserver;
use Cognesy\Agents\Telemetry\AgentsTelemetryProjector;
use Cognesy\Events\Dispatchers\EventDispatcher;
use Cognesy\Http\Telemetry\HttpClientTelemetryProjector;
use Cognesy\Messages\Messages;
use Cognesy\Polyglot\Inference\LLMProvider;
use Cognesy\Polyglot\Telemetry\PolyglotTelemetryProjector;
use Cognesy\Telemetry\Application\Projector\CompositeTelemetryProjector;
use Cognesy\Telemetry\Application\Projector\RuntimeEventBridge;

$events = new EventDispatcher('examples.d05.telemetry-logfire');
$hub = exampleLogfireHub('examples.d05.telemetry-logfire');

(new RuntimeEventBridge(new CompositeTelemetryProjector([
    new AgentsTelemetryProjector($hub),
    new PolyglotTelemetryProjector($hub),
    new HttpClientTelemetryProjector($hub),
])))->attachTo($events);

$logger = new AgentEventConsoleObserver(
    useColors: true,
    showTimestamps: true,
    showContinuation: true,
    showToolArgs: true,
);

$workDir = dirname(__DIR__, 3);

$agent = AgentBuilder::base($events)
    ->withCapability(new UseContextConfig(systemPrompt: <<<'SYSTEM'
        You inspect repositories with the bash tool.
        Every factual claim must come from a bash command.
        Use bash at least 3 separate times.
        Never combine commands with && or ;.
        SYSTEM))
    ->withCapability(new UseLLMConfig(llm: LLMProvider::using('openai')))
    ->withCapability(new UseBash(baseDir: $workDir))
    ->withCapability(new UseGuards(maxSteps: 6, maxTokens: 8192, maxExecutionTime: 45))
    ->build()
    ->wiretap($logger->wiretap());

$task = <<<'TASK'
Inspect this repository with the bash tool.

Requirements:
- Use the bash tool at least 3 separate times.
- Do not combine commands with && or ;.
- Run these as separate bash calls:
  1. pwd
  2. ls examples/D05_AgentTroubleshooting
  3. rg -n "UseBash|BashTool" packages/agents/src/Capability/Bash/*.php

Then answer in 3 short bullets:
1. What kind of repository this is
2. Where the agent telemetry examples live
3. How bash tooling is enabled for agents
TASK;

$state = AgentState::empty()->withMessages(Messages::fromString($task));

echo "=== Agent Execution Log ===\n\n";

$finalState = $agent->execute($state);
$hub->flush();

$toolNames = array_reduce(
    $finalState->stepExecutions()->all(),
    static function (array $names, $stepExecution): array {
        $stepNames = array_map(
            static fn($toolExecution): string => $toolExecution->name(),
            $stepExecution->step()->toolExecutions()->all(),
        );
        return [...$names, ...$stepNames];
    },
    [],
);
$toolCallCount = count($toolNames);

echo "\n=== Result ===\n";
$response = $finalState->finalResponse()->toString() ?: 'No response';
echo "Answer: {$response}\n";
echo "Status: {$finalState->status()->value}\n";
echo "Steps: {$finalState->stepCount()}\n";
echo "Tools used: " . implode(' > ', $toolNames) . "\n";
echo "Total tool calls: {$toolCallCount}\n";
echo "Telemetry: flushed to Logfire\n";

assert($finalState->status() === ExecutionStatus::Completed);
assert($response !== '');
assert($toolCallCount >= 3);
?>