> ## 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

Choose the right storage backend for your knowledge base — from local development with Chroma to multi-tenant production with mem0.

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

agent = Agent(
    name="Research Assistant",
    instructions="You are a research assistant.",
    knowledge=["./documents/"],
)

agent.start("What are the main findings?")
```

The user asks a question; the agent retrieves from mem0, Chroma, or a custom knowledge store you configured.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Knowledge Backends"
        A[🤖 Agent] --> B{Backend?}
        B -->|default| C[☁️ mem0]
        B -->|local| D[🗄️ Chroma]
        B -->|custom| E[🔌 KnowledgeStoreProtocol]
    end

    classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef process fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef backend fill:#189AB4,stroke:#7C90A0,color:#fff

    class A agent
    class B process
    class C,D,E backend

```

## How It Works

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant Agent
    participant Feature as Knowledge Backends

    User->>Agent: Request
    Agent->>Feature: Process request
    Feature-->>Agent: Result    Agent-->>User: Response
```

## Quick Start

<Steps>
  <Step title="Simple — enable with True">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent

    agent = Agent(
        name="ResearchAssistant",
        instructions="You are a research assistant.",
        knowledge=["./documents/"],
        memory={"user_id": "user123"},
    )

    response = agent.chat("What are the main findings?")
    ```
  </Step>

  <Step title="With backend config">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent

    agent = Agent(
        name="ResearchAssistant",
        instructions="You are a research assistant.",
        knowledge={
            "vector_store": {
                "provider": "chroma",
                "config": {
                    "collection_name": "my_docs",
                    "path": "./.praison/knowledge/my_docs",
                }
            }
        },
        memory={"user_id": "user123"},
    )
    ```
  </Step>
</Steps>

## 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                   |

## 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>

<Warning>
  **`delete_all()` requires a scope.** The Chroma and SQLite backends now enforce the same contract as mem0: an **unscoped** `delete_all()` raises `ScopeRequiredError` instead of silently wiping the entire (typically shared) collection or table. Scoped deletes are unchanged.

  ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  # ✅ Scoped — deletes only Alice's items
  knowledge.delete_all(user_id="alice")

  # ❌ Unscoped — raises ScopeRequiredError (no data-loss / cross-tenant wipe)
  knowledge.delete_all()
  ```
</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, MemoryConfig

