Legwork
À quoi ça sert
Recherche décisionnelle. Pas de la recherche universitaire. La question est toujours une variante de « que devrions-nous faire à propos de X », et le livrable est jugé sur son utilité, pas sur son exhaustivité.
Cela façonne tout ce qui suit. Une affirmation est jugée sur le fait que sa source soit le bon type de chose pour cette affirmation, pas sur la respectabilité de son domaine. Trois sources qui s'accordent ne comptent que si elles auraient pu être en désaccord. Et une session qui ne peut pas répondre à la question le dit plutôt que de produire un texte alambiqué.
Principe d'autonomie. Déduisez ce dont vous avez besoin du contexte, choisissez un niveau et commencez. Annoncez le niveau en une ligne et poursuivez ; ne demandez pas à l'utilisateur de choisir. Il peut rediriger en cours de session, ce qui coûte bien moins qu'une question bloquante à chaque requête. Arrêtez-vous uniquement pour une erreur critique ou une requête incompréhensible.
Niveaux
La profondeur augmente la rigueur. Elle n'augmente jamais la longueur.
| rapide | standard | approfondi | |
|---|---|---|---|
| Cadrage | Décision plus 2-3 sous-questions | Plus les falsificateurs nommés | Plus les angles secondaires |
| Collecte | Extraits SERP ; ne récupérer que pour fixer un chiffre | Récupération directe des sources principales par constat | Une source primaire pour chaque constat |
| Remise en question | Regroupement par indépendance uniquement | Une recherche d'infirmation par constat | Passage d'infirmation par constat plus un audit d'origine |
| Format | bref | bref ou rapport | rapport |
| Porte de niveau | --level quick | --level standard | --level deep |
| Temps approximatif | 3-5 min | 8-12 min | 20-40 min |
La valeur par défaut est standard. Utilisez le niveau nommé par l'utilisateur (quick, standard, deep, ou un équivalent comme « analyse rapide » ou « aller en profondeur »), sinon $LEGWORK_DEFAULT_MODE, sinon standard. « Deep research » seul est une phrase d'invocation pour cette compétence, pas une demande de niveau approfondi – passez au niveau par défaut.
Montez silencieusement d'un niveau si le cadrage révèle que la question est matériellement plus importante que ce que la requête impliquait, et dites-le dans la même ligne d'ouverture.
Ligne d'ouverture, puis directement dans la Phase 1 :
Exécution standard (~8-12 min). Dites « approfondi » pour les sources primaires et un passage d'infirmation.
Pipeline
Quatre phases. Instructions complètes dans methodology.md.
- Cadrage – nommer la décision, les sous-questions qui la trancheraient, et quelles preuves changeraient la réponse.
- Collecte – récupérer pour chaque sous-question, en enregistrant chaque récupération.
- Remise en question – chercher le cas infirmant ; regrouper les sources par indépendance.
- Rédaction – assembler le livrable, puis le passer à la porte.
Les phases 2 et 3 s'entrelacent par constat plutôt que de fonctionner comme des portes strictes.
Avant de commencer
Ancrez la date. Exécutez date -u +%Y-%m-%d et utilisez cette chaîne pour la session, dans le nom du dossier de sortie, et dans chaque requête de recherche qui pourrait renvoyer du contenu daté. Ne vous fiez jamais à votre propre sens de l'année en cours, et ne laissez jamais un sous-agent la déterminer lui-même.
Vérifiez si cette session a déjà été effectuée. Lisez d'abord l'index. C'est un répartiteur : une ligne par session passée, et la colonne de résumé existe pour que vous puissiez décider d'ouvrir un rapport sans payer le coût d'ouverture.
python3 ${CLAUDE_SKILL_DIR}/scripts/index.py list --base "$OUTPUT_BASE"
Choisissez ensuite l'un des quatre chemins, et dites lequel dans votre ligne d'ouverture :
| L'index dit | Faites ceci |
|---|---|
| Une session couvre ce sujet et n'est pas obsolète | Répondez à partir d'elle. Lisez ce rapport, pas le web. |
| Une session couvre ce sujet mais est marquée comme obsolète | Rafraîchissez-la |
| Une session est proche mais répond à une question différente | Nouvelle session, et faites une référence croisée |
| Rien ne correspond | Nouvelle session |
Répondre à partir d'une session antérieure est un succès, pas un raccourci. Ouvrez le rapport, lisez les constats qui se rapportent à la question, et répondez avec leurs bandes de confiance intactes. Dites clairement qu'il provient d'une session d'une date donnée. Relancer une recherche déjà effectuée et toujours valide est le gaspillage que cet index existe pour empêcher.
Rafraîchir plutôt que relancer
Un rafraîchissement met à jour le rapport existant sur place – même dossier, même fichier. Ne créez pas un second dossier : deux dossiers décrivant une question, c'est ainsi qu'un lecteur finit par agir sur celui qu'il a ouvert par hasard.
-
Reprenez le journal des récupérations plutôt que d'en commencer un nouveau. Tout ce qu'il contient est déjà payé.
python3 ${CLAUDE_SKILL_DIR}/scripts/sources.py resume --tsv "$OUT/$BASE.tsv"Cela affiche les angles déjà traités, les pages déjà récupérées, et lesquelles ne contiennent encore aucune citation. Travaillez les angles non couverts et revérifiez les affirmations qui décident de la réponse ; ne récupérez pas ce qui est déjà enregistré et à jour.
-
Déplacez chaque affirmation qui est maintenant erronée dans
## Remplacéavec la date et la raison. Ne supprimez jamais une affirmation en silence. Quelqu'un a peut-être agi dessus, et une future session doit savoir que ce terrain a été couvert. Si la même réponse a maintenant été renversée deux fois, dites-le clairement – c'est le signal le plus fort dans le document que la question est instable. -
Ajoutez une ligne
## Chronologieindiquant ce qui a changé. -
Mettez à jour la ligne de l'index.
Archivage de la session
Chaque session se termine dans l'index, y compris celle qui n'a pas pu répondre – « nous avons cherché et n'avons rien trouvé » est exactement ce dont une future session a besoin pour ne pas chercher à nouveau.
python3 ${CLAUDE_SKILL_DIR}/scripts/index.py add --base "$OUTPUT_BASE" \
--folder "$BASE" --topic "Tri des Outlook pour les petites structures" --level deep \
--one-liner "Pas de tri natif en dessous d'E5 ; l'écart est réel mais étroit"
Le résumé indique ce que la session a conclu, pas son sujet. « Notes sur la galerie de plugins » est inutile dans six mois ; « Aucune voie de soumission n'existe, l'installation se fait par URL ou pas du tout » répond à la question tout seul. La porte avertit lorsqu'une session manque dans un index qui existe.
Politique de récupération
Deux échelles, premiers échelons gratuits. Ne montez qu'aussi haut que nécessaire.
Pour chercher :
| Échelon | Appel | Quand |
|---|---|---|
| 1 | WebSearch | Toujours. Trois variantes de requête par angle. |
| 2 | platforms.py search --on ... | La réponse est un enregistrement détenu par une plateforme : un fil de discussion, un paquet, un dépôt, un article de presse daté, un journal des modifications d'un fournisseur. Gratuit, sans clé, et renvoie l'enregistrement plutôt qu'une page à son sujet. |
| 3 | bd_search.py -m general --engine bing --country XX --language yy | Faible après trois variantes, ou la question est géo-spécifique. Un deuxième moteur est la raison de payer. |
| 4 | bd_search.py -m discover --intent "..." | Deux moteurs encore faibles. |
Pour ouvrir une page :
| Échelon | Appel | Quand |
|---|---|---|
| 1 | fetch.py "<url>" --find "terme" | Toujours en premier. Gratuit, et le seul transport gratuit qui donne le texte de la page, donc les chiffres peuvent être retracés et les citations vérifiées. |
| 2 | WebFetch | fetch.py a quitté avec le code 3 et la page ne vaut pas la peine d'être payée. |
| 3 | bd_search.py "<url>" -m scrape | Bloqué : protection anti-bot, paywall, 403. |
| 4 | bd_search.py "<url>" -m render | Une coque rendue côté client. |
| 5 | bd_search.py "<url>" -m pipeline --pipeline NOM | Une plateforme qui bloque tout ce qui précède. Facturé par enregistrement ; -m reddit est celui où c'est la seule route, pas un dernier recours. |
Exécutez platforms.py list pour les dix plateformes gratuites, et bd_search.py --help pour les modes payants. En cas de code de sortie 2 (authentification ou quota), dites à l'utilisateur d'exécuter brightdata login. Ne réessayez pas.
Un résultat de recherche est une piste, pas une page que vous lisez. Aux niveaux standard et approfondi, ouvrez ce que vous citez : la porte traite une citation reposant uniquement sur des lignes websearch comme non ouverte, un avertissement au niveau standard et une erreur au niveau approfondi. Le niveau rapide est conçu pour être basé sur les extraits en premier et n'est pas concerné.
Enregistrez l'échec avant la solution de repli. Une page qui ne s'ouvre pas est une preuve concernant la session – --status blocked – et le reçu en tient compte.
Limitez ce que vous récupérez. --max-chars 8000 sur les appels de scraping, et préférez --find à une limite aveugle : sur une longue page, les huit mille premiers caractères sont généralement la navigation.
Sources que vous pouvez utiliser
La recherche est ce qu'un étranger pourrait établir. Cette limite est importante dans les deux sens.
Ne présentez jamais les propres enregistrements de l'utilisateur comme un constat. Si la question concerne la propre entreprise, le produit ou la position sur le marché de l'utilisateur, ne fouillez pas dans ses comptes privés – son registraire, sa facturation, sa boîte de réception, ses fichiers internes – et ne rapportez pas ce que vous y avez trouvé comme si cela avait été découvert. Il le sait déjà. C'est circulaire, cela gonfle les preuves apparentes, et cela masque à quel point un étranger peut réellement voir peu de choses. Cherchez l'équivalent public et rapportez ce qu'un étranger trouverait, y compris rien.
Utilisez l'accès exclusif que l'utilisateur vous a donné, sur des tiers. Un abonnement payant, un ensemble de données privé ou une base de données interne que l'utilisateur vous a explicitement offert est un avantage réel lors de la recherche sur des concurrents, des fournisseurs ou un marché. Utilisez-le, enregistrez-le comme une source comme les autres, et notez dans les Limitations que le constat repose sur un accès que le lecteur pourrait ne pas avoir.
Lorsqu'une entité s'avère n'avoir aucune empreinte publique du tout, c'est la réponse. Listez ce que vous avez vérifié, dites que l'existence ou l'échelle n'a pas pu être vérifiée de l'extérieur, et ne comblez pas le vide à partir d'un accès privilégié. C'est la forme « n'a pas pu répondre » appliquée à une entité plutôt qu'à l'ensemble de la question.
Le journal des récupérations
Chaque récupération, à chaque niveau, ajoute une ligne :
python3 ${CLAUDE_SKILL_DIR}/scripts/sources.py log \
--tsv "$OUT/$BASE.tsv" \
--url "https://acme.example/pricing" \
--kind vendor_pricing \
--angle "que facture le titulaire" \
--via webfetch \
--date 2026-07-01 \
--title "Tarifs" \
--quote "Forfait Équipe : 30 dollars américains par utilisateur par mois, facturé annuellement." \
--text-file /tmp/page.txt
Deux champs portent le poids, et aucun ne peut être vérifié par quoi que ce soit en aval.
--angle est la sous-question à laquelle cette récupération répondait. La corroboration est comptée par angles plutôt que par sources car la propre dispersion de Legwork gonfle les nombres de sources. Enregistrez l'angle honnêtement – réutiliser une même chaîne dans une session détruit silencieusement la vérification, et aucun script ne peut dire que vous l'avez fait.
--quote est la phrase textuelle qui a rendu la source digne d'être citée. Enregistrez-en une pour chaque source que vous avez l'intention de citer, au moment où vous la lisez. Environ la moitié de tous les constats ne contiennent aucun chiffre, donc pour ceux-ci, la citation est la seule preuve qui existe, et c'est la seule partie qui survit au changement de la page. Lorsque le texte de la page est sur disque – tout ce qui a été ouvert avec fetch.py, ou un -m scrape avec --out – la citation est vérifiée par rapport à lui et quote_verified: false signifie que vous avez mal cité la page.
--from-fetch remplit l'url, le titre, la date et le texte de la page à partir du fichier sidecar que ces deux-là écrivent, donc rien n'a à être retapé :
python3 ${CLAUDE_SKILL_DIR}/scripts/sources.py log --tsv "$OUT/$BASE.tsv" \
--from-fetch /tmp/legwork/ab12cd34ef56.json \
--angle "que facture le titulaire" --kind vendor_pricing \
--query "site:acme.example tarifs" --quote "Forfait Équipe : 30 dollars américains ..."
sources.py log --help couvre le reste : --via transports (incluant api, local et mcp), --text-file extraction numérique, --kind. Enregistrez le transport que vous avez réellement utilisé – récupérer à nouveau une page via un autre transport pour la rendre enregistrable déforme la piste plutôt que de l'enregistrer. sources.py kinds affiche quels types de sources conviennent à quelles affirmations.
Sous-agents
La récupération est la seule phase qui vaut la peine d'être parallélisée, un sous-agent par angle. Briefez-les à partir de subagent-brief.md, qui contient le modèle textuellement, la forme de retour requise, et la raison pour laquelle chaque ligne s'y trouve. Un sous-agent a zéro contexte, donc tout ce dont il a besoin va dans le brief.
Trois choses restent avec vous plutôt que dans le brief :
- Faites correspondre le modèle à la forme de l'angle. La collecte d'extraits et la fixation d'un chiffre connu fonctionnent bien sur un modèle bon marché – passez l'override explicitement, ne laissez jamais un sous-agent hériter du modèle de session par accident. Mais le travail de source primaire de niveau approfondi, et tout ce qui reconstruit une énumération, reste sur le modèle de l'orchestrateur. Mesuré : lors d'une comparaison, les orchestrateurs ont ouvert 3, 8 et 32 pages de fournisseurs dans la gamme du bon marché au performant, et seul le plus faible a rempli chaque cellule de la grille à partir d'un seul agrégateur tout en signalant un succès.
- Ne collez jamais la transcription d'un sous-agent dans votre synthèse. Prenez le retour structuré, vérifiez que la chaîne d'angle est revenue inchangée, enregistrez chaque ligne, travaillez à partir du journal.
- Le cadrage, la remise en question et la synthèse sont des jugements. Ils restent sur le modèle principal.
Scripts
Tous basés sur la bibliothèque standard uniquement. Pas de virtualenv. Tout python3 >= 3.9.
| Script | Objectif |
|---|---|
fetch.py "<url>" --find TERME | Ouvrir une page gratuitement et conserver son texte ; quitte avec le code 3 en cas de blocage ou de coque |
platforms.py list | search --on X | Dix sources natives de plateformes gratuites, renvoyant des enregistrements plutôt que des pages |
sources.py kinds | log | score | Vocabulaire des types de sources, le journal des récupérations, le score d'adéquation par type d'affirmation |
sources.py receipt | Les compteurs de récupération pour la ligne de reçu, tirés du journal |
sources.py stale --claim-kind K | Quelles sources enregistrées sont devenues obsolètes, selon la demi-vie de ce type d'affirmation |
sources.py resume | Ce qu'une session précédente a déjà récupéré, afin qu'une reprise l'ignore |
independence.py groups | check | Réduire les sources à des voix indépendantes ; compter la corroboration tenant compte des angles |
independence.py portfolio | Concentration des sources sur l'ensemble de la session, pas au sein d'un seul constat |
index.py add | list | L'index de recherche – archiver une session, en trouver une passée, repérer les obsolètes |
matrix.py check --report CHEMIN | Complétude d'une matrice de comparaison |
check.py --report CHEMIN --level NIVEAU | La porte de livraison |
finish.py --report CHEMIN --level NIVEAU | Porte, balayage d'obsolescence et archivage en un seul appel |
| `bd_search.py "<requête | url>" -m MODE --json` |
Sortie
Résolvez la base de sortie une fois au début de la session :
OUTPUT_BASE="${LEGWORK_OUTPUT:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)/docs/research}"
BASE="[Sujet]_Recherche_$(date +%Y%m%d)"
OUT="$OUTPUT_BASE/$BASE"; mkdir -p "$OUT"
…
[tronqué pour la traduction]