Observability
phi-agent collects structured metrics automatically. No setup required — every session writes a session_metrics.json file alongside the existing session data.
What's Collected
Per-turn metrics (TurnMetrics)
| Field | Description |
|---|---|
turn_number |
Which turn in the session |
duration_ms |
Total turn time |
time_to_first_token_ms |
Time until first output token (user experience metric) |
llm_duration_ms |
Pure LLM time |
tool_duration_ms |
Tool execution time |
input_tokens / output_tokens |
Token usage from LLM response |
tool_call_count / tools_used |
Which tools ran and how many |
tool_success / tool_failed |
Tool success/failure counts |
outcome |
completed / tool_calls / error / cancelled / max_turns |
has_thinking |
Whether the model used extended thinking |
user_input |
Truncated to 80 characters |
Per-session aggregates (SessionMetrics)
| Field | Description |
|---|---|
total_turns |
Total turns in session |
total_input_tokens / total_output_tokens |
Cumulative tokens |
estimated_cost |
Cost estimate based on model pricing |
tool_breakdown |
Per-tool call counts (e.g. {"shell": 5, "check_quality": 2}) |
tool_fail_rate |
Fraction of tool calls that failed |
p50_turn_ms / p95_turn_ms / p99_turn_ms |
Latency percentiles |
outcome |
completed / failed / cancelled / max_turns |
error_count |
How many turns ended in error |
CLI Commands
# List all sessions on this machine
phi metrics list
# Output:
# Session Turns Tokens Cost Outcome
# 20260729_abc12345 (phi-bard) 5 27,000 $0.18 ✅ completed
# 20260729_def67890 (phi) 3 11,000 $0.06 ✅ completed
# Show detailed breakdown for a session
phi metrics show 20260729_abc12345
# Show the most recent session
phi metrics last
Environment Variables
| Variable | Default | Description |
|---|---|---|
PHI_METRICS_ENABLED |
true |
Set to false to disable metrics entirely (useful for resource-constrained devices) |
PHI_NODE_ID |
"" |
Node identifier — distinguishes which machine produced the metrics |
PHI_COST_PER_1K_TOKENS |
built-in | Custom model pricing. Format: input_cost,output_cost per 1K tokens (e.g. 0.002,0.008). Falls back to built-in pricing for Claude/GPT models. |
Custom Business Metrics
The custom field lets you inject arbitrary JSON data without the framework knowing about it:
use phi_telemetry::{init_telemetry, save_metrics};
// Set up telemetry with custom session data
let mut handle = init_telemetry(agent.runtime(), session_id, node_id, model);
handle.set_session_custom(serde_json::json!({
"product": "my-app",
"version": "1.0"
}));
// ... agent runs ...
// Shutdown and save
handle.shutdown().await;
let session = handle.session.read().await;
let mut session = session.clone();
session.finalize(SessionOutcome::Completed);
save_metrics(&session, &session_dir)?;
Result in session_metrics.json:
Architecture
Observability runs in an independent tokio task, communicating with the agent via an mpsc channel:
sequenceDiagram
participant A as Agent Task<br/>(runtime)
participant O as Observer Task<br/>(tokio::spawn)
Note over A,O: Normal operation
A->>O: on_turn_end hook → tx.send(TurnEnd)
O->>O: build metrics, accumulate in memory
Note over A,O: Observer panic
A->>O: tx.send(msg)
O--xA: channel closed
Note over A: let _ = send, continues unaffected
- Observer panics never crash the agent — hook silently drops failed sends
- Metrics accumulate in memory during the session;
save_metrics()writes to disk aftershutdown() - Channel is unbounded — never blocks the agent's hot path
File Layout
~/.phi-agent/sessions/<session_id>/
├── turn_001.jsonl ← full event stream (dialogue, thinking, tool args/results)
├── turn_002.jsonl
├── session_meta.json ← session metadata
├── session.log ← tracing logs
└── session_metrics.json ← structured metrics (a few KB)