Audit-grade agent observability for Python — capture, redact, sign, and ship every model call, tool invocation, and decision an AI agent makes.
Project description
Herald
Audit-grade agent observability for Python.
Status: v0.0.1 — design lock. The public API surface is defined; capture wiring lands in phase 1. Imports work, decorators are pass-through stubs, tests pass.
When an AI agent makes a wrong decision in production — denies the wrong refund, gives the wrong medical recommendation, executes the wrong trade — most teams cannot reconstruct what the agent saw, what it asked the model, what the model returned, or why it decided what it did. Herald is the recorder that captures all of it.
One line of Python installs it. From that point forward every model call, every tool invocation, every retry, every decision lands in a structured, queryable, replayable record. The recording is local-first (survives process crash), audit-grade (tamper-evident hash chain on the events that matter), and ships to a Herald backend for long-term storage and query.
Built on a high-performance .NET capture engine that already powers latency-sensitive workloads in trading, healthcare, and game development. The Python SDK is the front door for the language the agents are actually written in.
Install
pip install herald-py
The PyPI distribution is herald-py; the import name is herald (import herald).
Optional extras for framework-specific auto-instrumentation:
pip install "herald-py[openai]"
pip install "herald-py[anthropic]"
pip install "herald-py[langchain]"
pip install "herald-py[fastapi]"
pip install "herald-py[otlp]"
Quickstart
import os
import herald
from anthropic import AsyncAnthropic
herald.configure(
backend=os.environ["HERALD_BACKEND"],
api_key=os.environ["HERALD_API_KEY"],
service="refund-agent",
environment="production",
redact={"card_number": "last4", "email": "domain_only"},
)
herald.auto_instrument()
client = AsyncAnthropic()
@herald.tool(name="lookup_order")
async def lookup_order(order_id: str) -> dict:
return await orders_service.fetch(order_id)
@herald.agent(name="refund-agent", version="3.1.0")
async def decide_refund(order_id: str) -> dict:
order = await lookup_order(order_id)
response = await client.messages.create(
model="claude-opus-4-7",
max_tokens=512,
messages=[{"role": "user", "content": f"Decide refund for {order}"}],
)
decision = parse(response.content[0].text)
herald.audit(
event="refund_decision",
decision=decision["outcome"],
order_id=order_id,
rationale=decision["rationale"],
)
return decision
What gets captured for one invocation of decide_refund:
- The agent run boundary (one
run_idfor everything below). - Each
@herald.toolcall as a child span with input and output. - The
anthropic.messages.createcall with prompt, response, token counts, and dollar cost. - The
herald.audit(...)event in the tamper-evident audit ledger. - All HTTP, database, and retrieval calls inside the run via auto-instrumentation.
What this is
Three nouns, three decorators:
| Decorator | Meaning |
|---|---|
@herald.agent |
Top-level boundary of an agent run. Everything inside shares one run_id. |
@herald.tool |
Anything the agent can invoke (your code, an external API, a database query). |
@herald.llm_call |
Manual model invocation outside the auto-instrumented SDKs. |
Plus:
herald.configure(...)— one-time setup at process start.herald.auto_instrument()— patches every supported library detected in the venv.herald.span(...)— context manager for arbitrary capture boundaries.herald.record(...)— emit an arbitrary structured event.herald.audit(...)— emit an audit-channel event with synchronous durability and chain-of-custody.
Features
- Async-native. Built for
asynciofrom the ground up. - Fully typed. PEP 561 marker shipped;
mypy --strictclean. - OpenTelemetry-compatible. Default wire format is OTLP/gRPC with the GenAI semantic conventions, so any OTel-aware backend can ingest Herald output without modification.
- Auto-instrumentation. One line patches OpenAI, Anthropic, LangChain, LangGraph, LlamaIndex, FastAPI, httpx, SQLAlchemy, and a long tail of agent-stack libraries.
- Audit channel.
herald.audit(...)events bypass sampling, write synchronously to local disk, and carry a tamper-evident SHA-256 chain. - Local durability. SQLite-backed ring buffer that survives process crashes; events drain to the backend asynchronously.
- Replay. Pull a captured run and re-execute the LLM portions against a different model or prompt to test fixes safely.
- OSS escape hatch. Apache-2 licensed; works against any OTLP backend, not just Herald.
Status and roadmap
| Phase | Scope | State |
|---|---|---|
| 0 — Design lock | Public API surface frozen, stub package on PyPI. | In progress. |
| 1 — Minimum viable SDK | configure(), auto_instrument() for OpenAI + Anthropic, three decorators, audit(), SQLite durability, OTLP export. |
Planned. |
| 2 — First vertical integrations | LangChain, LangGraph, LlamaIndex, FastAPI middleware. Cost table. | Planned. |
| 3 — Server-side ingest | OTLP receiver in Herald.Server, audit ledger sink, run index, query API. | Planned. |
| 4 — Replay API | Replayer.fetch_run(), Replayer.replay(), diff computation. |
Planned. |
| 5 — Managed cloud GA | Multi-tenant deployment, billing, dashboard. | Planned. |
The current release is scaffolding only — decorators are pass-through, no events leave the process. Public surface is the contract; behavior arrives in phase 1.
Development
git clone https://github.com/smuchow1962/Herald.Py.git
cd Herald.Py
python -m venv .venv
.venv/Scripts/pip install -e ".[dev]"
.venv/Scripts/pytest -q
.venv/Scripts/ruff check .
.venv/Scripts/mypy
Targeting Python 3.10+. Built with hatchling. Ruff for lint and import sort, mypy in --strict mode for type checking, pytest with pytest-asyncio for tests.
License
Apache-2.0. See LICENSE.md.
Copyright 2026 MMPWorks LLC.
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 herald_py-0.0.1.tar.gz.
File metadata
- Download URL: herald_py-0.0.1.tar.gz
- Upload date:
- Size: 16.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6e35d2f9b3f5f0b95b3c14c642f31211a9c3d139b0b007af63ea9328bc1709b4
|
|
| MD5 |
c61ce23428ee6721d60daf585d6db6b4
|
|
| BLAKE2b-256 |
424e92a5709d4e9a248564b83bd6892d9f6b5669cfe8b3128c7164d3723a559d
|
File details
Details for the file herald_py-0.0.1-py3-none-any.whl.
File metadata
- Download URL: herald_py-0.0.1-py3-none-any.whl
- Upload date:
- Size: 14.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f1aaff6f874dd884223b352bf1050854f5d802312f5a4bdd48326047c0f759b0
|
|
| MD5 |
1781e66fdb20778decaa3b4faee77237
|
|
| BLAKE2b-256 |
bb1cdb3c39f9efebe5fae31b21ffbeb016ee4879a89498eac9cdec2684ed5a7a
|