Telemetry & Feedback¶
Observability and Reinforcement Loop
Agentomatic provides built-in, production-grade OpenTelemetry tracing, Prometheus metrics, and feedback collection loops. This ensures you can monitor agent behaviors, track latency, audit costs, and capture user corrections to continuously improve your prompts.
📡 1. OpenTelemetry Auto-Instrumentation¶
When enabled and the telemetry extra is installed, Agentomatic instruments its
FastAPI application and httpx clients. This creates standard HTTP server and
client spans. Add @traced to agent nodes and application helpers when you
need domain spans for LLM calls, retrieval, database work, or business steps.
platform = AgentPlatform.from_folder(
"agents/",
enable_telemetry=True, # Enabled by default
)
app = platform.build()
Trace Spans & Metadata¶
Use explicit spans for domain-specific execution phases:
| Span Name | Attributes | Description |
|---|---|---|
support_agent.query_kb |
Application-defined attributes | Example retrieval span created with @traced. |
Any @traced("...") name |
Safe function arguments + duration_ms |
A domain operation you choose to instrument. |
| Framework HTTP spans | Standard HTTP attributes | Incoming FastAPI and outgoing httpx calls, when their instrumentors are installed. |
Configuring Exporters¶
To route traces to APM backends (like Jaeger, Datadog, Honeycomb, or Google Cloud Trace), configure OTLP environment variables:
# Production Exporter Setup
export OTEL_SERVICE_NAME="agentomatic-platform"
export OTEL_EXPORTER_OTLP_ENDPOINT="http://jaeger-collector:4317"
export OTEL_TRACES_SAMPLER="parentbased_traceidratio"
export OTEL_TRACES_SAMPLER_ARG="0.1" # Sample 10% of traces in production
💡 If no OTLP endpoint is defined, spans are recorded but are not exported by default. Set
AGENTOMATIC_OTEL_CONSOLE=1for local console output, or set an OTLP endpoint for a collector.
Adding Custom Spans in Agent Code¶
You can instrument custom processing nodes or helper functions in your agent folder (e.g., nodes.py or tools.py) using the @traced decorator:
from agentomatic.observability.telemetry import traced
@traced("support_agent.query_kb")
async def query_knowledge_base(query: str) -> list[str]:
# This automatically records a span named 'support_agent.query_kb'
# and tracks any exceptions raised during execution.
return await vector_store.similarity_search(query)
📊 2. Agent Metrics & Instrumentation¶
Agentomatic comes equipped with Prometheus instruments for tracking agent-specific metrics. If prometheus_client is not installed, it falls back to a graceful no-op dummy state.
Core Metrics¶
The framework records the following counters, gauges, and histograms:
from agentomatic.observability.metrics import (
AGENT_INVOCATION_COUNT, # Counter: ["agent_name", "status"]
AGENT_DURATION, # Histogram: ["agent_name"]
LLM_DURATION, # Histogram: ["provider", "model"]
ERROR_COUNT, # Counter: ["error_type", "agent_name"]
ACTIVE_AGENTS, # Gauge: Concurrent active agent runs
)
Context-Based Tracking¶
Track agent run durations and statuses in your code using the async context manager track_agent_invocation:
from agentomatic.observability.metrics import track_agent_invocation
async def run_analytics(agent_name: str):
async with track_agent_invocation(agent_name):
# ACTIVE_AGENTS increments
await process_graph()
# SUCCESS increments
# DURATION is observed and ACTIVE_AGENTS decrements
Concurrency Primitives & Circuit Breakers¶
Protect external APIs and rate-limited LLM providers using built-in circuit breakers and semaphores:
- AgentSemaphore: Limit concurrent executions per agent.
- CircuitBreaker: Automatically trip and fail fast when external services experience consecutive failures.
from agentomatic.observability.concurrency import CircuitBreaker, AgentSemaphore
# 1. Initialize a circuit breaker
llm_breaker = CircuitBreaker(
name="llm_provider",
failure_threshold=5, # Trip after 5 failures
reset_timeout=60.0, # Reset connection attempt after 60s
)
# 2. Wrap LLM calls
async with llm_breaker.call():
response = await chat_llm.ainvoke(query)
💬 3. Feedback Collection Loop¶
Gather real-user feedback directly through your agent API.
platform = AgentPlatform.from_folder(
folder_path="agents/",
enable_feedback=True, # Auto-registers feedback endpoints
store=SQLAlchemyStore("sqlite+aiosqlite:///data.db")
)
When enabled, Agentomatic mounts these three endpoints for every registered agent:
1. POST /api/v1/{agent}/feedback — Submit a rating (1-5), comment, or correct answer.
2. GET /api/v1/{agent}/feedback — List historical feedback entries.
3. GET /api/v1/{agent}/feedback/export — Export logs as a JSONL dataset.
Each exported line is {"query", "expected_answer", "metadata"}, where
expected_answer is the submitted correction (or the rated response), so
the export can seed an optimization dataset. The
thread store keeps query, response and correction with every feedback
row (older SQL databases gain these columns automatically at startup); only
rows with a query are exported.
Submitting Feedback (Client Example)¶
import httpx
# Submit a thumbs-down correction to train the optimizer
async with httpx.AsyncClient() as client:
await client.post(
"http://localhost:8000/api/v1/support_agent/feedback",
json={
"query": "How do I reset my password?",
"response": "Use the settings panel.",
"correction": "Click 'Forgot Password' on the login screen.",
"rating": 1,
"feedback_type": "correction",
}
)
⚡ 4. The /optimize/invoke Endpoint¶
For automated evaluation and prompt tuning, standard response text is not enough. Evaluation metrics like faithfulness or context_recall require access to intermediate contexts and steps.
Agentomatic mounts a dedicated POST /api/v1/{agent}/optimize/invoke endpoint which returns the full pipeline context:
Request Payload¶
{
"query": "What are the working hours?",
"system_prompt_override": "You are a concise office assistant..."
}
Response Payload¶
{
"response": "Working hours are 9:00 AM to 5:00 PM.",
"retrieval_context": [
"Office Policy: Standard business hours are 9 AM to 5 PM."
],
"tool_calls": [
{
"name": "search_handbook",
"args": {"query": "hours"},
"result": "..."
}
],
"steps_taken": ["search_db", "format_reply"],
"reasoning": "Retrieved working hours from office policy docs.",
"citations": [{"source": "handbook_policy.pdf", "page": 4}],
"duration_ms": 341.2,
"metadata": {}
}
The PromptOptimizer automatically utilizes this endpoint when running optimization loops. It fetches intermediate retrieval context and tool logs, enabling deep evaluation scores.