Documentation · Getting started · Blog
You bring the agent. langclaw is the production runtime around it - declared on one app object, wired for you:
- RBAC - declarative, three-axis (tools · subagents · workflows)
- Durable workflows - typed, multi-step, resumable
- Scheduled jobs - cron through the same agent pipeline
- Persistent memory - conversation state across sessions
- Subagent delegation - specialists in isolated contexts
- Pluggable tool ecosystem - bring any LangChain tool
Built on the LangChain ecosystem and deepagents.
Like FastAPI, but for agents - define everything on one app object (tools, roles, subagents, workflows, channels), and langclaw handles the wiring, middleware, message routing, and state persistence. Bring your own LangChain tools, models, and agents.
Think OpenClaw or Hermes - a personal AI that:
- lives in your chat apps
- remembers across sessions
- runs jobs on a schedule
- respects who's allowed to do what
- Framework, not a fork:
uv add langclawand build on top of it - like Flask/FastAPI for agentic systems. No repo cloning, no boilerplate. - Multi-channel from day one: Telegram, Discord, Slack, WebSocket out of the box. Add custom channels with a single
app.add_channel()call. - Declarative RBAC, three axes:
app.role("analyst", tools=["*"], subagents=["researcher"], workflows=["digest"])- gate tools, subagents, and workflows per role. Subagents and workflows are default-deny; permissions run as middleware before the LLM sees anything. - Subagent delegation: Register specialist subagents that run in isolated contexts. The main agent delegates via a built-in
tasktool; results flow back cleanly or stream directly to the channel. - Durable workflows: Register typed, multi-step routines with
@app.workflow(). Bounded parallelism, live progress, and crash-resume are handled for you - and the agent, a user, or a cron job can run them. (jump ↓) - Scheduled jobs: Ask the agent to schedule recurring tasks, or fire a saved workflow on cron - both publish to the same message bus and flow through the same pipeline as user messages.
- Guardrails middleware: Content filtering, PII redaction, and rate limiting run as composable middleware before every LLM call.
- Pluggable everything: Message bus (asyncio / RabbitMQ / Kafka), checkpointer (SQLite / Postgres), agent filesystem/shell backend (local-shell / filesystem / state / store), LLM providers - swap backends via config, not code changes.
- Not a wrapper: langclaw compiles down to a real LangGraph
CompiledStateGraph. Bring any LangChain tool, model, or integration.
from langclaw import Langclaw
app = Langclaw()
@app.tool()
async def greet(name: str) -> str:
"""Say hello to someone."""
return f"Hello, {name}!"
if __name__ == "__main__":
app.run()That's it. Langclaw wires up the message bus, checkpointer, channels (from your .env), and middleware - then starts listening.
Here's a research assistant with custom tools, subagent delegation, RBAC, lifecycle hooks, and a slash command - all on one app object:
from langclaw import Langclaw
from langclaw.gateway.commands import CommandContext
app = Langclaw(
system_prompt=(
"## Research Assistant\n"
"You are a financial research assistant.\n"
"Check stock prices before answering. For complex questions, "
"delegate to the deep-researcher subagent."
),
)
# -- Custom tool: stock price lookup ------------------------------------------
@app.tool()
async def get_stock_price(ticker: str) -> dict:
"""Fetch the latest quote for a US stock ticker."""
... # httpx call to Yahoo Finance
return {"ticker": ticker, "price": 182.52, "change_pct": "+1.23%"}
# -- Subagent: deep research in isolated context ------------------------------
app.subagent(
"deep-researcher",
description="Multi-step research using web search and synthesis",
system_prompt="You are a thorough researcher. Search, synthesise, cite.",
tools=["web_search", "web_fetch"],
output="channel", # stream results directly to the user
)
# -- RBAC: who can use what ---------------------------------------------------
app.role("analyst", tools=["*"])
app.role("free", tools=["web_search"])
# -- Command: bypasses the LLM entirely --------------------------------------
@app.command("watchlist", description="show watchlist prices (no AI)")
async def watchlist_cmd(ctx: CommandContext) -> str:
return "AAPL: $182.52 | MSFT: $441.20 | NVDA: $135.80"
# -- Lifecycle hooks ----------------------------------------------------------
@app.on_startup
async def setup():
... # open DB connections, HTTP clients, etc.
@app.on_shutdown
async def teardown():
... # clean up resources
if __name__ == "__main__":
app.run()See examples/ for complete, runnable versions.
A workflow is a durable, typed, multi-step routine you register once and run many ways - the agent calls it as a workflow_<name> tool, a user runs /workflows run, or a cron job fires it. You write the control flow; langclaw validates input and output, bounds parallelism, streams progress to the channel, and resumes after a crash.
from pydantic import BaseModel
from langclaw import Langclaw
app = Langclaw()
class Brief(BaseModel):
topic: str
angles: list[str] = ["overview", "risks", "recent news"]
@app.workflow("research", input=Brief, max_concurrency=4,
description="Search several angles in parallel, then synthesize.")
async def research(ctx, inp: Brief) -> str:
ctx.phase("gather") # named progress, shown live in-channel
def search(angle: str): # one search per angle...
return lambda c: c.tool("web_search", query=f"{inp.topic} {angle}")
findings = await ctx.parallel([search(a) for a in inp.angles]) # ...run in parallel
ctx.phase("synthesize")
return "\n\n".join(f"## {a}\n{r}" for a, r in zip(inp.angles, findings))A workflow body composes these kinds of step (each becomes a memoized, resumable unit once LANGCLAW__WORKFLOWS__DURABLE_STEPS=true - off by default):
ctx.tool(name, **kw)- call a registered tool (deterministic capability)ctx.subagent(type, prompt)- delegate to a subagent (its own tools, isolated context)ctx.llm(prompt, schema=Model)- one model call, no tools, no agent loop - for one-shot judgment (classify / score / extract). With a Pydanticschemayou get a validated object back from a single structured call; without it, plain text. Neither Claude Code nor deepagents exposes a bare model-call step - this is langclaw's, and like every step it's memoized and crash-resumable whendurable_stepsis enabled.ctx.parallel([...])- fan the above out concurrently, bounded bymax_concurrency
LANGCLAW__WORKFLOWS__ENABLED=true # workflows are off by defaultNow the agent sees a workflow_research tool, and users can drive it directly:
/workflows run research {"topic": "solid-state batteries"}
/workflows runs # recent runs from the journal
/workflows status <run_id>
Three ways to author the body:
| Mode | Who writes it | Reach for it when |
|---|---|---|
python (recommended) |
You | You want typed, testable, deterministic control flow - ctx.parallel / ctx.phase / ctx.tool / ctx.subagent / ctx.llm. |
saved |
The agent, once | The agent invents a repeatable job: it writes a sandboxed JS body via the eval interpreter, saves it to workflows/<name>.js, and it loads as a tool. |
llm_authored (experimental) |
The model, per run | Low-stakes, supervised work where you declare only the typed contract and let the model write the body fresh each run. |
Put it on a schedule. A saved workflow fired by cron re-runs with zero LLM cost - the frozen body executes verbatim - and delivers to the channel that scheduled it (e.g. a daily digest to your Telegram). Saved + cron is validated end-to-end; see examples/workflow_research.py and examples/hn_digest_eval.py.
How
savedandllm_authoredactually run - programmatic tool calling (PTC):
- The model writes a small JS program in the
langchain-quickjscode-interpreter sandbox.- It calls tools, loops, branches, retries - makes one-shot model calls via
tools.llm({ prompt })(the JS sibling ofctx.llm), and can hand off to deepagents subagents viatools.task.- Same approach as Claude Code's dynamic workflows. The honest difference is scale: langclaw's sandbox is tuned for scripted control flow and a few subagents, not the tens-to-hundreds-of-agents parallel fan-out Claude's targets.
- Needs
uv add "langclaw[interpreter]"; capability-scoped to the tools you allow viauses_tools- no filesystem, network, or ambient host APIs.
Every message - whether from a user or a cron job - follows the same path:
Channel (Telegram / Discord / Slack / WebSocket)
│
├── /command ──▶ CommandRouter ──▶ instant response (no LLM)
│
└── message ──▶ InboundMessage ──▶ Message Bus
│
GatewayManager
│
SessionManager ──▶ Checkpointer
│
Middleware Pipeline
(RBAC → Rate Limit → Content Filter → PII)
│
LangGraph Agent ──▶ Tools / Subagents
│
OutboundMessage ──▶ Channel
Cron jobs publish InboundMessage to the same bus, flowing through the identical pipeline. Commands bypass everything - they're fast system operations handled before the bus.
uv add langclawWith channel and backend extras:
uv add "langclaw[telegram,postgres,rabbitmq]"
# Or install all common extras (everything except telegram-e2e):
uv add "langclaw[all]"Available extras: telegram, telegram-e2e, discord, slack, matrix, websocket, postgres, rabbitmq, kafka, mcp, search, gmail, interpreter. all bundles every extra except telegram-e2e.
-
Install the framework and a channel plugin (e.g., Telegram):
uv add "langclaw[telegram]" -
Set your environment variables in a
.envfile:OPENAI_API_KEY=sk-... # plain provider env var, no LANGCLAW__ prefix LANGCLAW__CHANNELS__TELEGRAM__TOKEN=123456:ABC-DEF...
-
Run your agent (Choose one option):
Option 1: Using the CLI (Recommended for new projects)
langclaw init langclaw gateway
Option 2: Programmatically (For custom setups) Create an
app.pyfile:from langclaw import Langclaw app = Langclaw( system_prompt="You are a friendly assistant. Keep answers short and helpful." ) @app.tool() async def reverse_text(text: str) -> str: """Reverse the given text. Useful for word games and puzzles.""" return text[::-1] if __name__ == "__main__": app.run()
Then run it:
python app.py
| Package | Purpose |
|---|---|
app.py |
Langclaw class - the developer's primary interface (decorators, lifecycle, wiring) |
agents/ |
LangGraph agent construction, tool wiring, subagent delegation, pluggable filesystem/shell backend (backend.py) |
gateway/ |
Channel orchestration (GatewayManager), command routing, message dispatch |
bus/ |
Message bus abstraction - asyncio (dev), RabbitMQ / Kafka (prod) |
middleware/ |
Request pipeline: RBAC, rate limit, content filter, PII redaction |
workflows/ |
Durable, typed, multi-step routines - registry, runtime, saved-workflow store, crash-resume |
interpreter/ |
Opt-in sandboxed code interpreter (RLM) - PTC tool allowlist + middleware |
config/ |
Pydantic Settings with LANGCLAW__ env prefix (nested __ delimiter) |
cron/ |
Scheduled jobs via APScheduler v4 |
session/ |
Maps (channel, user, context) to LangGraph thread IDs |
checkpointer/ |
Conversation state persistence - SQLite (dev), Postgres (prod) |
providers/ |
LLM model resolution via init_chat_model |
cli/ |
Typer CLI: langclaw init, langclaw gateway, langclaw agent, langclaw probe, langclaw cron, langclaw status |
[!note] Status: designed, not yet on
mainThe groundwork - per-request, re-rootable agent backends - is inmain. The layered prompt assembly lives onfeat/business-workspaces-layered-promptand isn't merged yet. Full design: docs/BUSINESS_WORKSPACES.md.
One agent, many employees. A single marketing agent should serve every marketer from one shared playbook, while each person keeps their own private memory. That's two layers with two owners:
- Org layer - shared and authoritative: brand voice, operating rules, compliance. The agent reads it but can't edit it from below.
- Employee layer - personal memory and preferences, owned by that one person and invisible to everyone else.
What the design gives you:
- Org is immutable from below - an employee's agent reads org rules but can't rewrite them through tool use.
- Promotion path - a good personal tactic can be proposed up into the org playbook, reviewed, then it reaches everyone with no fan-out.
- Privacy by construction - one person's memory never enters another's prompt (scoped per
(channel, user_id)). - Zero disruption - off by default; today's single-workspace setups keep working unchanged.
Target layout - one agent, layered:
workspace/marketing/
├── org/ # shared, authoritative, agent-read-only (persona, playbook, policy)
├── teams/<id>/ # optional middle tier
└── users/<id>/ # private per-employee memory, owner read/write
This is the open-core path to running claw-like agents across a team or a whole company.
- Workflows - durable, typed, multi-step routines (
@app.workflow()); python / saved / llm-authored modes; runnable by the agent, a user (/workflows), or cron - Three-axis RBAC -
app.role(tools=…, subagents=…, workflows=…); subagents and workflows are default-deny; enforced as middleware before the LLM - Named agents -
app.agent()registers independent agents (own model, tools, and history); switch with/agent(auto-routing still planned) - Code interpreter (opt-in) - sandboxed JS
evalso the agent can script tool loops, branches, and fan-out over an allowlist - Subagent delegation -
app.subagent()with isolated context and per-subagent model/tool sets; can stream results to the originating channel (output="channel") - Guardrails middleware -
ContentFilterMiddleware(keyword/regex) andPIIMiddleware(redaction) in the built-in stack - Heartbeat / proactive wake-up - event-driven condition checks that fire messages through the agent pipeline
- Pluggable agent backend - deepagents filesystem/shell backend selectable via
config.agents.backend(local-shell / filesystem / state / store); defaults tolocal_shellfor theexecutetool
- Business workspaces - one agent for many employees: a shared, immutable org layer + private per-employee memory, with a promotion path for good tactics. Design final; backend groundwork in
main, layered read-path on a branch (details ↑)
- Multi-agent auto-routing - route to a named agent automatically by channel or detected intent
- More channels - WhatsApp, REST API gateway
- Plugin ecosystem -
langclaw-*tool packs installable viauv add - Observability - OpenTelemetry tracing for the full message flow
git clone https://github.com/tisu19021997/langclaw.git
cd langclaw
uv sync --group dev
uv run pytest tests/ -v
uv run ruff check . && uv run ruff format .MIT - see LICENSE for details.
