Blog | Entwickler | | 14 Minuten Lesezeit

Ersetzen Sie den CrewAI-Speicher in der Produktion durch die VectorAI-Datenbank

Ersetzen des CrewAI-Speichers in der Produktion durch die VectorAI-Datenbank

Zusammenfassung

  • In diesem Tutorial wird das Standard-Speicher-Backend von CrewAI durch VectorAI DB ersetzt, um den Speicher der Agenten produktionsbereit er zu gestalten.
  • Es befasst sich mit drei zentralen Problemen: parallele Sperren, Speicherverlust nach Neustarts und Speicherlecks zwischen Benutzern.
  • Ein benutzerdefinierter VectorAIStorage-Anbieter fügt persistenten Vektorspeicher hinzu, ohne dabei bestehende Agenten, Aufgaben und die Crew-Logik zu verändern.
  • Per-Nutzer -Bereiche isolieren Speicher in Multi-Tenant-Bereitstellungen, sodass Agenten nur den Kontext abrufen, der zur richtigen Nutzer gehört.
  • Das Muster verbessert die persistente Agentenspeicherung, ohne dabei das Verhalten bei der langfristigen Speicherung in SQLite und der Datenextraktion aus dem Speicher zu verändern.

Wenn Sie eine CrewAI-Anwendung mit memory=True und du siehst "database is locked" Fehler unter gleichzeitiger Auslastung, Speicherverlust nach einem Neustart des Containers oder Speicherleck zwischen Benutzern in einer Multi-Tenant- Deployment – alle drei lassen sich auf dieselbe Ursache zurückführen: Das standardmäßige Speicher-Backend von CrewAI hält den Produktionsbedingungen nicht stand.

In diesem Tutorial erfahren Sie, wie Sie es durch VectorAI DB ersetzen können. Für die Umstellung sind eine neue Datei und zwei Zeilen in Ihrer Crew-Instanziierung erforderlich. Ihre Agenten, Aufgaben und die Crew-Logik bleiben dabei unverändert.

Voraussetzungen

Bevor Sie beginnen, benötigen Sie:

  • CrewAI 1.14.6 installiert
  • Docker ist installiert und läuft
  • VectorAI DB Community Edition lokal ausgeführt
  • Python .10 oder höher
  • Ein OpenAI-API-Schlüssel

Warum das Standard-Speicher-Backend im Produktivbetrieb versagt

Das Standard-Speicher-Backend von CrewAI funktioniert in der Entwicklung gut. Unter Produktionsbedingungen treten jedoch drei spezifische Fehler auf.

Parallele Verriegelung

Aktuelle CrewAI-Versionen verwenden LanceDB mit einem Wiederholungsmechanismus. Das mindert das Problem zwar, beseitigt es aber nicht vollständig. In der Anleitung zur Einrichtung des mem0-Produktionsspeichers wird darauf hingewiesen, dass der parallele Betrieb mehrerer Crews auf gemeinsam genutztem Speicher weiterhin zu „Database is locked“-Fehlern führen kann. Zu viele gleichzeitige Schreibvorgänge können zudem das Wiederholungslimit von LanceDB ausschöpfen und zu fehlgeschlagenen Schreibvorgängen führen. Frühere CrewAI-Versionen verwendeten ChromaDB als Standard-Vektor-Backend, das unter gleichzeitiger Last eigene Einschränkungen hinsichtlich der Single-Thread-Verarbeitung aufweist. Einen tieferen Einblick, wie sich diese „ Zustimmung “-Beschränkungen auf den Einsatz von Produktionsagenten auswirken, bietet unser Vergleich der Vektordatenbanken „ eingebettet “.

Kurzzeitlagerung in Behältern

Der Standardspeicherort ist an den Rechner gebunden. Ohne explizite Einbindung eines Volumes verschwindet das lokale LanceDB-Verzeichnis beim Neustart des Containers, und alle gespeicherten Daten gehen damit verloren. Wie TechJack Solutions in ihrem CrewAI-Produktionsleitfaden feststellt: „Der standardmäßige lokale Speicher ist in Containern kurzlebig.“ Wir haben dies direkt überprüft, und das Testskript ist auf GitHub verfügbar. Durch das Löschen des Speicherverzeichnisses wurden alle gespeicherten Daten unwiederbringlich gelöscht.

Keine pro-Nutzer isolation

