Quickstart: Python

Wire wytness_ai into your AGT-instrumented agent in about five minutes. Install, set four env values (plus the optional endpoint), call wytness_ai.init(). Every audit entry AGT chains is auto-forwarded to Wytness with PII pseudonymisation and a per-event Ed25519 envelope signed with your customer-held key.

wytness-ai 1.12.0ChangelogBuilt on AGT

1. Install the wrapper

Microsoft's Agent Governance Toolkit (AGT) is a hard dependency of wytness-ai — installed automatically. One command gets you the open-source identity, hash-chain, and policy primitives, plus the Wytness compliance layer on top.

terminal
pip install wytness-ai
Note
Need just AGT, no Wytness on your runtime? See the direct-ingest pathpip install agent-governance-toolkit and point its native CloudEvents sink at api.wytness.ai/ingest. You get the dashboard but not customer-held PII keys or Evidence Packs.

2. Generate keys

Wytness adds two customer-held primitives on top of AGT — per-event Ed25519 non-repudiation and zero-knowledge PII pseudonymisation. Both rely on keys you generate in your browser; the private halves never reach Wytness.

Open the Keys page. Click Generate on each of the Signing, Encryption, and HMAC tabs, then download each private half. The public halves auto-register against your org. The four env values you need are then shown as a copy-block on the Onboarding "Instrument" step:

  • WYTNESS_API_KEY — transport credential (server-issued, prefix wyt_api_live_…)
  • WYTNESS_SIGNING_KEY — base64 Ed25519 private key, 32 bytes decoded (always required — non-repudiation is the product promise)
  • WYTNESS_PII_PUBKEY — base64 X25519 public key, 32 bytes (optional; required together with the secret to enable PII pseudonymisation)
  • WYTNESS_PII_SECRET — base64 HMAC secret, ≥16 bytes (optional; required together with the pubkey)
Important
The signing private key and the PII HMAC secret are the roots of trust for the Wytness guarantees. Treat them like database passwords. If either leaks, an attacker can forge events that look like they came from your agent (signing key) or correlate pseudonymised events back to identities (HMAC secret). Wytness never sees either — store them in your secrets manager and load them as env vars at process start.

3. Set the environment

Set the four values as environment variables. In production use your secrets manager (Azure Key Vault, AWS Secrets Manager, Vercel env, k8s secrets, etc.); in local dev a .env file loaded by python-dotenv is fine. wytness_ai.init() reads them directly — no constructor boilerplate. If you use a .env file locally, add it to .gitignore before the first git add.

.env
WYTNESS_API_KEY=wyt_api_live_...
WYTNESS_ENDPOINT=https://api.wytness.ai
WYTNESS_SIGNING_KEY=<base64 Ed25519 private key>
WYTNESS_PII_PUBKEY=<base64 X25519 public key>
WYTNESS_PII_SECRET=<base64 HMAC secret>

4. Wire it in

Call wytness_ai.init() once at process start, before AGT's AuditLog is first instantiated. The wrapper hooks AGT's audit log cleanly so every entry it chains flows through the Wytness path — your existing AGT code stays unchanged.

agent.py
import wytness_ai
from wytness_ai import AuditLog
# Reads WYTNESS_* env vars by default. pii_fields lists the dotted paths
# whose values should be pseudonymised (emails, customer names, etc.).
wytness_ai.init(pii_fields=["customer.email", "customer.name"])
audit = AuditLog()
audit.log(
event_type="tool_invocation",
agent_did="billing-agent",
action="send_invoice",
policy_decision="allow",
data={
"customer": {"email": "alice@example.com", "name": "Alice"},
"amount": 99.99,
},
)
# Drain the in-flight batch on exit.
wytness_ai.shutdown()
Note
Auto-wire is on by default. Pass wytness_ai.init(auto_wire=False) if you'd rather attach the audit sink yourself — see the SDK API reference.

5. Run it

Run your agent normally. The wrapper batches events (default 100 or 5 seconds, whichever first), pseudonymises declared PII fields, signs each event with your Ed25519 key, and POSTs the canonical-shape envelope to api.wytness.ai/ingest. Failed POSTs are written to a local dead-letter file for replay once connectivity returns.

