Référence bibliographique express (citation-digest)
Entrez un article, obtenez « qui le cite + ce que chacun essaie de résoudre + regroupé par catégorie », livré en markdown ou page Notion.
Pipeline en quatre étapes, les scripts sont déjà prêts, ne pas réinventer :
s2_citations.py → citations.json Qui le cite (avec nb citations/date/DOI/arXiv)
kimi_q1.py → kimi.json Texte original de Kimi Q1 pour chaque article (blocs structurés)
(à vous de juger)→ classify.json Résultat de classification
build_content.py → content.json + digest.md
notion_publish.py→ Page Notion
0. Prérequis
- Clé S2 : priorité à
$SEMANTIC_SCHOLAR_API_KEY, puis--key, puis~/.env/semantic-scholar. Fonctionne sans clé mais beaucoup de 429 ; le script réessaie automatiquement (8 × 12s). - Token Notion :
~/.notion-tokenou$NOTION_TOKEN; la page cible doit d'abord être partagée avec cette intégration, sinon la recherche échoue.
1. Récupération des citations
python3 scripts/s2_citations.py --title "MP-GUI: Modality Perception with MLLMs for GUI Understanding" --out citations.json
# ou --arxiv 2503.14021 / --paper-id 0e931ba8...
Le script intègre trois règles que vous rencontrerez forcément :
- Dédoublonnage : S2 renvoie souvent le même article arXiv sous deux entrées (par ex. « A Survey on ... » et « A Summary on ... », une avec ArXiv, l'autre avec seulement DOI
10.48550/arXiv.xxxx). Dédoublonnage par arXiv id → DOI → titre, on garde l'entrée la plus citée. - Plus de 20 articles : ne garder que deux top 10 :
--max-total 20 --top 10, on conserve l'union des « top 10 par nombre de citations » et « top 10 par date ». Activé par défaut, ne pas refiltrer. - Correspondance de titre : l'interface de recherche prend le résultat avec le plus grand score de chevauchement de tokens ; un score <0.55 liste les candidats sur stderr et demande confirmation.
Consultez d'abord citations.json pour source.citationCount, c'est le nombre de citations à rapporter.
2. Récupération du résumé Kimi
python3 scripts/kimi_q1.py --in citations.json --out kimi.json --sleep 10 --resume
- Appelle
POST https://papers.cool/arxiv/kimi?paper=<arxiv_id>(implémentation locale du MCP papers-cool), doit inclure les en-têtesUser-Agent/Origin/Referersinon il est bloqué. - Renvoie un HTML FAQ, on prend uniquement la Q1 « Quel problème cet article essaie-t-il de résoudre ? », converti en blocs :
###→ heading_3,-→ bulleted_list_item,**bold**→ gras. Texte original, pas de compression ni de réécriture. - papers.cool ne traite pas que arXiv, il a aussi une section venue :
/venue/<CODE>.<YEAR>, CODE ∈ AAAI/ACL/COLM/COLT/CoRL/CVPR/ECCV/EMNLP/ICCV/ICLR/ICML/IJCAI/INTERSPEECH/IWSLT/MICCAI/MLSYS/NAACL/NDSS/NeurIPS/OSDI/UAI. Pour les articles sans arXiv id, le script utilise automatiquement ce fallback : page liste/venue/<CODE>.<YEAR>?show=5000pour trouver l'ID de l'article parid="title-<pid>"(ex.49@2026@IJCAI, le numéro IJCAI est la fin du DOI), puisPOST /venue/kimi?paper=<pid>, retourne le même HTML FAQ. La page liste par défaut ne donne que 25 entrées, il faut impérativement?show=. - Les articles qui ne passent ni par l'un ni par l'autre (IEEE MIPR, AIoTC, etc. non pris en charge par papers.cool) vont dans
missing, on les remplace par le résumé ou le titre, avec une annotation « Pas un résumé Kimi » —build_content.py/notion_publish.pyajoute automatiquement le marqueur ⚠️ poursource != "kimi", et il faut préciser la raison dansnotesdeclassify.json. --sleep 10plus fiable que 2 ; pour un article non mis en cache, Kimi doit le calculer, 60-90s par article. Chaque article est écrit immédiatement dans--out; pour reprendre après interruption, ajouter--resume.- ⚠️ N'utilisez pas
pkill -f kimi_q1.py: plusieurs sessions Claude exécutant ce skill en même temps peuvent s'interrompre mutuellement (perte avérée de 26 articles déjà récupérés mais pas encore écrits). Pour exécuter en arrière-plan, faites d'abordcp scripts/kimi_q1.py <scratchpad>/pcfetch_<session_id>.pyavec un nom de processus distinct.
3. Classification (à vous, pas de script)
Écrivez classify.json :
{
"categories": [
{"name": "🧩 Innovation de scénario / problème", "desc": "Nouvelle tâche, nouveau benchmark, nouvelle dimension d'évaluation, ou redéfinition d'un problème",
"papers": ["2503.14021", "10.1109/CVPR52734.2025.02766", "Titre exact"]},
{"name": "🔧 Amélioration technique", "desc": "Problème connu, contribution sur la méthode/chaîne d'entraînement/système/architecture",
"papers": ["..."]},
{"name": "📚 Survey uniquement", "desc": "Ne propose pas de nouvelle méthode ou tâche, valeur dans la synthèse et les questions ouvertes",
"papers": ["..."]}
],
"notes": {"2504.20464": "S2 a deux entrées en double pour cet article, fusionné"}
}
papers peut contenir arXiv id / DOI / titre (correspondance exacte ou partielle). Ceux qui ne sont pas couverts iront automatiquement dans « ❓ Non classé », pas de perte silencieuse.
Critères (utiliser ces trois si l'utilisateur n'en précise pas) :
- Innovation de scénario / problème : propose une nouvelle tâche, un nouveau benchmark, une nouvelle dimension d'évaluation (ex. « regarder non seulement l'état final mais aussi le processus »), ou redéfinit un phénomène comme un problème (ex. « l'outil tombe en panne mais on ne l'utilise pas »).
- Amélioration technique : problème reconnu, contribution sur la méthode, la chaîne d'entraînement, l'implémentation système, l'architecture.
- Survey uniquement : Survey / Review / article de position, ne propose pas de nouvelle méthode expérimentale.
- Cas limites (benchmark = innovation de problème ou catégorie séparée, articles analytiques) : classer selon les critères ci-dessus, puis mentionner brièvement la justification dans la réponse, ne pas classer en silence.
Étiquettes dans le texte : pour les articles normaux utiliser « Problème à résoudre », pour les surveys utiliser « Aperçu essentiel » — mais comme le texte Q1 est repris tel quel, ne pas ajouter d'étiquette en général, laisser le texte parler.
4. Assemblage + écriture
python3 scripts/build_content.py --citations citations.json --kimi kimi.json \
--classify classify.json --out content.json --md digest.md
5. Publication Notion
python3 scripts/notion_publish.py --content content.json --page-title "lecture d'article topic"
# ou --page-id xxx ; pour remplacer le contenu existant ajouter --replace
Structure fixe : un toggle racine nommé d'après l'article → paragraphe de description + trois toggles de catégorie → un toggle par article (première ligne grise de métadonnées : Lieu de publication · Date · Cité N · arXiv:xxx, cliquable ; dessous le texte Q1 original).
Pièges (tous rencontrés en pratique)
| Symptôme | Cause / Solution |
|---|---|
POST /v1/blocks/{id}/children → 400 invalid_request_url | Cette intégration n'accepte que PATCH pour ajouter ; notion_publish.py utilise déjà PATCH |
| Vouloir « déplacer » un bloc existant | Notion n'a pas d'API move, seule solution : lire l'ancien bloc → recréer dans le nouveau parent → supprimer l'ancien bloc |
Supprimer un bloc → Can't edit block that is archived | Utiliser DELETE /v1/blocks/{id} (après PATCH {archived:true} on ne peut plus le modifier) |
| Un seul rich_text trop long | Chaque segment ≤2000 caractères ; seg() découpe automatiquement à 1900 caractères (le long abstract anglais des résumés tombe dedans, testé à 2063 car.) |
PATCH /blocks/{id}/children → 400 ... start_cursor provided is invalid | Incohérence temporelle de Notion : un bloc fraîchement créé peut rejeter un ajout immédiat, mais l'écriture réussit souvent. req() a une retry avec backoff pour ce 400 ; ne pas se précipiter à renvoyer toute la page, inspecter d'abord la structure réelle via les toggles racine |
| Avant de renvoyer la page entière | Le script n'est pas idempotent, relancer crée un deuxième toggle racine. D'abord DELETE /v1/blocks/<id_du_toggle_racine_précédent> (ligne root toggle: dans les logs), puis relancer |
| Trop d'éléments en un seul append | Chaque requête ≤100 blocks (le script découpe par 80) |
| Imbrication impossible | Maximum deux niveaux par requête : toggle avec children contenant un autre toggle peut marcher, trois niveaux nécessitent des append séparés |
| S2 renvoie 429 en continu | Normal, retry avec backoff ; les requêtes parallèles aggravent le problème, mieux vaut série + sleep |
Checklist avant de terminer
-
citations.json.source.citationCountcorrespond au nombre d'articles listés (<=20 tout lister ; >20 c'est l'union des deux listes) - Chaque article a une ligne de métadonnées (nombre de citations obligatoire), avec lien cliquable s'il a un arXiv
- Les articles sans texte Kimi original sont annotés avec la raison (⚠️ +
notes) - Le niveau supérieur Notion n'a qu'un seul toggle racine (pas de doublon laissé par une réexécution)
- Nombre d'articles classés + non classés = total des articles
- Vérification sur Notion d'un toggle : titre → métadonnées → texte Q1 original (sous-titres/listes/gras conservés)