Skip to main content
View source

Deep Agent

View as Markdown

A planning-capable RocketRide agent built on the Deep Agents library, with optional managed sub-agents for hierarchical delegation.

About Deep Agents

Deep Agents is an open-source agent framework built on LangChain and LangGraph. It layers the machinery long-running tasks need — upfront planning, state that persists across steps, and long-context management — on top of the standard tools-plus-LLM agent loop.

What it does

Runs an agent loop via deepagents.create_deep_agent (built on LangChain/LangGraph), which layers strategic planning, persistent state, and long-context management on top of the standard LangChain tool-calling loop. The package ships two node variants:

  • Deep Agent (agent_deepagent): the orchestrator. Consumes questions and produces answers, runs standalone with its own tools, and registers as a tool (classType: ["agent", "tool"]) so a parent agent can delegate to it via <nodeId>.run_agent.
  • Deep Agent Subagent (agent_deepagent_subagent): a managed worker (classType: ["deepagent"]). It has no questions lane and cannot be invoked directly or called as a tool; it must be wired into a Deep Agent via the deepagent invoke channel, which delegates to it based on its description.

Sub-agents are optional: with none connected, the Deep Agent behaves as a standard single-agent node. Inference is routed through the host LLM channel using a JSON envelope protocol, so any LLM that can follow JSON instructions works; native function-calling support is not required. Agent lifecycle progress (tool calls, LLM calls, agent steps) is streamed as SSE thinking events.

Example pipelines

Research assistant with an HTTP tool

chat → agent_deepagent → response_answers

The Deep Agent node on the canvas with an LLM and an HTTP Request tool connected

Download example.pipe

llm_anthropic is wired to llm and tool_http_request to tool. Chat questions reach the agent, which can call the HTTP tool before returning an answer.

Hierarchical team with specialists

webhook → agent_deepagent → response, plus two Deep Agent Subagents on the deepagent channel: a researcher (tools: tool_exa_search) and a coder (tools: tool_python, tool_github), each with its own llm. The orchestrator breaks the task apart and delegates each piece to the sub-agent whose Description matches, and the plural tool_calls envelope lets it delegate to several sub-agents in parallel in a single turn.

Agent-callable research service

A parent agent (e.g. agent_rocketride) with this Deep Agent connected as a tool. With a filled-in Agent description, the parent invokes <nodeId>.run_agent when it hits a question needing deep multi-step research rather than attempting it in its own loop.

Connections

Deep Agent

ChannelRequiredDescription
llmyes (min 1)LLM used by the agent
toolnoTools available to the agent (via control-plane invoke)
deepagentnoDeep Agent Subagent nodes for hierarchical delegation

Deep Agent Subagent

ChannelRequiredDescription
llmyes (min 1)LLM this sub-agent thinks with
toolnoTools available to this sub-agent

The sub-agent's LLM and tool channels are independent of the orchestrator's. When the orchestrator delegates, the sub-agent's LLM and tool calls are routed back through this node's own channels.

Lanes

Lane inLane outDescription
questionsanswersSend the agent a task, receive its final answer

Deep Agent only. The Subagent declares no lanes; it is driven by an orchestrator, not by direct questions.

As a tool

The Deep Agent (not the Subagent) exposes itself as an invokable tool, <nodeId>.run_agent, so parent agents can delegate to it in nested pipelines.

  • Input: {query: string, context?: object}. query must be a non-empty string; context, when provided, is attached to the question as a RocketRide.agent.tool_context.v1 JSON payload.
  • Output: {content, meta, stack}.

When agent_description is non-empty it is included in the tool's description so parent agents can select this agent correctly.

Configuration

Both variants are steered the same way: by default you add Instructions, and turning on Advanced Mode swaps them for direct prompt fields. The field-level details:

Description (Subagent)

The most important field on a sub-agent. The orchestrating Deep Agent reads only this text when deciding which sub-agent gets a task — it cannot see the sub-agent's prompt, tools, or LLM. Keep it specific and action-oriented: "Searches the web and summarizes findings with sources" gets routed work; "helper agent" never gets picked. If a sub-agent sits idle while the orchestrator does everything itself, fix this field first.

Agent description (Deep Agent)

The same idea one level up: when the Deep Agent is used as a tool by a parent agent, this text is folded into the run_agent tool description so the caller can decide when to invoke it. Leave it blank if nothing calls this agent as a tool.

Instructions, System prompt & Advanced Mode

With Advanced Mode off (the default), you steer the agent by adding Instructions — each non-empty line is appended, on its own line, to the built-in system prompt that already handles planning and state. With Advanced Mode on, the Instructions list is replaced by the raw prompt fields (system_prompt, and agent_description on the orchestrator) for full control; a blank system_prompt falls back to the built-in default. Stay in default mode unless the built-in prompt is actively in your way — replacing it discards the planning behavior that makes Deep Agents worth choosing.

Require tool call

