AISquare Explainability SDK
Lightweight Python SDK for tracing, graphing, and policy auditing of AI agents. Captures execution traces from any Python agent (Agno, LangChain, plain Python) and delivers them to the AISquare Explainability Gateway.
Installation
pip install aisquare[explainability]
For Agno auto-instrumentation:
pip install aisquare[explainability,agno]
For Google ADK auto-instrumentation:
pip install "aisquare[adk]"
For pydantic-ai auto-instrumentation:
pip install "aisquare[pydantic_ai]"
That one command is complete: the extra carries the SDK's tracing runtime as well as
pydantic-ai-slim>=2.0,<3.
For the OTLP/HTTP transport:
pip install "aisquare[explainability,otlp]"
Quick start
Every trace needs an agent identity on its root span — agent_name is the key
the gateway routes traces by. Name your agent explicitly and pre-register it:
# .env: EXPLAINABILITY_GATEWAY_URL, EXPLAINABILITY_API_KEY,
# EXPLAINABILITY_AGENTS=support-bot <- pre-registers; must equal agent_name below
import aisquare.explainability as sdk
sdk.init_from_env()
with sdk.AgentRunTracer(agent_name="support-bot"):
with sdk.LLMCallTracer(model="gpt-4o", provider="openai") as llm:
... # your agent logic — nested spans inherit the run's routing identity
# IMPORTANT for short-lived scripts: flush ensures traces reach the gateway
# before the process exits. Long-running services don't need this.
sdk.flush()
Using a framework instead of manual tracers? The identity contract is the
same — the agent's explicit name becomes agent.name on the root span: Agno
Agent(name="support-bot"), LangChain metadata={"agent_name": "support-bot"},
or GovernedAgent(..., agent_name="support-bot").
What the SDK captures
The SDK collects two layers of signal:
Auto-instrumentation (zero tracing code): The AgnoAdapter installs openinference-instrumentation-agno, which automatically wraps every Agno agent run, LLM call, and tool invocation as an OTel span. The Agno Agent(name=...) becomes the trace's routing identity — always name your agents.
Manual tracers (governance-grade): Nine context-manager tracers you can add to any Python code — framework-agnostic:
| Tracer | Purpose |
|---|---|
AgentRunTracer |
Wraps a full agent run as the root span |
LLMCallTracer |
Records an LLM inference call with I/O and token counts |
ToolCallTracer |
Records a tool invocation with parameters, result, and errors |
RetrievalTracer |
Records a RAG retrieval with documents and scores |
HumanInterventionTracer |
Records a human-in-the-loop review or correction |
RoutingTracer |
Records a routing/delegation decision with selected and rejected paths |
DecisionTracer |
Records a general decision point with options, selected, and rejected paths |
PolicyGateTracer |
Records a policy-gate evaluation with the policies checked and the allow/deny outcome |
MemoryTracer |
Records memory read/write operations |
Decorators are also available: @trace_tool and @trace_retrieval.
Manual instrumentation (any framework)
Leaf tracers (LLM, tool, routing, ...) always run nested inside an
AgentRunTracer — its agent_name is stamped as agent.name on the root
span, the attribute the gateway routes the trace by. A trace with no agent
identity is rejected at ingest (409 no_agent_identity).
import aisquare.explainability as sdk
sdk.init_from_env()
with sdk.AgentRunTracer(agent_name="MyAgent", run_id="abc-123") as run:
run.set_input("User query")
with sdk.LLMCallTracer(model="gpt-4o-mini", provider="openai") as llm:
response = call_openai(...)
llm.set_input_messages([{"role": "user", "content": "..."}])
llm.set_output_messages([{"role": "assistant", "content": response}])
llm.set_token_counts(prompt=100, completion=50)
with sdk.RoutingTracer(decision_type="tool_selection") as rt:
rt.set_selected("web_search", reason="Query requires fresh data")
rt.set_rejected([{"name": "cached_search", "reason": "Cache is stale"}])
run.set_output("Agent final answer")
sdk.flush()
Transports
Traces reach the gateway one of two ways.
inbox is the default. Spans land in a local SQLite inbox and a sweeper posts them to
/v1/traces/ingest, so a process that dies mid-run loses nothing.
otlp posts OTLP/HTTP protobuf straight to /v1/traces, honouring Retry-After on a
retry, with the inbox kept as the durable fallback for a refusal that could succeed later.
sdk.init_from_env(transport="otlp") # or set EXPLAINABILITY_TRANSPORT=otlp
sdk.init() takes the same transport argument. The OTLP transport needs
pip install "aisquare[explainability,otlp]" and fails loudly without it rather than
dropping spans silently.
To wire the transport into an OpenTelemetry pipeline you already own, use
span_processors(), OTLPHttpExporter and OTLPTransportConfig instead of letting init()
build the pipeline.
Environment variables
| Variable | Description |
|---|---|
EXPLAINABILITY_GATEWAY_URL |
Gateway base URL for trace ingest |
EXPLAINABILITY_API_KEY |
API key for trace ingest |
EXPLAINABILITY_AGENTS |
Comma-separated agent names registered at init; must match your AgentRunTracer / framework agent names |
AISQUARE_AGENT_NAME |
The SDK's default agent identity, in two roles: the identity used for policy checks, and (since 1.0.6) the fallback trace identity stamped on spans that would otherwise be rootless so the trace still routes. Keep it equal to your agent_name. |
EXPLAINABILITY_TRANSPORT |
inbox (default: SQLite inbox + sweeper to /v1/traces/ingest) or otlp (OTLP/HTTP protobuf to /v1/traces, retries honouring Retry-After, partialSuccess logged, inbox as durable fallback; needs pip install "aisquare[otlp]"). Tuning: EXPLAINABILITY_OTLP_*, see docs/reference/sdk-api.md |
Multiple agents: name each agent in code (AgentRunTracer(agent_name=...)
or the framework equivalent) — that per-run name is what routes each trace.
AISQUARE_AGENT_NAME holds a single name and is a single-agent convenience
only; it cannot distinguish between agents in the same process.
Diagnostics
The SDK ships a built-in health checker:
explainability-doctor
Its agent_identity check warns when no agent identity is configured (the
classic precondition for 409 no_agent_identity ingest rejections), and its
delivery_backlog check surfaces traces stuck in the local inbox after
gateway 409s — with the remediation for each rejection code.
To prove an integration end to end, send one probe trace and read it back:
python -m aisquare.explainability.selfcheck # OTLP transport (default)
python -m aisquare.explainability.selfcheck --transport inbox
The default is the OTLP transport, so the first command needs
pip install "aisquare[explainability,otlp]". The --transport inbox form runs on the base
install.
It prints the resolved agent name, studio, workspace, run status and span count, and exits
0 when the probe arrived and routed to the expected agent, 1 on a usage error, 2 on a
rejected key, 3 when the gateway could not route the probe, 4 on a mismatch or a probe
that never became visible, 5 when the gateway is unreachable.
License
MIT
Metadata
Release files for aisquare 1.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| aisquare-1.3.0.tar.gz | 2.9 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aisquare-1.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 3.3 MB
Release files / aisquare-1.3.0.tar.gz
| Download URL | aisquare-1.3.0.tar.gz |
|---|---|
| Size | 2.9 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d1b211863744b1b845609f49cdfeb896efda9320db1512618e1627bf305f1521
|
|
BLAKE2b-256 checksum How to use checksums |
8160c61ea770c6a0e949a08f467df960d63eb5780a8a5ab1308c3802493b026a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / aisquare-1.3.0-py3-none-any.whl
| Download URL | aisquare-1.3.0-py3-none-any.whl |
|---|---|
| Size | 387.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5d579e68e5fd54cac21f32fa23b0a7a91cf929b87ccfde536f7ff0a90a1d08d4
|
|
BLAKE2b-256 checksum How to use checksums |
cc5bafc6c3f2adc71133507de819a7349263d61721d7c859f1c4a0cf55a43c33
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|