Your AI agent shouldn't make everyone wait.
Give any Neuron AI agent background tools and sub-agents. It hands slow work to other processes, runs it side by side, and keeps talking with your user. When the work is done, the agent picks up the results and carries on, by itself.
An advanced extension of Neuron AI, available under a commercial license.
New to agents? Three words first
This page uses a few words that are still new to many developers. Here is all you need to follow along.
- Agent
- A program that uses a language model (LLM) to decide what to do next. You give it instructions and tools; it chooses which tools to call, reads their results and writes an answer.
- Tool
- A function the agent can call: search the web, query your database, run a shell command, send an email.
- Turn
- Everything that happens between a message and the agent's answer to it. In one turn the model may call many tools, one after another, before it replies.
Today, in most frameworks, a turn is synchronous: the agent cannot answer until every tool it called has returned. That is the problem this package solves.
The problem: one slow tool freezes the whole conversation
An agent works in a loop: the model thinks, calls a tool, waits for the result, thinks again, calls the next tool, and so on until it can answer. Every tool call is a wait, and the waits add up.
This is fine when tools take milliseconds. It breaks down as soon as your agent does real work:
- Your user stares at a spinner. A research task that reads twenty web pages, a test suite, a data export, a deployment: minutes, not seconds. Nobody can use the chat in the meantime.
- Work happens one step at a time. Three independent research jobs run one after another, even though nothing stops them from running at the same moment.
- Web requests time out. PHP and the web servers in front of it often cut a request after 30 or 60 seconds. A turn that runs longer simply dies, and so does the work.
- A crash loses everything. If the process serving the turn stops, the tools it was waiting for stop with it, and nobody knows how far they got.
The usual workaround is to build a job queue by hand, invent a way to send the result back to the model, and hope the conversation is in a sane state when it arrives. That is a lot of hard, easy-to-get-wrong code, and it is the same code in every project.
One synchronous turn
13 min, start to answerTimes are illustrative.
The idea: delegate, don't wait
Think of how a team lead works. They don't stand at a colleague's desk until the job is done. They hand it over, go back to their own work, and read the report when it lands in their inbox.
Neuron Subagents gives your agent that same habit:
It's like the buzzer a restaurant gives you when you order: you go sit with your friends, and it buzzes when your food is ready. If you're in the middle of a sentence, you finish it first, then you go and pick it up.
Your agents stay ordinary Neuron agents. You keep your provider, your instructions and your other tools. You add one toolkit.
Do more at once, finish sooner
Run tools and sub-agents in parallel
Every background task runs in its own process. When the model starts three tasks, all three run at the same time, and the agent doesn't sit idle while they do: it can keep working on the parts of the job that don't depend on them.
An example. Your user asks:
“Compare our pricing with Acme, Globex and Initech, and draft the release notes for version 2.3.”
Three research tasks and a draft
elapsed 13:00The longer and more independent the jobs, the bigger the gain: the agent finishes in the time of its slowest task, not the sum of all of them.
Times are illustrative. How many tasks truly run at once depends on how you run them: one process each by default, or as many queue workers as you give them.
| Step | Without background tasks | With Neuron Subagents |
|---|---|---|
| Research Acme (≈4 min) | 0:00 → 4:00 | 0:00 → 4:00, in its own process |
| Research Globex (≈4 min) | 4:00 → 8:00 | 0:00 → 4:00, in its own process |
| Research Initech (≈4 min) | 8:00 → 12:00 | 0:00 → 4:00, in its own process |
| Draft the release notes | 12:00 → 13:00 | 0:00 → 1:00, while the research runs |
| The user can use the chat | after 13 minutes | after about 1 minute |
| The comparison is ready | after 13 minutes | after about 4 minutes |
Sub-agents: specialists with a clean desk
A sub-agent is a whole agent that your main agent can hand a job to: a researcher, a code reviewer, a data analyst. It gets only the instructions the main agent writes for it, not the whole conversation, so it starts focused and doesn't fill the main conversation with its intermediate steps. The main agent reads its final report.
The model sees a tool called researcher with one argument, prompt.
ResearchAgent is any agent of yours: nothing in it is specific to this package.
new Subagent( 'researcher', 'Research a topic on the web and report the findings.', fn (Task $task): Agent => new ResearchAgent(), )
Long commands in the background, a free chat for your user
Some tools are fast most of the time and slow some of the time. bash is the classic case:
ls takes a millisecond, the full test suite takes twenty minutes.
With an on-demand background tool, the model picks the mode call by call:
- A normal call runs right away inside the turn, and the model reads the output immediately, like any tool.
- A call with
run_in_background: truebecomes a background task. The agent replies, and the user can keep chatting about something else while the command runs.
Here a normal bash call keeps its usual short time limit, while a background call may run for
25 minutes. You don't have to explain any of this in your prompts: the toolkit
tells the model which tools run in the background, when to use run_in_background, and how to read
results when they arrive.
For tools that are always slow, such as a deployment, skip onDemand(): every call then runs
in the background, and the model sees the tool as deploy_background.
(new ToolInBackground(BashTool::make(), leaseTimeout: 1800)) ->onDemand(fn (BashTool $tool) => $tool->setTimeout(1500))
The agent picks up where it left off, on its own
This is what turns background tasks from a job queue into a real agent feature.
When a task finishes, you don't poll for it, and your user doesn't have to ask “is it done yet?”. The result is delivered into the conversation as a message, and that message starts a new turn of the agent. The model reads the result with the whole conversation in front of it, and continues the work: it summarises the research, fixes the failing test, or starts the next step.
Results wait for the right moment. A conversation can only do one thing at a time, so the delivery is careful never to talk over anyone:
- The conversation is free: the result is delivered right away, and the agent answers it.
- The agent is busy answering something else: the result waits a few seconds and tries again, until that turn has ended. It never interrupts a turn in progress.
- The conversation waits for your user, for example on a tool call that needs their approval: the result waits until that is settled.
- Several tasks finish close together: they are delivered together, in one message, so the agent answers them in a single turn instead of one turn each.
The model is told that a delivered message was written by the system, not by the user, and that everything inside a result is data, never instructions. Every result is wrapped in tags the result itself cannot fake, so a web page that a sub-agent read can't end its block early and pretend to be your user.
<background-results id="dlv_01a1c6dc-..."> These background tasks have finished. This message was written by the system, not by the user. <task id="sub_01a1c6da-..." tool="researcher" status="completed"> Acme charges per seat; Globex and Initech charge per usage... </task dlv_01a1c6dc-...> </background-results dlv_01a1c6dc-...>
Show your users what's happening
Background work only feels good when people can see it. Everything the package knows about a conversation's tasks is stored in your own database, and your application can read it to build the experience you want.
Tasks in progress. For each open task you can read its tool, its arguments, its status (pending, completed or failed), how many attempts it took, whether a stop was asked, and its result or error.
Results already delivered. Once a result reaches the conversation, it lives in the chat history as a
message carrying background metadata, with the delivery ID and the IDs of its tasks. Your chat
page can show it as a system notice instead of as something your user wrote.
use NeuronAI\Subagents\Ledger\Ledgers; $tasks = (new Ledgers($persistence))->of($threadId)->tasks(); foreach ($tasks as $task) { $task->id; // "sub_01a1c6da-..." $task->tool; // "researcher" $task->inputs; // ['prompt' => 'Research Acme pricing...'] $task->status; // TaskStatus::Pending, Completed or Failed $task->attempts; // 0 while queued, 1 or more once a worker started it $task->stopRequested; // true after the model asked to stop it }
foreach ($agent->getChatHistory()->getMessages() as $message) { if ($message->getMetadata('background') !== null) { // A delivered result: show it as a notice, or hide it } }
The agent's answer, live. The answer to a result is written by a background process, not by the web
request your user is waiting on. Reload the conversation to show it, or turn on stream: true to
stream that turn through the agent's streaming channel, straight to your front end.
The model can check too. Next to the background tools, the agent gets two more: check_tool, to tell the user how a task is doing when they ask, and
stop_tool, to cancel a task that is no longer needed.
With this you can build:
- a “3 tasks running” tray above the chat input,
- a status chip for each task: queued, running, stopping, done, failed,
- a timeline that shows where each result entered the conversation,
- notifications when a long job finishes while the user is on another page.
Built for production, not just for the demo
Background work is where the hard problems live: processes die, jobs get delivered twice, two workers grab the same task. The package handles them so you don't have to.
- Nothing is lost when a process dies. A scheduled sweep runs every minute and starts again any work whose process disappeared. It executes nothing itself, it only rings the bell again.
- Sub-agents resume where they stopped. A sub-agent whose process crashed is picked up by another one and continues its own conversation, up to three attempts.
- A tool call never runs twice. If a background tool call crashes halfway, the package can't know whether it already took effect (did the email go out?), so it reports the task as failed instead of running it again.
- Duplicates are harmless. A signal delivered twice, late, or to two workers at once changes nothing: only one of them does the work, and the result is reported once.
- Approval happens before the work starts. A tool that needs a human's approval asks for it in the conversation, before the task is created, never in a process nobody is watching.
- Stop when you want.
stop_toolends a task at its next step, and a stopped task reports nothing. - Each conversation sees only its own tasks. The toolkit works for the conversation your application bound the agent to: the model can never look at, or stop, another conversation's tasks.
- No new tables. Everything is kept in the stores your agents already use: Neuron's workflow persistence and message store, on a database or Redis.
- Your infrastructure, your choice. Start tasks as local PHP processes with zero setup, or send them to any job queue by implementing one method.
- Easy to test.
FakeRunnerkeeps the work in memory and runs it when your test says so, so you can test the full round trip, from the tool call to the agent's answer, in one PHPUnit test.
Getting started
1. Get your license
Neuron Subagents is an advanced extension of Neuron AI, released under a commercial license. Talk to us to get yours: it gives your team access to the package, with the details to add it to your projects through Composer.
2. Install the package
Once your project has access, install it like any other Composer package:
composer require neuron-core/subagents
It requires PHP 8.1 or later and an application that already uses Neuron AI.
3. Wire it in
Three parts, all built the same way in every process of your application.
- The
Backgroundobject holds what your whole application shares: the storage, the way to start other processes, and the way to build your agent for a conversation. - Your agent declares its background tools next to its other tools, through
toolkit(). Two agents can have different background tools. - A scheduler runs the sweep every minute, to recover from crashes.
// background.php: loaded by every process of your application $pdo = new PDO('sqlite:' . __DIR__ . '/app.sqlite', options: [PDO::ATTR_TIMEOUT => 5]); return new Background( persistence: new DatabasePersistence($pdo), runner: new ProcessRunner(__FILE__), parent: fn (string $agent, string $threadId, Background $background): Agent => (new $agent($background))->for($threadId), );
class SupportAgent extends Agent { public function __construct(protected Background $background) { parent::__construct(); } protected function tools(): array { return [ $this->background->toolkit($this, [ // A sub-agent, always in the background new Subagent( 'researcher', 'Research a topic and report the findings.', fn (Task $task): Agent => new ResearchAgent(), ), // bash: a normal call, or a background one when the model asks for it (new ToolInBackground(BashTool::make(), leaseTimeout: 1800)) ->onDemand(fn (BashTool $tool) => $tool->setTimeout(1500)), ]), // ...your other tools ]; } }
// Serve the conversation as usual $agent = (new SupportAgent($background))->for($threadId); $agent->chat(new UserMessage('Compare our pricing with Acme, Globex and Initech'));
# crontab: recover lost work every minute
* * * * * php /path/to/app/vendor/bin/neuron background sweep --bootstrap=/path/to/app/background.php That's the whole setup for a plain PHP application. On Laravel and Symfony, the integration packages do it for you.
Ready for Laravel and Symfony
Dedicated integration packages wire everything into your framework: no bootstrap file, no process runner to configure, no crontab line to remember. They build on Neuron Subagents, so your project needs a license for it, and they install the same way once it has access.
Laravel
composer require neuron-core/subagents-laravel
- Installs itself. The service provider is discovered automatically and there is nothing to migrate.
- Runs on your Laravel queue. Every task and every delivery is a queued job, on the connection and
queue you choose. Give a single tool a queue of its own, for example a dedicated
researchqueue for long sub-agents. - Uses the stores you already have. Background work is kept in the workflow persistence your agents already use, taken from the container.
- Sweeps on its own. The sweep is added to your schedule, every minute, on one server, without overlapping.
- Tells you when it needs help. Set an email address and you're notified when a sweep fails, or takes longer than its minute because you've outgrown a single sweeper.
- Built for your tests. Swap in
FakeRunner, or useQueue::fake()as with any other job.
$agent = app(SupportAgent::class)->for($conversation->id); $agent->chat(new UserMessage($request->input('message')));
Symfony
composer require neuron-core/subagents-symfony
- A bundle that registers everything in the service container, so your agents receive the
Backgroundby autowiring. - Runs on your application's own queue and scheduler, in the processes Symfony already runs, instead of a separate runner and crontab.
- Uses the stores your agents already share, so there's nothing new to set up in your database.
Three kinds of background work
| Kind | What the model sees | Runs in the background | Typical use |
|---|---|---|---|
| Sub-agent | a tool with a prompt argument | always | research, code review, analysis: anything that needs its own reasoning |
| Background tool | <tool>_background, with the tool's own arguments | always | deployments, exports, long reports: tools that are always slow |
| On-demand tool | the tool itself, plus run_in_background | when the model asks for it | bash, builds, test suites: tools that are fast or slow depending on the call |
Need something else? Any Neuron workflow can become a background task by implementing one interface.
Frequently asked questions
How is Neuron Subagents licensed?
Neuron AI is open source, under the MIT license. Neuron Subagents is an advanced extension built on top of it, released under a commercial license. The Laravel and Symfony integrations build on it, so they need that license too. Talk to us to find the license that fits your team.
Do I need to change my agents?
No. Your agents stay ordinary Neuron agents. You add the background toolkit to their tools(),
and give them stores every process can see, such as a database.
Do I need a queue?
No. Out of the box, each task runs in its own PHP process, started from your application. When you want a queue, the Laravel and Symfony packages use your framework's, and on any other stack you implement one small interface.
What storage do I need?
A store every process of your application can reach: a database or Redis. A single SQLite file is enough on one machine. In-memory stores only work in tests, where everything runs in one process.
What happens if my server restarts in the middle of a task?
The sweep notices the task has no live worker and starts it again. A sub-agent continues from where it stopped. A single tool call is reported as failed rather than run twice, because nobody can tell whether it already took effect.
Will a result interrupt my user while they're chatting?
Never. A result waits until the agent has finished its current turn, and is delivered as soon as the conversation is free.
Can the user, or the agent, cancel a task?
The agent can, with stop_tool, whenever the user asks or the task is no longer
needed. The task ends at its next step. A single long call, such as one 20-minute command, stops when it returns.
Does the sub-agent see the whole conversation?
No. It only receives the instructions the main agent writes for it. That keeps it focused, cheaper to run, and keeps the main conversation free of its intermediate steps.
Can a sub-agent start background tasks of its own?
Yes, if you give it the background toolkit. Its results then come back to its own conversation, not to your user's.
What about tools that need human approval?
The approval is asked in the conversation, before the task starts. A task never waits for a person that isn't there.
Does it work on Windows?
The built-in process runner needs a POSIX shell, so it runs on Linux and macOS. On Windows, run the tasks on a queue.
Glossary
- Agent
- a program that uses a language model to decide which tools to call and how to answer.
- Tool
- a function the agent can call.
- Turn
- everything between a message and the agent's answer to it, tool calls included.
- Thread (conversation)
- the history of messages between a user and an agent, identified by a thread ID your application owns.
- Sub-agent
- an agent that another agent hands a job to, and whose final answer it reads.
- Background task
- a tool call or a sub-agent job that runs in another process, identified by a task ID.
- Worker
- the process that runs a background task.
- Delivery
- the step that brings finished results back into the conversation and starts the agent's next turn.
- Sweep
- a small command, scheduled every minute, that restarts work lost to a crash.
- Persistence
- the database where Neuron keeps the state of agents and tasks, shared by every process.