Da das mem0-Team merkt an: „Es gibt keine per-Nutzer -isolation -Anweisung für CrewAI-Speichertypen.“ In unserem Test hat eine Variable ohne Gültigkeitsbereich recall() Ein Aufruf mit zwei Benutzern in derselben Sammlung lieferte die privaten Datensätze beider Benutzer in derselben Ergebnismenge zurück.

Speicher-Backend-Architektur

Alle drei Fehler lassen sich auf dieselbe Ursache zurückführen: das Standard-Speicher-Backend. So können Sie es ersetzen.

So funktioniert die Konfiguration des externen Speichers bei CrewAI

Zwei Dinge verdeutlichen den Austausch: wie CrewAI den Speicher initialisiert und wo der Austausch stattfindet.

Wenn Sie memory=True In einer Crew initialisiert CrewAI automatisch eine Memory Eine von LanceDB unterstützte Instanz. Sie speichert Datensätze nach der Ausführung von „ Aufgabe “, ruft vor jedem Agenten-Zug den relevanten Kontext ab und nutzt eine Schreibwarteschlange im Hintergrund, sodass die Speicherung die Ausführung des Agenten nicht blockiert.

Die Klasse „Memory“ akzeptiert einen Speicherparameter, der jedes Objekt aufnehmen kann, das das „StorageBackend“-Protokoll implementiert. Dieses Protokoll definiert die Schnittstelle, die CrewAI beim Lesen und Schreiben von Speicherinhalten aufruft. Der folgende Auszug implementiert save() und search(). Die übrigen Protokollmethoden finden Sie in der GitHub-Repo. Wenn Sie ein benutzerdefiniertes Speicherobjekt übergeben, leitet CrewAI alle Speicheroperationen über dieses Objekt statt über das standardmäßige LanceDB-Backend weiter.

LautNutzer funktioniertisolation über die scope Parameter. Jeder Speicher Aufzeichnung t mit einem Gültigkeitsbereichspfad versehen, und jeder Abrufvorgang filtert anhand dieses Pfads. Die Übergabe eines Gültigkeitsbereichspfads wie /user/alice, wobei alice ist die eindeutige Kennung des „ Nutzer“; bei jedem Schreib- und Lesevorgang werden Alices Erinnerungen niemals in Bobs Ergebnissen angezeigt und umgekehrt.

Ablauf des Schreib- und Lesevorgangs im Speicher

Die folgenden Schritte ersetzen lediglich das Vektorspeicher-Backend.

Schritt 1: Abhängigkeiten installieren

Installieren Sie die beiden Pakete gemeinsam:

pip install "crewai==1.14.6" actian-vectorai-client

In unserer Testumgebung wiesen diese beiden Pakete widersprüchliche Protobuf-Anforderungen auf. Die Abhängigkeitskette von CrewAI legt fest, dass protobuf<6.0 über opentelemetry-proto==1.34.1, während actian-vectorai-client erfordert die 6.33 Gencode-Laufzeitumgebung. Das Versionsschema von Protobuf bedeutet, dass ein 7.x Python Die Laufzeitumgebung erfüllt die Anforderungen von Gencode 6.33, da neuere Laufzeitumgebungen abwärtskompatibel mit älteren Gencode-Versionen sind. Legen Sie die Protobuf-Version fest, die wir validiert haben:

pip install "protobuf==7.35.1"

Du wirst ein pip check Warnung bezüglich opentelemetry-proto Danach. Hierbei handelt es sich lediglich um eine Warnung bezüglich der Einschränkung „ Metadaten “. Wir haben überprüft, dass CrewAI und die OpenTelemetry-Exporter unter Protobuf 7.35.1 korrekt importiert und ausgeführt werden.

Schritt 2: VectorAI DB starten

Laden Sie das Image herunter und starten Sie den Container mit einer Volume-Einbindung, damit der Speicher auch nach einem Neustart erhalten bleibt:

docker pull actian/vectorai:latest
docker run -d --name vectorai \
  -v ./local_data:/var/lib/actian-vectorai \
  -p 6573-6575:6573-6575 \
  -e ACTIAN_VECTORAI_ACCEPT_EULA=YES \
  actian/vectorai:latest

Die Datenträger-Einbindung (-v ./local_data:/var/lib/actian-vectorai) sorgt dafür, dass die Daten auch nach einem Neustart des Containers erhalten bleiben. Ohne diese Funktion verliert VectorAI DB alle gespeicherten Daten, wenn der Container beendet wird, wodurch das gleiche Problem mit dem kurzlebigen Speicher wieder auftritt, das Sie eigentlich beseitigen wollten.

