spb/ka6 Public
ka6 — bot FLAGSHIP du Groupe KA : explorateur structuré du web québécois. Crawling concurrent robuste, Claude Sonnet+Haiku, graphe de connaissances, dashboard temps réel.
Python 97.8%
Shell 2.2%
1# Author: Simon-Pierre Boucher2# Contact: contact@spboucher.ai3"""Extraction d'un GRAPHE DE CONNAISSANCES orienté CRÉATEURS / INFLUENCEURS (ka6).45Objectif initial du flagship : cartographier l'écosystème des créateurs de contenu6et influenceurs québécois — leurs plateformes, handles, nombre d'abonnés, niche,7langues — et leurs relations (agence qui les représente, marques avec qui ils8collaborent, plateformes où ils publient, collectifs dont ils sont membres).910On extrait aussi les entités classiques (entreprises, organisations, agences).11Données PUBLIQUES uniquement ; aucune donnée sensible ou privée.12"""1314from __future__ import annotations1516from typing import Any1718from anthropic import Anthropic1920GRAPH_TOOL = {21 "name": "record_graph",22 "description": "Enregistre les entités (dont créateurs/influenceurs) et leurs relations.",23 "input_schema": {24 "type": "object",25 "properties": {26 "entities": {27 "type": "array",28 "items": {29 "type": "object",30 "properties": {31 "temp_id": {"type": "string", "description": "Identifiant local unique (e1, e2…) référencé par les relations"},32 "type": {"type": "string",33 "enum": ["creator", "business", "organization", "agency", "brand", "person", "website"],34 "description": "Utilise 'creator' pour un influenceur/créateur de contenu, 'agency' pour une agence d'influence/talent, 'brand' pour une marque."},35 "name": {"type": "string"},36 "canonical_name": {"type": "string"},37 "handle": {"type": "string", "description": "Pseudo principal (ex. @nomcreateur)"},38 "platform": {"type": "string", "description": "Plateforme principale (Instagram, TikTok, YouTube, Twitch, X, Facebook, Snapchat…)"},39 "followers": {"type": "string", "description": "Nb d'abonnés principal (ex. '125K', '1.2M', 34000)"},40 "niche": {"type": "string", "description": "Créneau/thématique (mode, beauté, gaming, cuisine, humour, lifestyle, sport, tech, voyage, famille, musique…)"},41 "languages": {"type": "array", "items": {"type": "string"}, "description": "Langues du contenu (fr, en…)"},42 "sector": {"type": "string", "description": "Secteur pour les entreprises/agences/marques"},43 "description": {"type": "string"},44 "website": {"type": "string"},45 "email": {"type": "string"},46 "phone": {"type": "string"},47 "city": {"type": "string"},48 "region": {"type": "string", "description": "Ville/région du Québec"},49 "social_links": {50 "type": "array",51 "items": {52 "type": "object",53 "properties": {54 "platform": {"type": "string"},55 "url": {"type": "string"},56 "followers": {"type": "string", "description": "Abonnés sur CETTE plateforme si connu"},57 },58 "required": ["platform", "url"],59 },60 },61 "confidence": {"type": "number", "minimum": 0, "maximum": 1},62 },63 "required": ["temp_id", "type", "name"],64 },65 },66 "relations": {67 "type": "array",68 "items": {69 "type": "object",70 "properties": {71 "from": {"type": "string"},72 "to": {"type": "string"},73 "type": {74 "type": "string",75 "enum": ["REPRESENTED_BY", "COLLABORATES_WITH", "CREATES_ON", "SPONSORED_BY",76 "PROMOTES", "MEMBER_OF", "MANAGES", "APPEARS_WITH",77 "WORKS_AT", "FOUNDER_OF", "PARTNER_OF", "AFFILIATED_WITH"],78 "description": "Ex : créateur REPRESENTED_BY agence ; créateur COLLABORATES_WITH marque ; créateur CREATES_ON plateforme ; créateur MEMBER_OF collectif.",79 },80 "role": {"type": "string", "description": "Précision (ex. 'ambassadrice', 'campagne 2025', 'chaîne principale')"},81 "confidence": {"type": "number", "minimum": 0, "maximum": 1},82 },83 "required": ["from", "to", "type"],84 },85 },86 },87 "required": ["entities"],88 },89}9091SYSTEM = (92 "Tu es l'extracteur du bot FLAGSHIP ka6 du Groupe KA. Objectif initial : cartographier "93 "l'écosystème des CRÉATEURS DE CONTENU et INFLUENCEURS québécois à partir de pages publiques "94 "(répertoires, palmarès, rosters d'agences, articles, pages de marques).\n\n"95 "Pour chaque créateur/influenceur (type 'creator') extrais : nom, handle (@), plateforme "96 "principale, nombre d'abonnés, niche/thématique, langues, région, et TOUTES ses plateformes "97 "dans social_links (avec abonnés par plateforme si indiqués).\n"98 "Modélise les RELATIONS : REPRESENTED_BY (agence de talent/influence), COLLABORATES_WITH / "99 "SPONSORED_BY / PROMOTES (marques), CREATES_ON (plateformes), MEMBER_OF (collectif/réseau), "100 "MANAGES (gérant/agence -> créateur).\n"101 "Extrais aussi agences ('agency'), marques ('brand'), entreprises/organisations pertinentes.\n"102 "Règles : données PUBLIQUES uniquement ; priorise le Québec ; n'invente rien (laisse vide si "103 "absent) ; donne un temp_id unique par entité et référence-le dans les relations ; confidence "104 "réaliste. Réponds uniquement via l'outil record_graph."105)106107108class Extractor:109 def __init__(self, client: Anthropic, model: str):110 self.client = client111 self.model = model112113 def extract_graph(self, url: str, title: str, markdown: str) -> dict[str, Any]:114 if not markdown or not markdown.strip():115 return {"entities": [], "relations": []}116 content = markdown[:20000]117 resp = self.client.messages.create(118 model=self.model,119 max_tokens=3500,120 system=SYSTEM,121 tools=[GRAPH_TOOL],122 tool_choice={"type": "tool", "name": "record_graph"},123 messages=[{124 "role": "user",125 "content": f"URL: {url}\nTitre: {title}\n\nContenu (markdown):\n{content}",126 }],127 )128 for block in resp.content:129 if block.type == "tool_use" and block.name == "record_graph":130 data = block.input or {}131 return {"entities": data.get("entities", []) or [],132 "relations": data.get("relations", []) or []}133 return {"entities": [], "relations": []}134