> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Knowledge Backends

> Configure and use different knowledge storage backends in PraisonAI

# Knowledge Backends

PraisonAI supports multiple knowledge storage backends through a protocol-driven architecture. This allows you to choose the best backend for your use case while maintaining a consistent API.

## Available Backends

| Backend            | Description                           | Best For                           |
| ------------------ | ------------------------------------- | ---------------------------------- |
| **mem0** (default) | Long-term memory with semantic search | Multi-user apps, persistent memory |
| **chroma**         | Local vector database                 | Development, single-user apps      |
| **internal**       | Built-in lightweight storage          | Simple use cases                   |

## Agent-First Usage

The recommended way to use knowledge is through the Agent API:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

# Create agent with knowledge (uses mem0 by default)
agent = Agent(
    name="ResearchAssistant",
    instructions="You are a research assistant.",
    knowledge=["./documents/"],  # Add documents
    memory={"user_id": "user123"} ,  # Required for mem0 backend
)

# Chat automatically retrieves relevant context
response = agent.chat("What are the main findings?")
```

## Scope Identifiers

Knowledge backends support three scope identifiers for multi-tenant isolation:

| Identifier | Purpose                | Example               |
| ---------- | ---------------------- | --------------------- |
| `user_id`  | Isolate per user       | `"user_alice"`        |
| `agent_id` | Isolate per agent type | `"research_agent_v1"` |
| `run_id`   | Isolate per session    | `"session_abc123"`    |

<Warning>
  The **mem0 backend requires at least one scope identifier**. If none is provided, operations will fail with a `ScopeRequiredError`.
</Warning>

### Example with Scope

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

# User-scoped knowledge
agent = Agent(
    name="PersonalAssistant",
    instructions="You are a personal assistant.",
    knowledge=["./user_docs/"],
    memory={"user_id": "alice"} ,  # Knowledge scoped to Alice
)

# Agent-scoped knowledge (shared across users)
shared_agent = Agent(
    name="CompanyBot",
    instructions="You answer company policy questions.",
    knowledge=["./policies/"],
    agent_id="company_bot_v1",  # Shared knowledge
)
```

### Combining Multiple Scopes

Combine `user_id`, `agent_id`, and `run_id` to isolate knowledge down to a specific session for a specific agent and user.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

agent = Agent(
    name="SupportBot",
    instructions="Answer using the customer's session history.",
    knowledge=["./support_docs/"],
    user_id="customer_42",
    agent_id="support_bot_v1",
    run_id="session_2026_05_30",
)

agent.start("What did we discuss about my refund?")
```

You can also use the direct API for more control:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
results = knowledge.search(
    "refund discussion",
    user_id="customer_42",
    agent_id="support_bot_v1",
    run_id="session_2026_05_30",
)
```

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Combined Scope Filtering"
        A[📋 user_id] --> D[🔍 ChromaDB where = $and[...]]
        B[🤖 agent_id] --> D
        C[💬 run_id] --> D
        D --> E[✅ scoped results]
    end
    
    classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff
    
    class A,B,C input
    class D process
    class E output
```

<Note>
  When you pass more than one scope identifier, PraisonAI automatically combines them using ChromaDB's `$and` operator. A single identifier is passed through unchanged. You don't need to write the `$and` yourself.
</Note>

<Tip>
  All provided identifiers are required to match (logical AND). Omit an identifier to broaden the scope on that dimension.
</Tip>

**Multi-tenant SaaS application flow:**

* **Per-customer isolation** → set `user_id`
* **Per-agent isolation** (e.g. SupportBot vs. SalesBot share infra but not data) → also set `agent_id`
* **Per-conversation isolation** (e.g. ephemeral session memory) → also set `run_id`

## Identifier Naming Rules

**SQL/CQL backends** (PGVector, SingleStore, Cassandra) enforce strict identifier validation for security. Collection names and related identifiers must match the pattern `[A-Za-z0-9_]+` to prevent SQL injection attacks.

### Affected Fields

| Field             | Backend                          | Description                      |
| ----------------- | -------------------------------- | -------------------------------- |
| `collection_name` | PGVector, SingleStore, Cassandra | Table/collection name            |
| `schema`          | PGVector                         | PostgreSQL schema name           |
| `keyspace`        | Cassandra                        | Cassandra keyspace name          |
| `table_prefix`    | All SQL backends                 | Prefix for generated table names |

<Note>
  **mem0** and **chroma** backends are **not affected** by these restrictions. They accept any valid string identifiers.
</Note>

### Passing Examples

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# These identifiers pass validation
config = {
    "vector_store": {
        "provider": "pgvector",
        "config": {
            "collection_name": "user_docs",      # ✅ Valid
            "schema": "ai_knowledge",            # ✅ Valid
            "table_prefix": "knowledge_store_"   # ✅ Valid
        }
    }
}
```

