Langclaw¶
langclaw.Langclaw(config=None, *, system_prompt=None, context_schema=None, enable_interpreter=False, backend=None)
¶
Central application object for building multi-channel agent systems.
Wraps :func:~langclaw.agents.builder.create_claw_agent and the gateway
infrastructure, exposing a declarative API for tool/role/channel/middleware
registration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
LangclawConfig | None
|
Pre-built configuration. When |
None
|
system_prompt
|
str | None
|
Additional instructions appended after the base
|
None
|
context_schema
|
type[LangclawContext] | None
|
Custom context schema to use for the agent. If omitted, uses the default LangclawContext. |
None
|
enable_interpreter
|
bool
|
Opt into the sandboxed code interpreter (RLM)
programmatically — equivalent to setting
|
False
|
Source code in langclaw/app.py
tool(*, roles=None)
¶
Decorator to register a function as a LangChain tool.
If the decorated function is not already a BaseTool, it is
wrapped with langchain_core.tools.tool.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
roles
|
list[str] | None
|
Optional list of role names that should be granted access to this tool. When provided, the corresponding roles are created/updated in the RBAC config. |
None
|
Returns:
| Type | Description |
|---|---|
Callable
|
A decorator that registers the tool and returns it. |
Source code in langclaw/app.py
command(name, *, description='')
¶
Decorator to register a custom bot command.
Commands bypass the LLM and message bus — they are fast system
operations handled directly by the :class:CommandRouter.
The decorated function must accept a single
:class:~langclaw.gateway.commands.CommandContext argument and
return a str response.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Command name without the leading |
required |
description
|
str
|
Short help text shown by |
''
|
Returns:
| Type | Description |
|---|---|
Callable
|
A decorator that registers the command and returns the |
Callable
|
original function. |
Example::
@app.command("ping", description="check if bot is alive")
async def ping(ctx: CommandContext) -> str:
return "Pong!"
Source code in langclaw/app.py
role(name, *, tools=None, subagents=None, workflows=None)
¶
Define or update a permission role.
If the role already exists (from config or a prior call), each axis is merged independently (order-stable dedupe). Registering any role automatically enables the permissions system.
Three independent RBAC axes:
tools— default-deny for unknown roles;["*"]grants all.subagents— default-deny; subagent types reachable via thetasktool.["*"]allows every registered one.workflows— default-deny; workflows reachable as theworkflow_<name>tool.["*"]allows all.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Role identifier (e.g. |
required |
tools
|
list[str] | None
|
Tool names this role may invoke. Use |
None
|
subagents
|
list[str] | None
|
Subagent types this role may delegate to. |
None
|
workflows
|
list[str] | None
|
Workflow names this role may invoke. |
None
|
Source code in langclaw/app.py
agent(name, *, description, display_name=None, system_prompt=None, tools=None, model=None)
¶
Register a named agent that users can switch to via /switch <name>.
Named agents are fully independent agent instances built with the same
:func:~langclaw.agents.builder.create_claw_agent factory as the main
agent. Each named agent:
- Gets its own isolated LangGraph conversation thread
(
context_id = "agent:<name>"), so history never bleeds across modes. - Shares the same checkpointer backend as the main agent.
- Can use a different system prompt, tool set, or model.
Users switch between agents via the built-in /agent <name> command,
and can return to the main agent with /agent default.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Unique identifier used with |
required |
description
|
str
|
Short description shown by |
required |
display_name
|
str | None
|
Optional human-facing name for this agent. Injected
into the system prompt so the model knows its own
name, and shown alongside the routing key in
|
None
|
system_prompt
|
str | None
|
System prompt for this agent. When |
None
|
tools
|
list[Any] | None
|
Explicit list of tool instances for this agent.
|
None
|
model
|
str | BaseChatModel | None
|
Override the default model. Accepts
|
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Example::
app.agent(
"researcher",
description="Deep research mode with web tools",
system_prompt="You are a meticulous researcher. Always cite sources.",
tools=[web_search, web_fetch],
model="openai:gpt-4.1",
)
Source code in langclaw/app.py
subagent(name, *, description, graph=None, system_prompt=None, tools=None, model=None, roles=None, output='main_agent')
¶
Register a subagent that the main agent can delegate tasks to.
Subagents are invoked by the main agent via the task tool
provided by deepagents. Each subagent runs in an isolated
context and returns a single result.
There are three ways to define what the subagent does:
-
Declarative — pass
system_prompt(and optionallytools,model). Langclaw builds the agent, resolves tool names, and injects its middleware. -
Pre-built graph — pass a
RunnableorCompiledStateGraphviagraph. Langclaw wraps it into a deepagentsCompiledSubAgentand passes it through as-is. The runnable's state schema must include amessageskey. -
deepagents dict — pass a
SubAgentorCompiledSubAgentTypedDict viagraph. ForSubAgentdicts, Langclaw prepends its middleware (channel context, RBAC).CompiledSubAgentdicts (with arunnablekey) are passed through unchanged.
When graph is None, system_prompt is required.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Unique identifier used by the main agent when
calling the |
required |
description
|
str
|
What this subagent does. Be specific — the main agent uses this to decide when to delegate. |
required |
graph
|
Runnable | dict[str, Any] | None
|
A pre-built |
None
|
system_prompt
|
str | None
|
Instructions for the subagent (declarative mode).
Required when |
None
|
tools
|
list[str] | None
|
Tool names this subagent may use (declarative
mode only). Resolved at build time against all
registered tools. |
None
|
model
|
str | BaseChatModel | None
|
Override the main agent's model (declarative mode
only). Accepts |
None
|
roles
|
list[str] | None
|
Reserved for future RBAC scoping of which user roles may trigger this subagent. |
None
|
output
|
Literal['main_agent', 'channel']
|
|
'main_agent'
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If neither |
Example::
# Declarative — Langclaw builds the agent
app.subagent(
"researcher",
description="Researches topics using web search",
system_prompt="You are a thorough researcher...",
tools=["web_search", "web_fetch"],
model="openai:gpt-4.1",
)
# Pre-built LangGraph graph
my_graph = create_agent("openai:gpt-4.1", tools=[...])
app.subagent(
"my-graph",
description="Custom LangGraph pipeline",
graph=my_graph,
)
# deepagents SubAgent dict
app.subagent(
"analyst",
description="Financial analyst",
graph={
"system_prompt": "Analyze data.",
"tools": [my_tool],
"model": "openai:gpt-4.1",
},
)
Source code in langclaw/app.py
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 | |
workflow(name, *, description='', input=None, output=None, mode='python', max_steps=None, max_concurrency=8, timeout_s=None, uses_tools=None)
¶
Register an operator-authored workflow via decorator (issue #38).
A workflow is an async def (ctx, inp) -> output that orchestrates
multi-step agent work. Each step (ctx.agent / ctx.subagent /
ctx.tool / ctx.parallel) round-trips through the same bus →
gateway pipeline as an ordinary message, so RBAC, rate limiting, channel
context, and checkpointing are inherited. Unlike the interpreter
(eval), a workflow is durable, typed, named, and RBAC-gated.
Workflows are inert unless config.workflows.enabled is True.
Example::
class Brief(BaseModel):
topic: str
@app.workflow("research", input=Brief, description="Deep research")
async def research(ctx, inp: Brief) -> str:
ctx.phase("gather")
facts = await ctx.parallel([
lambda c: c.subagent("researcher", f"Find facts on {inp.topic}"),
lambda c: c.subagent("researcher", f"Find risks of {inp.topic}"),
])
ctx.phase("synthesize")
return await ctx.agent("writer", f"Summarize: {facts}")
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Unique workflow handle (invoked as |
required |
description
|
str
|
Becomes the tool description the LLM reads to
decide when to call this workflow — exactly like a
|
''
|
input
|
type | None
|
Optional Pydantic model validating the run input. Its
fields become the tool's argument schema, so add
|
None
|
output
|
type | None
|
Optional Pydantic model validating the run output. |
None
|
mode
|
str
|
|
'python'
|
max_steps
|
int | None
|
Per-workflow step budget ( |
None
|
max_concurrency
|
int
|
Fan-out width for |
8
|
timeout_s
|
float | None
|
Per-run wall-clock budget in seconds. |
None
|
uses_tools
|
list[str] | None
|
Tool names this workflow declares it needs. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the name collides with an existing workflow, tool, subagent, named agent, or command. |
Source code in langclaw/app.py
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 | |
add_channel(channel)
¶
add_middleware(middleware)
¶
on_startup(fn)
¶
on_shutdown(fn)
¶
run(*, probe=False, probe_port=None)
¶
Start the multi-channel gateway (blocking).
Wires up the message bus, checkpointer, channels, cron manager,
and agent, then runs GatewayManager until cancelled.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
probe
|
bool
|
When True, run a WebSocket-only gateway with every other channel disabled regardless of config. This isolates the surface for the probe harness so test traffic never reaches a real Telegram/Discord chat. Applied at the channel-assembly seam — the user's config file is never mutated. |
False
|
probe_port
|
int | None
|
Override the WebSocket port in probe mode (defaults to the
configured |
None
|