Ploy
Ploy
Agent SDK

Workflows

How the durable agent tick loop processes messages and survives failures.

Workflows

The Agent SDK uses Ploy's durable workflow system to run the agent loop. Each user message triggers a workflow execution that loops through AI calls and tool execution until the agent produces a final response. Every step is persisted, so the loop survives failures and restarts.

The Tick Loop

When handleMessage() triggers a workflow, the agent_run workflow executes these steps:

1. Load Workspace

Loads the user's workspace from state (or creates a default one). Appends the user's message to the conversation history and trims to the last 50 messages.

2. Build System Prompt

Resolves the system prompt (static string or async function) and appends the user's memory entries.

3. AI Call + Tool Execution Loop

The core loop runs up to maxSteps iterations (default: 30):

  1. Build the message array (system prompt + conversation history)
  2. Call the AI gateway at env.AI_URL/chat/completions
  3. If the AI responds without tool calls -- the run is complete, the response is stored
  4. If the AI responds with tool calls -- execute each tool, append results to the conversation, and loop

Each iteration is a durable step.run(), so completed steps are skipped on retry.

4. Check Queue

After the run completes, the workflow checks for queued messages. If any exist, the first is dequeued and a new workflow is triggered to process it.

Durability

Every step in the workflow is persisted by Ploy's workflow engine:

  • AI calls -- ai_call_1, ai_call_2, etc.
  • Tool executions -- tools_1, tools_2, etc.
  • State saves -- save_step_1, save_final_2, etc.

If a step fails, it's automatically retried. Completed steps are skipped on re-execution.

Message Queuing

When a user sends a message while a run is active, the message is queued in the workspace:

// This happens automatically inside handleMessage()
if (workspace.activeRunId) {
	workspace.queue.push({ text, timestamp: Date.now() });
	await saveWorkspace(state, workspace);
	return; // Don't trigger a new workflow
}

After the current run completes, the workflow dequeues and processes the next message. This ensures messages are processed in order without concurrent runs.

Configuration

The workflow is named agent_run and must be bound in ploy.yaml:

ploy.yaml
workflow:
  AGENT_WORKFLOW: agent_run

The SDK registers the workflow handler automatically. You do not need to implement it.

maxSteps

Control the maximum iterations of the AI-tool loop:

createAgent({
	maxSteps: 50, // default: 30
	// ...
});

This prevents runaway tool loops from consuming resources.

Timer Integration

Scheduled tasks use Ploy's timer binding. When a timer fires, the SDK's timer handler:

  1. Reads the task payload (userId, chatId, platform, description)
  2. Injects [Scheduled task]: {description} as a user message
  3. Triggers a new agent_run workflow

This means scheduled tasks are processed through the same durable loop as regular messages.

ploy.yaml
timer:
  SCHEDULER: default

The workflow and timer handlers are owned by the SDK. You only need to configure the bindings in ploy.yaml and implement the handle function for incoming requests.

Next Steps

How is this guide?

Last updated on