Smaller or weaker planning models occasionally narrate a multi-step tool chain in prose instead of actually calling the tools, producing a plausible-looking but ungrounded answer. When this toggle is on, any run that produces an answer without invoking at least one tool fails with a RocketRide.agent.guard.v1 error instead of delivering the ungrounded text. Off by default; enable it for determinism-critical pipelines. The guard counts real tool invocations only — internal/local reads (for example the wave agent's memory.peek) do not satisfy it.

Notes

Tool calling protocol

The host LLM is opaque to the driver, so tool calling uses a JSON envelope protocol: each LLM call is prefixed with a system preamble. Tool calls are JSON; an answer to the person is not.

  • Single tool call: {"type":"tool_call","name":"server.tool","args":{...}}
  • Parallel tool calls: {"type":"tool_calls","calls":[{"name":"...","args":{...}}, ...]}
  • Final answer: FINAL>>> followed by the answer as plain text

Why the answer left JSON. Arguments are structured data, where JSON earns its keep. An answer is prose, and the JSON form asked the model to escape a whole markdown table into a string value — one missed inner quote and the envelope stopped parsing, so the person read the envelope instead of the answer. After FINAL>>> there is nothing left to escape, and the text is delivered exactly as written. The {"type":"final","content":"..."} shape is still accepted by the parser for any model that emits it; it is no longer what the preamble asks for.

"Exactly as written" is literal: only the separator between the marker and the answer is removed — one newline, or one space on the same line. Whitespace after that belongs to the answer, so FINAL>>> followed by an indented line still arrives as a markdown code block.

The sentinel is matched at the start of a line and is checked before any JSON is parsed. Before it, because an answer may legitimately open with {; anchored, because protocol text travels — a delegation carrying instructions, a note quoting the format, a transcript replayed into a prompt — and an unanchored match would read a tool call that merely mentions FINAL>>> as a final answer and drop the call. A sentinel with nothing after it is not an answer, and goes back through the retry loop.

The plural tool_calls form dispatches all entries concurrently (LangGraph's async ToolNode fans them out via asyncio.gather), which is what unlocks parallel sub-agent delegation in a single turn.

Up to 3 attempts are made when the LLM produces malformed JSON. Each retry names the character that broke the parse and quotes the text around it, and what it asks for next depends on what was attempted: a broken answer is pointed at FINAL>>>, where nothing can break again, while a broken tool call is asked for the same call repaired — telling the model to answer in prose instead would trade the work for a sentence about it. A tolerant parser extracts the first balanced JSON object, rescuing responses wrapped in markdown fences, followed by trailing prose, or stacked with a stray second object (a common failure mode: a duplicate call or hallucinated final appended after a tool_call). An unparseable envelope that unmistakably opens as a final is salvaged for its content rather than printed at the person; a malformed TOOL call never is, since rescuing one would turn work the crew intended to do into a sentence claiming it was done.

Host tool descriptors are converted to LangChain BaseTool instances with typed Pydantic input schemas built from each tool's JSON-Schema inputSchema; tool execution and LLM calls are bridged off the event loop via asyncio.to_thread so concurrent calls do not serialize.

Hierarchical delegation

Connect one or more Deep Agent Subagent nodes to the deepagent invoke channel to turn the Deep Agent into an orchestrator. On each run:

  1. The orchestrator fans out a describe invoke to every connected Subagent node.
  2. Each sub-agent returns its name, description, system prompt, instructions, and a reference to its own engine channels.
  3. The orchestrator builds a deepagents.middleware.subagents.SubAgent record per descriptor, wiring each sub-agent's LLM and tools to its own channels, and passes them to create_deep_agent(subagents=...).
  4. The orchestrator's LLM gains a task(description, subagent_type) tool it calls to delegate work. Each sub-agent runs in its own AgentContext that inherits the run metadata, so SSE events route back to the same logical run.

Give each sub-agent its own LLM, tools, and a clear description: the description is the only signal the orchestrator uses to choose a delegate. A Subagent can be connected to multiple orchestrators simultaneously; each orchestrator independently includes it in its own hierarchical run. A describe failure on one node is logged and skipped, not fatal to the run.

Observability

The driver emits SSE thinking events throughout a run: host-tool discovery count, sub-agent collection count, agent start, per-tool start/completion/error (with tool name and input length), LLM call start/completion/error, and agent thinking/done transitions.

Upstream docs


Schema

Deep Agent (services.agent.json)

FieldTypeDescriptionDefault
advanced_modebooleanAdvanced Mode
When enabled, replace the Instructions list with direct Agent Description and System Prompt fields for full control.
false
agent_descriptionstringAgent description
What does this agent do? Describe its purpose and capabilities, this helps parent agents select and invoke it correctly.
""
instructionsarrayInstructions
Additional instructions to guide the agent. Each line is appended to the system prompt.
require_tool_callbooleanRequire tool call
Require the agent to invoke at least one tool before answering. When on, a run that answers without calling any tool fails with a guard error. Use for determinism-critical pipelines where an ungrounded or narrated answer must never be delivered. Off by default.
false
system_promptstringSystem prompt
Instructions that define this agent's role and behaviour. Leave blank to use the default.
""

Deep Agent Subagent (services.subagent.json)

FieldTypeDescriptionDefault
advanced_modebooleanAdvanced Mode
When enabled, replace the Instructions list with a direct System Prompt field for full control.
false
descriptionstringDescription
The orchestrator reads this description to decide when to delegate to this sub-agent. Keep it specific and action-oriented, this is the only signal the orchestrator uses to pick a sub-agent.
""
instructionsarrayInstructions
Additional instructions to guide this sub-agent. Each line is appended to the system prompt.
system_promptstringSystem prompt
Instructions that define this sub-agent's role and behaviour. Leave blank to use the default.
""

Dependencies

  • deepagents
  • langchain
  • langchain-core
  • pydantic