Use Xantly with Deep Agents

LangChain's Deep Agents framework, planner + sub-agent delegation + virtual file system. Routes through Xantly for per-role tier routing across 50-200 calls per run. Quality planner, Value sub-agents, Speed summarizers.

Deep Agents is LangChain's framework for building long-running, tool-using agents with planning and sub-agent delegation: the same architecture pattern Claude Code, Manus, and Devin use internally. Because it runs on LangChain's ChatModel abstraction, pointing the model at Xantly routes every planning call, every sub-agent step, and every tool-use loop through smart routing, semantic cache, and persistent memory.

Why this matters for Deep Agents specifically. Deep Agents burn tokens fast, a single planning session can fire 50-200 LLM calls across sub-agents and tool loops. Running those on Claude Sonnet at default rates gets expensive within minutes. Xantly's per-tenant bandit router picks the cheapest model that can reliably emit tool calls for each step, often swapping quality-tier models in and out of sub-agent calls while keeping the main planner on a frontier model. The result: the same deep-agent run, up to 80% cheaper, with identical final output.

Prerequisites

Setup

from deepagents import create_deep_agent
from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    base_url="https://api.xantly.com/v1",
    api_key="xantly_sk_...",
    model="xantly/auto-quality",
)

# Deep Agents 0.5+ API: model first, tools second, system_prompt as kwarg.
# Earlier releases used `instructions=`, if you're on <0.5, rename.
agent = create_deep_agent(
    model=model,
    tools=[],                       # your domain tools
    system_prompt="You are a thorough research agent.",
)

result = agent.invoke({
    "messages": [("user", "Research the impact of transformer architectures on speech recognition.")]
})
print(result["messages"][-1].content)

That's it, the agent now plans, sub-delegates, and runs tool loops through Xantly.

Full example with tools

from deepagents import create_deep_agent
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
import httpx

@tool
def search_web(query: str) -> str:
    """Search the web and return the top 3 results as text."""
    r = httpx.get("https://duckduckgo.com/?q=" + query, timeout=10)
    return r.text[:2000]

@tool
def fetch_url(url: str) -> str:
    """Fetch a URL and return its text content."""
    return httpx.get(url, timeout=15).text[:5000]

planner = ChatOpenAI(
    base_url="https://api.xantly.com/v1",
    api_key="xantly_sk_...",
    model="xantly/auto-quality",  # planning needs reasoning
)

agent = create_deep_agent(
    tools=[search_web, fetch_url],
    system_prompt=(
        "You are a technical research agent. Plan your research steps, "
        "delegate to sub-agents when appropriate, and produce a structured "
        "summary citing every source."
    ),
    model=planner,
)

result = agent.invoke({
    "messages": [("user", "What changed in open-weight LLM training between 2023 and 2025?")]
})
print(result["messages"][-1].content)

Model choice, the split matters

Deep Agents are more cost-sensitive than any other agent framework because they fan out so aggressively. Use different Xantly tiers for the planner vs sub-agents:

planner_model = ChatOpenAI(
    base_url="https://api.xantly.com/v1",
    api_key="xantly_sk_...",
    model="xantly/auto-quality",       # main planner, reasoning matters
)

sub_agent_model = ChatOpenAI(
    base_url="https://api.xantly.com/v1",
    api_key="xantly_sk_...",
    model="xantly/auto-value",         # sub-agents executing plans, 4-6x cheaper
)

agent = create_deep_agent(
    tools=[...],
    system_prompt="...",
    model=planner_model,
    subagents=[
        {
            "name": "researcher",
            "description": "Gathers information about a topic.",
            "prompt": "Research exhaustively and cite every source.",
            "model": sub_agent_model,
        },
        {
            "name": "summarizer",
            "description": "Compresses research into bullet points.",
            "prompt": "Extract the 5 most important facts, one line each.",
            "model": ChatOpenAI(
                base_url="https://api.xantly.com/v1",
                api_key="xantly_sk_...",
                model="xantly/auto-speed",   # summarization is a speed task
            ),
        },
    ],
)

Tier-by-tier recommendation:

Agent roleModel IDWhy
Main plannerxantly/auto-quality or anthropic/claude-sonnet-4.6Deep reasoning, long horizon
Research sub-agentxantly/auto-valueTool calls + extraction, balanced cost
Summarization sub-agentxantly/auto-speedShort, structured output
Verification sub-agentxantly/auto-safetyCorrectness-sensitive steps
Code-edit sub-agentanthropic/claude-sonnet-4.6Tool-calling reliability

Built-in file system tools

