Docs/SDK/OpenClaw

Instrument an OpenClaw agent (5-minute quickstart)

Add Dobby governance + evidence to any OpenClaw agent you drive from Python (via the community openclaw-sdk) with one import and one call. No agent code changes.

TL;DR: pip install "dobby-collector[openclaw]" + instrument_openclaw(default_model=...) once at startup. Every agent.execute(...) becomes one Dobby workload_run with the model, token usage, and tool calls. Verified against openclaw-sdk==2.1.0.
How OpenClaw fits. OpenClaw runs as a standalone Node "Gateway" process. You drive its agents from Python with the community openclaw-sdk. This integration captures runs your code starts via agent.execute() / execute_stream_typed(). Runs triggered straight from a chat channel (WhatsApp / Telegram / Slack) are not observable through the SDK — there is no passive subscribe — so drive the agents you want governed from Python.

1. Get a connector + bearer token

  1. Open the Dobby dashboard and pick a tenant.
  2. Click Workloads → Connect → Dobby SDK, choose Python, and pick OpenClaw as the framework.
  3. Copy the connector_id (wc_...) and api_key (dsdk_...) — the api_key is shown ONCE, store it in your secrets manager immediately.

2. Install

pip install "dobby-collector[openclaw]"

The [openclaw] extra pulls openclaw-sdk >= 2.1.0 as a transitive dep. If you already have it in your venv, the base dobby-collector package is enough.

3. Instrument your agent

import asyncio
from openclaw_sdk import OpenClawClient
from dobby_collector import init
from dobby_collector.integrations.openclaw import instrument_openclaw

# 1. Initialise the Dobby SDK once at process startup
init(
    api_key="dsdk_...",          # from your Dobby Workloads page
    connector_id="wc_...",        # ditto
    base_url="https://dobby-ai.com",
)

# 2. Instrument openclaw-sdk ONCE — patches Agent.execute /
#    Agent.execute_stream_typed. OpenClaw does NOT surface the model on a
#    run, so thread it in via default_model (same value CostTracker needs).
instrument_openclaw(default_model="claude-sonnet-4-6")

# 3. Drive your OpenClaw agent as usual — every run is captured automatically
async def main():
    async with OpenClawClient.connect() as client:
        agent = client.get_agent("incident-triage")
        result = await agent.execute("Summarise today's incidents")
        print(result.content)
        # → one workload_run in Dobby: model + token usage + tool calls

asyncio.run(main())
  • One init + one instrument call per process. Both are idempotent — calling twice is a no-op. uninstrument_openclaw() restores the originals.
  • One run per execute(). Concurrent runs (e.g. asyncio.gather) are isolated — no shared run state.
  • Non-blocking. Events buffer in-process; a background thread ships them every 10s (or sooner). Instrumentation NEVER raises into your agent — failures are logged and swallowed.

Streaming is supported too:

# Streaming works too — the typed event stream is captured as it arrives,
# and every event still passes through to your code unchanged.
# Note: execute_stream_typed is an async generator — iterate it directly, no await.
async for ev in agent.execute_stream_typed("Draft the incident report"):
    ...  # your handling — Dobby observes content / tool_call / tool_result / done

4. Production: use env vars

# Production: read from env vars instead of hardcoding
export DOBBY_API_KEY="dsdk_..."
export DOBBY_CONNECTOR_ID="wc_..."
export DOBBY_BASE_URL="https://dobby-ai.com"

# Then in code:
from dobby_collector import init
init()   # auto-reads DOBBY_* env vars

5. Verify it worked

Within ~10 seconds of agent.execute() completing, a new run appears on Workloads → Runs. Click into it to see the per-event timeline (tool calls, model, token usage). For programmatic verification in BigQuery:

SELECT
  external_run_id,
  status,
  JSON_VALUE(metadata_json, '$.framework')    AS framework,      -- "openclaw"
  JSON_VALUE(metadata_json, '$.sdk_version')  AS sdk_version,    -- "0.6.0" or newer
  JSON_VALUE(metadata_json, '$.llm_calls[0].model') AS model,    -- your default_model
  ARRAY_LENGTH(JSON_QUERY_ARRAY(metadata_json, '$.tool_calls')) AS tool_calls,
  ARRAY_LENGTH(JSON_QUERY_ARRAY(metadata_json, '$.llm_calls'))  AS llm_calls
FROM `workload_run_payloads`
WHERE connector_id = 'wc_...'
ORDER BY created_at DESC
LIMIT 5

Expected on a successful run: framework="openclaw", model = your default_model, llm_calls = 1, and tool_calls matching the tools your agent used.

What the integration captures

openclaw-sdkDobby SdkEventBecomes…
agent.execute() beginsrun.startedOpens a workload_run (prompt = the query)
ToolCall / ToolCallEvent + ToolResultEventtool.start / tool.endtool_calls[] with name, args, output
ExecutionResult.token_usage / DoneEventllm.completionllm_calls[] with model, completion, token usage
execute() returns / raisesrun.completed / run.failedCloses the run + triggers compliance scan

Troubleshooting

The model shows as "unknown"

openclaw-sdk does not surface the model name on any event or result, so you must thread it in: instrument_openclaw(default_model="claude-sonnet-4-6"). Use the model your OpenClaw agent actually runs (its AgentConfig.llm_model).

Runs triggered from WhatsApp / Telegram don't appear

Expected. The community SDK can only stream runs it starts via execute*() — there is no passive subscribe for channel-triggered runs. Drive the runs you want governed from your instrumented Python process.

ImportError: instrument_openclaw requires openclaw-sdk>=2.1.0

Install the extra: pip install "dobby-collector[openclaw]" (or pip install openclaw-sdk).

No run shows up in Dobby

The background sender may be killed before it flushes. Call dobby_collector.shutdown() before process exit (or rely on the atexit handler on normal exit). For short-lived processes, set flush_interval_seconds=2.0 on init().

Related

Need help? Reach out at [email protected] or open a ticket from your Dobby dashboard. Include your connector_id in the subject line for faster triage.
Python Collector: OpenClaw | Dobby AI Docs