Présentation
Imp est une implémentation complète de DSPy pour la machine virtuelle BEAM. Le projet décrit un cadre déclaratif permettant de programmer des modèles de langage (LLM) dans Elixir, en spécifiant chaque étape comme une fonction typée. L’objectif est de combiner la fiabilité d’OTP avec la flexibilité des appels LLM, sans écrire de prompts ni de parseurs manuels.
Architecture et mécanismes
Chaque appel à un modèle est déclaré via Imp.signature/2, qui génère automatiquement le prompt à partir du type d’entrée et du type de sortie. Le résultat est renvoyé sous forme de champs typés, par exemple :kind limité à l’énumération ["bug","feature","question"]. Sur le BEAM, chaque tâche s’exécute dans un processus OTP : l’agent possède son état, reçoit des messages et est supervisé comme tout autre composant d’une application Elixir. Cette approche rend possible le chaînage de plusieurs agents longs‑termes tout en conservant la robustesse du modèle d’acteur.
lm = Imp.req_llm("openai:gpt-5.4-mini", api_key: System.fetch_env!("OPENAI_API_KEY"))
triage = "issue -> kind: enum[bug,feature,question], summary"
|> Imp.signature("Triage a GitHub issue.")
|> Imp.predict(lm: lm)
{ :ok, prediction } = Imp.call(triage, %{issue: "App crashes on startup…"})
{Imp.get(prediction, :kind), Imp.get(prediction, :summary)}Le code ci‑dessus montre la création d’un modèle LLM, la définition d’une signature et l’appel du programme, le tout sans texte de prompt explicite.
Optimisation et métriques
Imp intègre un système d’optimisation basé sur trois ensembles de données : trainset (exemples étiquetés pour l’apprentissage), valset (sélection du meilleur programme) et testset (évaluation finale). L’exemple du README utilise l’optimiseur Imp.Optimizer.GEPA avec le modèle de réflexion strong_lm et une limite de max_metric_calls: 300. La métrique Imp.exact_match(:kind) mesure la correspondance exacte du champ :kind. Après optimisation, le programme amélioré est ré‑évalué sur le même testset, ce qui permet de comparer les scores avant et après.
D’autres optimiseurs sont mentionnés : LabeledFewShot, BootstrapFewShot, MIPROv2, SIMBA et des approches de fine‑tuning comme GRPO. Chaque méthode explore un espace différent – exemples sélectionnés, combinaisons d’instructions ou ajustement des poids du modèle – tout en conservant la trace des modifications sous forme JSON diffable.
Outils et intégration
Imp considère un outil comme une fonction Elixir. La fonction Imp.react/3 crée un agent capable d’appeler successivement des outils jusqu’à obtenir une réponse. L’exemple fourni montre un outil fetch qui lit une page web via la bibliothèque Req. Le dépôt indique que Req ~> 0.6 doit être ajouté aux dépendances pour activer cet outil.
fetch = Imp.tool(:fetch, "Read a web page as text.", fn %{"url" => url} ->
Req.get!(url).body
end, schema: %{"type" => "object", "properties" => %{"url" => %{"type" => "string"}}, "required" => ["url"]})
researcher = Imp.react("question -> answer", [fetch], lm: lm)
question = "What version does https://raw.githubusercontent.com/elixir-lang/elixir/v1.18.0/VERSION say?"
Imp.call(researcher, %{question: question})Cette construction montre comment un agent peut combiner un LLM avec un accès réseau, le tout orchestré par le superviseur OTP. Imp fournit ainsi une pile complète : description déclarative, exécution concurrente, mesure précise et boucle d’amélioration automatisée.