Startseite / Artikel

Erstellen Sie einen KI-Agenten, der niemals vergisst (Teil 2): Teleportation

Teil 2 eines KI-Agenten, der niemals vergisst: Gedächtnisteleportation über Sessions hinweg, Fähigkeiten, Verfall sowie branchenspezifisches Gedächtnis für Claude Code und ähnliche Agenten.

Verlag: REACTAPP.TOP28 Min. Lesezeit6515 Wörter

Quellen: Build an AI Agent That Never Forgets (Part 2): Teleportation, Skills, Decay, and Branch-Scoped Memory

Die folgenden Notizen skizzieren einen praktischen Weg durch „Build an AI Agent That Never Forgets (Part 2): Teleportation, Skills, Decay, and Branch-Scoped Memory“. Der Schwerpunkt liegt auf Verträgen, Überprüfungen sowie Code-Platzhaltern, anstatt auf motivierenden Formulierungen. Während der Überblicksphase sollten Sie zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgsindikator sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Notieren Sie außerdem die Laufzeiten sowie die Kosten in Form von Tokens oder Abfragen neben den funktionalen Ergebnissen. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Rechnungen, wenn der Prozess von einer Demo in gemeinsam genutzte Umgebungen übergeht.

Wo wir aufgehört haben

Die „Where We Left Off“-Ebene funktioniert am besten, wenn sie als messbare Oberfläche betrachtet wird. Erfassen Sie einen erfolgreichen Ablauf, einen Fehlerfall sowie die Rollback-Anmerkung, bevor Sie den Umfang erweitern. Bewahren Sie die Konfiguration außerhalb des Anwendungscode auf. Umgebungsdateien, Geheimdatenspeicher und Feature-Flags sollten an einem Ort gesammelt sein, den Betreuer ohne das Durchlesen des gesamten Graphen überprüfen können. Halten Sie den Zustand des Graphen flach und typisiert. Verschachtelte Datenblöcke verbergen, welcher Knoten welches Feld geschrieben hat, und führen zu Unterbrechungen beim Wiederaufnehmen der Arbeit.

Schritt 5: Speicherteleportation

Die Phase der Gedächtnisteleportation in Schritt 5 funktioniert am besten, wenn sie als messbare Struktur betrachtet wird. Erfassen Sie vor der Erweiterung des Umfangs einen goldenen Transkriptbeispiel, einen Fehlerfall sowie die Rollback-Anmerkung. Dokumentieren Sie den erfolgreichen Ablauf sowie den Wiederherstellungsprozess gemeinsam. Wiederholversuche, menschliche Eingriffe und die Handhabung von Fehlnachrichten gehören zum Produkt selbst, nicht zu späteren Optimierungen. Halten Sie den Graphenzustand einfach und typisiert – verschachtelte Datenblöcke verbergen, welcher Knoten welches Feld geschrieben hat, und führen zu Unterbrechungen beim Fortsetzen des Ablaufs.

Der Workflow

Die Workflow-Phase funktioniert am besten, wenn sie als messbarer Bereich betrachtet wird. Erfassen Sie vor der Erweiterung des Umfangs ein „goldenes“ Transkript, einen Fehlerfall sowie eine Notiz zur Rücksetzung. Ziehen Sie kleine, testbare Einheiten vor umfangreichen Skripten vor. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf ein verworrenes Ablaufverfahren. Halten Sie den Zustand des Graphen einfach und typisiert. Verschachtelte Datenblöcke verbergen, welcher Knoten welches Feld geschrieben hat, und führen zu Störungen nach Unterbrechungen.

Agent A: snapshot("for_review.vdb")
         |  send file to Agent B
         v
Agent B: open("for_review.vdb")  ->  work  ->  add new memories
         |  send file back
         v
Agent A: merge("for_review.vdb")
         -> new memories imported
         -> conflicts detected and resolved

Die Workflow-Phase funktioniert am besten, wenn sie als messbarer Bereich betrachtet wird. Erfassen Sie vor der Erweiterung des Umfangs ein „goldenes“ Transkript, einen Fehlerfall sowie eine Notiz zur Rücksetzung. Erhalten Sie neben den funktionalen Ergebnissen auch Aufzeichnungen zu Laufzeiten sowie Kosten für Token oder Abfragen. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Rechnungen, wenn der Ablauf von einer Demo in gemeinsame Umgebungen übergeht.

Schnappschuss

Zur Snapshot-Phase sollten die Eingaben, der Verantwortliche für den Schritt sowie die Abbruchkriterien vor dem Codeändern definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckten Zuständen schließen zu müssen. Bewahren Sie die Konfiguration außerhalb des Anwendungscode auf. Umgebungsdateien, Geheimdatenspeicher und Feature-Flags sollten an einem Ort gesammelt sein, den die Operator überprüfen können, ohne den gesamten Ablauf durchzulesen. Setzen Sie menschliche Freigabe für Kanten voraus, die Geld ausgeben oder Produktionsdaten ändern. Eine Verkabelung zur Kompilierzeit bedeutet nicht automatisch vollständige Geschäftsabdeckung.

class MemoryTeleport:
    def __init__(self, db: vectlite.Database):
        self.db = db
    def snapshot(self, path: str) -> str:
        """Create a portable copy of the entire database."""
        self.db.snapshot(path)
        return path

Diff – Vorschau ohne Änderung

Für die Diff-Voransicht ohne Änderung des Entwicklungsstadiums sollten Eingabedaten, der Verantwortliche für den Schritt sowie die Abbruchkriterien vor der Codeänderung definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckten Zuständen schließen zu müssen. Dokumentieren Sie gemeinsam den erfolgreichen Ablauf sowie den Notfallweg. Wiederholungsversuche, menschliche Überprüfungen und die Handhabung von Fehlern gehören zum Produkt selbst, nicht zu späteren Optimierungen. Setzen Sie menschliche Freigabe für Schritte voraus, die Geld kosten oder Produktionsdaten ändern. Eine Verkabelung zur Kompilierzeit bedeutet noch nicht vollständige Geschäftsabdeckung.

def diff(self, remote_path: str,
         namespaces: list[str] | None = None) -> MergeResult:
    """Preview what a merge would do, without writing anything."""
    return self._merge_internal(remote_path, namespaces, dry_run=True)

Verschmelzen – der interessante Teil

In der Phase „Merge the interesting part“ sollten die Eingaben, der Verantwortliche für den Schritt sowie die Abbruchkriterien vor dem Codeändern definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf verborgene Zustände schließen zu müssen. Bevorzugen Sie kleine, testbare Einheiten vor umfangreichen Skripten. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf ein verworrenes Ablaufschema. Setzen Sie menschliche Freigabe für Schritte ein, die Geld ausgeben oder Produktionsdaten ändern. Eine Verkabelung zur Kompilierzeit bedeutet nicht automatisch vollständige Geschäftsabdeckung. In der Phase „Merge the interesting part“ sollten die Eingaben, der Verantwortliche für den Schritt sowie die Abbruchkriterien vor dem Codeändern definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf verborgene Zustände schließen zu müssen. Erhalten Sie neben den funktionalen Ergebnissen auch Aufzeichnungen der Laufzeiten sowie der Kosten für Tokens oder Abfragen. Frühzeitige Sichtbarkeit der Kosten verhindert überraschende Rechnungen, wenn der Ablauf von einer Demo in eine gemeinsame Umgebung wechselt.

