--- 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).