Skip to main content

Wiring an Artifact-Capable Workflow

The six artifact toolsdocx_read, xlsx_read, pdf_read, docx_write, xlsx_write, pdf_write — are not enabled by default on every agent. A workflow opts in by declaring each tool as a node and edging it to a deep_agent node with the tools handle. This page covers exactly that wiring.

If you are building a workflow in the visual editor, the editor renders these nodes and edges directly — you do not need to write the JSON by hand. The structure below is what gets stored.

Anatomy of a tool-bearing workflow

Every artifact-capable workflow has the same four ingredients:

  1. An agent_input node — accepts user_prompt (and optionally session_id) from the calling Responses API request.
  2. A deep_agent node — the LLM-driven orchestrator. Receives the user prompt and decides which tools to call.
  3. One tool node per artifact tooldocx_read, pdf_write, etc. Each is its own first-class node with isTool: true.
  4. tools edges — from deep_agent (handle tools) to each tool node (handle tool). This is the line that tells the runtime "this tool is available to this agent."

The tools edge is the only thing that gates availability. There is no separate allowlist, no per-agent permission table — if the edge exists, the tool is callable; if it does not, the tool simply is not part of the agent's tool list.

Node and edge shape

A tool node

A tool node is minimal: a stable id, a type from the artifact tool names, and data.isTool: true. The runtime instantiates the corresponding worker node class on demand.

{
"id": "docxWrite-8",
"type": "docx_write",
"position": { "x": 1250, "y": 280 },
"data": {
"label": "DOCX Write",
"isTool": true,
"params": {},
"handles": ["tool"],
"isInput": false,
"isOutput": true,
"inputs": [],
"outputs": [],
"errors": []
}
}

All six tools use the exact same shape — only the id, type, and label change.

A tools edge

{
"id": "e-deep-wDocx",
"source": "deepAgent-4",
"target": "docxWrite-8",
"sourceHandle": "tools",
"targetHandle": "tool"
}

sourceHandle: "tools" and targetHandle: "tool" are the magic strings. Any other handle combination is treated as a data-flow edge and the runtime will not register the target as a tool.

The deep_agent declaration

The deep agent itself is an ordinary node, with one detail that matters for tooling — its handles must include "tools":

{
"id": "deepAgent-4",
"type": "deep_agent",
"data": {
"params": {
"model": { "value": "claude-sonnet-4-6", "isExpression": false },
"system_prompt": { "value": "You are a document transformation agent...", "isExpression": false },
"user_prompt": { "value": "@agentInput-3.user_prompt", "isExpression": true, "isAttachedToInputNode": true },
"streaming": { "value": true, "isExpression": false }
},
"handles": ["inputs", "outputs", "tools", "agents"]
}
}

Tools are discovered at compile time by walking out_edges(deep_agent_id, sourceHandle="tools") and looking up each target's type in the worker NODE_MAPPING. Unknown types fail loudly at workflow validation, not silently at run time.

A complete worked example

Below is the full topology used by the end-to-end template round-trip test (sandbox/test-artifacts/create_template_workflow.py) — one deep agent, all six artifact tools, an input, and an output.

{
"name": "document-transformer",
"type": "streaming",
"nodes": [
{ "id": "agentInput-3", "type": "agent_input", "data": { "isInput": true, "handles": ["outputs"] } },
{ "id": "deepAgent-4", "type": "deep_agent", "data": { "handles": ["inputs","outputs","tools","agents"], "params": { "model": { "value": "claude-sonnet-4-6" } } } },

{ "id": "docxRead-5", "type": "docx_read", "data": { "isTool": true, "handles": ["tool"] } },
{ "id": "xlsxRead-6", "type": "xlsx_read", "data": { "isTool": true, "handles": ["tool"] } },
{ "id": "pdfRead-7", "type": "pdf_read", "data": { "isTool": true, "handles": ["tool"] } },
{ "id": "docxWrite-8", "type": "docx_write", "data": { "isTool": true, "handles": ["tool"] } },
{ "id": "xlsxWrite-9", "type": "xlsx_write", "data": { "isTool": true, "handles": ["tool"] } },
{ "id": "pdfWrite-10", "type": "pdf_write", "data": { "isTool": true, "handles": ["tool"] } },

{ "id": "agentOutput-11","type": "agent_output", "data": { "isOutput": true, "handles": ["inputs"] } }
],
"edges": [
{ "source": "agentInput-3", "target": "deepAgent-4", "sourceHandle": "outputs", "targetHandle": "inputs" },

{ "source": "deepAgent-4", "target": "docxRead-5", "sourceHandle": "tools", "targetHandle": "tool" },
{ "source": "deepAgent-4", "target": "xlsxRead-6", "sourceHandle": "tools", "targetHandle": "tool" },
{ "source": "deepAgent-4", "target": "pdfRead-7", "sourceHandle": "tools", "targetHandle": "tool" },
{ "source": "deepAgent-4", "target": "docxWrite-8", "sourceHandle": "tools", "targetHandle": "tool" },
{ "source": "deepAgent-4", "target": "xlsxWrite-9", "sourceHandle": "tools", "targetHandle": "tool" },
{ "source": "deepAgent-4", "target": "pdfWrite-10", "sourceHandle": "tools", "targetHandle": "tool" },

{ "source": "deepAgent-4", "target": "agentOutput-11", "sourceHandle": "outputs", "targetHandle": "inputs" }
]
}

This single workflow lets the agent read three input templates, transform them, and emit three new output documents in one Responses API turn.

Choosing which tools to expose

The six tools are independent — add only the ones a workflow actually needs.

Use caseMinimum tools to wire
Agent reads a docx and summarises itdocx_read
Agent generates a PDF report from a promptpdf_write
Agent fills an XLSX template with computed dataxlsx_read, xlsx_write
Document round-trip across all three formatsAll six

Fewer tools shorten the agent's tool-selection prompt and reduce the chance of wrong-tool calls in ambiguous prompts. Wire the maximum set only when you genuinely need it.

System prompt guidance

Wiring the tools makes them available; the system prompt is how you steer the agent toward using them well. A few patterns that hold up in practice:

  • Name the tools explicitly in the system prompt. The agent reads tool names from the schema, but reinforcing them in prose ("Use docx_read to inspect the template before calling docx_write") materially improves first-attempt tool selection.
  • State an execution discipline when the workflow has a natural order — e.g. "First read all templates, then produce all outputs." Without it, the agent may interleave reads and writes unhelpfully.
  • Tell the agent how documents are referenced — by artifact_id (within the run) or by s3_key (for inputs supplied by the caller). See Sending Documents to the Agent.

The sample system prompt in sandbox/test-artifacts/create_template_workflow.py is a working starting point.

Validating the wiring

Two cheap checks before sending traffic:

  1. Workflow create returns 200 — backend validation rejects unknown node types and orphan tool edges, so a successful create means the topology is well-formed.
  2. First response.in_progress event lists your tools — when you fire a Responses API request against the agent, the agent's first turn enumerates its tool list internally; if a tool is missing, the agent simply will not call it. Issue a test prompt that explicitly names the tool ("Call docx_write with this content") — if the agent does not invoke it, the edge is missing or the handle names are wrong.

See also