@@ -56,7 +56,10 @@ digest.py ←─── cron 3x/jour (GitHub Actions)
5656 ├── 7. PHASE 3 — Synthèse (LLM de synthèse, 1 appel par catégorie OPML)
5757 │ Markdown éditorial : 1-2 phrases par sujet, regroupement thématique
5858 │
59- └── 8. Génère le RSS puis avance last_success.json après succès
59+ ├── 8. Annexe de scoring déterministe (Python, aucun appel LLM)
60+ │ Pour chaque article retenu : score phase 1 (3-5) + raison
61+ │
62+ └── 9. Génère output/digest.xml puis avance last_success.json après succès
6063 │
6164 ├──► git commit → main (seen.json + last_success.json + digest.xml)
6265 ├──► audit détaillé → artefact Actions, rétention 30 jours
@@ -94,7 +97,8 @@ OPML (catégories → feeds)
9497 LLM de synthèse (par catégorie)
9598 input : articles dédupliqués
9699 output : Markdown avec puces et liens
97- └─► feedgen → digest.xml (RSS 2.0)
100+ └─► annexe Python score + raison/article
101+ └─► feedgen → digest.xml (RSS 2.0)
98102 └─► 1 item RSS par run
99103```
100104
@@ -151,12 +155,14 @@ veille/
151155│ ├── audit-summary.md # Synthèse compteurs par run, 30 derniers jours
152156│ └── audit-errors.md # Erreurs survenues par run, 30 derniers jours
153157├── tests/
154- │ └── test_collection.py # Tests fenêtre de collecte + plafonds de coût
158+ │ ├── test_collection.py # Tests fenêtre de collecte + plafonds de coût
159+ │ └── test_score_details.py # Tests du rendu de scoring visible
155160├── collection.py # Fenêtre dynamique, priorité temporelle, plafonds
156161├── digest.py # Pipeline complet (load, fetch, score, dédup, synth, audit)
157162├── audit.py # Logs Markdown des 3 phases (cf. §6 Logs d'audit)
158163├── llm_client.py # Mini-abstraction LLM (route anthropic/ vs openrouter/)
159164├── prompt.py # Prompts LLM isolés (itérables indépendamment du code)
165+ ├── score_details.py # Annexe score + raison, rendue sans LLM
160166├── requirements.txt # feedparser, feedgen, anthropic, openai, httpx
161167├── seen.json # Hashes SHA1 des articles traités (fenêtre 14 jours)
162168├── last_success.json # Timestamp du dernier run terminé avec succès
@@ -181,6 +187,11 @@ applique les plafonds par source puis par run. Ce module est testé sans réseau
181187risquer de casser la logique Python, et l'historique git des changements
182188de prompts est séparé de celui du code.
183189
190+ ** ` score_details.py ` ** : produit l'annexe visible d'évaluation du scoring à
191+ partir des données de phase 1. Le rendu est déterministe : aucun modèle ne peut
192+ oublier un score ou l'associer au mauvais article. Seuls les articles retenus
193+ (scores 3 à 5) apparaissent, avec leur raison.
194+
184195** ` sources.opml ` ** : source de vérité des feeds. Le script le lit à chaque run —
185196modifier l'OPML suffit pour ajouter/retirer une source. Même fichier utilisable
186197dans Reeder pour abonnement direct.
@@ -455,6 +466,20 @@ trouver la nouvelle URL.
455466- La phase 2 est intra-catégorie : les doublons cross-catégorie ne sont pas
456467 détectés actuellement (cf. roadmap).
457468
469+ ### Évaluer la pertinence du scoring dans le digest
470+
471+ Chaque catégorie du bulletin se termine par ** Évaluation du scoring** . Pour
472+ chaque article réellement transmis à la synthèse, cette annexe affiche :
473+
474+ ``` text
475+ 5/5 — Titre — Source. Raison : justification du modèle de filtrage
476+ ```
477+
478+ Le score affiché est ` score_phase1 ` , c'est-à-dire la note initiale avant la
479+ déduplication. Les scores 1 et 2 restent exclus du digest et sont consultables
480+ dans ` logs/audit-details.md ` . L'annexe est construite par Python après la
481+ synthèse ; elle ne dépend donc pas du respect d'une consigne par le LLM.
482+
458483### Mettre à jour les modèles LLM
459484
460485Quand le fournisseur publie de nouveaux modèles, mettre à jour dans ` digest.py ` :
@@ -620,6 +645,14 @@ finale (regroupement thématique, ton éditorial, Markdown structuré) demande
620645plus de nuance, donc un modèle plus capable. Séparer les deux rôles permet de
621646choisir le bon modèle pour chaque tâche, sans surdimensionner le filtrage.
622647
648+ ### Pourquoi les scores sont-ils rendus par Python ?
649+
650+ Le LLM de synthèse peut regrouper plusieurs articles sous une même puce. Lui
651+ demander d'insérer les scores créerait un risque d'omission ou de mauvaise
652+ association. L'annexe est donc générée depuis les objets d'articles après la
653+ phase 2 : chaque titre, lien, score initial et raison restent liés sans appel
654+ supplémentaire et peuvent être couverts par des tests unitaires.
655+
623656### Pourquoi un seul item RSS par run ?
624657
625658Le digest est un ** bulletin éditorial** , pas un agrégateur. Un item par run
0 commit comments