Memory System (Deutsch)
Auswahl eines Einbettungsanbieters (v3.8.16+)
Abschnitt betitelt „Auswahl eines Einbettungsanbieters (v3.8.16+)“Die Speicher-Engine von OmniRoute unterstützt vier Einbettungsquellen (src/lib/memory/embedding/). Jede bietet unterschiedliche Vor- und Nachteile hinsichtlich Latenz, Kosten, Modellqualität und Einrichtungsaufwand.
Die Einbettungsquellen
Abschnitt betitelt „Die Einbettungsquellen“| Anbieter | Quelle | Latenz | Kosten | Qualität | Einrichtung |
|---|---|---|---|---|---|
transformers |
Lokales ONNX-Modell (Xenova/all-MiniLM-L6-v2) | ~50-150ms (CPU) | Kostenlos | Gut | Nur npm install |
static |
Vorberechnete Vektoren (zwischengespeichert) | <1ms | Kostenlos | N. z. (abhängig vom Cache-Treffer) | Keine |
remote |
OpenAI-/Cohere-/Voyage-API | ~100-300ms | $0.02-0.10/1M Token | Ausgezeichnet | API-Schlüssel |
auto |
Wählt zur Laufzeit die beste verfügbare Quelle aus | Wie die gewählte Quelle | Kostenlos | Wie die gewählte Quelle | Keine |
| (cache) | LRU-In-Memory-Schicht über einer beliebigen Quelle | <1ms (Treffer), volle Latenz (Fehlschlag) | Kostenlos | Wie die zugrunde liegende Quelle | Immer aktiv (keine auswählbare Quelle) |
Entscheidungsbaum
Abschnitt betitelt „Entscheidungsbaum“ Wie sieht Ihr Bereitstellungskontext aus? │ ┌───────────┼───────────┬──────────────┐ │ │ │ │ ENTW./TEST KLEINE PROD. GROSSE PROD. EDGE/OFFLINE │ │ │ │ ▼ ▼ ▼ ▼ transformers transformers remote (Qdrant) transformers (kostenlos, keine API) (beste Qualität) (kein Internet) │ │ │ │ └────────┬──┴───────────┴──────────────┘ │ ▼ IMMER die `cache`-Schicht darüber hinzufügen (`LruCache` umschließt jeden Anbieter)Datenbank- und API-Konfiguration
Abschnitt betitelt „Datenbank- und API-Konfiguration“Optionen für Speichereinbettungen werden über die Einstellungs-API/-Benutzeroberfläche und nicht über Umgebungsvariablen konfiguriert. Die relevanten Datenbankschlüssel unter „Einstellungen“ (normalizeMemorySettings in src/lib/memory/settings.ts) sind:
memoryEmbeddingSource:"transformers"(lokal),"remote"(API-basiert, z. B. OpenAI),"static"(externer Speicher) oder"auto"memoryEmbeddingProviderModel: Modellkennung für Remote-/statische Quellen (z. B."text-embedding-3-small")memoryTransformersEnabled:true|falsememoryStaticEnabled:true|falsememoryVectorStore:"sqlite-vec","qdrant"oder"auto"
Lokales Modell (transformers)
Abschnitt betitelt „Lokales Modell (transformers)“Verwendet intern transformers.js, um lokale Modelle auszuführen:
# Im Code gelesene Umgebungsvariablen (src/lib/memory/embedding/index.ts):MEMORY_TRANSFORMERS_MODEL=Xenova/all-MiniLM-L6-v2 # HF-Modell-RepositoryMEMORY_STATIC_MODEL=minishlab/potion-base-8M # Statisches HF-Potion-ModellMEMORY_STATIC_CACHE_DIR=<DATA_DIR>/embeddings # Cache-VerzeichnisLRU-Einbettungscache
Abschnitt betitelt „LRU-Einbettungscache“Der Cache ist standardmäßig immer aktiviert und wird über Umgebungsvariablen konfiguriert:
MEMORY_EMBEDDING_CACHE_MAX=1000 # Maximale Anzahl zwischengespeicherter ElementeMEMORY_EMBEDDING_CACHE_TTL_MS=300000 # TTL (5 Min.)Leistungswerte
Abschnitt betitelt „Leistungswerte“Benchmark auf einem typischen x86-Server mit 4 Kernen (Texte mit jeweils ~100 Tokens):
| Anbieter | p50 | p95 | p99 | Kosten / 1 Mio. Embeddings |
|---|---|---|---|---|
transformers (CPU) |
80ms | 180ms | 350ms | Kostenlos |
remote (OpenAI) |
120ms | 220ms | 400ms | ~$0.02 (ada-002) / $0.13 (3-large) |
static (Qdrant) |
15ms | 30ms | 60ms | Abhängig vom Qdrant-Hosting |
cache (Treffer) |
<1ms | <1ms | 2ms | Kostenlos |
Muster zur Faktenextraktion (v3.8.16+)
Abschnitt betitelt „Muster zur Faktenextraktion (v3.8.16+)“Das Modul extraction.ts (src/lib/memory/extraction.ts) verwendet Musterabgleich mit regulären Ausdrücken, um strukturierte Fakten aus Konversationsnachrichten zu extrahieren. Das Verständnis dieser Muster hilft Ihnen, die Extraktionsqualität für Ihren Anwendungsfall zu optimieren.
Standardmäßige Musterkategorien
Abschnitt betitelt „Standardmäßige Musterkategorien“| Kategorie | Beispielmuster | Erfasst |
|---|---|---|
| PREFERENCE_PATTERNS | "Ich bevorzuge <X>", "Ich mag <X>", "Ich hasse <X>" |
Benutzerpräferenzen |
| DECISION_PATTERNS | "Ich werde <X> verwenden", "Ich habe mich für <X> entschieden" |
Benutzerentscheidungen (episodisch) |
| PATTERN_PATTERNS | "Ich mache normalerweise <X>", "Ich mache immer <X>", "Ich mache nie <X>" |
Dauerhafte Verhaltensmuster |
Beispielmuster (vereinfacht)
Abschnitt betitelt „Beispielmuster (vereinfacht)“// Aus src/lib/memory/extraction.tsconst PREFERENCE_PATTERNS = [ /\bI\s+(?:really\s+)?prefer\s+([^.,\n]+)/gi, /\bI\s+(?:really\s+)?like\s+([^.,\n]+)/gi, /\bI\s+(?:hate|dislike|avoid)\s+([^.,\n]+)/gi,];const DECISION_PATTERNS = [ /\bI'?(?:ll|will)\s+use\s+([^.,\n]+)/gi, /\bI\s+(?:have\s+)?decided\s+(?:to\s+)?([^.,\n]+)/gi,];const PATTERN_PATTERNS = [/\bI\s+usually\s+([^.,\n]+)/gi, /\bI\s+always\s+([^.,\n]+)/gi];Was extrahiert wird
Abschnitt betitelt „Was extrahiert wird“Wenn ein Benutzer Folgendes sagt:
„Ich bevorzuge TypeScript. Ich werde Postgres für dieses Projekt verwenden. Ich committe immer vor dem Pushen. Ich mag Python nicht.“ Die Extraktion erzeugt 4 Erinnerungen:
Schlüssel Kategorie Typ Inhalt preference:typescriptPräferenz faktisch “TypeScript” decision:postgres_for_this_projectEntscheidung episodisch “Postgres für dieses Projekt” pattern:commit_before_pushingMuster faktisch “vor dem Pushen committen” preference:pythonPräferenz faktisch “Python”
Extraktionsgrenzen
Abschnitt betitelt „Extraktionsgrenzen“Um eine unkontrollierte Extraktion zu verhindern, gelten die folgenden Grenzen:
| Mindestlänge des Inhalts | 3 Zeichen | | Maximallänge des Inhalts | 500 Zeichen |
Wann die Extraktion deaktiviert werden sollte
Abschnitt betitelt „Wann die Extraktion deaktiviert werden sollte“Die Extraktion wird automatisch ausgeführt, sobald der Speicher aktiviert ist; es gibt keinen separaten
Schalter nur für die Extraktion. Um sie zu deaktivieren, deaktivieren Sie den Speicher vollständig (enabled: false
über PUT /api/settings/memory). Dies kann in folgenden Fällen sinnvoll sein:
- Sie haben ein hohes Nachrichtenvolumen und die Extraktionskosten sind nicht unerheblich
- Ihre Konversationen sind überwiegend temporär (Chat, Debugging) und haben keinen langfristigen Wert
- Sie erfassen den Kontext bereits über benutzerdefinierte Plugins
Optimierung von Hybrid-RRF (v3.8.16+)
Abschnitt betitelt „Optimierung von Hybrid-RRF (v3.8.16+)“Der Algorithmus Reciprocal Rank Fusion (RRF) kombiniert Ergebnisse aus FTS5 (Schlüsselwörter) und Vektorsuche (Semantik). Der Parameter k steuert, wie stark niedriger eingestufte Ergebnisse gewichtet werden.
Die Formel
Abschnitt betitelt „Die Formel“Für jede infrage kommende Erinnerung lautet der RRF-Score:
RRF(d) = Σ 1 / (k + rank_i(d))Dabei gilt:
kist die Konstante (Standardwert 60)rank_i(d)ist der Rang des Dokumentsdim i-ten Abrufsystem (FTS, Vektor)- Die Summe erstreckt sich über alle Abrufsysteme
Auswirkungen von k auf die Ergebnisse
Abschnitt betitelt „Auswirkungen von k auf die Ergebnisse“k-Wert |
Auswirkung | Am besten geeignet für |
|---|---|---|
k=0 |
Reine Rangfusion (keine Glättung) | Theoretische Basislinie |
k=10-30 |
Gewichtet die besten Ergebnisse stark; niedrige Ränge tragen kaum bei | Wenn die Top-3-Ergebnisse meist korrekt sind |
k=60 (Standard) |
Ausgewogen — alle Top-10-Ergebnisse tragen wesentlich bei | Allgemeine Suche |
k=100+ |
Flacher — selbst Ergebnisse mit niedrigem Rang können dominieren, wenn sie in mehreren Systemen erscheinen | Wenn Trefferquote > Präzision entscheidend ist |
k in der Praxis optimieren
Abschnitt betitelt „k in der Praxis optimieren“# StandardwertMEMORY_RRF_K=60
# Aggressive Präzision (kleiner Speicher, wenige Dokumente)MEMORY_RRF_K=20
# Maximale Trefferquote (großer Speicher, vielfältige Abfragen)MEMORY_RRF_K=120Beispiel mit k=20:
- FTS-Rang 1 → Beitrag
1/21 = 0.048 - FTS-Rang 10 → Beitrag
1/30 = 0.033 - Vektorrang 1 → Beitrag
0.048 - Kombiniertes Maximum:
0.096
Beispiel mit k=60:
- FTS-Rang 1 → Beitrag
1/61 = 0.016 - FTS-Rang 10 → Beitrag
1/70 = 0.014 - Vektorrang 1 → Beitrag
0.016 - Kombiniertes Maximum:
0.033
Bei einem höheren k ist der relative Unterschied zwischen Rang 1 und Rang 10 kleiner, sodass sich der Algorithmus stärker auf den Konsens zwischen den Abrufsystemen als auf die Konfidenz des höchsten Rangs stützt.
Wann k geändert werden sollte
Abschnitt betitelt „Wann k geändert werden sollte“| Symptom | Versuch |
|---|---|
| Das Top-Ergebnis gewinnt immer, ist aber falsch | k senken (z. B. 20) — die Konfidenz des höchsten Rangs zählt stärker |
| Die richtige Antwort ist in den Top 5, aber nicht auf Rang 1 | k erhöhen (z. B. 100) — eine flachere Bewertung belohnt Konsens |
| Die Trefferquote ist hoch, aber die Präzision niedrig | k senken — die Rangfolge schärfen |
| Die Trefferquote ist niedrig (relevante Dokumente fehlen) | k erhöhen — niedriger eingestuften Dokumenten eine Chance geben |
RRF-Gewichtung
Abschnitt betitelt „RRF-Gewichtung“Die Reciprocal Rank Fusion verwendet gleiche Gewichtungen für den Rang der semantischen Vektorsuche und den Rang der Volltextsuche:
RRF(d) = 1/(k + rank_vector) + 1/(k + rank_fts)Es gibt keine Umgebungsvariablen, mit denen sich die einzelnen Gewichtungen anpassen lassen (MEMORY_RRF_VECTOR_WEIGHT/MEMORY_RRF_FTS_WEIGHT existieren nicht).
Zusammenfassungsstrategie (v3.8.16+)
Abschnitt betitelt „Zusammenfassungsstrategie (v3.8.16+)“Das Modul summarization.ts (src/lib/memory/summarization.ts) komprimiert ältere Erinnerungen, um die aktive Menge klein zu halten und gleichzeitig die Abrufbarkeit zu bewahren.
Wann die Zusammenfassung ausgelöst wird
Abschnitt betitelt „Wann die Zusammenfassung ausgelöst wird“| Auslöser | Schwellenwert (Standard) |
|---|---|
| Manuelle Auslösung über die API | k. A. |
Was zusammengefasst wird
Abschnitt betitelt „Was zusammengefasst wird“Aus summarization.ts werden zwei Einstiegspunkte exportiert:
summarizeMemories(apiKeyId, sessionId?, maxTokens = 4000)— verdichtet die Erinnerungen einer Sitzung zu einem einzigen Zusammenfassungstext, der durch ein Token-Budget begrenzt ist.summarizeMemoriesOlderThan(apiKeyId, days, dryRun)— die von der API verwendete altersbasierte Komprimierung: Sie wählt jede Erinnerung aus, die älter alsdaysist, erstellt daraus eine einzige verdichtete Zusammenfassungserinnerung und löscht (wenndryRunden Wertfalsehat) die Originale. Übergeben SiedryRun: true, um eine Vorschau der Kandidatenmenge und der gesamten Token-Anzahl anzuzeigen, ohne Änderungen vorzunehmen.
Es gibt weder einen Clustering-Durchlauf nach Tags/Schlüsseln noch eine Bewertung einzelner Erinnerungen als „zentral“ oder „zusammenfassbar“ — die Auswahl basiert ausschließlich auf dem Altersgrenzwert, und der Zusammenfassungstext besteht aus einer verdichteten, mit dem Typ präfixierten Zeile pro Kandidat.
Zusammenfassung auslösen
Abschnitt betitelt „Zusammenfassung auslösen“Die Zusammenfassung ist manuell / optional — die Einstellung autoSummarize ist
standardmäßig false, sodass nichts automatisch komprimiert wird. Lösen Sie sie über die API aus:
curl -X POST http://localhost:20128/api/memory/summarize \ -H "Authorization: Bearer $OMNIROUTE_KEY"Um sie deaktiviert zu lassen, belassen Sie autoSummarize einfach auf dem Standardwert (false).
Tipps zur Qualität der Zusammenfassung
Abschnitt betitelt „Tipps zur Qualität der Zusammenfassung“- Zeigen Sie zuerst mit
dryRuneine Vorschau an —summarizeMemoriesOlderThan(..., true)gibt die Kandidatenliste und die gesamte Token-Anzahl zurück, sodass Sie vor dem Löschen der Originale überprüfen können, was zusammengeführt würde. - Führen Sie die Zusammenfassung in Zeiten mit geringem Datenverkehr aus, wenn Sie über einen großen Erinnerungsbestand verfügen — der LLM-Aufruf ist der langsame Teil
# Cron-Stil: täglich um 3 Uhr zusammenfassen0 3 * * * curl -X POST http://localhost:20128/api/memory/summarize \ -H "Authorization: Bearer $OMNIROUTE_KEY"MemoryBackend-Provider-Muster
Abschnitt betitelt „MemoryBackend-Provider-Muster“Maßgebliche Quelle:
src/lib/memory/backend.ts,src/lib/memory/genericBackend.ts,src/lib/memory/manager.tsTests:src/lib/memory/__tests__/generic-backend.test.ts
Das MemoryBackend-Provider-Muster führt eine austauschbare Backend-Abstraktionsschicht über der bestehenden Memory-Engine ein. Statt an eine einzige Speicherimplementierung gebunden zu sein, unterstützt das Memory-System nun mehrere Backends (SQLite, Obsidian, Notion, benutzerdefinierte HTTP-Backends) mit konfigurierbarem Primär-/Fallback-Routing.
Architektur
Abschnitt betitelt „Architektur“┌──────────────────────────────────────────────────────────┐│ API-Routen ││ (src/app/api/memory/route.ts) │└──────────────────────┬───────────────────────────────────┘ │┌──────────────────────▼───────────────────────────────────┐│ MemoryManager ││ Singleton-Orchestrator (manager.ts) ││ ││ Primär ──► Backend A (z. B. SQLite) ││ Fallback ──► Backend B (z. B. Obsidian) ││ Backend C (z. B. Notion via GenericBackend)│└──────────────────────┬───────────────────────────────────┘ │ ┌──────────────┼──────────────┐ ▼ ▼ ▼┌────────────┐ ┌────────────┐ ┌──────────────────┐│ SQLite- │ │ Obsidian- │ │ GenericMemory- ││ Backend │ │ Backend │ │ Backend (HTTP) │└────────────┘ └────────────┘ └──────────────────┘Kernschnittstelle (backend.ts)
Abschnitt betitelt „Kernschnittstelle (backend.ts)“Jedes Backend muss die Schnittstelle MemoryBackend implementieren:
interface MemoryBackend { readonly id: string; readonly displayName: string;
// CRUD-Operationen create(input: CreateMemoryInput): Promise<Memory>; get(id: string): Promise<Memory | null>; update(id: string, updates: Partial<...>): Promise<boolean>; delete(id: string): Promise<boolean>; list(filter: MemoryFilter): Promise<{ data: Memory[]; total: number; byType: Record<string, number> }>;
// Suche search(config: SearchConfig): Promise<Memory[]>;
// Zustandsprüfung health(): Promise<HealthCheckResult>;
// Lebenszyklus (optional) initialize?(): Promise<void>; shutdown?(): Promise<void>;}MemoryManager (manager.ts)
Abschnitt betitelt „MemoryManager (manager.ts)“Singleton-Orchestrator, der:
- Backends über
register(backend)registriert — wird beim Start ausindex.tsaufgerufen - Primär- und Fallback-Backends über
configure(primary, fallbacks)konfiguriert - CRUD-Operationen und Suchanfragen an das primäre Backend weiterleitet, bei Fehlern über die Fallback-Kette
- regelmäßig Zustandsprüfungen für alle Backends durchführt
Fallback-Verhalten:
| Operation | Primär | Fallbacks |
|---|---|---|
create |
✅ Nur primäres Backend | ❌ |
get |
✅ Zuerst primäres versuchen | ✅ Fallback, falls null |
update |
✅ Nur primäres Backend | ✅ Asynchrone Synchronisierung |
delete |
✅ Nur primäres Backend | ✅ Asynchrone Synchronisierung |
list |
✅ Nur primäres Backend | ❌ |
search |
✅ Zuerst primäres Backend | ✅ Fallback bei Fehler |
GenericMemoryBackend (genericBackend.ts)
Abschnitt betitelt „GenericMemoryBackend (genericBackend.ts)“Ein generischer HTTP-Konnektor, der jede REST-API an ein MemoryBackend anpasst. Nützlich für:
- Notion — Verbindung über die Notion API
- Obsidian — Verbindung über die Obsidian Local REST API
- Benutzerdefinierte Backends — jeder Dienst, der eine RESTful Memory-API bereitstellt
Konfiguration:
interface GenericBackendConfig { baseUrl: string; // Basis-URL der Backend-API apiKey?: string; // Bearer-Token für die Authentifizierung headers?: Record<string, string>; // Benutzerdefinierte HTTP-Header timeout?: number; // Anfrage-Timeout (Standard: 30000ms) backendType?: string; // Für die Protokollierung
// Überschreibungen für Endpunkte (Standardwerte folgen REST-Konventionen) endpoints?: { search?: string; // Standard: "/memories/search" create?: string; // Standard: "/memories" list?: string; // Standard: "/memories" get?: string; // Standard: "/memories/{id}" update?: string; // Standard: "/memories/{id}" delete?: string; // Standard: "/memories/{id}" health?: string; // Standard: "/health" };
// Zuordnungen von Abfrageparameternamen queryParams?: { query?/apiKeyId?/limit?/offset?/strategy?/maxTokens?/type?/sessionId?/orderBy?/orderDir?/options? };
// Zuordnungen von Pfadparameternamen pathParams?: { id?/memoryId? };}Bekannte Backends sind in KNOWN_BACKENDS vorkonfiguriert:
createKnownBackend("obsidian"); // → GenericMemoryBackend verweist auf localhost:27123createKnownBackend("notion"); // → GenericMemoryBackend verweist auf api.notion.com/v1Integrierte Backends
Abschnitt betitelt „Integrierte Backends“SQLiteBackend (sqliteBackend.ts)
Abschnitt betitelt „SQLiteBackend (sqliteBackend.ts)“Das standardmäßige primäre Backend. Kapselt den vorhandenen SQLite-basierten Speicher unter Verwendung von src/lib/memory/store.ts. Wird beim Start automatisch registriert.
import { sqliteBackend } from "./sqliteBackend";memoryManager.register(sqliteBackend);ObsidianBackend (obsidianBackend.ts)
Abschnitt betitelt „ObsidianBackend (obsidianBackend.ts)“Kapselt die vorhandene Obsidian-Integration (src/lib/memory/obsidianBackend.ts). Stellt über die Obsidian Local REST API eine Verbindung zu einem Obsidian-Vault her.
Einstellungen
Abschnitt betitelt „Einstellungen“Die Einstellungen für Speicher-Backends werden in der App-Einstellungstabelle gespeichert und über src/lib/memory/settings.ts verwaltet:
| Einstellung | Umgebungs-/Konfigurationsschlüssel | Standard | Beschreibung |
|---|---|---|---|
| Primäres Backend | memoryPrimaryBackend |
"sqlite" |
ID des primären Backends |
| Fallback-Backends | memoryFallbackBackends |
[] |
Geordnete IDs der Fallback-Backends |
| Backend-Konfigurationen | memoryBackendConfigs |
{} |
Konfigurationsüberschreibungen je Backend |
Die Einstellungen werden über normalizeMemorySettings() normalisiert und bei getMemorySettings() zwischengespeichert.
Initialisierungsablauf
Abschnitt betitelt „Initialisierungsablauf“App-Bootstrap → index.ts-Importe (Nebeneffekt): registrieren SQLiteBackend → initMemoryBackends() wird aus dem App-Lebenszyklus aufgerufen: 1. Einstellungen laden (getMemorySettings) 2. Primäres Backend und Fallbacks konfigurieren 3. Alle Backends initialisieren (Integritätsprüfung) 4. Bereit für AnfragenHinzufügen eines neuen Backends
Abschnitt betitelt „Hinzufügen eines neuen Backends“MemoryBackend-Schnittstelle implementieren insrc/lib/memory/<name>Backend.ts- Exportieren aus
src/lib/memory/index.ts - Registrieren mit
memoryManager.register(yourBackend)beim Start - Konfigurieren über die Einstellungen:
memoryPrimaryBackendauf die ID Ihres Backends setzen - Testen mit
src/lib/memory/__tests__/generic-backend.test.tsals Referenz
Beispiel: Brain-Backend
Abschnitt betitelt „Beispiel: Brain-Backend“import { createGenericMemoryBackend } from "./genericBackend";
const brainBackend = createGenericMemoryBackend("brain", "BK-Brain", { baseUrl: process.env.BRAIN_API_URL || "http://localhost:9099", apiKey: process.env.BRAIN_API_KEY, endpoints: { search: "/api/memory/search", create: "/api/memory", health: "/api/health", },});
memoryManager.register(brainBackend);Verifizierung
Abschnitt betitelt „Verifizierung“Unit-Tests
Abschnitt betitelt „Unit-Tests“npx vitest run src/lib/memory/__tests__/generic-backend.test.ts --reporter=verboseErwartete Ausgabe: 35 Tests, alle erfolgreich, mit Abdeckung für:
- Konstruktor (2)
- Integritätsprüfung (4) — Erfolg, Fehler 500, Netzwerkfehler, Latenz
- Initialisierung (2) — Erfolg, Fehler
- Erstellen (2) — Standardendpunkt, benutzerdefinierter Endpunkt
- Abrufen (4) — Erfolg, 404 → null, Ausnahme bei anderem Status als 404, benutzerdefinierte Pfadparameter
- Aktualisieren (2) — Erfolg, 404 → false
- Löschen (2) — Erfolg, 404 → false
- Auflisten (2) — Abfrageparameter, benutzerdefinierte Parameternamen
- Suchen (3) — Abfrageparameter, benutzerdefinierter Endpunkt, Serialisierung von Optionen
- Authentifizierungs-Header (2) — Bearer-Token, benutzerdefinierte Header
- Factory (1)
Typprüfung
Abschnitt betitelt „Typprüfung“npm run typecheck:coreErwartet: 0 Fehler.
HagiCode
HagiCode ist ein agentischer Coding-Arbeitsplatz mit strukturierten Workflows, Multi-Agent-Ausführung und Hero-Dungeon-Ansichten.
Mit einem intelligenteren, schnelleren und unterhaltsameren agentischen Workflow wird aus Ideen nutzbare Software.

- SmartStrukturierte Workflows machen aus Absichten einen umsetzbaren Weg von der Idee bis zur Auslieferung.
- EfficientMulti-Agent-Workflows führen Recherche, Umsetzung und Prüfung parallel aus.
- FunHero Dungeon macht lange Coding-Sitzungen anschaulich und gemeinschaftlich.