> ## Documentation Index
> Fetch the complete documentation index at: https://docs.visiqlabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Strands Agents Quickstart

> Govern a Strands Agents agent's tools with VisIQ using the published `visiq` Python wheel.

<Note>
  **Prerequisites.** A VisIQ account ([sign in](https://app.visiqlabs.com)) with a
  harness key from **Settings → Harness Keys**, **Python 3.9+**, and a model
  provider key for Strands Agents (the sample calls a hosted model — any provider
  works). Full setup and fixes: [Before you start](/quickstart#before-you-start) ·
  [Troubleshooting](/troubleshooting).
</Note>

Add action governance, retrieval governance, and a full audit trail to a
[Strands Agents](https://strandsagents.com) agent. The [`visiq`](https://pypi.org/project/visiq/)
wheel — published on PyPI, compiled from the same governance core as the
TypeScript harness — makes every decision **locally, in-process** against a
cached rule bundle. You construct a `Governor` and route each tool call through
its gate: a policy **deny** raises `ToolBlocked` and a **mask** verdict hands your
callback only the redacted arguments.

## Install

```bash theme={null}
pip install visiq "strands-agents>=0.1"
```

## Set environment variables

```bash .env theme={null}
VISIQ_API_KEY=vq_prod_...
# VISIQ_ENDPOINT defaults to https://api.visiqlabs.com — set it ONLY for
# onprem / self-hosted deployments. A bare VISIQ_API_KEY reaches the managed
# SaaS control plane, loads a bundle, and governs automatically.
VISIQ_AGENT_ID=support-bot
```

| Variable         | Required | Description                                                                                                                                                                                  |
| ---------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `VISIQ_API_KEY`  | Yes      | Harness key (`vq_prod_...` or `vq_test_...`) from **Settings → Harness Keys** in the [dashboard](https://app.visiqlabs.com).                                                                 |
| `VISIQ_ENDPOINT` | Optional | Backend base URL — defaults to `https://api.visiqlabs.com`. Set it only for onprem / self-hosted deployments (those planes live on your own network and must never default to a VisIQ host). |
| `VISIQ_AGENT_ID` | No       | Stable agent identity for rule targeting. First-seen ids are auto-provisioned in monitor mode.                                                                                               |

The `visiq` wheel reads its configuration from the **process environment** and
does not auto-load a project `.env` — export the variables (or load them
yourself) before constructing the `Governor`.

## Govern your tools

Wrap each tool body in the gate, then wire the governed callables into
Strands Agents's native tool surface (`HookProvider` / `BeforeToolCallEvent`):

```python theme={null}
import functools
import inspect

from visiq import Governor, ToolBlocked  # pip install visiq

gov = Governor(agent_id="support-bot")


def scenario_search(query: str) -> list:
    # A stand-in retrieval source — your real RAG store returns the same shape:
    # a list of {"page_content", "metadata"} dicts gate_documents can filter.
    return [
        {"page_content": f"Q3 revenue was $4.2M. (matched: {query})",
         "metadata": {"classification": "internal"}},
    ]


def governed(name, fn):
    # Route one tool body through the gate. gate_tool decides BEFORE the body
    # runs: a deny raises ToolBlocked; a mask verdict hands the callback ONLY the
    # redacted arguments, never the originals.
    sig = inspect.signature(fn)

    @functools.wraps(fn)
    def wrapper(*a, **kw):
        bound = sig.bind(*a, **kw)
        bound.apply_defaults()
        args = dict(bound.arguments)
        try:
            return gov.gate_tool(name, args, lambda effective: fn(**effective))
        except ToolBlocked as e:
            return f"[BLOCKED BY POLICY] {e.reason}"

    wrapper.__name__ = name
    return wrapper


def issue_refund(customer_id: str, amount: int) -> str:
    return f"Refunded ${amount} to {customer_id}"


def search_knowledge(query: str) -> str:
    # Retrieval governance: drop / redact documents before the model sees them.
    docs = gov.gate_documents(scenario_search(query), query=query)
    return "\n\n".join(d["page_content"] for d in docs)

from strands import Agent
from strands.hooks import HookProvider, HookRegistry
from strands.hooks.events import BeforeToolCallEvent


class VisiqHooks(HookProvider):
    def register_hooks(self, registry: HookRegistry) -> None:
        registry.add_callback(BeforeToolCallEvent, self.before_tool)

    def before_tool(self, event: BeforeToolCallEvent) -> None:
        gov.gate_tool(event.tool_use["name"],
                      dict(event.tool_use.get("input", {})),
                      lambda effective: None)


agent = Agent(tools=[issue_refund, search_knowledge], hooks=[VisiqHooks()])
gov.start(tools=[{"name": "issue_refund"}, {"name": "search_knowledge"}])
gov.flush()  # deliver buffered audit events before the process exits
```

<Note>
  **Strands Agents interception surface.** Strands emits a `BeforeToolCallEvent`; register a `HookProvider` that gates each tool call before it executes. The `governed(...)`
  wrapper preserves each tool's real signature (via `functools.wraps`) so the
  framework still builds a correct per-parameter schema.
</Note>

## What happens at runtime

New agents start in **monitor** mode (observe-only) until you flip them to
**enforce** on the **Harness → Agents** page.

* **Decisions are local.** The SDK fetches one cached rule bundle
  (`GET /rules/bundle`, ETag revalidation) and refreshes it in the background.
  Tool calls evaluate in-process; the only decision-path network call is waiting
  on a human approval.
* **Fail-open by default, loudly — strict deny is opt-in.** Real policy outcomes
  always enforce: an explicit rule **deny**, the operator kill-switch, and an
  in-core mask/redact that cannot be applied (it downgrades to deny) all block.
  But a harness-**internal** failure — an unreachable backend, the governance
  core unavailable — by **default** proceeds with a loud `[VisIQ] FAIL-OPEN`
  stderr report so a VisIQ outage never disrupts your agent (owner decision,
  2026-07-15). Set `VISIQ_FAIL_MODE=closed` to make those harness-internal
  failures **deny** instead.
* **Deny blocks the call.** `gate_tool` raises `ToolBlocked` before the body
  runs, so the tool is never executed; catch it and return the reason to the
  model.
* **Approvals pause the call.** An `approval_required` decision holds the tool
  while a human decides via Slack or Email (Microsoft Teams delivery is built server-side; its connector card is coming soon) —
  the SDK polls for up to 120 seconds (`VISIQ_HITL_TIMEOUT_MS`), then fails closed.
* **Mask proceeds, redacted.** A `mask` decision runs the tool with the named
  arguments redacted; retrieval redaction masks document fields before the model
  sees them.

<Note>
  **This is the manual per-tool `Governor` pattern.** A turnkey one-call
  `visiq()`-style plugin for Strands Agents is not yet published — you wire each tool
  through `gov.gate_tool(...)` yourself, exactly as shown above. The `visiq` wheel
  that makes the decisions is published today; the one-call auto-wrapper is coming.
</Note>

## Verify it's working

Run the agent once, then open the [dashboard](https://app.visiqlabs.com):

* **Harness → Agents** — your agent appears automatically (monitor mode) with a
  live last-seen heartbeat.
* **Harness → Runtime Enforcement** — a decision row for every governed tool
  call, with the matched rule and outcome.

## Next steps

<CardGroup cols={2}>
  <Card title="Full Quickstart" icon="rocket" href="/quickstart">
    All supported frameworks and what happens behind the scenes.
  </Card>

  <Card title="Python SDK Reference" icon="book" href="/reference-python">
    The complete `Governor` API — gate\_tool, gate\_documents, fail modes.
  </Card>
</CardGroup>
