---
name: extract_relations_llm
kind: function
lang: py
domain: datascience
version: "1.0.0"
purity: impure
signature: "def extract_relations_llm(text: str, entities: list, relation_types: list[str], llm_chat_json: Callable[[list[dict]], dict], language_instruction: str = 'Respond in English.') -> list"
description: "Extrae relaciones entre entidades de un chunk de texto usando un LLM inyectado. Valida que from_name y to_name correspondan a entidades existentes, y usa 'related_to' como fallback para tipos de relacion no permitidos."
tags: [extraction, relation, llm, knowledge-graph, nlp, datascience, fuzzygraph, graph]
uses_functions: []
uses_types:
  - entity_candidate_py_datascience
  - relation_candidate_py_datascience
returns:
  - relation_candidate_py_datascience
returns_optional: false
error_type: "error_go_core"
imports: [logging, sys, os, typing]
params:
  - name: text
    desc: "texto fuente para extraccion de relaciones entre entidades (ej: documento OSINT, parrafo descriptivo)"
  - name: entities
    desc: "lista de EntityCandidate ya extraidas (ej: output de extract_entities_llm). Valida que las relaciones refieran entidades reales."
  - name: relation_types
    desc: "lista de nombres de relacion permitidos (ej: ['employs', 'funds', 'owns']). No permitidas se remapean a 'related_to'."
  - name: llm_chat_json
    desc: "callable que toma list[dict] mensajes y retorna dict con clave 'relations'. Inyeccion de dependencia del LLM."
  - name: language_instruction
    desc: "instruccion de lenguaje para el LLM (defecto: 'Respond in English.')"
output: "lista de RelationCandidate con from_name, to_name, relation_type, description, confidence"
tested: true
tests:
  - "texto con dos entidades relacionadas"
  - "texto con entidades pero sin relacion"
  - "menos de dos entidades retorna lista vacia"
  - "llm inventa entidad que no existe se descarta"
test_file_path: "python/functions/datascience/extract_relations_llm_test.py"
file_path: "python/functions/datascience/extract_relations_llm.py"
---

## Ejemplo

```python
from extract_relations_llm import extract_relations_llm
from python.types.datascience.entity_candidate import EntityCandidate

# Stub de llm_chat_json (en produccion usar llm_completion_retry o similar)
def my_llm(messages: list[dict]) -> dict:
    # Llamar al LLM real aqui
    return {"relations": [...]}

entities = [
    EntityCandidate(name="Acme Corp", type_label="Organization", confidence=0.95),
    EntityCandidate(name="John Smith", type_label="Person", confidence=0.9),
]

relation_types = ["employs", "funds", "owns", "communicates_with", "related_to"]

relations = extract_relations_llm(
    text="Acme Corp employs John Smith as CEO and funds his research.",
    entities=entities,
    relation_types=relation_types,
    llm_chat_json=my_llm,
)

for rel in relations:
    print(f"{rel.from_name} --[{rel.relation_type}]--> {rel.to_name} ({rel.confidence:.2f})")
# Acme Corp --[employs]--> John Smith (0.90)
# Acme Corp --[funds]--> John Smith (0.85)
```

## Notas

**Inyeccion de dependencia del LLM:** `llm_chat_json` recibe una lista de mensajes en formato OpenAI (`[{"role": "system", "content": ...}, {"role": "user", "content": ...}]`) y retorna un dict con la clave `"relations"`. Esto desacopla la funcion de cualquier proveedor de LLM concreto.

**Validacion de entidades:** Solo se aceptan relaciones donde `from_name` y `to_name` aparecen exactamente en los nombres de las entidades proporcionadas. Relaciones con nombres inventados por el LLM se descartan silenciosamente (con debug log).

**Fallback de tipo:** Si el LLM propone un `relation_type` que no esta en la lista permitida, se reemplaza por `"related_to"`. Si `"related_to"` tampoco esta en la lista, se incluye igualmente como catch-all seguro.

**Menos de 2 entidades:** La funcion retorna `[]` inmediatamente sin llamar al LLM, ya que no puede haber relaciones con menos de 2 participantes.

**Error handling:** Si `llm_chat_json` lanza una excepcion, se captura con warning y retorna `[]`. Si la respuesta no contiene la clave `"relations"` o no es una lista, idem.

**Confianza:** Los valores de confianza del LLM se clampean al rango `[0.0, 1.0]`. Valores no numericos se convierten a `0.0`.

Disenado para fuzzygraph — se compone con `extract_entities_llm` (paso anterior) y `deduplicate_relations` (paso siguiente en el pipeline de extraccion).
