Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 101 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,105 @@ RunAgent Cloud provides:

---

## 🧠 Persistent Memory: Revolutionary Serverless Memory System

RunAgent introduces **Persistent Memory** - the fastest serverless memory system for AI agents. Unlike traditional stateless serverless architectures, RunAgent enables your agents to maintain context and state across executions, creating truly intelligent and context-aware applications.

### Why Persistent Memory Matters

Traditional serverless functions are stateless by design, meaning each invocation starts fresh with no memory of previous interactions. RunAgent's Persistent Memory breaks this limitation, allowing your agents to:

- **Remember Context** - Maintain conversation history and user preferences across sessions
- **Learn from Interactions** - Build upon previous executions to improve responses
- **Stateful Workflows** - Create multi-step processes that remember where they left off
- **Cross-Language Persistence** - Memory works seamlessly across all SDK languages (Python, JavaScript, Rust, Go, Dart)

### How It Works

Persistent Memory in RunAgent is designed for speed and reliability:

```python
from runagent import RunAgentClient

# Create a client with persistent memory enabled
client = RunAgentClient(
agent_id="my-agent-id",
entrypoint_tag="chat",
user_id="user123", # User identifier for memory isolation
persistent_memory=True # Enable persistent memory
)

# First interaction - agent learns user preferences
result1 = client.run(message="I prefer dark mode interfaces")

# Second interaction - agent remembers the preference
result2 = client.run(message="What's my UI preference?")
# Agent responds: "You prefer dark mode interfaces"
```

### Multi-Language Support

Persistent Memory works identically across all SDKs:

**Python:**
```python
client = RunAgentClient(
agent_id="agent-id",
entrypoint_tag="entrypoint",
user_id="user123",
persistent_memory=True
)
```

**JavaScript:**
```javascript
const client = new RunAgentClient({
agentId: "agent-id",
entrypointTag: "entrypoint",
userId: "user123",
persistentMemory: true
});
```

**Rust:**
```rust
let client = RunAgentClient::new(
RunAgentClientConfig::new("agent-id", "entrypoint")
.with_user_id("user123")
.with_persistent_memory(true)
).await?;
```

**Dart:**
```dart
final client = await RunAgentClient.create(
RunAgentClientConfig.create(
agentId: "agent-id",
entrypointTag: "entrypoint",
userId: "user123",
persistentMemory: true,
),
);
```

### Key Benefits

- ⚑ **Fastest Serverless Memory** - Optimized for low-latency access and updates
- πŸ”’ **Secure & Isolated** - Each `user_id` has isolated memory space
- 🌐 **Universal** - Works with any framework (LangGraph, CrewAI, Letta, etc.)
- πŸ“ˆ **Scalable** - Built on serverless infrastructure that scales automatically
- πŸ”„ **Stateful Workflows** - Enable complex multi-turn conversations and workflows

### Use Cases

- **Conversational AI** - Maintain context across multiple user interactions
- **Personalization** - Remember user preferences and adapt responses
- **Multi-Step Processes** - Track progress through complex workflows
- **Learning Systems** - Agents that improve based on interaction history
- **Session Management** - Maintain state across distributed systems

---

## πŸ“š Documentation

