agent-feedback Python package is a dependency-free middleware that works with any ASGI or WSGI framework. A single constructor call wraps your existing application object, and Agent Feedback instruments eligible responses without touching your handlers, adding third-party packages, or blocking the product response path.
Installation
1
Install the package
2
Create a product key
Open the Agent Feedback dashboard, create a product, and copy the API key. Store it as
AGENT_FEEDBACK_KEY in your environment — never hard-code it.agent-feedback has no third-party runtime dependencies. It uses only the Python standard library (urllib.request, threading, hmac, hashlib, and queue).ASGI middleware
Wrap your ASGI application withAgentFeedbackASGI. This works with FastAPI, Starlette, Quart, Django (ASGI mode), and any other framework that exposes a standard ASGI callable. JSON object responses gain an _agentFeedback field, and HTML responses receive an injected <script id="agent-feedback"> tag.
- FastAPI
- Starlette
- Django ASGI
With customer context
WSGI middleware
Wrap your WSGI application’swsgi_app attribute with AgentFeedbackWSGI. This works with Flask, Django (WSGI mode), Bottle, Pyramid, and any other WSGI framework.
- Flask
- Django WSGI
ASGI vs WSGI: what gets instrumented
The two middleware classes use different strategies based on what each protocol allows.
Both middleware classes read the response body for
application/json and text/html responses and inject the feedback contract directly. WSGI instrumentation is limited to finite responses that declare a Content-Length — streaming and chunked responses are forwarded without modification. Both add Cache-Control: private, no-store to instrumented responses.
Configuration options
BothAgentFeedbackASGI and AgentFeedbackWSGI accept the same options after app. All fields except api_key are optional.
str
required
Your Agent Feedback product key. Must match the pattern
af_live_<keyId>_<secret>. Store this in an environment variable.tuple[str, ...]
default:"()"
Glob-style path patterns that opt routes in. When empty, all routes are instrumented (subject to
exclude). Supports * (any path segment) and ** (any path). Example: ("/search", "/docs/*").tuple[str, ...]
default:"()"
Glob-style patterns to skip. The default exclusion list (
/health, /healthz, /metrics, /favicon.ico, /robots.txt, /_agent-feedback/*, /api/v2/outcomes) is always applied. Patterns you add here extend that list.Callable[[Any], str | None] | None
Callback that receives the ASGI
scope object (or WSGI environ) and returns an opaque customer identifier. Used for grouping interactions by account in the dashboard.Callable[[Any], str | None] | None
Callback that returns a session identifier. Links a sequence of interactions for session-level analytics.
"auto" | "ask" | "off"
default:"\"auto\""
Controls the instruction sent to agents inside the feedback envelope.
"auto"— instructs the agent to submit a review autonomously."ask"— instructs the agent to submit only when the outcome is clear."off"— disables instrumentation entirely.
Callable[[Any], str | None] | None
Callback that receives the ASGI
scope object (or WSGI environ) and returns a hint about the agent runtime making the request (e.g. "claude", "gpt-4o"). Used for runtime-level breakdown in the dashboard.float
default:"0.5"
How often the background thread flushes telemetry events, in seconds. The queue also flushes when it reaches 50 events.
int
default:"1000"
Maximum number of telemetry events held in memory. When the queue is full, the oldest event is dropped to make room for the newest.
Agent helper functions
Importfeedback_from_response and submit_product_outcome from agent_feedback to build a feedback-aware agent runtime that submits outcomes deterministically.
feedback_from_response
Extracts the FeedbackEnvelope dictionary from a response. Checks the parsed body first (JSON _agentFeedback field or HTML <script id="agent-feedback"> tag), then falls back to the Agent-Feedback response header.
submit_product_outcome
Submits a compact outcome review to the URL embedded in the envelope. Validates the envelope, enforces the allowed-origin allow-list, and raises ValueError on invalid input.