DevOps workflow diagram representing a long-running AI process in PHP
← Back to Blog
October 2026·PHP & Laravel·12 min read

What PHP developers should know about long-running AI workflows

Build long-running AI workflows in PHP with durable state, short queue jobs, checkpoints, idempotency and recovery instead of one fragile process.

Long-running AI workflows in PHP should not be implemented as one PHP process that stays alive until an agent finishes. The reliable design is a sequence of short, restartable jobs coordinated by durable workflow state. Each job performs one bounded step, records what happened and schedules the next command only after the state change commits.

That distinction matters because an AI workflow can outlive an HTTP request, a queue worker, a deployment and sometimes the attention span of the person who started it. It may wait for a model provider, call several tools, pause for human approval, retry after a rate limit and resume the following day. PHP is completely capable of running that system. It simply needs application architecture that treats time and failure as normal inputs.

I do not think PHP's request-oriented history is the limitation people often assume it is. In fact, the shared-nothing model encourages a useful question: what must be persisted so any healthy worker can continue this run? The trouble starts when a team writes a synchronous agent loop first and then tries to make the process immortal.

“Long-running” describes the workflow, not the worker

A workflow that starts at 10:00 and finishes at 15:00 does not need a worker to hold five hours of CPU time. Most of those hours may be waiting: for a provider's batch result, for a document conversion, for a scheduled retry or for a person to approve an action. The business operation is long-lived, but its compute steps can remain short.

This leads to the most useful mental model I know for PHP developers:

  • The workflow is durable data. It has an identity, state, version, deadlines and history.
  • A queue job is a command. It asks the workflow to attempt one bounded transition.
  • A worker is disposable compute. It may disappear after any instruction without erasing the workflow.
  • An external call is an uncertain boundary. A timeout does not prove the provider did nothing.
  • A checkpoint is a recovery boundary. It says which work is accepted and what remains.

Once these responsibilities are separated, PHP-FPM can continue serving short web requests while CLI queue workers perform background work. The browser receives a run ID immediately, then reads progress from an API or receives events through SSE or WebSockets. The workflow does not depend on keeping the original connection open.

The fragile version is one giant agent job

The first implementation often looks attractive: dispatch one job, enter a loop, ask the model what to do, call a tool and repeat until the model says it is finished. All context lives in memory. A final database write records the result.

That design combines too many failure domains. A worker timeout discards progress. A deployment kills the process. A malformed provider response can leave the loop in an unknown position. Retrying the whole job may repeat an email, payment, publication or API mutation that already succeeded. Even a harmless retry can spend tokens again and produce a different answer.

Increasing the worker timeout is not a durability strategy. It only gives the same fragile unit more time to fail. Memory limits, process restarts and infrastructure termination still exist. Queue systems also have visibility or reservation rules: if a job runs longer than the queue considers it reserved, another worker may receive it.

There are valid long-running PHP processes. Queue workers, consumers and socket servers are examples. They are supervisors of repeated bounded work, not the only home of a business workflow. Their memory should be treated as a cache, and deployments should be able to restart them deliberately.

A durable architecture for PHP AI workflows

For Laravel applications, I usually start with a relational workflow record, an append-only transition history, an outbox table, queue workers and object storage for large artifacts. Redis can accelerate queues, locks, rate limits and progress views, but the accepted business state remains durable.

ConcernDurable ownerWorker responsibility
Current workflow stateSQL row with versionLoad, validate and transition
Prompts and generated artifactsObject storage plus metadataStore immutable version and hash
Next commandTransactional outboxPublish after commit
Queue deliveryRedis, SQS or another brokerAssume at-least-once handling
External action identityIdempotency/operation tableReuse the same stable key
Live progressRebuildable projectionEmit after accepted transitions

The state can be simple: queued, researching, drafting, awaiting_approval, executing, completed and intervention_required. The important part is not the exact names. Every transition must define its command, guards, resulting state, evidence and side effects.

This is the same reason I prefer explicit state machines for production AI agents. They move orchestration out of an improvised prompt loop and into application code that can be reviewed and tested.