Konfliktlösungsstrategien.

def merge(self, remote_path: str,
          namespaces: list[str] | None = None,
          strategy: ConflictStrategy = ConflictStrategy.NEWER_WINS
          ) -> MergeResult:
    """Merge memories from a remote .vdb into the local database."""
    return self._merge_internal(
        remote_path, namespaces,
        dry_run=False, strategy=strategy,
    )

Strategien zur Konfliktlösung

Während der Phase der Konfliktlösungsstrategien sollten Sie zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgsindikatoren sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Bewahren Sie die Konfiguration außerhalb des Anwendungscode auf. Umgebungsdateien, Geheimdatenspeicher und Feature-Flags sollten an einem Ort gesammelt sein, den Betreuer ohne das Durchlesen des gesamten Systems überprüfen können. Erstellen Sie nach aufwändigen Schritten einen Checkpoint. Das System sollte bei einer Wiederholung eines späteren Schritts nicht erneut die gleiche LLM-Aufrufgebühr berechnen.

class ConflictStrategy(str, Enum):
    LOCAL_WINS = "local_wins"      # keep local version
    REMOTE_WINS = "remote_wins"    # keep remote version
    NEWER_WINS = "newer_wins"      # keep whichever has a more recent updated_at
    KEEP_BOTH = "keep_both"        # import remote as a new record

Der Merge-Engine

Beim Arbeiten an der Phase des Merge-Engines sollten Sie zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgsignal sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Dokumentieren Sie sowohl den erfolgreichen Ablauf als auch den Wiederherstellungsprozess gemeinsam. Wiederholte Versuche, menschliche Überprüfungen sowie die Handhabung von Fehlern gehören zum Produkt selbst und nicht zu späteren Optimierungen. Legen Sie nach aufwändigen Schritten einen Checkpoint an – das Resume sollte keine doppelten Gebühren für denselben LLM-Aufruf erheben, wenn ein Operator einen späteren Knoten erneut versucht.

def _merge_internal(self, remote_path, namespaces=None,
                    dry_run=False, strategy=ConflictStrategy.NEWER_WINS):
    result = MergeResult()
    remote_db = vectlite.open(remote_path, dimension=DIMENSION)
    try:
        ns_list = namespaces or self._discover_namespaces(remote_db)
        local_index = self._build_index(self.db, ns_list)        for ns in ns_list:
            remote_records = remote_db.list(namespace=ns, limit=10_000)            for record in remote_records:
                rec_id = record["id"]
                remote_meta = record.get("metadata", {})
                remote_hash = remote_meta.get("content_hash", "")                if rec_id in local_index:
                    local_info = local_index[rec_id]                    if local_info["content_hash"] == remote_hash:
                        result.skipped_duplicates += 1  # same content
                        continue                    # Conflict -- same ID, different content
                    conflict = MergeConflict(
                        record_id=rec_id,
                        namespace=ns,
                        local_content=local_info.get("content", ""),
                        remote_content=remote_meta.get("content", ""),
                        local_updated=local_info.get("updated_at", ""),
                        remote_updated=remote_meta.get("updated_at", ""),
                    )
                    if not dry_run:
                        self._resolve_conflict(conflict, record, strategy)
                        result.conflict_resolutions += 1
                    result.conflicts.append(conflict)
                else:
                    # New record -- import
                    if not dry_run:
                        self._import_record(record, ns)
                    result.imported += 1
    finally:
        remote_db.close()    return result

In Aktion sehen

Während der Phase „Es funktioniert“ sollten Sie zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgszeichen sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Ziehen Sie kleine, testbare Einheiten vor großen Skripten. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf ein verworrenes Ablaufschema. Legen Sie nach teuren Schritten Kontrollpunkte an. Das Wiederaufnehmen des Vorgangs sollte keine erneuten Gebühren für denselben LLM-Aufruf verursachen, wenn ein Operator einen späteren Knoten erneut ausführt.

from agentmem.store.memory_store import MemoryStore
from agentmem.services.teleport import MemoryTeleport
# Agent A: has been working for weeks
store_a = MemoryStore("agent_a.vdb")
tp = MemoryTeleport(store_a.db)# Create a snapshot for Agent B
tp.snapshot("for_agent_b.vdb")# ... send for_agent_b.vdb to Agent B's machine ...# Agent B works with the snapshot, adds new memories
store_b = MemoryStore("for_agent_b.vdb")
store_b.store(Memory(
    id="mem_new_finding",
    content="The payment API requires idempotency keys for all POST requests",
    memory_type=MemoryType.REFERENCE,
    importance=0.8,
))
store_b.save()
store_b.close()# ... send for_agent_b.vdb back to Agent A ...# Agent A merges the new knowledge
result = tp.merge("for_agent_b.vdb")
print(result.summary())
# -> Imported: 1 new memories
#    Skipped:  47 duplicates
#    Conflicts: 0

Während der Phase „Es funktioniert“ sollten Sie zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgszeichen sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Notieren Sie die Laufzeiten sowie die Kosten für Tokens oder Abfragen neben den funktionalen Ergebnissen. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Gebühren, wenn der Ablauf von einer Demo in gemeinsame Umgebungen wechselt.

Warum .vdb als Transportformat funktioniert

.vdb eignet sich am besten als Transportformat, wenn es als messbare Struktur betrachtet wird. Erfassen Sie ein „goldenes“ Transkript, einen Fehlerfall sowie die Notizen zum Rollback, bevor Sie den Umfang erweitern. Bewahren Sie die Konfiguration außerhalb des Anwendungscode auf. Umgebungsdateien, Geheimdatenspeicher sowie Feature-Flags sollten an einem Ort gesammelt sein, den Betreiber ohne das Durchlesen des gesamten Graphen überprüfen können. Halten Sie den Zustand des Graphen flach und typisiert – verschachtelte Datenblöcke verbergen, welcher Knoten welches Feld geschrieben hat, und führen zu Unterbrechungen beim Wiederaufnehmen der Verarbeitung.

Schritt 6: Skill Engine

Die Phase des Skill Engine in Schritt 6 funktioniert am besten, wenn sie als messbarer Ansatz betrachtet wird. Erfassen Sie vor der Erweiterung des Umfangs ein erfolgreiches Beispiel, einen Fehlerfall sowie die Notizen zur Rücksetzung. Dokumentieren Sie gleichzeitig den erfolgreichen Ablauf und den Notfallweg. Wiederholungsversuche, menschliche Kontrollen sowie die Handhabung von Fehlern gehören zum Produkt selbst und nicht zu späteren Optimierungen. Halten Sie den Zustand der Graphen einfach und typisiert – verschachtelte Datenstrukturen verbergen, welcher Knoten welches Feld geschrieben hat, und führen zu Unterbrechungen beim Fortsetzen der Verarbeitung.

