Subagents
An agent delegating to another agent needs no special API — a second graph, wrapped as a tool. Isolated context, and a depth cap that is structural rather than policy.
visvoai-core has no dedicated "subagent" concept, and that's deliberate:
an agent is just a compiled graph, and a tool is just a callable. "Call
another agent" is therefore a ~25-line helper — build a second graph, wrap
invoking it as a tool, hand that tool to the caller like any other.
def make_agent_tool(name, description, model, tools, system_prompt):
"""Wrap a whole agent as one callable tool. The caller's model sees a
normal tool named `name`; invoking it runs a fresh, isolated conversation
on its own graph and returns only the final answer."""
from visvoai.core import ask
from visvoai.core.runtime import AgentRuntime
graph = AgentRuntime().build_graph(
model=model, core_tools=tools, system_prompt=system_prompt)
async def agent_tool(task: str) -> str:
return await ask(graph, task)
agent_tool.__name__ = name
agent_tool.__doc__ = description
return agent_toolresearcher = make_agent_tool(
name="researcher",
description="Delegate a research task; returns a written answer.",
model=researcher_model,
tools=[lookup], # ← no dispatch tool in here = depth cap
system_prompt="You are a precise researcher. Use lookup; cite what you find.",
)
lead = AgentRuntime().build_graph(
model=lead_model,
core_tools=[researcher], # the subagent, dispatched like any tool
system_prompt="You are the lead. Delegate research; synthesize answers.",
)
from visvoai.core import ask
print(await ask(lead, "explain step caps — ask research if unsure"))The lead model sees researcher as an ordinary function-shaped tool
(_callable_to_base_tool picks up its name/docstring exactly like any
other plain-function tool — see
Defining tools).
Calling it runs a brand-new, isolated conversation on the subagent's own
graph — its own system prompt, its own step cap, its own tool list — and
only the final text answer comes back to the caller. Nothing about the
caller's message history or state leaks into the subagent's turn, and
nothing about the subagent's intermediate tool calls leaks back out.
The depth cap is structural, not policy
There's no recursion guard to configure: the researcher's own tools list
is [lookup] — it simply doesn't include the dispatch tool that would let
it call another agent. A subagent cannot recurse because the capability
to call make_agent_tool's output doesn't exist in its tool list, not
because the model is trusted to decline. This is the same trick a
production consumer uses for real depth limits: control what's callable,
not what's "allowed."
RuntimeContext.subagent_depth and .parent_tool_call_id exist precisely
to let a richer subagent implementation track nesting explicitly (for UI
threading, logging, or an actual configurable depth limit) once you go
beyond the two-level lead/researcher pattern shown here — see
Extension seams.
(Full runnable file, with scripted models so it needs no API key:
visvoai-core/examples/08_subagents.py.)
Next: Migrating from LangChain.