AEI Framework est un framework Rust open source pour construire des réseaux de neurones dynamiques, modulaires, évolutifs, intégrés et multi-agents.
- Modifier la structure du réseau à l'exécution.
- Ajouter ou supprimer des neurones et des synapses à la volée.
- Fournir une architecture simple et bien documentée.
JsonlEventStore<T> persiste tout type d'événement implémentant Serialize et DeserializeOwned au format JSON Lines. Les alias FileEventStore et FileMemoryEventStore fournissent des magasins prêts à l'emploi pour les événements de domaine et de mémoire.
Étendez le réseau en envoyant une commande gérée par l'infrastructure orientée événements :
use aei_framework::{
AddRandomNeuronCommand, AddRandomNeuronHandler, FileEventStore,
};
use rand::thread_rng;
use std::path::PathBuf;
let store = FileEventStore::new(PathBuf::from("events.log"));
let mut handler = AddRandomNeuronHandler::new(store, thread_rng()).unwrap();
let new_neuron_id = handler.handle(AddRandomNeuronCommand).unwrap();
println!("Neurone ajouté : {new_neuron_id}");Réduisez le réseau via un gestionnaire dédié :
use aei_framework::{
RemoveRandomNeuronCommand, RemoveRandomNeuronHandler, FileEventStore,
};
use rand::thread_rng;
use std::path::PathBuf;
let store = FileEventStore::new(PathBuf::from("events.log"));
let mut handler = RemoveRandomNeuronHandler::new(store, thread_rng()).unwrap();
if let Ok(removed_id) = handler.handle(RemoveRandomNeuronCommand) {
println!("Neurone supprimé : {removed_id}");
}Muter la fonction d’activation d’un neurone choisi aléatoirement :
use aei_framework::{
MutateRandomNeuronActivationCommand, MutateRandomNeuronActivationHandler,
FileEventStore,
};
use rand::thread_rng;
use std::path::PathBuf;
let store = FileEventStore::new(PathBuf::from("events.log"));
let mut handler =
MutateRandomNeuronActivationHandler::new(store, thread_rng()).unwrap();
if let Ok(neuron_id) = handler.handle(MutateRandomNeuronActivationCommand { exclude_io: false }) {
println!("Activation mutée : {neuron_id}");
}Créez une synapse entre deux neurones choisis aléatoirement en utilisant le gestionnaire orienté événements :
use aei_framework::{
application::{AddRandomSynapseCommand, AddRandomSynapseHandler},
infrastructure::FileEventStore,
};
use std::path::PathBuf;
let store = FileEventStore::new(PathBuf::from("events.log"));
let mut handler = AddRandomSynapseHandler::new(store, rand::thread_rng()).unwrap();
let synapse_id = handler.handle(AddRandomSynapseCommand).unwrap();
println!("Synapse créée : {synapse_id}");Supprimez une synapse sélectionnée aléatoirement via un gestionnaire orienté événements :
use aei_framework::{
RemoveRandomSynapseCommand, RemoveRandomSynapseHandler, FileEventStore,
};
use rand::thread_rng;
use std::path::PathBuf;
let store = FileEventStore::new(PathBuf::from("events.log"));
let mut handler = RemoveRandomSynapseHandler::new(store, thread_rng()).unwrap();
if let Ok(removed_id) = handler.handle(RemoveRandomSynapseCommand) {
println!("Synapse supprimée : {removed_id}");
}Assignez un poids précis à une synapse existante :
use aei_framework::{SetSynapseWeightCommand, SetSynapseWeightHandler, FileEventStore};
use std::path::PathBuf;
let store = FileEventStore::new(PathBuf::from("events.log"));
let mut handler = SetSynapseWeightHandler::new(store).unwrap();
let synapse_id = uuid::Uuid::new_v4(); // identifiant d'une synapse existante
handler
.handle(SetSynapseWeightCommand { synapse_id, new_weight: 0.5 })
.unwrap();Ajustez le poids d'une synapse en ajoutant un bruit gaussien :
use aei_framework::{
MutateRandomSynapseWeightCommand, MutateRandomSynapseWeightHandler, FileEventStore,
};
use rand::thread_rng;
use std::path::PathBuf;
let store = FileEventStore::new(PathBuf::from("events.log"));
let mut handler = MutateRandomSynapseWeightHandler::new(store, thread_rng()).unwrap();
if let Ok(synapse_id) =
handler.handle(MutateRandomSynapseWeightCommand { std_dev: 0.1 })
{
println!("Synapse mutée : {synapse_id}");
}Enregistrez et interrogez des expériences passées dans un tampon borné et scoré :
use aei_framework::{
application::memory::{
AddMemoryEntryCommand, AddMemoryEntryHandler, MemoryQuery, MemoryQueryHandler,
},
infrastructure::{projection::MemoryProjection, FileMemoryEventStore},
};
use serde_json::json;
use std::path::PathBuf;
let store = FileMemoryEventStore::new(PathBuf::from("memory.log"));
let mut handler = AddMemoryEntryHandler::new(store, 50).unwrap();
handler
.handle(AddMemoryEntryCommand {
event_type: "interaction".into(),
payload: json!({"msg": "bonjour"}),
score: 0.7,
})
.unwrap();
let mut store = handler.base.store;
let events = store.load().unwrap();
let projection = MemoryProjection::from_events(50, &events);
let qh = MemoryQueryHandler::new(&projection);
let _entries = qh.handle(MemoryQuery::GetByEventType {
event_type: "interaction".into(),
limit: 10,
});Évaluez le potentiel exploratoire des composants du réseau :
use aei_framework::{
CuriosityScope, RecalculateCuriosityScoreCommand, RecalculateCuriosityScoreHandler,
FileEventStore,
};
use std::path::PathBuf;
let store = FileEventStore::new(PathBuf::from("events.log"));
let mut handler = RecalculateCuriosityScoreHandler::new(store).unwrap();
handler
.handle(RecalculateCuriosityScoreCommand {
target_ids: vec![],
scope: CuriosityScope::All,
})
.unwrap();Le framework émet des messages d'information via la crate log. Pour afficher ces journaux, initialisez une implémentation de logger comme env_logger dans votre application :
env_logger::init();Avec un logger configuré, la progression des gestionnaires de commandes sera rapportée au niveau info.
Un flux de commandes minimal est disponible dans examples/basic.rs :
cargo run --example basicIl ajoute des neurones, les relie par une synapse et interroge le modèle de lecture.
src/
domain/ # primitives, agrégats et événements de domaine
application/ # commandes, requêtes et gestionnaires
infrastructure/
event_store.rs # implémentations du magasin d'événements
projection/ # projections pour la lecture
examples/
tests/
docs/
en/
fr/
AEIF suit le Domain-Driven Design avec Event Sourcing et CQRS. Les opérations modifiant l'état sont exprimées sous forme de commandes transformées en événements immuables et ajoutées à un journal. Les agrégats tels que domain::Network rejouent ces événements pour reconstruire leur état. Les lectures sont servies via des requêtes traitées par des projections situées sous infrastructure/projection.
Commande → Événement → Application → Projection
- Une commande exprime l'intention de modifier l'état.
- Le gestionnaire émet et persiste un événement de domaine.
- L'agrégat applique l'événement pour mettre à jour son état.
- Les projections consomment l'événement pour rafraîchir les modèles de lecture.
Les guides en anglais et en français sont disponibles dans docs/en et docs/fr. Un glossaire des termes métiers et techniques est disponible dans GLOSSARY.md.
cargo build # Compiler le projet
cargo test # Exécuter la suite de testsLes contributions sont les bienvenues !
Voir CHANGELOG.md pour la liste des modifications.
Distribué sous la licence publique Mozilla 2.0.
- Les identificateurs de neurones et de synapses utilisent
Uuid. Les réseaux sérialisés avec d'anciens identifiants numériques ne sont pas pris en charge. - La persistance JSON est disponible via
save_jsonetload_json. - Des abstractions en couches sont prévues mais non encore implémentées.