Praxis Observability
Observability and tracing abstraction layer for Praxis AI agent framework.
Overview
praxis-observability provides a trait-based abstraction for observing and tracing AI agent executions. It enables you to track LLM calls, tool executions, and overall agent behavior across different observability backends.
Features
- Backend Agnostic: Trait-based design allows multiple observability providers
- Langfuse Integration: Built-in support for Langfuse tracing
- Async Fire-and-Forget: Non-blocking tracing that doesn't impact agent performance
- Structured Data: Rich context captured for each node execution
- Extensible: Easy to add custom observers for other platforms
Installation
Add to your Cargo.toml:
[]
= "0.1"
Quick Start
Using Langfuse Observer
use LangfuseObserver;
// Initialize observer
let observer = new?;
// Use with Praxis graph
let graph = builder
.llm_client
.mcp_executor
.with_observer
.build?;
Implementing Custom Observer
use async_trait;
use ;
Configuration
Environment Variables (Langfuse)
LANGFUSE_PUBLIC_KEY=pk-xxx
LANGFUSE_SECRET_KEY=sk-xxx
LANGFUSE_HOST=https://cloud.langfuse.com
Configuration File (TOML)
[]
= true
= "langfuse"
[]
= "${LANGFUSE_PUBLIC_KEY}"
= "${LANGFUSE_SECRET_KEY}"
= "https://cloud.langfuse.com"
Trace Structure
When using Langfuse, traces are organized as:
Trace (per graph execution run)
├── Span: LLM Node #1
│ ├── Input: Messages sent to LLM
│ └── Output: AI response (text or tool calls)
├── Span: Tool Node #1
│ ├── Input: Tool calls
│ └── Output: Tool results
├── Span: LLM Node #2
│ └── ...
└── Status: Success/Error
Architecture
Observer Pattern
The Observer trait defines the contract for tracing:
- trace_start: Initialize a new trace for a graph execution
- trace_llm_node: Record LLM node execution with input/output
- trace_tool_node: Record tool node execution with tool calls/results
- trace_end: Finalize trace with status and duration
Fire-and-Forget Design
All tracing operations are executed asynchronously in background tasks:
if let Some = &observer
This ensures tracing never blocks the main execution flow.
Node Exit Triggers
Observability is triggered immediately after each node exits in the graph execution loop:
- Node Execution:
node.execute()completes - Message Extraction: New messages added by the node are extracted
- Persistence (async): Messages are saved to the database (fire-and-forget)
- Observability (async): Observation is sent to the observer (fire-and-forget)
- Graph Continues: Next node is determined and executed
This design ensures:
- Complete messages are traced (no partial streaming chunks)
- Tracing happens in real-time as nodes complete
- No blocking on I/O operations
Langfuse Batch Ingestion
The Langfuse implementation uses the batch ingestion API for optimal performance:
// Each event is wrapped in a batch
Event Types:
trace-create: Creates/updates a tracegeneration-create: Records an LLM generation (for LLM nodes)span-create: Records a span (for Tool nodes)
Benefits:
- Single endpoint for all event types
- Atomic operations per node
- Easy to batch multiple events in the future
Data Captured
LLM Node Observation
Input Format (complete message history):
Output Format (new AI message):
Additional Fields:
- Duration: Time taken for LLM call (ms)
- Model: LLM model identifier (e.g.,
gpt-4o-mini) - Token Usage: Prompt, completion, and total tokens
- Metadata: Run ID, conversation ID, timestamps
Tool Node Observation
Input Format (tool calls):
Output Format (tool results):
Additional Fields:
- Duration: Time taken for tool execution (ms)
- Status: Success or error for each tool call
- Metadata: Run ID, conversation ID, timestamps
Best Practices
- Always use Arc: Wrap observers in
Arcfor sharing across async tasks - Handle errors gracefully: Observer failures should log but not crash the agent
- Avoid blocking: Never block in observer implementations
- Batch when possible: For high-volume scenarios, implement batching
- Add metadata: Include custom tags/metadata for better filtering
Examples
See the examples/ directory for:
simple_trace.rs: Basic observer usagecustom_observer.rs: Implementing a custom observermetadata.rs: Adding custom metadata to traces
License
MIT