File size: 2,974 Bytes
7e3f910
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
"""Extraction de claims structurées à partir d'un texte libre (via LLM)."""
from __future__ import annotations

from typing import Any, List

from pydantic import BaseModel, Field

from jdm_agent.factcheck.models import Claim


EXTRACTION_PROMPT = """Tu es un extracteur d'affirmations factuelles pour la base \
de connaissance JeuxDeMots (JDM).

Pour le texte fourni, extrais TOUTES les affirmations atomiques vérifiables \
sous forme de triplets `(subject, relation, object)` où `relation` est un nom \
technique JDM (commence par `r_`).

Relations JDM courantes :
- r_isa          : A est un B (générique/hyperonyme)              ex: chat r_isa mammifère
- r_carac        : A est <adj>                                     ex: eau r_carac liquide
- r_has_color    : A a pour couleur B                              ex: sang r_has_color rouge
- r_has_part     : A a pour partie B                               ex: voiture r_has_part roue
- r_agent        : verbe-A est typiquement fait par B              ex: manger r_agent chat
- r_patient      : verbe-A est typiquement appliqué à B            ex: lire r_patient livre
- r_instr        : verbe-A se fait avec B                          ex: couper r_instr couteau
- r_lieu         : A se trouve à B (ou verbe-A se passe à B)       ex: poisson r_lieu mer
- r_has_conseq   : A a pour conséquence B                          ex: pluie r_has_conseq inondation
- r_has_causatif : A a pour cause B                                ex: fatigue r_has_causatif travail
- r_telic_role   : A sert à faire B                                ex: couteau r_telic_role couper

Règles :
- subject et object EN MINUSCULES, à l'infinitif si verbe.
- Une phrase peut contenir PLUSIEURS claims (sépare-les).
- `polarity` = false pour les négations ("ne ... pas").
- Si la phrase n'est PAS factuellement vérifiable (opinion, métaphore vague), ignore-la.
- Si tu hésites entre plusieurs relations, prends la plus précise.
- `text` reproduit la formulation originale de la phrase ayant produit le claim.

Renvoie UNIQUEMENT la liste structurée — pas de commentaire libre.
"""


class _ClaimList(BaseModel):
    """Wrapper pour structured output (certains providers exigent un objet racine)."""
    claims: List[Claim] = Field(default_factory=list)


def extract_claims(text: str, llm: Any) -> List[Claim]:
    """Demande au LLM d'extraire des claims structurées du texte.

    Args:
        text: le texte à analyser.
        llm: une instance LangChain BaseChatModel supportant `with_structured_output`.

    Returns:
        Liste de `Claim` (peut être vide si aucune affirmation factuelle).
    """
    from langchain_core.messages import HumanMessage, SystemMessage

    structured = llm.with_structured_output(_ClaimList)
    result: _ClaimList = structured.invoke([
        SystemMessage(content=EXTRACTION_PROMPT),
        HumanMessage(content=f"Texte à analyser :\n\n{text}"),
    ])
    return list(result.claims)