### Failing Examples

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# These identifiers fail validation with ValueError
config = {
    "vector_store": {
        "provider": "pgvector", 
        "config": {
            "collection_name": "my-collection",    # ❌ Contains dash
            "schema": "public.docs",               # ❌ Contains dot
            "table_prefix": "data with spaces"     # ❌ Contains spaces
        }
    }
}
```

### Security Context

This validation was added in **PraisonAI 4.6.34** to address **GHSA-3643-7v76-5cj2** (SQL identifier injection). Previously, user-controlled collection names were interpolated directly into SQL DDL/DML statements. For more details, see the [security advisory](https://github.com/advisories/GHSA-3643-7v76-5cj2).

## Direct Knowledge API

For advanced use cases, you can use the Knowledge class directly:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Knowledge

# Initialize with config
knowledge = Knowledge(config={
    "vector_store": {
        "provider": "chroma",
        "config": {
            "collection_name": "my_docs",
            "path": "./.praison/knowledge/my_docs",
        }
    }
})

# Add documents
knowledge.add("./documents/", memory={"user_id": "user123"})

# Search
results = knowledge.search("query", user_id="user123", limit=10)
```

## Normalization Guarantees

PraisonAI normalizes all backend results to ensure consistent behavior:

* **metadata is ALWAYS a dict** (never `None`)
* **text field is always present** (mapped from `memory` for mem0)
* **score is always a float** (defaults to 0.0)

This means you can safely access metadata without null checks:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Safe - metadata is guaranteed to be a dict
for result in results['results']:
    source = result.get('metadata', {}).get('source', 'unknown')
    # This works even if the backend returns metadata=None
```

## Protocol-Driven Architecture

All backends implement the `KnowledgeStoreProtocol`:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import KnowledgeStoreProtocol

class MyCustomBackend:
    """Custom backend implementing the protocol."""
    
    def search(self, query, *, user_id=None, agent_id=None, run_id=None, **kwargs):
        # Your implementation
        pass
    
    def add(self, content, *, user_id=None, agent_id=None, run_id=None, **kwargs):
        # Your implementation
        pass
    
    # ... other methods
```

## Configuration Options

### mem0 Backend (Default)

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
config = {
    "vector_store": {
        "provider": "qdrant",  # mem0 uses qdrant by default
        "config": {
            "collection_name": "my_collection",
        }
    }
}
```

### Chroma Backend

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
config = {
    "vector_store": {
        "provider": "chroma",
        "config": {
            "collection_name": "my_collection",
            "path": "./.praison/knowledge/my_collection",
        }
    }
}
```

## Error Handling

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents.knowledge import (
    ScopeRequiredError,
    BackendNotAvailableError,
)

try:
    results = knowledge.search("query")  # Missing scope!
except ScopeRequiredError as e:
    print(f"Please provide user_id, agent_id, or run_id: {e}")
except BackendNotAvailableError as e:
    print(f"Backend not available: {e}")
```

## Best Practices

1. **Always provide scope identifiers** for mem0 backend
2. **Use user\_id for user-specific data** (multi-tenant apps)
3. **Use agent\_id for shared agent knowledge** (company policies, FAQs)
4. **Use run\_id for ephemeral session data** (conversation context)
5. **Prefer Agent API over direct Knowledge API** for most use cases