Sobald der Container gestartet ist, ist der gRPC-Port auf 6574 ist das, was VectorAIStorage mit dem eine Verbindung hergestellt wird. Die lokale Benutzeroberfläche ist verfügbar unter http://localhost:6575.

Schritt 3: Erstellen des benutzerdefinierten Speicheranbieters

VectorAIStorage implementiert die StorageBackend Protokoll, das von CrewAI Memory Klasse erwartet. Die folgende Datei enthält die Methoden, die CrewAI beim Speichern und Abrufen von Speicherdatensätzen aufruft. Erstellen Sie eine Datei mit dem Namen vectorai_storage.py im Stammverzeichnis Ihres Projekts. Die vollständige Datei finden Sie unter GitHub, und die folgenden Methoden decken die zentralen Entwurfsentscheidungen ab.

Scope-Pfade enthalten „ Nutzer “-Bezeichner, die möglicherweise aus „ Nutzer “-Eingaben stammen. Entfernen Sie Pipe-Zeichen aus jedem „ Nutzer “-Bezeichner, bevor Sie ihn an VectorAIStorage um eine Umgehung des Bereichsfilters zu verhindern.

import threading
import uuid
from actian_vectorai import VectorAIClient, VectorParams, Distance, PointStruct, Filter, Field
from actian_vectorai.exceptions import CollectionExistsError
from crewai.memory.types import MemoryRecord, ScopeInfo
# json, datetime, and Any are used in _record_to_payload and _payload_to_record
# in the full file on GitHub

VECTOR_DIM = 1536  # matches OpenAI text-embedding-3-small, CrewAI's default embedder
COLLECTION_NAME = "crewai_memories"
_collection_lock = threading.Lock()

# _record_to_payload and _payload_to_record are defined in the full file on GitHub
# https://github.com/Tiioluwani/crewai-vectorai-memory

def _build_scope_ancestors(scope: str) -> list[str]:
    parts = scope.strip("/").split("/")
    ancestors: list[str] = ["/"]
    current = ""
    for part in parts:
        if part:
            current = f"{current}/{part}"
            ancestors.append(current)
    return ancestors

class VectorAIStorage:
    def __init__(self, host: str = "localhost:6574", collection: str = COLLECTION_NAME) -> None:
        self._host = host
        self._collection = collection
        self._client = VectorAIClient(host)
        self._client.connect()
        self._ensure_collection()

    def close(self) -> None:
        self._client.shutdown()

    def _ensure_collection(self) -> None:
        with _collection_lock:
            try:
                self._client.collections.create(
                    self._collection,
                    vectors_config=VectorParams(size=VECTOR_DIM, distance=Distance.Cosine),
                )
            except CollectionExistsError:
                pass

    def _scope_filter(self, scope_prefix: str | None) -> Filter | None:
        if not scope_prefix or not scope_prefix.strip("/"):
            return None
        prefix = scope_prefix.rstrip("/")
        if not prefix.startswith("/"):
            prefix = "/" + prefix
        return Filter(must=[Field("scope_ancestors_str").text(f"|{prefix}|")])

    def save(self, records: list[MemoryRecord]) -> None:
        if not records:
            return
        points = []
        for record in records:
            vector = record.embedding if record.embedding else [0.0] * VECTOR_DIM
            points.append(
                PointStruct(
                    id=str(uuid.uuid4()),
                    vector=vector,
                    payload=self._record_to_payload(record),
                )
            )
        self._client.points.upsert(self._collection, points)

    def search(
        self,
        query_embedding: list[float],
        scope_prefix: str | None = None,
        categories: list[str] | None = None,
        metadata_filter: dict[str, Any] | None = None,
        limit: int = 10,
        min_score: float = 0.0,
    ) -> list[tuple[MemoryRecord, float]]:
        fetch_limit = max(limit * 20, 200) if (scope_prefix or categories or metadata_filter) else limit
        results = self._client.points.search(
            self._collection,
            vector=query_embedding,
            limit=fetch_limit,
            filter=self._scope_filter(scope_prefix),
        )
        out: list[tuple[MemoryRecord, float]] = []
        for hit in results:
            score = float(hit.score)
            if score < min_score:
                continue
            record = self._payload_to_record(hit.payload)
            if categories and not any(c in record.categories for c in categories):
                continue
            if metadata_filter and not all(
                record.metadata.get(k) == v for k, v in metadata_filter.items()
            ):
                continue
            out.append((record, score))
            if len(out) >= limit:
                break
        return out

