Back to blog

Observe Mode

Apr 13, 2026

Adding governance to a live agent pipeline is risky. You write a policy, deploy a callback handler, and hope the receipts and alerts match what you meant. If they don't, you find out in production, usually from an alert at 2am or an auditor asking questions you can't answer.

Asqav's framework callback handlers have an observe mode. With observe=True the handler signs nothing. It logs each action it sees and the context it would send for signing, so you can read what your agent does before you turn signing on.

from asqav.extras.langchain import AsqavCallbackHandler

# Observe mode: logs what it would sign and signs nothing
handler = AsqavCallbackHandler(observe=True)

# Attach to your agent as usual
agent.invoke(
    "Delete all inactive user accounts",
    config={"callbacks": [handler]},
)

The handler sees every tool call that goes through LangChain's callback path, plus chain events. In observe mode it writes each one to the asqav logger at INFO level instead of signing it. It does not check your policies or block the tool, so the agent runs as it would without Asqav. Creating the handler still calls the Asqav API to create or fetch its agent, so it needs an API key. The log includes tool input, so use test data.

Semantic action patterns

Observe mode pairs well with semantic action types. Instead of signing a generic tool call, you label the action with what it actually means.

agent = asqav.Agent.create("db-maintenance")

# Tag the action with a semantic pattern
sig = agent.sign("data:delete", context={
    "query": "DROP TABLE inactive_users",
    "target": "production-db"
})

Labels like data:delete, api:transfer or file:write let you write policies against categories of behavior instead of individual tool names. A policy with the action pattern data:* still applies when a new tool signs under the same label.

Preflight explanations

Once you know what the governance layer will do, you probably want to know why. The preflight endpoint returns plain-English explanations alongside the allow/deny decision.

check = agent.preflight("sql-destructive")
if not check.cleared:
    for reason in check.reasons:
        print(reason)
    # "Policy 'prod-db-protection' blocks sql-destructive actions
    #  outside the maintenance window (Sat 02:00-06:00 UTC)"

The explanation includes the policy name, the rule that matched, and the specific condition that triggered it. No more guessing which of your twelve policies blocked the action.

A safer path to production

Run your agent with observe=True and read the log. When it shows the actions and context you expect, drop the flag and the handler starts signing. Turning signing on does not make a policy stop the tool: when a policy refuses a signature, the handler logs the refusal and the call runs without a receipt. If a tool must not run unsigned, put @asqav.sign on its function (see Enforcement).

Install or upgrade:

pip install --upgrade asqav

Full docs at asqav.com/docs. Source on GitHub. If something doesn't work the way you expect, open an issue.

Stay ahead of AI compliance

Get practical insights on AI agent security and compliance obligations. No spam, unsubscribe anytime.