---
description: Docker best practices
glob_patterns: ["Dockerfile", "docker-compose*.yml"]
trigger_keywords: ["docker", "container", "compose"]
---
## Docker Guidelines
- Always use multi-stage builds
- Pin base image versions
- Never run as root in production

Die drei Aktivierungssignale

Die drei Aktivierungssignalsstufen funktionieren am besten, wenn sie als messbare Struktur betrachtet werden. Erfassen Sie vor der Erweiterung des Umfangs ein „goldenes Transkript“, einen Fehlerfall sowie eine Rücksetznotiz. Ziehen Sie kleine, testbare Einheiten vor umfangreichen Skripten vor. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf ein verworrenes Ablaufverfahren. Halten Sie den Graphenzustand strukturiert und typisiert. Verschachtelte Datenblöcke verbergen, welcher Knoten welches Feld geschrieben hat, und führen zu Störungen bei Unterbrechungen.

User message: "deploy the containers to staging"
     |
     v
+-- SkillEngine.activate() ----------------------------+
|                                                      |
|  1. Hybrid search (semantic + keyword via VectLite)  |
|     -> matches "docker" skill (score 0.91)           |
|     -> matches "devops" skill (score 0.78)           |
|                                                      |
|  2. Glob match (if file path detected in message)    |
|     -> "Dockerfile" mentioned? -> boost "docker"     |
|                                                      |
|  3. Keyword boost                                    |
|     -> "containers" matches docker triggers          |
|                                                      |
|  4. Weighted combination -> top-k activated skills   |
+------------------------------------------------------+
     |
     v
Injected into system prompt as ## Active skills

Die drei Aktivierungssignalsstufen funktionieren am besten, wenn sie als messbare Struktur betrachtet werden. Erfassen Sie vor der Erweiterung des Umfangs ein „goldenes Transkript“, einen Fehlerfall sowie eine Rücksetznotiz. Erhalten Sie neben den funktionalen Ergebnissen auch Aufzeichnungen zu Laufzeiten sowie Kosten für Token oder Abfragen. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Gebühren, wenn der Ablauf von einer Demo in gemeinsame Umgebungen übergeht.

Das Skill-Datenmodell

In der Phase des Datenmodells „The Skill“ sollten die Eingaben, der Verantwortliche für den Schritt sowie die Abbruchkriterien definiert werden, bevor der Code geändert wird. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Die Konfiguration sollte außerhalb des Anwendungscode gespeichert werden. Umgebungsdateien, Geheimdatenspeicher sowie Feature-Flags sollten an einem Ort zusammengefasst sein, den die Operator überprüfen können, ohne den gesamten Ablauf durchzulesen. Wählen Sie bei dem nächsten Schritt, der ein Codeabschnitt oder einen Toolaufruf ist, strukturierte Ausgaben mit Schema-Validierung statt freier Prosa.

@dataclass
class Skill:
    name: str                    # "docker", "python", "security"
    content: str                 # Full Markdown instructions
    glob_patterns: list[str]     # ["Dockerfile", "*.py"]
    trigger_keywords: list[str]  # ["container", "docker"]
    tools_allowed: list[str]     # ["docker_build", "docker_push"]
    importance: float = 0.7      # Base weight
    tags: list[str] = field(default_factory=list)
    description: str = ""

Registrierung – Indexierung von Fähigkeiten in VectLite

Für die Indexierungsfähigkeiten bei der Registrierung in einer bestimmten Phase sollten vor dem Ändern des Codes die Eingabedaten, der Verantwortliche für den jeweiligen Schritt sowie die Abbruchkriterien definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Dokumentieren Sie gemeinsam den erfolgreichen Ablauf sowie den Notfallweg. Wiederholungsversuche, menschliche Überprüfungen und die Handhabung von Fehlern gehören zum Produkt selbst, nicht zu späteren Optimierungen. Setzen Sie menschliche Freigabe für Schritte voraus, bei denen Geld ausgegeben wird oder Produktionsdaten geändert werden. Eine Verkabelung zur Kompilierzeit bedeutet nicht automatisch vollständige Geschäftsabdeckung.

NAMESPACE = "__skills__"
class SkillEngine:
    def __init__(self, db: vectlite.Database,
                 max_active: int = 3, min_score: float = 0.30):
        self.db = db
        self.max_active = max_active
        self.min_score = min_score    def register(self, skill: Skill) -> str:
        skill_id = f"skill_{skill.name}_{uuid.uuid4().hex[:6]}"        # Rich embedding text: name + description + keywords + content
        embed_text = (
            f"{skill.name}: {skill.description or ''} "
            f"{' '.join(skill.trigger_keywords)} "
            f"{skill.content[:500]}"
        )
        vector = embed(embed_text)
        sparse = vectlite.sparse_terms(embed_text)        self.db.upsert(
            skill_id, vector, skill.to_metadata(),
            sparse=sparse, namespace=NAMESPACE,
        )
        return skill_id

Aktivierung – die Kernlogik zur Abgleichung

Für die Aktivierung der Kernabgleichsphase sollten vor dem Ändern des Codes die Eingaben, der Verantwortliche für den Schritt sowie die Abbruchkriterien definiert werden. Die Bediener sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Bevorzugen Sie kleine, testbare Einheiten vor umfangreichen Skripten. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf ein verworrenes Ablaufverfahren. Setzen Sie menschliche Freigabe bei Schritten ein, die Geld ausgeben oder Produktionsdaten ändern. Kompilierzeitbezogene Verbindungen bedeuten noch keine vollständige Geschäftsabwicklung. Für die Aktivierung der Kernabgleichsphase sollten vor dem Ändern des Codes die Eingaben, der Verantwortliche für den Schritt sowie die Abbruchkriterien definiert werden. Die Bediener sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Erhalten Sie neben den funktionalen Ergebnissen auch Aufzeichnungen über Laufzeiten sowie Kosten für Tokens oder Abfragen. Frühzeitige Sichtbarkeit der Kosten verhindert überraschende Rechnungen, wenn der Ablauf von einer Demo auf eine gemeinsame Umgebung wechselt.

Umgebungen.