Das scope_ancestors_str Das Feld speichert jeden Vorfahrenpfad als durch Pipe-Zeichen getrennte Zeichenfolge, sodass VectorAI DB serverseitig nach Geltungsbereich filtern kann, ohne dass ein nativer Präfixoperator erforderlich ist. search() Bei Vorhandensein eines Filters werden 20-mal zu viele Datensätze abgerufen, da VectorAI DB erst nach der Rangfolge eines internen Kandidatenfensters filtert und nicht davor. Eine nach Bereich gefilterte Suche liefert möglicherweise weniger Ergebnisse als vorhanden sind, wenn die Datensätze des „ Nutzer“ außerhalb dieses Fensters liegen, gibt jedoch niemals Datensätze aus dem falschen „ Nutzer “ zurück. Rufen Sie close() wenn Ihre Anwendung beendet wird, um die gRPC-Verbindung freizugeben.

Schritt 4: Konfigurieren Sie die Crew für die Verwendung des benutzerdefinierten Anbieters

Mit VectorAIStorage Erstellen Sie dort eine Datei mit dem Namen main.py im Stammverzeichnis Ihres Projekts und fügen Sie Folgendes hinzu:

from crewai import Crew, Agent, Task, Process
from crewai.memory import Memory
from vectorai_storage import VectorAIStorage

# Replace with your actual user identifier
user_id = "alice"

storage = VectorAIStorage(host="localhost:6574")

crew = Crew(
    agents=[
        Agent(
            role="Research Analyst",
            goal="Research and summarize topics accurately",
            backstory="You are an experienced research analyst.",
            llm="gpt-4o-mini",
        )
    ],
    tasks=[
        Task(
            description="Summarize the latest developments in vector databases.",
            expected_output="A concise summary of key developments.",
        )
    ],
    memory=Memory(
        storage=storage,
        root_scope=f"/user/{user_id}",
    ),
    process=Process.sequential,
    verbose=True,
)

result = crew.kickoff()
print(result)
storage.close()

Das root_scope Der Parameter begrenzt jeden Speicherzugriff auf /user/alice. Erstellen Sie in einer mandantenfähigen Deployment eine VectorAIStorage Instanz pro Anfrage und übergibt die Kennung des aktuellen Nutzerals root_scope. Zwei Crew-Instanzen, die gleichzeitig mit unterschiedlichen root_scope Werte speichern und rufen Speicher in völlig getrennten Namensräumen ab.

Schritt 5: Überprüfen Sie, ob die drei Fehlerursachen behoben sind

Bevor wir das Backend austauschen, hier die Ausgabe der Testumgebung, die auf dem standardmäßigen LanceDB-Speicher von CrewAI ausgeführt wurde:

Standard-Lancedb-Speicher

Erstellen Sie mit VectorAI DB als Backend eine Datei mit dem Namen test_failure_modes.py im Stammverzeichnis Ihres Projekts. Fügen Sie dann Folgendes hinzu:

import threading
from vectorai_storage import VectorAIStorage
from crewai.memory.types import MemoryRecord

def make_record(content, scope, n):
    return MemoryRecord(
        content=content,
        scope=scope,
        categories=["test"],
        importance=0.5,
        # Embeddings are synthetic. Isolation in Test 3 depends on the
        # scope filter, not vector similarity.
        embedding=[float(n % 10) / 10.0] * 1536,
    )

# Test 1: Concurrent writes
print("=== Test 1: Concurrent writes ===")
errors = []

def write_memories(user_id, n):
    try:
        storage = VectorAIStorage()
        for i in range(5):
            storage.save([make_record(f"Memory {i} for user {user_id}", f"/user/{user_id}", i)])
        storage.close()
    except Exception as e:
        errors.append(str(e))

threads = [threading.Thread(target=write_memories, args=(f"user{i}", i)) for i in range(5)]
for t in threads:
    t.start()
for t in threads:
    t.join()

if errors:
    print(f"FAIL: {len(errors)} concurrent write error(s): {errors[0]}")
else:
    print("PASS: 5 concurrent writers completed without errors")

