Lightweight agentic loop detector and safety monitor. No LLM required.
Project description
agentguard
Lightweight agentic loop detector and safety monitor. Zero required dependencies.
The problem
LLM agents get stuck. A loop, a dangerous command, a runaway budget, and your agent keeps going. Existing guardrail frameworks are heavy, opinionated, and require you to rewrite your agent around them.
agentguard is different: one method call, drop-in anywhere, no LLM required.
Quick start
pip install nakata-agentguard
from agentguard import AgentGuard, Action
guard = AgentGuard()
# In your tool execution loop:
report = guard.record("bash", {"cmd": "git status"})
if report.action == Action.HALT:
raise RuntimeError(report.reason)
What it catches
Loop detection (5 modes)
from agentguard import AgentGuard, GuardConfig
guard = AgentGuard(GuardConfig(exact_threshold=3))
# Exact: same (tool, args) repeated N times
for _ in range(3):
guard.record("bash", {"cmd": "git status"})
# report.action == Action.HALT, "loop detected ('bash:...' called 3x)"
# Also catches:
# Near-duplicate: args differ only in whitespace/numbers
# Error-loop: tool returning errors repeatedly
# Pattern: ABCABC repeating sequence
# Stall: same tool called N times with any args
Dangerous pattern detection (110+ patterns)
No LLM required, pure regex across 9 categories:
| Category | Examples |
|---|---|
| DESTRUCTIVE | rm -rf, DROP TABLE, docker system prune, terraform destroy |
| DATA_WIPE | dd if=, mkfs, > /dev/sda |
| PRIVILEGE_ESCALATION | sudo, chmod 777, docker run --privileged, nsenter |
| EXFILTRATION | reverse shells, curl -d, /dev/tcp/, Metasploit payloads |
| CODE_INJECTION | eval $(), shell=True, pickle.loads, curl | bash, SQL injection |
| SECRETS | SSH keys, AWS/GCloud creds, OpenAI keys, Stripe live keys, kubeconfig |
| NETWORK | nmap, masscan, ettercap, C2 frameworks, crypto miners |
| DESTRUCTIVE (cloud) | aws s3 rm --recursive, gcloud ... delete, kubectl delete --all |
| DESTRUCTIVE (git) | git push --force, git reset --hard, git filter-branch |
report = guard.record("bash", {"cmd": "rm -rf /"})
# report.action == Action.HALT
# report.dangers[0].category == DangerCategory.DESTRUCTIVE
# report.dangers[0].severity == 10
Budget monitoring
guard = AgentGuard(GuardConfig(token_limit=100_000, cost_limit_usd=5.0))
report = guard.record("llm", {"prompt": "..."}, tokens=1500, cost_usd=0.002)
# report.budget.token_pct → 0.015
# report.budget.is_warning → False (< 80%)
# report.budget.is_exceeded → False
Output monitoring
Catches the "464MB cat" class of bugs, tool output that is too large, binary, or stuck in a repeated-line loop.
guard = AgentGuard(GuardConfig(output_max_bytes=512_000, output_warn_bytes=100_000))
report = guard.record("bash", {"cmd": "cat bigfile"}, output="A" * 600_000)
# report.action == Action.HALT
Rate limiting
guard = AgentGuard(GuardConfig(rate_halt_cps=25.0, rate_warn_cps=10.0))
# 26 calls/second → Action.HALT
Configuration
from agentguard import AgentGuard, GuardConfig
guard = AgentGuard(GuardConfig(
# Loop
exact_threshold=3,
stall_threshold=5,
halt_on_loop=True,
# Danger
halt_on_severity=9,
warn_on_severity=6,
# Budget
token_limit=50_000,
cost_limit_usd=2.0,
# Rate
rate_halt_cps=25.0,
rate_warn_cps=10.0,
# Custom rules file (TOML or JSON)
rules_file="agentguard.toml",
))
Config file
Generate a starter config:
agentguard init # creates agentguard.toml
agentguard init --output my.toml # custom path
# agentguard.toml
[guard]
halt_on_severity = 9
warn_on_severity = 6
token_limit = 100000
[[patterns]]
pattern = "my_internal_secret_func"
category = "secrets"
severity = 8
description = "internal secret function"
[[allowlist]]
tool = "bash"
pattern = "ls\\s+-la"
reason = "safe directory listing"
Load it:
guard = AgentGuard(GuardConfig(rules_file="agentguard.toml"))
Allowlist
Skip danger checks for known-safe patterns:
from agentguard import AgentGuard, GuardConfig
from agentguard.allowlist import Allowlist
guard = AgentGuard(GuardConfig(
allowlist=Allowlist.from_list([
{"tool": "bash", "pattern": r"ls\s+-la", "reason": "safe listing"},
])
))
CLI commands
# Start stateful daemon (enables loop detection across calls)
agentguard serve --port 7420 --halt-severity 9 --token-limit 100000
agentguard serve --webhook-url https://hooks.example.com/alert # POST on halt/warn
# Check status / reset session
agentguard status
agentguard reset
# One-shot explain (diagnostic output)
agentguard explain bash '{"cmd": "rm -rf /"}'
agentguard explain bash '{"cmd": "sudo apt update"}'
# Analyse a saved session
agentguard audit session.json
# Generate config file
agentguard init
# Claude Code hooks (PreToolUse + PostToolUse)
agentguard hooks # print JSON
agentguard hooks --install # write to ~/.claude/hooks.json
Metis integration
agentguard's check command is designed as a Metis PostToolUse hook.
agentguard hooks --install # one-time setup
Or add to ~/.metis/hooks.toml manually:
[[hooks]]
event = "SessionStart"
command = "agentguard serve --port 7420"
background = true
[[hooks]]
event = "PreToolUse"
command = "agentguard check"
[[hooks]]
event = "PostToolUse"
command = "agentguard check"
The daemon maintains session state across calls, enabling loop detection. Without the daemon, agentguard check runs stateless (danger + budget only).
HTTP daemon
pip install "nakata-agentguard[serve]"
agentguard serve --port 7420
# Record a tool call
curl -s -X POST http://localhost:7420/record \
-H 'Content-Type: application/json' \
-d '{"tool":"bash","args":{"cmd":"ls -la"}}'
# Status
curl http://localhost:7420/status
# Multi-session
curl http://localhost:7420/status?session=agent-1
# Prometheus metrics
curl http://localhost:7420/metrics
# Health
curl http://localhost:7420/health
# Reset
curl -X POST http://localhost:7420/reset
curl -X POST "http://localhost:7420/reset?session=all"
Webhook
agentguard serve --webhook-url https://hooks.example.com/agentguard
The daemon POSTs {"action":"halt","reason":"...","session":"default","ts":1234567890} to the URL on every halt or warn event. Webhook failures never block the agent.
Prometheus metrics
GET /metrics returns Prometheus text exposition:
agentguard_uptime_seconds 42.1
agentguard_active_sessions 3
agentguard_records_total 158
agentguard_tool_calls_total 158
agentguard_loop_events_total 2
agentguard_danger_events_total 4
agentguard_total_tokens 45200
agentguard_total_cost_usd 0.091
Session snapshot
# Save
guard.save("session.json")
# Load and audit
guard2 = AgentGuard.load("session.json")
print(guard2.stats())
# CLI audit
# agentguard audit session.json
nakata cluster
agentguard is part of the nakata AI reliability cluster:
- halluguard, reverse-RAG hallucination detector
- adaptmem, domain-adapted retrieval memory
- claimcheck, end-to-end claim verification pipeline
- agentguard, agentic loop and safety monitor
License
MIT
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
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 nakata_agentguard-0.4.0.tar.gz.
File metadata
- Download URL: nakata_agentguard-0.4.0.tar.gz
- Upload date:
- Size: 38.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5887ca0dc072388b030eeea09cfd54212a3bf5105aaecc8a33799500b6ad7f96
|
|
| MD5 |
504251d13c4c165c9dbfb4fcf2cf80e8
|
|
| BLAKE2b-256 |
253ad62f727ea0c82c7e1905121baa9c9f84683a98cb6a5621c3d890c20fc611
|
File details
Details for the file nakata_agentguard-0.4.0-py3-none-any.whl.
File metadata
- Download URL: nakata_agentguard-0.4.0-py3-none-any.whl
- Upload date:
- Size: 31.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
79513a1e0b6618f8b711ac68dae3f777d61aad9ac5da60cfe73984c2a447340a
|
|
| MD5 |
7513c23fc7d7d97055cbb26e38156887
|
|
| BLAKE2b-256 |
7ac20cf6ccc9e6fd8b007ba35f75cb6f201b1a7458273b428eeb199a9ea3f2dd
|