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):
- Build the message array (system prompt + conversation history)
- Call the AI gateway at
env.AI_URL/chat/completions - If the AI responds without tool calls -- the run is complete, the response is stored
- 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:
workflow:
AGENT_WORKFLOW: agent_runThe 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:
- Reads the task payload (userId, chatId, platform, description)
- Injects
[Scheduled task]: {description}as a user message - Triggers a new
agent_runworkflow
This means scheduled tasks are processed through the same durable loop as regular messages.
timer:
SCHEDULER: defaultThe 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
- createAgent() -- Configure
maxStepsand other options - Hooks --
onRunCompleteandonRunErrorfire at the end of the loop - State & Workspaces -- How workspace state persists across workflow steps
- Ploy Workflows -- The underlying Ploy workflow system
How is this guide?
Last updated on