# Test 2: Persistence across reconnect
print("\n=== Test 2: Persistence across reconnect ===")
storage1 = VectorAIStorage()
storage1.save([make_record("Persistent memory test", "/user/persist_test", 1)])
storage1.close()

storage2 = VectorAIStorage()
results = storage2.search(query_embedding=[0.1] * 1536, scope_prefix="/user/persist_test", limit=5)
if results:
    print(f"PASS: Memory persisted across reconnect: {results[0][0].content[:50]}")
else:
    print("FAIL: Memory not found after reconnect")
storage2.delete(scope_prefix="/user/persist_test")
storage2.close()

# Test 3 verifies scope-filter isolation at the storage level.
# Application-level enforcement is handled by root_scope on the Memory class in main.py,
# which automatically prepends the user scope to every save and recall operation.
print("\n=== Test 3: Per-user isolation ===")
storage3 = VectorAIStorage()
storage3.save([make_record("Alice preference: prefers dark mode", "/user/alice", 1)])
storage3.save([make_record("Bob preference: speaks Spanish", "/user/bob", 2)])

alice_results = storage3.search(query_embedding=[0.1] * 1536, scope_prefix="/user/alice", limit=5)
bob_results = storage3.search(query_embedding=[0.2] * 1536, scope_prefix="/user/bob", limit=5)

alice_contents = [r.content for r, _ in alice_results]
bob_contents = [r.content for r, _ in bob_results]

alice_leaked = any("Bob" in c for c in alice_contents)
bob_leaked = any("Alice" in c for c in bob_contents)

if not alice_leaked and not bob_leaked:
    print(f"PASS: Per-user isolation confirmed: Alice sees {len(alice_results)} record(s), Bob sees {len(bob_results)} record(s), no cross-contamination")
else:
    print(f"FAIL: Memory leaked — Alice results: {alice_contents}, Bob results: {bob_contents}")

storage3.delete(scope_prefix="/user/alice")
storage3.delete(scope_prefix="/user/bob")
storage3.close()

print("\nAll failure mode tests complete.")

parallele Schreibvorgänge

Die drei Tests bestätigen, dass VectorAI DB alle drei Fehlermodi im Produktionsbetrieb behebt. VectorAI DB verarbeitet unter der Testlast gleichzeitige Schreibvorgänge ohne Sperren, bewahrt den Speicherinhalt auch bei erneuten Verbindungsaufbauten der Clients bei und beschränkt jeden Abrufvorgang auf den „ Nutzer “, dem die Daten gehören.

Was dieses Muster abdeckt und wie man mit dem Rest umgeht

Drei Bereiche fallen weiterhin nicht unter den Geltungsbereich des Swaps.

Langzeitgedächtnis: Hier wird weiterhin SQLite über KickoffTaskOutputsSQLiteStorage. Diese Ebene speichert die Ausführungsergebnisse von „ Aufgabe “ über mehrere Durchläufe hinweg und befindet sich außerhalb dessen, was VectorAIStorage Änderungen. Wenn Ihr „ Deployment “ Langzeitspeicher benötigt, damit die Daten auch nach einem Neustart des Containers erhalten bleiben, mounten Sie ein Volume für die SQLite-Datei oder ersetzen Sie diese Layer separat.

Qualität der Gedächtnisauswertung: Diese hängt von der in CrewAI integrierten LLM-Analysepipeline ab, die in diesem Tutorial unverändert bleibt. Das LLM leitet bei jedem Speichervorgang den Umfang, die Kategorien und die Wichtigkeit ab. Wenn Ihre Agenten Erinnerungen von geringer Qualität oder irrelevante Erinnerungen speichern, handelt es sich um ein Extraktionsproblem und nicht um ein Speicherproblem.

Abrufverzögerung und Token-Budgets: Diese nehmen mit der Größe des Speichers zu. VectorAIStorage lässt die Abrufbeschränkungen unkonfiguriert. Wie in Schritt 3 erwähnt, search() Bei vorhandenem Filter erfolgt ein Overfetch um den Faktor 20. Optimierung limit Eine Aufskalierung ohne Berücksichtigung dieses Multiplikators führt im großen Maßstab zu inkonsistenten Ergebnissen. Bei großvolumigen Bereitstellungen sollte feinabstimmen die limit Parameter für Abrufvorgänge festlegen und überwachen, wie viel Kontext bei jedem Agenten-Zug abgerufen wird.

Zum Abschluss

