mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-04-28 01:46:22 +02:00
Structure the tech specs directory (#836)
Tech spec some subdirectories for different languages
This commit is contained in:
parent
48da6c5f8b
commit
e7efb673ef
423 changed files with 0 additions and 0 deletions
135
docs/tech-specs/es/__TEMPLATE.es.md
Normal file
135
docs/tech-specs/es/__TEMPLATE.es.md
Normal 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]
|
||||
280
docs/tech-specs/es/agent-explainability.es.md
Normal file
280
docs/tech-specs/es/agent-explainability.es.md
Normal 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)
|
||||
113
docs/tech-specs/es/architecture-principles.es.md
Normal file
113
docs/tech-specs/es/architecture-principles.es.md
Normal 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.
|
||||
249
docs/tech-specs/es/cassandra-consolidation.es.md
Normal file
249
docs/tech-specs/es/cassandra-consolidation.es.md
Normal 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`
|
||||
687
docs/tech-specs/es/cassandra-performance-refactor.es.md
Normal file
687
docs/tech-specs/es/cassandra-performance-refactor.es.md
Normal 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.
|
||||
410
docs/tech-specs/es/collection-management.es.md
Normal file
410
docs/tech-specs/es/collection-management.es.md
Normal 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
|
||||
144
docs/tech-specs/es/document-embeddings-chunk-id.es.md
Normal file
144
docs/tech-specs/es/document-embeddings-chunk-id.es.md
Normal 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.
|
||||
675
docs/tech-specs/es/embeddings-batch-processing.es.md
Normal file
675
docs/tech-specs/es/embeddings-batch-processing.es.md
Normal 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)
|
||||
269
docs/tech-specs/es/entity-centric-graph.es.md
Normal file
269
docs/tech-specs/es/entity-centric-graph.es.md
Normal 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__]]
|
||||
188
docs/tech-specs/es/explainability-cli.es.md
Normal file
188
docs/tech-specs/es/explainability-cli.es.md
Normal 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`
|
||||
355
docs/tech-specs/es/extraction-flows.es.md
Normal file
355
docs/tech-specs/es/extraction-flows.es.md
Normal 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
|
||||
213
docs/tech-specs/es/extraction-provenance-subgraph.es.md
Normal file
213
docs/tech-specs/es/extraction-provenance-subgraph.es.md
Normal 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.
|
||||
803
docs/tech-specs/es/extraction-time-provenance.es.md
Normal file
803
docs/tech-specs/es/extraction-time-provenance.es.md
Normal 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)
|
||||
304
docs/tech-specs/es/flow-class-definition.es.md
Normal file
304
docs/tech-specs/es/flow-class-definition.es.md
Normal 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
|
||||
644
docs/tech-specs/es/flow-configurable-parameters.es.md
Normal file
644
docs/tech-specs/es/flow-configurable-parameters.es.md
Normal 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)
|
||||
394
docs/tech-specs/es/graph-contexts.es.md
Normal file
394
docs/tech-specs/es/graph-contexts.es.md
Normal 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)
|
||||
467
docs/tech-specs/es/graphql-query.es.md
Normal file
467
docs/tech-specs/es/graphql-query.es.md
Normal 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
|
||||
309
docs/tech-specs/es/graphrag-performance-optimization.es.md
Normal file
309
docs/tech-specs/es/graphrag-performance-optimization.es.md
Normal 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)
|
||||
725
docs/tech-specs/es/import-export-graceful-shutdown.es.md
Normal file
725
docs/tech-specs/es/import-export-graceful-shutdown.es.md
Normal 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)
|
||||
524
docs/tech-specs/es/jsonl-prompt-output.es.md
Normal file
524
docs/tech-specs/es/jsonl-prompt-output.es.md
Normal 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)
|
||||
1097
docs/tech-specs/es/large-document-loading.es.md
Normal file
1097
docs/tech-specs/es/large-document-loading.es.md
Normal file
File diff suppressed because it is too large
Load diff
215
docs/tech-specs/es/logging-strategy.es.md
Normal file
215
docs/tech-specs/es/logging-strategy.es.md
Normal 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 |
|
||||
168
docs/tech-specs/es/mcp-tool-arguments.es.md
Normal file
168
docs/tech-specs/es/mcp-tool-arguments.es.md
Normal 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]
|
||||
562
docs/tech-specs/es/mcp-tool-bearer-token.es.md
Normal file
562
docs/tech-specs/es/mcp-tool-bearer-token.es.md
Normal 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
|
||||
266
docs/tech-specs/es/minio-to-s3-migration.es.md
Normal file
266
docs/tech-specs/es/minio-to-s3-migration.es.md
Normal 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
|
||||
165
docs/tech-specs/es/more-config-cli.es.md
Normal file
165
docs/tech-specs/es/more-config-cli.es.md
Normal 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`
|
||||
780
docs/tech-specs/es/multi-tenant-support.es.md
Normal file
780
docs/tech-specs/es/multi-tenant-support.es.md
Normal 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`
|
||||
217
docs/tech-specs/es/neo4j-user-collection-isolation.es.md
Normal file
217
docs/tech-specs/es/neo4j-user-collection-isolation.es.md
Normal 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.
|
||||
769
docs/tech-specs/es/ontology-extract-phase-2.es.md
Normal file
769
docs/tech-specs/es/ontology-extract-phase-2.es.md
Normal 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`
|
||||
277
docs/tech-specs/es/ontology.es.md
Normal file
277
docs/tech-specs/es/ontology.es.md
Normal 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)
|
||||
1075
docs/tech-specs/es/ontorag.es.md
Normal file
1075
docs/tech-specs/es/ontorag.es.md
Normal file
File diff suppressed because it is too large
Load diff
239
docs/tech-specs/es/openapi-spec.es.md
Normal file
239
docs/tech-specs/es/openapi-spec.es.md
Normal 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`
|
||||
965
docs/tech-specs/es/pubsub.es.md
Normal file
965
docs/tech-specs/es/pubsub.es.md
Normal 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
|
||||
1516
docs/tech-specs/es/python-api-refactor.es.md
Normal file
1516
docs/tech-specs/es/python-api-refactor.es.md
Normal file
File diff suppressed because it is too large
Load diff
208
docs/tech-specs/es/query-time-explainability.es.md
Normal file
208
docs/tech-specs/es/query-time-explainability.es.md
Normal 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`
|
||||
196
docs/tech-specs/es/rag-streaming-support.es.md
Normal file
196
docs/tech-specs/es/rag-streaming-support.es.md
Normal 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`
|
||||
99
docs/tech-specs/es/schema-refactoring-proposal.es.md
Normal file
99
docs/tech-specs/es/schema-refactoring-proposal.es.md
Normal 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"}]
|
||||
578
docs/tech-specs/es/streaming-llm-responses.es.md
Normal file
578
docs/tech-specs/es/streaming-llm-responses.es.md
Normal 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/`
|
||||
621
docs/tech-specs/es/structured-data-2.es.md
Normal file
621
docs/tech-specs/es/structured-data-2.es.md
Normal 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)
|
||||
567
docs/tech-specs/es/structured-data-descriptor.es.md
Normal file
567
docs/tech-specs/es/structured-data-descriptor.es.md
Normal 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.
|
||||
```
|
||||
145
docs/tech-specs/es/structured-data-schemas.es.md
Normal file
145
docs/tech-specs/es/structured-data-schemas.es.md
Normal 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.
|
||||
260
docs/tech-specs/es/structured-data.es.md
Normal file
260
docs/tech-specs/es/structured-data.es.md
Normal 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.
|
||||
281
docs/tech-specs/es/structured-diag-service.es.md
Normal file
281
docs/tech-specs/es/structured-diag-service.es.md
Normal 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`
|
||||
499
docs/tech-specs/es/tool-group.es.md
Normal file
499
docs/tech-specs/es/tool-group.es.md
Normal 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.
|
||||
479
docs/tech-specs/es/tool-services.es.md
Normal file
479
docs/tech-specs/es/tool-services.es.md
Normal 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`
|
||||
404
docs/tech-specs/es/universal-decoder.es.md
Normal file
404
docs/tech-specs/es/universal-decoder.es.md
Normal 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.
|
||||
307
docs/tech-specs/es/vector-store-lifecycle.es.md
Normal file
307
docs/tech-specs/es/vector-store-lifecycle.es.md
Normal 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`
|
||||
Loading…
Add table
Add a link
Reference in a new issue