mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-12 01:37:06 +02:00
feat(skills): 5 méthodes expertes auto-injectées (routeur lexical)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
afe0a837d4
commit
31dce6036b
9 files changed
+159
-1
No files matched your search
@@ -64,6 +64,7 @@ PROFILE_FIELDS = {
|
||||
"self_review",
|
||||
"rag_enabled",
|
||||
"embed_model",
|
||||
"skills_enabled",
|
||||
"keep_alive",
|
||||
*GENERATION_FIELDS,
|
||||
}
|
||||
@@ -107,6 +108,8 @@ DEFAULT_CONFIG: dict = {
|
||||
# Mémoire long-terme (RAG) entre sessions, via un modèle d'embedding.
|
||||
"rag_enabled": True,
|
||||
"embed_model": "auto",
|
||||
# Skills : méthodes expertes injectées automatiquement selon la tâche.
|
||||
"skills_enabled": True,
|
||||
# Durée de maintien du modèle en VRAM (préchargement). "0" = décharge
|
||||
# aussitôt, "-1" = jamais, "30m" = 30 minutes.
|
||||
"keep_alive": "30m",
|
||||
|
||||
@@ -18,7 +18,8 @@ from fastapi import APIRouter, HTTPException
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel
|
||||
|
||||
from .. import agent_config, coder, db, enhance, memory, rag, router as msg_router
|
||||
from .. import agent_config, coder, db, enhance, memory, rag, skills
|
||||
from .. import router as msg_router
|
||||
from ..tools import check_html, _safe_path
|
||||
from ..agent import run_agent
|
||||
from ..config import settings
|
||||
@@ -237,6 +238,18 @@ async def chat(req: ChatRequest) -> StreamingResponse:
|
||||
+ "\n---\n".join(memories),
|
||||
})
|
||||
|
||||
# Skill : méthode experte injectée pour ce tour (jamais persistée).
|
||||
if cfg.get("skills_enabled", True):
|
||||
skill = skills.pick_skill(req.content)
|
||||
if skill:
|
||||
convo.insert(1, {
|
||||
"role": "system",
|
||||
"content": (
|
||||
"Méthode à suivre pour cette tâche :\n" + skill["body"]
|
||||
),
|
||||
})
|
||||
yield _sse("notice", {"message": f"📘 Méthode : {skill['title']}"})
|
||||
|
||||
if plan:
|
||||
yield _sse("plan", {"steps": plan})
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
"""Skills : méthodes expertes injectées automatiquement selon la tâche.
|
||||
|
||||
Sélection lexicale instantanée (aucun appel LLM) : au plus UNE skill par
|
||||
message, injectée en message système pour ce tour uniquement.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
_SKILLS_DIR = os.path.join(os.path.dirname(__file__), "..", "skills")
|
||||
|
||||
# Seuil : nombre minimal de mots-clés distincts trouvés dans le message.
|
||||
_MIN_HITS = 2
|
||||
|
||||
|
||||
def _load() -> dict[str, dict]:
|
||||
out: dict[str, dict] = {}
|
||||
if not os.path.isdir(_SKILLS_DIR):
|
||||
return out
|
||||
for fname in sorted(os.listdir(_SKILLS_DIR)):
|
||||
if not fname.endswith(".md"):
|
||||
continue
|
||||
with open(os.path.join(_SKILLS_DIR, fname), encoding="utf-8") as f:
|
||||
raw = f.read()
|
||||
m = re.match(r"^---\n(.*?)\n---\n(.*)$", raw, re.S)
|
||||
if not m:
|
||||
continue
|
||||
meta: dict[str, str] = {}
|
||||
for line in m.group(1).splitlines():
|
||||
if ":" in line:
|
||||
key, _, value = line.partition(":")
|
||||
meta[key.strip()] = value.strip()
|
||||
keywords = [
|
||||
k.strip().lower()
|
||||
for k in meta.get("keywords", "").split(",")
|
||||
if k.strip()
|
||||
]
|
||||
out[meta.get("name", fname[:-3])] = {
|
||||
"name": meta.get("name", fname[:-3]),
|
||||
"title": meta.get("title", fname[:-3]),
|
||||
"keywords": keywords,
|
||||
"body": m.group(2).strip(),
|
||||
}
|
||||
return out
|
||||
|
||||
|
||||
ALL_SKILLS: dict[str, dict] = _load()
|
||||
|
||||
|
||||
def pick_skill(message: str) -> dict | None:
|
||||
"""Meilleure skill pour ce message, ou None si rien d'assez net."""
|
||||
low = message.lower()
|
||||
best, best_hits = None, 0
|
||||
for skill in ALL_SKILLS.values():
|
||||
hits = sum(1 for kw in skill["keywords"] if kw in low)
|
||||
if hits > best_hits:
|
||||
best, best_hits = skill, hits
|
||||
if best is None or best_hits < _MIN_HITS:
|
||||
return None
|
||||
return {"name": best["name"], "title": best["title"], "body": best["body"]}
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
name: analyse-donnees
|
||||
title: Analyse de données
|
||||
keywords: csv, json, données, tableau, statistique, moyenne, analyse, colonnes, tri, filtre, graphique, export
|
||||
---
|
||||
Méthode d'analyse de données :
|
||||
1. EXAMINER : lis un échantillon du fichier réel (read_file) AVANT tout traitement. Identifie séparateur, encodage, en-têtes, types de colonnes.
|
||||
2. VALIDER : repère valeurs manquantes, doublons, incohérences de type. Signale-les explicitement au lieu de les masquer.
|
||||
3. TRANSFORMER : script clair et borné (pas de dépendance exotique) ; garde les données d'origine intactes, écris le résultat dans un nouveau fichier.
|
||||
4. PRÉSENTER : résumé chiffré (compte, min/max, moyennes pertinentes) + limites de l'analyse (données ignorées, hypothèses faites).
|
||||
Interdit : supposer le format sans avoir lu le fichier ; modifier les données sources ; présenter des chiffres sans dire comment ils sont calculés.
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
name: creation-web
|
||||
title: Création web
|
||||
keywords: site, page, html, css, landing, formulaire, portfolio, interface, responsive, boutique, vitrine, menu, header, footer, animation
|
||||
---
|
||||
Méthode de création web, dans cet ordre :
|
||||
1. STRUCTURE : écris d'abord le HTML sémantique COMPLET (header, main, sections, footer). Contenu réel, pas de lorem ipsum si le sujet est connu.
|
||||
2. STYLE : CSS cohérent — palette limitée (3-4 couleurs), typographie lisible, espacements réguliers. Mobile-first, responsive (flexbox/grid, max-width sur images).
|
||||
3. INTERACTIVITÉ : JavaScript minimal et sans dépendance externe. Chaque interaction doit fonctionner hors ligne.
|
||||
4. VÉRIFIER : run_check sur chaque fichier produit ; contrôle les liens internes. Si les outils navigateur (mcp_playwright_*) sont disponibles, ouvre la page et lis la console pour vérifier qu'elle est propre.
|
||||
Interdit : livrer sans vérification ; référencer des images ou CDN externes non demandés ; produire un fichier tronqué (utilise write_file en plusieurs morceaux).
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
name: debogage-systematique
|
||||
title: Débogage systématique
|
||||
keywords: bug, plante, erreur, exception, traceback, crash, échoue, marche pas, fonctionne pas, cassé, debug, débogue, corrige le bug, ne s'affiche pas, undefined, null, NaN
|
||||
---
|
||||
Méthode de débogage à suivre STRICTEMENT, étape par étape :
|
||||
1. REPRODUIRE : identifie l'entrée exacte et le comportement observé vs attendu. Si le message d'erreur est fourni, cite-le et pars de là.
|
||||
2. LOCALISER : lis le code concerné (read_file / grep_search) AVANT toute modification. Trouve la ligne qui produit le symptôme.
|
||||
3. HYPOTHÈSE : formule UNE cause précise. Vérifie-la en lisant le code, pas en devinant.
|
||||
4. CORRIGER : modification minimale et ciblée (edit_file). Ne réécris pas tout le fichier. Ne corrige qu'une cause à la fois.
|
||||
5. VÉRIFIER : relis le code modifié (run_check) et explique pourquoi le symptôme disparaît. Signale tout autre problème repéré sans le corriger.
|
||||
Interdit : proposer une correction sans avoir lu le code ; corriger plusieurs choses à la fois ; conclure « ça devrait marcher » sans vérification.
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: redaction-structuree
|
||||
title: Rédaction structurée
|
||||
keywords: rédige, écris un texte, article, documentation, readme, rapport, résumé, lettre, mail, présentation, plan
|
||||
---
|
||||
Méthode de rédaction :
|
||||
1. PLAN : annonce la structure (titres/sections) avant de rédiger. Adapte la longueur à la demande — court par défaut.
|
||||
2. RÉDACTION : paragraphes courts, une idée par paragraphe, français précis. Concret d'abord, généralités interdites. Markdown propre (titres, listes, gras parcimonieux).
|
||||
3. RELECTURE : traque répétitions, phrases creuses, incohérences de ton. Coupe tout ce qui n'apporte rien.
|
||||
Interdit : remplissage, formules toutes faites (« dans un monde où… »), conclusion qui répète l'introduction.
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
name: refactor-sur
|
||||
title: Refactor sûr
|
||||
keywords: refactor, refactorise, réorganise, nettoie, simplifie, renomme, découpe, extrait, duplication, dette
|
||||
---
|
||||
Méthode de refactoring, comportement STRICTEMENT identique avant/après :
|
||||
1. COMPRENDRE : lis TOUT le code concerné et ses usages (grep_search sur chaque symbole touché) avant de modifier quoi que ce soit.
|
||||
2. PETITS PAS : une seule transformation à la fois (renommage, extraction, déplacement). Jamais plusieurs changements mélangés.
|
||||
3. VÉRIFIER après CHAQUE pas : run_check sur les fichiers modifiés ; re-grep pour confirmer qu'aucun usage n'est resté sur l'ancien nom.
|
||||
4. RÉCAPITULER : liste ce qui a changé et pourquoi le comportement est inchangé.
|
||||
Interdit : changer le comportement ou l'API publique sans le signaler ; renommer sans vérifier tous les usages ; réécrire un fichier entier quand edit_file suffit.
|
||||
@@ -0,0 +1,26 @@
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
os.environ.setdefault("DATA_DIR", tempfile.mkdtemp())
|
||||
os.environ.setdefault("WORKSPACE_DIR", tempfile.mkdtemp())
|
||||
|
||||
from app import skills # noqa: E402
|
||||
|
||||
|
||||
def test_five_skills_loaded():
|
||||
assert len(skills.ALL_SKILLS) == 5
|
||||
assert all(s["title"] and s["body"] for s in skills.ALL_SKILLS.values())
|
||||
|
||||
|
||||
def test_bug_message_picks_debug():
|
||||
s = skills.pick_skill("mon script plante avec une erreur TypeError au démarrage")
|
||||
assert s and s["name"] == "debogage-systematique"
|
||||
|
||||
|
||||
def test_web_message_picks_web():
|
||||
s = skills.pick_skill("crée une page html responsive pour ma boutique")
|
||||
assert s and s["name"] == "creation-web"
|
||||
|
||||
|
||||
def test_banal_message_picks_nothing():
|
||||
assert skills.pick_skill("bonjour, quelle heure est-il ?") is None
|
||||
Reference in new issue
Block a user