Make each queue job small enough to repeat

A useful job boundary surrounds one meaningful transition, not one entire user goal. A document workflow might use separate commands for extracting text, classifying it, retrieving evidence, drafting an answer, requesting approval and executing the approved action.

Each job follows roughly the same protocol:

  1. Load the workflow by ID and expected version.
  2. Check whether this command is still legal in the current state.
  3. Claim an operation or acquire a short lease when concurrency requires it.
  4. Perform the bounded computation or external call.
  5. Validate and store the result as a versioned artifact.
  6. Commit the state transition, event and next outbox command atomically.
  7. Acknowledge the queue message only after the durable commit.
final class DraftAnswer implements ShouldQueue
{
    public int $tries = 4;
    public int $timeout = 90;

    public function __construct(
        public readonly string $workflowId,
        public readonly int $expectedVersion,
    ) {}

    public function handle(DraftWorkflow $service): void
    {
        $service->draft(
            workflowId: $this->workflowId,
            expectedVersion: $this->expectedVersion,
        );
    }

    public function backoff(): array
    {
        return [10, 60, 300];
    }
}

The job payload contains identifiers and expectations, not the only copy of the workflow context. If the payload becomes stale, the handler should exit safely or reject it as a version conflict. If it is delivered twice, the second execution should observe that the transition or operation already happened.

Laravel job chains and batches are useful execution tools, but they are not automatically a workflow history. A chain describes dispatch order. A batch groups jobs and tracks aggregate completion. Neither replaces domain state when steps can wait for days, branch on policy or require operator intervention.

Persist decisions, artifacts and checkpoints

Long-running AI work produces more than a status string. It produces source selections, structured model decisions, prompt versions, tool results, policy flags and artifacts that a human may approve. Those values need stable identities.

I store large content outside the workflow row and reference it by an immutable artifact ID and hash. The row keeps compact control data: current state, version, active artifact, attempt budget and next deadline. Transition events explain how it moved. This makes the workflow inspectable without turning one JSON column into an unbounded transcript.

A checkpoint should contain enough information to resume from the last accepted boundary. It should not be a dump of every in-memory object. At minimum, it identifies the workflow version, completed step IDs, accepted artifacts, pending command and external operation identities.

That is the practical difference between replay and repetition. Replay reconstructs state from durable evidence. Repetition blindly runs previous code again. I covered the recovery contract in more detail in why AI agent checkpoints enable safe resume.

Use several timeouts, not one magic number

A production workflow needs separate limits for separate risks:

  • HTTP client timeout: how long one provider request may occupy a worker.
  • Job timeout: how long the queue worker permits the command to run.
  • Queue reservation or visibility timeout: when another worker may receive unacknowledged work.
  • Workflow deadline: when the business operation becomes stale or must escalate.
  • Human approval expiry: how long an artifact remains valid for review.

These values must be ordered deliberately. Laravel's queue documentation warns that a worker timeout should be shorter than the connection's retry_after value; otherwise a job may be processed twice. The same principle applies to a cloud broker's visibility timeout. The worker must fail and release control before the broker assumes the worker disappeared.

Retries also need classification. A 429 response or transient network failure may be retried with backoff. Invalid input will not improve on attempt four. A policy rejection should not be retried at all. An ambiguous timeout after an external write requires reconciliation before another write is attempted.

This is why every AI automation needs a retry budget. Attempts, elapsed time and money are different resources. A workflow should consume them explicitly and move to a visible intervention state when the budget is exhausted.

Idempotency belongs at the side-effect boundary

Queue uniqueness and overlap locks are useful, but they do not prove that a remote operation happened once. Locks expire. Workers die. Messages are delivered again. Exactly-once delivery is not the guarantee to build around.

Create a stable operation ID before the external call and reuse it on every retry. Send it as the provider's idempotency key when supported. Store the provider's operation ID and response separately. When support is missing, design a lookup or reconciliation path using a business reference you control.

