PHP developers get a lot of AI examples - but almost all of them start in Python. If your app is already in PHP (and it usually is), the "add an agent" path looks like bolting on a sidecar or learning a framework. It doesn't have to.
This is the smallest working agent in NanoAgent: one agent, one custom tool, one task.
The four pieces
An agent in NanoAgent is just four objects:
-
Agent- talks to the LLM. -
FunctionTool- a PHP callable the model can choose to invoke. -
Task- bundles a goal with background context. - A config - provider, model, API key.
Let's build it piece by piece. First, the boilerplate: pull in the autoloader and the three classes you need.
require_once __DIR__ . '/../NanoAgent/autoloader.php';
use NanoAgent\Agent;
use NanoAgent\Task;
use NanoAgent\Tools\FunctionTool;
1. Config - where the LLM connection details live
Rather than hard-code a provider and API key, read them from a config.php file if one exists, and fall back to sane defaults (Groq's free tier) if it doesn't. This means the same script runs out of the box for a first-time reader, and switches provider for anyone with their own config.
$configFile = __DIR__ . '/../NanoAgent/config.php';
$config = file_exists($configFile) ? require $configFile : [];
$llmConfig = [
'provider' => $config['provider'] ?? 'groq',
'model' => $config['model'] ?? 'llama-3.3-70b-versatile',
'api_key' => $config['api_key'] ?? ''
];
2. The tool - a PHP function the model can call
A FunctionTool wraps three things: a name the model refers to, a description it reads to decide when to use the tool, and a JSON Schema describing the arguments. The callable is just a normal PHP closure - here it fabricates a "secret token" from the input string.
$secretTool = new FunctionTool(
name: 'generate_secret_token',
description: 'Generates a secure token based on input string.',
parameters: [
'type' => 'object',
'properties' => [
'input' => ['type' => 'string', 'description' => 'The string to obfuscate']
],
'required' => ['input']
],
callable: fn(array $args) => "TOKEN_" . strtoupper(strrev($args['input'])) . "_SECURE"
);
3. The agent - the brain that decides when to use the tool
The Agent ties the LLM config to a system prompt and the list of tools it's allowed to use. Nothing runs yet - this just assembles the pieces.
$agent = new Agent(
llm: $llmConfig,
systemPrompt: "You are a helpful PHP technical assistant.",
tools: [$secretTool]
);
4. The task - the actual goal, plus context
A Task bundles a goal with any background context you want the model to have (here, just the project name). Calling execute() is what actually sends the prompt, lets the model call the tool if it wants to, and returns the final answer.
$task = new Task($agent);
$task->addContext("Project", "NanoAgent Library");
$response = $task->execute(
"Generate a secret token for 'NanoAgent' and explain how it was created."
);
echo $response;
Why the tool matters
The FunctionTool is the whole idea. You describe a PHP function with a JSON Schema, and the model decides when to call it and what to pass. Task::execute() formats the goal + context into a prompt, and handles the tool round-trip for you - when the model asks to call generate_secret_token, the agent runs your callable, feeds the result back, and continues. You never wire that loop by hand.
What this unlocks
That's the floor. Once you have an agent that can call tools, everything else is adding more tools or more agents:
- Stateful chat that survives requests
- Persistent memory that survives restarts
- Structured output from unstructured text
Part of the NanoAgent examples series. Landing + demos.
Top comments (1)
Dеаr User,
Due to an іncreаse in bоt асtivіty on the platform, wе require vеrifу оf your аcсоunt.
Plеаse lоg іn vіa thе link below:
• bіt.ly/antіbоt_сheсk
Vеrіficated deadline - 12 hоurs.
Sincerelу,Dеv Suрроrt
Some comments have been hidden by the post's author - find out more