Arize Phoenix + Agent Memory: Tracing Stateful Agents

Free · Open source (MIT) · Works with LangChain, CrewAI, AutoGen · No signup

Tracing agent behavior across sessions is broken in most setups. Your LLM agents forget everything between runs, making Arize Phoenix show disconnected traces instead of coherent user journeys. You need persistent memory that survives restarts and integrates with Phoenix's tracing infrastructure.

The Agent Memory Problem

Standard agent frameworks store conversational state in memory variables or session objects. When the process restarts, container redeploys, or you switch between development machines, that state vanishes. Your Phoenix traces show individual interactions, but miss the broader context of how agents evolve their understanding over time.

This breaks agent evaluation in Phoenix. You can't measure how well your agent learns from past interactions, maintains user preferences, or builds upon previous conversations. Each trace looks like a fresh start, even when users expect continuity.

Consider a support agent that should remember a user's previous issues, preferred communication style, or resolved tickets. Without persistent memory, Phoenix shows each interaction as isolated, making it impossible to evaluate the agent's contextual performance or identify patterns in multi-session user journeys.

The Fix: Persistent Memory with BotWire

Install BotWire to add persistent key-value memory that survives restarts and integrates seamlessly with Phoenix tracing:

pip install botwire

Here's a basic agent with persistent memory:

from botwire import Memory
import openai

# Create persistent memory namespace
agent_memory = Memory("support-agent")

def handle_user_request(user_id: str, message: str):
    # Retrieve user context
    user_context = agent_memory.get(f"user:{user_id}:context") or {}
    previous_issues = agent_memory.get(f"user:{user_id}:issues") or []
    
    # Build prompt with persistent context
    context_prompt = f"User history: {previous_issues}\nCurrent context: {user_context}"
    
    response = openai.chat.completions.create(
        model="gpt-4",
        messages=[
            {"role": "system", "content": context_prompt},
            {"role": "user", "content": message}
        ]
    )
    
    # Update persistent memory
    previous_issues.append({"message": message, "timestamp": "2024-01-15"})
    agent_memory.set(f"user:{user_id}:issues", previous_issues)
    
    return response.choices[0].message.content

How It Works

The Memory class provides a simple key-value interface backed by BotWire's HTTP API. Data persists across process restarts, making your Phoenix traces show true multi-session behavior.

Memory operations are straightforward:

For structured agent state, use namespaced keys:

from botwire import Memory

agent_memory = Memory("customer-service")

# Store user preferences
agent_memory.set("user:42:preferences", {
    "communication_style": "formal",
    "timezone": "EST",
    "language": "en"
})

# Store conversation summaries
agent_memory.set("user:42:summary", "VIP customer, prefers email over phone")

# List all user data
user_keys = agent_memory.list_keys("user:42:")
print(user_keys)  # ['user:42:preferences', 'user:42:summary']

Memory persists across machines and deployments. If you run the same namespace on different servers, they share the same memory space. This enables distributed agents that maintain consistent state.

The free tier provides 1000 writes per day per namespace with unlimited reads - sufficient for most development and small production workloads.

Phoenix Integration Pattern

When tracing agents with Phoenix, include memory operations in your spans to show how persistent state influences agent decisions:

from botwire import Memory
from opentelemetry import trace

tracer = trace.get_tracer(__name__)
memory = Memory("traced-agent")

def process_with_memory(user_id: str, query: str):
    with tracer.start_as_current_span("agent_process") as span:
        # Trace memory retrieval
        with tracer.start_as_current_span("memory_lookup") as memory_span:
            user_state = memory.get(f"user:{user_id}")
            memory_span.set_attribute("memory.keys_found", len(user_state or {}))
        
        # Your agent logic with memory context
        response = your_llm_call(query, context=user_state)
        
        # Trace memory updates
        with tracer.start_as_current_span("memory_update"):
            updated_state = {**user_state, "last_query": query}
            memory.set(f"user:{user_id}", updated_state)
        
        span.set_attribute("agent.memory_namespace", "traced-agent")
        return response

When NOT to Use BotWire

BotWire isn't the right choice for:

FAQ

Why not just use Redis? Redis requires setup, authentication, and infrastructure management. BotWire works immediately with no configuration - just import and use.

Is this actually free? Yes, 1000 writes/day per namespace forever. No credit card, no signup, no API keys. You only pay if you need higher limits.

What about data privacy? Self-host the open source version (MIT license) if you need full control. The hosted version at botwire.dev stores data encrypted at rest.

Get Started

Add persistent memory to your agents and see the full picture in Phoenix traces. Install BotWire and your agents will maintain state across sessions, giving you meaningful evaluation data.

pip install botwire

Check out the full API and self-hosting options at botwire.dev.

Install in one command:

pip install botwire

Start free at botwire.dev