Skip to content

Context & registries

Hierarchical context (no parent pollution)

Every LLM call or sub-agent call runs in a temporary child context. The child can accumulate tool-call messages and scratch reasoning freely; when the call finishes, only the final output is merged back into the parent — so parents stay small no matter how deep the call tree gets.

from langstitch import Context, run_llm, run_worker_agent, get_llm_provider

ctx = Context(data={"question": q}, messages=[{"role": "user", "content": q}])

def call_model(llm_ctx):
    # llm_ctx.system  = resolved persona
    # llm_ctx.tools   = selected (by tag/name/role) and materialized just for this call
    return get_llm_provider().invoke(
        [{"role": "system", "content": llm_ctx.system}] + llm_ctx.messages,
    )

answer = run_llm(ctx, call_model, persona="assistant",
                 tool_tags=["billing"], key="answer", as_message="assistant")
# ctx.data["answer"] is set; the child's tool traffic + scratch were discarded.

# Delegate to a sub-agent (runs with only its allowed tools, isolated context):
findings = run_worker_agent(ctx, "researcher", carry=["question"])

ContextBuilder does the lazy selection; Context.scope(...) / ContextScope give you the raw building blocks if you need finer control. LLMContext is the object your model callback receives.

Dynamic registries & lazy materialization

Tools and worker agents are not eagerly loaded when a request arrives. The registries hold cheap specs and refresh themselves automatically when anything new registers (and on demand via refresh_registries()); the actual callables are materialized only when a node selects them.

The graph server exposes introspection helpers (each hits the live registries):

Server.get_all_tools()          # [ToolSpec, ...]
Server.get_all_worker_agents()  # [AgentSpec, ...]
Server.get_input_guardrails()
Server.get_output_guardrails()
Server.get_skills(); Server.get_policies(); Server.get_personas()
Server.get_tool("now"); Server.get_worker_agent("researcher")
Server.refresh_registries()

The same accessors are module-level functions:

import langstitch

langstitch.get_all_tools()
langstitch.get_all_worker_agents()
langstitch.refresh_registries()

Worker agents

Register a delegatable sub-agent with @worker_agent; it runs with only its allowed tools in an isolated context, and only its result merges back into the parent.

from langstitch import worker_agent

@worker_agent(role="researcher", tools=["web_search"], persona="researcher")
def researcher(ctx):
    ...

Why isolation matters

Deep tool/agent call trees can balloon a single message history. Hierarchical context keeps each parent small and deterministic, which improves latency, cost, and reproducibility.