Deep Agents ships with read_file, write_file, edit_file, ls as virtual file system tools (in-memory by default). Every call to these is a tool_use round-trip with your model. Because Xantly caches tool_use output where safe, repeated file reads inside the same session return instantly.

No config needed, the file system tools just work with Xantly routing.

Streaming

for event in agent.stream({
    "messages": [("user", "Build a plan and execute it step by step")],
    "files": {},          # or a real file dict
}):
    print(event)

Persistent memory across sessions

Deep Agents has its own in-memory planner state. Xantly adds cross-session memory: so the agent can recall prior research, prior plans, prior sub-agent conclusions even after the process restarts. Opt in by setting a request-level header:

model = ChatOpenAI(
    base_url="https://api.xantly.com/v1",
    api_key="xantly_sk_...",
    model="xantly/auto-quality",
    default_headers={
        "X-Xantly-Memory-Mode": "recall",
        "X-Xantly-Session-Id": "project-deepresearch-2026",
    },
)

All calls from this model instance share the session id, so Xantly's L3 graph memory accumulates knowledge across agent invocations and resurfaces it automatically on future calls.

Cost observability per step

Each LLM call inside Deep Agents logs separately in your Xantly dashboard. Tag runs for attribution:

model = ChatOpenAI(
    base_url="https://api.xantly.com/v1",
    api_key="xantly_sk_...",
    model="xantly/auto-quality",
    default_headers={
        "X-Xantly-Tag": "deepagent-run-42",
    },
)

Dashboard shows per-run: total tokens, per-sub-agent cost, cache hit rate, average tool-call latency.

Verify

pip install deepagents langchain-openai
from deepagents import create_deep_agent
from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    base_url="https://api.xantly.com/v1",
    api_key="xantly_sk_...",
    model="xantly/auto-speed",
)

agent = create_deep_agent(tools=[], system_prompt="Reply in one word.", model=model)
print(agent.invoke({"messages": [("user", "Capital of France?")]})["messages"][-1].content)

Expected: a one-word response. If you get a response, the full Deep Agents → Xantly → upstream chain is working.

What you get

Gotchas

Deep Agents emits a LOT of calls. A single "research X exhaustively" prompt can fire 50-200 LLM calls. Set a daily USD cap on your Xantly API key (Dashboard → API Keys → Daily Budget) before your first serious run. The cap returns 402 Payment Required when exceeded, the agent sees it cleanly and stops, rather than silently spending four figures.

Tool-calling reliability matters more than any other framework. If a sub-agent can't emit reliable tool calls, the planner loops. Always default sub-agents to anthropic/claude-sonnet-4.6 or xantly/auto-quality, speed-tier models will misfire on complex tool schemas.

Async. agent.ainvoke(...) works. Underlying OpenAI/Anthropic SDKs are async-native.

Structured output. If your tools return JSON, stick with xantly/auto-quality or explicit strong models, schema adherence drops off fast in speed-tier models.

File-system tools are in-memory by default. Persist across runs by passing files={} explicitly and saving the result's files back in. Xantly caches content fingerprints but can't resurrect files from a crashed process.

xantly/auto-speed may produce low-information responses on short prompts. Cheap speed-tier models (especially when given Deep Agents' large boilerplate system prompt with no real workspace context) sometimes return generic refusals. Xantly detects this and replaces them with a structured diagnostic message that:

  1. Starts with the marker [xantly:fallback] so callers can detect it on the wire
  2. Names the model that failed and suggests xantly/auto-quality
  3. Surfaces the reason in xantly_metadata.fallback_applied (values: low_information, low_information_formal, empty_user_message)
  4. Is also reflected in the response header x-xantly-fallback-applied: <reason>

Mitigation in practice: use xantly/auto-quality (or pin anthropic/claude-sonnet-4-5) for the planner, leave xantly/auto-value for the sub-agents. In real production agent runs, the workspace context, file dumps, prior conversation, project metadata, gives sub-agents enough grounding that this rarely triggers.

import uuid
SESSION = f"deepagent-{uuid.uuid4().hex[:8]}"

planner = ChatOpenAI(
    base_url="https://api.xantly.com/v1",
    api_key="xantly_sk_...",
    model="xantly/auto-quality",   # avoid speed-tier for the planner
    default_headers={
        "x-xantly-agent-framework": "deepagents",
        "x-xantly-agent-session": SESSION,
    },
)

# Detect synthesized responses programmatically:
response = planner.invoke("...")
if response.content.lstrip().lower().startswith("[xantly:fallback]"):
    # Upgrade and retry, log the incident, etc.
    ...

Next steps