from collections.abc import Sequence from techdocs_graphrag.models import EntityType, GraphFact, RetrievalContext, RetrievedChunk ENTITY_TYPES = ", ".join(entity_type.value for entity_type in EntityType) EXTRACTION_SYSTEM_PROMPT = f"""\ Tu es un système d'extraction de graphe de connaissances pour de la documentation technique. À partir du texte fourni par l'utilisateur, extrais : 1. les entités techniques importantes (composants, classes, fonctions, bibliothèques, concepts...) ; 2. le type de chaque entité, choisi parmi : {ENTITY_TYPES} ; 3. les relations factuelles explicitement affirmées par le texte ; 4. une preuve textuelle courte (25 mots maximum) recopiée du texte pour chaque relation. Règles : - Nomme chaque entité sous sa forme canonique, telle qu'écrite dans le code (ex. StateGraph, MemorySaver). - Utilise le singulier pour les concepts (Channel et non Channels). - Écris les relations en anglais, en UPPER_SNAKE_CASE, orientées source -> cible (ex. USES, PERSISTS_WITH, PART_OF, DEPENDS_ON, CONFIGURES, RETURNS, RAISES, EXTENDS, ENABLES). - N'invente aucune relation absente du texte. - Si le texte ne contient rien d'exploitable, renvoie des listes vides. Réponds uniquement avec un objet JSON valide de cette forme : {{ "entities": [{{"name": "StateGraph", "type": "Component"}}], "relations": [ {{ "source": "StateGraph", "relation": "USES", "target": "Channel", "evidence": "StateGraph utilise des Channels pour gérer l'état" }} ] }} """ ANSWER_SYSTEM_PROMPT = """\ Tu es un assistant expert en documentation technique. Règles : - Réponds uniquement à partir du contexte fourni : faits du graphe et extraits documentaires. - Réponds dans la langue de la question. - Explique clairement comment les composants sont reliés entre eux lorsque c'est utile. - Cite tes sources en ligne sous la forme [nom_du_document]. - Ajoute un court exemple de code uniquement s'il est présent ou directement déductible du contexte. - Si le contexte ne suffit pas, dis-le explicitement au lieu d'inventer. """ NO_FACTS = "Aucun fait pertinent." NO_CHUNKS = "Aucun extrait pertinent." def render_answer_prompt(question: str, context: RetrievalContext) -> str: return "\n\n".join( ( f"Question :\n{question}", f"Faits du graphe de connaissances :\n{render_facts(context.facts)}", f"Extraits documentaires :\n{render_chunks(context.chunks)}", ) ) def render_facts(facts: Sequence[GraphFact]) -> str: if not facts: return NO_FACTS return "\n".join( f"- {fact.source} --{fact.relation}--> {fact.target}" f" | preuve : {' / '.join(fact.evidence[:2]) or 'n/a'}" f" | sources : {', '.join(fact.documents) or 'n/a'}" for fact in facts ) def render_chunks(chunks: Sequence[RetrievedChunk]) -> str: if not chunks: return NO_CHUNKS return "\n\n".join(f"[{chunk.document}]\n{chunk.text}" for chunk in chunks)