def activate(self, message: str, file_path: str | None = None,
             k: int | None = None) -> list[ActivatedSkill]:
    k = k or self.max_active
    # 1. Hybrid search via VectLite
    vector = embed(message)
    sparse = vectlite.sparse_terms(message)
    results = self.db.search(
        vector,
        k=min(k * 3, self.count()),  # over-fetch for re-ranking
        sparse=sparse, fusion="rrf",
        namespace=NAMESPACE,
    )    # Build candidates with normalized semantic scores
    candidates = {}
    max_score = max((r.get("score", 0) for r in results), default=1) or 1
    for r in results:
        meta = r.get("metadata", {})
        name = meta.get("name", "")
        if name:
            candidates[name] = _Candidate(
                skill=self._meta_to_skill(meta),
                semantic_score=r.get("score", 0) / max_score,
            )    # 2. Keyword boost
    msg_lower = message.lower()
    for name, cand in candidates.items():
        hits = sum(1 for kw in cand.skill.trigger_keywords
                   if kw.lower() in msg_lower)
        if hits > 0:
            cand.keyword_score = min(0.5, hits * 0.2)    # 3. Glob match
    detected_paths = self._detect_paths(message)
    if file_path:
        detected_paths.append(file_path)
    for name, cand in candidates.items():
        for pattern in cand.skill.glob_patterns:
            for path in detected_paths:
                if fnmatch.fnmatch(os.path.basename(path), pattern):
                    cand.glob_score = 0.35
                    break    # 4. Weighted combination
    activated = []
    for name, cand in candidates.items():
        final_score = (
            cand.semantic_score * 0.55
            + cand.keyword_score * 0.25
            + cand.glob_score * 0.15
            + cand.skill.importance * 0.05
        )
        if final_score >= self.min_score:
            activated.append(ActivatedSkill(
                skill=cand.skill,
                score=final_score,
                activated_by=...,  # "semantic", "keyword", "glob", "combined"
            ))    activated.sort(key=lambda a: a.score, reverse=True)    # Gap filter: drop skills scoring < 75% of top skill
    if activated:
        top = activated[0].score
        activated = [a for a in activated if a.score >= top * 0.75]    return activated[:k]

Laden von Fähigkeiten aus Markdown-Dateien

Während der Phase des Ladens von Fähigkeiten aus Markdown sollten Sie zunächst den Ablaufplan aufschreiben: erforderliche Eingaben, Erfolgsindikatoren sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Bewahren Sie die Konfiguration außerhalb des Anwendungscode auf. Dateien zur Konfiguration, Speicher für Geheimnisse sowie Feature-Flags sollten an einem Ort gespeichert werden, den Betreiber überprüfen können, ohne den gesamten Codeverlauf durchlesen zu müssen. Erstellen Sie Zwischenchecks nach aufwändigen Schritten. Das System sollte bei einer Wiederholung eines späteren Schritts nicht denselben LLM-Aufruf erneut berechnen.

def load_from_directory(self, dir_path: str) -> list[str]:
    """Load skills from .md files with optional YAML-like frontmatter."""
    ids = []
    for md_file in sorted(Path(dir_path).glob("*.md")):
        skill = self._parse_skill_file(md_file)
        if skill:
            ids.append(self.register(skill))
    return ids
---
description: Python best practices
glob_patterns: ["*.py", "*.pyx", "pyproject.toml"]
trigger_keywords: ["python", "pip", "venv", "pytest"]
tools_allowed: ["python_run", "pytest"]
importance: 0.8
tags: ["language", "backend"]
---
## Python Guidelines
- Use type hints for all function signatures
- Prefer pathlib over os.path
- Use pytest with fixtures, not unittest
- Format with black, lint with ruff

Prompt-Injektion

Beim Arbeiten an der Phase der Prompt-Injektion sollten Sie zunächst einen Vertrag aufschreiben: erforderliche Eingaben, Erfolgsignal sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Dokumentieren Sie sowohl den erfolgreichen Ablauf als auch den Notfallweg gemeinsam. Wiederholte Versuche, menschliche Überprüfungen sowie die Handhabung von Fehlern gehören zum Produkt selbst und nicht zu späteren Optimierungen. Cachen Sie stabile Systemanweisungen sowie Tool-Schemata – das erneute Senden identischer Vorlagen ist eine häufige Ursache für Probleme.

def format_for_prompt(self, activated: list[ActivatedSkill]) -> str:
    if not activated:
        return ""
    lines = ["\n\n## Active skills\n"]
    for act in activated:
        lines.append(f"### {act.skill.name}")
        if act.skill.tools_allowed:
            lines.append(f"**Allowed tools:** {', '.join(act.skill.tools_allowed)}")
        lines.append(act.skill.content)
        lines.append("")    return "\n".join(lines)

Beobachten, wie es funktioniert

Während der Phase „Es funktioniert“ sollten Sie zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgszeichen sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Ziehen Sie kleine, testbare Einheiten vor großen Skripten. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf ein verworrenes Ablaufverfahren. Legen Sie nach teuren Schritten Kontrollpunkte an. Das Wiederaufnehmen des Vorgangs sollte keine erneuten Gebühren für denselben LLM-Aufruf verursachen, wenn ein Operator einen späteren Knoten erneut ausführt.

engine = SkillEngine(db)
engine.register(Skill(
    name="docker",
    content="## Docker\n- Always use multi-stage builds\n- Pin versions",
    glob_patterns=["Dockerfile", "docker-compose*.yml"],
    trigger_keywords=["docker", "container", "image", "compose"],
))engine.register(Skill(
    name="python",
    content="## Python\n- Use type hints\n- Prefer pathlib\n- Use pytest",
    glob_patterns=["*.py", "pyproject.toml"],
    trigger_keywords=["python", "pip", "venv"],
))# Semantic + keyword match
activated = engine.activate("deploy the containers to staging")
# -> [ActivatedSkill(skill=<docker>, score=0.87, activated_by="combined")]# Glob match
activated = engine.activate("review the Dockerfile")
# -> [ActivatedSkill(skill=<docker>, score=0.91, activated_by="combined")]# Semantic match only (no exact keywords)
activated = engine.activate("optimize the CI/CD pipeline for faster builds")
# -> [ActivatedSkill(skill=<docker>, score=0.52, activated_by="semantic")]

Während der Phase „Es funktioniert“ sollten Sie zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgszeichen sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Notieren Sie die Laufzeiten sowie die Kosten für Token oder Abfragen neben den funktionalen Ergebnissen. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Gebühren, wenn der Ablauf von einer Demo in gemeinsame Umgebungen wechselt.

Schritt 7: Speicherrückgang und Konfliktlösung

Die Phase des Speicherrückgangs im Schritt 7 funktioniert am besten, wenn sie als messbare Struktur betrachtet wird. Erfassen Sie eine „goldene“ Transkription, einen Fehlerfall sowie die Notizen zur Rücksetzung, bevor Sie den Umfang erweitern. Bewahren Sie die Konfiguration außerhalb des Anwendungscode auf. Umgebungsdateien, Geheimdatenspeicher sowie Feature-Flags sollten an einem Ort gesammelt sein, den Betreuer ohne Durchsicht des gesamten Graphen prüfen können. Halten Sie den Zustand des Graphen flach und typisiert. Verschachtelte Datenblöcke verbergen, welcher Knoten welches Feld geschrieben hat, und führen zu Unterbrechungen beim Wiederaufnehmen der Verarbeitung.

Die Rückgangsformel

