NeuroNetes provides comprehensive observability at the token and turn level, with built-in metrics, logging, and tracing tailored for agent workloads.
Time to first token latency.
Type: Histogram Labels:
agent_class: AgentClass namemodel: Model nameroute: Request route/endpointExample:
# P95 TTFT
histogram_quantile(0.95,
rate(neuronetes_ttft_seconds_bucket[5m])
)
# By agent class
histogram_quantile(0.95,
rate(neuronetes_ttft_seconds_bucket{agent_class="code-assistant"}[5m])
)
Token generation throughput.
Type: Gauge Labels:
agent_classmodeldirection: input/outputExample:
# Current output tokens/sec
neuronetes_tokens_per_second{direction="output"}
# Aggregate across all pools
sum(neuronetes_tokens_per_second{direction="output"})
Total token count.
Type: Counter Labels:
agent_classmodeldirection: input/outputExample:
# Input tokens over time
rate(neuronetes_tokens_total{direction="input"}[5m])
# Total tokens processed
sum(neuronetes_tokens_total)
Context window utilization.
Type: Histogram Labels:
agent_classmodelExample:
# Average context length
avg(neuronetes_context_length_tokens)
# P99 context length
histogram_quantile(0.99,
rate(neuronetes_context_length_tokens_bucket[5m])
)
Tool invocation latency.
Type: Histogram Labels:
tool_nameagent_classstatus: success/failureExample:
# P95 tool latency
histogram_quantile(0.95,
rate(neuronetes_tool_latency_seconds_bucket[5m])
)
# By tool
histogram_quantile(0.95,
rate(neuronetes_tool_latency_seconds_bucket{tool_name="code_search"}[5m])
)
Total tool invocations.
Type: Counter Labels:
tool_nameagent_classstatusExample:
# Tool call rate
rate(neuronetes_tool_invocations_total[5m])
# Success rate
rate(neuronetes_tool_invocations_total{status="success"}[5m]) /
rate(neuronetes_tool_invocations_total[5m])
RAG retrieval latency.
Type: Histogram Labels:
agent_classvector_storeExample:
# P95 retrieval latency
histogram_quantile(0.95,
rate(neuronetes_retrieval_latency_seconds_bucket[5m])
)
Cache misses for retrieval.
Type: Counter Labels:
agent_classcache_typeExample:
# Miss rate
rate(neuronetes_retrieval_misses_total[5m]) /
rate(neuronetes_retrieval_requests_total[5m])
Safety guardrail blocks.
Type: Counter Labels:
guardrail_type: pii-detection, safety-check, etc.action: block, redact, warnagent_classExample:
# Total blocks
sum(neuronetes_safety_blocks_total{action="block"})
# Block rate by type
rate(neuronetes_safety_blocks_total[5m])
Guardrail check latency.
Type: Histogram Labels:
guardrail_typeagent_classCost tracking in USD.
Type: Counter Labels:
agent_classmodelcost_type: compute, storage, networkExample:
# Hourly cost
rate(neuronetes_cost_usd[1h]) * 3600
# Cost by model
sum by (model) (rate(neuronetes_cost_usd[1h]) * 3600)
Active conversation sessions.
Type: Gauge Labels:
agent_classpoolExample:
# Current concurrent sessions
neuronetes_concurrent_sessions
# Peak sessions
max_over_time(neuronetes_concurrent_sessions[1h])
Session lifetime.
Type: Histogram Labels:
agent_classExample:
# Average session duration
avg(neuronetes_session_duration_seconds)
# P95 duration
histogram_quantile(0.95,
rate(neuronetes_session_duration_seconds_bucket[5m])
)
Current replica count.
Type: Gauge Labels:
poolstatus: ready, not_ready, warmExample:
# Ready replicas
neuronetes_agentpool_replicas{status="ready"}
# Warm pool size
neuronetes_agentpool_replicas{status="warm"}
Scaling operations.
Type: Counter Labels:
pooldirection: up, downExample:
# Scale operations per hour
sum by (pool) (
rate(neuronetes_agentpool_scale_operations_total[1h]) * 3600
)
All logs are JSON-structured:
{
"timestamp": "2024-01-15T10:30:45.123Z",
"level": "info",
"msg": "request completed",
"agent_class": "code-assistant",
"session_id": "sess-abc123",
"request_id": "req-xyz789",
"ttft_ms": 450,
"total_tokens": 1523,
"input_tokens": 342,
"output_tokens": 1181,
"tool_calls": 3,
"duration_ms": 3420
}
PII is automatically redacted:
{
"msg": "processing request",
"user_input": "My email is [REDACTED] and phone is [REDACTED]",
"pii_detected": ["email", "phone"],
"redaction_time_ms": 12
}
Configuration:
apiVersion: neuronetes.io/v1alpha1
kind: AgentClass
metadata:
name: secure-agent
spec:
observability:
logging:
# Auto-redact PII
redactPII: true
# Patterns to redact
redactionPatterns:
- email
- phone
- ssn
- credit_card
# Sample rate (% of logs to keep)
sampleRate: 100
NeuroNetes integrates with OpenTelemetry:
Request Span
├── Ingress Span
│ └── Auth Span
├── Router Span
│ └── Session Lookup Span
├── Agent Span
│ ├── Model Inference Span
│ │ ├── Context Fetch Span
│ │ ├── Prompt Processing Span
│ │ ├── Token Generation Span
│ │ └── Context Save Span
│ ├── Tool Call Span (code_search)
│ │ ├── Vector Search Span
│ │ └── Result Processing Span
│ └── Tool Call Span (file_read)
└── Response Span
Standard attributes:
agent_classmodelsession_idrequest_idinput_tokensoutput_tokensttft_mstool_callsguardrails_triggeredExample trace:
{
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"span_id": "00f067aa0ba902b7",
"name": "agent.inference",
"kind": "INTERNAL",
"start_time": "2024-01-15T10:30:45.000Z",
"end_time": "2024-01-15T10:30:48.420Z",
"attributes": {
"agent_class": "code-assistant",
"model": "codellama-34b",
"input_tokens": 342,
"output_tokens": 1181,
"ttft_ms": 450,
"tool_calls": ["code_search", "file_read"],
"cache_hits": 2
}
}
Deploy Jaeger:
kubectl apply -f https://raw.githubusercontent.com/jaegertracing/jaeger-kubernetes/main/all-in-one/jaeger-all-in-one-template.yml
Configure NeuroNetes:
apiVersion: v1
kind: ConfigMap
metadata:
name: neuronetes-config
data:
tracing.yaml: |
enabled: true
exporter: jaeger
endpoint: jaeger-collector:14268
sampleRate: 0.1 # 10% sampling
Pre-built dashboards are available:
{
"dashboard": {
"title": "NeuroNetes Agent Overview",
"panels": [
{
"title": "TTFT P95",
"targets": [{
"expr": "histogram_quantile(0.95, rate(neuronetes_ttft_seconds_bucket[5m]))"
}],
"type": "graph"
},
{
"title": "Tokens/Sec",
"targets": [{
"expr": "sum(neuronetes_tokens_per_second{direction='output'})"
}],
"type": "graph"
},
{
"title": "Active Sessions",
"targets": [{
"expr": "neuronetes_concurrent_sessions"
}],
"type": "stat"
}
]
}
}
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-alerts
data:
neuronetes.rules: |
groups:
- name: neuronetes
interval: 30s
rules:
# High TTFT
- alert: HighTTFT
expr: |
histogram_quantile(0.95,
rate(neuronetes_ttft_seconds_bucket[5m])
) > 1.0
for: 5m
labels:
severity: warning
annotations:
summary: "High time-to-first-token"
description: "P95 TTFT is {{ $value }}s (threshold: 1s)"
# Low throughput
- alert: LowThroughput
expr: |
sum(neuronetes_tokens_per_second{direction="output"}) < 50
for: 10m
labels:
severity: warning
annotations:
summary: "Low token throughput"
description: "Throughput is {{ $value }} tokens/sec"
# High error rate
- alert: HighErrorRate
expr: |
rate(neuronetes_requests_failed_total[5m]) /
rate(neuronetes_requests_total[5m]) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate"
description: "Error rate is {{ $value | humanizePercentage }}"
# Safety blocks spike
- alert: SafetyBlocksSpike
expr: |
rate(neuronetes_safety_blocks_total[5m]) > 10
for: 5m
labels:
severity: warning
annotations:
summary: "Spike in safety blocks"
description: "{{ $value }} blocks/sec"
# Cost budget exceeded
- alert: BudgetExceeded
expr: |
sum(rate(neuronetes_cost_usd[1h]) * 3600) > 100
for: 1h
labels:
severity: warning
annotations:
summary: "Hourly cost budget exceeded"
description: "Cost is ${{ $value }}/hour"
Configure notification channels:
apiVersion: v1
kind: Secret
metadata:
name: alertmanager-config
stringData:
alertmanager.yml: |
global:
resolve_timeout: 5m
route:
group_by: ['alertname', 'agent_class']
group_wait: 10s
group_interval: 10s
repeat_interval: 12h
receiver: 'default'
routes:
- match:
severity: critical
receiver: 'pagerduty'
- match:
severity: warning
receiver: 'slack'
receivers:
- name: 'default'
webhook_configs:
- url: 'http://webhook-service/alerts'
- name: 'slack'
slack_configs:
- api_url: 'SLACK_WEBHOOK_URL'
channel: '#neuronetes-alerts'
- name: 'pagerduty'
pagerduty_configs:
- service_key: 'PAGERDUTY_KEY'
package metrics
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
var (
customMetric = promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "neuronetes_custom_metric_seconds",
Help: "Custom metric description",
Buckets: prometheus.DefBuckets,
},
[]string{"label1", "label2"},
)
)
func RecordCustomMetric(duration float64, labels ...string) {
customMetric.WithLabelValues(labels...).Observe(duration)
}
apiVersion: v1
kind: ConfigMap
metadata:
name: custom-metrics
data:
metrics.yaml: |
metrics:
- name: business_metric
type: gauge
help: "Business-specific metric"
labels: ["tenant", "product"]
- name: quality_score
type: histogram
help: "Generated content quality score"
buckets: [0.1, 0.3, 0.5, 0.7, 0.9, 1.0]
# Good: Bounded cardinality
neuronetes_requests_total{agent_class="code-assistant", status="200"}
# Bad: Unbounded cardinality
neuronetes_requests_total{user_id="12345", session_id="abc..."}
# High-traffic endpoints
sampleRate: 1 # 1%
# Low-traffic endpoints
sampleRate: 100 # 100%
# Errors
errorSampleRate: 100 # Always sample errors
redactPII: true
redactionPatterns:
- email
- phone
- ssn
- api_key
// Good
log.Info("request processed",
"request_id", reqID,
"duration_ms", duration,
"tokens", tokens)
// Bad
log.Info(fmt.Sprintf("Request %s took %d ms with %d tokens",
reqID, duration, tokens))
Focus on:
histogram_quantile(0.95, rate(neuronetes_ttft_seconds_bucket[5m]))
neuronetes_model_load_seconds
neuronetes_queue_wait_seconds
neuronetes_gpu_available
3. Solutions:
- Increase warm pool
- Add more replicas
- Use faster GPUs
### High Tool Latency
1. Check per-tool metrics:
```promql
histogram_quantile(0.95,
rate(neuronetes_tool_latency_seconds_bucket{tool_name="code_search"}[5m])
)
kubectl port-forward svc/prometheus 9090
curl localhost:9090/api/v1/label/__name__/values | grep neuronetes
kubectl get servicemonitor -n neuronetes-system
kubectl get configmap prometheus-config -o yaml