agent = Agent(
    name="SupportBot",
    instructions="Answer using the customer's session history.",
    knowledge=["./support_docs/"],
    memory=MemoryConfig(user_id="customer_42", session_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>

### MongoDB backend

The MongoDB knowledge adapter honors the same `user_id` / `agent_id` / `run_id` scope on `add()` and `search()`.

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

agent = Agent(
    name="SupportBot",
    instructions="Answer using the customer's session history.",
    knowledge={
        "vector_store": {
            "provider": "mongodb",
            "config": {
                "connection_string": "mongodb+srv://...",
                "database": "praison",
                "collection": "support_knowledge",
            },
        },
    },
    memory=MemoryConfig(user_id="customer_42", session_id="session_2026_08_09"),
)

agent.start("What did we agree on for the refund?")
```

Direct API — same scope keywords:

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

kb = Knowledge(config={
    "vector_store": {
        "provider": "mongodb",
        "config": {
            "connection_string": "mongodb+srv://...",
            "database": "praison",
            "collection": "support_knowledge",
        },
    }
})

kb.add("Refund promised: $49 by Aug 12.",
       user_id="customer_42", agent_id="support_bot_v1", run_id="session_2026_08_09")

kb.search("refund promise",
          user_id="customer_42", agent_id="support_bot_v1", run_id="session_2026_08_09")
```

On Atlas `$vectorSearch`, the scope is injected as the stage-level `filter`; on the text-search fallback, it is merged into the `find()` query. Only provided scopes are applied — omit an identifier to broaden the search on that dimension.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Q[🔎 Query + scope] --> V{Atlas vector<br/>search available?}
    V -->|Yes| VS["$vectorSearch<br/>filter: user_id / agent_id / run_id"]
    V -->|No| TS["find #40;$text#41; + scope filter"]
    VS --> R[✅ Scoped results]
    TS --> R

    classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef decision fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff

    class Q input
    class V decision
    class VS,TS process
    class R output
```

<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`

## 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
```

## Supported Providers

Set `vector_store.provider` to any name below. The value resolves through the Knowledge adapter registry.

| Provider                                                 | Resolves to               | Notes                                                                 |
| -------------------------------------------------------- | ------------------------- | --------------------------------------------------------------------- |
| `mem0`                                                   | `Mem0Adapter`             | Default when no provider set. Multi-tenant, scope-required.           |
| `chroma`                                                 | `ChromaAdapter`           | Local persistent vector DB.                                           |
| `chromadb`                                               | `ChromaAdapter`           | Alias for `chroma`.                                                   |
| `rag`                                                    | `ChromaAdapter`           | Alias for `chroma`.                                                   |
| `mongodb`                                                | `MongoDBKnowledgeAdapter` | Requires connection string.                                           |
| `sqlite`                                                 | `SQLiteKnowledgeAdapter`  | Zero-dependency fallback.                                             |
| *anything registered via `register_knowledge_adapter()`* | Custom adapter            | See [Custom Knowledge Adapters](/docs/features/knowledge-custom-adapters). |

<Warning>
  Setting `vector_store.provider` to a name that isn't a built-in adapter and hasn't been registered raises `ValueError` with the list of valid names. This is intentional — the old behaviour silently ran on Mem0, hiding typos and custom-adapter bugs. The implicit default (omit `vector_store` entirely) still falls back to Mem0.

  Explicit falsey values (`""` or `None`) raise too. Only the missing-config default is silent.
</Warning>

<Note>
  `qdrant`, `pgvector`, `milvus`, and `pinecone` are **not** top-level Knowledge providers. They are inner backends of mem0. Set `provider: "mem0"` and place the store name inside mem0's nested `config` — see [mem0 Backend](#mem0-backend-default) below.
</Note>

## Configuration Options

### mem0 Backend (Default)

Top-level `provider` is `mem0`; qdrant, pgvector, and similar stores live inside mem0's nested `config`.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
config = {
    "vector_store": {
        "provider": "mem0",
        "config": {
            "vector_store": {
                "provider": "qdrant",  # mem0's inner backend
                "config": {
                    "collection_name": "my_collection",
                }
            }
        }
    }
}
```

### Chroma Backend

`path` is optional. Omit it and the store persists under an absolute per-project directory: `<project>/.praisonai/knowledge/chroma`. The store is isolated per project, so multiple projects (or Windows setups) never share one on-disk sqlite.

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

<Warning>
  A corrupt or cross-project Chroma store raises `RuntimeError: Chroma persist failed at '<path>' (PanicException: ...)` instead of crashing the process. Delete `<path>` and re-index, or point `path` at a fresh directory. See [Knowledge Storage](/docs/features/knowledge-storage).
</Warning>

## Error Handling

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents 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}")
```

## Collection Naming Rules

<Note>
  **Enhanced Security (PR #1597):** Knowledge stores now validate collection names to prevent SQL injection attacks.
</Note>

Knowledge stores that interpolate collection names into DDL/DML now require collection names to match `^[A-Za-z0-9_]+$`. Affected backends:

* Cassandra
* pgvector
* SingleStore vector

Invalid names raise: `ValueError("collection_name must be non-empty and contain only alphanumerics and underscores")`

**Valid examples:**

* `my_collection`
* `UserData123`
* `agent_v2_docs`

**Invalid examples:**

* `my-collection` (contains hyphen)
* `user.docs` (contains dot)
* `data collection` (contains space)
* `../../etc` (path traversal attempt)

## Best Practices

<AccordionGroup>
  <Accordion title="Always provide scope identifiers for mem0">
    The mem0 backend requires at least one scope identifier. Without it, operations raise `ScopeRequiredError`.

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    agent = Agent(
        knowledge=["./docs"],
        memory={"user_id": "alice"},
    )
    ```
  </Accordion>

  <Accordion title="Use alphanumeric collection names">
    Collection names must match `^[A-Za-z0-9_]+$` for backends that use DDL/DML (Cassandra, pgvector, SingleStore).

    Valid: `my_collection`, `UserData123`. Invalid: `my-collection`, `user.docs`.
  </Accordion>

  <Accordion title="Prefer Agent API over direct Knowledge API">
    The Agent API handles scoping, retrieval, and context injection automatically. Use the direct `Knowledge` class only when you need custom control over indexing or search.
  </Accordion>

  <Accordion title="Implement KnowledgeStoreProtocol for custom backends">
    Any class implementing `KnowledgeStoreProtocol` works as a backend — no base class inheritance needed.

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

    class MyBackend:
        def search(self, query, *, user_id=None, **kwargs): ...
        def add(self, content, *, user_id=None, **kwargs): ...
    ```
  </Accordion>
</AccordionGroup>

***

## Extension: Custom Adapters

Register your own backend under any provider name, then reach it through `vector_store.provider`.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent, Knowledge
from praisonaiagents.knowledge.adapters import register_knowledge_adapter

class QdrantKnowledgeAdapter:
    def __init__(self, config=None, verbose=0):
        self.config = config
    def store(self, *args, **kwargs): ...
    def search(self, *args, **kwargs): return []
    def delete(self, *args, **kwargs): ...

register_knowledge_adapter("qdrant", QdrantKnowledgeAdapter)

agent = Agent(
    name="Researcher",
    instructions="Answer using the knowledge base.",
    knowledge=Knowledge(config={"vector_store": {"provider": "qdrant"}}),
)
```

Register before the first `Agent(...)` or `Knowledge(...)` call reads `.memory`. See [Custom Knowledge Adapters](/docs/features/knowledge-custom-adapters) for the full protocol contract and registration API.

***

## Behaviour on Unknown Provider

An explicitly-configured provider that isn't a built-in adapter and hasn't been registered raises `ValueError`.

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

Knowledge(config={"vector_store": {"provider": "definitely_not_a_store"}})
# ValueError: unknown knowledge vector_store provider 'definitely_not_a_store'.
# Valid: chroma, chromadb, mem0, mongodb, rag, sqlite
```

Confirm which adapter you actually got:

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

kb = Knowledge(config={
    "vector_store": {
        "provider": "chroma",
        "config": {"collection_name": "my_docs"},
    }
})
print(type(kb.memory).__name__)   # 'ChromaAdapter'
```

<Warning>
  Explicit `""` and `None` raise as well. Only the implicit default (omit `vector_store` entirely) silently falls back to Mem0 with a debug log — the raise catches typos and custom-adapter registration bugs early.
</Warning>

***

## Shipped vs Plugin Providers

Top-level `vector_store.provider` accepts only providers with a registered Knowledge adapter.

| Provider                                               | Status  | How to use                                                                                                                |
| ------------------------------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------- |
| `chroma`                                               | Shipped | Works out of the box — recommended default                                                                                |
| `qdrant`, `pinecone`, `weaviate`, `milvus`, `pgvector` | Plugin  | Call `register_knowledge_adapter(name, adapter_cls)` first (see [Extension: Custom Adapters](#extension-custom-adapters)) |

<Note>
  `qdrant`, `pgvector`, `milvus`, and `pinecone` also work as **inner** backends of mem0 without registration — set `provider: "mem0"` and nest the store under mem0's own `config`. See [mem0 Backend](#mem0-backend-default).
</Note>

***

## Related

<CardGroup cols={2}>
  <Card title="Incremental Indexing" icon="arrows-rotate" href="/docs/features/incremental-indexing">
    Skip unchanged files for fast knowledge base updates
  </Card>

  <Card title="Knowledge" icon="book" href="/docs/features/knowledge">
    Core knowledge retrieval and agent integration
  </Card>

  <Card title="Vector Store" icon="database" href="/docs/features/vector-store">
    Store and query embeddings with a pluggable, namespace-aware backend
  </Card>
</CardGroup>