terminal
python agent.py

Open your app.wytness.ai dashboard within ~5 seconds of the first event to confirm it arrived. Events show the AGT identity, the AGT-supplied hash chain, and the Wytness envelope signature side-by-side.

What you'll see in the dashboard

  • Each AGT AuditEntry as a row in the Events page
  • Three-layer verdict: AGT identity (Ed25519 DID), AGT-supplied hash chain, Wytness envelope (Ed25519)
  • PII fields rendered as typed tokens (EMAIL_3c866e6f, etc.); reveal in-browser with your X25519 private key
  • Source format chip showing wytness_agt_py (Python wrapper) on every event
  • Per-agent Identity Badge pulled from AGT's DID + trust score

Full working example

Save as test_audit.py, then run with python test_audit.py.

test_audit.py
import wytness_ai
from wytness_ai import AuditLog
wytness_ai.init(pii_fields=["customer.email"])
audit = AuditLog()
audit.log(
event_type="tool_invocation",
agent_did="test-agent",
action="hello_world",
policy_decision="allow",
data={"customer": {"email": "alice@example.com"}, "message": "Hi"},
)
audit.log(
event_type="tool_invocation",
agent_did="test-agent",
action="add_numbers",
policy_decision="allow",
data={"a": 40, "b": 2, "result": 42},
)
wytness_ai.shutdown()
print("Done — check your dashboard.")

What happens under the hood

  1. AGT runs unchanged — identity check, policy evaluation, hash-chain entry
  2. The wrapper's WytnessAuditSink receives the entry, applies your pii_fields pseudonymisation
  3. The canonical-shape data payload is signed with your Ed25519 key, wrapped in a CloudEvents 1.0 envelope
  4. The batch is POSTed to api.wytness.ai/ingest as application/vnd.wytness.agt+json
  5. Failed POSTs are written to a local dead-letter file for replay once connectivity returns

Automatic LLM capture

Prefer not to write AuditLog().log(...) by hand? Point init() at your model client with wrap= and every Anthropic Messages API call (including the beta namespace) and every OpenAI Chat Completions and Responses call — streaming included — is recorded automatically — prompt, reply, reasoning (Anthropic extended thinking), latency, and token usage. Token usage needs no opt-in: on OpenAI streams the SDK requests it and hides the resulting usage-only chunk, so your loop is unchanged. Other providers are not captured. Captured calls ride the same signed, PII-pseudonymised path as manual entries.

agent.py
import anthropic
import wytness_ai
wytness_ai.init(
wrap=anthropic, # or "openai", a list, or True to auto-detect installed providers
agent_did="did:web:acme.example:support-bot",
pii_fields=["customer.email"],
)
client = anthropic.Anthropic()
client.messages.create( # recorded automatically — no audit.log() needed
model="claude-haiku-4-5",
max_tokens=512,
messages=[{"role": "user", "content": "Summarise ticket #4821"}],
)
wytness_ai.shutdown()
Note
Anthropic and OpenAI clients are supported. Streaming calls (stream=True and messages.stream(...)) are captured too — one event per call when the stream ends. If you stop a raw stream early, close it (or use a with block) — breaking out of a bare loop without closing skips capture. Helper streams are captured at context exit. OpenAI streamed usage needs no opt-in — the SDK requests it and hides the resulting usage-only chunk, so your loop is unchanged.

Using LangChain, CrewAI, or another framework?

AGT 4.1.0 ships 12 first-party framework integrations: LangChain, LangGraph, CrewAI, Haystack, MCP, Flowise, Langflow, Swarm, A2A, AI Card, plus HTTP and Django middleware. These adapters perform trust verification (gating tool execution against trust scores) — for audit emission, the wrapper picks up every entry AGT's AuditLog().log(...) chains regardless of how you wired AGT in. See the framework integrations guide for the wire-up pattern.

Next steps

Ready to audit your AI agents?

Start recording

We set no cookies. Sign-in and preferences use essential first-party browser storage only — no tracking, advertising, or third-party analytics. Privacy Policy

Quickstart: Python
TABLE OF CONTENTS