Die Dekadeformel funktioniert am besten, wenn sie als messbare Oberfläche betrachtet wird. Erfassen Sie einen erfolgreichen Fall, einen Fehlerfall sowie die Rollback-Anmerkung, bevor Sie den Umfang erweitern. Dokumentieren Sie gemeinsam den erfolgreichen Ablauf und den Wiederherstellungsprozess. Wiederholversuche, menschliche Kontrollen sowie die Handhabung von Fehlern gehören zum Produkt selbst und nicht zu späteren Optimierungen. Halten Sie den Graphenzustand strukturiert und typisiert – verschachtelte Datenstrukturen verbergen, welcher Knoten welches Feld geschrieben hat, und führen zu Unterbrechungen beim Fortsetzen der Verarbeitung.

new_importance = current_importance * 2^(-elapsed_hours / half_life)

The MemoryDecayEngine

Die The MemoryDecayEngine-Ebene funktioniert am besten, wenn sie als messbare Oberfläche betrachtet wird. Erfassen Sie einen erfolgreichen Fall, einen Fehlerfall sowie die Rollback-Anmerkung, bevor Sie den Umfang erweitern. Ziehen Sie kleine, testbare Einheiten vor großen, komplexen Skripten vor. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf einen verworrenen Ablauf. Halten Sie den Zustand der Graphen flach und typisiert – verschachtelte Datenstrukturen verbergen, welcher Knoten welches Feld geschrieben hat, und führen zu Störungen bei Unterbrechungen.

class MemoryDecayEngine:
    def __init__(self, store: MemoryStore,
                 half_life_hours: float = 168.0,      # 1 week
                 tombstone_threshold: float = 0.05,
                 conflict_threshold: float = 0.80):
        self.store = store
        self.half_life_hours = half_life_hours
        self.tombstone_threshold = tombstone_threshold
        self.conflict_threshold = conflict_threshold

Anwendung der Zerfallslogik

Die Anwendungszerfallsphase funktioniert am besten, wenn sie als messbare Oberfläche betrachtet wird. Erfassen Sie ein „goldenes“ Transkript, einen Fehlerfall sowie die Rollback-Anmerkung, bevor Sie den Umfang erweitern. Betrachten Sie diese Phase als Vertrag zwischen Eingaben und validierten Ausgaben. Benennen Sie die Artefakte, definieren Sie Erfolgskontrollen und lehnen Sie stille, teilweise abgeschlossene Arbeiten ab. Halten Sie den Graphenzustand flach und typisiert. Verschachtelte Blöcke verbergen, welcher Knoten welches Feld geschrieben hat, und führen zu Unterbrechungen beim Fortsetzen der Arbeit.

def apply_decay(self, now: datetime | None = None) -> DecayReport:
    """Run exponential decay over all memories."""
    now = now or datetime.now(timezone.utc)
    report = DecayReport()
    for mtype in MemoryType:
        memories = self.store.recall_by_type(mtype, k=500)
        for mem in memories:
            if mem.superseded_by:
                continue  # already replaced            report.total_scanned += 1
            last_accessed = self._parse_time(
                mem.last_accessed or mem.created_at
            )
            elapsed_hours = max(
                (now - last_accessed).total_seconds() / 3600.0, 0.0
            )            # The core formula
            decay_factor = math.pow(
                2.0, -elapsed_hours / self.half_life_hours
            )
            new_importance = mem.importance * decay_factor            if new_importance < self.tombstone_threshold:
                # Below threshold -> auto-delete
                self.store.update_importance(mem.id, 0.0)
                self.store.forget(mem.id)
                self.store.compact()
                report.pruned += 1
            elif abs(new_importance - mem.importance) > 0.001:
                # Importance decayed -> update
                self.store.update_importance(mem.id, new_importance)
                report.decayed += 1
            else:
                report.untouched += 1    return report

Konflikterkennung

Die Konflikterkennungsphase funktioniert am besten, wenn sie als messbare Größe betrachtet wird. Erfassen Sie ein „goldenes“ Transkript, einen Fehlerfall sowie die Rollback-Anmerkung, bevor Sie den Umfang erweitern. Notieren Sie die Zeiten sowie die Kosten für Tokens oder Abfragen neben den funktionalen Ergebnissen. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Rechnungen, wenn sich der Einsatzbereich von einer Demo auf gemeinsame Umgebungen verschiebt. Halten Sie den Zustand des Graphen einfach und typisiert. Verschachtelte Datenblöcke verbergen, welcher Knoten welches Feld geschrieben hat, und führen zu Unterbrechungen beim Fortsetzen der Verarbeitung.

def check_conflicts(self, new_memory: Memory) -> list[ConflictResult]:
    """Find existing memories that contradict the new one."""
    # Get candidates via VectLite search
    similar = self.store.find_similar(
        new_memory.content,
        memory_type=new_memory.memory_type,
        k=5, min_score=0.0,
    )    new_hash = self.store._hash(new_memory.content)
    new_vector = embed(new_memory.content)    conflicts = []
    for existing, _ in similar:
        if existing.id == new_memory.id:
            continue
        if existing.superseded_by:
            continue        # Skip exact duplicates (same hash = not a conflict)
        if self.store._hash(existing.content) == new_hash:
            continue        # Compute direct cosine similarity
        existing_vector = embed(existing.content)
        cosine_sim = self._cosine_similarity(new_vector, existing_vector)        if cosine_sim >= self.conflict_threshold:
            conflicts.append(ConflictResult(
                existing=existing,
                new_content=new_memory.content,
                similarity=cosine_sim,
                resolution=self.default_resolution,
            ))    return conflicts

Speicher mit Konfliktlösung

Der Store mit der Konfliktlösungsphase funktioniert am besten, wenn er als messbarer Ansatz betrachtet wird. Erfassen Sie einen erfolgreichen Fall, einen Fehlerfall sowie die Rollback-Anmerkung, bevor Sie den Umfang erweitern. Bewahren Sie die Konfiguration außerhalb des Anwendungscode auf. Umgebungsdateien, Geheimdatenspeicher und Feature-Flags sollten an einem Ort gesammelt sein, den Betreuer ohne das Durchlesen des gesamten Graphen überprüfen können. Halten Sie den Zustand des Graphen flach und typisiert. Verschachtelte Datenblöcke verbergen, welcher Knoten welches Feld geschrieben hat, und führen zu Unterbrechungen beim Fortsetzen der Verarbeitung.

def store_with_conflict_check(self, memory: Memory,
                              resolution=None) -> tuple[str, list]:
    resolution = resolution or self.default_resolution
    conflicts = self.check_conflicts(memory)
    if not conflicts:
        return self.store.store(memory), []    for conflict in conflicts:
        conflict.resolution = resolution        if resolution == ConflictResolution.SUPERSEDE:
            mem_id = self.store.store(memory)
            self.store.mark_superseded(conflict.existing.id, mem_id)        elif resolution == ConflictResolution.KEEP_BOTH:
            mem_id = self.store.store(memory)        elif resolution == ConflictResolution.KEEP_EXISTING:
            mem_id = ""  # don't store the new one    return mem_id, conflicts

