Skip to Content
DocsFAQ

FAQ

Setup

How is this different from LangChain / LangGraph?

LangChain is a building-block library; LangGraph is a graph DSL on top. Loom is a harness. One configured Agent with a typed protocol surface for everything (memory, tools, runtime, permissions). One kwarg flips the agent loop strategy among twelve built-in shapes.

The biggest concrete difference: user_id is a typed primitive in Loom. In LangGraph it’s a string in config["configurable"] and typoing it leaks data across tenants. See Migrating from LangGraph.

How is this different from CrewAI / AutoGen / OpenAI Agents SDK?

Those are sibling-only multi-agent frameworks (one coordination shape per team). Loom supports recursive composition , architectures wrap each other. Reflexion-of-Supervisor is a team that learns from its own failures; you can’t express that in sibling-only frameworks. See Recursive composition.

Why Python 3.11+?

Strict typing (Self, Generic[T] quirks resolved), better asyncio cancellation semantics, and StrEnum. The pyproject.toml declares requires-python = ">=3.11".

Does it support sync code?

Yes. Sync @tool functions are dispatched to a worker thread via anyio.to_thread.run_sync so they don’t block the event loop. The agent loop itself is async, agent.run(...) is awaited. For sync entry points, wrap with asyncio.run(...):

import asyncio from loomflow import Agent result = asyncio.run(Agent("...").run("..."))

Will my OpenAI / Anthropic SDK code keep working?

Yes. Under the hood, OpenAIModel wraps AsyncOpenAI and AnthropicModel wraps AsyncAnthropic. Pass your own client instance for custom config:

from openai import AsyncOpenAI from loomflow.model.openai import OpenAIModel from loomflow import Agent client = AsyncOpenAI(timeout=30.0, max_retries=0) agent = Agent("...", model=OpenAIModel("gpt-4o", client=client))

See Migrating from the OpenAI SDK.

Cost

How do I limit cost per user?

StandardBudget(BudgetConfig(per_user_max_*)). See Per-user budget caps.

What does auto_extract cost?

A small extraction LLM call after every agent.run(). With gpt-4o-mini it’s ~$0.0001 per run. Disable for cost-sensitive bulk processing: Agent(..., auto_extract=False). See Auto-extract observability.

Does the framework retry expensive calls?

Yes. Transient model errors (5xx, 429, network blips) retry automatically with exponential backoff. The RetryPolicy is tunable; disable retries entirely with RetryPolicy.disabled(). See RetryPolicy.

Multi-tenancy

How do I make the agent multi-tenant?

Pass user_id= on every agent.run(). The framework partitions memory, tracks per-user budgets, attributes audit log entries, and exposes user_id to tools via get_run_context() automatically. See Multi-tenancy.

Can different tenants use different models?

Yes. Either construct a different Agent per tenant, or use a Router that dispatches to specialists with different models. Or use PerUserPermissions to gate tool access per tenant.

How do I delete a user’s data (GDPR)?

await agent.memory.forget(user_id="alice")

See GDPR ops. The audit log is intentionally immutable; for compliance, redact at query time.

Memory

What’s the difference between Memory and a Vector store?

Memory holds conversational state. Episodes, facts, working blocks built up across runs. VectorStore holds arbitrary corpora , documents you retrieve from but don’t grow. See Episode vs Fact and RAG.

Why bi-temporal facts?

Production systems need to answer “what did the agent believe at time T?”. For audit, compliance, debugging. Plain “last value wins” stores can’t. See Bi-temporal facts.

Can I write facts manually?

Yes. await agent.memory.facts.append(Fact(...)). Goes through the same supersession logic as auto-extracted facts.

Tools

How do I make the agent ask before destructive tool calls?

Mark the tool destructive and use Mode.DEFAULT:

@tool(destructive=True) def delete_x(...): ... agent = Agent( "...", permissions=StandardPermissions(mode=Mode.DEFAULT), approval_handler=my_approver, tools=[delete_x], )

See Approval handlers.

Can tools see who’s calling them?

Yes, get_run_context() returns RunContext.user_id, session_id, run_id, and metadata. Don’t plumb scope through tool signatures. See Run vs Session vs RunContext.

How do I parallelize tool calls?

You don’t have to. The agent loop dispatches every tool call in the same model turn under one anyio.create_task_group. Tool results are appended in arrival order. See ReAct.

Architectures

Which architecture should I default to?

ReAct. Swap it when:

See the cheat sheet on Architectures.

Can I write my own architecture?

Yes. Implement the Architecture protocol (three methods). See Custom architectures.

Do architectures compose?

Yes. Reflexion(base=Supervisor(workers=...)) is a team that learns. See Recursive composition.

Production

How do I survive a process crash mid-run?

Use a journaled runtime:

agent = Agent("...", runtime=SqliteRuntime("./journal.db"))

Restart, call agent.resume(session_id, prompt) with the same session_id. See Runtime.

How do I get traces / metrics into Datadog / Honeycomb?

Wire OTelTelemetry(tracer_provider=...). Every span / metric flows through OTel. For local dev, the built-in ConsoleTelemetry / FileTelemetry / InMemoryTelemetry sinks need no collector. See Telemetry.

How do I audit everything for compliance?

audit_log=FileAuditLog("./audit.jsonl", secret="prod-secret"). Every tool call and run-lifecycle entry lands with HMAC signature covering user_id. See Audit log attribution.

Should I use the bench/multi_tenant.py benchmark?

Before any release that touches the agent loop, memory, or budget. It catches isolation regressions that unit tests miss. See Load testing isolation.

Migrations

What broke in 0.2?

Two things, model= is now required, and resolver errors raise ConfigError instead of ValueError. See Migrating from 0.1 → 0.2.

What changed in 0.10?

Three opt-out points (anonymous bucket sentinel, deprecation warnings on legacy protocol shapes, bounded in-process state by default). Everything else is additive. See Upgrading 0.9 → 0.10.

Stuck

Where do I file a bug?

github.com/Anurich/LoomFlow/issues .

How do I read the source efficiently?

The architecture tour recommends a 1500-line reading path that covers the framework end-to-end. See Architecture (internals).

Last updated on