Docs / Architecture / Agents

Agents

Ploton works with any AI agent framework — LangChain, CrewAI, custom builds. Here's how agents integrate with subagents.

Agent integration

Ploton is not an agent framework. It’s infrastructure that any agent can call.

Building with LangChain, CrewAI, AutoGPT, Semantic Kernel, or your own custom agent loop — doesn’t matter. Your agent lists its available subagents in the system prompt. When it needs real-world capabilities, it routes the request to the right subagent. No adapter, no plugin system, no vendor lock-in.

System prompt setup

Add your deployed subagents to your agent’s system prompt:

You have access to these Ploton subagents:
- crm-agent: CRM and sales data
- billing-agent: Payments and invoices
- media-agent: Image and video processing

When a request needs capabilities you don't have,
delegate to the relevant subagent.
If no subagent fits, decline the request.

Your agent reads this list and decides which subagent handles each request based on the user’s ask. The agent does the routing; Ploton does the execution.

The agent-subagent loop

The typical flow:

sequenceDiagram
    participant App as Your App
    participant Agent as Your Agent
    participant Ploton
    participant Subagent
    participant Service as External Service

    App->>Agent: Assign task
    Agent->>Agent: Check system prompt for subagents
    Agent->>Ploton: POST /v1/tasks + sub_agent
    Ploton->>Subagent: Route to scoped subagent
    Ploton-->>Agent: 200 OK (task_id)
    Note over Agent: Continues other work
    Subagent->>Service: Auth, fetch, execute
    Service-->>Subagent: Response
    Ploton->>App: Webhook (task.complete)
    App->>Agent: Resume with result

In code:

// Inside your agent's tool handler
async function handleExternalDataRequest(agentContext: AgentContext) {
	// Agent routes to the right subagent
	const response = await fetch("https://api.ploton.ai/v1/tasks", {
		method: "POST",
		headers: {
			Authorization: `Bearer ${process.env.PLOTON_API_KEY}`,
			"Content-Type": "application/json",
		},
		body: JSON.stringify({
			prompt: `Pull all active subscriptions for ${agentContext.userId}. Return plan name, amount, and next billing date.`,
			sub_agent: "billing-agent",
			user_id: agentContext.userId,
			metadata: {
				agent_session: agentContext.sessionId,
				step: "fetch_subscription_data",
			},
		}),
	});

	const task = await response.json();

	// Agent continues — doesn't block on the subagent
	return {
		status: "delegated_to_subagent",
		task_id: task.id,
		sub_agent: "billing-agent",
		message: "Subscription data request submitted. Will resume on webhook.",
	};
}

The metadata field is how you correlate Ploton results with your agent’s state. When the webhook comes back, use agent_session and step to resume the right agent at the right point.

Framework integration patterns

LangChain / LangGraph

Register a routing tool that delegates to the appropriate subagent:

from langchain.tools import Tool

def call_ploton_subagent(input_str: str) -> str:
    """Parse subagent name and prompt from input, route to Ploton."""
    # Your routing logic here
    return call_ploton_api(prompt=input_str, sub_agent=determine_subagent(input_str))

ploton_tool = Tool(
    name="ploton_subagent",
    description="Route tasks to Ploton subagents. Available subagents: crm-agent (CRM and sales data), billing-agent (payments and invoices), media-agent (image and video processing). Use when you need external service access.",
    func=call_ploton_subagent,
)

agent = initialize_agent(
    tools=[ploton_tool, ...other_tools],
    llm=llm,
)

The tool description lists available subagents so the LLM knows when and where to route.

CrewAI

from crewai import Tool

ploton_tool = Tool(
    name="Ploton Subagent Router",
    description="Routes tasks to specialized Ploton subagents: crm-agent, billing-agent, media-agent. Returns a task ID — results arrive via webhook.",
    func=call_ploton_subagent,
)

Custom agent loops

If you’re running your own agent loop, subagent routing slots into the tool-use step:

const SUBAGENTS = ["crm-agent", "billing-agent", "media-agent"];

while (!task.isComplete()) {
	const action = await agent.decide(task.context);

	if (action.type === "external_service") {
		// Route to the right subagent
		const subAgent = determineSubagent(action.prompt, SUBAGENTS);
		const plotonTask = await createPlotonTask(action.prompt, subAgent, task.userId);
		task.awaitCallback(plotonTask.id);
		continue; // Process next task in queue
	}

	if (action.type === "internal") {
		await executeInternally(action);
	}
}

Agent design considerations

When to route to a subagent

Route to a subagent when your agent needs to:

  • Authenticate with a third-party service — OAuth flows, token management, credential handling
  • Fetch data from external APIs — CRM records, payment data, calendar events, file storage
  • Take actions on external services — send emails, post messages, create records, upload files
  • Handle user interaction — collect approvals, preferences, or missing data

Don’t route to a subagent for internal computations, reasoning, or queries against your own database. If it doesn’t touch an external service, you don’t need a subagent.

Keep prompts specific

Subagent output quality tracks directly with prompt quality. Vague prompts get vague results. See the Prompt Engineering guide for patterns that work.

Handle the async gap

Between creating a task and receiving the webhook, your agent needs a strategy:

  • Queue-based — agent enqueues tasks and processes results as they arrive. Best for high-throughput systems.
  • Polling fallback — agent periodically checks task status. Useful when webhooks aren’t available.
  • Hybrid — webhooks as primary delivery, polling as a fallback for missed events.

Use metadata for correlation

Include enough metadata to pick up where you left off when the result arrives:

{
	"metadata": {
		"agent_session_id": "sess_abc123",
		"workflow_step": "enrich_contact_data",
		"attempt": 1,
		"parent_task_id": "your_internal_task_id"
	}
}

Next steps