Zero-dependency observability and runtime guards for AI agents
Project description
AgentGuard SDK (Python)
Zero-dependency observability and runtime guards for AI agents. Pure Python stdlib — nothing to audit, nothing that can break. Trace reasoning steps, catch loops, enforce budgets, and replay runs deterministically.
Install
pip install agentguard47
Quickstart
from agentguard import Tracer, LoopGuard, BudgetGuard, JsonlFileSink
sink = JsonlFileSink("traces.jsonl")
tracer = Tracer(
sink=sink,
service="my-agent",
guards=[LoopGuard(max_repeats=3), BudgetGuard(max_cost_usd=5.00)],
)
with tracer.trace("agent.run") as span:
span.event("reasoning.step", data={"thought": "search docs"})
with span.span("tool.search"):
pass # your tool here
agentguard report traces.jsonl # summary table
agentguard view traces.jsonl # Gantt timeline in browser
Guards
from agentguard import LoopGuard, BudgetGuard, TimeoutGuard, FuzzyLoopGuard, RateLimitGuard
# Exact loop detection
guard = LoopGuard(max_repeats=3)
guard.check(tool_name="search", tool_args={"query": "agent loops"})
# Fuzzy loop detection (same tool, different args + A-B-A-B patterns)
fuzzy = FuzzyLoopGuard(max_tool_repeats=5, max_alternations=3)
fuzzy.check("search", {"q": "docs"})
# Budget enforcement with warning callback
budget = BudgetGuard(
max_cost_usd=5.00,
warn_at_pct=0.8,
on_warning=lambda msg: print(f"WARNING: {msg}"),
)
budget.consume(tokens=150, calls=1, cost_usd=0.02)
# Wall-clock timeout
timeout = TimeoutGuard(max_seconds=30)
timeout.start()
timeout.check()
# Rate limiting
rate = RateLimitGuard(max_calls_per_minute=60)
rate.check()
Auto-Instrumentation
from agentguard import Tracer, patch_openai, patch_anthropic
from agentguard import trace_agent, trace_tool
tracer = Tracer()
@trace_agent(tracer)
def my_agent(query):
return search(query)
@trace_tool(tracer)
def search(q):
return f"results for {q}"
# Monkey-patch OpenAI/Anthropic (safe if not installed)
patch_openai(tracer)
patch_anthropic(tracer)
Async Support
from agentguard import AsyncTracer, JsonlFileSink
from agentguard import async_trace_agent, async_trace_tool, patch_openai_async
tracer = AsyncTracer(sink=JsonlFileSink("traces.jsonl"), service="my-agent")
patch_openai_async(tracer)
@async_trace_agent(tracer)
async def my_agent(query: str) -> str:
return await search(query)
@async_trace_tool(tracer)
async def search(q: str) -> str:
return f"results for {q}"
Evaluation as Code
from agentguard import EvalSuite
result = (
EvalSuite("traces.jsonl")
.assert_no_loops()
.assert_tool_called("search", min_times=1)
.assert_budget_under(tokens=50000)
.assert_cost_under(max_cost_usd=1.00)
.assert_completes_within(30.0)
.assert_no_errors()
.assert_no_budget_warnings()
.run()
)
print(result.summary)
Replay
from agentguard import Recorder, Replayer
recorder = Recorder("runs.jsonl")
recorder.record_call("llm", {"prompt": "hi"}, {"text": "hello"})
replayer = Replayer("runs.jsonl")
resp = replayer.replay_call("llm", {"prompt": "hi"})
Production Features
# Metadata attached to every event
tracer = Tracer(
sink=sink,
metadata={"env": "production", "git_sha": "abc123"},
)
# Probabilistic sampling (emit 10% of traces)
tracer = Tracer(sink=sink, sampling_rate=0.1)
# HttpSink with gzip, retry, idempotency
from agentguard import HttpSink
sink = HttpSink(
url="https://app.agentguard47.com/api/ingest",
api_key="ag_...",
compress=True,
max_retries=3,
)
CLI
agentguard report traces.jsonl # human-readable summary
agentguard view traces.jsonl # Gantt trace viewer in browser
agentguard summarize traces.jsonl # event-level breakdown
agentguard eval traces.jsonl # run evaluation assertions
agentguard eval traces.jsonl --ci # CI mode (stricter checks, exit code)
Export
from agentguard.export import export_json, export_csv
export_json("traces.jsonl", "traces.json")
export_csv("traces.jsonl", "traces.csv")
Benchmark
python -m agentguard.bench
Migration Guide (v0.5 → v1.0)
| v0.5 | v1.0 |
|---|---|
from agentguard.tracing import JsonlFileSink |
from agentguard import JsonlFileSink |
from agentguard.instrument import trace_agent |
from agentguard import trace_agent |
from agentguard.instrument import patch_openai |
from agentguard import patch_openai |
budget.record_tokens(150) |
budget.consume(tokens=150) |
| (no async support) | AsyncTracer, async_trace_agent, patch_openai_async |
| (no fuzzy loops) | FuzzyLoopGuard, RateLimitGuard |
| (no budget warnings) | BudgetGuard(warn_at_pct=0.8, on_warning=...) |
| (no sampling) | Tracer(sampling_rate=0.1) |
| (no metadata) | Tracer(metadata={"env": "prod"}) |
| (no gzip) | HttpSink(compress=True) |
Links
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
agentguard47-0.7.0.tar.gz
(68.0 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agentguard47-0.7.0.tar.gz.
File metadata
- Download URL: agentguard47-0.7.0.tar.gz
- Upload date:
- Size: 68.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
59c5e4236788f88cfffdadf08ed2473477add70b28f158df64a7d9f23328e45d
|
|
| MD5 |
702593da95b60102b38d12d5975686df
|
|
| BLAKE2b-256 |
44809484af554a723f7da3e6182ff471240ddc1846e9a76cec45c8a3f2894b53
|
File details
Details for the file agentguard47-0.7.0-py3-none-any.whl.
File metadata
- Download URL: agentguard47-0.7.0-py3-none-any.whl
- Upload date:
- Size: 43.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
77a15be3a37df197385717f32bde15a1a183d3ca7015e86d8fa244d0a25e1f59
|
|
| MD5 |
4c624bd428e0b004e2ba33049730bf9b
|
|
| BLAKE2b-256 |
0df6154368ab0ed731b0bbd618cd624f2327d1bad0089c3bcf1e8ef70975c3ca
|