Stateful AI agents with editable memory blocks and persistent storage
Project description
Asterix
Stateful AI agents with editable memory blocks and persistent storage.
Note: Asterix is in Beta (v0.1.3). Core features are stable and production-ready. Enhanced features and optimizations are in active development.
Asterix is a lightweight Python library for building AI agents that can remember, learn, and persist their state across sessions. No servers required - just pip install and start building.
✨ Features
- 🧠 Editable Memory Blocks - Agents can read and write their own memory via built-in tools
- 💾 Persistent Storage - State saves across sessions (JSON/SQLite backends)
- 🔍 Semantic Search - Qdrant Cloud integration for long-term memory retrieval
- 🛠️ Enhanced Tool System - Easy decorator pattern with parameter validation, retry logic, and categories
- 📚 Auto-Documentation - Tools automatically generate markdown/JSON documentation
- 🚨 Smart Error Handling - Helpful error messages with suggestions and context
- 🔄 Multi-Model Support - Works with Groq, OpenAI, and extensible to others
- 📦 No Server Required - Pure Python library, runs anywhere
🚀 Quick Start
Installation
pip install asterix-agent
Or with UV (faster):
uv pip install asterix-agent
Basic Usage
from asterix import Agent, BlockConfig
# Create an agent with custom memory blocks
agent = Agent(
blocks={
"task": BlockConfig(size=1500, priority=1),
"notes": BlockConfig(size=1000, priority=2)
},
model="openai/gpt-5-mini"
)
# Chat with your agent
response = agent.chat("Hello! Remember that I prefer Python over JavaScript.")
print(response)
# Agent automatically updates its memory
# Memory persists across conversations
Add Custom Tools
@agent.tool(name="read_file", description="Read a file from disk")
def read_file(filepath: str) -> str:
with open(filepath, 'r') as f:
return f.read()
# Now your agent can read files
response = agent.chat("Read config.yaml and summarize the settings")
🛠️ Advanced Tool Features
Parameter Validation
Tools can define validation constraints for their parameters:
from asterix.tools.base import Tool, ParameterConstraint
@agent.tool(
name="create_user",
description="Create a new user account",
constraints={
"username": ParameterConstraint(
min_length=3,
max_length=20,
pattern=r'^[a-zA-Z0-9_]+$'
),
"age": ParameterConstraint(
min_value=13,
max_value=120
)
}
)
def create_user(username: str, age: int) -> str:
return f"Created user {username}, age {age}"
Tool Categories
Organize tools by category for better discovery:
from asterix.tools.base import ToolCategory
# Tools are automatically categorized
memory_tools = agent._tool_registry.get_by_category(ToolCategory.MEMORY)
file_tools = agent._tool_registry.get_by_category(ToolCategory.FILE_OPS)
# List all categories with counts
categories = agent._tool_registry.list_categories()
print(categories) # {"memory": 5, "file_operations": 3, "custom": 2}
Retry Logic
Enable automatic retries for transient failures:
from asterix.tools.base import Tool
@agent.tool(
name="fetch_data",
description="Fetch data from API",
retry_on_error=True,
max_retries=3
)
def fetch_data(url: str) -> str:
# Will retry up to 3 times with exponential backoff
response = requests.get(url)
return response.text
Error Handling
Rich error context and helpful suggestions:
# Automatic error suggestions
try:
agent.get_tool("read_fil") # Typo!
except ToolNotFoundError as e:
print(e) # "Tool 'read_fil' not found. Did you mean: read_file?"
Auto-Documentation
Generate documentation for your tools:
# Single tool documentation
docs = agent._tool_registry.generate_tool_docs("read_file", format="markdown")
print(docs)
# Complete registry documentation
full_docs = agent._tool_registry.generate_registry_docs(
format="markdown",
group_by_category=True
)
# Save to file
with open("TOOL_REFERENCE.md", "w") as f:
f.write(full_docs)
# Export as JSON catalog
catalog = agent._tool_registry.export_tool_catalog("json")
# Quick reference guide
quick_ref = agent._tool_registry.generate_quick_reference()
Tool Discovery
Find tools by various criteria:
# Filter by category
memory_tools = agent._tool_registry.filter_tools(category=ToolCategory.MEMORY)
# Filter by name pattern
search_tools = agent._tool_registry.filter_tools(name_pattern="search")
# Filter by capabilities
validated_tools = agent._tool_registry.filter_tools(has_validation=True)
retry_tools = agent._tool_registry.filter_tools(has_retry=True)
# Get detailed tool info
info = agent._tool_registry.get_tool_info("core_memory_append")
print(f"Category: {info['category']}")
print(f"Constraints: {info['constraints']}")
print(f"Examples: {info['examples']}")
Save & Load State
# Save agent state
agent.save_state()
# Later session - load previous state
agent = Agent.load_state("agent_id")
agent.chat("What were we discussing?") # Remembers everything!
🔍 Logging
Asterix uses Python's standard logging module. By default, logs are not displayed. To enable logging:
Console Logging
import logging
# Show all logs
logging.basicConfig(level=logging.INFO)
# Or just show errors
logging.basicConfig(level=logging.ERROR)
File Logging
import logging
logging.basicConfig(
level=logging.DEBUG,
filename='asterix.log',
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
Fine-grained Control
import logging
# Control specific modules
logging.getLogger('asterix.agent').setLevel(logging.DEBUG)
logging.getLogger('asterix.core').setLevel(logging.WARNING)
📚 Configuration
Environment Variables
Create a .env file in your project root:
# LLM Provider (at least one required)
GROQ_API_KEY=your-groq-api-key
OPENAI_API_KEY=your-openai-api-key
# Vector Storage (required)
QDRANT_URL=https://your-cluster.cloud.qdrant.io:6333
QDRANT_API_KEY=your-qdrant-api-key
# Optional
ASTERIX_STATE_DIR=./agent_states
ASTERIX_LOG_LEVEL=INFO
YAML Configuration (Optional)
# agent_config.yaml
agent_id: "my_agent"
max_heartbeat_steps: 10
# LLM Configuration
llm:
provider: "openai"
model: "gpt-5-mini"
temperature: 0.1
max_tokens: 1000
# Memory Blocks
blocks:
task:
size: 1500
priority: 1
description: "Current task and progress"
notes:
size: 1000
priority: 2
description: "Important notes and reminders"
# Storage
storage:
qdrant_url: "${QDRANT_URL}"
qdrant_api_key: "${QDRANT_API_KEY}"
state_backend: "json"
state_dir: "./agent_states"
# Memory Management
memory:
eviction_strategy: "summarize_and_archive"
context_window_threshold: 0.85
# Embeddings
embedding:
provider: "openai"
model: "text-embedding-3-small"
dimensions: 1536
Load from YAML:
agent = Agent.from_yaml("agent_config.yaml")
Tool Configuration
Configure tool behavior when registering:
from asterix.tools.base import Tool, ToolCategory, ParameterConstraint
@agent.tool(
name="advanced_tool",
description="Tool with full configuration",
category=ToolCategory.DATA,
constraints={
"query": ParameterConstraint(min_length=1, max_length=500)
},
examples=[
"advanced_tool(query='search term')",
"advanced_tool(query='another example')"
],
retry_on_error=True,
max_retries=3
)
def advanced_tool(query: str) -> str:
return f"Processed: {query}"
🧠 Memory System
Built-in Memory Tools
Agents have 5 built-in tools for managing their memory:
core_memory_append- Add content to a memory blockcore_memory_replace- Replace content in a memory blockarchival_memory_insert- Store information in Qdrant for long-term retrievalarchival_memory_search- Search archived memories semanticallyconversation_search- Search conversation history
These tools are called automatically by the agent when needed.
Memory Blocks
Configure memory blocks with custom sizes and priorities:
blocks = {
"task": BlockConfig(
size=2000, # Max tokens before eviction
priority=1, # Lower = evicted first
description="Current task context"
),
"user_prefs": BlockConfig(
size=500,
priority=5, # High priority = rarely evicted
description="User preferences and settings"
)
}
Automatic Memory Management
When a block exceeds its token limit:
- Content is summarized by LLM
- Full content archived in Qdrant
- Block replaced with summary
- Original retrievable via semantic search
🛠️ Custom Tools
Register custom tools using the decorator pattern:
from asterix import Agent
agent = Agent(...)
@agent.tool(
name="execute_shell",
description="Run a shell command and return output"
)
def execute_shell(command: str) -> str:
import subprocess
result = subprocess.run(command, shell=True, capture_output=True, text=True)
return result.stdout
@agent.tool(name="search_web")
def search_web(query: str) -> str:
# Your web search implementation
return "Search results..."
# Agent can now use these tools
response = agent.chat("List all Python files in the current directory")
Tool Development
Creating custom tools with full features:
from asterix.tools.base import Tool, ToolCategory, ParameterConstraint
class MyCustomTool(Tool):
def __init__(self):
super().__init__(
name="my_tool",
description="Custom tool with validation",
func=self.execute,
category=ToolCategory.CUSTOM,
constraints={
"param": ParameterConstraint(min_length=5)
},
retry_on_error=True,
max_retries=2
)
def execute(self, param: str) -> str:
# Your tool logic here
return f"Processed: {param}"
# Register with agent
agent.register_tool(MyCustomTool())
💾 State Persistence
Save & Load
# Save agent state to disk
agent.save_state() # Saves to ./agent_states/{agent_id}.json
# Load from disk
agent = Agent.load_state("agent_id")
# Custom state directory
agent = Agent(..., state_dir="./my_agents")
agent.save_state()
State Backends
# JSON (default)
agent = Agent(..., state_backend="json")
# SQLite (better for many agents)
agent = Agent(..., state_backend="sqlite", state_db="agents.db")
# Custom backend
from asterix.storage import SQLiteStateBackend
# Example structure for custom backend:
class RedisBackend:
def save_state(self, agent_id: str, state: dict) -> None:
"""Save agent state"""
pass
def load_state(self, agent_id: str) -> dict:
"""Load agent state"""
pass
agent = Agent(..., state_backend=RedisBackend())
⚠️ Error Handling
Asterix provides detailed error messages with context and suggestions:
Tool Errors
from asterix.tools.base import ToolNotFoundError, ToolExecutionError, ToolValidationError
try:
# Typo in tool name
agent._tool_registry.execute_tool("read_fle", filepath="test.txt")
except ToolNotFoundError as e:
print(e) # Suggests similar tool names
try:
# Invalid parameter
agent._tool_registry.execute_tool("create_user", username="ab", age=5)
except ToolValidationError as e:
print(e) # Shows validation constraints and provided value
Validation Errors
# Parameter validation errors include helpful context
# "Tool 'create_user' parameter 'username' validation failed:
# username length must be >= 3, got 2
# Provided value: ab"
Error Context
All tool errors include rich metadata for debugging:
try:
result = tool.execute(invalid_param="value")
except Exception as e:
# Error metadata includes:
# - Tool name
# - Exception type
# - Parameters provided
# - Retry attempts (if applicable)
# - Full stack trace in logs
pass
📖 Examples
For complete working examples, see the examples/ directory:
basic_chat.py- Simple conversation agentcustom_tools.py- Tool registration with validationpersistent_agent.py- State save/load demonstrationtool_documentation.py- Auto-documentation generationcli_agent.py- Full-featured CLI agent with file operationsyaml_config.py- YAML configuration example
CLI Agent with File Operations
from asterix import Agent, BlockConfig
import os
agent = Agent(
blocks={
"current_task": BlockConfig(size=2000, priority=1),
"file_context": BlockConfig(size=3000, priority=2)
},
model="openai/gpt-5-mini"
)
@agent.tool(name="list_files")
def list_files(directory: str = ".") -> str:
files = os.listdir(directory)
return "\n".join(files)
@agent.tool(name="read_file")
def read_file(filepath: str) -> str:
with open(filepath, 'r') as f:
return f.read()
# Use the agent
agent.chat("List all Python files and review main.py for potential issues")
Multi-Agent System
# Orchestrator agent
main_agent = Agent(
agent_id="orchestrator",
blocks={"plan": BlockConfig(size=1500)},
model="openai/gpt-5-mini"
)
# Specialized agents
code_reviewer = Agent(
agent_id="reviewer",
blocks={"code": BlockConfig(size=3000)},
model="openai/gpt-5-mini"
)
# Coordination
task = "Review auth.py for security issues"
plan = main_agent.chat(f"Break down: {task}")
review = code_reviewer.chat(f"Execute: {plan}")
summary = main_agent.chat(f"Summarize: {review}")
🔧 Advanced Usage
Direct Memory Access
# Get all memory blocks
memory = agent.get_memory()
print(memory["task"])
# Update memory manually
agent.update_memory("task", "New content")
# Archival memory search is handled automatically by the agent
# when it needs to retrieve information. The agent will use the
# archival_memory_search tool internally when needed.
# To manually search, you can access the tool:
tool_result = agent._tool_registry.execute_tool(
"archival_memory_search",
query="user preferences",
k=5
)
🧪 Testing
# Install dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# With coverage
pytest --cov=asterix --cov-report=html
# Run specific test
pytest tests/test_agent.py::test_memory_tools
📊 Project Status
Current Version: 0.1.3 (Beta)
Roadmap:
- Core agent implementation
- Memory tools system
- State persistence
- Qdrant integration
- Enhanced tool registration (parameter validation, categories, retry logic)
- Auto-documentation system
- Performance optimizations
- Advanced monitoring and observability
- Streaming responses
- Multi-agent collaboration
- Custom memory backends (Redis, PostgreSQL)
📚 Tool Reference
Built-in Memory Tools
Asterix provides 5 built-in tools for memory management:
| Tool | Category | Description |
|---|---|---|
core_memory_append |
memory | Add content to a memory block |
core_memory_replace |
memory | Replace content in a memory block |
archival_memory_insert |
memory | Store information in long-term memory (Qdrant) |
archival_memory_search |
memory | Search long-term memory |
conversation_search |
memory | Search conversation history |
Tool System Features
- Automatic Schema Generation - Type hints → OpenAI function schemas
- Parameter Validation - Min/max values, lengths, patterns, allowed values
- Category Organization - Group tools by purpose (memory, file_ops, web, etc.)
- Retry Logic - Automatic retries with exponential backoff
- Error Recovery - Smart error messages with hints and suggestions
- Auto-Documentation - Generate markdown/JSON/YAML docs from metadata
- Tool Discovery - Filter and search tools by name, category, capabilities
Generate Documentation
# In Python
from asterix import Agent
agent = Agent()
# Generate tool reference
docs = agent._tool_registry.generate_registry_docs(format="markdown")
with open("TOOL_REFERENCE.md", "w") as f:
f.write(docs)
# Export tool catalog
catalog = agent._tool_registry.export_tool_catalog("json")
with open("tool_catalog.json", "w") as f:
f.write(catalog)
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/your-feature) - Commit your changes (
git commit -m 'Add your feature') - Push to the branch (
git push origin feature/your-feature) - Open a Pull Request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments
📞 Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: Full Docs
So that everyone can build better agents without worrying about memory (Let's hope OpenAI doesn't make this library meaningless)
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file asterix_agent-0.1.3.tar.gz.
File metadata
- Download URL: asterix_agent-0.1.3.tar.gz
- Upload date:
- Size: 117.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d41555639b5359ddf9453fbfa2e8f0282d42c47ab32b5f612c8c46cad01452c4
|
|
| MD5 |
97093e59ef404a5d7b71eaf190ddf356
|
|
| BLAKE2b-256 |
ef3f5eeb9db19d4cb10851da355a8a8d328839a3c9294757f633aaee4c06a2db
|
File details
Details for the file asterix_agent-0.1.3-py3-none-any.whl.
File metadata
- Download URL: asterix_agent-0.1.3-py3-none-any.whl
- Upload date:
- Size: 80.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a7d84e706ada96919c840ef8261c8022eca4d755d367e05a0854ac1a6d7389bb
|
|
| MD5 |
5adda483e99539a067260bde7bbaba1f
|
|
| BLAKE2b-256 |
709c266ddaa930a57cd4619d6a5c7316c285542412cac1824b4dc24c8f39ae02
|