- **[Getting Started](https://docs.run-agent.ai/get-started/introduction.md)** - Deploy your first agent in 5 minutes
Expand All @@ -527,7 +626,7 @@ RunAgent Cloud provides:

## 🧠 Action Memory System (Coming Soon)

RunAgent is introducing **Action Memory** - a revolutionary approach to agent reliability that focuses on *how to remember* rather than *what to remember*.
Building on our Persistent Memory foundation, RunAgent is introducing **Action Memory** - an advanced approach to agent reliability that focuses on *how to remember* rather than *what to remember*.

### How It Will Work

Expand All @@ -536,7 +635,7 @@ RunAgent is introducing **Action Memory** - a revolutionary approach to agent re
- **Reliability Focus**: Learns from successful outcomes to improve future decisions
- **Ecosystem Integration**: Works with any framework - LangGraph, CrewAI, Letta, and more

This will ensure your agents become more reliable over time, regardless of which programming language or framework you use to interact with them.
This will ensure your agents become more reliable over time, building upon the Persistent Memory system to create truly intelligent, context-aware agents.

---

Expand Down
166 changes: 166 additions & 0 deletions examples/agno_persistent/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
from textwrap import dedent
from typing import List, Optional
from agno.agent import Agent
from agno.db.base import SessionType
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIChat
from agno.session import AgentSession

# Single SQLite DB for all agents/sessions
# Note: The backend automatically maps "rad" folder to /persistent/rad
# So we can use relative path "rad/agents.db" and it will persist across VM restarts
db = SqliteDb(db_file="rad/agents.db")


def get_chat_agent(
user: str = "user",
new_session: bool = True,
session_id: Optional[str] = None,
) -> Agent:
"""
Create and return a simple persistent chatbot Agent.

Args:
user: User ID to associate with the agent.
new_session: If False, will try to resume the latest existing session
for this user (unless session_id is provided).
session_id: If provided and new_session=False, use this specific session.

Returns:
An initialized Agent instance.
"""
resolved_session_id: Optional[str] = None

if not new_session:
# If caller gave a specific session_id, use it
if session_id is not None:
resolved_session_id = session_id
else:
# Otherwise, try to resume the latest existing session for this user
existing_sessions: List[AgentSession] = db.get_sessions( # type: ignore
user_id=user,
session_type=SessionType.AGENT,
)
if len(existing_sessions) > 0:
resolved_session_id = existing_sessions[0].session_id

agent = Agent(
user_id=user,
session_id=resolved_session_id,
model=OpenAIChat(id="gpt-4o-mini"),
instructions=dedent(
"""\
You are a helpful, general-purpose AI assistant.

Goals:
- Answer questions clearly and concisely.
- Ask for clarification when needed.
- Use a friendly, professional tone.

You have access to the conversation history via the session.
Use that context to keep multi-turn conversations coherent.
"""
),
db=db,
# Persist and reuse chat history across runs for this session
read_chat_history=True,
markdown=True,
)

return agent


def agent_print_response(prompt: str = None, user: str = "user", session_id: Optional[str] = None, new_session: bool = False, **kwargs):
"""
Non-streaming response with session management.

Args:
prompt: User's message/prompt (can also be passed as 'message' in kwargs)
user: User ID for session management (default: "user")
session_id: Optional specific session ID to resume (default: None, will auto-resume latest)
new_session: If True, creates a new session. If False, resumes existing session (default: False)
**kwargs: Additional parameters (supports 'message' as alias for 'prompt')

Returns:
Serializable response content with session metadata
"""
# Handle prompt from kwargs if not provided directly
if prompt is None:
prompt = kwargs.get('message', '')

agent = get_chat_agent(user=user, new_session=new_session, session_id=session_id)

# Get the response object
response = agent.run(prompt)

# Extract the actual content from the response object
if hasattr(response, 'content'):
return {
"content": response.content,
"session_id": agent.session_id,
"user_id": agent.user_id
}
elif hasattr(response, 'messages') and response.messages:
# Get the last message content
content = response.messages[-1].content if hasattr(response.messages[-1], 'content') else str(response.messages[-1])
return {
"content": content,
"session_id": agent.session_id,
"user_id": agent.user_id
}
elif hasattr(response, 'text'):
return {
"content": response.text,
"session_id": agent.session_id,
"user_id": agent.user_id
}
else:
# Fallback: convert to string
return {
"content": str(response),
"session_id": agent.session_id,
"user_id": agent.user_id
}


def agent_print_response_stream(prompt: str = None, user: str = "user", session_id: Optional[str] = None, new_session: bool = False, **kwargs):
"""
Streaming response with session management.

Args:
prompt: User's message/prompt (can also be passed as 'message' in kwargs)
user: User ID for session management (default: "user")
session_id: Optional specific session ID to resume (default: None, will auto-resume latest)
new_session: If True, creates a new session. If False, resumes existing session (default: False)
**kwargs: Additional parameters (supports 'message' as alias for 'prompt')

Yields:
Serializable chunks with content and session metadata
"""
# Handle prompt from kwargs if not provided directly
if prompt is None:
prompt = kwargs.get('message', '')

agent = get_chat_agent(user=user, new_session=new_session, session_id=session_id)

# Yield session info first
yield {
"type": "session_info",
"session_id": agent.session_id,
"user_id": agent.user_id
}

# Stream the response
for chunk in agent.run(prompt, stream=True):
yield {
"type": "content",
"content": chunk.content if hasattr(chunk, 'content') else str(chunk)
}

# Yield final session confirmation
yield {
"type": "session_end",
"session_id": agent.session_id
}


2 changes: 2 additions & 0 deletions examples/agno_persistent/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
agno
openai
33 changes: 33 additions & 0 deletions examples/agno_persistent/runagent.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"agent_name": "Agno Memory",
"description": "My AI agent",
"framework": "default",
"template": "",
"version": "1.0.0",
"created_at": "2025-11-28T20:59:22.479436",
"template_source": {
"repo_url": "https://github.com/runagent-dev/runagent.git",
"author": "runagent-cli",
"path": "/home/azureuser/runagent/examples/agno_persistent"
},
"agent_architecture": {
"entrypoints": [
{
"file": "agent.py",
"module": "agent_print_response",
"tag":"agno_print_response"
},
{
"file": "agent.py",
"module": "agent_print_response_stream",
"tag":"agno_print_response_stream"
}
]
},
"env_vars": {},
"agent_id": "b298c025-4c9f-4466-886e-14745efe664b",
"auth_settings": {
"type": "api_key"
},
"persistent_folders": ["rad"]
}
10 changes: 8 additions & 2 deletions examples/journalist_agent/agent/runagent.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,14 @@
]
},
"env_vars": {},
"agent_id": "9fac4988-d88e-4d6c-994c-7495c11de8b9",
"agent_id": "9fac4988-d88e-4d6c-994c-7495c14de8b9",
"auth_settings": {
"type": "api_key"
}
},
"persistent_folders": [
".network",
".sqlite",
"data",
"embeddings"
]
}
Loading