Structure the tech specs directory (#836)

Tech spec some subdirectories for different languages
This commit is contained in:
cybermaggedon 2026-04-21 16:06:41 +01:00 committed by GitHub
parent 48da6c5f8b
commit e7efb673ef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
423 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,135 @@
---
layout: default
title: "Especificación Técnica de Carga de Conocimiento desde la Línea de Comandos"
parent: "Spanish (Beta)"
---
# Especificación Técnica de Carga de Conocimiento desde la Línea de Comandos
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
Esta especificación describe las interfaces de línea de comandos para cargar conocimiento en TrustGraph, permitiendo a los usuarios importar datos de diversas fuentes a través de herramientas de línea de comandos. La integración admite cuatro casos de uso principales:
1. **[Caso de Uso 1]**: [Descripción]
2. **[Caso de Uso 2]**: [Descripción]
3. **[Caso de Uso 3]**: [Descripción]
4. **[Caso de Uso 4]**: [Descripción]
## Objetivos
- **[Objetivo 1]**: [Descripción]
- **[Objetivo 2]**: [Descripción]
- **[Objetivo 3]**: [Descripción]
- **[Objetivo 4]**: [Descripción]
- **[Objetivo 5]**: [Descripción]
- **[Objetivo 6]**: [Descripción]
- **[Objetivo 7]**: [Descripción]
- **[Objetivo 8]**: [Descripción]
## Antecedentes
[Describa el estado actual y las limitaciones que esta especificación aborda]
Las limitaciones actuales incluyen:
- [Limitación 1]
- [Limitación 2]
- [Limitación 3]
- [Limitación 4]
Esta especificación aborda estas deficiencias mediante [descripción]. Al [capacidad], TrustGraph puede:
- [Beneficio 1]
- [Beneficio 2]
- [Beneficio 3]
- [Beneficio 4]
## Diseño Técnico
### Arquitectura
La carga de conocimiento desde la línea de comandos requiere los siguientes componentes técnicos:
1. **[Componente 1]**
- [Descripción de la funcionalidad del componente]
- [Características clave]
- [Puntos de integración]
Módulo: [module-path]
2. **[Componente 2]**
- [Descripción de la funcionalidad del componente]
- [Características clave]
- [Puntos de integración]
Módulo: [module-path]
3. **[Componente 3]**
- [Descripción de la funcionalidad del componente]
- [Características clave]
- [Puntos de integración]
Módulo: [module-path]
### Modelos de Datos
#### [Modelo de Datos 1]
[Descripción del modelo de datos y su estructura]
Ejemplo:
```
[Example data structure]
```
Este enfoque permite:
- [Beneficio 1]
- [Beneficio 2]
- [Beneficio 3]
- [Beneficio 4]
### APIs
Nuevas APIs:
- [Descripción de la API 1]
- [Descripción de la API 2]
- [Descripción de la API 3]
APIs modificadas:
- [API modificada 1] - [Descripción de los cambios]
- [API modificada 2] - [Descripción de los cambios]
### Detalles de implementación
[Enfoque y convenciones de implementación]
[Notas adicionales de implementación]
## Consideraciones de seguridad
[Consideraciones de seguridad específicas de esta implementación]
## Consideraciones de rendimiento
[Consideraciones de rendimiento y posibles cuellos de botella]
## Estrategia de pruebas
[Enfoque y estrategia de pruebas]
## Plan de migración
[Estrategia de migración si es aplicable]
## Cronograma
[Información del cronograma si se especifica]
## Preguntas abiertas
- [Pregunta abierta 1]
- [Pregunta abierta 2]
## Referencias
[Referencias si es aplicable]

View file

@ -0,0 +1,280 @@
---
layout: default
title: "Explicabilidad del Agente: Registro de Origen"
parent: "Spanish (Beta)"
---
# Explicabilidad del Agente: Registro de Origen
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
Agregar el registro de origen al bucle del agente de React para que las sesiones del agente puedan ser rastreadas y depuradas utilizando la misma infraestructura de explicabilidad que GraphRAG.
**Decisiones de Diseño:**
- Escribir en `urn:graph:retrieval` (grafo de explicabilidad genérico)
- Cadena de dependencia lineal por ahora (análisis N → derivado de → análisis N-1)
- Las herramientas son cajas negras opacas (registrar solo la entrada/salida)
- El soporte de DAG se pospone a una iteración futura
## Tipos de Entidades
Tanto GraphRAG como Agent utilizan PROV-O como la ontología base con subtipos específicos de TrustGraph:
### Tipos de GraphRAG
| Entidad | Tipo PROV-O | Tipos TG | Descripción |
|--------|-------------|----------|-------------|
| Pregunta | `prov:Activity` | `tg:Question`, `tg:GraphRagQuestion` | La consulta del usuario |
| Exploración | `prov:Entity` | `tg:Exploration` | Bordes recuperados del grafo de conocimiento |
| Enfoque | `prov:Entity` | `tg:Focus` | Bordes seleccionados con razonamiento |
| Síntesis | `prov:Entity` | `tg:Synthesis` | Respuesta final |
### Tipos de Agente
| Entidad | Tipo PROV-O | Tipos TG | Descripción |
|--------|-------------|----------|-------------|
| Pregunta | `prov:Activity` | `tg:Question`, `tg:AgentQuestion` | La consulta del usuario |
| Análisis | `prov:Entity` | `tg:Analysis` | Cada ciclo de pensar/actuar/observar |
| Conclusión | `prov:Entity` | `tg:Conclusion` | Respuesta final |
### Tipos de Document RAG
| Entidad | Tipo PROV-O | Tipos TG | Descripción |
|--------|-------------|----------|-------------|
| Pregunta | `prov:Activity` | `tg:Question`, `tg:DocRagQuestion` | La consulta del usuario |
| Exploración | `prov:Entity` | `tg:Exploration` | Fragmentos recuperados del almacén de documentos |
| Síntesis | `prov:Entity` | `tg:Synthesis` | Respuesta final |
**Nota:** Document RAG utiliza un subconjunto de los tipos de GraphRAG (no hay un paso de "Enfoque" ya que no hay una fase de selección/razonamiento de bordes).
### Subtipos de Pregunta
Todas las entidades de "Pregunta" comparten `tg:Question` como un tipo base, pero tienen un subtipo específico para identificar el mecanismo de recuperación:
| Subtipo | Patrón URI | Mecanismo |
|---------|-------------|-----------|
| `tg:GraphRagQuestion` | `urn:trustgraph:question:{uuid}` | RAG de grafo de conocimiento |
| `tg:DocRagQuestion` | `urn:trustgraph:docrag:{uuid}` | RAG de documento/fragmento |
| `tg:AgentQuestion` | `urn:trustgraph:agent:{uuid}` | Agente ReAct |
Esto permite consultar todas las preguntas a través de `tg:Question` mientras se filtra por un mecanismo específico a través del subtipo.
## Modelo de Origen
```
Question (urn:trustgraph:agent:{uuid})
│ tg:query = "User's question"
│ prov:startedAtTime = timestamp
│ rdf:type = prov:Activity, tg:Question
↓ prov:wasDerivedFrom
Analysis1 (urn:trustgraph:agent:{uuid}/i1)
│ tg:thought = "I need to query the knowledge base..."
│ tg:action = "knowledge-query"
│ tg:arguments = {"question": "..."}
│ tg:observation = "Result from tool..."
│ rdf:type = prov:Entity, tg:Analysis
↓ prov:wasDerivedFrom
Analysis2 (urn:trustgraph:agent:{uuid}/i2)
│ ...
↓ prov:wasDerivedFrom
Conclusion (urn:trustgraph:agent:{uuid}/final)
│ tg:answer = "The final response..."
│ rdf:type = prov:Entity, tg:Conclusion
```
### Modelo de Origen (Provenance) del Documento RAG
```
Question (urn:trustgraph:docrag:{uuid})
│ tg:query = "User's question"
│ prov:startedAtTime = timestamp
│ rdf:type = prov:Activity, tg:Question
↓ prov:wasGeneratedBy
Exploration (urn:trustgraph:docrag:{uuid}/exploration)
│ tg:chunkCount = 5
│ tg:selectedChunk = "chunk-id-1"
│ tg:selectedChunk = "chunk-id-2"
│ ...
│ rdf:type = prov:Entity, tg:Exploration
↓ prov:wasDerivedFrom
Synthesis (urn:trustgraph:docrag:{uuid}/synthesis)
│ tg:content = "The synthesized answer..."
│ rdf:type = prov:Entity, tg:Synthesis
```
## Cambios Requeridos
### 1. Cambios en el Esquema
**Archivo:** `trustgraph-base/trustgraph/schema/services/agent.py`
Agregar los campos `session_id` y `collection` a `AgentRequest`:
```python
@dataclass
class AgentRequest:
question: str = ""
state: str = ""
group: list[str] | None = None
history: list[AgentStep] = field(default_factory=list)
user: str = ""
collection: str = "default" # NEW: Collection for provenance traces
streaming: bool = False
session_id: str = "" # NEW: For provenance tracking across iterations
```
**Archivo:** `trustgraph-base/trustgraph/messaging/translators/agent.py`
Actualizar el traductor para manejar `session_id` y `collection` tanto en `to_pulsar()` como en `from_pulsar()`.
### 2. Agregar un Productor de Explicabilidad al Servicio de Agente
**Archivo:** `trustgraph-flow/trustgraph/agent/react/service.py`
Registrar un productor de "explicabilidad" (mismo patrón que GraphRAG):
```python
from ... base import ProducerSpec
from ... schema import Triples
# In __init__:
self.register_specification(
ProducerSpec(
name = "explainability",
schema = Triples,
)
)
```
### 3. Generación de Triples de Proveniencia
**Archivo:** `trustgraph-base/trustgraph/provenance/agent.py`
Crear funciones auxiliares (similares a `question_triples`, `exploration_triples`, etc. de GraphRAG):
```python
def agent_session_triples(session_uri, query, timestamp):
"""Generate triples for agent Question."""
return [
Triple(s=session_uri, p=RDF_TYPE, o=PROV_ACTIVITY),
Triple(s=session_uri, p=RDF_TYPE, o=TG_QUESTION),
Triple(s=session_uri, p=TG_QUERY, o=query),
Triple(s=session_uri, p=PROV_STARTED_AT_TIME, o=timestamp),
]
def agent_iteration_triples(iteration_uri, parent_uri, thought, action, arguments, observation):
"""Generate triples for one Analysis step."""
return [
Triple(s=iteration_uri, p=RDF_TYPE, o=PROV_ENTITY),
Triple(s=iteration_uri, p=RDF_TYPE, o=TG_ANALYSIS),
Triple(s=iteration_uri, p=TG_THOUGHT, o=thought),
Triple(s=iteration_uri, p=TG_ACTION, o=action),
Triple(s=iteration_uri, p=TG_ARGUMENTS, o=json.dumps(arguments)),
Triple(s=iteration_uri, p=TG_OBSERVATION, o=observation),
Triple(s=iteration_uri, p=PROV_WAS_DERIVED_FROM, o=parent_uri),
]
def agent_final_triples(final_uri, parent_uri, answer):
"""Generate triples for Conclusion."""
return [
Triple(s=final_uri, p=RDF_TYPE, o=PROV_ENTITY),
Triple(s=final_uri, p=RDF_TYPE, o=TG_CONCLUSION),
Triple(s=final_uri, p=TG_ANSWER, o=answer),
Triple(s=final_uri, p=PROV_WAS_DERIVED_FROM, o=parent_uri),
]
```
### 4. Definiciones de tipo
**Archivo:** `trustgraph-base/trustgraph/provenance/namespaces.py`
Agregar tipos de entidad de explicabilidad y predicados de agente:
```python
# Explainability entity types (used by both GraphRAG and Agent)
TG_QUESTION = TG + "Question"
TG_EXPLORATION = TG + "Exploration"
TG_FOCUS = TG + "Focus"
TG_SYNTHESIS = TG + "Synthesis"
TG_ANALYSIS = TG + "Analysis"
TG_CONCLUSION = TG + "Conclusion"
# Agent predicates
TG_THOUGHT = TG + "thought"
TG_ACTION = TG + "action"
TG_ARGUMENTS = TG + "arguments"
TG_OBSERVATION = TG + "observation"
TG_ANSWER = TG + "answer"
```
## Archivos Modificados
| Archivo | Cambio |
|------|--------|
| `trustgraph-base/trustgraph/schema/services/agent.py` | Agregar session_id y collection a AgentRequest |
| `trustgraph-base/trustgraph/messaging/translators/agent.py` | Actualizar el traductor para nuevos campos |
| `trustgraph-base/trustgraph/provenance/namespaces.py` | Agregar tipos de entidad, predicados de agente y predicados de Document RAG |
| `trustgraph-base/trustgraph/provenance/triples.py` | Agregar tipos de TG a los constructores de triples de GraphRAG, agregar constructores de triples de Document RAG |
| `trustgraph-base/trustgraph/provenance/uris.py` | Agregar generadores de URI de Document RAG |
| `trustgraph-base/trustgraph/provenance/__init__.py` | Exportar nuevos tipos, predicados y funciones de Document RAG |
| `trustgraph-base/trustgraph/schema/services/retrieval.py` | Agregar explain_id y explain_graph a DocumentRagResponse |
| `trustgraph-base/trustgraph/messaging/translators/retrieval.py` | Actualizar DocumentRagResponseTranslator para campos de explicabilidad |
| `trustgraph-flow/trustgraph/agent/react/service.py` | Agregar lógica de productor y registro de explicabilidad |
| `trustgraph-flow/trustgraph/retrieval/document_rag/document_rag.py` | Agregar devolución de llamada de explicabilidad y emitir triples de procedencia |
| `trustgraph-flow/trustgraph/retrieval/document_rag/rag.py` | Agregar productor de explicabilidad y conectar la devolución de llamada |
| `trustgraph-cli/trustgraph/cli/show_explain_trace.py` | Manejar tipos de traza de agente |
| `trustgraph-cli/trustgraph/cli/list_explain_traces.py` | Listar sesiones de agente junto con GraphRAG |
## Archivos Creados
| Archivo | Propósito |
|------|---------|
| `trustgraph-base/trustgraph/provenance/agent.py` | Generadores de triples específicos del agente |
## Actualizaciones de la CLI
**Detección:** Tanto GraphRAG como las Preguntas del Agente tienen el tipo `tg:Question`. Se distinguen por:
1. Patrón de URI: `urn:trustgraph:agent:` vs `urn:trustgraph:question:`
2. Entidades derivadas: `tg:Analysis` (agente) vs `tg:Exploration` (GraphRAG)
**`list_explain_traces.py`:**
- Muestra la columna Tipo (Agente vs GraphRAG)
**`show_explain_trace.py`:**
- Detecta automáticamente el tipo de traza
- La representación del agente muestra: Pregunta → Paso(s) de análisis → Conclusión
## Compatibilidad con versiones anteriores
- `session_id` por defecto es `""` - las solicitudes antiguas funcionan, pero no tendrán procedencia
- `collection` por defecto es `"default"` - alternativa razonable
- La CLI maneja correctamente ambos tipos de traza
## Verificación
```bash
# Run an agent query
tg-invoke-agent -q "What is the capital of France?"
# List traces (should show agent sessions with Type column)
tg-list-explain-traces -U trustgraph -C default
# Show agent trace
tg-show-explain-trace "urn:trustgraph:agent:xxx"
```
## Trabajo Futuro (No Incluido en esta Solicitud de Incorporación)
- Dependencias de DAG (cuando el análisis N utiliza resultados de múltiples análisis anteriores)
- Enlace de procedencia específico de la herramienta (KnowledgeQuery → su traza GraphRAG)
- Emisión de procedencia en streaming (emitir a medida que se avanza, no en lote al final)

View file

@ -0,0 +1,113 @@
---
layout: default
title: "Fundamentos de la Arquitectura del Gráfico de Conocimiento"
parent: "Spanish (Beta)"
---
# Fundamentos de la Arquitectura del Gráfico de Conocimiento
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Fundamento 1: Modelo de Gráfico Sujeto-Predicado-Objeto (SPO)
**Decisión**: Adoptar SPO/RDF como el modelo de representación de conocimiento central.
**Justificación**:
Proporciona la máxima flexibilidad e interoperabilidad con las tecnologías de gráficos existentes.
Permite la traducción fluida a otros lenguajes de consulta de gráficos (por ejemplo, SPO → Cypher, pero no al revés).
Crea una base que "desbloquea muchas" capacidades posteriores.
Admite tanto relaciones de nodo a nodo (SPO) como relaciones de nodo a literal (RDF).
**Implementación**:
Estructura de datos central: `node → edge → {node | literal}`
Mantener la compatibilidad con los estándares RDF al tiempo que se admiten operaciones SPO extendidas.
## Fundamento 2: Integración Nativa del Gráfico de Conocimiento con LLM
**Decisión**: Optimizar la estructura y las operaciones del gráfico de conocimiento para la interacción con LLM.
**Justificación**:
El caso de uso principal implica que los LLM interactúen con los gráficos de conocimiento.
Las opciones de tecnología de gráficos deben priorizar la compatibilidad con LLM por encima de otras consideraciones.
Permite flujos de trabajo de procesamiento del lenguaje natural que aprovechan el conocimiento estructurado.
**Implementación**:
Diseñar esquemas de gráficos que los LLM puedan comprender y razonar eficazmente.
Optimizar para patrones de interacción comunes con LLM.
## Fundamento 3: Navegación del Gráfico Basada en Incrustaciones
**Decisión**: Implementar un mapeo directo de las consultas de lenguaje natural a los nodos del gráfico a través de incrustaciones.
**Justificación**:
Permite la ruta más sencilla posible desde la consulta de PNL hasta la navegación del gráfico.
Evita pasos complejos de generación de consultas intermedias.
Proporciona capacidades de búsqueda semántica eficientes dentro de la estructura del gráfico.
**Implementación**:
`NLP Query → Graph Embeddings → Graph Nodes`
Mantener representaciones de incrustación para todas las entidades del gráfico.
Admitir la coincidencia de similitud semántica directa para la resolución de consultas.
## Fundamento 4: Resolución de Entidades Distribuidas con Identificadores Deterministas
**Decisión**: Admitir la extracción de conocimiento en paralelo con la identificación determinista de entidades (regla del 80%).
**Justificación**:
**Ideal**: La extracción en un solo proceso con visibilidad completa del estado permite una resolución de entidades perfecta.
**Realidad**: Los requisitos de escalabilidad exigen capacidades de procesamiento en paralelo.
**Compromiso**: Diseñar para la identificación determinista de entidades en procesos distribuidos.
**Implementación**:
Desarrollar mecanismos para generar identificadores consistentes y únicos en diferentes extractores de conocimiento.
La misma entidad mencionada en diferentes procesos debe resolverse en el mismo identificador.
Reconocer que aproximadamente el 20% de los casos extremos pueden requerir modelos de procesamiento alternativos.
Diseñar mecanismos de respaldo para escenarios complejos de resolución de entidades.
## Fundamento 5: Arquitectura Orientada a Eventos con Publicación-Suscripción
**Decisión**: Implementar un sistema de mensajería de publicación-suscripción para la coordinación del sistema.
**Justificación**:
Permite un acoplamiento débil entre los componentes de extracción, almacenamiento y consulta de conocimiento.
Admite actualizaciones y notificaciones en tiempo real en todo el sistema.
Facilita flujos de trabajo de procesamiento distribuidos y escalables.
**Implementación**:
Coordinación basada en mensajes entre los componentes del sistema.
Flujos de eventos para actualizaciones de conocimiento, finalización de la extracción y resultados de consultas.
## Fundamento 6: Comunicación de Agentes Reentrantes
**Decisión**: Admitir operaciones de publicación-suscripción reentrantes para el procesamiento basado en agentes.
**Justificación**:
Permite flujos de trabajo sofisticados de agentes donde los agentes pueden activar y responder entre sí.
Admite canalizaciones complejas de procesamiento de conocimiento con varios pasos.
Permite patrones de procesamiento recursivos e iterativos.
**Implementación**:
El sistema de publicación-suscripción debe manejar las llamadas reentrantes de forma segura.
Mecanismos de coordinación de agentes que previenen bucles infinitos.
Soporte para la orquestación de flujos de trabajo de agentes.
## Fundamento 7: Integración con Almacenes de Datos Columnares
**Decisión**: Asegurar la compatibilidad de las consultas con los sistemas de almacenamiento columnar.
**Justificación**:
Permite consultas analíticas eficientes sobre grandes conjuntos de datos de conocimiento.
Admite casos de uso de inteligencia empresarial e informes.
Une la representación de conocimiento basada en gráficos con los flujos de trabajo analíticos tradicionales.
**Implementación**:
Capa de traducción de consultas: Consultas de gráfico → Consultas de columna.
Estrategia de almacenamiento híbrida que admite tanto operaciones de gráfico como cargas de trabajo analíticas.
Mantener el rendimiento de las consultas en ambos paradigmas.
--
## Resumen de los Principios de la Arquitectura
1. **Flexibilidad Primero**: El modelo SPO/RDF proporciona la máxima adaptabilidad.
2. **Optimización para LLM**: Todas las decisiones de diseño consideran los requisitos de interacción con LLM.
3. **Eficiencia Semántica**: Mapeo directo de incrustaciones a nodos para un rendimiento de consulta óptimo.
4. **Escalabilidad Pragmática**: Equilibrar la precisión perfecta con el procesamiento distribuido práctico.
5. **Coordinación Orientada a Eventos**: La publicación-suscripción permite un acoplamiento débil y la escalabilidad.
6. **Amigable para Agentes**: Admite flujos de trabajo complejos de procesamiento basados en agentes.
7. **Compatibilidad Analítica**: Une los paradigmas de gráficos y columnas para consultas integrales.
Estos fundamentos establecen una arquitectura de gráfico de conocimiento que equilibra la rigurosidad teórica con los requisitos prácticos de escalabilidad, optimizada para la integración con LLM y el procesamiento distribuido.

View file

@ -0,0 +1,249 @@
---
layout: default
title: "Especificación Técnica: Consolidación de la Configuración de Cassandra"
parent: "Spanish (Beta)"
---
# Especificación Técnica: Consolidación de la Configuración de Cassandra
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
**Estado:** Borrador
**Autor:** Asistente
**Fecha:** 2024-09-03
## Visión General
Esta especificación aborda los patrones de nombres y configuración inconsistentes para los parámetros de conexión de Cassandra en todo el código base de TrustGraph. Actualmente, existen dos esquemas de nombres de parámetros diferentes (`cassandra_*` frente a `graph_*`), lo que provoca confusión y complejidad en el mantenimiento.
## Declaración del Problema
El código base actualmente utiliza dos conjuntos distintos de parámetros de configuración de Cassandra:
1. **Módulos de Conocimiento/Config/Biblioteca** utilizan:
- `cassandra_host` (lista de hosts)
- `cassandra_usuario`
- `cassandra_contraseña`
2. **Módulos de Gráficos/Almacenamiento** utilizan:
- `graph_host` (único host, a veces convertido en lista)
- `graph_username`
- `graph_password`
3. **Exposición inconsistente de la línea de comandos:**
- Algunos procesadores (p. ej., `kg-store`) no expone la configuración de Cassandra como argumentos de línea de comandos
- Otros procesadores los expone con nombres y formatos diferentes
- El texto de ayuda no refleja los valores predeterminados de las variables de entorno
Ambos conjuntos de parámetros se conectan al mismo clúster de Cassandra, pero con diferentes convenciones de nombres, lo que provoca:
- Confusión en la configuración para los usuarios
- Mayor carga de mantenimiento
- Documentación inconsistente
- Posibilidad de configuración incorrecta
- Incapacidad de anular la configuración mediante argumentos de línea de comandos en algunos procesadores
## Solución Propuesta
### 1. Estandarizar los Nombres de los Parámetros
Todos los módulos utilizarán nombres de parámetros consistentes `cassandra_*`:
- `cassandra_host` - Lista de hosts (almacenado internamente como lista)
- `cassandra_username` - Nombre de usuario para la autenticación
- `cassandra_contraseña` - Contraseña para la autenticación
### 2. Argumentos de Línea de Comandos
Todos los procesadores DEBEN exponer la configuración de Cassandra a través de argumentos de línea de comandos:
- `--cassandra-host` - Lista separada por comas de hosts
- `--cassandra-username` - Nombre de usuario para la autenticación
- `--cassandra-password` - Contraseña para la autenticación
### 3. Fallback de Variables de Entorno
Si los parámetros de la línea de comandos no se proporcionan explícitamente, el sistema verificará las variables de entorno:
- `CASSANDRA_HOST` - Lista separada por comas de hosts
- `CASSANDRA_USERNAME` - Nombre de usuario para la autenticación
- `CASSANDRA_PASSWORD` - Contraseña para la autenticación
### 4. Valores Predeterminados
Si ni los parámetros de la línea de comandos ni las variables de entorno no se especifican:
- `cassandra_host` se establece en `["cassandra"]`
- `cassandra_username` se establece en `None` (sin autenticación)
- `cassandra_password` se establece en `None` (sin autenticación)
### 5. Requisitos de Ayuda
La salida `--help` DEBE:
- Mostrar los valores predeterminados de las variables de entorno
- Nunca mostrar los valores de la contraseña (mostrar `****` o `<set>` en su lugar)
- Indicar claramente el orden de resolución en la ayuda
Ejemplo de salida de ayuda:
```
--cassandra-host HOST
Lista de hosts de Cassandra, separada por comas (predeterminado: prod-cluster-1,prod-cluster-2)
[desde la variable de entorno CASSANDRA_HOST]
--cassandra-username USERNAME
Nombre de usuario de Cassandra (predeterminado: cassandra_user)
[desde la variable de entorno CASSANDRA_USERNAME]
--cassandra-password PASSWORD
Contraseña de Cassandra (predeterminado: <establecido desde el entorno>)
```
## Detalles de Implementación
### Orden de Resolución
Para cada parámetro de Cassandra, el orden de resolución será:
1. Valor del argumento de la línea de comandos
2. Variable de entorno (`CASSANDRA_*`)
3. Valor predeterminado
### Manejo del parámetro Host
El parámetro `cassandra_host`:
- La línea de comandos acepta una cadena separada por comas: `--cassandra-host "host1,host2,host3"`
- La variable de entorno acepta una cadena separada por comas: `CASSANDRA_HOST="host1,host2,host3"`
- Internamente siempre se almacena como una lista: `["host1", "host2", "host3"]`
### Código de Ejemplo
```python
# Código antiguo
@staticmethod
def add_args(parser):
parser.add_argument(
'-g', '--graph-host',
default="localhost",
help='Host del gráfico (predeterminado: localhost)'
)
parser.add_argument(
'--graph-username',
default=None,
help='Nombre de usuario de Cassandra'
)
# Código nuevo
@staticmethod
def add_args(parser):
FlowProcessor.add_args(parser)
add_cassandra_args(parser) # Usa el asistente estándar
```
### Actualización de Módulos que Utilizan `graph_*` Parámetros
1. Cambiar los nombres de los parámetros de `graph_*` a `cassandra_*`
2. Reemplazar los métodos `add_args()` personalizados
3. Utilizar las funciones `add_cassandra_args()` estándar
4. Actualizar las cadenas de documentación
Ejemplo de transformación:
```python
# Código antiguo
@staticmethod
def add_args(parser):
parser.add_argument(
'-g', '--graph-host',
default="localhost",
help=f'Host del gráfico (predeterminado: localhost)'
)
parser.add_argument(
'--graph-username',
default=None,
help=f'Nombre de usuario de Cassandra'
)
# Código nuevo
@staticmethod
def add_args(parser):
FlowProcessor.add_args(parser)
add_cassandra_args(parser) # Usa el asistente estándar
```
### Actualización de Módulos que Utilizan `cassandra_*` Parámetros
1. Agregar soporte para argumentos de línea de comandos (p. ej., `kg-store`)
2. Reemplazar las definiciones de argumentos existentes con `add_cassandra_args()`
3. Utilizar las funciones `resolve_cassandra_config()` para una resolución consistente
4. Asegurar el manejo consistente de las listas de hosts
### Actualización de Pruebas y Documentación
1. Actualizar todos los archivos de prueba
2. Actualizar la documentación de la línea de comandos
3. Actualizar la documentación de la API
4. Actualizar los ejemplos de Docker Compose
5. Actualizar la documentación de referencia de la configuración
## Compatibilidad con versiones anteriores
Para mantener la compatibilidad con versiones anteriores durante la transición:
1. **Advertencias de desuso** para los parámetros `graph_*`
2. **Alias de parámetros** - aceptar nombres antiguos y nuevos inicialmente
3. **Implementación gradual** durante varios lanzamientos
4. **Actualizaciones de documentación** con guía de migración
Ejemplo de código para la compatibilidad con versiones anteriores:
```python
def __init__(self, **params):
# Manejar parámetros graph_* desactualizados
if 'graph_host' in params:
warnings.warn("graph_host está desactualizado, usa cassandra_host", DeprecationWarning)
params.setdefault('cassandra_host', params.pop('graph_host'))
if 'graph_username' in params:
warnings.warn("graph_username está desactualizado, usa cassandra_username", DeprecationWarning)
params.setdefault('cassandra_username', params.pop('graph_username'))
# ... continuar con la resolución estándar
```
## Estrategia de Pruebas
1. **Pruebas unitarias** para la lógica de resolución de la configuración
2. **Pruebas de integración** con diversas combinaciones de configuración
3. **Pruebas de variables de entorno**
4. **Pruebas de compatibilidad con versiones anteriores** con parámetros desactualizados
5. **Pruebas de Docker compose** con variables de entorno
## Actualizaciones de Documentación
1. Actualizar toda la documentación de la línea de comandos
2. Actualizar la documentación de la API
3. Crear una guía de migración
4. Actualizar los ejemplos de Docker Compose
5. Actualizar la documentación de referencia de la configuración
## Riesgos y Mitigación
| Riesgo | Impacto | Mitigación |
|------|--------|------------|
| Cambios que rompen para los usuarios | Alto | Implementar un período de compatibilidad con versiones anteriores |
| Confusión en la configuración durante la transición | Medio | Documentación clara y advertencias de desuso |
| Fallos de prueba | Medio | Actualizaciones exhaustivas de prueba |
| Problemas de implementación de Docker | Alto | Actualizar todos los ejemplos de Docker Compose |
## Criterios de Éxito
- [ ] Todos los módulos utilizan nombres de parámetros consistentes `cassandra_*`
- [ ] Todos los procesadores expone la configuración de Cassandra a través de argumentos de línea de comandos
- [ ] La salida de la ayuda muestra los valores predeterminados de las variables de entorno
- [ ] Los valores de la contraseña nunca se muestran en la ayuda
- [ ] La retroalimentación de las variables de entorno funciona correctamente
- [ ] El parámetro `cassandra_host` se gestiona de forma consistente como una lista internamente
- [ ] Se mantiene la compatibilidad con versiones anteriores durante al menos 2 lanzamientos
- [ ] Todas las pruebas pasan con el nuevo sistema de configuración
- [ ] La documentación está actualizada
- [ ] Los ejemplos de Docker Compose funcionan con las variables de entorno
## Cronograma
- **Semana 1:** Implementar el asistente de configuración común y actualizar los módulos que utilizan `graph_*`
- **Semana 2:** Agregar soporte de variable de entorno a los módulos existentes que utilizan `cassandra_*`
- **Semana 3:** Actualizar las cadenas de documentación
- **Semana 4:** Pruebas de integración y correcciones de errores
## Consideraciones Futuras
- Considerar extender este patrón a otras configuraciones de bases de datos (p. ej., Elasticsearch)
- Implementar la validación de la configuración y mejores mensajes de error
- Agregar soporte para la configuración de conexión de piscina (connection pooling)
- Considerar agregar soporte para archivos `.env`

View file

@ -0,0 +1,687 @@
---
layout: default
title: "Especificación Técnica: Refactorización del Rendimiento de la Base de Conocimiento Cassandra"
parent: "Spanish (Beta)"
---
# Especificación Técnica: Refactorización del Rendimiento de la Base de Conocimiento Cassandra
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
**Estado:** Borrador
**Autor:** Asistente
**Fecha:** 2025-09-18
## Resumen
Esta especificación aborda los problemas de rendimiento en la implementación de la base de conocimiento TrustGraph Cassandra y propone optimizaciones para el almacenamiento y la consulta de triples RDF.
## Implementación Actual
### Diseño del Esquema
La implementación actual utiliza un diseño de tabla única en `trustgraph-flow/trustgraph/direct/cassandra_kg.py`:
```sql
CREATE TABLE triples (
collection text,
s text,
p text,
o text,
PRIMARY KEY (collection, s, p, o)
);
```
**Índices Secundarios:**
`triples_s` EN `s` (sujeto)
`triples_p` EN `p` (predicado)
`triples_o` EN `o` (objeto)
### Patrones de Consulta
La implementación actual admite 8 patrones de consulta distintos:
1. **get_all(colección, límite=50)** - Recupera todas las triples para una colección
```sql
SELECT s, p, o FROM triples WHERE collection = ? LIMIT 50
```
2. **get_s(colección, s, límite=10)** - Consulta por tema.
```sql
SELECT p, o FROM triples WHERE collection = ? AND s = ? LIMIT 10
```
3. **get_p(colección, p, límite=10)** - Consulta por predicado
```sql
SELECT s, o FROM triples WHERE collection = ? AND p = ? LIMIT 10
```
4. **get_o(colección, o, límite=10)** - Consulta por objeto
```sql
SELECT s, p FROM triples WHERE collection = ? AND o = ? LIMIT 10
```
5. **get_sp(colección, s, p, limit=10)** - Consulta por sujeto + predicado
```sql
SELECT o FROM triples WHERE collection = ? AND s = ? AND p = ? LIMIT 10
```
6. **get_po(collection, p, o, limit=10)** - Consulta por predicado + objeto ⚠️
```sql
SELECT s FROM triples WHERE collection = ? AND p = ? AND o = ? LIMIT 10 ALLOW FILTERING
```
7. **get_os(collection, o, s, limit=10)** - Consulta por objeto + sujeto ⚠️
```sql
SELECT p FROM triples WHERE collection = ? AND o = ? AND s = ? LIMIT 10 ALLOW FILTERING
```
8. **get_spo(colección, s, p, o, límite=10)** - Coincidencia exacta de tripleta.
```sql
SELECT s as x FROM triples WHERE collection = ? AND s = ? AND p = ? AND o = ? LIMIT 10
```
### Arquitectura Actual
**Archivo: `trustgraph-flow/trustgraph/direct/cassandra_kg.py`**
Clase única `KnowledgeGraph` que gestiona todas las operaciones
Agrupación de conexiones a través de una lista global `_active_clusters`
Nombre de tabla fijo: `"triples"`
Espacio de claves por modelo de usuario
Replicación SimpleStrategy con factor 1
**Puntos de Integración:**
**Ruta de Escritura:** `trustgraph-flow/trustgraph/storage/triples/cassandra/write.py`
**Ruta de Consulta:** `trustgraph-flow/trustgraph/query/triples/cassandra/service.py`
**Almacén de Conocimiento:** `trustgraph-flow/trustgraph/tables/knowledge.py`
## Problemas de Rendimiento Identificados
### Problemas a Nivel de Esquema
1. **Diseño de Clave Primaria Ineficiente**
Actual: `PRIMARY KEY (collection, s, p, o)`
Resulta en un agrupamiento deficiente para patrones de acceso comunes
Obliga al uso de índices secundarios costosos
2. **Uso Excesivo de Índices Secundarios** ⚠️
Tres índices secundarios en columnas de alta cardinalidad (s, p, o)
Los índices secundarios en Cassandra son costosos y no escalan bien
Las consultas 6 y 7 requieren `ALLOW FILTERING`, lo que indica un modelado de datos deficiente
3. **Riesgo de Particiones Calientes**
Una única clave de partición `collection` puede crear particiones calientes
Las colecciones grandes se concentrarán en nodos individuales
No hay estrategia de distribución para el equilibrio de carga
### Problemas a Nivel de Consulta
1. **Uso de ALLOW FILTERING** ⚠️
Dos tipos de consulta (get_po, get_os) requieren `ALLOW FILTERING`
Estas consultas escanean múltiples particiones y son extremadamente costosas
El rendimiento disminuye linealmente con el tamaño de los datos
2. **Patrones de Acceso Ineficientes**
No hay optimización para patrones de consulta RDF comunes
Faltan índices compuestos para combinaciones de consulta frecuentes
No se tiene en cuenta los patrones de recorrido de grafos
3. **Falta de Optimización de Consultas**
No hay almacenamiento en caché de sentencias preparadas
No hay sugerencias de consulta ni estrategias de optimización
No se tiene en cuenta la paginación más allá de un simple LIMIT
## Declaración del Problema
La implementación actual de la base de conocimiento de Cassandra tiene dos cuellos de botella críticos de rendimiento:
### 1. Rendimiento Ineficiente de la Consulta get_po
La consulta `get_po(collection, p, o)` es extremadamente ineficiente debido a que requiere `ALLOW FILTERING`:
```sql
SELECT s FROM triples WHERE collection = ? AND p = ? AND o = ? LIMIT 10 ALLOW FILTERING
```
**¿Por qué esto es problemático:**
`ALLOW FILTERING` obliga a Cassandra a escanear todas las particiones dentro de la colección.
El rendimiento disminuye linealmente con el tamaño de los datos.
Este es un patrón de consulta RDF común (encontrar sujetos que tengan una relación específica de predicado-objeto).
Crea una carga significativa en el clúster a medida que los datos crecen.
### 2. Estrategia de Clustering Deficiente
La clave primaria actual `PRIMARY KEY (collection, s, p, o)` proporciona beneficios de clustering mínimos:
**Problemas con el clustering actual:**
`collection` como clave de partición no distribuye los datos de manera efectiva.
La mayoría de las colecciones contienen datos diversos, lo que hace que el clustering sea ineficaz.
No se tiene en cuenta los patrones de acceso comunes en las consultas RDF.
Las colecciones grandes crean particiones "calientes" en nodos individuales.
Las columnas de clustering (s, p, o) no optimizan para los patrones típicos de recorrido de grafos.
**Impacto:**
Las consultas no se benefician de la localidad de los datos.
Utilización deficiente de la caché.
Distribución desigual de la carga en los nodos del clúster.
Cuellos de botella de escalabilidad a medida que las colecciones crecen.
## Solución Propuesta: Estrategia de Desnormalización de 4 Tablas
### Resumen
Reemplace la única tabla `triples` con cuatro tablas diseñadas específicamente, cada una optimizada para patrones de consulta específicos. Esto elimina la necesidad de índices secundarios y ALLOW FILTERING, al tiempo que proporciona un rendimiento óptimo para todos los tipos de consulta. La cuarta tabla permite una eliminación eficiente de colecciones a pesar de las claves de partición compuestas.
### Nuevo Diseño de Esquema
**Tabla 1: Consultas Centradas en el Sujeto (triples_s)**
```sql
CREATE TABLE triples_s (
collection text,
s text,
p text,
o text,
PRIMARY KEY ((collection, s), p, o)
);
```
**Optimiza:** get_s, get_sp, get_os
**Clave de partición:** (colección, s) - Mejor distribución que solo la colección.
**Agrupamiento:** (p, o) - Permite búsquedas eficientes de predicados/objetos para un sujeto.
**Tabla 2: Consultas Predicado-Objeto (triples_p)**
```sql
CREATE TABLE triples_p (
collection text,
p text,
o text,
s text,
PRIMARY KEY ((collection, p), o, s)
);
```
**Optimiza:** get_p, get_po (¡elimina ALLOW FILTERING!)
**Clave de partición:** (colección, p) - Acceso directo mediante predicado
**Agrupamiento:** (o, s) - Recorrido eficiente de objeto a sujeto
**Tabla 3: Consultas centradas en objetos (triples_o)**
```sql
CREATE TABLE triples_o (
collection text,
o text,
s text,
p text,
PRIMARY KEY ((collection, o), s, p)
);
```
**Optimiza:** get_o
**Clave de partición:** (colección, o) - Acceso directo por objeto
**Clustering:** (s, p) - Recorrido eficiente de sujeto-predicado
**Tabla 4: Gestión de colecciones y consultas SPO (triples_collection)**
```sql
CREATE TABLE triples_collection (
collection text,
s text,
p text,
o text,
PRIMARY KEY (collection, s, p, o)
);
```
**Optimiza:** get_spo, delete_collection
**Clave de partición:** solo colección - Permite operaciones eficientes a nivel de colección.
**Clustering:** (s, p, o) - Orden estándar de tripletas.
**Propósito:** Uso dual para búsquedas exactas de SPO y como índice de eliminación.
### Mapeo de consultas
| Consulta original | Tabla de destino | Mejora de rendimiento |
|----------------|-------------|------------------------|
| get_all(collection) | triples_s | PERMITE FILTRADO (aceptable para escaneo) |
| get_s(collection, s) | triples_s | Acceso directo a la partición |
| get_p(collection, p) | triples_p | Acceso directo a la partición |
| get_o(collection, o) | triples_o | Acceso directo a la partición |
| get_sp(collection, s, p) | triples_s | Partición + clustering |
| get_po(collection, p, o) | triples_p | **¡Ya no se permite ALLOW FILTERING!** |
| get_os(collection, o, s) | triples_o | Partición + clustering |
| get_spo(collection, s, p, o) | triples_collection | Búsqueda exacta de clave |
| delete_collection(collection) | triples_collection | Lee el índice, eliminación por lotes de todos |
### Estrategia de eliminación de colecciones
Con claves de partición compuestas, no podemos simplemente ejecutar `DELETE FROM table WHERE collection = ?`. En cambio:
1. **Fase de lectura:** Consulta `triples_collection` para enumerar todas las tripletas:
```sql
SELECT s, p, o FROM triples_collection WHERE collection = ?
```
Esto es eficiente ya que `collection` es la clave de partición para esta tabla.
2. **Fase de eliminación:** Para cada triple (s, p, o), elimine de las 4 tablas utilizando claves de partición completas:
```sql
DELETE FROM triples_s WHERE collection = ? AND s = ? AND p = ? AND o = ?
DELETE FROM triples_p WHERE collection = ? AND p = ? AND o = ? AND s = ?
DELETE FROM triples_o WHERE collection = ? AND o = ? AND s = ? AND p = ?
DELETE FROM triples_collection WHERE collection = ? AND s = ? AND p = ? AND o = ?
```
Agrupado en lotes de 100 para mayor eficiencia.
**Análisis de compensaciones:**
✅ Mantiene un rendimiento óptimo de las consultas con particiones distribuidas.
✅ No hay particiones con alta carga para colecciones grandes.
❌ Lógica de eliminación más compleja (leer y luego eliminar).
❌ Tiempo de eliminación proporcional al tamaño de la colección.
### Beneficios
1. **Elimina ALLOW FILTERING** - Cada consulta tiene una ruta de acceso óptima (excepto el escaneo get_all).
2. **No se requieren índices secundarios** - Cada tabla ES el índice para su patrón de consulta.
3. **Mejor distribución de datos** - Las claves de partición compuestas distribuyen la carga de manera efectiva.
4. **Rendimiento predecible** - El tiempo de consulta es proporcional al tamaño del resultado, no a los datos totales.
5. **Aprovecha las fortalezas de Cassandra** - Diseñado para la arquitectura de Cassandra.
6. **Permite la eliminación de colecciones** - triples_collection sirve como índice de eliminación.
## Plan de implementación
### Archivos que requieren cambios
#### Archivo de implementación principal
**`trustgraph-flow/trustgraph/direct/cassandra_kg.py`** - Se requiere una reescritura completa.
**Métodos actuales a refactorizar:**
```python
# Schema initialization
def init(self) -> None # Replace single table with three tables
# Insert operations
def insert(self, collection, s, p, o) -> None # Write to all three tables
# Query operations (API unchanged, implementation optimized)
def get_all(self, collection, limit=50) # Use triples_by_subject
def get_s(self, collection, s, limit=10) # Use triples_by_subject
def get_p(self, collection, p, limit=10) # Use triples_by_po
def get_o(self, collection, o, limit=10) # Use triples_by_object
def get_sp(self, collection, s, p, limit=10) # Use triples_by_subject
def get_po(self, collection, p, o, limit=10) # Use triples_by_po (NO ALLOW FILTERING!)
def get_os(self, collection, o, s, limit=10) # Use triples_by_subject
def get_spo(self, collection, s, p, o, limit=10) # Use triples_by_subject
# Collection management
def delete_collection(self, collection) -> None # Delete from all three tables
```
#### Archivos de Integración (No se requieren cambios en la lógica)
**`trustgraph-flow/trustgraph/storage/triples/cassandra/write.py`**
No se necesitan cambios: utiliza la API KnowledgeGraph existente.
Se beneficia automáticamente de las mejoras de rendimiento.
**`trustgraph-flow/trustgraph/query/triples/cassandra/service.py`**
No se necesitan cambios: utiliza la API KnowledgeGraph existente.
Se beneficia automáticamente de las mejoras de rendimiento.
### Archivos de Prueba que Requieren Actualizaciones
#### Pruebas Unitarias
**`tests/unit/test_storage/test_triples_cassandra_storage.py`**
Actualizar las expectativas de las pruebas para los cambios en el esquema.
Agregar pruebas para la consistencia de múltiples tablas.
Verificar que no haya "ALLOW FILTERING" en los planes de consulta.
**`tests/unit/test_query/test_triples_cassandra_query.py`**
Actualizar las aserciones de rendimiento.
Probar los 8 patrones de consulta contra las nuevas tablas.
Verificar el enrutamiento de consultas a las tablas correctas.
#### Pruebas de Integración
**`tests/integration/test_cassandra_integration.py`**
Pruebas de extremo a extremo con el nuevo esquema.
Comparaciones de referencia de rendimiento.
Verificación de la consistencia de los datos en todas las tablas.
**`tests/unit/test_storage/test_cassandra_config_integration.py`**
Actualizar las pruebas de validación de esquema.
Probar escenarios de migración.
### Estrategia de Implementación
#### Fase 1: Esquema y Métodos Centrales
1. **Reescribir el método `init()`** - Crear cuatro tablas en lugar de una.
2. **Reescribir el método `insert()`** - Escrituras por lotes a las cuatro tablas.
3. **Implementar sentencias preparadas** - Para un rendimiento óptimo.
4. **Agregar lógica de enrutamiento de tablas** - Dirigir las consultas a las tablas óptimas.
5. **Implementar la eliminación de colecciones** - Leer de triples_collection, eliminar por lotes de todas las tablas.
#### Fase 2: Optimización de Métodos de Consulta
1. **Reescribir cada método get_*** para usar la tabla óptima.
2. **Eliminar todo el uso de ALLOW FILTERING**.
3. **Implementar el uso eficiente de la clave de clustering**.
4. **Agregar registro del rendimiento de las consultas**.
#### Fase 3: Gestión de Colecciones
1. **Actualizar `delete_collection()`** - Eliminar de las tres tablas.
2. **Agregar verificación de consistencia** - Asegurar que todas las tablas se mantengan sincronizadas.
3. **Implementar operaciones por lotes** - Para operaciones multi-tabla atómicas.
### Detalles Clave de la Implementación
#### Estrategia de Escritura por Lotes
```python
def insert(self, collection, s, p, o):
batch = BatchStatement()
# Insert into all four tables
batch.add(self.insert_subject_stmt, (collection, s, p, o))
batch.add(self.insert_po_stmt, (collection, p, o, s))
batch.add(self.insert_object_stmt, (collection, o, s, p))
batch.add(self.insert_collection_stmt, (collection, s, p, o))
self.session.execute(batch)
```
#### Lógica de enrutamiento de consultas
```python
def get_po(self, collection, p, o, limit=10):
# Route to triples_p table - NO ALLOW FILTERING!
return self.session.execute(
self.get_po_stmt,
(collection, p, o, limit)
)
def get_spo(self, collection, s, p, o, limit=10):
# Route to triples_collection table for exact SPO lookup
return self.session.execute(
self.get_spo_stmt,
(collection, s, p, o, limit)
)
```
#### Lógica de eliminación de colecciones
```python
def delete_collection(self, collection):
# Step 1: Read all triples from collection table
rows = self.session.execute(
f"SELECT s, p, o FROM {self.collection_table} WHERE collection = %s",
(collection,)
)
# Step 2: Batch delete from all 4 tables
batch = BatchStatement()
count = 0
for row in rows:
s, p, o = row.s, row.p, row.o
# Delete using full partition keys for each table
batch.add(SimpleStatement(
f"DELETE FROM {self.subject_table} WHERE collection = ? AND s = ? AND p = ? AND o = ?"
), (collection, s, p, o))
batch.add(SimpleStatement(
f"DELETE FROM {self.po_table} WHERE collection = ? AND p = ? AND o = ? AND s = ?"
), (collection, p, o, s))
batch.add(SimpleStatement(
f"DELETE FROM {self.object_table} WHERE collection = ? AND o = ? AND s = ? AND p = ?"
), (collection, o, s, p))
batch.add(SimpleStatement(
f"DELETE FROM {self.collection_table} WHERE collection = ? AND s = ? AND p = ? AND o = ?"
), (collection, s, p, o))
count += 1
# Execute every 100 triples to avoid oversized batches
if count % 100 == 0:
self.session.execute(batch)
batch = BatchStatement()
# Execute remaining deletions
if count % 100 != 0:
self.session.execute(batch)
logger.info(f"Deleted {count} triples from collection {collection}")
```
#### Optimización de sentencias preparadas
```python
def prepare_statements(self):
# Cache prepared statements for better performance
self.insert_subject_stmt = self.session.prepare(
f"INSERT INTO {self.subject_table} (collection, s, p, o) VALUES (?, ?, ?, ?)"
)
self.insert_po_stmt = self.session.prepare(
f"INSERT INTO {self.po_table} (collection, p, o, s) VALUES (?, ?, ?, ?)"
)
self.insert_object_stmt = self.session.prepare(
f"INSERT INTO {self.object_table} (collection, o, s, p) VALUES (?, ?, ?, ?)"
)
self.insert_collection_stmt = self.session.prepare(
f"INSERT INTO {self.collection_table} (collection, s, p, o) VALUES (?, ?, ?, ?)"
)
# ... query statements
```
## Estrategia de Migración
### Enfoque de Migración de Datos
#### Opción 1: Despliegue Blue-Green (Recomendado)
1. **Implementar el nuevo esquema junto con el existente** - Utilizar nombres de tabla diferentes temporalmente
2. **Período de escritura dual** - Escribir tanto en el esquema antiguo como en el nuevo durante la transición
3. **Migración en segundo plano** - Copiar los datos existentes a las nuevas tablas
4. **Cambiar las lecturas** - Dirigir las consultas a las nuevas tablas una vez que los datos se hayan migrado
5. **Eliminar las tablas antiguas** - Después del período de verificación
#### Opción 2: Migración In-Place
1. **Adición de esquema** - Crear nuevas tablas en el keyspace existente
2. **Script de migración de datos** - Copia por lotes de la tabla antigua a las nuevas tablas
3. **Actualización de la aplicación** - Implementar el nuevo código después de que se complete la migración
4. **Limpieza de la tabla antigua** - Eliminar la tabla antigua e índices
### Compatibilidad hacia atrás
#### Estrategia de Despliegue
```python
# Environment variable to control table usage during migration
USE_LEGACY_TABLES = os.getenv('CASSANDRA_USE_LEGACY', 'false').lower() == 'true'
class KnowledgeGraph:
def __init__(self, ...):
if USE_LEGACY_TABLES:
self.init_legacy_schema()
else:
self.init_optimized_schema()
```
#### Script de Migración
```python
def migrate_data():
# Read from old table
old_triples = session.execute("SELECT collection, s, p, o FROM triples")
# Batch write to new tables
for batch in batched(old_triples, 100):
batch_stmt = BatchStatement()
for row in batch:
# Add to all three new tables
batch_stmt.add(insert_subject_stmt, row)
batch_stmt.add(insert_po_stmt, (row.collection, row.p, row.o, row.s))
batch_stmt.add(insert_object_stmt, (row.collection, row.o, row.s, row.p))
session.execute(batch_stmt)
```
### Estrategia de Validación
#### Comprobaciones de Consistencia de Datos
```python
def validate_migration():
# Count total records in old vs new tables
old_count = session.execute("SELECT COUNT(*) FROM triples WHERE collection = ?", (collection,))
new_count = session.execute("SELECT COUNT(*) FROM triples_by_subject WHERE collection = ?", (collection,))
assert old_count == new_count, f"Record count mismatch: {old_count} vs {new_count}"
# Spot check random samples
sample_queries = generate_test_queries()
for query in sample_queries:
old_result = execute_legacy_query(query)
new_result = execute_optimized_query(query)
assert old_result == new_result, f"Query results differ for {query}"
```
## Estrategia de Pruebas
### Pruebas de Rendimiento
#### Escenarios de Referencia
1. **Comparación del Rendimiento de Consultas**
Métricas de rendimiento antes y después para los 8 tipos de consultas
Centrarse en la mejora del rendimiento de get_po (eliminar ALLOW FILTERING)
Medir la latencia de las consultas bajo varios tamaños de datos
2. **Pruebas de Carga**
Ejecución concurrente de consultas
Rendimiento de escritura con operaciones por lotes
Utilización de memoria y CPU
3. **Pruebas de Escalabilidad**
Rendimiento con tamaños de colección crecientes
Distribución de consultas de múltiples colecciones
Utilización de nodos del clúster
#### Conjuntos de Datos de Prueba
**Pequeño:** 10K triples por colección
**Mediano:** 100K triples por colección
**Grande:** 1M+ triples por colección
**Múltiples colecciones:** Probar la distribución de particiones
### Pruebas Funcionales
#### Actualizaciones de Pruebas Unitarias
```python
# Example test structure for new implementation
class TestCassandraKGPerformance:
def test_get_po_no_allow_filtering(self):
# Verify get_po queries don't use ALLOW FILTERING
with patch('cassandra.cluster.Session.execute') as mock_execute:
kg.get_po('test_collection', 'predicate', 'object')
executed_query = mock_execute.call_args[0][0]
assert 'ALLOW FILTERING' not in executed_query
def test_multi_table_consistency(self):
# Verify all tables stay in sync
kg.insert('test', 's1', 'p1', 'o1')
# Check all tables contain the triple
assert_triple_exists('triples_by_subject', 'test', 's1', 'p1', 'o1')
assert_triple_exists('triples_by_po', 'test', 'p1', 'o1', 's1')
assert_triple_exists('triples_by_object', 'test', 'o1', 's1', 'p1')
```
#### Actualizaciones de la prueba de integración
```python
class TestCassandraIntegration:
def test_query_performance_regression(self):
# Ensure new implementation is faster than old
old_time = benchmark_legacy_get_po()
new_time = benchmark_optimized_get_po()
assert new_time < old_time * 0.5 # At least 50% improvement
def test_end_to_end_workflow(self):
# Test complete write -> query -> delete cycle
# Verify no performance degradation in integration
```
### Plan de Reversión
#### Estrategia de Reversión Rápida
1. **Alternancia de variables de entorno** - Vuelva a las tablas heredadas inmediatamente.
2. **Mantenga las tablas heredadas** - No las elimine hasta que se demuestre el rendimiento.
3. **Alertas de monitoreo** - Desencadenadores de reversión automatizados basados en tasas de error/latencia.
#### Validación de la Reversión
```python
def rollback_to_legacy():
# Set environment variable
os.environ['CASSANDRA_USE_LEGACY'] = 'true'
# Restart services to pick up change
restart_cassandra_services()
# Validate functionality
run_smoke_tests()
```
## Riesgos y Consideraciones
### Riesgos de Rendimiento
**Aumento de la latencia de escritura** - 4 operaciones de escritura por inserción (un 33% más que el enfoque de 3 tablas)
**Sobrecarga de almacenamiento** - 4 veces más espacio de almacenamiento requerido (un 33% más que el enfoque de 3 tablas)
**Fallos en la escritura por lotes** - Se necesita un manejo adecuado de errores
**Complejidad de la eliminación** - La eliminación de la colección requiere un bucle de lectura y eliminación
### Riesgos Operacionales
**Complejidad de la migración** - Migración de datos para conjuntos de datos grandes
**Desafíos de consistencia** - Asegurar que todas las tablas permanezcan sincronizadas
**Lagunas de monitoreo** - Se necesitan nuevas métricas para las operaciones de múltiples tablas
### Estrategias de Mitigación
1. **Implementación gradual** - Comenzar con colecciones pequeñas
2. **Monitoreo integral** - Realizar un seguimiento de todas las métricas de rendimiento
3. **Validación automatizada** - Verificación continua de la consistencia
4. **Capacidad de reversión rápida** - Selección de tablas basada en el entorno
## Criterios de Éxito
### Mejoras de Rendimiento
[ ] **Eliminar ALLOW FILTERING** - Las consultas get_po y get_os se ejecutan sin filtrado
[ ] **Reducción de la latencia de la consulta** - Mejora del 50% o más en los tiempos de respuesta de las consultas
[ ] **Mejor distribución de la carga** - Sin particiones "calientes", distribución uniforme de la carga en los nodos del clúster
[ ] **Rendimiento escalable** - El tiempo de consulta es proporcional al tamaño del resultado, no a la cantidad total de datos
### Requisitos Funcionales
[ ] **Compatibilidad de la API** - Todo el código existente continúa funcionando sin cambios
[ ] **Consistencia de datos** - Las tres tablas permanecen sincronizadas
[ ] **Cero pérdida de datos** - La migración preserva todas las triples existentes
[ ] **Compatibilidad con versiones anteriores** - Capacidad de volver al esquema heredado
### Requisitos Operacionales
[ ] **Migración segura** - Implementación blue-green con capacidad de reversión
[ ] **Cobertura de monitoreo** - Métricas integrales para operaciones de múltiples tablas
[ ] **Cobertura de pruebas** - Todos los patrones de consulta se prueban con puntos de referencia de rendimiento
[ ] **Documentación** - Procedimientos de implementación y operación actualizados
## Cronograma
### Fase 1: Implementación
[ ] Reescribir `cassandra_kg.py` con el esquema de múltiples tablas
[ ] Implementar operaciones de escritura por lotes
[ ] Agregar optimización de sentencias preparadas
[ ] Actualizar pruebas unitarias
### Fase 2: Pruebas de Integración
[ ] Actualizar pruebas de integración
[ ] Pruebas de rendimiento
[ ] Pruebas de carga con volúmenes de datos realistas
[ ] Scripts de validación para la consistencia de los datos
### Fase 3: Planificación de la Migración
[ ] Scripts de implementación blue-green
[ ] Herramientas de migración de datos
[ ] Actualizaciones del panel de monitoreo
[ ] Procedimientos de reversión
### Fase 4: Despliegue en Producción
[ ] Implementación gradual en producción
[ ] Monitoreo y validación del rendimiento
[ ] Limpieza de tablas heredadas
[ ] Actualizaciones de la documentación
## Conclusión
Esta estrategia de desnormalización de múltiples tablas aborda directamente los dos cuellos de botella de rendimiento críticos:
1. **Elimina el costoso ALLOW FILTERING** al proporcionar estructuras de tabla óptimas para cada patrón de consulta
2. **Mejora la eficacia de la agrupación** a través de claves de partición compuestas que distribuyen la carga de manera adecuada
El enfoque aprovecha las fortalezas de Cassandra al tiempo que mantiene la compatibilidad total de la API, lo que garantiza que el código existente se beneficie automáticamente de las mejoras de rendimiento.

View file

@ -0,0 +1,410 @@
---
layout: default
title: "Especificación Técnica de Gestión de Colecciones"
parent: "Spanish (Beta)"
---
# Especificación Técnica de Gestión de Colecciones
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Descripción General
Esta especificación describe las capacidades de gestión de colecciones para TrustGraph, que requieren la creación explícita de colecciones y proporcionan un control directo sobre el ciclo de vida de la colección. Las colecciones deben crearse explícitamente antes de su uso, lo que garantiza una sincronización adecuada entre los metadatos del bibliotecario y todos los backends de almacenamiento. La función admite cuatro casos de uso principales:
1. **Creación de Colecciones**: Crear explícitamente colecciones antes de almacenar datos
2. **Listado de Colecciones**: Ver todas las colecciones existentes en el sistema
3. **Gestión de Metadatos de Colecciones**: Actualizar los nombres, las descripciones y las etiquetas de las colecciones
4. **Eliminación de Colecciones**: Eliminar colecciones y sus datos asociados en todos los tipos de almacenamiento
## Objetivos
**Creación Explícita de Colecciones**: Requerir que las colecciones se creen antes de que se puedan almacenar datos
**Sincronización de Almacenamiento**: Asegurar que las colecciones existan en todos los backends de almacenamiento (vectores, objetos, triples)
**Visibilidad de Colecciones**: Permitir a los usuarios listar e inspeccionar todas las colecciones en su entorno
**Limpieza de Colecciones**: Permitir la eliminación de colecciones que ya no son necesarias
**Organización de Colecciones**: Compatibilidad con etiquetas para un mejor seguimiento y descubrimiento de colecciones
**Gestión de Metadatos**: Asociar metadatos significativos con las colecciones para una mayor claridad operativa
**Descubrimiento de Colecciones**: Facilitar la búsqueda de colecciones específicas mediante el filtrado y la búsqueda
**Transparencia Operacional**: Proporcionar una visibilidad clara del ciclo de vida y el uso de las colecciones
**Gestión de Recursos**: Permitir la limpieza de colecciones no utilizadas para optimizar la utilización de recursos
**Integridad de Datos**: Prevenir la existencia de colecciones huérfanas en el almacenamiento sin seguimiento de metadatos
## Antecedentes
Anteriormente, las colecciones en TrustGraph se creaban implícitamente durante las operaciones de carga de datos, lo que provocaba problemas de sincronización en los que las colecciones podían existir en los backends de almacenamiento sin metadatos correspondientes en el bibliotecario. Esto creaba desafíos de gestión y posibles datos huérfanos.
El modelo de creación explícita de colecciones aborda estos problemas mediante:
La necesidad de crear colecciones antes de su uso a través de `tg-set-collection`
La difusión de la creación de colecciones a todos los backends de almacenamiento
El mantenimiento de un estado sincronizado entre los metadatos del bibliotecario y el almacenamiento
La prevención de escrituras en colecciones inexistentes
La provisión de una gestión clara del ciclo de vida de las colecciones
Esta especificación define el modelo de gestión explícita de colecciones. Al requerir la creación explícita de colecciones, TrustGraph garantiza:
Que las colecciones se rastreen en los metadatos del bibliotecario desde su creación
Que todos los backends de almacenamiento conozcan las colecciones antes de recibir datos
Que no existan colecciones huérfanas en el almacenamiento
Una visibilidad y un control claros del ciclo de vida de las colecciones
Un manejo de errores coherente cuando las operaciones hacen referencia a colecciones inexistentes
## Diseño Técnico
### Arquitectura
El sistema de gestión de colecciones se implementará dentro de la infraestructura existente de TrustGraph:
1. **Integración del Servicio del Bibliotecario**
Las operaciones de gestión de colecciones se agregarán al servicio del bibliotecario existente
No se requiere un nuevo servicio; aprovecha los patrones de autenticación y acceso existentes
Gestiona el listado, la eliminación y la gestión de metadatos de colecciones
Módulo: trustgraph-librarian
2. **Tabla de Metadatos de Colecciones de Cassandra**
Nueva tabla en el keyspace existente del bibliotecario
Almacena metadatos de colecciones con acceso específico al usuario
Clave primaria: (user_id, collection_id) para una correcta multi-tenencia
Módulo: trustgraph-librarian
3. **CLI de Gestión de Colecciones**
Interfaz de línea de comandos para operaciones de colecciones
Proporciona comandos de listado, eliminación, etiquetado y gestión de etiquetas
Se integra con el marco de CLI existente
Módulo: trustgraph-cli
### Modelos de Datos
#### Tabla de Metadatos de Colecciones de Cassandra
Los metadatos de la colección se almacenarán en una tabla estructurada de Cassandra en el keyspace del bibliotecario:
```sql
CREATE TABLE collections (
user text,
collection text,
name text,
description text,
tags set<text>,
created_at timestamp,
updated_at timestamp,
PRIMARY KEY (user, collection)
);
```
Estructura de la tabla:
**user** + **collection**: Clave primaria compuesta que asegura el aislamiento del usuario
**name**: Nombre legible por humanos de la colección
**description**: Descripción detallada del propósito de la colección
**tags**: Conjunto de etiquetas para la categorización y el filtrado
**created_at**: Marca de tiempo de creación de la colección
**updated_at**: Marca de tiempo de la última modificación
Este enfoque permite:
Gestión de colecciones multi-inquilino con aislamiento de usuario
Consulta eficiente por usuario y colección
Sistema de etiquetado flexible para la organización
Seguimiento del ciclo de vida para obtener información operativa
#### Ciclo de vida de la colección
Las colecciones se crean explícitamente en el bibliotecario antes de que puedan comenzar las operaciones de datos:
1. **Creación de la colección** (Dos caminos):
**Camino A: Creación iniciada por el usuario** a través de `tg-set-collection`:
El usuario proporciona el ID de la colección, el nombre, la descripción y las etiquetas
El bibliotecario crea un registro de metadatos en la tabla `collections`
El bibliotecario transmite "crear-colección" a todos los backends de almacenamiento
Todos los procesadores de almacenamiento crean la colección y confirman el éxito
La colección está ahora lista para las operaciones de datos
**Camino B: Creación automática al enviar un documento**:
El usuario envía un documento que especifica un ID de colección
El bibliotecario comprueba si existe la colección en la tabla de metadatos
Si no existe: El bibliotecario crea metadatos con valores predeterminados (nombre=id_colección, descripción/etiquetas vacías)
El bibliotecario transmite "crear-colección" a todos los backends de almacenamiento
Todos los procesadores de almacenamiento crean la colección y confirman el éxito
El procesamiento del documento continúa con la colección ahora establecida
Ambos caminos garantizan que la colección exista en los metadatos del bibliotecario Y en todos los backends de almacenamiento antes de permitir las operaciones de datos.
2. **Validación de almacenamiento**: Las operaciones de escritura validan la existencia de la colección:
Los procesadores de almacenamiento comprueban el estado de la colección antes de aceptar escrituras
Las escrituras en colecciones inexistentes devuelven un error
Esto evita las escrituras directas que omiten la lógica de creación de colecciones del bibliotecario
3. **Comportamiento de la consulta**: Las operaciones de consulta gestionan las colecciones inexistentes de forma elegante:
Las consultas a colecciones inexistentes devuelven resultados vacíos
No se lanza ningún error para las operaciones de consulta
Permite la exploración sin requerir que la colección exista
4. **Actualizaciones de metadatos**: Los usuarios pueden actualizar los metadatos de la colección después de la creación:
Actualice el nombre, la descripción y las etiquetas a través de `tg-set-collection`
Las actualizaciones se aplican solo a los metadatos del bibliotecario
Los backends de almacenamiento mantienen la colección, pero las actualizaciones de metadatos no se propagan
5. **Eliminación explícita**: Los usuarios eliminan las colecciones a través de `tg-delete-collection`:
El bibliotecario transmite "eliminar-colección" a todos los backends de almacenamiento
Espera la confirmación de todos los procesadores de almacenamiento
Elimina el registro de metadatos del bibliotecario solo después de que se complete la limpieza del almacenamiento
Garantiza que no queden datos huérfanos en el almacenamiento
**Principio clave**: El bibliotecario es el único punto de control para la creación de colecciones. Ya sea iniciado por un comando de usuario o por la presentación de un documento, el bibliotecario garantiza el seguimiento adecuado de los metadatos y la sincronización del backend de almacenamiento antes de permitir las operaciones de datos.
Operaciones requeridas:
**Crear colección**: Operación del usuario a través de `tg-set-collection` O automática al enviar un documento
**Actualizar metadatos de la colección**: Operación del usuario para modificar el nombre, la descripción y las etiquetas
**Eliminar colección**: Operación del usuario para eliminar la colección y sus datos en todos los almacenes
**Listar colecciones**: Operación del usuario para ver las colecciones con filtrado por etiquetas
#### Gestión de colecciones multi-almacén
Las colecciones existen en varios backends de almacenamiento en TrustGraph:
**Tiendas vectoriales** (Qdrant, Milvus, Pinecone): Almacena incrustaciones y datos vectoriales
**Tiendas de objetos** (Cassandra): Almacena documentos y datos de archivos
**Tiendas de triples** (Cassandra, Neo4j, Memgraph, FalkorDB): Almacena datos de grafos/RDF
Después de la última línea traducida, incluir una línea final que contenga exactamente: [[__END_OF_TRANSLATION__]]
Cada tipo de tienda implementa:
**Seguimiento del estado de la colección**: Mantener conocimiento de qué colecciones existen
**Creación de colecciones**: Aceptar y procesar operaciones de "crear-colección"
**Validación de colecciones**: Comprobar si la colección existe antes de aceptar escrituras
**Eliminación de colecciones**: Eliminar todos los datos para la colección especificada
El servicio de bibliotecario coordina las operaciones de colección en todos los tipos de tienda, asegurando:
Que las colecciones se creen en todos los backends antes de su uso
Que todos los backends confirmen la creación antes de devolver el éxito
Un ciclo de vida de colección sincronizado en todos los tipos de almacenamiento
Un manejo de errores consistente cuando las colecciones no existen
#### Seguimiento del estado de la colección por tipo de almacenamiento
Cada backend de almacenamiento realiza el seguimiento del estado de la colección de manera diferente según sus capacidades:
**Triple Store de Cassandra:**
Utiliza la tabla `triples_collection` existente
Crea un triple de marcador de sistema cuando se crea una colección
Consulta: `SELECT collection FROM triples_collection WHERE collection = ? LIMIT 1`
Comprobación de existencia de colección eficiente de partición única
**Almacenes de vectores Qdrant/Milvus/Pinecone:**
Las API nativas de colección proporcionan la comprobación de existencia
Las colecciones se crean con la configuración de vectores adecuada
El método `collection_exists()` utiliza la API de almacenamiento
La creación de colecciones valida los requisitos de dimensión
**Almacenes de grafos Neo4j/Memgraph/FalkorDB:**
Utiliza nodos `:CollectionMetadata` para realizar el seguimiento de las colecciones
Propiedades del nodo: `{user, collection, created_at}`
Consulta: `MATCH (c:CollectionMetadata {user: $user, collection: $collection})`
Separado de los nodos de datos para una separación limpia
Permite una enumeración y validación eficientes de las colecciones
**Almacén de objetos de Cassandra:**
Utiliza la tabla de metadatos de la colección o filas de marcador
Patrón similar al triple store
Valida la colección antes de las escrituras de documentos
### API
API de administración de colecciones (Bibliotecario):
**Crear/Actualizar colección**: Crear una nueva colección o actualizar los metadatos existentes a través de `tg-set-collection`
**Listar colecciones**: Recuperar colecciones para un usuario con filtrado opcional por etiqueta
**Eliminar colección**: Eliminar la colección y los datos asociados, propagándose a todos los tipos de tienda
API de administración de almacenamiento (Todos los procesadores de almacenamiento):
**Crear colección**: Manejar la operación de "crear-colección", establecer la colección en el almacenamiento
**Eliminar colección**: Manejar la operación de "eliminar-colección", eliminar todos los datos de la colección
**Comprobación de existencia de colección**: Validación interna antes de aceptar operaciones de escritura
API de operación de datos (Comportamiento modificado):
**API de escritura**: Validar que la colección exista antes de aceptar datos, devolver un error si no es así
**API de consulta**: Devolver resultados vacíos para colecciones inexistentes sin error
### Detalles de implementación
La implementación seguirá los patrones existentes de TrustGraph para la integración de servicios y la estructura de comandos de la CLI.
#### Eliminación en cascada de colecciones
Cuando un usuario inicia la eliminación de una colección a través del servicio de bibliotecario:
1. **Validación de metadatos**: Verificar que la colección exista y que el usuario tenga permiso para eliminarla
2. **Propagación a tiendas**: El bibliotecario coordina la eliminación en todos los escritores de tiendas:
Escritor de almacenamiento de vectores: Eliminar incrustaciones e índices de vectores para el usuario y la colección
Escritor de almacenamiento de objetos: Eliminar documentos y archivos para el usuario y la colección
Escritor de triple store: Eliminar datos de grafos y triples para el usuario y la colección
3. **Limpieza de metadatos**: Eliminar el registro de metadatos de la colección de Cassandra
4. **Manejo de errores**: Si falla la eliminación de alguna tienda, mantener la coherencia a través de mecanismos de reversión o reintento
#### Interfaz de administración de colecciones
**⚠️ ENFOQUE ANTIGUO: REEMPLAZADO POR UN PATRÓN BASADO EN CONFIGURACIÓN**
La arquitectura basada en colas descrita a continuación ha sido reemplazada por un enfoque basado en configuración que utiliza `CollectionConfigHandler`. Todos los backends de almacenamiento ahora reciben actualizaciones de colecciones a través de mensajes de configuración en lugar de colas de administración dedicadas.
~~Todos los escritores de tiendas implementan una interfaz de administración de colecciones estandarizada con un esquema común:~~
~~**Esquema de mensaje (`StorageManagementRequest`):**~~
```json
{
"operation": "create-collection" | "delete-collection",
"user": "user123",
"collection": "documents-2024"
}
```
~~**Arquitectura de la Cola:**~~
~~**Cola de Gestión de Almacenes Vectoriales** (`vector-storage-management`): Almacenes de vectores/incrustaciones~~
~~**Cola de Gestión de Almacenes de Objetos** (`object-storage-management`): Almacenes de objetos/documentos~~
~~**Cola de Gestión de Almacenes Triples** (`triples-storage-management`): Almacenes de grafos/RDF~~
~~**Cola de Respuestas de Almacenamiento** (`storage-management-response`): Todas las respuestas se envían aquí~~
**Implementación Actual:**
Todos los backends de almacenamiento ahora utilizan `CollectionConfigHandler`:
**Integración de Configuración Push**: Los servicios de almacenamiento se registran para recibir notificaciones de configuración push.
**Sincronización Automática**: Las colecciones se crean/eliminan en función de los cambios de configuración.
**Modelo Declarativo**: Las colecciones se definen en el servicio de configuración, y los backends se sincronizan para que coincidan.
**Sin Solicitud/Respuesta**: Elimina la sobrecarga de coordinación y el seguimiento de respuestas.
**Seguimiento del Estado de la Colección**: Se mantiene a través de la caché `known_collections`.
**Operaciones Idempotentes**: Es seguro procesar la misma configuración varias veces.
Cada backend de almacenamiento implementa:
`create_collection(user: str, collection: str, metadata: dict)` - Crear estructuras de colección
`delete_collection(user: str, collection: str)` - Eliminar todos los datos de la colección
`collection_exists(user: str, collection: str) -> bool` - Validar antes de escribir
#### Refactorización del Almacén Triples de Cassandra
Como parte de esta implementación, el almacén triples de Cassandra se refactorizará de un modelo de una tabla por colección a un modelo de una tabla unificada:
**Arquitectura Actual:**
Keyspace por usuario, tabla separada por colección
Esquema: `(s, p, o)` con `PRIMARY KEY (s, p, o)`
Nombres de tabla: las colecciones de usuario se convierten en tablas separadas de Cassandra.
**Nueva Arquitectura:**
Keyspace por usuario, una sola tabla "triples" para todas las colecciones
Esquema: `(collection, s, p, o)` con `PRIMARY KEY (collection, s, p, o)`
Aislamiento de colecciones a través de la partición de colecciones
**Cambios Requeridos:**
1. **Refactorización de la Clase TrustGraph** (`trustgraph/direct/cassandra.py`):
Eliminar el parámetro `table` del constructor, usar la tabla "triples" fija.
Agregar el parámetro `collection` a todos los métodos.
Actualizar el esquema para incluir la colección como la primera columna.
**Actualizaciones de Índice**: Se crearán nuevos índices para admitir los 8 patrones de consulta:
Índice en `(s)` para consultas basadas en el sujeto.
Índice en `(p)` para consultas basadas en el predicado.
Índice en `(o)` para consultas basadas en el objeto.
Nota: Cassandra no admite índices secundarios de varias columnas, por lo que estos son índices de una sola columna.
**Rendimiento del Patrón de Consulta:**
`get_all()` - escaneo de partición en `collection`
`get_s(s)` - utiliza la clave primaria de manera eficiente (`collection, s`)
`get_p(p)` - utiliza `idx_p` con `collection` de filtrado
`get_o(o)` - utiliza `idx_o` con `collection` de filtrado
`get_sp(s, p)` - utiliza la clave primaria de manera eficiente (`collection, s, p`)
⚠️ `get_po(p, o)` - requiere `ALLOW FILTERING` (utiliza `idx_p` o `idx_o` más filtrado)
`get_os(o, s)` - utiliza `idx_o` con filtrado adicional en `s`
`get_spo(s, p, o)` - utiliza toda la clave primaria de manera eficiente
**Nota sobre ALLOW FILTERING**: El patrón de consulta `get_po` requiere `ALLOW FILTERING` porque necesita tanto las restricciones de predicado como las de objeto sin un índice compuesto adecuado. Esto es aceptable porque este patrón de consulta es menos común que las consultas basadas en el sujeto en el uso típico de un almacén triples.
2. **Actualizaciones del Escritor de Almacenamiento** (`trustgraph/storage/triples/cassandra/write.py`):
Mantener una única conexión TrustGraph por usuario en lugar de por (usuario, colección).
Pasar la colección a las operaciones de inserción.
Mejor utilización de recursos con menos conexiones.
3. **Actualizaciones del Servicio de Consulta** (`trustgraph/query/triples/cassandra/service.py`):
Una única conexión TrustGraph por usuario.
Pasar la colección a todas las operaciones de consulta.
Mantener la misma lógica de consulta con el parámetro de colección.
**Beneficios:**
**Eliminación Simplificada de Colecciones**: Eliminar utilizando la clave de partición `collection` en las 4 tablas.
**Eficiencia de Recursos**: Menos conexiones de base de datos y objetos de tabla.
**Operaciones entre Colecciones**: Más fácil de implementar operaciones que abarcan múltiples colecciones.
**Arquitectura Consistente**: Se alinea con el enfoque unificado de metadatos de colección.
**Validación de Colecciones**: Es fácil comprobar la existencia de la colección mediante la tabla `triples_collection`.
Las operaciones de colección serán atómicas siempre que sea posible y proporcionarán un manejo de errores y una validación adecuados.
## Consideraciones de seguridad
Las operaciones de administración de colecciones requieren la autorización adecuada para evitar el acceso no autorizado o la eliminación de colecciones. El control de acceso se alineará con los modelos de seguridad de TrustGraph existentes.
## Consideraciones de rendimiento
Las operaciones de listado de colecciones pueden requerir paginación para entornos con un gran número de colecciones. Las consultas de metadatos deben optimizarse para patrones de filtrado comunes.
## Estrategia de pruebas
La prueba exhaustiva cubrirá:
Flujo de trabajo de creación de colecciones de extremo a extremo
Sincronización del almacenamiento en segundo plano
Validación de escritura para colecciones inexistentes
Manejo de consultas de colecciones inexistentes
Eliminación de colecciones en cascada en todos los almacenes
Manejo de errores y escenarios de recuperación
Pruebas unitarias para cada almacenamiento en segundo plano
Pruebas de integración para operaciones entre almacenes
## Estado de la implementación
### ✅ Componentes completados
1. **Servicio de administración de colecciones Librarian** (`trustgraph-flow/trustgraph/librarian/collection_manager.py`)
Operaciones CRUD de metadatos de colecciones (listar, actualizar, eliminar)
Integración de la tabla de metadatos de colecciones de Cassandra a través de `LibraryTableStore`
Coordinación de la eliminación de colecciones en cascada en todos los tipos de almacenamiento
Manejo de solicitudes/respuestas asíncronas con una gestión de errores adecuada
2. **Esquema de metadatos de colecciones** (`trustgraph-base/trustgraph/schema/services/collection.py`)
Esquemas `CollectionManagementRequest` y `CollectionManagementResponse`
Esquema `CollectionMetadata` para registros de colecciones
Definiciones de temas de cola de solicitudes/respuestas de colecciones
3. **Esquema de administración de almacenamiento** (`trustgraph-base/trustgraph/schema/services/storage.py`)
Esquemas `StorageManagementRequest` y `StorageManagementResponse`
Temas de cola de administración de almacenamiento definidos
Formato de mensaje para operaciones de colecciones a nivel de almacenamiento
4. **Esquema de tabla Cassandra de 4 tablas** (`trustgraph-flow/trustgraph/direct/cassandra_kg.py`)
Claves de partición compuestas para el rendimiento de la consulta
Tabla `triples_collection` para consultas SPO y seguimiento de eliminación
Implementación de la eliminación de colecciones con el patrón de lectura y eliminación
### ✅ Migración a patrón basado en configuración - COMPLETADO
**Todos los backends de almacenamiento se han migrado del patrón basado en cola al patrón `CollectionConfigHandler` basado en configuración.**
Migraciones completadas:
`trustgraph-flow/trustgraph/storage/triples/cassandra/write.py`
`trustgraph-flow/trustgraph/storage/triples/neo4j/write.py`
`trustgraph-flow/trustgraph/storage/triples/memgraph/write.py`
`trustgraph-flow/trustgraph/storage/triples/falkordb/write.py`
`trustgraph-flow/trustgraph/storage/doc_embeddings/qdrant/write.py`
`trustgraph-flow/trustgraph/storage/graph_embeddings/qdrant/write.py`
`trustgraph-flow/trustgraph/storage/doc_embeddings/milvus/write.py`
`trustgraph-flow/trustgraph/storage/graph_embeddings/milvus/write.py`
`trustgraph-flow/trustgraph/storage/doc_embeddings/pinecone/write.py`
`trustgraph-flow/trustgraph/storage/graph_embeddings/pinecone/write.py`
`trustgraph-flow/trustgraph/storage/objects/cassandra/write.py`
Todos los backends ahora:
Heredan de `CollectionConfigHandler`
Se registran para notificaciones de configuración push a través de `self.register_config_handler(self.on_collection_config)`
Implementan `create_collection(user, collection, metadata)` y `delete_collection(user, collection)`
Utilizan `collection_exists(user, collection)` para validar antes de escribir
Se sincronizan automáticamente con los cambios del servicio de configuración
Infraestructura basada en cola heredada eliminada:
✅ Esquemas `StorageManagementRequest` y `StorageManagementResponse` eliminados
✅ Definiciones de temas de cola de administración de almacenamiento eliminadas
✅ Consumidor/productor de administración de almacenamiento eliminado de todos los backends
✅ Manejadores `on_storage_management` eliminados de todos los backends

View file

@ -0,0 +1,144 @@
---
layout: default
title: "Identificador de fragmento de incrustaciones de documentos"
parent: "Spanish (Beta)"
---
# Identificador de fragmento de incrustaciones de documentos
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
Actualmente, el almacenamiento de incrustaciones de documentos almacena directamente el texto del fragmento en la carga útil de la base de datos vectorial, duplicando datos que existen en Garage. Esta especificación reemplaza el almacenamiento del texto del fragmento con referencias `chunk_id`.
## Estado actual
```python
@dataclass
class ChunkEmbeddings:
chunk: bytes = b""
vectors: list[list[float]] = field(default_factory=list)
@dataclass
class DocumentEmbeddingsResponse:
error: Error | None = None
chunks: list[str] = field(default_factory=list)
```
Carga útil del almacén de vectores:
```python
payload={"doc": chunk} # Duplicates Garage content
```
## Diseño
### Cambios en el esquema
**ChunkEmbeddings** - reemplazar "chunk" con "chunk_id":
```python
@dataclass
class ChunkEmbeddings:
chunk_id: str = ""
vectors: list[list[float]] = field(default_factory=list)
```
**DocumentEmbeddingsResponse** - devolver `chunk_ids` en lugar de `chunks`:
```python
@dataclass
class DocumentEmbeddingsResponse:
error: Error | None = None
chunk_ids: list[str] = field(default_factory=list)
```
### Carga útil del almacén de vectores
Todos los almacenes (Qdrant, Milvus, Pinecone):
```python
payload={"chunk_id": chunk_id}
```
### Cambios en el Documento RAG
El procesador de documentos RAG recupera el contenido de los fragmentos de Garage:
```python
# Get chunk_ids from embeddings store
chunk_ids = await self.rag.doc_embeddings_client.query(...)
# Fetch chunk content from Garage
docs = []
for chunk_id in chunk_ids:
content = await self.rag.librarian_client.get_document_content(
chunk_id, self.user
)
docs.append(content)
```
### Cambios en la API/SDK
**DocumentEmbeddingsClient** devuelve chunk_ids:
```python
return resp.chunk_ids # Changed from resp.chunks
```
**Formato de cable** (DocumentEmbeddingsResponseTranslator):
```python
result["chunk_ids"] = obj.chunk_ids # Changed from chunks
```
### Cambios en la CLI
La herramienta de la CLI muestra los chunk_ids (los usuarios pueden obtener el contenido por separado si es necesario).
## Archivos a Modificar
### Esquema
`trustgraph-base/trustgraph/schema/knowledge/embeddings.py` - ChunkEmbeddings
`trustgraph-base/trustgraph/schema/services/query.py` - DocumentEmbeddingsResponse
### Mensajería/Traductores
`trustgraph-base/trustgraph/messaging/translators/embeddings_query.py` - DocumentEmbeddingsResponseTranslator
### Cliente
`trustgraph-base/trustgraph/base/document_embeddings_client.py` - return chunk_ids
### SDK/API de Python
`trustgraph-base/trustgraph/api/flow.py` - document_embeddings_query
`trustgraph-base/trustgraph/api/socket_client.py` - document_embeddings_query
`trustgraph-base/trustgraph/api/async_flow.py` - if applicable
`trustgraph-base/trustgraph/api/bulk_client.py` - import/export document embeddings
`trustgraph-base/trustgraph/api/async_bulk_client.py` - import/export document embeddings
### Servicio de Embeddings
`trustgraph-flow/trustgraph/embeddings/document_embeddings/embeddings.py` - pass chunk_id
### Escritores de Almacenamiento
`trustgraph-flow/trustgraph/storage/doc_embeddings/qdrant/write.py`
`trustgraph-flow/trustgraph/storage/doc_embeddings/milvus/write.py`
`trustgraph-flow/trustgraph/storage/doc_embeddings/pinecone/write.py`
### Servicios de Consulta
`trustgraph-flow/trustgraph/query/doc_embeddings/qdrant/service.py`
`trustgraph-flow/trustgraph/query/doc_embeddings/milvus/service.py`
`trustgraph-flow/trustgraph/query/doc_embeddings/pinecone/service.py`
### Gateway
`trustgraph-flow/trustgraph/gateway/dispatch/document_embeddings_query.py`
`trustgraph-flow/trustgraph/gateway/dispatch/document_embeddings_export.py`
`trustgraph-flow/trustgraph/gateway/dispatch/document_embeddings_import.py`
### Document RAG
`trustgraph-flow/trustgraph/retrieval/document_rag/rag.py` - add librarian client
`trustgraph-flow/trustgraph/retrieval/document_rag/document_rag.py` - fetch from Garage
### CLI
`trustgraph-cli/trustgraph/cli/invoke_document_embeddings.py`
`trustgraph-cli/trustgraph/cli/save_doc_embeds.py`
`trustgraph-cli/trustgraph/cli/load_doc_embeds.py`
## Beneficios
1. Única fuente de verdad: solo el texto de los chunks en Garage.
2. Almacenamiento de vectores reducido.
3. Permite el rastreo de origen en tiempo de consulta a través del chunk_id.

View file

@ -0,0 +1,675 @@
---
layout: default
title: "Especificación Técnica del Procesamiento por Lotes de Embeddings"
parent: "Spanish (Beta)"
---
# Especificación Técnica del Procesamiento por Lotes de Embeddings
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
Esta especificación describe optimizaciones para el servicio de embeddings para soportar el procesamiento por lotes de múltiples textos en una sola solicitud. La implementación actual procesa un texto a la vez, perdiendo las significativas ventajas de rendimiento que los modelos de embeddings proporcionan al procesar lotes.
1. **Ineficiencia en el Procesamiento de un Solo Texto**: La implementación actual envuelve textos individuales en una lista, lo que no aprovecha las capacidades de procesamiento por lotes de FastEmbed.
2. **Sobrecarga de una Solicitud por Texto**: Cada texto requiere un viaje de ida y vuelta separado de Pulsar.
3. **Ineficiencia en la Inferencia del Modelo**: Los modelos de embeddings tienen una sobrecarga fija por lote; los lotes pequeños desperdician recursos de GPU/CPU.
4. **Procesamiento Serial en los Llamadores**: Los servicios clave iteran sobre los elementos y llaman a los embeddings uno a la vez.
## Objetivos
**Soporte para la API de Lotes**: Permitir el procesamiento de múltiples textos en una sola solicitud.
**Compatibilidad con Versiones Anteriores**: Mantener el soporte para solicitudes de un solo texto.
**Mejora Significativa del Rendimiento**: Apuntar a una mejora de rendimiento de 5 a 10 veces para operaciones por lotes.
**Latencia Reducida por Texto**: Disminuir la latencia amortizada al incrustar múltiples textos.
**Eficiencia de Memoria**: Procesar lotes sin un consumo excesivo de memoria.
**Independencia del Proveedor**: Soporte para el procesamiento por lotes en FastEmbed, Ollama y otros proveedores.
**Migración de Llamadores**: Actualizar todos los llamadores de embeddings para que utilicen la API de lotes cuando sea beneficioso.
## Antecedentes
### Implementación Actual - Servicio de Embeddings
La implementación de embeddings en `trustgraph-flow/trustgraph/embeddings/fastembed/processor.py` presenta una ineficiencia de rendimiento significativa:
```python
# fastembed/processor.py line 56
async def on_embeddings(self, text, model=None):
use_model = model or self.default_model
self._load_model(use_model)
vecs = self.embeddings.embed([text]) # Single text wrapped in list
return [v.tolist() for v in vecs]
```
**Problemas:**
1. **Tamaño de lote 1**: El método `embed()` de FastEmbed está optimizado para el procesamiento por lotes, pero siempre lo llamamos con `[text]`, un lote de tamaño 1.
2. **Sobrecarga por solicitud**: Cada solicitud de incrustación incurre en:
Serialización/deserialización de mensajes de Pulsar
Latencia de ida y vuelta de la red
Sobrecarga de inicio de inferencia del modelo
Sobrecarga de programación asíncrona de Python
3. **Limitación del esquema**: El esquema `EmbeddingsRequest` solo admite un texto:
```python
@dataclass
class EmbeddingsRequest:
text: str = "" # Single text only
```
### Llamadores actuales - Procesamiento serial
#### 1. API Gateway
**Archivo:** `trustgraph-flow/trustgraph/gateway/dispatch/embeddings.py`
La puerta de enlace acepta solicitudes de incrustación de texto único a través de HTTP/WebSocket y las reenvía al servicio de incrustaciones. Actualmente no existe un punto final por lotes.
```python
class EmbeddingsRequestor(ServiceRequestor):
# Handles single EmbeddingsRequest -> EmbeddingsResponse
request_schema=EmbeddingsRequest, # Single text only
response_schema=EmbeddingsResponse,
```
**Impacto:** Los clientes externos (aplicaciones web, scripts) deben realizar N solicitudes HTTP para incrustar N textos.
#### 2. Servicio de Incorporación de Documentos
**Archivo:** `trustgraph-flow/trustgraph/embeddings/document_embeddings/embeddings.py`
Procesa fragmentos de documentos uno a la vez:
```python
async def on_message(self, msg, consumer, flow):
v = msg.value()
# Single chunk per request
resp = await flow("embeddings-request").request(
EmbeddingsRequest(text=v.chunk)
)
vectors = resp.vectors
```
**Impacto:** Cada fragmento de documento requiere una llamada de incrustación separada. Un documento con 100 fragmentos = 100 solicitudes de incrustación.
#### 3. Servicio de Incrustaciones de Grafos
**Archivo:** `trustgraph-flow/trustgraph/embeddings/graph_embeddings/embeddings.py`
Recorre las entidades e incrusta cada una de forma secuencial:
```python
async def on_message(self, msg, consumer, flow):
for entity in v.entities:
# Serial embedding - one entity at a time
vectors = await flow("embeddings-request").embed(
text=entity.context
)
entities.append(EntityEmbeddings(
entity=entity.entity,
vectors=vectors,
chunk_id=entity.chunk_id,
))
```
**Impacto:** Un mensaje con 50 entidades = 50 solicitudes de incrustación serial. Esto es un cuello de botella importante durante la construcción del grafo de conocimiento.
#### 4. Servicio de Incrustaciones de Filas
**Archivo:** `trustgraph-flow/trustgraph/embeddings/row_embeddings/embeddings.py`
Itera sobre textos únicos e incrusta cada uno de forma serial:
```python
async def on_message(self, msg, consumer, flow):
for text, (index_name, index_value) in texts_to_embed.items():
# Serial embedding - one text at a time
vectors = await flow("embeddings-request").embed(text=text)
embeddings_list.append(RowIndexEmbedding(
index_name=index_name,
index_value=index_value,
text=text,
vectors=vectors
))
```
**Impacto:** Procesar una tabla con 100 valores indexados únicos = 100 solicitudes de incrustación seriales.
#### 5. EmbeddingsClient (Cliente Base)
**Archivo:** `trustgraph-base/trustgraph/base/embeddings_client.py`
El cliente utilizado por todos los procesadores de flujo solo admite la incrustación de texto único:
```python
class EmbeddingsClient(RequestResponse):
async def embed(self, text, timeout=30):
resp = await self.request(
EmbeddingsRequest(text=text), # Single text
timeout=timeout
)
return resp.vectors
```
**Impacto:** Todos los clientes que utilizan esta herramienta están limitados a operaciones de texto único.
#### 6. Herramientas de Línea de Comandos
**Archivo:** `trustgraph-cli/trustgraph/cli/invoke_embeddings.py`
La herramienta de la línea de comandos acepta un único argumento de texto:
```python
def query(url, flow_id, text, token=None):
result = flow.embeddings(text=text) # Single text
vectors = result.get("vectors", [])
```
**Impacto:** Los usuarios no pueden realizar incrustaciones por lotes desde la línea de comandos. El procesamiento de un archivo de textos requiere N invocaciones.
#### 7. SDK de Python
El SDK de Python proporciona dos clases de cliente para interactuar con los servicios de TrustGraph. Ambas solo admiten la incrustación de un solo texto.
**Archivo:** `trustgraph-base/trustgraph/api/flow.py`
```python
class FlowInstance:
def embeddings(self, text):
"""Get embeddings for a single text"""
input = {"text": text}
return self.request("service/embeddings", input)["vectors"]
```
**Archivo:** `trustgraph-base/trustgraph/api/socket_client.py`
```python
class SocketFlowInstance:
def embeddings(self, text: str, **kwargs: Any) -> Dict[str, Any]:
"""Get embeddings for a single text via WebSocket"""
request = {"text": text}
return self.client._send_request_sync(
"embeddings", self.flow_id, request, False
)
```
**Impacto:** Los desarrolladores de Python que utilizan el SDK deben iterar sobre los textos y realizar N llamadas de API separadas. No existe soporte para la inserción por lotes para los usuarios del SDK.
### Impacto en el rendimiento
Para la ingestión típica de documentos (1000 fragmentos de texto):
**Actual:** 1000 solicitudes separadas, 1000 llamadas de inferencia de modelos.
**Por lotes (batch_size=32):** 32 solicitudes, 32 llamadas de inferencia de modelos (reducción del 96.8%).
Para la inserción de gráficos (mensaje con 50 entidades):
**Actual:** 50 llamadas `await` secuenciales, ~5-10 segundos.
**Por lotes:** 1-2 llamadas por lotes, ~0.5-1 segundo (mejora de 5-10 veces).
FastEmbed y bibliotecas similares logran una escalabilidad de rendimiento cercana a la lineal con el tamaño del lote hasta los límites del hardware (típicamente 32-128 textos por lote).
## Diseño técnico
### Arquitectura
La optimización del procesamiento por lotes de inserciones requiere cambios en los siguientes componentes:
#### 1. **Mejora del esquema**
Extender `EmbeddingsRequest` para admitir múltiples textos.
Extender `EmbeddingsResponse` para devolver múltiples conjuntos de vectores.
Mantener la compatibilidad con versiones anteriores con solicitudes de un solo texto.
Módulo: `trustgraph-base/trustgraph/schema/services/llm.py`
#### 2. **Mejora del servicio base**
Actualizar `EmbeddingsService` para manejar solicitudes por lotes.
Agregar configuración del tamaño del lote.
Implementar el manejo de solicitudes con conocimiento del lote.
Módulo: `trustgraph-base/trustgraph/base/embeddings_service.py`
#### 3. **Actualizaciones del procesador del proveedor**
Actualizar el procesador FastEmbed para pasar el lote completo a `embed()`.
Actualizar el procesador de Ollama para manejar lotes (si es compatible).
Agregar procesamiento secuencial de respaldo para los proveedores que no admiten lotes.
Módulos:
`trustgraph-flow/trustgraph/embeddings/fastembed/processor.py`
`trustgraph-flow/trustgraph/embeddings/ollama/processor.py`
#### 4. **Mejora del cliente**
Agregar un método de inserción por lotes a `EmbeddingsClient`.
Admitir tanto API individuales como por lotes.
Agregar inserción automática por lotes para grandes entradas.
Módulo: `trustgraph-base/trustgraph/base/embeddings_client.py`
#### 5. **Actualizaciones del llamador: Procesadores de flujo**
Actualizar `graph_embeddings` para agrupar los contextos de las entidades.
Actualizar `row_embeddings` para agrupar los textos del índice.
Actualizar `document_embeddings` si el agrupamiento de mensajes es factible.
Módulos:
`trustgraph-flow/trustgraph/embeddings/graph_embeddings/embeddings.py`
`trustgraph-flow/trustgraph/embeddings/row_embeddings/embeddings.py`
`trustgraph-flow/trustgraph/embeddings/document_embeddings/embeddings.py`
#### 6. **Mejora de la puerta de enlace de API**
Agregar un punto final de inserción por lotes.
Admitir una matriz de textos en el cuerpo de la solicitud.
Módulo: `trustgraph-flow/trustgraph/gateway/dispatch/embeddings.py`
#### 7. **Mejora de la herramienta de la línea de comandos**
Agregar soporte para múltiples textos o entrada de archivos.
Agregar un parámetro de tamaño de lote.
Módulo: `trustgraph-cli/trustgraph/cli/invoke_embeddings.py`
#### 8. **Mejora del SDK de Python**
Agregar el método `embeddings_batch()` a `FlowInstance`.
Agregar el método `embeddings_batch()` a `SocketFlowInstance`.
Admitir tanto API individuales como por lotes para los usuarios del SDK.
Módulos:
`trustgraph-base/trustgraph/api/flow.py`
`trustgraph-base/trustgraph/api/socket_client.py`
### Modelos de datos
#### EmbeddingsRequest
```python
@dataclass
class EmbeddingsRequest:
texts: list[str] = field(default_factory=list)
```
Uso:
Texto único: `EmbeddingsRequest(texts=["hello world"])`
Lote: `EmbeddingsRequest(texts=["text1", "text2", "text3"])`
#### EmbeddingsResponse
```python
@dataclass
class EmbeddingsResponse:
error: Error | None = None
vectors: list[list[list[float]]] = field(default_factory=list)
```
Estructura de la respuesta:
`vectors[i]` contiene el conjunto de vectores para `texts[i]`
Cada conjunto de vectores es `list[list[float]]` (los modelos pueden devolver múltiples vectores por texto)
Ejemplo: 3 textos → `vectors` tiene 3 entradas, cada una conteniendo los incrustados de ese texto
### APIs
#### EmbeddingsClient
```python
class EmbeddingsClient(RequestResponse):
async def embed(
self,
texts: list[str],
timeout: float = 300,
) -> list[list[list[float]]]:
"""
Embed one or more texts in a single request.
Args:
texts: List of texts to embed
timeout: Timeout for the operation
Returns:
List of vector sets, one per input text
"""
resp = await self.request(
EmbeddingsRequest(texts=texts),
timeout=timeout
)
if resp.error:
raise RuntimeError(resp.error.message)
return resp.vectors
```
#### Punto final de incrustaciones de la API Gateway
Punto final actualizado que admite incrustaciones individuales o por lotes:
```
POST /api/v1/embeddings
Content-Type: application/json
{
"texts": ["text1", "text2", "text3"],
"flow_id": "default"
}
Response:
{
"vectors": [
[[0.1, 0.2, ...]],
[[0.3, 0.4, ...]],
[[0.5, 0.6, ...]]
]
}
```
### Detalles de implementación
#### Fase 1: Cambios en el esquema
**EmbeddingsRequest:**
```python
@dataclass
class EmbeddingsRequest:
texts: list[str] = field(default_factory=list)
```
**Respuesta de Inserción:**
```python
@dataclass
class EmbeddingsResponse:
error: Error | None = None
vectors: list[list[list[float]]] = field(default_factory=list)
```
**Actualizaciones en EmbeddingsService.on_request:**
```python
async def on_request(self, msg, consumer, flow):
request = msg.value()
id = msg.properties()["id"]
model = flow("model")
vectors = await self.on_embeddings(request.texts, model=model)
response = EmbeddingsResponse(error=None, vectors=vectors)
await flow("response").send(response, properties={"id": id})
```
#### Fase 2: Actualización del Procesador FastEmbed
**Actual (Ineficiente):**
```python
async def on_embeddings(self, text, model=None):
use_model = model or self.default_model
self._load_model(use_model)
vecs = self.embeddings.embed([text]) # Batch of 1
return [v.tolist() for v in vecs]
```
**Actualizado:**
```python
async def on_embeddings(self, texts: list[str], model=None):
"""Embed texts - processes all texts in single model call"""
if not texts:
return []
use_model = model or self.default_model
self._load_model(use_model)
# FastEmbed handles the full batch efficiently
all_vecs = list(self.embeddings.embed(texts))
# Return list of vector sets, one per input text
return [[v.tolist()] for v in all_vecs]
```
#### Fase 3: Actualización del Servicio de Inserción de Gráficos
**Actual (Serial):**
```python
async def on_message(self, msg, consumer, flow):
entities = []
for entity in v.entities:
vectors = await flow("embeddings-request").embed(text=entity.context)
entities.append(EntityEmbeddings(...))
```
**Actualizado (Lote):**
```python
async def on_message(self, msg, consumer, flow):
# Collect all contexts
contexts = [entity.context for entity in v.entities]
# Single batch embedding call
all_vectors = await flow("embeddings-request").embed(texts=contexts)
# Pair results with entities
entities = [
EntityEmbeddings(
entity=entity.entity,
vectors=vectors[0], # First vector from the set
chunk_id=entity.chunk_id,
)
for entity, vectors in zip(v.entities, all_vectors)
]
```
#### Fase 4: Actualización del Servicio de Incrustación de Filas
**Actual (Serial):**
```python
for text, (index_name, index_value) in texts_to_embed.items():
vectors = await flow("embeddings-request").embed(text=text)
embeddings_list.append(RowIndexEmbedding(...))
```
**Actualizado (Lote):**
```python
# Collect texts and metadata
texts = list(texts_to_embed.keys())
metadata = list(texts_to_embed.values())
# Single batch embedding call
all_vectors = await flow("embeddings-request").embed(texts=texts)
# Pair results
embeddings_list = [
RowIndexEmbedding(
index_name=meta[0],
index_value=meta[1],
text=text,
vectors=vectors[0] # First vector from the set
)
for text, meta, vectors in zip(texts, metadata, all_vectors)
]
```
#### Fase 5: Mejora de la Herramienta de Línea de Comandos (CLI)
**CLI Actualizada:**
```python
def main():
parser = argparse.ArgumentParser(...)
parser.add_argument(
'text',
nargs='*', # Zero or more texts
help='Text(s) to convert to embedding vectors',
)
parser.add_argument(
'-f', '--file',
help='File containing texts (one per line)',
)
parser.add_argument(
'--batch-size',
type=int,
default=32,
help='Batch size for processing (default: 32)',
)
```
Uso:
```bash
# Single text (existing)
tg-invoke-embeddings "hello world"
# Multiple texts
tg-invoke-embeddings "text one" "text two" "text three"
# From file
tg-invoke-embeddings -f texts.txt --batch-size 64
```
#### Fase 6: Mejora del SDK de Python
**FlowInstance (cliente HTTP):**
```python
class FlowInstance:
def embeddings(self, texts: list[str]) -> list[list[list[float]]]:
"""
Get embeddings for one or more texts.
Args:
texts: List of texts to embed
Returns:
List of vector sets, one per input text
"""
input = {"texts": texts}
return self.request("service/embeddings", input)["vectors"]
```
**SocketFlowInstance (cliente WebSocket):**
```python
class SocketFlowInstance:
def embeddings(self, texts: list[str], **kwargs: Any) -> list[list[list[float]]]:
"""
Get embeddings for one or more texts via WebSocket.
Args:
texts: List of texts to embed
Returns:
List of vector sets, one per input text
"""
request = {"texts": texts}
response = self.client._send_request_sync(
"embeddings", self.flow_id, request, False
)
return response["vectors"]
```
**Ejemplos de uso del SDK:**
```python
# Single text
vectors = flow.embeddings(["hello world"])
print(f"Dimensions: {len(vectors[0][0])}")
# Batch embedding
texts = ["text one", "text two", "text three"]
all_vectors = flow.embeddings(texts)
# Process results
for text, vecs in zip(texts, all_vectors):
print(f"{text}: {len(vecs[0])} dimensions")
```
## Consideraciones de seguridad
**Límites de tamaño de la solicitud**: Aplicar un tamaño máximo de lote para evitar el agotamiento de recursos.
**Manejo de tiempos de espera**: Ajustar los tiempos de espera de manera adecuada para el tamaño del lote.
**Límites de memoria**: Monitorear el uso de memoria para lotes grandes.
**Validación de entrada**: Validar todos los textos en el lote antes de procesarlos.
## Consideraciones de rendimiento
### Mejoras esperadas
**Rendimiento:**
Texto único: ~10-50 textos/segundo (dependiendo del modelo)
Lote (tamaño 32): ~200-500 textos/segundo (mejora de 5 a 10 veces)
**Latencia por texto:**
Texto único: 50-200 ms por texto
Lote (tamaño 32): 5-20 ms por texto (promedio)
**Mejoras específicas del servicio:**
| Servicio | Actual | En lote | Mejora |
|---------|---------|---------|-------------|
| Incrustaciones de grafos (50 entidades) | 5-10 s | 0.5-1 s | 5-10 veces |
| Incrustaciones de filas (100 textos) | 10-20 s | 1-2 s | 5-10 veces |
| Ingestión de documentos (1000 fragmentos) | 100-200 s | 10-30 s | 5-10 veces |
### Parámetros de configuración
```python
# Recommended defaults
DEFAULT_BATCH_SIZE = 32
MAX_BATCH_SIZE = 128
BATCH_TIMEOUT_MULTIPLIER = 2.0
```
## Estrategia de Pruebas
### Pruebas Unitarias
Inserción de texto única (compatibilidad con versiones anteriores)
Manejo de lotes vacíos
Aplicación del tamaño máximo del lote
Manejo de errores para fallas parciales del lote
### Pruebas de Integración
Inserción de lote de extremo a extremo a través de Pulsar
Procesamiento por lotes del servicio de inserción de gráficos
Procesamiento por lotes del servicio de inserción de filas
Punto final de lote de la puerta de enlace de la API
### Pruebas de Rendimiento
Comparación de rendimiento de inserción única frente a inserción por lotes
Uso de memoria con varios tamaños de lote
Análisis de la distribución de la latencia
## Plan de Migración
Esta es una versión que introduce cambios importantes. Todas las fases se implementan juntas.
### Fase 1: Cambios en el Esquema
Reemplazar `text: str` con `texts: list[str]` en EmbeddingsRequest
Cambiar el tipo de `vectors` a `list[list[list[float]]]` en EmbeddingsResponse
### Fase 2: Actualizaciones del Procesador
Actualizar la firma de `on_embeddings` en los procesadores FastEmbed y Ollama
Procesar el lote completo en una única llamada al modelo
### Fase 3: Actualizaciones del Cliente
Actualizar `EmbeddingsClient.embed()` para que acepte `texts: list[str]`
### Fase 4: Actualizaciones del Llamador
Actualizar graph_embeddings para procesar contextos de entidades por lotes
Actualizar row_embeddings para procesar textos de índice por lotes
Actualizar document_embeddings para usar el nuevo esquema
Actualizar la herramienta de línea de comandos
### Fase 5: Puerta de Enlace de la API
Actualizar el punto final de inserción para el nuevo esquema
### Fase 6: SDK de Python
Actualizar la firma de `FlowInstance.embeddings()`
Actualizar la firma de `SocketFlowInstance.embeddings()`
## Preguntas Abiertas
**Inserción de Lotes Grandes en Streaming**: ¿Debemos admitir la transmisión de resultados para lotes muy grandes (>100 textos)?
**Límites Específicos del Proveedor**: ¿Cómo debemos manejar los proveedores con tamaños máximos de lote diferentes?
**Manejo de Fallas Parciales**: Si un texto en un lote falla, ¿deberíamos fallar todo el lote o devolver resultados parciales?
**Inserción por Lotes de Documentos**: ¿Debemos realizar la inserción por lotes en varios mensajes de Chunk o mantener el procesamiento por mensaje?
## Referencias
[Documentación de FastEmbed](https://github.com/qdrant/fastembed)
[API de Inserción de Ollama](https://github.com/ollama/ollama)
[Implementación del Servicio de Inserción](trustgraph-base/trustgraph/base/embeddings_service.py)
[Optimización del Rendimiento de GraphRAG](graphrag-performance-optimization.md)

View file

@ -0,0 +1,269 @@
---
layout: default
title: "Almacenamiento de Grafos de Conocimiento Centrados en Entidades en Cassandra"
parent: "Spanish (Beta)"
---
# Almacenamiento de Grafos de Conocimiento Centrados en Entidades en Cassandra
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
Este documento describe un modelo de almacenamiento para grafos de conocimiento de estilo RDF en Apache Cassandra. El modelo utiliza un enfoque **centrado en entidades** donde cada entidad conoce cada cuadrupla en la que participa y el rol que juega. Esto reemplaza un enfoque tradicional de permutaciones SPO con múltiples tablas con solo dos tablas.
## Antecedentes y Motivación
### El Enfoque Tradicional
Un almacén de cuádruplas RDF estándar en Cassandra requiere múltiples tablas desnormalizadas para cubrir los patrones de consulta, normalmente de 6 o más tablas que representan diferentes permutaciones de Sujeto, Predicado, Objeto y Conjunto de Datos (SPOD). Cada cuadrupla se escribe en cada tabla, lo que resulta en una amplificación de escritura significativa, una sobrecarga operativa y una complejidad del esquema.
Además, la resolución de etiquetas (obtención de nombres legibles para humanos para las entidades) requiere consultas de ida y vuelta separadas, lo que es particularmente costoso en casos de uso de IA y GraphRAG donde las etiquetas son esenciales para el contexto de los LLM.
### La Perspectiva Centrada en Entidades
Cada cuadrupla `(D, S, P, O)` involucra hasta 4 entidades. Al escribir una fila para la participación de cada entidad en la cuadrupla, garantizamos que **cualquier consulta con al menos un elemento conocido golpeará una clave de partición**. Esto cubre los 16 patrones de consulta con una sola tabla de datos.
Beneficios clave:
**2 tablas** en lugar de 7+
**4 escrituras por cuadrupla** en lugar de 6+
**Resolución de etiquetas de forma gratuita**: las etiquetas de una entidad están ubicadas junto con sus relaciones, lo que calienta naturalmente la caché de la aplicación.
**Los 16 patrones de consulta** se sirven mediante lecturas de partición única.
**Operaciones más simples**: una sola tabla de datos para ajustar, compactar y reparar.
## Esquema
### Tabla 1: quads_by_entity
La tabla de datos principal. Cada entidad tiene una partición que contiene todas las cuádruplas en las que participa. Nombrada para reflejar el patrón de consulta (búsqueda por entidad).
Después de la última línea traducida, incluir una línea final que contenga exactamente: [[__END_OF_TRANSLATION__]]
```sql
CREATE TABLE quads_by_entity (
collection text, -- Collection/tenant scope (always specified)
entity text, -- The entity this row is about
role text, -- 'S', 'P', 'O', 'G' — how this entity participates
p text, -- Predicate of the quad
otype text, -- 'U' (URI), 'L' (literal), 'T' (triple/reification)
s text, -- Subject of the quad
o text, -- Object of the quad
d text, -- Dataset/graph of the quad
dtype text, -- XSD datatype (when otype = 'L'), e.g. 'xsd:string'
lang text, -- Language tag (when otype = 'L'), e.g. 'en', 'fr'
PRIMARY KEY ((collection, entity), role, p, otype, s, o, d, dtype, lang)
);
```
**Clave de partición**: `(collection, entity)` — con alcance a la colección, una partición por entidad.
**Justificación del orden de las columnas de clustering**:
1. **role** — la mayoría de las consultas comienzan con "dónde está esta entidad como sujeto/objeto".
2. **p** — el siguiente filtro más común, "muéstrame todas las relaciones `knows`".
3. **otype** — permite filtrar por relaciones con valores URI frente a relaciones con valores literales.
4. **s, o, d** — columnas restantes para la unicidad.
5. **dtype, lang** — distingue literales con el mismo valor pero con diferentes metadatos de tipo (por ejemplo, `"thing"` vs `"thing"@en` vs `"thing"^^xsd:string`).
### Tabla 2: quads_by_collection
Soporta consultas y eliminaciones a nivel de colección. Proporciona un manifiesto de todos los cuads pertenecientes a una colección. Nombrado para reflejar el patrón de consulta (búsqueda por colección).
```sql
CREATE TABLE quads_by_collection (
collection text,
d text, -- Dataset/graph of the quad
s text, -- Subject of the quad
p text, -- Predicate of the quad
o text, -- Object of the quad
otype text, -- 'U' (URI), 'L' (literal), 'T' (triple/reification)
dtype text, -- XSD datatype (when otype = 'L')
lang text, -- Language tag (when otype = 'L')
PRIMARY KEY (collection, d, s, p, o, otype, dtype, lang)
);
```
Agrupados primero por conjunto de datos, lo que permite la eliminación a nivel de colección o de conjunto de datos. Las columnas `otype`, `dtype` y `lang` se incluyen en la clave de agrupación para distinguir literales con el mismo valor pero con diferentes metadatos de tipo; en RDF, `"thing"`, `"thing"@en` y `"thing"^^xsd:string` son valores semánticamente distintos.
## Ruta de escritura
Para cada cuadruple entrante `(D, S, P, O)` dentro de una colección `C`, escriba **4 filas** en `quads_by_entity` y **1 fila** en `quads_by_collection`.
### Ejemplo
Dado el cuadruple en la colección `tenant1`:
```
Dataset: https://example.org/graph1
Subject: https://example.org/Alice
Predicate: https://example.org/knows
Object: https://example.org/Bob
```
Escriba 4 filas en `quads_by_entity`:
| collection | entity | role | p | otype | s | o | d |
|---|---|---|---|---|---|---|---|
| tenant1 | https://example.org/graph1 | G | https://example.org/knows | U | https://example.org/Alice | https://example.org/Bob | https://example.org/graph1 |
| tenant1 | https://example.org/Alice | S | https://example.org/knows | U | https://example.org/Alice | https://example.org/Bob | https://example.org/graph1 |
| tenant1 | https://example.org/knows | P | https://example.org/knows | U | https://example.org/Alice | https://example.org/Bob | https://example.org/graph1 |
| tenant1 | https://example.org/Bob | O | https://example.org/knows | U | https://example.org/Alice | https://example.org/Bob | https://example.org/graph1 |
Escriba 1 fila en `quads_by_collection`:
| collection | d | s | p | o | otype | dtype | lang |
|---|---|---|---|---|---|---|---|
| tenant1 | https://example.org/graph1 | https://example.org/Alice | https://example.org/knows | https://example.org/Bob | U | | |
### Ejemplo Literal
Para una tripleta de etiqueta:
```
Dataset: https://example.org/graph1
Subject: https://example.org/Alice
Predicate: http://www.w3.org/2000/01/rdf-schema#label
Object: "Alice Smith" (lang: en)
```
El `otype` es `'L'`, `dtype` es `'xsd:string'`, y `lang` es `'en'`. El valor literal `"Alice Smith"` se almacena en `o`. Solo se necesitan 3 filas en `quads_by_entity`; no se escribe ninguna fila para el literal como entidad, ya que los literales no son entidades consultables de forma independiente.
## Patrones de consulta
### Los 16 patrones DSPO
En la tabla a continuación, "Prefijo perfecto" significa que la consulta utiliza un prefijo contiguo de las columnas de clustering. "Escaneo de partición + filtro" significa que Cassandra lee una porción de una partición y filtra en memoria; es eficiente, pero no es una coincidencia de prefijo pura.
| # | Conocido | Búsqueda de entidad | Prefijo de clustering | Eficiencia |
|---|---|---|---|---|
| 1 | D,S,P,O | entidad=S, rol='S', p=P | Coincidencia completa | Prefijo perfecto |
| 2 | D,S,P,? | entidad=S, rol='S', p=P | Filtro en D | Escaneo de partición + filtro |
| 3 | D,S,?,O | entidad=S, rol='S' | Filtro en D, O | Escaneo de partición + filtro |
| 4 | D,?,P,O | entidad=O, rol='O', p=P | Filtro en D | Escaneo de partición + filtro |
| 5 | ?,S,P,O | entidad=S, rol='S', p=P | Filtro en O | Escaneo de partición + filtro |
| 6 | D,S,?,? | entidad=S, rol='S' | Filtro en D | Escaneo de partición + filtro |
| 7 | D,?,P,? | entidad=P, rol='P' | Filtro en D | Escaneo de partición + filtro |
| 8 | D,?,?,O | entidad=O, rol='O' | Filtro en D | Escaneo de partición + filtro |
| 9 | ?,S,P,? | entidad=S, rol='S', p=P | — | **Prefijo perfecto** |
| 10 | ?,S,?,O | entidad=S, rol='S' | Filtro en O | Escaneo de partición + filtro |
| 11 | ?,?,P,O | entidad=O, rol='O', p=P | — | **Prefijo perfecto** |
| 12 | D,?,?,? | entidad=D, rol='G' | — | **Prefijo perfecto** |
| 13 | ?,S,?,? | entidad=S, rol='S' | — | **Prefijo perfecto** |
| 14 | ?,?,P,? | entidad=P, rol='P' | — | **Prefijo perfecto** |
| 15 | ?,?,?,O | entidad=O, rol='O' | — | **Prefijo perfecto** |
| 16 | ?,?,?,? | — | Escaneo completo | Exploración solo |
**Resultado clave**: 7 de los 15 patrones no triviales son coincidencias perfectas de prefijo de clustering. Los 8 restantes son lecturas de partición única con filtrado dentro de la partición. Cada consulta con al menos un elemento conocido coincide con una clave de partición.
El patrón 16 (?,?,?,?) no ocurre en la práctica, ya que la colección siempre se especifica, lo que lo reduce al patrón 12.
### Ejemplos comunes de consultas
**Todo sobre una entidad:**
```sql
SELECT * FROM quads_by_entity
WHERE collection = 'tenant1' AND entity = 'https://example.org/Alice';
```
**Todas las relaciones de salida para una entidad:**
```sql
SELECT * FROM quads_by_entity
WHERE collection = 'tenant1' AND entity = 'https://example.org/Alice'
AND role = 'S';
```
**Predicado específico para una entidad:**
```sql
SELECT * FROM quads_by_entity
WHERE collection = 'tenant1' AND entity = 'https://example.org/Alice'
AND role = 'S' AND p = 'https://example.org/knows';
```
**Etiqueta para una entidad (idioma específico):**
```sql
SELECT * FROM quads_by_entity
WHERE collection = 'tenant1' AND entity = 'https://example.org/Alice'
AND role = 'S' AND p = 'http://www.w3.org/2000/01/rdf-schema#label'
AND otype = 'L';
```
Luego, filtre según la aplicación `lang = 'en'`, si es necesario.
**Solo relaciones con valores URI (enlaces de entidad a entidad):**
```sql
SELECT * FROM quads_by_entity
WHERE collection = 'tenant1' AND entity = 'https://example.org/Alice'
AND role = 'S' AND p = 'https://example.org/knows' AND otype = 'U';
```
**Búsqueda inversa: ¿a qué entidades hace referencia esta?**
```sql
SELECT * FROM quads_by_entity
WHERE collection = 'tenant1' AND entity = 'https://example.org/Bob'
AND role = 'O';
```
## Resolución de etiquetas y calentamiento de la caché
Una de las ventajas más significativas del modelo centrado en entidades es que **la resolución de etiquetas se convierte en un efecto secundario gratuito**.
En el modelo tradicional de múltiples tablas, recuperar etiquetas requiere consultas separadas: recuperar triples, identificar los URI de las entidades en los resultados y luego recuperar `rdfs:label` para cada uno. Este patrón N+1 es costoso.
En el modelo centrado en entidades, consultar una entidad devuelve **todos** sus cuads, incluidas sus etiquetas, tipos y otras propiedades. Cuando la aplicación almacena en caché los resultados de las consultas, las etiquetas se precalientan antes de que alguien las solicite.
Dos regímenes de uso confirman que esto funciona bien en la práctica:
**Consultas orientadas al usuario**: conjuntos de resultados naturalmente pequeños, las etiquetas son esenciales. Las lecturas de entidades precalientan la caché.
**Consultas de IA/masivas**: conjuntos de resultados grandes con límites estrictos. Las etiquetas son innecesarias o se necesitan solo para un subconjunto curado de entidades que ya están en la caché.
La preocupación teórica de resolver etiquetas para conjuntos de resultados enormes (por ejemplo, 30 000 entidades) se mitiga por la observación práctica de que ningún consumidor humano o de IA procesa útilmente tantas etiquetas. Los límites de consulta a nivel de aplicación garantizan que la presión de la caché siga siendo manejable.
## Particiones anchas y reificación
La reificación (declaraciones RDF-star sobre declaraciones) crea entidades de centro, por ejemplo, un documento de origen que admite miles de hechos extraídos. Esto puede producir particiones anchas.
Factores atenuantes:
**Límites de consulta a nivel de aplicación**: todas las consultas de GraphRAG y orientadas al usuario imponen límites estrictos, por lo que las particiones anchas nunca se escanean completamente en la ruta de lectura activa.
**Cassandra maneja las lecturas parciales de manera eficiente**: un escaneo de columna de agrupación con una parada temprana es rápido incluso en particiones grandes.
**Eliminación de colecciones** (la única operación que podría recorrer particiones completas) es un proceso de segundo plano aceptable.
## Eliminación de colecciones
Se activa mediante una llamada a la API, se ejecuta en segundo plano (consistencia eventual).
1. Leer `quads_by_collection` para la colección de destino para obtener todos los cuads.
2. Extraer entidades únicas de los cuads (valores s, p, o, d).
3. Para cada entidad única, eliminar la partición de `quads_by_entity`.
4. Eliminar las filas de `quads_by_collection`.
La tabla `quads_by_collection` proporciona el índice necesario para localizar todas las particiones de entidades sin un escaneo completo de la tabla. Las eliminaciones a nivel de partición son eficientes porque `(collection, entity)` es la clave de partición.
## Ruta de migración desde el modelo de múltiples tablas
El modelo centrado en entidades puede coexistir con el modelo de múltiples tablas existente durante la migración:
1. Implementar las tablas `quads_by_entity` y `quads_by_collection` junto con las tablas existentes.
2. Escribir duplicados nuevos cuads tanto en las tablas antiguas como en las nuevas.
3. Rellenar los datos existentes en las nuevas tablas.
4. Migrar las rutas de consulta un patrón a la vez.
5. Desactivar las tablas antiguas una vez que todas las lecturas se hayan migrado.
## Resumen
| Aspecto | Tradicional (6 tablas) | Centrado en entidades (2 tablas) |
|---|---|---|
| Tablas | 7+ | 2 |
| Escrituras por quad | 6+ | 5 (4 datos + 1 manifiesto) |
| Resolución de etiquetas | Viajes separados | Gratuito a través del calentamiento de la caché |
| Patrones de consulta | 16 en 6 tablas | 16 en 1 tabla |
| Complejidad del esquema | Alto | Bajo |
| Sobrecarga operativa | 6 tablas para ajustar/reparar | 1 tabla de datos |
| Soporte de reificación | Complejidad adicional | Ajuste natural |
| Filtrado de tipo de objeto | No disponible | Nativo (a través de la agrupación de otype) |
Después de la última línea traducida, incluir una línea final que contenga exactamente: [[__END_OF_TRANSLATION__]]

View file

@ -0,0 +1,188 @@
---
layout: default
title: "Especificación Técnica de la CLI para Explicabilidad"
parent: "Spanish (Beta)"
---
# Especificación Técnica de la CLI para Explicabilidad
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Estado
Borrador
## Visión General
Esta especificación describe las herramientas de CLI para depurar y explorar datos de explicabilidad en TrustGraph. Estas herramientas permiten a los usuarios rastrear cómo se derivaron las respuestas y depurar la cadena de origen desde los bordes hasta los documentos fuente.
Tres herramientas de CLI:
1. **`tg-show-document-hierarchy`** - Muestra la jerarquía documento → página → fragmento → borde
2. **`tg-list-explain-traces`** - Lista todas las sesiones GraphRAG con preguntas
3. **`tg-show-explain-trace`** - Muestra la trazabilidad completa de explicabilidad para una sesión
## Objetivos
- **Depuración**: Permitir a los desarrolladores inspeccionar los resultados del procesamiento de documentos.
- **Auditabilidad**: Rastrear cualquier hecho extraído hasta su documento fuente.
- **Transparencia**: Mostrar exactamente cómo GraphRAG derivó una respuesta.
- **Usabilidad**: Interfaz CLI sencilla con valores predeterminados apropiados.
## Antecedentes
TrustGraph tiene dos sistemas de origen:
1. **Origen en tiempo de extracción** (ver `extraction-time-provenance.md`): Registra las relaciones documento → página → fragmento → borde durante la ingestión. Almacenado en el gráfico llamado `urn:graph:source` utilizando `prov:wasDerivedFrom`.
2. **Explicabilidad en tiempo de consulta** (ver `query-time-explainability.md`): Registra la cadena de pregunta → exploración → enfoque → síntesis durante las consultas GraphRAG. Almacenado en el gráfico llamado `urn:graph:retrieval`.
Limitaciones actuales:
- No hay una manera fácil de visualizar la jerarquía del documento después del procesamiento.
- Se deben consultar manualmente los triples para ver los datos de explicabilidad.
- No hay una vista consolidada de una sesión GraphRAG.
## Diseño Técnico
### Herramienta 1: `tg-show-document-hierarchy`
**Propósito**: Dado un ID de documento, recorrer y mostrar todas las entidades derivadas.
**Uso**:
```bash
tg-show-document-hierarchy "urn:trustgraph:doc:abc123"
tg-show-document-hierarchy --show-content --max-content 500 "urn:trustgraph:doc:abc123"
```
**Argumentos**:
| Arg | Descripción |
|---|---|
| `document_id` | URI del documento (posicional) |
| `-u/--api-url` | URL del gateway (por defecto: `$TRUSTGRAPH_URL`) |
| `-t/--token` | Token de autenticación (por defecto: `$TRUSTGRAPH_TOKEN`) |
| `-U/--user` | ID de usuario (por defecto: `trustgraph`) |
| `-C/--collection` | Colección (por defecto: `default`) |
| `--show-content` | Incluir el contenido del blob/documento |
| `--max-content` | Máx. caracteres por blob (por defecto: 200) |
| `--format` | Salida: `tree` (por defecto), `json` |
**Implementación**:
1. Consultar triples: `?child prov:wasDerivedFrom <document_id>` en `urn:graph:source`
2. Consultar recursivamente los hijos de cada resultado
3. Construir la estructura del árbol: Documento → Páginas → Fragmentos
4. Si `--show-content`, recuperar el contenido de la API del bibliotecario
5. Mostrar como árbol anidado o JSON
**Ejemplo de Salida**:
```
Document: urn:trustgraph:doc:abc123
Título: "Sample PDF"
Tipo: application/pdf
└── Página 1: urn:trustgraph:doc:abc123/p1
├── Fragmento 0: urn:trustgraph:doc:abc123/p1/c0
Contenido: "The quick brown fox..." [truncado]
└── Fragmento 1: urn:trustgraph:doc:abc123/p1/c1
Contenido: "Machine learning is..." [truncado]
```
### Herramienta 2: `tg-list-explain-traces`
**Propósito**: Listar todas las sesiones GraphRAG (preguntas) en una colección.
**Uso**:
```bash
tg-list-explain-traces
tg-list-explain-traces --limit 20 --format json
```
**Argumentos**:
| Arg | Descripción |
|---|---|
| `-u/--api-url` | URL del gateway |
| `-t/--token` | Token de autenticación |
| `-U/--user` | ID de usuario |
| `-C/--collection` | Colección |
| `--limit` | Máx. resultados (por defecto: 50) |
| `--format` | Salida: `table` (por defecto), `json` |
**Implementación**:
1. Consultar: `?session tg:query ?text` en `urn:graph:retrieval`
2. Consultar los tiempos: `?session prov:startedAtTime ?time`
3. Mostrar como tabla
**Ejemplo de Salida**:
```
Session ID | Pregunta | Tiempo
----------------------------------------------|--------------------------------|---------------------
urn:trustgraph:question:abc123 | ¿Cuál fue la Guerra Fría? | 2024-01-15 10:30:00
urn:trustgraph:question:def456 | ¿Quién fundó la NASA? | 2024-01-15 09:15:00
```
### Herramienta 3: `tg-show-explain-trace`
**Propósito**: Mostrar la trazabilidad completa de explicabilidad para una sesión GraphRAG.
**Uso**:
```bash
tg-show-explain-trace "urn:trustgraph:question:abc123"
tg-show-explain-trace --max-answer 1000 --show-provenance "urn:trustgraph:question:abc123"
```
**Argumentos**:
| Arg | Descripción |
|---|---|
| `question_id` | URI de la pregunta (posicional) |
| `-u/--api-url` | URL del gateway |
| `-t/--token` | Token de autenticación |
| `-U/--user` | ID de usuario |
| `-C/--collection` | Colección |
| `--max-answer` | Máx. caracteres para la respuesta (por defecto: 500) |
| `--show-provenance` | Rastrear los bordes hasta los documentos fuente |
| `--format` | Salida: `text` (por defecto), `json` |
**Implementación**:
1. Obtener el texto de la pregunta del predicado `tg:query`
2. Encontrar la exploración: `?exp prov:wasGeneratedBy <question_id>`
3. Encontrar el enfoque: `?focus prov:wasDerivedFrom <exploration_id>`
4. Obtener los bordes seleccionados: `<focus_id> tg:selectedEdge ?edge`
5. Para cada borde, obtener `tg:edge` (triple acotado) y `tg:reasoning`
6. Encontrar la síntesis: `?synth prov:wasDerivedFrom <focus_id>`
7. Obtener la respuesta del documento a través de la API del bibliotecario
8. Si `--show-provenance`, rastrear los bordes hasta los documentos fuente
**Ejemplo de Salida**:
```
=== Sesión GraphRAG: urn:trustgraph:question:abc123 ===
Pregunta: ¿Cuál fue la Guerra Fría?
Tiempo: 2024-01-15 10:30:00
--- Exploración ---
Se recuperaron 50 bordes de la gráfica de conocimiento
--- Enfoque (Selección de Bordes) ---
Se seleccionaron 12 bordes:
1. "The Cold War"
2. "Yuri Andropov"
3. "Khrushchev"
4. "NATO"
5. "Warsaw Pact"
6. "Berlin Wall"
7. "Cuban Missile Crisis"
8. "Detente"
9. "Détente"
10. "Joseph Stalin"
11. "Nikita Khrushchev"
12. "US-Soviet Relations"
--- Trazabilidad ---
```
## Referencias
- CLI de explicabilidad: `docs/tech-specs/query-time-explainability.md`
- Origen en tiempo de extracción: `docs/tech-specs/extraction-time-provenance.md`
- Ejemplo de CLI: `trustgraph-cli/trustgraph/cli/invoke_graph_rag.py`

View file

@ -0,0 +1,355 @@
---
layout: default
title: "Flujos de extracción"
parent: "Spanish (Beta)"
---
# Flujos de extracción
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
Este documento describe cómo los datos fluyen a través de la canalización de extracción de TrustGraph, desde la presentación del documento hasta el almacenamiento en almacenes de conocimiento.
## Resumen
```
┌──────────┐ ┌─────────────┐ ┌─────────┐ ┌────────────────────┐
│ Librarian│────▶│ PDF Decoder │────▶│ Chunker │────▶│ Knowledge │
│ │ │ (PDF only) │ │ │ │ Extraction │
│ │────────────────────────▶│ │ │ │
└──────────┘ └─────────────┘ └─────────┘ └────────────────────┘
│ │
│ ├──▶ Triples
│ ├──▶ Entity Contexts
│ └──▶ Rows
└──▶ Document Embeddings
```
## Almacenamiento de contenido
### Almacenamiento de objetos (S3/Minio)
El contenido de los documentos se almacena en un almacenamiento de objetos compatible con S3:
Formato de la ruta: `doc/{object_id}` donde object_id es un UUID
Todos los tipos de documentos se almacenan aquí: documentos fuente, páginas, fragmentos
### Almacenamiento de metadatos (Cassandra)
Los metadatos de los documentos almacenados en Cassandra incluyen:
ID del documento, título, tipo (MIME)
Referencia al almacenamiento de objetos `object_id`
Referencia `parent_id` para documentos secundarios (páginas, fragmentos)
`document_type`: "fuente", "página", "fragmento", "respuesta"
### Umbral de contenido en línea frente a transmisión
La transmisión de contenido utiliza una estrategia basada en el tamaño:
**< 2MB**: El contenido se incluye directamente en el mensaje (codificado en base64)
**≥ 2MB**: Solo se envía `document_id`; el procesador recupera el contenido a través de la API del bibliotecario
## Etapa 1: Envío de documentos (Bibliotecario)
### Punto de entrada
Los documentos ingresan al sistema a través de la operación `add-document` del bibliotecario:
1. El contenido se carga en el almacenamiento de objetos
2. Se crea un registro de metadatos en Cassandra
3. Devuelve el ID del documento
### Activación de la extracción
La operación `add-processing` activa la extracción:
Especifica `document_id`, `flow` (ID de la canalización), `collection` (almacén de destino)
La operación `load_document()` del bibliotecario recupera el contenido y lo publica en la cola de entrada del flujo
### Esquema: Documento
```
Document
├── metadata: Metadata
│ ├── id: str # Document identifier
│ ├── user: str # Tenant/user ID
│ ├── collection: str # Target collection
│ └── metadata: list[Triple] # (largely unused, historical)
├── data: bytes # PDF content (base64, if inline)
└── document_id: str # Librarian reference (if streaming)
```
**Enrutamiento**: Basado en el campo `kind`:
`application/pdf` → cola `document-load` → Decodificador de PDF
`text/plain` → cola `text-load` → Fragmentador
## Etapa 2: Decodificador de PDF
Convierte documentos PDF en páginas de texto.
### Proceso
1. Obtener contenido (inline `data` o a través de `document_id` desde el bibliotecario)
2. Extraer páginas utilizando PyPDF
3. Para cada página:
Guardar como documento hijo en el bibliotecario (`{doc_id}/p{page_num}`)
Emitir triples de procedencia (página derivada del documento)
Enviar al fragmentador
### Esquema: TextDocument
```
TextDocument
├── metadata: Metadata
│ ├── id: str # Page URI (e.g., https://trustgraph.ai/doc/xxx/p1)
│ ├── user: str
│ ├── collection: str
│ └── metadata: list[Triple]
├── text: bytes # Page text content (if inline)
└── document_id: str # Librarian reference (e.g., "doc123/p1")
```
## Etapa 3: Fragmentador
Divide el texto en fragmentos de un tamaño configurado.
### Parámetros (configurables)
`chunk_size`: Tamaño de fragmento objetivo en caracteres (predeterminado: 2000)
`chunk_overlap`: Solapamiento entre fragmentos (predeterminado: 100)
### Proceso
1. Obtener el contenido del texto (en línea o a través del bibliotecario)
2. Dividir utilizando un divisor de caracteres recursivo
3. Para cada fragmento:
Guardar como documento hijo en el bibliotecario (`{parent_id}/c{index}`)
Emitir triples de procedencia (fragmento derivado de página/documento)
Enviar a los procesadores de extracción
### Esquema: Fragmento
```
Chunk
├── metadata: Metadata
│ ├── id: str # Chunk URI
│ ├── user: str
│ ├── collection: str
│ └── metadata: list[Triple]
├── chunk: bytes # Chunk text content
└── document_id: str # Librarian chunk ID (e.g., "doc123/p1/c3")
```
### Jerarquía de Identificadores de Documentos
Los documentos secundarios codifican su linaje en el identificador:
Fuente: `doc123`
Página: `doc123/p5`
Fragmento de página: `doc123/p5/c2`
Fragmento de texto: `doc123/c2`
## Etapa 4: Extracción de Conocimiento
Múltiples patrones de extracción disponibles, seleccionados por la configuración del flujo.
### Patrón A: Basic GraphRAG
Dos procesadores paralelos:
**kg-extract-definitions**
Entrada: Fragmento
Salida: Triples (definiciones de entidades), EntityContexts
Extrae: etiquetas de entidades, definiciones
**kg-extract-relationships**
Entrada: Fragmento
Salida: Triples (relaciones), EntityContexts
Extrae: relaciones sujeto-predicado-objeto
### Patrón B: Basado en Ontología (kg-extract-ontology)
Entrada: Fragmento
Salida: Triples, EntityContexts
Utiliza una ontología configurada para guiar la extracción
### Patrón C: Basado en Agente (kg-extract-agent)
Entrada: Fragmento
Salida: Triples, EntityContexts
Utiliza un marco de agente para la extracción
### Patrón D: Extracción de Filas (kg-extract-rows)
Entrada: Fragmento
Salida: Filas (datos estructurados, no triples)
Utiliza una definición de esquema para extraer registros estructurados
### Esquema: Triples
```
Triples
├── metadata: Metadata
│ ├── id: str
│ ├── user: str
│ ├── collection: str
│ └── metadata: list[Triple] # (set to [] by extractors)
└── triples: list[Triple]
└── Triple
├── s: Term # Subject
├── p: Term # Predicate
├── o: Term # Object
└── g: str | None # Named graph
```
### Esquema: EntityContexts
```
EntityContexts
├── metadata: Metadata
└── entities: list[EntityContext]
└── EntityContext
├── entity: Term # Entity identifier (IRI)
├── context: str # Textual description for embedding
└── chunk_id: str # Source chunk ID (provenance)
```
### Esquema: Filas
```
Rows
├── metadata: Metadata
├── row_schema: RowSchema
│ ├── name: str
│ ├── description: str
│ └── fields: list[Field]
└── rows: list[dict[str, str]] # Extracted records
```
## Etapa 5: Generación de Incrustaciones (Embeddings)
### Incrustaciones de Grafos (Graph Embeddings)
Convierte los contextos de las entidades en incrustaciones vectoriales.
**Proceso:**
1. Recibir EntityContexts (Contextos de Entidades)
2. Llamar al servicio de incrustaciones con el texto del contexto
3. Salida: GraphEmbeddings (mapeo de entidad a vector)
**Esquema: GraphEmbeddings**
```
GraphEmbeddings
├── metadata: Metadata
└── entities: list[EntityEmbeddings]
└── EntityEmbeddings
├── entity: Term # Entity identifier
├── vector: list[float] # Embedding vector
└── chunk_id: str # Source chunk (provenance)
```
### Incrustaciones de documentos
Convierte texto de fragmentos directamente en incrustaciones vectoriales.
**Proceso:**
1. Recibir fragmento
2. Llamar al servicio de incrustaciones con el texto del fragmento
3. Salida: Incrustaciones de documentos
**Esquema: Incrustaciones de documentos**
```
DocumentEmbeddings
├── metadata: Metadata
└── chunks: list[ChunkEmbeddings]
└── ChunkEmbeddings
├── chunk_id: str # Chunk identifier
└── vector: list[float] # Embedding vector
```
### Incrustaciones de filas
Convierte los campos de índice de fila en incrustaciones vectoriales.
**Proceso:**
1. Recibir filas
2. Incrustar campos de índice configurados
3. Salida a la tienda de vectores de filas
## Etapa 6: Almacenamiento
### Triple Store
Recibe: Triples
Almacenamiento: Cassandra (tablas centradas en entidades)
Los grafos con nombre separan el conocimiento central de la procedencia:
`""` (predeterminado): Hechos de conocimiento central
`urn:graph:source`: Procedencia de extracción
`urn:graph:retrieval`: Explicabilidad en tiempo de consulta
### Tienda de vectores (Incrustaciones de grafos)
Recibe: GraphEmbeddings
Almacenamiento: Qdrant, Milvus o Pinecone
Indexado por: IRI de entidad
Metadatos: chunk_id para la procedencia
### Tienda de vectores (Incrustaciones de documentos)
Recibe: DocumentEmbeddings
Almacenamiento: Qdrant, Milvus o Pinecone
Indexado por: chunk_id
### Tienda de filas
Recibe: Filas
Almacenamiento: Cassandra
Estructura de tabla basada en esquema
### Tienda de vectores de filas
Recibe: Incrustaciones de filas
Almacenamiento: Base de datos de vectores
Indexado por: Campos de índice de fila
## Análisis de campos de metadatos
### Campos utilizados activamente
| Campo | Uso |
|-------|-------|
| `metadata.id` | Identificador de documento/fragmento, registro, procedencia |
| `metadata.user` | Multitenencia, enrutamiento de almacenamiento |
| `metadata.collection` | Selección de colección de destino |
| `document_id` | Referencia de bibliotecario, enlace de procedencia |
| `chunk_id` | Seguimiento de la procedencia a través de la canalización |
<<<<<<< HEAD
### Campos potencialmente redundantes
| Campo | Estado |
|-------|--------|
| `metadata.metadata` | Establecido en `[]` por todos los extractores; los metadatos a nivel de documento ahora se gestionan por el bibliotecario en el momento de la presentación |
=======
### Campos eliminados
| Campo | Estado |
|-------|--------|
| `metadata.metadata` | Eliminado de la clase `Metadata`. Los triples de metadatos a nivel de documento ahora se emiten directamente por el bibliotecario a la triple store en el momento de la presentación, y no se transmiten a través de la canalización de extracción. |
>>>>>>> e3bcbf73 (The metadata field (list of triples) in the pipeline Metadata class)
### Patrón de campos de bytes
Todos los campos de contenido (`data`, `text`, `chunk`) son `bytes`, pero se decodifican inmediatamente a cadenas UTF-8 por todos los procesadores. Ningún procesador utiliza bytes sin procesar.
## Configuración del flujo
Los flujos se definen externamente y se proporcionan al bibliotecario a través del servicio de configuración. Cada flujo especifica:
Colas de entrada (`text-load`, `document-load`)
Cadena de procesadores
Parámetros (tamaño del fragmento, método de extracción, etc.)
Patrones de flujo de ejemplo:
`pdf-graphrag`: PDF → Decodificador → Fragmentador → Definiciones + Relaciones → Incrustaciones
`text-graphrag`: Texto → Fragmentador → Definiciones + Relaciones → Incrustaciones
`pdf-ontology`: PDF → Decodificador → Fragmentador → Extracción de ontología → Incrustaciones
`text-rows`: Texto → Fragmentador → Extracción de filas → Tienda de filas

View file

@ -0,0 +1,213 @@
---
layout: default
title: "Origen de la extracción: Modelo de subgrafo"
parent: "Spanish (Beta)"
---
# Origen de la extracción: Modelo de subgrafo
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Problema
Actualmente, la generación de la procedencia en tiempo de extracción crea una reificación completa por cada
triple extraído: un `stmt_uri`, `activity_uri` y metadatos PROV-O asociados para cada hecho de conocimiento. El procesamiento de un bloque
que produce 20 relaciones genera aproximadamente 220 triples de procedencia además de los aproximadamente 20 triples de conocimiento, lo que supone una sobrecarga de aproximadamente 10:1.
Esto es costoso (almacenamiento, indexación, transmisión) y semánticamente
inexacto. Cada bloque se procesa mediante una única llamada a un LLM que produce
todos sus triples en una sola transacción. El modelo actual, que es por triple,
oscurece esto al crear la ilusión de 20 eventos de extracción independientes.
Además, dos de los cuatro procesadores de extracción (kg-extract-ontology,
kg-extract-agent) no tienen ninguna procedencia, lo que deja lagunas en el
registro de auditoría.
## Solución
Reemplazar la reificación por triple con un **modelo de subgrafo**: un único
registro de procedencia por extracción de bloque, compartido entre todos los triples producidos a partir de ese
bloque.
### Cambio de terminología
| Antiguo | Nuevo |
|-----|-----|
| `stmt_uri` (`https://trustgraph.ai/stmt/{uuid}`) | `subgraph_uri` (`https://trustgraph.ai/subgraph/{uuid}`) |
| `statement_uri()` | `subgraph_uri()` |
| `tg:reifies` (1:1, identidad) | `tg:contains` (1:muchos, contención) |
### Estructura objetivo
Todos los triples de procedencia se almacenan en el grafo con nombre `urn:graph:source`.
```
# Subgraph contains each extracted triple (RDF-star quoted triples)
<subgraph> tg:contains <<s1 p1 o1>> .
<subgraph> tg:contains <<s2 p2 o2>> .
<subgraph> tg:contains <<s3 p3 o3>> .
# Derivation from source chunk
<subgraph> prov:wasDerivedFrom <chunk_uri> .
<subgraph> prov:wasGeneratedBy <activity> .
# Activity: one per chunk extraction
<activity> rdf:type prov:Activity .
<activity> rdfs:label "{component_name} extraction" .
<activity> prov:used <chunk_uri> .
<activity> prov:wasAssociatedWith <agent> .
<activity> prov:startedAtTime "2026-03-13T10:00:00Z" .
<activity> tg:componentVersion "0.25.0" .
<activity> tg:llmModel "gpt-4" . # if available
<activity> tg:ontology <ontology_uri> . # if available
# Agent: stable per component
<agent> rdf:type prov:Agent .
<agent> rdfs:label "{component_name}" .
```
### Comparación de volúmenes
Para un bloque que produce N triples extraídos:
| | Viejo (por triple) | Nuevo (subgrafo) |
|---|---|---|
| `tg:contains` / `tg:reifies` | N | N |
| Triples de actividad | ~9 x N | ~9 |
| Triples de agente | 2 x N | 2 |
| Metadatos de declaración/subgrafo | 2 x N | 2 |
| **Total de triples de procedencia** | **~13N** | **N + 13** |
| **Ejemplo (N=20)** | **~260** | **33** |
## Alcance
### Procesadores a Actualizar (procedencia existente, por triple)
**kg-extract-definitions**
(`trustgraph-flow/trustgraph/extract/kg/definitions/extract.py`)
Actualmente llama a `statement_uri()` + `triple_provenance_triples()` dentro
del bucle por definición.
Cambios:
Mover la creación de `subgraph_uri()` y `activity_uri()` antes del bucle
Recolectar los triples `tg:contains` dentro del bucle
Emitir el bloque compartido de actividad/agente una vez después del bucle
**kg-extract-relationships**
(`trustgraph-flow/trustgraph/extract/kg/relationships/extract.py`)
Mismo patrón que definiciones. Mismos cambios.
### Procesadores a Agregar Procedencia (actualmente ausente)
**kg-extract-ontology**
(`trustgraph-flow/trustgraph/extract/kg/ontology/extract.py`)
Actualmente emite triples sin procedencia. Agregar procedencia de subgrafo
utilizando el mismo patrón: un subgrafo por bloque, `tg:contains` para cada
triple extraído.
**kg-extract-agent**
(`trustgraph-flow/trustgraph/extract/kg/agent/extract.py`)
Actualmente emite triples sin procedencia. Agregar procedencia de subgrafo
utilizando el mismo patrón.
### Cambios en la Biblioteca Compartida de Procedencia
**`trustgraph-base/trustgraph/provenance/triples.py`**
Reemplazar `triple_provenance_triples()` con `subgraph_provenance_triples()`
Nueva función acepta una lista de triples extraídos en lugar de uno solo
Genera un `tg:contains` por triple, bloque compartido de actividad/agente
Eliminar el `triple_provenance_triples()` antiguo
**`trustgraph-base/trustgraph/provenance/uris.py`**
Reemplazar `statement_uri()` con `subgraph_uri()`
**`trustgraph-base/trustgraph/provenance/namespaces.py`**
Reemplazar `TG_REIFIES` con `TG_CONTAINS`
### No Incluido en el Alcance
**kg-extract-topics**: procesador de estilo antiguo, no se utiliza actualmente en
flujos estándar
**kg-extract-rows**: produce filas no triples, modelo de procedencia
diferente
**Procedencia en tiempo de consulta** (`urn:graph:retrieval`): preocupación separada,
ya utiliza un patrón diferente (pregunta/exploración/énfasis/síntesis)
**Procedencia de documento/página/bloque** (decodificador de PDF, segmentador): ya utiliza
`derived_entity_triples()` que es por entidad, no por triple — no hay
problema de redundancia
## Notas de Implementación
### Reestructuración del Bucle del Procesador
Antes (por triple, en relaciones):
```python
for rel in rels:
# ... build relationship_triple ...
stmt_uri = statement_uri()
prov_triples = triple_provenance_triples(
stmt_uri=stmt_uri,
extracted_triple=relationship_triple,
...
)
triples.extend(set_graph(prov_triples, GRAPH_SOURCE))
```
Después (subgrafo):
```python
sg_uri = subgraph_uri()
for rel in rels:
# ... build relationship_triple ...
extracted_triples.append(relationship_triple)
prov_triples = subgraph_provenance_triples(
subgraph_uri=sg_uri,
extracted_triples=extracted_triples,
chunk_uri=chunk_uri,
component_name=default_ident,
component_version=COMPONENT_VERSION,
llm_model=llm_model,
ontology_uri=ontology_uri,
)
triples.extend(set_graph(prov_triples, GRAPH_SOURCE))
```
### Nueva Firma de Asistente
```python
def subgraph_provenance_triples(
subgraph_uri: str,
extracted_triples: List[Triple],
chunk_uri: str,
component_name: str,
component_version: str,
llm_model: Optional[str] = None,
ontology_uri: Optional[str] = None,
timestamp: Optional[str] = None,
) -> List[Triple]:
"""
Build provenance triples for a subgraph of extracted knowledge.
Creates:
- tg:contains link for each extracted triple (RDF-star quoted)
- One prov:wasDerivedFrom link to source chunk
- One activity with agent metadata
"""
```
### Cambio importante
Este es un cambio importante en el modelo de trazabilidad. La trazabilidad no
se ha publicado, por lo que no se necesita ninguna migración. El código antiguo `tg:reifies` /
`statement_uri` se puede eliminar por completo.

View file

@ -0,0 +1,803 @@
---
layout: default
title: "Proveniencia en Tiempo de Extracción: Capa de Origen"
parent: "Spanish (Beta)"
---
# Proveniencia en Tiempo de Extracción: Capa de Origen
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
Este documento captura notas sobre la proveniencia en tiempo de extracción para futuros trabajos de especificación. La proveniencia en tiempo de extracción registra la "capa de origen": de dónde provienen los datos originalmente, cómo se extrajeron y transformaron.
Esto es diferente de la proveniencia en tiempo de consulta (ver `query-time-provenance.md`), que registra el razonamiento del agente.
## Declaración del Problema
### Implementación Actual
Actualmente, la proveniencia funciona de la siguiente manera:
Los metadatos del documento se almacenan como triples RDF en el grafo de conocimiento.
Un ID de documento vincula los metadatos al documento, de modo que el documento aparece como un nodo en el grafo.
Cuando se extraen aristas (relaciones/hechos) de los documentos, una relación `subjectOf` vincula la arista extraída con el documento de origen.
### Problemas con el Enfoque Actual
1. **Carga repetitiva de metadatos:** Los metadatos del documento se empaquetan y cargan repetidamente con cada lote de triples extraídos de ese documento. Esto es un desperdicio y redundante: los mismos metadatos viajan como carga con cada salida de extracción.
<<<<<<< HEAD
2. **Proveniencia superficial:** La relación `subjectOf` actual solo vincula los hechos directamente con el documento de nivel superior. No hay visibilidad de la cadena de transformación: qué página proporcionó el hecho, qué fragmento, qué método de extracción se utilizó.
=======
2. **Proveniencia superficial:** La relación `subjectOf` actual solo vincula los hechos directamente al documento de nivel superior. No hay visibilidad de la cadena de transformación: qué página proporcionó el hecho, qué fragmento, qué método de extracción se utilizó.
>>>>>>> 82edf2d (New md files from RunPod)
### Estado Deseado
1. **Cargar metadatos una vez:** Los metadatos del documento deben cargarse una vez y adjuntarse al nodo del documento de nivel superior, no repetirse con cada lote de triples.
<<<<<<< HEAD
2. **DAG de proveniencia rica:** Capturar toda la cadena de transformación desde el documento de origen a través de todos los artefactos intermedios hasta los hechos extraídos. Por ejemplo, una transformación de un documento PDF:
=======
2. **DAG de proveniencia enriquecido:** Capturar la cadena de transformación completa desde el documento de origen a través de todos los artefactos intermedios hasta los hechos extraídos. Por ejemplo, una transformación de un documento PDF:
>>>>>>> 82edf2d (New md files from RunPod)
```
PDF file (source document with metadata)
→ Page 1 (decoded text)
→ Chunk 1
→ Extracted edge/fact (via subjectOf)
→ Extracted edge/fact
→ Chunk 2
→ Extracted edge/fact
→ Page 2
→ Chunk 3
→ ...
```
3. **Almacenamiento unificado:** El grafo de procedencia se almacena en el mismo grafo de conocimiento que el conocimiento extraído. Esto permite consultar la procedencia de la misma manera que se consulta el conocimiento: siguiendo los enlaces hacia atrás a lo largo de la cadena desde cualquier hecho hasta su ubicación de origen exacta.
<<<<<<< HEAD
4. **Identificadores estables:** Cada artefacto intermedio (página, fragmento) tiene un identificador estable como un nodo en el grafo.
=======
4. **Identificadores estables:** Cada artefacto intermedio (página, fragmento) tiene un identificador estable como nodo en el grafo.
>>>>>>> 82edf2d (New md files from RunPod)
5. **Enlace padre-hijo:** Los documentos derivados se vinculan a sus padres hasta el documento fuente de nivel superior, utilizando tipos de relación consistentes.
6. **Atribución precisa de hechos:** La relación `subjectOf` en los bordes extraídos apunta al padre inmediato (fragmento), no al documento de nivel superior. La procedencia completa se recupera recorriendo el DAG.
## Casos de uso
### UC1: Atribución de la fuente en las respuestas de GraphRAG
**Escenario:** Un usuario ejecuta una consulta de GraphRAG y recibe una respuesta del agente.
**Flujo:**
1. El usuario envía una consulta al agente de GraphRAG.
2. El agente recupera hechos relevantes del grafo de conocimiento para formular una respuesta.
3. De acuerdo con la especificación de procedencia en tiempo de consulta, el agente informa qué hechos contribuyeron a la respuesta.
4. Cada hecho se vincula a su fragmento de origen a través del grafo de procedencia.
5. Los fragmentos se vinculan a páginas, las páginas se vinculan a documentos fuente.
<<<<<<< HEAD
**Resultado de la experiencia de usuario:** La interfaz muestra la respuesta del LLM junto con la atribución de la fuente. El usuario puede:
=======
**Resultado de la experiencia del usuario:** La interfaz muestra la respuesta del LLM junto con la atribución de la fuente. El usuario puede:
>>>>>>> 82edf2d (New md files from RunPod)
Ver qué hechos respaldaron la respuesta.
Profundizar desde hechos → fragmentos → páginas → documentos.
Examinar los documentos fuente originales para verificar las afirmaciones.
Comprender exactamente dónde en un documento (en qué página, en qué sección) se originó un hecho.
**Valor:** Los usuarios pueden verificar las respuestas generadas por la IA con fuentes primarias, lo que genera confianza y permite la verificación de hechos.
### UC2: Depuración de la calidad de la extracción
Un hecho parece incorrecto. Rastrear hacia atrás a través del fragmento → página → documento para ver el texto original. ¿Fue una mala extracción, o la fuente en sí misma estaba equivocada?
### UC3: Reextracción incremental
El documento fuente se actualiza. ¿Qué fragmentos/hechos se derivaron de él? Invalidar y regenerar solo esos, en lugar de volver a procesar todo.
### UC4: Eliminación de datos / Derecho al olvido
Se debe eliminar un documento fuente (GDPR, legal, etc.). Recorrer el DAG para encontrar y eliminar todos los hechos derivados.
### UC5: Resolución de conflictos
Dos hechos se contradicen. Rastrear ambos hasta sus fuentes para comprender por qué y decidir a cuál confiar (fuente más autorizada, más reciente, etc.).
### UC6: Ponderación de la autoridad de la fuente
Algunas fuentes son más autorizadas que otras. Los hechos se pueden ponderar o filtrar según la autoridad/calidad de sus documentos de origen.
### UC7: Comparación de la canalización de extracción
Comparar los resultados de diferentes métodos/versiones de extracción. ¿Qué extractor produjo mejores hechos del mismo documento fuente?
## Puntos de integración
<<<<<<< HEAD
### Bibliotecario
El componente de bibliotecario ya proporciona almacenamiento de documentos con identificadores de documentos únicos. El sistema de procedencia se integra con esta infraestructura existente.
#### Capacidades existentes (ya implementadas)
**Vinculación de documentos padre-hijo:**
Campo `parent_id` en `DocumentMetadata`: vincula el documento hijo al documento padre.
Campo `document_type`: valores: `"source"` (original) o `"extracted"` (derivado).
API `add-child-document`: crea un documento hijo con `document_type = "extracted"` automático.
API `list-children`: recupera todos los hijos de un documento padre.
Eliminación en cascada: eliminar un padre elimina automáticamente todos los documentos hijo.
**Identificación de documentos:**
=======
### Librarian
El componente de "Librarian" ya proporciona almacenamiento de documentos con identificadores de documentos únicos. El sistema de procedencia se integra con esta infraestructura existente.
#### Capacidades Existentes (ya implementadas)
**Vinculación de Documentos Padre-Hijo:**
Campo `parent_id` en `DocumentMetadata`: vincula el documento hijo al documento padre.
Campo `document_type`: valores: `"source"` (original) o `"extracted"` (derivado).
API `add-child-document`: crea un documento hijo con `document_type = "extracted"` automáticamente.
API `list-children`: recupera todos los hijos de un documento padre.
Eliminación en cascada: eliminar un padre elimina automáticamente todos los documentos hijos.
**Identificación de Documentos:**
>>>>>>> 82edf2d (New md files from RunPod)
Los identificadores de documentos son especificados por el cliente (no generados automáticamente).
Documentos indexados por una clave compuesta `(user, document_id)` en Cassandra.
Identificadores de objetos (UUID) generados internamente para el almacenamiento de blobs.
<<<<<<< HEAD
**Soporte de metadatos:**
=======
**Soporte de Metadatos:**
>>>>>>> 82edf2d (New md files from RunPod)
Campo `metadata: list[Triple]`: triples RDF para metadatos estructurados.
`title`, `comments`, `tags`: metadatos básicos del documento.
`time`: marca de tiempo, `kind`: tipo MIME.
<<<<<<< HEAD
**Arquitectura de almacenamiento:**
Los metadatos se almacenan en Cassandra (espacio de claves `librarian`, tabla `document`).
El contenido se almacena en MinIO/S3 blob storage (cubeta `library`).
Entrega inteligente de contenido: documentos < 2 MB incrustados, documentos más grandes transmitidos por flujo.
#### Archivos clave
`trustgraph-flow/trustgraph/librarian/librarian.py`: operaciones principales del bibliotecario.
`trustgraph-flow/trustgraph/librarian/service.py`: procesador de servicios, carga de documentos.
`trustgraph-flow/trustgraph/tables/library.py`: almacén de tablas de Cassandra.
`trustgraph-base/trustgraph/schema/services/library.py`: definiciones de esquema.
#### Aspectos a abordar
El bibliotecario tiene los componentes básicos, pero actualmente:
1. La vinculación padre-hijo es de un solo nivel: no hay ayudantes de recorrido de DAG multinivel.
=======
**Arquitectura de Almacenamiento:**
Los metadatos se almacenan en Cassandra (espacio de claves `librarian`, tabla `document`).
El contenido se almacena en MinIO/S3 (cubeta `library`).
Entrega inteligente de contenido: documentos < 2MB incrustados, documentos más grandes transmitidos por flujo.
#### Archivos Clave
`trustgraph-flow/trustgraph/librarian/librarian.py`: Operaciones principales de "Librarian".
`trustgraph-flow/trustgraph/librarian/service.py`: Procesador de servicios, carga de documentos.
`trustgraph-flow/trustgraph/tables/library.py`: Almacén de tablas de Cassandra.
`trustgraph-base/trustgraph/schema/services/library.py`: Definiciones de esquema.
#### Aspectos a Abordar
"Librarian" tiene los componentes básicos, pero actualmente:
1. La vinculación padre-hijo es de un solo nivel: no hay ayudantes para la travesía de DAG de varios niveles.
>>>>>>> 82edf2d (New md files from RunPod)
2. No hay un vocabulario estándar de tipos de relación (por ejemplo, `derivedFrom`, `extractedFrom`).
3. Los metadatos de procedencia (método de extracción, confianza, posición de fragmento) no están estandarizados.
4. No hay una API de consulta para recorrer toda la cadena de procedencia desde un hecho hasta la fuente.
<<<<<<< HEAD
## Diseño de flujo de extremo a extremo
Cada procesador en la canalización sigue un patrón consistente:
Recibe el ID del documento del componente anterior.
Recupera el contenido del bibliotecario.
Produce artefactos secundarios.
Para cada hijo: guarda en el bibliotecario, emite un borde al gráfico, reenvía el ID al componente posterior.
### Flujos de procesamiento
Existen dos flujos dependiendo del tipo de documento:
#### Flujo de documento PDF
=======
## Diseño de Flujo de Extremo a Extremo
Cada procesador en la canalización sigue un patrón consistente:
Recibe el ID del documento desde el componente anterior.
Recupera el contenido de "Librarian".
Produce artefactos hijos.
Para cada hijo: guarda en "Librarian", emite un borde al grafo, reenvía el ID al componente posterior.
### Flujos de Procesamiento
Existen dos flujos dependiendo del tipo de documento:
#### Flujo de Documentos PDF
>>>>>>> 82edf2d (New md files from RunPod)
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Librarian (initiate processing) │
│ 1. Emit root document metadata to knowledge graph (once) │
│ 2. Send root document ID to PDF extractor │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ PDF Extractor (per page) │
│ 1. Fetch PDF content from librarian using document ID │
│ 2. Extract pages as text │
│ 3. For each page: │
│ a. Save page as child document in librarian (parent = root doc) │
│ b. Emit parent-child edge to knowledge graph │
│ c. Send page document ID to chunker │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ Chunker (per chunk) │
│ 1. Fetch page content from librarian using document ID │
│ 2. Split text into chunks │
│ 3. For each chunk: │
│ a. Save chunk as child document in librarian (parent = page) │
│ b. Emit parent-child edge to knowledge graph │
│ c. Send chunk document ID + chunk content to next processor │
└─────────────────────────────────────────────────────────────────────────┘
─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
Post-chunker optimization: messages carry both
chunk ID (for provenance) and content (to avoid
librarian round-trip). Chunks are small (2-4KB).
─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
┌─────────────────────────────────────────────────────────────────────────┐
│ Knowledge Extractor (per chunk) │
│ 1. Receive chunk ID + content directly (no librarian fetch needed) │
│ 2. Extract facts/triples and embeddings from chunk content │
│ 3. For each triple: │
│ a. Emit triple to knowledge graph │
│ b. Emit reified edge linking triple → chunk ID (edge pointing │
│ to edge - first use of reification support) │
│ 4. For each embedding: │
│ a. Emit embedding with its entity ID │
│ b. Link entity ID → chunk ID in knowledge graph │
└─────────────────────────────────────────────────────────────────────────┘
```
#### Flujo de documentos de texto
<<<<<<< HEAD
Los documentos de texto omiten el extractor de PDF y van directamente al fragmentador:
=======
Los documentos de texto omiten el extractor de PDF y van directamente al procesador de fragmentos:
>>>>>>> 82edf2d (New md files from RunPod)
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Librarian (initiate processing) │
│ 1. Emit root document metadata to knowledge graph (once) │
│ 2. Send root document ID directly to chunker (skip PDF extractor) │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ Chunker (per chunk) │
│ 1. Fetch text content from librarian using document ID │
│ 2. Split text into chunks │
│ 3. For each chunk: │
│ a. Save chunk as child document in librarian (parent = root doc) │
│ b. Emit parent-child edge to knowledge graph │
│ c. Send chunk document ID + chunk content to next processor │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ Knowledge Extractor │
│ (same as PDF flow) │
└─────────────────────────────────────────────────────────────────────────┘
```
El DAG resultante es un nivel más corto:
```
PDF: Document → Pages → Chunks → Triples/Embeddings
Text: Document → Chunks → Triples/Embeddings
```
<<<<<<< HEAD
El diseño se adapta a ambos porque el componente de segmentación trata su entrada de forma genérica; utiliza cualquier ID de documento que reciba como elemento padre, independientemente de si se trata de un documento fuente o de una página.
=======
El diseño se adapta a ambos casos porque el componente de división de texto (chunker) trata su entrada de forma genérica; utiliza cualquier ID de documento que reciba como elemento padre, independientemente de si se trata de un documento fuente o de una página.
>>>>>>> 82edf2d (New md files from RunPod)
### Esquema de metadatos (PROV-O)
Los metadatos de procedencia utilizan la ontología W3C PROV-O. Esto proporciona un vocabulario estándar y permite la futura firma/autenticación de los resultados de la extracción.
<<<<<<< HEAD
#### Conceptos principales de PROV-O
| Tipo PROV-O | Uso en TrustGraph |
|-------------|------------------|
| `prov:Entity` | Documento, Página, Segmento, Triple, Incrustación |
| `prov:Activity` | Instancias de operaciones de extracción |
| `prov:Agent` | Componentes de TG (extractor de PDF, segmentador, etc.) con versiones |
=======
#### Conceptos básicos de PROV-O
| Tipo PROV-O | Uso en TrustGraph |
|-------------|------------------|
| `prov:Entity` | Documento, Página, Fragmento, Triple, Incrustación |
| `prov:Activity` | Instancias de operaciones de extracción |
| `prov:Agent` | Componentes de TG (extractor de PDF, componente de división de texto, etc.) con versiones |
>>>>>>> 82edf2d (New md files from RunPod)
#### Relaciones PROV-O
| Predicado | Significado | Ejemplo |
|-----------|---------|---------|
| `prov:wasDerivedFrom` | Entidad derivada de otra entidad | Página wasDerivedFrom Documento |
| `prov:wasGeneratedBy` | Entidad generada por una actividad | Página wasGeneratedBy PDFExtractionActivity |
| `prov:used` | Actividad que utiliza una entidad como entrada | PDFExtractionActivity used Documento |
| `prov:wasAssociatedWith` | Actividad realizada por un agente | PDFExtractionActivity wasAssociatedWith tg:PDFExtractor |
#### Metadatos en cada nivel
**Documento fuente (emitido por Librarian):**
```
doc:123 a prov:Entity .
doc:123 dc:title "Research Paper" .
doc:123 dc:source <https://example.com/paper.pdf> .
doc:123 dc:date "2024-01-15" .
doc:123 dc:creator "Author Name" .
doc:123 tg:pageCount 42 .
doc:123 tg:mimeType "application/pdf" .
```
**Página (emitida por el extractor de PDF):**
```
page:123-1 a prov:Entity .
page:123-1 prov:wasDerivedFrom doc:123 .
page:123-1 prov:wasGeneratedBy activity:pdf-extract-456 .
page:123-1 tg:pageNumber 1 .
activity:pdf-extract-456 a prov:Activity .
activity:pdf-extract-456 prov:used doc:123 .
activity:pdf-extract-456 prov:wasAssociatedWith tg:PDFExtractor .
activity:pdf-extract-456 tg:componentVersion "1.2.3" .
activity:pdf-extract-456 prov:startedAtTime "2024-01-15T10:30:00Z" .
```
**Fragmento (emitido por el procesador de fragmentos):**
```
chunk:123-1-1 a prov:Entity .
chunk:123-1-1 prov:wasDerivedFrom page:123-1 .
chunk:123-1-1 prov:wasGeneratedBy activity:chunk-789 .
chunk:123-1-1 tg:chunkIndex 1 .
chunk:123-1-1 tg:charOffset 0 .
chunk:123-1-1 tg:charLength 2048 .
activity:chunk-789 a prov:Activity .
activity:chunk-789 prov:used page:123-1 .
activity:chunk-789 prov:wasAssociatedWith tg:Chunker .
activity:chunk-789 tg:componentVersion "1.0.0" .
activity:chunk-789 tg:chunkSize 2048 .
activity:chunk-789 tg:chunkOverlap 200 .
```
**Triple (emitido por el Extractor de Conocimiento):**
```
# The extracted triple (edge)
entity:JohnSmith rel:worksAt entity:AcmeCorp .
# Subgraph containing the extracted triples
subgraph:001 tg:contains <<entity:JohnSmith rel:worksAt entity:AcmeCorp>> .
subgraph:001 prov:wasDerivedFrom chunk:123-1-1 .
subgraph:001 prov:wasGeneratedBy activity:extract-999 .
activity:extract-999 a prov:Activity .
activity:extract-999 prov:used chunk:123-1-1 .
activity:extract-999 prov:wasAssociatedWith tg:KnowledgeExtractor .
activity:extract-999 tg:componentVersion "2.1.0" .
activity:extract-999 tg:llmModel "claude-3" .
activity:extract-999 tg:ontology <http://example.org/ontologies/business-v1> .
```
**Incrustación (almacenada en un almacén de vectores, no en un almacén de triples):**
Las incrustaciones se almacenan en el almacén de vectores con metadatos, no como triples RDF. Cada registro de incrustación contiene:
| Campo | Descripción | Ejemplo |
|-------|-------------|---------|
| vector | El vector de incrustación | [0.123, -0.456, ...] |
| entity | URI del nodo que representa la incrustación | `entity:JohnSmith` |
| chunk_id | Fragmento de origen (procedencia) | `chunk:123-1-1` |
| model | Modelo de incrustación utilizado | `text-embedding-ada-002` |
| component_version | Versión del incrustador de TG | `1.0.0` |
El campo `entity` vincula la incrustación al grafo de conocimiento (URI del nodo). El campo `chunk_id` proporciona la procedencia de vuelta al fragmento de origen, lo que permite el recorrido ascendente del DAG hasta el documento original.
#### Extensiones del Espacio de Nombres de TrustGraph
Predicados personalizados dentro del espacio de nombres `tg:` para metadatos específicos de la extracción:
| Predicado | Dominio | Descripción |
|-----------|--------|-------------|
<<<<<<< HEAD
| `tg:contains` | Subgrafo | Indica un triple contenido en este subgrafo de extracción |
=======
| `tg:contains` | Subgrafo | Apunta a un triple contenido en este subgrafo de extracción |
>>>>>>> 82edf2d (New md files from RunPod)
| `tg:pageCount` | Documento | Número total de páginas en el documento de origen |
| `tg:mimeType` | Documento | Tipo MIME del documento de origen |
| `tg:pageNumber` | Página | Número de página en el documento de origen |
| `tg:chunkIndex` | Fragmento | Índice del fragmento dentro del fragmento principal |
| `tg:charOffset` | Fragmento | Desplazamiento de caracteres en el texto principal |
| `tg:charLength` | Fragmento | Longitud del fragmento en caracteres |
| `tg:chunkSize` | Actividad | Tamaño de fragmento configurado |
| `tg:chunkOverlap` | Actividad | Solapamiento configurado entre fragmentos |
| `tg:componentVersion` | Actividad | Versión del componente de TG |
| `tg:llmModel` | Actividad | LLM utilizado para la extracción |
| `tg:ontology` | Actividad | URI de la ontología utilizada para guiar la extracción |
| `tg:embeddingModel` | Actividad | Modelo utilizado para las incrustaciones |
| `tg:sourceText` | Declaración | Texto exacto del cual se extrajo un triple |
| `tg:sourceCharOffset` | Declaración | Desplazamiento de caracteres dentro del fragmento donde comienza el texto de origen |
| `tg:sourceCharLength` | Declaración | Longitud del texto de origen en caracteres |
<<<<<<< HEAD
#### Inicialización del Vocabulario (Por Colección)
=======
#### Arranque del Vocabulario (Por Colección)
>>>>>>> 82edf2d (New md files from RunPod)
El grafo de conocimiento es neutral con respecto a la ontología y se inicializa vacío. Cuando se escriben datos de procedencia PROV-O en una colección por primera vez, el vocabulario debe inicializarse con etiquetas RDF para todas las clases y predicados. Esto garantiza una visualización legible por humanos en las consultas y la interfaz de usuario.
**Clases PROV-O:**
```
prov:Entity rdfs:label "Entity" .
prov:Activity rdfs:label "Activity" .
prov:Agent rdfs:label "Agent" .
```
**Predicados PROV-O:**
```
prov:wasDerivedFrom rdfs:label "was derived from" .
prov:wasGeneratedBy rdfs:label "was generated by" .
prov:used rdfs:label "used" .
prov:wasAssociatedWith rdfs:label "was associated with" .
prov:startedAtTime rdfs:label "started at" .
```
**Predicados de TrustGraph:**
```
tg:contains rdfs:label "contains" .
tg:pageCount rdfs:label "page count" .
tg:mimeType rdfs:label "MIME type" .
tg:pageNumber rdfs:label "page number" .
tg:chunkIndex rdfs:label "chunk index" .
tg:charOffset rdfs:label "character offset" .
tg:charLength rdfs:label "character length" .
tg:chunkSize rdfs:label "chunk size" .
tg:chunkOverlap rdfs:label "chunk overlap" .
tg:componentVersion rdfs:label "component version" .
tg:llmModel rdfs:label "LLM model" .
tg:ontology rdfs:label "ontology" .
tg:embeddingModel rdfs:label "embedding model" .
tg:sourceText rdfs:label "source text" .
tg:sourceCharOffset rdfs:label "source character offset" .
tg:sourceCharLength rdfs:label "source character length" .
```
<<<<<<< HEAD
**Nota de implementación:** Este vocabulario de inicio debe ser idempotente, es decir, seguro de ejecutar varias veces sin crear duplicados. Podría activarse durante el procesamiento inicial de un documento en una colección, o como un paso separado de inicialización de la colección.
=======
**Nota de implementación:** Este proceso de inicialización del vocabulario debe ser idempotente, es decir, seguro de ejecutar varias veces sin crear duplicados. Podría activarse durante el procesamiento inicial de un documento en una colección, o como un paso separado de inicialización de la colección.
>>>>>>> 82edf2d (New md files from RunPod)
#### Origen de los Sub-Fragmentos (Aspiracional)
Para un seguimiento de origen más detallado, sería valioso registrar exactamente dónde dentro de un fragmento se extrajo una tripleta. Esto permite:
Resaltar el texto fuente exacto en la interfaz de usuario.
Verificar la precisión de la extracción en comparación con la fuente.
Depurar la calidad de la extracción a nivel de oración.
**Ejemplo con seguimiento de posición:**
```
# The extracted triple
entity:JohnSmith rel:worksAt entity:AcmeCorp .
# Subgraph with sub-chunk provenance
subgraph:001 tg:contains <<entity:JohnSmith rel:worksAt entity:AcmeCorp>> .
subgraph:001 prov:wasDerivedFrom chunk:123-1-1 .
subgraph:001 tg:sourceText "John Smith has worked at Acme Corp since 2019" .
subgraph:001 tg:sourceCharOffset 1547 .
subgraph:001 tg:sourceCharLength 46 .
```
**Ejemplo con rango de texto (alternativa):**
```
subgraph:001 tg:contains <<entity:JohnSmith rel:worksAt entity:AcmeCorp>> .
subgraph:001 prov:wasDerivedFrom chunk:123-1-1 .
subgraph:001 tg:sourceRange "1547-1593" .
subgraph:001 tg:sourceText "John Smith has worked at Acme Corp since 2019" .
```
**Consideraciones de implementación:**
La extracción basada en LLM puede no proporcionar naturalmente las posiciones de los caracteres.
<<<<<<< HEAD
Se podría solicitar al LLM que devuelva la oración/frase original junto con las triples extraídas.
=======
Se podría solicitar al LLM que devuelva la oración/frase fuente junto con las triples extraídas.
>>>>>>> 82edf2d (New md files from RunPod)
Alternativamente, se puede realizar un procesamiento posterior para hacer coincidir de forma difusa las entidades extraídas con el texto fuente.
Compromiso entre la complejidad de la extracción y la granularidad de la procedencia.
Puede ser más fácil de lograr con métodos de extracción estructurados que con la extracción de LLM de formato libre.
Esto se considera una meta a largo plazo; primero se debe implementar la procedencia a nivel de fragmento, y el seguimiento de subfragmentos puede ser una mejora futura si es factible.
### Modelo de almacenamiento dual
El DAG de procedencia se construye progresivamente a medida que los documentos fluyen a través de la canalización:
| Almacén | ¿Qué se almacena | Propósito |
|-------|---------------|---------|
| Bibliotecario | Contenido del documento + enlaces padre-hijo | Recuperación de contenido, eliminación en cascada |
| Gráfico de conocimiento | Bordes padre-hijo + metadatos | Consultas de procedencia, atribución de hechos |
Ambos almacenes mantienen la misma estructura DAG. El bibliotecario almacena el contenido; el gráfico almacena las relaciones y permite las consultas de recorrido.
### Principios de diseño clave
<<<<<<< HEAD
1. **El ID del documento como unidad de flujo**: Los procesadores pasan ID, no contenido. El contenido se recupera del bibliotecario cuando es necesario.
2. **Emitir una vez en la fuente**: Los metadatos se escriben en el gráfico una vez al inicio del procesamiento, no se repiten en procesos posteriores.
=======
1. **ID del documento como unidad de flujo**: Los procesadores pasan ID, no contenido. El contenido se recupera del bibliotecario cuando es necesario.
2. **Emitir una vez en la fuente**: Los metadatos se escriben en el gráfico una vez al inicio del procesamiento, no se repiten aguas abajo.
>>>>>>> 82edf2d (New md files from RunPod)
3. **Patrón de procesador consistente**: Cada procesador sigue el mismo patrón de recepción/recuperación/producción/guardado/emisión/reenvío.
4. **Construcción progresiva del DAG**: Cada procesador agrega su nivel al DAG. La cadena completa de procedencia se construye de forma incremental.
<<<<<<< HEAD
5. **Optimización posterior al fragmentador**: Después de la fragmentación, los mensajes contienen tanto el ID como el contenido. Los fragmentos son pequeños (2-4 KB), por lo que incluir el contenido evita viajes de ida y vuelta innecesarios al bibliotecario, al tiempo que preserva la procedencia a través del ID.
=======
5. **Optimización posterior al fragmentador**: Después de la fragmentación, los mensajes contienen tanto el ID como el contenido. Los fragmentos son pequeños (2-4 KB), por lo que incluir el contenido evita viajes de ida y vuelta innecesarios al bibliotecario al tiempo que preserva la procedencia a través del ID.
>>>>>>> 82edf2d (New md files from RunPod)
## Tareas de implementación
### Cambios en el bibliotecario
#### Estado actual
Inicia el procesamiento de documentos enviando el ID del documento al primer procesador.
<<<<<<< HEAD
No tiene conexión con el almacén de triples; los metadatos se incluyen en los resultados de la extracción.
=======
No hay conexión con el almacén de triples; los metadatos se incluyen en los resultados de la extracción.
>>>>>>> 82edf2d (New md files from RunPod)
`add-child-document` crea enlaces padre-hijo de un solo nivel.
`list-children` devuelve solo los hijos inmediatos.
#### Cambios requeridos
**1. Nueva interfaz: Conexión al almacén de triples**
El bibliotecario debe emitir directamente los bordes de metadatos del documento al gráfico de conocimiento al iniciar el procesamiento.
Agregar un cliente/publicador del almacén de triples al servicio del bibliotecario.
Al iniciar el procesamiento: emitir los metadatos del documento raíz como bordes del gráfico (una vez).
**2. Vocabulario de tipos de documentos**
Estandarizar los valores de `document_type` para los documentos hijo:
<<<<<<< HEAD
`source` - documento original cargado.
`page` - página extraída de la fuente (PDF, etc.).
`chunk` - fragmento de texto derivado de la página o la fuente.
=======
`source`: documento original cargado.
`page`: página extraída de la fuente (PDF, etc.).
`chunk`: fragmento de texto derivado de la página o la fuente.
>>>>>>> 82edf2d (New md files from RunPod)
#### Resumen de cambios de interfaz
| Interfaz | Cambio |
|-----------|--------|
| Almacén de triples | Nueva conexión de salida: emitir bordes de metadatos del documento |
| Inicio del procesamiento | Emitir metadatos al gráfico antes de reenviar el ID del documento |
### Cambios en el extractor de PDF
#### Estado actual
Recibe el contenido del documento (o transmite documentos grandes).
Extrae texto de las páginas PDF.
Envía el contenido de la página al fragmentador.
No interactúa con el bibliotecario ni con el almacén de triples.
#### Cambios requeridos
**1. Nueva interfaz: Cliente del bibliotecario**
El extractor de PDF debe guardar cada página como un documento hijo en el bibliotecario.
Agregar un cliente del bibliotecario al servicio del extractor de PDF.
Para cada página: llamar a `add-child-document` con padre = ID del documento raíz.
**2. Nueva interfaz: Conexión al almacén de triples**
El extractor de PDF debe emitir bordes padre-hijo al gráfico de conocimiento.
Agregar un cliente/publicador del almacén de triples.
Para cada página: emitir un borde que vincule el documento de la página con el documento padre.
**3. Cambiar el formato de salida**
En lugar de reenviar el contenido de la página directamente, reenvíe el ID del documento de la página.
<<<<<<< HEAD
El componente "Chunker" recuperará el contenido del "librarian" utilizando el ID.
=======
El componente "Chunker" obtendrá el contenido del "librarian" utilizando el ID.
>>>>>>> 82edf2d (New md files from RunPod)
#### Resumen de Cambios en la Interfaz
| Interfaz | Cambio |
|-----------|--------|
| Librarian | Nueva salida: guardar documentos hijos |
| Triple store | Nueva salida: emitir aristas padre-hijo |
| Mensaje de salida | Cambio de contenido a ID de documento |
### Cambios en el Componente "Chunker"
#### Estado Actual
Recibe contenido de página/texto
Lo divide en fragmentos
Reenvía el contenido del fragmento a los procesadores posteriores
No interactúa con el "librarian" ni con el "triple store"
#### Cambios Requeridos
**1. Cambiar el manejo de la entrada**
<<<<<<< HEAD
Recibir el ID del documento en lugar del contenido, y recuperarlo del "librarian".
Agregar un cliente de "librarian" al servicio "chunker"
Recuperar el contenido de la página utilizando el ID del documento
=======
Recibir el ID del documento en lugar del contenido, obtenerlo del "librarian".
Agregar un cliente de "librarian" al servicio "chunker"
Obtener el contenido de la página utilizando el ID del documento
>>>>>>> 82edf2d (New md files from RunPod)
**2. Nueva interfaz: Cliente de "Librarian" (escritura)**
Guardar cada fragmento como un documento hijo en el "librarian".
Para cada fragmento: llamar a `add-child-document` con parent = ID del documento de la página
<<<<<<< HEAD
**3. Nueva interfaz: Conexión con el "Triple store"**
Emitir aristas padre-hijo al grafo de conocimiento.
Agregar un cliente/publicador de "triple store"
=======
**3. Nueva interfaz: Conexión al "Triple Store"**
Emitir aristas padre-hijo al grafo de conocimiento.
Agregar un cliente/publicador del "triple store"
>>>>>>> 82edf2d (New md files from RunPod)
Para cada fragmento: emitir una arista que vincule el documento del fragmento con el documento de la página
**4. Cambiar el formato de salida**
Reenviar tanto el ID del documento del fragmento como el contenido del fragmento (optimización posterior al componente "chunker").
<<<<<<< HEAD
Los procesadores posteriores reciben el ID para la trazabilidad y el contenido para trabajar con él
=======
Los procesadores posteriores reciben el ID para la trazabilidad + el contenido para trabajar con él
>>>>>>> 82edf2d (New md files from RunPod)
#### Resumen de Cambios en la Interfaz
| Interfaz | Cambio |
|-----------|--------|
| Mensaje de entrada | Cambio de contenido a ID de documento |
<<<<<<< HEAD
| Librarian | Nueva salida (lectura + escritura) - recuperar contenido, guardar documentos hijos |
| Triple store | Nueva salida - emitir aristas padre-hijo |
| Mensaje de salida | Cambio de contenido-único a ID + contenido |
### Cambios en el Extractor de Conocimiento
=======
| Librarian | Nueva salida (lectura + escritura) - obtener contenido, guardar documentos hijos |
| Triple store | Nueva salida - emitir aristas padre-hijo |
| Mensaje de salida | Cambio de contenido-único a ID + contenido |
### Cambios en el "Knowledge Extractor"
>>>>>>> 82edf2d (New md files from RunPod)
#### Estado Actual
Recibe contenido del fragmento
Extrae triples y embeddings
<<<<<<< HEAD
Los emite al "triple store" y al almacén de "embeddings"
=======
Los emite al "triple store" y al "embedding store"
>>>>>>> 82edf2d (New md files from RunPod)
La relación `subjectOf` apunta al documento de nivel superior (no al fragmento)
#### Cambios Requeridos
**1. Cambiar el manejo de la entrada**
Recibir el ID del fragmento junto con el contenido.
Utilizar el ID del fragmento para la vinculación de trazabilidad (el contenido ya está incluido según la optimización)
<<<<<<< HEAD
**2. Actualizar la trazabilidad de los triples**
=======
**2. Actualizar la trazabilidad de triples**
>>>>>>> 82edf2d (New md files from RunPod)
Vincular los triples extraídos al fragmento (no al documento de nivel superior).
Utilizar la reificación para crear una arista que apunte a la arista
Relación `subjectOf`: triple → ID del documento del fragmento
Primer uso del soporte de reificación existente
<<<<<<< HEAD
**3. Actualizar la trazabilidad de los "embeddings"**
Vincular los ID de las entidades de "embedding" al fragmento.
Emitir una arista: ID de la entidad de "embedding" → ID del documento del fragmento
=======
**3. Actualizar la trazabilidad de embeddings**
Vincular los ID de las entidades de embedding al fragmento.
Emitir una arista: ID de la entidad de embedding → ID del documento del fragmento
>>>>>>> 82edf2d (New md files from RunPod)
#### Resumen de Cambios en la Interfaz
| Interfaz | Cambio |
|-----------|--------|
<<<<<<< HEAD
| Mensaje de entrada | Se espera el ID del fragmento + contenido (no solo contenido) |
| Triple store | Utilizar la reificación para la trazabilidad de triple → fragmento |
| Trazabilidad de "embeddings" | Vincular ID de entidad → ID de fragmento |
=======
| Mensaje de entrada | Esperar ID del fragmento + contenido (no solo contenido) |
| Triple store | Utilizar la reificación para la trazabilidad de triple → fragmento |
| Trazabilidad de embeddings | Vincular ID de entidad → ID de fragmento |
>>>>>>> 82edf2d (New md files from RunPod)
## Referencias
Trazabilidad en tiempo de consulta: `docs/tech-specs/query-time-provenance.md`
Estándar PROV-O para el modelado de trazabilidad
Metadatos de origen existentes en el grafo de conocimiento (necesitan auditoría)

View file

@ -0,0 +1,304 @@
---
layout: default
title: "Especificación de la Definición del Esquema de Flujo"
parent: "Spanish (Beta)"
---
<<<<<<< HEAD
# Especificación de la Definición del Esquema de Flujo
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
Un esquema de flujo define una plantilla de patrón de flujo de datos completo en el sistema TrustGraph. Cuando se instancia, crea una red interconectada de procesadores que manejan la ingesta de datos, el procesamiento, el almacenamiento y la consulta como un sistema unificado.
## Estructura
Una definición de esquema de flujo consta de cinco secciones principales:
### 1. Sección de Clase
Define procesadores de servicios compartidos que se instancian una vez por esquema de flujo. Estos procesadores manejan las solicitudes de todas las instancias de flujo de esta clase.
=======
# Especificación de la Definición de la Hoja de Flujo
## Resumen
Una hoja de flujo define una plantilla de patrón de flujo de datos completo en el sistema TrustGraph. Cuando se instancia, crea una red interconectada de procesadores que manejan la ingesta de datos, el procesamiento, el almacenamiento y la consulta como un sistema unificado.
## Estructura
Una definición de hoja de flujo consta de cinco secciones principales:
### 1. Sección de Clase
Define procesadores de servicios compartidos que se instancian una vez por hoja de flujo. Estos procesadores manejan las solicitudes de todas las instancias de flujo de esta clase.
>>>>>>> 82edf2d (New md files from RunPod)
```json
"class": {
"service-name:{class}": {
"request": "queue-pattern:{class}",
"response": "queue-pattern:{class}",
"settings": {
"setting-name": "fixed-value",
"parameterized-setting": "{parameter-name}"
}
}
}
```
**Características:**
Compartidas entre todas las instancias de flujo de la misma clase.
Típicamente servicios costosos o sin estado (modelos de lenguaje grandes, modelos de embedding).
Utilice la variable de plantilla `{class}` para el nombre de la cola.
Los ajustes pueden ser valores fijos o parametrizados con la sintaxis `{parameter-name}`.
Ejemplos: `embeddings:{class}`, `text-completion:{class}`, `graph-rag:{class}`.
### 2. Sección de Flujo
Define los procesadores específicos del flujo que se instancian para cada instancia de flujo individual. Cada flujo obtiene su propio conjunto aislado de estos procesadores.
```json
"flow": {
"processor-name:{id}": {
"input": "queue-pattern:{id}",
"output": "queue-pattern:{id}",
"settings": {
"setting-name": "fixed-value",
"parameterized-setting": "{parameter-name}"
}
}
}
```
**Características:**
Instancia única por flujo
Maneja datos y estado específicos del flujo
Utilice la variable de plantilla `{id}` para el nombre de la cola
Los ajustes pueden ser valores fijos o parametrizados con la sintaxis `{parameter-name}`
Ejemplos: `chunker:{id}`, `pdf-decoder:{id}`, `kg-extract-relationships:{id}`
### 3. Sección de Interfaces
Define los puntos de entrada y los contratos de interacción para el flujo. Estos forman la superficie de la API para sistemas externos y comunicación entre componentes internos.
Las interfaces pueden adoptar dos formas:
**Patrón de "Enviar y Olvidar"** (cola única):
```json
"interfaces": {
"document-load": "persistent://tg/flow/document-load:{id}",
"triples-store": "persistent://tg/flow/triples-store:{id}"
}
```
**Patrón de solicitud/respuesta** (objeto con campos de solicitud/respuesta):
```json
"interfaces": {
"embeddings": {
"request": "non-persistent://tg/request/embeddings:{class}",
"response": "non-persistent://tg/response/embeddings:{class}"
}
}
```
**Tipos de Interfaces:**
**Puntos de Entrada**: Lugares donde los sistemas externos inyectan datos (`document-load`, `agent`)
**Interfaces de Servicio**: Patrones de solicitud/respuesta para servicios (`embeddings`, `text-completion`)
**Interfaces de Datos**: Puntos de conexión de flujo de datos "dispara y olvida" (`triples-store`, `entity-contexts-load`)
### 4. Sección de Parámetros
Mapea los nombres de parámetros específicos del flujo a definiciones de parámetros almacenadas centralmente:
```json
"parameters": {
"model": "llm-model",
"temp": "temperature",
"chunk": "chunk-size"
}
```
**Características:**
Las claves son nombres de parámetros utilizados en la configuración del procesador (por ejemplo, `{model}`)
Los valores hacen referencia a definiciones de parámetros almacenadas en schema/config
Permite la reutilización de definiciones de parámetros comunes en diferentes flujos
Reduce la duplicación de esquemas de parámetros
### 5. Metadatos
Información adicional sobre el plano del flujo:
```json
"description": "Human-readable description",
"tags": ["capability-1", "capability-2"]
```
## Variables de Plantilla
### Variables del Sistema
#### {id}
Reemplazado con el identificador único de la instancia del flujo.
Crea recursos aislados para cada flujo.
Ejemplo: `flow-123`, `customer-A-flow`
#### {class}
Reemplazado con el nombre de la plantilla del flujo.
Crea recursos compartidos entre flujos de la misma clase.
Ejemplo: `standard-rag`, `enterprise-rag`
### Variables de Parámetro
#### {parameter-name}
Parámetros personalizados definidos en el momento de la ejecución del flujo.
Los nombres de los parámetros coinciden con las claves en la sección `parameters` del flujo.
Se utilizan en la configuración de los procesadores para personalizar el comportamiento.
Ejemplos: `{model}`, `{temp}`, `{chunk}`
Reemplazado con los valores proporcionados al iniciar el flujo.
Validados contra definiciones de parámetros almacenadas centralmente.
## Configuración de Procesadores
La configuración proporciona valores de configuración a los procesadores en el momento de la creación. Pueden ser:
### Configuración Fija
Valores directos que no cambian:
```json
"settings": {
"model": "gemma3:12b",
"temperature": 0.7,
"max_retries": 3
}
```
### Configuración parametrizada
Valores que utilizan parámetros proporcionados al iniciar el flujo:
```json
"settings": {
"model": "{model}",
"temperature": "{temp}",
"endpoint": "https://{region}.api.example.com"
}
```
Los nombres de los parámetros en la configuración corresponden a las claves en la sección `parameters` del flujo.
### Ejemplos de configuración
**Procesador LLM con parámetros:**
```json
// In parameters section:
"parameters": {
"model": "llm-model",
"temp": "temperature",
"tokens": "max-tokens",
"key": "openai-api-key"
}
// In processor definition:
"text-completion:{class}": {
"request": "non-persistent://tg/request/text-completion:{class}",
"response": "non-persistent://tg/response/text-completion:{class}",
"settings": {
"model": "{model}",
"temperature": "{temp}",
"max_tokens": "{tokens}",
"api_key": "{key}"
}
}
```
**Chunker con Configuraciones Fijas y Parametrizadas:**
```json
// In parameters section:
"parameters": {
"chunk": "chunk-size"
}
// In processor definition:
"chunker:{id}": {
"input": "persistent://tg/flow/chunk:{id}",
"output": "persistent://tg/flow/chunk-load:{id}",
"settings": {
"chunk_size": "{chunk}",
"chunk_overlap": 100,
"encoding": "utf-8"
}
}
```
## Patrones de Colas (Pulsar)
Los planos de flujo utilizan Apache Pulsar para el envío de mensajes. Los nombres de las colas siguen el formato de Pulsar:
```
<persistence>://<tenant>/<namespace>/<topic>
```
### Componentes:
**persistencia**: `persistent` or `non-persistent` (Modo de persistencia de Pulsar)
**inquilino**: `tg` para definiciones de planos de flujo proporcionados por TrustGraph
**espacio de nombres**: Indica el patrón de mensajería
`flow`: Servicios de envío y olvido
`request`: Parte de solicitud de servicios de solicitud/respuesta
`response`: Parte de respuesta de servicios de solicitud/respuesta
**tema**: El nombre específico de la cola/tema con variables de plantilla
### Colas Persistentes
Patrón: `persistent://tg/flow/<topic>:{id}`
Utilizado para servicios de envío y olvido y flujo de datos duradero
Los datos persisten en el almacenamiento de Pulsar a través de reinicios
Ejemplo: `persistent://tg/flow/chunk-load:{id}`
### Colas No Persistentes
Patrón: `non-persistent://tg/request/<topic>:{class}` or `non-persistent://tg/response/<topic>:{class}`
Utilizado para patrones de mensajería de solicitud/respuesta
Efímero, no se persiste en disco por Pulsar
Menor latencia, adecuado para comunicación estilo RPC
Ejemplo: `non-persistent://tg/request/embeddings:{class}`
## Arquitectura del Flujo de Datos
El plano de flujo crea un flujo de datos unificado donde:
1. **Pipeline de Procesamiento de Documentos**: Fluye desde la ingesta hasta la transformación y el almacenamiento
<<<<<<< HEAD
2. **Servicios de Consulta**: Procesadores integrados que consultan las mismas bases de datos y servicios
=======
2. **Servicios de Consulta**: Procesadores integrados que consultan las mismas fuentes de datos y servicios
>>>>>>> 82edf2d (New md files from RunPod)
3. **Servicios Compartidos**: Procesadores centralizados que todos los flujos pueden utilizar
4. **Escritores de Almacenamiento**: Persisten los datos procesados en los almacenes apropiados
Todos los procesadores (tanto `{id}` como `{class}`) trabajan juntos como un gráfico de flujo de datos cohesivo, no como sistemas separados.
## Ejemplo de Instanciación de Flujo
Dado:
ID de instancia de flujo: `customer-A-flow`
Plano de flujo: `standard-rag`
Mapeos de parámetros de flujo:
`"model": "llm-model"`
`"temp": "temperature"`
`"chunk": "chunk-size"`
Parámetros proporcionados por el usuario:
`model`: `gpt-4`
`temp`: `0.5`
`chunk`: `512`
Expansiones de plantilla:
`persistent://tg/flow/chunk-load:{id}``persistent://tg/flow/chunk-load:customer-A-flow`
`non-persistent://tg/request/embeddings:{class}``non-persistent://tg/request/embeddings:standard-rag`
`"model": "{model}"``"model": "gpt-4"`
`"temperature": "{temp}"``"temperature": "0.5"`
`"chunk_size": "{chunk}"``"chunk_size": "512"`
Esto crea:
Pipeline de procesamiento de documentos aislado para `customer-A-flow`
Servicio de incrustación compartido para todos los flujos `standard-rag`
Flujo de datos completo desde la ingesta de documentos hasta la consulta
Procesadores configurados con los valores de parámetro proporcionados
## Beneficios
1. **Eficiencia de Recursos**: Los servicios costosos se comparten entre los flujos
2. **Aislamiento de Flujos**: Cada flujo tiene su propio pipeline de procesamiento de datos
3. **Escalabilidad**: Se pueden instanciar múltiples flujos desde la misma plantilla
4. **Modularidad**: Clara separación entre componentes compartidos y específicos del flujo
5. **Arquitectura Unificada**: La consulta y el procesamiento son parte del mismo flujo de datos

View file

@ -0,0 +1,644 @@
---
layout: default
title: "Especificación Técnica de Parámetros Configurables para Flow Blueprint"
parent: "Spanish (Beta)"
---
<<<<<<< HEAD
# Especificación Técnica de Parámetros Configurables para Flow Blueprint
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
Esta especificación describe la implementación de parámetros configurables para flow blueprints en TrustGraph. Los parámetros permiten a los usuarios personalizar los parámetros del procesador en el momento de la ejecución del flujo, proporcionando valores que reemplazan los marcadores de posición de parámetros en la definición del flow blueprint.
Los parámetros funcionan a través de la sustitución de variables de plantilla en los parámetros del procesador, de manera similar a cómo funcionan las variables `{id}` y `{class}`, pero con valores proporcionados por el usuario.
La integración admite cuatro casos de uso principales:
1. **Selección de Modelo**: Permitir a los usuarios elegir diferentes modelos LLM (por ejemplo, `gemma3:8b`, `gpt-4`, `claude-3`) para los procesadores.
2. **Configuración de Recursos**: Ajustar los parámetros del procesador, como los tamaños de lote, los tamaños de lote y los límites de concurrencia.
=======
# Especificación Técnica de Parámetros Configurables para el Blueprint de Flujo
## Resumen
Esta especificación describe la implementación de parámetros configurables para blueprints de flujo en TrustGraph. Los parámetros permiten a los usuarios personalizar los parámetros del procesador en el momento de la ejecución del flujo, proporcionando valores que reemplazan los marcadores de posición de parámetros en la definición del blueprint del flujo.
Los parámetros funcionan mediante la sustitución de variables de plantilla en los parámetros del procesador, de manera similar a como funcionan las variables `{id}` y `{class}`, pero con valores proporcionados por el usuario.
La integración admite cuatro casos de uso principales:
1. **Selección de Modelo**: Permitir a los usuarios elegir diferentes modelos de LLM (por ejemplo, `gemma3:8b`, `gpt-4`, `claude-3`) para los procesadores.
2. **Configuración de Recursos**: Ajustar los parámetros del procesador, como los tamaños de lote, los tamaños de fragmento y los límites de concurrencia.
>>>>>>> 82edf2d (New md files from RunPod)
3. **Ajuste del Comportamiento**: Modificar el comportamiento del procesador a través de parámetros como la temperatura, el número máximo de tokens o los umbrales de recuperación.
4. **Parámetros Específicos del Entorno**: Configurar puntos finales, claves de API o URL específicas de la región para cada implementación.
## Objetivos
<<<<<<< HEAD
**Configuración Dinámica del Procesador**: Permitir la configuración en tiempo de ejecución de los parámetros del procesador a través de la sustitución de parámetros.
**Validación de Parámetros**: Proporcionar verificación de tipos y validación para los parámetros en el momento de la ejecución del flujo.
**Valores Predeterminados**: Admitir valores predeterminados sensatos, al tiempo que se permite la sobrescritura para usuarios avanzados.
**Sustitución de Plantillas**: Reemplazar sin problemas los marcadores de posición de parámetros en los parámetros del procesador.
**Integración de la Interfaz de Usuario**: Permitir la entrada de parámetros a través de interfaces de API y de interfaz de usuario.
**Seguridad de Tipos**: Asegurar que los tipos de parámetros coincidan con los tipos de parámetros del procesador esperados.
**Documentación**: Esquemas de parámetros auto-documentados dentro de las definiciones de flow blueprint.
**Compatibilidad con Versiones Anteriores**: Mantener la compatibilidad con los flow blueprints existentes que no utilizan parámetros.
## Antecedentes
Los flow blueprints en TrustGraph ahora admiten parámetros de procesador que pueden contener valores fijos o marcadores de posición de parámetros. Esto crea una oportunidad para la personalización en tiempo de ejecución.
=======
**Configuración Dinámica del Procesador**: Permitir la configuración en tiempo de ejecución de los parámetros del procesador mediante la sustitución de parámetros.
**Validación de Parámetros**: Proporcionar verificación de tipos y validación para los parámetros en el momento de la ejecución del flujo.
**Valores Predeterminados**: Admitir valores predeterminados razonables, al tiempo que se permite la sobrescritura para usuarios avanzados.
**Sustitución de Plantillas**: Reemplazar sin problemas los marcadores de posición de parámetros en los parámetros del procesador.
**Integración de la Interfaz de Usuario**: Permitir la entrada de parámetros a través de interfaces de API y de la interfaz de usuario.
**Seguridad de Tipos**: Asegurar que los tipos de parámetros coincidan con los tipos de parámetros del procesador esperados.
**Documentación**: Esquemas de parámetros auto-documentados dentro de las definiciones de blueprints de flujo.
**Compatibilidad con Versiones Anteriores**: Mantener la compatibilidad con los blueprints de flujo existentes que no utilizan parámetros.
## Antecedentes
Los blueprints de flujo en TrustGraph ahora admiten parámetros de procesador que pueden contener valores fijos o marcadores de posición de parámetros. Esto crea una oportunidad para la personalización en tiempo de ejecución.
>>>>>>> 82edf2d (New md files from RunPod)
Los parámetros de procesador actuales admiten:
Valores fijos: `"model": "gemma3:12b"`
Marcadores de posición de parámetros: `"model": "gemma3:{model-size}"`
Esta especificación define cómo se:
<<<<<<< HEAD
Declaran en las definiciones de flow blueprint.
=======
Declaran en las definiciones de blueprints de flujo.
>>>>>>> 82edf2d (New md files from RunPod)
Validan cuando se ejecutan los flujos.
Sustituyen en los parámetros del procesador.
Exponen a través de las API y la interfaz de usuario.
Al aprovechar los parámetros de procesador parametrizados, TrustGraph puede:
<<<<<<< HEAD
Reducir la duplicación de flow blueprints mediante el uso de parámetros para las variaciones.
=======
Reducir la duplicación de blueprints de flujo mediante el uso de parámetros para las variaciones.
>>>>>>> 82edf2d (New md files from RunPod)
Permitir a los usuarios ajustar el comportamiento del procesador sin modificar las definiciones.
Admitir configuraciones específicas del entorno a través de los valores de los parámetros.
Mantener la seguridad de tipos a través de la validación del esquema de parámetros.
## Diseño Técnico
### Arquitectura
El sistema de parámetros configurables requiere los siguientes componentes técnicos:
1. **Definición del Esquema de Parámetros**
<<<<<<< HEAD
Definiciones de parámetros basadas en JSON Schema dentro de los metadatos del flow blueprint.
=======
Definiciones de parámetros basadas en esquemas JSON dentro de los metadatos del blueprint de flujo.
>>>>>>> 82edf2d (New md files from RunPod)
Definiciones de tipos que incluyen cadenas, números, booleanos, enumeraciones y tipos de objetos.
Reglas de validación que incluyen valores mínimos/máximos, patrones y campos obligatorios.
Módulo: trustgraph-flow/trustgraph/flow/definition.py
2. **Motor de Resolución de Parámetros**
Validación de parámetros en tiempo de ejecución contra el esquema.
Aplicación de valores predeterminados para los parámetros no especificados.
Inyección de parámetros en el contexto de ejecución del flujo.
Coerción y conversión de tipos según sea necesario.
Módulo: trustgraph-flow/trustgraph/flow/parameter_resolver.py
3. **Integración del Almacén de Parámetros**
Recuperación de definiciones de parámetros del almacén de esquemas/configuración.
Almacenamiento en caché de definiciones de parámetros utilizadas con frecuencia.
Validación contra esquemas almacenados centralmente.
Módulo: trustgraph-flow/trustgraph/flow/parameter_store.py
4. **Extensiones del Lanzador de Flujos**
Extensiones de API para aceptar valores de parámetros durante el lanzamiento del flujo.
Resolución de mapeo de parámetros (nombres de flujo a nombres de definición).
Manejo de errores para combinaciones de parámetros no válidas.
Módulo: trustgraph-flow/trustgraph/flow/launcher.py
5. **Formularios de Parámetros de la Interfaz de Usuario**
<<<<<<< HEAD
Generación dinámica de formularios a partir de los metadatos de los parámetros del flujo.
Visualización ordenada de parámetros utilizando el campo `order`.
Etiquetas descriptivas de parámetros utilizando el campo `description`.
Validación de entrada contra las definiciones de tipo de parámetro.
=======
Generación dinámica de formularios a partir de metadatos de parámetros del flujo.
Visualización ordenada de parámetros utilizando el campo `order`.
Etiquetas descriptivas de parámetros utilizando el campo `description`.
Validación de entrada contra definiciones de tipos de parámetros.
>>>>>>> 82edf2d (New md files from RunPod)
Preajustes y plantillas de parámetros.
Módulo: trustgraph-ui/components/flow-parameters/
### Modelos de Datos
#### Definiciones de Parámetros (Almacenadas en Esquema/Configuración)
Las definiciones de parámetros se almacenan centralmente en el sistema de esquemas y configuración con el tipo "parameter-type":
```json
{
"llm-model": {
"type": "string",
"description": "LLM model to use",
"default": "gpt-4",
"enum": [
{
"id": "gpt-4",
"description": "OpenAI GPT-4 (Most Capable)"
},
{
"id": "gpt-3.5-turbo",
"description": "OpenAI GPT-3.5 Turbo (Fast & Efficient)"
},
{
"id": "claude-3",
"description": "Anthropic Claude 3 (Thoughtful & Safe)"
},
{
"id": "gemma3:8b",
"description": "Google Gemma 3 8B (Open Source)"
}
],
"required": false
},
"model-size": {
"type": "string",
"description": "Model size variant",
"default": "8b",
"enum": ["2b", "8b", "12b", "70b"],
"required": false
},
"temperature": {
"type": "number",
"description": "Model temperature for generation",
"default": 0.7,
"minimum": 0.0,
"maximum": 2.0,
"required": false
},
"chunk-size": {
"type": "integer",
"description": "Document chunk size",
"default": 512,
"minimum": 128,
"maximum": 2048,
"required": false
}
}
```
#### Diagrama de flujo con referencias de parámetros
Los diagramas de flujo definen los metadatos de los parámetros con referencias de tipo, descripciones y orden:
```json
{
"flow_class": "document-analysis",
"parameters": {
"llm-model": {
"type": "llm-model",
"description": "Primary LLM model for text completion",
"order": 1
},
"llm-rag-model": {
"type": "llm-model",
"description": "LLM model for RAG operations",
"order": 2,
"advanced": true,
"controlled-by": "llm-model"
},
"llm-temperature": {
"type": "temperature",
"description": "Generation temperature for creativity control",
"order": 3,
"advanced": true
},
"chunk-size": {
"type": "chunk-size",
"description": "Document chunk size for processing",
"order": 4,
"advanced": true
},
"chunk-overlap": {
"type": "integer",
"description": "Overlap between document chunks",
"order": 5,
"advanced": true,
"controlled-by": "chunk-size"
}
},
"class": {
"text-completion:{class}": {
"request": "non-persistent://tg/request/text-completion:{class}",
"response": "non-persistent://tg/response/text-completion:{class}",
"parameters": {
"model": "{llm-model}",
"temperature": "{llm-temperature}"
}
},
"rag-completion:{class}": {
"request": "non-persistent://tg/request/rag-completion:{class}",
"response": "non-persistent://tg/response/rag-completion:{class}",
"parameters": {
"model": "{llm-rag-model}",
"temperature": "{llm-temperature}"
}
}
},
"flow": {
"chunker:{id}": {
"input": "persistent://tg/flow/chunk:{id}",
"output": "persistent://tg/flow/chunk-load:{id}",
"parameters": {
"chunk_size": "{chunk-size}",
"chunk_overlap": "{chunk-overlap}"
}
}
}
}
```
<<<<<<< HEAD
La sección `parameters` mapea los nombres de parámetros específicos del flujo (claves) a objetos de metadatos de parámetros que contienen:
`type`: Referencia a la definición de parámetro definida centralmente (por ejemplo, "llm-model")
`description`: Descripción legible por humanos para su visualización en la interfaz de usuario
`order`: Orden de visualización para los formularios de parámetros (los números más bajos aparecen primero)
`advanced` (opcional): Bandera booleana que indica si este es un parámetro avanzado (por defecto: falso). Cuando se establece en verdadero, la interfaz de usuario puede ocultar este parámetro de forma predeterminada o colocarlo en una sección "Avanzado"
`controlled-by` (opcional): Nombre de otro parámetro que controla el valor de este parámetro cuando está en modo simple. Cuando se especifica, este parámetro hereda su valor del parámetro de control, a menos que se anule explícitamente
Este enfoque permite:
Definiciones de tipos de parámetros reutilizables en múltiples plantillas de flujo
Gestión y validación centralizadas de tipos de parámetros
Descripciones y orden de parámetros específicos del flujo
Experiencia de interfaz de usuario mejorada con formularios de parámetros descriptivos
Validación de parámetros consistente en todos los flujos
Adición fácil de nuevos tipos de parámetros estándar
Interfaz de usuario simplificada con separación de modo básico/avanzado
Herencia de valores de parámetros para configuraciones relacionadas
#### Solicitud de inicio de flujo
La API de inicio de flujo acepta parámetros utilizando los nombres de parámetros del flujo:
=======
La sección `parameters` asigna nombres de parámetros específicos del flujo (claves) a objetos de metadatos de parámetros que contienen:
`type`: Referencia a la definición de parámetro definida centralmente (por ejemplo, "llm-model").
`description`: Descripción legible por humanos para su visualización en la interfaz de usuario.
`order`: Orden de visualización para los formularios de parámetros (los números más bajos aparecen primero).
`advanced` (opcional): Marcador booleano que indica si este es un parámetro avanzado (por defecto: falso). Cuando se establece en verdadero, la interfaz de usuario puede ocultar este parámetro de forma predeterminada o colocarlo en una sección "Avanzado".
`controlled-by` (opcional): Nombre de otro parámetro que controla el valor de este parámetro cuando está en modo simple. Cuando se especifica, este parámetro hereda su valor del parámetro de control, a menos que se anule explícitamente.
Este enfoque permite:
Definiciones de tipos de parámetros reutilizables en múltiples plantillas de flujo.
Gestión y validación centralizadas de tipos de parámetros.
Descripciones y orden de parámetros específicos del flujo.
Experiencia de interfaz de usuario mejorada con formularios de parámetros descriptivos.
Validación de parámetros coherente en todos los flujos.
Adición fácil de nuevos tipos de parámetros estándar.
Interfaz de usuario simplificada con separación de modo básico/avanzado.
Herencia de valores de parámetros para configuraciones relacionadas.
#### Solicitud de inicio del flujo
La API de inicio del flujo acepta parámetros utilizando los nombres de parámetros del flujo:
>>>>>>> 82edf2d (New md files from RunPod)
```json
{
"flow_class": "document-analysis",
"flow_id": "customer-A-flow",
"parameters": {
"llm-model": "claude-3",
"llm-temperature": 0.5,
"chunk-size": 1024
}
}
```
Nota: En este ejemplo, `llm-rag-model` no se proporciona explícitamente, pero heredará el valor "claude-3" de `llm-model` debido a su relación `controlled-by`. De manera similar, `chunk-overlap` podría heredar un valor calculado basado en `chunk-size`.
<<<<<<< HEAD
El sistema realizará lo siguiente:
1. Extraer metadatos de parámetros de la definición de la plantilla de flujo.
=======
El sistema realizará las siguientes acciones:
1. Extraer metadatos de parámetros de la definición de la plantilla del flujo.
>>>>>>> 82edf2d (New md files from RunPod)
2. Mapear los nombres de los parámetros del flujo a sus definiciones de tipo (por ejemplo, `llm-model` → tipo `llm-model`).
3. Resolver las relaciones de control (por ejemplo, `llm-rag-model` hereda de `llm-model`).
4. Validar los valores proporcionados por el usuario y los valores heredados en función de las definiciones de tipo de los parámetros.
5. Sustituir los valores resueltos en los parámetros del procesador durante la instanciación del flujo.
### Detalles de implementación
#### Proceso de resolución de parámetros
Cuando se inicia un flujo, el sistema realiza los siguientes pasos de resolución de parámetros:
1. **Carga de la plantilla del flujo**: Cargar la definición de la plantilla del flujo y extraer los metadatos de los parámetros.
2. **Extracción de metadatos**: Extraer `type`, `description`, `order`, `advanced` y `controlled-by` para cada parámetro definido en la sección `parameters` de la plantilla del flujo.
3. **Búsqueda de la definición de tipo**: Para cada parámetro en la plantilla del flujo:
<<<<<<< HEAD
Recuperar la definición de tipo del parámetro del almacén de esquema/configuración utilizando el campo `type`.
=======
Recuperar la definición de tipo del parámetro del almacén de esquemas/configuración utilizando el campo `type`.
>>>>>>> 82edf2d (New md files from RunPod)
Las definiciones de tipo se almacenan con el tipo "parameter-type" en el sistema de configuración.
Cada definición de tipo contiene el esquema del parámetro, el valor predeterminado y las reglas de validación.
4. **Resolución del valor predeterminado**:
Para cada parámetro definido en la plantilla del flujo:
Comprobar si el usuario ha proporcionado un valor para este parámetro.
Si no se proporciona ningún valor del usuario, utilizar el valor `default` de la definición de tipo del parámetro.
Crear un mapa de parámetros completo que contenga tanto los valores proporcionados por el usuario como los valores predeterminados.
5. **Resolución de la herencia de parámetros** (relaciones de control):
Para los parámetros con el campo `controlled-by`, comprobar si se ha proporcionado un valor explícito.
Si no se proporciona ningún valor explícito, heredar el valor del parámetro de control.
Si el parámetro de control también no tiene valor, utilizar el valor predeterminado de la definición de tipo.
Validar que no existan dependencias circulares en las relaciones `controlled-by`.
6. **Validación**: Validar el conjunto completo de parámetros (proporcionados por el usuario, predeterminados y heredados) en función de las definiciones de tipo.
7. **Almacenamiento**: Almacenar el conjunto completo de parámetros resueltos con la instancia del flujo para su trazabilidad.
8. **Sustitución de plantillas**: Reemplazar los marcadores de posición de parámetros en los parámetros del procesador con los valores resueltos.
9. **Instanciación del procesador**: Crear procesadores con los parámetros sustituidos.
**Notas importantes de implementación**:
El servicio de flujo DEBE combinar los parámetros proporcionados por el usuario con los valores predeterminados de las definiciones de tipo de parámetros.
El conjunto completo de parámetros (incluidos los valores predeterminados aplicados) DEBE almacenarse con el flujo para su trazabilidad.
La resolución de parámetros se realiza al inicio del flujo, no en el momento de la instanciación del procesador.
Los parámetros obligatorios sin valores predeterminados DEBEN provocar que el inicio del flujo falle con un mensaje de error claro.
<<<<<<< HEAD
#### Herencia de parámetros con control
=======
#### Herencia de parámetros con "controlled-by"
>>>>>>> 82edf2d (New md files from RunPod)
El campo `controlled-by` permite la herencia de valores de parámetros, lo que es especialmente útil para simplificar las interfaces de usuario al tiempo que se mantiene la flexibilidad:
**Escenario de ejemplo**:
El parámetro `llm-model` controla el modelo LLM primario.
El parámetro `llm-rag-model` tiene el valor `"controlled-by": "llm-model"`.
En el modo simple, establecer `llm-model` en "gpt-4" establece automáticamente `llm-rag-model` en "gpt-4" también.
En el modo avanzado, los usuarios pueden anular `llm-rag-model` con un valor diferente.
**Reglas de resolución**:
1. Si un parámetro tiene un valor proporcionado explícitamente, utilizar ese valor.
2. Si no hay ningún valor explícito y `controlled-by` está establecido, utilizar el valor del parámetro de control.
3. Si el parámetro de control no tiene valor, recurrir al valor predeterminado de la definición de tipo.
4. Las dependencias circulares en las relaciones `controlled-by` dan como resultado un error de validación.
**Comportamiento de la interfaz de usuario**:
En el modo básico/simple: Los parámetros con `controlled-by` pueden estar ocultos o mostrarse como de solo lectura con el valor heredado.
En el modo avanzado: Se muestran todos los parámetros y se pueden configurar individualmente.
Cuando cambia un parámetro de control, los parámetros dependientes se actualizan automáticamente, a menos que se anulen explícitamente.
<<<<<<< HEAD
#### Integración de Pulsar
=======
#### Integración con Pulsar
>>>>>>> 82edf2d (New md files from RunPod)
1. **Operación de inicio de flujo**
La operación de inicio de flujo de Pulsar debe aceptar un campo `parameters` que contenga un mapa de valores de parámetros.
El esquema de Pulsar para la solicitud de inicio de flujo debe actualizarse para incluir el campo opcional `parameters`.
Ejemplo de solicitud:
```json
{
"flow_class": "document-analysis",
"flow_id": "customer-A-flow",
"parameters": {
"model": "claude-3",
"size": "12b",
"temp": 0.5,
"chunk": 1024
}
}
```
2. **Operación Get-Flow**
El esquema de Pulsar para la respuesta de get-flow debe actualizarse para incluir el campo `parameters`
Esto permite a los clientes recuperar los valores de los parámetros que se utilizaron cuando se inició el flujo.
Ejemplo de respuesta:
```json
{
"flow_id": "customer-A-flow",
"flow_class": "document-analysis",
"status": "running",
"parameters": {
"model": "claude-3",
"size": "12b",
"temp": 0.5,
"chunk": 1024
}
}
```
#### Implementación del Servicio de Flujo
El servicio de configuración de flujo (`trustgraph-flow/trustgraph/config/service/flow.py`) requiere las siguientes mejoras:
1. **Función de Resolución de Parámetros**
```python
async def resolve_parameters(self, flow_class, user_params):
"""
Resolve parameters by merging user-provided values with defaults.
Args:
flow_class: The flow blueprint definition dict
user_params: User-provided parameters dict
Returns:
Complete parameter dict with user values and defaults merged
"""
```
Esta función debe:
Extraer los metadatos de los parámetros de la sección `parameters` del plano de flujo.
<<<<<<< HEAD
Para cada parámetro, obtener la definición de tipo de la tienda de configuración.
=======
Para cada parámetro, obtener la definición de tipo del almacén de configuración.
>>>>>>> 82edf2d (New md files from RunPod)
Aplicar los valores predeterminados para cualquier parámetro que no sea proporcionado por el usuario.
Manejar las relaciones de herencia de `controlled-by`.
Devolver el conjunto de parámetros completo.
2. **Método `handle_start_flow` Modificado**
Llamar a `resolve_parameters` después de cargar el plano de flujo.
Utilizar el conjunto de parámetros resuelto completo para la sustitución de plantillas.
Almacenar el conjunto de parámetros completo (no solo los proporcionados por el usuario) con el flujo.
Validar que todos los parámetros requeridos tengan valores.
3. **Obtención del Tipo de Parámetro**
Las definiciones de tipo de parámetro se almacenan en la configuración con el tipo "parameter-type".
<<<<<<< HEAD
Cada definición de tipo contiene un esquema, un valor predeterminado y reglas de validación.
=======
Cada definición de tipo contiene el esquema, el valor predeterminado y las reglas de validación.
>>>>>>> 82edf2d (New md files from RunPod)
Almacenar en caché los tipos de parámetro utilizados con frecuencia para reducir las búsquedas en la configuración.
#### Integración del Sistema de Configuración
3. **Almacenamiento de Objetos de Flujo**
<<<<<<< HEAD
Cuando un flujo se agrega al sistema de configuración por el componente de flujo en el administrador de configuración, el objeto de flujo debe incluir los valores de parámetros resueltos.
El administrador de configuración debe almacenar tanto los parámetros originales proporcionados por el usuario como los valores resueltos (con los valores predeterminados aplicados).
Los objetos de flujo en el sistema de configuración deben incluir:
`parameters`: Los valores de parámetros resueltos finales utilizados para el flujo.
=======
Cuando un flujo se agrega al sistema de configuración por el componente de flujo en el administrador de configuración, el objeto de flujo debe incluir los valores de parámetro resueltos.
El administrador de configuración debe almacenar tanto los parámetros originales proporcionados por el usuario como los valores resueltos (con los valores predeterminados aplicados).
Los objetos de flujo en el sistema de configuración deben incluir:
`parameters`: Los valores de parámetro resueltos finales utilizados para el flujo.
>>>>>>> 82edf2d (New md files from RunPod)
#### Integración de la CLI
4. **Comandos de la CLI de la Biblioteca**
Los comandos de la CLI que inician flujos necesitan soporte de parámetros:
<<<<<<< HEAD
Aceptar valores de parámetros a través de indicadores de línea de comandos o archivos de configuración.
=======
Aceptar valores de parámetro a través de indicadores de línea de comandos o archivos de configuración.
>>>>>>> 82edf2d (New md files from RunPod)
Validar los parámetros contra las definiciones del plano de flujo antes de la presentación.
Soporte para la entrada de archivos de parámetros (JSON/YAML) para conjuntos de parámetros complejos.
Los comandos de la CLI que muestran flujos deben mostrar información de parámetros:
<<<<<<< HEAD
Mostrar los valores de parámetros utilizados cuando se inició el flujo.
Mostrar los parámetros disponibles para un plano de flujo.
Mostrar los esquemas y valores predeterminados de validación de parámetros.
=======
Mostrar los valores de parámetro utilizados cuando se inició el flujo.
Mostrar los parámetros disponibles para un plano de flujo.
Mostrar los esquemas de validación y los valores predeterminados de los parámetros.
>>>>>>> 82edf2d (New md files from RunPod)
#### Integración de la Clase Base del Procesador
5. **Soporte de ParameterSpec**
Las clases base del procesador deben admitir la sustitución de parámetros a través del mecanismo ParametersSpec existente.
La clase ParametersSpec (ubicada en el mismo módulo que ConsumerSpec y ProducerSpec) debe mejorarse si es necesario para admitir la sustitución de plantillas de parámetros.
<<<<<<< HEAD
Los procesadores deben poder invocar ParametersSpec para configurar sus parámetros con los valores de parámetros resueltos en el momento del lanzamiento del flujo.
La implementación de ParametersSpec debe:
Aceptar configuraciones de parámetros que contengan marcadores de posición de parámetros (por ejemplo, `{model}`, `{temperature}`).
Admitir la sustitución de parámetros en tiempo de ejecución cuando se instancia el procesador.
Validar que los valores sustituidos coincidan con los tipos y restricciones esperados.
Proporcionar manejo de errores para referencias de parámetros faltantes o no válidos.
=======
Los procesadores deben poder invocar ParametersSpec para configurar sus parámetros con los valores de parámetro resueltos en el momento del inicio del flujo.
La implementación de ParametersSpec debe:
Aceptar configuraciones de parámetros que contengan marcadores de posición de parámetros (por ejemplo, `{model}`, `{temperature}`).
Admitir la sustitución de parámetros en tiempo de ejecución cuando se instancia el procesador.
Validar que los valores sustituidos coincidan con los tipos y las restricciones esperadas.
Proporcionar el manejo de errores para las referencias de parámetros faltantes o no válidas.
>>>>>>> 82edf2d (New md files from RunPod)
#### Reglas de Sustitución
Los parámetros utilizan el formato `{parameter-name}` en los parámetros del procesador.
Los nombres de los parámetros en los parámetros coinciden con las claves en la sección `parameters` del flujo.
La sustitución se produce junto con la sustitución de `{id}` y `{class}`.
<<<<<<< HEAD
Las referencias de parámetros no válidas dan como resultado errores en el momento del lanzamiento.
La validación de tipos se basa en la definición de parámetro almacenada de forma centralizada.
**IMPORTANTE**: Todos los valores de parámetros se almacenan y transmiten como cadenas.
=======
Las referencias de parámetros no válidas dan como resultado errores en el momento del inicio.
La validación de tipos se basa en la definición de parámetro almacenada de forma centralizada.
**IMPORTANTE**: Todos los valores de los parámetros se almacenan y transmiten como cadenas.
>>>>>>> 82edf2d (New md files from RunPod)
Los números se convierten a cadenas (por ejemplo, `0.7` se convierte en `"0.7"`).
Los booleanos se convierten a cadenas en minúsculas (por ejemplo, `true` se convierte en `"true"`).
Esto es requerido por el esquema de Pulsar que define `parameters = Map(String())`.
Ejemplo de resolución:
```
Flow parameter mapping: "model": "llm-model"
Processor parameter: "model": "{model}"
User provides: "model": "gemma3:8b"
Final parameter: "model": "gemma3:8b"
Example with type conversion:
Parameter type default: 0.7 (number)
Stored in flow: "0.7" (string)
Substituted in processor: "0.7" (string)
```
## Estrategia de Pruebas
Pruebas unitarias para la validación del esquema de parámetros.
Pruebas de integración para la sustitución de parámetros en los parámetros del procesador.
Pruebas de extremo a extremo para el lanzamiento de flujos con diferentes valores de parámetros.
Pruebas de la interfaz de usuario para la generación y validación de formularios de parámetros.
Pruebas de rendimiento para flujos con muchos parámetros.
<<<<<<< HEAD
Casos extremos: parámetros faltantes, tipos no válidos, referencias de parámetros no definidos.
## Plan de Migración
1. El sistema debe seguir soportando planos de flujo sin parámetros
declarados.
2. El sistema debe seguir soportando flujos sin parámetros especificados:
=======
Casos extremos: parámetros faltantes, tipos inválidos, referencias de parámetros no definidos.
## Plan de Migración
1. El sistema debe continuar admitiendo planos de flujo sin parámetros
declarados.
2. El sistema debe continuar admitiendo flujos sin parámetros especificados:
>>>>>>> 82edf2d (New md files from RunPod)
Esto funciona para flujos sin parámetros y para flujos con parámetros
(que tienen valores predeterminados).
## Preguntas Abiertas
<<<<<<< HEAD
P: ¿Deben los parámetros soportar objetos anidados complejos o limitarse a tipos simples?
=======
P: ¿Deben los parámetros admitir objetos anidados complejos o limitarse a tipos simples?
>>>>>>> 82edf2d (New md files from RunPod)
R: Los valores de los parámetros se codificarán como cadenas, por lo que probablemente
queremos limitarnos a cadenas.
P: ¿Se deben permitir los marcadores de posición de parámetros en los nombres de las colas o solo en
los parámetros?
R: Solo en los parámetros para evitar inyecciones extrañas y casos límite.
P: ¿Cómo manejar los conflictos entre los nombres de los parámetros y las variables del sistema como
`id` y `class`?
R: No es válido especificar "id" y "class" al iniciar un flujo.
<<<<<<< HEAD
P: ¿Debemos soportar parámetros calculados (derivados de otros parámetros)?
=======
P: ¿Debemos admitir parámetros calculados (derivados de otros parámetros)?
>>>>>>> 82edf2d (New md files from RunPod)
R: Solo la sustitución de cadenas para evitar inyecciones extrañas y casos límite.
## Referencias
Especificación de JSON Schema: https://json-schema.org/
<<<<<<< HEAD
Especificación de la Definición del Plano de Flujo: docs/tech-specs/flow-class-definition.md
=======
Especificación de la definición del plano de flujo: docs/tech-specs/flow-class-definition.md
>>>>>>> 82edf2d (New md files from RunPod)

View file

@ -0,0 +1,394 @@
---
layout: default
title: "Especificación Técnica de Contextos de Grafos"
parent: "Spanish (Beta)"
---
# Especificación Técnica de Contextos de Grafos
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
Esta especificación describe los cambios en los primitivos de grafos centrales de TrustGraph para
alinearse con RDF 1.2 y admitir la semántica completa del conjunto de datos RDF. Este es un
cambio importante para la serie de versiones 2.x.
### Versionamiento
- **2.0**: Lanzamiento para adoptantes tempranos. Características principales disponibles,
puede que no esté completamente lista para producción.
- **2.1 / 2.2**: Lanzamiento de producción. Estabilidad y completitud validadas.
La flexibilidad en cuanto a la madurez es intencional: los adoptantes tempranos pueden acceder a nuevas
capacidades antes de que todas las funciones estén listas para producción.
## Objetivos
Los objetivos principales de este trabajo son habilitar metadatos sobre hechos/afirmaciones:
- **Información temporal**: Asociar hechos con metadatos de tiempo
- Cuándo se creyó que un hecho era verdadero
- Cuándo un hecho se volvió verdadero
- Cuándo se descubrió que un hecho era falso
- **Origen/Fuentes**: Realizar un seguimiento de las fuentes que respaldan un hecho
- "Este hecho fue respaldado por la fuente X"
- Enlazar los hechos con sus documentos de origen
- **Veracidad/Confianza**: Registrar afirmaciones sobre la verdad
- "La persona P afirmó que esto era cierto"
- "La persona Q afirma que esto es falso"
- Habilitar la puntuación de confianza y la detección de conflictos
**Hipótesis**: La reificación (RDF-star / triples con comillas) es el mecanismo clave
para lograr estos resultados, ya que todos requieren hacer afirmaciones sobre afirmaciones.
## Antecedentes
Para expresar "el hecho (Alice conoce a Bob) se descubrió el 2024-01-15" o
"la fuente X respalda la afirmación (Y causa Z)", necesita referenciar un borde
como algo sobre lo cual se pueden hacer afirmaciones. Los triples estándar no admiten esto.
### Limitaciones actuales
La clase `Value` actual en `trustgraph-base/trustgraph/schema/core/primitives.py`
puede representar:
- Nodos URI (`is_uri=True`)
- Valores literales (`is_uri=False`)
El campo `type` existe, pero no se utiliza para representar los tipos de datos XSD.
## Diseño técnico
### Características de RDF a admitir
#### Características principales (relacionadas con los objetivos de reificación)
Estas características están directamente relacionadas con los objetivos temporales, de origen y de veracidad:
1. **RDF 1.2 Triples con comillas (RDF-star)**
- Bordes que apuntan a otros bordes
- Un triple puede aparecer como sujeto u objeto de otro triple
- Permite hacer afirmaciones sobre afirmaciones (reificación)
- Mecanismo central para anotar hechos individuales
2. **Conjunto de datos RDF / Grafos con nombre**
- Soporte para múltiples grafos con nombre dentro de un conjunto de datos
- Cada grafo identificado por un IRI
- Pasa de triples (s, p, o) a cuads (s, p, o, g)
- Incluye un grafo predeterminado más cero o más grafos con nombre
- El IRI del grafo puede ser un sujeto en las afirmaciones, por ejemplo:
```
<graph-source-A> <discoveredOn> "2024-01-15"
<graph-source-A> <hasVeracity> "high"
```
- Nota: Los grafos con nombre son una característica separada de la reificación. Tienen
usos más allá de la anotación de afirmaciones (particionamiento, control de acceso, organización del conjunto de datos)
y deben tratarse como una capacidad distinta.
3. **Nodos vacíos** (Soporte limitado)
- Nodos anónimos sin un URI global
- Compatible al cargar datos RDF externos
- **Estado limitado**: No hay garantías sobre la identidad estable después de la carga
- Encontrarlos a través de consultas comodín (coincidencia por conexiones, no por ID)
- No es una característica de primera clase: no confíe en el manejo preciso de nodos vacíos.
#### Correcciones oportunistas (cambio importante de la versión 2.0)
Estas características no están directamente relacionadas con los objetivos de la reificación, pero son
mejoras valiosas a incluir al realizar cambios importantes:
4. **Tipos de literales**
- Utilice correctamente el campo `type` para los tipos de datos XSD
- Ejemplos: xsd:string, xsd:integer, xsd:dateTime, etc.
- Corrige la limitación actual: no se pueden representar fechas o enteros correctamente.
5. **Etiquetas de idioma**
- Soporte para atributos de idioma en literales (por ejemplo, @en, @fr)
- Nota: un literal tiene una etiqueta de idioma O un tipo de datos, no ambos
(excepto para rdf:langString)
- Importante para casos de uso de IA/multilingües.
### Modelos de datos
#### Término (cambiar de Value)
La clase `Value` se renombrará a `Term` para reflejar mejor la terminología de RDF.
Este cambio de nombre tiene dos propósitos:
1. Alinea la nomenclatura con los conceptos de RDF (un "Término" puede ser un IRI, literal, nodo vacío o triple con comillas,
no solo un "valor")
2. Fuerza la revisión del código en la interfaz de cambio importante: cualquier código que aún haga referencia a `Value`
se considera roto y debe actualizarse.
Un Término puede representar:
- **IRI/URI**: un nodo/recurso con nombre
- **Nodo vacío**: un nodo anónimo con alcance local
- **Literal**: un valor de datos con:
- Un tipo de datos (tipo XSD), O
- Una etiqueta de idioma
- **Triple con comillas**: un triple utilizado como término (RDF 1.2)
##### Enfoque elegido: Clase única con discriminador de tipo
Los requisitos de serialización impulsan la estructura: se necesita un discriminador de tipo en el formato de cable
independientemente de la representación de Python. Una clase única con un campo de tipo es la opción natural y se alinea con la
patrón `Value` actual.
Los códigos de tipo de un solo carácter proporcionan una serialización compacta:
```python
from dataclasses import dataclass
# Constantes de tipo de término
IRI = "i" # Nodo IRI/URI
BLANK = "b" # Nodo vacío
LITERAL = "l" # Valor literal
TRIPLE = "t" # Triple con comillas (RDF 1.2)
@dataclass
class Term:
type: str = "" # Uno de: IRI, BLANK, LITERAL, TRIPLE
# Para términos IRI (type == IRI)
iri: str = ""
# Para nodos vacíos (type == BLANK)
id: str = ""
# Para literales (type == LITERAL)
value: str = ""
datatype: str = "" # URI de tipo de datos XSD (mutuamente excluyente con el idioma)
language: str = "" # Etiqueta de idioma (mutuamente excluyente con el tipo de datos)
# Para triples con comillas (type == TRIPLE)
triple: "Triple | None" = None
```
Ejemplos de uso:
```python
# Término IRI
node = Term(type=IRI, iri="http://example.org/Alice")
# Literal con tipo de datos
age = Term(type=LITERAL, value="42", datatype="xsd:integer")
# Literal con etiqueta de idioma
label = Term(type=LITERAL, value="Hello", language="en")
# Nodo vacío
anon = Term(type=BLANK, id="_:b1")
# Triple con comillas (afirmación sobre una afirmación)
inner = Triple(
s=Term(type=IRI, iri="http://example.org/Alice"),
p=Term(type=IRI, iri="http://example.org/knows"),
o=Term(type=IRI, iri="http://example.org/Bob"),
)
reified = Term(type=TRIPLE, triple=inner)
```
##### Alternativas consideradas
**Opción B: Unión de clases especializadas** (`Term = IRI | BlankNode | Literal | QuotedTriple`)
- Rechazada: La serialización aún necesitaría un discriminador de tipo, lo que agregaría complejidad.
**Opción C: Clase base con subclases**
- Rechazada: El mismo problema de serialización, además de peculiaridades de la herencia de dataclass.
#### Triple / Cuad
La clase `Triple` gana un campo de grafo opcional para convertirse en un cuad:
```python
@dataclass
class Triple:
s: Term | None = None # Sujeto
p: Term | None = None # Predicado
o: Term | None = None # Objeto
g: str | None = None # Nombre del grafo (IRI), None = grafo predeterminado
```
Decisiones de diseño:
- **Nombre del campo**: `g` para la coherencia con `s`, `p`, `o`
- **Opcional**: `None` significa el grafo predeterminado (sin nombre)
- **Tipo**: Cadena simple (IRI) en lugar de Term
- Los nombres de los grafos siempre son IRIs
- Los nodos vacíos como nombres de grafos se descartaron (demasiado confusos)
- No es necesario el conjunto completo de mecanismos de Term
Nota: El nombre de la clase permanece `Triple` incluso si técnicamente es un cuad ahora.
Esto evita la alteración y la terminología "triple" todavía es la terminología común. El contexto del grafo es
metadatos sobre dónde vive el triple.
### Patrones de consulta candidatos
El motor de consulta actual acepta combinaciones de términos S, P, O. Con los triples con comillas,
un triple en sí mismo se convierte en un término válido en esas posiciones. A continuación, se presentan patrones de consulta
candidatos que admiten los objetivos originales.
#### Semántica de parámetros de grafo
Siguiendo las convenciones de SPARQL para la compatibilidad hacia atrás:
- **`g` omitido / None**: Consulta solo el grafo predeterminado
- **`g` = IRI específico**: Consulta solo ese grafo con nombre
- **`g` = comodín / `*`**: Consulta en todos los grafos (equivalente a `GRAPH ?g { ... }` de SPARQL)
Esto mantiene las consultas simples simples y hace que las consultas de grafos con nombre sean opcionales.
Las consultas entre grafos (g=comodín) se admiten completamente. El esquema de Cassandra incluye tablas dedicadas (SPOG, POSG, OSPG)
donde g es una columna de agrupación, en lugar de una clave de partición, lo que permite consultas eficientes en todos los grafos.
#### Consultas temporales
**Encontrar todos los hechos descubiertos después de una fecha determinada:**
```
S: ? # cualquier triple con comillas
P: <discoveredOn>
O: > "2024-01-15"^^xsd:date # comparación de fecha
```
**Encontrar cuándo se creyó que un hecho era verdadero:**
```
S: << <Alice> <knows> <Bob> >> # triple con comillas como sujeto
P: <believedTrueFrom>
O: ? # devuelve la fecha
```
**Encontrar los hechos que se descubrió que eran falsos:**
```
S: ? # cualquier triple con comillas
P: <discoveredFalseOn>
O: ? # tiene cualquier valor (existe)
```
#### Consultas de origen
**Encontrar todos los hechos respaldados por una fuente específica:**
```
S: ? # cualquier triple con comillas
P: <supportedBy>
O: <source:document-123>
```
**Encontrar qué fuentes respaldan un hecho específico:**
```
S: << <DrugA> <treats> <DiseaseB> >> # triple con comillas como sujeto
P: <supportedBy>
O: ? # devuelve las IRIs de la fuente
```
#### Consultas de veracidad
**Encontrar las afirmaciones que una persona marcó como verdaderas:**
```
S: ? # cualquier triple con comillas
P: <assertedTrueBy>
O: <person:Alice>
```
**Encontrar las afirmaciones conflictivas (el mismo hecho, diferente veracidad):**
```
# Primera consulta: hechos afirmados como verdaderos
S: ?
P: <assertedTrueBy>
O: ?
# Segunda consulta: hechos afirmados como falsos
S: ?
P: <assertedFalseBy>
O: ?
# Lógica de la aplicación: encontrar la intersección de los sujetos
```
**Encontrar los hechos con una puntuación de confianza por debajo del umbral:**
```
S: ? # cualquier triple con comillas
P: <trustScore>
O: < 0.5 # comparación numérica
```
### Arquitectura
Se requieren cambios significativos en varios componentes:
#### Este repositorio (trustgraph)
- **Primitivos del esquema** (`trustgraph-base/trustgraph/schema/core/primitives.py`)
- `Value``Term` cambio de nombre
- Nueva estructura de `Term` con discriminador de tipo
- `Triple` gana el campo `g` para el contexto del grafo
- **Traductores de mensajes** (`trustgraph-base/trustgraph/messaging/translators/`)
- Actualizaciones para las nuevas estructuras de `Term` y `Triple`
- **Componentes de puerta de enlace**
- Manejar nuevas estructuras de `Term` y cuádruple
- **Núcleos de conocimiento**
- ...
- **Pruebas**
- ...
Esto se deja para futuras implementaciones.
- **Vector store boundary**
- ...
Esto se deja para futuras implementaciones.
## Consideraciones de seguridad
Los grafos con nombre no son una característica de seguridad. Los usuarios y las colecciones siguen siendo los límites de seguridad.
Los grafos con nombre son puramente para la organización de datos y el soporte de la reificación.
## Consideraciones de rendimiento
- Los triples con comillas agregan profundidad de anidamiento; esto puede afectar el rendimiento de las consultas.
- Se necesitan estrategias de indexación para consultas con ámbito de grafo.
- El diseño del esquema de Cassandra deberá acomodar el almacenamiento de cuádruples de forma eficiente.
### Límite del almacén vectorial
Los almacenes vectoriales siempre hacen referencia a IRIs:
- Nunca bordes (triples con comillas)
- Nunca valores literales
- Nunca nodos vacíos
Esto mantiene el almacén vectorial simple; se encarga de la similitud semántica de las entidades con nombre. La estructura del grafo maneja las relaciones, la reificación y los metadatos. Los triples con comillas y los grafos con nombre no complican las operaciones vectoriales.
## Estrategia de pruebas
Utilice la estrategia de prueba existente. Dado que esta es una versión importante, se prestará especial atención al
conjunto de pruebas de extremo a extremo para validar que las nuevas estructuras funcionan correctamente en todos los componentes.
## Plan de migración
- La versión 2.0 es una versión importante; no se requiere compatibilidad con versiones anteriores
- Los datos existentes pueden necesitar migrarse al nuevo esquema (por determinarse según el diseño final)
- Considere herramientas de migración para convertir triples existentes
## Preguntas abiertas
- **Nodos vacíos**: Soporte limitado confirmado. Es posible que deba decidirse sobre una estrategia de skolemización (generar IRIs
al cargar o preservar los ID de nodos vacíos).
- **Sintaxis de consulta**: ¿Cuál es la sintaxis concreta para especificar triples con comillas en las consultas? Debe definirse
la API de consulta.
- ~~**Vocabulario de predicados**~~: Resuelto. Se permiten todos los predicados RDF válidos, incluidos los personalizados definidos por el usuario.
Supuestos mínimos sobre la validez de RDF. Muy pocos valores bloqueados (por ejemplo, `rdfs:label` se utiliza en algunos lugares).
Estrategia: evite bloquear cualquier cosa a menos que sea absolutamente necesario.
- ~~**Impacto del almacén vectorial**~~: Resuelto. Los almacenes vectoriales siempre apuntan a IRIs.
solo; nunca bordes, literales o nodos vacíos. Los triples con comillas y la reificación no afectan al almacén vectorial.
- ~~**Semántica del grafo con nombre**~~: Resuelta. Las consultas predeterminadas son para el grafo predeterminado (coincide con el comportamiento de SPARQL,
compatible con versiones anteriores). Se requiere un parámetro de grafo explícito para consultar grafos con nombre o todos los grafos.
## Referencias
- [Conceptos RDF 1.2](https://www.w3.org/TR/rdf12-concepts/)
- [RDF-star y SPARQL-star](https://w3c.github.io/rdf-star/)
- [Conjunto de datos RDF](https://www.w3.org/TR/rdf11-concepts/#section-dataset)

View file

@ -0,0 +1,467 @@
---
layout: default
title: "Especificación Técnica de la Consulta GraphQL"
parent: "Spanish (Beta)"
---
# Especificación Técnica de la Consulta GraphQL
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Descripción General
Esta especificación describe la implementación de una interfaz de consulta GraphQL para el almacenamiento de datos estructurados de TrustGraph en Apache Cassandra. Basándose en las capacidades de datos estructurados descritas en la especificación structured-data.md, este documento detalla cómo se ejecutarán las consultas GraphQL contra las tablas de Cassandra que contienen objetos estructurados extraídos e importados.
<<<<<<< HEAD
El servicio de consulta GraphQL proporcionará una interfaz flexible y segura para consultar datos estructurados almacenados en Cassandra. Se adaptará dinámicamente a los cambios de esquema, admitirá consultas complejas que incluyan relaciones entre objetos y se integrará perfectamente con la arquitectura existente basada en mensajes de TrustGraph.
=======
El servicio de consulta GraphQL proporcionará una interfaz flexible y segura para consultar datos estructurados almacenados en Cassandra. Se adaptará dinámicamente a los cambios de esquema, admitirá consultas complejas, incluidas las relaciones entre objetos, y se integrará perfectamente con la arquitectura existente basada en mensajes de TrustGraph.
>>>>>>> 82edf2d (New md files from RunPod)
## Objetivos
**Soporte de Esquema Dinámico**: Adaptación automática a los cambios de esquema en la configuración sin reiniciar el servicio.
**Cumplimiento de los Estándares GraphQL**: Proporcionar una interfaz GraphQL estándar compatible con las herramientas y clientes GraphQL existentes.
<<<<<<< HEAD
**Consultas Eficientes de Cassandra**: Traducir consultas GraphQL en consultas CQL eficientes de Cassandra, respetando las claves de partición y los índices.
**Resolución de Relaciones**: Soporte para resolutores de campos GraphQL para relaciones entre diferentes tipos de objetos.
**Seguridad de Tipos**: Garantizar la ejecución de consultas y la generación de respuestas seguras, basadas en definiciones de esquema.
**Rendimiento Escalable**: Manejar consultas concurrentes de manera eficiente con un grupo de conexiones y optimización de consultas adecuados.
**Integración de Solicitud/Respuesta**: Mantener la compatibilidad con el patrón de solicitud/respuesta basado en Pulsar de TrustGraph.
**Manejo de Errores**: Proporcionar informes de errores completos para discrepancias de esquema, errores de consulta y problemas de validación de datos.
## Antecedentes
La implementación del almacenamiento de datos estructurados (trustgraph-flow/trustgraph/storage/objects/cassandra/) escribe objetos en tablas de Cassandra según las definiciones de esquema almacenadas en el sistema de configuración de TrustGraph. Estas tablas utilizan una estructura de clave de partición compuesta con claves primarias definidas por colección y esquema, lo que permite consultas eficientes dentro de las colecciones.
Limitaciones actuales que esta especificación aborda:
No hay una interfaz de consulta para los datos estructurados almacenados en Cassandra.
Incapacidad de aprovechar las poderosas capacidades de consulta de GraphQL para datos estructurados.
=======
**Consultas Eficientes de Cassandra**: Traducir las consultas GraphQL en consultas CQL eficientes de Cassandra, respetando las claves de partición y los índices.
**Resolución de Relaciones**: Soporte para los resolvedores de campos GraphQL para las relaciones entre diferentes tipos de objetos.
**Seguridad de Tipos**: Garantizar la ejecución de consultas y la generación de respuestas seguras, basadas en las definiciones del esquema.
**Rendimiento Escalable**: Manejar las consultas concurrentes de manera eficiente con un correcto agrupamiento de conexiones y optimización de consultas.
**Integración de Solicitud/Respuesta**: Mantener la compatibilidad con el patrón de solicitud/respuesta basado en Pulsar de TrustGraph.
**Manejo de Errores**: Proporcionar informes de errores completos para las discrepancias del esquema, los errores de consulta y los problemas de validación de datos.
## Antecedentes
La implementación del almacenamiento de datos estructurados (trustgraph-flow/trustgraph/storage/objects/cassandra/) escribe objetos en tablas de Cassandra según las definiciones de esquema almacenadas en el sistema de configuración de TrustGraph. Estas tablas utilizan una estructura de clave de partición compuesta con claves primarias definidas por la colección y el esquema, lo que permite consultas eficientes dentro de las colecciones.
Limitaciones actuales que esta especificación aborda:
No hay una interfaz de consulta para los datos estructurados almacenados en Cassandra.
Incapacidad de aprovechar las potentes capacidades de consulta de GraphQL para los datos estructurados.
>>>>>>> 82edf2d (New md files from RunPod)
Falta de soporte para la navegación de relaciones entre objetos relacionados.
Falta de un lenguaje de consulta estandarizado para el acceso a datos estructurados.
El servicio de consulta GraphQL cerrará estas brechas al:
Proporcionar una interfaz GraphQL estándar para consultar tablas de Cassandra.
Generar dinámicamente esquemas GraphQL a partir de la configuración de TrustGraph.
<<<<<<< HEAD
Traducir de manera eficiente las consultas GraphQL a CQL de Cassandra.
Soporte para la resolución de relaciones a través de resolutores de campos.
=======
Traducir de forma eficiente las consultas GraphQL a CQL de Cassandra.
Soporte para la resolución de relaciones a través de resolvedores de campos.
>>>>>>> 82edf2d (New md files from RunPod)
## Diseño Técnico
### Arquitectura
El servicio de consulta GraphQL se implementará como un nuevo procesador de flujo de TrustGraph, siguiendo patrones establecidos:
**Ubicación del Módulo**: `trustgraph-flow/trustgraph/query/objects/cassandra/`
**Componentes Clave**:
1. **Procesador del Servicio de Consulta GraphQL**
Extiende la clase base FlowProcessor.
Implementa un patrón de solicitud/respuesta similar a los servicios de consulta existentes.
Supervisa la configuración para las actualizaciones del esquema.
Mantiene el esquema GraphQL sincronizado con la configuración.
2. **Generador de Esquema Dinámico**
Convierte las definiciones de esquema de TrustGraph RowSchema en tipos GraphQL.
Crea tipos de objetos GraphQL con definiciones de campos adecuadas.
<<<<<<< HEAD
Genera el tipo de consulta raíz con resolutores basados en colecciones.
=======
Genera el tipo de consulta raíz con resolvedores basados en colecciones.
>>>>>>> 82edf2d (New md files from RunPod)
Actualiza el esquema GraphQL cuando cambia la configuración.
3. **Ejecutor de Consultas**
Analiza las consultas GraphQL entrantes utilizando la biblioteca Strawberry.
Valida las consultas contra el esquema actual.
Ejecuta las consultas y devuelve respuestas estructuradas.
Maneja los errores con elegancia con mensajes de error detallados.
4. **Traductor de Consultas de Cassandra**
Convierte las selecciones GraphQL en consultas CQL.
Optimiza las consultas según los índices y las claves de partición disponibles.
Maneja el filtrado, la paginación y la clasificación.
<<<<<<< HEAD
Administra el grupo de conexiones y el ciclo de vida de la sesión.
5. **Resolutor de Relaciones**
Implementa resolutores de campos para relaciones de objetos.
=======
Administra el agrupamiento de conexiones y el ciclo de vida de la sesión.
5. **Resolvedor de Relaciones**
Implementa resolvedores de campos para las relaciones de objetos.
>>>>>>> 82edf2d (New md files from RunPod)
Realiza una carga por lotes eficiente para evitar consultas N+1.
Almacena en caché las relaciones resueltas dentro del contexto de la solicitud.
Admite la navegación de relaciones tanto directa como inversa.
### Monitoreo del Esquema de Configuración
<<<<<<< HEAD
El servicio se registrará con un controlador de configuración para recibir actualizaciones de esquema:
=======
El servicio se registrará con un controlador de configuración para recibir actualizaciones del esquema:
>>>>>>> 82edf2d (New md files from RunPod)
```python
self.register_config_handler(self.on_schema_config)
```
Cuando los esquemas cambian:
1. Analizar las nuevas definiciones de esquema desde la configuración.
2. Regenerar los tipos y resolutores de GraphQL.
3. Actualizar el esquema ejecutable.
4. Limpiar cualquier caché dependiente del esquema.
### Generación de Esquema GraphQL
Para cada RowSchema en la configuración, generar:
1. **Tipo de Objeto GraphQL**:
Mapear tipos de campo (string → String, integer → Int, float → Float, boolean → Boolean).
Marcar los campos obligatorios como no anulables en GraphQL.
Agregar descripciones de campo desde el esquema.
2. **Campos de Consulta de Nivel Superior**:
Consulta de colección (por ejemplo, `customers`, `transactions`).
Argumentos de filtrado basados en campos indexados.
Soporte de paginación (límite, desplazamiento).
Opciones de ordenamiento para campos ordenables.
3. **Campos de Relación**:
Identificar relaciones de clave externa desde el esquema.
Crear resolutores de campo para objetos relacionados.
Soporte tanto para relaciones de objeto único como para listas.
### Flujo de Ejecución de Consulta
1. **Recepción de Solicitud**:
Recibir ObjectsQueryRequest de Pulsar.
Extraer la cadena de consulta GraphQL y las variables.
Identificar el contexto de usuario y colección.
2. **Validación de Consulta**:
Analizar la consulta GraphQL utilizando Strawberry.
Validar contra el esquema actual.
Comprobar las selecciones de campo y los tipos de argumentos.
3. **Generación de CQL**:
Analizar las selecciones de GraphQL.
Construir la consulta CQL con las cláusulas WHERE adecuadas.
Incluir la colección en la clave de partición.
Aplicar filtros basados en los argumentos de GraphQL.
4. **Ejecución de Consulta**:
Ejecutar la consulta CQL contra Cassandra.
Mapear los resultados a la estructura de respuesta de GraphQL.
Resolver cualquier campo de relación.
Formatear la respuesta de acuerdo con la especificación de GraphQL.
5. **Entrega de Respuesta**:
Crear una respuesta ObjectsQueryResponse con los resultados.
Incluir cualquier error de ejecución.
Enviar la respuesta a través de Pulsar con el ID de correlación.
### Modelos de Datos
<<<<<<< HEAD
> **Nota**: Existe un esquema existente de StructuredQueryRequest/Response en `trustgraph-base/trustgraph/schema/services/structured_query.py`. Sin embargo, le faltan campos críticos (usuario, colección) y utiliza tipos subóptimos. Los esquemas a continuación representan la evolución recomendada, que debe reemplazar los esquemas existentes o crearse como nuevos tipos de ObjectsQueryRequest/Response.
=======
> **Nota**: Existe un esquema existente de StructuredQueryRequest/Response en `trustgraph-base/trustgraph/schema/services/structured_query.py`. Sin embargo, carece de campos críticos (usuario, colección) y utiliza tipos subóptimos. Los esquemas a continuación representan la evolución recomendada, que debe reemplazar los esquemas existentes o crearse como nuevos tipos de ObjectsQueryRequest/Response.
>>>>>>> 82edf2d (New md files from RunPod)
#### Esquema de Solicitud (ObjectsQueryRequest)
```python
from pulsar.schema import Record, String, Map, Array
class ObjectsQueryRequest(Record):
user = String() # Cassandra keyspace (follows pattern from TriplesQueryRequest)
collection = String() # Data collection identifier (required for partition key)
query = String() # GraphQL query string
variables = Map(String()) # GraphQL variables (consider enhancing to support all JSON types)
operation_name = String() # Operation to execute for multi-operation documents
```
<<<<<<< HEAD
**Justificación de los cambios desde la solicitud de consulta estructurada existente:**
=======
**Justificación de los cambios con respecto a la solicitud de consulta estructurada existente:**
>>>>>>> 82edf2d (New md files from RunPod)
Se agregaron los campos `user` y `collection` para que coincidan con el patrón de otros servicios de consulta.
Estos campos son esenciales para identificar el espacio de claves de Cassandra y la colección.
Las variables permanecen como Map(String()) por ahora, pero idealmente deberían admitir todos los tipos JSON.
#### Esquema de respuesta (ObjectsQueryResponse)
```python
from pulsar.schema import Record, String, Array
from ..core.primitives import Error
class GraphQLError(Record):
message = String()
path = Array(String()) # Path to the field that caused the error
extensions = Map(String()) # Additional error metadata
class ObjectsQueryResponse(Record):
error = Error() # System-level error (connection, timeout, etc.)
data = String() # JSON-encoded GraphQL response data
errors = Array(GraphQLError) # GraphQL field-level errors
extensions = Map(String()) # Query metadata (execution time, etc.)
```
**Justificación de los cambios desde StructuredQueryResponse existente:**
Distingue entre errores del sistema (`error`) y errores de GraphQL (`errors`)
Utiliza objetos GraphQLError estructurados en lugar de un array de cadenas
Agrega el campo `extensions` para el cumplimiento de la especificación de GraphQL
Mantiene los datos como una cadena JSON para la compatibilidad, aunque los tipos nativos serían preferibles
### Optimización de consultas de Cassandra
El servicio optimizará las consultas de Cassandra mediante:
1. **Respetando las claves de partición:**
Siempre incluir la colección en las consultas
Utilizar eficientemente las claves primarias definidas en el esquema
Evitar escaneos completos de la tabla
2. **Aprovechando los índices:**
Utilizar índices secundarios para filtrar
Combinar múltiples filtros siempre que sea posible
<<<<<<< HEAD
Emitir una advertencia cuando las consultas puedan ser ineficientes
=======
Advertir cuando las consultas puedan ser ineficientes
>>>>>>> 82edf2d (New md files from RunPod)
3. **Carga por lotes:**
Recopilar consultas de relaciones
Ejecutar en lotes para reducir los viajes de ida y vuelta
Almacenar en caché los resultados dentro del contexto de la solicitud
4. **Administración de conexiones:**
Mantener sesiones de Cassandra persistentes
Utilizar un grupo de conexiones
Manejar la reconexión en caso de fallos
### Ejemplos de consultas GraphQL
#### Consulta simple de colección
```graphql
{
customers(status: "active") {
customer_id
name
email
registration_date
}
}
```
#### Consulta con Relaciones
```graphql
{
orders(order_date_gt: "2024-01-01") {
order_id
total_amount
customer {
name
email
}
items {
product_name
quantity
price
}
}
}
```
#### Consulta paginada
```graphql
{
products(limit: 20, offset: 40) {
product_id
name
price
category
}
}
```
### Dependencias de Implementación
**Strawberry GraphQL**: Para la definición del esquema GraphQL y la ejecución de consultas.
**Cassandra Driver**: Para la conectividad con la base de datos (ya utilizado en el módulo de almacenamiento).
**TrustGraph Base**: Para FlowProcessor y definiciones de esquema.
**Sistema de Configuración**: Para la supervisión y las actualizaciones del esquema.
### Interfaz de Línea de Comandos
El servicio proporcionará un comando de la CLI: `kg-query-objects-graphql-cassandra`
Argumentos:
`--cassandra-host`: Punto de contacto del clúster de Cassandra.
`--cassandra-username`: Nombre de usuario de autenticación.
`--cassandra-password`: Contraseña de autenticación.
`--config-type`: Tipo de configuración para los esquemas (por defecto: "schema").
Argumentos estándar de FlowProcessor (configuración de Pulsar, etc.).
## Integración de la API
### Temas de Pulsar
**Tema de entrada**: `objects-graphql-query-request`
Esquema: ObjectsQueryRequest
<<<<<<< HEAD
Recibe consultas GraphQL de los servicios de puerta de enlace.
=======
Recibe consultas GraphQL de los servicios de la puerta de enlace.
>>>>>>> 82edf2d (New md files from RunPod)
**Tema de salida**: `objects-graphql-query-response`
Esquema: ObjectsQueryResponse
Devuelve los resultados de la consulta y los errores.
### Integración de la puerta de enlace
La puerta de enlace y la puerta de enlace inversa necesitarán puntos finales para:
1. Aceptar consultas GraphQL de los clientes.
2. Enviar a través de Pulsar al servicio de consulta.
<<<<<<< HEAD
3. Devolver respuestas a los clientes.
4. Compatibilidad con consultas de introspección de GraphQL.
=======
3. Devolver las respuestas a los clientes.
4. Compatibilidad con las consultas de introspección de GraphQL.
>>>>>>> 82edf2d (New md files from RunPod)
### Integración de la herramienta de agente
Una nueva clase de herramienta de agente permitirá:
Generación de consultas GraphQL a partir de lenguaje natural.
Ejecución directa de consultas GraphQL.
Interpretación y formato de resultados.
<<<<<<< HEAD
Integración con flujos de decisión de agentes.
=======
Integración con los flujos de decisión del agente.
>>>>>>> 82edf2d (New md files from RunPod)
## Consideraciones de seguridad
**Limitación de la profundidad de la consulta**: Prevenir consultas profundamente anidadas que puedan causar problemas de rendimiento.
**Análisis de la complejidad de la consulta**: Limitar la complejidad de la consulta para evitar el agotamiento de recursos.
**Permisos a nivel de campo**: Futuro soporte para el control de acceso a nivel de campo basado en los roles de usuario.
**Saneamiento de entrada**: Validar y limpiar todas las entradas de la consulta para prevenir ataques de inyección.
**Limitación de velocidad**: Implementar la limitación de velocidad de las consultas por usuario/colección.
## Consideraciones de rendimiento
**Planificación de consultas**: Analizar las consultas antes de la ejecución para optimizar la generación de CQL.
<<<<<<< HEAD
**Caché de resultados**: Considerar el almacenamiento en caché de datos accedidos con frecuencia a nivel del resolutor de campos.
**Creación de grupos de conexiones**: Mantener grupos de conexiones eficientes a Cassandra.
**Operaciones por lotes**: Combinar múltiples consultas cuando sea posible para reducir la latencia.
=======
**Caché de resultados**: Considerar el almacenamiento en caché de los datos accedidos con frecuencia a nivel del resolutor de campos.
**Creación de grupos de conexiones**: Mantener grupos de conexiones eficientes a Cassandra.
**Operaciones por lotes**: Combinar múltiples consultas siempre que sea posible para reducir la latencia.
>>>>>>> 82edf2d (New md files from RunPod)
**Supervisión**: Supervisar las métricas de rendimiento de las consultas para la optimización.
## Estrategia de pruebas
### Pruebas Unitarias
Generación de esquemas a partir de definiciones de RowSchema
Análisis y validación de consultas GraphQL
Lógica de generación de consultas CQL
Implementaciones de resolutores de campos
### Pruebas de Contrato
Cumplimiento del contrato de mensajes Pulsar
Validez del esquema GraphQL
Verificación del formato de respuesta
Validación de la estructura de errores
### Pruebas de Integración
Ejecución de consultas de extremo a extremo contra una instancia de prueba de Cassandra
Manejo de actualizaciones de esquema
Resolución de relaciones
Paginación y filtrado
Escenarios de error
### Pruebas de Rendimiento
Rendimiento de consultas bajo carga
Tiempo de respuesta para diversas complejidades de consulta
Uso de memoria con conjuntos de resultados grandes
Eficiencia del pool de conexiones
## Plan de Migración
No se requiere migración, ya que se trata de una nueva funcionalidad. El servicio:
1. Leerá los esquemas existentes desde la configuración
2. Se conectará a las tablas de Cassandra existentes creadas por el módulo de almacenamiento
3. Comenzará a aceptar consultas inmediatamente después de la implementación
## Cronograma
Semana 1-2: Implementación del servicio principal y generación de esquemas
Semana 3: Ejecución de consultas y traducción de CQL
Semana 4: Resolución de relaciones y optimización
Semana 5: Pruebas y ajuste de rendimiento
Semana 6: Integración con la puerta de enlace y documentación
## Preguntas Abiertas
1. **Evolución del Esquema**: ¿Cómo debe el servicio manejar las consultas durante las transiciones de esquema?
Opción: Encolar consultas durante las actualizaciones de esquema
Opción: Compatibilidad con múltiples versiones de esquema simultáneamente
2. **Estrategia de Caché**: ¿Deben almacenarse en caché los resultados de las consultas?
Considerar: Expiración basada en el tiempo
Considerar: Invalidación basada en eventos
3. **Soporte de Federación**: ¿Debe el servicio admitir la federación de GraphQL para combinarlo con otras fuentes de datos?
Permitiría consultas unificadas a través de datos estructurados y de grafos
4. **Soporte de Suscripciones**: ¿Debe el servicio admitir suscripciones de GraphQL para actualizaciones en tiempo real?
Requeriría soporte de WebSocket en la puerta de enlace
5. **Escalares Personalizados**: ¿Se deben admitir tipos de datos escalares personalizados para tipos de datos específicos del dominio?
Ejemplos: DateTime, UUID, campos JSON
## Referencias
Especificación Técnica de Datos Estructurados: `docs/tech-specs/structured-data.md`
Documentación de Strawberry GraphQL: https://strawberry.rocks/
Especificación de GraphQL: https://spec.graphql.org/
<<<<<<< HEAD
Referencia de CQL de Apache Cassandra: https://cassandra.apache.org/doc/stable/cassandra/cql/
=======
Referencia CQL de Apache Cassandra: https://cassandra.apache.org/doc/stable/cassandra/cql/
>>>>>>> 82edf2d (New md files from RunPod)
Documentación del Procesador de Flujo de TrustGraph: Documentación interna

View file

@ -0,0 +1,309 @@
---
layout: default
title: "Especificación Técnica de Optimización de Rendimiento de GraphRAG"
parent: "Spanish (Beta)"
---
# Especificación Técnica de Optimización de Rendimiento de GraphRAG
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
Esta especificación describe optimizaciones de rendimiento integrales para el algoritmo GraphRAG (Graph Retrieval-Augmented Generation) en TrustGraph. La implementación actual sufre de cuellos de botella de rendimiento significativos que limitan la escalabilidad y los tiempos de respuesta. Esta especificación aborda cuatro áreas principales de optimización:
1. **Optimización de la Recorrido del Grafo**: Eliminar consultas ineficientes a la base de datos y implementar exploración de grafos por lotes.
2. **Optimización de la Resolución de Etiquetas**: Reemplazar la obtención secuencial de etiquetas con operaciones paralelas/por lotes.
3. **Mejora de la Estrategia de Almacenamiento en Caché**: Implementar un almacenamiento en caché inteligente con desalojo LRU (Least Recently Used) y precarga.
4. **Optimización de Consultas**: Agregar memorización de resultados y almacenamiento en caché de incrustaciones para mejorar los tiempos de respuesta.
## Objetivos
- **Reducir el Volumen de Consultas a la Base de Datos**: Lograr una reducción del 50-80% en el volumen total de consultas a la base de datos a través del procesamiento por lotes y el almacenamiento en caché.
- **Mejorar los Tiempos de Respuesta**: Reducir el tiempo de construcción de subgrafos en un factor de 3 a 5 y el tiempo de resolución de etiquetas en un factor de 2 a 3.
- **Mejorar la Escalabilidad**: Compatibilizar con grafos de conocimiento más grandes con una mejor gestión de la memoria.
- **Mantener la Precisión**: Preservar la funcionalidad existente de GraphRAG y la calidad de los resultados.
- **Habilitar la Concurrencia**: Mejorar las capacidades de procesamiento paralelo para múltiples solicitudes concurrentes.
- **Reducir la Huella de Memoria**: Implementar estructuras de datos eficientes y gestión de la memoria.
- **Agregar Observabilidad**: Incluir métricas de rendimiento y capacidades de monitoreo.
- **Garantizar la Confiabilidad**: Agregar un manejo adecuado de errores y mecanismos de tiempo de espera.
## Antecedentes
La implementación actual de GraphRAG en `trustgraph-flow/trustgraph/retrieval/graph_rag/graph_rag.py` presenta varios problemas de rendimiento críticos que afectan gravemente la escalabilidad del sistema:
### Problemas de Rendimiento Actuales
**1. Recorrido Ineficiente del Grafo (`follow_edges` function, líneas 79-127)**
- Realiza 3 consultas separadas a la base de datos por entidad y nivel de profundidad.
- Patrón de consulta: consultas basadas en sujeto, predicado y objeto para cada entidad.
- Sin procesamiento por lotes: Cada consulta procesa solo una entidad a la vez.
- Sin detección de ciclos: Puede volver a visitar los mismos nodos varias veces.
- La implementación recursiva sin memorización conduce a una complejidad exponencial.
- Complejidad temporal: O(entidades × max_path_length × triple_limit³)
**2. Resolución Secuencial de Etiquetas (`get_labelgraph` function, líneas 144-171)**
- Procesa cada componente de triple (sujeto, predicado, objeto) de forma secuencial.
- Cada llamada a `maybe_label` potencialmente desencadena una consulta a la base de datos.
- No hay ejecución paralela ni procesamiento por lotes de las consultas de etiquetas.
- Resulta en hasta 3 consultas individuales a la base de datos por tamaño del subgrafo.
**3. Estrategia de Almacenamiento en Caché Primitiva (`maybe_label` function, líneas 62-77)**
- Un diccionario de caché simple sin límites de tamaño ni TTL (Time To Live).
- No hay política de desalojo de caché, lo que lleva a un crecimiento ilimitado de la memoria.
- Las fallas de caché desencadenan consultas individuales a la base de datos.
- No hay precarga ni calentamiento inteligente de la caché.
**4. Patrones de Consulta Subóptimos**
- Las consultas de similitud de vectores de entidades no se almacenan en caché entre solicitudes similares.
- No hay memorización de resultados para patrones de consulta repetidos.
- Faltan optimizaciones de consulta para patrones de acceso comunes.
**5. Problemas Críticos del Ciclo de Vida de los Objetos (`rag.py:96-102`)**
- **El objeto GraphRag se recrea por solicitud**: Se crea una nueva instancia para cada consulta, perdiendo todos los beneficios de la caché.
- **El objeto Query tiene una vida útil extremadamente corta**: Se crea y destruye dentro de la ejecución de una sola consulta (líneas 201-207).
- **La caché de etiquetas se restablece por solicitud**: El calentamiento de la caché y el conocimiento acumulado se pierden entre las solicitudes.
- **Sobrecarga de recreación del cliente**: Los clientes de la base de datos se restablecen potencialmente para cada solicitud.
- **No hay optimización entre solicitudes**: No se puede beneficiar de patrones de consulta o uso compartido de resultados.
### Análisis del Impacto en el Rendimiento
Escenario de peor caso actual para una consulta típica:
- **Recuperación de Entidades**: 1 consulta de similitud de vectores.
- **Recorrido del Grafo**: consultas de entidades × max_path_length × 3 × triple_limit.
- **Resolución de Etiquetas**: consultas individuales de tamaño del subgrafo × 3 de etiquetas.
Para parámetros predeterminados (50 entidades, longitud de ruta 2, límite de triple 30, tamaño de subgrafo 150):
- **Número mínimo de consultas**: 1 + (50 × 2 × 3 × 30) + (150 × 3) = **9.451 consultas a la base de datos**.
- **Tiempo de respuesta**: 15-30 segundos para grafos de tamaño moderado.
- **Uso de memoria**: Crecimiento ilimitado de la caché con el tiempo.
- **Eficacia de la caché**: 0% - las cachés se restablecen en cada solicitud.
- **Sobrecarga de creación de objetos**: Se crean/destruyen objetos GraphRag + Query por solicitud.
Esta especificación aborda estas deficiencias implementando consultas por lotes, almacenamiento en caché inteligente y procesamiento paralelo. Al optimizar los patrones de consulta y el acceso a los datos, TrustGraph puede:
- Compatibilizar con grafos de conocimiento a escala empresarial con millones de entidades.
- Proporcionar tiempos de respuesta de subsegundo para consultas típicas.
- Manejar cientos de solicitudes concurrentes de GraphRAG.
- Escalar de manera eficiente con el tamaño y la complejidad del grafo.
## Diseño Técnico
### Arquitectura
La optimización del rendimiento de GraphRAG requiere los siguientes componentes técnicos:
#### 1. **Refactorización Arquitectónica del Ciclo de Vida de los Objetos**
- **Hacer que GraphRag tenga una vida útil prolongada**: Mover la instancia de GraphRag al nivel del Procesador para la persistencia entre solicitudes.
- **Preservar las cachés**: Mantener la caché de etiquetas, la caché de incrustaciones y la caché de resultados de consulta entre las solicitudes.
- **Optimizar el objeto Query**: Refactorizar Query como un contexto de ejecución ligero, no como un contenedor de datos.
- **Persistencia de la conexión**: Mantener las conexiones del cliente de la base de datos entre las solicitudes.
Módulo: `trustgraph-flow/trustgraph/retrieval/graph_rag/rag.py` (modificado)
#### 2. **Motor de Recorrido del Grafo Optimizada**
- Reemplazar `follow_edges` recursivo con una búsqueda en amplitud iterativa.
- Implementar procesamiento por lotes de entidades en cada nivel de recorrido.
- Agregar detección de ciclos utilizando el seguimiento de nodos visitados.
- Incluir finalización temprana cuando se alcanzan los límites.
Módulo: `trustgraph-flow/trustgraph/retrieval/graph_rag/optimized_traversal.py`
#### 3. **Sistema de Resolución de Etiquetas Paralelo**
- Consultas de etiquetas por lotes para múltiples entidades simultáneamente.
- Implementar patrones async/await para el acceso concurrente a la base de datos.
- Agregar precarga inteligente para patrones de etiquetas comunes.
- Incluir estrategias de calentamiento de etiquetas.
Módulo: `trustgraph-flow/trustgraph/retrieval/graph_rag/label_resolver.py`
#### 4. **Capa de Almacenamiento en Caché Conservadora**
- Caché LRU con un TTL (Tiempo de Vida) corto para las etiquetas (5 minutos) para equilibrar el rendimiento y la coherencia.
- Métricas de caché y monitoreo de la tasa de aciertos.
- **No se almacena en caché las incrustaciones**: Ya se almacenan en caché por consulta, no hay beneficio entre consultas.
- **No se almacenan en caché los resultados de la consulta**: Debido a las preocupaciones sobre la consistencia de la mutación del grafo.
Módulo: `trustgraph-flow/trustgraph/retrieval/graph_rag/cache_manager.py`
#### 5. **Marco de Optimización de Consultas**
- Análisis y sugerencias de optimización de patrones de consulta.
- Coordinador de consultas por lotes para el acceso a la base de datos.
- Pooling de conexiones y gestión de tiempos de espera de consulta.
- Monitoreo de rendimiento y recopilación de métricas.
Módulo: `trustgraph-flow/trustgraph/retrieval/graph_rag/query_optimizer.py`
### Modelos de Datos
#### Estado de Recorrido del Grafo Optimizada
El motor de recorrido mantiene el siguiente estado:
```python
@dataclass
class GraphTraversalState:
current_node: Node
path: List[Node]
visited_nodes: Set[Node]
```
#### Caching
```python
class Cache:
def __init__(self, max_size: int):
self.cache = {}
self.max_size = max_size
def get(self, key):
if key in self.cache:
return self.cache.pop(key)
return None
def put(self, key, value):
if len(self.cache) >= self.max_size:
# Remove least recently used
oldest_key = next(iter(self.cache))
del self.cache[oldest_key]
self.cache[key] = value
```
#### PerformanceMetrics
```python
@dataclass
class PerformanceMetrics:
total_queries: int
cache_hits: int
cache_misses: int
avg_response_time: float
subgraph_construction_time: float
label_resolution_time: float
total_entities_processed: int
memory_usage_mb: float
```
### Seguridad
**Prevenir Inyección de Consultas:**
- Validar todos los identificadores de entidad y parámetros de consulta.
- Usar consultas parametrizadas para todas las interacciones con la base de datos.
- Implementar límites de complejidad de consulta para prevenir ataques de denegación de servicio.
**Protección de Recursos:**
- Hacer cumplir los límites máximos del tamaño del subgrafo.
- Implementar tiempos de espera de consulta para evitar el agotamiento de recursos.
- Agregar monitoreo del uso de la memoria y límites.
**Control de Acceso:**
- Mantener el aislamiento existente de usuarios y colecciones.
- Agregar registro de auditoría para operaciones que afectan el rendimiento.
- Implementar la limitación de velocidad para las operaciones costosas.
## Consideraciones de Rendimiento
### Mejoras de Rendimiento Esperadas
**Reducción de Consultas:**
- Actual: ~9.000+ consultas por solicitud típica.
- Optimizada: ~50-100 consultas por lotes (reducción del 98%).
**Mejoras en el Tiempo de Respuesta:**
- Recorrido del grafo: 15-20s → 3-5s (4-5 veces más rápido).
- Resolución de etiquetas: 8-12s → 2-4s (3 veces más rápido).
- Consulta general: 25-35s → 6-10s (mejora de 3-4 veces).
**Eficiencia de la Memoria:**
- Los tamaños de caché limitados evitan las fugas de memoria.
- Las estructuras de datos eficientes reducen la huella de memoria en un ~40%.
- Mejor recolección de basura a través de una limpieza adecuada de recursos.
**Mejoras de Escalabilidad:**
- Compatibilidad con grafos de conocimiento 3-5 veces más grandes (limitado por las necesidades de coherencia de la caché).
- Capacidad de solicitud concurrente 3-5 veces mayor.
- Mejor utilización de recursos a través de la reutilización de conexiones.
### Monitoreo del Rendimiento
**Métricas en Tiempo Real:**
- Tiempos de ejecución de operaciones por tipo de operación.
- Tasas de aciertos y efectividad de la caché.
- Utilización del grupo de conexiones de la base de datos.
- Uso de memoria y impacto de la recolección de basura.
**Pruebas de Rendimiento:**
- Pruebas comparativas contra la implementación actual.
- Pruebas de carga con volúmenes de datos realistas.
- Pruebas de estrés para límites de memoria y conexión.
- Pruebas de regresión para mejoras de rendimiento.
## Estrategia de Pruebas
### Pruebas Unitarias
- Pruebas de componentes individuales para recorrido, almacenamiento en caché y resolución de etiquetas.
- Interacciones simuladas con la base de datos para pruebas de rendimiento.
- Pruebas de desalojo y expiración de TTL de la caché.
- Escenarios de manejo de errores y tiempo de espera.
### Pruebas de Integración
- Pruebas de extremo a extremo de la consulta GraphRAG con optimizaciones.
- Pruebas de interacción con la base de datos con datos reales.
- Manejo de solicitudes concurrentes y gestión de recursos.
- Detección de fugas de memoria y verificación de limpieza de recursos.
### Pruebas de Rendimiento
- Pruebas comparativas contra la implementación actual.
- Pruebas de carga con diferentes tamaños y complejidades de grafos.
- Pruebas de estrés para límites de memoria y conexión.
- Pruebas de regresión para mejoras de rendimiento.
### Pruebas de Compatibilidad
- Verificar la compatibilidad de la API GraphRAG existente.
- Probar con varios backends de bases de datos de grafos.
- Validar la precisión de los resultados en comparación con la implementación actual.
## Plan de Implementación
### Enfoque de Implementación Directa
Dado que se permiten cambios en las API, implemente optimizaciones directamente sin complejidad de migración:
1. **Reemplace el método `follow_edges`**: Reescriba con un recorrido iterativo por lotes.
2. **Optimice `get_labelgraph`**: Implemente la resolución de etiquetas en paralelo.
3. **Agregue GraphRag de larga duración**: Modifique el Procesador para usar una instancia persistente.
4. **Implemente la estrategia de almacenamiento en caché**: Agregue una capa de caché LRU a la clase GraphRag.
### Alcance de los Cambios
- **Clase Query**: Reemplace ~50 líneas en `follow_edges`, agregue ~30 líneas de manejo por lotes.
- **Clase GraphRag**: Agregue una capa de almacenamiento en caché (~40 líneas).
- **Clase Procesador**: Modifique para usar una instancia GraphRag persistente (~20 líneas).
- **Total**: ~140 líneas de cambios enfocados, principalmente dentro de clases existentes.
## Cronograma
**Semana 1: Implementación Central**
- Reemplace `follow_edges` con un recorrido iterativo por lotes.
- Implemente la resolución de etiquetas en paralelo en `get_labelgraph`.
- Agregue una instancia GraphRag de larga duración al Procesador.
- Implemente la capa de almacenamiento en caché.
**Semana 2: Pruebas e Integración**
- Pruebas unitarias para la nueva lógica de recorrido y almacenamiento en caché.
- Pruebas comparativas de rendimiento con la implementación actual.
- Pruebas de integración con datos de grafo reales.
- Revisión de código y optimización.
**Semana 3: Implementación**
- Implemente la implementación optimizada.
- Monitoree las mejoras de rendimiento.
- Ajuste el TTL y los tamaños de lote de la caché en función del uso real.
## Preguntas Abiertas
- **Pooling de Conexiones de la Base de Datos**: ¿Deberíamos implementar un pooling de conexiones personalizado o confiar en el pooling de clientes de la base de datos existente?
- **Persistencia de la Caché**: ¿Deberían las cachés de etiquetas y de incrustaciones persistir entre reinicios del servicio?
- **Almacenamiento en Caché Distribuido**: Para implementaciones multi instancia, ¿deberíamos implementar un almacenamiento en caché distribuido con Redis/Memcached?
- **Formato de Resultados de Consulta**: ¿Deberíamos optimizar la representación interna del triple para una mejor eficiencia de la memoria?
- **Integración de Monitoreo**: ¿Qué métricas deben exponerse a los sistemas de monitoreo existentes (Prometheus, etc.)?
## Referencias
- [Implementación Original de GraphRAG](trustgraph-flow/trustgraph/retrieval/graph_rag/graph_rag.py)
- [Principios de Arquitectura de TrustGraph](architecture-principles.md)
- [Especificación de Gestión de Colecciones](collection-management.md)

View file

@ -0,0 +1,725 @@
---
layout: default
title: "Especificación Técnica para el Cierre de Sesión Gratuito de Importación/Exportación"
parent: "Spanish (Beta)"
---
<<<<<<< HEAD
# Especificación Técnica para el Cierre de Sesión Gratuito de Importación/Exportación
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Declaración del Problema
Actualmente, la puerta de enlace TrustGraph experimenta pérdida de mensajes durante el cierre de la conexión websocket tanto en las operaciones de importación como de exportación. Esto ocurre debido a condiciones de carrera donde los mensajes en tránsito se descartan antes de llegar a su destino (colas Pulsar para importaciones, clientes websocket para exportaciones).
### Problemas del Lado de Importación
1. El búfer de la cola asyncio del publicador no se vacía durante el apagado.
2. La conexión websocket se cierra antes de asegurarse de que los mensajes en cola lleguen a Pulsar.
3. No hay un mecanismo de confirmación para la entrega exitosa de mensajes.
### Problemas del Lado de Exportación
=======
# Especificación Técnica para el Cierre Gratuito de Importación/Exportación
## Declaración del Problema
Actualmente, la puerta de enlace TrustGraph experimenta pérdida de mensajes durante el cierre de WebSocket tanto en las operaciones de importación como de exportación. Esto ocurre debido a condiciones de carrera donde los mensajes en tránsito se descartan antes de llegar a su destino (colas de Pulsar para importaciones, clientes de WebSocket para exportaciones).
### Problemas del Lado de la Importación
1. El búfer de la cola asyncio del publicador no se vacía durante el apagado.
2. El WebSocket se cierra antes de asegurarse de que los mensajes en cola lleguen a Pulsar.
3. No hay un mecanismo de confirmación para la entrega exitosa de mensajes.
### Problemas del Lado de la Exportación
>>>>>>> 82edf2d (New md files from RunPod)
1. Los mensajes se confirman en Pulsar antes de la entrega exitosa a los clientes.
2. Los tiempos de espera codificados de forma rígida causan la pérdida de mensajes cuando las colas están llenas.
3. No hay un mecanismo de retroalimentación para manejar consumidores lentos.
4. Múltiples puntos de búfer donde los datos pueden perderse.
## Descripción General de la Arquitectura
```
Import Flow:
Client -> Websocket -> TriplesImport -> Publisher -> Pulsar Queue
Export Flow:
Pulsar Queue -> Subscriber -> TriplesExport -> Websocket -> Client
```
## Soluciones propuestas
### 1. Mejoras para el publicador (lado de importación)
#### A. Vaciado de la cola de forma gradual
**Archivo**: `trustgraph-base/trustgraph/base/publisher.py`
```python
class Publisher:
def __init__(self, client, topic, schema=None, max_size=10,
chunking_enabled=True, drain_timeout=5.0):
self.client = client
self.topic = topic
self.schema = schema
self.q = asyncio.Queue(maxsize=max_size)
self.chunking_enabled = chunking_enabled
self.running = True
self.draining = False # New state for graceful shutdown
self.task = None
self.drain_timeout = drain_timeout
async def stop(self):
"""Initiate graceful shutdown with draining"""
self.running = False
self.draining = True
if self.task:
# Wait for run() to complete draining
await self.task
async def run(self):
"""Enhanced run method with integrated draining logic"""
while self.running or self.draining:
try:
producer = self.client.create_producer(
topic=self.topic,
schema=JsonSchema(self.schema),
chunking_enabled=self.chunking_enabled,
)
drain_end_time = None
while self.running or self.draining:
try:
# Start drain timeout when entering drain mode
if self.draining and drain_end_time is None:
drain_end_time = time.time() + self.drain_timeout
logger.info(f"Publisher entering drain mode, timeout={self.drain_timeout}s")
# Check drain timeout
if self.draining and time.time() > drain_end_time:
if not self.q.empty():
logger.warning(f"Drain timeout reached with {self.q.qsize()} messages remaining")
self.draining = False
break
# Calculate wait timeout based on mode
if self.draining:
# Shorter timeout during draining to exit quickly when empty
timeout = min(0.1, drain_end_time - time.time())
else:
# Normal operation timeout
timeout = 0.25
# Get message from queue
id, item = await asyncio.wait_for(
self.q.get(),
timeout=timeout
)
# Send the message (single place for sending)
if id:
producer.send(item, { "id": id })
else:
producer.send(item)
except asyncio.TimeoutError:
# If draining and queue is empty, we're done
if self.draining and self.q.empty():
logger.info("Publisher queue drained successfully")
self.draining = False
break
continue
except asyncio.QueueEmpty:
# If draining and queue is empty, we're done
if self.draining and self.q.empty():
logger.info("Publisher queue drained successfully")
self.draining = False
break
continue
# Flush producer before closing
if producer:
producer.flush()
producer.close()
except Exception as e:
logger.error(f"Exception in publisher: {e}", exc_info=True)
if not self.running and not self.draining:
return
# If handler drops out, sleep a retry
await asyncio.sleep(1)
async def send(self, id, item):
"""Send still works normally - just adds to queue"""
if self.draining:
# Optionally reject new messages during drain
raise RuntimeError("Publisher is shutting down, not accepting new messages")
await self.q.put((id, item))
```
**Beneficios Clave del Diseño:**
**Ubicación Única para el Envío**: Todas las llamadas a `producer.send()` se realizan en un solo lugar dentro del método `run()`.
**Máquina de Estados Clara**: Tres estados claros: en ejecución, en drenaje, detenido.
**Protección contra Tiempo de Espera**: No se quedará bloqueado indefinidamente durante el drenaje.
**Mejor Observabilidad**: Registro claro del progreso del drenaje y las transiciones de estado.
**Rechazo de Mensajes Opcional**: Puede rechazar nuevos mensajes durante la fase de apagado.
#### B. Orden de Apagado Mejorado
**Archivo**: `trustgraph-flow/trustgraph/gateway/dispatch/triples_import.py`
```python
class TriplesImport:
async def destroy(self):
"""Enhanced destroy with proper shutdown order"""
# Step 1: Stop accepting new messages
self.running.stop()
# Step 2: Wait for publisher to drain its queue
logger.info("Draining publisher queue...")
await self.publisher.stop()
# Step 3: Close websocket only after queue is drained
if self.ws:
await self.ws.close()
```
### 2. Mejoras para el Suscriptor (Lado de Exportación)
#### A. Patrón de Drenaje Integrado
**Archivo**: `trustgraph-base/trustgraph/base/subscriber.py`
```python
class Subscriber:
def __init__(self, client, topic, subscription, consumer_name,
schema=None, max_size=100, metrics=None,
backpressure_strategy="block", drain_timeout=5.0):
# ... existing init ...
self.backpressure_strategy = backpressure_strategy
self.running = True
self.draining = False # New state for graceful shutdown
self.drain_timeout = drain_timeout
self.pending_acks = {} # Track messages awaiting delivery
async def stop(self):
"""Initiate graceful shutdown with draining"""
self.running = False
self.draining = True
if self.task:
# Wait for run() to complete draining
await self.task
async def run(self):
"""Enhanced run method with integrated draining logic"""
while self.running or self.draining:
if self.metrics:
self.metrics.state("stopped")
try:
self.consumer = self.client.subscribe(
topic = self.topic,
subscription_name = self.subscription,
consumer_name = self.consumer_name,
schema = JsonSchema(self.schema),
)
if self.metrics:
self.metrics.state("running")
logger.info("Subscriber running...")
drain_end_time = None
while self.running or self.draining:
# Start drain timeout when entering drain mode
if self.draining and drain_end_time is None:
drain_end_time = time.time() + self.drain_timeout
logger.info(f"Subscriber entering drain mode, timeout={self.drain_timeout}s")
# Stop accepting new messages from Pulsar during drain
self.consumer.pause_message_listener()
# Check drain timeout
if self.draining and time.time() > drain_end_time:
async with self.lock:
total_pending = sum(
q.qsize() for q in
list(self.q.values()) + list(self.full.values())
)
if total_pending > 0:
logger.warning(f"Drain timeout reached with {total_pending} messages in queues")
self.draining = False
break
# Check if we can exit drain mode
if self.draining:
async with self.lock:
all_empty = all(
q.empty() for q in
list(self.q.values()) + list(self.full.values())
)
if all_empty and len(self.pending_acks) == 0:
logger.info("Subscriber queues drained successfully")
self.draining = False
break
# Process messages only if not draining
if not self.draining:
try:
msg = await asyncio.to_thread(
self.consumer.receive,
timeout_millis=250
)
except _pulsar.Timeout:
continue
except Exception as e:
logger.error(f"Exception in subscriber receive: {e}", exc_info=True)
raise e
if self.metrics:
self.metrics.received()
# Process the message
await self._process_message(msg)
else:
# During draining, just wait for queues to empty
await asyncio.sleep(0.1)
except Exception as e:
logger.error(f"Subscriber exception: {e}", exc_info=True)
finally:
# Negative acknowledge any pending messages
for msg in self.pending_acks.values():
self.consumer.negative_acknowledge(msg)
self.pending_acks.clear()
if self.consumer:
self.consumer.unsubscribe()
self.consumer.close()
self.consumer = None
if self.metrics:
self.metrics.state("stopped")
if not self.running and not self.draining:
return
# If handler drops out, sleep a retry
await asyncio.sleep(1)
async def _process_message(self, msg):
"""Process a single message with deferred acknowledgment"""
# Store message for later acknowledgment
msg_id = str(uuid.uuid4())
self.pending_acks[msg_id] = msg
try:
id = msg.properties()["id"]
except:
id = None
value = msg.value()
delivery_success = False
async with self.lock:
# Deliver to specific subscribers
if id in self.q:
delivery_success = await self._deliver_to_queue(
self.q[id], value
)
# Deliver to all subscribers
for q in self.full.values():
if await self._deliver_to_queue(q, value):
delivery_success = True
# Acknowledge only on successful delivery
if delivery_success:
self.consumer.acknowledge(msg)
del self.pending_acks[msg_id]
else:
# Negative acknowledge for retry
self.consumer.negative_acknowledge(msg)
del self.pending_acks[msg_id]
async def _deliver_to_queue(self, queue, value):
"""Deliver message to queue with backpressure handling"""
try:
if self.backpressure_strategy == "block":
# Block until space available (no timeout)
await queue.put(value)
return True
elif self.backpressure_strategy == "drop_oldest":
# Drop oldest message if queue full
if queue.full():
try:
queue.get_nowait()
if self.metrics:
self.metrics.dropped()
except asyncio.QueueEmpty:
pass
await queue.put(value)
return True
elif self.backpressure_strategy == "drop_new":
# Drop new message if queue full
if queue.full():
if self.metrics:
self.metrics.dropped()
return False
await queue.put(value)
return True
except Exception as e:
logger.error(f"Failed to deliver message: {e}")
return False
```
**Beneficios clave del diseño (que coinciden con el patrón del editor):**
<<<<<<< HEAD
**Ubicación de procesamiento única**: Todo el procesamiento de mensajes se realiza en el método `run()`.
**Máquina de estados limpia**: Tres estados claros: en ejecución, vaciado, detenido.
**Pausa durante el vaciado**: Deja de aceptar nuevos mensajes de Pulsar mientras vacía las colas existentes.
**Protección por tiempo de espera**: No se quedará bloqueado indefinidamente durante el vaciado.
**Limpieza adecuada**: Reconoce negativamente cualquier mensaje no entregado al apagar.
=======
**Ubicación de procesamiento única**: Todo el procesamiento de mensajes se realiza en el método `run()`
**Máquina de estados limpia**: Tres estados claros: en ejecución, vaciado, detenido
**Pausa durante el vaciado**: Deja de aceptar nuevos mensajes de Pulsar mientras vacía las colas existentes
**Protección por tiempo de espera**: No se quedará bloqueado indefinidamente durante el vaciado
**Limpieza adecuada**: Reconoce negativamente cualquier mensaje no entregado al apagar
>>>>>>> 82edf2d (New md files from RunPod)
#### B. Mejoras del controlador de exportación
**Archivo**: `trustgraph-flow/trustgraph/gateway/dispatch/triples_export.py`
```python
class TriplesExport:
async def destroy(self):
"""Enhanced destroy with graceful shutdown"""
# Step 1: Signal stop to prevent new messages
self.running.stop()
# Step 2: Wait briefly for in-flight messages
await asyncio.sleep(0.5)
# Step 3: Unsubscribe and stop subscriber (triggers queue drain)
if hasattr(self, 'subs'):
await self.subs.unsubscribe_all(self.id)
await self.subs.stop()
# Step 4: Close websocket last
if self.ws and not self.ws.closed:
await self.ws.close()
async def run(self):
"""Enhanced run with better error handling"""
self.subs = Subscriber(
client = self.pulsar_client,
topic = self.queue,
consumer_name = self.consumer,
subscription = self.subscriber,
schema = Triples,
backpressure_strategy = "block" # Configurable
)
await self.subs.start()
self.id = str(uuid.uuid4())
q = await self.subs.subscribe_all(self.id)
consecutive_errors = 0
max_consecutive_errors = 5
while self.running.get():
try:
resp = await asyncio.wait_for(q.get(), timeout=0.5)
await self.ws.send_json(serialize_triples(resp))
consecutive_errors = 0 # Reset on success
except asyncio.TimeoutError:
continue
except queue.Empty:
continue
except Exception as e:
logger.error(f"Exception sending to websocket: {str(e)}")
consecutive_errors += 1
if consecutive_errors >= max_consecutive_errors:
logger.error("Too many consecutive errors, shutting down")
break
# Brief pause before retry
await asyncio.sleep(0.1)
# Graceful cleanup handled in destroy()
```
### 3. Mejoras a nivel de socket
**Archivo**: `trustgraph-flow/trustgraph/gateway/endpoint/socket.py`
```python
class SocketEndpoint:
async def listener(self, ws, dispatcher, running):
"""Enhanced listener with graceful shutdown"""
async for msg in ws:
if msg.type == WSMsgType.TEXT:
await dispatcher.receive(msg)
continue
elif msg.type == WSMsgType.BINARY:
await dispatcher.receive(msg)
continue
else:
# Graceful shutdown on close
logger.info("Websocket closing, initiating graceful shutdown")
running.stop()
# Allow time for dispatcher cleanup
await asyncio.sleep(1.0)
break
async def handle(self, request):
"""Enhanced handler with better cleanup"""
# ... existing setup code ...
try:
async with asyncio.TaskGroup() as tg:
running = Running()
dispatcher = await self.dispatcher(
ws, running, request.match_info
)
worker_task = tg.create_task(
self.worker(ws, dispatcher, running)
)
lsnr_task = tg.create_task(
self.listener(ws, dispatcher, running)
)
except ExceptionGroup as e:
logger.error("Exception group occurred:", exc_info=True)
# Attempt graceful dispatcher shutdown
try:
await asyncio.wait_for(
dispatcher.destroy(),
timeout=5.0
)
except asyncio.TimeoutError:
logger.warning("Dispatcher shutdown timed out")
except Exception as de:
logger.error(f"Error during dispatcher cleanup: {de}")
except Exception as e:
logger.error(f"Socket exception: {e}", exc_info=True)
finally:
# Ensure dispatcher cleanup
if dispatcher and hasattr(dispatcher, 'destroy'):
try:
await dispatcher.destroy()
except:
pass
# Ensure websocket is closed
if ws and not ws.closed:
await ws.close()
return ws
```
## Opciones de configuración
Agregar soporte de configuración para ajustar el comportamiento:
```python
# config.py
class GracefulShutdownConfig:
# Publisher settings
PUBLISHER_DRAIN_TIMEOUT = 5.0 # Seconds to wait for queue drain
PUBLISHER_FLUSH_TIMEOUT = 2.0 # Producer flush timeout
# Subscriber settings
SUBSCRIBER_DRAIN_TIMEOUT = 5.0 # Seconds to wait for queue drain
BACKPRESSURE_STRATEGY = "block" # Options: "block", "drop_oldest", "drop_new"
SUBSCRIBER_MAX_QUEUE_SIZE = 100 # Maximum queue size before backpressure
# Socket settings
SHUTDOWN_GRACE_PERIOD = 1.0 # Seconds to wait for graceful shutdown
MAX_CONSECUTIVE_ERRORS = 5 # Maximum errors before forced shutdown
# Monitoring
LOG_QUEUE_STATS = True # Log queue statistics on shutdown
METRICS_ENABLED = True # Enable metrics collection
```
## Estrategia de pruebas
### Pruebas unitarias
```python
async def test_publisher_queue_drain():
"""Verify Publisher drains queue on shutdown"""
publisher = Publisher(...)
# Fill queue with messages
for i in range(10):
await publisher.send(f"id-{i}", {"data": i})
# Stop publisher
await publisher.stop()
# Verify all messages were sent
assert publisher.q.empty()
assert mock_producer.send.call_count == 10
async def test_subscriber_deferred_ack():
"""Verify Subscriber only acks on successful delivery"""
subscriber = Subscriber(..., backpressure_strategy="drop_new")
# Fill queue to capacity
queue = await subscriber.subscribe("test")
for i in range(100):
await queue.put({"data": i})
# Try to add message when full
msg = create_mock_message()
await subscriber._process_message(msg)
# Verify negative acknowledgment
assert msg.negative_acknowledge.called
assert not msg.acknowledge.called
```
### Pruebas de Integración
```python
async def test_import_graceful_shutdown():
"""Test import path handles shutdown gracefully"""
# Setup
import_handler = TriplesImport(...)
await import_handler.start()
# Send messages
messages = []
for i in range(100):
msg = {"metadata": {...}, "triples": [...]}
await import_handler.receive(msg)
messages.append(msg)
# Shutdown while messages in flight
await import_handler.destroy()
# Verify all messages reached Pulsar
received = await pulsar_consumer.receive_all()
assert len(received) == 100
async def test_export_no_message_loss():
"""Test export path doesn't lose acknowledged messages"""
# Setup Pulsar with test messages
for i in range(100):
await pulsar_producer.send({"data": i})
# Start export handler
export_handler = TriplesExport(...)
export_task = asyncio.create_task(export_handler.run())
# Receive some messages
received = []
for _ in range(50):
msg = await websocket.receive()
received.append(msg)
# Force shutdown
await export_handler.destroy()
# Continue receiving until websocket closes
while not websocket.closed:
try:
msg = await websocket.receive()
received.append(msg)
except:
break
# Verify no acknowledged messages were lost
assert len(received) >= 50
```
## Plan de Implementación
### Fase 1: Correcciones Críticas (Semana 1)
Corregir el tiempo de confirmación del suscriptor (prevenir la pérdida de mensajes)
Agregar el vaciado de la cola del publicador
<<<<<<< HEAD
Implementar en el entorno de pruebas
=======
Desplegar en el entorno de pruebas
>>>>>>> 82edf2d (New md files from RunPod)
### Fase 2: Cierre Gratuito (Semana 2)
Implementar la coordinación del cierre
Agregar estrategias de contrapresión
Pruebas de rendimiento
### Fase 3: Monitoreo y Ajuste (Semana 3)
Agregar métricas para las profundidades de la cola
Agregar alertas para la pérdida de mensajes
Ajustar los valores de tiempo de espera según los datos de producción
## Monitoreo y Alertas
### Métricas a Monitorear
`publisher.queue.depth` - Tamaño actual de la cola del publicador
`publisher.messages.dropped` - Mensajes perdidos durante el cierre
`subscriber.messages.negatively_acknowledged` - Entregas fallidas
<<<<<<< HEAD
`websocket.graceful_shutdowns` - Cierres gratuitos exitosos
=======
`websocket.graceful_shutdowns` - Cierres suaves exitosos
>>>>>>> 82edf2d (New md files from RunPod)
`websocket.forced_shutdowns` - Cierres forzados/por tiempo de espera
### Alertas
Profundidad de la cola del publicador > 80% de capacidad
Cualquier pérdida de mensajes durante el cierre
Tasa de acuse de recibo negativo del suscriptor > 1%
Tiempo de espera del cierre excedido
## Compatibilidad con Versiones Anteriores
Todos los cambios mantienen la compatibilidad con versiones anteriores:
El comportamiento predeterminado no cambia sin configuración
<<<<<<< HEAD
Las implementaciones existentes continúan funcionando
=======
Los despliegues existentes continúan funcionando
>>>>>>> 82edf2d (New md files from RunPod)
Degradación gradual si las nuevas funciones no están disponibles
## Consideraciones de Seguridad
No se introducen nuevos vectores de ataque
La contrapresión evita los ataques de agotamiento de memoria
Los límites configurables previenen el abuso de recursos
## Impacto en el Rendimiento
Sobre carga mínima durante la operación normal
El cierre puede tardar hasta 5 segundos más (configurable)
El uso de memoria está limitado por los límites del tamaño de la cola
El impacto en la CPU es insignificante (<1% de aumento)

View file

@ -0,0 +1,524 @@
---
layout: default
title: "Especificación técnica de la salida de JSONL para prompts"
parent: "Spanish (Beta)"
---
# Especificación técnica de la salida de JSONL para prompts
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
Esta especificación describe la implementación del formato de salida JSONL (JSON Lines)
para respuestas de prompts en TrustGraph. JSONL permite la extracción resistente a
la truncación de datos estructurados de las respuestas de los LLM, abordando problemas
críticos con las salidas de matrices JSON que se corrompen cuando las respuestas de
los LLM alcanzan los límites de tokens de salida.
Esta implementación admite los siguientes casos de uso:
1. **Extracción resistente a la truncación**: Extraer resultados parciales válidos
incluso cuando la salida del LLM se trunca a la mitad de la respuesta.
2. **Extracción a gran escala**: Manejar la extracción de muchos elementos sin riesgo
de fallo completo debido a los límites de tokens.
3. **Extracción de tipos mixtos**: Admitir la extracción de múltiples tipos de
entidades (definiciones, relaciones, entidades, atributos) en un solo prompt.
4. **Salida compatible con el streaming**: Permitir el procesamiento futuro de
resultados de extracción de forma incremental.
## Objetivos
**Compatibilidad con versiones anteriores**: Las indicaciones existentes que utilizan `response-type: "text"` y
`response-type: "json"` siguen funcionando sin modificaciones.
<<<<<<< HEAD
**Resiliencia a la truncación**: Las salidas parciales del LLM producen resultados válidos parciales
=======
**Resiliencia ante la truncación**: Las salidas parciales del LLM producen resultados válidos parciales
>>>>>>> 82edf2d (New md files from RunPod)
en lugar de un fallo completo.
**Validación de esquema**: Soporte para la validación de esquemas JSON para objetos individuales.
**Uniones discriminadas**: Soporte para salidas de tipos mixtos utilizando un campo `type`
discriminador.
**Cambios mínimos en la API**: Extiende la configuración de indicaciones existente con un nuevo
tipo de respuesta y clave de esquema.
<<<<<<< HEAD
## Contexto
=======
## Antecedentes
>>>>>>> 82edf2d (New md files from RunPod)
### Arquitectura actual
El servicio de indicaciones admite dos tipos de respuesta:
1. `response-type: "text"`: Respuesta de texto sin formato devuelta tal cual.
2. `response-type: "json"`: JSON analizado de la respuesta, validado contra
un esquema `schema` opcional.
Implementación actual en `trustgraph-flow/trustgraph/template/prompt_manager.py`:
```python
class Prompt:
def __init__(self, template, response_type = "text", terms=None, schema=None):
self.template = template
self.response_type = response_type
self.terms = terms
self.schema = schema
```
### Limitaciones actuales
Cuando las indicaciones de extracción solicitan una salida en formato de matrices JSON (`[{...}, {...}, ...]`):
**Corrupción por truncamiento**: Si el LLM alcanza los límites de tokens de salida a la mitad de la matriz,
toda la respuesta se vuelve JSON inválido y no se puede analizar.
**Análisis todo o nada**: Debe recibir la salida completa antes de analizarla.
**Sin resultados parciales**: Una respuesta truncada produce cero datos utilizables.
**No es fiable para extracciones grandes**: Cuantos más elementos se extraen, mayor es el riesgo de fallo.
<<<<<<< HEAD
Esta especificación aborda estas limitaciones introduciendo el formato JSONL para
=======
Esta especificación aborda estas limitaciones mediante la introducción del formato JSONL para
>>>>>>> 82edf2d (New md files from RunPod)
las indicaciones de extracción, donde cada elemento extraído es un objeto JSON completo en su
propia línea.
## Diseño técnico
### Extensión del tipo de respuesta
Agregue un nuevo tipo de respuesta `"jsonl"` junto con los tipos existentes `"text"` y `"json"`.
#### Cambios de configuración
**Nuevo valor del tipo de respuesta:**
```
"response-type": "jsonl"
```
**Interpretación del esquema:**
La clave existente `"schema"` se utiliza tanto para el tipo de respuesta `"json"` como para el tipo de respuesta `"jsonl"`.
La interpretación depende del tipo de respuesta:
`"json"`: El esquema describe toda la respuesta (típicamente un array u objeto).
`"jsonl"`: El esquema describe cada línea/objeto individual.
```json
{
"response-type": "jsonl",
"schema": {
"type": "object",
"properties": {
"entity": { "type": "string" },
"definition": { "type": "string" }
},
"required": ["entity", "definition"]
}
}
```
Esto evita cambios en las herramientas de configuración y los editores.
### Especificación del formato JSONL
#### Extracción simple
<<<<<<< HEAD
Para los prompts que extraen un solo tipo de objeto (definiciones, relaciones,
temas, filas), la salida es un objeto JSON por línea sin envoltorio:
**Formato de salida del prompt:**
=======
Para las indicaciones que extraen un solo tipo de objeto (definiciones, relaciones,
temas, filas), la salida es un objeto JSON por línea sin envoltorio:
**Formato de salida de la indicación:**
>>>>>>> 82edf2d (New md files from RunPod)
```
{"entity": "photosynthesis", "definition": "Process by which plants convert sunlight"}
{"entity": "chlorophyll", "definition": "Green pigment in plants"}
{"entity": "mitochondria", "definition": "Powerhouse of the cell"}
```
**Contraste con el formato anterior de matriz JSON:**
```json
[
{"entity": "photosynthesis", "definition": "Process by which plants convert sunlight"},
{"entity": "chlorophyll", "definition": "Green pigment in plants"},
{"entity": "mitochondria", "definition": "Powerhouse of the cell"}
]
```
Si el modelo de lenguaje (LLM) trunca después de la línea 2, el formato de matriz JSON produce JSON inválido,
mientras que JSONL produce dos objetos válidos.
#### Extracción de Tipos Mixtos (Uniones Discriminadas)
<<<<<<< HEAD
Para las indicaciones que extraen múltiples tipos de objetos (por ejemplo, tanto definiciones como
=======
Para indicaciones que extraen múltiples tipos de objetos (por ejemplo, tanto definiciones como
>>>>>>> 82edf2d (New md files from RunPod)
relaciones, o entidades, relaciones y atributos), utilice un campo `"type"`
como discriminador:
**Formato de salida de la indicación:**
```
{"type": "definition", "entity": "DNA", "definition": "Molecule carrying genetic instructions"}
{"type": "relationship", "subject": "DNA", "predicate": "located_in", "object": "cell nucleus", "object-entity": true}
{"type": "definition", "entity": "RNA", "definition": "Molecule that carries genetic information"}
{"type": "relationship", "subject": "RNA", "predicate": "transcribed_from", "object": "DNA", "object-entity": true}
```
**Esquema para uniones discriminadas utiliza `oneOf`:**
```json
{
"response-type": "jsonl",
"schema": {
"oneOf": [
{
"type": "object",
"properties": {
"type": { "const": "definition" },
"entity": { "type": "string" },
"definition": { "type": "string" }
},
"required": ["type", "entity", "definition"]
},
{
"type": "object",
"properties": {
"type": { "const": "relationship" },
"subject": { "type": "string" },
"predicate": { "type": "string" },
"object": { "type": "string" },
"object-entity": { "type": "boolean" }
},
"required": ["type", "subject", "predicate", "object", "object-entity"]
}
]
}
}
```
#### Extracción de Ontología
Para la extracción basada en ontologías con entidades, relaciones y atributos:
**Formato de salida del prompt:**
```
{"type": "entity", "entity": "Cornish pasty", "entity_type": "fo/Recipe"}
{"type": "entity", "entity": "beef", "entity_type": "fo/Food"}
{"type": "relationship", "subject": "Cornish pasty", "subject_type": "fo/Recipe", "relation": "fo/has_ingredient", "object": "beef", "object_type": "fo/Food"}
{"type": "attribute", "entity": "Cornish pasty", "entity_type": "fo/Recipe", "attribute": "fo/serves", "value": "4 people"}
```
### Detalles de implementación
#### Clase Prompt
La clase `Prompt` existente no requiere cambios. El campo `schema` se reutiliza
para JSONL, y su interpretación está determinada por `response_type`:
```python
class Prompt:
def __init__(self, template, response_type="text", terms=None, schema=None):
self.template = template
self.response_type = response_type
self.terms = terms
self.schema = schema # Interpretation depends on response_type
```
#### PromptManager.load_config
No se requieren cambios: la carga de configuración existente ya gestiona la
clave `schema`.
#### Análisis de JSONL
Agregar un nuevo método de análisis para respuestas JSONL:
```python
def parse_jsonl(self, text):
"""
Parse JSONL response, returning list of valid objects.
Invalid lines (malformed JSON, empty lines) are skipped with warnings.
This provides truncation resilience - partial output yields partial results.
"""
results = []
for line_num, line in enumerate(text.strip().split('\n'), 1):
line = line.strip()
# Skip empty lines
if not line:
continue
# Skip markdown code fence markers if present
if line.startswith('```'):
continue
try:
obj = json.loads(line)
results.append(obj)
except json.JSONDecodeError as e:
# Log warning but continue - this provides truncation resilience
logger.warning(f"JSONL parse error on line {line_num}: {e}")
return results
```
#### Cambios en PromptManager.invoke
Extender el método invoke para manejar el nuevo tipo de respuesta:
```python
async def invoke(self, id, input, llm):
logger.debug("Invoking prompt template...")
terms = self.terms | self.prompts[id].terms | input
resp_type = self.prompts[id].response_type
prompt = {
"system": self.system_template.render(terms),
"prompt": self.render(id, input)
}
resp = await llm(**prompt)
if resp_type == "text":
return resp
if resp_type == "json":
try:
obj = self.parse_json(resp)
except:
logger.error(f"JSON parse failed: {resp}")
raise RuntimeError("JSON parse fail")
if self.prompts[id].schema:
try:
validate(instance=obj, schema=self.prompts[id].schema)
logger.debug("Schema validation successful")
except Exception as e:
raise RuntimeError(f"Schema validation fail: {e}")
return obj
if resp_type == "jsonl":
objects = self.parse_jsonl(resp)
if not objects:
logger.warning("JSONL parse returned no valid objects")
return []
# Validate each object against schema if provided
if self.prompts[id].schema:
validated = []
for i, obj in enumerate(objects):
try:
validate(instance=obj, schema=self.prompts[id].schema)
validated.append(obj)
except Exception as e:
logger.warning(f"Object {i} failed schema validation: {e}")
return validated
return objects
raise RuntimeError(f"Response type {resp_type} not known")
```
### Prompts Afectados
Los siguientes prompts deben migrarse al formato JSONL:
| ID del Prompt | Descripción | Campo de Tipo |
|-----------|-------------|------------|
| `extract-definitions` | Extracción de entidades/definiciones | No (un solo tipo) |
| `extract-relationships` | Extracción de relaciones | No (un solo tipo) |
| `extract-topics` | Extracción de temas/definiciones | No (un solo tipo) |
| `extract-rows` | Extracción de filas estructuradas | No (un solo tipo) |
| `agent-kg-extract` | Extracción combinada de definiciones + relaciones | Sí: `"definition"`, `"relationship"` |
| `extract-with-ontologies` / `ontology-extract` | Extracción basada en ontología | Sí: `"entity"`, `"relationship"`, `"attribute"` |
### Cambios en la API
#### Perspectiva del Cliente
El análisis JSONL es transparente para los clientes de la API del servicio de prompts. El análisis se
realiza en el servidor en el servicio de prompts, y la respuesta se devuelve a través del
campo estándar `PromptResponse.object` como una matriz JSON serializada.
Cuando los clientes llaman al servicio de prompts (a través de `PromptClient.prompt()` o similar):
**`response-type: "json"`** con esquema de matriz → el cliente recibe Python `list`
**`response-type: "jsonl"`** → el cliente recibe Python `list`
Desde la perspectiva del cliente, ambos devuelven estructuras de datos idénticas. La
<<<<<<< HEAD
diferencia es completamente en cómo se analiza la salida del LLM en el servidor:
Formato de matriz JSON: Una sola llamada a `json.loads()`; falla completamente si se trunca
Formato JSONL: Análisis línea por línea; produce resultados parciales si se trunca
Esto significa que el código de cliente existente que espera una lista de los prompts de extracción
no requiere cambios al migrar los prompts de JSON a JSONL.
=======
diferencia radica completamente en cómo se analiza la salida del LLM en el servidor:
Formato de matriz JSON: Una sola llamada a `json.loads()`; falla por completo si se trunca
Formato JSONL: Análisis línea por línea; produce resultados parciales si se trunca
Esto significa que el código de cliente existente que espera una lista de los prompts de extracción
no requiere cambios al migrar los prompts del formato JSON al formato JSONL.
>>>>>>> 82edf2d (New md files from RunPod)
#### Valor de Retorno del Servidor
Para `response-type: "jsonl"`, el método `PromptManager.invoke()` devuelve un
`list[dict]` que contiene todos los objetos analizados y validados correctamente. Esta
lista se serializa luego a JSON para el campo `PromptResponse.object`.
#### Manejo de Errores
Resultados vacíos: Devuelve una lista vacía `[]` con un registro de advertencia
Fallo parcial de análisis: Devuelve una lista de objetos analizados correctamente con
registros de advertencia para los fallos
Fallo completo de análisis: Devuelve una lista vacía `[]` con registros de advertencia
<<<<<<< HEAD
Esto difiere de `response-type: "json"` que lanza `RuntimeError` en
=======
Esto difiere de `response-type: "json"`, que lanza `RuntimeError` en
>>>>>>> 82edf2d (New md files from RunPod)
caso de fallo de análisis. El comportamiento permisivo para JSONL es intencional para proporcionar
resistencia a la truncación.
### Ejemplo de Configuración
Ejemplo completo de configuración de prompt:
```json
{
"prompt": "Extract all entities and their definitions from the following text. Output one JSON object per line.\n\nText:\n{{text}}\n\nOutput format per line:\n{\"entity\": \"<name>\", \"definition\": \"<definition>\"}",
"response-type": "jsonl",
"schema": {
"type": "object",
"properties": {
"entity": {
"type": "string",
"description": "The entity name"
},
"definition": {
"type": "string",
"description": "A clear definition of the entity"
}
},
"required": ["entity", "definition"]
}
}
```
## Consideraciones de seguridad
**Validación de entrada**: El análisis JSON utiliza `json.loads()` estándar, que es seguro
contra ataques de inyección.
**Validación de esquema**: Utiliza `jsonschema.validate()` para la aplicación de esquemas.
**Sin nueva superficie de ataque**: El análisis de JSONL es estrictamente más seguro que el análisis de matrices JSON
debido al procesamiento línea por línea.
## Consideraciones de rendimiento
**Memoria**: El análisis línea por línea utiliza menos memoria máxima que la carga de matrices JSON completas.
<<<<<<< HEAD
**Latencia**: El rendimiento del análisis es comparable al análisis de matrices JSON.
**Validación**: La validación de esquema se ejecuta por objeto, lo que añade sobrecarga, pero
permite resultados parciales en caso de fallo de la validación.
## Estrategia de pruebas
=======
**Latencia**: El rendimiento del análisis es comparable al análisis de matrices JSON.
**Validación**: La validación de esquemas se ejecuta por objeto, lo que añade sobrecarga, pero
permite resultados parciales en caso de fallo de la validación.
## Estrategia de pruebas
>>>>>>> 82edf2d (New md files from RunPod)
### Pruebas Unitarias
Análisis de JSONL con entrada válida
Análisis de JSONL con líneas vacías
Análisis de JSONL con bloques de código Markdown
<<<<<<< HEAD
Análisis de JSONL con línea final truncada
Análisis de JSONL con líneas JSON inválidas intercaladas
Validación de esquema con uniones discriminadas `oneOf`
Compatibilidad hacia atrás: las indicaciones existentes `"text"` y `"json"` no se modifican
=======
Análisis de JSONL con líneas finales truncadas
Análisis de JSONL con líneas JSON inválidas intercaladas
Validación de esquema con uniones discriminadas `oneOf`
Compatibilidad con versiones anteriores: las indicaciones `"text"` y `"json"` existentes no se modifican
>>>>>>> 82edf2d (New md files from RunPod)
### Pruebas de Integración
Extracción de extremo a extremo con indicaciones JSONL
Extracción con truncamiento simulado (respuesta artificialmente limitada)
Extracción de tipos mixtos con discriminador de tipo
Extracción de ontología con los tres tipos
### Pruebas de Calidad de Extracción
Comparar los resultados de la extracción: formato JSONL frente a formato de matriz JSON.
Verificar la resistencia a la truncación: JSONL produce resultados parciales donde JSON falla.
## Plan de Migración
### Fase 1: Implementación
1. Implementar el método `parse_jsonl()` en `PromptManager`.
2. Extender `invoke()` para manejar `response-type: "jsonl"`.
3. Agregar pruebas unitarias.
### Fase 2: Migración de Prompts
1. Actualizar el prompt y la configuración de `extract-definitions`.
2. Actualizar el prompt y la configuración de `extract-relationships`.
3. Actualizar el prompt y la configuración de `extract-topics`.
4. Actualizar el prompt y la configuración de `extract-rows`.
5. Actualizar el prompt y la configuración de `agent-kg-extract`.
6. Actualizar el prompt y la configuración de `extract-with-ontologies`.
### Fase 3: Actualizaciones Posteriores
1. Actualizar cualquier código que consuma los resultados de la extracción para manejar el tipo de retorno de lista.
2. Actualizar el código que categoriza las extracciones de tipos mixtos según el campo `type`.
3. Actualizar las pruebas que afirman sobre el formato de salida de la extracción.
## Preguntas Abiertas
Ninguna por el momento.
## Referencias
<<<<<<< HEAD
Implementación actual: `trustgraph-flow/trustgraph/template/prompt_manager.py`.
Especificación de JSON Lines: https://jsonlines.org/.
Esquema JSON `oneOf`: https://json-schema.org/understanding-json-schema/reference/combining.html#oneof.
Especificación relacionada: Streaming LLM Responses (`docs/tech-specs/streaming-llm-responses.md`).
=======
Implementación actual: `trustgraph-flow/trustgraph/template/prompt_manager.py`
Especificación de JSON Lines: https://jsonlines.org/
Esquema JSON `oneOf`: https://json-schema.org/understanding-json-schema/reference/combining.html#oneof
Especificación relacionada: Streaming LLM Responses (`docs/tech-specs/streaming-llm-responses.md`)
>>>>>>> 82edf2d (New md files from RunPod)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,215 @@
---
layout: default
title: "Estrategia de Registro de TrustGraph"
parent: "Spanish (Beta)"
---
# Estrategia de Registro de TrustGraph
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Visión general
TrustGraph utiliza el módulo integrado de Python `logging` para todas las operaciones de registro, con una configuración centralizada y la integración opcional de Loki para la agregación de registros. Esto proporciona un enfoque estandarizado y flexible para el registro en todos los componentes del sistema.
## Configuración predeterminada
### Nivel de registro
- **Nivel predeterminado**: `INFO`
- **Configurable a través de**: Argumento de línea de comandos `--log-level`
- **Opciones**: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`
### Destinos de salida
1. **Consola (stdout)**: Siempre habilitado - asegura la compatibilidad con entornos contenedorizados
2. **Loki**: Agregación centralizada de registros opcional (habilitado por defecto, se puede deshabilitar)
## Módulo de registro centralizado
Todas las configuraciones de registro se gestionan mediante el módulo `trustgraph.base.logging`, que proporciona:
- `add_logging_args(parser)` - Agrega argumentos de línea de comandos estándar para registro
- `setup_logging(args)` - Configura el registro a partir de los argumentos analizados
Este módulo se utiliza en todos los componentes del lado del servidor:
- Servicios basados en AsyncProcessor
- API Gateway
- Servidor MCP
## Guía de implementación
### 1. Inicialización del registrador
Cada módulo debe crear su propio registrador utilizando el nombre del módulo:
```python
import logging
logger = logging.getLogger(__name__)
```
El nombre del registrador se utiliza automáticamente como etiqueta en Loki para filtrar y buscar.
### 2. Inicialización del servicio
Todos los servicios del lado del servidor obtienen automáticamente la configuración de registro a través del módulo centralizado:
```python
from trustgraph.base import add_logging_args, setup_logging
import argparse
def main():
parser = argparse.ArgumentParser()
# Agrega argumentos estándar de registro (incluida la configuración de Loki)
add_logging_args(parser)
# Agrega tus argumentos específicos del servicio
parser.add_argument('--port', type=int, default=8080)
args = parser.parse_args()
args = vars(args)
# Configura el registro temprano en el inicio
setup_logging(args)
# Resto de la inicialización del servicio
logger = logging.getLogger(__name__)
logger.info("Servicio iniciado...")
```
### 3. Argumentos de línea de comandos
Todos los servicios admiten estos argumentos de registro:
**Nivel de registro:**
```bash
--log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}
```
**Configuración de Loki:**
```bash
--loki-enabled # Habilita Loki (predeterminado)
--no-loki-enabled # Deshabilita Loki
--loki-url URL # URL de envío a Loki (predeterminado: http://loki:3100/loki/api/v1/push)
--loki-username USERNAME # Nombre de usuario opcional de autenticación
--loki-password PASSWORD # Contraseña opcional de autenticación
```
**Ejemplos:**
```bash
# Predeterminado - nivel INFO, Loki habilitado
./my-service
# Modo DEBUG, solo consola
./my-service --log-level DEBUG --no-loki-enabled
# URL de Loki personalizada con autenticación
./my-service --loki-url http://loki.prod:3100/loki/api/v1/push \
--loki-username admin --loki-password secret
```
### 4. Variables de entorno
La configuración de Loki admite los valores predeterminados de las variables de entorno:
```bash
export LOKI_URL=http://loki.prod:3100/loki/api/v1/push
export LOKI_USERNAME=admin
export LOKI_PASSWORD=secret
```
Los argumentos de la línea de comandos tienen prioridad sobre las variables de entorno.
### 5. Mejores prácticas de registro
#### Uso de los niveles de registro
- **DEBUG**: Información detallada para diagnosticar problemas (valores de variables, entrada/salida de funciones)
- **INFO**: Mensajes informativos generales (servicio iniciado, configuración cargada, hitos de procesamiento)
- **WARNING**: Mensajes de advertencia para situaciones potencialmente dañinas (características obsoletas, errores recuperables)
- **ERROR**: Mensajes de error para problemas serios (operaciones fallidas, excepciones)
- **CRITICAL**: Mensajes críticos para fallas del sistema que requieren atención inmediata
#### Formato de mensaje
```python
# Bueno - incluye contexto
logger.info(f"Procesando documento: {doc_id}, tamaño: {doc_size} bytes")
logger.error(f"No se pudo conectar a la base de datos: {error}", exc_info=True)
# No bueno - carece de contexto
logger.info("Procesando documento")
logger.error("Conexión fallida")
```
#### Consideraciones de rendimiento
```python
# Usa la incrustación para operaciones costosas
logger.debug("Resultado de la operación costosa: %s", expensive_function())
# Comprueba el nivel de registro para operaciones de depuración muy costosas
if logger.isEnabledFor(logging.DEBUG):
debug_data = compute_expensive_debug_info()
logger.debug(f"Datos de depuración: {debug_data}")
```
#### 6. Registro estructurado con Loki
Para datos complejos, usa el registro estructurado con etiquetas adicionales para Loki:
```python
logger.info("Solicitud procesada", extra={
'tags': {
'request_id': request_id,
'user_id': user_id,
'status': 'éxito'
}
})
```
Estas etiquetas se convierten en etiquetas de búsqueda en Loki, además de las etiquetas automáticas:
- `severity` - Nivel de registro (DEBUG, INFO, etc.)
- `logger` - Nombre del módulo (del `__name__`)
### 7. Registro de excepciones
Siempre incluye trazas de pila para las excepciones:
```python
try:
process_data()
except Exception as e:
logger.error(f"No se pudo procesar los datos: {e}", exc_info=True)
```
### 8. Dependencias
El módulo centralizado de registro requiere:
- `python-logging-loki` - Para la integración de Loki (opcional, funciona si falta)
Ya incluido en `trustgraph-base/pyproject.toml` y `requirements.txt`.
## Migración
Para código existente:
1. **Servicios que ya utilizan AsyncProcessor**: No se necesita cambios, la integración de Loki es automática
2. **Servicios que no utilizan AsyncProcessor** (api-gateway, mcp-server): Ya actualizado
3. **Herramientas de línea de comandos**: Fuera del alcance - continúe usando `print()` o registro simple
### De `print()` a registro:
```python
# Antes
print(f"Procesando documento {doc_id}")
# Después
logger = logging.getLogger(__name__)
logger.info(f"Procesando documento {doc_id}")
```
## Resumen de configuración
| Argumento | Predeterminado | Variable de entorno | Descripción |
|---|---|---|---|
| `--log-level` | `INFO` | - | Nivel de registro en la consola y Loki |
| `--loki-enabled` | `True` | - | Habilita el registro de Loki |
| `--loki-url` | `http://loki:3100/loki/api/v1/push` | `LOKI_URL` | Puntero al API de Loki |
| `--loki-username` | `None` | `LOKI_USERNAME` | Nombre de usuario de autenticación para Loki |
| `--loki-password` | `None` | `LOKI_PASSWORD` | Contraseña de autenticación para Loki |

View file

@ -0,0 +1,168 @@
---
layout: default
title: "Especificación de Argumentos para la Herramienta MCP"
parent: "Spanish (Beta)"
---
# Especificación de Argumentos para la Herramienta MCP
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Visión General
**Nombre de la característica**: Soporte para Argumentos de la Herramienta MCP
**Autor**: Asistente de Código Claude
**Fecha**: 21/08/2025
**Estado**: Finalizado
### Resumen Ejecutivo
Permitir que los agentes ReACT invoquen las herramientas MCP (Protocolo de Contexto del Modelo) con argumentos definidos correctamente, agregando soporte para la especificación de argumentos a las configuraciones de las herramientas MCP, de manera similar a como funcionan actualmente las herramientas de plantillas de prompts.
### Declaración del Problema
Actualmente, las herramientas MCP en el marco de agentes ReACT no pueden especificar sus argumentos esperados. El método `McpToolImpl.get_arguments()` devuelve una lista vacía, lo que obliga a los LLMs a adivinar la estructura de parámetros correcta basándose únicamente en los nombres y las descripciones de las herramientas. Esto conduce a:
- Invocaciones de herramientas poco fiables debido a la adivinación de parámetros
- Mala experiencia de usuario cuando las herramientas fallan debido a argumentos incorrectos
- Falta de validación de parámetros de la herramienta antes de la ejecución
- Falta de documentación de parámetros en los prompts del agente
### Objetivos
- [ ] Permitir que las configuraciones de las herramientas MCP especifiquen los argumentos esperados (nombre, tipo, descripción)
- [ ] Actualizar el administrador de agentes para exponer los argumentos de las herramientas MCP a los LLMs a través de prompts
- [ ] Mantener la compatibilidad hacia atrás con las configuraciones existentes de las herramientas MCP
- [ ] Soporte para la validación de argumentos similar a las herramientas de plantillas de prompts
### Objetivos No
- Descubrimiento dinámico de argumentos de los servidores MCP (mejoras futuras)
- Validación de tipos de argumentos más allá de la estructura básica
- Esquemas de argumentos complejos (objetos anidados, arrays)
## Antecedentes y Contexto
### Estado Actual
Las herramientas MCP se configuran en el sistema de agente ReACT con metadatos mínimos:
```json
{
"type": "mcp-tool",
"name": "get_bank_balance",
"description": "Obtener el saldo de la cuenta bancaria",
"mcp-tool": "get_bank_balance"
}
```
El método `McpToolImpl.get_arguments()` devuelve `[]`, lo que significa que los LLMs no reciben ninguna guía de argumentos en sus prompts.
### Limitaciones
1. **Sin especificación de argumentos**: Las herramientas MCP no pueden definir parámetros esperados
2. **Inferencia de parámetros por parte del LLM**: Los agentes deben deducir parámetros a partir de los nombres/descripciones de las herramientas
3. **Falta de información en el prompt**: Los prompts de los agentes no muestran detalles de los argumentos para las herramientas MCP
4. **Sin validación**: Los parámetros inválidos solo se detectan en el momento de la ejecución de la herramienta MCP
### Componentes Relacionados
- **trustgraph-flow/agent/react/service.py**: Carga de la configuración de las herramientas y creación del AgentManager
- **trustgraph-flow/agent/react/tools.py**: Implementación de McpToolImpl
- **trustgraph-flow/agent/react/agent_manager.py**: Generación de prompts con argumentos de las herramientas
- **CLI tools**: Herramientas de línea de comandos para la gestión de herramientas MCP
- **Workbench**: Interfaz de usuario externa para la configuración de las herramientas de los agentes
## Requisitos
### Requisitos Funcionales
1. **Argumentos de la Configuración de la Herramienta MCP**: Las configuraciones de las herramientas MCP **DEBEN** soportar un array opcional de `arguments` con campos de nombre, tipo y descripción
2. **Exposición de Argumentos**: El método `McpToolImpl.get_arguments()` **DEBE** devolver los argumentos configurados en lugar de una lista vacía
3. **Integración en el Prompt**: Los prompts de los agentes **DEBEN** incluir detalles de los argumentos de las herramientas MCP cuando se especifican
4. **Compatibilidad hacia atrás**: Las configuraciones existentes de las herramientas MCP sin argumentos **DEBEN** seguir funcionando
5. **Soporte CLI**: Las herramientas CLI existentes (`tg-invoke-mcp-tool`) soportan argumentos (ya implementado)
### Requisitos No Funcionales
1. **Compatibilidad hacia atrás**: Sin cambios que rompan con las configuraciones existentes de las herramientas MCP
2. **Rendimiento**: Sin impacto significativo en el rendimiento de la generación de prompts
3. **Consistencia**: El manejo de argumentos **DEBE** seguir los patrones de las herramientas de plantillas de prompts
### Historias de Usuario
1. Como **desarrollador de agentes**, quiero especificar argumentos para las herramientas MCP en la configuración, para que los LLMs puedan invocar las herramientas con los parámetros correctos
2. Como **usuario de Workbench**, quiero configurar argumentos para las herramientas MCP en la interfaz de usuario, para que los agentes usen las herramientas correctamente
3. Como **LLM en un agente ReACT**, quiero ver las especificaciones de argumentos de las herramientas en los prompts, para poder proporcionar los parámetros correctos
## Diseño
### Arquitectura de Alto Nivel
Extender la configuración de las herramientas MCP para que coincidan con el patrón de las plantillas de prompts, agregando:
1. Un array opcional de `arguments` a las configuraciones de las herramientas MCP
2. Modificar `McpToolImpl` para aceptar y devolver los argumentos configurados
3. Actualizar la carga de la configuración de las herramientas para manejar los argumentos de las herramientas MCP
4. Asegurar que los prompts de los agentes incluyan información sobre los argumentos de las herramientas MCP
### Esquema de Configuración
```json
{
"type": "mcp-tool",
"name": "get_bank_balance",
"description": "Obtener el saldo de la cuenta bancaria",
"mcp-tool": "get_bank_balance",
"arguments": [
{
"name": "account_id",
"type": "string",
"description": "Identificador de la cuenta bancaria"
},
{
"name": "date",
"type": "string",
"description": "Fecha para la consulta de saldo (opcional, formato: YYYY-MM-DD)"
}
]
}
```
### Flujo de Datos
1. **Carga de la configuración**: La configuración de la herramienta MCP con argumentos se carga por `on_tools_config()`
2. **Creación de la herramienta**: Los argumentos se parsean y se pasan a `McpToolImpl` a través del constructor
3. **Generación del prompt**: `agent_manager.py` llama a `tool.arguments` para incluirlos en los prompts de los LLMs
4. **Invocación de la herramienta**: El LLM proporciona los parámetros, que se pasan sin cambios a las herramientas MCP
## Consideraciones de Seguridad
- **Sin nueva superficie de ataque**: Los argumentos se parsean a partir de las fuentes de configuración existentes sin nuevas entradas
- **Validación de parámetros**: Los argumentos se pasan a las herramientas MCP sin cambios - la validación permanece a nivel de la herramienta MCP
- **Integridad de la configuración**: Las especificaciones de argumentos son parte de la configuración de la herramienta - el mismo modelo de seguridad aplica
## Impacto en el Rendimiento
- **Sobrehead mínimo**: El análisis de argumentos ocurre solo durante la carga de la configuración, no por solicitud
- **Aumento del tamaño del prompt**: Los prompts del agente incluirán detalles de los argumentos de las herramientas MCP, aumentando ligeramente el uso de tokens
- **Uso de la memoria**: Aumento insignificante para almacenar las especificaciones de argumentos en los objetos de la herramienta
## Documentación
### Documentación del Usuario
- [ ] Actualizar la guía de configuración de las herramientas MCP con ejemplos de argumentos
- [ ] Agregar especificación de argumentos a la ayuda de la línea de comandos
- [ ] Crear ejemplos de patrones comunes de argumentos de la herramienta MCP
### Documentación del Desarrollador
- [ ] Documentar la clase `McpToolImpl` con la lógica de análisis de argumentos
- [ ] Agregar comentarios en el código para la lógica de análisis de argumentos
- [ ] Documentar el flujo de argumentos en la arquitectura del sistema
## Preguntas Abiertas
1. **Validación de tipos**: ¿Deberíamos validar los tipos/formatos de los argumentos más allá de una verificación básica de estructura?
2. **Descubrimiento dinámico**: ¿Una futura mejora para consultar automáticamente los esquemas de las herramientas MCP de los servidores?
## Alternativas Consideradas
1. **Descubrimiento dinámico de esquemas MCP**: Consultar los servidores MCP para obtener los esquemas de las herramientas a tiempo de ejecución - rechazado debido a preocupaciones de complejidad y fiabilidad
2. **Registro de argumentos separado**: Almacenar los argumentos de las herramientas MCP en una sección de configuración separada - rechazado por mantener la consistencia con el enfoque de las plantillas de prompts
3. **Validación de tipo**: Validación completa de esquema JSON para los argumentos - aplazado como una mejora futura para mantener la implementación inicial simple
## Referencias
- [Especificación del Protocolo MCP](https://github.com/modelcontextprotocol/spec)
- [Implementación de las herramientas MCP](./trustgraph-flow/trustgraph/agent/react/service.py#L114-129)
- [Implementación actual de las herramientas MCP](./trustgraph-flow/trustgraph/agent/react/tools.py#L58-86)
## Apéndice
[Cualquier información, diagramas o ejemplos adicionales]

View file

@ -0,0 +1,562 @@
---
layout: default
title: "Especificación de autenticación de tokens Bearer para herramientas MCP"
parent: "Spanish (Beta)"
---
# Especificación de autenticación de tokens Bearer para herramientas MCP
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
> **⚠️ IMPORTANTE: SOLO PARA UN ÚNICO ARREGLO (SINGLE-TENANT)**
>
> Esta especificación describe un **mecanismo básico de autenticación a nivel de servicio** para herramientas MCP. **NO** es una solución de autenticación completa y **NO es adecuada** para:
> - Entornos multiusuario
> - Despliegues multi-inquilino (multi-tenant)
> - Autenticación federada
> - Propagación del contexto del usuario
> - Autorización por usuario
>
> Esta función proporciona **un único token estático por herramienta MCP**, compartido entre todos los usuarios y sesiones. Si necesita autenticación por usuario o por inquilino, esta no es la solución adecuada.
## Descripción general
**Nombre de la función**: Soporte de autenticación de tokens Bearer para herramientas MCP
**Autor**: Claude Code Assistant
**Fecha**: 2025-11-11
**Estado**: En desarrollo
### Resumen ejecutivo
Permite que las configuraciones de las herramientas MCP especifiquen tokens Bearer opcionales para autenticarse con servidores MCP protegidos. Esto permite que TrustGraph invoque de forma segura las herramientas MCP alojadas en servidores que requieren autenticación, sin modificar las interfaces de invocación de agentes o herramientas.
**IMPORTANTE**: Este es un mecanismo de autenticación básico diseñado para escenarios de autenticación de servicio a servicio en un único arreglo (single-tenant). **NO** es adecuado para:
Entornos multiusuario donde diferentes usuarios necesitan diferentes credenciales
Despliegues multi-inquilino (multi-tenant) que requieren aislamiento por inquilino
Escenarios de autenticación federada
Autenticación o autorización a nivel de usuario
Gestión dinámica de credenciales o actualización de tokens
Esta función proporciona un token Bearer estático y a nivel de sistema por configuración de herramienta MCP, compartido entre todos los usuarios e invocaciones de esa herramienta.
### Declaración del problema
Actualmente, las herramientas MCP solo pueden conectarse a servidores MCP accesibles públicamente. Muchos despliegues de MCP en producción requieren autenticación mediante tokens Bearer por motivos de seguridad. Sin soporte de autenticación:
Las herramientas MCP no pueden conectarse a servidores MCP protegidos
Los usuarios deben exponer los servidores MCP públicamente o implementar proxies inversos
No existe una forma estandarizada de pasar credenciales a las conexiones MCP
No se pueden aplicar las mejores prácticas de seguridad en los puntos finales de MCP
### Objetivos
[ ] Permitir que las configuraciones de las herramientas MCP especifiquen un parámetro opcional `auth-token`
[ ] Actualizar el servicio de la herramienta MCP para usar tokens Bearer al conectarse a servidores MCP
[ ] Actualizar las herramientas de la CLI para admitir la configuración/visualización de tokens de autenticación
[ ] Mantener la compatibilidad con versiones anteriores con configuraciones de MCP sin autenticación
[ ] Documentar las consideraciones de seguridad para el almacenamiento de tokens
### Objetivos no incluidos
Actualización dinámica de tokens o flujos OAuth (solo tokens estáticos)
Cifrado de tokens almacenados (la seguridad del sistema de configuración está fuera del alcance)
Métodos de autenticación alternativos (autenticación básica, claves API, etc.)
Validación o verificación de la fecha de caducidad de los tokens
**Autenticación por usuario**: Esta función **NO** admite credenciales específicas del usuario
**Aislamiento multi-inquilino (multi-tenant)**: Esta función **NO** proporciona gestión de tokens por inquilino
**Autenticación federada**: Esta función **NO** se integra con proveedores de identidad (SSO, OAuth, SAML, etc.)
**Autenticación con conocimiento del contexto**: Los tokens no se pasan en función del contexto del usuario o la sesión
## Antecedentes y contexto
### Estado actual
Las configuraciones de las herramientas MCP se almacenan en el grupo de configuración `mcp` con esta estructura:
```json
{
"remote-name": "tool_name",
"url": "http://mcp-server:3000/api"
}
```
El servicio de la herramienta MCP se conecta a los servidores utilizando `streamablehttp_client(url)` sin ningún encabezado de autenticación.
### Limitaciones
**Limitaciones del Sistema Actual:**
1. **Sin soporte de autenticación:** No se puede conectar a servidores MCP protegidos.
2. **Exposición de seguridad:** Los servidores MCP deben ser accesibles públicamente o utilizar solo seguridad a nivel de red.
3. **Problemas de implementación en producción:** No se pueden seguir las mejores prácticas de seguridad para los puntos finales de la API.
**Limitaciones de Esta Solución:**
1. **Solo para un único inquilino:** Un token estático por herramienta MCP, compartido entre todos los usuarios.
2. **Sin credenciales por usuario:** No se puede autenticar como diferentes usuarios ni pasar el contexto del usuario.
3. **Sin soporte para múltiples inquilinos:** No se pueden aislar las credenciales por inquilino u organización.
4. **Solo tokens estáticos:** No hay soporte para la renovación, rotación o manejo de la expiración de los tokens.
5. **Autenticación a nivel de servicio:** Autentica el servicio TrustGraph, no a usuarios individuales.
6. **Contexto de seguridad compartido:** Todas las invocaciones de una herramienta MCP utilizan la misma credencial.
### Aplicabilidad del Caso de Uso
**✅ Casos de Uso Apropiados:**
Implementaciones de TrustGraph de un solo inquilino.
Autenticación de servicio a servicio (TrustGraph → Servidor MCP).
Entornos de desarrollo y pruebas.
Herramientas MCP internas a las que accede el sistema TrustGraph.
Escenarios en los que todos los usuarios comparten el mismo nivel de acceso a la herramienta MCP.
Credenciales de servicio estáticas y de larga duración.
**❌ Casos de Uso Inapropiados:**
Sistemas multiusuario que requieren autenticación por usuario.
Implementaciones SaaS multiinquilino con requisitos de aislamiento de inquilinos.
Escenarios de autenticación federada (SSO, OAuth, SAML).
Sistemas que requieren la propagación del contexto del usuario a los servidores MCP.
Entornos que necesitan la renovación dinámica de tokens o tokens de corta duración.
Aplicaciones donde diferentes usuarios necesitan diferentes niveles de permisos.
Requisitos de cumplimiento para registros de auditoría a nivel de usuario.
**Ejemplo de Escenario Apropiado:**
Una implementación de TrustGraph de una sola organización donde todos los empleados utilizan la misma herramienta MCP interna (por ejemplo, búsqueda de bases de datos de la empresa). El servidor MCP requiere autenticación para evitar el acceso externo, pero todos los usuarios internos tienen el mismo nivel de acceso.
**Ejemplo de Escenario Inapropiado:**
Una plataforma SaaS multiinquilino de TrustGraph donde el inquilino A y el inquilino B necesitan acceder a sus propios servidores MCP aislados con credenciales separadas. Esta función NO admite la administración de tokens por inquilino.
### Componentes Relacionados
**trustgraph-flow/trustgraph/agent/mcp_tool/service.py**: Servicio de invocación de la herramienta MCP.
**trustgraph-cli/trustgraph/cli/set_mcp_tool.py**: Herramienta de línea de comandos para crear/actualizar configuraciones de MCP.
**trustgraph-cli/trustgraph/cli/show_mcp_tools.py**: Herramienta de línea de comandos para mostrar configuraciones de MCP.
**SDK de Python para MCP**: `streamablehttp_client` de `mcp.client.streamable_http`
## Requisitos
### Requisitos Funcionales
1. **Token de Autenticación de la Configuración de MCP:** Las configuraciones de la herramienta MCP DEBEN admitir un campo opcional `auth-token`.
2. **Uso del Token Bearer:** El servicio de la herramienta MCP DEBE enviar el encabezado `Authorization: Bearer {token}` cuando se configure un token de autenticación.
3. **Soporte de la CLI:** `tg-set-mcp-tool` DEBE aceptar un parámetro opcional `--auth-token`.
4. **Visualización del Token:** `tg-show-mcp-tools` DEBE indicar cuándo se ha configurado un token de autenticación (enmascarado por motivos de seguridad).
5. **Compatibilidad con versiones anteriores:** Las configuraciones de la herramienta MCP existentes sin un token de autenticación DEBEN seguir funcionando.
### Requisitos No Funcionales
1. **Compatibilidad con versiones anteriores:** No hay cambios disruptivos para las configuraciones de la herramienta MCP existentes.
2. **Rendimiento:** No hay un impacto significativo en el rendimiento de la invocación de la herramienta MCP.
3. **Seguridad:** Los tokens se almacenan en la configuración (documentar las implicaciones de seguridad).
### Historias de Usuario
1. Como **ingeniero de DevOps**, quiero configurar tokens de portador para las herramientas MCP para que pueda asegurar los puntos finales del servidor MCP.
2. Como **usuario de la CLI**, quiero establecer tokens de autenticación al crear herramientas MCP para que pueda conectarme a servidores protegidos.
3. Como **administrador del sistema**, quiero ver qué herramientas MCP tienen la autenticación configurada para que pueda auditar la configuración de seguridad.
## Diseño
### Arquitectura de Alto Nivel
Extender la configuración y el servicio de la herramienta MCP para admitir la autenticación con token de portador:
1. Agregar un campo opcional `auth-token` al esquema de configuración de la herramienta MCP.
2. Modificar el servicio de la herramienta MCP para leer el token de autenticación y pasarlo al cliente HTTP.
3. Actualizar las herramientas de la CLI para admitir la configuración y visualización de tokens de autenticación.
4. Documentar las consideraciones de seguridad y las mejores prácticas.
### Esquema de Configuración
**Esquema Actual:**
```json
{
"remote-name": "tool_name",
"url": "http://mcp-server:3000/api"
}
```
**Nuevo Esquema** (con token de autenticación opcional):
```json
{
"remote-name": "tool_name",
"url": "http://mcp-server:3000/api",
"auth-token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```
**Descripciones de campos:**
`remote-name` (opcional): Nombre utilizado por el servidor MCP (por defecto, la clave de configuración).
`url` (obligatorio): URL del punto final del servidor MCP.
`auth-token` (opcional): Token de tipo Bearer para la autenticación.
### Flujo de datos
1. **Almacenamiento de la configuración:** El usuario ejecuta `tg-set-mcp-tool --id my-tool --tool-url http://server/api --auth-token xyz123`.
2. **Carga de la configuración:** El servicio de la herramienta MCP recibe la actualización de la configuración a través de la devolución de llamada `on_mcp_config()`.
3. **Invocación de la herramienta:** Cuando se invoca la herramienta:
El servicio lee `auth-token` de la configuración (si está presente).
Crea un diccionario de encabezados: `{"Authorization": "Bearer {token}"}`.
Pasa los encabezados a `streamablehttp_client(url, headers=headers)`.
El servidor MCP valida el token y procesa la solicitud.
### Cambios en la API
No hay cambios en la API externa, solo una extensión del esquema de configuración.
### Detalles del componente
#### Componente 1: service.py (Servicio de la herramienta MCP)
**Archivo:** `trustgraph-flow/trustgraph/agent/mcp_tool/service.py`
**Propósito:** Invocar herramientas MCP en servidores remotos.
**Cambios requeridos** (en el método `invoke_tool()`):
1. Comprobar si `auth-token` está presente en la configuración `self.mcp_services[name]`.
2. Crear un diccionario de encabezados con el encabezado de autorización si existe un token.
3. Pasar los encabezados a `streamablehttp_client(url, headers=headers)`.
**Código actual** (líneas 42-89):
```python
async def invoke_tool(self, name, parameters):
try:
if name not in self.mcp_services:
raise RuntimeError(f"MCP service {name} not known")
if "url" not in self.mcp_services[name]:
raise RuntimeError(f"MCP service {name} URL not defined")
url = self.mcp_services[name]["url"]
if "remote-name" in self.mcp_services[name]:
remote_name = self.mcp_services[name]["remote-name"]
else:
remote_name = name
logger.info(f"Invoking {remote_name} at {url}")
# Connect to a streamable HTTP server
async with streamablehttp_client(url) as (
read_stream,
write_stream,
_,
):
# ... rest of method
```
**Código Modificado**:
```python
async def invoke_tool(self, name, parameters):
try:
if name not in self.mcp_services:
raise RuntimeError(f"MCP service {name} not known")
if "url" not in self.mcp_services[name]:
raise RuntimeError(f"MCP service {name} URL not defined")
url = self.mcp_services[name]["url"]
if "remote-name" in self.mcp_services[name]:
remote_name = self.mcp_services[name]["remote-name"]
else:
remote_name = name
# Build headers with optional bearer token
headers = {}
if "auth-token" in self.mcp_services[name]:
token = self.mcp_services[name]["auth-token"]
headers["Authorization"] = f"Bearer {token}"
logger.info(f"Invoking {remote_name} at {url}")
# Connect to a streamable HTTP server with headers
async with streamablehttp_client(url, headers=headers) as (
read_stream,
write_stream,
_,
):
# ... rest of method (unchanged)
```
#### Componente 2: set_mcp_tool.py (Herramienta de Configuración de Línea de Comandos)
**Archivo**: `trustgraph-cli/trustgraph/cli/set_mcp_tool.py`
**Propósito**: Crear/actualizar configuraciones de la herramienta MCP.
**Cambios Requeridos**:
1. Agregar un argumento opcional `--auth-token` a argparse.
2. Incluir `auth-token` en el archivo JSON de configuración cuando se proporciona.
**Argumentos Actuales**:
`--id` (requerido): Identificador de la herramienta MCP.
`--remote-name` (opcional): Nombre de la herramienta MCP remota.
`--tool-url` (requerido): Punto final de la URL de la herramienta MCP.
`-u, --api-url` (opcional): URL de la API de TrustGraph.
**Nuevo Argumento**:
`--auth-token` (opcional): Token Bearer para la autenticación.
**Construcción de Configuración Modificada**:
```python
# Build configuration object
config = {
"url": args.tool_url,
}
if args.remote_name:
config["remote-name"] = args.remote_name
if args.auth_token:
config["auth-token"] = args.auth_token
# Store configuration
api.config().put([
ConfigValue(type="mcp", key=args.id, value=json.dumps(config))
])
```
#### Componente 3: show_mcp_tools.py (Herramienta de visualización de línea de comandos)
**Archivo**: `trustgraph-cli/trustgraph/cli/show_mcp_tools.py`
**Propósito**: Mostrar configuraciones de la herramienta MCP.
**Cambios requeridos**:
1. Agregar columna "Auth" a la tabla de salida.
2. Mostrar "Sí" o "No" según la presencia de un token de autenticación.
3. No mostrar el valor real del token (seguridad).
**Salida actual**:
```
ID Remote Name URL
---------- ------------- ------------------------
my-tool my-tool http://server:3000/api
```
**Nueva Salida**:
```
ID Remote Name URL Auth
---------- ------------- ------------------------ ------
my-tool my-tool http://server:3000/api Yes
other-tool other-tool http://other:3000/api No
```
#### Componente 4: Documentación
**Archivo**: `docs/cli/tg-set-mcp-tool.md`
**Cambios Requeridos**:
1. Documentar el nuevo parámetro `--auth-token`
2. Proporcionar un ejemplo de uso con autenticación
3. Documentar las consideraciones de seguridad
## Plan de Implementación
### Fase 1: Crear Especificación Técnica
[x] Escribir una especificación técnica completa que documente todos los cambios
### Fase 2: Actualizar el Servicio MCP Tool
[ ] Modificar `invoke_tool()` en `service.py` para leer el token de autenticación desde la configuración
[ ] Construir un diccionario de encabezados y pasarlo a `streamablehttp_client`
[ ] Probar con un servidor MCP autenticado
### Fase 3: Actualizar las Herramientas CLI
[ ] Agregar el argumento `--auth-token` a `set_mcp_tool.py`
[ ] Incluir el token de autenticación en la configuración JSON
[ ] Agregar una columna "Auth" a la salida de `show_mcp_tools.py`
[ ] Probar los cambios de la herramienta CLI
### Fase 4: Actualizar la Documentación
[ ] Documentar el parámetro `--auth-token` en `tg-set-mcp-tool.md`
[ ] Agregar una sección de consideraciones de seguridad
[ ] Proporcionar un ejemplo de uso
### Fase 5: Pruebas
[ ] Probar que la herramienta MCP se conecta correctamente con el token de autenticación
[ ] Probar la compatibilidad con versiones anteriores (las herramientas sin token de autenticación aún funcionan)
[ ] Probar que las herramientas CLI aceptan y almacenan correctamente el token de autenticación
[ ] Probar que el comando "show" muestra el estado de la autenticación correctamente
### Resumen de Cambios en el Código
| Archivo | Tipo de Cambio | Líneas | Descripción |
|------|------------|-------|-------------|
| `service.py` | Modificado | ~52-66 | Agregar la lectura del token de autenticación y la construcción de encabezados |
| `set_mcp_tool.py` | Modificado | ~30-60 | Agregar el argumento --auth-token y el almacenamiento en la configuración |
| `show_mcp_tools.py` | Modificado | ~40-70 | Agregar la columna Auth a la visualización |
| `tg-set-mcp-tool.md` | Modificado | Varios | Documentar el nuevo parámetro |
## Estrategia de Pruebas
### Pruebas Unitarias
**Lectura del Token de Autenticación**: Probar que `invoke_tool()` lee correctamente el token de autenticación desde la configuración
**Construcción de Encabezados**: Probar que el encabezado de Autorización se construye correctamente con el prefijo Bearer
**Compatibilidad con Versiones Anteriores**: Probar que las herramientas sin token de autenticación funcionan sin cambios
**Análisis de Argumentos de la CLI**: Probar que el argumento `--auth-token` se analiza correctamente
### Pruebas de Integración
**Conexión Autenticada**: Probar que el servicio de la herramienta MCP se conecta a un servidor autenticado
**De Extremo a Extremo**: Probar el flujo CLI → almacenamiento de configuración → invocación del servicio con el token de autenticación
**Token No Requerido**: Probar que la conexión a un servidor no autenticado aún funciona
### Pruebas Manuales
**Servidor MCP Real**: Probar con un servidor MCP real que requiere la autenticación con token Bearer
**Flujo de Trabajo de la CLI**: Probar el flujo de trabajo completo: configurar la herramienta con la autenticación → invocar la herramienta → verificar el éxito
**Enmascaramiento de la Autenticación**: Verificar que el estado de la autenticación se muestra, pero el valor del token no se expone
## Migración e Implementación
### Estrategia de Migración
No se requiere migración: esta es una funcionalidad puramente adicional:
Las configuraciones existentes de la herramienta MCP que no tienen `auth-token` siguen funcionando sin cambios
Las nuevas configuraciones pueden incluir opcionalmente el campo `auth-token`
Las herramientas CLI aceptan, pero no requieren, el parámetro `--auth-token`
### Plan de Implementación
1. **Fase 1**: Implementar los cambios principales del servicio en el entorno de desarrollo/pruebas
2. **Fase 2**: Implementar las actualizaciones de las herramientas CLI
3. **Fase 3**: Actualizar la documentación
4. **Fase 4**: Implementación en producción con monitoreo
### Plan de Reversión
Los cambios principales son compatibles con versiones anteriores: las herramientas existentes no se ven afectadas
Si surgen problemas, el manejo del token de autenticación se puede deshabilitar eliminando la lógica de construcción de encabezados
Los cambios de la CLI son independientes y se pueden revertir por separado
## Consideraciones de Seguridad
### ⚠️ Limitación Crítica: Solo Autenticación de Un Solo Inquilino
**Este mecanismo de autenticación NO es adecuado para entornos multiusuario o multiinquilino.**
**Credenciales compartidas**: Todos los usuarios e invocaciones comparten el mismo token por herramienta MCP
**Sin contexto de usuario**: El servidor MCP no puede distinguir entre diferentes usuarios de TrustGraph
**Sin aislamiento de inquilinos**: Todos los inquilinos comparten la misma credencial para cada herramienta MCP
**Limitación del registro de auditoría**: El servidor MCP muestra todas las solicitudes de la misma credencial
**Ámbito de permisos**: No se pueden hacer cumplir diferentes niveles de permiso para diferentes usuarios
**NO use esta función si:**
Su implementación de TrustGraph sirve a múltiples organizaciones (multiinquilino)
Necesita realizar un seguimiento de qué usuario accedió a qué herramienta MCP
Diferentes usuarios requieren diferentes niveles de permiso
Necesita cumplir con los requisitos de auditoría a nivel de usuario
Su servidor MCP aplica límites de velocidad o cuotas por usuario
**Soluciones alternativas para escenarios multiusuario/multitenant:**
Implementar la propagación del contexto del usuario a través de encabezados personalizados
Implementar instancias de TrustGraph separadas por tenant
Utilizar aislamiento a nivel de red (VPCs, service meshes)
Implementar una capa de proxy que gestione la autenticación por usuario
### Almacenamiento de tokens
**Riesgo**: Los tokens de autenticación se almacenan en texto plano en el sistema de configuración
**Mitigación**:
Documentar que los tokens se almacenan sin cifrar
Recomendar el uso de tokens de corta duración siempre que sea posible
Recomendar un control de acceso adecuado en el almacenamiento de la configuración
Considerar una mejora futura para el almacenamiento de tokens cifrados
### Exposición de tokens
**Riesgo**: Los tokens podrían exponerse en registros o en la salida de la línea de comandos
**Mitigación**:
No registrar los valores de los tokens (solo registrar "autenticación configurada: sí/no")
El comando "show" de la línea de comandos muestra solo el estado enmascarado, no el token real
No incluir tokens en los mensajes de error
### Seguridad de la red
**Riesgo**: Los tokens se transmiten a través de conexiones no cifradas
**Mitigación**:
Documentar la recomendación de utilizar URL HTTPS para los servidores MCP
Advertir a los usuarios sobre el riesgo de transmisión en texto plano con HTTP
### Acceso a la configuración
**Riesgo**: El acceso no autorizado al sistema de configuración expone los tokens
**Mitigación**:
Documentar la importancia de asegurar el acceso al sistema de configuración
Recomendar el principio de mínimo privilegio para el acceso a la configuración
Considerar el registro de auditoría para los cambios de configuración (mejora futura)
### Entornos multiusuario
**Riesgo**: En las implementaciones multiusuario, todos los usuarios comparten las mismas credenciales de MCP
**Comprensión del riesgo**:
El usuario A y el usuario B utilizan el mismo token al acceder a una herramienta de MCP
El servidor MCP no puede distinguir entre diferentes usuarios de TrustGraph
No hay forma de aplicar permisos o límites de velocidad por usuario
Los registros de auditoría en el servidor MCP muestran todas las solicitudes del mismo identificador
Si la sesión de un usuario se ve comprometida, el atacante tiene el mismo acceso a MCP que todos los usuarios
**Esto NO es un error; es una limitación fundamental de este diseño.**
## Impacto en el rendimiento
**Bajo sobrecosto**: La construcción de encabezados agrega un tiempo de procesamiento insignificante
**Impacto en la red**: El encabezado HTTP adicional agrega ~50-200 bytes por solicitud
**Uso de memoria**: Aumento insignificante para almacenar la cadena de token en la configuración
## Documentación
### Documentación para el usuario
[ ] Actualizar `tg-set-mcp-tool.md` con el parámetro `--auth-token`
[ ] Agregar una sección de consideraciones de seguridad
[ ] Proporcionar un ejemplo de uso con un token de tipo "bearer"
[ ] Documentar las implicaciones del almacenamiento de tokens
### Documentación para el desarrollador
[ ] Agregar comentarios en línea para el manejo de tokens de autenticación en `service.py`
[ ] Documentar la lógica de construcción de encabezados
[ ] Actualizar la documentación del esquema de configuración de la herramienta MCP
## Preguntas abiertas
1. **Cifrado de tokens**: ¿Debemos implementar el almacenamiento de tokens cifrados en el sistema de configuración?
2. **Actualización de tokens**: ¿Soporte futuro para flujos de actualización de OAuth o rotación de tokens?
3. **Métodos de autenticación alternativos**: ¿Debemos admitir la autenticación básica, las claves API u otros métodos?
## Alternativas consideradas
1. **Variables de entorno para tokens**: Almacenar tokens en variables de entorno en lugar de en la configuración
**Rechazada**: Complica la implementación y la gestión de la configuración
2. **Almacén de secretos separado**: Utilizar un sistema de gestión de secretos dedicado
**Aplazada**: Fuera del alcance de la implementación inicial, considerar una mejora futura
3. **Múltiples métodos de autenticación**: Admitir Basic, API key, OAuth, etc.
**Rechazada**: Los tokens de tipo "bearer" cubren la mayoría de los casos de uso, mantener la implementación inicial simple
4. **Almacenamiento de tokens cifrado**: Cifrar los tokens en el sistema de configuración
**Aplazada**: La seguridad del sistema de configuración es una preocupación más amplia, posponer a un trabajo futuro
5. **Tokens por invocación**: Permitir que los tokens se pasen en el momento de la invocación
**Rechazada**: Viola la separación de responsabilidades, el agente no debe manejar las credenciales
## Referencias
[Especificación del protocolo MCP](https://github.com/modelcontextprotocol/spec)
[Autenticación Bearer HTTP (RFC 6750)](https://tools.ietf.org/html/rfc6750)
[Servicio de la herramienta MCP actual](../trustgraph-flow/trustgraph/agent/mcp_tool/service.py)
[Especificación de argumentos de la herramienta MCP](./mcp-tool-arguments.md)
## Apéndice
### Ejemplo de uso
**Configuración de la herramienta MCP con autenticación:**
```bash
tg-set-mcp-tool \
--id secure-tool \
--tool-url https://secure-server.example.com/mcp \
--auth-token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
**Mostrando herramientas de MCP:**
```bash
tg-show-mcp-tools
ID Remote Name URL Auth
----------- ----------- ------------------------------------ ------
secure-tool secure-tool https://secure-server.example.com/mcp Yes
public-tool public-tool http://localhost:3000/mcp No
```
### Ejemplo de configuración
**Almacenado en el sistema de configuración**:
```json
{
"type": "mcp",
"key": "secure-tool",
"value": "{\"url\": \"https://secure-server.example.com/mcp\", \"auth-token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\"}"
}
```
### Mejores prácticas de seguridad
1. **Utilice HTTPS**: Siempre utilice URL HTTPS para los servidores MCP con autenticación
2. **Tokens de corta duración**: Utilice tokens con fecha de caducidad siempre que sea posible
3. **Privilegios mínimos**: Conceda a los tokens los permisos mínimos requeridos
4. **Control de acceso**: Restrinja el acceso al sistema de configuración
5. **Rotación de tokens**: Rote los tokens regularmente
6. **Registro de auditoría**: Supervise los cambios de configuración para detectar eventos de seguridad

View file

@ -0,0 +1,266 @@
---
layout: default
title: "Especificación Técnica: Soporte para Backend de Almacenamiento Compatible con S3"
parent: "Spanish (Beta)"
---
# Especificación Técnica: Soporte para Backend de Almacenamiento Compatible con S3
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
El servicio Librarian utiliza almacenamiento de objetos compatible con S3 para el almacenamiento de blobs de documentos. Esta especificación documenta la implementación que permite el soporte para cualquier backend compatible con S3, incluyendo MinIO, Ceph RADOS Gateway (RGW), AWS S3, Cloudflare R2, DigitalOcean Spaces, y otros.
## Arquitectura
### Componentes de Almacenamiento
**Almacenamiento de Blobs**: Almacenamiento de objetos compatible con S3 a través de la biblioteca de cliente `minio` de Python.
**Almacenamiento de Metadatos**: Cassandra (almacena el mapeo de object_id y los metadatos del documento).
**Componente Afectado**: Solo el servicio Librarian.
**Patrón de Almacenamiento**: Almacenamiento híbrido con metadatos en Cassandra y contenido en almacenamiento compatible con S3.
### Implementación
**Biblioteca**: Cliente `minio` de Python (soporta cualquier API compatible con S3).
**Ubicación**: `trustgraph-flow/trustgraph/librarian/blob_store.py`
**Operaciones**:
`add()` - Almacenar blob con object_id UUID.
`get()` - Recuperar blob por object_id.
`remove()` - Eliminar blob por object_id.
`ensure_bucket()` - Crear bucket si no existe.
**Bucket**: `library`
**Ruta del Objeto**: `doc/{object_id}`
**Tipos MIME Soportados**: `text/plain`, `application/pdf`
### Archivos Clave
1. `trustgraph-flow/trustgraph/librarian/blob_store.py` - Implementación de BlobStore.
2. `trustgraph-flow/trustgraph/librarian/librarian.py` - Inicialización de BlobStore.
3. `trustgraph-flow/trustgraph/librarian/service.py` - Configuración del servicio.
4. `trustgraph-flow/pyproject.toml` - Dependencias (paquete `minio`).
5. `docs/apis/api-librarian.md` - Documentación de la API.
## Backends de Almacenamiento Soportados
La implementación funciona con cualquier sistema de almacenamiento de objetos compatible con S3:
### Probado/Soportado
**Ceph RADOS Gateway (RGW)** - Sistema de almacenamiento distribuido con API S3 (configuración predeterminada).
**MinIO** - Almacenamiento de objetos auto-alojado ligero.
**Garage** - Almacenamiento S3-compatible geo-distribuido ligero.
### Debería Funcionar (Compatible con S3)
**AWS S3** - Almacenamiento de objetos en la nube de Amazon.
**Cloudflare R2** - Almacenamiento S3-compatible de Cloudflare.
**DigitalOcean Spaces** - Almacenamiento de objetos de DigitalOcean.
**Wasabi** - Almacenamiento en la nube S3-compatible.
**Backblaze B2** - Almacenamiento de respaldo S3-compatible.
Cualquier otro servicio que implemente la API REST S3.
## Configuración
### Argumentos de la Línea de Comandos
```bash
librarian \
--object-store-endpoint <hostname:port> \
--object-store-access-key <access_key> \
--object-store-secret-key <secret_key> \
[--object-store-use-ssl] \
[--object-store-region <region>]
```
**Nota:** No incluya `http://` ni `https://` en el punto final. Use `--object-store-use-ssl` para habilitar HTTPS.
### Variables de entorno (Alternativa)
```bash
OBJECT_STORE_ENDPOINT=<hostname:port>
OBJECT_STORE_ACCESS_KEY=<access_key>
OBJECT_STORE_SECRET_KEY=<secret_key>
OBJECT_STORE_USE_SSL=true|false # Optional, default: false
OBJECT_STORE_REGION=<region> # Optional
```
### Ejemplos
**Ceph RADOS Gateway (predeterminado):**
```bash
--object-store-endpoint ceph-rgw:7480 \
--object-store-access-key object-user \
--object-store-secret-key object-password
```
**MinIO:**
```bash
--object-store-endpoint minio:9000 \
--object-store-access-key minioadmin \
--object-store-secret-key minioadmin
```
**Almacenamiento (compatible con S3):**
```bash
--object-store-endpoint garage:3900 \
--object-store-access-key GK000000000000000000000001 \
--object-store-secret-key b171f00be9be4c32c734f4c05fe64c527a8ab5eb823b376cfa8c2531f70fc427
```
**AWS S3 con SSL:**
```bash
--object-store-endpoint s3.amazonaws.com \
--object-store-access-key AKIAIOSFODNN7EXAMPLE \
--object-store-secret-key wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY \
--object-store-use-ssl \
--object-store-region us-east-1
```
## Autenticación
Todos los backends compatibles con S3 requieren la autenticación AWS Signature Version 4 (o v2):
**Clave de acceso** - Identificador público (como nombre de usuario)
**Clave secreta** - Clave de firma privada (como contraseña)
El cliente de Python de MinIO gestiona automáticamente todos los cálculos de firma.
### Creación de credenciales
**Para MinIO:**
```bash
# Use default credentials or create user via MinIO Console
minioadmin / minioadmin
```
**Para Ceph RGW:**
```bash
radosgw-admin user create --uid="trustgraph" --display-name="TrustGraph Service"
# Returns access_key and secret_key
```
**Para AWS S3:**
Crear usuario IAM con permisos de S3
Generar clave de acceso en la consola de AWS
## Selección de la biblioteca: Cliente de Python para MinIO
**Justificación:**
Ligera (~500 KB frente a los ~50 MB de boto3)
Compatible con S3: funciona con cualquier punto final de la API de S3
API más simple que boto3 para operaciones básicas
Ya en uso, no se necesita migración
Probada en batalla con MinIO y otros sistemas S3
## Implementación de BlobStore
**Ubicación:** `trustgraph-flow/trustgraph/librarian/blob_store.py`
```python
from minio import Minio
import io
import logging
logger = logging.getLogger(__name__)
class BlobStore:
"""
S3-compatible blob storage for document content.
Supports MinIO, Ceph RGW, AWS S3, and other S3-compatible backends.
"""
def __init__(self, endpoint, access_key, secret_key, bucket_name,
use_ssl=False, region=None):
"""
Initialize S3-compatible blob storage.
Args:
endpoint: S3 endpoint (e.g., "minio:9000", "ceph-rgw:7480")
access_key: S3 access key
secret_key: S3 secret key
bucket_name: Bucket name for storage
use_ssl: Use HTTPS instead of HTTP (default: False)
region: S3 region (optional, e.g., "us-east-1")
"""
self.client = Minio(
endpoint=endpoint,
access_key=access_key,
secret_key=secret_key,
secure=use_ssl,
region=region,
)
self.bucket_name = bucket_name
protocol = "https" if use_ssl else "http"
logger.info(f"Connected to S3-compatible storage at {protocol}://{endpoint}")
self.ensure_bucket()
def ensure_bucket(self):
"""Create bucket if it doesn't exist"""
found = self.client.bucket_exists(bucket_name=self.bucket_name)
if not found:
self.client.make_bucket(bucket_name=self.bucket_name)
logger.info(f"Created bucket {self.bucket_name}")
else:
logger.debug(f"Bucket {self.bucket_name} already exists")
async def add(self, object_id, blob, kind):
"""Store blob in S3-compatible storage"""
self.client.put_object(
bucket_name=self.bucket_name,
object_name=f"doc/{object_id}",
length=len(blob),
data=io.BytesIO(blob),
content_type=kind,
)
logger.debug("Add blob complete")
async def remove(self, object_id):
"""Delete blob from S3-compatible storage"""
self.client.remove_object(
bucket_name=self.bucket_name,
object_name=f"doc/{object_id}",
)
logger.debug("Remove blob complete")
async def get(self, object_id):
"""Retrieve blob from S3-compatible storage"""
resp = self.client.get_object(
bucket_name=self.bucket_name,
object_name=f"doc/{object_id}",
)
return resp.read()
```
## Beneficios Clave
1. **Sin dependencia de un proveedor** - Funciona con cualquier almacenamiento compatible con S3.
2. **Ligero** - El cliente MinIO tiene un tamaño de aproximadamente 500 KB.
3. **Configuración sencilla** - Solo necesita el punto de acceso y las credenciales.
4. **Sin migración de datos** - Reemplazo directo entre diferentes backends.
5. **Probado en batalla** - El cliente MinIO funciona con todas las principales implementaciones de S3.
## Estado de la implementación
Todo el código se ha actualizado para utilizar nombres de parámetros S3 genéricos:
`blob_store.py` - Actualizado para aceptar `endpoint`, `access_key`, `secret_key`
`librarian.py` - Nombres de parámetros actualizados
`service.py` - Argumentos de la línea de comandos y configuración actualizados
✅ Documentación actualizada
## Mejoras futuras
1. **Soporte para SSL/TLS** - Agregar la bandera `--s3-use-ssl` para HTTPS.
2. **Lógica de reintento** - Implementar un reintento exponencial para fallos transitorios.
3. **URLs prefirmadas** - Generar URLs temporales de carga/descarga.
4. **Soporte para múltiples regiones** - Replicar blobs a través de regiones.
5. **Integración con CDN** - Servir blobs a través de una CDN.
6. **Clases de almacenamiento** - Utilizar clases de almacenamiento de S3 para la optimización de costos.
7. **Políticas de ciclo de vida** - Archivado/eliminación automática.
8. **Versionado** - Almacenar múltiples versiones de blobs.
## Referencias
Cliente de MinIO para Python: https://min.io/docs/minio/linux/developers/python/API.html
API S3 de Ceph RGW: https://docs.ceph.com/en/latest/radosgw/s3/
Referencia de la API S3: https://docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html

View file

@ -0,0 +1,165 @@
---
layout: default
title: "Especificación Técnica de la CLI para Configuración"
parent: "Spanish (Beta)"
---
# Especificación Técnica de la CLI para Configuración
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Descripción general
Esta especificación describe las capacidades mejoradas de configuración a través de la línea de comandos para TrustGraph, lo que permite a los usuarios gestionar elementos de configuración individuales utilizando comandos CLI granulares. La integración soporta cuatro casos de uso principales:
1. **Listar Elementos de Configuración**: Mostrar las claves de configuración de un tipo específico.
2. **Obtener Elemento de Configuración**: Recuperar los valores de configuración específicos.
3. **Establecer Elemento de Configuración**: Establecer o actualizar elementos de configuración individuales.
4. **Eliminar Elemento de Configuración**: Eliminar elementos de configuración específicos.
## Objetivos
- **Control Granular**: Permitir la gestión de elementos de configuración individuales en lugar de operaciones masivas.
- **Listado Basado en Tipo**: Permitir a los usuarios explorar los elementos de configuración por tipo.
- **Operaciones en Elemento Individual**: Proporcionar comandos para obtener/establecer/eliminar elementos de configuración individuales.
- **Integración con API**: Aprovechar la API de Config existente para todas las operaciones.
- **Patrón CLI Consistente**: Seguir las convenciones y patrones CLI establecidos de TrustGraph.
- **Manejo de Errores**: Proporcionar mensajes de error claros para operaciones inválidas.
- **Salida JSON**: Soporte para salida estructurada para uso programático.
- **Documentación**: Incluir ayuda y ejemplos de uso completos.
## Antecedentes
Actualmente, TrustGraph proporciona la gestión de la configuración a través de la API de Config y un único comando de línea de comandos `tg-show-config` que muestra toda la configuración. Si bien esto funciona para la visualización de la configuración, carece de capacidades de gestión granular.
Las limitaciones actuales incluyen:
- No hay forma de listar los elementos de configuración por tipo desde la línea de comandos.
- No hay ningún comando de línea de comandos para recuperar valores de configuración específicos.
- No hay ningún comando de línea de comandos para establecer elementos de configuración individuales.
- No hay ningún comando de línea de comandos para eliminar elementos de configuración específicos.
Esta especificación aborda estas lagunas agregando cuatro nuevos comandos de línea de comandos que proporcionan la gestión de configuración granular. Al exponer las operaciones de la API de Config individual a través de comandos CLI, TrustGraph puede:
- Permitir la gestión de la configuración a través de scripts.
- Permitir la exploración de la estructura de la configuración por tipo.
- Soporte para actualizaciones de configuración dirigidas.
- Proporcionar un control granular de la configuración.
## Diseño Técnico
### Arquitectura
La configuración CLI mejorada requiere los siguientes componentes técnicos:
1. **tg-list-config-items**
- Lista las claves de configuración para un tipo específico.
- Llama al método de API `Config.list(type)`.
- Salida la lista de claves de configuración.
Módulo: `trustgraph.cli.list_config_items`
2. **tg-get-config-item**
- Recupera el(los) elemento(s) de configuración específico(s).
- Llama al método de API `Config.get([ConfigKey(type, key)])`.
- Salida los valores de configuración en formato JSON.
Módulo: `trustgraph.cli.get_config_item`
3. **tg-put-config-item**
- Establece o actualiza un elemento de configuración.
- Llama al método de API `Config.put([ConfigValue(type, key, value)])`.
- Acepta los parámetros de tipo, clave y valor.
Módulo: `trustgraph.cli.put_config_item`
4. **tg-delete-config-item**
- Elimina un elemento de configuración.
- Llama al método de API `Config.delete([ConfigKey(type, key)])`.
- Acepta los parámetros de tipo y clave.
Módulo: `trustgraph.cli.delete_config_item`
### Modelos de Datos
#### ConfigKey y ConfigValue
Los comandos utilizan las estructuras de datos existentes de `trustgraph.api.types`:
```python
@dataclasses.dataclass
class ConfigKey:
type : str
key : str
@dataclasses.dataclass
class ConfigValue:
type : str
key : str
value : str
```
Esto permite:
- Manejo de datos consistente entre la CLI y la API.
- Operaciones de configuración seguras por tipo.
- Formatos de entrada/salida estructurados.
- Integración con la API de Config existente.
### Especificaciones de la CLI
#### tg-list-config-items
```bash
tg-list-config-items --type <tipo-de-configuración> [--formato texto|json] [--url-api <url>]
```
- **Propósito**: Listar todas las claves de configuración para un tipo dado.
- **Llamada a la API**: `Config.list(type)`
- **Salida**:
- `texto` (predeterminado): Claves de configuración separadas por nuevas líneas.
- `json`: Array JSON de claves de configuración.
#### tg-get-config-item
```bash
tg-get-config-item --type <tipo-de-configuración> --clave <clave> [--formato texto|json] [--url-api <url>]
```
- **Propósito**: Recuperar el elemento de configuración específico.
- **Llamada a la API**: `Config.get([ConfigKey(type, key)])`
- **Salida**:
- `texto` (predeterminado): Cadena de texto sin comillas ni codificación.
- `json`: Cadena de texto codificada en JSON.
#### tg-put-config-item
```bash
tg-put-config-item --type <tipo-de-configuración> --clave <clave> --valor <valor> [--url-api <url>]
tg-put-config-item --type <tipo-de-configuración> --clave <clave> --stdin [--url-api <url>]
```
- **Propósito**: Establecer o actualizar el elemento de configuración.
- **Llamada a la API**: `Config.put([ConfigValue(type, key, value)])`
- **Opciones de entrada**:
- `--valor`: Valor de cadena proporcionado directamente en la línea de comandos.
- `--stdin`: Leer todo el valor de entrada desde la entrada estándar como el valor del elemento de configuración.
- **Salida**: Confirmación de éxito
#### tg-delete-config-item
```bash
tg-delete-config-item --type <tipo-de-configuración> --clave <clave> [--url-api <url>]
```
- **Propósito**: Eliminar el elemento de configuración.
- **Llamada a la API**: `Config.delete([ConfigKey(type, key)])`
- **Salida**: Confirmación de éxito
### Detalles de Implementación
- Se debe usar un formato JSON para la salida de la API.
- Se debe usar un formato de texto sin comillas para la salida de la API.
- Se deben usar los parámetros de línea de comandos para especificar el tipo, la clave y el valor.
- Se debe proporcionar una confirmación de éxito para las operaciones de la línea de comandos.
## Preguntas Abiertas
- ¿Deberían los comandos admitir operaciones en lote (múltiples claves) además de elementos individuales?
- ¿Cuál debe ser el formato de salida para las confirmaciones de éxito?
- ¿Cómo deberían descubrir los usuarios los tipos de configuración?
## Referencias
- API de Config existente: `trustgraph/api/config.py`
- Patrones de CLI: `trustgraph-cli/trustgraph/cli/show_config.py`
- Tipos de datos: `trustgraph/api/types.py`

View file

@ -0,0 +1,780 @@
---
layout: default
title: "Especificación técnica: Soporte para entornos multi-inquilino"
parent: "Spanish (Beta)"
---
# Especificación técnica: Soporte para entornos multi-inquilino
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
Habilite implementaciones multi-inquilino corrigiendo las discrepancias en los nombres de los parámetros que impiden la personalización de la cola y agregando la parametrización del espacio de claves de Cassandra.
## Contexto de la arquitectura
### Resolución de colas basada en flujos
El sistema TrustGraph utiliza una **arquitectura basada en flujos** para la resolución dinámica de colas, lo que inherentemente admite la multi-inquilinización:
Las **definiciones de flujo** se almacenan en Cassandra y especifican los nombres de las colas a través de definiciones de interfaz.
Los **nombres de las colas utilizan plantillas** con variables `{id}` que se reemplazan con los ID de las instancias de flujo.
Los **servicios resuelven dinámicamente las colas** buscando las configuraciones de flujo en el momento de la solicitud.
**Cada inquilino puede tener flujos únicos** con diferentes nombres de cola, lo que proporciona aislamiento.
Ejemplo de definición de interfaz de flujo:
```json
{
"interfaces": {
"triples-store": "persistent://tg/flow/triples-store:{id}",
"graph-embeddings-store": "persistent://tg/flow/graph-embeddings-store:{id}"
}
}
```
Cuando el inquilino A inicia el flujo `tenant-a-prod` y el inquilino B inicia el flujo `tenant-b-prod`, automáticamente obtienen colas aisladas:
`persistent://tg/flow/triples-store:tenant-a-prod`
`persistent://tg/flow/triples-store:tenant-b-prod`
**Servicios diseñados correctamente para la multi-inquilinización:**
**Knowledge Management (núcleos)** - Resuelve dinámicamente las colas a partir de la configuración del flujo que se pasa en las solicitudes.
**Servicios que necesitan correcciones:**
🔴 **Config Service** - La falta de coincidencia en el nombre del parámetro impide la personalización de la cola.
🔴 **Librarian Service** - Temas de gestión de almacenamiento codificados (se discute a continuación).
🔴 **Todos los servicios** - No se puede personalizar el keyspace de Cassandra.
## Declaración del problema
### Problema #1: Falta de coincidencia en el nombre del parámetro en AsyncProcessor
**CLI define:** `--config-queue` (nombre poco claro)
**Argparse convierte a:** `config_queue` (en el diccionario de parámetros)
**El código busca:** `config_push_queue`
**Resultado:** El parámetro se ignora, por defecto a `persistent://tg/config/config`
**Impacto:** Afecta a más de 32 servicios que heredan de AsyncProcessor.
**Bloquea:** Los despliegues multi-inquilinos no pueden usar colas de configuración específicas del inquilino.
**Solución:** Cambiar el nombre del parámetro de la CLI a `--config-push-queue` para mayor claridad (el cambio importante es aceptable, ya que la función actualmente está rota).
### Problema #2: Falta de coincidencia en el nombre del parámetro en Config Service
**CLI define:** `--push-queue` (nombre ambiguo)
**Argparse convierte a:** `push_queue` (en el diccionario de parámetros)
**El código busca:** `config_push_queue`
**Resultado:** El parámetro se ignora.
**Impacto:** El servicio de configuración no puede usar una cola de envío personalizada.
**Solución:** Cambiar el nombre del parámetro de la CLI a `--config-push-queue` para mayor coherencia y claridad (el cambio importante es aceptable).
### Problema #3: Keyspace de Cassandra codificado
**Actual:** El keyspace está codificado como `"config"`, `"knowledge"`, `"librarian"` en varios servicios.
**Resultado:** No se puede personalizar el keyspace para los despliegues multi-inquilinos.
**Impacto:** Servicios de configuración, núcleos y bibliotecario.
**Bloquea:** Múltiples inquilinos no pueden usar keyspaces de Cassandra separados.
### Problema #4: Arquitectura de gestión de colecciones ✅ COMPLETADO
**Anterior:** Las colecciones se almacenaban en el keyspace de Cassandra del bibliotecario a través de una tabla de colecciones separada.
**Anterior:** El bibliotecario utilizaba 4 temas de gestión de almacenamiento codificados para coordinar la creación/eliminación de colecciones:
`vector_storage_management_topic`
`object_storage_management_topic`
`triples_storage_management_topic`
`storage_management_response_topic`
**Problemas (Resueltos):**
Los temas codificados no se podían personalizar para los despliegues multi-inquilinos.
Coordinación asíncrona compleja entre el bibliotecario y 4 o más servicios de almacenamiento.
Infraestructura de tabla y gestión de Cassandra separada.
Colas de solicitud/respuesta no persistentes para operaciones críticas.
**Solución implementada:** Se migraron las colecciones al almacenamiento del servicio de configuración, se utiliza el envío de configuración para la distribución.
**Estado:** Todos los backends de almacenamiento se han migrado al patrón `CollectionConfigHandler`.
## Solución
Esta especificación aborda los problemas #1, #2, #3 y #4.
### Parte 1: Corregir las faltas de coincidencia en el nombre del parámetro
#### Cambio 1: Clase base AsyncProcessor - Cambiar el nombre del parámetro de la CLI
**Archivo:** `trustgraph-base/trustgraph/base/async_processor.py`
**Línea:** 260-264
**Actual:**
```python
parser.add_argument(
'--config-queue',
default=default_config_queue,
help=f'Config push queue {default_config_queue}',
)
```
**Fijo:**
```python
parser.add_argument(
'--config-push-queue',
default=default_config_queue,
help=f'Config push queue (default: {default_config_queue})',
)
```
**Justificación:**
Nombres más claros y explícitos.
Coincide con el nombre de la variable interna `config_push_queue`.
El cambio es aceptable ya que la función actualmente no está operativa.
No se necesita ningún cambio en el código de params.get() - ya busca el nombre correcto.
#### Cambio 2: Servicio de Configuración - Renombrar Parámetro de la Interfaz de Línea de Comandos
**Archivo:** `trustgraph-flow/trustgraph/config/service/service.py`
**Línea:** 276-279
**Actual:**
```python
parser.add_argument(
'--push-queue',
default=default_config_push_queue,
help=f'Config push queue (default: {default_config_push_queue})'
)
```
**Fijo:**
```python
parser.add_argument(
'--config-push-queue',
default=default_config_push_queue,
help=f'Config push queue (default: {default_config_push_queue})'
)
```
**Justificación:**
Nombres más claros: "config-push-queue" es más explícito que simplemente "push-queue".
Coincide con el nombre de la variable interna `config_push_queue`.
Consistente con el parámetro `--config-push-queue` de AsyncProcessor.
El cambio es aceptable ya que la función actualmente no está operativa.
No se necesita ningún cambio en el código en params.get() - ya busca el nombre correcto.
### Parte 2: Agregar la parametrización del espacio de claves de Cassandra
#### Cambio 3: Agregar el parámetro de espacio de claves al módulo cassandra_config
**Archivo:** `trustgraph-base/trustgraph/base/cassandra_config.py`
**Agregar argumento de la línea de comandos** (en la función `add_cassandra_args()`):
```python
parser.add_argument(
'--cassandra-keyspace',
default=None,
help='Cassandra keyspace (default: service-specific)'
)
```
**Agregar soporte para variables de entorno** (en la función `resolve_cassandra_config()`):
```python
keyspace = params.get(
"cassandra_keyspace",
os.environ.get("CASSANDRA_KEYSPACE")
)
```
**Actualizar el valor de retorno** de `resolve_cassandra_config()`:
Actualmente devuelve: `(hosts, username, password)`
Cambiar para que devuelva: `(hosts, username, password, keyspace)`
**Justificación:**
Coherente con el patrón de configuración de Cassandra existente
Disponible para todos los servicios a través de `add_cassandra_args()`
Admite la configuración mediante la línea de comandos y variables de entorno
#### Cambio 4: Servicio de Configuración - Utilizar Espacios de Claves Parametrizados
**Archivo:** `trustgraph-flow/trustgraph/config/service/service.py`
**Línea 30** - Eliminar el espacio de claves codificado de forma rígida:
```python
# DELETE THIS LINE:
keyspace = "config"
```
**Líneas 69-73** - Actualización de la resolución de la configuración de Cassandra:
**Actual:**
```python
cassandra_host, cassandra_username, cassandra_password = \
resolve_cassandra_config(params)
```
**Fijo:**
```python
cassandra_host, cassandra_username, cassandra_password, keyspace = \
resolve_cassandra_config(params, default_keyspace="config")
```
**Justificación:**
Mantiene la compatibilidad con versiones anteriores, utilizando "config" como valor predeterminado.
Permite la sobrescritura a través de `--cassandra-keyspace` o `CASSANDRA_KEYSPACE`.
#### Cambio 5: Servicio de Conocimiento (Cores) - Utilizar Espacios de Claves Parametrizados
**Archivo:** `trustgraph-flow/trustgraph/cores/service.py`
**Línea 37** - Eliminar el espacio de claves codificado de forma rígida:
```python
# DELETE THIS LINE:
keyspace = "knowledge"
```
**Actualización de la resolución de la configuración de Cassandra** (ubicación similar a la del servicio de configuración):
```python
cassandra_host, cassandra_username, cassandra_password, keyspace = \
resolve_cassandra_config(params, default_keyspace="knowledge")
```
#### Cambio 6: Servicio de Bibliotecario - Utilizar Claves de Espacio de Claves Parametrizadas
**Archivo:** `trustgraph-flow/trustgraph/librarian/service.py`
**Línea 51** - Eliminar el espacio de claves codificado de forma rígida:
```python
# DELETE THIS LINE:
keyspace = "librarian"
```
**Actualización de la resolución de la configuración de Cassandra** (ubicación similar a la del servicio de configuración):
```python
cassandra_host, cassandra_username, cassandra_password, keyspace = \
resolve_cassandra_config(params, default_keyspace="librarian")
```
### Parte 3: Migrar la gestión de colecciones al servicio de configuración
#### Resumen
Migrar las colecciones del espacio de claves de Cassandra librarian al almacenamiento del servicio de configuración. Esto elimina los temas de gestión de almacenamiento codificados de forma rígida y simplifica la arquitectura utilizando el mecanismo de envío de configuración existente para la distribución.
#### Arquitectura actual
```
API Request → Gateway → Librarian Service
CollectionManager
Cassandra Collections Table (librarian keyspace)
Broadcast to 4 Storage Management Topics (hardcoded)
Wait for 4+ Storage Service Responses
Response to Gateway
```
#### Nueva Arquitectura
```
API Request → Gateway → Librarian Service
CollectionManager
Config Service API (put/delete/getvalues)
Cassandra Config Table (class='collections', key='user:collection')
Config Push (to all subscribers on config-push-queue)
All Storage Services receive config update independently
```
#### Cambio 7: Administrador de Colecciones - Utilizar la API del Servicio de Configuración
**Archivo:** `trustgraph-flow/trustgraph/librarian/collection_manager.py`
**Eliminar:**
Uso de `LibraryTableStore` (Líneas 33, 40-41)
Inicialización de productores de gestión de almacenamiento (Líneas 86-140)
Método `on_storage_response` (Líneas 400-430)
Seguimiento de `pending_deletions` (Líneas 57, 90-96 y uso en todo el código)
**Añadir:**
Cliente del servicio de configuración para llamadas a la API (patrón de solicitud/respuesta)
**Configuración del Cliente:**
```python
# In __init__, add config request/response producers/consumers
from trustgraph.schema.services.config import ConfigRequest, ConfigResponse
# Producer for config requests
self.config_request_producer = Producer(
client=pulsar_client,
topic=config_request_queue,
schema=ConfigRequest,
)
# Consumer for config responses (with correlation ID)
self.config_response_consumer = Consumer(
taskgroup=taskgroup,
client=pulsar_client,
flow=None,
topic=config_response_queue,
subscriber=f"{id}-config",
schema=ConfigResponse,
handler=self.on_config_response,
)
# Tracking for pending config requests
self.pending_config_requests = {} # request_id -> asyncio.Event
```
**Modificar `list_collections` (Líneas 145-180):**
```python
async def list_collections(self, user, tag_filter=None, limit=None):
"""List collections from config service"""
# Send getvalues request to config service
request = ConfigRequest(
id=str(uuid.uuid4()),
operation='getvalues',
type='collections',
)
# Send request and wait for response
response = await self.send_config_request(request)
# Parse collections from response
collections = []
for key, value_json in response.values.items():
if ":" in key:
coll_user, collection = key.split(":", 1)
if coll_user == user:
metadata = json.loads(value_json)
collections.append(CollectionMetadata(**metadata))
# Apply tag filtering in-memory (as before)
if tag_filter:
collections = [c for c in collections if any(tag in c.tags for tag in tag_filter)]
# Apply limit
if limit:
collections = collections[:limit]
return collections
async def send_config_request(self, request):
"""Send config request and wait for response"""
event = asyncio.Event()
self.pending_config_requests[request.id] = event
await self.config_request_producer.send(request)
await event.wait()
return self.pending_config_requests.pop(request.id + "_response")
async def on_config_response(self, message, consumer, flow):
"""Handle config response"""
response = message.value()
if response.id in self.pending_config_requests:
self.pending_config_requests[response.id + "_response"] = response
self.pending_config_requests[response.id].set()
```
**Modificar `update_collection` (Líneas 182-312):**
```python
async def update_collection(self, user, collection, name, description, tags):
"""Update collection via config service"""
# Create metadata
metadata = CollectionMetadata(
user=user,
collection=collection,
name=name,
description=description,
tags=tags,
)
# Send put request to config service
request = ConfigRequest(
id=str(uuid.uuid4()),
operation='put',
type='collections',
key=f'{user}:{collection}',
value=json.dumps(metadata.to_dict()),
)
response = await self.send_config_request(request)
if response.error:
raise RuntimeError(f"Config update failed: {response.error.message}")
# Config service will trigger config push automatically
# Storage services will receive update and create collections
```
**Modificar `delete_collection` (Líneas 314-398):**
```python
async def delete_collection(self, user, collection):
"""Delete collection via config service"""
# Send delete request to config service
request = ConfigRequest(
id=str(uuid.uuid4()),
operation='delete',
type='collections',
key=f'{user}:{collection}',
)
response = await self.send_config_request(request)
if response.error:
raise RuntimeError(f"Config delete failed: {response.error.message}")
# Config service will trigger config push automatically
# Storage services will receive update and delete collections
```
**Formato de Metadatos de Colección:**
Almacenado en la tabla de configuración como: `class='collections', key='user:collection'`
El valor es una instancia de CollectionMetadata serializada en formato JSON (sin campos de marca de tiempo)
Campos: `user`, `collection`, `name`, `description`, `tags`
Ejemplo: `class='collections', key='alice:my-docs', value='{"user":"alice","collection":"my-docs","name":"My Documents","description":"...","tags":["work"]}'`
#### Cambio 8: Servicio de Bibliotecario - Eliminar la Infraestructura de Gestión de Almacenamiento
**Archivo:** `trustgraph-flow/trustgraph/librarian/service.py`
**Eliminar:**
Productores de gestión de almacenamiento (Líneas 173-190):
`vector_storage_management_producer`
`object_storage_management_producer`
`triples_storage_management_producer`
Consumidor de respuesta de almacenamiento (Líneas 192-201)
Controlador `on_storage_response` (Líneas 467-473)
**Modificar:**
Inicialización de CollectionManager (Líneas 215-224) - eliminar los parámetros del productor de almacenamiento
**Nota:** La API externa de colecciones permanece sin cambios:
`list-collections`
`update-collection`
`delete-collection`
#### Cambio 9: Eliminar la Tabla de Colecciones de LibraryTableStore
**Archivo:** `trustgraph-flow/trustgraph/tables/library.py`
**Eliminar:**
Sentencia CREATE de la tabla de colecciones (Líneas 114-127)
Sentencias preparadas de colecciones (Líneas 205-240)
Todos los métodos de colección (Líneas 578-717):
`ensure_collection_exists`
`list_collections`
`update_collection`
`delete_collection`
`get_collection`
`create_collection`
**Justificación:**
Las colecciones ahora se almacenan en la tabla de configuración
El cambio importante es aceptable: no se necesita migración de datos
Simplifica significativamente el servicio de bibliotecario
#### Cambio 10: Servicios de Almacenamiento - Gestión de Colecciones Basada en Configuración ✅ COMPLETADO
**Estado:** Todos los 11 backends de almacenamiento se han migrado para usar `CollectionConfigHandler`.
**Servicios Afectados (11 en total):**
Incrustaciones de documentos: milvus, pinecone, qdrant
Incrustaciones de grafos: milvus, pinecone, qdrant
Almacenamiento de objetos: cassandra
Almacenamiento de triples: cassandra, falkordb, memgraph, neo4j
**Archivos:**
`trustgraph-flow/trustgraph/storage/doc_embeddings/milvus/write.py`
`trustgraph-flow/trustgraph/storage/doc_embeddings/pinecone/write.py`
`trustgraph-flow/trustgraph/storage/doc_embeddings/qdrant/write.py`
`trustgraph-flow/trustgraph/storage/graph_embeddings/milvus/write.py`
`trustgraph-flow/trustgraph/storage/graph_embeddings/pinecone/write.py`
`trustgraph-flow/trustgraph/storage/graph_embeddings/qdrant/write.py`
`trustgraph-flow/trustgraph/storage/objects/cassandra/write.py`
`trustgraph-flow/trustgraph/storage/triples/cassandra/write.py`
`trustgraph-flow/trustgraph/storage/triples/falkordb/write.py`
`trustgraph-flow/trustgraph/storage/triples/memgraph/write.py`
`trustgraph-flow/trustgraph/storage/triples/neo4j/write.py`
**Patrón de Implementación (todos los servicios):**
1. **Registrar el controlador de configuración en `__init__`:**
```python
# Add after AsyncProcessor initialization
self.register_config_handler(self.on_collection_config)
self.known_collections = set() # Track (user, collection) tuples
```
2. **Implementar el manejador de configuración:**
```python
async def on_collection_config(self, config, version):
"""Handle collection configuration updates"""
logger.info(f"Collection config version: {version}")
if "collections" not in config:
return
# Parse collections from config
# Key format: "user:collection" in config["collections"]
config_collections = set()
for key in config["collections"].keys():
if ":" in key:
user, collection = key.split(":", 1)
config_collections.add((user, collection))
# Determine changes
to_create = config_collections - self.known_collections
to_delete = self.known_collections - config_collections
# Create new collections (idempotent)
for user, collection in to_create:
try:
await self.create_collection_internal(user, collection)
self.known_collections.add((user, collection))
logger.info(f"Created collection: {user}/{collection}")
except Exception as e:
logger.error(f"Failed to create {user}/{collection}: {e}")
# Delete removed collections (idempotent)
for user, collection in to_delete:
try:
await self.delete_collection_internal(user, collection)
self.known_collections.discard((user, collection))
logger.info(f"Deleted collection: {user}/{collection}")
except Exception as e:
logger.error(f"Failed to delete {user}/{collection}: {e}")
```
3. **Inicializar colecciones conocidas al inicio:**
```python
async def start(self):
"""Start the processor"""
await super().start()
await self.sync_known_collections()
async def sync_known_collections(self):
"""Query backend to populate known_collections set"""
# Backend-specific implementation:
# - Milvus/Pinecone/Qdrant: List collections/indexes matching naming pattern
# - Cassandra: Query keyspaces or collection metadata
# - Neo4j/Memgraph/FalkorDB: Query CollectionMetadata nodes
pass
```
4. **Refactorizar los métodos de manejo existentes:**
```python
# Rename and remove response sending:
# handle_create_collection → create_collection_internal
# handle_delete_collection → delete_collection_internal
async def create_collection_internal(self, user, collection):
"""Create collection (idempotent)"""
# Same logic as current handle_create_collection
# But remove response producer calls
# Handle "already exists" gracefully
pass
async def delete_collection_internal(self, user, collection):
"""Delete collection (idempotent)"""
# Same logic as current handle_delete_collection
# But remove response producer calls
# Handle "not found" gracefully
pass
```
5. **Eliminar la infraestructura de administración de almacenamiento:**
Eliminar la configuración y el inicio de `self.storage_request_consumer`
Eliminar la configuración de `self.storage_response_producer`
Eliminar el método de despachador de `on_storage_management`
Eliminar las métricas para la administración de almacenamiento
Eliminar las importaciones: `StorageManagementRequest`, `StorageManagementResponse`
**Consideraciones específicas del backend:**
**Almacenes de vectores (Milvus, Pinecone, Qdrant):** Realizar un seguimiento de `(user, collection)` lógico en `known_collections`, pero puede crear múltiples colecciones de backend por dimensión. Continuar con el patrón de creación perezosa. Las operaciones de eliminación deben eliminar todas las variantes de dimensión.
**Objetos Cassandra:** Las colecciones son propiedades de fila, no estructuras. Realizar un seguimiento de la información a nivel de keyspace.
**Almacenes de grafos (Neo4j, Memgraph, FalkorDB):** Consultar nodos `CollectionMetadata` al inicio. Crear/eliminar nodos de metadatos durante la sincronización.
**Triples de Cassandra:** Utilizar la API `KnowledgeGraph` para las operaciones de colección.
**Puntos clave de diseño:**
**Consistencia eventual:** No hay mecanismo de solicitud/respuesta, el empuje de configuración se transmite.
**Idempotencia:** Todas las operaciones de creación/eliminación deben ser seguras para reintentar.
**Manejo de errores:** Registrar los errores, pero no bloquear las actualizaciones de configuración.
**Autocuración:** Las operaciones fallidas se volverán a intentar en el siguiente empuje de configuración.
**Formato de clave de colección:** `"user:collection"` en `config["collections"]`
#### Cambio 11: Actualizar el esquema de la colección: eliminar las marcas de tiempo
**Archivo:** `trustgraph-base/trustgraph/schema/services/collection.py`
**Modificar CollectionMetadata (líneas 13-21):**
Eliminar los campos `created_at` y `updated_at`:
```python
class CollectionMetadata(Record):
user = String()
collection = String()
name = String()
description = String()
tags = Array(String())
# Remove: created_at = String()
# Remove: updated_at = String()
```
**Modificar CollectionManagementRequest (líneas 25-47):**
Eliminar campos de marca de tiempo:
```python
class CollectionManagementRequest(Record):
operation = String()
user = String()
collection = String()
timestamp = String()
name = String()
description = String()
tags = Array(String())
# Remove: created_at = String()
# Remove: updated_at = String()
tag_filter = Array(String())
limit = Integer()
```
**Justificación:**
Las marcas de tiempo no aportan valor a las colecciones.
El servicio de configuración mantiene su propio seguimiento de versiones.
Simplifica el esquema y reduce el almacenamiento.
#### Beneficios de la migración del servicio de configuración
1. ✅ **Elimina los temas de gestión de almacenamiento codificados de forma rígida** - Soluciona el bloqueo de multi-inquilino.
2. ✅ **Coordinación más sencilla** - No hay esperas asíncronas complejas para 4 o más respuestas de almacenamiento.
3. ✅ **Consistencia eventual** - Los servicios de almacenamiento se actualizan de forma independiente a través de la configuración.
4. ✅ **Mayor fiabilidad** - Configuración persistente frente a solicitud/respuesta no persistente.
5. ✅ **Modelo de configuración unificado** - Las colecciones se tratan como configuración.
6. ✅ **Reduce la complejidad** - Elimina aproximadamente 300 líneas de código de coordinación.
7. ✅ **Listo para multi-inquilino** - La configuración ya admite el aislamiento de inquilinos a través de espacios de claves.
8. ✅ **Seguimiento de versiones** - El mecanismo de versión del servicio de configuración proporciona un registro de auditoría.
## Notas de implementación
### Compatibilidad con versiones anteriores
**Cambios de parámetros:**
Los cambios de nombre de los parámetros de la CLI son cambios importantes, pero aceptables (la función actualmente no está operativa).
Los servicios funcionan sin parámetros (utilizan los valores predeterminados).
Los espacios de claves predeterminados se conservan: "config", "knowledge", "librarian".
Cola predeterminada: `persistent://tg/config/config`
**Gestión de colecciones:**
**Cambio importante:** La tabla de colecciones se elimina del espacio de claves de librarian.
**No se proporciona migración de datos** - aceptable para esta fase.
La API externa de colecciones no cambia (operaciones de lista, actualización y eliminación).
El formato de los metadatos de la colección se simplifica (se eliminan las marcas de tiempo).
### Requisitos de prueba
**Pruebas de parámetros:**
1. Verificar que el parámetro `--config-push-queue` funciona en el servicio graph-embeddings.
2. Verificar que el parámetro `--config-push-queue` funciona en el servicio text-completion.
3. Verificar que el parámetro `--config-push-queue` funciona en el servicio de configuración.
4. Verificar que el parámetro `--cassandra-keyspace` funciona para el servicio de configuración.
5. Verificar que el parámetro `--cassandra-keyspace` funciona para el servicio cores.
6. Verificar que el parámetro `--cassandra-keyspace` funciona para el servicio librarian.
7. Verificar que los servicios funcionan sin parámetros (utiliza los valores predeterminados).
8. Verificar la implementación multi-inquilino con nombres de cola y espacios de claves personalizados.
**Pruebas de gestión de colecciones:**
9. Verificar la operación `list-collections` a través del servicio de configuración.
10. Verificar que `update-collection` crea/actualiza en la tabla de configuración.
11. Verificar que `delete-collection` elimina de la tabla de configuración.
12. Verificar que se activa la propagación de la configuración cuando se actualizan las colecciones.
13. Verificar que el filtrado de etiquetas funciona con el almacenamiento basado en la configuración.
14. Verificar que las operaciones de la colección funcionan sin campos de marca de tiempo.
### Ejemplo de implementación multi-inquilino
```bash
# Tenant: tg-dev
graph-embeddings \
-p pulsar+ssl://broker:6651 \
--pulsar-api-key <KEY> \
--config-push-queue persistent://tg-dev/config/config
config-service \
-p pulsar+ssl://broker:6651 \
--pulsar-api-key <KEY> \
--config-push-queue persistent://tg-dev/config/config \
--cassandra-keyspace tg_dev_config
```
## Análisis de Impacto
### Servicios Afectados por el Cambio 1-2 (Renombramiento de Parámetro de la CLI)
Todos los servicios que heredan de AsyncProcessor o FlowProcessor:
config-service
cores-service
librarian-service
graph-embeddings
document-embeddings
text-completion-* (todos los proveedores)
extract-* (todos los extractores)
query-* (todos los servicios de consulta)
retrieval-* (todos los servicios RAG)
storage-* (todos los servicios de almacenamiento)
Y más de 20 servicios
### Servicios Afectados por los Cambios 3-6 (Espacio de Claves de Cassandra)
config-service
cores-service
librarian-service
### Servicios Afectados por los Cambios 7-11 (Gestión de Colecciones)
**Cambios Inmediatos:**
librarian-service (collection_manager.py, service.py)
tables/library.py (eliminación de la tabla de colecciones)
schema/services/collection.py (eliminación de la marca de tiempo)
**Cambios Completados (Cambio 10):** ✅
Todos los servicios de almacenamiento (11 en total) - migrados a la configuración push para las actualizaciones de colecciones a través de `CollectionConfigHandler`
Esquema de gestión de almacenamiento eliminado de `storage.py`
## Consideraciones Futuras
### Modelo de Espacio de Claves por Usuario
Algunos servicios utilizan **espacios de claves por usuario** dinámicamente, donde cada usuario obtiene su propio espacio de claves de Cassandra:
**Servicios con espacios de claves por usuario:**
1. **Servicio de Consulta de Triples** (`trustgraph-flow/trustgraph/query/triples/cassandra/service.py:65`)
Utiliza `keyspace=query.user`
2. **Servicio de Consulta de Objetos** (`trustgraph-flow/trustgraph/query/objects/cassandra/service.py:479`)
Utiliza `keyspace=self.sanitize_name(user)`
3. **Acceso Directo al Gráfico de Conocimiento** (`trustgraph-flow/trustgraph/direct/cassandra_kg.py:18`)
Parámetro predeterminado `keyspace="trustgraph"`
**Estado:** Estos **no se modifican** en esta especificación.
**Revisión Futura Requerida:**
Evaluar si el modelo de espacio de claves por usuario crea problemas de aislamiento de inquilinos
Considerar si las implementaciones multi-inquilino necesitan patrones de prefijos de espacio de claves (por ejemplo, `tenant_a_user1`)
Revisar posibles colisiones de ID de usuario entre inquilinos
Evaluar si un espacio de claves compartido único por inquilino con aislamiento de filas basado en el usuario es preferible
**Nota:** Esto no bloquea la implementación multi-inquilino actual, pero debe revisarse antes de las implementaciones multi-inquilino de producción.
## Fases de Implementación
### Fase 1: Correcciones de Parámetros (Cambios 1-6)
Corregir el nombre del parámetro `--config-push-queue`
Agregar soporte para el parámetro `--cassandra-keyspace`
**Resultado:** Configuración de cola y espacio de claves multi-inquilino habilitada
### Fase 2: Migración de la Gestión de Colecciones (Cambios 7-9, 11)
Migrar el almacenamiento de colecciones al servicio de configuración
Eliminar la tabla de colecciones de librarian
Actualizar el esquema de colecciones (eliminar marcas de tiempo)
**Resultado:** Elimina la gestión de almacenamiento codificada, simplifica librarian
### Fase 3: Actualizaciones del Servicio de Almacenamiento (Cambio 10) ✅ COMPLETADO
Se actualizaron todos los servicios de almacenamiento para usar la configuración push para las colecciones a través de `CollectionConfigHandler`
Se eliminó la infraestructura de solicitud/respuesta de gestión de almacenamiento
Se eliminaron las definiciones de esquema heredadas
**Resultado:** Se logró una gestión de colecciones basada en configuración completa
## Referencias
Problema de GitHub: https://github.com/trustgraph-ai/trustgraph/issues/582
Archivos relacionados:
`trustgraph-base/trustgraph/base/async_processor.py`
`trustgraph-base/trustgraph/base/cassandra_config.py`
`trustgraph-base/trustgraph/schema/core/topic.py`
`trustgraph-base/trustgraph/schema/services/collection.py`
`trustgraph-flow/trustgraph/config/service/service.py`
`trustgraph-flow/trustgraph/cores/service.py`
`trustgraph-flow/trustgraph/librarian/service.py`
`trustgraph-flow/trustgraph/librarian/collection_manager.py`
`trustgraph-flow/trustgraph/tables/library.py`

View file

@ -0,0 +1,217 @@
---
layout: default
title: "Soporte de aislamiento de usuarios/colecciones en Neo4j"
parent: "Spanish (Beta)"
---
# Soporte de aislamiento de usuarios/colecciones en Neo4j
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Declaración del problema
La implementación actual de almacenamiento y consulta de triples en Neo4j carece de aislamiento de usuarios/colecciones, lo que genera una vulnerabilidad de seguridad para entornos multi-inquilinos. Todos los triples se almacenan en el mismo espacio de grafo sin ningún mecanismo para evitar que los usuarios accedan a los datos de otros usuarios o mezclen colecciones.
A diferencia de otros backends de almacenamiento en TrustGraph:
- **Cassandra**: Utiliza espacios de claves y tablas separados por usuario y colección
- **Almacenes vectoriales** (Milvus, Qdrant, Pinecone): Utilizan espacios de nombres específicos de la colección
- **Neo4j**: Actualmente comparte todos los datos en un único grafo (vulnerabilidad de seguridad)
## Arquitectura actual
### Modelo de datos
- **Nodos**: Etiqueta `:Node` con propiedad `uri`, etiqueta `:Literal` con propiedad `value`
- **Relaciones**: Etiqueta `:Rel` con propiedad `uri`
- **Índices**: `Node.uri`, `Literal.value`, `Rel.uri`
### Flujo de mensajes
- Los mensajes `Triples` contienen los campos `metadata.user` y `metadata.collection`
- El servicio de almacenamiento recibe la información del usuario/colección, pero la ignora
- El servicio de consulta espera `user` y `collection` en `TriplesQueryRequest`, pero los ignora
### Problema de seguridad actual
```cypher
# Cualquier usuario puede consultar cualquier dato - sin aislamiento
MATCH (src:Node)-[rel:Rel]->(dest:Node)
RETURN src.uri, rel.uri, dest.uri
```
## Solución propuesta: Filtrado basado en propiedades (Recomendado)
### Descripción general
Añadir las propiedades `user` y `collection` a todos los nodos y relaciones, y luego filtrar todas las operaciones por estas propiedades. Este enfoque proporciona un fuerte aislamiento manteniendo la flexibilidad de consulta y la compatibilidad con versiones anteriores.
### Cambios en el modelo de datos
#### Estructura de nodos mejorada
```cypher
// Entidades de nodo
CREATE (n:Node {
uri: "http://example.com/entity1",
user: "john_doe",
collection: "production_v1"
})
// Entidades de literal
CREATE (n:Literal {
value: "literal value",
user: "john_doe",
collection: "production_v1"
})
```
#### Estructura de relaciones mejorada
```cypher
// Relaciones con propiedades user/collection
CREATE (src)-[:Rel {
uri: "http://example.com/predicate1",
user: "john_doe",
collection: "production_v1"
}]->(dest)
```
#### Índices actualizados
```cypher
// Índices compuestos para un filtrado eficiente
CREATE INDEX node_user_collection_uri FOR (n:Node) ON (n.user, n.collection, n.uri);
CREATE INDEX literal_user_collection_value FOR (n:Literal) ON (n.user, n.collection, n.value);
CREATE INDEX rel_user_collection_uri FOR ()-[r:Rel]-() ON (r.user, r.collection, r.uri);
// Mantener índices existentes para la compatibilidad con versiones anteriores (opcional)
CREATE INDEX Node_uri FOR (n:Node) ON (n.uri);
CREATE INDEX Literal_value FOR (n:Literal) ON (n.value);
CREATE INDEX Rel_uri FOR ()-[r:Rel]-() ON (r.uri);
```
### Cambios de implementación
#### Servicio de almacenamiento (`write.py`)
**Código actual:**
```python
def create_node(self, uri):
summary = self.io.execute_query(
"MERGE (n:Node {uri: $uri})",
uri=uri, database_=self.db,
).summary
```
**Código actualizado:**
```python
def create_node(self, uri, user, collection):
summary = self.io.execute_query(
"MERGE (n:Node {uri: $uri, user: $user, collection: $collection})",
uri=uri, user=user, collection=collection, database_=self.db,
).summary
```
#### Función query
```python
def query_triples(self, query):
# Implementar lógica para filtrar por usuario y colección en la consulta
# Por ejemplo, reemplazar 'user' y 'collection' en la consulta
# Usar la función de ejecución de consultas de Neo4j para reemplazar
# los marcadores de posición con los valores del usuario y la colección
# y luego ejecutar la consulta.
# Ejemplo (pseudocódigo):
# resultado = neo4j.execute_query(query.replace("user", self.user), self.collection)
# return resultado
pass
```
#### Función store_triples
```python
def store_triples(self, triples):
# Implementar lógica para almacenar triples con las propiedades user y collection
# Por ejemplo, añadir las propiedades user y collection a los nodos al crear
# nuevos nodos y relaciones.
pass
```
### Pruebas
### Pruebas unitarias
```python
def test_user_collection_isolation():
# Almacenar triples para user1/collection1
processor.store_triples(triples_user1_coll1)
# Almacenar triples para user2/collection2
processor.store_triples(triples_user2_coll2)
# Consultar como user1 solo debería devolver datos de user1/collection1
resultados = processor.query_triples(query_user1_coll1)
assert all_results_belong_to_user1_coll1(resultados)
# Consultar como user2 solo debería devolver datos de user2/collection2
resultados = processor.query_triples(query_user2_coll2)
assert all_results_belong_to_user2_coll2(resultados)
```
### Pruebas de integración
- Escenarios de múltiples usuarios con datos superpuestos
- Consultas de cross-colección (deberían fallar)
- Pruebas de migración con datos existentes
- Pruebas de rendimiento con grandes conjuntos de datos
### Pruebas de seguridad
- Intentar consultar datos de otros usuarios
- Ataques de inyección SQL en parámetros de usuario/colección
- Verificar el aislamiento completo bajo diferentes patrones de consulta
## Consideraciones de rendimiento
### Estrategia de índice
- Índices compuestos en `(user, collection, uri)` para un filtrado óptimo
- Considerar índices parciales si algunas colecciones son mucho más grandes
- Supervisar el uso y el rendimiento de los índices
### Optimización de consultas
- Utilizar EXPLAIN para verificar el uso de índices en las consultas filtradas
- Considerar el almacenamiento en caché de resultados para datos accedidos con frecuencia
- Perfilar el uso de memoria con un gran número de usuarios/colecciones
### Escalabilidad
- Cada combinación de usuario/colección crea islas de datos separadas
- Supervisar el tamaño de la base de datos y el uso de la piscina de conexiones
- Considerar estrategias de escalado horizontal si es necesario
## Seguridad y cumplimiento
### Garantías de aislamiento de datos
- **Físico**: Todos los datos del usuario almacenados con propiedades de usuario/colección explícitas
- **Lógico**: Todas las consultas filtradas por contexto de usuario/colección
- **Control de acceso**: Validación a nivel de servicio para evitar el acceso no autorizado
### Requisitos de auditoría
- Registrar todos los accesos de datos con contexto de usuario/colección
- Rastrear las actividades de migración y los movimientos de datos
- Supervisar los intentos de violar el aislamiento
### Consideraciones de cumplimiento
- GDPR: Mayor capacidad para localizar y eliminar datos específicos del usuario
- SOC2: Claros controles de aislamiento de datos y acceso
- HIPAA: Fuerte aislamiento de inquilinos para datos de atención médica
## Riesgos y mitigaciones
| Riesgo | Impacto | Probabilidad | Mitigación |
|------|--------|------------|------------|
| Consulta sin filtro de usuario/colección | Alto | Medio | Validación obligatoria, pruebas exhaustivas |
| Degradación del rendimiento | Medio | Bajo | Optimización del índice, perfilado de consultas |
| Corrupción de datos durante la migración | Alto | Bajo | Estrategia de copia de seguridad, procedimientos de reversión |
| Complejidad de consultas multi-colección | Medio | Medio | Documentar los patrones de consulta, proporcionar ejemplos |
## Criterios de éxito
1. **Seguridad**: Cero acceso de datos cruzado de usuarios en producción
2. **Rendimiento**: <10% de impacto en el rendimiento de las consultas en comparación con las consultas no filtradas
3. **Migración**: 100% de los datos existentes migrados sin pérdida de datos
4. **Usabilidad**: Todos los patrones de consulta existentes funcionan con contexto de usuario/colección
5. **Cumplimiento**: Rastro de auditoría completo del acceso de datos de usuario/colección
## Conclusión
El enfoque de filtrado basado en propiedades proporciona el mejor equilibrio de seguridad, rendimiento y mantenibilidad para añadir aislamiento de usuarios/colecciones a Neo4j. Se alinea con los patrones de multi-inquilinos existentes de TrustGraph al tiempo que aprovecha las fortalezas de Neo4j en la consulta y el indexado de grafos.
Esta solución garantiza que el backend de Neo4j de TrustGraph cumpla con los mismos estándares de seguridad que otros backends de almacenamiento, evitando las vulnerabilidades de aislamiento de datos al tiempo que mantiene la flexibilidad y el poder de las consultas de grafos.

View file

@ -0,0 +1,769 @@
---
layout: default
title: "Extracción de Conocimiento Ontológico - Fase 2, Refactorización"
parent: "Spanish (Beta)"
---
# Extracción de Conocimiento Ontológico - Fase 2, Refactorización
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
**Estado**: Borrador
**Autor**: Sesión de Análisis 2025-12-03
**Relacionado**: `ontology.md`, `ontorag.md`
## Resumen
Este documento identifica inconsistencias en el sistema actual de extracción de conocimiento basado en ontologías y propone una refactorización para mejorar el rendimiento de los LLM y reducir la pérdida de información.
## Implementación Actual
### Cómo Funciona Actualmente
1. **Carga de la Ontología** (`ontology_loader.py`)
Carga el archivo JSON de la ontología con claves como `"fo/Recipe"`, `"fo/Food"`, `"fo/produces"`
Los ID de las clases incluyen el prefijo del espacio de nombres en la clave.
Ejemplo de `food.ontology`:
```json
"classes": {
"fo/Recipe": {
"uri": "http://purl.org/ontology/fo/Recipe",
"rdfs:comment": "A Recipe is a combination..."
}
}
```
2. **Construcción del prompt** (`extract.py:299-307`, `ontology-prompt.md`)
La plantilla recibe diccionarios `classes`, `object_properties`, `datatype_properties`
La plantilla itera: `{% for class_id, class_def in classes.items() %}`
El LLM ve: `**fo/Recipe**: A Recipe is a combination...`
El formato de salida de ejemplo muestra:
```json
{"subject": "recipe:cornish-pasty", "predicate": "rdf:type", "object": "Recipe"}
{"subject": "recipe:cornish-pasty", "predicate": "has_ingredient", "object": "ingredient:flour"}
```
3. **Análisis de la respuesta** (`extract.py:382-428`)
Espera un array JSON: `[{"subject": "...", "predicate": "...", "object": "..."}]`
Valida contra un subconjunto de la ontología
Expande los URIs mediante `expand_uri()` (extract.py:473-521)
4. **Expansión de URIs** (`extract.py:473-521`)
Comprueba si el valor está en el diccionario `ontology_subset.classes`
Si se encuentra, extrae el URI de la definición de la clase
Si no se encuentra, construye el URI: `f"https://trustgraph.ai/ontology/{ontology_id}#{value}"`
### Ejemplo de flujo de datos
**JSON de la ontología → Loader → Prompt:**
```
"fo/Recipe" → classes["fo/Recipe"] → LLM sees "**fo/Recipe**"
```
**LLM → Analizador → Salida:**
```
"Recipe" → not in classes["fo/Recipe"] → constructs URI → LOSES original URI
"fo/Recipe" → found in classes → uses original URI → PRESERVES URI
```
## Problemas Identificados
### 1. **Ejemplos Inconsistentes en la Instrucción**
**Problema**: La plantilla de la instrucción muestra ID de clase con prefijos (`fo/Recipe`) pero la salida de ejemplo utiliza nombres de clase sin prefijos (`Recipe`).
**Ubicación**: `ontology-prompt.md:5-52`
```markdown
## Ontology Classes:
- **fo/Recipe**: A Recipe is...
## Example Output:
{"subject": "recipe:cornish-pasty", "predicate": "rdf:type", "object": "Recipe"}
```
**Impacto**: El modelo de lenguaje (LLM) recibe señales contradictorias sobre qué formato utilizar.
### 2. **Pérdida de información en la expansión de URI**
**Problema**: Cuando el LLM devuelve nombres de clase sin prefijo, siguiendo el ejemplo, `expand_uri()` no puede encontrarlos en el diccionario de ontología y construye URI de respaldo, perdiendo los URI originales correctos.
**Ubicación**: `extract.py:494-500`
```python
if value in ontology_subset.classes: # Looks for "Recipe"
class_def = ontology_subset.classes[value] # But key is "fo/Recipe"
if isinstance(class_def, dict) and 'uri' in class_def:
return class_def['uri'] # Never reached!
return f"https://trustgraph.ai/ontology/{ontology_id}#{value}" # Fallback
```
**Impacto:**
URI original: `http://purl.org/ontology/fo/Recipe`
URI construido: `https://trustgraph.ai/ontology/food#Recipe`
Significado semántico perdido, interrumpe la interoperabilidad.
### 3. **Formato ambiguo de instancia de entidad**
**Problema:** No hay una guía clara sobre el formato de la URI de la instancia de entidad.
**Ejemplos en la solicitud:**
`"recipe:cornish-pasty"` (prefijo similar a un espacio de nombres)
`"ingredient:flour"` (prefijo diferente)
**Comportamiento real** (extract.py:517-520):
```python
# Treat as entity instance - construct unique URI
normalized = value.replace(" ", "-").lower()
return f"https://trustgraph.ai/{ontology_id}/{normalized}"
```
**Impacto**: El modelo de lenguaje debe adivinar la convención de prefijos sin contexto ontológico.
### 4. **Sin Guía de Prefijos de Espacio de Nombres**
**Problema**: El archivo JSON de la ontología contiene definiciones de espacios de nombres (líneas 10-25 en food.ontology):
```json
"namespaces": {
"fo": "http://purl.org/ontology/fo/",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
...
}
```
Pero estas líneas nunca se muestran al LLM. El LLM no sabe:
Qué significa "fo"
Qué prefijo usar para las entidades
A qué espacio de nombres se aplica a qué elementos
### 5. **Etiquetas No Utilizadas en el Prompt**
**Problema**: Cada clase tiene campos `rdfs:label` (por ejemplo, `{"value": "Recipe", "lang": "en-gb"}`), pero la plantilla del prompt no los utiliza.
**Actual**: Muestra solo `class_id` y `comment`
```jinja
- **{{class_id}}**{% if class_def.comment %}: {{class_def.comment}}{% endif %}
```
**Disponible pero no utilizado**:
```python
"rdfs:label": [{"value": "Recipe", "lang": "en-gb"}]
```
**Impacto**: Podría proporcionar nombres legibles por humanos junto con identificadores técnicos.
## Soluciones propuestas
### Opción A: Normalizar a identificadores sin prefijos
**Enfoque**: Eliminar los prefijos de los identificadores de clase antes de mostrarlos al LLM.
**Cambios**:
1. Modificar `build_extraction_variables()` para transformar las claves:
```python
classes_for_prompt = {
k.split('/')[-1]: v # "fo/Recipe" → "Recipe"
for k, v in ontology_subset.classes.items()
}
```
2. Actualizar el ejemplo de la instrucción para que coincida (ya utiliza nombres sin prefijos).
3. Modificar `expand_uri()` para que gestione ambos formatos:
```python
# Try exact match first
if value in ontology_subset.classes:
return ontology_subset.classes[value]['uri']
# Try with prefix
for prefix in ['fo/', 'rdf:', 'rdfs:']:
prefixed = f"{prefix}{value}"
if prefixed in ontology_subset.classes:
return ontology_subset.classes[prefixed]['uri']
```
**Ventajas:**
Más limpio, más legible para los humanos.
Coincide con ejemplos de prompts existentes.
Los LLM funcionan mejor con tokens más simples.
**Desventajas:**
Colisiones de nombres de clase si múltiples ontologías tienen el mismo nombre de clase.
Pierde la información del espacio de nombres.
Requiere lógica de respaldo para las búsquedas.
### Opción B: Utilizar IDs con Prefijos Completos de Forma Consistente
**Enfoque:** Actualizar los ejemplos para utilizar IDs con prefijos que coincidan con lo que se muestra en la lista de clases.
**Cambios:**
1. Actualizar el ejemplo del prompt (ontology-prompt.md:46-52):
```json
[
{"subject": "recipe:cornish-pasty", "predicate": "rdf:type", "object": "fo/Recipe"},
{"subject": "recipe:cornish-pasty", "predicate": "rdfs:label", "object": "Cornish Pasty"},
{"subject": "recipe:cornish-pasty", "predicate": "fo/produces", "object": "food:cornish-pasty"},
{"subject": "food:cornish-pasty", "predicate": "rdf:type", "object": "fo/Food"}
]
```
2. Agregar una explicación del espacio de nombres a la instrucción:
```markdown
## Namespace Prefixes:
- **fo/**: Food Ontology (http://purl.org/ontology/fo/)
- **rdf:**: RDF Schema
- **rdfs:**: RDF Schema
Use these prefixes exactly as shown when referencing classes and properties.
```
3. Mantener `expand_uri()` tal cual (funciona correctamente cuando se encuentran coincidencias).
**Ventajas**:
Consistencia entre entrada y salida.
Sin pérdida de información.
Preserva la semántica del espacio de nombres.
Funciona con múltiples ontologías.
**Desventajas**:
Tokens más verbosos para el LLM.
Requiere que el LLM rastree los prefijos.
### Opción C: Híbrida: Mostrar tanto la etiqueta como el ID.
**Enfoque**: Mejorar el prompt para mostrar tanto las etiquetas legibles por humanos como los ID técnicos.
**Cambios**:
1. Actualizar la plantilla del prompt:
```jinja
{% for class_id, class_def in classes.items() %}
- **{{class_id}}** (label: "{{class_def.labels[0].value if class_def.labels else class_id}}"){% if class_def.comment %}: {{class_def.comment}}{% endif %}
{% endfor %}
```
Ejemplo de salida:
```markdown
- **fo/Recipe** (label: "Recipe"): A Recipe is a combination...
```
2. Instrucciones de actualización:
```markdown
When referencing classes:
- Use the full prefixed ID (e.g., "fo/Recipe") in JSON output
- The label (e.g., "Recipe") is for human understanding only
```
**Ventajas**:
Más claro para los modelos de lenguaje (LLM).
Preserva toda la información.
Explícito sobre qué usar.
**Desventajas**:
Requiere un prompt más largo.
Plantilla más compleja.
## Enfoque Implementado
**Formato Simplificado de Entidad-Relación-Atributo** - reemplaza completamente el formato basado en triples anterior.
El nuevo enfoque se eligió porque:
1. **Sin Pérdida de Información**: Los URI originales se conservan correctamente.
2. **Lógica Más Simple**: No se necesita transformación, las búsquedas directas en diccionarios funcionan.
3. **Seguridad de Espacios de Nombres**: Maneja múltiples ontologías sin colisiones.
4. **Corrección Semántica**: Mantiene la semántica RDF/OWL.
## Implementación Completada
### Lo que se Construyó:
1. **Nueva Plantilla de Prompt** (`prompts/ontology-extract-v2.txt`)
✅ Secciones claras: Tipos de Entidad, Relaciones, Atributos.
✅ Ejemplo utilizando identificadores de tipo completos (`fo/Recipe`, `fo/has_ingredient`).
✅ Instrucciones para usar los identificadores exactos del esquema.
✅ Nuevo formato JSON con matrices de entidades/relaciones/atributos.
2. **Normalización de Entidades** (`entity_normalizer.py`)
`normalize_entity_name()` - Convierte los nombres a un formato seguro para URI.
`normalize_type_identifier()` - Maneja las barras diagonales en los tipos (`fo/Recipe``fo-recipe`).
`build_entity_uri()` - Crea URI únicos utilizando la tupla (nombre, tipo).
`EntityRegistry` - Realiza un seguimiento de las entidades para la eliminación de duplicados.
3. **Analizador JSON** (`simplified_parser.py`)
✅ Analiza el nuevo formato: `{entities: [...], relationships: [...], attributes: [...]}`
✅ Admite nombres de campo en formato kebab-case y snake_case.
✅ Devuelve clases de datos estructuradas.
✅ Manejo de errores con registro.
4. **Convertidor de Triples** (`triple_converter.py`)
`convert_entity()` - Genera automáticamente triples de tipo + etiqueta.
`convert_relationship()` - Conecta los URI de las entidades a través de propiedades.
`convert_attribute()` - Agrega valores literales.
✅ Busca URI completos a partir de las definiciones de la ontología.
5. **Procesador Principal Actualizado** (`extract.py`)
✅ Se eliminó el código antiguo de extracción basado en triples.
✅ Se agregó el método `extract_with_simplified_format()`.
✅ Ahora utiliza exclusivamente el nuevo formato simplificado.
✅ Llama al indicador con el ID `extract-with-ontologies-v2`.
## Casos de Prueba
### Prueba 1: Preservación de URI
```python
# Given ontology class
classes = {"fo/Recipe": {"uri": "http://purl.org/ontology/fo/Recipe", ...}}
# When LLM returns
llm_output = {"subject": "x", "predicate": "rdf:type", "object": "fo/Recipe"}
# Then expanded URI should be
assert expanded == "http://purl.org/ontology/fo/Recipe"
# Not: "https://trustgraph.ai/ontology/food#Recipe"
```
### Prueba 2: Colisión Multi-Ontología
```python
# Given two ontologies
ont1 = {"fo/Recipe": {...}}
ont2 = {"cooking/Recipe": {...}}
# LLM should use full prefix to disambiguate
llm_output = {"object": "fo/Recipe"} # Not just "Recipe"
```
### Prueba 3: Formato de Instancia de Entidad
```python
# Given prompt with food ontology
# LLM should create instances like
{"subject": "recipe:cornish-pasty"} # Namespace-style
{"subject": "food:beef"} # Consistent prefix
```
## Preguntas Abiertas
1. **¿Deben las instancias de entidades usar prefijos de espacio de nombres?**
Actual: `"recipe:cornish-pasty"` (arbitrario)
Alternativa: ¿Usar prefijo de ontología `"fo:cornish-pasty"`?
Alternativa: Sin prefijo, expandir en URI `"cornish-pasty"` → URI completa?
2. **¿Cómo manejar el dominio/rango en el prompt?**
Actualmente muestra: `(Recipe → Food)`
¿Debería ser: `(fo/Recipe → fo/Food)`?
3. **¿Debemos validar las restricciones de dominio/rango?**
TODO comentario en extract.py:470
Detectaría más errores pero sería más complejo
4. **¿Qué tal las propiedades inversas y las equivalencias?**
La ontología tiene `owl:inverseOf`, `owl:equivalentClass`
Actualmente no se utilizan en la extracción
¿Deberían usarse?
## Métricas de Éxito
✅ Pérdida de información de URI cero (100% de preservación de los URI originales)
✅ El formato de salida del LLM coincide con el formato de entrada
✅ No hay ejemplos ambiguos en el prompt
✅ Las pruebas pasan con múltiples ontologías
✅ Calidad de extracción mejorada (medida por el porcentaje de triples válidos)
## Enfoque Alternativo: Formato de Extracción Simplificado
### Filosofía
En lugar de pedirle al LLM que comprenda la semántica de RDF/OWL, pídele que haga lo que hace bien: **encontrar entidades y relaciones en el texto**.
Deje que el código se encargue de la construcción de URI, la conversión de RDF y las formalidades de la web semántica.
### Ejemplo: Clasificación de Entidades
**Texto de entrada:**
```
Cornish pasty is a traditional British pastry filled with meat and vegetables.
```
**Esquema de Ontología (mostrado al LLM):**
```markdown
## Entity Types:
- Recipe: A recipe is a combination of ingredients and a method
- Food: A food is something that can be eaten
- Ingredient: An ingredient combines a quantity and a food
```
**Lo que el LLM devuelve (JSON simple):**
```json
{
"entities": [
{
"entity": "Cornish pasty",
"type": "Recipe"
}
]
}
```
**¿Qué código produce (triples RDF):**
```python
# 1. Normalize entity name + type to ID (type prevents collisions)
entity_id = "recipe-cornish-pasty" # normalize("Cornish pasty", "Recipe")
entity_uri = "https://trustgraph.ai/food/recipe-cornish-pasty"
# Note: Same name, different type = different URI
# "Cornish pasty" (Recipe) → recipe-cornish-pasty
# "Cornish pasty" (Food) → food-cornish-pasty
# 2. Generate triples
triples = [
# Type triple
Triple(
s=Value(value=entity_uri, is_uri=True),
p=Value(value="http://www.w3.org/1999/02/22-rdf-syntax-ns#type", is_uri=True),
o=Value(value="http://purl.org/ontology/fo/Recipe", is_uri=True)
),
# Label triple (automatic)
Triple(
s=Value(value=entity_uri, is_uri=True),
p=Value(value="http://www.w3.org/2000/01/rdf-schema#label", is_uri=True),
o=Value(value="Cornish pasty", is_uri=False)
)
]
```
### Beneficios
1. **El LLM no necesita:**
Entender la sintaxis de URI
Inventar prefijos de identificadores (`recipe:`, `ingredient:`)
Conocer `rdf:type` o `rdfs:label`
Construir identificadores de la web semántica
2. **El LLM solo necesita:**
Encontrar entidades en el texto
Mapearlas a clases de ontología
Extraer relaciones y atributos
3. **El código se encarga de:**
Normalización y construcción de URI
Generación de triples RDF
Asignación automática de etiquetas
Gestión de espacios de nombres
### ¿Por qué esto funciona mejor?
**Indicación más simple** = menos confusión = menos errores
**IDs consistentes** = el código controla las reglas de normalización
**Etiquetas generadas automáticamente** = no faltan triples rdfs:label
**El LLM se centra en la extracción** = en lo que realmente es bueno
### Ejemplo: Relaciones de Entidades
**Texto de entrada:**
```
Cornish pasty is a traditional British pastry filled with beef and potatoes.
```
**Esquema de Ontología (mostrado al LLM):**
```markdown
## Entity Types:
- Recipe: A recipe is a combination of ingredients and a method
- Food: A food is something that can be eaten
- Ingredient: An ingredient combines a quantity and a food
## Relationships:
- has_ingredient: Relates a recipe to an ingredient it uses (Recipe → Ingredient)
- food: Relates an ingredient to the food that is required (Ingredient → Food)
```
**Lo que el LLM devuelve (JSON simple):**
```json
{
"entities": [
{
"entity": "Cornish pasty",
"type": "Recipe"
},
{
"entity": "beef",
"type": "Food"
},
{
"entity": "potatoes",
"type": "Food"
}
],
"relationships": [
{
"subject": "Cornish pasty",
"subject-type": "Recipe",
"relation": "has_ingredient",
"object": "beef",
"object-type": "Food"
},
{
"subject": "Cornish pasty",
"subject-type": "Recipe",
"relation": "has_ingredient",
"object": "potatoes",
"object-type": "Food"
}
]
}
```
**¿Qué código produce (triples RDF):**
```python
# Normalize entity names to URIs
cornish_pasty_uri = "https://trustgraph.ai/food/cornish-pasty"
beef_uri = "https://trustgraph.ai/food/beef"
potatoes_uri = "https://trustgraph.ai/food/potatoes"
# Look up relation URI from ontology
has_ingredient_uri = "http://purl.org/ontology/fo/ingredients" # from fo/has_ingredient
triples = [
# Entity type triples (as before)
Triple(s=cornish_pasty_uri, p=rdf_type, o="http://purl.org/ontology/fo/Recipe"),
Triple(s=cornish_pasty_uri, p=rdfs_label, o="Cornish pasty"),
Triple(s=beef_uri, p=rdf_type, o="http://purl.org/ontology/fo/Food"),
Triple(s=beef_uri, p=rdfs_label, o="beef"),
Triple(s=potatoes_uri, p=rdf_type, o="http://purl.org/ontology/fo/Food"),
Triple(s=potatoes_uri, p=rdfs_label, o="potatoes"),
# Relationship triples
Triple(
s=Value(value=cornish_pasty_uri, is_uri=True),
p=Value(value=has_ingredient_uri, is_uri=True),
o=Value(value=beef_uri, is_uri=True)
),
Triple(
s=Value(value=cornish_pasty_uri, is_uri=True),
p=Value(value=has_ingredient_uri, is_uri=True),
o=Value(value=potatoes_uri, is_uri=True)
)
]
```
**Puntos clave:**
El modelo de lenguaje (LLM) devuelve nombres de entidades en lenguaje natural: `"Cornish pasty"`, `"beef"`, `"potatoes"`
El LLM incluye tipos para disambiguar: `subject-type`, `object-type`
El LLM utiliza el nombre de la relación del esquema: `"has_ingredient"`
El código deriva IDs consistentes utilizando (nombre, tipo): `("Cornish pasty", "Recipe")``recipe-cornish-pasty`
El código busca el URI de la relación en la ontología: `fo/has_ingredient` → URI completo
La misma tupla (nombre, tipo) siempre obtiene el mismo URI (desduplicación)
### Ejemplo: Disambiguación del nombre de la entidad
**Problema:** El mismo nombre puede referirse a diferentes tipos de entidad.
**Caso real:**
```
"Cornish pasty" can be:
- A Recipe (instructions for making it)
- A Food (the dish itself)
```
**Cómo se gestiona:**
El modelo de lenguaje grande (LLM) devuelve ambos como entidades separadas:
```json
{
"entities": [
{"entity": "Cornish pasty", "type": "Recipe"},
{"entity": "Cornish pasty", "type": "Food"}
],
"relationships": [
{
"subject": "Cornish pasty",
"subject-type": "Recipe",
"relation": "produces",
"object": "Cornish pasty",
"object-type": "Food"
}
]
}
```
**Resolución de código:**
```python
# Different types → different URIs
recipe_uri = normalize("Cornish pasty", "Recipe")
# → "https://trustgraph.ai/food/recipe-cornish-pasty"
food_uri = normalize("Cornish pasty", "Food")
# → "https://trustgraph.ai/food/food-cornish-pasty"
# Relationship connects them correctly
triple = Triple(
s=recipe_uri, # The Recipe
p="http://purl.org/ontology/fo/produces",
o=food_uri # The Food
)
```
**¿Por qué funciona esto?:**
El tipo se incluye en TODAS las referencias (entidades, relaciones, atributos).
El código utiliza la tupla `(name, type)` como clave de búsqueda.
No hay ambigüedad, no hay colisiones.
### Ejemplo: Atributos de Entidad
**Texto de entrada:**
```
This Cornish pasty recipe serves 4-6 people and takes 45 minutes to prepare.
```
**Esquema de Ontología (mostrado al LLM):**
```markdown
## Entity Types:
- Recipe: A recipe is a combination of ingredients and a method
## Attributes:
- serves: Indicates what the recipe is intended to serve (Recipe → text)
- preparation_time: Time needed to prepare the recipe (Recipe → text)
```
**Lo que el LLM devuelve (JSON simple):**
```json
{
"entities": [
{
"entity": "Cornish pasty recipe",
"type": "Recipe"
}
],
"attributes": [
{
"entity": "Cornish pasty recipe",
"entity-type": "Recipe",
"attribute": "serves",
"value": "4-6 people"
},
{
"entity": "Cornish pasty recipe",
"entity-type": "Recipe",
"attribute": "preparation_time",
"value": "45 minutes"
}
]
}
```
**¿Qué código produce (triples RDF):**
```python
# Normalize entity name to URI
recipe_uri = "https://trustgraph.ai/food/cornish-pasty-recipe"
# Look up attribute URIs from ontology
serves_uri = "http://purl.org/ontology/fo/serves" # from fo/serves
prep_time_uri = "http://purl.org/ontology/fo/preparation_time" # from fo/preparation_time
triples = [
# Entity type triple
Triple(
s=Value(value=recipe_uri, is_uri=True),
p=Value(value=rdf_type, is_uri=True),
o=Value(value="http://purl.org/ontology/fo/Recipe", is_uri=True)
),
# Label triple (automatic)
Triple(
s=Value(value=recipe_uri, is_uri=True),
p=Value(value=rdfs_label, is_uri=True),
o=Value(value="Cornish pasty recipe", is_uri=False)
),
# Attribute triples (objects are literals, not URIs)
Triple(
s=Value(value=recipe_uri, is_uri=True),
p=Value(value=serves_uri, is_uri=True),
o=Value(value="4-6 people", is_uri=False) # Literal value!
),
Triple(
s=Value(value=recipe_uri, is_uri=True),
p=Value(value=prep_time_uri, is_uri=True),
o=Value(value="45 minutes", is_uri=False) # Literal value!
)
]
```
**Puntos Clave:**
El LLM extrae valores literales: `"4-6 people"`, `"45 minutes"`
El LLM incluye el tipo de entidad para la desambiguación: `entity-type`
El LLM utiliza el nombre del atributo del esquema: `"serves"`, `"preparation_time"`
El código busca el URI del atributo de las propiedades del tipo de datos de la ontología
**El objeto es literal** (`is_uri=False`), no una referencia de URI
Los valores permanecen como texto natural, no se necesita normalización
**Diferencia con las Relaciones:**
Relaciones: tanto el sujeto como el objeto son entidades (URIs)
Atributos: el sujeto es una entidad (URI), el objeto es un valor literal (cadena/número)
### Ejemplo Completo: Entidades + Relaciones + Atributos
**Texto de Entrada:**
```
Cornish pasty is a savory pastry filled with beef and potatoes.
This recipe serves 4 people.
```
**Lo que el LLM devuelve:**
```json
{
"entities": [
{
"entity": "Cornish pasty",
"type": "Recipe"
},
{
"entity": "beef",
"type": "Food"
},
{
"entity": "potatoes",
"type": "Food"
}
],
"relationships": [
{
"subject": "Cornish pasty",
"subject-type": "Recipe",
"relation": "has_ingredient",
"object": "beef",
"object-type": "Food"
},
{
"subject": "Cornish pasty",
"subject-type": "Recipe",
"relation": "has_ingredient",
"object": "potatoes",
"object-type": "Food"
}
],
"attributes": [
{
"entity": "Cornish pasty",
"entity-type": "Recipe",
"attribute": "serves",
"value": "4 people"
}
]
}
```
**Resultado:** Se generaron 11 triples RDF:
3 triples de tipo de entidad (rdf:type)
3 triples de etiqueta de entidad (rdfs:label) - automático
2 triples de relación (has_ingredient)
1 triple de atributo (serves)
¡Todo proviene de extracciones simples y en lenguaje natural realizadas por el LLM!
## Referencias
Implementación actual: `trustgraph-flow/trustgraph/extract/kg/ontology/extract.py`
Plantilla de prompt: `ontology-prompt.md`
Casos de prueba: `tests/unit/test_extract/test_ontology/`
Ontología de ejemplo: `e2e/test-data/food.ontology`

View file

@ -0,0 +1,277 @@
---
layout: default
title: "Especificación Técnica de la Estructura de Ontologías"
parent: "Spanish (Beta)"
---
# Especificación Técnica de la Estructura de Ontologías
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
Esta especificación describe la estructura y el formato de las ontologías dentro del sistema TrustGraph. Las ontologías proporcionan modelos de conocimiento formales que definen clases, propiedades y relaciones, lo que permite capacidades de razonamiento e inferencia. El sistema utiliza un formato de configuración inspirado en OWL que representa ampliamente los conceptos de OWL/RDFS, al tiempo que está optimizado para los requisitos de TrustGraph.
**Convención de Nombres**: Este proyecto utiliza kebab-case para todos los identificadores (claves de configuración, puntos finales de la API, nombres de módulos, etc.) en lugar de snake_case.
## Objetivos
- **Gestión de Clases y Propiedades**: Definir clases similares a OWL con propiedades, dominios, rangos y restricciones de tipo.
- **Soporte Semántico Avanzado**: Permitir propiedades completas de RDFS/OWL, incluyendo etiquetas, soporte multilingüe y restricciones formales.
- **Soporte para Múltiples Ontologías**: Permitir que múltiples ontologías coexistan e interactúen.
- **Validación y Razonamiento**: Asegurar que las ontologías cumplan con estándares similares a OWL, con verificación de consistencia y soporte de inferencia.
- **Compatibilidad con Estándares**: Soporte de importación/exportación en formatos estándar (Turtle, RDF/XML, OWL/XML) al tiempo que se mantiene la optimización interna.
## Antecedentes
TrustGraph almacena ontologías como elementos de configuración en un sistema flexible de clave-valor. Si bien el formato está inspirado en OWL (Web Ontology Language), está optimizado para los casos de uso específicos de TrustGraph y no se adhiere estrictamente a todas las especificaciones de OWL.
Las ontologías en TrustGraph permiten:
- Definición de tipos de objetos formales y sus propiedades.
- Especificación de dominios, rangos y restricciones de tipo de propiedades.
- Razonamiento e inferencia lógicos.
- Relaciones complejas y restricciones de cardinalidad.
- Soporte multilingüe para la internacionalización.
## Estructura de la Ontología
### Almacenamiento de la Configuración
Las ontologías se almacenan como elementos de configuración con el siguiente patrón:
- **Tipo**: `ontology`
- **Clave**: Identificador de ontología único (por ejemplo, `natural-world`, `domain-model`).
- **Valor**: Ontología completa en formato JSON.
### Estructura JSON
El formato JSON de la ontología consta de cuatro secciones principales:
#### 1. Metadatos
Contiene información administrativa y descriptiva sobre la ontología:
```json
{
"metadata": {
"name": "The natural world",
"description": "Ontology covering the natural order",
"version": "1.0.0",
"created": "2025-09-20T12:07:37.068Z",
"modified": "2025-09-20T12:12:20.725Z",
"creator": "current-user",
"namespace": "http://trustgraph.ai/ontologies/natural-world",
"imports": ["http://www.w3.org/2002/07/owl#"]
}
}
```
**Campos:**
- `name`: Nombre legible por humanos de la ontología.
- `description`: Descripción breve del propósito de la ontología.
- `version`: Número de versión semántico.
- `created`: Marca de tiempo ISO 8601 de creación.
- `modified`: Marca de tiempo ISO 8601 de última modificación.
- `creator`: Identificador del usuario/sistema que creó.
- `namespace`: URI base para elementos de la ontología.
- `imports`: Matriz de URIs de ontologías importadas.
#### 2. Clases
Define los tipos de objetos y sus relaciones jerárquicas:
```json
{
"classes": {
"animal": {
"uri": "http://trustgraph.ai/ontologies/natural-world#animal",
"type": "owl:Class",
"rdfs:label": [{"value": "Animal", "lang": "en"}],
"rdfs:comment": "An animal",
"rdfs:subClassOf": "lifeform",
"owl:equivalentClass": ["creature"],
"owl:disjointWith": ["plant"],
"dcterms:identifier": "ANI-001"
}
}
}
```
**Propiedades soportadas:**
- `uri`: URI completa de la clase.
- `type`: Siempre `"owl:Class"`.
- `rdfs:label`: Matriz de etiquetas con etiquetas de idioma.
- `rdfs:comment`: Descripción de la clase.
- `rdfs:subClassOf`: Identificador de la clase padre (herencia simple).
- `owl:equivalentClass`: Lista de clases equivalentes.
- `owl:disjointWith`: Lista de clases disjuntas.
- `dcterms:identifier`: Identificador de la clase.
#### 3. Propiedades de Objetos
Define las relaciones entre las clases:
```json
{
"objectProperties": {
"hasPart": {
"uri": "http://trustgraph.ai/ontologies/natural-world#hasPart",
"type": "owl:ObjectProperty",
"rdfs:label": [{"value": "hasPart", "lang": "en"}],
"rdfs:comment": "Represents a part-whole relationship",
"domain": "lifeform",
"range": "lifeform"
}
}
}
```
**Propiedades soportadas:**
- `uri`: URI completa de la propiedad de objeto.
- `type`: Siempre `"owl:ObjectProperty"`.
- `rdfs:label`: Matriz de etiquetas con etiquetas de idioma.
- `rdfs:comment`: Descripción de la propiedad de objeto.
- `domain`: Clase de dominio de la propiedad de objeto.
- `range`: Clase de rango de la propiedad de objeto.
#### 4. Propiedades de Datos
Define las características de las clases:
```json
{
"datatypeProperties": {
"number-of-legs": {
"uri": "http://trustgraph.ai/ontologies/natural-world#number-of-legs",
"type": "owl:DatatypeProperty",
"rdfs:label": [{"value": "number-of-legs", "lang": "en"}],
"rdfs:comment": "Count of number of legs of the animal",
"rdfs:range": "xsd:nonNegativeInteger",
"rdfs:domain": "animal"
}
}
}
```
**Propiedades soportadas:**
- `uri`: URI completa de la propiedad de datos.
- `type`: Siempre `"owl:DatatypeProperty"`.
- `rdfs:label`: Matriz de etiquetas con etiquetas de idioma.
- `rdfs:comment`: Descripción de la propiedad de datos.
- `rdfs:range`: Tipo de datos de la propiedad de datos.
- `rdfs:domain`: Clase de dominio de la propiedad de datos.
### Estructura JSON
El formato JSON de la ontología consta de cuatro secciones principales:
#### 1. Metadatos
Contiene información administrativa y descriptiva sobre la ontología:
```json
{
"metadata": {
"name": "The natural world",
"description": "Ontology covering the natural order",
"version": "1.0.0",
"created": "2025-09-20T12:07:37.068Z",
"modified": "2025-09-20T12:12:20.725Z",
"creator": "current-user",
"namespace": "http://trustgraph.ai/ontologies/natural-world",
"imports": ["http://www.w3.org/2002/07/owl#"]
}
}
```
**Campos:**
- `name`: Nombre legible por humanos de la ontología.
- `description`: Descripción breve del propósito de la ontología.
- `version`: Número de versión semántico.
- `created`: Marca de tiempo ISO 8601 de creación.
- `modified`: Marca de tiempo ISO 8601 de última modificación.
- `creator`: Identificador del usuario/sistema que creó.
- `namespace`: URI base para elementos de la ontología.
- `imports`: Matriz de URIs de ontologías importadas.
#### 2. Clases
Define los tipos de objetos y sus relaciones jerárquicas:
```json
{
"classes": {
"lifeform": {
"uri": "http://trustgraph.ai/ontologies/natural-world#lifeform",
"type": "owl:Class",
"rdfs:label": [{"value": "Lifeform", "lang": "en"}],
"rdfs:comment": "A living thing"
},
"animal": {
"uri": "http://trustgraph.ai/ontologies/natural-world#animal",
"type": "owl:Class",
"rdfs:label": [{"value": "Animal", "lang": "en"}],
"rdfs:comment": "An animal",
"rdfs:subClassOf": "lifeform"
},
"cat": {
"uri": "http://trustgraph.ai/ontologies/natural-world#cat",
"type": "owl:Class",
"rdfs:label": [{"value": "Cat", "lang": "en"}],
"rdfs:comment": "A cat",
"rdfs:subClassOf": "animal"
},
"dog": {
"uri": "http://trustgraph.ai/ontologies/natural-world#dog",
"type": "owl:Class",
"rdfs:label": [{"value": "Dog", "lang": "en"}],
"rdfs:comment": "A dog",
"rdfs:subClassOf": "animal",
"owl:disjointWith": ["cat"]
}
},
"objectProperties": {},
"datatypeProperties": {
"number-of-legs": {
"uri": "http://trustgraph.ai/ontologies/natural-world#number-of-legs",
"type": "owl:DatatypeProperty",
"rdfs:label": [{"value": "number-of-legs", "lang": "en"}],
"rdfs:comment": "Count of number of legs of the animal",
"rdfs:range": "xsd:nonNegativeInteger",
"rdfs:domain": "animal"
}
}
}
```
## Reglas de Validación
### Validación Estructural
1. **Consistencia de URI**: Todos los URIs deben seguir el patrón `{namespace}#{identifier}`.
2. **Jerarquía de Clases**: No debe haber herencia circular en `rdfs:subClassOf`.
3. **Dominios/Rangos de Propiedades**: Deben referenciar clases existentes o tipos XSD válidos.
4. **Clases Disjuntas**: No pueden ser subclases entre sí.
5. **Propiedades Inversas**: Deben ser bidireccionales si se especifican.
### Validación Semántica
1. **Identificadores Únicos**: Los identificadores de clase y propiedad deben ser únicos dentro de una ontología.
2. **Etiquetas de Idioma**: Deben seguir el formato de etiqueta de idioma BCP 47.
3. **Restricciones de Cardinalidad**: `minCardinality``maxCardinality` cuando ambos están especificados.
4. **Propiedades Funcionales**: No pueden tener `maxCardinality` > 1.
## Soporte de Formato de Importación/Exportación
Si bien el formato interno es JSON, el sistema admite la conversión a/desde formatos de ontología estándar:
- **Turtle (.ttl)**: Serialización RDF compacta.
- **RDF/XML (.rdf, .owl)**: Formato estándar de W3C.
- **OWL/XML (.owx)**: Formato XML específico de OWL.
- **JSON-LD (.jsonld)**: JSON para Linked Data.
## Referencias
- [OWL 2 Web Ontology Language](https://www.w3.org/TR/owl2-overview/)
- [RDF Schema 1.1](https://www.w3.org/TR/rdf-schema/)
- [XML Schema Datatypes](https://www.w3.org/TR/xmlschema-2/)
- [BCP 47 Language Tags](https://tools.ietf.org/html/bcp47)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,239 @@
---
layout: default
title: "Especificación OpenAPI - Especificación Técnica"
parent: "Spanish (Beta)"
---
# Especificación OpenAPI - Especificación Técnica
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Objetivo
Crear una especificación OpenAPI 3.1 completa y modular para la puerta de enlace de la API REST de TrustGraph que:
Documente todos los puntos finales REST.
Utilice `$ref` externo para la modularidad y el mantenimiento.
Se mapee directamente con el código del traductor de mensajes.
Proporcione esquemas precisos de solicitud/respuesta.
## Fuente de la Verdad
La API está definida por:
**Traductores de Mensajes**: `trustgraph-base/trustgraph/messaging/translators/*.py`
**Administrador del Despachador**: `trustgraph-flow/trustgraph/gateway/dispatch/manager.py`
**Administrador de Puntos Finales**: `trustgraph-flow/trustgraph/gateway/endpoint/manager.py`
## Estructura de Directorios
```
openapi/
├── openapi.yaml # Main entry point
├── paths/
│ ├── config.yaml # Global services
│ ├── flow.yaml
│ ├── librarian.yaml
│ ├── knowledge.yaml
│ ├── collection-management.yaml
│ ├── flow-services/ # Flow-hosted services
│ │ ├── agent.yaml
│ │ ├── document-rag.yaml
│ │ ├── graph-rag.yaml
│ │ ├── text-completion.yaml
│ │ ├── prompt.yaml
│ │ ├── embeddings.yaml
│ │ ├── mcp-tool.yaml
│ │ ├── triples.yaml
│ │ ├── objects.yaml
│ │ ├── nlp-query.yaml
│ │ ├── structured-query.yaml
│ │ ├── structured-diag.yaml
│ │ ├── graph-embeddings.yaml
│ │ ├── document-embeddings.yaml
│ │ ├── text-load.yaml
│ │ └── document-load.yaml
│ ├── import-export/
│ │ ├── core-import.yaml
│ │ ├── core-export.yaml
│ │ └── flow-import-export.yaml # WebSocket import/export
│ ├── websocket.yaml
│ └── metrics.yaml
├── components/
│ ├── schemas/
│ │ ├── config/
│ │ ├── flow/
│ │ ├── librarian/
│ │ ├── knowledge/
│ │ ├── collection/
│ │ ├── ai-services/
│ │ ├── common/
│ │ └── errors/
│ ├── parameters/
│ ├── responses/
│ └── examples/
└── security/
└── bearerAuth.yaml
```
## Mapeo de Servicios
### Servicios Globales (`/api/v1/{kind}`)
`config` - Gestión de configuración
`flow` - Ciclo de vida del flujo
`librarian` - Biblioteca de documentos
`knowledge` - Núcleos de conocimiento
`collection-management` - Metadatos de colección
### Servicios Alojados en el Flujo (`/api/v1/flow/{flow}/service/{kind}`)
**Solicitud/Respuesta:**
`agent`, `text-completion`, `prompt`, `mcp-tool`
`graph-rag`, `document-rag`
`embeddings`, `graph-embeddings`, `document-embeddings`
`triples`, `objects`, `nlp-query`, `structured-query`, `structured-diag`
**Enviar y Olvidar:**
`text-load`, `document-load`
### Importación/Exportación
`/api/v1/import-core` (POST)
`/api/v1/export-core` (GET)
`/api/v1/flow/{flow}/import/{kind}` (WebSocket)
`/api/v1/flow/{flow}/export/{kind}` (WebSocket)
### Otros
`/api/v1/socket` (WebSocket multiplexado)
`/api/metrics` (Prometheus)
## Enfoque
### Fase 1: Configuración
1. Crear estructura de directorios
2. Crear el archivo principal `openapi.yaml` con metadatos, servidores, seguridad
3. Crear componentes reutilizables (errores, parámetros comunes, esquemas de seguridad)
### Fase 2: Esquemas Comunes
Crear esquemas compartidos utilizados en todos los servicios:
`RdfValue`, `Triple` - Estructuras RDF/triple
`ErrorObject` - Respuesta de error
`DocumentMetadata`, `ProcessingMetadata` - Estructuras de metadatos
Parámetros comunes: `FlowId`, `User`, `Collection`
### Fase 3: Servicios Globales
Para cada servicio global (configuración, flujo, biblioteca, conocimiento, gestión de colecciones):
1. Crear el archivo de ruta en `paths/`
2. Crear el esquema de solicitud en `components/schemas/{service}/`
3. Crear el esquema de respuesta
4. Agregar ejemplos
5. Referenciar desde el archivo principal `openapi.yaml`
### Fase 4: Servicios Alojados en el Flujo
Para cada servicio alojado en el flujo:
1. Crear el archivo de ruta en `paths/flow-services/`
2. Crear los esquemas de solicitud/respuesta en `components/schemas/ai-services/`
3. Agregar la documentación de la bandera de transmisión cuando corresponda
4. Referenciar desde el archivo principal `openapi.yaml`
### Fase 5: Importación/Exportación y WebSocket
1. Documentar los puntos finales principales de importación/exportación
2. Documentar los patrones de protocolo WebSocket
3. Documentar los puntos finales de importación/exportación WebSocket a nivel de flujo
### Fase 6: Validación
1. Validar con herramientas de validación de OpenAPI
2. Probar con Swagger UI
3. Verificar que todos los traductores estén cubiertos
## Convención de Nombres de Campos
Todos los campos JSON utilizan **kebab-case**:
`flow-id`, `blueprint-name`, `doc-limit`, `entity-limit`, etc.
## Creación de Archivos de Esquema
Para cada traductor en `trustgraph-base/trustgraph/messaging/translators/`:
1. **Leer el método del traductor `to_pulsar()`** - Define el esquema de solicitud
2. **Leer el método del traductor `from_pulsar()`** - Define el esquema de respuesta
3. **Extraer nombres y tipos de campos**
4. **Crear el esquema OpenAPI** con:
Nombres de campos (kebab-case)
Tipos (string, integer, boolean, object, array)
Campos obligatorios
Valores predeterminados
Descripciones
### Ejemplo de Proceso de Mapeo
```python
# From retrieval.py DocumentRagRequestTranslator
def to_pulsar(self, data: Dict[str, Any]) -> DocumentRagQuery:
return DocumentRagQuery(
query=data["query"], # required string
user=data.get("user", "trustgraph"), # optional string, default "trustgraph"
collection=data.get("collection", "default"), # optional string, default "default"
doc_limit=int(data.get("doc-limit", 20)), # optional integer, default 20
streaming=data.get("streaming", False) # optional boolean, default false
)
```
Traducción:
```yaml
# components/schemas/ai-services/DocumentRagRequest.yaml
type: object
required:
- query
properties:
query:
type: string
description: Search query
user:
type: string
default: trustgraph
collection:
type: string
default: default
doc-limit:
type: integer
default: 20
description: Maximum number of documents to retrieve
streaming:
type: boolean
default: false
description: Enable streaming responses
```
## Respuestas de transmisión
Los servicios que admiten la transmisión devuelven múltiples respuestas con la bandera `end_of_stream`:
`agent`, `text-completion`, `prompt`
`document-rag`, `graph-rag`
Documente este patrón en el esquema de respuesta de cada servicio.
## Respuestas de error
Todos los servicios pueden devolver:
```yaml
error:
oneOf:
- type: string
- $ref: '#/components/schemas/ErrorObject'
```
Donde `ErrorObject` es:
```yaml
type: object
properties:
type:
type: string
message:
type: string
```
## Referencias
Traductores: `trustgraph-base/trustgraph/messaging/translators/`
Mapeo del despachador: `trustgraph-flow/trustgraph/gateway/dispatch/manager.py`
Enrutamiento de puntos finales: `trustgraph-flow/trustgraph/gateway/endpoint/manager.py`
Resumen del servicio: `API_SERVICES_SUMMARY.md`

View file

@ -0,0 +1,965 @@
---
layout: default
title: "Infraestructura Pub/Sub"
parent: "Spanish (Beta)"
---
# Infraestructura Pub/Sub
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
Este documento cataloga todas las conexiones entre el código base de TrustGraph y la infraestructura pub/sub. Actualmente, el sistema está codificado para usar Apache Pulsar. Este análisis identifica todos los puntos de integración para informar futuras refactorizaciones hacia una abstracción pub/sub configurable.
## Estado actual: Puntos de integración de Pulsar
### 1. Uso directo del cliente de Pulsar
**Ubicación:** `trustgraph-flow/trustgraph/gateway/service.py`
La puerta de enlace de la API importa y crea una instancia directamente del cliente de Pulsar:
**Línea 20:** `import pulsar`
**Líneas 54-61:** Creación directa de `pulsar.Client()` con `pulsar.AuthenticationToken()` opcional
**Líneas 33-35:** Configuración predeterminada del host de Pulsar desde variables de entorno
**Líneas 178-192:** Argumentos de la línea de comandos para `--pulsar-host`, `--pulsar-api-key` y `--pulsar-listener`
**Líneas 78, 124:** Pasa `pulsar_client` a `ConfigReceiver` y `DispatcherManager`
Esta es la única ubicación que crea directamente un cliente de Pulsar fuera de la capa de abstracción.
### 2. Marco base del procesador
**Ubicación:** `trustgraph-base/trustgraph/base/async_processor.py`
La clase base para todos los procesadores proporciona conectividad de Pulsar:
**Línea 9:** `import _pulsar` (para el manejo de excepciones)
**Línea 18:** `from . pubsub import PulsarClient`
**Línea 38:** Crea `pulsar_client_object = PulsarClient(**params)`
**Líneas 104-108:** Propiedades que exponen `pulsar_host` y `pulsar_client`
**Línea 250:** El método estático `add_args()` llama a `PulsarClient.add_args(parser)` para los argumentos de la línea de comandos
**Líneas 223-225:** Manejo de excepciones para `_pulsar.Interrupted`
Todos los procesadores heredan de `AsyncProcessor`, lo que convierte a este en el punto de integración central.
### 3. Abstracción del consumidor
**Ubicación:** `trustgraph-base/trustgraph/base/consumer.py`
Consume mensajes de colas e invoca funciones de controlador:
**Importaciones de Pulsar:**
**Línea 12:** `from pulsar.schema import JsonSchema`
**Línea 13:** `import pulsar`
**Línea 14:** `import _pulsar`
**Uso específico de Pulsar:**
**Líneas 100, 102:** `pulsar.InitialPosition.Earliest` / `pulsar.InitialPosition.Latest`
**Línea 108:** Envoltorio `JsonSchema(self.schema)`
**Línea 110:** `pulsar.ConsumerType.Shared`
**Líneas 104-111:** `self.client.subscribe()` con parámetros específicos de Pulsar
**Líneas 143, 150, 65:** Métodos `consumer.unsubscribe()` y `consumer.close()`
**Línea 162:** Excepción `_pulsar.Timeout`
**Líneas 182, 205, 232:** `consumer.acknowledge()` / `consumer.negative_acknowledge()`
**Archivo de especificación:** `trustgraph-base/trustgraph/base/consumer_spec.py`
**Línea 22:** Hace referencia a `processor.pulsar_client`
### 4. Abstracción del productor
**Ubicación:** `trustgraph-base/trustgraph/base/producer.py`
Envía mensajes a colas:
**Importaciones de Pulsar:**
**Línea 2:** `from pulsar.schema import JsonSchema`
**Uso específico de Pulsar:**
**Línea 49:** Envoltorio `JsonSchema(self.schema)`
**Líneas 47-51:** `self.client.create_producer()` con parámetros específicos de Pulsar (tema, esquema, habilitación de fragmentación)
**Líneas 31, 76:** Método `producer.close()`
**Líneas 64-65:** `producer.send()` con mensaje y propiedades
**Archivo de especificación:** `trustgraph-base/trustgraph/base/producer_spec.py`
**Línea 18:** Hace referencia a `processor.pulsar_client`
### 5. Abstracción del publicador
**Ubicación:** `trustgraph-base/trustgraph/base/publisher.py`
Publicación de mensajes asíncrona con almacenamiento en búfer de cola:
**Importaciones de Pulsar:**
**Línea 2:** `from pulsar.schema import JsonSchema`
**Línea 6:** `import pulsar`
**Uso específico de Pulsar:**
**Línea 52:** Envoltorio `JsonSchema(self.schema)`
**Líneas 50-54:** `self.client.create_producer()` con parámetros específicos de Pulsar
**Líneas 101, 103:** `producer.send()` con mensaje y propiedades opcionales
**Líneas 106-107:** Métodos `producer.flush()` y `producer.close()`
### 6. Abstracción del suscriptor
**Ubicación:** `trustgraph-base/trustgraph/base/subscriber.py`
Proporciona la distribución de mensajes a múltiples destinatarios desde colas:
**Importaciones de Pulsar:**
**Línea 6:** `from pulsar.schema import JsonSchema`
**Línea 8:** `import _pulsar`
**Uso específico de Pulsar:**
**Línea 55:** `JsonSchema(self.schema)` wrapper
**Línea 57:** `self.client.subscribe(**subscribe_args)`
**Líneas 101, 136, 160, 167-172:** Excepciones de Pulsar: `_pulsar.Timeout`, `_pulsar.InvalidConfiguration`, `_pulsar.AlreadyClosed`
**Líneas 159, 166, 170:** Métodos de consumidor: `negative_acknowledge()`, `unsubscribe()`, `close()`
**Líneas 247, 251:** Reconocimiento de mensajes: `acknowledge()`, `negative_acknowledge()`
**Archivo de especificaciones:** `trustgraph-base/trustgraph/base/subscriber_spec.py`
**Línea 19:** Referencias a `processor.pulsar_client`
### 7. Sistema de esquemas (Heart of Darkness)
**Ubicación:** `trustgraph-base/trustgraph/schema/`
Cada esquema de mensaje en el sistema se define utilizando el marco de esquemas de Pulsar.
**Primitivos principales:** `schema/core/primitives.py`
**Línea 2:** `from pulsar.schema import Record, String, Boolean, Array, Integer`
Todos los esquemas heredan de la clase base de Pulsar `Record`
Todos los tipos de campo son tipos de Pulsar: `String()`, `Integer()`, `Boolean()`, `Array()`, `Map()`, `Double()`
**Esquemas de ejemplo:**
`schema/services/llm.py` (Línea 2): `from pulsar.schema import Record, String, Array, Double, Integer, Boolean`
`schema/services/config.py` (Línea 2): `from pulsar.schema import Record, Bytes, String, Boolean, Array, Map, Integer`
**Nomenclatura de temas:** `schema/core/topic.py`
**Líneas 2-3:** Formato del tema: `{kind}://{tenant}/{namespace}/{topic}`
Esta estructura de URI es específica de Pulsar (por ejemplo, `persistent://tg/flow/config`)
**Impacto:**
Todas las definiciones de mensajes de solicitud/respuesta en todo el código base utilizan esquemas de Pulsar
Esto incluye servicios para: config, flow, llm, prompt, query, storage, agent, collection, diagnosis, library, lookup, nlp_query, objects_query, retrieval, structured_query
Las definiciones de esquemas se importan y utilizan ampliamente en todos los procesadores y servicios
## Resumen
### Dependencias de Pulsar por Categoría
1. **Instanciación del cliente:**
Directo: `gateway/service.py`
Abstracto: `async_processor.py``pubsub.py` (PulsarClient)
2. **Transporte de mensajes:**
Consumidor: `consumer.py`, `consumer_spec.py`
Productor: `producer.py`, `producer_spec.py`
Publicador: `publisher.py`
Suscriptor: `subscriber.py`, `subscriber_spec.py`
3. **Sistema de esquemas:**
Tipos base: `schema/core/primitives.py`
Todos los esquemas de servicio: `schema/services/*.py`
Nomenclatura de temas: `schema/core/topic.py`
4. **Conceptos específicos de Pulsar requeridos:**
Mensajería basada en temas
Sistema de esquemas (Registro, tipos de campo)
Suscripciones compartidas
Reconocimiento de mensajes (positivo/negativo)
Posicionamiento del consumidor (más temprano/más reciente)
Propiedades del mensaje
Posiciones iniciales y tipos de consumidor
Soporte de fragmentación
Temas persistentes frente a no persistentes
### Desafíos de refactorización
La buena noticia: la capa de abstracción (Consumidor, Productor, Publicador, Suscriptor) proporciona una encapsulación limpia de la mayoría de las interacciones de Pulsar.
Los desafíos:
1. **Ubicuidad del sistema de esquemas:** Cada definición de mensaje utiliza `pulsar.schema.Record` y los tipos de campo de Pulsar
2. **Enums específicos de Pulsar:** `InitialPosition`, `ConsumerType`
3. **Excepciones de Pulsar:** `_pulsar.Timeout`, `_pulsar.Interrupted`, `_pulsar.InvalidConfiguration`, `_pulsar.AlreadyClosed`
4. **Fichas de método:** `acknowledge()`, `negative_acknowledge()`, `subscribe()`, `create_producer()`, etc.
5. **Formato de URI de tema:** Estructura de Pulsar `kind://tenant/namespace/topic`
### Próximos pasos
Para hacer que la infraestructura de pub/sub sea configurable, necesitamos:
1. Crear una interfaz de abstracción para el sistema de cliente/esquemas
2. Abstractar enums y excepciones específicas de Pulsar
3. Crear envoltorios de esquema o definiciones de esquema alternativas
4. Implementar la interfaz tanto para Pulsar como para sistemas alternativos (Kafka, RabbitMQ, Redis Streams, etc.)
5. Actualizar `pubsub.py` para que sea configurable y admita varios backends
6. Proporcionar una ruta de migración para implementaciones existentes
## Borrador de enfoque 1: Patrón de adaptador con capa de traducción de esquemas
### Idea clave
El **sistema de esquemas** es el punto de integración más profundo; todo lo demás se deriva de él. Necesitamos resolver esto primero, o tendremos que reescribir todo el código base.
### Estrategia: Interrupción mínima con adaptadores
**1. Mantener los esquemas de Pulsar como la representación interna**
No reescribir todas las definiciones de esquemas.
Los esquemas permanecen `pulsar.schema.Record` internamente.
Utilizar adaptadores para traducir en la frontera entre nuestro código y el backend de publicación/suscripción.
**2. Crear una capa de abstracción de publicación/suscripción:**
```
┌─────────────────────────────────────┐
│ Existing Code (unchanged) │
│ - Uses Pulsar schemas internally │
│ - Consumer/Producer/Publisher │
└──────────────┬──────────────────────┘
┌──────────────┴──────────────────────┐
│ PubSubFactory (configurable) │
│ - Creates backend-specific client │
└──────────────┬──────────────────────┘
┌──────┴──────┐
│ │
┌───────▼─────┐ ┌────▼─────────┐
│ PulsarAdapter│ │ KafkaAdapter │ etc...
│ (passthrough)│ │ (translates) │
└──────────────┘ └──────────────┘
```
**3. Defina interfaces abstractas:**
`PubSubClient` - conexión del cliente
`PubSubProducer` - envío de mensajes
`PubSubConsumer` - recepción de mensajes
`SchemaAdapter` - traducción de esquemas de Pulsar a/desde JSON o formatos específicos del backend
**4. Detalles de implementación:**
Para el **adaptador de Pulsar**: Casi una transmisión directa, traducción mínima.
Para **otros backends** (Kafka, RabbitMQ, etc.):
Serializar objetos de registro de Pulsar a JSON/bytes.
Mapear conceptos como:
`InitialPosition.Earliest/Latest` → auto.offset.reset de Kafka
`acknowledge()` → confirmación de Kafka
`negative_acknowledge()` → patrón de re-cola o cola de mensajes no entregados (DLQ).
URIs de temas → nombres de temas específicos del backend.
### Análisis
**Ventajas:**
✅ Cambios mínimos en el código de los servicios existentes.
✅ Los esquemas permanecen sin cambios (sin reescritura masiva).
✅ Ruta de migración gradual.
✅ Los usuarios de Pulsar no notan ninguna diferencia.
✅ Se agregan nuevos backends a través de adaptadores.
**Desventajas:**
⚠️ Aún mantiene la dependencia de Pulsar (para las definiciones de esquemas).
⚠️ Algunos problemas de compatibilidad al traducir conceptos.
### Consideración alternativa
Crear un sistema de esquemas **TrustGraph** que sea independiente de pub/sub (usando dataclasses o Pydantic), y luego generar esquemas de Pulsar/Kafka/etc a partir de él. Esto requiere reescribir cada archivo de esquema y podría provocar cambios importantes.
### Recomendación para la versión preliminar 1
Comience con el **enfoque de adaptador** porque:
1. Es práctico: funciona con el código existente.
2. Demuestra el concepto con un riesgo mínimo.
3. Puede evolucionar hacia un sistema de esquemas nativo más adelante, si es necesario.
4. Impulsado por la configuración: una variable de entorno cambia entre backends.
## Enfoque de la versión preliminar 2: Sistema de esquemas independiente del backend con dataclasses
### Concepto central
Utilice **dataclasses** de Python como el formato de definición de esquema neutral. Cada backend de pub/sub proporciona su propia serialización/deserialización para dataclasses, eliminando la necesidad de que los esquemas de Pulsar permanezcan en el código base.
### Polimorfismo de esquema a nivel de fábrica
En lugar de traducir esquemas de Pulsar, **cada backend proporciona su propia gestión de esquemas** que funciona con dataclasses de Python estándar.
### Flujo del publicador
```python
# 1. Get the configured backend from factory
pubsub = get_pubsub() # Returns PulsarBackend, MQTTBackend, etc.
# 2. Get schema class from the backend
# (Can be imported directly - backend-agnostic)
from trustgraph.schema.services.llm import TextCompletionRequest
# 3. Create a producer/publisher for a specific topic
producer = pubsub.create_producer(
topic="text-completion-requests",
schema=TextCompletionRequest # Tells backend what schema to use
)
# 4. Create message instances (same API regardless of backend)
request = TextCompletionRequest(
system="You are helpful",
prompt="Hello world",
streaming=False
)
# 5. Send the message
producer.send(request) # Backend serializes appropriately
```
### Flujo del consumidor
```python
# 1. Get the configured backend
pubsub = get_pubsub()
# 2. Create a consumer
consumer = pubsub.subscribe(
topic="text-completion-requests",
schema=TextCompletionRequest # Tells backend how to deserialize
)
# 3. Receive and deserialize
msg = consumer.receive()
request = msg.value() # Returns TextCompletionRequest dataclass instance
# 4. Use the data (type-safe access)
print(request.system) # "You are helpful"
print(request.prompt) # "Hello world"
print(request.streaming) # False
```
### ¿Qué sucede detrás de escena?
**Para el backend de Pulsar:**
`create_producer()` → crea un productor de Pulsar con un esquema JSON o un registro generado dinámicamente.
`send(request)` → serializa la clase de datos a formato JSON/Pulsar y lo envía a Pulsar.
`receive()` → recibe un mensaje de Pulsar, lo deserializa de nuevo a la clase de datos.
**Para el backend de MQTT:**
`create_producer()` → se conecta a un broker de MQTT, no es necesario registrar ningún esquema.
`send(request)` → convierte la clase de datos a JSON y lo publica en un tema de MQTT.
`receive()` → se suscribe a un tema de MQTT y deserializa el JSON a la clase de datos.
**Para el backend de Kafka:**
`create_producer()` → crea un productor de Kafka y registra el esquema Avro si es necesario.
`send(request)` → serializa la clase de datos a formato Avro y lo envía a Kafka.
`receive()` → recibe un mensaje de Kafka y lo deserializa de Avro de nuevo a la clase de datos.
### Puntos Clave del Diseño
1. **Creación del objeto de esquema**: La instancia de la clase de datos (`TextCompletionRequest(...)`) es idéntica independientemente del backend.
2. **El backend se encarga de la codificación**: Cada backend sabe cómo serializar su clase de datos al formato de cable.
3. **Definición del esquema en la creación**: Al crear el productor/consumidor, se especifica el tipo de esquema.
4. **Se mantiene la seguridad de tipos**: Se obtiene un objeto `TextCompletionRequest` adecuado, no un diccionario.
5. **Sin filtración del backend**: El código de la aplicación nunca importa bibliotecas específicas del backend.
### Ejemplo de Transformación
**Actual (específico de Pulsar):**
```python
# schema/services/llm.py
from pulsar.schema import Record, String, Boolean, Integer
class TextCompletionRequest(Record):
system = String()
prompt = String()
streaming = Boolean()
```
**Nuevo (Independiente del backend):**
```python
# schema/services/llm.py
from dataclasses import dataclass
@dataclass
class TextCompletionRequest:
system: str
prompt: str
streaming: bool = False
```
### Integración con el Backend
Cada backend se encarga de la serialización/deserialización de dataclasses:
**Backend de Pulsar:**
Genera clases `pulsar.schema.Record` dinámicamente a partir de dataclasses
O serializa dataclasses a JSON y utiliza el esquema JSON de Pulsar
Mantiene la compatibilidad con implementaciones de Pulsar existentes
**Backend de MQTT/Redis:**
Serialización directa de instancias de dataclass a JSON
Utiliza `dataclasses.asdict()` / `from_dict()`
Ligero, no se necesita un registro de esquemas
**Backend de Kafka:**
Genera esquemas Avro a partir de definiciones de dataclass
Utiliza el registro de esquemas de Confluent
Serialización con seguridad de tipos y soporte para la evolución del esquema
### Arquitectura
```
┌─────────────────────────────────────┐
│ Application Code │
│ - Uses dataclass schemas │
│ - Backend-agnostic │
└──────────────┬──────────────────────┘
┌──────────────┴──────────────────────┐
│ PubSubFactory (configurable) │
│ - get_pubsub() returns backend │
└──────────────┬──────────────────────┘
┌──────┴──────┐
│ │
┌───────▼─────────┐ ┌────▼──────────────┐
│ PulsarBackend │ │ MQTTBackend │
│ - JSON schema │ │ - JSON serialize │
│ - or dynamic │ │ - Simple queues │
│ Record gen │ │ │
└─────────────────┘ └───────────────────┘
```
### Detalles de implementación
**1. Definiciones de esquema:** Clases de datos simples con sugerencias de tipo
`str`, `int`, `bool`, `float` para tipos primitivos
`list[T]` para arreglos
`dict[str, T]` para mapas
Clases de datos anidadas para tipos complejos
**2. Cada backend proporciona:**
Serializador: `dataclass → bytes/wire format`
Deserializador: `bytes/wire format → dataclass`
Registro de esquema (si es necesario, como Pulsar/Kafka)
**3. Abstracción de consumidor/productor:**
Ya existe (consumer.py, producer.py)
Actualizar para usar la serialización del backend
Eliminar importaciones directas de Pulsar
**4. Mapeos de tipo:**
Pulsar `String()` → Python `str`
Pulsar `Integer()` → Python `int`
Pulsar `Boolean()` → Python `bool`
Pulsar `Array(T)` → Python `list[T]`
Pulsar `Map(K, V)` → Python `dict[K, V]`
Pulsar `Double()` → Python `float`
Pulsar `Bytes()` → Python `bytes`
### Ruta de migración
1. **Crear versiones de clases de datos** de todos los esquemas en `trustgraph/schema/`
2. **Actualizar clases de backend** (Consumidor, Productor, Publicador, Suscriptor) para usar la serialización proporcionada por el backend
3. **Implementar PulsarBackend** con esquema JSON o generación dinámica de registros
4. **Probar con Pulsar** para garantizar la compatibilidad hacia atrás con las implementaciones existentes
5. **Agregar nuevos backends** (MQTT, Kafka, Redis, etc.) según sea necesario
6. **Eliminar importaciones de Pulsar** de los archivos de esquema
### Beneficios
**Sin dependencia de pub/sub** en las definiciones de esquema
**Python estándar** - fácil de entender, tipificar, documentar
**Herramientas modernas** - funciona con mypy, autocompletado de IDE, analizadores
**Optimizada para el backend** - cada backend utiliza la serialización nativa
**Sin sobrecarga de traducción** - serialización directa, sin adaptadores
**Seguridad de tipos** - objetos reales con tipos adecuados
**Validación fácil** - se puede usar Pydantic si es necesario
### Desafíos y soluciones
**Desafío:** El `Record` de Pulsar tiene validación de campo en tiempo de ejecución
**Solución:** Usar clases de datos de Pydantic para la validación si es necesario, o características de clase de datos de Python 3.10+ con `__post_init__`
**Desafío:** Algunas características específicas de Pulsar (como el tipo `Bytes`)
**Solución:** Mapear al tipo `bytes` en la clase de datos, el backend se encarga de la codificación apropiadamente
**Desafío:** Nombres de temas (`persistent://tenant/namespace/topic`)
**Solución:** Abstracto los nombres de los temas en las definiciones de esquema, el backend convierte al formato adecuado
**Desafío:** Evolución y versionado del esquema
**Solución:** Cada backend maneja esto de acuerdo con sus capacidades (versiones de esquema de Pulsar, registro de esquema de Kafka, etc.)
**Desafío:** Tipos complejos anidados
**Solución:** Usar clases de datos anidadas, los backends serializan/deserializan recursivamente
### Decisiones de diseño
1. **¿Clases de datos simples o Pydantic?**
✅ **Decisión: Usar clases de datos de Python simples**
Más simple, sin dependencias adicionales
La validación no es necesaria en la práctica
Más fácil de entender y mantener
2. **Evolución del esquema:**
✅ **Decisión: No se necesita un mecanismo de versionado**
Los esquemas son estables y duraderos
Las actualizaciones normalmente agregan nuevos campos (compatible con versiones anteriores)
Los backends manejan la evolución del esquema según sus capacidades
3. **Compatibilidad hacia atrás:**
✅ **Decisión: Cambio de versión importante, no se requiere compatibilidad hacia atrás**
Será un cambio importante con instrucciones de migración
La ruptura limpia permite un mejor diseño
Se proporcionará una guía de migración para las implementaciones existentes
4. **Tipos anidados y estructuras complejas:**
✅ **Decisión: Usar clases de datos anidadas de forma natural**
Las clases de datos de Python manejan el anidamiento perfectamente
`list[T]` para arreglos, `dict[K, V]` para mapas
Los backends serializan/deserializan recursivamente
Ejemplo:
```python
@dataclass
class Value:
value: str
is_uri: bool
@dataclass
class Triple:
s: Value # Nested dataclass
p: Value
o: Value
@dataclass
class GraphQuery:
triples: list[Triple] # Array of nested dataclasses
metadata: dict[str, str]
```
5. **Valores predeterminados y campos opcionales:**
✅ **Decisión: Combinación de campos obligatorios, valores predeterminados y campos opcionales**
Campos obligatorios: Sin valor predeterminado
Campos con valores predeterminados: Siempre presentes, tienen un valor predeterminado razonable
Campos verdaderamente opcionales: `T | None = None`, omitidos de la serialización cuando `None`
Ejemplo:
```python
@dataclass
class TextCompletionRequest:
system: str # Required, no default
prompt: str # Required, no default
streaming: bool = False # Optional with default value
metadata: dict | None = None # Truly optional, can be absent
```
**Semántica de serialización importante:**
Cuando `metadata = None`:
```json
{
"system": "...",
"prompt": "...",
"streaming": false
// metadata field NOT PRESENT
}
```
Cuando `metadata = {}` (explícitamente vacío):
```json
{
"system": "...",
"prompt": "...",
"streaming": false,
"metadata": {} // Field PRESENT but empty
}
```
**Diferencia clave:**
`None` → campo ausente en JSON (no se serializa)
Valor vacío (`{}`, `[]`, `""`) → campo presente con valor vacío
Esto es importante semánticamente: "no proporcionado" vs "explícitamente vacío"
Los sistemas de serialización deben omitir los campos `None`, no codificarlos como `null`
## Esquema de Implementación Borrador 3: Detalles de Implementación
### Formato Genérico para Nombres de Colas
Reemplace los nombres de colas específicos del sistema de respaldo con un formato genérico que los sistemas de respaldo puedan mapear adecuadamente.
**Formato:** `{qos}/{tenant}/{namespace}/{queue-name}`
Donde:
`qos`: Nivel de Calidad de Servicio
`q0` = mejor esfuerzo (enviar y olvidar, sin confirmación)
`q1` = al menos una vez (requiere confirmación)
`q2` = exactamente una vez (confirmación de dos fases)
`tenant`: Agrupación lógica para multi-inquilino
`namespace`: Sub-agrupación dentro del inquilino
`queue-name`: Nombre real de la cola/tema
**Ejemplos:**
```
q1/tg/flow/text-completion-requests
q2/tg/config/config-push
q0/tg/metrics/stats
```
### Mapeo de temas del backend
Cada backend mapea el formato genérico a su formato nativo:
**Backend de Pulsar:**
```python
def map_topic(self, generic_topic: str) -> str:
# Parse: q1/tg/flow/text-completion-requests
qos, tenant, namespace, queue = generic_topic.split('/', 3)
# Map QoS to persistence
persistence = 'persistent' if qos in ['q1', 'q2'] else 'non-persistent'
# Return Pulsar URI: persistent://tg/flow/text-completion-requests
return f"{persistence}://{tenant}/{namespace}/{queue}"
```
**Backend de MQTT:**
```python
def map_topic(self, generic_topic: str) -> tuple[str, int]:
# Parse: q1/tg/flow/text-completion-requests
qos, tenant, namespace, queue = generic_topic.split('/', 3)
# Map QoS level
qos_level = {'q0': 0, 'q1': 1, 'q2': 2}[qos]
# Build MQTT topic including tenant/namespace for proper namespacing
mqtt_topic = f"{tenant}/{namespace}/{queue}"
return mqtt_topic, qos_level
```
### Función de ayuda de tema actualizada
```python
# schema/core/topic.py
def topic(queue_name, qos='q1', tenant='tg', namespace='flow'):
"""
Create a generic topic identifier that can be mapped by backends.
Args:
queue_name: The queue/topic name
qos: Quality of service
- 'q0' = best-effort (no ack)
- 'q1' = at-least-once (ack required)
- 'q2' = exactly-once (two-phase ack)
tenant: Tenant identifier for multi-tenancy
namespace: Namespace within tenant
Returns:
Generic topic string: qos/tenant/namespace/queue_name
Examples:
topic('my-queue') # q1/tg/flow/my-queue
topic('config', qos='q2', namespace='config') # q2/tg/config/config
"""
return f"{qos}/{tenant}/{namespace}/{queue_name}"
```
### Configuración e Inicialización
**Argumentos de Línea de Comandos + Variables de Entorno:**
```python
# In base/async_processor.py - add_args() method
@staticmethod
def add_args(parser):
# Pub/sub backend selection
parser.add_argument(
'--pubsub-backend',
default=os.getenv('PUBSUB_BACKEND', 'pulsar'),
choices=['pulsar', 'mqtt'],
help='Pub/sub backend (default: pulsar, env: PUBSUB_BACKEND)'
)
# Pulsar-specific configuration
parser.add_argument(
'--pulsar-host',
default=os.getenv('PULSAR_HOST', 'pulsar://localhost:6650'),
help='Pulsar host (default: pulsar://localhost:6650, env: PULSAR_HOST)'
)
parser.add_argument(
'--pulsar-api-key',
default=os.getenv('PULSAR_API_KEY', None),
help='Pulsar API key (env: PULSAR_API_KEY)'
)
parser.add_argument(
'--pulsar-listener',
default=os.getenv('PULSAR_LISTENER', None),
help='Pulsar listener name (env: PULSAR_LISTENER)'
)
# MQTT-specific configuration
parser.add_argument(
'--mqtt-host',
default=os.getenv('MQTT_HOST', 'localhost'),
help='MQTT broker host (default: localhost, env: MQTT_HOST)'
)
parser.add_argument(
'--mqtt-port',
type=int,
default=int(os.getenv('MQTT_PORT', '1883')),
help='MQTT broker port (default: 1883, env: MQTT_PORT)'
)
parser.add_argument(
'--mqtt-username',
default=os.getenv('MQTT_USERNAME', None),
help='MQTT username (env: MQTT_USERNAME)'
)
parser.add_argument(
'--mqtt-password',
default=os.getenv('MQTT_PASSWORD', None),
help='MQTT password (env: MQTT_PASSWORD)'
)
```
**Función de Fábrica:**
```python
# In base/pubsub.py or base/pubsub_factory.py
def get_pubsub(**config) -> PubSubBackend:
"""
Create and return a pub/sub backend based on configuration.
Args:
config: Configuration dict from command-line args
Must include 'pubsub_backend' key
Returns:
Backend instance (PulsarBackend, MQTTBackend, etc.)
"""
backend_type = config.get('pubsub_backend', 'pulsar')
if backend_type == 'pulsar':
return PulsarBackend(
host=config.get('pulsar_host'),
api_key=config.get('pulsar_api_key'),
listener=config.get('pulsar_listener'),
)
elif backend_type == 'mqtt':
return MQTTBackend(
host=config.get('mqtt_host'),
port=config.get('mqtt_port'),
username=config.get('mqtt_username'),
password=config.get('mqtt_password'),
)
else:
raise ValueError(f"Unknown pub/sub backend: {backend_type}")
```
**Uso en AsyncProcessor:**
```python
# In async_processor.py
class AsyncProcessor:
def __init__(self, **params):
self.id = params.get("id")
# Create backend from config (replaces PulsarClient)
self.pubsub = get_pubsub(**params)
# Rest of initialization...
```
### Interfaz de Backend
```python
class PubSubBackend(Protocol):
"""Protocol defining the interface all pub/sub backends must implement."""
def create_producer(self, topic: str, schema: type, **options) -> BackendProducer:
"""
Create a producer for a topic.
Args:
topic: Generic topic format (qos/tenant/namespace/queue)
schema: Dataclass type for messages
options: Backend-specific options (e.g., chunking_enabled)
Returns:
Backend-specific producer instance
"""
...
def create_consumer(
self,
topic: str,
subscription: str,
schema: type,
initial_position: str = 'latest',
consumer_type: str = 'shared',
**options
) -> BackendConsumer:
"""
Create a consumer for a topic.
Args:
topic: Generic topic format (qos/tenant/namespace/queue)
subscription: Subscription/consumer group name
schema: Dataclass type for messages
initial_position: 'earliest' or 'latest' (MQTT may ignore)
consumer_type: 'shared', 'exclusive', 'failover' (MQTT may ignore)
options: Backend-specific options
Returns:
Backend-specific consumer instance
"""
...
def close(self) -> None:
"""Close the backend connection."""
...
```
```python
class BackendProducer(Protocol):
"""Protocol for backend-specific producer."""
def send(self, message: Any, properties: dict = {}) -> None:
"""Send a message (dataclass instance) with optional properties."""
...
def flush(self) -> None:
"""Flush any buffered messages."""
...
def close(self) -> None:
"""Close the producer."""
...
```
```python
class BackendConsumer(Protocol):
"""Protocol for backend-specific consumer."""
def receive(self, timeout_millis: int = 2000) -> Message:
"""
Receive a message from the topic.
Raises:
TimeoutError: If no message received within timeout
"""
...
def acknowledge(self, message: Message) -> None:
"""Acknowledge successful processing of a message."""
...
def negative_acknowledge(self, message: Message) -> None:
"""Negative acknowledge - triggers redelivery."""
...
def unsubscribe(self) -> None:
"""Unsubscribe from the topic."""
...
def close(self) -> None:
"""Close the consumer."""
...
```
```python
class Message(Protocol):
"""Protocol for a received message."""
def value(self) -> Any:
"""Get the deserialized message (dataclass instance)."""
...
def properties(self) -> dict:
"""Get message properties/metadata."""
...
```
### Refactorización de Clases Existentes
Las clases existentes `Consumer`, `Producer`, `Publisher`, `Subscriber` permanecen en gran medida intactas:
**Responsabilidades actuales (mantener):**
Modelo de subprocesos asíncronos y grupos de tareas
Lógica de reconexión y manejo de reintentos
Recopilación de métricas
Limitación de velocidad
Gestión de la concurrencia
**Cambios necesarios:**
Eliminar importaciones directas de Pulsar (`pulsar.schema`, `pulsar.InitialPosition`, etc.)
Aceptar `BackendProducer`/`BackendConsumer` en lugar del cliente de Pulsar
Delegar las operaciones de publicación/suscripción reales a instancias de backend
Mapear conceptos genéricos a llamadas de backend
**Ejemplo de refactorización:**
```python
# OLD - consumer.py
class Consumer:
def __init__(self, client, topic, subscriber, schema, ...):
self.client = client # Direct Pulsar client
# ...
async def consumer_run(self):
# Uses pulsar.InitialPosition, pulsar.ConsumerType
self.consumer = self.client.subscribe(
topic=self.topic,
schema=JsonSchema(self.schema),
initial_position=pulsar.InitialPosition.Earliest,
consumer_type=pulsar.ConsumerType.Shared,
)
# NEW - consumer.py
class Consumer:
def __init__(self, backend_consumer, schema, ...):
self.backend_consumer = backend_consumer # Backend-specific consumer
self.schema = schema
# ...
async def consumer_run(self):
# Backend consumer already created with right settings
# Just use it directly
while self.running:
msg = await asyncio.to_thread(
self.backend_consumer.receive,
timeout_millis=2000
)
await self.handle_message(msg)
```
### Comportamientos Específicos del Backend
**Backend de Pulsar:**
Mapea `q0``non-persistent://`, `q1`/`q2``persistent://`
Soporta todos los tipos de consumidores (compartido, exclusivo, de respaldo)
Soporta la posición inicial (earliest/latest)
Reconocimiento nativo de mensajes
Soporte para el registro de esquemas
**Backend de MQTT:**
Mapea `q0`/`q1`/`q2` → Niveles de QoS de MQTT 0/1/2
Incluye el inquilino/espacio de nombres en la ruta del tema para el espaciado de nombres
Genera automáticamente ID de cliente a partir de los nombres de suscripción
Ignora la posición inicial (no hay historial de mensajes en MQTT básico)
Ignora el tipo de consumidor (MQTT utiliza ID de cliente, no grupos de consumidores)
Modelo de publicación/suscripción simple
### Resumen de Decisiones de Diseño
1. ✅ **Nombres de cola genéricos**: Formato `qos/tenant/namespace/queue-name`
2. ✅ **QoS en el ID de la cola**: Determinado por la definición de la cola, no por la configuración
3. ✅ **Reconexión**: Manejada por las clases Consumer/Producer, no por los backends
4. ✅ **Temas de MQTT**: Incluir inquilino/espacio de nombres para un espaciado de nombres adecuado
5. ✅ **Historial de mensajes**: MQTT ignora el parámetro `initial_position` (mejora futura)
6. ✅ **ID de cliente**: El backend de MQTT genera automáticamente a partir del nombre de la suscripción
### Mejoras Futuras
**Historial de mensajes de MQTT:**
Se podría agregar una capa de persistencia opcional (por ejemplo, mensajes retenidos, almacenamiento externo)
Permitiría soportar `initial_position='earliest'`
No es necesario para la implementación inicial

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,208 @@
---
layout: default
title: "Explicabilidad en Tiempo de Consulta"
parent: "Spanish (Beta)"
---
# Explicabilidad en Tiempo de Consulta
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Estado
Implementado
## Descripción General
Esta especificación describe cómo GraphRAG registra y comunica datos de explicabilidad durante la ejecución de consultas. El objetivo es una trazabilidad completa: desde la respuesta final, a través de los bordes seleccionados, hasta los documentos de origen.
La explicabilidad en tiempo de consulta captura lo que hizo la tubería de GraphRAG durante el razonamiento. Se conecta con la trazabilidad de la extracción de tiempo, que registra de dónde provienen los hechos del grafo de conocimiento.
## Terminología
| Término | Definición |
|---|---|
| **Explicabilidad** | El registro de cómo se derivó un resultado |
| **Sesión** | Una ejecución de consulta de GraphRAG |
| **Selección de borde** | Selección de bordes relevantes basada en LLM, con razonamiento |
| **Cadena de trazabilidad** | Ruta desde el borde → fragmento → página → documento |
## Arquitectura
### Flujo de Explicabilidad
```
Consulta de GraphRAG
├─► Actividad de la Sesión
│ └─► Texto de la consulta, marca de tiempo
├─► Entidad de Recuperación
│ └─► Todos los bordes recuperados del subgrafo
├─► Entidad de Selección
│ └─► Bordes seleccionados con razonamiento del LLM
│ └─► Cada borde enlaza con la trazabilidad de extracción
└─► Entidad de Respuesta
└─► Referencia a la respuesta sintetizada (en el bibliotecario)
```
### Tubería de GraphRAG de Dos Etapas
1. **Selección de borde:** El LLM selecciona bordes relevantes del subgrafo, proporcionando una justificación para cada uno
2. **Síntesis:** El LLM genera la respuesta a partir de los bordes seleccionados
Esta separación permite la explicabilidad: sabemos exactamente qué bordes contribuyeron.
### Almacenamiento
- Los triples de explicabilidad se almacenan en una colección configurable (por defecto: `explainability`)
- Utiliza la ontología PROV-O para las relaciones de trazabilidad
- Reificación RDF para referencias a bordes
- El contenido de la respuesta se almacena en el servicio del bibliotecario (no en línea, demasiado grande)
### Streaming en Tiempo Real
Los eventos de explicabilidad se transmiten al cliente a medida que se ejecuta la consulta:
1. Se crea la sesión → se emite un evento
2. Se recuperan los bordes → se emite un evento
3. Se seleccionan los bordes con razonamiento → se emite un evento
4. Se sintetiza la respuesta → se emite un evento
El cliente recibe `explain_id` y `explain_collection` para obtener los detalles completos.
## Estructura de URI
Todos los URI utilizan el espacio de nombres `urn:trustgraph:` con UUIDs:
| Entidad | Patrón de URI |
|---|---|
| Sesión | `urn:trustgraph:session:{uuid}` |
| Recuperación | `urn:trustgraph:prov:retrieval:{uuid}` |
| Selección | `urn:trustgraph:prov:selection:{uuid}` |
| Respuesta | `urn:trustgraph:prov:answer:{uuid}` |
| Selección de borde | `urn:trustgraph:prov:edge:{uuid}:{index}` |
## Modelo RDF (PROV-O)
### Actividad de la Sesión
```turtle
<session-uri> a prov:Activity ;
rdfs:label "Sesión de consulta GraphRAG" ;
prov:startedAtTime "2024-01-15T10:30:00Z" ;
tg:query "¿Cuál fue la Guerra contra el Terrorismo?" .
```
### Entidad de Recuperación
```turtle
<retrieval-uri> a prov:Entity ;
rdfs:label "Bordes recuperados" ;
prov:wasGeneratedBy <session-uri> ;
tg:edgeCount 50 .
```
### Entidad de Selección
```turtle
<selection-uri> a prov:Entity ;
rdfs:label "Bordes seleccionados" ;
prov:wasDerivedFrom <retrieval-uri> ;
tg:selectedEdge <edge-sel-0> ;
tg:selectedEdge <edge-sel-1> .
<edge-sel-0> tg:edge << <s> <p> <o> >> ;
tg:reasoning "Este borde establece la relación clave..." .
```
### Entidad de Respuesta
```turtle
<answer-uri> a prov:Entity ;
rdfs:label "Respuesta de GraphRAG" ;
prov:wasDerivedFrom <selection-uri> ;
tg:document <urn:trustgraph:answer:{uuid}> .
```
La `tg:document` hace referencia a la respuesta almacenada en el servicio del bibliotecario.
## Constantes de Espacio de Nombres
Definidas en `trustgraph-base/trustgraph/provenance/namespaces.py`:
| Constante | URI |
|---|---|
| `TG_QUERY` | `https://trustgraph.ai/ns/query` |
| `TG_EDGE_COUNT` | `https://trustgraph.ai/ns/edgeCount` |
| `TG_SELECTED_EDGE` | `https://trustgraph.ai/ns/selectedEdge` |
| `TG_EDGE` | `https://trustgraph.ai/ns/edge` |
| `TG_REASONING` | `https://trustgraph.ai/ns/reasoning` |
| `TG_CONTENT` | `https://trustgraph.ai/ns/content` |
| `TG_DOCUMENT` | `https://trustgraph.ai/ns/document` |
## Esquema GraphRagResponse
```python
@dataclass
class GraphRagResponse:
error: Error | None = None
response: str = ""
end_of_stream: bool = False
explain_id: str | None = None
explain_collection: str | None = None
message_type: str = "" # "chunk" or "explain"
end_of_session: bool = False
```
### Tipos de Mensaje
| message_type | Propósito |
|---|---|
| `chunk` | Texto de la respuesta (streaming o final) |
| `explain` | Evento de explicabilidad con referencia IRI |
### Ciclo de Vida de la Sesión
1. Múltiples mensajes `explain` (sesión, recuperación, selección, respuesta)
2. Múltiples mensajes `chunk` (respuesta en streaming)
3. Final `chunk` con `end_of_session=True`
## Formato de Selección de Bordes
El LLM devuelve un JSONL con los bordes seleccionados:
```jsonl
{"id": "edge-hash-1", "reasoning": "Este borde muestra la relación clave..."}
{"id": "edge-hash-2", "reasoning": "Proporciona evidencia de apoyo..."}
```
El `id` es un hash de `(labeled_s, labeled_p, labeled_o)` calculado por `edge_id()`.
## Preservación de URI
### El Problema
GraphRAG muestra etiquetas legibles para el LLM, pero la explicabilidad necesita los URI originales para el rastreo de la trazabilidad.
### Solución
`get_labelgraph()` devuelve:
- `labeled_edges`: Lista de `(label_s, label_p, label_o)` para el LLM
- `uri_map`: Diccionario que mapea `edge_id(labels)``(uri_s, uri_p, uri_o)`
Cuando se guarda la información de explicabilidad, se utilizan los URI de `uri_map`.
## Rastreando la Trazabilidad
### Desde el borde hasta la fuente
Se pueden rastrear los bordes seleccionados de vuelta a la fuente:
### Referencias
- PROV-O (W3C Provenance Ontology): https://www.w3.org/TR/prov-o/
- RDF-star: https://w3c.github.io/rdf-star/
- Trazabilidad de tiempo de extracción: `docs/tech-specs/extraction-time-provenance.md`

View file

@ -0,0 +1,196 @@
---
layout: default
title: "Especificación Técnica de Soporte de Streaming para RAG"
parent: "Spanish (Beta)"
---
# Especificación Técnica de Soporte de Streaming para RAG
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Visión general
Esta especificación describe la adición de soporte de streaming a los servicios GraphRAG y DocumentRAG, permitiendo respuestas en tiempo real, token por token, para consultas de recuperación de conocimiento y documentos. Esto extiende la arquitectura de streaming existente, ya implementada para servicios de completado de texto, prompts y agentes de LLM.
## Objetivos
- **Experiencia de usuario consistente**: Proporcionar la misma experiencia de streaming en todos los servicios TrustGraph.
- **Cambios mínimos en la API**: Añadir soporte de streaming con una única bandera `streaming`, siguiendo patrones establecidos.
- **Compatibilidad hacia atrás**: Mantener el comportamiento no de streaming existente como predeterminado.
- **Reutilizar la infraestructura existente**: Aprovechar el streaming ya implementado en PromptClient.
- **Soporte de Gateway**: Permitir el streaming a través de un gateway websocket para aplicaciones cliente.
## Antecedentes
Servicios de streaming actualmente implementados:
- **Servicio de completado de texto de LLM**: Fase 1 - Streaming desde proveedores de LLM.
- **Servicio de prompts**: Fase 2 - Streaming a través de plantillas de prompts.
- **Servicio de agente**: Fase 3-4 - Streaming de respuestas ReAct con fragmentos incrementales de "pensamiento/observación/respuesta".
Limitaciones actuales para servicios RAG:
- GraphRAG y DocumentRAG solo soportan respuestas de bloqueo.
- Los usuarios deben esperar a que la respuesta completa del LLM antes de ver cualquier salida.
- Mala experiencia de usuario para respuestas largas de consultas de conocimiento o documentos.
- Experiencia inconsistente en comparación con otros servicios de TrustGraph.
Esta especificación aborda estas limitaciones añadiendo soporte de streaming a GraphRAG y DocumentRAG. Al permitir respuestas token por token, TrustGraph puede:
- Proporcionar una experiencia de usuario consistente de streaming para todos los tipos de consultas.
- Reducir la latencia percibida para las consultas RAG.
- Facilitar una mejor retroalimentación del progreso para las consultas de ejecución prolongada.
- Soporte para visualización en tiempo real en aplicaciones cliente.
## Diseño Técnico
### Arquitectura
La implementación de streaming de RAG aprovecha la infraestructura existente:
1. **Streaming de PromptClient** (Ya implementado)
- `kg_prompt()` y `document_prompt()` ya aceptan los parámetros `streaming` y `chunk_callback`.
- Estos llaman `prompt()` internamente con soporte de streaming.
- No se necesitan cambios en PromptClient.
- Módulo: `trustgraph-base/trustgraph/base/prompt_client.py`
2. **Servicio GraphRAG** (Necesita pasar el parámetro `streaming`)
- Añadir el parámetro `streaming` al método `query()`.
- Pasar la bandera de streaming y los callbacks a `prompt_client.kg_prompt()`.
- El esquema GraphRagRequest debe incluir el campo `streaming`.
- Módulos:
- `trustgraph-flow/trustgraph/retrieval/graph_rag/graph_rag.py`
- `trustgraph-flow/trustgraph/retrieval/graph_rag/rag.py` (Procesador)
- `trustgraph-base/trustgraph/schema/graph_rag.py` (Esquema de solicitud)
- `trustgraph-flow/trustgraph/gateway/dispatch/graph_rag.py` (Gateway)
3. **Servicio DocumentRAG** (Necesita pasar el parámetro `streaming`)
- Añadir el parámetro `streaming` al método `query()`.
- Pasar la bandera de streaming y los callbacks a `prompt_client.document_prompt()`.
- El esquema DocumentRagRequest debe incluir el campo `streaming`.
- Módulos:
- `trustgraph-flow/trustgraph/retrieval/document_rag/document_rag.py`
- `trustgraph-flow/trustgraph/retrieval/document_rag/rag.py` (Procesador)
- `trustgraph-base/trustgraph/schema/document_rag.py` (Esquema de solicitud)
- `trustgraph-flow/trustgraph/gateway/dispatch/document_rag.py` (Gateway)
### Flujo de datos
**No de streaming (actual)**:
```
Client → Gateway → RAG Service → PromptClient.kg_prompt(streaming=False)
Prompt Service → LLM
Respuesta completa
Client ← Gateway ← RAG Service ← Respuesta
```
**Streaming (propuesto)**:
```
Client → Gateway → RAG Service → PromptClient.kg_prompt(streaming=True, chunk_callback=cb)
Prompt Service → LLM (streaming)
Fragmento → callback → Respuesta RAG (fragmento)
↓ ↓
Client ← Gateway ← ────────────────────────────────── Flujo de respuesta
```
### APIs
**Cambios en GraphRAG**:
1. **GraphRag.query()** - Añadir parámetros de streaming
```python
async def query(
self, query, user, collection,
verbose=False, streaming=False, chunk_callback=None # NUEVO
):
# ... código existente de recuperación de entidades/triples ...
if streaming and chunk_callback:
resp = await self.prompt_client.kg_prompt(
query, kg,
streaming=True,
chunk_callback=chunk_callback
)
else:
resp = await self.prompt_client.kg_prompt(query, kg)
return resp
```
2. **Esquema GraphRagRequest** - Añadir campo de streaming
```python
class GraphRagRequest(Record):
query = String()
user = String()
collection = String()
streaming = Boolean() # NUEVO
```
3. **Esquema GraphRagResponse** - Añadir campos de streaming (seguir el patrón de Agent)
```python
class GraphRagResponse(Record):
response = String() # LEGADO: respuesta completa
chunk = String() # NUEVO: fragmento de streaming
end_of_stream = Boolean() # NUEVO: indica el último fragmento
```
4. **Procesador** - Pasar el streaming a través
```python
async def handle(self, msg):
# ... código existente ...
async def send_chunk(chunk):
await self.respond(GraphRagResponse(
chunk=chunk,
end_of_stream=False,
response=None
))
if request.streaming:
resp = await self.prompt_client.kg_prompt(
request.query,
request.context,
streaming=True
)
else:
resp = await self.prompt_client.kg_prompt(
request.query,
request.context
)
# Procesar respuesta
# ...
```
### Plan de migración
No se requiere migración:
- El soporte de streaming es opcional a través del parámetro `streaming` (predeterminado a False).
- Los clientes existentes siguen funcionando sin cambios.
- Los nuevos clientes pueden optar por habilitar el streaming.
## Cronograma
Tiempo estimado de implementación: 4-6 horas.
- Fase 1 (2 horas): Soporte de streaming para GraphRAG.
- Fase 2 (2 horas): Soporte de streaming para DocumentRAG.
- Fase 3 (1-2 horas): Actualizaciones del Gateway y banderas de la CLI.
- Pruebas: Integradas en cada fase.
## Preguntas abiertas
- ¿Deberíamos añadir soporte de streaming al servicio NLP Query también?
- ¿Queremos transmitir solo los pasos intermedios (por ejemplo, "Recuperando entidades...", "Consultando el gráfico...") o también la salida del LLM?
- ¿Debería GraphRAG/DocumentRAG incluir metadatos del fragmento (por ejemplo, número de fragmento, número total esperado)?
## Referencias
- Implementación existente: `docs/tech-specs/streaming-llm-responses.md`
- Streaming de PromptClient: `trustgraph-base/trustgraph/base/prompt_client.py`
- Streaming de Agent: `trustgraph-flow/trustgraph/agent/react/agent_manager.py`

View file

@ -0,0 +1,99 @@
---
layout: default
title: "Propuesta de Refactorización del Directorio de Esquemas"
parent: "Spanish (Beta)"
---
# Propuesta de Refactorización del Directorio de Esquemas
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Problemas Actuales
1. **Estructura plana** - Tener todos los esquemas en un solo directorio dificulta la comprensión de las relaciones.
2. **Preocupaciones mixtas** - Tipos centrales, objetos de dominio y contratos de API mezclados.
3. **Nombres poco claros** - Archivos como "object.py", "types.py", "topic.py" no indican claramente su propósito.
4. **Sin capa clara** - No es fácil ver qué depende de qué.
## Estructura Propuesta
```
trustgraph-base/trustgraph/schema/
├── __init__.py
├── core/ # Tipos primitivos centrales utilizados en todas partes
│ ├── __init__.py
│ ├── primitives.py # Error, Value, Triple, Field, RowSchema
│ ├── metadata.py # Registro de metadatos
│ └── topic.py # Utilitarios de tema
├── knowledge/ # Modelos del dominio de conocimiento y extracción
│ ├── __init__.py
│ ├── graph.py # EntityContext, EntityEmbeddings, Triples
│ ├── document.py # Document, TextDocument, Chunk
│ ├── knowledge.py # Tipos de extracción de conocimiento
│ ├── embeddings.py # Todos los tipos relacionados con la incorporación (movidos de múltiples archivos)
│ └── nlp.py # Tipos de Definición, Tema, Relación, Hecho
└── services/ # Contratos de solicitud/respuesta de servicios
├── __init__.py
├── llm.py # TextCompletion, Embeddings, Solicitudes/respuestas de Herramientas
├── retrieval.py # Consultas/respuestas de GraphRAG, DocumentRAG
├── query.py # Consultas/respuestas de GraphEmbeddingsRequest, DocumentEmbeddingsRequest
├── agent.py # Solicitudes/respuestas del agente
├── flow.py # Solicitudes/respuestas de flujo
├── prompt.py # Solicitudes/respuestas del servicio de prompt
├── config.py # Servicio de configuración
├── library.py # Servicio de bibliotecario
└── lookup.py # Servicio de búsqueda
```
## Cambios Clave
1. **Organización jerárquica** - Separación clara entre tipos centrales, modelos de conocimiento y contratos de servicio.
2. **Nombres mejorados**:
- `types.py``core/primitives.py` (propósito más claro)
- `object.py` → Dividir entre archivos apropiados según el contenido real
- `documents.py``knowledge/document.py` (singular, consistente)
- `models.py``services/llm.py` (qué tipo de modelos)
- `prompt.py` → Dividir: partes del servicio a `services/prompt.py`, tipos de datos a `knowledge/nlp.py`
3. **Agrupamiento lógico**:
- Todos los tipos de incorporación consolidados en `knowledge/embeddings.py`
- Todos los contratos de servicio relacionados con LLM en `services/llm.py`
- Separación clara de pares de solicitud/respuesta en el directorio de servicios
- Grupos de tipos de extracción de conocimiento con otros modelos de dominio de conocimiento
4. **Claridad de dependencias**:
- Los tipos centrales no tienen dependencias
- Los modelos de conocimiento dependen solo de los centrales
- Los contratos de servicio pueden depender de los centrales y de los modelos de conocimiento
## Beneficios de la Migración
1. **Navegación más fácil** - Los desarrolladores pueden encontrar rápidamente lo que necesitan.
2. **Mejor modularidad** - Límites claros entre diferentes preocupaciones.
3. **Importaciones más simples** - Rutas de importación más intuitivas.
4. **Futurizable** - Fácil de agregar nuevos tipos de conocimiento o servicios sin desorden.
## Cambios de Importación de Ejemplo
```python
# Antes
from trustgraph.schema import Error, Triple, GraphEmbeddings, TextCompletionRequest
# Después
from trustgraph.schema.core import Error, Triple
from trustgraph.schema.knowledge import GraphEmbeddings
from trustgraph.schema.services import TextCompletionRequest
```
## Notas de Implementación
1. Mantener la compatibilidad hacia atrás manteniendo las importaciones en el `__init__.py` raíz.
2. Mover los archivos gradualmente, actualizando las importaciones según sea necesario.
3. Considerar agregar un `legacy.py` que importe todo para el período de transición.
4. Actualizar la documentación para reflejar la nueva estructura.
<function_calls>
<invoke name="TodoWrite">
<parameter name="todos">[{"id": "1", "content": "Examinar la estructura actual del directorio de esquemas", "status": "completed", "priority": "high"}, {"id": "2", "content": "Analizar los archivos de esquema y sus propósitos", "status": "completed", "priority": "high"}, {"id": "3", "content": "Proponer una estructura y nombres mejorados", "status": "completed", "priority": "high"}]

View file

@ -0,0 +1,578 @@
---
layout: default
title: "Especificación Técnica de Respuestas de LLM en Streaming"
parent: "Spanish (Beta)"
---
# Especificación Técnica de Respuestas de LLM en Streaming
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
Esta especificación describe la implementación del soporte de streaming para respuestas de LLM
en TrustGraph. El streaming permite la entrega en tiempo real de tokens generados
a medida que son producidos por el LLM, en lugar de esperar a que se complete
la generación de la respuesta.
Esta implementación admite los siguientes casos de uso:
1. **Interfaces de Usuario en Tiempo Real**: Transmite tokens a la interfaz de usuario a medida que se generan,
proporcionando retroalimentación visual inmediata.
2. **Reducción del Tiempo Hasta el Primer Token**: Los usuarios ven la salida inmediatamente
en lugar de esperar a que se complete la generación.
3. **Manejo de Respuestas Largas**: Maneja salidas muy largas que de otro modo
podrían provocar un tiempo de espera o exceder los límites de memoria.
4. **Aplicaciones Interactivas**: Permite interfaces de chat y agentes receptivas.
## Objetivos
**Compatibilidad con Versiones Anteriores**: Los clientes existentes que no utilizan streaming continúan funcionando
sin modificaciones.
**Diseño de API Consistente**: El streaming y el no streaming utilizan los mismos patrones de esquema
con una divergencia mínima.
**Flexibilidad del Proveedor**: Soporte de streaming cuando esté disponible, con una
alternativa gradual cuando no esté disponible.
**Implementación por Fases**: Implementación incremental para reducir el riesgo.
**Soporte de Extremo a Extremo**: Streaming desde el proveedor de LLM hasta las aplicaciones cliente
a través de Pulsar, la API de Gateway y la API de Python.
## Antecedentes
### Arquitectura Actual
El flujo actual de finalización de texto de LLM funciona de la siguiente manera:
1. El cliente envía `TextCompletionRequest` con los campos `system` y `prompt`.
2. El servicio de LLM procesa la solicitud y espera a que se complete la generación.
3. Se devuelve un único `TextCompletionResponse` con la cadena `response` completa.
Esquema actual (`trustgraph-base/trustgraph/schema/services/llm.py`):
```python
class TextCompletionRequest(Record):
system = String()
prompt = String()
class TextCompletionResponse(Record):
error = Error()
response = String()
in_token = Integer()
out_token = Integer()
model = String()
```
### Limitaciones actuales
**Latencia**: Los usuarios deben esperar a que se complete la generación antes de ver cualquier resultado.
**Riesgo de tiempo de espera**: Las generaciones largas pueden exceder los umbrales de tiempo de espera del cliente.
**Mala experiencia de usuario**: La falta de retroalimentación durante la generación crea la percepción de lentitud.
**Uso de recursos**: Las respuestas completas deben almacenarse en búfer en la memoria.
Esta especificación aborda estas limitaciones al permitir la entrega incremental de respuestas, manteniendo la compatibilidad total con versiones anteriores.
## Diseño técnico
### Fase 1: Infraestructura
La Fase 1 establece la base para la transmisión mediante la modificación de esquemas, API y herramientas de línea de comandos.
#### Cambios en el esquema
##### Esquema de LLM (`trustgraph-base/trustgraph/schema/services/llm.py`)
**Cambios en la solicitud:**
```python
class TextCompletionRequest(Record):
system = String()
prompt = String()
streaming = Boolean() # NEW: Default false for backward compatibility
```
`streaming`: Cuando `true`, solicita la entrega de la respuesta en modo de transmisión.
Predeterminado: `false` (el comportamiento existente se mantiene).
**Cambios en la respuesta:**
```python
class TextCompletionResponse(Record):
error = Error()
response = String()
in_token = Integer()
out_token = Integer()
model = String()
end_of_stream = Boolean() # NEW: Indicates final message
```
`end_of_stream`: Cuando `true`, indica que esta es la respuesta final (o única).
Para solicitudes no de transmisión: Respuesta única con `end_of_stream=true`.
Para solicitudes de transmisión: Múltiples respuestas, todas con `end_of_stream=false`
excepto la última.
##### Esquema de la solicitud (`trustgraph-base/trustgraph/schema/services/prompt.py`)
El servicio de solicitud envuelve la finalización de texto, por lo que refleja el mismo patrón:
**Cambios en la solicitud:**
```python
class PromptRequest(Record):
id = String()
terms = Map(String())
streaming = Boolean() # NEW: Default false
```
**Cambios en la respuesta:**
```python
class PromptResponse(Record):
error = Error()
text = String()
object = String()
end_of_stream = Boolean() # NEW: Indicates final message
```
#### Cambios en la API de Gateway
La API de Gateway debe exponer capacidades de transmisión a clientes HTTP/WebSocket.
**Actualizaciones de la API REST:**
`POST /api/v1/text-completion`: Aceptar el parámetro `streaming` en el cuerpo de la solicitud.
El comportamiento de la respuesta depende de la bandera de transmisión:
`streaming=false`: Respuesta JSON única (comportamiento actual).
`streaming=true`: Flujo de eventos enviados por el servidor (SSE) o mensajes WebSocket.
**Formato de respuesta (transmisión):**
Cada fragmento transmitido sigue la misma estructura de esquema:
```json
{
"response": "partial text...",
"end_of_stream": false,
"model": "model-name"
}
```
Fragmento final:
```json
{
"response": "final text chunk",
"end_of_stream": true,
"in_token": 150,
"out_token": 500,
"model": "model-name"
}
```
#### Cambios en la API de Python
La API del cliente de Python debe soportar tanto modos de transmisión como no de transmisión
manteniendo la compatibilidad con versiones anteriores.
**Actualizaciones de LlmClient** (`trustgraph-base/trustgraph/clients/llm_client.py`):
```python
class LlmClient(BaseClient):
def request(self, system, prompt, timeout=300, streaming=False):
"""
Non-streaming request (backward compatible).
Returns complete response string.
"""
# Existing behavior when streaming=False
async def request_stream(self, system, prompt, timeout=300):
"""
Streaming request.
Yields response chunks as they arrive.
"""
# New async generator method
```
**Actualizaciones de PromptClient** (`trustgraph-base/trustgraph/base/prompt_client.py`):
Patrón similar con el parámetro `streaming` y la variante de generador asíncrono.
#### Cambios en la herramienta de línea de comandos
**tg-invoke-llm** (`trustgraph-cli/trustgraph/cli/invoke_llm.py`):
```
tg-invoke-llm [system] [prompt] [--no-streaming] [-u URL] [-f flow-id]
```
La transmisión está habilitada de forma predeterminada para una mejor experiencia de usuario interactiva.
La opción `--no-streaming` desactiva la transmisión.
Cuando la transmisión está habilitada: envía los tokens a la salida estándar a medida que llegan.
Cuando la transmisión no está habilitada: espera la respuesta completa y luego la muestra.
**tg-invoke-prompt** (`trustgraph-cli/trustgraph/cli/invoke_prompt.py`):
```
tg-invoke-prompt [template-id] [var=value...] [--no-streaming] [-u URL] [-f flow-id]
```
El mismo patrón que `tg-invoke-llm`.
#### Cambios en la Clase Base del Servicio LLM
**LlmService** (`trustgraph-base/trustgraph/base/llm_service.py`):
```python
class LlmService(FlowProcessor):
async def on_request(self, msg, consumer, flow):
request = msg.value()
streaming = getattr(request, 'streaming', False)
if streaming and self.supports_streaming():
async for chunk in self.generate_content_stream(...):
await self.send_response(chunk, end_of_stream=False)
await self.send_response(final_chunk, end_of_stream=True)
else:
response = await self.generate_content(...)
await self.send_response(response, end_of_stream=True)
def supports_streaming(self):
"""Override in subclass to indicate streaming support."""
return False
async def generate_content_stream(self, system, prompt, model, temperature):
"""Override in subclass to implement streaming."""
raise NotImplementedError()
```
--
### Fase 2: Prueba de concepto de VertexAI
La Fase 2 implementa el streaming en un único proveedor (VertexAI) para validar la
infraestructura y habilitar pruebas de extremo a extremo.
#### Implementación de VertexAI
**Módulo:** `trustgraph-vertexai/trustgraph/model/text_completion/vertexai/llm.py`
**Cambios:**
1. Sobreescribir `supports_streaming()` para devolver `True`
2. Implementar generador asíncrono `generate_content_stream()`
3. Manejar tanto los modelos Gemini como Claude (a través de la API de VertexAI Anthropic)
**Streaming de Gemini:**
```python
async def generate_content_stream(self, system, prompt, model, temperature):
model_instance = self.get_model(model, temperature)
response = model_instance.generate_content(
[system, prompt],
stream=True # Enable streaming
)
for chunk in response:
yield LlmChunk(
text=chunk.text,
in_token=None, # Available only in final chunk
out_token=None,
)
# Final chunk includes token counts from response.usage_metadata
```
**Claude (a través de VertexAI Anthropic) en modo de transmisión:**
```python
async def generate_content_stream(self, system, prompt, model, temperature):
with self.anthropic_client.messages.stream(...) as stream:
for text in stream.text_stream:
yield LlmChunk(text=text)
# Token counts from stream.get_final_message()
```
#### Pruebas
Pruebas unitarias para el ensamblaje de respuestas en streaming.
Pruebas de integración con VertexAI (Gemini y Claude).
Pruebas de extremo a extremo: CLI -> Gateway -> Pulsar -> VertexAI -> back.
Pruebas de compatibilidad con versiones anteriores: Las solicitudes no en streaming aún funcionan.
--
### Fase 3: Todos los proveedores de LLM
La fase 3 extiende el soporte de streaming a todos los proveedores de LLM en el sistema.
#### Estado de implementación del proveedor
Cada proveedor debe:
1. **Soporte completo de streaming**: Implementar `generate_content_stream()`
2. **Modo de compatibilidad**: Manejar la bandera `end_of_stream` correctamente
(devolver una única respuesta con `end_of_stream=true`)
| Proveedor | Paquete | Soporte de streaming |
|----------|---------|-------------------|
| OpenAI | trustgraph-flow | Completo (API de streaming nativo) |
| Claude/Anthropic | trustgraph-flow | Completo (API de streaming nativo) |
| Ollama | trustgraph-flow | Completo (API de streaming nativo) |
| Cohere | trustgraph-flow | Completo (API de streaming nativo) |
| Mistral | trustgraph-flow | Completo (API de streaming nativo) |
| Azure OpenAI | trustgraph-flow | Completo (API de streaming nativo) |
| Google AI Studio | trustgraph-flow | Completo (API de streaming nativo) |
| VertexAI | trustgraph-vertexai | Completo (Fase 2) |
| Bedrock | trustgraph-bedrock | Completo (API de streaming nativo) |
| LM Studio | trustgraph-flow | Completo (compatible con OpenAI) |
| LlamaFile | trustgraph-flow | Completo (compatible con OpenAI) |
| vLLM | trustgraph-flow | Completo (compatible con OpenAI) |
| TGI | trustgraph-flow | Por determinar |
| Azure | trustgraph-flow | Por determinar |
#### Patrón de implementación
Para proveedores compatibles con OpenAI (OpenAI, LM Studio, LlamaFile, vLLM):
```python
async def generate_content_stream(self, system, prompt, model, temperature):
response = await self.client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": system},
{"role": "user", "content": prompt}
],
temperature=temperature,
stream=True
)
async for chunk in response:
if chunk.choices[0].delta.content:
yield LlmChunk(text=chunk.choices[0].delta.content)
```
--
### Fase 4: API del Agente
La Fase 4 extiende la transmisión a la API del Agente. Esto es más complejo porque
la API del Agente ya es inherentemente multi-mensaje (pensamiento → acción → observación
→ repetir → respuesta final).
#### Esquema actual del Agente
```python
class AgentStep(Record):
thought = String()
action = String()
arguments = Map(String())
observation = String()
user = String()
class AgentRequest(Record):
question = String()
state = String()
group = Array(String())
history = Array(AgentStep())
user = String()
class AgentResponse(Record):
answer = String()
error = Error()
thought = String()
observation = String()
```
#### Cambios Propuestos al Esquema del Agente
**Solicitar Cambios:**
```python
class AgentRequest(Record):
question = String()
state = String()
group = Array(String())
history = Array(AgentStep())
user = String()
streaming = Boolean() # NEW: Default false
```
**Cambios en la respuesta:**
El agente produce múltiples tipos de salida durante su ciclo de razonamiento:
Pensamientos (razonamiento)
Acciones (llamadas a herramientas)
Observaciones (resultados de las herramientas)
Respuesta (respuesta final)
Errores
Dado que `chunk_type` identifica qué tipo de contenido se está enviando, los campos separados
`answer`, `error`, `thought` y `observation` se pueden combinar en
un único campo `content`:
```python
class AgentResponse(Record):
chunk_type = String() # "thought", "action", "observation", "answer", "error"
content = String() # The actual content (interpretation depends on chunk_type)
end_of_message = Boolean() # Current thought/action/observation/answer is complete
end_of_dialog = Boolean() # Entire agent dialog is complete
```
**Semántica de los campos:**
`chunk_type`: Indica qué tipo de contenido se encuentra en el campo `content`
`"thought"`: Razonamiento/pensamiento del agente
`"action"`: Herramienta/acción que se está invocando
`"observation"`: Resultado de la ejecución de la herramienta
`"answer"`: Respuesta final a la pregunta del usuario
`"error"`: Mensaje de error
`content`: El contenido transmitido real, interpretado según `chunk_type`
`end_of_message`: Cuando `true`, el tipo de fragmento actual está completo
Ejemplo: Todos los tokens para el pensamiento actual han sido enviados
Permite a los clientes saber cuándo pasar a la siguiente etapa
`end_of_dialog`: Cuando `true`, la interacción completa del agente ha finalizado
Este es el mensaje final en la transmisión
#### Comportamiento de la transmisión del agente
Cuando `streaming=true`:
1. **Transmisión de pensamientos:**
Múltiples fragmentos con `chunk_type="thought"`, `end_of_message=false`
El fragmento final del pensamiento tiene `end_of_message=true`
2. **Notificación de acción:**
Un solo fragmento con `chunk_type="action"`, `end_of_message=true`
3. **Observación:**
Fragmento(s) con `chunk_type="observation"`, el final tiene `end_of_message=true`
4. **Repetir** los pasos 1-3 mientras el agente razona
5. **Respuesta final:**
`chunk_type="answer"` con la respuesta final en `content`
El último fragmento tiene `end_of_message=true`, `end_of_dialog=true`
**Secuencia de ejemplo de la transmisión:**
```
{chunk_type: "thought", content: "I need to", end_of_message: false, end_of_dialog: false}
{chunk_type: "thought", content: " search for...", end_of_message: true, end_of_dialog: false}
{chunk_type: "action", content: "search", end_of_message: true, end_of_dialog: false}
{chunk_type: "observation", content: "Found: ...", end_of_message: true, end_of_dialog: false}
{chunk_type: "thought", content: "Based on this", end_of_message: false, end_of_dialog: false}
{chunk_type: "thought", content: " I can answer...", end_of_message: true, end_of_dialog: false}
{chunk_type: "answer", content: "The answer is...", end_of_message: true, end_of_dialog: true}
```
Cuando `streaming=false`:
Comportamiento actual preservado
Respuesta única con respuesta completa
`end_of_message=true`, `end_of_dialog=true`
#### Gateway y API de Python
Gateway: Nuevo punto final SSE/WebSocket para la transmisión de agentes
API de Python: Nuevo método de generador asíncrono `agent_stream()`
--
## Consideraciones de seguridad
**Sin nueva superficie de ataque**: La transmisión utiliza la misma autenticación/autorización
**Limitación de velocidad**: Aplicar límites de velocidad por token o por fragmento si es necesario
**Manejo de conexiones**: Terminar correctamente las transmisiones en caso de desconexión del cliente
**Gestión de tiempos de espera**: Las solicitudes de transmisión requieren un manejo adecuado de los tiempos de espera
## Consideraciones de rendimiento
**Memoria**: La transmisión reduce el uso máximo de memoria (sin almacenamiento en búfer de la respuesta completa)
**Latencia**: El tiempo hasta el primer token se reduce significativamente
**Sobrecarga de conexión**: Las conexiones SSE/WebSocket tienen una sobrecarga de mantenimiento activo
**Rendimiento de Pulsar**: Múltiples mensajes pequeños frente a un mensaje grande único
tradeoff
## Estrategia de pruebas
### Pruebas unitarias
Serialización/deserialización de esquema con nuevos campos
Compatibilidad con versiones anteriores (los campos faltantes utilizan los valores predeterminados)
Lógica de ensamblaje de fragmentos
### Pruebas de integración
Implementación de transmisión de cada proveedor de LLM
Puntos finales de transmisión de la API de Gateway
Métodos de transmisión del cliente de Python
### Pruebas de extremo a extremo
Salida de transmisión de la herramienta de línea de comandos
Flujo completo: Cliente → Gateway → Pulsar → LLM → de vuelta
Cargas de trabajo mixtas de transmisión/no transmisión
### Pruebas de compatibilidad con versiones anteriores
Los clientes existentes funcionan sin modificaciones
Las solicitudes no de transmisión se comportan de la misma manera
## Plan de migración
### Fase 1: Infraestructura
Implementar cambios de esquema (compatible con versiones anteriores)
Implementar actualizaciones de la API de Gateway
Implementar actualizaciones de la API de Python
Lanzar actualizaciones de la herramienta de línea de comandos
### Fase 2: VertexAI
Implementar la implementación de transmisión de VertexAI
Validar con cargas de trabajo de prueba
### Fase 3: Todos los proveedores
Implementar las actualizaciones de los proveedores de forma incremental
Monitorear problemas
### Fase 4: API de agente
Implementar cambios de esquema de agente
Implementar la implementación de transmisión de agente
Actualizar la documentación
## Cronograma
| Fase | Descripción | Dependencias |
|-------|-------------|--------------|
| Fase 1 | Infraestructura | Ninguna |
| Fase 2 | PoC de VertexAI | Fase 1 |
| Fase 3 | Todos los proveedores | Fase 2 |
| Fase 4 | API de agente | Fase 3 |
## Decisiones de diseño
Las siguientes preguntas se resolvieron durante la especificación:
1. **Conteo de tokens en la transmisión**: Los conteos de tokens son deltas, no totales acumulados.
Los consumidores pueden sumarlos si es necesario. Esto coincide con la forma en que la mayoría de los proveedores informan
el uso y simplifica la implementación.
2. **Manejo de errores en la transmisión**: Si ocurre un error, el campo `error` se
completa y no se necesitan otros campos. Un error siempre es la comunicación final
no se permiten ni se esperan mensajes posteriores después de
un error. Para las transmisiones de LLM/Prompt, `end_of_stream=true`. Para las transmisiones de agente,
`chunk_type="error"` con `end_of_dialog=true`.
3. **Recuperación de respuesta parcial**: El protocolo de mensajería (Pulsar) es resistente,
por lo que no se necesita reintento a nivel de mensaje. Si un cliente pierde el seguimiento de la transmisión
o se desconecta, debe reintentar la solicitud completa desde cero.
4. **Transmisión del servicio de prompt**: La transmisión solo es compatible con texto (`text`)
respuestas, no respuestas estructuradas (`object`). El servicio de prompt lo sabe desde
el principio si la salida será JSON o texto según la plantilla del prompt. Si se realiza una
solicitud de transmisión para un prompt de salida JSON, el
servicio debe:
Devolver el JSON completo en una sola respuesta con `end_of_stream=true`, o
Rechazar la solicitud de transmisión con un error
## Preguntas abiertas
Ninguna en este momento.
## Referencias
Esquema de LLM actual: `trustgraph-base/trustgraph/schema/services/llm.py`
Esquema de prompt actual: `trustgraph-base/trustgraph/schema/services/prompt.py`
Esquema de agente actual: `trustgraph-base/trustgraph/schema/services/agent.py`
Base del servicio de LLM: `trustgraph-base/trustgraph/base/llm_service.py`
Proveedor de VertexAI: `trustgraph-vertexai/trustgraph/model/text_completion/vertexai/llm.py`
API de Gateway: `trustgraph-base/trustgraph/api/`
Herramientas de CLI: `trustgraph-cli/trustgraph/cli/`

View file

@ -0,0 +1,621 @@
---
layout: default
title: "Especificación Técnica de Datos Estructurados (Parte 2)"
parent: "Spanish (Beta)"
---
# Especificación Técnica de Datos Estructurados (Parte 2)
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
Esta especificación aborda los problemas y las deficiencias identificadas durante la implementación inicial de la integración de datos estructurados de TrustGraph, como se describe en `structured-data.md`.
## Declaraciones del Problema
### 1. Inconsistencia en la Nomenclatura: "Objeto" vs "Fila"
La implementación actual utiliza la terminología de "objeto" en todo (por ejemplo, `ExtractedObject`, extracción de objetos, incrustaciones de objetos). Esta nomenclatura es demasiado genérica y causa confusión:
"Objeto" es un término ambiguo en el software (objetos de Python, objetos JSON, etc.)
Los datos que se están manejando son fundamentalmente tabulares: filas en tablas con esquemas definidos.
"Fila" describe con mayor precisión el modelo de datos y se alinea con la terminología de la base de datos.
Esta inconsistencia aparece en los nombres de los módulos, los nombres de las clases, los tipos de mensajes y la documentación.
### 2. Limitaciones de Consulta de la Tienda de Filas
La implementación actual de la tienda de filas tiene limitaciones de consulta significativas:
**Desajuste con el Lenguaje Natural**: Las consultas tienen dificultades con las variaciones de datos del mundo real. Por ejemplo:
Es difícil encontrar una base de datos de calles que contenga `"CHESTNUT ST"` cuando se pregunta por `"Chestnut Street"`.
Las abreviaturas, las diferencias de mayúsculas y minúsculas y las variaciones de formato interrumpen las consultas de coincidencia exacta.
Los usuarios esperan una comprensión semántica, pero la tienda proporciona una coincidencia literal.
**Problemas de Evolución del Esquema**: Los cambios en los esquemas causan problemas:
Los datos existentes pueden no cumplir con los esquemas actualizados.
Los cambios en la estructura de la tabla pueden romper las consultas y la integridad de los datos.
No hay una ruta de migración clara para las actualizaciones del esquema.
### 3. Se Requieren Incrustaciones de Filas
Relacionado con el problema 2, el sistema necesita incrustaciones vectoriales para los datos de las filas para permitir:
Búsqueda semántica en datos estructurados (encontrar "Chestnut Street" cuando los datos contienen "CHESTNUT ST").
Coincidencia de similitud para consultas difusas.
Búsqueda híbrida que combina filtros estructurados con similitud semántica.
Mejor soporte para consultas en lenguaje natural.
El servicio de incrustación se especificó pero no se implementó.
### 4. Ingesta de Datos de Filas Incompleta
La canalización de ingesta de datos estructurados no está completamente operativa:
Existen indicaciones de diagnóstico para clasificar los formatos de entrada (CSV, JSON, etc.).
El servicio de ingesta que utiliza estas indicaciones no está integrado en el sistema.
No hay una ruta de extremo a extremo para cargar datos preestructurados en la tienda de filas.
## Objetivos
**Flexibilidad del Esquema**: Permitir la evolución del esquema sin romper los datos existentes ni requerir migraciones.
**Nomenclatura Consistente**: Estandarizar la terminología de "fila" en todo el código base.
**Consultas Semánticas**: Compatibilidad con la coincidencia difusa/semántica a través de incrustaciones de filas.
**Canalización de Ingesta Completa**: Proporcionar una ruta de extremo a extremo para cargar datos estructurados.
## Diseño Técnico
### Esquema Unificado de Almacenamiento de Filas
La implementación anterior creó una tabla de Cassandra separada para cada esquema. Esto causó problemas cuando los esquemas evolucionaron, ya que los cambios en la estructura de la tabla requerían migraciones.
El nuevo diseño utiliza una única tabla unificada para todos los datos de filas:
```sql
CREATE TABLE rows (
collection text,
schema_name text,
index_name text,
index_value frozen<list<text>>,
data map<text, text>,
source text,
PRIMARY KEY ((collection, schema_name, index_name), index_value)
)
```
#### Definiciones de columnas
| Columna | Tipo | Descripción |
|--------|------|-------------|
| `collection` | `text` | Identificador de recolección/importación de datos (procedente de metadatos) |
| `schema_name` | `text` | Nombre del esquema al que se ajusta esta fila |
| `index_name` | `text` | Nombre del(los) campo(s) indexado(s), unidos por comas para compuestos |
| `index_value` | `frozen<list<text>>` | Valor(es) del índice como una lista |
| `data` | `map<text, text>` | Datos de la fila como pares clave-valor |
| `source` | `text` | URI opcional que enlaza a información de procedencia en el grafo de conocimiento. Una cadena vacía o NULL indica que no hay fuente. |
#### Manejo de índices
Cada fila se almacena varias veces: una vez por cada campo indexado definido en el esquema. Los campos de clave primaria se tratan como un índice sin un marcador especial, lo que proporciona flexibilidad futura.
**Ejemplo de índice de un solo campo:**
El esquema define `email` como indexado
`index_name = "email"`
`index_value = ['foo@bar.com']`
**Ejemplo de índice compuesto:**
El esquema define un índice compuesto en `region` y `status`
`index_name = "region,status"` (nombres de los campos ordenados y unidos por comas)
`index_value = ['US', 'active']` (valores en el mismo orden que los nombres de los campos)
**Ejemplo de clave primaria:**
El esquema define `customer_id` como clave primaria
`index_name = "customer_id"`
`index_value = ['CUST001']`
#### Patrones de consulta
Todas las consultas siguen el mismo patrón, independientemente del índice que se utilice:
```sql
SELECT * FROM rows
WHERE collection = 'import_2024'
AND schema_name = 'customers'
AND index_name = 'email'
AND index_value = ['foo@bar.com']
```
#### Compensaciones de diseño
**Ventajas:**
Los cambios de esquema no requieren cambios en la estructura de la tabla.
Los datos de las filas son opacos para Cassandra; las adiciones/eliminaciones de campos son transparentes.
Patrón de consulta consistente para todos los métodos de acceso.
No hay índices secundarios de Cassandra (que pueden ser lentos a escala).
Tipos nativos de Cassandra en todo momento (`map`, `frozen<list>`).
**Compensaciones:**
Amplificación de escritura: cada inserción de fila = N inserciones (una por cada campo indexado).
Sobrecarga de almacenamiento debido a la duplicación de datos de las filas.
La información del tipo se almacena en la configuración del esquema, la conversión se realiza en la capa de la aplicación.
#### Modelo de consistencia
El diseño acepta ciertas simplificaciones:
1. **Sin actualizaciones de filas**: El sistema es de solo escritura. Esto elimina las preocupaciones de coherencia sobre la actualización de múltiples copias de la misma fila.
2. **Tolerancia a los cambios de esquema**: Cuando los esquemas cambian (por ejemplo, se agregan o eliminan índices), las filas existentes conservan su indexación original. Las filas antiguas no se podrán descubrir a través de nuevos índices. Los usuarios pueden eliminar y recrear un esquema para garantizar la coherencia si es necesario.
### Seguimiento y eliminación de particiones
#### El problema
Con la clave de partición `(collection, schema_name, index_name)`, la eliminación eficiente requiere conocer todas las claves de partición para eliminar. Eliminar solo por `collection` o `collection + schema_name` requiere conocer todos los valores de `index_name` que tienen datos.
#### Tabla de seguimiento de particiones
Una tabla de búsqueda secundaria realiza un seguimiento de qué particiones existen:
```sql
CREATE TABLE row_partitions (
collection text,
schema_name text,
index_name text,
PRIMARY KEY ((collection), schema_name, index_name)
)
```
Esto permite una detección eficiente de particiones para operaciones de eliminación.
#### Comportamiento del Escritor de Filas
El escritor de filas mantiene una caché en memoria de pares registrados de `(collection, schema_name)`. Al procesar una fila:
1. Comprobar si `(collection, schema_name)` está en la caché.
2. Si no está en la caché (primera fila para este par):
Buscar la configuración del esquema para obtener todos los nombres de índice.
Insertar entradas en `row_partitions` para cada `(collection, schema_name, index_name)`.
Agregar el par a la caché.
3. Continuar con la escritura de los datos de la fila.
El escritor de filas también supervisa los eventos de cambio de configuración del esquema. Cuando cambia un esquema, las entradas de caché relevantes se eliminan para que la siguiente fila active un nuevo registro con los nombres de índice actualizados.
Este enfoque garantiza:
Las escrituras de la tabla de búsqueda se realizan una vez por par de `(collection, schema_name)`, no por fila.
La tabla de búsqueda refleja los índices que estaban activos cuando se escribieron los datos.
Los cambios de esquema durante la importación se detectan correctamente.
#### Operaciones de Eliminación
**Eliminar colección:**
```sql
-- 1. Discover all partitions
SELECT schema_name, index_name FROM row_partitions WHERE collection = 'X';
-- 2. Delete each partition from rows table
DELETE FROM rows WHERE collection = 'X' AND schema_name = '...' AND index_name = '...';
-- (repeat for each discovered partition)
-- 3. Clean up the lookup table
DELETE FROM row_partitions WHERE collection = 'X';
```
**Eliminar colección + esquema:**
```sql
-- 1. Discover partitions for this schema
SELECT index_name FROM row_partitions WHERE collection = 'X' AND schema_name = 'Y';
-- 2. Delete each partition from rows table
DELETE FROM rows WHERE collection = 'X' AND schema_name = 'Y' AND index_name = '...';
-- (repeat for each discovered partition)
-- 3. Clean up the lookup table entries
DELETE FROM row_partitions WHERE collection = 'X' AND schema_name = 'Y';
```
### Incrustaciones de Filas
Las incrustaciones de filas permiten la coincidencia semántica/difusa en los valores indexados, resolviendo el problema de la falta de coincidencia del lenguaje natural (por ejemplo, encontrar "CHESTNUT ST" al buscar "Chestnut Street").
#### Descripción General del Diseño
Cada valor indexado se incrusta y se almacena en un almacén de vectores (Qdrant). En el momento de la consulta, la consulta se incrusta, se encuentran vectores similares y los metadatos asociados se utilizan para buscar las filas reales en Cassandra.
#### Estructura de la Colección Qdrant
Una colección Qdrant por tupla `(user, collection, schema_name, dimension)`:
**Nombre de la colección:** `rows_{user}_{collection}_{schema_name}_{dimension}`
Los nombres se limpian (los caracteres no alfanuméricos se reemplazan con `_`, se convierten a minúsculas, los prefijos numéricos reciben el prefijo `r_`)
**Justificación:** Permite la eliminación limpia de una instancia `(user, collection, schema_name)` eliminando las colecciones Qdrant correspondientes; el sufijo de dimensión permite que diferentes modelos de incrustación coexistan.
#### Qué se Incrusta
La representación de texto de los valores de índice:
| Tipo de Índice | Ejemplo `index_value` | Texto a Incrustar |
|------------|----------------------|---------------|
| Campo único | `['foo@bar.com']` | `"foo@bar.com"` |
| Compuesto | `['US', 'active']` | `"US active"` (unidos por espacios) |
#### Estructura del Punto
Cada punto Qdrant contiene:
```json
{
"id": "<uuid>",
"vector": [0.1, 0.2, ...],
"payload": {
"index_name": "street_name",
"index_value": ["CHESTNUT ST"],
"text": "CHESTNUT ST"
}
}
```
| Campo de carga útil | Descripción |
|---------------|-------------|
| `index_name` | Los campos indexados que representa esta incrustación. |
| `index_value` | La lista original de valores (para la búsqueda en Cassandra). |
| `text` | El texto que se incrustó (para depuración/visualización). |
Nota: `user`, `collection` y `schema_name` se derivan implícitamente del nombre de la colección de Qdrant.
#### Flujo de consulta
1. El usuario consulta "Chestnut Street" dentro del usuario U, la colección X, el esquema Y.
2. Incrusta el texto de la consulta.
3. Determina el(los) nombre(s) de la colección de Qdrant que coinciden con el prefijo `rows_U_X_Y_`.
4. Busca en la(s) colección(es) de Qdrant que coincidan para encontrar los vectores más cercanos.
5. Obtiene los puntos coincidentes con cargas útiles que contienen `index_name` y `index_value`.
6. Consulta Cassandra:
```sql
SELECT * FROM rows
WHERE collection = 'X'
AND schema_name = 'Y'
AND index_name = '<from payload>'
AND index_value = <from payload>
```
7. Devolver filas coincidentes
#### Opcional: Filtrado por Nombre de Índice
Las consultas pueden filtrar opcionalmente por `index_name` en Qdrant para buscar solo campos específicos:
**"Encontrar cualquier campo que coincida con 'Chestnut'"** → buscar todos los vectores en la colección
**"Encontrar street_name que coincida con 'Chestnut'"** → filtrar donde `payload.index_name = 'street_name'`
#### Arquitectura
Los incrustados de filas siguen el **patrón de dos etapas** utilizado por GraphRAG (incrustados de grafos, incrustados de documentos):
**Etapa 1: Cálculo de incrustados** (`trustgraph-flow/trustgraph/embeddings/row_embeddings/`) - Consume `ExtractedObject`, calcula incrustados a través del servicio de incrustados, produce `RowEmbeddings`
**Etapa 2: Almacenamiento de incrustados** (`trustgraph-flow/trustgraph/storage/row_embeddings/qdrant/`) - Consume `RowEmbeddings`, escribe vectores en Qdrant
El escritor de filas de Cassandra es un consumidor paralelo separado:
**Escritor de filas de Cassandra** (`trustgraph-flow/trustgraph/storage/rows/cassandra`) - Consume `ExtractedObject`, escribe filas en Cassandra
Los tres servicios consumen del mismo flujo, manteniéndolos desacoplados. Esto permite:
Escalado independiente de las escrituras de Cassandra frente a la generación de incrustados frente al almacenamiento de vectores
Los servicios de incrustados se pueden desactivar si no son necesarios
Las fallas en un servicio no afectan a los demás
Arquitectura consistente con las canalizaciones de GraphRAG
#### Ruta de Escritura
**Etapa 1 (procesador de incrustados de filas):** Al recibir un `ExtractedObject`:
1. Buscar el esquema para encontrar campos indexados
2. Para cada campo indexado:
Construir la representación de texto del valor del índice
Calcular el incrustado a través del servicio de incrustados
3. Producir un mensaje `RowEmbeddings` que contenga todos los vectores calculados
**Etapa 2 (escritura de incrustados de filas en Qdrant):** Al recibir un `RowEmbeddings`:
1. Para cada incrustado en el mensaje:
Determinar la colección de Qdrant a partir de `(user, collection, schema_name, dimension)`
Crear la colección si es necesario (creación perezosa en la primera escritura)
Insertar el punto con el vector y la carga útil
#### Tipos de Mensajes
```python
@dataclass
class RowIndexEmbedding:
index_name: str # The indexed field name(s)
index_value: list[str] # The field value(s)
text: str # Text that was embedded
vectors: list[list[float]] # Computed embedding vectors
@dataclass
class RowEmbeddings:
metadata: Metadata
schema_name: str
embeddings: list[RowIndexEmbedding]
```
#### Integración de Eliminación
Las colecciones de Qdrant se descubren mediante la comparación de prefijos en el patrón de nombre de la colección:
**Eliminar `(user, collection)`:**
1. Listar todas las colecciones de Qdrant que coincidan con el prefijo `rows_{user}_{collection}_`
2. Eliminar cada colección que coincida
3. Eliminar particiones de filas de Cassandra (como se documenta anteriormente)
4. Limpiar las entradas de `row_partitions`
**Eliminar `(user, collection, schema_name)`:**
1. Listar todas las colecciones de Qdrant que coincidan con el prefijo `rows_{user}_{collection}_{schema_name}_`
2. Eliminar cada colección que coincida (maneja múltiples dimensiones)
3. Eliminar particiones de filas de Cassandra
4. Limpiar `row_partitions`
#### Ubicaciones de los Módulos
| Etapa | Módulo | Punto de Entrada |
|-------|--------|-------------|
| Etapa 1 | `trustgraph-flow/trustgraph/embeddings/row_embeddings/` | `row-embeddings` |
| Etapa 2 | `trustgraph-flow/trustgraph/storage/row_embeddings/qdrant/` | `row-embeddings-write-qdrant` |
### API de Consulta de Incrustaciones de Filas
La consulta de incrustaciones de filas es una **API separada** del servicio de consulta de filas GraphQL:
| API | Propósito | Backend |
|-----|---------|---------|
| Consulta de Filas (GraphQL) | Coincidencia exacta en campos indexados | Cassandra |
| Consulta de Incrustaciones de Filas | Coincidencia difusa/semántica | Qdrant |
Esta separación mantiene la claridad:
El servicio GraphQL se enfoca en consultas exactas y estructuradas
La API de incrustaciones maneja la similitud semántica
Flujo de trabajo del usuario: búsqueda difusa a través de incrustaciones para encontrar candidatos, luego una consulta exacta para obtener los datos completos de la fila
#### Esquema de Solicitud/Respuesta
```python
@dataclass
class RowEmbeddingsRequest:
vectors: list[list[float]] # Query vectors (pre-computed embeddings)
user: str = ""
collection: str = ""
schema_name: str = ""
index_name: str = "" # Optional: filter to specific index
limit: int = 10 # Max results per vector
@dataclass
class RowIndexMatch:
index_name: str = "" # The matched index field(s)
index_value: list[str] = [] # The matched value(s)
text: str = "" # Original text that was embedded
score: float = 0.0 # Similarity score
@dataclass
class RowEmbeddingsResponse:
error: Error | None = None
matches: list[RowIndexMatch] = []
```
#### Procesador de Consultas
Módulo: `trustgraph-flow/trustgraph/query/row_embeddings/qdrant`
Punto de entrada: `row-embeddings-query-qdrant`
El procesador:
1. Recibe `RowEmbeddingsRequest` con vectores de consulta
2. Encuentra la colección Qdrant apropiada mediante la coincidencia de prefijos
3. Busca los vectores más cercanos con un filtro opcional `index_name`
4. Devuelve `RowEmbeddingsResponse` con información del índice correspondiente
#### Integración con la API Gateway
La puerta de enlace expone consultas de incrustaciones de filas a través del patrón estándar de solicitud/respuesta:
| Componente | Ubicación |
|-----------|----------|
| Despachador | `trustgraph-flow/trustgraph/gateway/dispatch/row_embeddings_query.py` |
| Registro | Agrega `"row-embeddings"` a `request_response_dispatchers` en `manager.py` |
Nombre de la interfaz de flujo: `row-embeddings`
Definición de la interfaz en el plano de flujo:
```json
{
"interfaces": {
"row-embeddings": {
"request": "non-persistent://tg/request/row-embeddings:{id}",
"response": "non-persistent://tg/response/row-embeddings:{id}"
}
}
}
```
#### Soporte del SDK de Python
El SDK proporciona métodos para consultas de incrustaciones de filas:
```python
# Flow-scoped query (preferred)
api = Api(url)
flow = api.flow().id("default")
# Query with text (SDK computes embeddings)
matches = flow.row_embeddings_query(
text="Chestnut Street",
collection="my_collection",
schema_name="addresses",
index_name="street_name", # Optional filter
limit=10
)
# Query with pre-computed vectors
matches = flow.row_embeddings_query(
vectors=[[0.1, 0.2, ...]],
collection="my_collection",
schema_name="addresses"
)
# Each match contains:
for match in matches:
print(match.index_name) # e.g., "street_name"
print(match.index_value) # e.g., ["CHESTNUT ST"]
print(match.text) # e.g., "CHESTNUT ST"
print(match.score) # e.g., 0.95
```
#### Utilidad de Línea de Comandos (CLI)
Comando: `tg-invoke-row-embeddings`
```bash
# Query by text (computes embedding automatically)
tg-invoke-row-embeddings \
--text "Chestnut Street" \
--collection my_collection \
--schema addresses \
--index street_name \
--limit 10
# Query by vector file
tg-invoke-row-embeddings \
--vectors vectors.json \
--collection my_collection \
--schema addresses
# Output formats
tg-invoke-row-embeddings --text "..." --format json
tg-invoke-row-embeddings --text "..." --format table
```
#### Patrón de uso típico
La consulta de incrustaciones de filas se utiliza típicamente como parte de un flujo de búsqueda difusa a exacta:
```python
# Step 1: Fuzzy search via embeddings
matches = flow.row_embeddings_query(
text="chestnut street",
collection="geo",
schema_name="streets"
)
# Step 2: Exact lookup via GraphQL for full row data
for match in matches:
query = f'''
query {{
streets(where: {{ {match.index_name}: {{ eq: "{match.index_value[0]}" }} }}) {{
street_name
city
zip_code
}}
}}
'''
rows = flow.rows_query(query, collection="geo")
```
Este patrón de dos pasos permite:
Encontrar "CHESTNUT ST" cuando el usuario busca "Chestnut Street"
Recuperar datos de fila completos con todos los campos
Combinar la similitud semántica con el acceso a datos estructurados
### Ingesta de Datos de Fila
Se pospone a una fase posterior. Se diseñará junto con otros cambios de ingesta.
## Impacto en la Implementación
### Análisis del Estado Actual
La implementación existente tiene dos componentes principales:
| Componente | Ubicación | Líneas | Descripción |
|-----------|----------|-------|-------------|
| Servicio de Consulta | `trustgraph-flow/trustgraph/query/objects/cassandra/service.py` | ~740 | Monolítico: generación de esquema GraphQL, análisis de filtros, consultas de Cassandra, manejo de solicitudes |
| Escritor | `trustgraph-flow/trustgraph/storage/objects/cassandra/write.py` | ~540 | Creación de tablas por esquema, índices secundarios, inserción/eliminación |
**Patrón de Consulta Actual:**
```sql
SELECT * FROM {keyspace}.o_{schema_name}
WHERE collection = 'X' AND email = 'foo@bar.com'
ALLOW FILTERING
```
**Nuevo Patrón de Consulta:**
```sql
SELECT * FROM {keyspace}.rows
WHERE collection = 'X' AND schema_name = 'customers'
AND index_name = 'email' AND index_value = ['foo@bar.com']
```
### Cambios Clave
1. **La semántica de las consultas se simplifica**: El nuevo esquema solo admite coincidencias exactas en `index_value`. Los filtros GraphQL actuales (`gt`, `lt`, `contains`, etc.) ya sea:
Se convierten en filtrado posterior de los datos devueltos (si es necesario)
Se eliminan en favor de usar la API de embeddings para la coincidencia difusa
2. **El código GraphQL está fuertemente acoplado**: El código `service.py` actual incluye la generación de tipos de Strawberry, el análisis de filtros y las consultas específicas de Cassandra. Agregar otro backend de almacenamiento de filas duplicaría aproximadamente 400 líneas de código GraphQL.
### Refactorización Propuesta
La refactorización tiene dos partes:
#### 1. Separar el Código GraphQL
Extraer componentes GraphQL reutilizables en un módulo compartido:
```
trustgraph-flow/trustgraph/query/graphql/
├── __init__.py
├── types.py # Filter types (IntFilter, StringFilter, FloatFilter)
├── schema.py # Dynamic schema generation from RowSchema
└── filters.py # Filter parsing utilities
```
Esto permite:
Reutilización en diferentes backends de almacenamiento de datos.
Una separación más clara de responsabilidades.
Pruebas más fáciles de la lógica de GraphQL de forma independiente.
#### 2. Implementar el Nuevo Esquema de Tabla
Refactorizar el código específico de Cassandra para utilizar la tabla unificada:
**Escritor** (`trustgraph-flow/trustgraph/storage/rows/cassandra/`):
Una única tabla `rows` en lugar de tablas por esquema.
Escribir N copias por fila (una por índice).
Registrarse en la tabla `row_partitions`.
Creación de tabla más sencilla (configuración única).
**Servicio de Consulta** (`trustgraph-flow/trustgraph/query/rows/cassandra/`):
Consultar la tabla `rows` unificada.
Utilizar el módulo GraphQL extraído para la generación de esquemas.
Manejo de filtros simplificado (solo coincidencia exacta a nivel de base de datos).
### Cambios de Nombre de Módulos
Como parte de la limpieza de nombres de "objeto" a "fila":
| Actual | Nuevo |
|---------|-----|
| `storage/objects/cassandra/` | `storage/rows/cassandra/` |
| `query/objects/cassandra/` | `query/rows/cassandra/` |
| `embeddings/object_embeddings/` | `embeddings/row_embeddings/` |
### Nuevos Módulos
| Módulo | Propósito |
|--------|---------|
| `trustgraph-flow/trustgraph/query/graphql/` | Utilidades GraphQL compartidas |
| `trustgraph-flow/trustgraph/query/row_embeddings/qdrant/` | API de consulta de incrustaciones de filas |
| `trustgraph-flow/trustgraph/embeddings/row_embeddings/` | Cálculo de incrustaciones de filas (Etapa 1) |
| `trustgraph-flow/trustgraph/storage/row_embeddings/qdrant/` | Almacenamiento de incrustaciones de filas (Etapa 2) |
## Referencias
[Especificación Técnica de Datos Estructurados](structured-data.md)

View file

@ -0,0 +1,567 @@
---
layout: default
title: "Especificación del Descriptor de Datos Estructurados"
parent: "Spanish (Beta)"
---
# Especificación del Descriptor de Datos Estructurados
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
El Descriptor de Datos Estructurados es un lenguaje de configuración basado en JSON que describe cómo analizar, transformar e importar datos estructurados en TrustGraph. Proporciona un enfoque declarativo para la ingesta de datos, admitiendo múltiples formatos de entrada y complejas canalizaciones de transformación sin requerir código personalizado.
## Conceptos Clave
### 1. Definición de Formato
Describe el tipo de archivo de entrada y las opciones de análisis. Determina qué analizador utilizar y cómo interpretar los datos de origen.
### 2. Mapeo de Campos
Mapea las rutas de origen a los campos de destino con transformaciones. Define cómo fluyen los datos desde las fuentes de entrada a los campos del esquema de salida.
### 3. Canalización de Transformación
Cadena de transformaciones de datos que se pueden aplicar a los valores de los campos, incluyendo:
Limpieza de datos (recorte, normalización)
Conversión de formato (análisis de fechas, conversión de tipos)
Cálculos (aritméticos, manipulación de cadenas)
Consultas (tablas de referencia, sustituciones)
### 4. Reglas de Validación
Comprobaciones de calidad de datos aplicadas para garantizar la integridad de los datos:
Validación de tipo
Comprobaciones de rango
Coincidencia de patrones (expresiones regulares)
Validación de campos obligatorios
Lógica de validación personalizada
### 5. Configuración Global
Configuración que se aplica a todo el proceso de importación:
Tablas de consulta para la enriquecimiento de datos
Variables y constantes globales
Especificaciones de formato de salida
Políticas de manejo de errores
## Estrategia de Implementación
La implementación del importador sigue esta canalización:
1. **Análisis de Configuración** - Carga y valida el descriptor JSON
2. **Inicialización del Analizador** - Carga el analizador apropiado (CSV, XML, JSON, etc.) basado en `format.type`
3. **Aplicación de Preprocesamiento** - Ejecuta filtros y transformaciones globales
4. **Procesamiento de Registros** - Para cada registro de entrada:
Extrae datos utilizando rutas de origen (JSONPath, XPath, nombres de columna)
Aplica transformaciones a nivel de campo en secuencia
Valida los resultados contra las reglas definidas
Aplica valores predeterminados para datos faltantes
5. **Aplicación de Postprocesamiento** - Ejecuta desduplicación, agregación, etc.
6. **Generación de Salida** - Produce datos en el formato de destino especificado
## Soporte de Expresiones de Ruta
Diferentes formatos de entrada utilizan lenguajes de expresiones de ruta apropiados:
**CSV**: Nombres de columna o índices (`"column_name"` o `"[2]"`)
**JSON**: Sintaxis JSONPath (`"$.user.profile.email"`)
**XML**: Expresiones XPath (`"//product[@id='123']/price"`)
**Ancho Fijo**: Nombres de campo de las definiciones de campo
## Beneficios
**Base de Código Única** - Un único importador maneja múltiples formatos de entrada
**Fácil de Usar** - Usuarios no técnicos pueden crear configuraciones
**Reutilizable** - Las configuraciones se pueden compartir y versionar
**Flexible** - Transformaciones complejas sin codificación personalizada
**Robusto** - Validación integrada y manejo de errores integral
**Mantenible** - El enfoque declarativo reduce la complejidad de la implementación
## Especificación del Lenguaje
El Descriptor de Datos Estructurados utiliza un formato de configuración JSON con la siguiente estructura de nivel superior:
```json
{
"version": "1.0",
"metadata": {
"name": "Configuration Name",
"description": "Description of what this config does",
"author": "Author Name",
"created": "2024-01-01T00:00:00Z"
},
"format": { ... },
"globals": { ... },
"preprocessing": [ ... ],
"mappings": [ ... ],
"postprocessing": [ ... ],
"output": { ... }
}
```
### Definición del formato
Describe el formato de los datos de entrada y las opciones de análisis:
```json
{
"format": {
"type": "csv|json|xml|fixed-width|excel|parquet",
"encoding": "utf-8",
"options": {
// Format-specific options
}
}
}
```
#### Opciones de formato CSV
```json
{
"format": {
"type": "csv",
"options": {
"delimiter": ",",
"quote_char": "\"",
"escape_char": "\\",
"skip_rows": 1,
"has_header": true,
"null_values": ["", "NULL", "null", "N/A"]
}
}
}
```
#### Opciones de formato JSON
```json
{
"format": {
"type": "json",
"options": {
"root_path": "$.data",
"array_mode": "records|single",
"flatten": false
}
}
}
```
#### Opciones de formato XML
```json
{
"format": {
"type": "xml",
"options": {
"root_element": "//records/record",
"namespaces": {
"ns": "http://example.com/namespace"
}
}
}
}
```
### Configuración Global
Defina tablas de búsqueda, variables y configuración global:
```json
{
"globals": {
"variables": {
"current_date": "2024-01-01",
"batch_id": "BATCH_001",
"default_confidence": 0.8
},
"lookup_tables": {
"country_codes": {
"US": "United States",
"UK": "United Kingdom",
"CA": "Canada"
},
"status_mapping": {
"1": "active",
"0": "inactive"
}
},
"constants": {
"source_system": "legacy_crm",
"import_type": "full"
}
}
}
```
### Mapeo de campos
Defina cómo los datos de origen se mapean a los campos de destino con transformaciones:
```json
{
"mappings": [
{
"target_field": "person_name",
"source": "$.name",
"transforms": [
{"type": "trim"},
{"type": "title_case"},
{"type": "required"}
],
"validation": [
{"type": "min_length", "value": 2},
{"type": "max_length", "value": 100},
{"type": "pattern", "value": "^[A-Za-z\\s]+$"}
]
},
{
"target_field": "age",
"source": "$.age",
"transforms": [
{"type": "to_int"},
{"type": "default", "value": 0}
],
"validation": [
{"type": "range", "min": 0, "max": 150}
]
},
{
"target_field": "country",
"source": "$.country_code",
"transforms": [
{"type": "lookup", "table": "country_codes"},
{"type": "default", "value": "Unknown"}
]
}
]
}
```
### Tipos de Transformación
Funciones de transformación disponibles:
#### Transformaciones de Cadenas de Texto
```json
{"type": "trim"},
{"type": "upper"},
{"type": "lower"},
{"type": "title_case"},
{"type": "replace", "pattern": "old", "replacement": "new"},
{"type": "regex_replace", "pattern": "\\d+", "replacement": "XXX"},
{"type": "substring", "start": 0, "end": 10},
{"type": "pad_left", "length": 10, "char": "0"}
```
#### Conversiones de tipo
```json
{"type": "to_string"},
{"type": "to_int"},
{"type": "to_float"},
{"type": "to_bool"},
{"type": "to_date", "format": "YYYY-MM-DD"},
{"type": "parse_json"}
```
#### Operaciones de datos
```json
{"type": "default", "value": "default_value"},
{"type": "lookup", "table": "table_name"},
{"type": "concat", "values": ["field1", " - ", "field2"]},
{"type": "calculate", "expression": "${field1} + ${field2}"},
{"type": "conditional", "condition": "${age} > 18", "true_value": "adult", "false_value": "minor"}
```
### Reglas de validación
Comprobaciones de calidad de datos con manejo de errores configurable:
#### Validaciones básicas
```json
{"type": "required"},
{"type": "not_null"},
{"type": "min_length", "value": 5},
{"type": "max_length", "value": 100},
{"type": "range", "min": 0, "max": 1000},
{"type": "pattern", "value": "^[A-Z]{2,3}$"},
{"type": "in_list", "values": ["active", "inactive", "pending"]}
```
#### Validaciones Personalizadas
```json
{
"type": "custom",
"expression": "${age} >= 18 && ${country} == 'US'",
"message": "Must be 18+ and in US"
},
{
"type": "cross_field",
"fields": ["start_date", "end_date"],
"expression": "${start_date} < ${end_date}",
"message": "Start date must be before end date"
}
```
### Preprocesamiento y Postprocesamiento
Operaciones globales aplicadas antes/después del mapeo de campos:
```json
{
"preprocessing": [
{
"type": "filter",
"condition": "${status} != 'deleted'"
},
{
"type": "sort",
"field": "created_date",
"order": "asc"
}
],
"postprocessing": [
{
"type": "deduplicate",
"key_fields": ["email", "phone"]
},
{
"type": "aggregate",
"group_by": ["country"],
"functions": {
"total_count": {"type": "count"},
"avg_age": {"type": "avg", "field": "age"}
}
}
]
}
```
### Configuración de salida
Defina cómo se deben mostrar los datos procesados:
```json
{
"output": {
"format": "trustgraph-objects",
"schema_name": "person",
"options": {
"batch_size": 1000,
"confidence": 0.9,
"source_span_field": "raw_text",
"metadata": {
"source": "crm_import",
"version": "1.0"
}
},
"error_handling": {
"on_validation_error": "skip|fail|log",
"on_transform_error": "skip|fail|default",
"max_errors": 100,
"error_output": "errors.json"
}
}
}
```
## Ejemplo Completo
```json
{
"version": "1.0",
"metadata": {
"name": "Customer Import from CRM CSV",
"description": "Imports customer data from legacy CRM system",
"author": "Data Team",
"created": "2024-01-01T00:00:00Z"
},
"format": {
"type": "csv",
"encoding": "utf-8",
"options": {
"delimiter": ",",
"has_header": true,
"skip_rows": 1
}
},
"globals": {
"variables": {
"import_date": "2024-01-01",
"default_confidence": 0.85
},
"lookup_tables": {
"country_codes": {
"US": "United States",
"CA": "Canada",
"UK": "United Kingdom"
}
}
},
"preprocessing": [
{
"type": "filter",
"condition": "${status} == 'active'"
}
],
"mappings": [
{
"target_field": "full_name",
"source": "customer_name",
"transforms": [
{"type": "trim"},
{"type": "title_case"}
],
"validation": [
{"type": "required"},
{"type": "min_length", "value": 2}
]
},
{
"target_field": "email",
"source": "email_address",
"transforms": [
{"type": "trim"},
{"type": "lower"}
],
"validation": [
{"type": "pattern", "value": "^[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}$"}
]
},
{
"target_field": "age",
"source": "age",
"transforms": [
{"type": "to_int"},
{"type": "default", "value": 0}
],
"validation": [
{"type": "range", "min": 0, "max": 120}
]
},
{
"target_field": "country",
"source": "country_code",
"transforms": [
{"type": "lookup", "table": "country_codes"},
{"type": "default", "value": "Unknown"}
]
}
],
"output": {
"format": "trustgraph-objects",
"schema_name": "customer",
"options": {
"confidence": "${default_confidence}",
"batch_size": 500
},
"error_handling": {
"on_validation_error": "log",
"max_errors": 50
}
}
}
```
## Indicación para el LLM para la Generación de Descriptores
La siguiente indicación se puede utilizar para que un LLM analice datos de muestra y genere una configuración de descriptor:
```
I need you to analyze the provided data sample and create a Structured Data Descriptor configuration in JSON format.
The descriptor should follow this specification:
- version: "1.0"
- metadata: Configuration name, description, author, and creation date
- format: Input format type and parsing options
- globals: Variables, lookup tables, and constants
- preprocessing: Filters and transformations applied before mapping
- mappings: Field-by-field mapping from source to target with transformations and validations
- postprocessing: Operations like deduplication or aggregation
- output: Target format and error handling configuration
ANALYZE THE DATA:
1. Identify the format (CSV, JSON, XML, etc.)
2. Detect delimiters, encodings, and structure
3. Find data types for each field
4. Identify patterns and constraints
5. Look for fields that need cleaning or transformation
6. Find relationships between fields
7. Identify lookup opportunities (codes that map to values)
8. Detect required vs optional fields
CREATE THE DESCRIPTOR:
For each field in the sample data:
- Map it to an appropriate target field name
- Add necessary transformations (trim, case conversion, type casting)
- Include appropriate validations (required, patterns, ranges)
- Set defaults for missing values
Include preprocessing if needed:
- Filters to exclude invalid records
- Sorting requirements
Include postprocessing if beneficial:
- Deduplication on key fields
- Aggregation for summary data
Configure output for TrustGraph:
- format: "trustgraph-objects"
- schema_name: Based on the data entity type
- Appropriate error handling
DATA SAMPLE:
[Insert data sample here]
ADDITIONAL CONTEXT (optional):
- Target schema name: [if known]
- Business rules: [any specific requirements]
- Data quality issues to address: [known problems]
Generate a complete, valid Structured Data Descriptor configuration that will properly import this data into TrustGraph. Include comments explaining key decisions.
```
### Ejemplo de uso (Prompt)
```
I need you to analyze the provided data sample and create a Structured Data Descriptor configuration in JSON format.
[Standard instructions from above...]
DATA SAMPLE:
```csv
CustomerID,Nombre,Email,Edad,País,Estado,Fecha de Inscripción,Total de Compras
1001,"Smith, John",john.smith@email.com,35,US,1,2023-01-15,5420.50
1002,"doe, jane",JANE.DOE@GMAIL.COM,28,CA,1,2023-03-22,3200.00
1003,"Bob Johnson",bob@,62,UK,0,2022-11-01,0
1004,"Alice Chen","alice.chen@company.org",41,US,1,2023-06-10,8900.25
1005,,invalid-email,25,XX,1,2024-01-01,100
```
ADDITIONAL CONTEXT:
- Target schema name: customer
- Business rules: Email should be valid and lowercase, names should be title case
- Data quality issues: Some emails are invalid, some names are missing, country codes need mapping
```
### Solicitud para Analizar Datos Existentes Sin Muestra
```
I need you to help me create a Structured Data Descriptor configuration for importing [data type] data.
The source data has these characteristics:
- Format: [CSV/JSON/XML/etc]
- Fields: [list the fields]
- Data quality issues: [describe any known issues]
- Volume: [approximate number of records]
Requirements:
- [List any specific transformation needs]
- [List any validation requirements]
- [List any business rules]
Please generate a Structured Data Descriptor configuration that will:
1. Parse the input format correctly
2. Clean and standardize the data
3. Validate according to the requirements
4. Handle errors gracefully
5. Output in TrustGraph ExtractedObject format
Focus on making the configuration robust and reusable.
```

View file

@ -0,0 +1,145 @@
---
layout: default
title: "Cambios en el Esquema de Datos Estructurados en Pulsar"
parent: "Spanish (Beta)"
---
# Cambios en el Esquema de Datos Estructurados en Pulsar
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Visión general
Basado en la especificación `STRUCTURED_DATA.md`, este documento propone las adiciones y modificaciones necesarias en el esquema de Pulsar para soportar las capacidades de datos estructurados en TrustGraph.
## Cambios de Esquema Requeridos
### 1. Mejoras en el Esquema Central
#### Definición de Campo Mejorada
La clase `Field` existente en `core/primitives.py` necesita propiedades adicionales:
```python
class Field(Record):
name = String()
type = String() # int, string, long, bool, float, double, timestamp
size = Integer()
primary = Boolean()
description = String()
# CAMPOS NUEVOS:
required = Boolean() # Indica si el campo es obligatorio
enum_values = Array(String()) # Para campos de tipo enum
indexed = Boolean() # Indica si el campo debe ser indexado
```
### 2. Nuevos Esquemas de Conocimiento
#### 2.1 Envío de Datos Estructurados
Nuevo archivo: `knowledge/structured.py`
```python
from pulsar.schema import Record, String, Bytes, Map
from ..core.metadata import Metadata
class StructuredDataSubmission(Record):
metadata = Metadata()
format = String() # "json", "csv", "xml"
schema_name = String() # Referencia al esquema en la configuración
data = Bytes() # Datos brutos para ingerir
options = Map(String()) # Opciones específicas del formato
```
#### 2.2 Nuevo Servicio de Consulta Estructurada
Nuevo archivo: `services/nlp_query.py`
```python
from pulsar.schema import Record, String, Array, Map, Integer, Double
from ..core.primitives import Error
class NLPToStructuredQueryRequest(Record):
natural_language_query = String()
max_results = Integer()
context_hints = Map(String()) # Contexto opcional para la generación de consulta
class NLPToStructuredQueryResponse(Record):
error = Error()
graphql_query = String() # Consulta GraphQL generada
variables = Map(String()) # Variables de GraphQL si existen
detected_schemas = Array(String()) # Esquemas a los que apunta la consulta
confidence = Double()
```
#### 2.2 Servicio de Consulta Estructurada
Nuevo archivo: `services/structured_query.py`
```python
from pulsar.schema import Record, String, Map, Array
from ..core.primitives import Error
class StructuredQueryRequest(Record):
query = String() # Consulta GraphQL
variables = Map(String()) # Variables de GraphQL
operation_name = String() # Nombre opcional de operación para documentos con múltiples operaciones
class StructuredQueryResponse(Record):
error = Error()
data = String() # Datos de respuesta GraphQL codificados en JSON
errors = Array(String()) # Errores GraphQL si existen
```
#### 2.2 Salida de Extracción de Objetos
Nuevo archivo: `knowledge/object.py`
```python
from pulsar.schema import Record, String, Map, Double
from ..core.metadata import Metadata
class ExtractedObject(Record):
metadata = Metadata()
schema_name = String() # Esquema al que pertenece este objeto
values = Map(String()) # Nombre del campo -> valor
confidence = Double()
source_span = String() # Rango de texto donde se encontró el objeto
```
### 4. Esquemas de Conocimiento Mejorados
#### 4.1 Mejora en la Incorporación de Objetos
Actualiza `knowledge/embeddings.py` para soportar una mejor incorporación de objetos estructurados:
```python
class StructuredObjectEmbedding(Record):
metadata = Metadata()
vectors = Array(Array(Double()))
schema_name = String()
object_id = String() # Valor clave primaria
field_embeddings = Map(Array(Double())) # Incorporaciones por campo
```
## Puntos de Integración
### Integración de Flujo
Los esquemas se utilizarán en nuevos módulos de flujo:
- `trustgraph-flow/trustgraph/decoding/structured` - Utiliza StructuredDataSubmission
- `trustgraph-flow/trustgraph/query/nlp_query/cassandra` - Utiliza esquemas de consulta NLP
- `trustgraph-flow/trustgraph/query/objects/cassandra` - Utiliza esquemas de consulta estructurada
- `trustgraph-flow/trustgraph/extract/object/row/` - Consumo de Chunk, produce ExtractedObject
- `trustgraph-flow/trustgraph/storage/objects/cassandra` - Utiliza Esquema de Filas
- `trustgraph-flow/trustgraph/embeddings/object_embeddings/qdrant` - Utiliza esquemas de incorporación de objetos
## Notas de Implementación
1. **Versionado del Esquema**: Considerar añadir un campo `version` al Esquema de Fila para soporte de migración futuro.
2. **Sistema de Tipos**: El `Field.type` debería soportar todos los tipos nativos de Cassandra.
3. **Operaciones en Lotes**: La mayoría de los servicios deben soportar tanto operaciones individuales como en lote.
4. **Manejo de Errores**: Reporte de errores consistente en todos los nuevos servicios.
5. **Compatibilidad hacia atrás**: Los esquemas existentes permanecen sin cambios, excepto por las pequeñas mejoras en los Campos.
## Próximos Pasos
1. Implementar los archivos de esquema en la nueva estructura.
2. Actualizar los servicios existentes para reconocer los nuevos tipos de esquema.
3. Implementar los módulos de flujo que utilicen estos esquemas.
4. Añadir puntos finales gateway/rev-gateway para los nuevos servicios.
5. Crear pruebas unitarias para la validación del esquema.

View file

@ -0,0 +1,260 @@
---
layout: default
title: "Especificación Técnica de Datos Estructurados"
parent: "Spanish (Beta)"
---
# Especificación Técnica de Datos Estructurados
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Descripción General
Esta especificación describe la integración de TrustGraph con flujos de datos estructurados, lo que permite que el sistema trabaje con datos que se pueden representar como filas en tablas u objetos en almacenes de objetos. La integración admite cuatro casos de uso principales:
1. **Extracción de Datos No Estructurados a Estructurados**: Leer fuentes de datos no estructurados, identificar y extraer estructuras de objetos y almacenarlas en un formato tabular.
2. **Ingesta de Datos Estructurados**: Cargar datos que ya están en formatos estructurados directamente en el almacén estructurado junto con los datos extraídos.
3. **Consultas en Lenguaje Natural**: Convertir preguntas en lenguaje natural en consultas estructuradas para extraer datos coincidentes del almacén.
4. **Consultas Estructuradas Directas**: Ejecutar consultas estructuradas directamente contra el almacén de datos para una recuperación de datos precisa.
## Objetivos
**Acceso Unificado a Datos**: Proporcionar una única interfaz para acceder tanto a datos estructurados como no estructurados dentro de TrustGraph.
**Integración Fluida**: Permitir una interoperabilidad fluida entre la representación de conocimiento basada en gráficos de TrustGraph y los formatos de datos estructurados tradicionales.
**Extracción Flexible**: Admitir la extracción automática de datos estructurados de diversas fuentes no estructuradas (documentos, texto, etc.).
**Versatilidad de Consulta**: Permitir a los usuarios consultar datos utilizando tanto lenguaje natural como lenguajes de consulta estructurados.
**Consistencia de Datos**: Mantener la integridad y la consistencia de los datos en diferentes representaciones de datos.
**Optimización del Rendimiento**: Garantizar el almacenamiento y la recuperación eficientes de datos estructurados a escala.
**Flexibilidad del Esquema**: Admitir enfoques de esquema-en-escritura y esquema-en-lectura para adaptarse a diversas fuentes de datos.
**Compatibilidad con Versiones Anteriores**: Preservar la funcionalidad existente de TrustGraph al agregar capacidades de datos estructurados.
## Antecedentes
Actualmente, TrustGraph destaca en el procesamiento de datos no estructurados y en la creación de gráficos de conocimiento a partir de diversas fuentes. Sin embargo, muchos casos de uso empresariales implican datos que son inherentemente estructurados: registros de clientes, registros de transacciones, bases de datos de inventario y otros conjuntos de datos tabulares. Estos conjuntos de datos estructurados a menudo deben analizarse junto con contenido no estructurado para proporcionar información integral.
Las limitaciones actuales incluyen:
No hay soporte nativo para la ingesta de formatos de datos preestructurados (CSV, matrices JSON, exportaciones de bases de datos).
Incapacidad para preservar la estructura inherente al extraer datos tabulares de documentos.
Falta de mecanismos de consulta eficientes para patrones de datos estructurados.
Falta de un puente entre las consultas tipo SQL y las consultas de gráficos de TrustGraph.
Esta especificación aborda estas deficiencias mediante la introducción de una capa de datos estructurados que complementa las capacidades existentes de TrustGraph. Al admitir datos estructurados de forma nativa, TrustGraph puede:
Servir como una plataforma unificada para el análisis tanto de datos estructurados como no estructurados.
Permitir consultas híbridas que abarquen tanto las relaciones de gráficos como los datos tabulares.
Proporcionar interfaces familiares para los usuarios acostumbrados a trabajar con datos estructurados.
Desbloquear nuevos casos de uso en la integración de datos y la inteligencia empresarial.
## Diseño Técnico
### Arquitectura
La integración de datos estructurados requiere los siguientes componentes técnicos:
1. **Servicio de Conversión de Lenguaje Natural a Consulta Estructurada**
Convierte preguntas en lenguaje natural en consultas estructuradas.
Admite múltiples objetivos de lenguaje de consulta (inicialmente sintaxis tipo SQL).
Se integra con las capacidades existentes de NLP de TrustGraph.
Módulo: trustgraph-flow/trustgraph/query/nlp_query/cassandra
2. **Soporte de Esquema de Configuración****[COMPLETO]**
Sistema de configuración extendido para almacenar esquemas de datos estructurados.
Soporte para definir estructuras de tablas, tipos de campos y relaciones.
Capacidades de versionado y migración de esquemas.
3. **Módulo de Extracción de Objetos****[COMPLETO]**
Integración mejorada del flujo de extracción de conocimiento.
Identifica y extrae objetos estructurados de fuentes no estructuradas.
Mantiene el origen y las puntuaciones de confianza.
Registra un controlador de configuración (ejemplo: trustgraph-flow/trustgraph/prompt/template/service.py) para recibir datos de configuración y decodificar información del esquema.
Recibe objetos y los decodifica en objetos ExtractedObject para su entrega en la cola de Pulsar.
NOTA: Existe código en `trustgraph-flow/trustgraph/extract/object/row/`. Este fue un intento anterior y deberá refactorizarse por completo, ya que no se ajusta a las API actuales. Úselo si es útil, comience desde cero si no.
Requiere una interfaz de línea de comandos: `kg-extract-objects`
Módulo: trustgraph-flow/trustgraph/extract/kg/objects/
4. **Módulo de Escritura de Almacén Estructurado****[COMPLETO]**
Recibe objetos en formato ExtractedObject de las colas de Pulsar.
Implementación inicial dirigida a Apache Cassandra como el almacén de datos estructurados.
Maneja la creación dinámica de tablas basada en los esquemas encontrados.
Administra el mapeo de esquemas a tablas de Cassandra y la transformación de datos.
Proporciona operaciones de escritura por lotes y en streaming para la optimización del rendimiento.
No hay salidas de Pulsar: este es un servicio terminal en el flujo de datos.
**Manejo de Esquemas**:
Supervisa los mensajes ExtractedObject entrantes en busca de referencias de esquema.
Cuando se encuentra un nuevo esquema por primera vez, crea automáticamente la tabla correspondiente en Cassandra.
Mantiene una caché de esquemas conocidos para evitar intentos redundantes de creación de tablas.
Se debe considerar si se reciben definiciones de esquema directamente o si se confía en los nombres de esquema en los mensajes ExtractedObject.
**Mapeo de Tablas de Cassandra**:
El espacio de claves tiene el nombre derivado del campo `user` del campo de metadatos de ExtractedObject.
La tabla tiene el nombre derivado del campo `schema_name` de ExtractedObject.
La colección del metadato se convierte en parte de la clave de partición para garantizar:
Distribución de datos natural en los nodos de Cassandra.
Consultas eficientes dentro de una colección específica.
Aislamiento lógico entre diferentes importaciones de datos/fuentes.
Estructura de la clave primaria: `PRIMARY KEY ((collection, <schema_primary_key_fields>), <clustering_keys>)`
La colección siempre es el primer componente de la clave de partición.
Los campos de la clave primaria definidos en el esquema siguen como parte de la clave de partición compuesta.
Esto requiere que las consultas especifiquen la colección, lo que garantiza un rendimiento predecible.
Las definiciones de campos se mapean a columnas de Cassandra con conversiones de tipo:
`string``text`
`integer``int` o `bigint` según la sugerencia de tamaño.
`float``float` o `double` según las necesidades de precisión.
`boolean``boolean`
`timestamp``timestamp`
`enum``text` con validación a nivel de aplicación.
Los campos indexados crean índices secundarios de Cassandra (excluyendo los campos que ya están en la clave primaria).
Los campos obligatorios se imponen a nivel de aplicación (Cassandra no admite NOT NULL).
**Almacenamiento de Objetos**:
Extrae valores del mapa ExtractedObject.values.
Realiza la conversión de tipo y la validación antes de la inserción.
Maneja los campos opcionales faltantes de forma elegante.
Mantiene metadatos sobre el origen del objeto (documento de origen, puntajes de confianza).
Admite escrituras idempotentes para manejar escenarios de retransmisión de mensajes.
**Notas de Implementación**:
El código existente en `trustgraph-flow/trustgraph/storage/objects/cassandra/` está obsoleto y no cumple con las API actuales.
Debe referenciar `trustgraph-flow/trustgraph/storage/triples/cassandra` como un ejemplo de un procesador de almacenamiento que funciona.
Es necesario evaluar el código existente para identificar cualquier componente reutilizable antes de decidir refactorizar o reescribir.
Módulo: trustgraph-flow/trustgraph/storage/objects/cassandra
5. **Servicio de Consulta Estructurada****[COMPLETO]**
Acepta consultas estructuradas en formatos definidos.
Ejecuta consultas contra el almacén estructurado.
Devuelve objetos que coinciden con los criterios de la consulta.
Admite paginación y filtrado de resultados.
Módulo: trustgraph-flow/trustgraph/query/objects/cassandra
6. **Integración de Herramientas de Agente**
Nueva clase de herramienta para marcos de agentes.
Permite que los agentes consulten almacenes de datos estructurados.
Proporciona interfaces de consulta de lenguaje natural y estructuradas.
Se integra con los procesos de toma de decisiones existentes de los agentes.
7. **Servicio de Ingestión de Datos Estructurados**
Acepta datos estructurados en múltiples formatos (JSON, CSV, XML).
Analiza y valida los datos entrantes según los esquemas definidos.
Convierte los datos en flujos de objetos normalizados.
Emite objetos a colas de mensajes apropiadas para su procesamiento.
Admite cargas masivas e ingestión en streaming.
Módulo: trustgraph-flow/trustgraph/decoding/structured
8. **Servicio de Incrustación de Objetos**
Genera incrustaciones vectoriales para objetos estructurados.
Permite la búsqueda semántica en datos estructurados.
Admite la búsqueda híbrida que combina consultas estructuradas con similitud semántica.
Se integra con almacenes de vectores existentes.
Módulo: trustgraph-flow/trustgraph/embeddings/object_embeddings/qdrant
### Modelos de Datos
#### Mecanismo de Almacenamiento de Esquemas
Los esquemas se almacenan en el sistema de configuración de TrustGraph utilizando la siguiente estructura:
**Tipo**: `schema` (valor fijo para todos los esquemas de datos estructurados)
**Clave**: El nombre/identificador único del esquema (por ejemplo, `customer_records`, `transaction_log`)
**Valor**: Definición de esquema JSON que contiene la estructura
Ejemplo de entrada de configuración:
```
Type: schema
Key: customer_records
Value: {
"name": "customer_records",
"description": "Customer information table",
"fields": [
{
"name": "customer_id",
"type": "string",
"primary_key": true
},
{
"name": "name",
"type": "string",
"required": true
},
{
"name": "email",
"type": "string",
"required": true
},
{
"name": "registration_date",
"type": "timestamp"
},
{
"name": "status",
"type": "string",
"enum": ["active", "inactive", "suspended"]
}
],
"indexes": ["email", "registration_date"]
}
```
Este enfoque permite:
Definición dinámica del esquema sin cambios en el código
Actualizaciones y versiones de esquema fáciles
Integración consistente con la gestión de configuración de TrustGraph existente
Soporte para múltiples esquemas dentro de un único despliegue
### APIs
Nuevas APIs:
Esquemas de Pulsar para los tipos anteriores
Interfaces de Pulsar en nuevos flujos
Se necesita un medio para especificar los tipos de esquema en los flujos para que los flujos sepan qué
tipos de esquema cargar
APIs añadidas a la puerta de enlace y a la puerta de enlace de revisión
APIs modificadas:
Puntos finales de extracción de conocimiento: añadir opción de salida de objeto estructurado
Puntos finales de agentes: añadir soporte para herramientas de datos estructurados
### Detalles de implementación
Siguiendo las convenciones existentes: estos son simplemente nuevos módulos de procesamiento.
Todo está en los paquetes de trustgraph-flow, excepto los elementos del esquema
en trustgraph-base.
Se necesita algo de trabajo de interfaz de usuario en el Workbench para poder demostrar / probar
esta funcionalidad.
## Consideraciones de seguridad
No hay consideraciones adicionales.
## Consideraciones de rendimiento
Algunas preguntas sobre el uso de consultas e índices de Cassandra para que las consultas
no ralenticen el sistema.
## Estrategia de pruebas
Utilizar la estrategia de pruebas existente, se crearán pruebas unitarias, de contrato e de integración.
## Plan de migración
Ninguno.
## Cronograma
No especificado.
## Preguntas abiertas
¿Se puede hacer que esto funcione con otros tipos de almacenamiento? Nuestro objetivo es utilizar
interfaces que hagan que los módulos que funcionan con un almacenamiento sean aplicables a
otros almacenamientos.
## Referencias
n/a.

View file

@ -0,0 +1,281 @@
---
layout: default
title: "Especificación Técnica del Servicio de Diagnóstico de Datos Estructurados"
parent: "Spanish (Beta)"
---
# Especificación Técnica del Servicio de Diagnóstico de Datos Estructurados
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Descripción General
Esta especificación describe un nuevo servicio invocable para diagnosticar y analizar datos estructurados dentro de TrustGraph. El servicio extrae la funcionalidad de la herramienta de línea de comandos `tg-load-structured-data` existente y la expone como un servicio de solicitud/respuesta, lo que permite el acceso programático a las capacidades de detección de tipos de datos y generación de descriptores.
El servicio admite tres operaciones principales:
1. **Detección de Tipo de Datos**: Analiza una muestra de datos para determinar su formato (CSV, JSON o XML).
2. **Generación de Descriptores**: Genera un descriptor de datos estructurados de TrustGraph para una muestra de datos y tipo dados.
3. **Diagnóstico Combinado**: Realiza la detección de tipo y la generación de descriptores en secuencia.
## Objetivos
**Modularización del Análisis de Datos**: Extrae la lógica de diagnóstico de datos de la CLI en componentes de servicio reutilizables.
**Habilitar el Acceso Programático**: Proporciona acceso basado en API a las capacidades de análisis de datos.
**Admitir Múltiples Formatos de Datos**: Maneja los formatos de datos CSV, JSON y XML de manera consistente.
**Generar Descriptores Precisos**: Produce descriptores de datos estructurados que mapean con precisión los datos de origen a los esquemas de TrustGraph.
**Mantener la Compatibilidad Inversa**: Garantiza que la funcionalidad existente de la CLI continúe funcionando.
**Habilitar la Composición de Servicios**: Permite que otros servicios aprovechen las capacidades de diagnóstico de datos.
**Mejorar la Capacidad de Pruebas**: Separa la lógica de negocio de la interfaz de la CLI para una mejor prueba.
**Admitir el Análisis por Flujo**: Permite el análisis de muestras de datos sin cargar archivos completos.
## Antecedentes
Actualmente, el comando `tg-load-structured-data` proporciona una funcionalidad completa para analizar datos estructurados y generar descriptores. Sin embargo, esta funcionalidad está estrechamente acoplada a la interfaz de la CLI, lo que limita su reutilización.
Las limitaciones actuales incluyen:
Lógica de diagnóstico de datos incrustada en el código de la CLI.
Sin acceso programático a la detección de tipos y la generación de descriptores.
Difícil de integrar las capacidades de diagnóstico en otros servicios.
Capacidad limitada para componer flujos de trabajo de análisis de datos.
Esta especificación aborda estas deficiencias mediante la creación de un servicio dedicado para el diagnóstico de datos estructurados. Al exponer estas capacidades como un servicio, TrustGraph puede:
Permitir que otros servicios analicen datos de forma programática.
Admitir canalizaciones de procesamiento de datos más complejas.
Facilitar la integración con sistemas externos.
Mejorar la mantenibilidad mediante la separación de responsabilidades.
## Diseño Técnico
### Arquitectura
El servicio de diagnóstico de datos estructurados requiere los siguientes componentes técnicos:
1. **Procesador del Servicio de Diagnóstico**
Maneja las solicitudes de diagnóstico entrantes.
Orquesta la detección de tipos y la generación de descriptores.
Devuelve respuestas estructuradas con los resultados del diagnóstico.
Módulo: `trustgraph-flow/trustgraph/diagnosis/structured_data/service.py`
2. **Detector de Tipo de Datos**
Utiliza la detección algorítmica para identificar el formato de datos (CSV, JSON, XML).
Analiza la estructura de datos, los delimitadores y los patrones de sintaxis.
Devuelve el formato detectado y las puntuaciones de confianza.
Módulo: `trustgraph-flow/trustgraph/diagnosis/structured_data/type_detector.py`
3. **Generador de Descriptores**
Utiliza el servicio de prompts para generar descriptores.
Invoca prompts específicos del formato (diagnose-csv, diagnose-json, diagnose-xml).
Mapea los campos de datos a los campos del esquema de TrustGraph a través de las respuestas del prompt.
Módulo: `trustgraph-flow/trustgraph/diagnosis/structured_data/descriptor_generator.py`
### Modelos de Datos
#### StructuredDataDiagnosisRequest
Mensaje de solicitud para operaciones de diagnóstico de datos estructurados:
```python
class StructuredDataDiagnosisRequest:
operation: str # "detect-type", "generate-descriptor", or "diagnose"
sample: str # Data sample to analyze (text content)
type: Optional[str] # Data type (csv, json, xml) - required for generate-descriptor
schema_name: Optional[str] # Target schema name for descriptor generation
options: Dict[str, Any] # Additional options (e.g., delimiter for CSV)
```
#### RespuestaDiagnósticoDatosEstructurados
Mensaje de respuesta que contiene los resultados del diagnóstico:
```python
class StructuredDataDiagnosisResponse:
operation: str # The operation that was performed
detected_type: Optional[str] # Detected data type (for detect-type/diagnose)
confidence: Optional[float] # Confidence score for type detection
descriptor: Optional[Dict] # Generated descriptor (for generate-descriptor/diagnose)
error: Optional[str] # Error message if operation failed
metadata: Dict[str, Any] # Additional metadata (e.g., field count, sample records)
```
#### Estructura del descriptor
El descriptor generado sigue el formato de descriptor de datos estructurados existente:
```json
{
"format": {
"type": "csv",
"encoding": "utf-8",
"options": {
"delimiter": ",",
"has_header": true
}
},
"mappings": [
{
"source_field": "customer_id",
"target_field": "id",
"transforms": [
{"type": "trim"}
]
}
],
"output": {
"schema_name": "customer",
"options": {
"batch_size": 1000,
"confidence": 0.9
}
}
}
```
### Interfaz de Servicio
El servicio expondrá las siguientes operaciones a través del patrón de solicitud/respuesta:
1. **Operación de Detección de Tipo**
Entrada: Muestra de datos
Procesamiento: Analizar la estructura de datos utilizando la detección algorítmica
Salida: Tipo detectado con una puntuación de confianza
2. **Operación de Generación de Descriptores**
Entrada: Muestra de datos, tipo, nombre del esquema de destino
Procesamiento:
Llamar al servicio de solicitud con el ID de solicitud específico del formato (diagnóstico-csv, diagnóstico-json o diagnóstico-xml)
Pasar la muestra de datos y los esquemas disponibles a la solicitud
Recibir el descriptor generado de la respuesta de la solicitud
Salida: Descriptor de datos estructurados
3. **Operación de Diagnóstico Combinado**
Entrada: Muestra de datos, nombre de esquema opcional
Procesamiento:
Utilizar la detección algorítmica para identificar el formato primero
Seleccionar la solicitud específica del formato basada en el tipo detectado
Llamar al servicio de solicitud para generar el descriptor
Salida: Tanto el tipo detectado como el descriptor
### Detalles de Implementación
El servicio seguirá las convenciones del servicio TrustGraph:
1. **Registro de Servicio**
Registrarse como tipo de servicio `structured-diag`
Utilizar temas estándar de solicitud/respuesta
Implementar la clase base FlowProcessor
Registrar PromptClientSpec para la interacción con el servicio de solicitud
2. **Gestión de la Configuración**
Acceder a las configuraciones del esquema a través del servicio de configuración
Almacenar en caché los esquemas para mejorar el rendimiento
Manejar las actualizaciones de configuración de forma dinámica
3. **Integración de Solicitudes**
Utilizar la infraestructura existente del servicio de solicitud
Llamar al servicio de solicitud con los ID de solicitud específicos del formato:
`diagnose-csv`: Para el análisis de datos CSV
`diagnose-json`: Para el análisis de datos JSON
`diagnose-xml`: Para el análisis de datos XML
Las solicitudes están configuradas en la configuración de la solicitud, no codificadas de forma rígida en el servicio
Pasar los esquemas y las muestras de datos como variables de la solicitud
Analizar las respuestas de la solicitud para extraer los descriptores
4. **Manejo de Errores**
Validar las muestras de datos de entrada
Proporcionar mensajes de error descriptivos
Manejar los datos incorrectos de forma elegante
Manejar las fallas del servicio de solicitud
5. **Muestreo de Datos**
Procesar tamaños de muestra configurables
Manejar los registros incompletos de forma adecuada
Mantener la coherencia del muestreo
### Integración de la API
El servicio se integrará con las API existentes de TrustGraph:
Componentes Modificados:
`tg-load-structured-data` CLI: Refactorizado para utilizar el nuevo servicio para las operaciones de diagnóstico
Flow API: Extendido para admitir solicitudes de diagnóstico de datos estructurados
Nuevos Puntos Finales del Servicio:
`/api/v1/flow/{flow}/diagnose/structured-data`: Punto final de WebSocket para solicitudes de diagnóstico
`/api/v1/diagnose/structured-data`: Punto final REST para el diagnóstico sincrónico
### Flujo de Mensajes
```
Client → Gateway → Structured Diag Service → Config Service (for schemas)
Type Detector (algorithmic)
Prompt Service (diagnose-csv/json/xml)
Descriptor Generator (parses prompt response)
Client ← Gateway ← Structured Diag Service (response)
```
## Consideraciones de seguridad
Validación de entrada para prevenir ataques de inyección
Límites de tamaño en muestras de datos para prevenir DoS
Sanitización de descriptores generados
Control de acceso a través de la autenticación TrustGraph existente
## Consideraciones de rendimiento
Almacenar en caché las definiciones de esquema para reducir las llamadas al servicio de configuración
Limitar los tamaños de muestra para mantener un rendimiento receptivo
Utilizar procesamiento en streaming para grandes muestras de datos
Implementar mecanismos de tiempo de espera para análisis de larga duración
## Estrategia de pruebas
1. **Pruebas unitarias**
Detección de tipo para varios formatos de datos
Precisión de la generación de descriptores
Escenarios de manejo de errores
2. **Pruebas de integración**
Flujo de solicitud/respuesta del servicio
Recuperación y almacenamiento en caché del esquema
Integración de la CLI
3. **Pruebas de rendimiento**
Procesamiento de grandes muestras
Manejo de solicitudes concurrentes
Uso de memoria bajo carga
## Plan de migración
1. **Fase 1**: Implementar el servicio con la funcionalidad principal
2. **Fase 2**: Refactorizar la CLI para que utilice el servicio (mantener la compatibilidad con versiones anteriores)
3. **Fase 3**: Agregar puntos finales de la API REST
4. **Fase 4**: Descartar la lógica integrada de la CLI (con un período de aviso)
## Cronograma
Semana 1-2: Implementar el servicio principal y la detección de tipo
Semana 3-4: Agregar la generación de descriptores y la integración
Semana 5: Pruebas y documentación
Semana 6: Refactorización de la CLI y migración
## Preguntas abiertas
¿Debería el servicio admitir formatos de datos adicionales (por ejemplo, Parquet, Avro)?
¿Cuál debería ser el tamaño máximo de la muestra para el análisis?
¿Deben almacenarse en caché los resultados del diagnóstico para solicitudes repetidas?
¿Cómo debe manejar el servicio los escenarios de esquemas múltiples?
¿Deben los ID de las indicaciones ser parámetros configurables para el servicio?
## Referencias
[Especificación del descriptor de datos estructurados](structured-data-descriptor.md)
[Documentación de carga de datos estructurados](structured-data.md)
`tg-load-structured-data` implementación: `trustgraph-cli/trustgraph/cli/load_structured_data.py`

View file

@ -0,0 +1,499 @@
---
layout: default
title: "Grupo de Herramientas TrustGraph"
parent: "Spanish (Beta)"
---
# Grupo de Herramientas TrustGraph
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Especificación Técnica v1.0
### Resumen Ejecutivo
Esta especificación define un sistema de agrupación de herramientas para agentes de TrustGraph que permite un control preciso sobre qué herramientas están disponibles para solicitudes específicas. El sistema introduce un filtrado de herramientas basado en grupos a través de la configuración y la especificación a nivel de solicitud, lo que permite mejores límites de seguridad, gestión de recursos y partición funcional de las capacidades de los agentes.
### 1. Descripción General
#### 1.1 Declaración del Problema
Actualmente, los agentes de TrustGraph tienen acceso a todas las herramientas configuradas, independientemente del contexto de la solicitud o los requisitos de seguridad. Esto crea varios desafíos:
**Riesgo de Seguridad**: Herramientas sensibles (por ejemplo, modificación de datos) están disponibles incluso para consultas de solo lectura.
**Desperdicio de Recursos**: Se cargan herramientas complejas incluso cuando las consultas simples no las requieren.
**Confusión Funcional**: Los agentes pueden seleccionar herramientas inapropiadas cuando existen alternativas más simples.
**Aislamiento Multi-inquilino**: Diferentes grupos de usuarios necesitan acceso a diferentes conjuntos de herramientas.
#### 1.2 Descripción General de la Solución
El sistema de agrupación de herramientas introduce:
1. **Clasificación por Grupos**: Las herramientas se etiquetan con membresías de grupos durante la configuración.
2. **Filtrado a Nivel de Solicitud**: AgentRequest especifica qué grupos de herramientas están permitidos.
3. **Aplicación en Tiempo de Ejecución**: Los agentes solo tienen acceso a las herramientas que coinciden con los grupos solicitados.
4. **Agrupación Flexible**: Las herramientas pueden pertenecer a múltiples grupos para escenarios complejos.
### 2. Cambios en el Esquema
#### 2.1 Mejora del Esquema de Configuración de Herramientas
La configuración de herramientas existente se mejora con un campo `group`:
**Antes:**
```json
{
"name": "knowledge-query",
"type": "knowledge-query",
"description": "Query the knowledge graph"
}
```
**Después:**
```json
{
"name": "knowledge-query",
"type": "knowledge-query",
"description": "Query the knowledge graph",
"group": ["read-only", "knowledge", "basic"]
}
```
**Especificación del campo de grupo:**
`group`: Array(String) - Lista de grupos a los que pertenece esta herramienta.
**Opcional**: Las herramientas sin campo de grupo pertenecen al grupo "predeterminado".
**Múltiple pertenencia**: Las herramientas pueden pertenecer a múltiples grupos.
**Sensible a mayúsculas y minúsculas**: Los nombres de los grupos deben coincidir exactamente con la cadena.
#### 2.1.2 Mejora de la transición de estado de la herramienta
Las herramientas pueden especificar opcionalmente transiciones de estado y disponibilidad basada en el estado:
```json
{
"name": "knowledge-query",
"type": "knowledge-query",
"description": "Query the knowledge graph",
"group": ["read-only", "knowledge", "basic"],
"state": "analysis",
"available_in_states": ["undefined", "research"]
}
```
**Especificación del campo de estado:**
`state`: String - **Opcional** - Estado al que se debe pasar después de la ejecución exitosa de la herramienta.
`available_in_states`: Array(String) - **Opcional** - Estados en los que esta herramienta está disponible.
**Comportamiento predeterminado**: Las herramientas sin `available_in_states` están disponibles en todos los estados.
**Transición de estado**: Solo ocurre después de la ejecución exitosa de la herramienta.
#### 2.2 Mejora del esquema AgentRequest
El esquema `AgentRequest` en `trustgraph-base/trustgraph/schema/services/agent.py` se ha mejorado:
**AgentRequest actual:**
`question`: String - Consulta del usuario.
`plan`: String - Plan de ejecución (se puede eliminar).
`state`: String - Estado del agente.
`history`: Array(AgentStep) - Historial de ejecución.
**AgentRequest mejorado:**
`question`: String - Consulta del usuario.
`state`: String - Estado de ejecución del agente (ahora se utiliza activamente para el filtrado de herramientas).
`history`: Array(AgentStep) - Historial de ejecución.
`group`: Array(String) - **NUEVO** - Grupos de herramientas permitidos para esta solicitud.
**Cambios en el esquema:**
**Eliminado**: El campo `plan` ya no es necesario y se puede eliminar (originalmente estaba destinado a la especificación de herramientas).
**Añadido**: El campo `group` para la especificación de grupos de herramientas.
**Mejorado**: El campo `state` ahora controla la disponibilidad de herramientas durante la ejecución.
**Comportamientos de los campos:**
**Grupo de campos:**
**Opcional**: Si no se especifica, el valor predeterminado es ["default"].
**Intersección**: Solo las herramientas que coinciden con al menos un grupo especificado están disponibles.
**Arreglo vacío**: No hay herramientas disponibles (el agente solo puede usar el razonamiento interno).
**Comodín**: El grupo especial "*" otorga acceso a todas las herramientas.
**Campo de estado:**
**Opcional**: Si no se especifica, el valor predeterminado es "undefined".
**Filtrado basado en el estado**: Solo las herramientas disponibles en el estado actual son elegibles.
**Estado predeterminado**: El estado "undefined" permite todas las herramientas (sujeto al filtrado de grupos).
**Transiciones de estado**: Las herramientas pueden cambiar de estado después de una ejecución exitosa.
### 3. Ejemplos de grupos personalizados
Las organizaciones pueden definir grupos específicos del dominio:
```json
{
"financial-tools": ["stock-query", "portfolio-analysis"],
"medical-tools": ["diagnosis-assist", "drug-interaction"],
"legal-tools": ["contract-analysis", "case-search"]
}
```
### 4. Detalles de implementación
#### 4.1 Carga y filtrado de herramientas
**Fase de configuración:**
1. Todas las herramientas se cargan desde la configuración con sus asignaciones de grupo.
2. Las herramientas sin grupos explícitos se asignan al grupo "predeterminado".
3. La pertenencia al grupo se valida y se almacena en el registro de herramientas.
**Fase de procesamiento de solicitudes:**
1. La solicitud del agente llega con una especificación de grupo opcional.
2. El agente filtra las herramientas disponibles según la intersección de grupos.
3. Solo las herramientas que coinciden se pasan al contexto de ejecución del agente.
4. El agente opera con el conjunto de herramientas filtrado durante todo el ciclo de vida de la solicitud.
#### 4.2 Lógica de filtrado de herramientas
**Filtrado combinado de grupos y estado:**
```
For each configured tool:
tool_groups = tool.group || ["default"]
tool_states = tool.available_in_states || ["*"] // Available in all states
For each request:
requested_groups = request.group || ["default"]
current_state = request.state || "undefined"
Tool is available if:
// Group filtering
(intersection(tool_groups, requested_groups) is not empty OR "*" in requested_groups)
AND
// State filtering
(current_state in tool_states OR "*" in tool_states)
```
**Lógica de transición de estado:**
```
After successful tool execution:
if tool.state is defined:
next_request.state = tool.state
else:
next_request.state = current_request.state // No change
```
#### 4.3 Puntos de Integración del Agente
**Agente ReAct:**
El filtrado de herramientas se produce en agent_manager.py durante la creación del registro de herramientas.
La lista de herramientas disponibles se filtra tanto por grupo como por estado antes de la generación del plan.
Las transiciones de estado actualizan el campo AgentRequest.state después de la ejecución exitosa de la herramienta.
La siguiente iteración utiliza el estado actualizado para el filtrado de herramientas.
**Agente Basado en la Confianza:**
El filtrado de herramientas se produce en planner.py durante la generación del plan.
La validación de ExecutionStep asegura que solo se utilicen las herramientas elegibles por grupo y estado.
El controlador de flujo hace cumplir la disponibilidad de las herramientas en tiempo de ejecución.
Las transiciones de estado son gestionadas por el Controlador de Flujo entre los pasos.
### 5. Ejemplos de Configuración
#### 5.1 Configuración de Herramientas con Grupos y Estados
```yaml
tool:
knowledge-query:
type: knowledge-query
name: "Knowledge Graph Query"
description: "Query the knowledge graph for entities and relationships"
group: ["read-only", "knowledge", "basic"]
state: "analysis"
available_in_states: ["undefined", "research"]
graph-update:
type: graph-update
name: "Graph Update"
description: "Add or modify entities in the knowledge graph"
group: ["write", "knowledge", "admin"]
available_in_states: ["analysis", "modification"]
text-completion:
type: text-completion
name: "Text Completion"
description: "Generate text using language models"
group: ["read-only", "text", "basic"]
state: "undefined"
# No available_in_states = available in all states
complex-analysis:
type: mcp-tool
name: "Complex Analysis Tool"
description: "Perform complex data analysis"
group: ["advanced", "compute", "expensive"]
state: "results"
available_in_states: ["analysis"]
mcp_tool_id: "analysis-server"
reset-workflow:
type: mcp-tool
name: "Reset Workflow"
description: "Reset to initial state"
group: ["admin"]
state: "undefined"
available_in_states: ["analysis", "results"]
```
#### 5.2 Ejemplos de solicitudes con flujos de trabajo de estado
**Solicitud de investigación inicial:**
```json
{
"question": "What entities are connected to Company X?",
"group": ["read-only", "knowledge"],
"state": "undefined"
}
```
*Herramientas disponibles: knowledge-query, text-completion*
*Después de knowledge-query: estado → "análisis"*
**Fase de análisis:**
```json
{
"question": "Continue analysis based on previous results",
"group": ["advanced", "compute", "write"],
"state": "analysis"
}
```
*Herramientas disponibles: análisis complejo, actualización de gráficos, restablecimiento del flujo de trabajo*
*Después del análisis complejo: estado → "resultados"*
**Fase de resultados:**
```json
{
"question": "What should I do with these results?",
"group": ["admin"],
"state": "results"
}
```
*Herramientas disponibles: reset-workflow solamente*
*Después de reset-workflow: estado → "indefinido"*
**Ejemplo de flujo de trabajo: flujo completo:**
1. **Inicio (indefinido):** Use knowledge-query → transiciones a "análisis"
2. **Estado de análisis:** Use complex-analysis → transiciones a "resultados"
3. **Estado de resultados:** Use reset-workflow → transiciones de vuelta a "indefinido"
4. **De vuelta al inicio:** Todas las herramientas iniciales están disponibles nuevamente
### 6. Consideraciones de seguridad
#### 6.1 Integración de control de acceso
**Filtrado a nivel de puerta de enlace:**
La puerta de enlace puede hacer cumplir las restricciones de grupo basadas en los permisos del usuario
Prevenir la elevación de privilegios a través de la manipulación de solicitudes
El registro de auditoría incluye los grupos de herramientas solicitados y concedidos
**Ejemplo de lógica de la puerta de enlace:**
```
user_permissions = get_user_permissions(request.user_id)
allowed_groups = user_permissions.tool_groups
requested_groups = request.group
# Validate request doesn't exceed permissions
if not is_subset(requested_groups, allowed_groups):
reject_request("Insufficient permissions for requested tool groups")
```
#### 6.2 Auditoría y Monitoreo
**Registro de Auditoría Mejorado:**
Registrar los grupos de herramientas solicitados y el estado inicial por solicitud.
Registrar las transiciones de estado y el uso de herramientas por pertenencia a un grupo.
Monitorear los intentos de acceso no autorizados a grupos y las transiciones de estado inválidas.
Generar alertas sobre patrones de uso de grupos inusuales o flujos de trabajo de estado sospechosos.
### 7. Estrategia de Migración
#### 7.1 Compatibilidad con Versiones Anteriores
**Fase 1: Cambios Aditivos**
Agregar un campo opcional `group` a las configuraciones de las herramientas.
Agregar un campo opcional `group` al esquema de AgentRequest.
Comportamiento predeterminado: Todas las herramientas existentes pertenecen al grupo "predeterminado".
Las solicitudes existentes sin el campo de grupo utilizan el grupo "predeterminado".
**Comportamiento Existente Preservado:**
Las herramientas sin configuración de grupo siguen funcionando (grupo predeterminado).
Las herramientas sin configuración de estado están disponibles en todos los estados.
Las solicitudes sin especificación de grupo acceden a todas las herramientas (grupo predeterminado).
Las solicitudes sin especificación de estado utilizan el estado "indefinido" (todas las herramientas disponibles).
No hay cambios que rompan la compatibilidad con las implementaciones existentes.
### 8. Monitoreo y Observabilidad
#### 8.1 Nuevas Métricas
**Uso de Grupos de Herramientas:**
`agent_tool_group_requests_total` - Contador de solicitudes por grupo.
`agent_tool_group_availability` - Indicador de herramientas disponibles por grupo.
`agent_filtered_tools_count` - Histograma del número de herramientas después del filtrado por grupo + estado.
**Métricas de Flujo de Trabajo de Estado:**
`agent_state_transitions_total` - Contador de transiciones de estado por herramienta.
`agent_workflow_duration_seconds` - Histograma del tiempo empleado en cada estado.
`agent_state_availability` - Indicador de herramientas disponibles por estado.
**Métricas de Seguridad:**
`agent_group_access_denied_total` - Contador de accesos no autorizados a grupos.
`agent_invalid_state_transition_total` - Contador de transiciones de estado inválidas.
`agent_privilege_escalation_attempts_total` - Contador de solicitudes sospechosas.
#### 8.2 Mejoras en el Registro
**Registro de Solicitudes:**
```json
{
"request_id": "req-123",
"requested_groups": ["read-only", "knowledge"],
"initial_state": "undefined",
"state_transitions": [
{"tool": "knowledge-query", "from": "undefined", "to": "analysis", "timestamp": "2024-01-01T10:00:01Z"}
],
"available_tools": ["knowledge-query", "text-completion"],
"filtered_by_group": ["graph-update", "admin-tool"],
"filtered_by_state": [],
"execution_time": "1.2s"
}
```
### 9. Estrategia de Pruebas
#### 9.1 Pruebas Unitarias
**Lógica de Filtrado de Herramientas:**
Cálculos de intersección de grupos de pruebas
Lógica de filtrado basada en el estado de la prueba
Verificar la asignación predeterminada de grupos y estados
Probar el comportamiento de los comodines en los grupos
Validar el manejo de grupos vacíos
Probar escenarios de filtrado combinados de grupos y estados
**Validación de la Configuración:**
Probar la carga de herramientas con varias configuraciones de grupos y estados
Verificar la validación del esquema para especificaciones inválidas de grupos y estados
Probar la compatibilidad con versiones anteriores con las configuraciones existentes
Validar las definiciones y ciclos de transición de estados
#### 9.2 Pruebas de Integración
**Comportamiento del Agente:**
Verificar que los agentes solo vean las herramientas filtradas por grupo y estado
Probar la ejecución de solicitudes con varias combinaciones de grupos
Probar las transiciones de estado durante la ejecución del agente
Validar el manejo de errores cuando no hay herramientas disponibles
Probar el progreso del flujo de trabajo a través de múltiples estados
**Pruebas de Seguridad:**
Probar la prevención de la escalada de privilegios
Verificar la precisión del registro de auditoría
Probar la integración de la puerta de enlace con los permisos de usuario
#### 9.3 Escenarios de Extremo a Extremo
**Uso Multi-inquilino con Flujos de Trabajo de Estado:**
```
Scenario: Different users with different tool access and workflow states
Given: User A has "read-only" permissions, state "undefined"
And: User B has "write" permissions, state "analysis"
When: Both request knowledge operations
Then: User A gets read-only tools available in "undefined" state
And: User B gets write tools available in "analysis" state
And: State transitions are tracked per user session
And: All usage and transitions are properly audited
```
**Progresión del estado del flujo de trabajo:**
```
Scenario: Complete workflow execution
Given: Request with groups ["knowledge", "compute"] and state "undefined"
When: Agent executes knowledge-query tool (transitions to "analysis")
And: Agent executes complex-analysis tool (transitions to "results")
And: Agent executes reset-workflow tool (transitions to "undefined")
Then: Each step has correctly filtered available tools
And: State transitions are logged with timestamps
And: Final state allows initial workflow to repeat
```
### 10. Consideraciones de rendimiento
#### 10.1 Impacto de la carga de herramientas
**Carga de configuración:**
Los metadatos del grupo y el estado se cargan una vez al inicio.
Sobrecarga de memoria mínima por herramienta (campos adicionales).
No hay impacto en el tiempo de inicialización de la herramienta.
**Procesamiento de solicitudes:**
El filtrado combinado de grupo+estado se realiza una vez por solicitud.
Complejidad O(n), donde n = número de herramientas configuradas.
Las transiciones de estado agregan una sobrecarga mínima (asignación de cadenas).
Impacto insignificante para un número típico de herramientas (< 100).
#### 10.2 Estrategias de optimización
**Conjuntos de herramientas precalculados:**
Almacenar en caché los conjuntos de herramientas por combinación de grupo+estado.
Evitar el filtrado repetido para patrones comunes de grupo/estado.
Intercambio entre memoria y computación para combinaciones de uso frecuente.
**Carga diferida:**
Cargar las implementaciones de las herramientas solo cuando sea necesario.
Reducir el tiempo de inicio para implementaciones con muchas herramientas.
Registro dinámico de herramientas basado en los requisitos del grupo.
### 11. Mejoras futuras
#### 11.1 Asignación dinámica de grupos
**Agrupación con conocimiento del contexto:**
Asignar herramientas a grupos según el contexto de la solicitud.
Disponibilidad del grupo basada en el tiempo (solo durante el horario de atención).
Restricciones de grupo basadas en la carga (herramientas costosas durante el bajo uso).
#### 11.2 Jerarquías de grupos
**Estructura de grupo anidada:**
```json
{
"knowledge": {
"read": ["knowledge-query", "entity-search"],
"write": ["graph-update", "entity-create"]
}
}
```
#### 11.3 Recomendaciones de herramientas
**Sugerencias basadas en grupos:**
Sugerir grupos de herramientas óptimos para tipos de solicitud.
Aprender de los patrones de uso para mejorar las recomendaciones.
Proporcionar grupos de respaldo cuando las herramientas preferidas no están disponibles.
### 12. Preguntas abiertas
1. **Validación de grupos**: ¿Deben los nombres de grupo no válidos en las solicitudes causar errores graves o advertencias?
2. **Descubrimiento de grupos**: ¿Debería el sistema proporcionar una API para listar los grupos disponibles y sus herramientas?
3. **Grupos dinámicos**: ¿Deben los grupos ser configurables en tiempo de ejecución o solo al inicio?
4. **Herencia de grupos**: ¿Deben las herramientas heredar grupos de sus categorías o implementaciones principales?
5. **Monitoreo del rendimiento**: ¿Qué métricas adicionales son necesarias para realizar un seguimiento eficaz del uso de herramientas basado en grupos?
### 13. Conclusión
El sistema de grupos de herramientas proporciona:
**Seguridad**: Control de acceso granular sobre las capacidades del agente.
**Rendimiento**: Reducción de la sobrecarga de carga y selección de herramientas.
**Flexibilidad**: Clasificación de herramientas multidimensional.
**Compatibilidad**: Integración perfecta con arquitecturas de agentes existentes.
Este sistema permite que las implementaciones de TrustGraph gestionen mejor el acceso a las herramientas, mejoren los límites de seguridad y optimicen el uso de recursos, al tiempo que mantiene la compatibilidad con versiones anteriores con las configuraciones y solicitudes existentes.

View file

@ -0,0 +1,479 @@
---
layout: default
title: "Servicios de Herramientas: Herramientas de Agente Dinámicamente Conectables"
parent: "Spanish (Beta)"
---
# Servicios de Herramientas: Herramientas de Agente Dinámicamente Conectables
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Estado
Implementado
## Resumen
Esta especificación define un mecanismo para herramientas de agente dinámicamente conectables llamadas "servicios de herramientas". A diferencia de los tipos de herramientas integradas existentes (`KnowledgeQueryImpl`, `McpToolImpl`, etc.), los servicios de herramientas permiten introducir nuevas herramientas mediante:
1. Desplegar un nuevo servicio basado en Pulsar
2. Agregar un descriptor de configuración que le indique al agente cómo invocarlo
Esto permite la extensibilidad sin modificar el marco de respuesta del agente principal.
## Terminología
| Término | Definición |
|------|------------|
| **Herramienta Integrada** | Tipos de herramientas existentes con implementaciones codificadas en `tools.py` |
| **Servicio de Herramienta** | Un servicio de Pulsar que se puede invocar como una herramienta de agente, definido por un descriptor de servicio |
| **Herramienta** | Una instancia configurada que hace referencia a un servicio de herramienta, expuesta al agente/LLM |
Este es un modelo de dos niveles, análogo a las herramientas MCP:
MCP: El servidor MCP define la interfaz de la herramienta → La configuración de la herramienta la referencia
Servicios de Herramienta: El servicio de herramienta define la interfaz de Pulsar → La configuración de la herramienta la referencia
## Antecedentes: Herramientas Existentes
### Implementación de Herramienta Integrada
Las herramientas se definen actualmente en `trustgraph-flow/trustgraph/agent/react/tools.py` con implementaciones tipadas:
```python
class KnowledgeQueryImpl:
async def invoke(self, question):
client = self.context("graph-rag-request")
return await client.rag(question, self.collection)
```
Cada tipo de herramienta:
Tiene un servicio Pulsar predefinido al que llama (por ejemplo, `graph-rag-request`)
Conoce el método exacto para llamar al cliente (por ejemplo, `client.rag()`)
Tiene argumentos tipados definidos en la implementación
### Registro de herramientas (service.py:105-214)
Las herramientas se cargan desde la configuración con un campo `type` que se mapea a una implementación:
```python
if impl_id == "knowledge-query":
impl = functools.partial(KnowledgeQueryImpl, collection=data.get("collection"))
elif impl_id == "text-completion":
impl = TextCompletionImpl
# ... etc
```
## Arquitectura
### Modelo de Dos Capas
#### Capa 1: Descriptor del Servicio de Herramienta
Un servicio de herramienta define una interfaz de servicio de Pulsar. Declara:
Las colas de Pulsar para solicitud/respuesta
Los parámetros de configuración que requiere de las herramientas que lo utilizan
```json
{
"id": "custom-rag",
"request-queue": "non-persistent://tg/request/custom-rag",
"response-queue": "non-persistent://tg/response/custom-rag",
"config-params": [
{"name": "collection", "required": true}
]
}
```
Un servicio de herramienta que no necesita parámetros de configuración:
```json
{
"id": "calculator",
"request-queue": "non-persistent://tg/request/calc",
"response-queue": "non-persistent://tg/response/calc",
"config-params": []
}
```
#### Nivel 2: Descriptor de la herramienta
Una herramienta hace referencia a un servicio de herramienta y proporciona:
Valores de parámetros de configuración (que satisfacen los requisitos del servicio)
Metadatos de la herramienta para el agente (nombre, descripción)
Definiciones de argumentos para el LLM
```json
{
"type": "tool-service",
"name": "query-customers",
"description": "Query the customer knowledge base",
"service": "custom-rag",
"collection": "customers",
"arguments": [
{
"name": "question",
"type": "string",
"description": "The question to ask about customers"
}
]
}
```
Múltiples herramientas pueden referenciar el mismo servicio con diferentes configuraciones:
```json
{
"type": "tool-service",
"name": "query-products",
"description": "Query the product knowledge base",
"service": "custom-rag",
"collection": "products",
"arguments": [
{
"name": "question",
"type": "string",
"description": "The question to ask about products"
}
]
}
```
### Formato de solicitud
Cuando se invoca una herramienta, la solicitud al servicio de la herramienta incluye:
`user`: Desde la solicitud del agente (multitenencia)
`config`: Valores de configuración codificados en JSON provenientes de la descripción de la herramienta
`arguments`: Argumentos codificados en JSON provenientes del LLM
```json
{
"user": "alice",
"config": "{\"collection\": \"customers\"}",
"arguments": "{\"question\": \"What are the top customer complaints?\"}"
}
```
El servicio de herramientas recibe esto como diccionarios analizados en el método `invoke`.
### Implementación Genérica del Servicio de Herramientas
Una clase `ToolServiceImpl` invoca servicios de herramientas basándose en la configuración:
```python
class ToolServiceImpl:
def __init__(self, context, request_queue, response_queue, config_values, arguments, processor):
self.request_queue = request_queue
self.response_queue = response_queue
self.config_values = config_values # e.g., {"collection": "customers"}
# ...
async def invoke(self, **arguments):
client = await self._get_or_create_client()
response = await client.call(user, self.config_values, arguments)
if isinstance(response, str):
return response
else:
return json.dumps(response)
```
## Decisiones de Diseño
### Modelo de Configuración de Dos Niveles
Los servicios de herramientas siguen un modelo de dos niveles similar a las herramientas de MCP:
1. **Servicio de Herramienta**: Define la interfaz del servicio de Pulsar (tema, parámetros de configuración requeridos)
2. **Herramienta**: Hace referencia a un servicio de herramienta, proporciona valores de configuración, define argumentos de LLM
Esta separación permite:
Que un servicio de herramienta sea utilizado por múltiples herramientas con diferentes configuraciones
Una distinción clara entre la interfaz del servicio y la configuración de la herramienta
La reutilización de las definiciones de servicio
### Mapeo de Solicitudes: Transmisión con Sobre
La solicitud a un servicio de herramienta es un sobre estructurado que contiene:
`user`: Propagado desde la solicitud del agente para la multi-inquilinato
Valores de configuración: Del descriptor de la herramienta (por ejemplo, `collection`)
`arguments`: Argumentos proporcionados por el LLM, transmitidos como un diccionario
El administrador del agente analiza la respuesta del LLM en `act.arguments` como un diccionario (`agent_manager.py:117-154`). Este diccionario se incluye en el sobre de la solicitud.
### Manejo de Esquemas: No Tipado
Las solicitudes y las respuestas utilizan diccionarios no tipados. No hay validación de esquema a nivel del agente; el servicio de herramienta es responsable de validar sus entradas. Esto proporciona la máxima flexibilidad para definir nuevos servicios.
### Interfaz de Cliente: Temas Directos de Pulsar
Los servicios de herramientas utilizan temas directos de Pulsar sin requerir configuración de flujo. El descriptor del servicio de herramienta especifica los nombres completos de las colas:
```json
{
"id": "joke-service",
"request-queue": "non-persistent://tg/request/joke",
"response-queue": "non-persistent://tg/response/joke",
"config-params": [...]
}
```
Esto permite que los servicios se alojen en cualquier espacio de nombres.
### Manejo de errores: Convención de error estándar
Las respuestas del servicio de herramientas siguen la convención de esquema existente con un campo `error`:
```python
@dataclass
class Error:
type: str = ""
message: str = ""
```
Estructura de la respuesta:
Éxito: `error` es `None`, la respuesta contiene el resultado
Error: `error` se completa con `type` y `message`
Esto coincide con el patrón utilizado en todo el esquema de servicios existente (por ejemplo, `PromptResponse`, `QueryResponse`, `AgentResponse`).
### Correlación de solicitudes/respuestas
Las solicitudes y las respuestas se correlacionan mediante un `id` en las propiedades del mensaje de Pulsar:
La solicitud incluye `id` en las propiedades: `properties={"id": id}`
La(s) respuesta(s) incluyen el mismo `id`: `properties={"id": id}`
Esto sigue el patrón existente utilizado en todo el código base (por ejemplo, `agent_service.py`, `llm_service.py`).
### Soporte de transmisión
Los servicios de herramientas pueden devolver respuestas de transmisión:
Múltiples mensajes de respuesta con el mismo `id` en las propiedades
Cada respuesta incluye el campo `end_of_stream: bool`
La respuesta final tiene `end_of_stream: True`
Esto coincide con el patrón utilizado en `AgentResponse` y otros servicios de transmisión.
### Manejo de la respuesta: retorno de cadena
Todas las herramientas existentes siguen el mismo patrón: **recibir argumentos como un diccionario, devolver la observación como una cadena**.
| Herramienta | Manejo de la respuesta |
|------|------------------|
| `KnowledgeQueryImpl` | Devuelve `client.rag()` directamente (cadena) |
| `TextCompletionImpl` | Devuelve `client.question()` directamente (cadena) |
| `McpToolImpl` | Devuelve una cadena, o `json.dumps(output)` si no es una cadena |
| `StructuredQueryImpl` | Formatea el resultado a una cadena |
| `PromptImpl` | Devuelve `client.prompt()` directamente (cadena) |
Los servicios de herramientas siguen el mismo contrato:
El servicio devuelve una respuesta de cadena (la observación)
Si la respuesta no es una cadena, se convierte mediante `json.dumps()`
No se necesita ninguna configuración de extracción en el descriptor
Esto mantiene el descriptor simple y delega la responsabilidad de devolver una respuesta de texto adecuada al agente en el servicio.
## Guía de configuración
Para agregar un nuevo servicio de herramientas, se requieren dos elementos de configuración:
### 1. Configuración del servicio de herramientas
Se almacena bajo la clave de configuración `tool-service`. Define las colas de Pulsar y los parámetros de configuración disponibles.
| Campo | Requerido | Descripción |
|-------|----------|-------------|
| `id` | Sí | Identificador único para el servicio de herramientas |
| `request-queue` | Sí | Tema de Pulsar completo para las solicitudes (por ejemplo, `non-persistent://tg/request/joke`) |
| `response-queue` | Sí | Tema de Pulsar completo para las respuestas (por ejemplo, `non-persistent://tg/response/joke`) |
| `config-params` | No | Matriz de parámetros de configuración que el servicio acepta |
Cada parámetro de configuración puede especificar:
`name`: Nombre del parámetro (obligatorio)
`required`: Si el parámetro debe ser proporcionado por las herramientas (por defecto: falso)
Ejemplo:
```json
{
"id": "joke-service",
"request-queue": "non-persistent://tg/request/joke",
"response-queue": "non-persistent://tg/response/joke",
"config-params": [
{"name": "style", "required": false}
]
}
```
### 2. Configuración de la herramienta
Almacenado bajo la clave de configuración `tool`. Define una herramienta que el agente puede utilizar.
| Campo | Requerido | Descripción |
|-------|----------|-------------|
| `type` | Sí | Debe ser `"tool-service"` |
| `name` | Sí | Nombre de la herramienta expuesto al LLM |
| `description` | Sí | Descripción de lo que hace la herramienta (mostrada al LLM) |
| `service` | Sí | ID del servicio de herramientas a invocar |
| `arguments` | No | Matriz de definiciones de argumentos para el LLM |
| *(parámetros de configuración)* | Varía | Cualquier parámetro de configuración definido por el servicio |
Cada argumento puede especificar:
`name`: Nombre del argumento (requerido)
`type`: Tipo de datos, por ejemplo, `"string"` (requerido)
`description`: Descripción mostrada al LLM (requerido)
Ejemplo:
```json
{
"type": "tool-service",
"name": "tell-joke",
"description": "Tell a joke on a given topic",
"service": "joke-service",
"style": "pun",
"arguments": [
{
"name": "topic",
"type": "string",
"description": "The topic for the joke (e.g., programming, animals, food)"
}
]
}
```
### Carga de la configuración
Utilice `tg-put-config-item` para cargar configuraciones:
```bash
# Load tool-service config
tg-put-config-item tool-service/joke-service < joke-service.json
# Load tool config
tg-put-config-item tool/tell-joke < tell-joke.json
```
El agente-administrador debe reiniciarse para cargar nuevas configuraciones.
## Detalles de implementación
### Esquema
Tipos de solicitud y respuesta en `trustgraph-base/trustgraph/schema/services/tool_service.py`:
```python
@dataclass
class ToolServiceRequest:
user: str = "" # User context for multi-tenancy
config: str = "" # JSON-encoded config values from tool descriptor
arguments: str = "" # JSON-encoded arguments from LLM
@dataclass
class ToolServiceResponse:
error: Error | None = None
response: str = "" # String response (the observation)
end_of_stream: bool = False
```
### Servidor: Servicio DynamicToolService
Clase base en `trustgraph-base/trustgraph/base/dynamic_tool_service.py`:
```python
class DynamicToolService(AsyncProcessor):
"""Base class for implementing tool services."""
def __init__(self, **params):
topic = params.get("topic", default_topic)
# Constructs topics: non-persistent://tg/request/{topic}, non-persistent://tg/response/{topic}
# Sets up Consumer and Producer
async def invoke(self, user, config, arguments):
"""Override this method to implement the tool's logic."""
raise NotImplementedError()
```
### Cliente: ToolServiceImpl
Implementación en `trustgraph-flow/trustgraph/agent/react/tools.py`:
```python
class ToolServiceImpl:
def __init__(self, context, request_queue, response_queue, config_values, arguments, processor):
# Uses the provided queue paths directly
# Creates ToolServiceClient on first use
async def invoke(self, **arguments):
client = await self._get_or_create_client()
response = await client.call(user, config_values, arguments)
return response if isinstance(response, str) else json.dumps(response)
```
### Archivos
| Archivo | Propósito |
|------|---------|
| `trustgraph-base/trustgraph/schema/services/tool_service.py` | Esquemas de solicitud/respuesta |
| `trustgraph-base/trustgraph/base/tool_service_client.py` | Cliente para invocar servicios |
| `trustgraph-base/trustgraph/base/dynamic_tool_service.py` | Clase base para la implementación de servicios |
| `trustgraph-flow/trustgraph/agent/react/tools.py` | Clase `ToolServiceImpl` |
| `trustgraph-flow/trustgraph/agent/react/service.py` | Carga de configuración |
### Ejemplo: Servicio de Chistes
Un ejemplo de servicio en `trustgraph-flow/trustgraph/tool_service/joke/`:
```python
class Processor(DynamicToolService):
async def invoke(self, user, config, arguments):
style = config.get("style", "pun")
topic = arguments.get("topic", "")
joke = pick_joke(topic, style)
return f"Hey {user}! Here's a {style} for you:\n\n{joke}"
```
Configuración del servicio de herramientas:
```json
{
"id": "joke-service",
"request-queue": "non-persistent://tg/request/joke",
"response-queue": "non-persistent://tg/response/joke",
"config-params": [{"name": "style", "required": false}]
}
```
Configuración de la herramienta:
```json
{
"type": "tool-service",
"name": "tell-joke",
"description": "Tell a joke on a given topic",
"service": "joke-service",
"style": "pun",
"arguments": [
{"name": "topic", "type": "string", "description": "The topic for the joke"}
]
}
```
### Compatibilidad hacia atrás
Los tipos de herramientas integradas existentes continúan funcionando sin cambios.
`tool-service` es un nuevo tipo de herramienta, además de los tipos existentes (`knowledge-query`, `mcp-tool`, etc.).
## Consideraciones futuras
### Servicios de autoanuncio
Una mejora futura podría permitir que los servicios publiquen sus propios descriptores:
Los servicios publican en un tema `tool-descriptors` conocido al inicio.
El agente se suscribe y registra dinámicamente las herramientas.
Permite una verdadera funcionalidad de conexión y uso sin cambios de configuración.
Esto está fuera del alcance de la implementación inicial.
## Referencias
Implementación actual de la herramienta: `trustgraph-flow/trustgraph/agent/react/tools.py`
Registro de herramientas: `trustgraph-flow/trustgraph/agent/react/service.py:105-214`
Esquemas del agente: `trustgraph-base/trustgraph/schema/services/agent.py`

View file

@ -0,0 +1,404 @@
---
layout: default
title: "Decodificador Universal de Documentos"
parent: "Spanish (Beta)"
---
# Decodificador Universal de Documentos
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Título
Decodificador universal de documentos impulsado por `unstructured`: ingrese cualquier formato de documento común
a través de un servicio único con un registro completo de la procedencia y la integración de la biblioteca,
registrando las posiciones de origen como metadatos del gráfico de conocimiento para
la trazabilidad de extremo a extremo.
## Problema
Actualmente, TrustGraph tiene un decodificador específico para PDF. Para admitir formatos adicionales
(DOCX, XLSX, HTML, Markdown, texto plano, PPTX, etc.), es necesario
escribir un nuevo decodificador por formato o adoptar una biblioteca de extracción universal.
Cada formato tiene una estructura diferente; algunos son basados en páginas, otros no,
y la cadena de procedencia debe registrar de dónde se originó cada parte del texto extraído
en el documento original.
## Enfoque
### Biblioteca: `unstructured`
Utilice `unstructured.partition.auto.partition()`, que detecta automáticamente el formato
a partir del tipo MIME o la extensión del archivo y extrae elementos estructurados
(Título, TextoNarrativo, Tabla, ElementoLista, etc.). Cada elemento lleva
metadatos que incluyen:
`page_number` (para formatos basados en páginas como PDF, PPTX)
`element_id` (único para cada elemento)
`coordinates` (cuadro delimitador para PDF)
`text` (el contenido de texto extraído)
`category` (tipo de elemento: Título, TextoNarrativo, Tabla, etc.)
### Tipos de Elemento
`unstructured` extrae elementos tipados de los documentos. Cada elemento tiene
una categoría y metadatos asociados:
**Elementos de texto:**
`Title` — encabezados de sección
`NarrativeText` — párrafos del cuerpo
`ListItem` — elementos de lista con viñetas/numerados
`Header`, `Footer` — encabezados/pies de página
`FigureCaption` — leyendas para figuras/imágenes
`Formula` — expresiones matemáticas
`Address`, `EmailAddress` — información de contacto
`CodeSnippet` — bloques de código (de Markdown)
**Tablas:**
`Table` — datos tabulares estructurados. `unstructured` proporciona tanto
`element.text` (texto plano) como `element.metadata.text_as_html`
(HTML completo `<table>` con filas, columnas y encabezados conservados).
Para formatos con una estructura de tabla explícita (DOCX, XLSX, HTML),
la extracción es muy fiable. Para PDF, la detección de tablas depende de
la estrategia de `hi_res` con el análisis de diseño.
**Imágenes:**
`Image` — imágenes integradas detectadas mediante el análisis de diseño (requiere
la estrategia de `hi_res`). Con `extract_image_block_to_payload=True`,
devuelve los datos de la imagen como base64 en `element.metadata.image_base64`.
El texto OCR de la imagen está disponible en `element.text`.
### Manejo de Tablas
Las tablas son una salida de primera clase. Cuando el decodificador encuentra un elemento `Table`,
conserva la estructura HTML en lugar de aplanarla a texto plano. Esto proporciona
a la biblioteca de extracción LLM descendente una mejor entrada para extraer conocimiento
estructurado de los datos tabulares.
El texto de la página/sección se ensambla de la siguiente manera:
Elementos de texto: texto plano, unido con saltos de línea
Elementos de tabla: marcado de tabla HTML de `text_as_html`, envuelto en un
marcador `<table>` para que el LLM pueda distinguir las tablas del texto narrativo.
Por ejemplo, una página con un encabezado, un párrafo y una tabla produce:
```
Financial Overview
Revenue grew 15% year-over-year driven by enterprise adoption.
<table>
<tr><th>Quarter</th><th>Revenue</th><th>Growth</th></tr>
<tr><td>Q1</td><td>$12M</td><td>12%</td></tr>
<tr><td>Q2</td><td>$14M</td><td>17%</td></tr>
</table>
```
Esto preserva la estructura de la tabla mediante la segmentación y en la
canalización de extracción, donde el LLM puede extraer relaciones directamente de
celdas estructuradas en lugar de adivinar la alineación de columnas a partir de
espacios en blanco.
### Manejo de imágenes
Las imágenes se extraen y se almacenan en la biblioteca como documentos
secundarios con un `document_type="image"` y un ID `urn:image:{uuid}`. Obtienen
tripletas de procedencia con el tipo `tg:Image`, vinculadas a su página/sección
principal a través de `prov:wasDerivedFrom`. Los metadatos de la imagen (coordenadas,
dimensiones, element_id) se registran en la procedencia.
**Es crucial que las imágenes NO se emitan como salidas de TextDocument.** Se
almacenan únicamente; no se envían a la canalización de segmentación ni a ningún
proceso de texto. Esto es intencional:
1. Todavía no existe una canalización de procesamiento de imágenes (la integración
del modelo de visión es un trabajo futuro).
2. Alimentar datos de imagen en base64 o fragmentos de OCR en la
canalización de extracción de texto produciría tripletas de KG basura.
Las imágenes también se excluyen del texto de la página ensamblada; cualquier `Image`
elemento se omite silenciosamente al concatenar los textos de los elementos para una
página/sección. La cadena de procedencia registra que las imágenes existen y dónde
aparecieron en el documento, por lo que pueden ser recuperadas por una futura
canalización de procesamiento de imágenes sin volver a importar el documento.
#### Trabajo futuro
Enviar `tg:Image` entidades a un modelo de visión para descripción,
interpretación de diagramas o extracción de datos de gráficos.
Almacenar las descripciones de las imágenes como documentos de texto secundarios que se integran en
la canalización estándar de fragmentación/extracción.
Vincular el conocimiento extraído de nuevo a las imágenes de origen a través de la procedencia.
### Estrategias de sección
Para formatos basados en páginas (PDF, PPTX, XLSX), los elementos siempre se agrupan
por página/diapositiva/hoja primero. Para formatos no basados en páginas (DOCX, HTML, Markdown,
etc.), el decodificador necesita una estrategia para dividir el documento en
secciones. Esto se puede configurar en tiempo de ejecución a través de `--section-strategy`.
Cada estrategia es una función de agrupación sobre la lista de `unstructured`
elementos. La salida es una lista de grupos de elementos; el resto de la
canalización (ensamblaje de texto, almacenamiento en la biblioteca, procedencia,
emisión de TextDocument) es idéntico independientemente de la estrategia.
#### `whole-document` (predeterminado)
Emite todo el documento como una sola sección. Permite que el
fragmentador (chunker) posterior gestione todas las divisiones.
Enfoque más simple, buena línea de base
Puede producir un TextDocument muy grande para archivos grandes, pero el
fragmentador se encarga de eso.
Mejor cuando se desea el máximo contexto por sección.
#### `heading`
Divide en elementos de encabezado (`Title`). Cada sección es un encabezado más
todo el contenido hasta el siguiente encabezado de igual o mayor nivel. Los
encabezados anidados crean secciones anidadas.
Produce unidades temáticamente coherentes.
Funciona bien para documentos estructurados (informes, manuales, especificaciones).
Proporciona al LLM de extracción el contexto del encabezado junto con el contenido.
Retrocede a `whole-document` si no se encuentran encabezados.
#### `element-type`
Divide cuando el tipo de elemento cambia significativamente; específicamente,
comienza una nueva sección en las transiciones entre texto narrativo y tablas.
Los elementos consecutivos de la misma categoría general (texto, texto, texto o
tabla, tabla) permanecen agrupados.
Mantiene las tablas como secciones independientes.
Bueno para documentos con contenido mixto (informes con tablas de datos).
Las tablas reciben una atención de extracción dedicada.
#### `count`
Agrupa un número fijo de elementos por sección. Configurable a través de
`--section-element-count` (predeterminado: 20).
Simple y predecible.
No respeta la estructura del documento.
Útil como alternativa o para la experimentación.
#### `size`
Acumula elementos hasta que se alcanza un límite de caracteres, luego comienza una
nueva sección. Respeta los límites de los elementos; nunca los divide a la mitad.
Configurable a través de `--section-max-size` (por defecto: 4000 caracteres).
Produce tamaños de sección aproximadamente uniformes.
Respeta los límites de los elementos (a diferencia del fragmentador posterior).
Buen compromiso entre estructura y control de tamaño.
Si un solo elemento excede el límite, se convierte en su propia sección.
#### Interacción con formatos basados en páginas
Para formatos basados en páginas, el agrupamiento de páginas siempre tiene prioridad.
Las estrategias de sección opcionalmente se pueden aplicar *dentro* de una página si es muy
grande (por ejemplo, una página PDF con una tabla enorme), controlado por
`--section-within-pages` (por defecto: falso). Cuando es falso, cada página es
siempre una sección, independientemente del tamaño.
### Detección de formato
El decodificador necesita conocer el tipo MIME del documento para pasarlo a
`unstructured`'s `partition()`. Dos caminos:
**Ruta del bibliotecario** (`document_id` configurado): primero, recupera los metadatos del documento
del bibliotecario; esto nos da el `kind` (tipo MIME)
que se registró en el momento de la carga. Luego, recupera el contenido del documento.
Dos llamadas al bibliotecario, pero la recuperación de metadatos es ligera.
**Ruta integrada** (compatibilidad con versiones anteriores, `data` configurado): no hay metadatos
disponibles en el mensaje. Usa `python-magic` para detectar el formato
a partir de los bytes del contenido como una opción alternativa.
No se necesitan cambios en el esquema `Document`; el bibliotecario ya almacena el tipo MIME.
### Arquitectura
Un único servicio `universal-decoder` que:
1. Recibe un mensaje `Document` (en línea o a través de una referencia a la biblioteca).
2. Si la ruta es a través de la biblioteca: recupera los metadatos del documento (obtiene el tipo MIME), luego
recupera el contenido. Si la ruta es en línea: detecta el formato a partir de los bytes del contenido.
3. Llama a `partition()` para extraer los elementos.
4. Agrupa elementos: por página para formatos basados en páginas, por estrategia de sección configurada para formatos no basados en páginas.
sección.
5. Para cada página/sección:
Genera un ID `urn:page:{uuid}` o `urn:section:{uuid}`
Ensambla el texto de la página: el texto narrativo como texto plano, las tablas como HTML,
imágenes omitidas
Calcula los desplazamientos de caracteres para cada elemento dentro del texto de la página.
Guarda en el sistema de gestión de documentos como documento hijo.
Emite triples de procedencia con metadatos de posición.
Envía `TextDocument` a la siguiente etapa para la segmentación.
6. Para cada elemento de imagen:
Genera un ID `urn:image:{uuid}`.
Guarda los datos de la imagen en el sistema de gestión de documentos como documento hijo.
Emite triples de procedencia (almacenados solo, no enviados a la siguiente etapa).
### Manejo de formatos
| Formato | Tipo MIME | Basado en páginas | Notas |
|----------|------------------------------------|------------|--------------------------------|
| PDF | application/pdf | Sí | Agrupación por página |
| DOCX | application/vnd.openxmlformats... | No | Utiliza estrategia de secciones |
| PPTX | application/vnd.openxmlformats... | Sí | Agrupación por diapositiva |
| XLSX/XLS | application/vnd.openxmlformats... | Sí | Agrupación por hoja |
| HTML | text/html | No | Utiliza estrategia de secciones |
| Markdown | text/markdown | No | Utiliza estrategia de secciones |
| Texto plano | text/plain | No | Utiliza estrategia de secciones |
| CSV | text/csv | No | Utiliza estrategia de secciones |
| RST | text/x-rst | No | Utiliza estrategia de secciones |
| RTF | application/rtf | No | Utiliza estrategia de secciones |
| ODT | application/vnd.oasis... | No | Utiliza estrategia de secciones |
| TSV | text/tab-separated-values | No | Utiliza estrategia de secciones |
### Metadatos de procedencia
Cada entidad de página/sección registra metadatos de posición como metadatos de procedencia
en `GRAPH_SOURCE`, lo que permite una trazabilidad completa desde las triples del grafo de conocimiento
hasta las posiciones del documento de origen.
#### Campos existentes (ya están en `derived_entity_triples`)
`page_number` — número de página/hoja/diapositiva (indexado desde 1, solo para páginas)
`char_offset` — desplazamiento de caracteres de esta página/sección dentro del
texto completo del documento.
`char_length` — longitud de caracteres del texto de esta página/sección.
#### Nuevos campos (extender `derived_entity_triples`)
`mime_type` — formato original del documento (por ejemplo, `application/pdf`)
`element_types` — lista separada por comas de categorías de elementos `unstructured`
encontradas en esta página/sección (por ejemplo, "Título,TextoNarrativo,Tabla")
`table_count` — número de tablas en esta página/sección.
`image_count` — número de imágenes en esta página/sección.
Estos requieren nuevos predicados del espacio de nombres TG:
```
TG_SECTION_TYPE = "https://trustgraph.ai/ns/Section"
TG_IMAGE_TYPE = "https://trustgraph.ai/ns/Image"
TG_ELEMENT_TYPES = "https://trustgraph.ai/ns/elementTypes"
TG_TABLE_COUNT = "https://trustgraph.ai/ns/tableCount"
TG_IMAGE_COUNT = "https://trustgraph.ai/ns/imageCount"
```
Esquema URN de imagen: `urn:image:{uuid}`
(`TG_MIME_TYPE` ya existe.)
#### Nuevo tipo de entidad
Para formatos que no son de página (DOCX, HTML, Markdown, etc.) donde el decodificador
emite todo el documento como una sola unidad en lugar de dividirlo por
página, la entidad obtiene un nuevo tipo:
```
TG_SECTION_TYPE = "https://trustgraph.ai/ns/Section"
```
Esto distingue las secciones de las páginas al consultar el origen:
| Entidad | Tipo | Cuándo se utiliza |
|----------|-----------------------------|----------------------------------------|
| Documento | `tg:Document` | Archivo original subido |
| Página | `tg:Page` | Formatos basados en páginas (PDF, PPTX, XLSX) |
| Sección | `tg:Section` | Formatos que no son de página (DOCX, HTML, MD, etc.) |
| Imagen | `tg:Image` | Imágenes incrustadas (almacenadas, no procesadas) |
| Fragmento | `tg:Chunk` | Resultado del fragmentador |
| Subgrafo | `tg:Subgraph` | Resultado de la extracción del grafo de conocimiento |
El tipo se establece por el decodificador según si se está agrupando por página
o emitiendo una sección de todo el documento. `derived_entity_triples` obtiene
un parámetro booleano opcional `section`; cuando es verdadero, la entidad se
clasifica como `tg:Section` en lugar de `tg:Page`.
#### Cadena completa de origen
```
KG triple
→ subgraph (extraction provenance)
→ chunk (char_offset, char_length within page)
→ page/section (page_number, char_offset, char_length within doc, mime_type, element_types)
→ document (original file in librarian)
```
Cada enlace es un conjunto de triples en el grafo denominado `GRAPH_SOURCE`.
### Configuración del servicio
Argumentos de línea de comandos:
```
--strategy Partitioning strategy: auto, hi_res, fast (default: auto)
--languages Comma-separated OCR language codes (default: eng)
--section-strategy Section grouping: whole-document, heading, element-type,
count, size (default: whole-document)
--section-element-count Elements per section for 'count' strategy (default: 20)
--section-max-size Max chars per section for 'size' strategy (default: 4000)
--section-within-pages Apply section strategy within pages too (default: false)
```
Además de los argumentos estándar de `FlowProcessor` y la cola del bibliotecario.
### Integración del flujo
El decodificador universal ocupa la misma posición en el flujo de procesamiento
que el decodificador PDF actual:
```
Document → [universal-decoder] → TextDocument → [chunker] → Chunk → ...
```
Se registra:
`input` consumidor (esquema de documento)
`output` productor (esquema de TextDocument)
`triples` productor (esquema de Triples)
Solicitud/respuesta del bibliotecario (para la recuperación y el almacenamiento de documentos secundarios)
### Despliegue
Nuevo contenedor: `trustgraph-flow-universal-decoder`
Dependencia: `unstructured[all-docs]` (incluye PDF, DOCX, PPTX, etc.)
Puede ejecutarse junto con o reemplazar el decodificador PDF existente, dependiendo de
la configuración del flujo.
El decodificador PDF existente permanece disponible para entornos donde
las dependencias de `unstructured` son demasiado pesadas.
### ¿Qué Cambia?
| Component | Change |
|------------------------------|-------------------------------------------------|
| `provenance/namespaces.py` | Add `TG_SECTION_TYPE`, `TG_IMAGE_TYPE`, `TG_ELEMENT_TYPES`, `TG_TABLE_COUNT`, `TG_IMAGE_COUNT` |
| `provenance/triples.py` | Add `mime_type`, `element_types`, `table_count`, `image_count` kwargs |
| `provenance/__init__.py` | Export new constants |
| New: `decoding/universal/` | New decoder service module |
| `setup.cfg` / `pyproject` | Add `unstructured[all-docs]` dependency |
| Docker | New container image |
| Flow definitions | Wire universal-decoder as document input |
### What Doesn't Change
Chunker (receives TextDocument, works as before)
Downstream extractors (receive Chunk, unchanged)
Librarian (stores child documents, unchanged)
Schema (Document, TextDocument, Chunk unchanged)
Query-time provenance (unchanged)
## Risks
`unstructured[all-docs]` has heavy dependencies (poppler, tesseract,
libreoffice for some formats). Container image will be larger.
Mitigation: offer a `[light]` variant without OCR/office deps.
Some formats may produce poor text extraction (scanned PDFs without
OCR, complex XLSX layouts). Mitigation: configurable `strategy`
parameter, and the existing Mistral OCR decoder remains available
for high-quality PDF OCR.
`unstructured` version updates may change element metadata.
Mitigation: pin version, test extraction quality per format.

View file

@ -0,0 +1,307 @@
---
layout: default
title: "Gestión del ciclo de vida del almacén de vectores"
parent: "Spanish (Beta)"
---
# Gestión del ciclo de vida del almacén de vectores
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
## Resumen
Este documento describe cómo TrustGraph gestiona las colecciones de almacenes de vectores en diferentes implementaciones de backend (Qdrant, Pinecone, Milvus). El diseño aborda el desafío de admitir incrustaciones con diferentes dimensiones sin codificar valores de dimensión.
## Declaración del problema
Los almacenes de vectores requieren que se especifique la dimensión de la incrustación al crear colecciones/índices. Sin embargo:
Los diferentes modelos de incrustación producen diferentes dimensiones (por ejemplo, 384, 768, 1536)
La dimensión no se conoce hasta que se genera la primera incrustación
Una sola colección de TrustGraph puede recibir incrustaciones de múltiples modelos
Codificar una dimensión (por ejemplo, 384) causa fallos con otros tamaños de incrustación
## Principios de diseño
1. **Creación perezosa**: Las colecciones se crean bajo demanda durante la primera escritura, no durante las operaciones de gestión de colecciones.
2. **Nombres basados en la dimensión**: Los nombres de las colecciones incluyen la dimensión de la incrustación como sufijo.
3. **Degradación gradual**: Las consultas contra colecciones inexistentes devuelven resultados vacíos, no errores.
4. **Soporte para múltiples dimensiones**: Una sola colección lógica puede tener múltiples colecciones físicas (una por dimensión).
## Arquitectura
### Convención de nombres de colecciones
Las colecciones del almacén de vectores utilizan sufijos de dimensión para admitir múltiples tamaños de incrustación:
**Incrustaciones de documentos:**
Qdrant: `d_{user}_{collection}_{dimension}`
Pinecone: `d-{user}-{collection}-{dimension}`
Milvus: `doc_{user}_{collection}_{dimension}`
**Incrustaciones de gráficos:**
Qdrant: `t_{user}_{collection}_{dimension}`
Pinecone: `t-{user}-{collection}-{dimension}`
Milvus: `entity_{user}_{collection}_{dimension}`
Ejemplos:
`d_alice_papers_384` - Colección de documentos de Alice con incrustaciones de 384 dimensiones
`d_alice_papers_768` - Misma colección lógica con incrustaciones de 768 dimensiones
`t_bob_knowledge_1536` - Gráfico de conocimiento de Bob con incrustaciones de 1536 dimensiones
### Fases del ciclo de vida
#### 1. Solicitud de creación de colección
**Flujo de solicitud:**
```
User/System → Librarian → Storage Management Topic → Vector Stores
```
**Comportamiento:**
El bibliotecario transmite las solicitudes `create-collection` a todos los backends de almacenamiento.
Los procesadores de almacenes vectoriales reconocen la solicitud, pero **no crean colecciones físicas**.
La respuesta se devuelve inmediatamente con éxito.
La creación real de la colección se pospone hasta la primera escritura.
**Justificación:**
La dimensión es desconocida en el momento de la creación.
Evita la creación de colecciones con dimensiones incorrectas.
Simplifica la lógica de gestión de colecciones.
#### 2. Operaciones de escritura (Creación diferida)
**Flujo de escritura:**
```
Data → Storage Processor → Check Collection → Create if Needed → Insert
```
**Comportamiento:**
1. Extraer la dimensión de incrustación del vector: `dim = len(vector)`
2. Construir el nombre de la colección con el sufijo de dimensión
3. Verificar si la colección existe con esa dimensión específica
4. Si no existe:
Crear la colección con la dimensión correcta
Registrar: `"Lazily creating collection {name} with dimension {dim}"`
5. Insertar la incrustación en la colección específica de la dimensión
**Escenario de ejemplo:**
```
1. User creates collection "papers"
→ No physical collections created yet
2. First document with 384-dim embedding arrives
→ Creates d_user_papers_384
→ Inserts data
3. Second document with 768-dim embedding arrives
→ Creates d_user_papers_768
→ Inserts data
Result: Two physical collections for one logical collection
```
#### 3. Operaciones de consulta
**Flujo de consulta:**
```
Query Vector → Determine Dimension → Check Collection → Search or Return Empty
```
**Comportamiento:**
1. Extraer la dimensión del vector de consulta: `dim = len(vector)`
2. Construir el nombre de la colección con el sufijo de dimensión
3. Comprobar si la colección existe
4. Si existe:
Realizar una búsqueda de similitud
Devolver los resultados
5. Si no existe:
Registrar: `"Collection {name} does not exist, returning empty results"`
Devolver una lista vacía (no se genera ningún error)
**Múltiples Dimensiones en la Misma Consulta:**
Si la consulta contiene vectores de diferentes dimensiones
Cada dimensión consulta su colección correspondiente
Los resultados se agregan
Las colecciones faltantes se omiten (no se consideran errores)
**Justificación:**
Consultar una colección vacía es un caso de uso válido
Devolver resultados vacíos es semánticamente correcto
Evita errores durante el inicio del sistema o antes de la ingesta de datos
#### 4. Eliminación de Colecciones
**Flujo de Eliminación:**
```
Delete Request → List All Collections → Filter by Prefix → Delete All Matches
```
**Comportamiento:**
1. Construir el patrón de prefijo: `d_{user}_{collection}_` (observar el guion bajo al final)
2. Listar todas las colecciones en el almacén vectorial
3. Filtrar las colecciones que coincidan con el prefijo
4. Eliminar todas las colecciones que coincidan
5. Registrar cada eliminación: `"Deleted collection {name}"`
6. Registro resumido: `"Deleted {count} collection(s) for {user}/{collection}"`
**Ejemplo:**
```
Collections in store:
- d_alice_papers_384
- d_alice_papers_768
- d_alice_reports_384
- d_bob_papers_384
Delete "papers" for alice:
→ Deletes: d_alice_papers_384, d_alice_papers_768
→ Keeps: d_alice_reports_384, d_bob_papers_384
```
**Justificación:**
Asegura una limpieza completa de todas las variantes de dimensión.
La coincidencia de patrones evita la eliminación accidental de colecciones no relacionadas.
Operación atómica desde la perspectiva del usuario (todas las dimensiones se eliminan juntas).
## Características de comportamiento
### Operaciones normales
**Creación de colecciones:**
✓ Devuelve éxito inmediatamente.
✓ No se asigna almacenamiento físico.
✓ Operación rápida (sin E/S de backend).
**Primera escritura:**
✓ Crea la colección con la dimensión correcta.
✓ Ligeramente más lenta debido a la sobrecarga de la creación de la colección.
✓ Las escrituras posteriores a la misma dimensión son rápidas.
**Consultas antes de cualquier escritura:**
✓ Devuelve resultados vacíos.
✓ No hay errores ni excepciones.
✓ El sistema permanece estable.
**Escrituras de dimensiones mixtas:**
✓ Crea automáticamente colecciones separadas por dimensión.
✓ Cada dimensión está aislada en su propia colección.
✓ No hay conflictos de dimensiones ni errores de esquema.
**Eliminación de colecciones:**
✓ Elimina todas las variantes de dimensión.
✓ Limpieza completa.
✓ No hay colecciones huérfanas.
### Casos límite
**Múltiples modelos de incrustación:**
```
Scenario: User switches from model A (384-dim) to model B (768-dim)
Behavior:
- Both dimensions coexist in separate collections
- Old data (384-dim) remains queryable with 384-dim vectors
- New data (768-dim) queryable with 768-dim vectors
- Cross-dimension queries return results only for matching dimension
```
**Primeras Escrituras Concurrentes:**
```
Scenario: Multiple processes write to same collection simultaneously
Behavior:
- Each process checks for existence before creating
- Most vector stores handle concurrent creation gracefully
- If race condition occurs, second create is typically idempotent
- Final state: Collection exists and both writes succeed
```
**Migración de Dimensiones:**
```
Scenario: User wants to migrate from 384-dim to 768-dim embeddings
Behavior:
- No automatic migration
- Old collection (384-dim) persists
- New collection (768-dim) created on first new write
- Both dimensions remain accessible
- Manual deletion of old dimension collections possible
```
**Consultas de Colecciones Vacías:**
```
Scenario: Query a collection that has never received data
Behavior:
- Collection doesn't exist (never created)
- Query returns empty list
- No error state
- System logs: "Collection does not exist, returning empty results"
```
## Notas de Implementación
### Detalles Específicos del Backend de Almacenamiento
**Qdrant:**
Utiliza `collection_exists()` para verificaciones de existencia
Utiliza `get_collections()` para listar durante la eliminación
La creación de colecciones requiere `VectorParams(size=dim, distance=Distance.COSINE)`
**Pinecone:**
Utiliza `has_index()` para verificaciones de existencia
Utiliza `list_indexes()` para listar durante la eliminación
La creación de índices requiere esperar el estado "ready"
La especificación serverless se configura con la región de la nube
**Milvus:**
Las clases directas (`DocVectors`, `EntityVectors`) gestionan el ciclo de vida
Caché interno `self.collections[(dim, user, collection)]` para mejorar el rendimiento
Los nombres de las colecciones se sanitizan (solo alfanumérico y guión bajo)
Admite esquemas con IDs de auto-incremento
### Consideraciones de Rendimiento
**Latencia de la Primera Escritura:**
Sobrecarga adicional debido a la creación de la colección
Qdrant: ~100-500ms
Pinecone: ~10-30 segundos (provisionamiento serverless)
Milvus: ~500-2000ms (incluye indexación)
**Rendimiento de la Consulta:**
La verificación de existencia agrega una sobrecarga mínima (~1-10ms)
No hay impacto en el rendimiento una vez que la colección existe
Cada colección de dimensiones se optimiza de forma independiente
**Sobrecarga de Almacenamiento:**
Metadatos mínimos por colección
La principal sobrecarga es el almacenamiento por dimensión
Compromiso: Espacio de almacenamiento vs. flexibilidad de las dimensiones
## Consideraciones Futuras
**Consolidación Automática de Dimensiones:**
Se podría agregar un proceso en segundo plano para identificar y fusionar variantes de dimensiones no utilizadas
Requeriría re-embedding o reducción de dimensiones
**Descubrimiento de Dimensiones:**
Se podría exponer una API para listar todas las dimensiones utilizadas para una colección
Útil para la administración y el monitoreo
**Preferencia de Dimensión Predeterminada:**
Se podría rastrear la dimensión "primaria" por colección
Utilizar para consultas cuando el contexto de la dimensión no está disponible
**Cuotas de Almacenamiento:**
Es posible que se necesiten límites de dimensiones por colección
Prevenir la proliferación de variantes de dimensiones
## Notas de Migración
**Desde el Sistema de Sufijo de Dimensión Anterior:**
Colecciones antiguas: `d_{user}_{collection}` (sin sufijo de dimensión)
Colecciones nuevas: `d_{user}_{collection}_{dim}` (con sufijo de dimensión)
No hay migración automática: las colecciones antiguas permanecen accesibles
Considere un script de migración manual si es necesario
Se pueden ejecutar ambos esquemas de nombres simultáneamente
## Referencias
Gestión de Colecciones: `docs/tech-specs/collection-management.md`
Esquema de Almacenamiento: `trustgraph-base/trustgraph/schema/services/storage.py`
Servicio de Bibliotecario: `trustgraph-flow/trustgraph/librarian/service.py`