Beispiel für die Funktionsweise

Die Phase „Es funktioniert“ funktioniert am besten, wenn sie als messbare Struktur betrachtet wird. Erfassen Sie einen erfolgreichen Ablauf, einen Fehlerfall sowie die Notizen zur Rücksetzung, bevor Sie den Umfang erweitern. Dokumentieren Sie gemeinsam den erfolgreichen Ablauf und den Wiederherstellungsprozess. Wiederholversuche, menschliche Kontrollen sowie die Handhabung von Fehlern gehören zum Produkt selbst und nicht zu späteren Optimierungen. Halten Sie den Zustand der Graphen strukturiert und typisiert – verschachtelte Datenstrukturen verbergen, welcher Knoten welches Feld geschrieben hat, und führen zu Unterbrechungen beim Fortsetzen des Ablaufs.

store = MemoryStore("decay_demo.vdb")
decay = MemoryDecayEngine(store)
# Store two memories
store.store(Memory(id="m1", content="The database is PostgreSQL 14",
                   memory_type=MemoryType.PROJECT, importance=0.7))# Later, the project migrates
new_mem = Memory(id="m2", content="The database is PostgreSQL 16",
                 memory_type=MemoryType.PROJECT, importance=0.8)mem_id, conflicts = decay.store_with_conflict_check(new_mem)
print(conflicts[0])
# -> Conflict: [project] sim=0.953 -> supersede
# m1 is now marked as superseded by m2# Weeks later, run decay
report = decay.apply_decay()
print(report)
# -> Decay: 15 scanned, 8 decayed, 2 pruned, 5 untouched

Schritt 8: arbeitstree-konformes Speichermanagement

Die Step 8 Worktree-Aware Memory-Ebene funktioniert am besten, wenn sie als messbare Struktur betrachtet wird. Erfassen Sie vor der Erweiterung des Umfangs ein „goldenes“ Transkript, einen Fehlerfall sowie eine Notiz zur Rücksetzung. Ziehen Sie kleine, testbare Einheiten vor umfangreichen Skripten vor. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf ein verworrenes Ablaufverfahren. Halten Sie den Graphenzustand strukturiert und typisiert. Verschachtelte Datenblöcke verbergen, welcher Knoten welches Feld geschrieben hat, und führen zu Störungen bei Unterbrechungen. Die Step 8 Worktree-Aware Memory-Ebene funktioniert am besten, wenn sie als messbare Struktur betrachtet wird. Erfassen Sie vor der Erweiterung des Umfangs ein „goldenes“ Transkript, einen Fehlerfall sowie eine Notiz zur Rücksetzung. Erhalten Sie neben den funktionalen Ergebnissen auch Aufzeichnungen zu Laufzeiten sowie Kosten für Token oder Abfragen. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Gebühren, wenn der Ablauf von einer Demo in gemeinsame Umgebungen wechselt.

Die Namespace-Strategie

In der Phase der Namespace-Strategie sollten die Eingabedaten, der Verantwortliche für den Schritt sowie die Abbruchkriterien definiert werden, bevor der Code geändert wird. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckten Zuständen schließen zu müssen. Die Konfiguration sollte außerhalb des Anwendungscode gespeichert werden. Umgebungsdateien, Geheimdatenspeicher und Feature-Flags sollten an einem Ort zusammengefasst sein, den die Operator überprüfen können, ohne den gesamten Ablauf durchzulesen. Menschliche Freigabe sollte für Vorgänge erforderlich sein, die Geld ausgeben oder Produktionsdaten ändern. Eine Verkabelung zur Kompilierzeit bedeutet nicht automatisch vollständige Geschäftsabdeckung.

# Global -- shared across all branches
GLOBAL_TYPES = {MemoryType.USER, MemoryType.FEEDBACK}
# Scoped -- isolated per branch
SCOPED_TYPES = {MemoryType.PROJECT, MemoryType.REFERENCE}
Memory Type     Branch          VectLite Namespace
-----------------------------------------------------
user            (any)           "user"
feedback        (any)           "feedback"
project         main            "project:main"
project         feature/auth    "project:feature/auth"
reference       main            "reference:main"
reference       feature/auth    "reference:feature/auth"

The WorktreeManager

Für die The WorktreeManager-Phase sollten vor dem Ändern des Codes die Eingaben, der Verantwortliche für den Schritt sowie die Abbruchkriterien definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Dokumentieren Sie gemeinsam den erfolgreichen Ablauf sowie den Notfallweg. Wiederholungsversuche, menschliche Überprüfungen und die Handhabung von Fehlern gehören zum Produkt selbst, nicht zu späteren Optimierungen. Setzen Sie menschliche Freigabe für Schritte voraus, die Geld kosten oder Produktionsdaten ändern. Eine Verkabelung zur Kompilierzeit bedeutet noch nicht vollständige Geschäftsabdeckung.

BRANCH_SEP = ":"
class WorktreeManager:
    def __init__(self, db: vectlite.Database,
                 branch: str | None = None):
        self.db = db
        self._branch = branch or self._detect_git_branch()    def resolve_namespace(self, memory_type: MemoryType) -> str:
        """Map a MemoryType to its VectLite namespace."""
        if memory_type in GLOBAL_TYPES:
            return memory_type.value            # "user", "feedback"
        return f"{memory_type.value}{BRANCH_SEP}{self._branch}"
        # -> "project:main", "reference:feature/auth"    def recall_namespaces(self) -> list[str]:
        """All namespaces to search for the current branch."""
        namespaces = []
        for mt in MemoryType:
            if mt in GLOBAL_TYPES:
                namespaces.append(mt.value)
            else:
                namespaces.append(f"{mt.value}{BRANCH_SEP}{self._branch}")
        return namespaces
        # -> ["user", "feedback", "project:main", "reference:main"]

Autodetektion der Git-Branch

Zur automatischen Erkennung der Git-Branch-Phase sollten die Eingabedaten, der Verantwortliche für den Schritt sowie die Abbruchkriterien vor dem Codeändern definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckten Zuständen schließen zu müssen. Bevorzugen Sie kleine, testbare Einheiten vor umfangreichen Skripten. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf ein verworrenes Pipeline-System. Setzen Sie menschliche Freigabe bei Schritten ein, die Geld ausgeben oder Produktionsdaten ändern. Kompilierzeit-basierte Verbindungen bedeuten noch nicht vollständige Geschäftsabläufe.

@staticmethod
def _detect_git_branch() -> str:
    """Auto-detect the current git branch."""
    try:
        result = subprocess.run(
            ["git", "rev-parse", "--abbrev-ref", "HEAD"],
            capture_output=True, text=True, timeout=5,
        )
        if result.returncode == 0:
            branch = result.stdout.strip()
            if branch and branch != "HEAD":
                return branch
    except (FileNotFoundError, subprocess.TimeoutExpired):
        pass
    return "main"  # fallback