Consider the uncomfortable sequence: the provider accepts a publication, the network drops the response and PHP throws a timeout. The database says “unknown,” not “failed.” Retrying immediately may publish twice. A reconciliation job should first ask the provider whether the known operation or business reference exists. Idempotency is the contract that makes duplicate delivery survivable.

Model human approval as a state, never as a sleeping job

A worker should not sleep while waiting for a person. Commit awaiting_approval, record the exact artifact version under review and release the worker. The approval endpoint later verifies the reviewer, artifact hash, workflow version and expiry before dispatching the next command.

This makes waits cheap and explicit. It also prevents an approval from authorizing a newer artifact the reviewer never saw. Cancellation, rejection and expiry become ordinary transitions rather than exceptions inside a process nobody can inspect.

Scheduled provider work uses the same pattern. Store the provider job ID and a waiting_for_provider state. Poll with delayed jobs or accept a signed webhook. Both paths must converge on one transition protected by versioning and idempotency.

Assume deployments and worker restarts will happen mid-run

Long-lived queue workers keep the booted application in memory. They do not automatically see new code after a deployment, and they may retain resources longer than expected. Run them under a process monitor, restart them gracefully during deployment and set sensible job or memory limits.

The harder problem is compatibility. A queued payload created by release A may be consumed by release B. A workflow paused last week may resume after several schema and prompt changes. Payloads, workflow state and artifacts therefore need versions. New code should either read the previous version or migrate it deliberately.

Deployments should stop taking new work, let bounded jobs finish or time out, then restart workers. A five-hour monolithic job makes that operationally painful. Ninety-second steps make it ordinary. The workflow continues because its state lives outside the worker.

Observe the run as a timeline of decisions

Queue depth and worker CPU are not enough. For each workflow, I want to see the current state, state age, last accepted transition, pending command, attempt number, provider operation ID, next deadline and reason for intervention. Those fields answer operational questions faster than thousands of unstructured log lines.

Measure both job latency and workflow latency. A queue may process every job in two seconds while the product takes six hours because approvals or polling intervals dominate. Track cost and token use per workflow too; a retry storm can be operationally green while financially wrong.

Events should describe domain facts such as draft_submitted, approval_expired and provider_result_reconciled. That is why AI agent observability starts with events, not dashboards. Dashboards are projections; durable evidence is the source.

The PHP long-running workflow checklist

  • Return a workflow ID from the initiating HTTP request instead of holding the connection open.
  • Represent workflow state and allowed transitions explicitly in application code.
  • Split work into bounded, restartable queue commands.
  • Keep the durable state, artifacts and operation IDs outside worker memory.
  • Commit state changes and outbox commands atomically.
  • Version job payloads, workflow schemas, prompts and artifacts.
  • Set provider, job, reservation and business timeouts independently.
  • Classify failures before retrying and enforce retry budgets.
  • Use idempotency plus reconciliation around external side effects.
  • Model human approval and provider waits as durable states.
  • Restart workers gracefully on deployment and test compatibility across releases.
  • Test duplicate delivery, stale commands, worker termination and resume paths.

PHP does not need to imitate a permanently running agent process to handle long-running AI work. It needs to make the workflow permanent and the processes replaceable. Once each step can be retried, inspected and resumed from durable evidence, the system becomes easier to deploy, cheaper to operate and much less surprising during failure.

Sources checked on October 13, 2026: the PHP manual on command-line usage and the Laravel 13.x documentation on queues, retries, timeouts, workers and failed jobs. The architecture and examples are practical engineering guidance, not a guarantee provided by PHP or Laravel. This article reflects experience with PHP backends, queues, automation and AI agents without identifying an employer or client. It was prepared with AI assistance and editorial review. Cover: DevOps business process image from Pixabay.

If you are turning an AI prototype into a durable PHP system, I can help review its workflow boundaries, queue design and recovery paths. See my AI engineering consulting work or contact me.

Igor Gawrys
Igor Gawrys
AI Engineer & IT Consultant · Katowice, Poland