
Mémoire agentive pour la CTI en Python — graphes de connaissances STIX, résolution d'alias d'acteurs de menace, RAG hors ligne en priorité, serveur MCP pour Claude Code et agents LangChain
Le seul système de mémoire agentique conçu pour le renseignement sur les cybermenaces.
Lorsqu’un analyste senior quitte l’équipe, deux ou trois années de contexte disparaissent avec lui — environnements clients, enquêtes antérieures, TTPs des acteurs, schémas de faux positifs, chaque « attendez, on a déjà vu ça » durement acquis. ZettelForge est un système de mémoire agentique construit pour que le contexte reste dans l’équipe.
Il extrait les CVE, acteurs de menace, IOC et techniques ATT&CK à partir des notes d’analystes et des rapports de menace, résout les alias (APT28 = Fancy Bear = STRONTIUM = Sofacy), construit un graphe de connaissances STIX 2.1, et restitue chaque enquête passée à vos analystes — et à Claude Code via MCP — en langage naturel. Fonctionne entièrement en processus. Pas de clés API. Pas de cloud. Aucune donnée ne quitte l’hôte.
Star · pip install zettelforge · Docs · ThreatRecall (hébergé) · Changelog
v2.6.2 (2026-04-27) : L’éditeur web de configuration intègre des menus déroulants fonctionnels pour tous les champs d’énumération (fournisseur LLM/embedding, niveau de log, action PII, format de synthèse) et un bouton Appliquer fonctionnel. Nouvel extra
[crewai]expose ZettelForge en tant qu’outils CrewAI --pip install zettelforge[crewai]. Changelog complet
Si ZettelForge correspond à un workflow CTI que vous utilisez, une étoile est le signal le plus rapide que cette catégorie mérite d’être soutenue.
Chaque SOC perd des analystes. Quand ils partent, le contexte d’enquête, l’attribution des acteurs et les schémas de faux positifs spécifiques à l’environnement disparaissent avec eux. Leurs remplaçants rouvrent les mêmes tickets, relisent les mêmes rapports et reconstruisent les mêmes modèles mentaux depuis zéro.
Les systèmes de mémoire généralistes basés sur l’IA ne résolvent pas ce problème pour les équipes de sécurité. Ils ne peuvent pas distinguer APT28 de Fancy Bear, ne savent pas que CVE-2024-3094 est la backdoor XZ Utils, ne savent pas analyser Sigma ou YARA, et n’ont aucune notion des identifiants de technique MITRE ATT&CK. Quand un analyste CTI leur donne une année de rapports de renseignement, ils renvoient une recherche sémantique floue sur l’historique des conversations.
ZettelForge a été conçu pour les analystes qui pensent en graphes de menace. Il extrait automatiquement les CVE, acteurs de menace, IOC et techniques ATT&CK, résout les alias entre conventions de nommage, construit un graphe de connaissances avec relations causales, et récupère les mémoires via une recherche hybride consciente de l’intention — le tout en processus, sans dépendance API externe.
L’augmentation de mémoire comble 33 % de l’écart entre les petits et grands modèles sur les tâches CTI (CTI-REALM, Microsoft 2026, avec GPT-4 comme référence du grand modèle). Voir le rapport de benchmark complet pour la méthodologie et les comparaisons.
Extraction d’entités -- Identifie automatiquement les CVE, acteurs de menace, IOC (IP, domaines, hachages, URLs, e-mails), techniques MITRE ATT&CK, campagnes, ensembles d’intrusion, outils, personnes, lieux et organisations. Regex + NER LLM avec types STIX 2.1 tout au long.
Graphe de connaissances -- Les entités deviennent des nœuds, la cooccurrence devient des arêtes. Le LLM infère des triplets causaux (« APT28 utilise Cobalt Strike »). Les arêtes temporelles et les suppressions suivent l’évolution du renseignement.
Résolution d’alias -- APT28, Fancy Bear, Sofacy, STRONTIUM résolvent tous vers le même nœud d’acteur. Fonctionne automatiquement lors du stockage et du rappel.
Récupération hybride -- Similarité vectorielle (768-dim fastembed, ONNX) + parcours de graphe (BFS sur les arêtes du graphe de connaissances), pondéré par classification d’intention. Cinq types d’intention : factuel, temporel, relationnel, exploratoire, causal.
Évolution de la mémoire -- Avec evolve=True, le nouveau renseignement est comparé à la mémoire existante. Le LLM décide AJOUTER, METTRE À JOUR, SUPPRIMER ou NE RIEN FAIRE. Le renseignement obsolète est remplacé. Les contradictions sont résolues. Les doublons sont ignorés.
Synthèse RAG -- Synthétise des réponses à travers toutes les mémoires stockées avec le format direct_answer.
En processus par architecture -- fastembed (ONNX) pour les embeddings, llama-cpp-python pour l’inférence LLM locale facultative, SQLite + LanceDB pour le stockage, et Ollama sur localhost par défaut. Aucune clé API externe n’est requise. Un accès réseau sortant peut se produire lors de la première exécution lorsque les modèles d’embedding/LLM sont téléchargés ; une fois les modèles préchargés, il peut fonctionner entièrement hors ligne (y compris sur des hôtes isolés).
Journalisation d’audit au format OCSF -- Chaque opération émet un événement structuré dans le schéma Open Cybersecurity Schema Framework. Ce que vous faites du flux de logs (SIEM, stockage WORM, rien) vous appartient.
pip install zettelforge
from zettelforge import MemoryManager
mm = MemoryManager()
# Stocker du CTI -- les entités (CVE, acteurs, IDs ATT&CK, IOC) sont extraites via regex
mm.remember("APT28 utilise Cobalt Strike pour le mouvement latéral via T1021")
mm.remember("APT28 (Fancy Bear) cible les sous-traitants de l'OTAN avec du spear-phishing")
mm.remember("CVE-2024-3094 est la backdoor XZ Utils (CVSS 10.0) affectant sshd")
# Le rappel combine recherche vectorielle + graphe ; la résolution d'alias agit (Fancy Bear -> APT28)
for note in mm.recall("Quels outils Fancy Bear utilise-t-il ?", k=3):
print(f"[{note.metadata.tier}] {note.content.raw}")
Cela fonctionne sur une fraîche installation pip sans services externes. Les embeddings s’exécutent en processus via fastembed (~80MB de modèle ONNX téléchargé au premier appel). MemoryManager() écrit dans ~/.amem/ par défaut ; remplacez avec ZETTELFORGE_DATA_DIR ou via la configuration. Une copie exécutable se trouve dans examples/quickstart.py.
ollama pull qwen3.5:9b && ollama serve
# Avec Ollama en cours d'exécution, synthesize() renvoie un vrai résumé à travers les notes stockées
answer = mm.synthesize("Résumer les TTP connus d'APT28")
print(answer["synthesis"]["answer"])
# Le NER LLM en arrière-plan enrichit également les notes stockées avec des entités supplémentaires
ZettelForge détecte automatiquement Ollama. Pour utiliser un autre fournisseur (local llama-cpp, litellm pour plus de 100 fournisseurs, mock pour les tests), voir Configuration. Sans LLM, synthesize() renvoie toujours une réponse structurée mais le champ answer est un espace réservé de secours — seuls remember et recall produisent des résultats utiles en mode pip uniquement.
# Nouveau renseignement arrive -- evolve=True active l'évolution de la mémoire :
# le LLM extrait les faits, les compare aux notes existantes, décide AJOUTER/METTRE À JOUR/SUPPRIMER/NE RIEN FAIRE
mm.remember(
"APT28 a changé de tactique. Ils ont abandonné DROPBEAR et exploitent désormais les périphériques périphériques.",
domain="cti",
evolve=True, # la note APT28 existante est remplacée, pas dupliquée
)
Chaque appel à remember() déclenche un pipeline :
Chaque appel à recall() combine deux stratégies de récupération :
pip install zettelforge
Créez ou modifiez .claude.json à la racine de votre projet (ou ~/.claude/.claude.json pour un accès global) :
{
"mcpServers": {
"zettelforge": {
"command": "python3",
"args": ["-m", "zettelforge.mcp"]
}
}
}
Si ZettelForge est installé dans un environnement virtuel, utilisez le chemin complet de cet interpréteur Python :
{
"mcpServers": {
"zettelforge": {
"command": "/home/user/.venvs/zettelforge/bin/python",
"args": ["-m", "zettelforge.mcp"]
}
}
}
Lancez Claude Code et vérifiez que les outils sont disponibles :
claude
# Dans la session, demandez : "Quels outils avez-vous disponibles depuis zettelforge ?"
Sept outils sont exposés : zettelforge_remember, zettelforge_recall, zettelforge_synthesize, zettelforge_entity, zettelforge_graph, zettelforge_stats et zettelforge_sync (nécessite le package entreprise). Voir la référence du protocole MCP pour les schémas complets, les exemples de requêtes/réponses JSON-RPC, les codes d’erreur et le cycle de vie du singleton paresseux. Pour le dépannage, les chemins de virtualenv et les tests manuels d’outils, voir set-up-mcp-server.
Évalué sur des benchmarks académiques publiés :
La colonne Score rapporte les mesures de ZettelForge effectuées avec des modèles hébergés sur Ollama, à une exception près : la ligne LOCOMO a été remesurée en v2.1.1 en utilisant un juge cloud Ollama pour l’évaluation (pas la génération locale). Voir le rapport de benchmark complet pour la méthodologie spécifique au benchmark, l’historique des versions et la configuration du juge par suite.
Les règles Sigma et YARA sont des primitives de mémoire de première classe. Analysez, validez et ingérez une règle, et ses balises deviennent des arêtes de graphe : les techniques MITRE ATT&CK, les CVE, les alias d’acteurs de menace, les outils et les familles de malwares se résolvent dans la même ontologie que toute autre note. Un supertype DetectionRule partagé porte les sous-types SigmaRule et YaraRule, de sorte qu’un seul UUID de règle est adressable dans les deux formats.
Les règles Sigma sont validées par rapport au schéma JSON SigmaHQ sous licence. Les règles YARA sont analysées avec plyara et vérifiées par rapport au standard de métadonnées YARA CCCS (niveaux : strict, warn, non_cccs). L’ingestion est idempotente — réingérer une règle inchangée renvoie la note originale via un source_ref basé sur le hachage du contenu.
from zettelforge import MemoryManager
from zettelforge.sigma import ingest_rule as ingest_sigma
from zettelforge.yara import ingest_rule as ingest_yara
mm = MemoryManager()
ingest_sigma("rules/proc_creation_win_office_macro.yml", mm)
ingest_yara("rules/webshell_china_chopper.yar", mm, tier="warn")
# Ingestion en masse depuis SigmaHQ ou un dépôt de règles privé
python -m zettelforge.sigma.ingest /path/to/sigma/rules/
python -m zettelforge.yara.ingest /path/to/yara/rules/ --tier warn
# Vérification CI -- analyse + validation, sans écriture
python -m zettelforge.sigma.ingest rules/ --dry-run
Un explicateur de règles LLM (zettelforge.detection.explainer.explain) produit un résumé JSON structuré — intention, champs clés, notes d’évasion, hypothèses de faux positifs — pour toute DetectionRule. Il s’exécute de manière synchrone à la demande en v1 ; le câblage de la file d’attente d’enrichissement asynchrone est prévu en v1.1. Limité en débit via ZETTELFORGE_EXPLAIN_RPM (par défaut 60 appels/minute).
Références : Spécification Sigma, Règles SigmaHQ, CCCS YARA, Documentation YARA.
Ingérez les chasses ATHF terminées dans la mémoire ZettelForge. Les techniques MITRE et les IOC sont extraites et liées dans le graphe de connaissances.
python examples/athf_bridge.py /path/to/hunts/
# 12 chasse(s) analysée(s)
# 12/12 chasses ingérées dans ZettelForge
Voir examples/athf_bridge.py.
ThreatRecall est la distribution commerciale de ZettelForge avec des extensions entreprise activées. Elle est proposée par défaut en SaaS géré, avec des déploiements auto-hébergés sur site et isolés (air-gapped) en option pour les environnements classifiés. Modules complémentaires entreprise :
Le SaaS se déploie en quelques minutes sans infrastructure à maintenir. L’auto-hébergé est livré sous forme de bundle déployable pour les environnements où le trafic réseau sortant est restreint ou interdit.
Rejoindre la liste d’attente -- nous recrutons actuellement des partenaires de conception.
Voir config.default.yaml pour toutes les options.
Voir CONTRIBUTING.md pour la configuration de développement.
MIT -- Voir LICENSE.
Construit par Patrick Roland -- LinkedIn | Directeur des services SOC, Summit 7 Systems | Vétéran du nucléaire de la Navy | CISSP, CCP (CMMC 2.0 Professional)
ZettelForge est sous licence MIT. Mettez une étoile au dépôt, ouvrez des issues et soumettez des PR — toutes les contributions sont les bienvenues.
| Capacité | ZettelForge | Mem0 | Graphiti | Cognee |
|---|
| Extraction d’entités CTI (CVE, acteurs, IOC) | Oui | Non | Non | Non |
| Ontologie STIX 2.1 | Oui | Non | Non | Non |
| Résolution d’alias d’acteurs de menace | Oui (APT28 = Fancy Bear) | Non | Non | Non |
| Graphe de connaissances avec triplets causaux | Oui | Non | Oui | Oui |
| Récupération classée par intention (5 types) | Oui | Non | Non | Non |
| En processus / sans API externe requise | Oui | Non | Non | Non |
| Journaux d’audit au format OCSF | Oui | Non | Non | Non |
| Serveur MCP (Claude Code) | Oui | Non | Non | Non |
| Benchmark | Ce qui est mesuré | Score |
|---|
| CTI Retrieval (sous-ensemble CTIBench) | Attribution, lien CVE, multi-sauts | 75,0% |
| RAGAS | Qualité de récupération (présence de mots-clés) | 78,1% |
| LOCOMO (ACL 2024) | Rappel de mémoire conversationnelle | 22,0% |
| Variable | Défaut | Description |
|---|
AMEM_DATA_DIR | ~/.amem | Répertoire de données |
ZETTELFORGE_BACKEND | sqlite | Backend SQLite communautaire. TypeDB disponible via extension. |
ZETTELFORGE_LLM_PROVIDER | local | local (llama-cpp) ou ollama |