Skip to main content

Stateful AI agents with editable memory blocks and persistent storage

Project description

Asterix

Stateful AI agents with editable memory blocks and persistent storage.

Note: This library is in active development. Core features are stable, but some APIs may change in future releases.

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.

Python 3.10+ License: MIT


✨ 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 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 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 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 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:

  1. core_memory_append - Add content to a memory block
  2. core_memory_replace - Replace content in a memory block
  3. archival_memory_insert - Store information in Qdrant for long-term retrieval
  4. archival_memory_search - Search archived memories semantically
  5. conversation_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:

  1. Content is summarized by LLM
  2. Full content archived in Qdrant
  3. Block replaced with summary
  4. 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 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 StateBackend

class RedisBackend(StateBackend):
    def save(self, agent_id: str, state: dict): ...
    def load(self, agent_id: str) -> dict: ...

agent = Agent(..., state_backend=RedisBackend())

⚠️ Error Handling

Asterix provides detailed error messages with context and suggestions:

Tool Errors

from asterix.tools 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:

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")

# Search archival memory
results = agent.search_archival("user preferences", k=5)
for result in results:
    print(f"Score: {result.score}, Text: {result.summary}")

Heartbeat Control (Advanced)

# Manual heartbeat loop control
controller = agent.create_heartbeat_controller()

for step in controller.run("Complex multi-step task"):
    if step.needs_tool_execution:
        # Custom tool execution logic
        results = my_custom_executor(step.tool_calls)
        controller.submit_tool_results(results)
    
    elif step.is_complete:
        response = step.get_response()
        break

🧪 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.0 (Alpha)

Roadmap:

  • Core agent implementation
  • Memory tools system
  • State persistence
  • Qdrant integration
  • Enhanced tool registration
  • Performance optimizations
  • Extended documentation
  • Tutorial series

📚 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.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/your-feature)
  3. Commit your changes (git commit -m 'Add your feature')
  4. Push to the branch (git push origin feature/your-feature)
  5. Open a Pull Request

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.


🙏 Acknowledgments


📞 Support


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

asterix_agent-0.1.2.tar.gz (116.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

asterix_agent-0.1.2-py3-none-any.whl (80.2 kB view details)

Uploaded Python 3

File details

Details for the file asterix_agent-0.1.2.tar.gz.

File metadata

  • Download URL: asterix_agent-0.1.2.tar.gz
  • Upload date:
  • Size: 116.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.4

File hashes

Hashes for asterix_agent-0.1.2.tar.gz
Algorithm Hash digest
SHA256 f0c78d271719aa289072e19adb21c05f9f31f6e274fc1fd6e7d508d80df60cb0
MD5 8e9f68f34bd80cd63629508b68e680b1
BLAKE2b-256 6b8b7fba7d2026a1c1281024fb5f070b88402ee91872ec11a66832782e6b5ff6

See more details on using hashes here.

File details

Details for the file asterix_agent-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: asterix_agent-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 80.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.4

File hashes

Hashes for asterix_agent-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 09e03a6296f5c8afce6c03b0eb92a6a7c500690a6c646cbcb7a41560880d51de
MD5 6c897136405e89a82da8c93cbd8ec6de
BLAKE2b-256 067aafe99d7e00497a934ec37269ea9f9113e984f794e3beaa1ec2eedbd62eba

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page