Zur automatischen Erkennung der Git-Branch-Phase sollten die Eingabedaten, der Eigentümer des Schritts sowie die Abbruchkriterien vor der Codeänderung definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Erhalten Sie neben den funktionalen Ergebnissen auch Aufzeichnungen der Laufzeiten sowie der Kosten für Token oder Abfragen. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Rechnungen, wenn der Weg von einer Demo-Umgebung in gemeinsam genutzte Umgebungen wechselt.

Branchen wechseln

Während der Bearbeitung der Phase „Switching branches“ sollten Sie zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgsignal sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Bewahren Sie die Konfiguration außerhalb des Anwendungscode auf. Umgebungsdateien, Geheimdatenspeicher und Feature-Flags sollten an einem Ort gesammelt sein, den Betreiber überprüfen können, ohne den gesamten Codeverlauf durchlesen zu müssen. Erstellen Sie nach aufwändigen Schritten einen Checkpoint. Das Wiederaufnehmen des Vorgangs sollte keine doppelte Abrechnung für denselben LLM-Aufruf verursachen, wenn ein Betreiber einen späteren Knoten erneut ausführt.

def switch_branch(self, branch: str) -> str:
    """Switch to a different branch context. Returns previous branch."""
    prev = self._branch
    self._branch = branch
    return prev

Zusammenführen von Branches (ähnlich wie git merge für Erinnerungen)

Wenn Sie mit Schritten wie „git stage“ zur Verschmelzung von Branches arbeiten, notieren Sie zunächst den Vertrag: erforderliche Eingaben, Erfolgsignal sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Dokumentieren Sie gleichzeitig den erfolgreichen Ablauf sowie den Notfallweg. Wiederholte Versuche, menschliche Überprüfungen und die Handhabung von Fehlern gehören zum Produkt selbst, nicht zu späteren Optimierungen. Legen Sie nach aufwändigen Schritten einen Checkpoint an. Das Resume sollte bei einer Wiederholung eines Schritts durch einen Operator nicht denselben LLM-Aufruf erneut berechnen.

def merge_branch(self, source: str,
                 into: str | None = None) -> BranchMergeResult:
    """Merge branch-scoped memories from source into target."""
    target = into or self._branch
    result = BranchMergeResult(source=source, target=target)
    for mt in SCOPED_TYPES:  # only project + reference
        src_ns = f"{mt.value}{BRANCH_SEP}{source}"
        tgt_ns = f"{mt.value}{BRANCH_SEP}{target}"        src_records = self.db.list(namespace=src_ns, limit=500)        # Build dedup set from target
        tgt_records = self.db.list(namespace=tgt_ns, limit=500)
        tgt_hashes = {
            r.get("metadata", {}).get("content_hash", "")
            for r in tgt_records
        }        for record in src_records:
            content_hash = record.get("metadata", {}).get("content_hash", "")
            if content_hash in tgt_hashes:
                result.skipped_duplicates += 1
                continue            # Import with new ID into target namespace
            new_id = f"mem_{uuid.uuid4().hex[:12]}"
            new_meta = dict(record.get("metadata", {}))
            new_meta["merged_from"] = source            self.db.upsert(
                new_id, record["vector"], new_meta,
                sparse=record.get("sparse", {}),
                namespace=tgt_ns,
            )
            result.imported += 1    return result

Differenzieren und löschen

Während der Phase des Vergleichs und Löschens sollte man zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgsignal sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Man sollte kleine, testbare Einheiten vor umfangreichen Skripten bevorzugen. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf ein verworrenes Ablaufverfahren. Legen Sie nach teuren Schritten einen Kontrollpunkt an. Die Wiederaufnahme des Vorgangs sollte keine erneute Abrechnung für denselben LLM-Aufruf vornehmen, wenn ein Operator einen späteren Knoten erneut versucht. Während der Phase des Vergleichs und Löschens sollte man zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgssignal sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Notieren Sie die Laufzeiten sowie die Kosten für Token oder Abfragen neben den funktionalen Ergebnissen. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Rechnungen, wenn der Ablauf von einer Demo-Umgebung in gemeinsame Umgebungen wechselt.

def diff_branch(self, source: str,
                target: str | None = None) -> BranchDiffResult:
    """Like git diff -- preview without modifying anything."""
    # Same logic as merge_branch but without writing
    ...
def delete_branch(self, branch: str) -> int:
    """Delete all branch-scoped memories. Global types untouched."""
    total_deleted = 0
    for mt in SCOPED_TYPES:
        ns = f"{mt.value}{BRANCH_SEP}{branch}"
        records = self.db.list(namespace=ns, limit=500)
        for record in records:
            self.db.delete(record["id"])
            total_deleted += 1
    if total_deleted > 0:
        self.db.compact()
    return total_deleted

Auswertung des gesamten Lebenszyklus

Die Phase der Auswertung des gesamten Lebenszyklus funktioniert am besten, wenn sie als messbare Struktur betrachtet wird. Erfassen Sie ein Beispiel für einen erfolgreichen Ablauf, einen Fehlerfall sowie eine Notiz zur Rücksetzung, bevor Sie den Umfang erweitern. Bewahren Sie die Konfiguration außerhalb des Anwendungscode auf. Umgebungsdateien, Geheimdatenspeicher und Feature-Flags sollten an einem Ort gesammelt sein, den Betreuer ohne das Durchlesen des gesamten Graphen überprüfen können. Halten Sie den Zustand des Graphen strukturiert und typisiert. Verschachtelte Datenstrukturen verbergen, welcher Knoten welches Feld geschrieben hat, und führen zu Störungen bei Unterbrechungen.

wt = WorktreeManager(db, branch="main")
# Store memories on main
store.store(Memory(content="FastAPI backend", memory_type=MemoryType.PROJECT))
# -> stored in namespace "project:main"store.store(Memory(content="I prefer type hints", memory_type=MemoryType.USER))
# -> stored in namespace "user" (global)# Switch to feature branch
wt.switch_branch("feature/auth")store.store(Memory(content="OAuth2 with PKCE for mobile clients",
                   memory_type=MemoryType.PROJECT))
# -> stored in namespace "project:feature/auth"# Recall on feature/auth -- sees global + branch-scoped
namespaces = wt.recall_namespaces()
# -> ["user", "feedback", "project:feature/auth", "reference:feature/auth"]
# "I prefer type hints" is visible (global)
# "FastAPI backend" is NOT visible (it's in project:main)
# "OAuth2 with PKCE" is visible (feature branch)# Merge feature into main
result = wt.merge_branch("feature/auth", into="main")
# -> Imported: 1, Skipped: 0# Now on main, "OAuth2 with PKCE" is also visible
wt.switch_branch("main")
# -> recall sees: "FastAPI backend" + "OAuth2 with PKCE" + all globals# Clean up
wt.delete_branch("feature/auth")
# -> project:feature/auth and reference:feature/auth emptied