Durch das Ersetzen des Standard-Speicher-Backends von CrewAI durch VectorAI DB werden die drei Fehlerquellen behoben, die dazu führen, dass memory=True unzuverlässig im Produktionsbetrieb: parallele Sperren, temporäre Speicherung und fehlende per-Nutzer isolation . Die wesentliche Änderung besteht aus einer neuen Datei und einem Konfigurationsargument in Ihrer Crew. Ihre Agenten, Aufgaben und Crew-Logik bleiben unverändert.

Sie können dem soeben erstellten persistenten, isolierten Backend mit mem0 eine Ebene zur Extraktion semantischer Daten hinzufügen. Die Community Edition ist für den Einstieg kostenlos.


Häufig gestellte Fragen

Funktioniert diese Anleitung mit CrewAI 1.14.7?

Diese Anleitung bezieht sich auf CrewAI 1.14.6 und wurde unter 1.14.7 nicht getestet. Bevor Sie mit 1.14.7 fortfahren, vergewissern Sie sich, dass die storage Parameter aktivieren Memory noch existiert und dass die Methodensignaturen in crewai/memory/storage/backend.py entsprechen VectorAIStorage Implementierungen.

Was passiert mit dem Langzeit-Speicher (SQLite)?

Dieses Tutorial ersetzt lediglich das Vektorspeicher-Backend. Langzeitspeicher über KickoffTaskOutputsSQLiteStorage bleibt unverändert. Mounten Sie ein Volume für die SQLite-Datei, wenn diese auch nach einem Neustart des Containers erhalten bleiben soll.

Kann ich dieses Muster mit mem0 anstelle einer direkten VectorAI-DB-Verbindung verwenden?

Ja, aber es handelt sich um einen anderen Integrationsweg. mem0 wird über die external_memory Parameter, nicht der StorageBackend Swap – darum geht es in diesem Tutorial. Hinzufügen einer Ebene zur Extraktion semantischer Informationen mit mem0 deckt diesen Weg ab.

Beeinflusst die Nutzung eines externen Speicheranbieters die Leistung der Crew?

Ja. Bei jedem Speichern von Erinnerungen wird nun zusätzlich zur LLM-Analyse-Pipeline von CrewAI ein gRPC-Aufruf an die VectorAI-Datenbank gesendet. Der Netzwerk-Overhead eines gRPC-Aufrufs ist auf einer lokalen Deployment gering, doch sollten Sie sowohl die Latenz beim Speichern als auch beim Abrufen in Ihrer eigenen Umgebung messen, bevor Sie die Lösung in der Produktion bereitstellen. feinabstimmen die limit Parameter bei Abrufvorgängen, falls der abgerufene Kontext zu groß wird.


Häufige Probleme

Nach dem Wechsel zu VectorAI DB treten weiterhin Fehlermeldungen wie „Datenbank ist gesperrt“ auf

„The Crew“ greift wahrscheinlich immer noch auf das Standard-Backend zurück. Bestätigen Memory(storage=VectorAIStorage(...)) in der Crew korrekt weitergegeben wird und dass VectorAIStorage Importiert ohne Fehler.

Die Instanziierung des Speicheranbieters schlägt fehl

VectorAI DB ist wahrscheinlich nicht erreichbar. Überprüfen Sie, ob der Container läuft, indem Sie folgenden Befehl ausführen: docker ps und dass Port 6574 freigegeben ist. Wenn Sie sich auf einem Remote-Host befinden, übergeben Sie die richtige Adresse an VectorAIStorage(host="your-host:6574").

Speicherlecks zwischen Benutzern nach der Konfiguration pro-Nutzer isolation

Überprüfen Sie, ob root_scope wird gemäß Nutzer bei der Instanziierung der Crew festgelegt und nicht zwischen den Instanzen gemeinsam genutzt. Zwei Crew-Instanzen, die sich dieselbe root_scope Speichern Sie Werte und Erinnerungen im selben Namensraum.

VectorAIStorage erfüllt das StorageBackend-Protokoll nicht.

Führen Sie dies aus, um dies zu bestätigen VectorAIStorage entspricht dem Protokoll:

python -c "from crewai.memory.storage.backend import StorageBackend; from vectorai_storage import VectorAIStorage; print(issubclass(VectorAIStorage, StorageBackend))"

Falls es zurückkehrt False, vergleiche die Methodensignaturen in vectorai_storage.py gegen crewai/memory/storage/backend.py in Ihrem installierten Paket.