VisvoAI Docs
visvoai-core

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_tool
researcher = 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.

On this page