Endgültige Projektstruktur

Die finale Projektstrukturphase funktioniert am besten, wenn sie als messbare Struktur betrachtet wird. Erfassen Sie einen erfolgreichen Ablauf, einen Fehlerfall sowie eine Notiz zur Rücksetzung, bevor Sie den Umfang erweitern. Dokumentieren Sie gleichzeitig den erfolgreichen Ablauf und den Wiederherstellungsablauf. Versuche, menschliche Kontrollpunkte sowie die Handhabung von Fehlern gehören zum Produkt selbst und nicht zu späteren Optimierungen. Halten Sie den Zustand der Graphen einfach und typisiert – verschachtelte Strukturen verbergen, welcher Knoten welches Feld geschrieben hat, und führen zu Unterbrechungen beim Fortsetzen der Verarbeitung.

agentmem/
  core/
    memory_types.py       # MemoryType + Memory dataclass
    embedder.py           # embed() / embed_batch()
  store/
    memory_store.py       # MemoryStore -- VectLite persistence
  services/
    extractor.py          # Background memory extraction
    session.py            # Session summary
    compaction.py         # 4-level cascading compaction
    swarm.py              # Multi-agent swarm
    teleport.py           # Snapshot / merge / diff
    skills.py             # Skill engine
    decay.py              # Exponential decay + conflict resolution
    worktree.py           # Branch-scoped namespaces
  agent.py                # Agent -- the full loop
  mcp_server.py           # MCP server (bonus)

Das Gesamtbild

The The Complete Picture-Phase funktioniert am besten, wenn sie als messbare Struktur betrachtet wird. Erfassen Sie ein „goldenes Transkript“, einen Fehlerfall sowie eine Notiz zur Rücksetzung, bevor Sie den Umfang erweitern. Ziehen Sie kleine, testbare Einheiten vor umfangreichen Skripten vor. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf ein verworrenes Ablaufverfahren. Halten Sie den Zustand der Graphen flach und typisiert – eingebettete Datenblöcke verbergen, welcher Knoten welches Feld geschrieben hat, und führen zu Störungen bei Unterbrechungen. The The Complete Picture-Phase funktioniert am besten, wenn sie als messbare Struktur betrachtet wird. Erfassen Sie ein „goldenes Transkript“, einen Fehlerfall sowie eine Notiz zur Rücksetzung, bevor Sie den Umfang erweitern. Erfassen Sie außerdem die Laufzeiten sowie die Kosten für Token oder Abfragen zusammen mit den funktionalen Ergebnissen. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Gebühren, wenn der Ablauf von einer Demo in gemeinsame Umgebungen wechselt.

User message
     |
     v
+-- Agent.chat() ----------------------------------------+
|                                                        |
|  RECALL: hybrid search (HNSW + BM25 + RRF)      [P1]  |
|  SKILLS: semantic + keyword + glob activation    [P2]  |
|  BUILD PROMPT: memories + skills + session summary     |
|  COMPACT: snip -> micro -> collapse -> auto      [P1]  |
|  LLM RESPONSE                                         |
|  EXTRACT: background sub-agent learns facts      [P1]  |
|  SESSION: update summary if threshold crossed    [P1]  |
|                                                        |
|  TELEPORT: snapshot / merge / diff               [P2]  |
|  DECAY: exponential forgetting + conflict detect [P2]  |
|  WORKTREE: branch-scoped namespaces              [P2]  |
|  SWARM: multi-agent via shared namespaces        [P1]  |
|                                                        |
|  Storage: single .vdb file (VectLite)                  |
+--------------------------------------------------------+

Was VectLite ermöglicht

Für die von What VectLite ermöglichte Phase sollten Sie vor dem Ändern des Codes die Eingaben, den Verantwortlichen für den Schritt sowie die Abbruchkriterien definieren. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Bewahren Sie die Konfiguration außerhalb des Anwendungscode auf. Umgebungsdateien, Geheimdatenspeicher und Feature-Flags sollten an einem Ort gesammelt sein, den die Operator überprüfen können, ohne den gesamten Ablaufverlauf durchlesen zu müssen. Setzen Sie menschliche Freigabe für Kanten voraus, die Geld ausgeben oder Produktionsdaten ändern. Eine Verkabelung zur Kompilierzeit bedeutet nicht automatisch vollständige Geschäftsabdeckung.

Vergleich mit Claude Code

Für den Vergleich mit der jeweiligen Phase sollten vor dem Ändern des Codes die Eingabedaten, der Verantwortliche für den Schritt sowie die Abbruchkriterien definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Dokumentieren Sie gemeinsam den erfolgreichen Ablauf sowie den Notfallweg. Wiederholungsversuche, menschliche Überprüfungen und die Handhabung von Fehlern gehören zum Produkt selbst, nicht zu späteren Optimierungen. Setzen Sie menschliche Freigabe für Schritte voraus, die Geld kosten oder Produktionsdaten ändern. Eine Verkabelung zur Kompilierzeit bedeutet noch nicht vollständige Geschäftsabdeckung.

Outprobieren

In der Phase „Try It“ sollten die Eingaben, der Verantwortliche für den Schritt sowie die Abbruchkriterien vor dem Ändern des Codes definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Bevorzugen Sie kleine, testbare Einheiten vor umfangreichen Skripten. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf ein verworrenes Ablaufverfahren. Setzen Sie menschliche Freigabe bei Vorgängen ein, die Geld kosten oder Produktionsdaten ändern. Eine Verkabelung zur Kompilierzeit bedeutet nicht automatisch vollständige Geschäftsabdeckung.

pip install vectlite sentence-transformers
git clone https://github.com/mcsedition-hub/agentmem-vectlite.git
cd agentmem-vectlite
python demo.py

In der Phase „Try It“ sollten die Eingaben, der Verantwortliche für den Schritt sowie die Abbruchkriterien vor dem Ändern des Codes definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Erhalten Sie neben den funktionalen Ergebnissen auch Aufzeichnungen der Laufzeiten sowie der Kosten für Tokens oder Abfragen. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Rechnungen, wenn der Ablauf von einer Demo in gemeinsame Umgebungen übergeht.

Betriebskontrollliste

In der Phase der Betriebskontrollliste sollten vor dem Ändern des Codes die Eingaben, der Verantwortliche für den jeweiligen Schritt sowie die Abbruchkriterien definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen.

Betrachten Sie diese Phase als Vertrag zwischen den Eingaben und den validierten Ausgaben. Benennen Sie die Artefakte, definieren Sie Erfolgskontrollen und lehnen Sie stille, unvollständige Abschlüsse ab.

Führen Sie menschliche Freigabe für Schritte ein, die Geld ausgeben oder Produktionsdaten ändern. Kompilierzeitbezogene Verbindungen entsprechen nicht der Geschäftsabschlussqualität.

Schreiben Sie ein kurzes Handbuch: Wie man Schlüssel rotiert, wie man die Warteschlange leert und wie man den letzten Eingang rückgängig macht.